Skip to content

Commit

Permalink
add yield tests from nadako/haxe-coroutines#6
Browse files Browse the repository at this point in the history
  • Loading branch information
Simn committed Feb 15, 2024
1 parent 62bdf66 commit 9f0238f
Show file tree
Hide file tree
Showing 18 changed files with 904 additions and 43 deletions.
8 changes: 4 additions & 4 deletions src/coro/coroFromTexpr.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ open Type
open CoroTypes
open CoroFunctions

let terminate cb kind t p =
if cb.cb_next.next_kind = NextUnknown then
cb.cb_next <- {next_kind = kind; next_type = t; next_pos = p}

let e_no_value = Texpr.Builder.make_null t_dynamic null_pos

type coro_ret =
Expand Down Expand Up @@ -35,6 +31,10 @@ let expr_to_coro ctx (vresult,verror) cb_root e =
if cb.cb_next.next_kind = NextUnknown && e != e_no_value && cb != ctx.cb_unreachable then
DynArray.add cb.cb_el e
in
let terminate cb kind t p =
if cb.cb_next.next_kind = NextUnknown && cb != ctx.cb_unreachable then
cb.cb_next <- {next_kind = kind; next_type = t; next_pos = p}
in
let fall_through cb_from cb_to =
terminate cb_from (NextFallThrough cb_to) t_dynamic null_pos
in
Expand Down
2 changes: 1 addition & 1 deletion src/coro/coroToTexpr.ml
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ let block_to_texpr_coroutine ctx cb vcontinuation vresult verror p =
cb.cb_id
in
if not (DynArray.empty cb.cb_el) then
add_state (Some cb_next.cb_id) []
add_state (Some (skip_loop cb_next)) []
else
skip_loop cb
| NextReturnVoid | NextReturn _ as r ->
Expand Down
1 change: 0 additions & 1 deletion src/filters/filters.ml
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,6 @@ let run tctx main before_destruction =
"add_final_return",if com.config.pf_add_final_return then add_final_return else (fun e -> e);
"RenameVars",(match com.platform with
| Eval -> (fun e -> e)
| Jvm -> (fun e -> e)
| _ -> (fun e -> RenameVars.run tctx.c.curclass.cl_path locals e));
"mark_switch_break_loops",mark_switch_break_loops;
] in
Expand Down
19 changes: 19 additions & 0 deletions tests/misc/coroutines/src/BaseCase.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@:keepSub
@:keep
class BaseCase implements utest.ITest {
var dummy:String = '';

public function new() {}

public function setup() {
dummy = '';
}

function assert<T>(expected:Array<T>, generator:Iterator<T>, ?p:haxe.PosInfos) {
dummy = '';
for (it in generator) {
Assert.equals(expected.shift(), it, p);
}
Assert.equals(0, expected.length, p);
}
}
9 changes: 9 additions & 0 deletions tests/misc/coroutines/src/Main.hx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import yield.*;

