From 55b926410db3d46e5455243772f3714f746e0ae5 Mon Sep 17 00:00:00 2001 From: Alan Stagner Date: Thu, 3 Dec 2020 08:30:36 -0800 Subject: [PATCH] Add continue statement (#822) Note that documentation is still required. --- src/vm/wren_compiler.c | 19 +++++++++++++++ test/language/continue/closure_in_for.wren | 9 ++++++++ test/language/continue/closure_in_while.wren | 9 ++++++++ test/language/continue/exit_local_scopes.wren | 20 ++++++++++++++++ test/language/continue/in_for_loop.wren | 12 ++++++++++ .../continue/in_function_in_loop.wren | 7 ++++++ test/language/continue/in_method_in_loop.wren | 9 ++++++++ test/language/continue/in_while_loop.wren | 12 ++++++++++ test/language/continue/nested_for_loop.wren | 17 ++++++++++++++ test/language/continue/nested_while_loop.wren | 23 +++++++++++++++++++ test/language/continue/outside_loop.wren | 1 + 11 files changed, 138 insertions(+) create mode 100644 test/language/continue/closure_in_for.wren create mode 100644 test/language/continue/closure_in_while.wren create mode 100644 test/language/continue/exit_local_scopes.wren create mode 100644 test/language/continue/in_for_loop.wren create mode 100644 test/language/continue/in_function_in_loop.wren create mode 100644 test/language/continue/in_method_in_loop.wren create mode 100644 test/language/continue/in_while_loop.wren create mode 100644 test/language/continue/nested_for_loop.wren create mode 100644 test/language/continue/nested_while_loop.wren create mode 100644 test/language/continue/outside_loop.wren diff --git a/src/vm/wren_compiler.c b/src/vm/wren_compiler.c index ee8eb78a..820a68ec 100644 --- a/src/vm/wren_compiler.c +++ b/src/vm/wren_compiler.c @@ -87,6 +87,7 @@ typedef enum TOKEN_BANGEQ, TOKEN_BREAK, + TOKEN_CONTINUE, TOKEN_CLASS, TOKEN_CONSTRUCT, TOKEN_ELSE, @@ -567,6 +568,7 @@ typedef struct static Keyword keywords[] = { {"break", 5, TOKEN_BREAK}, + {"continue", 8, TOKEN_CONTINUE}, {"class", 5, TOKEN_CLASS}, {"construct", 9, TOKEN_CONSTRUCT}, {"else", 4, TOKEN_ELSE}, @@ -2621,6 +2623,7 @@ GrammarRule rules[] = /* TOKEN_EQEQ */ INFIX_OPERATOR(PREC_EQUALITY, "=="), /* TOKEN_BANGEQ */ INFIX_OPERATOR(PREC_EQUALITY, "!="), /* TOKEN_BREAK */ UNUSED, + /* TOKEN_CONTINUE */ UNUSED, /* TOKEN_CLASS */ UNUSED, /* TOKEN_CONSTRUCT */ { NULL, NULL, constructorSignature, PREC_NONE, NULL }, /* TOKEN_ELSE */ UNUSED, @@ -3028,6 +3031,22 @@ void statement(Compiler* compiler) // bytecode. emitJump(compiler, CODE_END); } + else if (match(compiler, TOKEN_CONTINUE)) + { + if (compiler->loop == NULL) + { + error(compiler, "Cannot use 'continue' outside of a loop."); + return; + } + + // Since we will be jumping out of the scope, make sure any locals in it + // are discarded first. + discardLocals(compiler, compiler->loop->scopeDepth + 1); + + // emit a jump back to the top of the loop + int loopOffset = compiler->fn->code.count - compiler->loop->start + 2; + emitShortArg(compiler, CODE_LOOP, loopOffset); + } else if (match(compiler, TOKEN_FOR)) { forStatement(compiler); diff --git a/test/language/continue/closure_in_for.wren b/test/language/continue/closure_in_for.wren new file mode 100644 index 00000000..ad6dd8e7 --- /dev/null +++ b/test/language/continue/closure_in_for.wren @@ -0,0 +1,9 @@ +var f +for (i in [1, 2, 3]) { + var j = 4 + f = Fn.new { System.print(i + j) } + continue +} + +f.call() +// expect: 7 \ No newline at end of file diff --git a/test/language/continue/closure_in_while.wren b/test/language/continue/closure_in_while.wren new file mode 100644 index 00000000..6f4d035f --- /dev/null +++ b/test/language/continue/closure_in_while.wren @@ -0,0 +1,9 @@ +var f +while (f == null) { + var i = "i" + f = Fn.new { System.print(i) } + continue +} + +f.call() +// expect: i diff --git a/test/language/continue/exit_local_scopes.wren b/test/language/continue/exit_local_scopes.wren new file mode 100644 index 00000000..ea771932 --- /dev/null +++ b/test/language/continue/exit_local_scopes.wren @@ -0,0 +1,20 @@ +for (i in 0..5) { + { + var a = "a" + { + var b = "b" + { + var c = "c" + if (i == 1) continue + } + } + } + + System.print(i) +} + +// expect: 0 +// expect: 2 +// expect: 3 +// expect: 4 +// expect: 5 diff --git a/test/language/continue/in_for_loop.wren b/test/language/continue/in_for_loop.wren new file mode 100644 index 00000000..291c7961 --- /dev/null +++ b/test/language/continue/in_for_loop.wren @@ -0,0 +1,12 @@ +for (i in [1, 2, 3, 4, 5]) { + System.print(i) + if (i > 2) continue + System.print(i) +} +// expect: 1 +// expect: 1 +// expect: 2 +// expect: 2 +// expect: 3 +// expect: 4 +// expect: 5 diff --git a/test/language/continue/in_function_in_loop.wren b/test/language/continue/in_function_in_loop.wren new file mode 100644 index 00000000..89085924 --- /dev/null +++ b/test/language/continue/in_function_in_loop.wren @@ -0,0 +1,7 @@ +var done = false +while (!done) { + Fn.new { + continue // expect error + } + done = true +} \ No newline at end of file diff --git a/test/language/continue/in_method_in_loop.wren b/test/language/continue/in_method_in_loop.wren new file mode 100644 index 00000000..2da0b2fa --- /dev/null +++ b/test/language/continue/in_method_in_loop.wren @@ -0,0 +1,9 @@ +var done = false +while (!done) { + class Foo { + method { + continue // expect error + } + } + done = true +} diff --git a/test/language/continue/in_while_loop.wren b/test/language/continue/in_while_loop.wren new file mode 100644 index 00000000..c05295f2 --- /dev/null +++ b/test/language/continue/in_while_loop.wren @@ -0,0 +1,12 @@ +var i = 0 +while (true) { + i = i + 1 + System.print(i) + if (i <= 2) continue + System.print(i) + break +} +// expect: 1 +// expect: 2 +// expect: 3 +// expect: 3 diff --git a/test/language/continue/nested_for_loop.wren b/test/language/continue/nested_for_loop.wren new file mode 100644 index 00000000..68e71001 --- /dev/null +++ b/test/language/continue/nested_for_loop.wren @@ -0,0 +1,17 @@ +for (i in 0..2) { + System.print("outer %(i)") + if (i == 1) continue + + for (j in 0..2) { + if (j == 1) continue + System.print("inner %(j)") + } +} + +// expect: outer 0 +// expect: inner 0 +// expect: inner 2 +// expect: outer 1 +// expect: outer 2 +// expect: inner 0 +// expect: inner 2 \ No newline at end of file diff --git a/test/language/continue/nested_while_loop.wren b/test/language/continue/nested_while_loop.wren new file mode 100644 index 00000000..f113a77b --- /dev/null +++ b/test/language/continue/nested_while_loop.wren @@ -0,0 +1,23 @@ +var i = 0 +while (i <= 2) { + i = i + 1 + + System.print("outer %(i)") + if (i == 2) continue + + var j = 0 + while (j <= 2) { + j = j + 1 + + if(j == 2) continue + System.print("inner %(j)") + } +} + +// expect: outer 1 +// expect: inner 1 +// expect: inner 3 +// expect: outer 2 +// expect: outer 3 +// expect: inner 1 +// expect: inner 3 \ No newline at end of file diff --git a/test/language/continue/outside_loop.wren b/test/language/continue/outside_loop.wren new file mode 100644 index 00000000..4209d942 --- /dev/null +++ b/test/language/continue/outside_loop.wren @@ -0,0 +1 @@ +continue // expect error