├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── TestContinuation.hxml ├── TestContinuation.hxproj ├── TestForkJoin.hxml ├── TestForkJoin.hxproj ├── TestForkMeta.hxml ├── TestForkMeta.hxproj ├── TestGenerator.hxml ├── TestGenerator.hxproj ├── TestIfElse.hxml ├── TestIfElse.hxproj ├── TestMacro.hxml ├── TestMacro.hxproj ├── TestNode.hxml ├── TestNode.hxproj ├── TestTailCall.hxml ├── TestTailCall.hxproj ├── com └── dongxiguo │ └── continuation │ ├── Continuation.hx │ └── utils │ ├── EventSelector.hx │ ├── ForkJoin.hx │ ├── Generator.hx │ ├── Sleep.hx │ ├── StateMachine.hx │ └── Tuple.hx ├── configure ├── haxelib.xml └── tests ├── Counter.hx ├── M.hx ├── Sample.hx ├── TestContinuation.hx ├── TestForkJoin.hx ├── TestForkMeta.hx ├── TestGenerator.hx ├── TestIfElse.hx ├── TestMacro.hx ├── TestNode.hx └── TestTailCall.hx /.gitignore: -------------------------------------------------------------------------------- 1 | /cross-platform.xml 2 | /release.zip 3 | /bin 4 | *.swn 5 | *.swo 6 | *.swp 7 | .* 8 | /dox 9 | /haxelib.json 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | before_install: 4 | - curl -sL https://deb.nodesource.com/setup_5.x | sudo -E bash - 5 | - travis_retry sudo add-apt-repository ppa:eyecreate/haxe -y 6 | - travis_retry sudo apt-get update -qq 7 | - travis_retry sudo apt-get install -qq haxe neko ocaml mono-devel nodejs 8 | - travis_retry sudo haxelib setup .haxelib 9 | - travis_retry sudo haxelib install nodejs 10 | - travis_retry sudo haxelib install hxjava 11 | - travis_retry sudo haxelib install hxcs 12 | - travis_retry sudo haxelib install hxcpp 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, 杨博 (Yang Bo) 2 | All rights reserved. 3 | 4 | Author: 杨博 (Yang Bo) 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | * Neither the name of the nor the names of its contributors 15 | may be used to endorse or promote products derived from this software 16 | without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all release.zip: \ 2 | haxelib.json \ 3 | haxelib.xml \ 4 | LICENSE \ 5 | $(wildcard com/dongxiguo/continuation/*.hx com/dongxiguo/continuation/*/*.hx) 6 | 7 | haxelib.json: haxelib.xml 8 | haxelib convertxml haxelib.xml 9 | 10 | release.zip: 11 | zip -u $@ $^ 12 | 13 | 14 | clean: 15 | $(RM) -r bin release.zip haxedoc.xml haxelib.json 16 | 17 | test: \ 18 | bin/TestContinuation.n bin/TestContinuation.swf bin/TestContinuation.js bin/TestContinuation_cpp \ 19 | bin/TestForkMeta_java bin/TestForkMeta_cs bin/TestForkMeta.swf bin/TestForkMeta.js \ 20 | bin/Sample.swf bin/Sample.js bin/Sample_cs bin/Sample_java bin/Sample_cpp \ 21 | bin/TestNode.js 22 | neko bin/TestContinuation.n 23 | node bin/TestContinuation.js 24 | bin/TestContinuation_cpp/TestContinuation 25 | java -jar bin/TestForkMeta_java/TestForkMeta.jar 26 | mono bin/TestForkMeta_cs/bin/TestForkMeta.exe 27 | node bin/TestForkMeta.js 28 | node bin/Sample.js 29 | mono bin/Sample_cs/bin/Sample.exe 30 | java -jar bin/Sample_java/Sample.jar 31 | bin/Sample_cpp/Sample 32 | node bin/TestNode.js 33 | 34 | 35 | 36 | bin/TestForkMeta_cs: \ 37 | com/dongxiguo/continuation/Continuation.hx \ 38 | com/dongxiguo/continuation/utils/ForkJoin.hx \ 39 | tests/TestForkMeta.hx \ 40 | | bin 41 | haxe -cs $@ -main tests.TestForkMeta 42 | 43 | bin/TestForkMeta_java: \ 44 | com/dongxiguo/continuation/Continuation.hx \ 45 | com/dongxiguo/continuation/utils/ForkJoin.hx \ 46 | tests/TestForkMeta.hx \ 47 | | bin 48 | haxe -java $@ -main tests.TestForkMeta 49 | 50 | bin/TestForkMeta.swf: \ 51 | com/dongxiguo/continuation/Continuation.hx \ 52 | com/dongxiguo/continuation/utils/ForkJoin.hx \ 53 | tests/TestForkMeta.hx \ 54 | | bin 55 | haxe -swf $@ -main tests.TestForkMeta 56 | 57 | bin/TestForkMeta.js: \ 58 | com/dongxiguo/continuation/Continuation.hx \ 59 | com/dongxiguo/continuation/utils/ForkJoin.hx \ 60 | tests/TestForkMeta.hx \ 61 | | bin 62 | haxe -js $@ -main tests.TestForkMeta 63 | 64 | bin/TestContinuation_cpp: \ 65 | com/dongxiguo/continuation/Continuation.hx \ 66 | tests/TestContinuation.hx \ 67 | | bin 68 | haxe -cpp $@ -main tests.TestContinuation 69 | 70 | bin/TestContinuation.n: \ 71 | com/dongxiguo/continuation/Continuation.hx \ 72 | tests/TestContinuation.hx \ 73 | | bin 74 | haxe -neko $@ -main tests.TestContinuation 75 | 76 | bin/TestContinuation.swf: \ 77 | com/dongxiguo/continuation/Continuation.hx \ 78 | tests/TestContinuation.hx \ 79 | | bin 80 | haxe -swf $@ -main tests.TestContinuation 81 | 82 | bin/TestContinuation.js: \ 83 | com/dongxiguo/continuation/Continuation.hx \ 84 | tests/TestContinuation.hx \ 85 | | bin 86 | haxe -js $@ -main tests.TestContinuation 87 | 88 | bin/TestNode.js: \ 89 | com/dongxiguo/continuation/Continuation.hx \ 90 | tests/TestNode.hx \ 91 | | bin 92 | haxe -js $@ -main tests.TestNode -lib nodejs 93 | 94 | bin/Sample_java: \ 95 | com/dongxiguo/continuation/Continuation.hx \ 96 | tests/Sample.hx \ 97 | | bin 98 | $(RM) -r $@ 99 | haxe -java $@ -main tests.Sample 100 | 101 | bin/Sample_cpp: \ 102 | com/dongxiguo/continuation/Continuation.hx \ 103 | tests/Sample.hx \ 104 | | bin 105 | $(RM) -r $@ 106 | haxe -cpp $@ -main tests.Sample 107 | 108 | bin/Sample_cs: \ 109 | com/dongxiguo/continuation/Continuation.hx \ 110 | tests/Sample.hx \ 111 | | bin 112 | $(RM) -r $@ 113 | haxe -cs $@ -main tests.Sample 114 | 115 | bin/Sample.swf: \ 116 | com/dongxiguo/continuation/Continuation.hx \ 117 | tests/Sample.hx \ 118 | | bin 119 | haxe -swf $@ -main tests.Sample 120 | 121 | bin/Sample.js: \ 122 | com/dongxiguo/continuation/Continuation.hx \ 123 | tests/Sample.hx \ 124 | | bin 125 | haxe -js $@ -main tests.Sample 126 | 127 | dox: cross-platform.xml 128 | haxelib run dox \ 129 | --input-path $< \ 130 | --output-path $@ \ 131 | --include '^com(\.dongxiguo(\.continuation(\..*)?)?)?$$' 132 | touch $@ 133 | 134 | cross-platform.xml: \ 135 | $(wildcard com/dongxiguo/continuation/*.hx com/dongxiguo/continuation/*/*.hx) 136 | haxe -D doc-gen -xml $@ $^ 137 | 138 | bin: 139 | mkdir $@ 140 | 141 | .PHONY: all clean test 142 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | haxe-continuation 2 | ================= 3 | 4 | [![Join the chat at https://gitter.im/Atry/haxe-continuation](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Atry/haxe-continuation?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | [![Build Status](https://travis-ci.org/Atry/haxe-continuation.svg?branch=haxe-3.1)](https://travis-ci.org/Atry/haxe-continuation) 6 | 7 | An *asynchronous functions* is a function that accept its last parameter 8 | as a callback function. 9 | And **haxe-continuation** is a macro library enables you to invoke and write 10 | asynchronous functions like synchronization functions, and automatically 11 | transform these functions into *continuation-passing style (CPS)*. That means 12 | you can write code looks like *multithreading* without platform 13 | multithreading support. 14 | 15 | ## Installation 16 | 17 | I have uploaded haxe-continuation on [haxelib](http://lib.haxe.org/p/continuation). 18 | To install, type the following command in shell: 19 | 20 | haxelib install continuation 21 | 22 | Now you can use continuation in your project: 23 | 24 | Output to JavaScript: 25 | 26 | haxe -lib continuation -main Your.hx -js your-output.js 27 | 28 | , or output to SWF: 29 | 30 | haxe -lib continuation -main Your.hx -swf your-output.swf 31 | 32 | , or output to any other platform that Haxe supports. 33 | 34 | haxe-continuation is tested with Haxe 3.1.3. 35 | 36 | ## Usage 37 | 38 | To write a CPS function, put `@:build(com.dongxiguo.continuation.Continuation.cpsByMeta(":async"))` 39 | before a class, and mark the CPS functions in that class as `@:async`: 40 | 41 | ``` haxe 42 | import com.dongxiguo.continuation.Continuation; 43 | @:build(com.dongxiguo.continuation.Continuation.cpsByMeta(":async")) 44 | class Sample 45 | { 46 | 47 | // An asynchronous function without automatical CPS transformation. 48 | static function sleepOneSecond(handler:Void->Void):Void 49 | { 50 | haxe.Timer.delay(handler, 1000); 51 | } 52 | 53 | // The magic @:async transforms this function to: 54 | // static function asyncTest(__return:Void->Void):Void 55 | @:async static function asyncTest():Void 56 | { 57 | trace("Start continuation."); 58 | for (i in 0...10) 59 | { 60 | // Magic @await prefix to invoke an asynchronous function. 61 | @await sleepOneSecond(); 62 | trace("Run sleepOneSecond " + i + " times."); 63 | } 64 | trace("Continuation is done."); 65 | } 66 | 67 | public static function main() 68 | { 69 | asyncTest(function() 70 | { 71 | trace("Handler without continuation."); 72 | }); 73 | } 74 | 75 | } 76 | ``` 77 | 78 | In CPS functions, `@await` is a magic word to invoke other 79 | async functions. When calling an asynchronous function with the `@await` prefix, you need not to explicitly pass a callback 80 | function. Instead, the code after `@await` will be captured as the callback 81 | function for the callee. 82 | 83 | Then you could compile it to JavaScript and run it in Node.js: 84 | 85 | ``` 86 | $ haxe -main Sample -js Sample.js -lib continuation && node Sample.js 87 | Start continuation. 88 | Run sleepOneSecond 0 times. 89 | Run sleepOneSecond 1 times. 90 | Run sleepOneSecond 2 times. 91 | Run sleepOneSecond 3 times. 92 | Run sleepOneSecond 4 times. 93 | Run sleepOneSecond 5 times. 94 | Run sleepOneSecond 6 times. 95 | Run sleepOneSecond 7 times. 96 | Run sleepOneSecond 8 times. 97 | Run sleepOneSecond 9 times. 98 | Continuation is done. 99 | Handler without continuation. 100 | ``` 101 | 102 | You may look into the `Sample.js` generated by Haxe compiler: 103 | 104 | ``` javascript 105 | Sample.asyncTest = function(__return) { 106 | console.log("Start continuation."); 107 | var __iterator_min = 0; 108 | var __iterator_max = 10; 109 | var __doCount = 0; 110 | var __continue_0; 111 | var __continue_01 = null; 112 | __continue_01 = function() { 113 | if(__iterator_min < __iterator_max) { 114 | if(__doCount++ == 0) do (function(i) { 115 | i; 116 | Sample.sleepOneSecond(function() { 117 | console.log("Run sleepOneSecond " + i + " times."); 118 | __continue_01(); 119 | }); 120 | })(__iterator_min++); while(--__doCount != 0); 121 | } else { 122 | console.log("Continuation is done."); 123 | __return(); 124 | } 125 | }; 126 | __continue_0 = __continue_01; 127 | __continue_0(); 128 | }; 129 | Sample.main = function() { 130 | Sample.asyncTest(function() { 131 | console.log("Handler without continuation."); 132 | }); 133 | }; 134 | ``` 135 | 136 | You would notice that the `for` loop and the `@await sleepOneSecond()` in Haxe source now became an asynchronous recursion of function `__continue_01`. 137 | 138 | Another way is using `Continuation.cpsFunction` macro to write nested CPS functions: 139 | 140 | ``` haxe 141 | import com.dongxiguo.continuation.Continuation; 142 | class Sample2 143 | { 144 | // An asynchronous function without automatically CPS transformation. 145 | static function sleepOneSecond(handler:Void->Void):Void 146 | { 147 | haxe.Timer.delay(handler, 1000); 148 | } 149 | public static function main() 150 | { 151 | // This magic macro will transform function asyncTest to: 152 | // function asyncTest(__return:Void->Void):Void 153 | Continuation.cpsFunction(function asyncTest():Void 154 | { 155 | trace("Start continuation."); 156 | for (i in 0...10) 157 | { 158 | // Magic @await prefix to invoke an asynchronous function. 159 | @await sleepOneSecond(); 160 | trace("Run sleepOneSecond " + i + " times."); 161 | } 162 | trace("Continuation is done."); 163 | }); 164 | asyncTest(function() 165 | { 166 | trace("Handler without continuation."); 167 | }); 168 | } 169 | } 170 | ``` 171 | 172 | 173 | See https://github.com/Atry/haxe-continuation/blob/haxe-3.1/tests/TestContinuation.hx 174 | for more examples. 175 | 176 | ### Forking 177 | 178 | Another feature in **haxe-continuation** is *forking*. 179 | The analogy to multithreaded code is instead of processing a list of items serially (one after the other), 180 | you start a thread for each item, and wait for all threads to return. 181 | This can increase performance by issuing several blocking calls at once, 182 | and serving each one as soon as the results are available. 183 | 184 | An example fork: 185 | 186 | ``` haxe 187 | @:build(com.dongxiguo.continuation.Continuation.cpsByMeta(":async")) 188 | class Sample 189 | { 190 | @:async function loadAllFiles(files:Array):Array 191 | { 192 | var output:Array = 193 | [ 194 | // Start a separate "thread" for each element in `files` array 195 | @fork(file in files) 196 | { 197 | // The code block executed by each "thread" 198 | @await loadFile(file); 199 | } 200 | ]; 201 | // At this point, all sub-threads have finished executing. 202 | return output; 203 | } 204 | } 205 | ``` 206 | 207 | ### Working with [hx-node](https://github.com/cloudshift/hx-node) 208 | 209 | Look at https://github.com/Atry/haxe-continuation/blob/haxe-3.1/tests/TestNode.hx. 210 | The example forks 5 threads, and calls Node.js's asynchronous functions in each thread. 211 | 212 | ### Generator 213 | 214 | haxe-continuation also provides an utility to wrap CPS functions into `Iterator`s. 215 | 216 | For example: 217 | 218 | ``` haxe 219 | using com.dongxiguo.continuation.utils.Generator; 220 | @:build(com.dongxiguo.continuation.Continuation.cpsByMeta(":async")) 221 | class TestGenerator 222 | { 223 | @:async 224 | static function intGenerator(yield:YieldFunction):Void 225 | { 226 | for (i in 1...4) 227 | { 228 | for (j in 1...(i+1)) 229 | { 230 | trace('$j * $i ='); 231 | @await yield(i * j); 232 | trace("-------"); 233 | } 234 | } 235 | } 236 | public static function main() 237 | { 238 | for (i in intGenerator) 239 | { 240 | trace(i); 241 | } 242 | } 243 | } 244 | ``` 245 | 246 | The output: 247 | 248 | TestGenerator.hx:47: 1 * 1 = 249 | TestGenerator.hx:59: 1 250 | TestGenerator.hx:49: ------- 251 | TestGenerator.hx:47: 1 * 2 = 252 | TestGenerator.hx:59: 2 253 | TestGenerator.hx:49: ------- 254 | TestGenerator.hx:47: 2 * 2 = 255 | TestGenerator.hx:59: 4 256 | TestGenerator.hx:49: ------- 257 | TestGenerator.hx:47: 1 * 3 = 258 | TestGenerator.hx:59: 3 259 | TestGenerator.hx:49: ------- 260 | TestGenerator.hx:47: 2 * 3 = 261 | TestGenerator.hx:59: 6 262 | TestGenerator.hx:49: ------- 263 | TestGenerator.hx:47: 3 * 3 = 264 | TestGenerator.hx:59: 9 265 | TestGenerator.hx:49: ------- 266 | 267 | ### Working with [Unity](http://unity3d.com/) 268 | 269 | You can use `@await` to create coroutines for Unity. 270 | 271 | ``` haxe 272 | // Must compile with `haxe -lib continuation -net-lib UnityEngine.dll` 273 | 274 | import com.dongxiguo.continuation.utils.Generator; 275 | 276 | @:nativeGen 277 | @:build(com.dongxiguo.continuation.Continuation.cpsByMeta(":async")) 278 | class MyBehaviour extends unityengine.MonoBehaviour 279 | { 280 | 281 | var texture:unityengine.Texture2D; 282 | 283 | @:async function downloadAvatar(yield:YieldFunction):unityengine.Texture2D 284 | { 285 | var url = "https://avatars3.githubusercontent.com/u/601530"; 286 | var www = new unityengine.WWW(url); 287 | // Wait for download to complete 288 | @await yield(www); 289 | return www.texture; 290 | } 291 | 292 | @:async function run(yield:YieldFunction):Void 293 | { 294 | this.texture = @await downloadAvatar(yield); 295 | } 296 | 297 | function Start():Void 298 | { 299 | this.StartCoroutine(Generator.toEnumerator(run)); 300 | } 301 | } 302 | ``` 303 | 304 | The expression `@await downloadAvatar(yield)` shows that the things you can await are not only Unity built-in instructions, but also your own asynchronous functions. Thus, `haxe-continuation` is more powerful than Unity's native C# coroutines. 305 | 306 | ## Links 307 | 308 | * [haxe-continuation API documentation](http://atry.github.io/haxe-continuation/dox/com/dongxiguo/continuation/) 309 | * [Test cases and examples](https://github.com/Atry/haxe-continuation/tree/haxe-3.1/tests) 310 | 311 | ## License 312 | 313 | Copyright (c) 2012, 杨博 (Yang Bo) 314 | All rights reserved. 315 | 316 | Author: 杨博 (Yang Bo) 317 | 318 | Redistribution and use in source and binary forms, with or without 319 | modification, are permitted provided that the following conditions are met: 320 | 321 | * Redistributions of source code must retain the above copyright notice, 322 | this list of conditions and the following disclaimer. 323 | * Redistributions in binary form must reproduce the above copyright notice, 324 | this list of conditions and the following disclaimer in the documentation 325 | and/or other materials provided with the distribution. 326 | * Neither the name of the nor the names of its contributors 327 | may be used to endorse or promote products derived from this software 328 | without specific prior written permission. 329 | 330 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 331 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 332 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 333 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 334 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 335 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 336 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 337 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 338 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 339 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 340 | POSSIBILITY OF SUCH DAMAGE. 341 | -------------------------------------------------------------------------------- /TestContinuation.hxml: -------------------------------------------------------------------------------- 1 | -cp . 2 | -js bin/TestContinuation.js 3 | -main tests.TestContinuation 4 | -------------------------------------------------------------------------------- /TestContinuation.hxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /TestForkJoin.hxml: -------------------------------------------------------------------------------- 1 | -cp . 2 | -lib nodejs 3 | -swf bin/TestNode.swf 4 | -swf-header 0:0:0:FFFFFF 5 | --flash-strict 6 | -swf-version 11 7 | -D nativeTrace 8 | -main tests.TestForkJoin 9 | -------------------------------------------------------------------------------- /TestForkJoin.hxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /TestForkMeta.hxml: -------------------------------------------------------------------------------- 1 | -cp . 2 | -js bin/TestForkMeta.js 3 | -D 4 | -main tests.TestForkMeta 5 | --no-opt 6 | -------------------------------------------------------------------------------- /TestForkMeta.hxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /TestGenerator.hxml: -------------------------------------------------------------------------------- 1 | -cp . 2 | -lib nodejs 3 | -swf bin/TestGenerator.swf 4 | -swf-header 0:0:0:FFFFFF 5 | --flash-strict 6 | -swf-version 11 7 | -D nativeTrace 8 | -main tests.TestGenerator 9 | -------------------------------------------------------------------------------- /TestGenerator.hxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /TestIfElse.hxml: -------------------------------------------------------------------------------- 1 | -cp . 2 | -js bin/TestIfElse.js 3 | -main tests.TestIfElse 4 | -------------------------------------------------------------------------------- /TestIfElse.hxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /TestMacro.hxml: -------------------------------------------------------------------------------- 1 | -cp . 2 | -js bin/TestMacro.js 3 | -main tests.TestMacro 4 | -------------------------------------------------------------------------------- /TestMacro.hxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /TestNode.hxml: -------------------------------------------------------------------------------- 1 | -cp . 2 | -lib nodejs 3 | -js bin/TestNode.js 4 | -D nativeTrace 5 | -main tests.TestNode 6 | -------------------------------------------------------------------------------- /TestNode.hxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /TestTailCall.hxml: -------------------------------------------------------------------------------- 1 | -cp . 2 | -swf bin/TestTailCall.swf 3 | -swf-header 0:0:0:FFFFFF 4 | --flash-strict 5 | -swf-version 11 6 | -main tests.TestTailCall 7 | --no-inline 8 | -------------------------------------------------------------------------------- /TestTailCall.hxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /com/dongxiguo/continuation/Continuation.hx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012-2014, 杨博 (Yang Bo) 2 | // All rights reserved. 3 | // 4 | // Author: 杨博 (Yang Bo) 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // * Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | // * Neither the name of the nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package com.dongxiguo.continuation; 31 | 32 | #if macro 33 | import haxe.macro.Context; 34 | import haxe.macro.Type; 35 | import haxe.macro.Expr; 36 | import haxe.ds.GenericStack.GenericCell; 37 | #end 38 | 39 | using Lambda; 40 | 41 | @:final 42 | class Continuation 43 | { 44 | /** 45 | Wrap a function to a CPS function. 46 | 47 | In the wrapped function, you can use `.async()` suffix to invoke other 48 | asynchronous functions. 49 | **/ 50 | @:noUsing 51 | macro 52 | public static function cpsFunction(expr:Expr):Expr 53 | { 54 | switch (expr.expr) 55 | { 56 | case EFunction(name, f): 57 | { 58 | var originExpr = f.expr; 59 | return 60 | { 61 | pos: expr.pos, 62 | expr: EFunction( 63 | name, 64 | { 65 | ret: TPath( 66 | { 67 | sub: null, 68 | params: [], 69 | pack: [], 70 | name: "Void" 71 | }), 72 | params: f.params, 73 | args: f.args.concat( 74 | [ 75 | { 76 | name: "__return", 77 | opt: false, 78 | value: null, 79 | type: f.ret == null ? null : TFunction( 80 | [ f.ret ], 81 | TPath( 82 | { 83 | sub: null, 84 | params: [], 85 | pack: [], 86 | name: "Void" 87 | })) 88 | } 89 | ]), 90 | expr: ContinuationDetail.transform( 91 | originExpr, 92 | ANY, 93 | function(transformed:Array) 94 | { 95 | transformed.push( 96 | { 97 | expr: ECall(macro __return, []), 98 | pos: originExpr.pos, 99 | }); 100 | return 101 | { 102 | pos: originExpr.pos, 103 | expr: EBlock(transformed), 104 | } 105 | }) 106 | }) 107 | }; 108 | } 109 | default: 110 | { 111 | throw "Expect function."; 112 | } 113 | } 114 | } 115 | 116 | /** 117 | A [build macro](http://haxe.org/manual/macro-type-building.html) that 118 | enables CPS transformation for the annotated class. 119 | 120 | In 121 | `@:build(com.dongxiguo.continuation.Continuation.cpsByMeta("youMeta")) 122 | class YourClass`, 123 | all `@youMeta` methods in `YourClass` will be transfromed to CPS functions. 124 | In these `@youMeta` methods, 125 | some macros are performed to enable the magic `.async()` suffix syntax that 126 | invokes other asynchronous functions. 127 | **/ 128 | @:noUsing 129 | macro 130 | public static function cpsByMeta(metaName:String):Array 131 | { 132 | var bf = Context.getBuildFields(); 133 | for (field in bf) 134 | { 135 | switch (field.kind) 136 | { 137 | case FFun(f): 138 | { 139 | var originReturnType = f.ret; 140 | for (m in field.meta) 141 | { 142 | if (m.name == metaName) 143 | { 144 | f.args = f.args.concat( 145 | [ 146 | { 147 | name: "__return", 148 | opt: false, 149 | value: null, 150 | type: originReturnType == null ? null : TFunction( 151 | [ originReturnType ], 152 | TPath( 153 | { 154 | sub: null, 155 | params: [], 156 | pack: [], 157 | name: "Void" 158 | })) 159 | } 160 | ]); 161 | f.ret = TPath( 162 | { 163 | sub: null, 164 | params: [], 165 | pack: [], 166 | name: "Void" 167 | }); 168 | var originExpr = f.expr; 169 | f.expr = ContinuationDetail.transform( 170 | originExpr, 171 | ANY, 172 | function(transformed) 173 | { 174 | transformed.push( 175 | { 176 | expr: ECall(macro __return, []), 177 | pos: originExpr.pos, 178 | }); 179 | return 180 | { 181 | pos: originExpr.pos, 182 | expr: EBlock(transformed), 183 | } 184 | }); 185 | break; 186 | } 187 | } 188 | } 189 | default: 190 | { 191 | continue; 192 | } 193 | } 194 | } 195 | return bf; 196 | } 197 | 198 | } 199 | 200 | /** 201 | For internal use only. Don't access it immediately. 202 | **/ 203 | @:final 204 | @:dox(hide) 205 | class ContinuationDetail 206 | { 207 | #if macro 208 | 209 | static function pushMulti( 210 | gc:GenericCell, 211 | a:Array):GenericCell 212 | { 213 | for (e in a) 214 | { 215 | gc = new GenericCell(e, gc); 216 | } 217 | return gc; 218 | } 219 | 220 | static function toReverseArray(gc:GenericCell):Array 221 | { 222 | var result = []; 223 | while (gc != null) 224 | { 225 | result.unshift(gc.elt); 226 | gc = gc.next; 227 | } 228 | return result; 229 | } 230 | 231 | static var seed:Int = 0; 232 | 233 | static function unpack(exprs: Array, pos: Position):Expr 234 | { 235 | if (exprs.length != 1) 236 | { 237 | Context.error("Expect one return value, but there are " + exprs.length + 238 | " return values.", pos); 239 | } 240 | return exprs[0]; 241 | } 242 | 243 | static function unblock(expr:Expr):Expr 244 | { 245 | switch (expr.expr) 246 | { 247 | case EBlock([ b ]): return unblock(b); 248 | case _: return expr; 249 | } 250 | } 251 | 252 | static function transformTailCallAwait(e:Expr, originParams:Array, rest:Array->Expr):Expr 253 | { 254 | // 优化 e 是另一个异步函数的情况 255 | function transformNext(i:Int, transformedParameters:Null>):Expr 256 | { 257 | if (i == originParams.length) 258 | { 259 | return transformNoDelay(e, ANY, function(functionResult) 260 | { 261 | var a = toReverseArray(transformedParameters); 262 | a.push( 263 | { 264 | expr: EConst(CIdent("__return")), 265 | pos: e.pos 266 | }); 267 | return 268 | { 269 | pos: e.pos, 270 | expr: ECall( 271 | unpack(functionResult, e.pos), 272 | a), 273 | }; 274 | }); 275 | } 276 | else 277 | { 278 | return transformNoDelay( 279 | originParams[i], 280 | ANY, 281 | function(parameterResult:Array):Expr 282 | { 283 | return transformNext( 284 | i + 1, 285 | pushMulti( 286 | transformedParameters, 287 | parameterResult)); 288 | }); 289 | } 290 | } 291 | return transformNext(0, null); 292 | } 293 | 294 | static function transformAwait(e:Expr, originParams:Array, rest:Array->Expr):Expr 295 | { 296 | function transformNext(i:Int, transformedParameters:Null>):Expr 297 | { 298 | if (i == originParams.length) 299 | { 300 | return transformNoDelay(e, ANY, function(functionResult) 301 | { 302 | var transformedCalleeExpr = unpack(functionResult, e.pos); 303 | var typingExpr = switch (transformedCalleeExpr.expr) 304 | { 305 | case EField(e, fieldName): 306 | { 307 | switch (e.expr) 308 | { 309 | case EConst(c): 310 | { 311 | switch (c) 312 | { 313 | case CIdent(s): 314 | { 315 | if (s == "super") 316 | { 317 | { 318 | pos: transformedCalleeExpr.pos, 319 | expr: EField(macro this, fieldName), 320 | } 321 | } 322 | else 323 | { 324 | transformedCalleeExpr; 325 | } 326 | } 327 | default: transformedCalleeExpr; 328 | } 329 | } 330 | default: transformedCalleeExpr; 331 | } 332 | } 333 | default: transformedCalleeExpr; 334 | } 335 | var handlerArgResult = []; 336 | var handlerArgDefs = []; 337 | var functionType = try 338 | { 339 | Context.follow(Context.typeof(typingExpr)); 340 | } 341 | catch (_:Dynamic) 342 | { 343 | null; 344 | } 345 | if (functionType == null) 346 | { 347 | var name = "__parameter_" + seed++; 348 | handlerArgResult.push( 349 | { 350 | pos: e.pos, 351 | expr: EConst(CIdent(name)) 352 | }); 353 | handlerArgDefs.push( 354 | { 355 | opt: true, 356 | name: name, 357 | type: null, 358 | value: null 359 | }); 360 | } 361 | else 362 | { 363 | switch (functionType) 364 | { 365 | case TFun(args, _): 366 | { 367 | switch (Context.follow(args[args.length - 1].t)) 368 | { 369 | case TFun(args, _): 370 | { 371 | for (handlerArg in args) 372 | { 373 | var name = "__parameter_" + seed++; 374 | handlerArgResult.push( 375 | { 376 | pos: e.pos, 377 | expr: EConst(CIdent(name)) 378 | }); 379 | handlerArgDefs.push( 380 | { 381 | opt: handlerArg.opt, 382 | name: name, 383 | type: haxe.macro.TypeTools.toComplexType(handlerArg.t), 384 | value: null 385 | }); 386 | } 387 | } 388 | default: 389 | { 390 | var name = "__parameter_" + seed++; 391 | handlerArgResult.push( 392 | { 393 | pos: e.pos, 394 | expr: EConst(CIdent(name)) 395 | }); 396 | handlerArgDefs.push( 397 | { 398 | opt: true, 399 | name: name, 400 | type: null, 401 | value: null 402 | }); 403 | } 404 | } 405 | } 406 | default: 407 | { 408 | return Context.error("Expect function.", e.pos); 409 | } 410 | } 411 | } 412 | var a = toReverseArray(transformedParameters); 413 | a.push( 414 | { 415 | pos: e.pos, 416 | expr: EFunction(null, 417 | { 418 | ret: null, 419 | params: [], 420 | expr: rest(handlerArgResult), 421 | args: handlerArgDefs 422 | }) 423 | }); 424 | return 425 | { 426 | pos: e.pos, 427 | expr: ECall(transformedCalleeExpr, a), 428 | }; 429 | }); 430 | } 431 | else 432 | { 433 | return transformNoDelay( 434 | originParams[i], 435 | ANY, 436 | function(parameterResult:Array):Expr 437 | { 438 | return transformNext( 439 | i + 1, 440 | pushMulti( 441 | transformedParameters, 442 | parameterResult)); 443 | }); 444 | } 445 | } 446 | return transformNext(0, null); 447 | } 448 | 449 | static function transformCondition( 450 | pos:Position, 451 | econd:Expr, 452 | eif:Expr, 453 | eelse:Null, 454 | parameterRequirement:ParameterRequirement, 455 | rest:Array->Expr):Expr 456 | { 457 | var wrapper = new Wrapper(parameterRequirement, rest, false, "__endIf_"); 458 | return transformNoDelay( 459 | econd, 460 | EXACT(1), 461 | function(econdResult) 462 | { 463 | var declearation = wrapper.declearation; 464 | var entry = 465 | { 466 | pos: pos, 467 | expr: EIf( 468 | unpack(econdResult, econd.pos), 469 | transformNoDelay(eif, ANY, wrapper.invocation), 470 | eelse == null ? wrapper.invocation([]) : transformNoDelay(eelse, ANY, wrapper.invocation)), 471 | }; 472 | return macro { $declearation; $entry; } 473 | }); 474 | } 475 | 476 | public static function transform( 477 | origin:Expr, 478 | parameterRequirement:ParameterRequirement, 479 | rest:Array->Expr):Expr 480 | { 481 | return delay( 482 | origin.pos, 483 | function() 484 | { 485 | return transformNoDelay(origin, parameterRequirement, rest); 486 | }); 487 | } 488 | 489 | static function transformNoDelay( 490 | origin:Expr, 491 | parameterRequirement:ParameterRequirement, 492 | rest:Array->Expr):Expr 493 | { 494 | switch (origin.expr) 495 | { 496 | case EMeta({ name: "await", params: [] }, { expr: ECall(functionExpr, params) }): 497 | { 498 | return transformAwait(functionExpr, params, rest); 499 | } 500 | case EMeta({ name: "fork", params: [ { expr: EIn( { expr: EConst(CIdent(variableName)) }, idendifiers) } ] }, forkBody): 501 | { 502 | var afterForkExpr = rest([]); 503 | var transformedBody = transformNoDelay(forkBody, IGNORE, function(exprs) return 504 | { 505 | expr: EBlock(exprs.concat([macro __checkCounter()])), 506 | pos: origin.pos, 507 | }); 508 | return macro 509 | { 510 | var __iterator = com.dongxiguo.continuation.Continuation.ContinuationDetail.toIterator($idendifiers); 511 | if (com.dongxiguo.continuation.Continuation.ContinuationDetail.hasNext($idendifiers, __iterator)) 512 | { 513 | var __counter = 1; 514 | function __checkCounter():Void if (--__counter == 0) $afterForkExpr; 515 | do 516 | { 517 | var $variableName = com.dongxiguo.continuation.Continuation.ContinuationDetail.next($idendifiers, __iterator); 518 | __counter++; 519 | $transformedBody; 520 | } 521 | while (com.dongxiguo.continuation.Continuation.ContinuationDetail.hasNext($idendifiers, __iterator)); 522 | __checkCounter(); 523 | } 524 | }; 525 | } 526 | case EMeta(s, e): 527 | { 528 | return rest([origin]); 529 | } 530 | case EWhile(econd, e, normalWhile): 531 | { 532 | var continueName = "__continue_" + seed++; 533 | var continueIdent = 534 | { 535 | pos: origin.pos, 536 | expr: EConst(CIdent(continueName)) 537 | }; 538 | var breakName = "__break_" + seed++; 539 | #if no_inline 540 | var inlineBreakName = breakName; 541 | #else 542 | var inlineBreakName = "inline_" + breakName; 543 | #end 544 | var breakIdent = 545 | { 546 | pos: origin.pos, 547 | expr: EConst(CIdent(breakName)) 548 | }; 549 | var doBody = transform(e, 550 | IGNORE, 551 | function(eResult) 552 | { 553 | return 554 | { 555 | pos: origin.pos, 556 | expr: EBlock(eResult.concat([ macro $continueIdent()])) 557 | }; 558 | }); 559 | var continueBody = transform( 560 | econd, 561 | EXACT(1), 562 | function(econdResult) 563 | { 564 | return 565 | { 566 | pos: origin.pos, 567 | expr: EIf( 568 | unpack(econdResult, econd.pos), 569 | macro __do(), 570 | macro $breakIdent()) 571 | }; 572 | }); 573 | var breakBody = rest([]); 574 | if (normalWhile) 575 | { 576 | return macro 577 | { 578 | var __doCount = 0; 579 | function $inlineBreakName():Void 580 | { 581 | $breakBody; 582 | } 583 | function $continueName():Void 584 | { 585 | #if no_inline #else inline #end function __do() 586 | { 587 | #if no_inline #else inline #end function __break() 588 | { 589 | $breakIdent(); 590 | } 591 | #if no_inline #else inline #end function __continue() 592 | { 593 | $continueIdent(); 594 | } 595 | if (__doCount++ == 0) 596 | { 597 | // Check reenter; 598 | do 599 | { 600 | $doBody; 601 | } 602 | while (--__doCount != 0); 603 | } 604 | } 605 | $continueBody; 606 | } 607 | $continueIdent(); 608 | } 609 | } 610 | else 611 | { 612 | #if no_inline 613 | var inlineContinueName = continueName; 614 | #else 615 | var inlineContinueName = "inline_" + continueName; 616 | #end 617 | return macro 618 | { 619 | var __doCount = 0; 620 | function $inlineBreakName():Void 621 | { 622 | $breakBody; 623 | } 624 | function __do() 625 | { 626 | function $inlineContinueName():Void 627 | { 628 | $continueBody; 629 | } 630 | #if no_inline #else inline #end function __break() 631 | { 632 | $breakIdent(); 633 | } 634 | #if no_inline #else inline #end function __continue() 635 | { 636 | $continueIdent(); 637 | } 638 | if (__doCount++ == 0) 639 | { 640 | // Check reenter; 641 | do 642 | { 643 | $doBody; 644 | } 645 | while (--__doCount != 0); 646 | } 647 | } 648 | __do(); 649 | } 650 | } 651 | } 652 | case EVars(originVars): 653 | { 654 | var functionName = "__afterVar_" + seed++; 655 | function transformNext(i:Int, values:Array>):Expr 656 | { 657 | if (i == originVars.length) 658 | { 659 | return 660 | { 661 | pos: origin.pos, 662 | expr: ECall( 663 | { 664 | pos: origin.pos, 665 | expr: EConst(CIdent(functionName)), 666 | }, 667 | values), 668 | } 669 | } 670 | else 671 | { 672 | var originVar = originVars[i]; 673 | if (originVar.expr == null) 674 | { 675 | return transformNext(i + 1, values); 676 | } 677 | else 678 | { 679 | return transform(originVar.expr, ANY, function(varResult) 680 | { 681 | var v = values.concat([]); 682 | if (i + 1 < varResult.length) 683 | { 684 | return Context.error( 685 | "Expect " + varResult.length + " variable declarations.", 686 | origin.pos); 687 | } 688 | for (j in 0...varResult.length) 689 | { 690 | var slot = j + i + 1 - varResult.length; 691 | if (v[slot] == null) 692 | { 693 | var originVarJ = originVars[j]; 694 | v[slot] = 695 | if (originVarJ.type == null) 696 | { 697 | varResult[j]; 698 | } 699 | else 700 | { 701 | pos: origin.pos, 702 | expr: ECheckType(varResult[j], originVarJ.type), 703 | } 704 | } 705 | else 706 | { 707 | return Context.error( 708 | "Expect " + varResult.length + " variable declarations.", 709 | origin.pos); 710 | } 711 | } 712 | return transformNext(i + 1, v); 713 | }); 714 | } 715 | } 716 | } 717 | var entry = transformNext(0, []); 718 | return 719 | { 720 | pos: origin.pos, 721 | expr: ECall( 722 | { 723 | pos: origin.pos, 724 | expr: EParenthesis( 725 | { 726 | pos: origin.pos, 727 | expr: EFunction(null, 728 | { 729 | ret: null, 730 | expr: entry, 731 | params: [], 732 | args: 733 | [ 734 | { 735 | name: functionName, 736 | opt: false, 737 | type: null, 738 | value: null, 739 | }, 740 | ], 741 | }), 742 | }), 743 | }, 744 | [ 745 | { 746 | pos: origin.pos, 747 | expr: EFunction( 748 | null, 749 | { 750 | params: [], 751 | args: 752 | { 753 | var functionArgs = []; 754 | for (originVar in originVars) 755 | { 756 | functionArgs.push( 757 | { 758 | name: originVar.name, 759 | opt: false, 760 | type: originVar.type, 761 | value: null, 762 | }); 763 | } 764 | functionArgs; 765 | }, 766 | ret: null, 767 | expr: rest([]) 768 | }), 769 | }, 770 | ]), 771 | } 772 | } 773 | case EUntyped(e): 774 | { 775 | return transformNoDelay( 776 | e, 777 | EXACT(1), 778 | function(eResult) 779 | { 780 | return rest( 781 | [ 782 | { 783 | pos: origin.pos, 784 | expr: EUntyped(unpack(eResult, origin.pos)) 785 | } 786 | ]); 787 | }); 788 | } 789 | case EUnop(op, postFix, e): 790 | { 791 | return transformNoDelay( 792 | e, 793 | EXACT(1), 794 | function(eResult) 795 | { 796 | return rest( 797 | [ 798 | { 799 | pos: origin.pos, 800 | expr: EUnop(op, postFix, unpack(eResult, origin.pos)) 801 | } 802 | ]); 803 | }); 804 | } 805 | case ETry(e, catches): 806 | { 807 | var endTryName = "__endTry_" + seed++; 808 | var endTryIdent = 809 | { 810 | pos: origin.pos, 811 | expr: EConst(CIdent(endTryName)) 812 | } 813 | var isVoidTry = switch (Context.follow(Context.typeof(e))) 814 | { 815 | case TAbstract(t, params): 816 | { 817 | if (params.length != 0) 818 | { 819 | false; 820 | } 821 | else 822 | { 823 | var voidType = t.get(); 824 | voidType.module == "StdTypes" && voidType.name == "Void"; 825 | } 826 | } 827 | default: false; 828 | } 829 | var tryResultName = "__tryResult_" + seed++; 830 | var tryResultIdent = 831 | { 832 | pos: origin.pos, 833 | expr: EConst(CIdent(tryResultName)) 834 | } 835 | var endTryFunction = 836 | { 837 | pos: origin.pos, 838 | expr: EFunction( 839 | endTryName, 840 | { 841 | ret: null, 842 | params: [], 843 | expr: rest(isVoidTry ? [] : [ tryResultIdent ]), 844 | args: isVoidTry ? [] : 845 | [ 846 | { 847 | name: tryResultName, 848 | opt: true, 849 | type: null, 850 | value: null 851 | } 852 | ] 853 | }) 854 | } 855 | var tryBody = isVoidTry ? (macro { $e; __noException = true; }) : (macro { $tryResultIdent = $e; __noException = true; }); 856 | var transformedTry = 857 | { 858 | pos: origin.pos, 859 | expr: ETry(tryBody, catches.map( 860 | function(catchBody) 861 | { 862 | return 863 | { 864 | expr: transformNoDelay( 865 | catchBody.expr, 866 | parameterRequirement, 867 | function(catchResult) 868 | { 869 | switch (catchResult.length) 870 | { 871 | case 1: 872 | { 873 | return 874 | { 875 | pos: catchBody.expr.pos, 876 | expr: ECall( 877 | endTryIdent, isVoidTry ? [] : 878 | [ 879 | { 880 | pos: catchBody.expr.pos, 881 | expr: ECast( 882 | unpack(catchResult, catchBody.expr.pos), 883 | null) 884 | } 885 | ]) 886 | }; 887 | } 888 | default: 889 | { 890 | return 891 | { 892 | pos: origin.pos, 893 | expr: ECall(endTryIdent, catchResult) 894 | }; 895 | } 896 | } 897 | }), 898 | type: catchBody.type, 899 | name: catchBody.name 900 | } 901 | } 902 | ).array()) 903 | } 904 | return 905 | isVoidTry ? 906 | macro 907 | { 908 | $endTryFunction; 909 | var __noException = false; 910 | $transformedTry; 911 | if (__noException) 912 | { 913 | $endTryIdent(); 914 | } 915 | } : 916 | macro 917 | { 918 | $endTryFunction; 919 | var __noException = false; 920 | var $tryResultName = cast null; 921 | $transformedTry; 922 | if (__noException) 923 | { 924 | $endTryIdent($tryResultIdent); 925 | } 926 | }; 927 | } 928 | case EThrow(e): 929 | { 930 | return transformNoDelay( 931 | e, 932 | EXACT(1), 933 | function(eResult) 934 | { 935 | return rest( 936 | [ 937 | { 938 | pos: origin.pos, 939 | expr: EThrow(unpack(eResult, origin.pos)) 940 | } 941 | ]); 942 | }); 943 | } 944 | case ETernary(econd, eif, eelse): 945 | { 946 | return transformCondition(origin.pos, econd, eif, eelse, parameterRequirement, rest); 947 | } 948 | case ESwitch(e, cases, edef): 949 | { 950 | var wrapper = new Wrapper(parameterRequirement, rest, false, "__endSwitch_"); 951 | return transformNoDelay(e, EXACT(1), function(eResult) 952 | { 953 | function transformGuard(guard:Null):Expr 954 | { 955 | // Workaround to enable default: 956 | return parameterRequirement == IGNORE && guard == null ? macro true : guard; 957 | } 958 | var transformedCases = cases.map(function(c) 959 | { 960 | if (c.expr == null) 961 | { 962 | return { expr: wrapper.invocation([]), guard: transformGuard(c.guard), values: c.values }; 963 | } 964 | else 965 | { 966 | return { expr: transform(c.expr, ANY, wrapper.invocation), guard: transformGuard(c.guard), values: c.values }; 967 | } 968 | }).array(); 969 | var entry = if (edef == null) 970 | { 971 | pos: origin.pos, 972 | expr: ESwitch( 973 | unpack(eResult, e.pos), 974 | transformedCases, 975 | parameterRequirement == IGNORE ? wrapper.invocation([]) : null), 976 | } 977 | else if (edef.expr == null) 978 | { 979 | pos: origin.pos, 980 | expr: ESwitch(unpack(eResult, e.pos), transformedCases, wrapper.invocation([])), 981 | } 982 | else 983 | { 984 | var transformedDef = transform(edef, ANY, wrapper.invocation); 985 | { 986 | pos: origin.pos, 987 | expr: ESwitch(unpack(eResult, e.pos), transformedCases, macro { $transformedDef; } ), 988 | } 989 | } 990 | var declearation = wrapper.declearation; 991 | return macro 992 | { 993 | $declearation; 994 | $entry; 995 | } 996 | }); 997 | } 998 | case EReturn(returnExpr): 999 | { 1000 | if (returnExpr == null) 1001 | { 1002 | return 1003 | { 1004 | pos: origin.pos, 1005 | expr: ECall( 1006 | { 1007 | pos: origin.pos, 1008 | expr: EConst(CIdent("__return")) 1009 | }, 1010 | []) 1011 | }; 1012 | } 1013 | switch (returnExpr.expr) 1014 | { 1015 | case EMeta(s, e): 1016 | { 1017 | switch (e.expr) 1018 | { 1019 | case ECall(functionExpr, params): 1020 | { 1021 | if (s.name == "await" && s.params.empty()) 1022 | { 1023 | return transformTailCallAwait(functionExpr, params, rest); 1024 | } 1025 | else 1026 | { 1027 | return rest([origin]); 1028 | } 1029 | } 1030 | default: 1031 | { 1032 | return rest([origin]); 1033 | } 1034 | } 1035 | } 1036 | case ECall(e, originParams): 1037 | { 1038 | if (originParams.length == 0) 1039 | { 1040 | switch (e.expr) 1041 | { 1042 | case EField(prefixCall, field): 1043 | { 1044 | if (field == "async") 1045 | { 1046 | switch (prefixCall.expr) 1047 | { 1048 | case ECall(e, originParams): 1049 | { 1050 | Context.warning("`.async()` is deprecated. Please use `@await` instead.", origin.pos); 1051 | return transformTailCallAwait(e, originParams, rest); 1052 | } 1053 | default: 1054 | } 1055 | } 1056 | } 1057 | default: 1058 | } 1059 | } 1060 | } 1061 | default: 1062 | } 1063 | return transformNoDelay( 1064 | returnExpr, 1065 | ANY, 1066 | function(eResult) 1067 | { 1068 | return 1069 | { 1070 | pos: origin.pos, 1071 | expr: ECall( 1072 | { 1073 | pos: origin.pos, 1074 | expr: EConst(CIdent("__return")) 1075 | }, 1076 | eResult) 1077 | }; 1078 | }); 1079 | } 1080 | case EParenthesis(e): 1081 | { 1082 | return transformNoDelay( 1083 | e, 1084 | EXACT(1), 1085 | function(transformedExprs) 1086 | { 1087 | return rest( 1088 | [ 1089 | { 1090 | pos: origin.pos, 1091 | expr: EParenthesis(unpack(transformedExprs, origin.pos)), 1092 | }, 1093 | ]); 1094 | }); 1095 | } 1096 | case EObjectDecl(originFields): 1097 | { 1098 | function transformNext(i:Int, transformedFields:Null>):Expr 1099 | { 1100 | if (i == originFields.length) 1101 | { 1102 | return rest( 1103 | [ 1104 | { 1105 | pos: origin.pos, 1106 | expr: EObjectDecl(toReverseArray(transformedFields)), 1107 | } 1108 | ]); 1109 | } 1110 | else 1111 | { 1112 | var originField = originFields[i]; 1113 | return transformNoDelay( 1114 | originField.expr, 1115 | EXACT(1), 1116 | function(valueResult:Array):Expr 1117 | { 1118 | var t = transformedFields; 1119 | for (e in valueResult) 1120 | { 1121 | t = new GenericCell( 1122 | { 1123 | field: originField.field, 1124 | expr: unpack(valueResult, originField.expr.pos), 1125 | }, 1126 | t); 1127 | } 1128 | return transformNext(i + 1, t); 1129 | }); 1130 | } 1131 | } 1132 | return transformNext(0, null); 1133 | } 1134 | case ENew(t, originParams): 1135 | { 1136 | function transformNext(i:Int, transformedParameters:Null>):Expr 1137 | { 1138 | if (i == originParams.length) 1139 | { 1140 | return rest( 1141 | [ 1142 | { 1143 | pos: origin.pos, 1144 | expr: ENew( 1145 | t, 1146 | toReverseArray(transformedParameters)), 1147 | } 1148 | ]); 1149 | } 1150 | else 1151 | { 1152 | return transformNoDelay( 1153 | originParams[i], 1154 | ANY, 1155 | function(parameterResult:Array):Expr 1156 | { 1157 | return transformNext( 1158 | i + 1, 1159 | pushMulti( 1160 | transformedParameters, 1161 | parameterResult)); 1162 | }); 1163 | } 1164 | } 1165 | return transformNext(0, null); 1166 | } 1167 | case EIn(_, _): 1168 | { 1169 | // Unsupported. Don't change it. 1170 | return rest([origin]); 1171 | } 1172 | case EIf(econd, eif, eelse): 1173 | { 1174 | return transformCondition(origin.pos, econd, eif, eelse, parameterRequirement, rest); 1175 | } 1176 | case EFunction(_, _): 1177 | { 1178 | return rest([origin]); 1179 | } 1180 | case EFor(it, expr): 1181 | { 1182 | switch (it.expr) 1183 | { 1184 | case EIn(e1, e2): 1185 | { 1186 | var elementName = 1187 | switch (e1.expr) 1188 | { 1189 | case EConst(c): 1190 | switch (c) 1191 | { 1192 | case CIdent(s): 1193 | { 1194 | s; 1195 | } 1196 | default: 1197 | { 1198 | Context.error("Expect identify before \"in\".", e1.pos); 1199 | } 1200 | } 1201 | default: 1202 | { 1203 | Context.error("Expect identify before \"in\".", e1.pos); 1204 | } 1205 | } 1206 | var toIteratorExpr = 1207 | { 1208 | expr: ECall( 1209 | macro com.dongxiguo.continuation.Continuation.ContinuationDetail.toIterator, 1210 | [ e2 ]), 1211 | pos: Context.currentPos(), 1212 | } 1213 | var hasNextExpr = 1214 | { 1215 | expr: ECall( 1216 | macro com.dongxiguo.continuation.Continuation.ContinuationDetail.hasNext, 1217 | [ e2, macro __iterator ]), 1218 | pos: Context.currentPos(), 1219 | } 1220 | var nextExpr = 1221 | { 1222 | expr: ECall( 1223 | macro com.dongxiguo.continuation.Continuation.ContinuationDetail.next, 1224 | [ e2, macro __iterator ]), 1225 | pos: Context.currentPos(), 1226 | } 1227 | var body = transformNoDelay( 1228 | macro while ($hasNextExpr) 1229 | { 1230 | var $elementName = $nextExpr; 1231 | $expr; 1232 | }, 1233 | IGNORE, 1234 | rest); 1235 | return macro { var __iterator = $toIteratorExpr; $body; }; 1236 | } 1237 | default: 1238 | { 1239 | Context.error("Expect \"e1 in e2\"", it.pos); 1240 | return null; 1241 | } 1242 | } 1243 | } 1244 | case EField(e, field): 1245 | { 1246 | return transformNoDelay( 1247 | e, 1248 | EXACT(1), 1249 | function(eResult) 1250 | { 1251 | return rest( 1252 | [ 1253 | { 1254 | pos: origin.pos, 1255 | expr: EField(unpack(eResult, origin.pos), field) 1256 | } 1257 | ]); 1258 | }); 1259 | } 1260 | case EDisplayNew(_): 1261 | { 1262 | return rest([origin]); 1263 | } 1264 | case EDisplay(_, _): 1265 | { 1266 | return rest([origin]); 1267 | } 1268 | case EContinue: 1269 | { 1270 | return macro __continue(); 1271 | } 1272 | case EConst(_): 1273 | { 1274 | return rest([origin]); 1275 | } 1276 | case ECheckType(e, t): 1277 | { 1278 | return transformNoDelay( 1279 | e, 1280 | EXACT(1), 1281 | function(eResult) 1282 | { 1283 | return rest( 1284 | [ 1285 | { 1286 | pos: origin.pos, 1287 | expr: ECheckType(unpack(eResult, e.pos), t) 1288 | } 1289 | ]); 1290 | }); 1291 | } 1292 | case ECast(e, t): 1293 | { 1294 | return transformNoDelay( 1295 | e, 1296 | EXACT(1), 1297 | function(eResult) 1298 | { 1299 | return rest( 1300 | [ 1301 | { 1302 | pos: origin.pos, 1303 | expr: ECast(unpack(eResult, e.pos), t) 1304 | } 1305 | ]); 1306 | }); 1307 | } 1308 | case ECall(callExpr, originParams): 1309 | { 1310 | if (originParams.length == 0) 1311 | { 1312 | switch (callExpr.expr) 1313 | { 1314 | case EField(prefixCall, field): 1315 | { 1316 | if (field == "async") 1317 | { 1318 | switch (prefixCall.expr) 1319 | { 1320 | case ECall(e, originParams): 1321 | { 1322 | Context.warning("`.async()` is deprecated. Please use `@await` instead.", origin.pos); 1323 | return transformAwait(e, originParams, rest); 1324 | } 1325 | default: 1326 | } 1327 | } 1328 | } 1329 | default: 1330 | } 1331 | } 1332 | function transformNext(i:Int, transformedParameters:Null>):Expr 1333 | { 1334 | if (i == originParams.length) 1335 | { 1336 | return transformNoDelay(callExpr, EXACT(1), function(functionResult) 1337 | { 1338 | var handlerArgResult = []; 1339 | var handlerArgDefs = []; 1340 | return rest([ 1341 | { 1342 | pos: origin.pos, 1343 | expr: ECall( 1344 | unpack(functionResult, origin.pos), 1345 | toReverseArray(transformedParameters)), 1346 | }]); 1347 | }); 1348 | } 1349 | else 1350 | { 1351 | return transformNoDelay( 1352 | originParams[i], 1353 | ANY, 1354 | function(parameterResult:Array):Expr 1355 | { 1356 | return transformNext( 1357 | i + 1, 1358 | pushMulti( 1359 | transformedParameters, 1360 | parameterResult)); 1361 | }); 1362 | } 1363 | } 1364 | return transformNext(0, null); 1365 | } 1366 | case EBreak: 1367 | { 1368 | return 1369 | { 1370 | pos: origin.pos, 1371 | expr: ECall( 1372 | { 1373 | pos: origin.pos, 1374 | expr: EConst(CIdent("__break")), 1375 | }, 1376 | []) 1377 | }; 1378 | } 1379 | case EBlock(exprs): 1380 | { 1381 | if (exprs.length == 0) 1382 | { 1383 | return rest([origin]); 1384 | } 1385 | function transformNext(i:Int):Expr 1386 | { 1387 | if (i == exprs.length - 1) 1388 | { 1389 | return transform(exprs[i], parameterRequirement, rest); 1390 | } 1391 | else 1392 | { 1393 | return transform(exprs[i], IGNORE, function(transformedLine) 1394 | { 1395 | return 1396 | { 1397 | pos: origin.pos, 1398 | expr: EBlock(transformedLine.concat([transformNext(i + 1)])), 1399 | } 1400 | }); 1401 | } 1402 | } 1403 | return transformNext(0); 1404 | } 1405 | case EBinop(op, e1, e2): 1406 | { 1407 | switch (op) 1408 | { 1409 | case OpBoolOr: 1410 | { 1411 | return transformNoDelay(macro $e1 ? true : $e2 ? true : false, parameterRequirement, rest); 1412 | } 1413 | case OpBoolAnd: 1414 | { 1415 | return transformNoDelay(macro $e1 ? $e2 ? true : false : false, parameterRequirement, rest); 1416 | } 1417 | default: 1418 | { 1419 | return transformNoDelay( 1420 | e1, 1421 | EXACT(1), 1422 | function(e1Result) 1423 | { 1424 | return transformNoDelay(e2, EXACT(1), function(e2Result) 1425 | { 1426 | return rest( 1427 | [ 1428 | { 1429 | pos: origin.pos, 1430 | expr: EBinop( 1431 | op, 1432 | unpack(e1Result, e1.pos), 1433 | unpack(e2Result, e2.pos)) 1434 | } 1435 | ]); 1436 | }); 1437 | }); 1438 | } 1439 | } 1440 | } 1441 | case EArrayDecl( 1442 | [ 1443 | { 1444 | expr: EMeta( 1445 | { name: "fork", params: [ { expr: EIn( { expr: EConst(CIdent(variableName)) }, idendifiers) } ] }, 1446 | forkBody) 1447 | } 1448 | ]): 1449 | { 1450 | var toIteratorExpr = 1451 | { 1452 | expr: ECall( 1453 | macro com.dongxiguo.continuation.Continuation.ContinuationDetail.toIterator, 1454 | [ idendifiers ]), 1455 | pos: Context.currentPos(), 1456 | } 1457 | var hasNextExpr = 1458 | { 1459 | expr: ECall( 1460 | macro com.dongxiguo.continuation.Continuation.ContinuationDetail.hasNext, 1461 | [ idendifiers, macro __iterator ]), 1462 | pos: Context.currentPos(), 1463 | } 1464 | var nextExpr = 1465 | { 1466 | expr: ECall( 1467 | macro com.dongxiguo.continuation.Continuation.ContinuationDetail.next, 1468 | [ idendifiers, macro __iterator ]), 1469 | pos: Context.currentPos(), 1470 | } 1471 | switch (unblock(forkBody)) 1472 | { 1473 | case { expr: EIf(econd, eif, null) }: 1474 | { 1475 | var afterForkExpr = rest([ macro __results ]); 1476 | var transformedBody = transformNoDelay(eif, EXACT(1), function(exprs) 1477 | { 1478 | var elementExpr = unpack(exprs, eif.pos); 1479 | return macro 1480 | { 1481 | __results[__index] = $elementExpr; 1482 | __checkCounter(); 1483 | } 1484 | }); 1485 | return macro 1486 | { 1487 | var __iterator = com.dongxiguo.continuation.Continuation.ContinuationDetail.toIterator($idendifiers); 1488 | if (com.dongxiguo.continuation.Continuation.ContinuationDetail.hasNext($idendifiers, __iterator)) 1489 | { 1490 | var __results = []; 1491 | var __counter = 1; 1492 | var __i = 0; 1493 | function __checkCounter():Void if (--__counter == 0) $afterForkExpr; 1494 | do 1495 | { 1496 | var $variableName = com.dongxiguo.continuation.Continuation.ContinuationDetail.next($idendifiers, __iterator); 1497 | if ($econd) 1498 | { 1499 | var __index = __i; 1500 | __counter++; 1501 | $transformedBody; 1502 | __i++; 1503 | } 1504 | } 1505 | while (com.dongxiguo.continuation.Continuation.ContinuationDetail.hasNext($idendifiers, __iterator)); 1506 | __checkCounter(); 1507 | } 1508 | }; 1509 | } 1510 | default: 1511 | { 1512 | var afterForkExpr = rest([ macro __results ]); 1513 | var transformedBody = transformNoDelay(forkBody, EXACT(1), function(exprs) 1514 | { 1515 | var elementExpr = unpack(exprs, forkBody.pos); 1516 | return macro 1517 | { 1518 | __results[__index] = $elementExpr; 1519 | __checkCounter(); 1520 | } 1521 | }); 1522 | return macro 1523 | { 1524 | var __iterator = com.dongxiguo.continuation.Continuation.ContinuationDetail.toIterator($idendifiers); 1525 | if (com.dongxiguo.continuation.Continuation.ContinuationDetail.hasNext($idendifiers, __iterator)) 1526 | { 1527 | var __results = []; 1528 | var __counter = 1; 1529 | var __i = 0; 1530 | function __checkCounter():Void if (--__counter == 0) $afterForkExpr; 1531 | do 1532 | { 1533 | var $variableName = com.dongxiguo.continuation.Continuation.ContinuationDetail.next($idendifiers, __iterator); 1534 | var __index = __i; 1535 | __counter++; 1536 | $transformedBody; 1537 | __i++; 1538 | } 1539 | while (com.dongxiguo.continuation.Continuation.ContinuationDetail.hasNext($idendifiers, __iterator)); 1540 | __checkCounter(); 1541 | } 1542 | }; 1543 | } 1544 | } 1545 | } 1546 | case EArrayDecl( 1547 | [ 1548 | { 1549 | expr: EFor( 1550 | { 1551 | expr: EIn( 1552 | { 1553 | expr: EConst(CIdent(elementName)), 1554 | }, 1555 | e2), 1556 | }, 1557 | expr), 1558 | } 1559 | ]): 1560 | { 1561 | var toIteratorExpr = 1562 | { 1563 | expr: ECall( 1564 | macro com.dongxiguo.continuation.Continuation.ContinuationDetail.toIterator, 1565 | [ e2 ]), 1566 | pos: Context.currentPos(), 1567 | } 1568 | var hasNextExpr = 1569 | { 1570 | expr: ECall( 1571 | macro com.dongxiguo.continuation.Continuation.ContinuationDetail.hasNext, 1572 | [ e2, macro __iterator ]), 1573 | pos: Context.currentPos(), 1574 | } 1575 | var nextExpr = 1576 | { 1577 | expr: ECall( 1578 | macro com.dongxiguo.continuation.Continuation.ContinuationDetail.next, 1579 | [ e2, macro __iterator ]), 1580 | pos: Context.currentPos(), 1581 | } 1582 | switch (unblock(expr)) 1583 | { 1584 | case { expr: EIf(econd, eif, null) }: 1585 | { 1586 | var transformed = transformNoDelay( 1587 | macro 1588 | { 1589 | var __arrayBuilder = []; 1590 | while ($hasNextExpr) 1591 | { 1592 | var $elementName = $nextExpr; 1593 | if ($econd) __arrayBuilder.push($eif); 1594 | } 1595 | __arrayBuilder; 1596 | }, 1597 | EXACT(1), 1598 | rest); 1599 | return macro { var __iterator = $toIteratorExpr; $transformed; }; 1600 | } 1601 | case forBody: 1602 | { 1603 | var transformed = transformNoDelay( 1604 | macro 1605 | { 1606 | var __arrayBuilder = []; 1607 | while ($hasNextExpr) 1608 | { 1609 | var $elementName = $nextExpr; 1610 | __arrayBuilder.push($forBody); 1611 | } 1612 | __arrayBuilder; 1613 | }, 1614 | EXACT(1), 1615 | rest); 1616 | return macro { var __iterator = $toIteratorExpr; $transformed; }; 1617 | } 1618 | } 1619 | } 1620 | case EArrayDecl(originParams): 1621 | { 1622 | function transformNext(i:Int, transformedParameters:Null>):Expr 1623 | { 1624 | if (i == originParams.length) 1625 | { 1626 | return rest( 1627 | [ 1628 | { 1629 | pos: origin.pos, 1630 | expr: EArrayDecl( 1631 | toReverseArray(transformedParameters)), 1632 | } 1633 | ]); 1634 | } 1635 | else 1636 | { 1637 | return transformNoDelay( 1638 | originParams[i], 1639 | ANY, 1640 | function(parameterResult:Array):Expr 1641 | { 1642 | return transformNext( 1643 | i + 1, 1644 | pushMulti( 1645 | transformedParameters, 1646 | parameterResult)); 1647 | }); 1648 | } 1649 | } 1650 | return transformNext(0, null); 1651 | } 1652 | case EArray(e1, e2): 1653 | { 1654 | return transformNoDelay( 1655 | e1, 1656 | EXACT(1), 1657 | function(e1Result) 1658 | { 1659 | return transformNoDelay(e2, EXACT(1), function(e2Result) 1660 | { 1661 | return rest( 1662 | [ 1663 | { 1664 | pos: origin.pos, 1665 | expr: EArray( 1666 | unpack(e1Result, e1.pos), 1667 | unpack(e2Result, e2.pos)) 1668 | } 1669 | ]); 1670 | }); 1671 | }); 1672 | } 1673 | } 1674 | } 1675 | 1676 | @:isVar 1677 | static var delayFunctions(get_delayFunctions, set_delayFunctions):ArrayExpr>; 1678 | 1679 | static function set_delayFunctions(value:ArrayExpr>):ArrayExpr> 1680 | { 1681 | return delayFunctions = value; 1682 | } 1683 | 1684 | static function get_delayFunctions():ArrayExpr> 1685 | { 1686 | if (delayFunctions == null) 1687 | { 1688 | Context.onGenerate( 1689 | function(allType) 1690 | { 1691 | delayFunctions = null; 1692 | }); 1693 | return delayFunctions = []; 1694 | } 1695 | else 1696 | { 1697 | return delayFunctions; 1698 | } 1699 | } 1700 | 1701 | static function delay(pos:Position, delayedFunction:Void->Expr):Expr 1702 | { 1703 | var id = delayFunctions.length; 1704 | var idExpr = Context.makeExpr(id, Context.currentPos()); 1705 | delayFunctions.push(delayedFunction); 1706 | return 1707 | { 1708 | pos: pos, 1709 | expr: ECall(macro com.dongxiguo.continuation.Continuation.ContinuationDetail.runDelayedFunction, [idExpr]), 1710 | } 1711 | } 1712 | 1713 | static function hasArrayAccess(abstractType:AbstractType):Bool 1714 | { 1715 | if (abstractType.meta.has(":arrayAccess")) 1716 | { 1717 | return true; 1718 | } 1719 | else 1720 | { 1721 | return !abstractType.array.empty(); 1722 | } 1723 | } 1724 | 1725 | static function hasLength(abstractType:AbstractType):Bool 1726 | { 1727 | var impl = abstractType.impl; 1728 | if (impl == null) 1729 | { 1730 | return false; 1731 | } 1732 | else 1733 | { 1734 | for (field in impl.get().statics.get()) 1735 | { 1736 | switch (field) 1737 | { 1738 | case { kind: FVar(AccCall, _), name: "length" }: return true; 1739 | default: continue; 1740 | } 1741 | } 1742 | return false; 1743 | } 1744 | } 1745 | 1746 | #end 1747 | 1748 | @:noUsing 1749 | macro 1750 | public static function runDelayedFunction(id:Int):Expr 1751 | { 1752 | return delayFunctions[id](); 1753 | } 1754 | 1755 | @:noUsing 1756 | macro 1757 | public static function next(iterable:Expr, iterator:Expr):Expr 1758 | { 1759 | switch (Context.follow(Context.typeof(iterable))) 1760 | { 1761 | case TInst(_.get() => { module: "Array", name: "Array" }, _): 1762 | { 1763 | return macro $iterable[$iterator++]; 1764 | } 1765 | case TAbstract(_.get() => a, _) if (hasArrayAccess(a) && hasLength(a)): 1766 | { 1767 | return macro $iterable[$iterator++]; 1768 | } 1769 | case iterableType: 1770 | { 1771 | return macro $iterator.next(); 1772 | } 1773 | } 1774 | } 1775 | 1776 | @:noUsing 1777 | macro 1778 | public static function hasNext(iterable:Expr, iterator:Expr):Expr 1779 | { 1780 | switch (Context.follow(Context.typeof(iterable))) 1781 | { 1782 | case TInst(_.get() => { module: "Array", name: "Array" }, _): 1783 | { 1784 | return macro $iterator < $iterable.length; 1785 | } 1786 | case TAbstract(_.get() => a, _) if (hasArrayAccess(a) && hasLength(a)): 1787 | { 1788 | return macro $iterator < $iterable.length; 1789 | } 1790 | case iterableType: 1791 | { 1792 | return macro $iterator.hasNext(); 1793 | } 1794 | } 1795 | } 1796 | 1797 | @:noUsing 1798 | macro 1799 | public static function toIterator(iterable:Expr):Expr 1800 | { 1801 | switch (Context.follow(Context.typeof(iterable))) 1802 | { 1803 | case TInst(_.get() => { module: "Array", name: "Array" }, _): 1804 | { 1805 | return macro 0; 1806 | } 1807 | case TAbstract(_.get() => a, _) if (hasArrayAccess(a) && hasLength(a)): 1808 | { 1809 | return macro 0; 1810 | } 1811 | case iterableType: 1812 | { 1813 | function toType(c:ComplexType):Null 1814 | { 1815 | return c == null ? null : haxe.macro.Context.typeof( { expr: ECheckType(macro null, c), pos: Context.currentPos() } ); 1816 | } 1817 | if ( 1818 | Context.unify(iterableType, toType(TPath( 1819 | { 1820 | name: "Iterator", 1821 | pack: [], 1822 | sub: null, 1823 | params: 1824 | [ 1825 | TPType(TPath( 1826 | { 1827 | name: "Dynamic", 1828 | pack: [], 1829 | sub: null, 1830 | params: [], 1831 | })), 1832 | ] 1833 | })))) 1834 | { 1835 | return iterable; 1836 | } 1837 | else 1838 | { 1839 | return macro $iterable.iterator(); 1840 | } 1841 | } 1842 | } 1843 | } 1844 | 1845 | } 1846 | 1847 | #if macro 1848 | private enum ParameterRequirement 1849 | { 1850 | 1851 | /** 必须正好是numParameters个参数 */ 1852 | EXACT(numParameters:Int); 1853 | 1854 | /** 忽略参数 */ 1855 | IGNORE; 1856 | 1857 | /** 接受任意数量的参数 */ 1858 | ANY; 1859 | 1860 | } 1861 | 1862 | private class Wrapper 1863 | { 1864 | static var seed:Int = 0; 1865 | 1866 | public var declearation(default, null):Expr; 1867 | 1868 | public var invocation(default, null):Array->Expr; 1869 | 1870 | static function declearWrapper( 1871 | functionName:String, 1872 | numParameters:Int, 1873 | rest:Array->Expr):Expr 1874 | { 1875 | var parameterNames:Array = []; 1876 | for (i in 0...numParameters) 1877 | { 1878 | parameterNames.push(functionName + "_parameter_" + i); 1879 | } 1880 | return 1881 | { 1882 | pos: Context.currentPos(), 1883 | expr: EFunction( 1884 | functionName, 1885 | { 1886 | args: 1887 | { 1888 | var functionArgs:Array = []; 1889 | for (parameterName in parameterNames) 1890 | { 1891 | functionArgs.push( 1892 | { 1893 | name: parameterName, 1894 | opt: false, 1895 | type: null, 1896 | value: null, 1897 | }); 1898 | } 1899 | functionArgs; 1900 | }, 1901 | ret: null, 1902 | params: [], 1903 | expr: 1904 | { 1905 | pos: Context.currentPos(), 1906 | expr: EReturn( 1907 | rest( 1908 | { 1909 | var parameterExprs = []; 1910 | for (parameterName in parameterNames) 1911 | { 1912 | parameterExprs.push( 1913 | { 1914 | pos: Context.currentPos(), 1915 | expr: EConst(CIdent(parameterName)), 1916 | }); 1917 | } 1918 | parameterExprs; 1919 | })) 1920 | } 1921 | }), 1922 | } 1923 | } 1924 | 1925 | public function new( 1926 | parameterRequirement:ParameterRequirement, 1927 | rest:Array->Expr, 1928 | isInline:Bool, 1929 | prefix:String) 1930 | { 1931 | switch (parameterRequirement) 1932 | { 1933 | case ANY: 1934 | { 1935 | this.declearation = 1936 | { 1937 | pos: Context.currentPos(), 1938 | expr: EBlock([]), 1939 | }; 1940 | this.invocation = rest; 1941 | } 1942 | case IGNORE: 1943 | var functionName = prefix + Std.string(seed++); 1944 | if (isInline) 1945 | { 1946 | this.declearation = declearWrapper("inline_" + functionName, 0, rest); 1947 | } 1948 | else 1949 | { 1950 | this.declearation = declearWrapper(functionName, 0, rest); 1951 | } 1952 | this.invocation = function(parameters:Array):Expr 1953 | { 1954 | return 1955 | { 1956 | pos: Context.currentPos(), 1957 | expr: EBlock(parameters.concat( 1958 | [ 1959 | { 1960 | pos: Context.currentPos(), 1961 | expr: ECall( 1962 | { 1963 | pos: Context.currentPos(), 1964 | expr: EConst(CIdent(functionName)), 1965 | }, 1966 | []), 1967 | } 1968 | ])), 1969 | }; 1970 | }; 1971 | case EXACT(numParameters): 1972 | var functionName = prefix + Std.string(seed++); 1973 | #if no_inline 1974 | this.declearation = declearWrapper(functionName, numParameters, rest); 1975 | #else 1976 | this.declearation = declearWrapper("inline_" + functionName, numParameters, rest); 1977 | #end 1978 | this.invocation = function(parameters:Array):Expr 1979 | { 1980 | return 1981 | { 1982 | pos: Context.currentPos(), 1983 | expr: EBlock(parameters.concat( 1984 | [ 1985 | { 1986 | pos: Context.currentPos(), 1987 | expr: ECall( 1988 | { 1989 | pos: Context.currentPos(), 1990 | expr: EConst(CIdent(functionName)), 1991 | }, 1992 | parameters), 1993 | } 1994 | ])), 1995 | }; 1996 | }; 1997 | } 1998 | } 1999 | 2000 | } 2001 | #end 2002 | -------------------------------------------------------------------------------- /com/dongxiguo/continuation/utils/EventSelector.hx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, 杨博 (Yang Bo) 2 | // All rights reserved. 3 | // 4 | // Author: 杨博 (Yang Bo) 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // * Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | // * Neither the name of the nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package com.dongxiguo.continuation.utils; 31 | 32 | #if flash9 33 | 34 | import flash.events.Event; 35 | import flash.events.IEventDispatcher; 36 | 37 | class EventSelector 38 | { 39 | public static function select( 40 | self:IEventDispatcher, 41 | conditionCandidates:Iterable, 42 | handler:Event->Void):Void 43 | { 44 | function underlyingHandler(event:Event):Void 45 | { 46 | for (condition in conditionCandidates) 47 | { 48 | self.removeEventListener( 49 | condition.type, 50 | underlyingHandler, 51 | condition.useCapture); 52 | } 53 | handler(event); 54 | } 55 | for (condition in conditionCandidates) 56 | { 57 | self.addEventListener( 58 | condition.type, 59 | underlyingHandler, 60 | condition.useCapture, 61 | condition.priority); 62 | } 63 | } 64 | } 65 | 66 | typedef EventCondition = 67 | { 68 | 69 | @:optional 70 | var useCapture(default, never):Null; 71 | 72 | var type(default, never):String; 73 | 74 | @:optional 75 | var priority(default, never):Null; 76 | 77 | } 78 | 79 | #end 80 | -------------------------------------------------------------------------------- /com/dongxiguo/continuation/utils/ForkJoin.hx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2014, 杨博 (Yang Bo) 2 | // All rights reserved. 3 | // 4 | // Author: 杨博 (Yang Bo) 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // * Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | // * Neither the name of the nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package com.dongxiguo.continuation.utils; 31 | 32 | @:final 33 | class ForkJoin 34 | { 35 | /** 36 | Hangs up the current thread. 37 | 38 | Usage: `return @await ForkJoin.hang();` 39 | **/ 40 | @:noUsing 41 | @:extern 42 | public static inline function hang(handler:Dynamic):Void 43 | { 44 | } 45 | 46 | /** 47 | Forks the current thread. 48 | 49 | Usage: 50 |
`if (@await ForkJoin.fork())
 51 |     {
 52 |       // Forked thread #1
 53 |     }
 54 |     else
 55 |     {
 56 |       // Forked thread #2
 57 |     }`