function main() {
utest.UTest.run([
new TestBasic(),
Expand All @@ -7,5 +9,12 @@ function main() {
#if js
new TestJsPromise(),
#end
new TestYieldBasic(),
new TestYieldIf(),
new TestYieldFor(),
new TestYieldClosure(),
new TestYieldSwitch(),
new TestYieldTryCatch(),
new TestYieldWhile(),
]);
}
2 changes: 1 addition & 1 deletion tests/misc/coroutines/src/TestControlFlow.hx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class TestControlFlow extends utest.Test {
}

@:coroutine
private function mapCalls<TArg,TRet>(args:Array<TArg>, f:haxe.coro.Coroutine<TArg->TRet>):Array<TRet> {
private function mapCalls<TArg,TRet>(args:Array<TArg>, f:Coroutine<TArg->TRet>):Array<TRet> {
return [for (arg in args) f(arg)];
}

Expand Down
36 changes: 1 addition & 35 deletions tests/misc/coroutines/src/TestGenerator.hx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import haxe.coro.Coroutine;
import yield.Yield;

class TestGenerator extends utest.Test {
function testSimple() {
Expand Down Expand Up @@ -38,40 +38,6 @@ class TestGenerator extends utest.Test {
}
}

private typedef Yield<T> = Coroutine<T->Void>;

private function sequence<T>(f:Coroutine<Yield<T>->Void>):Iterator<T> {
var finished = false;
var nextValue:T = null;

var nextStep = null;

function finish(_, _) {
finished = true;
}

@:coroutine function yield(value:T) {
nextValue = value;
Coroutine.suspend(cont -> nextStep = cont);
}

function hasNext():Bool {
if (nextStep == null) {
nextStep = f.create(yield, finish);
nextStep(null, null);
}
return !finished;
}

function next():T {
var value = nextValue;
nextStep(null, null);
return value;
}

return {hasNext: hasNext, next: next};
}

private typedef Tree<T> = {
var leaf:T;
var ?left:Tree<T>;
Expand Down
1 change: 0 additions & 1 deletion tests/misc/coroutines/src/TestJsPromise.hx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import js.lib.Error;
import js.lib.Promise;
import haxe.coro.Coroutine;

@:coroutine
private function await<T>(p:Promise<T>):T {
Expand Down
1 change: 1 addition & 0 deletions tests/misc/coroutines/src/import.hx
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
import utest.Assert;
import utest.Async;
import haxe.coro.Coroutine;
126 changes: 126 additions & 0 deletions tests/misc/coroutines/src/yield/TestYieldBasic.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package yield;

import yield.Yield;

@:build(yield.YieldMacro.build())
class TestYieldBasic extends BaseCase {
public function testBasicYieldReturn() {
assert([10, 20], basicYieldReturn());
Assert.equals('123', dummy);
}

@:yield function basicYieldReturn():Iterator<Int> {
dummy += '1';
@:yield return 10;
dummy += '2';
@:yield return 20;
dummy += '3';
}

#if broken

public function testBasicYieldReturn_multipleIterations() {
var generator = basicYieldReturn();
assert([10, 20], generator);
Assert.equals('123', dummy);
assert([10, 20], generator);
Assert.equals('123', dummy);
}

#end

public function testBasicYieldBreak() {
assert([10], basicYieldBreak());
Assert.equals('12', dummy);
}

@:yield function basicYieldBreak() {
dummy += '1';
@:yield return 10;
dummy += '2';
return;
dummy += '3';
@:yield return 20;
dummy += '4';
}

public function testLocalVars() {
assert([10, 25, 40, 19, 30], localVars(10, 20, 30));
}

@:yield function localVars(a:Int, b:Int, a1:Int) {
var q = b;
@:yield return a;
var a = 5;
@:yield return a + q;
var q = q * 2;
@:yield return q;
for (a in 1...2) {
q = a * 10;
}
for (c in 1...2) {
q += 5;
}
for (i in 0...2) {
for (j in 0...2) {
q += i + j;
}
}
@:yield return q;
@:yield return a1;
}

public function testLocalVars_sameVarNameInTwoChildScopes() {
assert([10], localVars_sameVarNameInTwoChildScopes(true));
assert([20], localVars_sameVarNameInTwoChildScopes(false));
}

@:yield function localVars_sameVarNameInTwoChildScopes(condition:Bool) {
if (condition) {
var v = 10;
@:yield return v;
} else {
var v = 'ab';
@:yield return v.length * 10;
}
}

public function testLocalFunction() {
assert([10, 20, 30], localFunction());
}

@:yield function localFunction() {
inline function local1()
return 20;
function local2() {
return 30;
}
@:yield return 10;
@:yield return local1();
var value = local2();
@:yield return value;
}

public function testInheritance() {
var result = [for (it in descendantsOfParent()) it];
Assert.equals(2, result.length);
}

@:yield function descendantsOfParent():Iterator<Parent> {
@:yield return new Child1();
@:yield return new Child2();
}
}

private class Parent {
public function new() {}
}

private class Child1 extends Parent {}
private class Child2 extends Parent {}

function main() {
utest.UTest.run([
new TestYieldBasic()
]);
}
88 changes: 88 additions & 0 deletions tests/misc/coroutines/src/yield/TestYieldClosure.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package yield;

import yield.Yield;

@:build(yield.YieldMacro.build())
class TestYieldClosure extends BaseCase {
// @:yield function closure(arg) {
// var fn = @:yield function(arg2) {
// }
// @:yield function another(arg2) {
// trace({arg2:arg2});
// }
// }

var anchor:Dynamic;

public function testClosure() {
assert([20, 40, 60, 80, 20, 40, 60, 80, 100], closure(2));
Assert.equals('1234512345', dummy);
}

@:yield function closure(arg) {
var a:Dynamic = arg;
anchor = a;
var fn = @:yield function(arg2) {
var b:Dynamic = arg;
anchor = b;
dummy += '1';
@:yield return arg * 10;
dummy += '2';
@:yield return cast a * 20; // TODO: I had to insert these casts because this was errorring with Float should be Int
dummy += '3';
@:yield return cast b * 30;
dummy += '4';
@:yield return arg2 * 40;
dummy += '5';
}
for(i in fn(a)) {
@:yield return i;
}
@:yield function another(arg2) {
var b:Dynamic = arg;
anchor = b;
dummy += '1';
@:yield return arg * 10;
dummy += '2';
@:yield return cast a * 20;
dummy += '3';
@:yield return cast b * 30;
dummy += '4';
@:yield return arg2 * 40;
dummy += '5';
for(i in (@:yield function() @:yield return arg2 * 50)()) {
@:yield return i;
}
}
for(i in another(a)) {
@:yield return i;
}
}


public function testClosure_nested() {
assert([100], closure_nested(10));
}

@:yield function closure_nested(arg) {
@:yield function another(arg2) {
var fn = @:yield function() @:yield return arg2 * 10;
for(i in fn()) @:yield return i;
}
for(i in another(arg)) {
@:yield return i;
}
}


public function testClosure_withoutYield() {
assert([0, 10], closure_withoutYield(1));
}

@:yield function closure_withoutYield(arg:Int) {
var fn = function() return arg * 10;
for(i in 0...2) {
@:yield return fn() * i;
}
}
}
Loading

0 comments on commit 9f0238f

Please sign in to comment.