From e1f7dd437ca0c7754427b5f4ccb4d620932b1052 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilev Date: Fri, 2 Feb 2024 03:13:21 +0800 Subject: [PATCH] bare return statements --- src/cmd.js | 7 ++++++- src/eval.js | 15 ++++++++++++++- src/parse_js.js | 23 +++++++++++++++-------- test/test.js | 22 +++++++++++++++++++--- 4 files changed, 54 insertions(+), 13 deletions(-) diff --git a/src/cmd.js b/src/cmd.js index d07a79e..ca38072 100644 --- a/src/cmd.js +++ b/src/cmd.js @@ -624,7 +624,12 @@ const get_stmt_value_explorer = (state, stmt) => { if(stmt.result.ok) { if(stmt.type == 'return') { - result = stmt.children[0].result + if(stmt.expr == null) { + // add fake version number + result = {ok: true, value: undefined, version_number: 0} + } else { + result = stmt.children[0].result + } } else if(['let', 'const', 'assignment'].includes(stmt.type)) { if(stmt.children.find(c => c.type == 'assignment_pair') != null) { diff --git a/src/eval.js b/src/eval.js index 6407629..cd36646 100644 --- a/src/eval.js +++ b/src/eval.js @@ -192,7 +192,11 @@ const codegen = (node, node_cxt) => { '' ) } else if(node.type == 'return') { - return 'return ' + do_codegen(node.expr) + ';' + if(node.expr == null) { + return 'return ;' + } else { + return 'return ' + do_codegen(node.expr) + ';' + } } else if(node.type == 'throw') { return 'throw ' + do_codegen(node.expr) + ';' } else if(node.type == 'if') { @@ -1278,6 +1282,15 @@ const eval_statement = (s, eval_cxt, frame_cxt) => { } } else if(s.type == 'return') { + if(s.expr == null) { + return { + ok: true, + returned: true, + node: {...s, result: {ok: true}}, + eval_cxt, + } + } + const {node, eval_cxt: next_eval_cxt} = eval_frame_expr(s.expr, eval_cxt, frame_cxt) diff --git a/src/parse_js.js b/src/parse_js.js index b7d5e0a..3c01822 100644 --- a/src/parse_js.js +++ b/src/parse_js.js @@ -1225,15 +1225,10 @@ const assignment = if_ok( ) -const return_statement = +const return_statement = either( + // return expr if_ok( seq_select(1, [ - - // We forbid bare return statement - // TODO bare return statement - // TODO implement bare return statement compatible with ASI - // see https://riptutorial.com/javascript/example/15248/rules-of-automatic-semicolon-insertion - // see ASI_restrited unit test not_followed_by( literal('return'), newline, @@ -1245,7 +1240,19 @@ const return_statement = type: 'return', children: [value], }) - ) + ), + + // bare return statement + if_ok( + literal('return'), + node => ({ + ...node, + type: 'return', + children: [], + value: null, + }) + ), +) const if_branch = if_ok( seq_select(1, [ diff --git a/test/test.js b/test/test.js index f743450..36a9e1e 100644 --- a/test/test.js +++ b/test/test.js @@ -487,14 +487,13 @@ export const tests = [ ) }), - test('ASI_restrited', () => { - // Currently we forbid bare return statement, TODO + test('ASI_restricted', () => { assert_equal( do_parse(` return 1 `).ok, - false + true ) assert_equal( do_parse(` @@ -945,6 +944,23 @@ export const tests = [ assert_equal(x.result.version_number, 0) }), + test('bare return statement', () => { + const code = ` + function test() { + return + } + test() /*call*/ + ` + assert_value_explorer( + test_initial_state(code, code.indexOf('test() /*call*/')), + undefined, + ) + assert_value_explorer( + test_initial_state(code, code.indexOf('return')), + undefined, + ) + }), + test('array spread not iterable', () => { assert_code_error( `[...null]`,