58 | **/ 59 | @:noUsing 60 | @:extern 61 | public static inline function fork(handler:Bool->Void):Void 62 | { 63 | handler(true); 64 | handler(false); 65 | } 66 | 67 | /** 68 | Like `ForkJoin.startThreads`, but returns a `CollectFunction` instead of `JoinFunction`. 69 | **/ 70 | @:deprecated("Use `[ @fork(v in collectorIdentifiers) handler(v) ]` instead!") 71 | @:noUsing 72 | public static function startCollectors( 73 | collectorIdentifiers: Iterable, 74 | handler:Identifier->CollectFunction->Void):Void 75 | { 76 | if (Lambda.empty(collectorIdentifiers)) 77 | { 78 | throw "collectorIdentifiers must not be empty!"; 79 | } 80 | var counter = 1; 81 | var results:Array> = []; 82 | var quickCollectHandler = null; 83 | var i = 0; 84 | for (id in collectorIdentifiers) 85 | { 86 | counter++; 87 | var index = i; 88 | handler(id, function(result:Result, collectHandler:Array->Void) 89 | { 90 | if (results[index] != null) 91 | { 92 | throw "Cannot collect twice in one collector!"; 93 | } 94 | else 95 | { 96 | results[index] = result; 97 | if (--counter == 0) 98 | { 99 | collectHandler(results); 100 | } 101 | else 102 | { 103 | quickCollectHandler = collectHandler; 104 | } 105 | } 106 | }); 107 | i++; 108 | } 109 | if (--counter == 0) 110 | { 111 | quickCollectHandler(results); 112 | } 113 | 114 | } 115 | 116 | /** 117 | Forks threads for every element in `threadIdentifiers`. 118 | @param handler The callback function invoked for each element in `threadIdentifiers`. 119 | The `handler` can receive two parameters: 120 | the element in `threadIdentifiers` and the `JoinFunction`. 121 | **/ 122 | @:deprecated("Use `@fork(v in collectorIdentifiers) handler(v)` instead!") 123 | @:noUsing 124 | public static function startThreads( 125 | threadIdentifiers: Iterable, 126 | handler:Identifier->JoinFunction->Void):Void 127 | { 128 | if (Lambda.empty(threadIdentifiers)) 129 | { 130 | throw "threadIdentifiers must not be empty!"; 131 | } 132 | var counter = 1; 133 | var quickJoinHandler = null; 134 | for (id in threadIdentifiers) 135 | { 136 | counter++; 137 | var isJoined = false; 138 | handler(id, function(joinHandler) 139 | { 140 | if (isJoined) 141 | { 142 | throw "Cannot join twice in one thread!"; 143 | } 144 | else 145 | { 146 | isJoined = true; 147 | if (--counter == 0) 148 | { 149 | joinHandler(); 150 | } 151 | else 152 | { 153 | quickJoinHandler = joinHandler; 154 | } 155 | } 156 | }); 157 | } 158 | if (--counter == 0) 159 | { 160 | quickJoinHandler(); 161 | } 162 | } 163 | 164 | } 165 | 166 | /** 167 | The function should be invoke when the thread exit. The execution continues 168 | only if all child threads have invoked their `JoinFunction`. 169 | **/ 170 | typedef JoinFunction = (Void->Void)->Void; 171 | 172 | /** 173 | Like `JoinFunction`, 174 | but will collect all results from each child threads into an array. 175 | **/ 176 | typedef CollectFunction = Result->(Array->Void)->Void; 177 | -------------------------------------------------------------------------------- /com/dongxiguo/continuation/utils/Generator.hx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, 杨博 (Yang Bo) 2 | // All rights reserved. 3 | // 4 | // Author: 杨博 (Yang Bo) 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // * Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | // * Neither the name of the nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package com.dongxiguo.continuation.utils; 31 | 32 | /** 33 | An `Iterator` that yields each element lazily. 34 | **/ 35 | @:final 36 | class Generator 37 | { 38 | var status:IteratorStatus; 39 | 40 | public function new(runFunction:RunFunction) 41 | { 42 | this.status = UNKNOWN(runFunction.bind(this.yield.bind(), this.end.bind())); 43 | } 44 | 45 | function end():Void 46 | { 47 | switch (this.status) 48 | { 49 | case UNKNOWN(_): 50 | this.status = NO_NEXT; 51 | default: 52 | throw "Must not fork threads in a generator!"; 53 | } 54 | } 55 | 56 | function yield(nextValue:Element, handler:Void->Void):Void 57 | { 58 | switch (this.status) 59 | { 60 | case UNKNOWN(_): 61 | this.status = HAS_NEXT(nextValue, handler); 62 | default: 63 | throw "Must not fork threads in a generator!"; 64 | } 65 | } 66 | 67 | public function next():Null 68 | { 69 | var oldStatus; 70 | var newStatus = this.status; 71 | do 72 | { 73 | oldStatus = newStatus; 74 | switch (oldStatus) 75 | { 76 | case UNKNOWN(fetchFunction): 77 | { 78 | fetchFunction(); 79 | newStatus = this.status; 80 | } 81 | case NO_NEXT: 82 | { 83 | return null; 84 | } 85 | case HAS_NEXT(nextValue, fetchFunction): 86 | { 87 | this.status = UNKNOWN(fetchFunction); 88 | return nextValue; 89 | } 90 | } 91 | } 92 | while (oldStatus != newStatus); 93 | return throw "Expect yield or return."; 94 | } 95 | 96 | public function hasNext():Bool 97 | { 98 | var oldStatus; 99 | var newStatus = this.status; 100 | do 101 | { 102 | oldStatus = newStatus; 103 | switch (oldStatus) 104 | { 105 | case UNKNOWN(fetchFunction): 106 | { 107 | fetchFunction(); 108 | newStatus = this.status; 109 | } 110 | case NO_NEXT: 111 | { 112 | return false; 113 | } 114 | case HAS_NEXT(_, _): 115 | { 116 | return true; 117 | } 118 | } 119 | } 120 | while (oldStatus != newStatus); 121 | return throw "Expect yield or return."; 122 | } 123 | 124 | public static function iterator( 125 | runFunction:RunFunction):Generator 126 | { 127 | return new Generator(runFunction); 128 | } 129 | 130 | public static function toIterable( 131 | runFunction:RunFunction):Iterable 132 | { 133 | return 134 | { 135 | iterator: function() { return new Generator(runFunction); }, 136 | } 137 | } 138 | 139 | #if cs 140 | 141 | @:functionCode(' 142 | var generator = new com.dongxiguo.continuation.utils.Generator(runFunction); 143 | while (generator.hasNext()) 144 | { 145 | yield return generator.next(); 146 | } 147 | ') 148 | public static function toEnumerator(runFunction:RunFunction):cs.system.collections.IEnumerator return null; 149 | 150 | #end 151 | } 152 | 153 | typedef RunFunction = YieldFunction->(Void->Void)->Void; 154 | 155 | typedef YieldFunction = Element->(Void->Void)->Void; 156 | 157 | private enum IteratorStatus 158 | { 159 | UNKNOWN(fetchFunction:Void->Void); 160 | HAS_NEXT(nextValue:Element, fetchFunction:Void->Void); 161 | NO_NEXT; 162 | } 163 | -------------------------------------------------------------------------------- /com/dongxiguo/continuation/utils/Sleep.hx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, 杨博 (Yang Bo) 2 | // All rights reserved. 3 | // 4 | // Author: 杨博 (Yang Bo) 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // * Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | // * Neither the name of the nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package com.dongxiguo.continuation.utils; 31 | import haxe.Timer; 32 | 33 | /** 34 | * @author 杨博 35 | */ 36 | class Sleep 37 | { 38 | 39 | @:noUsing 40 | public static inline function sleep(ms:Int, f:Void->Void):Void 41 | { 42 | #if doc_gen 43 | #else 44 | Timer.delay(f, ms); 45 | #end 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /com/dongxiguo/continuation/utils/StateMachine.hx: -------------------------------------------------------------------------------- 1 | package com.dongxiguo.continuation.utils; 2 | 3 | class StateMachine 4 | { 5 | 6 | public function new() {} 7 | 8 | var nextEventFunction:NullVoid> = null; 9 | 10 | public inline function waitForNextEvent(f:Event->Void):Void 11 | { 12 | if (nextEventFunction == null) 13 | { 14 | nextEventFunction = f; 15 | } 16 | else 17 | { 18 | throw "Unable to wait for the next event twice!"; 19 | } 20 | } 21 | 22 | public inline function post(event:Event):Void 23 | { 24 | var f = nextEventFunction; 25 | nextEventFunction = null; 26 | f(event); 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /com/dongxiguo/continuation/utils/Tuple.hx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, 杨博 (Yang Bo) 2 | // All rights reserved. 3 | // 4 | // Author: 杨博 (Yang Bo) 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // * Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | // * Neither the name of the nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package com.dongxiguo.continuation.utils; 31 | 32 | /** 33 | * @author 杨博 34 | */ 35 | class Tuple 36 | { 37 | 38 | @:noUsing 39 | public static inline function pair(a0:A0, a1:A1, callback:A0->A1->Void):Void 40 | { 41 | callback(a0, a1); 42 | } 43 | 44 | @:noUsing 45 | public static inline function triple(a0:A0, a1:A1, a2:A2, callback:A0->A1->A2->Void):Void 46 | { 47 | callback(a0, a1, a2); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Travis will run this script 4 | -------------------------------------------------------------------------------- /haxelib.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Asynchronous programming in a natural multi-thread-like syntax, escaping from the callback hell. 15 |
  • Loop does not cause stack overflow any more.
  • Add lots of utilities: Tuple.pair, Tuple.triple, Sleep.sleep, StateMachine.
  • Some minor bug fixes.
