├── .gitignore ├── extraParams.hxml ├── tests ├── misc │ ├── EmptyImport.hx │ ├── packs │ │ └── Parent.hx │ ├── InheritanceTests.hx │ ├── UsingTests.hx │ ├── OnTypeYieldedTestMacro.hx │ ├── ErrorTests.hx │ ├── PrivateTests.hx │ ├── IterationTests.hx │ ├── OnTypeYieldedTests.hx │ ├── AbstractTests.hx │ ├── InferenceTests.hx │ ├── ImportTests.hx │ ├── AccessionTests.hx │ ├── YieldTests.hx │ ├── GenericTests.hx │ └── ScopeTests.hx ├── pack │ ├── enums │ │ ├── SomeClass.hx │ │ ├── SomeTypeDef.hx │ │ ├── EnumA.hx │ │ └── EnumB.hx │ ├── pack2 │ │ └── MiscYielded.hx │ ├── pack1 │ │ ├── MiscFunctions.hx │ │ ├── OperatorOverloading2.hx │ │ └── MoreMiscFunctions.hx │ └── pack3 │ │ └── SomeFunctions.hx ├── options │ ├── parsing │ │ ├── subparsing │ │ │ ├── unparsed │ │ │ │ └── UnparsedTests.hx │ │ │ └── SubParsingTests.hx │ │ ├── ParsingTests.hx │ │ └── recursive │ │ │ ├── RecursiveParsingTests.hx │ │ │ └── parsed │ │ │ └── RecursiveSubParsingTests.hx │ ├── ExtendTests.hx │ ├── ExplicitTests.hx │ └── KeywordTests.hx ├── issues │ ├── Issue5.hx │ └── Issue9.hx ├── Tests.hx └── eparsers │ ├── EIfTests.hx │ ├── EWhileTests.hx │ └── ETryTests.hx ├── tests.hxml ├── haxelib.json ├── .travis.yml ├── .vscode └── tasks.json ├── LICENSE.md ├── yield ├── parser │ ├── idents │ │ ├── IdentChannel.hx │ │ ├── IdentOption.hx │ │ ├── Statement.hx │ │ ├── ArgIdentData.hx │ │ ├── IdentRef.hx │ │ └── IdentData.hx │ ├── eactions │ │ ├── Action.hx │ │ └── ActionParser.hx │ ├── eparsers │ │ ├── BaseParser.hx │ │ ├── ETernaryParser.hx │ │ ├── EBlockParser.hx │ │ ├── EVarsParser.hx │ │ ├── EMetaParser.hx │ │ ├── EFunctionParser.hx │ │ ├── EConstParser.hx │ │ ├── EIfParser.hx │ │ ├── ETryParser.hx │ │ ├── EWhileParser.hx │ │ ├── EForParser.hx │ │ └── ESwitchParser.hx │ ├── tools │ │ ├── IdentCategory.hx │ │ ├── ImportTools.hx │ │ ├── ExpressionTools.hx │ │ └── FieldTools.hx │ ├── PositionManager.hx │ ├── env │ │ └── ClassData.hx │ ├── checks │ │ └── InitializationChecker.hx │ └── YieldSplitterOptimizer.hx ├── macrotime │ └── Tools.hx ├── YieldOption.hx └── generators │ ├── BuildingData.hx │ └── NameController.hx ├── .github └── workflows │ └── build.yml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | release.zip 2 | bin/ 3 | -------------------------------------------------------------------------------- /extraParams.hxml: -------------------------------------------------------------------------------- 1 | --macro yield.parser.Parser.auto() -------------------------------------------------------------------------------- /tests/misc/EmptyImport.hx: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | class EmptyImport { 4 | 5 | } -------------------------------------------------------------------------------- /tests/pack/enums/SomeClass.hx: -------------------------------------------------------------------------------- 1 | package pack.enums; 2 | 3 | class SomeClass { 4 | 5 | } -------------------------------------------------------------------------------- /tests/pack/enums/SomeTypeDef.hx: -------------------------------------------------------------------------------- 1 | package pack.enums; 2 | 3 | typedef SomeTypeDef = {} -------------------------------------------------------------------------------- /tests/pack/enums/EnumA.hx: -------------------------------------------------------------------------------- 1 | package pack.enums; 2 | 3 | enum EnumA { 4 | A; 5 | AA; 6 | AAA; 7 | } -------------------------------------------------------------------------------- /tests/pack/enums/EnumB.hx: -------------------------------------------------------------------------------- 1 | package pack.enums; 2 | 3 | 4 | enum EnumB { 5 | B; 6 | BB; 7 | BBB; 8 | } -------------------------------------------------------------------------------- /tests/pack/pack2/MiscYielded.hx: -------------------------------------------------------------------------------- 1 | package pack.pack2; 2 | 3 | @:yield 4 | class MiscYielded 5 | { 6 | public static inline function inlineMethod2 (s:String) { 7 | @yield return s+1; 8 | @yield return s+2; 9 | @yield return s+3; 10 | } 11 | } -------------------------------------------------------------------------------- /tests.hxml: -------------------------------------------------------------------------------- 1 | -cp tests 2 | -lib utest 3 | -main Tests 4 | -dce full 5 | -debug 6 | -D travis 7 | -D yield-parse= options.parsing.ParsingTests, options.parsing.subparsing, options.parsing.recursive.* 8 | -D yield-types= misc.EmptyImport 9 | --macro misc.OnTypeYieldedTestMacro.init() -------------------------------------------------------------------------------- /tests/pack/pack1/MiscFunctions.hx: -------------------------------------------------------------------------------- 1 | package pack.pack1; 2 | 3 | class MiscFunctions 4 | { 5 | 6 | public function new() { } 7 | 8 | public static function hello () { 9 | return "hello!"; 10 | } 11 | 12 | public static function sayHello (name:String) { 13 | return 'hello $name!'; 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /haxelib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yield", 3 | "url" : "https://github.com/dpomier/haxe-yield/", 4 | "license": "MIT", 5 | "tags": [ "cross", "utility", "macro" ], 6 | "description": "Cross platform C#-like `yield` generator for Haxe", 7 | "version": "3.2.2", 8 | "releasenote": "Various bugfixes and improved support", 9 | "contributors": ["dpomier"] 10 | } -------------------------------------------------------------------------------- /tests/options/parsing/subparsing/unparsed/UnparsedTests.hx: -------------------------------------------------------------------------------- 1 | package options.parsing.subparsing.unparsed; 2 | 3 | import utest.Assert; 4 | 5 | class UnparsedTests extends utest.Test { 6 | 7 | function testUnparsed () { 8 | var value:Int = unparsed(); 9 | Assert.equals(1, value); 10 | } 11 | 12 | function unparsed (): Int { 13 | @yield return 1; 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /tests/pack/pack3/SomeFunctions.hx: -------------------------------------------------------------------------------- 1 | package pack.pack3; 2 | 3 | /** 4 | * ... 5 | * @author Dimitri Pomier 6 | */ 7 | class SomeFunctions 8 | { 9 | public static function f0 (s:String) return 1; 10 | public static function f1 (s:String) return 2; 11 | 12 | @:noImportGlobal 13 | public static function n1 (s:String) return 3; 14 | private static function p1 (s:String) return 4; 15 | } -------------------------------------------------------------------------------- /tests/issues/Issue5.hx: -------------------------------------------------------------------------------- 1 | package issues; 2 | import utest.*; 3 | 4 | @:yield 5 | class Issue5 extends Test { 6 | 7 | function testIssue5 () { 8 | var it = iterator(); 9 | Assert.pass(); 10 | } 11 | 12 | function iterator ():Iterator { 13 | 14 | #if (haxe_ver >= 4.100) 15 | try Math.random() catch (e) {} 16 | #end 17 | 18 | @yield break; 19 | 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /tests/options/parsing/ParsingTests.hx: -------------------------------------------------------------------------------- 1 | package options.parsing; 2 | 3 | import utest.Assert; 4 | 5 | class ParsingTests extends utest.Test { 6 | 7 | function testParsing () { 8 | var it = simpleSplit(); 9 | Assert.equals(1, it.next()); 10 | Assert.equals(2, it.next()); 11 | Assert.equals(4, it.next()); 12 | Assert.isFalse(it.hasNext()); 13 | } 14 | 15 | function simpleSplit (): Iterator { 16 | @yield return 1; 17 | @yield return 2; 18 | @yield return 4; 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /tests/options/parsing/subparsing/SubParsingTests.hx: -------------------------------------------------------------------------------- 1 | package options.parsing.subparsing; 2 | 3 | import utest.Assert; 4 | 5 | class SubParsingTests extends utest.Test { 6 | 7 | function testParsing () { 8 | var it = simpleSplit(); 9 | Assert.equals(1, it.next()); 10 | Assert.equals(2, it.next()); 11 | Assert.equals(4, it.next()); 12 | Assert.isFalse(it.hasNext()); 13 | } 14 | 15 | function simpleSplit (): Iterator { 16 | @yield return 1; 17 | @yield return 2; 18 | @yield return 4; 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /tests/issues/Issue9.hx: -------------------------------------------------------------------------------- 1 | package issues; 2 | import utest.*; 3 | 4 | class Issue9 extends Test { 5 | 6 | public var array = [1,2,3]; 7 | 8 | function testIssue5 () { 9 | new A(this); 10 | } 11 | 12 | } 13 | 14 | @:yield 15 | class A { 16 | 17 | private var external:Issue9; 18 | 19 | public function new(v) { 20 | external = v; 21 | iterator().next(); 22 | Assert.pass(); 23 | } 24 | 25 | function iterator() : Iterator 26 | { 27 | for (i in external.array) {} 28 | 29 | @yield break; 30 | } 31 | } -------------------------------------------------------------------------------- /tests/options/parsing/recursive/RecursiveParsingTests.hx: -------------------------------------------------------------------------------- 1 | package options.parsing.recursive; 2 | 3 | import utest.Assert; 4 | 5 | class RecursiveParsingTests extends utest.Test { 6 | 7 | function testParsing () { 8 | var it = simpleSplit(); 9 | Assert.equals(1, it.next()); 10 | Assert.equals(2, it.next()); 11 | Assert.equals(4, it.next()); 12 | Assert.isFalse(it.hasNext()); 13 | } 14 | 15 | function simpleSplit (): Iterator { 16 | @yield return 1; 17 | @yield return 2; 18 | @yield return 4; 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /tests/options/parsing/recursive/parsed/RecursiveSubParsingTests.hx: -------------------------------------------------------------------------------- 1 | package options.parsing.recursive.parsed; 2 | 3 | import utest.Assert; 4 | 5 | class RecursiveSubParsingTests extends utest.Test { 6 | 7 | function testParsing () { 8 | var it = simpleSplit(); 9 | Assert.equals(1, it.next()); 10 | Assert.equals(2, it.next()); 11 | Assert.equals(4, it.next()); 12 | Assert.isFalse(it.hasNext()); 13 | } 14 | 15 | function simpleSplit (): Iterator { 16 | @yield return 1; 17 | @yield return 2; 18 | @yield return 4; 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /tests/misc/packs/Parent.hx: -------------------------------------------------------------------------------- 1 | package misc.packs; 2 | 3 | 4 | class Parent extends utest.Test { 5 | 6 | public function new () { 7 | super(); 8 | } 9 | 10 | private static var privateStatic:Bool = false; 11 | public static var publicStatic:Bool = false; 12 | private var privateMember:Bool = false; 13 | public var publicMember:Bool = false; 14 | 15 | public static function reset () 16 | { 17 | privateStatic = false; 18 | publicStatic = false; 19 | } 20 | 21 | public function resetMembers () 22 | { 23 | privateMember = false; 24 | publicMember = false; 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: xenial 3 | 4 | language: haxe 5 | 6 | os: 7 | - linux 8 | 9 | haxe: 10 | - "3.4.7" 11 | - "4.0.5" 12 | - "4.1.5" 13 | - "4.2.5" 14 | - "4.3.1" 15 | 16 | install: 17 | - haxelib install travix 18 | - haxelib run travix install 19 | 20 | script: 21 | - haxelib run travix interp 22 | - haxelib run travix neko 23 | - haxelib run travix python 24 | - haxelib run travix node 25 | - haxelib run travix java 26 | - haxelib run travix cpp 27 | - haxelib run travix cs 28 | - haxelib run travix php 29 | - haxelib run travix lua 30 | - haxelib run travix hl -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "hxml", 8 | "file": "build-dev.hxml", 9 | "problemMatcher": [ 10 | "$haxe-absolute", 11 | "$haxe", 12 | "$haxe-error", 13 | "$haxe-trace" 14 | ], 15 | "group": { 16 | "kind": "build", 17 | "isDefault": true 18 | } 19 | } 20 | ] 21 | } -------------------------------------------------------------------------------- /tests/pack/pack1/OperatorOverloading2.hx: -------------------------------------------------------------------------------- 1 | package pack.pack1; 2 | import yield.parser.Parser; 3 | 4 | #if (!cs && !java || haxe_ver >= 4.000) // error CS1004 repeated modifier 5 | @:build(yield.parser.Parser.run()) 6 | #end 7 | abstract OperatorOverloading2(String) { 8 | 9 | #if (!cs && !java || haxe_ver >= 4.000) 10 | 11 | public inline function new(s:String) { 12 | this = s; 13 | } 14 | 15 | @:op(A / B) 16 | public function devide(rhs:Int):Iterator { 17 | 18 | var len:Int = Math.floor(this.length / rhs); 19 | 20 | var i:Int = 0; 21 | 22 | for (y in 0...rhs) { 23 | @yield return new OperatorOverloading2(this.substr(i, len)); 24 | i += len; 25 | } 26 | 27 | @yield break; 28 | } 29 | 30 | #end 31 | } -------------------------------------------------------------------------------- /tests/misc/InheritanceTests.hx: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | import utest.Assert; 4 | import yield.YieldOption; 5 | 6 | @:build(yield.parser.Parser.run()) 7 | @:yield(YieldOption.Extend) 8 | class Parent extends utest.Test { 9 | 10 | private var parentMemeber:Int; 11 | 12 | public function new() { 13 | super(); 14 | parentMemeber = 14; 15 | } 16 | 17 | } 18 | 19 | class InheritanceTests extends Parent { 20 | 21 | public function new() 22 | { 23 | super(); 24 | } 25 | 26 | function testBasic () { 27 | var it = basic(); 28 | Assert.isTrue(it.hasNext()); 29 | Assert.equals("foo", it.next()); 30 | Assert.equals("bar", it.next()); 31 | Assert.isFalse(it.hasNext()); 32 | } 33 | 34 | function basic ():Iterator { 35 | @yield return "foo"; 36 | @yield return "bar"; 37 | } 38 | 39 | function testAccess () { 40 | var it = access(); 41 | Assert.isTrue(it.hasNext()); 42 | Assert.equals(14, it.next()); 43 | Assert.isFalse(it.hasNext()); 44 | } 45 | 46 | function access ():Iterator { 47 | @yield return parentMemeber; 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /tests/pack/pack1/MoreMiscFunctions.hx: -------------------------------------------------------------------------------- 1 | package pack.pack1; 2 | import misc.ImportTests; 3 | 4 | class MoreMiscFunctions 5 | { 6 | public static function a0 (s:String) return 0; 7 | public static function a1 (s:String) return 1; 8 | public static function a2 (s:String) return 2; 9 | private static function priv (s:String) return 100; 10 | private static function priv2 (s:String) return 200; 11 | 12 | public static function UppercaseFunction () return 42; 13 | } 14 | 15 | class MoreMiscFunctions2 16 | { 17 | public static function b0 (s:String) return 3; 18 | public static function b1 (s:String) return 4; 19 | } 20 | 21 | class MoreMiscFunctions3 22 | { 23 | public static function c0 (s:String) return 5; 24 | public static function c1 (s:String) return 6; 25 | public static function c2 (s:String) return 7; 26 | public static function c3 (s:String) return 8; 27 | } 28 | 29 | @:allow(misc.ImportTests) 30 | class MoreMiscFunctionsPriv 31 | { 32 | private static function d0 (s:String) return 9; 33 | } 34 | 35 | class MoreMiscFunctionsPriv2 36 | { 37 | private static function e0 (s:String) return 10; 38 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | The MIT License 3 | 4 | Copyright (C)2023 Dimitri Pomier 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a 7 | copy of this software and associated documentation files (the "Software"), 8 | to deal in the Software without restriction, including without limitation 9 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | and/or sell copies of the Software, and to permit persons to whom the 11 | Software is furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /yield/parser/idents/IdentChannel.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.idents; 26 | 27 | enum IdentChannel { 28 | Normal; 29 | IterationOp; 30 | } 31 | #end -------------------------------------------------------------------------------- /yield/parser/idents/IdentOption.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.idents; 26 | 27 | enum IdentOption { 28 | ReadOnly; 29 | KeepAsVar; 30 | IsVarLoop; 31 | } 32 | #end -------------------------------------------------------------------------------- /yield/parser/idents/Statement.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.idents; 26 | 27 | enum Statement { 28 | Definitions(data:IdentData, inlined:Bool); 29 | Accession(data:IdentData, defData:IdentData); 30 | Throw; 31 | } 32 | #end -------------------------------------------------------------------------------- /yield/parser/eactions/Action.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.eactions; 26 | import yield.parser.idents.IdentOption; 27 | import yield.parser.idents.IdentChannel; 28 | 29 | enum Action { 30 | DefineChannel(ic:IdentChannel); 31 | DefineOptions(options:Array, ?ic:IdentChannel); 32 | } 33 | #end -------------------------------------------------------------------------------- /yield/parser/idents/ArgIdentData.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.idents; 26 | import haxe.macro.Expr.FunctionArg; 27 | 28 | typedef ArgIdentData = { 29 | 30 | var originalArg:FunctionArg; 31 | 32 | /** 33 | * Local definition of the current extra-type constructor 34 | */ 35 | var definition:IdentData; 36 | } 37 | #end -------------------------------------------------------------------------------- /tests/misc/UsingTests.hx: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | import utest.Assert; 4 | 5 | using pack.pack1.MiscFunctions; 6 | using Lambda; 7 | 8 | @:yield 9 | class UsingTests extends utest.Test { 10 | 11 | function testSimpleUsing () { 12 | var it = simpleUsing("Patrick"); 13 | 14 | Assert.isTrue(it.hasNext()); 15 | Assert.equals("hello !", it.next()); 16 | Assert.isTrue(it.hasNext()); 17 | Assert.equals("hello Toto!", it.next()); 18 | Assert.isTrue(it.hasNext()); 19 | Assert.equals("hello Patrick!", it.next()); 20 | Assert.isFalse(it.hasNext()); 21 | } 22 | 23 | function simpleUsing (name:String): Iterator { 24 | 25 | #if (neko || js || php || python || lua) 26 | var msg = "".sayHello(); 27 | #else 28 | var msg:String = "".sayHello(); 29 | #end 30 | 31 | @yield return msg; 32 | @yield return "Toto".sayHello(); 33 | @yield return name.sayHello(); 34 | } 35 | 36 | function testLambda () { 37 | Assert.equals(3, lamba().count()); 38 | Assert.equals([0,2,4].toString(), lamba().array().toString()); 39 | } 40 | 41 | function lamba (): Iterable { 42 | @yield return 0; 43 | @yield return 2; 44 | @yield return 4; 45 | } 46 | 47 | function testLambaAnonymous () { 48 | 49 | var result:Iterable = lamba().flatMap(function (a:Int):Iterable { 50 | @yield return ""+a; 51 | @yield return ""+a+1; 52 | @yield return ""+a+2; 53 | }); 54 | 55 | Assert.equals(["0", "01", "02", "2", "21", "22", "4", "41", "42"].toString(), [for (item in result) item].toString()); 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /tests/misc/OnTypeYieldedTestMacro.hx: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | import haxe.macro.Expr; 4 | import haxe.macro.Context; 5 | import haxe.macro.ComplexTypeTools; 6 | import haxe.macro.ExprTools; 7 | import haxe.macro.TypeTools; 8 | import yield.parser.Parser; 9 | 10 | class OnTypeYieldedTestMacro { 11 | 12 | #if (macro && yield) 13 | private static function init () { 14 | Parser.onYield(onYield); 15 | } 16 | 17 | static function onYield (e:Expr, ?t:ComplexType):Null { 18 | 19 | var followed:Null = if (t != null) { 20 | ComplexTypeTools.toString(t); 21 | } else try { 22 | ComplexTypeTools.toString(TypeTools.toComplexType(Context.typeof(e))); 23 | } catch (_:Dynamic) { 24 | null; 25 | }; 26 | 27 | return switch followed { 28 | case "misc.OnTypeYieldedTests.SimpleModificationType": macro 3; 29 | case "misc.OnTypeYieldedTests.ReparsingType": macro function () { 30 | @yield return 1; 31 | @yield return 2; 32 | }; 33 | case "misc.OnTypeYieldedTests.ReEntranceType": macro new misc.OnTypeYieldedTests.SimpleModificationType(); 34 | case "misc.OnTypeYieldedTests.LoopType": 35 | switch e { 36 | case (macro @coroutine_test_loop($v) $expr): 37 | var loopCount = switch v.expr { case EConst(CInt(v)): Std.parseInt(v); case _: throw "wrong loop count"; }; 38 | if (loopCount == 4) 39 | macro "done"; 40 | else 41 | macro @coroutine_test_loop($v{loopCount + 1}) $expr; 42 | case _: 43 | macro @coroutine_test_loop(0) $e; 44 | } 45 | case _: null; 46 | } 47 | 48 | } 49 | #end 50 | 51 | } -------------------------------------------------------------------------------- /yield/parser/eparsers/BaseParser.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.eparsers; 26 | import yield.parser.env.WorkEnv; 27 | 28 | class BaseParser { 29 | 30 | private var m_ys:YieldSplitter; 31 | private var m_we:WorkEnv; 32 | 33 | public function new (yieldSplitter:YieldSplitter, env:WorkEnv) { 34 | m_ys = yieldSplitter; 35 | m_we = env; 36 | } 37 | 38 | } 39 | #end -------------------------------------------------------------------------------- /tests/misc/ErrorTests.hx: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | import utest.Assert; 4 | 5 | @:yield 6 | class ErrorTests extends utest.Test { 7 | 8 | #if ((haxe_ver >= 4.100) && (debug && !no_yield_follow_positions || yield_position_mapping)) 9 | function positionMapping (test:Int): Iterator { 10 | 11 | @yield return 0; 12 | 13 | var s:String = test == 0 ? null : "test-0"; 14 | var v:Int = s.length; // field 15 | 16 | @yield return v; 17 | 18 | var s:String = test == 1 ? null : "test-1"; 19 | var v:String = s.substr(0, 4); // call 20 | 21 | @yield return v; 22 | } 23 | 24 | function testFollowPosition () { 25 | var expectedTestCount = 2; 26 | 27 | function stringContains(best, otherwise, text, ?pos:haxe.PosInfos) { 28 | if(StringTools.contains(text, best)) 29 | Assert.stringContains(best, text, pos); 30 | else if(StringTools.contains(text, otherwise)) 31 | Assert.stringContains(otherwise, text, pos); 32 | else 33 | Assert.stringContains(best, text, pos); 34 | } 35 | 36 | var it:Iterator = cast positionMapping( 0 ); 37 | try while(it.hasNext()) it.next() catch(e) { 38 | stringContains("misc/ErrorTests.hx:14: ", "misc/ErrorTests.hx:14", Std.string(e.details())); 39 | expectedTestCount--; 40 | } 41 | 42 | var it:Iterator = cast positionMapping( 1 ); 43 | try while(it.hasNext()) it.next() catch(e) { 44 | stringContains("misc/ErrorTests.hx:19: ", "misc/ErrorTests.hx:19", Std.string(e.details())); 45 | expectedTestCount--; 46 | } 47 | 48 | #if (cpp || php) 49 | Assert.pass('FIXME: Temporarily disabled'); 50 | #else 51 | Assert.equals(0, expectedTestCount); 52 | #end 53 | } 54 | #end 55 | 56 | } -------------------------------------------------------------------------------- /yield/parser/tools/IdentCategory.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.tools; 26 | import haxe.macro.Type.ClassType; 27 | import haxe.macro.Expr.ComplexType; 28 | import yield.parser.idents.IdentData; 29 | 30 | enum IdentCategory { 31 | LocalVar(definition:IdentData); 32 | InstanceField(type:Null); 33 | InstanceStaticField(type:Null, c:ClassType); 34 | ImportedField(type:Null); 35 | Unknown; 36 | } 37 | #end -------------------------------------------------------------------------------- /yield/parser/eparsers/ETernaryParser.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.eparsers; 26 | import haxe.macro.Expr; 27 | 28 | class ETernaryParser extends BaseParser { 29 | 30 | public function run (e:Expr, subParsing:Bool, _econd:Expr, _eif:Expr, _eelse:Expr):Void { 31 | 32 | m_ys.parseOut(_econd, true, "Bool"); 33 | m_ys.parseOut(_eif, true); 34 | m_ys.parseOut(_eelse, true); 35 | 36 | if (!subParsing) m_ys.addIntoBlock(e); 37 | } 38 | 39 | } 40 | #end -------------------------------------------------------------------------------- /tests/Tests.hx: -------------------------------------------------------------------------------- 1 | package; 2 | import utest.Runner; 3 | import utest.ui.Report; 4 | import utest.ui.common.HeaderDisplayMode; 5 | 6 | class Tests 7 | { 8 | static function main () { 9 | 10 | var r:Runner = new Runner(); 11 | 12 | // parsers 13 | r.addCase(new eparsers.EIfTests()); 14 | r.addCase(new eparsers.EWhileTests()); 15 | r.addCase(new eparsers.EForTests()); 16 | r.addCase(new eparsers.ETryTests()); 17 | r.addCase(new eparsers.EFunctionTests()); 18 | r.addCase(new eparsers.ESwitchTests()); 19 | 20 | // general tests 21 | r.addCase(new misc.ScopeTests()); 22 | r.addCase(new misc.ImportTests()); 23 | r.addCase(new misc.UsingTests()); 24 | r.addCase(new misc.YieldTests()); 25 | r.addCase(new misc.IterationTests()); 26 | r.addCase(new misc.AbstractTests()); 27 | r.addCase(new misc.GenericTests()); 28 | r.addCase(new misc.AccessionTests()); 29 | r.addCase(new misc.InferenceTests()); 30 | r.addCase(new misc.InheritanceTests()); 31 | r.addCase(new misc.PrivateTests()); 32 | r.addCase(new misc.OnTypeYieldedTests()); 33 | r.addCase(new misc.ErrorTests()); 34 | 35 | // options tests 36 | r.addCase(new options.ExtendTests()); 37 | r.addCase(new options.KeywordTests()); 38 | r.addCase(new options.ExplicitTests()); 39 | r.addCase(new options.parsing.ParsingTests()); 40 | r.addCase(new options.parsing.subparsing.SubParsingTests()); 41 | r.addCase(new options.parsing.subparsing.unparsed.UnparsedTests()); 42 | r.addCase(new options.parsing.recursive.RecursiveParsingTests()); 43 | r.addCase(new options.parsing.recursive.parsed.RecursiveSubParsingTests()); 44 | 45 | // issues 46 | r.addCases(issues); 47 | 48 | Report.create(r); 49 | 50 | r.run(); 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /yield/parser/idents/IdentRef.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.idents; 26 | import haxe.macro.Expr; 27 | 28 | enum IdentRef { 29 | IEVars(eRef:Expr); 30 | IEFunction(eRef:Expr); 31 | IEField(eRef:Expr); 32 | IEConst(eRef:Expr); 33 | ICatch(cRef:Catch); 34 | IArg(aRef:FunctionArg, pos:Position); 35 | } 36 | 37 | typedef IdentRefTyped = { 38 | 39 | /** 40 | * Ident reference of a single item, even when it is `IEVars` 41 | */ 42 | ref:IdentRef, 43 | 44 | /** 45 | * Type of the ident reference 46 | */ 47 | type:ComplexType 48 | } 49 | #end -------------------------------------------------------------------------------- /tests/options/ExtendTests.hx: -------------------------------------------------------------------------------- 1 | package options; 2 | 3 | import utest.Assert; 4 | 5 | class ExtendTests extends utest.Test { 6 | 7 | function testShort () { 8 | Assert.isTrue(new ChildShort().getIt().next()); 9 | Assert.isTrue(new ChildShort().getChildIt().next()); 10 | } 11 | 12 | function testNormal () { 13 | Assert.isTrue(new ChildNormal().getIt().next()); 14 | Assert.isTrue(new ChildNormal().getChildIt().next()); 15 | } 16 | 17 | function testFull () { 18 | Assert.isTrue(new ChildFull().getIt().next()); 19 | Assert.isTrue(new ChildFull().getChildIt().next()); 20 | } 21 | 22 | } 23 | 24 | @:yield(Extend) 25 | private class Short { 26 | 27 | public function new () {} 28 | 29 | public function getIt ():Iterator { 30 | @yield return true; 31 | } 32 | } 33 | 34 | private class ChildShort extends Short { 35 | 36 | public function new () super(); 37 | 38 | public function getChildIt ():Iterator { 39 | @yield return true; 40 | } 41 | 42 | } 43 | 44 | @:yield(YieldOption.Extend) 45 | private class Normal { 46 | 47 | public function new () {} 48 | 49 | public function getIt ():Iterator { 50 | @yield return true; 51 | } 52 | 53 | } 54 | 55 | private class ChildNormal extends Normal { 56 | 57 | public function new () super(); 58 | 59 | public function getChildIt ():Iterator { 60 | @yield return true; 61 | } 62 | 63 | } 64 | 65 | @:yield(yield.YieldOption.Extend) 66 | private class Full { 67 | 68 | public function new () {} 69 | 70 | public function getIt ():Iterator { 71 | @yield return true; 72 | } 73 | 74 | } 75 | 76 | private class ChildFull extends Full { 77 | 78 | public function new () super(); 79 | 80 | public function getChildIt ():Iterator { 81 | @yield return true; 82 | } 83 | 84 | } -------------------------------------------------------------------------------- /tests/misc/PrivateTests.hx: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | import utest.Assert; 4 | 5 | class PrivateTests extends utest.Test { 6 | 7 | function testClass () { 8 | Assert.isTrue(new Class().getIt().next()); 9 | } 10 | 11 | function testNestedClass () { 12 | var it = new NestedClass().getIt(); 13 | Assert.equals("sub", it.next()); 14 | Assert.isTrue(it.next()); 15 | } 16 | 17 | #if (!cs && !java) // build failed 18 | function testAbstract () { 19 | Assert.isTrue(new Abstract().getIt().next()); 20 | } 21 | 22 | function testNestedAbstract () { 23 | var it = new NestedAbstract().getIt(); 24 | Assert.equals("sub", it.next()); 25 | Assert.isTrue(it.next()); 26 | } 27 | #end 28 | } 29 | 30 | @:yield 31 | private class Class { 32 | 33 | public function new () {} 34 | 35 | public function getIt ():Iterator { 36 | @yield return true; 37 | } 38 | } 39 | 40 | @:yield 41 | private class NestedClass { 42 | 43 | public function new () {} 44 | 45 | public function getIt ():Iterator { 46 | 47 | function getSubIt ():Iterator { 48 | @yield return "sub"; 49 | } 50 | 51 | @yield return getSubIt().next(); 52 | @yield return true; 53 | } 54 | } 55 | 56 | #if (!cs && !java) // build failed 57 | @:build(yield.parser.Parser.run()) 58 | abstract Abstract (Int) { 59 | 60 | public inline function new () this = 0; 61 | 62 | public inline function getIt ():Iterator { 63 | @yield return true; 64 | } 65 | } 66 | 67 | @:build(yield.parser.Parser.run()) 68 | private abstract NestedAbstract (Int) { 69 | 70 | public inline function new () this = 0; 71 | 72 | public inline function getIt ():Iterator { 73 | 74 | function getSubIt ():Iterator { 75 | @yield return "sub"; 76 | } 77 | 78 | @yield return getSubIt().next(); 79 | @yield return true; 80 | } 81 | } 82 | #end -------------------------------------------------------------------------------- /tests/misc/IterationTests.hx: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | import utest.Assert; 4 | 5 | @:yield 6 | class IterationTests extends utest.Test { 7 | 8 | function testReturnedType () { 9 | 10 | try { 11 | var b = getDynamic(); 12 | b.hasNext(); 13 | b.next(); 14 | b.iterator(); 15 | var c = getIterator(); 16 | c.hasNext(); 17 | c.next(); 18 | var d = getIterable(); 19 | d.iterator(); 20 | } catch (err:Dynamic) { 21 | Assert.isTrue(false); 22 | } 23 | 24 | Assert.isTrue(true); 25 | } 26 | 27 | function getDynamic ():Dynamic { 28 | @yield return 5; 29 | } 30 | 31 | function getIterator ():Iterator { 32 | @yield return 5; 33 | } 34 | 35 | function getIterable ():Iterable { 36 | @yield return 5; 37 | } 38 | 39 | function testIterableAPI () { 40 | 41 | var iterable:Iterable = iterableAPI(); 42 | var it = iterable.iterator(); 43 | 44 | Assert.equals(1, it.next()); 45 | Assert.equals(2, it.next()); 46 | Assert.equals(3, it.next()); 47 | Assert.equals(4, it.next()); 48 | Assert.equals(5, it.next()); 49 | Assert.isFalse(it.hasNext()); 50 | } 51 | 52 | function iterableAPI (): Iterable { 53 | var i = 0; 54 | while (i++ < 4) { 55 | @yield return i; 56 | } 57 | @yield return 5; 58 | } 59 | 60 | function testStructure () { 61 | 62 | var structure = { 63 | getIterator: function ():Iterator { 64 | @yield return 2; 65 | @yield return 4; 66 | @yield return 8; 67 | } 68 | }; 69 | 70 | Assert.isTrue(structure.getIterator().hasNext()); 71 | 72 | #if (neko || js || php || python || lua) 73 | var it = structure.getIterator(); 74 | #else 75 | var it:Iterator = structure.getIterator(); 76 | #end 77 | 78 | var expected:Int = 2; 79 | for (i in structure.getIterator()) { 80 | Assert.equals(expected, i); 81 | expected *= 2; 82 | } 83 | } 84 | 85 | } -------------------------------------------------------------------------------- /yield/macrotime/Tools.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | package yield.macrotime; 25 | import haxe.macro.Context; 26 | import haxe.macro.Expr; 27 | import haxe.macro.Type; 28 | 29 | class Tools { 30 | 31 | public static macro function getIterator (e:Expr): Expr { 32 | 33 | var t:Type = Context.typeExpr(e).t; 34 | 35 | if (Context.unify(t, Context.resolveType(macro:StdTypes.Iterable, Context.currentPos()))) { 36 | e.expr = ECall({ expr: EField({ expr: e.expr, pos: e.pos }, "iterator"), pos: e.pos }, []); 37 | return e; 38 | } 39 | 40 | if (Context.unify(t, Context.resolveType(macro:StdTypes.Iterator, Context.currentPos()))) { 41 | return e; 42 | } 43 | 44 | return e; 45 | } 46 | } -------------------------------------------------------------------------------- /yield/YieldOption.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | package yield; 25 | 26 | #if !macro 27 | extern 28 | #end 29 | enum YieldOption { 30 | 31 | /** 32 | * Use a custom keyword instead of "yield". 33 | */ 34 | Keyword(s:String); 35 | 36 | /** 37 | * If the option is enabled, the return type of iterative functions 38 | * needs to be explicitly specified. This is disabled by default. 39 | */ 40 | Explicit(?v:Bool); 41 | 42 | /** 43 | * If the option is enabled, all extending classes will be able 44 | * to use yield statements. If this option affects an interface, 45 | * all implementing classes and all extending interfaces will 46 | * be able to use yield statements. This is disabled by default. 47 | */ 48 | Extend(?v:Bool); 49 | } -------------------------------------------------------------------------------- /yield/parser/idents/IdentData.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.idents; 26 | import haxe.macro.Expr.ComplexType; 27 | import haxe.macro.Expr.Position; 28 | import yield.parser.env.WorkEnv; 29 | import yield.parser.env.WorkEnv.Scope; 30 | 31 | typedef IdentData = { 32 | 33 | /** 34 | * If identType is `Accession` it is ensure that names.length is equal to 1 35 | */ 36 | var names:Array; 37 | 38 | /** 39 | * If identType is `Accession` it is ensure that initialized.length is equal to 1 40 | */ 41 | var initialized:Array; 42 | 43 | /** 44 | * If identType is `Accession` it is ensure that types.length is equal to 1 45 | */ 46 | var types:Array>; 47 | 48 | var ident:IdentRef; 49 | 50 | var channel:IdentChannel; 51 | 52 | var options:Array; 53 | 54 | var scope:Scope; 55 | 56 | var env:WorkEnv; 57 | 58 | var pos:Position; 59 | } 60 | #end -------------------------------------------------------------------------------- /tests/misc/OnTypeYieldedTests.hx: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | import utest.Assert; 4 | import pack.pack2.MiscYielded; 5 | 6 | class SimpleModificationType { 7 | public function new () {} 8 | } 9 | 10 | class ReparsingType { 11 | public function new () {} 12 | } 13 | 14 | class ReEntranceType { 15 | public function new () {} 16 | } 17 | 18 | class LoopType { 19 | public function new () {} 20 | } 21 | 22 | @:yield 23 | class OnTypeYieldedTests extends utest.Test { 24 | 25 | function testSimpleModification () { 26 | var it:Iterator = simpleModification(); 27 | Assert.equals(3, it.next()); 28 | } 29 | function simpleModification ():Iterator { 30 | @yield return null; 31 | } 32 | 33 | function testImplicitModification () { 34 | var it:Iterator = implicitModification(); 35 | Assert.equals(3, it.next()); 36 | } 37 | function implicitModification () { 38 | @yield return new SimpleModificationType(); 39 | } 40 | 41 | function testReentrance () { 42 | var it:Iterator = reentrance(); 43 | Assert.isTrue(it.hasNext()); 44 | Assert.equals(3, it.next()); 45 | Assert.isFalse(it.hasNext()); 46 | } 47 | function reentrance ():Iterator { 48 | @yield return null; 49 | } 50 | 51 | function testReparsing () { 52 | var it:Iterator = reparsing(); 53 | 54 | var generatedFun = it.next(); 55 | var generatedIt = generatedFun(); 56 | 57 | Assert.equals(1, generatedIt.next()); 58 | Assert.equals(2, generatedIt.next()); 59 | } 60 | function reparsing () { 61 | @yield return new ReparsingType(); 62 | } 63 | 64 | // function testLooping () { 65 | // var it:Iterator = loop(); 66 | // Assert.isTrue(it.hasNext()); 67 | // Assert.equals("done", it.next()); 68 | // Assert.isFalse(it.hasNext()); 69 | 70 | // var it:Iterator = loopImplicit(); 71 | // Assert.isTrue(it.hasNext()); 72 | // Assert.equals("done", it.next()); 73 | // Assert.isFalse(it.hasNext()); 74 | // } 75 | // function loop ():Iterator { 76 | // @yield return new LoopType(); 77 | // } 78 | // function loopImplicit () { 79 | // @yield return new LoopType(); 80 | // } 81 | 82 | } -------------------------------------------------------------------------------- /yield/generators/BuildingData.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.generators; 26 | import haxe.macro.Expr; 27 | import haxe.macro.Expr.FunctionArg; 28 | import haxe.macro.Expr.TypeDefinition; 29 | import haxe.macro.Expr.TypePath; 30 | import yield.parser.YieldSplitter.IteratorBlockData; 31 | 32 | class BuildingData { 33 | 34 | public var typeDefinition (default, null):TypeDefinition; 35 | public var typePath (default, null):TypePath; 36 | public var constructorArgs (default, null):Array; 37 | public var constructorBlock (default, null):Array; 38 | public var givenArguments (default, null):Array; 39 | public var lastSequence (default, null):Int; 40 | public var instanceFunctionBody:Expr; 41 | 42 | public function new (td:TypeDefinition, tpack:TypePath, ibd:IteratorBlockData) { 43 | 44 | typeDefinition = td; 45 | typePath = tpack; 46 | constructorArgs = []; 47 | constructorBlock = []; 48 | givenArguments = []; 49 | lastSequence = ibd.length - 1; 50 | instanceFunctionBody = null; 51 | 52 | typeDefinition.pack = typePath.pack; 53 | } 54 | 55 | } 56 | #end -------------------------------------------------------------------------------- /yield/parser/eparsers/EBlockParser.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.eparsers; 26 | import haxe.macro.Expr; 27 | import yield.parser.env.WorkEnv.Scope; 28 | import yield.parser.idents.IdentRef.IdentRefTyped; 29 | import yield.parser.tools.ExpressionTools; 30 | 31 | class EBlockParser extends BaseParser { 32 | 33 | /** 34 | * Wrap `e` into an EBlock expression if it isn't already one 35 | */ 36 | public function runFromExpr (e:Expr, subParsing:Bool, conditionalScope:Bool = false, alternativeScope:Scope = null, predefineNativeVars:Array = null): Scope { 37 | 38 | var lexprs:Array = ExpressionTools.checkIsInEBlock(e); 39 | return run(e, subParsing, lexprs, conditionalScope, alternativeScope, predefineNativeVars); 40 | } 41 | 42 | public function run (e:Expr, subParsing:Bool, _exprs:Array, conditionalScope:Bool = false, alternativeScope:Scope = null, predefineNativeVars:Array = null): Scope { 43 | 44 | var posOriginalIterator = m_ys.cursor; 45 | 46 | m_ys.moveCursor(); 47 | 48 | var posFirstSubexpression = m_ys.cursor; 49 | 50 | var scope:Scope = m_we.openScope(conditionalScope, alternativeScope); 51 | 52 | // Predefine native variables 53 | if (predefineNativeVars != null) 54 | m_we.addLocalNativeDefinitions(predefineNativeVars); 55 | 56 | for (lexpr in _exprs) 57 | m_ys.parse(lexpr, subParsing); 58 | 59 | m_we.closeScope(); 60 | 61 | if (m_ys.cursor == posFirstSubexpression) { 62 | 63 | m_ys.spliceIteratorBlocks(m_ys.cursor, 1); 64 | 65 | if (!subParsing) m_ys.addIntoBlock(e); 66 | 67 | } else { 68 | 69 | e.expr = EBlock(m_ys.iteratorBlocks[posFirstSubexpression]); 70 | if (!subParsing) m_ys.addIntoBlock(e, posOriginalIterator); 71 | 72 | m_ys.spliceIteratorBlocks(posFirstSubexpression, 1); 73 | } 74 | 75 | return scope; 76 | } 77 | 78 | } 79 | #end -------------------------------------------------------------------------------- /yield/parser/PositionManager.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser; 26 | import haxe.macro.Expr; 27 | import yield.parser.YieldSplitter.IteratorBlockData; 28 | 29 | typedef LinkedPosition = { 30 | var pos:Int; 31 | var e:Expr; 32 | } 33 | 34 | class PositionManager { 35 | 36 | private var m_ibd:IteratorBlockData; 37 | private var m_posPointers:Array>; 38 | 39 | public function new (iteratorBlocks:IteratorBlockData) { 40 | 41 | m_ibd = iteratorBlocks; 42 | m_posPointers = new Array>(); 43 | } 44 | 45 | public function addPosPointer (lp:LinkedPosition): Void { 46 | 47 | if (m_posPointers.length <= lp.pos || m_posPointers[lp.pos] == null) { 48 | m_posPointers[lp.pos] = new Array(); 49 | } 50 | 51 | m_posPointers[lp.pos].push(lp); 52 | } 53 | 54 | public function moveLinkedPosition (lp:LinkedPosition, newPos:Int): Void { 55 | 56 | m_posPointers[lp.pos].remove(lp); 57 | 58 | if (newPos >= 0) { 59 | lp.pos = newPos; 60 | addPosPointer(lp); 61 | } 62 | } 63 | 64 | public function adjustIteratorPos (offset:Int, since:Int): Void { 65 | 66 | var ibdLength:Int = m_ibd.length; 67 | var posCount:Int = m_posPointers.length; 68 | 69 | for (i in since...ibdLength + 1) { 70 | 71 | if (i < posCount && m_posPointers[i] != null) { 72 | 73 | for (posPtr in m_posPointers[i]) { 74 | 75 | posPtr.pos += offset; 76 | } 77 | } 78 | } 79 | 80 | for (i in since...ibdLength + 1) { 81 | 82 | if (i < posCount && m_posPointers[i] != null) { 83 | 84 | var j:Int = m_posPointers[i].length; 85 | 86 | while (--j != -1) { 87 | 88 | if (m_posPointers[i][j].pos >= 0) { 89 | addPosPointer(m_posPointers[i][j]); 90 | } 91 | 92 | m_posPointers[i].splice(j, 1); 93 | } 94 | } 95 | } 96 | } 97 | 98 | } 99 | #end -------------------------------------------------------------------------------- /yield/parser/env/ClassData.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2018 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.env; 26 | import haxe.macro.Context; 27 | import haxe.macro.Expr; 28 | import haxe.macro.Type; 29 | import yield.parser.tools.ImportTools; 30 | 31 | class ClassData { 32 | 33 | public var module (default, null):Array; 34 | public var localClass (default, null):ClassType; 35 | public var localType (default, null):Type; 36 | public var localComplexType (default, null):ComplexType; 37 | public var isPrivate (default, null):Bool; 38 | public var isAbstract (default, null):Bool; 39 | public var abstractType (default, null):Null; 40 | public var classFields (default, null):Array; 41 | public var imports (default, null):Array; 42 | public var importedFields (default, null):Array; 43 | public var importedEnums (default, null):Array; 44 | public var usings (default, null):Array>; 45 | 46 | private inline function new () { 47 | 48 | } 49 | 50 | public static function of (ct:ClassType, t:Type):ClassData { 51 | 52 | var data = new ClassData(); 53 | 54 | data.module = Context.getModule(Context.getLocalModule()); 55 | data.localClass = ct; 56 | data.localType = t; 57 | data.localComplexType = Context.toComplexType(t); 58 | 59 | switch (ct.kind) { 60 | case KAbstractImpl(_.get() => a): 61 | data.isAbstract = true; 62 | data.abstractType = a; 63 | default: 64 | data.isAbstract = false; 65 | data.abstractType = null; 66 | } 67 | 68 | data.isPrivate = ct.isPrivate; 69 | 70 | data.classFields = Context.getBuildFields(); 71 | data.imports = Context.getLocalImports(); 72 | data.importedFields = ImportTools.getFieldShorthands(data.imports); 73 | data.importedEnums = ImportTools.getEnumConstructors(data.imports, data.module); 74 | data.usings = Context.getLocalUsing(); 75 | 76 | return data; 77 | } 78 | 79 | } 80 | 81 | #end -------------------------------------------------------------------------------- /tests/options/ExplicitTests.hx: -------------------------------------------------------------------------------- 1 | package options; 2 | 3 | import utest.Assert; 4 | 5 | class ExplicitTests extends utest.Test { 6 | 7 | function testShort () { 8 | var it = new Short().getIt(); 9 | Assert.isTrue(it.next()); 10 | } 11 | 12 | function testNormal () { 13 | var it = new Normal().getIt(); 14 | Assert.isTrue(it.next()); 15 | } 16 | 17 | function testFull () { 18 | var it = new Full().getIt(); 19 | Assert.isTrue(it.next()); 20 | } 21 | 22 | function testArgTrue () { 23 | Assert.isTrue(new ArgTrue().getExplicitIt().next()); 24 | } 25 | 26 | function testArgFalse () { 27 | Assert.isTrue(new ArgFalse().getExplicitIt().next()); 28 | Assert.isTrue(new ArgFalse().getNoExplicitIt().next()); 29 | } 30 | 31 | function testParent () { 32 | Assert.isTrue(new Parent().getParentIt().next()); 33 | Assert.isTrue(new Child().getChildIt().next()); 34 | } 35 | 36 | function testIgnoredParent () { 37 | Assert.isTrue(new Parent().getParentIt().next()); 38 | Assert.isTrue(new Child().getChildIt().next()); 39 | } 40 | 41 | } 42 | 43 | @:yield(Explicit) 44 | private class Short { 45 | 46 | public function new () {} 47 | 48 | public function getIt ():Iterator { 49 | @yield return true; 50 | } 51 | } 52 | 53 | @:yield(YieldOption.Explicit) 54 | private class Normal { 55 | 56 | public function new () {} 57 | 58 | public function getIt ():Iterator { 59 | @yield return true; 60 | } 61 | 62 | } 63 | 64 | @:yield(yield.YieldOption.Explicit) 65 | private class Full { 66 | 67 | public function new () {} 68 | 69 | public function getIt ():Iterator { 70 | @yield return true; 71 | } 72 | 73 | } 74 | 75 | @:yield(Explicit(true)) 76 | private class ArgTrue { 77 | 78 | public function new () {} 79 | 80 | public function getExplicitIt ():Iterator { 81 | @yield return true; 82 | } 83 | } 84 | 85 | @:yield(Explicit(false)) 86 | private class ArgFalse { 87 | 88 | public function new () {} 89 | 90 | public function getExplicitIt ():Iterator { 91 | @yield return true; 92 | } 93 | 94 | public function getNoExplicitIt () { 95 | @yield return true; 96 | } 97 | } 98 | 99 | // Tests with Expens 100 | 101 | @:yield(yield.YieldOption.Explicit, Extend) 102 | private class Parent { 103 | 104 | public function new () {}; 105 | 106 | public function getParentIt ():Iterator { 107 | @yield return true; 108 | } 109 | } 110 | 111 | private class Child extends Parent { 112 | 113 | public function new () super(); 114 | 115 | public function getChildIt ():Iterator { 116 | @yield return true; 117 | } 118 | } 119 | 120 | @:yield(yield.YieldOption.Explicit, Extend(false)) 121 | private class IgnoredParent { 122 | 123 | public function new () {}; 124 | 125 | public function getParentIt ():Iterator { 126 | @yield return true; 127 | } 128 | } 129 | 130 | @:yield 131 | private class IgnoredChild extends IgnoredParent { 132 | 133 | public function new () super(); 134 | 135 | public function getChildIt () { 136 | @yield return true; 137 | } 138 | } -------------------------------------------------------------------------------- /yield/parser/eparsers/EVarsParser.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.eparsers; 26 | import haxe.macro.Expr; 27 | import yield.parser.env.WorkEnv; 28 | import yield.parser.checks.TypeInferencer; 29 | import yield.parser.idents.IdentChannel; 30 | import yield.parser.idents.IdentOption; 31 | import yield.parser.idents.IdentRef; 32 | 33 | class EVarsParser extends BaseParser { 34 | 35 | public function run (e:Expr, subParsing:Bool, _vars:Array, ?ic:IdentChannel, ?options:Array): Void { 36 | 37 | if (ic == null) { 38 | ic = IdentChannel.Normal; 39 | } 40 | 41 | if (options == null) { 42 | options = []; 43 | } 44 | 45 | var names:Array = []; 46 | var initialized:Array = []; 47 | var types:Array = []; 48 | 49 | var isFinal:Bool = false; 50 | 51 | for (j in 0..._vars.length) { 52 | 53 | var typeInferred:Bool = false; 54 | 55 | #if (haxe_ver >= 4.000) 56 | isFinal = _vars[j].isFinal; 57 | #end 58 | 59 | if (!WorkEnv.isDynamicTarget()) { 60 | TypeInferencer.checkLocalVariableType(_vars[j], m_we, ic, e.pos); // Force typing because the type is required for member variables on static targets 61 | types.push(_vars[j].type != null ? _vars[j].type : macro:StdTypes.Dynamic); 62 | } else { 63 | 64 | if (_vars[j].type == null) { 65 | var inferred:Null = TypeInferencer.tryInferExpr(_vars[j].expr, m_we, ic); 66 | if (inferred != null) { 67 | _vars[j].type = inferred; 68 | } 69 | } 70 | 71 | types.push(_vars[j].type); 72 | } 73 | 74 | m_ys.parseVar(_vars[j], true, ic); 75 | names.push(_vars[j].name); 76 | initialized.push(_vars[j].expr != null); 77 | } 78 | 79 | if (ic == IdentChannel.IterationOp || isFinal) { 80 | options.push(IdentOption.ReadOnly); 81 | } 82 | 83 | m_we.addLocalDefinition(names, initialized, types, false, IdentRef.IEVars(e), ic, options, e.pos); 84 | 85 | if (!subParsing) m_ys.addIntoBlock( e ); 86 | } 87 | 88 | } 89 | #end -------------------------------------------------------------------------------- /tests/options/KeywordTests.hx: -------------------------------------------------------------------------------- 1 | package options; 2 | 3 | import utest.Assert; 4 | 5 | class KeywordTests extends utest.Test { 6 | 7 | function testShortOption () { 8 | 9 | var it = new Short().getIt(); 10 | Assert.isTrue(it.next()); 11 | Assert.isTrue(new Short().getBool()); 12 | 13 | } 14 | 15 | function testNormalOption () { 16 | 17 | var it = new Normal().getIt(); 18 | Assert.isTrue(it.next()); 19 | Assert.isTrue(new Normal().getBool()); 20 | 21 | } 22 | 23 | function testFullOption () { 24 | 25 | var it = new Full().getIt(); 26 | Assert.isTrue(it.next()); 27 | Assert.isTrue(new Full().getBool()); 28 | 29 | } 30 | 31 | function testParentOption () { 32 | 33 | var it = new Parent().getParentIt(); 34 | Assert.isTrue(it.next()); 35 | Assert.isTrue(new Parent().getParentBool()); 36 | 37 | var it = new Child().getChildIt(); 38 | Assert.isTrue(it.next()); 39 | Assert.isTrue(new Child().getChildBool()); 40 | 41 | } 42 | 43 | function testIgnoredParentOption () { 44 | 45 | var it = new IgnoredParent().getParentIt(); 46 | Assert.isTrue(it.next()); 47 | Assert.isTrue(new IgnoredParent().getParentBool()); 48 | 49 | var it = new IgnoredChild().getChildIt(); 50 | Assert.isTrue(it.next()); 51 | Assert.isTrue(new IgnoredChild().getChildBool()); 52 | 53 | } 54 | 55 | } 56 | 57 | 58 | 59 | @:yield(Keyword("short")) 60 | private class Short { 61 | 62 | public function new () {} 63 | 64 | public function getIt ():Iterator { 65 | @short return true; 66 | } 67 | 68 | public function getBool ():Bool { 69 | @yield return true; 70 | } 71 | } 72 | 73 | @:yield(YieldOption.Keyword("normal")) 74 | private class Normal { 75 | 76 | public function new () {} 77 | 78 | public function getIt ():Iterator { 79 | @normal return true; 80 | } 81 | 82 | public function getBool ():Bool { 83 | @yield return true; 84 | } 85 | 86 | } 87 | 88 | @:yield(yield.YieldOption.Keyword("full")) 89 | private class Full { 90 | 91 | public function new () {} 92 | 93 | public function getIt ():Iterator { 94 | @full return true; 95 | } 96 | 97 | public function getBool ():Bool { 98 | @yield return true; 99 | } 100 | 101 | } 102 | 103 | // Tests with Expens 104 | 105 | @:yield(yield.YieldOption.Keyword("parent"), Extend) 106 | private class Parent { 107 | 108 | public function new () {}; 109 | 110 | public function getParentIt ():Iterator { 111 | @parent return true; 112 | } 113 | 114 | public function getParentBool ():Bool { 115 | @yield return true; 116 | } 117 | } 118 | 119 | private class Child extends Parent { 120 | 121 | public function new () super(); 122 | 123 | public function getChildIt ():Iterator { 124 | @parent return true; 125 | } 126 | 127 | public function getChildBool ():Bool { 128 | @yield return true; 129 | } 130 | } 131 | 132 | @:yield(yield.YieldOption.Keyword("parent"), Extend(false)) 133 | private class IgnoredParent { 134 | 135 | public function new () {}; 136 | 137 | public function getParentIt ():Iterator { 138 | @parent return true; 139 | } 140 | 141 | public function getParentBool ():Bool { 142 | @yield return true; 143 | } 144 | } 145 | 146 | @:yield 147 | private class IgnoredChild extends IgnoredParent { 148 | 149 | public function new () super(); 150 | 151 | public function getChildIt ():Iterator { 152 | @yield return true; 153 | } 154 | 155 | public function getChildBool ():Bool { 156 | @parent return true; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /yield/parser/eparsers/EMetaParser.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.eparsers; 26 | import haxe.macro.Context; 27 | import haxe.macro.Expr; 28 | import yield.parser.eactions.Action; 29 | import yield.parser.eactions.ActionParser; 30 | import yield.parser.idents.IdentChannel; 31 | import yield.parser.tools.IdentCategory; 32 | import haxe.macro.ExprTools; 33 | import haxe.macro.Type; 34 | import yield.parser.checks.TypeInferencer; 35 | using haxe.macro.ComplexTypeTools; 36 | using haxe.macro.TypeTools; 37 | 38 | class EMetaParser extends BaseParser { 39 | 40 | public function run (e:Expr, subParsing:Bool, _s:MetadataEntry, _e:Expr): Void { 41 | 42 | if (_s.name == m_we.yieldKeyword) { 43 | 44 | if (_s.params == null || _s.params.length == 0) { 45 | parseYieldMeta(e, subParsing, _s, _e); 46 | return; 47 | } else { 48 | 49 | var tooManyArgs:Bool = false; 50 | 51 | for (lparamExpr in _s.params) { 52 | 53 | var actions:Array = ActionParser.getAction(lparamExpr); 54 | 55 | if (actions != null) { 56 | 57 | m_ys.actionParser.executeAction(actions, _e); 58 | m_ys.addIntoBlock(e); 59 | return; 60 | 61 | } else { 62 | tooManyArgs = true; 63 | } 64 | 65 | } 66 | 67 | if (tooManyArgs) Context.fatalError("Too many arguments", e.pos); 68 | } 69 | 70 | } else { 71 | 72 | var inlined:Bool = _s.name == ":inline"; 73 | 74 | m_ys.parseMetadataEntry(_s, true); 75 | m_ys.parse(_e, true, IdentChannel.Normal, inlined); 76 | if (!subParsing) m_ys.addIntoBlock(e); 77 | } 78 | } 79 | 80 | private function parseYieldMeta (e:Expr, subParsing:Bool, _s:MetadataEntry, _e:Expr): Void { 81 | 82 | switch (_e.expr) { 83 | 84 | case EReturn(__e): 85 | 86 | #if (!yield_debug_no_display && (display || yield_debug_display)) 87 | m_ys.addDisplayDummy(e); // TODO parse __e to know the type when inferred typing 88 | #else 89 | if (__e != null) { 90 | m_we.registerYield(__e); 91 | m_ys.parse(__e, true); 92 | } 93 | #end 94 | 95 | case EBreak: 96 | m_ys.registerBreakAction(e); 97 | default: 98 | Context.fatalError( "Unexpected " + m_we.yieldKeyword, e.pos ); 99 | } 100 | 101 | m_ys.addIntoBlock(e); 102 | m_ys.moveCursor(); 103 | } 104 | 105 | } 106 | #end -------------------------------------------------------------------------------- /yield/parser/eactions/ActionParser.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.eactions; 26 | import yield.parser.idents.IdentData; 27 | import yield.parser.env.WorkEnv; 28 | import haxe.Serializer; 29 | import haxe.Unserializer; 30 | import haxe.macro.Context; 31 | import haxe.macro.Expr; 32 | import yield.parser.eparsers.BaseParser; 33 | 34 | class ActionParser extends BaseParser { 35 | 36 | public static function addActionToExpr (actions:Array, e:Expr, env:WorkEnv): Void { 37 | 38 | var ls:Serializer = new Serializer(); 39 | ls.serialize(actions); 40 | 41 | var lexpr = e.expr; 42 | 43 | e.expr = EMeta({ 44 | name: env.yieldKeyword, 45 | params: [{ expr:EConst(CString( ls.toString() )), pos:e.pos }], 46 | pos:e.pos 47 | },{ 48 | expr: lexpr, 49 | pos: e.pos 50 | }); 51 | } 52 | 53 | public static function getAction (e:Expr): Null> { 54 | 55 | switch (e.expr) { 56 | case EConst(__c): 57 | 58 | switch (__c) { 59 | case CString(___s): 60 | 61 | var u = new Unserializer(___s); 62 | var o:Dynamic = null; 63 | 64 | try { 65 | o = u.unserialize(); 66 | } catch (err:Dynamic) { 67 | return null; 68 | } 69 | 70 | if (Std.is(o, Array)) { 71 | return o; 72 | } 73 | 74 | 75 | default: 76 | } 77 | default: 78 | } 79 | return null; 80 | } 81 | 82 | public function executeAction (actions:Array, e:Expr): Void { 83 | 84 | for (action in actions) { 85 | 86 | switch (action) { 87 | case DefineChannel(ic): 88 | 89 | switch (e.expr) { 90 | case EConst(_c): 91 | m_ys.econstParser.run(e, true, _c, ic); 92 | default: 93 | Context.fatalError("Action " + action.getName() + " only works for econst and doesn't accept " + e.expr.getName(), e.pos); 94 | } 95 | 96 | case DefineOptions(options, ic): 97 | 98 | switch (e.expr) { 99 | case EVars(_vars): 100 | 101 | m_ys.evarsParser.run(e, true, _vars, ic, options); 102 | 103 | case EConst(CIdent(_s)): 104 | var identData:Null = m_we.getLocalDefinitionOf(_s, ic); 105 | 106 | if (identData == null) { 107 | Context.fatalError("Action " + action.getName() + " cannot be applied on an unkwown local identifier", e.pos); 108 | } 109 | identData.options = options; 110 | 111 | default: 112 | Context.fatalError("Action " + action.getName() + " only works for econst(cident) and doesn't accept " + e.expr.getName(), e.pos); 113 | } 114 | } 115 | } 116 | } 117 | 118 | } 119 | #end -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions 2 | name: Build 3 | 4 | on: 5 | push: 6 | paths-ignore: 7 | - '**/*.md' 8 | pull_request: 9 | workflow_dispatch: 10 | # https://github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-workflow_dispatch/ 11 | 12 | jobs: 13 | build: 14 | runs-on: ${{ matrix.os }} 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | os: 19 | - ubuntu-latest 20 | haxe: 21 | - 3.4.7 22 | - 4.0.5 23 | - 4.1.5 24 | - 4.2.5 25 | - 4.3.3 26 | 27 | steps: 28 | - name: Show environment variables 29 | shell: bash 30 | run: env | sort 31 | 32 | - name: Git Checkout 33 | uses: actions/checkout@v2 #https://github.com/actions/checkout 34 | 35 | - name: "Cache Haxelib Repository" 36 | uses: actions/cache@v2 37 | with: 38 | path: $RUNNER_TOOL_CACHE/haxe/${{ matrix.haxe }}/x64/lib 39 | key: ${{ runner.os }}-haxelib-${{ hashFiles('**/haxelib.json') }} 40 | restore-keys: | 41 | ${{ runner.os }}-haxelib- 42 | 43 | - name: Upgrade brew 44 | if: runner.os == 'macOS' 45 | env: 46 | # https://docs.brew.sh/Manpage#environment 47 | HOMEBREW_NO_ANALYTICS: 1 48 | HOMEBREW_NO_INSTALL_CLEANUP: 1 49 | run: | 50 | echo "::group::brew update" && brew update && echo "::endgroup::" 51 | echo "::group::brew config" && brew config && echo "::endgroup::" 52 | 53 | # workaround to prevent "/usr/local/... is not inside a keg" during "brew install mono" 54 | rm /usr/local/bin/2to3 55 | rm /usr/local/share/man/man1/* 56 | rm /usr/local/share/man/man5/* 57 | 58 | - name: Set up Python 3 59 | uses: actions/setup-python@v5 # https://github.com/actions/setup-python 60 | with: 61 | python-version: '3.10' 62 | 63 | - name: Configure Python 3 64 | if: runner.os == 'Windows' 65 | shell: cmd 66 | # workaround for https://github.com/actions/setup-python/issues/123 67 | run: mklink "%pythonLocation%\python3.exe" "%pythonLocation%\python.exe" 68 | 69 | - name: Install Haxe ${{ matrix.haxe }} 70 | uses: krdlab/setup-haxe@v1 # https://github.com/krdlab/setup-haxe 71 | with: 72 | haxe-version: ${{ matrix.haxe }} 73 | 74 | - name: Install haxe libs 75 | shell: bash 76 | run: | 77 | haxelib install travix 78 | # to always use the latest version of travix comment out the previous line and uncomment the next 79 | # haxelib git travix https://github.com/back2dos/travix && pushd . && cd $(haxelib config)travix/git && haxe build-neko.hxml -lib hx3compat && popd 80 | haxelib run travix install 81 | 82 | 83 | ################################################## 84 | # Tests 85 | ################################################## 86 | - name: Test [interp ] 87 | run: haxelib run travix interp 88 | - name: Test [neko ] 89 | run: haxelib run travix neko 90 | - name: Test [python ] 91 | run: haxelib run travix python 92 | - name: Test [node ] 93 | run: haxelib run travix node 94 | # - name: Test [js ] 95 | # run: haxelib run travix js 96 | # - name: Test [flash ] 97 | # run: haxelib run travix flash 98 | - name: Test [java ] 99 | run: haxelib run travix java 100 | if: ${{ matrix.haxe != '3.4.7' }} 101 | - name: Test [cpp ] 102 | run: haxelib run travix cpp 103 | - name: Test [cs ] 104 | run: haxelib run travix cs 105 | if: ${{ matrix.haxe != '3.4.7' }} 106 | - name: Test [php ] 107 | run: haxelib run travix php 108 | - name: Test [php7 ] 109 | run: haxelib run travix php7 110 | # - name: Test [lua ] 111 | # run: haxelib run travix lua 112 | -------------------------------------------------------------------------------- /tests/misc/AbstractTests.hx: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | import utest.Assert; 4 | import pack.pack1.OperatorOverloading2; 5 | import yield.parser.Parser; 6 | 7 | class AbstractTests extends utest.Test { 8 | 9 | function testAbstractReturnType () { 10 | var it = TestAbstractReturnType.test(); 11 | Assert.equals(3, it.next()); 12 | } 13 | 14 | #if (!cs && !java || haxe_ver >= 4.000) 15 | function testOperatorOverloading () { 16 | var a = new OperatorOverloading("foo"); 17 | var it = a * 3; 18 | 19 | Assert.isTrue(it.hasNext()); 20 | Assert.equals(new OperatorOverloading("foo"), it.next()); 21 | Assert.equals(new OperatorOverloading("foofoo"), it.next()); 22 | Assert.equals(new OperatorOverloading("foofoofoo"), it.next()); 23 | Assert.isFalse(it.hasNext()); 24 | } 25 | #end 26 | 27 | #if (!cs && !java || haxe_ver >= 4.000) 28 | function testOperatorOverloadingWrapper () { 29 | var a = new Wrapper(new OperatorOverloading("foo")); 30 | var it = a.getSource(); 31 | 32 | Assert.isTrue(it.hasNext()); 33 | for (i in 0...10) Assert.equals(new Wrapper(new OperatorOverloading("foo")), it.next()); 34 | Assert.isTrue(it.hasNext()); 35 | 36 | var duplicator = a * 4; 37 | Assert.isTrue(duplicator.hasNext()); 38 | for (j in 0...4) Assert.equals(new OperatorOverloading("foo"), duplicator.next()); 39 | Assert.isFalse(duplicator.hasNext()); 40 | } 41 | #end 42 | 43 | #if (!cs && !java || haxe_ver >= 4.000) 44 | function testOperatorOverloading2 () { 45 | var a = new OperatorOverloading2("bar"); 46 | var it = a / 3; 47 | 48 | Assert.isTrue(it.hasNext()); 49 | Assert.equals(new OperatorOverloading2("b"), it.next()); 50 | Assert.equals(new OperatorOverloading2("a"), it.next()); 51 | Assert.equals(new OperatorOverloading2("r"), it.next()); 52 | Assert.isFalse(it.hasNext()); 53 | } 54 | #end 55 | 56 | function testForwardAbstract () { 57 | var myForward = new MyForward(); 58 | var it = myForward.numbers(); 59 | 60 | Assert.isTrue(it.hasNext()); 61 | Assert.equals(0, it.next()); 62 | Assert.equals(1, it.next()); 63 | Assert.equals(2, it.next()); 64 | Assert.isFalse(it.hasNext()); 65 | 66 | // MyForward has no field strNumbers 67 | //myForward.strNumbers(); 68 | } 69 | } 70 | 71 | @:yield 72 | class TestAbstractReturnType { 73 | public static function test ():AbstractIntIterator { 74 | @yield return 3; 75 | } 76 | } 77 | @:forward 78 | abstract AbstractIntIterator (Iterator) { } 79 | 80 | #if (!cs && !java || haxe_ver >= 4.000) // error CS1004 repeated modifier 81 | @:yield 82 | abstract OperatorOverloading(String) { 83 | public inline function new(s:String) { 84 | this = s; 85 | } 86 | 87 | @:op(A * B) 88 | public function repeat(rhs:Int):Iterator { 89 | var s:StringBuf = new StringBuf(); 90 | for (i in 0...rhs) { 91 | s.add(this); 92 | @yield return new OperatorOverloading(s.toString()); 93 | } 94 | } 95 | } 96 | #end 97 | 98 | #if (!cs && !java || haxe_ver >= 4.000) // error CS1004 repeated modifier 99 | @:build(yield.parser.Parser.run()) 100 | abstract Wrapper(OperatorOverloading) { 101 | public inline function new(s:OperatorOverloading) { 102 | this = s; 103 | } 104 | 105 | public function get(rhs:Int):Wrapper { 106 | return new Wrapper(this); 107 | } 108 | 109 | public function getSource():Iterator { 110 | while (true) @yield return new Wrapper(this); 111 | } 112 | 113 | @:op(A * B) 114 | public function deplicate(rhs:Int):Iterator { 115 | for (i in 0...rhs) { 116 | @yield return this; 117 | } 118 | } 119 | } 120 | #end 121 | 122 | @:forward(numbers) 123 | abstract MyForward(MyForwardedClass) { 124 | public inline function new() { 125 | this = new MyForwardedClass(); 126 | } 127 | } 128 | 129 | @:yield 130 | class MyForwardedClass { 131 | 132 | public function new () { } 133 | 134 | public function numbers () { 135 | for (i in 0...3) @yield return i; 136 | } 137 | 138 | public function strNumbers () { 139 | for (i in 0...3) @yield return Std.string(i); 140 | } 141 | } -------------------------------------------------------------------------------- /tests/misc/InferenceTests.hx: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | import utest.Assert; 4 | import haxe.Json; 5 | 6 | @:yield 7 | class InferenceTests extends utest.Test { 8 | 9 | function testFromConsts () { 10 | var it:Iterator = fromConsts(); 11 | Assert.isTrue(it.hasNext()); 12 | Assert.equals(3, it.next()); 13 | Assert.equals("", it.next()); 14 | Assert.equals(3.4, it.next()); 15 | Assert.equals(Json.stringify(~/[A-Z]/), Json.stringify(it.next())); 16 | Assert.isFalse(it.hasNext()); 17 | } 18 | 19 | function fromConsts ():Iterator { 20 | 21 | var a = 3; 22 | @yield return a; 23 | 24 | var b = ""; 25 | @yield return b; 26 | 27 | var c = 3.4; 28 | @yield return c; 29 | 30 | var d = ~/[A-Z]/; 31 | @yield return d; 32 | } 33 | 34 | function testFromIdents () { 35 | var it:Iterator = fromIdents(); 36 | Assert.isTrue(it.hasNext()); 37 | Assert.equals(3, it.next()); 38 | Assert.equals("", it.next()); 39 | Assert.equals(3.4, it.next()); 40 | Assert.equals(Json.stringify(~/[A-Z]/), Json.stringify(it.next())); 41 | Assert.isFalse(it.hasNext()); 42 | } 43 | 44 | function fromIdents ():Iterator { 45 | 46 | var a:Int; 47 | var b:String; 48 | var c:Float; 49 | var d:EReg; 50 | 51 | a = 3; 52 | var v1 = a; 53 | @yield return v1; 54 | 55 | b = ""; 56 | var v1 = b; 57 | @yield return v1; 58 | 59 | c = 3.4; 60 | var v1 = c; 61 | @yield return v1; 62 | 63 | d = ~/[A-Z]/; 64 | var v1 = d; 65 | @yield return v1; 66 | } 67 | 68 | function testFromIdentsFromConsts () { 69 | var it:Iterator = fromIdentsFromConsts(); 70 | Assert.isTrue(it.hasNext()); 71 | Assert.equals(3, it.next()); 72 | Assert.equals("", it.next()); 73 | Assert.equals(3.4, it.next()); 74 | Assert.equals(Json.stringify(~/[A-Z]/), Json.stringify(it.next())); 75 | Assert.isFalse(it.hasNext()); 76 | } 77 | 78 | function fromIdentsFromConsts ():Iterator { 79 | 80 | var a = 3; 81 | var b = ""; 82 | var c = 3.4; 83 | var d = ~/[A-Z]/; 84 | 85 | var v1 = a; 86 | @yield return v1; 87 | 88 | var v1 = b; 89 | @yield return v1; 90 | 91 | var v1 = c; 92 | @yield return v1; 93 | 94 | var v1 = d; 95 | @yield return v1; 96 | } 97 | 98 | function testFromArgs () { 99 | var it:Iterator = fromArgs(3, "", 3.4, ~/[A-Z]/); 100 | Assert.isTrue(it.hasNext()); 101 | Assert.equals(3, it.next()); 102 | Assert.equals("", it.next()); 103 | Assert.equals(3.4, it.next()); 104 | Assert.equals(Json.stringify(~/[A-Z]/), Json.stringify(it.next())); 105 | Assert.isFalse(it.hasNext()); 106 | } 107 | 108 | function fromArgs (a:Int, b:String, c:Float, d:EReg):Iterator { 109 | 110 | var v1 = a; 111 | @yield return v1; 112 | 113 | var v1 = b; 114 | @yield return v1; 115 | 116 | var v1 = c; 117 | @yield return v1; 118 | 119 | var v1 = d; 120 | @yield return v1; 121 | } 122 | 123 | function testFromArgsFromConsts () { 124 | var it:Iterator = fromArgsFromConsts(); 125 | Assert.isTrue(it.hasNext()); 126 | Assert.equals(3, it.next()); 127 | Assert.equals("", it.next()); 128 | Assert.equals(3.4, it.next()); 129 | Assert.isFalse(it.hasNext()); 130 | } 131 | 132 | function fromArgsFromConsts (a = 3, b = "", c = 3.4):Iterator { 133 | 134 | var v1 = a; 135 | @yield return v1; 136 | 137 | var v1 = b; 138 | @yield return v1; 139 | 140 | var v1 = c; 141 | @yield return v1; 142 | } 143 | 144 | function testInferredType () { 145 | inferredType(); 146 | Assert.isTrue(true); 147 | } 148 | 149 | function inferredType () { 150 | 151 | var i = 0; 152 | var len = 4; 153 | 154 | var a:Void->Int = function () return len; 155 | // var o = { 156 | // a:a 157 | // }; 158 | function toto () return a(); 159 | 160 | @yield return toto(); 161 | 162 | // var a:Void->Iterator = function () { 163 | // @yield return i; 164 | // @yield return i*2; 165 | // }; 166 | 167 | // var b:ArrayIterator> = [while (++i < len) { 168 | // function () { 169 | // @yield return i; 170 | // @yield return i*2; 171 | // }; 172 | // }]; 173 | 174 | } 175 | 176 | } -------------------------------------------------------------------------------- /yield/parser/tools/ImportTools.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.tools; 26 | import haxe.macro.Expr.ImportMode; 27 | import haxe.macro.Context; 28 | import haxe.macro.Type; 29 | import haxe.macro.Expr.Position; 30 | import haxe.macro.Expr.ComplexType; 31 | import haxe.macro.Expr.TypePath; 32 | import haxe.macro.Expr.ImportExpr; 33 | 34 | class ImportTools { 35 | 36 | public static function getFieldShorthands (imports:Array): Array { 37 | 38 | var ret = new Array(); 39 | 40 | for (iexpr in imports) { 41 | switch (iexpr.mode) { 42 | case IAsName(alias): 43 | ret.push(alias); 44 | default: 45 | } 46 | } 47 | 48 | return ret; 49 | } 50 | 51 | public static function getEnumConstructors (imports:Array, module:Array): Array { 52 | 53 | var enums = new Array(); 54 | 55 | inline function extractEnum (type:Type, into:Array):Void { 56 | switch (type) { 57 | case TEnum(t, params): into.push(t.get()); 58 | case _: null; 59 | } 60 | } 61 | 62 | // from module 63 | 64 | for (type in module) { 65 | extractEnum(type, enums); 66 | } 67 | 68 | // from imports 69 | 70 | for (iexpr in imports) { 71 | switch (iexpr.mode) { 72 | case INormal, IAsName(_): 73 | 74 | var module:String = iexpr.path[0].name; 75 | 76 | var name:String; 77 | for (i in 1...iexpr.path.length) { 78 | name = iexpr.path[i].name; 79 | module += "." + name; 80 | if (name.charAt(0) != name.charAt(0).toLowerCase()) { 81 | break; 82 | } 83 | } 84 | 85 | var modules:Array = Context.getModule(module); 86 | 87 | if (modules != null) 88 | for (type in modules) 89 | if (type != null) 90 | extractEnum(type, enums); 91 | 92 | case IAll: 93 | // TODO 94 | } 95 | } 96 | 97 | return enums; 98 | } 99 | 100 | public static function isImported (type:Array, ?localImports:Array):Bool { 101 | 102 | if (localImports == null) { 103 | localImports = Context.getLocalImports(); 104 | } 105 | 106 | var imported = false; 107 | 108 | for (localImport in localImports) { 109 | 110 | var isAll:Bool = switch (localImport.mode) { 111 | case INormal, IAsName(_): false; 112 | case IAll: 113 | 114 | var firstLetter:String = { 115 | var lastPart:String = localImport.path[localImport.path.length - 1].name; 116 | var i = 0; 117 | while (lastPart.charAt(i) == "_") { 118 | i++; 119 | } 120 | lastPart.charAt(i); 121 | }; 122 | 123 | if (firstLetter != "" && firstLetter == firstLetter.toUpperCase()) { 124 | true; 125 | } else { 126 | false; 127 | }; 128 | }; 129 | 130 | if (localImport.path.length == type.length || isAll && localImport.path.length == type.length - 1) { 131 | 132 | var i = localImport.path.length; 133 | while (--i >= 0) { 134 | if (localImport.path[i].name != type[i]) { 135 | break; 136 | } 137 | } 138 | if (i < 0) { 139 | imported = true; 140 | break; 141 | } 142 | 143 | } 144 | } 145 | 146 | return imported; 147 | } 148 | } 149 | #end -------------------------------------------------------------------------------- /yield/parser/checks/InitializationChecker.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.checks; 26 | import yield.parser.env.WorkEnv.Scope; 27 | import yield.parser.idents.IdentData; 28 | import yield.parser.idents.Statement; 29 | 30 | class InitializationChecker { 31 | 32 | public static function hasBeenInitialized (accession:IdentData, definition:IdentData, currentScope:Scope, previousScopes:Array = null): Bool { 33 | 34 | var info:{definitionReached:Bool} = { definitionReached: false }; 35 | 36 | // Ignore previous scope 37 | 38 | if (previousScopes == null) previousScopes = []; 39 | 40 | previousScopes.push(currentScope); 41 | 42 | if (isInitializedInScope( accession, definition, currentScope, info )) { 43 | return true; 44 | } 45 | 46 | var childList:Array = []; 47 | 48 | for (c in currentScope.children) childList.push(c); 49 | 50 | // Run through the various paths until reach the definition 51 | 52 | var i:Int = childList.length; 53 | var child:Scope; 54 | while (--i != -1) { 55 | 56 | child = childList[i]; 57 | if (previousScopes.indexOf(child) != -1) continue; 58 | 59 | if (!child.conditional) { 60 | 61 | previousScopes.push(child); 62 | if (hasBeenInitialized( accession, definition, child, previousScopes )) { 63 | return true; 64 | } 65 | 66 | } else { 67 | 68 | var result:Bool = true; 69 | var defaultDefined:Bool = false; 70 | 71 | for (s in child.conditionalAlternatives) { 72 | 73 | if (previousScopes.indexOf(s) != -1) continue; 74 | 75 | previousScopes.push(s); 76 | 77 | if (result) { 78 | 79 | if (s.defaultCondition) defaultDefined = true; 80 | 81 | if (!hasBeenInitialized(accession, definition, s, previousScopes)) { 82 | result = false; 83 | } 84 | } 85 | } 86 | 87 | if (result && defaultDefined) return true; 88 | } 89 | } 90 | 91 | // Parent's scope 92 | if (!info.definitionReached && currentScope.parent != null && previousScopes.indexOf(currentScope.parent) == -1) { 93 | if (currentScope.conditional) 94 | for (alt in currentScope.conditionalAlternatives) 95 | previousScopes.push(alt); 96 | return hasBeenInitialized(accession, definition, currentScope.parent, previousScopes); 97 | } else { 98 | return false; 99 | } 100 | } 101 | 102 | private static function isInitializedInScope (accession:IdentData, definition:IdentData, scope:Scope, info:{ definitionReached:Bool }): Bool { 103 | 104 | var j:Int = scope.localIdentStack.length; 105 | var statement:Statement; 106 | while (--j != -1) { 107 | statement = scope.localIdentStack[j]; 108 | 109 | switch statement { 110 | 111 | case Statement.Definitions(_data, _inlined): 112 | if (_data == definition) { 113 | 114 | info.definitionReached = true; 115 | return _data.initialized[_data.names.indexOf(accession.names[0])]; 116 | } 117 | 118 | case Statement.Accession(_data, _definition): 119 | if (_definition == definition) { 120 | return true; // stop the research, this ident will do the rest. 121 | } 122 | 123 | case Statement.Throw: 124 | return true; 125 | } 126 | } 127 | 128 | return false; 129 | } 130 | 131 | } 132 | #end -------------------------------------------------------------------------------- /yield/parser/eparsers/EFunctionParser.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.eparsers; 26 | import haxe.macro.Expr; 27 | import yield.parser.Parser; 28 | import yield.parser.env.WorkEnv; 29 | import yield.generators.NameController; 30 | import yield.parser.checks.TypeInferencer; 31 | import yield.parser.idents.IdentChannel; 32 | import yield.parser.idents.IdentOption; 33 | import yield.parser.idents.IdentRef; 34 | import yield.parser.idents.IdentRef.IdentRefTyped; 35 | import yield.parser.tools.ExpressionTools; 36 | import yield.parser.tools.MetaTools; 37 | import yield.parser.tools.MetaTools.MetaToolsOption; 38 | 39 | class EFunctionParser extends BaseParser { 40 | 41 | public function run (e:Expr, subParsing:Bool, _name:Null, _f:Function, inlined:Bool): Void { 42 | 43 | #if (haxe_ver < 4.000) 44 | 45 | if (_name != null && StringTools.startsWith(_name, "inline_")) { 46 | _name = _name.substr(7); 47 | inlined = true; 48 | } 49 | 50 | #end 51 | 52 | var safeName:String = NameController.anonymousFunction(m_we, _f, e.pos); 53 | 54 | MetaTools.option = MetaToolsOption.SkipNestedFunctions; 55 | 56 | if (!MetaTools.hasMeta(m_we.yieldKeyword, _f) || !Parser.parseFunction(safeName, _f, e.pos, m_we.getInheritedData())) { 57 | 58 | parseFun(_f, e.pos, true, false); 59 | 60 | } 61 | 62 | if (_name != null) { // If it isn't an anonymous function 63 | 64 | var ltype:Null = TypeInferencer.tryInferFunction(_f); 65 | if (ltype == null) ltype = macro:StdTypes.Dynamic; 66 | 67 | m_we.addLocalDefinition([_name], [true], [ltype], inlined, IdentRef.IEFunction(e), IdentChannel.Normal, [], e.pos); 68 | 69 | } 70 | 71 | if (!subParsing) m_ys.addIntoBlock(e); 72 | } 73 | 74 | public function parseFun (f:Function, pos:Position, subParsing:Bool, yieldedScope:Bool = true): Void { 75 | 76 | var lOriginalScope:Bool = m_ys.yieldedScope; 77 | m_ys.yieldedScope = yieldedScope; 78 | 79 | if (f.params != null) { 80 | for (lparam in f.params) { 81 | m_ys.parseTypeParamDecl(lparam, subParsing); 82 | } 83 | } 84 | 85 | if (f.ret != null) m_ys.parseComplexType(f.ret, subParsing); 86 | 87 | if (f.expr != null) { 88 | 89 | var fExprs:Array = ExpressionTools.checkIsInEBlock(f.expr); 90 | 91 | var predefineNativeVars:Array = []; 92 | 93 | // Define arguments as local variables 94 | if (f.args.length != 0) { 95 | 96 | for (arg in f.args) { 97 | 98 | var ltype:ComplexType = TypeInferencer.tryInferArg(arg); 99 | if (ltype == null) ltype = macro:StdTypes.Dynamic; 100 | predefineNativeVars.push({ ref: IdentRef.IArg(arg, f.expr.pos), type: ltype }); 101 | 102 | m_ys.parseFunctionArg(arg, true); 103 | 104 | if (!WorkEnv.isDynamicTarget()) { 105 | TypeInferencer.checkArgumentType(arg, pos); 106 | } 107 | 108 | var initValue:Expr = { expr: EConst(CIdent(arg.name)), pos: f.expr.pos }; 109 | var v:Var = { name: arg.name, type: arg.type, expr: initValue }; 110 | var evars:Expr = { expr: EVars([v]), pos: f.expr.pos }; 111 | 112 | fExprs.unshift(evars); 113 | } 114 | } 115 | 116 | m_ys.eblockParser.run(f.expr, subParsing, fExprs, false, null, predefineNativeVars); 117 | } 118 | 119 | m_ys.yieldedScope = lOriginalScope; 120 | } 121 | 122 | } 123 | #end -------------------------------------------------------------------------------- /yield/parser/eparsers/EConstParser.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.eparsers; 26 | import haxe.macro.Context; 27 | import yield.parser.idents.Statement; 28 | import haxe.macro.Expr; 29 | import yield.parser.idents.IdentChannel; 30 | import yield.parser.idents.IdentOption; 31 | import yield.parser.idents.IdentRef; 32 | import yield.parser.idents.IdentData; 33 | import yield.parser.env.WorkEnv; 34 | import yield.parser.tools.IdentCategory; 35 | 36 | class EConstParser extends BaseParser { 37 | 38 | public function run (e:Expr, subParsing:Bool, _c:Constant, ?ic:IdentChannel, initialized = false, beingModified = false): Void { 39 | 40 | if (ic == null) ic = IdentChannel.Normal; 41 | 42 | // Resolve the identifier 43 | 44 | switch (_c) { 45 | 46 | case CIdent(_s): 47 | 48 | if (_s == "true" || _s == "false" || _s == "null") { 49 | return; 50 | } 51 | 52 | if (_s == "this") { 53 | 54 | m_we.addInstanceAccession(null, m_we.classData.localComplexType, IdentRef.IEConst(e), ic, e.pos); 55 | 56 | } else { 57 | 58 | var exprCat:IdentCategory = m_we.getIdentCategoryOf(_s, ic); 59 | 60 | switch (exprCat) { 61 | 62 | case IdentCategory.InstanceField(_t): 63 | 64 | m_we.addInstanceAccession(_s, _t, IdentRef.IEConst(e), ic, e.pos); 65 | 66 | case IdentCategory.LocalVar(__definition): 67 | 68 | var defIndex:Int = __definition.names.indexOf(_s); 69 | 70 | var type:ComplexType = __definition.types[defIndex]; 71 | var initialized:Bool = initialized ? true : __definition.initialized[defIndex]; 72 | 73 | 74 | if (beingModified && __definition.options.indexOf(ReadOnly) != -1) { 75 | 76 | if (__definition.options.indexOf(IsVarLoop) != -1) { 77 | 78 | Context.error('Loop variable cannot be modified', e.pos); 79 | 80 | } else { 81 | 82 | Context.error('Cannot access field or identifier $_s for writing', e.pos); 83 | } 84 | } 85 | 86 | if (__definition.env == m_we) { 87 | 88 | m_we.addLocalAccession(_s, initialized, type, IdentRef.IEConst(e), ic, e.pos); 89 | 90 | } else { 91 | 92 | var data:IdentData = { 93 | names: [_s], 94 | initialized: [initialized], 95 | types: [type], 96 | ident: IdentRef.IEConst(e), 97 | channel: ic, 98 | options: __definition.options.copy(), 99 | scope: m_we.currentScope, 100 | env: m_we, 101 | pos: e.pos 102 | }; 103 | 104 | if (__definition.options.indexOf(IdentOption.KeepAsVar) != -1) { 105 | m_we.addParentAsVarDependencies(__definition.env, data); 106 | } else { 107 | m_we.addParentDependencies(__definition.env); 108 | __definition.env.addLocalAccession(_s, initialized, type, IdentRef.IEConst(e), ic, e.pos); 109 | } 110 | 111 | m_we.parentStack.push(Statement.Accession(data, __definition)); 112 | 113 | } 114 | 115 | case IdentCategory.InstanceStaticField(_t, _c): 116 | 117 | e.expr = EField(Context.parse(_c.module + "." + _c.name, e.pos), _s); 118 | 119 | case IdentCategory.ImportedField(_t): 120 | case IdentCategory.Unknown: 121 | } 122 | } 123 | 124 | case _: 125 | } 126 | 127 | if (!subParsing) m_ys.addIntoBlock(e); 128 | } 129 | 130 | } 131 | #end -------------------------------------------------------------------------------- /yield/parser/eparsers/EIfParser.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.eparsers; 26 | import haxe.macro.Context; 27 | import haxe.macro.Expr; 28 | import yield.parser.env.WorkEnv.Scope; 29 | import yield.parser.tools.ExpressionTools; 30 | 31 | class EIfParser extends BaseParser { 32 | 33 | public function run (e:Expr, subParsing:Bool, _econd:Expr, _eif:Expr, _eelse:Null, lastAlternativeScope:Scope = null): Void { 34 | 35 | if (!subParsing) m_ys.addIntoBlock(e); 36 | 37 | // Get alternative scopes 38 | 39 | var alternativeEconds:Array = []; 40 | var alternativeExprs:Array = []; 41 | 42 | function parseEif (e:Expr) { 43 | switch (e.expr) { 44 | case ExprDef.EIf(_econd, _eif, _eelse): 45 | 46 | alternativeEconds.push(_econd); 47 | alternativeExprs.push(_eif); 48 | 49 | if (_eelse == null) { 50 | _eelse = { expr: EBlock([]), pos: _eif.pos }; 51 | e.expr = ExprDef.EIf(_econd, _eif, _eelse); 52 | } 53 | 54 | parseEif(_eelse); 55 | 56 | default: 57 | 58 | alternativeExprs.push(e); 59 | } 60 | } 61 | 62 | parseEif(e); 63 | 64 | // Temporary shift the iterator's position 65 | 66 | var posInitial:UInt = m_ys.cursor; 67 | 68 | m_ys.moveCursor(); 69 | 70 | // Parse scopes 71 | 72 | var alternativeCount:Int = alternativeExprs.length; 73 | var isDefault:Bool; 74 | var previousAlternativeScope:Scope = null; 75 | 76 | var lgotoPostEIf:Expr = { expr: null, pos: e.pos }; 77 | var unYieldedScopes:Array> = []; 78 | 79 | for (i in 0...alternativeCount) { 80 | 81 | isDefault = i == alternativeCount - 1; 82 | 83 | var posFirst:UInt = m_ys.cursor; 84 | 85 | // parse econd 86 | if (!isDefault) { 87 | m_ys.parseOut(alternativeEconds[i], true, "Bool"); 88 | } 89 | 90 | // parse eif/eelse 91 | previousAlternativeScope = m_ys.eblockParser.runFromExpr(alternativeExprs[i], false, true, previousAlternativeScope); 92 | previousAlternativeScope.defaultCondition = isDefault; 93 | 94 | if (m_ys.cursor == posFirst) { 95 | 96 | // let exprs in the EIf expression 97 | m_ys.iteratorBlocks[posFirst] = []; 98 | 99 | unYieldedScopes.push(ExpressionTools.checkIsInEBlock(alternativeExprs[i])); 100 | 101 | } else { 102 | 103 | // remove first sub-expression 104 | m_ys.spliceIteratorBlocks(posFirst, 1); 105 | 106 | // add goto sub-expressions 107 | var altExprs:Array = ExpressionTools.checkIsInEBlock(alternativeExprs[i]); 108 | var ldefNext:Expr = { expr: null, pos: alternativeExprs[i].pos }; 109 | #if (yield_debug_no_display || !display && !yield_debug_display) 110 | altExprs.unshift(ldefNext); 111 | m_ys.registerSetAction(ldefNext, posFirst); 112 | #end 113 | 114 | // add goto post EIf 115 | m_ys.addIntoBlock(lgotoPostEIf); 116 | 117 | m_ys.moveCursor(); 118 | } 119 | } 120 | 121 | // Shift back the iterator's position if relevant 122 | 123 | if (m_ys.cursor == posInitial + 1) { 124 | 125 | m_ys.spliceIteratorBlocks(m_ys.cursor, 1); 126 | 127 | } else { 128 | 129 | #if (yield_debug_no_display || !display && !yield_debug_display) 130 | if (subParsing) Context.fatalError("Missing return value", e.pos); 131 | #end 132 | 133 | // add goto post EIf 134 | #if (yield_debug_no_display || !display && !yield_debug_display) 135 | for (lexprs in unYieldedScopes) lexprs.push(lgotoPostEIf); 136 | m_ys.registerGotoAction(lgotoPostEIf, m_ys.cursor); 137 | #end 138 | } 139 | } 140 | 141 | } 142 | #end -------------------------------------------------------------------------------- /yield/generators/NameController.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.generators; 26 | import haxe.crypto.Md5; 27 | import haxe.macro.Expr.Function; 28 | import haxe.macro.Expr.FunctionArg; 29 | import haxe.macro.Expr.Position; 30 | import yield.parser.env.WorkEnv; 31 | import yield.parser.env.WorkEnv.Scope; 32 | import yield.parser.idents.IdentChannel; 33 | 34 | class NameController { 35 | 36 | private static var ID_CHANNELS:Map = [ 37 | IdentChannel.Normal => "y", 38 | IdentChannel.IterationOp => "i" 39 | ]; 40 | 41 | private static var anonymousCounters:Map = new Map(); 42 | 43 | /** 44 | * Name of the extra-type generated when using yield statements. 45 | */ 46 | public static function extraTypeName (env:WorkEnv, number:UInt): String { 47 | return env.functionsPack.join("_") + "_" + env.functionName + "__I" + number; 48 | } 49 | 50 | /** 51 | * Unique name of the anonymous function. 52 | */ 53 | public static function anonymousFunction (env:WorkEnv, f:Function, pos:Position): String { 54 | var id:String = Md5.encode(env.functionsPack.join(".")); 55 | if (!anonymousCounters.exists(id)) 56 | anonymousCounters.set(id, 0); 57 | else 58 | anonymousCounters[id] += 1; 59 | return "anon" + anonymousCounters.get(id) + id; 60 | } 61 | 62 | /** 63 | * Field name of the class's instance. 64 | */ 65 | public static function fieldInstance (): String { 66 | return "_instance_"; 67 | } 68 | 69 | /** 70 | * Constructor arg name of the class's instance. 71 | */ 72 | public static function argInstance (): String { 73 | return "instance"; 74 | } 75 | 76 | /** 77 | * Field name of a required WorkEnv parent. 78 | */ 79 | public static function fieldParent (env:WorkEnv, dependence:WorkEnv): String { 80 | return "_d" + env.parentDependencies.indexOf(dependence) + "_"; 81 | } 82 | 83 | /** 84 | * Constructor arg name of a required WorkEnv parent. 85 | */ 86 | public static function argParent (env:WorkEnv, dependence:WorkEnv): String { 87 | return "_d" + env.parentDependencies.indexOf(dependence) + "_p"; 88 | } 89 | 90 | /** 91 | * Constructor arg name of a required parent's local. 92 | */ 93 | public static function argParentAsVar (name:String): String { 94 | return name + "_pav"; 95 | } 96 | 97 | /** 98 | * Constructor arg name of any user argument. 99 | */ 100 | public static function argArgument (argument:FunctionArg): String { 101 | return argument.name + "_a"; 102 | } 103 | 104 | /** 105 | * Name of any user local vars and functions. 106 | */ 107 | public static function localVar (originalName:String, scope:Scope, ic:IdentChannel, number:UInt): String { 108 | return originalName + "_" + scope.id + ID_CHANNELS[ic] + number; 109 | } 110 | 111 | /** 112 | * Name of any user local vars and functions from a parent environment. 113 | */ 114 | public static function parentVar (originalName:String, scope:Scope, ic:IdentChannel, envId:Int): String { 115 | return originalName + "_" + ID_CHANNELS[ic] + envId + "pav"; 116 | } 117 | 118 | /** 119 | * Name of any iterative functions. 120 | */ 121 | public static function iterativeFunction (iteratorPos:UInt): String { 122 | return "_" + iteratorPos + "_"; 123 | } 124 | 125 | /** 126 | * Name of the iteration's cursor field. 127 | */ 128 | public static function fieldCursor ():String return "_cursor_"; 129 | 130 | /** 131 | * Name of the iteration's current field. 132 | */ 133 | public static function fieldCurrent ():String return "_current_"; 134 | 135 | /** 136 | * Name of the iteration's isConsumed field. 137 | */ 138 | public static function fieldIsConsumed ():String return "_isConsumed_"; 139 | 140 | /** 141 | * Name of the iteration's isCompleted field. 142 | */ 143 | public static function fieldCompleted ():String return "_isComplete_"; 144 | 145 | } 146 | #end -------------------------------------------------------------------------------- /yield/parser/tools/ExpressionTools.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.tools; 26 | import haxe.macro.Context; 27 | import haxe.macro.Expr; 28 | import haxe.macro.ExprTools; 29 | import haxe.macro.Type; 30 | import haxe.macro.TypeTools; 31 | 32 | class ExpressionTools { 33 | 34 | public static function checkIsInEBlock (e:Expr): Array { 35 | 36 | switch (e.expr) { 37 | 38 | case EBlock(_exprs): 39 | 40 | return _exprs; 41 | 42 | default: 43 | 44 | var exprs:Array = [{expr:e.expr, pos:e.pos}]; 45 | 46 | e.expr = EBlock(exprs); 47 | 48 | return exprs; 49 | } 50 | } 51 | 52 | public static function getConstIdent (e:Expr): String { 53 | 54 | return switch (e.expr) { 55 | case EConst(_c): 56 | switch (_c) { 57 | case CIdent(__s): __s; 58 | case _: null; 59 | } 60 | default: null; 61 | }; 62 | } 63 | 64 | public static function getConstString (e:Expr): String { 65 | 66 | return switch (ExpressionTools.getExpr(e).expr) { 67 | case EConst(_c): 68 | switch (_c) { 69 | case CString(__s): __s; 70 | default: null; 71 | } 72 | default: null; 73 | } 74 | } 75 | 76 | public static function getExpr (e:Expr): Expr { 77 | return switch (e.expr) { 78 | case EParenthesis(_e): 79 | getExpr(_e); 80 | default: 81 | e; 82 | } 83 | } 84 | 85 | public static function makeCall (fnName:String, args:Array, pos:Position): Expr { 86 | 87 | return { 88 | expr : ExprDef.ECall( 89 | { expr: EField({ expr: EConst(CIdent("this")), pos: pos }, fnName), pos: pos }, 90 | args 91 | ), 92 | pos : pos 93 | }; 94 | } 95 | 96 | /** 97 | * Define the variable `ident` as a compiler directive. If the value of `arg` is not equals to `defaultValue`, the directive is ignored. Otherwise, if the directive is specified the value is overrode. 98 | */ 99 | public static macro function defineVarAsDirective (ident:Expr, defaultValue:Expr): Expr { 100 | 101 | var argName:String = ExprTools.toString(ident); 102 | var assign:Expr; 103 | 104 | switch (defaultValue.expr) { 105 | 106 | case EConst(c): 107 | 108 | switch (c) { 109 | case CString(_): 110 | assign = macro $ident = v; 111 | case CInt(_): 112 | assign = macro $ident = Std.parseInt(v); 113 | case CFloat(_): 114 | assign = macro $ident = Std.parseFloat(v); 115 | case CIdent(_): 116 | assign = macro if (v == "true" || v == "1") $ident = true; else if (v == "false" || v == "0") $ident = false; else if (v == "null") $ident = null; else $ident = $defaultValue; 117 | default: 118 | assign = macro $ident = $defaultValue; 119 | } 120 | 121 | default: 122 | Context.fatalError("The value must be a constant", defaultValue.pos); 123 | } 124 | 125 | var a = macro if ($ident == null) { 126 | 127 | $ident = $defaultValue; 128 | 129 | if (Context.defined($v{argName})) { 130 | 131 | var v = Context.definedValue($v{argName}); 132 | if (v != null) { 133 | v = StringTools.trim(v); 134 | $assign; 135 | } 136 | } 137 | }; 138 | 139 | trace(a); 140 | trace(ExprTools.toString(a)); 141 | return a; 142 | } 143 | 144 | /** 145 | * @param t Type of the type parameter. It must be a `TInst` with a `KTypeParameter` kind. 146 | * @param name Name of the type parameter, if relevant. 147 | * @return 148 | */ 149 | public static function convertToTypeParamDecl (t:Type, ?name:String): TypeParamDecl { 150 | 151 | var constraints:Array = []; 152 | var params:Array = []; 153 | 154 | switch (t) { 155 | case Type.TInst(_.get() => _t, _params): 156 | 157 | // name 158 | if (name == null) { 159 | name = _t.name; 160 | } 161 | 162 | // constraints 163 | switch (_t.kind) { 164 | case KTypeParameter(_constraints): 165 | for (c in _constraints) constraints.push(TypeTools.toComplexType(c)); 166 | default: 167 | } 168 | 169 | // params 170 | for (__t in _params) { 171 | params.push( convertToTypeParamDecl(__t) ); 172 | } 173 | 174 | default: return null; 175 | }; 176 | 177 | return { 178 | constraints: constraints, 179 | meta: null, 180 | name: name, 181 | params: params 182 | }; 183 | } 184 | 185 | } 186 | #end -------------------------------------------------------------------------------- /yield/parser/eparsers/ETryParser.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.eparsers; 26 | import haxe.macro.Expr; 27 | import yield.parser.env.WorkEnv.Scope; 28 | import yield.parser.idents.IdentChannel; 29 | import yield.parser.idents.IdentOption; 30 | import yield.parser.tools.ExpressionTools; 31 | 32 | class ETryParser extends BaseParser { 33 | 34 | public function run (e:Expr, subParsing:Bool, _e:Expr, _catches:Array): Void { 35 | 36 | if (subParsing) { 37 | runPreserveMode(e, _e, _catches); 38 | } else { 39 | runSplitMode(e, subParsing, _e, _catches); 40 | } 41 | } 42 | 43 | private function runPreserveMode (e:Expr, _e:Expr, _catches:Array): Void { 44 | 45 | m_ys.parseOut(_e, true); 46 | 47 | var previousAlternativeScope:Scope = null; 48 | 49 | for (lcatch in _catches) { 50 | m_ys.parseComplexType(lcatch.type, true); 51 | previousAlternativeScope = m_ys.eblockParser.runFromExpr(lcatch.expr, true, true, previousAlternativeScope, [{ ref: IdentRef.ICatch(lcatch), type: lcatch.type }]); 52 | } 53 | } 54 | 55 | private function runSplitMode (e:Expr, subParsing:Bool, _e:Expr, _catches:Array): Void { 56 | 57 | var posOriginalIterator = m_ys.cursor; 58 | 59 | m_ys.moveCursor(); 60 | 61 | var lgotoPost:Expr = { expr: null, pos: e.pos }; 62 | 63 | // Parse catches 64 | 65 | var previousCatchScope:Scope = null; 66 | 67 | for (lcatch in _catches) { 68 | previousCatchScope = parseCatch(lcatch, lgotoPost, previousCatchScope); 69 | } 70 | 71 | #if (yield_debug_no_display || !display && !yield_debug_display) 72 | 73 | // Parse try block 74 | 75 | var posFirstTry = m_ys.cursor; 76 | 77 | // goto the first try statement expressions 78 | e.expr = null; 79 | m_ys.registerGotoAction(e, posFirstTry); 80 | 81 | if (!subParsing) m_ys.addIntoBlock(e, posOriginalIterator); 82 | 83 | // Wrap each try-sub expressions in the ETry 84 | 85 | var ltryExprs:Array = ExpressionTools.checkIsInEBlock(_e); 86 | m_ys.eblockParser.run(_e, false, ltryExprs, true, previousCatchScope); 87 | 88 | var posEndTries = m_ys.cursor; 89 | 90 | for (i in posFirstTry...m_ys.cursor+1) { 91 | 92 | m_ys.iteratorBlocks[i] = [{ 93 | expr: ETry({expr: EBlock(m_ys.iteratorBlocks[i]),pos : _e.pos}, _catches), 94 | pos: e.pos 95 | }]; 96 | } 97 | 98 | var posPostETry = posEndTries + 1; 99 | 100 | // Goto after the try-catch 101 | 102 | m_ys.addIntoBlock(lgotoPost); 103 | 104 | m_ys.moveCursor(); 105 | 106 | // Setup Goto actions 107 | 108 | m_ys.registerGotoAction(lgotoPost, posPostETry); 109 | #end 110 | } 111 | 112 | private function parseCatch (c:Catch, gotoPostTry:Expr, previousCatchScope:Scope): Scope { 113 | 114 | previousCatchScope = m_we.openScope(true, previousCatchScope); 115 | 116 | var t = switch c.type { 117 | #if (haxe_ver >= 4.100) 118 | case null | (macro:haxe.Exception): 119 | previousCatchScope.defaultCondition = true; 120 | macro:haxe.Exception; 121 | #end 122 | case (macro:Dynamic) | (macro:StdTypes.Dynamic): 123 | previousCatchScope.defaultCondition = true; 124 | c.type; 125 | default: 126 | c.type; 127 | } 128 | 129 | // Add catch-argument as property 130 | var initializationValue:Expr = {expr:EConst(CIdent(c.name)), pos:c.expr.pos}; 131 | var ldefinition:Expr = { 132 | expr: EVars([{name: c.name, type: t, expr: initializationValue}]), 133 | pos: c.expr.pos 134 | }; 135 | m_we.addLocalDefinition([c.name], [true], [t], false, IdentRef.IEVars(ldefinition), IdentChannel.Normal, [], ldefinition.pos); 136 | 137 | 138 | var posOriginal = m_ys.cursor; 139 | 140 | var posFirst = m_ys.cursor; 141 | 142 | m_ys.parseComplexType(t, true); 143 | m_ys.parse(c.expr, false); 144 | 145 | // Replace the body with Goto action to the first catch sub-expressions 146 | #if (yield_debug_no_display || !display && !yield_debug_display) 147 | var lgotoNext:Expr = { expr: null, pos: c.expr.pos }; 148 | c.expr = {expr:EBlock([ldefinition, lgotoNext]), pos:c.expr.pos}; 149 | m_ys.registerGotoAction(lgotoNext, posFirst); 150 | #end 151 | 152 | // Goto after the try-catch 153 | m_ys.addIntoBlock(gotoPostTry); 154 | 155 | m_ys.moveCursor(); 156 | 157 | m_we.closeScope(); 158 | 159 | return previousCatchScope; 160 | } 161 | 162 | } 163 | #end -------------------------------------------------------------------------------- /yield/parser/eparsers/EWhileParser.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.eparsers; 26 | import haxe.macro.Context; 27 | import haxe.macro.Expr; 28 | 29 | class EWhileParser extends BaseParser { 30 | 31 | private static var continueStatements:Array> = []; 32 | private static var breakStatements:Array> = []; 33 | 34 | public function addContinue (e:Expr): Void { 35 | if (continueStatements.length != 0 && continueStatements[continueStatements.length - 1] != null) { 36 | continueStatements[continueStatements.length - 1].push(e); 37 | } 38 | } 39 | 40 | public function addBreak (e:Expr): Void { 41 | if (breakStatements.length != 0) { 42 | breakStatements[breakStatements.length - 1].push(e); 43 | } 44 | } 45 | 46 | public function run (e:Expr, subParsing:Bool, _econd:Expr, _e:Expr, _normalWhile:Bool, forceSplit:Bool = false): Void { 47 | 48 | if (subParsing) { 49 | runPreserveMode(e, subParsing, _econd, _e, _normalWhile, forceSplit); 50 | } else { 51 | runSplitMode(e, subParsing, _econd, _e, _normalWhile, forceSplit); 52 | } 53 | } 54 | 55 | private function runPreserveMode (e:Expr, subParsing:Bool, _econd:Expr, _e:Expr, _normalWhile:Bool, forceSplit:Bool = false): Void { 56 | 57 | m_ys.parseOut(_econd, true, "Bool"); 58 | m_ys.parseOut(_e, true); 59 | } 60 | 61 | private function runSplitMode (e:Expr, subParsing:Bool, _econd:Expr, _e:Expr, _normalWhile:Bool, forceSplit:Bool = false): Void { 62 | 63 | continueStatements.push([]); 64 | breakStatements.push([]); 65 | 66 | m_ys.parseOut(_econd, true, "Bool"); 67 | 68 | // While process 69 | 70 | var posOriginalDeclaration:Int = m_ys.cursor; 71 | 72 | var originalPosApart:Bool = false; 73 | 74 | if (m_ys.iteratorBlocks[posOriginalDeclaration].length != 0 || !_normalWhile) { 75 | originalPosApart = true; 76 | m_ys.moveCursor(); 77 | } 78 | 79 | var posWhileDeclaration:Int = m_ys.cursor; 80 | 81 | m_ys.moveCursor(); 82 | 83 | var posFirstSubExpression:Int = m_ys.cursor; 84 | 85 | m_ys.eblockParser.runFromExpr(_e, false, true); 86 | 87 | if (!forceSplit && m_ys.cursor == posFirstSubExpression) { // If there is no sub-yield expression 88 | 89 | if (originalPosApart) { 90 | m_ys.spliceIteratorBlocks(m_ys.cursor-1, 2); 91 | } else { 92 | m_ys.spliceIteratorBlocks(m_ys.cursor, 1); 93 | } 94 | 95 | if (!subParsing) m_ys.addIntoBlock(e); 96 | 97 | continueStatements.pop(); 98 | breakStatements.pop(); 99 | 100 | } else { 101 | 102 | #if (yield_debug_no_display || !display && !yield_debug_display) 103 | if (subParsing) Context.fatalError("Missing return value", e.pos); 104 | 105 | var posLastWhilePart:Int = m_ys.cursor; 106 | var posAfterWhile:Int = m_ys.cursor+1; 107 | 108 | // goto actions 109 | var lgotoWhileDecl:Expr = { expr: null, pos: _e.pos }; 110 | var lgotoFirstSubExpr:Expr = { expr: null, pos: _e.pos }; 111 | var lgotoAfterWhile:Expr = { expr: null, pos: _e.pos }; 112 | 113 | 114 | // Append in posOriginal goto while declaration 115 | if (originalPosApart) { 116 | if (_normalWhile) m_ys.addIntoBlock(lgotoWhileDecl, posOriginalDeclaration); 117 | else { 118 | m_ys.addIntoBlock(lgotoFirstSubExpr, posOriginalDeclaration); 119 | _normalWhile = true; 120 | } 121 | } 122 | 123 | // While declaration with body: goto first sub expression, otherwise goto after the While 124 | e.expr = EWhile(_econd, lgotoFirstSubExpr, _normalWhile); 125 | m_ys.addIntoBlock(e, posWhileDeclaration); 126 | 127 | m_ys.addIntoBlock(lgotoAfterWhile, posWhileDeclaration); 128 | 129 | // Append in posLastWhilePart goto the While declaration 130 | m_ys.addIntoBlock(lgotoWhileDecl, posLastWhilePart); 131 | 132 | m_ys.moveCursor(); 133 | 134 | 135 | // Setup actions 136 | 137 | m_ys.registerGotoAction( lgotoWhileDecl, posWhileDeclaration ); 138 | m_ys.registerGotoAction( lgotoFirstSubExpr, posFirstSubExpression ); 139 | m_ys.registerGotoAction( lgotoAfterWhile, posAfterWhile ); 140 | 141 | // Setup break and continue statements 142 | 143 | var continues:Array = continueStatements.pop(); 144 | var breaks:Array = breakStatements.pop(); 145 | 146 | for (lexpr in continues) { 147 | m_ys.registerGotoAction( lexpr, posWhileDeclaration ); 148 | } 149 | 150 | for (lexpr in breaks) { 151 | m_ys.registerGotoAction( lexpr, posAfterWhile ); 152 | } 153 | 154 | #end 155 | } 156 | 157 | } 158 | 159 | } 160 | #end -------------------------------------------------------------------------------- /tests/misc/ImportTests.hx: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | import utest.Assert; 4 | import pack.pack1.MiscFunctions; 5 | import pack.pack1.MoreMiscFunctions; 6 | import pack.pack1.MoreMiscFunctions.a0 as funcA0; 7 | import pack.pack1.MoreMiscFunctions.a1; 8 | import pack.pack1.MoreMiscFunctions.UppercaseFunction; 9 | import pack.pack1.MoreMiscFunctions.MoreMiscFunctions3.*; 10 | import pack.pack3.SomeFunctions.*; 11 | import pack.pack2.*; 12 | import haxe.ds.GenericStack; // import a generic type 13 | import misc.EmptyImport; // import a type that trigger the parsing of the module classes 14 | 15 | @:access(pack.pack1.MoreMiscFunctions.priv) 16 | class ImportTests extends utest.Test { 17 | 18 | function testSimpleImport () { 19 | var it = simpleImport("Patrick"); 20 | 21 | Assert.isTrue(it.hasNext()); 22 | Assert.equals("hello!", it.next()); 23 | Assert.isTrue(it.hasNext()); 24 | Assert.equals("hello Toto!", it.next()); 25 | Assert.isTrue(it.hasNext()); 26 | Assert.equals("hello Patrick!", it.next()); 27 | Assert.isFalse(it.hasNext()); 28 | } 29 | 30 | function simpleImport (name:String): Iterator { 31 | 32 | #if (neko || js || php || python || lua) 33 | var msg = MiscFunctions.hello(); 34 | #else 35 | var msg:String = MiscFunctions.hello(); 36 | #end 37 | 38 | @yield return msg; 39 | @yield return MiscFunctions.sayHello("Toto"); 40 | @yield return MiscFunctions.sayHello(name); 41 | } 42 | 43 | function testImportField () { 44 | var it = importField(); 45 | 46 | Assert.isTrue(it.hasNext()); 47 | Assert.equals(a1(""), it.next()); 48 | Assert.equals(UppercaseFunction(), it.next()); 49 | Assert.isFalse(it.hasNext()); 50 | } 51 | 52 | function importField (): Iterator { 53 | @yield return a1(""); 54 | @yield return UppercaseFunction(); 55 | } 56 | 57 | function testAsImport () { 58 | var it = asImport(); 59 | 60 | Assert.isTrue(it.hasNext()); 61 | Assert.equals(funcA0(""), it.next()); 62 | Assert.isFalse(it.hasNext()); 63 | } 64 | 65 | function asImport (): Iterator { 66 | @yield return funcA0(""); 67 | } 68 | 69 | function testAccessField () { 70 | var it = accessField(); 71 | 72 | misc.packs.Parent.Parent.reset(); 73 | 74 | Assert.isTrue(it.hasNext()); 75 | Assert.equals(0, it.next()); 76 | Assert.equals(100, it.next()); 77 | Assert.isFalse(it.hasNext()); 78 | } 79 | 80 | function accessField (): Iterator { 81 | @yield return misc.packs.Parent.publicStatic ? 1 : 0; 82 | @yield return pack.pack1.MoreMiscFunctions.priv(""); 83 | } 84 | 85 | function testAccessField2 () { 86 | var it = accessField2(); 87 | 88 | Assert.isTrue(it.hasNext()); 89 | Assert.equals(200, it.next()); 90 | Assert.isFalse(it.hasNext()); 91 | } 92 | 93 | @:access(pack.pack1.MoreMiscFunctions.priv2) 94 | function accessField2 (): Iterator { 95 | @yield return pack.pack1.MoreMiscFunctions.priv2(""); 96 | } 97 | 98 | //function testImportAllowedField () { 99 | //var it = importAllowedField(); 100 | // 101 | //Assert.isTrue(it.hasNext()); 102 | //Assert.equals(9, it.next()); 103 | //Assert.isFalse(it.hasNext()); 104 | //} 105 | // 106 | //function importAllowedField (): Iterator { 107 | //@yield return misc.pack.MoreMiscFunctions.MoreMiscFunctionsPriv.d0(""); 108 | //} 109 | 110 | function testAllField () { 111 | var it = allField(); 112 | 113 | Assert.isTrue(it.hasNext()); 114 | Assert.equals(5, it.next()); 115 | Assert.equals(6, it.next()); 116 | Assert.equals(7, it.next()); 117 | Assert.equals(8, it.next()); 118 | Assert.isFalse(it.hasNext()); 119 | } 120 | 121 | function allField (): Iterator { 122 | @yield return c0(""); 123 | @yield return c1(""); 124 | @yield return c2(""); 125 | @yield return c3(""); 126 | } 127 | 128 | function testAllField2 () { 129 | var it = allField2(); 130 | 131 | Assert.isTrue(it.hasNext()); 132 | Assert.equals(1, it.next()); 133 | Assert.equals(2, it.next()); 134 | Assert.isFalse(it.hasNext()); 135 | } 136 | 137 | function allField2 (): Iterator { 138 | @yield return f0(""); 139 | @yield return f1(""); 140 | } 141 | 142 | function testAllModule () { 143 | var it = allModule(); 144 | Assert.isTrue(it.hasNext()); 145 | Assert.equals("toto1", it.next()); 146 | Assert.isFalse(it.hasNext()); 147 | } 148 | 149 | function allModule () { 150 | @yield return MiscYielded.inlineMethod2("toto").next(); 151 | } 152 | 153 | function testUsingTypedef () { 154 | 155 | var it = usingTypedef0(); 156 | Assert.equals(0, it.next()); 157 | 158 | it = usingTypedef1(); 159 | Assert.equals(1, it.next()); 160 | 161 | it = usingTypedef2(); 162 | Assert.equals(2, it.next()); 163 | 164 | it = usingTypedef3(); 165 | Assert.equals(3, it.next()); 166 | 167 | //it = usingTypedef4(); 168 | //Assert.equals(4, it.next()); 169 | 170 | //it = usingTypedef5(); 171 | //Assert.equals(5, it.next()); 172 | 173 | //it = usingTypedef6(); 174 | //Assert.equals(6, it.next()); 175 | } 176 | 177 | function usingTypedef0 ():DynamicAlias { 178 | @yield return 0; 179 | } 180 | 181 | function usingTypedef1 ():DynamicIterator { 182 | @yield return 1; 183 | } 184 | 185 | function usingTypedef2 ():Enumerator { 186 | @yield return 2; 187 | } 188 | 189 | function usingTypedef3 ():Iterator { 190 | @yield return cast 3; 191 | } 192 | 193 | //function usingTypedef4 ():Enumerator { 194 | //@yield return cast 4; 195 | //} 196 | 197 | //function usingTypedef5> ():T { 198 | //@yield return cast 5; 199 | //} 200 | 201 | //function usingTypedef6> ():T { 202 | //@yield return cast 6; 203 | //} 204 | 205 | } 206 | 207 | typedef Enumerator = Iterator; 208 | typedef DynamicAlias = Dynamic; 209 | typedef DynamicIterator = Iterator; -------------------------------------------------------------------------------- /yield/parser/YieldSplitterOptimizer.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser; 26 | import haxe.macro.Expr.Position; 27 | import yield.parser.env.WorkEnv; 28 | import yield.parser.PositionManager.LinkedPosition; 29 | 30 | class YieldSplitterOptimizer { 31 | 32 | public static function optimizeAll (ys:YieldSplitter, pm:PositionManager, env:WorkEnv, pos:Position): Void { 33 | 34 | #if (yield_debug_no_display || !display && !yield_debug_display) 35 | 36 | var pass:Int = 0; 37 | var subpass:Int = 0; 38 | var needRepass:Bool = true; 39 | 40 | while (needRepass && ++pass < 10) { 41 | 42 | needRepass = false; 43 | subpass = 0; 44 | 45 | while (removeUselessSetActions(ys, pm, env, pos) && ++subpass < 10) 46 | needRepass = true; 47 | while (removeSuperfluousSetActionCalls(ys, pm, env, pos) && ++subpass < 20) 48 | needRepass = true; 49 | while (removeSuperfluousGotoActionCalls(ys, pm, env, pos) && ++subpass < 30) 50 | needRepass = true; 51 | } 52 | 53 | #if yield_debug 54 | if (env.debug) { 55 | trace('"${env.functionName}" optimazed in $pass pass'); 56 | } 57 | #end 58 | 59 | #end 60 | } 61 | 62 | public static function removeUselessSetActions (ys:YieldSplitter, pm:PositionManager, env:WorkEnv, pos:Position): Bool { 63 | 64 | var optimized:Bool = false; 65 | 66 | var setsToRemove:Array = []; 67 | 68 | for (iteratorBlock in ys.iteratorBlocks) { 69 | 70 | var lastSetAction:LinkedPosition; 71 | 72 | for (lexpr in iteratorBlock) { 73 | 74 | var isActionExpr:Bool = false; 75 | 76 | for (lset in env.setActions) { 77 | if (lexpr == lset.e) { 78 | 79 | if (lastSetAction != null) 80 | setsToRemove.push( lset ); 81 | lastSetAction = lset; 82 | 83 | isActionExpr = true; 84 | break; 85 | } 86 | } 87 | 88 | if (isActionExpr) continue; 89 | 90 | for (lgoto in env.gotoActions) { 91 | if (lexpr == lgoto.e) { 92 | if (lastSetAction != null) 93 | setsToRemove.push( lastSetAction ); 94 | break; 95 | } 96 | } 97 | 98 | break; 99 | } 100 | 101 | var i:Int = setsToRemove.length; 102 | while (--i != -1) { 103 | 104 | iteratorBlock.remove(setsToRemove[i].e); 105 | env.setActions.remove(setsToRemove[i]); 106 | 107 | setsToRemove.splice(i, 1); 108 | optimized = true; 109 | } 110 | } 111 | 112 | return optimized; 113 | } 114 | 115 | public static function removeSuperfluousSetActionCalls (ys:YieldSplitter, pm:PositionManager, env:WorkEnv, pos:Position): Bool { 116 | 117 | var optimized:Bool = false; 118 | 119 | for (aSet in env.setActions) { 120 | 121 | if (aSet.pos >= ys.iteratorBlocks.length) continue; 122 | 123 | for (lexpr in ys.iteratorBlocks[aSet.pos]) { 124 | 125 | var isAnotherExpr:Bool = true; 126 | 127 | for (lset in env.setActions) { 128 | if (lexpr == lset.e) { 129 | isAnotherExpr = false; 130 | break; 131 | } 132 | } 133 | if (!isAnotherExpr) continue; 134 | 135 | for (lgoto in env.gotoActions) { 136 | if (lexpr == lgoto.e) { 137 | 138 | pm.moveLinkedPosition(aSet, lgoto.pos); 139 | optimized = true; 140 | 141 | isAnotherExpr = false; 142 | break; 143 | } 144 | } 145 | 146 | if (isAnotherExpr) break; 147 | } 148 | } 149 | 150 | return optimized; 151 | } 152 | 153 | public static function removeSuperfluousGotoActionCalls (ys:YieldSplitter, pm:PositionManager, env:WorkEnv, pos:Position): Bool { 154 | 155 | var optimized:Bool = false; 156 | 157 | var uncrossedPositions:Array = []; 158 | 159 | for (aGoto in env.gotoActions) { 160 | 161 | if (aGoto.pos >= ys.iteratorBlocks.length) continue; 162 | 163 | for (lexpr in ys.iteratorBlocks[aGoto.pos]) { 164 | 165 | var isAnotherExpr:Bool = true; 166 | 167 | for (lgoto in env.gotoActions) { 168 | if (lexpr == lgoto.e && aGoto.pos != lgoto.pos) { 169 | 170 | if (uncrossedPositions.indexOf(aGoto.pos) == -1) 171 | uncrossedPositions.push(aGoto.pos); 172 | 173 | pm.moveLinkedPosition(aGoto, lgoto.pos); 174 | optimized = true; 175 | 176 | isAnotherExpr = false; 177 | break; 178 | } 179 | } 180 | 181 | if (isAnotherExpr) break; 182 | } 183 | } 184 | 185 | var i:Int = uncrossedPositions.length; 186 | 187 | uncrossedPositions.sort(function(a, b) { 188 | if (a < b) return -1; 189 | else if (b < a) return 1; 190 | else return 0; 191 | }); 192 | 193 | while (--i != -1) { 194 | ys.spliceIteratorBlocks(uncrossedPositions[i], 1); 195 | } 196 | 197 | return optimized; 198 | } 199 | 200 | } 201 | #end -------------------------------------------------------------------------------- /tests/eparsers/EIfTests.hx: -------------------------------------------------------------------------------- 1 | package eparsers; 2 | 3 | import utest.Assert; 4 | 5 | @:yield 6 | class EIfTests extends utest.Test { 7 | 8 | function testSimpleIf () { 9 | var it = simpleIf(); 10 | 11 | Assert.equals(1, it.next()); 12 | } 13 | 14 | function simpleIf ():Iterator { 15 | 16 | var a = 0; 17 | var condition = true; 18 | 19 | if (condition) { 20 | a = 1; 21 | } else { 22 | a = 2; 23 | } 24 | 25 | @yield return a; 26 | } 27 | 28 | function testYieldIf () { 29 | var it = yieldIf(); 30 | 31 | Assert.equals(1, it.next()); 32 | Assert.isTrue(it.hasNext()); 33 | Assert.equals(2, it.next()); 34 | Assert.isFalse(it.hasNext()); 35 | } 36 | 37 | function yieldIf ():Iterator { 38 | 39 | var a = 0; 40 | var condition = true; 41 | 42 | if (condition) { 43 | a = 1; 44 | @yield return a; 45 | a = 2; 46 | } else { 47 | 48 | } 49 | 50 | @yield return a; 51 | } 52 | 53 | function testYieldIf2 () { 54 | var it = yieldIf2(); 55 | 56 | Assert.equals(1, it.next()); 57 | Assert.isTrue(it.hasNext()); 58 | Assert.equals(2, it.next()); 59 | Assert.isFalse(it.hasNext()); 60 | } 61 | 62 | function yieldIf2 ():Iterator { 63 | 64 | var a = 0; 65 | var condition = true; 66 | 67 | if (condition) { 68 | a = 1; 69 | @yield return a; 70 | a = 2; 71 | } else { 72 | 73 | } 74 | 75 | @yield return a; 76 | } 77 | 78 | function testYieldIfElseTrue () { 79 | var it = yieldIfElseTrue(); 80 | 81 | Assert.equals(1, it.next()); 82 | Assert.isTrue(it.hasNext()); 83 | Assert.equals(2, it.next()); 84 | Assert.isFalse(it.hasNext()); 85 | 86 | // Part 2 87 | it = yieldIfElseFalse(); 88 | 89 | Assert.equals(3, it.next()); 90 | Assert.isTrue(it.hasNext()); 91 | Assert.equals(4, it.next()); 92 | Assert.isTrue(it.hasNext()); 93 | Assert.equals(5, it.next()); 94 | Assert.isFalse(it.hasNext()); 95 | } 96 | 97 | function yieldIfElseTrue ():Iterator { 98 | 99 | var a = 0; 100 | var condition = true; 101 | 102 | if (condition) { 103 | a = 1; 104 | @yield return a; 105 | a = 2; 106 | } else { 107 | 108 | @yield return 3; 109 | @yield return 4; 110 | } 111 | 112 | @yield return a; 113 | } 114 | 115 | function yieldIfElseFalse ():Iterator { 116 | 117 | var a = 0; 118 | var condition = false; 119 | 120 | if (condition) { 121 | a = 1; 122 | @yield return a; 123 | a = 2; 124 | } else { 125 | 126 | @yield return 3; 127 | @yield return 4; 128 | a = 5; 129 | } 130 | 131 | @yield return a; 132 | } 133 | 134 | function testYieldElse () { 135 | var it = yieldElse(); 136 | 137 | Assert.equals(4, it.next()); 138 | Assert.isTrue(it.hasNext()); 139 | Assert.equals(3, it.next()); 140 | Assert.isFalse(it.hasNext()); 141 | } 142 | 143 | function yieldElse ():Iterator { 144 | 145 | var a = 0; 146 | var condition = true; 147 | 148 | if (!condition) { 149 | 150 | } else { 151 | @yield return 4; 152 | a = 3; 153 | } 154 | 155 | @yield return a; 156 | } 157 | 158 | function testYieldElse2 () { 159 | var it = yieldElse2(); 160 | 161 | Assert.equals(0, it.next()); 162 | Assert.isFalse(it.hasNext()); 163 | } 164 | 165 | function yieldElse2 ():Iterator { 166 | 167 | var a = 0; 168 | var condition = true; 169 | 170 | if (!condition) { 171 | @yield return 20; 172 | } else { 173 | 174 | } 175 | 176 | @yield return a; 177 | } 178 | 179 | function testReturnedValue () { 180 | 181 | var it = returnedValue(true); 182 | Assert.isTrue(it.hasNext()); 183 | Assert.equals(1, it.next()); 184 | Assert.equals(3, it.next()); 185 | Assert.isFalse(it.hasNext()); 186 | 187 | var it = returnedValue(false); 188 | Assert.isTrue(it.hasNext()); 189 | Assert.equals(2, it.next()); 190 | Assert.equals(4, it.next()); 191 | Assert.isFalse(it.hasNext()); 192 | } 193 | 194 | function returnedValue (condition:Bool) { 195 | 196 | var a:Int = if (condition) 1 else 2; 197 | @yield return a; 198 | @yield return if (condition) 3 else 4; 199 | } 200 | 201 | function testIteratorReturnedValue () { 202 | 203 | var it = iteratorReturnedValue(true); 204 | Assert.isTrue(it.hasNext()); 205 | 206 | var it1 = it.next()(); 207 | Assert.equals(1, it1.next()); 208 | Assert.equals(3, it1.next()); 209 | 210 | var it2 = it.next()(); 211 | Assert.equals(6, it2.next()); 212 | Assert.equals(12, it2.next()); 213 | Assert.isFalse(it.hasNext()); 214 | 215 | 216 | 217 | var it = iteratorReturnedValue(false); 218 | Assert.isTrue(it.hasNext()); 219 | 220 | var it1 = it.next()(); 221 | Assert.equals(2, it1.next()); 222 | Assert.equals(4, it1.next()); 223 | 224 | var it2 = it.next()(); 225 | Assert.equals(8, it2.next()); 226 | Assert.equals(16, it2.next()); 227 | Assert.isFalse(it.hasNext()); 228 | } 229 | 230 | function iteratorReturnedValue (condition:Bool) { 231 | 232 | var a:Void->Iterator = if (condition) { 233 | function () { 234 | @yield return 1; 235 | @yield return 3; 236 | }; 237 | } else { 238 | function () { 239 | @yield return 2; 240 | @yield return 4; 241 | }; 242 | }; 243 | 244 | @yield return a; 245 | 246 | @yield return if (condition) { 247 | function () { 248 | @yield return 6; 249 | @yield return 12; 250 | }; 251 | } else { 252 | function () { 253 | @yield return 8; 254 | @yield return 16; 255 | }; 256 | }; 257 | } 258 | 259 | function testInitialization () { 260 | var it = initialization(2); 261 | Assert.isTrue(it.hasNext()); 262 | Assert.equals(2, it.next()); 263 | Assert.isFalse(it.hasNext()); 264 | } 265 | 266 | function initialization (a:Int) { 267 | 268 | var v:Int; 269 | 270 | if (a == 0) { 271 | v = 0; 272 | } else if (a == 1) { 273 | throw null; 274 | } else if (a == 2) { 275 | v = 2; 276 | } else { 277 | v = 3; 278 | } 279 | 280 | @yield return v; 281 | } 282 | 283 | } -------------------------------------------------------------------------------- /yield/parser/tools/FieldTools.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.tools; 26 | import haxe.macro.ComplexTypeTools; 27 | import haxe.macro.ExprTools; 28 | import haxe.macro.Compiler; 29 | import haxe.macro.Context; 30 | import haxe.macro.Expr; 31 | import haxe.macro.Expr.Access; 32 | import haxe.macro.Expr.Field; 33 | import haxe.macro.Type; 34 | import haxe.macro.Type.ClassField; 35 | import haxe.macro.Type.ClassType; 36 | import haxe.macro.TypeTools; 37 | import yield.parser.checks.TypeInferencer; 38 | 39 | class FieldTools { 40 | 41 | public static function makeFieldFromVar (v:Var, access:Array, initialValue:Null, pos:Position): Field { 42 | return { 43 | name : v.name, 44 | doc : null, 45 | access : access, 46 | kind : FVar(v.type, initialValue), 47 | pos : pos, 48 | meta : null 49 | }; 50 | } 51 | 52 | public static function makeField (name:String, access:Array, initialValue:Expr, pos:Position): Field { 53 | return { 54 | name : name, 55 | doc : null, 56 | access : access, 57 | kind : FVar(null, initialValue), 58 | pos : pos, 59 | meta : null 60 | }; 61 | } 62 | 63 | public static function makeFunctionField (name:String, access:Array, f:Function, pos:Position): Field { 64 | return { 65 | name : name, 66 | doc : null, 67 | access : access, 68 | kind : FFun(f), 69 | pos : pos, 70 | meta : null 71 | }; 72 | } 73 | 74 | public static function makeFieldAssignation (fieldName:String, value:Expr): Expr { 75 | return { 76 | expr: EBinop( 77 | OpAssign, 78 | { expr : EField( {expr:EConst(CIdent("this")), pos:value.pos} , fieldName), pos : value.pos}, 79 | value 80 | ), 81 | pos : value.pos 82 | } 83 | } 84 | 85 | /** 86 | * Determine the nature of the identifier `e`. 87 | */ 88 | public static function resolveIdent (s:String, classData:yield.parser.env.ClassData): IdentCategory { 89 | 90 | var lcat:Null; 91 | 92 | // Members 93 | 94 | lcat = getInstanceField(classData.classFields, classData.localClass, s); 95 | if (lcat != null) return lcat; 96 | 97 | // Statics 98 | 99 | lcat = getStaticField(classData.classFields, classData.localClass, s); 100 | if (lcat != null) return lcat; 101 | 102 | // Imported fields 103 | 104 | lcat = getImportedField(classData.importedFields, s); 105 | if (lcat != null) return lcat; 106 | 107 | // Used fields 108 | 109 | lcat = getUsedField(classData.usings, s); 110 | if (lcat != null) return lcat; 111 | 112 | return IdentCategory.Unknown; 113 | } 114 | 115 | private static function getInstanceField (classFields:Array, classType:ClassType, name:String): Null { 116 | 117 | for (i in 0...classFields.length) 118 | if (classFields[i].name == name && (classFields[i].access == null || classFields[i].access.indexOf(Access.AStatic) == -1)) 119 | return IdentCategory.InstanceField(TypeInferencer.tryInferField(classFields[i].kind)); 120 | 121 | if (classType.superClass != null) { 122 | var cf:Null = TypeTools.findField( classType.superClass.t.get(), name, false ); 123 | if (cf != null) return IdentCategory.InstanceField(null); 124 | } 125 | 126 | return null; 127 | } 128 | 129 | private static function getStaticField (classFields:Array, classType:ClassType, name:String): Null { 130 | 131 | for (i in 0...classFields.length) 132 | if (classFields[i].name == name && classFields[i].access != null && classFields[i].access.indexOf(Access.AStatic) != -1) 133 | return IdentCategory.InstanceStaticField(TypeInferencer.tryInferField(classFields[i].kind), classType); 134 | 135 | return null; 136 | } 137 | 138 | private static inline function getImportedField (importedFields:Array, name:String): Null { 139 | 140 | var i:Int = importedFields.indexOf(name); 141 | return i != -1 ? IdentCategory.ImportedField(null) : null; 142 | } 143 | 144 | private static function getUsedField (usings:Array>, name:String): Null { 145 | 146 | for (ref in usings) { 147 | var ct = ref.get(); 148 | for (field in ct.statics.get()) { 149 | if (field.name == name) { 150 | return IdentCategory.ImportedField(null); 151 | } 152 | } 153 | } 154 | 155 | return null; 156 | } 157 | 158 | public static function toString (field:Field): String { 159 | 160 | return switch (field.kind) { 161 | 162 | case FieldType.FFun(_f): 163 | 164 | "function " + field.name + " (" + [ for (arg in _f.args) (arg.opt ? "?" : "") + arg.name + (arg.type == null ? "" : ":" + ComplexTypeTools.toString(arg.type)) + (arg.value == null ? "" : " = " + ExprTools.toString(arg.value)) ].join(", ") + ") " + ExprTools.toString(_f.expr); 165 | 166 | case FieldType.FVar(_t, _e): 167 | 168 | "var " + field.name + (_t == null ? "" : ":" + ComplexTypeTools.toString(_t)) + (_e == null ? "" : " = " + ExprTools.toString(_e)) + ";"; 169 | 170 | case FieldType.FProp(_get, _set, _t, _e): 171 | 172 | "var " + field.name + " (" + _get + ", " + _set + ")" + (_t == null ? "" : ":" + ComplexTypeTools.toString(_t)) + (_e == null ? "" : " = " + ExprTools.toString(_e)) + ";"; 173 | } 174 | } 175 | 176 | } 177 | #end -------------------------------------------------------------------------------- /yield/parser/eparsers/EForParser.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.eparsers; 26 | import yield.parser.idents.IdentOption; 27 | import yield.parser.eactions.Action; 28 | import haxe.macro.Context; 29 | import haxe.macro.Expr; 30 | import haxe.macro.ExprTools; 31 | import yield.parser.eactions.ActionParser; 32 | import yield.parser.idents.IdentChannel; 33 | import yield.parser.tools.ExpressionTools; 34 | import yield.parser.tools.MetaTools; 35 | import yield.parser.tools.MetaTools.MetaToolsOption; 36 | 37 | class EForParser extends BaseParser { 38 | 39 | public function run (e:Expr, subParsing:Bool, _it:Expr, _expr:Expr): Void { 40 | 41 | var loopExprs:Array = ExpressionTools.checkIsInEBlock(_expr); 42 | 43 | MetaTools.option = MetaToolsOption.SkipNestedFunctions; 44 | 45 | if (#if (yield_debug_no_display || !display && !yield_debug_display) MetaTools.hasMetaExpr(m_we.yieldKeyword, _expr, true) #else false #end) { 46 | runSplitMode(e, subParsing, _it, _expr, loopExprs); 47 | } else { 48 | runPreserveMode(e, subParsing, _it, _expr, loopExprs); 49 | } 50 | } 51 | 52 | private function runSplitMode (e:Expr, subParsing:Bool, _it:Expr, _expr:Expr, loopExprs:Array): Void { 53 | 54 | var ewhileEcond:Expr = {expr: null, pos:e.pos}; 55 | var ewhileE:Expr = {expr: _expr.expr, pos:e.pos}; 56 | var ewhile:Expr = {expr: EWhile(ewhileEcond,ewhileE, true), pos:e.pos}; 57 | 58 | var updateExprs:Array = new Array(); 59 | 60 | switch (_it.expr) { 61 | #if (haxe_ver < 4.000) 62 | case EIn(__e1, __e2): 63 | #else 64 | case EBinop(OpIn, __e1, __e2): 65 | #end 66 | 67 | var opIdentName:String = ExpressionTools.getConstIdent(__e1); 68 | var localVar:Expr = __e1; 69 | 70 | var opIdent:Expr = { 71 | expr: EConst(CIdent(opIdentName)), 72 | pos: e.pos 73 | }; 74 | 75 | ActionParser.addActionToExpr([Action.DefineChannel(IdentChannel.IterationOp), Action.DefineOptions([IdentOption.ReadOnly], IdentChannel.IterationOp)], opIdent, m_we); 76 | 77 | switch (__e2.expr) { 78 | case EBinop(___op, ___e1, ___e2): 79 | 80 | switch (___op) { 81 | case OpInterval: // x...n 82 | 83 | // initialization 84 | 85 | var opVars:Array = [{ 86 | name: opIdentName, 87 | type: macro:StdTypes.Int, 88 | expr: ___e1 89 | }]; 90 | 91 | m_ys.evarsParser.run({expr:EVars(opVars), pos:e.pos}, false, opVars, IdentChannel.IterationOp); 92 | 93 | // loop 94 | 95 | ewhileEcond.expr = EBinop(OpLt, opIdent, ___e2); 96 | 97 | updateExprs.push({ 98 | expr: EVars([{ 99 | name: opIdentName, 100 | type: macro:StdTypes.Int, 101 | expr: {expr:EUnop(OpIncrement, true, opIdent), pos:e.pos} 102 | }]), 103 | pos: __e1.pos 104 | }); 105 | 106 | default: 107 | } 108 | 109 | default: 110 | 111 | // initialization 112 | 113 | var getIteratorField = macro @:pos(__e2.pos) yield.macrotime.Tools.getIterator; 114 | 115 | var lexprOp:Expr = { 116 | expr: EConst(CIdent("null")), 117 | pos:__e2.pos 118 | }; 119 | 120 | var opVars:Array = [{ 121 | name: opIdentName, 122 | type: macro:StdTypes.Iterator, 123 | expr: lexprOp 124 | }]; 125 | 126 | m_ys.evarsParser.run({expr:EVars(opVars), pos:e.pos}, false, opVars, IdentChannel.IterationOp); 127 | 128 | m_ys.parseOut(__e2, true); 129 | lexprOp.expr = ECall(getIteratorField, [__e2]); // Note that it is not parsed 130 | 131 | // loop 132 | 133 | ewhileEcond.expr = ECall({expr:EField(opIdent, "hasNext"), pos: _it.pos}, []); 134 | 135 | updateExprs.push({ 136 | expr: EVars([{ 137 | name: opIdentName, 138 | type: macro:StdTypes.Dynamic, 139 | expr: {expr:ECall({expr:EField(opIdent, "next"),pos:__e1.pos}, []), pos:__e1.pos} 140 | }]), 141 | pos: __e1.pos 142 | }); 143 | } 144 | 145 | default: 146 | } 147 | 148 | for (v in updateExprs) { 149 | ActionParser.addActionToExpr([Action.DefineOptions([IsVarLoop, ReadOnly])], v, m_we); 150 | loopExprs.unshift(v); 151 | } 152 | 153 | e.expr = ewhile.expr; 154 | m_ys.ewhileParser.run(ewhile, subParsing, ewhileEcond, ewhileE, true, true); 155 | } 156 | 157 | private function runPreserveMode (e:Expr, subParsing:Bool, _it:Expr, _expr:Expr, loopExprs:Array): Void { 158 | 159 | switch (_it.expr) { 160 | #if (haxe_ver < 4.000) 161 | case EIn(__e1, __e2): 162 | #else 163 | case EBinop(OpIn, __e1, __e2): 164 | #end 165 | 166 | m_ys.parseOut(__e2, true); 167 | 168 | var ltypeE1:ComplexType = switch (__e2.expr) { 169 | case ExprDef.EBinop(___op, ___e1, ___e2): 170 | 171 | if (___op == Binop.OpInterval) 172 | macro:StdTypes.Int; 173 | else 174 | macro:StdTypes.Dynamic; 175 | 176 | default: 177 | macro:StdTypes.Dynamic; 178 | } 179 | 180 | m_ys.eblockParser.run(_expr, true, loopExprs, true, null, [{ ref: IdentRef.IEConst(__e1), type: ltypeE1 }]); 181 | 182 | if (!subParsing) m_ys.addIntoBlock(e); 183 | 184 | default: 185 | } 186 | } 187 | 188 | } 189 | #end -------------------------------------------------------------------------------- /yield/parser/eparsers/ESwitchParser.hx: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License 3 | * 4 | * Copyright (C)2023 Dimitri Pomier 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the "Software"), 8 | * to deal in the Software without restriction, including without limitation 9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 | * and/or sell copies of the Software, and to permit persons to whom the 11 | * Software is furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 | * DEALINGS IN THE SOFTWARE. 23 | */ 24 | #if (macro || display) 25 | package yield.parser.eparsers; 26 | import haxe.macro.Context; 27 | import haxe.macro.Expr; 28 | import yield.parser.env.WorkEnv; 29 | import yield.parser.env.WorkEnv.Scope; 30 | import yield.parser.idents.IdentRef.IdentRefTyped; 31 | import yield.parser.tools.ExpressionTools; 32 | import yield.parser.tools.MetaTools; 33 | 34 | class ESwitchParser extends BaseParser { 35 | 36 | public function run (e:Expr, subParsing:Bool, _e:Expr, _cases:Array, _edef:Null): Void { 37 | 38 | // Parse subject 39 | 40 | m_ys.parseOut(_e, true, "Bool"); 41 | if (!subParsing) m_ys.addIntoBlock(e); 42 | 43 | // Temporary shift the iterator position 44 | 45 | var posInitial:UInt = m_ys.cursor; 46 | 47 | m_ys.moveCursor(); 48 | 49 | // Parse cases 50 | 51 | var yieldedCases:Bool = false; 52 | 53 | var lastCaseScope:Scope = null; 54 | 55 | var lgotoAfterSwitch:Expr = { expr: EConst(CIdent("null")), pos: _e.pos }; 56 | 57 | for (lcase in _cases) { 58 | 59 | if (lcase.guard != null) m_ys.parseOut(lcase.guard, true, "Bool"); 60 | 61 | if (lcase.expr == null) { 62 | lastCaseScope = m_we.openScope(true, lastCaseScope); 63 | lastCaseScope.defaultCondition = true; // switch may cover all possibilities without having a default case defined 64 | m_we.closeScope(); 65 | continue; 66 | } 67 | 68 | var lexprs:Array = ExpressionTools.checkIsInEBlock(lcase.expr); 69 | 70 | var predefineNativeVars:Array = []; 71 | 72 | // initialize params as variables (only if the case is yielded) 73 | if (lcase.values.length != 0) { 74 | 75 | var paramNames:Array = []; 76 | 77 | if (lcase.values.length != 0) 78 | for (v in getParamsFromCase(lcase.values[0], m_we)) 79 | paramNames.push(v); 80 | 81 | MetaTools.option = MetaToolsOption.SkipNestedFunctions; 82 | 83 | if (MetaTools.hasMetaExpr(m_we.yieldKeyword, lcase.expr)) { 84 | for (econst in paramNames) { 85 | 86 | predefineNativeVars.push({ ref: IdentRef.IEConst(econst), type: macro:StdTypes.Dynamic }); 87 | 88 | var initValue:Expr = { expr: econst.expr, pos: econst.pos }; 89 | var v:Var = { name: ExpressionTools.getConstIdent(econst), type: macro:StdTypes.Dynamic, expr: initValue }; 90 | var evars:Expr = { expr: EVars([v]), pos: econst.pos }; 91 | 92 | lexprs.unshift(evars); 93 | } 94 | } else { 95 | for (econst in paramNames) { 96 | predefineNativeVars.push({ ref: IdentRef.IEConst(econst), type: macro:StdTypes.Dynamic }); 97 | } 98 | } 99 | } 100 | 101 | var posInitialCase:UInt = m_ys.cursor; 102 | lastCaseScope = m_ys.eblockParser.run(lcase.expr, false, lexprs, true, lastCaseScope, predefineNativeVars); 103 | lastCaseScope.defaultCondition = true; // switch may cover all possibilities without having a default case defined 104 | 105 | if (m_ys.cursor == posInitialCase) { 106 | 107 | m_ys.iteratorBlocks[m_ys.cursor] = []; 108 | 109 | } else { 110 | 111 | yieldedCases = true; 112 | 113 | var posFirstSub:UInt = posInitialCase; 114 | 115 | var lnewExprs:Array = m_ys.iteratorBlocks[posFirstSub]; 116 | 117 | // Goto first sub-expression 118 | #if (yield_debug_no_display || !display && !yield_debug_display) 119 | var ldefineNextSubExpr:Expr = { expr: null, pos: _e.pos }; 120 | m_ys.registerSetAction(ldefineNextSubExpr, posFirstSub+1); 121 | lnewExprs.unshift(ldefineNextSubExpr); 122 | 123 | lcase.expr = { expr: EBlock( lnewExprs ), pos: lcase.expr.pos }; 124 | m_ys.spliceIteratorBlocks(posFirstSub, 1); 125 | #end 126 | 127 | // Goto after the switch 128 | m_ys.addIntoBlock(lgotoAfterSwitch); 129 | 130 | m_ys.moveCursor(); 131 | 132 | } 133 | 134 | } 135 | 136 | if (!yieldedCases) { 137 | 138 | m_ys.spliceIteratorBlocks(m_ys.cursor, 1); 139 | 140 | } else { 141 | 142 | #if (yield_debug_no_display || !display && !yield_debug_display) 143 | if (subParsing) Context.fatalError("Missing return value", e.pos); 144 | 145 | for (lcase in _cases) { 146 | if (lcase.expr == null) lcase.expr = lgotoAfterSwitch; 147 | else ExpressionTools.checkIsInEBlock(lcase.expr).push(lgotoAfterSwitch); // For non-yielded cases 148 | } 149 | 150 | m_ys.addIntoBlock(lgotoAfterSwitch, posInitial); 151 | 152 | m_ys.registerGotoAction(lgotoAfterSwitch, m_ys.cursor); 153 | #end 154 | } 155 | 156 | // Parse default 157 | 158 | if (_edef != null && _edef.expr != null) { 159 | var s = m_ys.eblockParser.runFromExpr(_edef, true, true, lastCaseScope); 160 | s.defaultCondition = true; 161 | } 162 | } 163 | 164 | private static function getParamsFromCase (value:Expr, env:WorkEnv): Array { 165 | 166 | var params:Array = []; 167 | 168 | switch (value.expr) { 169 | 170 | case ECall(_e, _params): 171 | for (larg in _params) 172 | for (p in getParamsFromCase(larg, env)) 173 | params.push(p); 174 | 175 | case EBinop(_op, _e1, _e2) if (_op == Binop.OpArrow): 176 | for (p in getParamsFromCase(_e2, env)) 177 | params.push(p); 178 | 179 | case EConst(_c): 180 | switch (_c) { 181 | case CIdent(_s): 182 | 183 | for (enumType in env.classData.importedEnums) { 184 | for (construct in enumType.constructs.keys()) { 185 | if (_s == construct) { 186 | return params; 187 | } 188 | } 189 | } 190 | params.push(value); 191 | 192 | default: 193 | } 194 | 195 | default: 196 | } 197 | return params; 198 | } 199 | 200 | } 201 | #end -------------------------------------------------------------------------------- /tests/misc/AccessionTests.hx: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | import utest.Assert; 4 | import misc.packs.Parent; 5 | 6 | @:yield 7 | class AccessionTests extends misc.packs.Parent { 8 | 9 | public function new() 10 | { 11 | super(); 12 | } 13 | 14 | function testStaticVar () { 15 | var it = staticVar(); 16 | staticVarField = false; 17 | Assert.isTrue(it.hasNext()); 18 | Assert.isFalse(staticVarField); 19 | Assert.equals(0, it.next()); 20 | Assert.isFalse(staticVarField); 21 | Assert.isFalse(it.hasNext()); 22 | Assert.isTrue(staticVarField); 23 | } 24 | 25 | static var staticVarField:Bool = false; 26 | 27 | function staticVar () { 28 | @yield return 0; 29 | staticVarField = true; 30 | } 31 | 32 | function testParentStaticVar () { 33 | var it = parentStaticVar(); 34 | Parent.privateStatic = false; 35 | Parent.publicStatic = false; 36 | Assert.isTrue(it.hasNext()); 37 | Assert.isFalse(Parent.privateStatic); 38 | Assert.isFalse(Parent.publicStatic); 39 | Assert.equals(0, it.next()); 40 | Assert.isFalse(Parent.privateStatic); 41 | Assert.isFalse(Parent.publicStatic); 42 | Assert.isFalse(it.hasNext()); 43 | Assert.isTrue(Parent.privateStatic); 44 | Assert.isTrue(Parent.publicStatic); 45 | Parent.reset(); 46 | } 47 | 48 | function parentStaticVar () { 49 | @yield return 0; 50 | Parent.privateStatic = true; 51 | Parent.publicStatic = true; 52 | } 53 | 54 | var member:Int; 55 | 56 | function testInstanceAccess () { 57 | 58 | this.member = -4; 59 | 60 | var it:Iterator = cast instanceAccess(); 61 | 62 | Assert.equals(-4, it.next()); 63 | Assert.equals(-4, it.next()); 64 | Assert.equals(-8, it.next()); 65 | Assert.equals(member, -8); 66 | 67 | Assert.equals(-8, it.next()); 68 | Assert.equals(10, it.next()); 69 | } 70 | 71 | function instanceAccess (): Iterator { 72 | 73 | @yield return this.member; 74 | @yield return member; 75 | member = -8; 76 | @yield return this.member; 77 | 78 | var member = 10; 79 | @yield return this.member; 80 | @yield return member; 81 | } 82 | 83 | function testParentMember () { 84 | var it = parentMember(); 85 | privateMember = false; 86 | publicMember = false; 87 | Assert.isTrue(it.hasNext()); 88 | Assert.isFalse(privateMember); 89 | Assert.isFalse(publicMember); 90 | Assert.equals(0, it.next()); 91 | Assert.isFalse(privateMember); 92 | Assert.isFalse(publicMember); 93 | Assert.isFalse(it.hasNext()); 94 | Assert.isTrue(privateMember); 95 | Assert.isTrue(publicMember); 96 | } 97 | 98 | function parentMember () { 99 | @yield return 0; 100 | privateMember = true; 101 | publicMember = true; 102 | } 103 | 104 | function testNestedAccess () { 105 | 106 | this.member = 20; 107 | 108 | var it:Iterator = cast nestedAccess(); 109 | 110 | Assert.equals(3, it.next()); 111 | Assert.equals(3, it.next()); 112 | Assert.equals(12, it.next()); 113 | Assert.equals(2, it.next()); 114 | Assert.equals(member, 20); 115 | } 116 | 117 | function nestedAccess (): Iterator { 118 | 119 | var a = 0; 120 | 121 | function b () { 122 | a = 3; 123 | @yield return a; 124 | @yield return a * 2; 125 | var a = 1; 126 | @yield return a*2; 127 | @yield return member; 128 | } 129 | 130 | var bIterator:Iterator = cast b(); 131 | 132 | @yield return bIterator.next(); 133 | @yield return a; 134 | a = 6; 135 | @yield return bIterator.next(); 136 | @yield return bIterator.next(); 137 | @yield return bIterator.next(); 138 | } 139 | 140 | #if (!cs && !java) // error: repeated modifier 141 | #if !lua // error Lua 5.2.3 / luarocks 2.4.3: attempt to call global '_iterator' (a nil value) 142 | function testAbstractAccessions () { 143 | var a = new MyAbstract(); 144 | Assert.isTrue(a.test()); 145 | } 146 | #end 147 | #end 148 | 149 | #if (interp && haxe_ver < 4.000) 150 | function testUntyped () { 151 | var it = untypedFunc(); 152 | Assert.isFalse(it.hasNext()); 153 | 154 | var it = untypedNestedFunc(); 155 | Assert.isFalse(it.next()); 156 | } 157 | 158 | function untypedFunc () untyped { 159 | 160 | try { 161 | unknown(); // should compile 162 | } catch (e:Dynamic) { } 163 | 164 | @yield break; 165 | } 166 | 167 | function untypedNestedFunc () { 168 | 169 | untyped function subEnv () { 170 | 171 | try { 172 | unknown(); // should compile 173 | } catch (e:Dynamic) { } 174 | 175 | @yield break; 176 | } 177 | 178 | @yield return subEnv().hasNext(); 179 | } 180 | 181 | function testUnknownIdent () { 182 | var it = unknownIdent(); 183 | Assert.isFalse(it.hasNext()); 184 | } 185 | 186 | function unknownIdent () { 187 | 188 | try { 189 | untyped unknown(); // should compile 190 | } catch (e:Dynamic) { } 191 | 192 | untyped try { 193 | unknown(); // should compile 194 | } catch (e:Dynamic) { } 195 | 196 | // unknown(); // TODO: test compilation error 197 | 198 | @yield break; 199 | } 200 | #end 201 | 202 | #if (haxe_ver >= 4.000) 203 | 204 | function testFinalAccess () { 205 | var it = finalAccess(); 206 | Assert.equals(1, it.next()); 207 | Assert.equals(2, it.next()); 208 | Assert.equals(3, it.next()); 209 | Assert.equals(4, it.next()); 210 | Assert.equals(5, it.next()); 211 | Assert.equals(6, it.next()); 212 | Assert.equals(7, it.next()); 213 | Assert.isFalse(it.hasNext()); 214 | } 215 | 216 | function finalAccess ():Iterator { 217 | 218 | final a = 1; 219 | @yield return a; 220 | // a = 2; // TODO: test compilation error 221 | 222 | final o = { 223 | v: 2 224 | }; 225 | @yield return o.v; 226 | o.v = 3; 227 | @yield return o.v; 228 | // o = 4; // TODO: test compilation error 229 | 230 | var o2:{ final v:Int; } = { 231 | v: 4 232 | }; 233 | @yield return o2.v; 234 | // o2.v = 5; // TODO: test compilation error 235 | 236 | for (i in 5...8) { 237 | final v = i; 238 | @yield return v; 239 | } 240 | } 241 | 242 | #end 243 | } 244 | 245 | #if (!cs && !java) // error: repeated modifier 246 | #if !lua // error Lua 5.2.3 / luarocks 2.4.3: attempt to call global '_iterator' (a nil value) 247 | @:build(yield.parser.Parser.run()) 248 | @:access(misc.packs.Parent) 249 | abstract MyAbstract (Parent) { 250 | public inline function new() { 251 | this = new Parent(); 252 | } 253 | 254 | public function test (): Bool { 255 | this.publicMember = false; 256 | this.privateMember = false; 257 | untyped Parent.privateStatic = false; 258 | Parent.publicStatic = false; 259 | 260 | var it = iterator(); 261 | return 262 | it.hasNext() 263 | && !this.privateMember && !this.publicMember && !Parent.privateStatic && !Parent.publicStatic 264 | && it.next() == null 265 | && !this.privateMember && !this.publicMember && !Parent.privateStatic && !Parent.publicStatic 266 | && !it.hasNext() 267 | && this.privateMember && this.publicMember && Parent.privateStatic && Parent.publicStatic; 268 | } 269 | 270 | function iterator () { 271 | @yield return null; 272 | this.privateMember = true; 273 | this.publicMember = true; 274 | Parent.privateStatic = true; 275 | Parent.publicStatic = true; 276 | } 277 | } 278 | #end 279 | #end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | yield 2 | ======= 3 | [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE.md) 4 | [![GitHub CI Status](https://github.com/dpomier/haxe-yield/actions/workflows/build.yml/badge.svg)](https://github.com/dpomier/haxe-yield/actions/workflows/build.yml) 5 | 6 | *Support from Haxe `3.4` to `4.3`* 7 | 8 | This library adds the `yield` metadata, which is similar to the `yield` keyword in C#. 9 | 10 | The `yield` metadata defines iterator blocks and indicates that the function, operator (see [operator overloading](https://haxe.org/manual/types-abstract-operator-overloading.html)), or accessor in which it appears is an iterator. 11 | 12 | When defining an iterator with `yield` statements, an extra class is implicitly created to hold the state for an iteration likewise implementing the [Iterator<T>](http://api.haxe.org/Iterator.html) or [Iterable<T>](http://api.haxe.org/Iterable.html) pattern for a custom type (see [iterators](https://haxe.org/manual/lf-iterators.html) for an example). 13 | 14 | Usage 15 | ----- 16 | 17 | Any `@yield` expressions are available for classes that are annotated with the `:yield` metadata, or available for all classes that extend classes annotated with `:yield(Extend)`. 18 | ```haxe 19 | @:yield 20 | class MyClass { 21 | // ... 22 | } 23 | ``` 24 | 25 | The following example shows the two forms of the `yield` metadata: 26 | ```haxe 27 | @yield return expression; 28 | @yield break; 29 | ``` 30 | 31 | Use `@yield return` to return each element one at a time.
32 | Use `@yield break` to end the iteration. 33 | 34 | Iterator methods can be run through using a `for` expression or [Lambda](https://haxe.org/manual/std-Lambda.html) functions. When `@yield return` is reached in the iterator method, `expression` is returned. Execution is restarted from that location the next time that the iterator function is called. 35 | 36 | The return type must be [Iterator<T>](http://api.haxe.org/Iterator.html) or [Iterable<T>](http://api.haxe.org/Iterable.html). If no return type is defined, the type will be [Dynamic](https://haxe.org/manual/types-dynamic.html), and can be unified to both Iterator or Iterable. 37 | 38 | Example 39 | ----- 40 | 41 | Here’s an example of the `yield` metadata usage: 42 | ```haxe 43 | @:yield 44 | class Test { 45 | function sayHello (name:String):Iterator { 46 | @yield return “Hello”; 47 | @yield return name + “!”; 48 | } 49 | } 50 | ``` 51 | 52 | Here the sayHello function usage: 53 | 54 | ```haxe 55 | for (word in sayHello(“World”)) { 56 | trace(word); // “Hello”, “World!” 57 | } 58 | ``` 59 | 60 | Call the sayHello method returns an Iterator<String>. The body of the method is not executed yet. 61 |
The `for` loop iterates over the iterator while the `Iterator.hasNext` method returns true. 62 |
The method `Iterator.hasNext` executes only once the body of sayHello until the next `@yield` expression is reached. 63 | In case of a `@yield return`, `Iterator.hasNext` will return true, and the result of the execution can be get once by calling `Iterator.next`. 64 | 65 | The `Iterator.next` method can also be used without calling `Iterator.hasNext`. If the end of sayHello is reached, `Iterator.next` returns the default value of the return type. 66 | 67 | Here’s a second example: 68 | ```haxe 69 | function getCounter ():Iterator { 70 | var i:UInt = 0; 71 | while (true) { 72 | @yield return i++; 73 | } 74 | } 75 | 76 | var counter:Iterator = getCounter(); 77 | 78 | counter.next(); // 0 79 | counter.next(); // 1 80 | counter.next(); // 2 81 | // ... 82 | counter.next(); // n 83 | ``` 84 | 85 | Advanced usage 86 | ----- 87 | 88 | ##### Defines 89 | 90 | You can compile with some haxe compilation parameters (or pass several `yield.YieldOption` into the `:yield` metadata): 91 | 92 | - `yield-extend`
93 | If the option is enabled, all extending classes will be able to use `@yield` expressions. If this option affects an interface, all implementing classes and all extending interfaces will be able to use `@yield` expressions. This is disabled by default. 94 | Compile with `-D yield-extend`. 95 | 96 | - `yield-explicit`
97 | If the option is enabled, the return type of iterative functions needs to be explicitly specified. This is disabled by default. 98 | Compile with `-D yield-explicit`. 99 | 100 | - `yield-keyword`
101 | Use a custom keyword instead of "yield". 102 | Compile with `-D yield-keyword=myCustomMetaName`. 103 | 104 | - `yield-parse`
105 | Specifies packages or classpaths to include in the yield parser. All the impacted classes will no longer need to be annotated with `:yield` to be able to use the `@yield` expressions. This can be recursive using the `*` wildcard. 106 | Compile with `-D yield-parse= my.package.one, my.packages.*, my.class.Foo`. 107 | 108 | - `yield-types`
109 | Specifies types which automatilally trigger the yield parser when imported. 110 | Compile with `-D yield-types= foo.Bar, foo.Bar.Subtype`. 111 | Available from init-macros through `yield.parser.Parser.parseOnImport`. 112 | 113 | - `yield-position-mapping`
114 | Gives more accurate positions when an exception arises. This is the default behavior in debug mode only when using Haxe 4.1 or above. It can be disabled with `-D no-yield-position-mapping`. 115 | Compile with `-D yield-position-mapping` to enforce this behavior in release mode too. 116 | 117 | ##### Macro API 118 | 119 | - `yield.parser.Parser.parseOnImport`
120 | Similar to `-D yield-types=some.Type`. 121 | 122 | - `yield.parser.Parser.onYield`
123 | Adds a callback which allows transforming each yielded expression. 124 | 125 | See [the coroutine library](https://github.com/dpomier/haxe-coroutine) for an example. You can also browse the unit tests. 126 | 127 | Install 128 | ----- 129 | 130 | To install the library, use `haxelib install yield` and compile your program with `-lib yield`. 131 | 132 | Development Builds 133 | ----- 134 | 135 | 1. To clone the github repository, use `git clone https://github.com/dpomier/haxe-yield` 136 | 137 | 2. To tell haxelib where your development copy is installed, use `haxelib dev yield my/repositories/haxe-yield` 138 | 139 | To return to release builds use `haxelib dev yield` 140 | 141 | To help to debug a specific function, you can use the haxe compilation parameter `-D yield-debug= myFunctionName` to see the result after the parsing is done. 142 | 143 | Alternatives 144 | ----- 145 | 146 | Other libraries addressing generators: 147 | 148 | * https://github.com/Aurel300/pecan - Macro-based library that lets you write coroutines with input/output and goto support. 149 | * https://github.com/RealyUniqueName/Coro - Haxe compiler plugin which adds generic coroutines implementation (including built-in async/await and generators) 150 | * https://lib.haxe.org/p/tink_await/ - Adds async/await for [tink_core](https://github.com/haxetink/tink_core) futures 151 | * https://github.com/haxe-continuation/haxe-continuation - Adds async/await 152 | * https://lib.haxe.org/p/moon-core/ - Utility library which includes generator functions, fibers, yield and await 153 | 154 | -------------------------------------------------------------------------------- /tests/misc/YieldTests.hx: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | import utest.Assert; 4 | import pack.pack2.MiscYielded; 5 | 6 | @:yield 7 | class YieldTests extends utest.Test { 8 | 9 | function testSimpleSplit () { 10 | var it:Iterator = cast simpleSplit(); 11 | 12 | Assert.equals(1, it.next()); 13 | Assert.equals(2, it.next()); 14 | Assert.equals(4, it.next()); 15 | Assert.isFalse(it.hasNext()); 16 | } 17 | 18 | function simpleSplit (): Iterator { 19 | @yield return 1; 20 | @yield return 2; 21 | @yield return 4; 22 | } 23 | 24 | function testStaticVar () { 25 | var it = staticVar(); 26 | Assert.isTrue(it.hasNext()); 27 | Assert.equals(0, it.next()); 28 | Assert.equals(1, it.next()); 29 | Assert.equals(2, it.next()); 30 | Assert.isFalse(it.hasNext()); 31 | } 32 | 33 | static var staticVar:Void->Iterator = function ():Iterator { 34 | 35 | for (i in 0...3) { 36 | @yield return i; 37 | } 38 | }; 39 | 40 | function testMember () { 41 | var it = this.member(); 42 | Assert.isTrue(it.hasNext()); 43 | Assert.equals(0, it.next()); 44 | Assert.equals(1, it.next()); 45 | Assert.equals(2, it.next()); 46 | Assert.isFalse(it.hasNext()); 47 | } 48 | 49 | var member:Void->Iterator = function ():Iterator { 50 | 51 | for (i in 0...3) { 52 | @yield return i; 53 | } 54 | }; 55 | 56 | function testStaticProperty () { 57 | var it = staticProperty(); 58 | Assert.isTrue(it.hasNext()); 59 | Assert.equals(0, it.next()); 60 | Assert.equals(1, it.next()); 61 | Assert.equals(2, it.next()); 62 | Assert.isFalse(it.hasNext()); 63 | } 64 | 65 | static var staticProperty(default, null) = function () { 66 | 67 | for (i in 0...3) { 68 | @yield return i; 69 | } 70 | }; 71 | 72 | function testProperty () { 73 | var it = this.propertyMember(); 74 | Assert.isTrue(it.hasNext()); 75 | Assert.equals(0, it.next()); 76 | Assert.equals(1, it.next()); 77 | Assert.equals(2, it.next()); 78 | Assert.isFalse(it.hasNext()); 79 | } 80 | 81 | var propertyMember(default, null) = function () { 82 | 83 | for (i in 0...3) { 84 | @yield return i; 85 | } 86 | }; 87 | 88 | function testVarDeclaration () { 89 | var it:Iterator = cast varDeclaration(); 90 | 91 | Assert.equals(1, it.next()); 92 | Assert.equals(2, it.next()); 93 | Assert.equals(4, it.next()); 94 | Assert.isFalse(it.hasNext()); 95 | } 96 | 97 | function varDeclaration (): Iterator { 98 | var a = 1; 99 | var a = 1; 100 | @yield return a; 101 | var b:Int = a + 1; 102 | @yield return b; 103 | @yield return b+a+1; 104 | } 105 | 106 | function testOneLine () { 107 | var it:Iterator = cast oneLine(); 108 | 109 | Assert.equals(72, it.next()); 110 | Assert.isFalse(it.hasNext()); 111 | } 112 | 113 | function oneLine (): Iterator @yield return 72; 114 | 115 | function testSimpleBreak () { 116 | var it = simpleBreak(5); 117 | 118 | Assert.isTrue(it.hasNext()); 119 | Assert.equals(1, it.next()); 120 | Assert.equals(2, it.next()); 121 | Assert.equals(3, it.next()); 122 | Assert.equals(4, it.next()); 123 | Assert.equals(5, it.next()); 124 | Assert.isFalse(it.hasNext()); 125 | } 126 | 127 | function simpleBreak (value:Int) { 128 | var i = 0; 129 | while (true) { 130 | if (i >= value) { 131 | @yield break; 132 | } 133 | i += 1; 134 | @yield return i; 135 | } 136 | @yield return -1; 137 | @yield return -1; 138 | } 139 | 140 | function testInlineMethods () { 141 | var it = inlineMethod1(); 142 | 143 | Assert.isTrue(it.hasNext()); 144 | Assert.equals(1, it.next()); 145 | Assert.equals(2, it.next()); 146 | Assert.equals(3, it.next()); 147 | Assert.isFalse(it.hasNext()); 148 | } 149 | 150 | static inline function inlineMethod1 () { 151 | @yield return 1; 152 | @yield return 2; 153 | @yield return 3; 154 | } 155 | 156 | function testInlineCrossModuleAccess () { 157 | var it = MiscYielded.inlineMethod2("hello"); 158 | 159 | Assert.isTrue(it.hasNext()); 160 | Assert.equals("hello1", it.next()); 161 | Assert.equals("hello2", it.next()); 162 | Assert.equals("hello3", it.next()); 163 | Assert.isFalse(it.hasNext()); 164 | } 165 | 166 | function testCheckType () { 167 | 168 | var a:Int = 3; 169 | 170 | var d = (a:Int); 171 | 172 | Assert.isTrue(true); 173 | } 174 | 175 | function checkType () { 176 | 177 | var a:Int = 3; 178 | 179 | var d:Int = (a:Int); 180 | 181 | @yield return null; 182 | } 183 | 184 | function func1 ():Iterator { 185 | 186 | function titi () { 187 | @yield return "hello"; 188 | } 189 | 190 | #if (neko || js || php || python || lua) 191 | var tutu = function ruru () {}; 192 | #else 193 | var tutu:Void->Void = function ruru () {}; 194 | #end 195 | 196 | #if (neko || js || php || python || lua) 197 | var lulu = (function nunu () {}); 198 | #else 199 | var lulu:Void->Void = (function nunu () {}); 200 | #end 201 | 202 | @yield return 3; 203 | } 204 | 205 | function func2 () { 206 | 207 | var thisVar = (function myFunc () {}); 208 | var condition:Bool = true; 209 | return (!condition ? function() {return 8; } : function() {return 16; })(); 210 | 211 | } 212 | 213 | var property (get, set) :Iterator; 214 | 215 | function get_property () { 216 | 217 | @yield return "one"; 218 | @yield return "two"; 219 | @yield return "three"; 220 | } 221 | 222 | function set_property (v:Iterator) { 223 | return null; 224 | } 225 | 226 | function testAnonymFor () { 227 | 228 | var res:Bool = false; 229 | 230 | for (i in (function ():Iterator { @yield return true; })()) { 231 | res = i; 232 | } 233 | 234 | Assert.isTrue(res); 235 | } 236 | 237 | function testAnonymSwitch () { 238 | 239 | var res:Bool = false; 240 | 241 | switch (3) { 242 | 243 | case 3 if ((function(){ 244 | @yield return 3; 245 | })().next() == 3): 246 | 247 | res = true; 248 | 249 | case 3: 250 | default: 251 | } 252 | 253 | Assert.isTrue(res); 254 | } 255 | 256 | function testAnonymIf () { 257 | 258 | var res:Bool = false; 259 | 260 | if ((function(){ 261 | @yield return 3; 262 | })().next() == 3) { 263 | 264 | res = true; 265 | } 266 | 267 | Assert.isTrue(res); 268 | } 269 | 270 | function testAnonymWhile () { 271 | 272 | var res:Bool = false; 273 | 274 | while (!res && (function(){ 275 | @yield return 3; 276 | })().next() == 3) { 277 | 278 | res = true; 279 | } 280 | 281 | Assert.isTrue(res); 282 | } 283 | 284 | function testCompilationOptimization () { 285 | var it = compilationOptimization(); 286 | Assert.isTrue(true); 287 | } 288 | 289 | function compilationOptimization ():Iterator { 290 | 291 | for (rule in 0...3) { 292 | if (rule < 3) { 293 | if (true) { 294 | @yield return null; 295 | } 296 | } 297 | } 298 | } 299 | 300 | #if (haxe_ver >= 4.000) 301 | function testIntersection () { 302 | var it = intersection(); 303 | Assert.equals("a", it.next()); 304 | Assert.equals("b", it.iterator().next()); 305 | } 306 | 307 | function intersection ():Iterator & Iterable { 308 | 309 | @yield return "a"; 310 | @yield return "b"; 311 | } 312 | #end 313 | 314 | } -------------------------------------------------------------------------------- /tests/eparsers/EWhileTests.hx: -------------------------------------------------------------------------------- 1 | package eparsers; 2 | 3 | import utest.Assert; 4 | 5 | @:yield 6 | class EWhileTests extends utest.Test { 7 | 8 | private static var NULL_INT:Int; 9 | 10 | public function new () { 11 | super(); 12 | var n:Null = null; 13 | NULL_INT = n; 14 | } 15 | 16 | function testSimpleWhile () { 17 | var it = simpleWhile(); 18 | 19 | Assert.equals(4, it.next()); 20 | } 21 | 22 | function simpleWhile ():Iterator { 23 | 24 | var a = 0; 25 | 26 | while (a < 4) { 27 | a += 1; 28 | } 29 | 30 | @yield return a; 31 | } 32 | 33 | function testYieldedWhile () { 34 | var it = yieldedWhile(); 35 | 36 | Assert.equals(0, it.next()); 37 | Assert.equals(1, it.next()); 38 | Assert.equals(2, it.next()); 39 | Assert.equals(3, it.next()); 40 | 41 | Assert.equals(NULL_INT, it.next()); 42 | } 43 | 44 | function yieldedWhile ():Iterator { 45 | 46 | var a = 0; 47 | 48 | while (a < 4) { 49 | @yield return a; 50 | a += 1; 51 | } 52 | 53 | var n:Null = null; 54 | var lnull:Int = n; 55 | @yield return lnull; 56 | } 57 | 58 | function testMultipleYieldedWhile () { 59 | var it = multipleYieldedWhile(); 60 | 61 | Assert.equals(0, it.next()); 62 | Assert.equals(10, it.next()); 63 | Assert.equals(1, it.next()); 64 | 65 | Assert.equals(1, it.next()); 66 | Assert.equals(10, it.next()); 67 | Assert.equals(2, it.next()); 68 | 69 | Assert.equals(2, it.next()); 70 | Assert.equals(10, it.next()); 71 | Assert.equals(3, it.next()); 72 | 73 | Assert.equals(NULL_INT, it.next()); 74 | } 75 | 76 | function multipleYieldedWhile ():Iterator { 77 | 78 | var a = 0; 79 | var b = 0; 80 | 81 | while (a < 3) { 82 | @yield return a; 83 | a += 1; 84 | @yield return 10; 85 | b += 1; 86 | @yield return b; 87 | } 88 | 89 | var n:Null = null; 90 | var lnull:Int = n; 91 | @yield return lnull; 92 | } 93 | 94 | function testNestedWhile () { 95 | var it = nestedWhile(); 96 | 97 | Assert.equals(6, it.next()); 98 | Assert.equals(1, it.next()); 99 | Assert.equals(1, it.next()); 100 | 101 | Assert.equals(0, it.next()); 102 | Assert.equals(10, it.next()); 103 | Assert.equals(21, it.next()); 104 | Assert.equals(22, it.next()); 105 | Assert.equals(23, it.next()); 106 | Assert.equals(11, it.next()); 107 | Assert.equals(1, it.next()); 108 | Assert.equals(2, it.next()); 109 | 110 | Assert.equals(NULL_INT, it.next()); 111 | 112 | 113 | Assert.isFalse(it.hasNext()); 114 | } 115 | 116 | function nestedWhile ():Iterator { 117 | 118 | var a = 0; 119 | var b = 0; 120 | var c = 0; 121 | 122 | while (a < 3) { 123 | while (a < 5) { 124 | while (a < 6) { 125 | a++; 126 | } 127 | b++; 128 | } 129 | c++; 130 | } 131 | 132 | @yield return a; 133 | @yield return b; 134 | @yield return c; 135 | 136 | a = 0; 137 | b = 10; 138 | c = 20; 139 | 140 | while (a < 3) { 141 | 142 | @yield return a; 143 | 144 | while (b < 12) { 145 | 146 | @yield return b; 147 | 148 | while (c < 23) { 149 | c++; 150 | @yield return c; 151 | } 152 | b++; 153 | } 154 | a++; 155 | } 156 | 157 | var n:Null = null; 158 | var lnull:Int = n; 159 | @yield return lnull; 160 | } 161 | 162 | function testBreakAndContinue () { 163 | var it = breakAndContinue(); 164 | 165 | Assert.isTrue(it.hasNext()); 166 | 167 | Assert.equals(0, it.next()); 168 | Assert.equals(2, it.next()); 169 | Assert.equals(3, it.next()); 170 | Assert.equals(100, it.next()); 171 | 172 | Assert.isFalse(it.hasNext()); 173 | } 174 | 175 | function breakAndContinue ():Iterator { 176 | var counter:Int = 0; 177 | 178 | while (true) { 179 | 180 | @yield return counter++; 181 | if (counter == 3) continue; 182 | counter++; 183 | if (counter == 5) break; 184 | } 185 | @yield return 100; 186 | } 187 | 188 | function testContinueStatements () { 189 | var it = continueStatement(); 190 | 191 | Assert.isTrue(it.hasNext()); 192 | 193 | Assert.equals(0, it.next()); 194 | Assert.equals(2, it.next()); 195 | Assert.equals(3, it.next()); 196 | Assert.equals(4, it.next()); 197 | Assert.equals(5, it.next()); 198 | Assert.equals(6, it.next()); 199 | Assert.equals(7, it.next()); 200 | Assert.equals(8, it.next()); 201 | 202 | Assert.equals(18, it.next()); 203 | Assert.equals(21, it.next()); 204 | Assert.equals(24, it.next()); 205 | Assert.equals(27, it.next()); 206 | Assert.equals(30, it.next()); 207 | Assert.equals(33, it.next()); 208 | 209 | Assert.equals(32, it.next()); 210 | for (i in 0...100) { 211 | Assert.equals(32, it.next()); 212 | } 213 | } 214 | 215 | function continueStatement ():Iterator { 216 | var counter:Int = 0; 217 | 218 | while (true) { 219 | 220 | @yield return counter++; 221 | 222 | while (counter < 16) { 223 | if (++counter > 8) continue; 224 | @yield return counter; 225 | } 226 | 227 | if (counter > 32) { 228 | counter = 32; 229 | continue; 230 | } else { 231 | counter += 2; 232 | } 233 | 234 | } 235 | } 236 | 237 | function testDoWhile () { 238 | var it = doWhile(); 239 | 240 | Assert.isTrue(it.hasNext()); 241 | 242 | Assert.equals(0, it.next()); 243 | Assert.equals(1, it.next()); 244 | Assert.equals(2, it.next()); 245 | Assert.equals(3, it.next()); 246 | 247 | Assert.isFalse(it.hasNext()); 248 | } 249 | 250 | function doWhile ():Iterator { 251 | var i = 0; 252 | do { 253 | @yield return i; 254 | } while (++i < 4); 255 | } 256 | 257 | function testLastPart () { 258 | var it = lastPart(); 259 | 260 | Assert.isTrue(it.hasNext()); 261 | 262 | Assert.equals(1, it.next()); 263 | Assert.equals(2, it.next()); 264 | Assert.equals(3, it.next()); 265 | 266 | Assert.equals(3, it.next()); 267 | Assert.equals(5, it.next()); 268 | 269 | Assert.isFalse(it.hasNext()); 270 | } 271 | 272 | function lastPart ():Iterator { 273 | var i = 0; 274 | var val = 0; 275 | var counter = 0; 276 | while (++i < 4) { 277 | @yield return i; 278 | counter += 1; 279 | } 280 | val = 5; 281 | @yield return counter; 282 | @yield return val; 283 | } 284 | 285 | function testReturnedValue () { 286 | 287 | var it = returnedValue(4); 288 | Assert.isTrue(it.hasNext()); 289 | Assert.equals(Std.string([0,1,2,3]), Std.string(it.next())); 290 | Assert.equals(Std.string([5,6,7]), Std.string(it.next())); 291 | Assert.isFalse(it.hasNext()); 292 | 293 | } 294 | 295 | function returnedValue (len:Int) { 296 | 297 | var i:Int = -1; 298 | 299 | var a:Array = [while (++i < len) i]; 300 | 301 | @yield return a; 302 | 303 | @yield return [while (++i < len * 2) i]; 304 | } 305 | 306 | function testIteratorReturnedValue () { 307 | 308 | var it = iteratorReturnedValue(4); 309 | Assert.isTrue(it.hasNext()); 310 | 311 | var array:Array = it.next(); 312 | 313 | Assert.equals(4, array.length); 314 | 315 | for (elem in array) { 316 | var subIt = elem(); 317 | Assert.isTrue(subIt.hasNext()); 318 | Assert.equals(4, subIt.next()); 319 | Assert.equals(8, subIt.next()); 320 | Assert.isFalse(subIt.hasNext()); 321 | } 322 | } 323 | 324 | function iteratorReturnedValue (len:Int) { 325 | var i:Int = -1; 326 | var a:ArrayIterator> = [while (++i < len) { 327 | function () { 328 | @yield return i; 329 | @yield return i*2; 330 | }; 331 | }]; 332 | 333 | @yield return a; 334 | } 335 | } -------------------------------------------------------------------------------- /tests/eparsers/ETryTests.hx: -------------------------------------------------------------------------------- 1 | package eparsers; 2 | 3 | import utest.Assert; 4 | 5 | @:yield 6 | class ETryTests extends utest.Test { 7 | 8 | function testSimpleTry () { 9 | var it = simpleTry(); 10 | 11 | var error = 0; 12 | 13 | try { 14 | it.next(); 15 | } catch (err:Dynamic) { 16 | #if (haxe_ver >= 4.300) 17 | error = if(err is haxe.ValueException) { 18 | (err:haxe.ValueException).value; 19 | } else { 20 | err; 21 | } 22 | #else 23 | error = err; // TInt 24 | #end 25 | } 26 | 27 | Assert.equals(1, error); 28 | } 29 | 30 | function simpleTry ():Iterator { 31 | 32 | var a = 1; 33 | try throw a; 34 | @yield return a; 35 | } 36 | 37 | function testTryY () { 38 | var it = tryY(); 39 | 40 | Assert.equals(0, it.next()); 41 | Assert.equals(1, it.next()); 42 | Assert.equals(25, it.next()); 43 | Assert.isFalse(it.hasNext()); 44 | } 45 | 46 | function tryY ():Iterator { 47 | 48 | var a = 10; 49 | 50 | try { 51 | a += 5; 52 | @yield return 0; 53 | @yield return 1; 54 | a += 10; 55 | 56 | } catch (err:Dynamic) { 57 | 58 | } 59 | 60 | @yield return a; 61 | } 62 | 63 | function testTryYThrow () { 64 | var it = tryYThrow(); 65 | 66 | Assert.equals(0, it.next()); 67 | Assert.equals(1, it.next()); 68 | Assert.equals(2, it.next()); 69 | Assert.equals(10, it.next()); 70 | Assert.isFalse(it.hasNext()); 71 | } 72 | 73 | function tryYThrow ():Iterator { 74 | 75 | var a = 10; 76 | 77 | try { 78 | @yield return 0; 79 | @yield return 1; 80 | @yield return 2; 81 | throw null; 82 | @yield return 3; 83 | @yield return 4; 84 | 85 | } catch (err:Dynamic) { } 86 | 87 | @yield return a; 88 | } 89 | 90 | function testCatchY () { 91 | var it = catchY(); 92 | 93 | Assert.equals(42, it.next()); 94 | Assert.isFalse(it.hasNext()); 95 | } 96 | function catchY ():Iterator { 97 | 98 | var a = 0; 99 | 100 | try { 101 | 102 | a += 42; 103 | 104 | } catch (err:String) { 105 | 106 | a += 1; 107 | @yield return 2; 108 | a += 3; 109 | @yield return 4; 110 | a += 4; 111 | 112 | } catch (err:Dynamic) { } 113 | @yield return a; 114 | } 115 | 116 | function testCatchY2 () { 117 | var it = catchY2(); 118 | 119 | Assert.equals(80, it.next()); 120 | Assert.equals(60, it.next()); 121 | Assert.equals(42, it.next()); 122 | Assert.isFalse(it.hasNext()); 123 | } 124 | function catchY2 ():Iterator { 125 | 126 | var a = 0; 127 | 128 | try { 129 | 130 | a += 20; 131 | @yield return 80; 132 | @yield return 60; 133 | a += 22; 134 | 135 | } catch (err:String) { 136 | 137 | a += 1; 138 | @yield return 2; 139 | a += 3; 140 | @yield return 4; 141 | a += 4; 142 | 143 | } catch (err:Dynamic) { } 144 | @yield return a; 145 | } 146 | 147 | function testCatchYThrow () { 148 | var it = catchYThrow(); 149 | 150 | Assert.equals(2, it.next()); 151 | Assert.equals(4, it.next()); 152 | Assert.equals(8, it.next()); 153 | Assert.isFalse(it.hasNext()); 154 | } 155 | function catchYThrow ():Iterator { 156 | 157 | var a = 0; 158 | 159 | try { 160 | 161 | throw "error"; 162 | 163 | } catch (err:String) { 164 | 165 | a += 1; 166 | @yield return 2; 167 | a += 3; 168 | @yield return 4; 169 | a += 4; 170 | 171 | } catch (err:Dynamic) { 172 | 173 | a += 20; 174 | var n:Null = null; 175 | var lnull:Int = n; 176 | @yield return lnull; 177 | @yield return lnull; 178 | a += 30; 179 | 180 | } 181 | @yield return a; 182 | } 183 | 184 | function testGettingError () { 185 | var it = gettingError(); 186 | 187 | Assert.equals("foobar", it.next()); 188 | Assert.equals(5, it.next()); 189 | Assert.equals(null, it.next()); 190 | Assert.isFalse(it.hasNext()); 191 | } 192 | function gettingError ():Iterator { 193 | 194 | try { 195 | 196 | throw "foobar"; 197 | 198 | } catch (err:String) { 199 | 200 | @yield return err; 201 | 202 | } catch (err:Dynamic) { } 203 | 204 | try { 205 | 206 | throw 5; 207 | 208 | } catch (err:String) { 209 | 210 | } catch (err:Dynamic) { 211 | @yield return err; 212 | } 213 | 214 | try { 215 | 216 | } catch (err:Dynamic) { 217 | @yield return 10; 218 | } 219 | 220 | @yield return null; 221 | } 222 | 223 | function testReturnedValue () { 224 | 225 | var it = returnedValue("string"); 226 | Assert.isTrue(it.hasNext()); 227 | Assert.equals("1", it.next()); 228 | Assert.equals("2", it.next()); 229 | Assert.isFalse(it.hasNext()); 230 | 231 | it = returnedValue(10); 232 | Assert.isTrue(it.hasNext()); 233 | Assert.equals("2", it.next()); 234 | Assert.equals("4", it.next()); 235 | Assert.isFalse(it.hasNext()); 236 | 237 | it = returnedValue(false); 238 | Assert.isTrue(it.hasNext()); 239 | Assert.equals("3", it.next()); 240 | Assert.equals("6", it.next()); 241 | Assert.isFalse(it.hasNext()); 242 | } 243 | 244 | function returnedValue (err:Dynamic) { 245 | 246 | var a:String = try { 247 | throw err; 248 | } catch (err:String) { 249 | "1"; 250 | } catch (err:Bool) { 251 | "3"; // prevents unification of Bool with Int 252 | } catch (err:Int) { 253 | "2"; 254 | } catch (err:Dynamic) { 255 | "3"; 256 | }; 257 | 258 | @yield return a; 259 | 260 | @yield return try { 261 | throw err; 262 | } catch (err:String) { 263 | "2"; 264 | } catch (err:Bool) { 265 | "6"; // prevents unification of Bool with Int 266 | } catch (err:Int) { 267 | "4"; 268 | } catch (err:Dynamic) { 269 | "6"; 270 | }; 271 | } 272 | 273 | function testInitialization () { 274 | var it = initialization(); 275 | Assert.isTrue(it.hasNext()); 276 | Assert.equals(2, it.next()); 277 | try { 278 | it.next(); 279 | Assert.isTrue(false); 280 | } catch (err:Dynamic) { 281 | Assert.isTrue(true); 282 | } 283 | Assert.isFalse(it.hasNext()); 284 | } 285 | 286 | function initialization () { 287 | 288 | var v:Int; 289 | 290 | try { 291 | 292 | v = 1; 293 | throw 5; 294 | 295 | } catch (err:String) { 296 | throw null; 297 | } catch (err:Int) { 298 | v = 2; 299 | } catch (err:Dynamic) { 300 | v = 3; 301 | @yield return err; 302 | } 303 | 304 | @yield return v; 305 | 306 | var v:Int; 307 | 308 | throw null; 309 | 310 | @yield return v; 311 | } 312 | 313 | function testTryInFor () { 314 | 315 | tryInFor(); // check compilation 316 | 317 | Assert.isTrue(true); 318 | } 319 | 320 | function tryInFor ():Iterator { 321 | 322 | var num = 3; 323 | 324 | for (i in 0...num) { 325 | 326 | try { 327 | 328 | } 329 | } 330 | 331 | @yield break; 332 | } 333 | 334 | function testNestedFunctionShouldBeOutOfTry () { 335 | var it = nestedFunctionShouldBeOutOfTry(); 336 | Assert.isTrue(it.next()); 337 | Assert.isTrue(it.next()); 338 | } 339 | 340 | function nestedFunctionShouldBeOutOfTry () { 341 | 342 | var throwErrors:Iterator = null; 343 | var errorThrowed = false; 344 | 345 | try { 346 | 347 | throwErrors = (function () { 348 | @yield return throw "error1"; 349 | @yield return throw "error2"; 350 | })(); 351 | 352 | throwErrors.next(); 353 | 354 | } catch (error:Dynamic) { 355 | errorThrowed = Std.string(error) == "error1"; 356 | } 357 | 358 | @yield return errorThrowed; 359 | 360 | errorThrowed = false; 361 | 362 | try { 363 | throwErrors.next(); 364 | } catch (error:Dynamic) { 365 | errorThrowed = Std.string(error) == "error2"; 366 | } 367 | 368 | @yield return errorThrowed; 369 | } 370 | } -------------------------------------------------------------------------------- /tests/misc/GenericTests.hx: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | import utest.Assert; 4 | import yield.parser.Parser; 5 | 6 | class GenericTests extends utest.Test { 7 | 8 | #if (haxe_ver >= 4.000) 9 | function testSelectiveFunctions () { 10 | var a = new SelectiveFunctions("foo"); 11 | var it = a.getString(); 12 | Assert.isTrue(it.hasNext()); 13 | for (j in 0...10) Assert.equals("foo", it.next()); 14 | Assert.isTrue(it.hasNext()); 15 | 16 | var b = new SelectiveFunctions(13); 17 | var it = b.getAny(); 18 | Assert.isTrue(it.hasNext()); 19 | for (j in 0...10) Assert.equals(13, it.next()); 20 | Assert.isTrue(it.hasNext()); 21 | } 22 | #end 23 | 24 | #if (haxe_ver >= 4.200) 25 | function testSelectiveFunctionsWrapper () { 26 | var a = new SFWrapper(new SelectiveFunctions("bar")); 27 | 28 | var it = a.getSource(); 29 | Assert.isTrue(it.hasNext()); 30 | Assert.equals(new SelectiveFunctions("bar"), it.next()); 31 | Assert.equals("bar", it.next().getString().next()); 32 | Assert.equals(new SelectiveFunctions("bar"), it.next()); 33 | Assert.isTrue(it.hasNext()); 34 | 35 | var b = new SFWrapper(8); 36 | var it = b.getSource(); 37 | Assert.isTrue(it.hasNext()); 38 | for (j in 0...10) Assert.equals(8, it.next()); 39 | Assert.isTrue(it.hasNext()); 40 | } 41 | #end 42 | 43 | function testFunctionStaticTypeParams () { 44 | 45 | var it = Statics.makeSource("foobar"); 46 | Assert.isTrue(it.hasNext()); 47 | for (j in 0...10) Assert.equals("foobar", it.next()); 48 | Assert.isTrue(it.hasNext()); 49 | 50 | var it = Statics.testConstraints(["3"]); 51 | //Statics.testConstraints([3]); // Constraint check failure 52 | } 53 | 54 | #if (haxe_ver >= 4.200) 55 | function testFunctionTypeParams () { 56 | 57 | var a = new AbstractWithConstraints(["5"]); 58 | //new AbstractWithConstraints([5]); // Constraint check failure 59 | 60 | Assert.isTrue(true); 61 | } 62 | #end 63 | 64 | function testGenericType () { 65 | var g = new GenericTest(4); 66 | var it = g.getMore(); 67 | Assert.equals("4", it.next()); 68 | Assert.equals("44", it.next()); 69 | Assert.equals("444", it.next()); 70 | } 71 | 72 | function testAbstractGenericType () { 73 | var g = new AbstractGenericTest(8); 74 | var it = g.getMore(); 75 | Assert.equals("88", it.next()); 76 | Assert.equals("8888", it.next()); 77 | } 78 | 79 | function testGenericFunction () { 80 | var it = GenericFunctions.getIt(4); 81 | for (_ in 0...3) Assert.equals(4, it.next()); 82 | } 83 | 84 | function testImplicitGenericFunction () { 85 | var it = GenericFunctions.getImplicitIt(4); 86 | for (_ in 0...3) Assert.equals(4, it.next()); 87 | } 88 | 89 | function testGenericFunctionAndType () { 90 | 91 | var it = new GenericFunctions().getItCumulatedWithType("foo", 4); 92 | for (_ in 0...3) Assert.equals(null, it.next()); 93 | var it = new GenericFunctions().getItCumulatedWithType("4", 4); 94 | for (_ in 0...3) Assert.equals("4", it.next()); 95 | } 96 | 97 | function testImplicitParamConstraint () { 98 | 99 | var it = GenericFunctions.getImplicitConstraitIt(4); 100 | Assert.equals(4, it.next()); 101 | Assert.equals(5, it.next()); 102 | 103 | // GenericFunctions.getImplicitConstraitIt("str"); // Should fail with Constraint check failure: String should be Float 104 | } 105 | 106 | function testNestedGenericFunction () { 107 | 108 | var it1 = new NestedGenericTest(4).getIt(); 109 | var it2 = new NestedGenericTest(12).getIt(); 110 | Assert.equals("foo", it1.next()); 111 | Assert.equals(16, it2.next()); 112 | } 113 | 114 | // function testNestedGenericInGenericFunction () { 115 | 116 | // var it1 = new NestedGenericTest(4).getCumulatedIt("foo"); 117 | // var it3 = new NestedGenericTest(12).getCumulatedIt("12"); 118 | // Assert.equals(null, it1.next()); 119 | // Assert.equals(16, it3.next()); 120 | // } 121 | } 122 | 123 | #if (haxe_ver >= 4.000) 124 | @:yield 125 | @:using(misc.GenericTests.SelectiveFunctions) 126 | abstract SelectiveFunctions(T) from T { 127 | public function new(t:T) this = t; 128 | 129 | function get() return this; 130 | 131 | static public function getString(v:SelectiveFunctions):Iterator { 132 | while (true) { 133 | @yield return v.get(); 134 | } 135 | } 136 | 137 | static public function getAny(v:SelectiveFunctions):Iterator { 138 | while (true) { 139 | @yield return v.get(); 140 | } 141 | } 142 | } 143 | #end 144 | 145 | #if (haxe_ver >= 4.200) 146 | @:build(yield.parser.Parser.run()) 147 | abstract SFWrapper(T) from T { 148 | public function new(t:T) this = t; 149 | 150 | function get() return this; 151 | 152 | public function getSource():Iterator { 153 | while (true) { 154 | @yield return get(); 155 | } 156 | } 157 | } 158 | #end 159 | 160 | @:yield 161 | class Statics { 162 | public static function makeSource(i:T):Iterator { 163 | while (true) @yield return i; 164 | } 165 | 166 | #if (haxe_ver < 4.000) 167 | public static function testConstraints, Measurable)>(a:T) { 168 | #else 169 | public static function testConstraints & Measurable>(a:T) { 170 | #end 171 | if (a.length == 0) @yield break; 172 | @yield return a.iterator(); 173 | } 174 | } 175 | 176 | typedef Measurable = { 177 | public var length(default, null):Int; 178 | } 179 | 180 | #if (haxe_ver >= 4.200) 181 | @:build(yield.parser.Parser.run()) 182 | abstract AbstractWithConstraints & Measurable>(T) from T { 183 | public function new(t:T) this = t; 184 | 185 | function get() return this; 186 | 187 | public function getSource():Iterator { 188 | while (true) { 189 | @yield return get(); 190 | } 191 | } 192 | } 193 | #end 194 | 195 | @:yield 196 | @:generic 197 | class GenericTest { 198 | 199 | var value:T; 200 | 201 | public function new (v:T) { 202 | value = v; 203 | } 204 | 205 | public function getMore () { 206 | var s = ""; 207 | while (true) { 208 | s += value; 209 | @yield return s; 210 | } 211 | } 212 | 213 | } 214 | 215 | @:yield 216 | @:generic 217 | abstract AbstractGenericTest (String) { 218 | 219 | public function new (v:T) { 220 | this = Std.string(v); 221 | } 222 | 223 | public function getMore () { 224 | while (true) { 225 | this += this; 226 | @yield return this; 227 | } 228 | } 229 | 230 | } 231 | 232 | @:yield 233 | class GenericFunctions { 234 | 235 | public function new () {} 236 | 237 | public static function getIt (v:T):Iterator { 238 | 239 | while (true) 240 | @yield return v; 241 | 242 | } 243 | 244 | public static function getImplicitIt (v:T) { 245 | 246 | while (true) 247 | @yield return v; 248 | 249 | } 250 | 251 | public static function getImplicitConstraitIt (v:T) { 252 | 253 | @yield return v; 254 | 255 | @yield return 5.; 256 | 257 | } 258 | 259 | public function getItCumulatedWithType (v:T, a:A) { 260 | 261 | while (true) 262 | @yield return Std.string(v) == Std.string(a) ? v : null; 263 | 264 | } 265 | 266 | } 267 | 268 | @:yield 269 | @:generic class NestedGenericTest { 270 | 271 | var v:Int; 272 | 273 | public function new (v:Int) { 274 | this.v = v; 275 | } 276 | 277 | public function getIt ():Dynamic { 278 | 279 | function it (v:T) { 280 | @yield return v; 281 | } 282 | 283 | return if (v > 10) { 284 | it(16); 285 | } else { 286 | it("foo"); 287 | } 288 | 289 | 290 | } 291 | 292 | // public function getCumulatedIt (a:Z):Dynamic { 293 | 294 | // function it (v:Y) { 295 | // @yield return Std.string(v) == Std.string(a) ? v : null; 296 | // } 297 | 298 | // return if (v > 10) { 299 | // it(16); 300 | // } else { 301 | // it("foo"); 302 | // } 303 | 304 | // } 305 | 306 | } -------------------------------------------------------------------------------- /tests/misc/ScopeTests.hx: -------------------------------------------------------------------------------- 1 | package misc; 2 | 3 | import utest.Assert; 4 | 5 | @:yield 6 | class ScopeTests extends utest.Test { 7 | 8 | function testEBlock () { 9 | var it = eblock(); 10 | 11 | Assert.equals(1, it.next()); 12 | } 13 | 14 | function eblock ():Iterator { 15 | 16 | var a = 0; 17 | 18 | { 19 | a = 1; 20 | var a = 10; 21 | a += 3; 22 | } 23 | 24 | @yield return a; 25 | } 26 | 27 | function testEBlockY () { 28 | var it = eblockY(); 29 | 30 | Assert.equals(null, it.next()); 31 | Assert.equals(null, it.next()); 32 | Assert.equals(1, it.next()); 33 | } 34 | 35 | function eblockY ():Iterator { 36 | 37 | var a = 0; 38 | 39 | { 40 | @yield return null; 41 | a = 1; 42 | @yield return null; 43 | var a = 10; 44 | } 45 | 46 | @yield return a; 47 | } 48 | 49 | function testEBlockNestedY () { 50 | var it = eblockNestedY(); 51 | 52 | Assert.equals(null, it.next()); 53 | Assert.equals(null, it.next()); 54 | Assert.equals(null, it.next()); 55 | Assert.equals(11, it.next()); 56 | Assert.isFalse(it.hasNext()); 57 | } 58 | 59 | function eblockNestedY ():Iterator { 60 | 61 | var a = 0; 62 | 63 | { 64 | { 65 | { 66 | @yield return null; 67 | a = 2; 68 | { 69 | a = 1; 70 | @yield return null; 71 | var a = 20; 72 | } 73 | @yield return null; 74 | var a = 30; 75 | } 76 | a += 10; 77 | } 78 | } 79 | 80 | @yield return a; 81 | } 82 | 83 | function testEif () { 84 | var it = eif(); 85 | 86 | Assert.equals(1, it.next()); 87 | } 88 | 89 | function eif ():Iterator { 90 | 91 | var a = 0; 92 | var condition = true; 93 | 94 | if (condition) { 95 | a = 1; 96 | var a = 10; 97 | } 98 | 99 | @yield return a; 100 | } 101 | 102 | function testEifY () { 103 | var it = eifY(); 104 | 105 | Assert.equals(null, it.next()); 106 | Assert.equals(null, it.next()); 107 | Assert.equals(1, it.next()); 108 | } 109 | 110 | function eifY ():Iterator { 111 | 112 | var a = 0; 113 | var condition = true; 114 | 115 | if (condition) { 116 | @yield return null; 117 | a = 1; 118 | @yield return null; 119 | var a = 10; 120 | } 121 | 122 | @yield return a; 123 | } 124 | 125 | function testEifNestedY () { 126 | var it = eifNestedY(); 127 | 128 | Assert.equals(null, it.next()); 129 | Assert.equals(null, it.next()); 130 | Assert.equals(1, it.next()); 131 | } 132 | 133 | function eifNestedY ():Iterator { 134 | 135 | var a = 0; 136 | var condition = true; 137 | 138 | if (condition) { 139 | { 140 | a = 2; 141 | @yield return null; 142 | var a = 10; 143 | } 144 | a = a - 1; 145 | @yield return null; 146 | var a = 10; 147 | } 148 | 149 | @yield return a; 150 | } 151 | 152 | function testEelse () { 153 | var it = eelse(); 154 | 155 | Assert.equals(1, it.next()); 156 | } 157 | 158 | function eelse ():Iterator { 159 | 160 | var a = 0; 161 | var condition = true; 162 | 163 | if (!condition) { 164 | a = 1; 165 | var a = 10; 166 | } else { 167 | a = 1; 168 | var a = 20; 169 | } 170 | 171 | @yield return a; 172 | } 173 | 174 | function testEelseY () { 175 | var it = eelseY(); 176 | 177 | Assert.equals(null, it.next()); 178 | Assert.equals(null, it.next()); 179 | Assert.equals(1, it.next()); 180 | } 181 | 182 | function eelseY ():Iterator { 183 | 184 | var a = 0; 185 | var condition = true; 186 | 187 | if (!condition) { 188 | a = 2; 189 | @yield return null; 190 | var a = 20; 191 | @yield return null; 192 | } else { 193 | a = 1; 194 | @yield return null; 195 | var a = 10; 196 | @yield return null; 197 | } 198 | 199 | @yield return a; 200 | } 201 | 202 | function testEelseNestedY () { 203 | var it = eelseNestedY(); 204 | 205 | Assert.equals(null, it.next()); 206 | Assert.equals(null, it.next()); 207 | Assert.equals(null, it.next()); 208 | Assert.equals(1, it.next()); 209 | } 210 | 211 | function eelseNestedY ():Iterator { 212 | 213 | var a = 0; 214 | var condition = true; 215 | 216 | if (!condition) { 217 | a = 2; 218 | @yield return null; 219 | var a = 20; 220 | @yield return null; 221 | } else { 222 | a = 2; 223 | { 224 | @yield return null; 225 | a += 1; 226 | var a = 10; 227 | } 228 | a -= 2; 229 | @yield return null; 230 | var a = 10; 231 | @yield return null; 232 | } 233 | 234 | @yield return a; 235 | } 236 | 237 | function testEWhile () { 238 | var it = ewhile(); 239 | 240 | Assert.equals(1, it.next()); 241 | } 242 | 243 | function ewhile ():Iterator { 244 | 245 | var a = 0; 246 | var i = 1; 247 | 248 | while (--i >= 0) { 249 | ++a; 250 | var a = 10; 251 | } 252 | 253 | @yield return a; 254 | } 255 | 256 | function testEWhileNestedY () { 257 | var it = ewhileNestedY(); 258 | 259 | Assert.equals(null, it.next()); 260 | Assert.equals(null, it.next()); 261 | Assert.equals(1, it.next()); 262 | } 263 | 264 | function ewhileNestedY ():Iterator { 265 | 266 | var a = 0; 267 | var i = 1; 268 | 269 | while (--i >= 0) { 270 | a += 2; 271 | { 272 | a+=2; 273 | } 274 | @yield return null; 275 | { 276 | a -= 2; 277 | @yield return null; 278 | var a = 10; 279 | } 280 | --a; 281 | var a = 10; 282 | } 283 | 284 | @yield return a; 285 | } 286 | 287 | function testEFor () { 288 | var it = efor(); 289 | 290 | Assert.equals(1, it.next()); 291 | } 292 | 293 | function efor ():Iterator { 294 | 295 | var a = 0; 296 | 297 | for (i in 0...1) { 298 | ++a; 299 | var a = 10; 300 | } 301 | 302 | @yield return a; 303 | } 304 | 305 | function testEForNestedY () { 306 | var it = eforNestedY(); 307 | 308 | Assert.equals(null, it.next()); 309 | Assert.equals(null, it.next()); 310 | Assert.equals(1, it.next()); 311 | } 312 | 313 | function eforNestedY ():Iterator { 314 | 315 | var a = 0; 316 | 317 | for (i in 0...1) { 318 | ++a; 319 | { 320 | ++a; 321 | var b = 1; 322 | @yield return null; 323 | { 324 | a -= b; 325 | var a = 0; 326 | @yield return null; 327 | a -= b; 328 | } 329 | } 330 | var a = 10; 331 | } 332 | 333 | @yield return a; 334 | } 335 | 336 | function testETry () { 337 | var it = etry(); 338 | 339 | Assert.equals(1, it.next()); 340 | } 341 | 342 | function etry ():Iterator { 343 | 344 | var a = 0; 345 | 346 | try { 347 | ++a; 348 | var a = 10; 349 | } 350 | 351 | @yield return a; 352 | } 353 | 354 | function testECatch () { 355 | var it = ecatch(); 356 | 357 | Assert.equals(1, it.next()); 358 | } 359 | 360 | function ecatch ():Iterator { 361 | 362 | var a = 0; 363 | 364 | try { 365 | a = 10; 366 | var a = 20; 367 | throw null; 368 | } catch (e:Dynamic) { 369 | a = 1; 370 | var a = 30; 371 | } 372 | 373 | @yield return a; 374 | } 375 | 376 | 377 | function testEfunction () { 378 | var it = efunction(); 379 | Assert.isTrue(it.hasNext()); 380 | Assert.equals(0, it.next()); 381 | Assert.isFalse(it.hasNext()); 382 | } 383 | 384 | function efunction () { 385 | 386 | var v:Int = 0; 387 | 388 | function f () v = 3; 389 | 390 | @yield return v; 391 | } 392 | 393 | function testSelf () { 394 | var it = self(); 395 | 396 | Assert.equals(5, it.next()); 397 | Assert.equals(true, it.next()); 398 | Assert.equals(5, it.next()); 399 | Assert.equals(true, it.next()); 400 | 401 | Assert.equals(false, it.hasNext()); 402 | } 403 | 404 | function self (recusive = true):Iterator { 405 | 406 | var a:Iterator = if (recusive) self(false) else [].iterator(); 407 | 408 | @yield return 5; 409 | 410 | @yield return a.hasNext(); 411 | 412 | @yield return a.next(); 413 | 414 | @yield return a.hasNext(); 415 | } 416 | 417 | function testNoFinalReturn ():Void { 418 | 419 | var result = ""; 420 | 421 | // Display powers of 2 up to the exponent of 8: 422 | for (i in noFinalReturn(2, 8)) { 423 | result += i + " "; 424 | } 425 | 426 | Assert.equals("2 4 8 16 32 64 128 256 ", result); 427 | } 428 | 429 | function noFinalReturn (number:Int, exponent:Int):Iterator { 430 | var result:Int = 1; 431 | 432 | for (i in 0...exponent) { 433 | result = result * number; 434 | @yield return result; 435 | } 436 | } 437 | } --------------------------------------------------------------------------------