16 |
17 | -------------------------------------------------------------------------------- /tests/Counter.hx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, 杨博 (Yang Bo) 2 | // All rights reserved. 3 | // 4 | // Author: 杨博 (Yang Bo) 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // * Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | // * Neither the name of the nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package tests; 31 | class Counter 32 | { 33 | var n:Int; 34 | public function new(n:Int) 35 | { 36 | this.n = n; 37 | } 38 | public function join(handler:Void->Void):Void 39 | { 40 | if (0 == --n) 41 | { 42 | handler(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/M.hx: -------------------------------------------------------------------------------- 1 | package tests; 2 | import haxe.macro.Expr; 3 | 4 | /** 5 | * ... 6 | * @author 杨博 7 | */ 8 | class M 9 | { 10 | public static function t(self:Expr):Expr 11 | { 12 | trace(haxe.macro.ExprTools.toString(self)); 13 | return self; 14 | } 15 | 16 | macro 17 | public static function m(e:Expr):Expr 18 | { 19 | return macro $e(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /tests/Sample.hx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, 杨博 (Yang Bo) 2 | // All rights reserved. 3 | // 4 | // Author: 杨博 (Yang Bo) 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // * Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | // * Neither the name of the nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package tests; 31 | import com.dongxiguo.continuation.Continuation; 32 | 33 | /** 34 | * @author 杨博 35 | */ 36 | class Sample 37 | { 38 | static function sleepOneSecond(handler:Void->Void):Void 39 | { 40 | #if (swf || as3 || java || js || python) 41 | haxe.Timer.delay(handler, 1000); 42 | #else 43 | handler(); 44 | #end 45 | } 46 | 47 | public static function main() 48 | { 49 | Continuation.cpsFunction(function asyncTest():Void 50 | { 51 | trace("Start continuation."); 52 | for (i in 0...10) 53 | { 54 | @await sleepOneSecond(); 55 | trace("Run sleepOneSecond " + i + " times."); 56 | } 57 | trace("Continuation is done."); 58 | }); 59 | 60 | asyncTest(function() 61 | { 62 | trace("Handler without continuation."); 63 | }); 64 | } 65 | 66 | } 67 | 68 | 69 | /** 70 | * @author 杨博 71 | */ 72 | @:build(com.dongxiguo.continuation.Continuation.cpsByMeta("cps")) 73 | class Sample2 74 | { 75 | static function sleepOneSecond(handler:Void->Void):Void 76 | { 77 | #if (swf || as3 || java || js || python) 78 | haxe.Timer.delay(handler, 1000); 79 | #else 80 | handler(); 81 | #end 82 | } 83 | @cps static function asyncTest():Void 84 | { 85 | trace("Start continuation."); 86 | for (i in 0...10) 87 | { 88 | @await sleepOneSecond(); 89 | trace("Run sleepOneSecond " + i + " times."); 90 | } 91 | trace("Continuation is done."); 92 | } 93 | public static function main() 94 | { 95 | asyncTest(function() 96 | { 97 | trace("Handler without continuation."); 98 | }); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /tests/TestContinuation.hx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012,2013, 杨博 (Yang Bo) 2 | // All rights reserved. 3 | // 4 | // Author: 杨博 (Yang Bo) 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // * Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | // * Neither the name of the nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package tests; 31 | 32 | import haxe.ds.IntMap; 33 | import haxe.ds.Vector; 34 | 35 | using com.dongxiguo.continuation.Continuation; 36 | 37 | typedef TwoParametersFunction = Int->String->Void; 38 | 39 | /** 40 | * @author 杨博 41 | */ 42 | @:build(com.dongxiguo.continuation.Continuation.cpsByMeta(":cps")) 43 | class TestContinuation 44 | { 45 | static function twoReturnValues(handler:TwoParametersFunction):Void { } 46 | 47 | @:cps static function testTwoReturnValues():Void 48 | { 49 | var i, j = @await twoReturnValues(); 50 | } 51 | 52 | @:cps static function ifNull(?p:Int) 53 | { 54 | if (p == null) 55 | { 56 | p = 1; 57 | } 58 | trace(p); 59 | } 60 | 61 | @:cps static function maybeTuple():Void 62 | { 63 | if (Math.random() < 0.5) 64 | { 65 | @await forkJoin(); 66 | } 67 | else 68 | { 69 | @await tuple2(1, 2); 70 | } 71 | } 72 | 73 | @:cps static function forkJoin():Int 74 | { 75 | var a:Array = [1, 2, 3, 4]; 76 | var joiner = new Counter(a.length); 77 | @await Lambda.iter(a); 78 | trace(a); 79 | @await read(4); 80 | trace(a); 81 | @await joiner.join(); 82 | if ((@await read(1)) == 1) 83 | { 84 | return 1; 85 | } 86 | else 87 | { 88 | return 2; 89 | } 90 | } 91 | 92 | static function good(a, b):Int 93 | { 94 | trace(a + b); 95 | return a + b; 96 | } 97 | 98 | static function xx(xxx):Int { return 1; } 99 | 100 | static function read(n:Int, handler:Int -> Void):Void 101 | { 102 | } 103 | 104 | @:cps static function write(n:Int):Int 105 | { 106 | @await forkJoin(); 107 | return n + 1; 108 | } 109 | @:cps static function void1(n:Int):Void 110 | { 111 | return; 112 | } 113 | 114 | @:cps static function void2(n:Int):Void 115 | { 116 | if (false) 117 | { 118 | return @await hang0(); 119 | Any.code.after._return_.will.be.gone(); 120 | } 121 | return @await void1(n); 122 | } 123 | 124 | @:cps static function baz(n:Int):Int 125 | { 126 | if (false) 127 | { 128 | return @await hang1(); 129 | Any.code.after._return_.will.be.gone(); 130 | } 131 | @await void2(3); 132 | return @await foo(n + 3); 133 | } 134 | 135 | static inline function hang0(handler:Void->Void):Void {} 136 | static inline function hang1(handler:T->Void):Void {} 137 | 138 | @:cps static function foo(n:Int):Int 139 | { 140 | if (true) 141 | { 142 | return @await hang1(); 143 | Any.code.after._return_.will.be.gone(); 144 | } 145 | return (@await read(3)) * 4 + (@await hang1()); 146 | } 147 | 148 | static function bar(n:Int, s:String, f:Float, handler:Int->Void):Void 149 | { 150 | 151 | } 152 | 153 | inline static function tuple2(p0, p1, handler):Void 154 | { 155 | handler(p0, p1); 156 | } 157 | 158 | static function doubleResult(handler:Int->String->Void):Void 159 | { 160 | 161 | } 162 | 163 | static function dummy():Void {} 164 | 165 | static function main() 166 | { 167 | baz(4, function(result) 168 | { 169 | trace(result); 170 | }); 171 | write(2, function(result) 172 | { 173 | good(1, 2); 174 | }); 175 | Continuation.cpsFunction( 176 | function testFor():Void 177 | { 178 | var y = [ @await tuple2(0, 1), 2, @await tuple2(3, 4)]; 179 | trace(y.length); 180 | var x = { b: 3, d: "xxx", asdf: @await read(34) }; 181 | for (j in [2, 4, 5]) 182 | { 183 | for (i in 0...123) 184 | { 185 | var a, b = switch (@await read(3)) 186 | { 187 | case 2: 188 | @await tuple2(2, 3); 189 | default: 190 | @await tuple2(1, 5); 191 | } 192 | } 193 | } 194 | } 195 | ); 196 | Continuation.cpsFunction( 197 | function testTry():Void 198 | { 199 | try 200 | { 201 | dummy(); 202 | } 203 | catch (x:Array) 204 | { 205 | trace("catch"); 206 | @await foo(@await read(1)); 207 | } 208 | @await read( 209 | try 210 | { 211 | good(3, 2); 212 | } 213 | catch (x:Array) 214 | { 215 | @await foo(@await read(x[0])); 216 | } 217 | catch (x:IntMap) 218 | { 219 | } 220 | catch (x:String) 221 | { 222 | @await read(3); 223 | }); 224 | } 225 | ); 226 | Continuation.cpsFunction( 227 | function voidFunction():Void 228 | { 229 | good(3, 2); 230 | } 231 | ); 232 | Continuation.cpsFunction( 233 | function intFunction():Int 234 | { 235 | return good(3, 2); 236 | } 237 | ); 238 | 239 | Continuation.cpsFunction(function functionOfFunction():(Int->Void)->Void 240 | { 241 | return Continuation.cpsFunction(function():Int { return 1; } ); 242 | }); 243 | 244 | Continuation.cpsFunction(function myFunction():Int 245 | { 246 | var ff = @await functionOfFunction(); 247 | trace(ff); 248 | return @await ff(); 249 | }); 250 | Continuation.cpsFunction(function multiVar() 251 | { 252 | var c = 1, a, b = @await doubleResult(); 253 | return @await tuple2(c, a); 254 | }); 255 | var asyncDo = read.bind(3); 256 | Continuation.cpsFunction(function myFunction():Int 257 | { 258 | var xxx = @await bar(234, "foo", 34.5); 259 | var result = @await read(2); 260 | 261 | var z = (@await asyncDo()) + 2 * (@await bar(@await asyncDo(), "foo", 34.5)) + @await read(@await read(2)); 262 | var x = good(@await asyncDo(), @await bar(@await asyncDo(), "foo", 34.5)); 263 | var c = @await asyncDo(); 264 | var a = 1 + 2 * x + z; 265 | var b = 3 + 4 + c, d = a + @await asyncDo(), e = (@await asyncDo()) * @await asyncDo(); 266 | return (@await asyncDo()) + a + b * e + d - c; 267 | }); 268 | 269 | Continuation.cpsFunction(function myFunction2():Int 270 | { 271 | good(3, 4); 272 | return 1 + @await myFunction(); 273 | }); 274 | Continuation.cpsFunction(function myFunction3():Int 275 | { 276 | good(4, 5); 277 | @await myFunction(); 278 | @await myFunction(); 279 | return @await read(3); 280 | }); 281 | Continuation.cpsFunction(function myFunction():Int 282 | { 283 | return good(1, good(good(2, 3), good(4, 5))); 284 | }); 285 | Continuation.cpsFunction(function myFunction():Int 286 | { 287 | return return 1; 288 | return 2; 289 | }); 290 | Continuation.cpsFunction(function myFunction():Int 291 | { 292 | return @await asyncDo(); 293 | return 2; 294 | }); 295 | Continuation.cpsFunction(function myFunction33():Int 296 | { 297 | if ((@await asyncDo()) == 0) 298 | { 299 | return 44; 300 | } 301 | else 302 | { 303 | return @await asyncDo(); 304 | } 305 | var a = ((@await asyncDo()) == 0 ? 1 : 2); 306 | return (if ((@await asyncDo()) == 0) 307 | { 308 | return 2; 309 | } 310 | else if ((@await asyncDo()) == 1) 311 | { 312 | @await asyncDo(); 313 | } 314 | else 315 | { 316 | 43; 317 | }) + (if ((@await asyncDo()) == 0) { @await asyncDo(); } else { 1; } ); 318 | }); 319 | 320 | Continuation.cpsFunction(function testWhile():Int 321 | { 322 | while ((@await asyncDo()) > 1) 323 | { 324 | @await asyncDo(); 325 | if (true) break; 326 | if ((@await asyncDo()) <= 4) continue; 327 | @await asyncDo(); 328 | } 329 | return 1; 330 | }); 331 | Continuation.cpsFunction(function testDoWhile():Void 332 | { 333 | do 334 | { 335 | 336 | } 337 | while (true); 338 | }); 339 | 340 | Continuation.cpsFunction(function testVectorForLoop():Void 341 | { 342 | var v = haxe.ds.Vector.fromArrayCopy([1, 2, 3]); 343 | for (e in v) 344 | { 345 | trace(@await asyncDo() * e); 346 | } 347 | var a1 = [ for (e in v) e * 2 ]; 348 | var a2 = [ for (e in v) if (e < 2) e * 2 ]; 349 | var a3 = [ for (e in v) { if (e < 2) e * 2; } ]; 350 | }); 351 | } 352 | 353 | 354 | } 355 | typedef A = Int; 356 | -------------------------------------------------------------------------------- /tests/TestForkJoin.hx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, 杨博 (Yang Bo) 2 | // All rights reserved. 3 | // 4 | // Author: 杨博 (Yang Bo) 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // * Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | // * Neither the name of the nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package tests; 31 | import haxe.Timer; 32 | 33 | using com.dongxiguo.continuation.utils.ForkJoin; 34 | 35 | /** 36 | * @author 杨博 37 | */ 38 | @:build(com.dongxiguo.continuation.Continuation.cpsByMeta(":cps")) 39 | class TestForkJoin 40 | { 41 | static function sleep(time_ms:Int, handler:Void->Void):Void 42 | { 43 | #if cs 44 | handler(); 45 | #else 46 | Timer.delay(handler, time_ms); 47 | #end 48 | } 49 | 50 | /** Start 4 worker threads. Every worker is a collector. */ 51 | @:cps public static function startWorkerThreads(parentId:Int, childrenIds:Array):Array 52 | { 53 | trace("Before fork"); 54 | var threadId, collect = @await ForkJoin.startCollectors(childrenIds); 55 | var result = @await collect( 56 | { 57 | trace("Start sub-thread #" + parentId + "." + threadId); 58 | 59 | trace("Sub-thread #" + parentId + "." + threadId + " is going to sleep."); 60 | @await sleep(Std.int(Math.random() * 5000.0)); 61 | trace("Sub-thread #" + parentId + "." + threadId + " is waken up."); 62 | 63 | trace("Collecting data from sub-thread #" + parentId + "." + threadId + "..."); 64 | 65 | threadId * parentId; 66 | }); 67 | trace("All sub-threads of #" + parentId + " are joined."); 68 | return result; 69 | } 70 | 71 | /** Start 4 manager threads. Every manager manages 6 workers. */ 72 | @:cps public static function startManagerThreads():Void 73 | { 74 | var threadIds = [ 0, 1, 2, 3 ]; 75 | trace("Before fork"); 76 | { 77 | var threadId, join = @await ForkJoin.startThreads(threadIds); 78 | trace("Start thread #" + threadId); 79 | 80 | trace("Data from sub-threads of #" + threadId + ": " + Std.string(@await startWorkerThreads(threadId, [0, 1, 2, 3, 4, 5]))); 81 | 82 | trace("Joining thread #" + threadId + "..."); 83 | @await join(); 84 | } 85 | trace("All threads are joined."); 86 | } 87 | 88 | public static function main() 89 | { 90 | startManagerThreads(function() { trace("All tests are done."); } ); 91 | trace("All threads are started."); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /tests/TestForkMeta.hx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, 杨博 (Yang Bo) 2 | // All rights reserved. 3 | // 4 | // Author: 杨博 (Yang Bo) 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // * Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | // * Neither the name of the nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package tests; 31 | import haxe.Timer; 32 | 33 | using com.dongxiguo.continuation.utils.ForkJoin; 34 | 35 | /** 36 | * @author 杨博 37 | */ 38 | @:build(com.dongxiguo.continuation.Continuation.cpsByMeta(":cps")) 39 | class TestForkMeta 40 | { 41 | static function sleep(time_ms:Int, handler:Void->Void):Void 42 | { 43 | #if cs 44 | handler(); 45 | #else 46 | Timer.delay(handler, time_ms); 47 | #end 48 | } 49 | 50 | /** Start 4 worker threads. Every worker is a collector. */ 51 | @:cps public static function startWorkerThreads(parentId:Int, childrenIds:Array):Array 52 | { 53 | trace("Before fork"); 54 | var result = 55 | [ 56 | @fork(threadId in childrenIds) 57 | { 58 | trace("Start sub-thread #" + parentId + "." + threadId); 59 | 60 | trace("Sub-thread #" + parentId + "." + threadId + " is going to sleep."); 61 | @await sleep(Std.int(Math.random() * 5000.0)); 62 | trace("Sub-thread #" + parentId + "." + threadId + " is waken up."); 63 | 64 | trace("Collecting data from sub-thread #" + parentId + "." + threadId + "..."); 65 | 66 | threadId * parentId; 67 | } 68 | ]; 69 | trace("All sub-threads of #" + parentId + " are joined."); 70 | return result; 71 | } 72 | 73 | /** Start 4 manager threads. Every manager manages 6 workers. */ 74 | @:cps public static function startManagerThreads():Void 75 | { 76 | var threadIds = [ 0, 1, 2, 3 ]; 77 | trace("Before fork"); 78 | @fork(threadId in threadIds) 79 | { 80 | trace("Start thread #" + threadId); 81 | 82 | trace("Data from sub-threads of #" + threadId + ": " + Std.string(@await startWorkerThreads(threadId, [0, 1, 2, 3, 4, 5]))); 83 | 84 | trace("Joining thread #" + threadId + "..."); 85 | } 86 | trace("All threads are joined."); 87 | } 88 | 89 | public static function main() 90 | { 91 | startManagerThreads(function() { trace("All tests are done."); } ); 92 | trace("All threads are started."); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /tests/TestGenerator.hx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, 杨博 (Yang Bo) 2 | // All rights reserved. 3 | // 4 | // Author: 杨博 (Yang Bo) 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // * Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | // * Neither the name of the nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package tests; 31 | 32 | using com.dongxiguo.continuation.utils.Generator; 33 | 34 | /** 35 | @author 杨博 36 | **/ 37 | @:build(com.dongxiguo.continuation.Continuation.cpsByMeta(":cps")) 38 | class TestGenerator 39 | { 40 | @:cps 41 | static function intGenerator(yield:YieldFunction):Void 42 | { 43 | for (i in 1...4) 44 | { 45 | for (j in 1...(i+1)) 46 | { 47 | trace('$j * $i ='); 48 | @await yield(i * j); 49 | trace("-------"); 50 | } 51 | } 52 | } 53 | 54 | public static function main() 55 | { 56 | for (i in intGenerator) 57 | { 58 | trace(i); 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /tests/TestIfElse.hx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012,2013, 杨博 (Yang Bo) 2 | // All rights reserved. 3 | // 4 | // Author: 杨博 (Yang Bo) 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // * Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | // * Neither the name of the nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package tests; 31 | 32 | /** 33 | * @author 杨博 34 | */ 35 | @:build(com.dongxiguo.continuation.Continuation.cpsByMeta("cps")) 36 | class TestIfElse 37 | { 38 | @cps static function ifElse():Int 39 | { 40 | var a = Math.random() < 0.5 ? "0" : "1", b = Math.random() < 0.5 ? "a" : "b"; 41 | return Std.parseInt( 42 | switch (Math.random()) 43 | { 44 | case 0: ""; 45 | case r if (r < 0.5): a; 46 | default: b; 47 | }); 48 | } 49 | 50 | static function main() 51 | { 52 | ifElse( 53 | function(i) 54 | { 55 | trace(i); 56 | }); 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /tests/TestMacro.hx: -------------------------------------------------------------------------------- 1 | package tests; 2 | 3 | /** 4 | @author 杨博 5 | **/ 6 | @:build(com.dongxiguo.continuation.Continuation.cpsByMeta(":cps")) 7 | class TestMacro 8 | { 9 | 10 | static function main():Void 11 | { 12 | var f = com.dongxiguo.continuation.Continuation.cpsFunction(function() @await M.m()); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /tests/TestNode.hx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, 杨博 (Yang Bo) 2 | // All rights reserved. 3 | // 4 | // Author: 杨博 (Yang Bo) 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // * Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | // * Neither the name of the nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package tests; 31 | #if (nodejs && js) 32 | import js.Node; 33 | import com.dongxiguo.continuation.Continuation; 34 | using Lambda; 35 | /** 36 | * @author 杨博 37 | */ 38 | @:build(com.dongxiguo.continuation.Continuation.cpsByMeta(":async")) 39 | class TestNode 40 | { 41 | /** 42 | * Writes content to fd. 43 | */ 44 | @:async static function writeAll(fd:Int, content:String):Null 45 | { 46 | var buffer = new NodeBuffer(content); 47 | var totalWritten = 0; 48 | while (totalWritten < buffer.length) 49 | { 50 | var err, written = 51 | @await Node.fs.write( 52 | fd, buffer, 53 | totalWritten, buffer.length - totalWritten, null); 54 | if (err != null) 55 | { 56 | return err; 57 | } 58 | totalWritten += written; 59 | } 60 | return null; 61 | } 62 | 63 | /** 64 | * Creates a directory named "TestNode", and concurrently put 5 files into it. 65 | */ 66 | @:async static function startTest():Void 67 | { 68 | var err = @await Node.fs.mkdir("TestNode"); 69 | if (err != null) 70 | { 71 | trace("Node.fs.mkdir failed: " + err); 72 | return; 73 | } 74 | 75 | // Lambda.iter() forks threads for each element. 76 | // Fork 5 threads now! 77 | var fileName = @await ["1.txt", "2.log", "3.txt", "4.ini", "5.conf"].iter(); 78 | 79 | // Note that some asynchronous functions return more than one values! 80 | // It's OK in CPS functions, just like Lua. 81 | var err, fd = @await Node.fs.open("TestNode/" + fileName, "w+"); 82 | if (err != null) 83 | { 84 | trace("Node.fs.open failed: " + err); 85 | return; 86 | } 87 | 88 | // Invoke another CPS function. 89 | var err = @await writeAll(fd, "Content of " + fileName); 90 | if (err != null) 91 | { 92 | trace("Node.fs.write failed: " + err); 93 | return; 94 | } 95 | 96 | var err = @await Node.fs.close(fd); 97 | if (err != null) 98 | { 99 | trace("Node.fs.close failed: " + err); 100 | return; 101 | } 102 | } 103 | 104 | public static function main():Void 105 | { 106 | startTest( 107 | function():Void 108 | { 109 | trace("Test is done!"); 110 | }); 111 | } 112 | 113 | } 114 | #end 115 | -------------------------------------------------------------------------------- /tests/TestTailCall.hx: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, 杨博 (Yang Bo) 2 | // All rights reserved. 3 | // 4 | // Author: 杨博 (Yang Bo) 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions are met: 8 | // 9 | // * Redistributions of source code must retain the above copyright notice, 10 | // this list of conditions and the following disclaimer. 11 | // * Redistributions in binary form must reproduce the above copyright notice, 12 | // this list of conditions and the following disclaimer in the documentation 13 | // and/or other materials provided with the distribution. 14 | // * Neither the name of the nor the names of its contributors 15 | // may be used to endorse or promote products derived from this software 16 | // without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | // POSSIBILITY OF SUCH DAMAGE. 29 | 30 | package tests; 31 | 32 | @:build(com.dongxiguo.continuation.Continuation.cpsByMeta(":cps")) 33 | class TestTailCall 34 | { 35 | 36 | @:cps 37 | static function loop0() 38 | { 39 | var c = 0; 40 | for (i in 0...0x7FF) 41 | { 42 | c += i; 43 | var a = 2; 44 | c += a; 45 | var b = 1; 46 | c -= b; 47 | } 48 | return c; 49 | } 50 | 51 | public static function main() 52 | { 53 | loop0(function(c) trace(c)); 54 | } 55 | 56 | } --------------------------------------------------------------------------------