From 091ca40b94753143bd7a68b5582e7fda703b93c8 Mon Sep 17 00:00:00 2001 From: Scott Whittaker Date: Fri, 17 Oct 2014 22:11:48 -0400 Subject: [PATCH 1/2] test_automatic_semicolon_insertion: verify `++` in `for` loop --- test/test_automatic_semicolon_insertion.rb | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/test_automatic_semicolon_insertion.rb b/test/test_automatic_semicolon_insertion.rb index 8cb89d1..d1ff702 100644 --- a/test/test_automatic_semicolon_insertion.rb +++ b/test/test_automatic_semicolon_insertion.rb @@ -131,6 +131,30 @@ def test_no_for_insertion assert_nil @parser.parse("for (a;b\n){}") end + def test_no_for_insertion_with_prefix_plus_plus + # RKelly erroneously inserts a semicolon after `b`: + assert_sexp([[:for, [:resolve, 'a'], + [:resolve, 'b'], + [:prefix, [:resolve, 'c'], '++'], + [:block, []]]], + @parser.parse("for (a;\nb\n++c){}")) + + # This correct code, on the other hand, fails to parse. + assert_sexp([[:for, [:resolve, 'a'], + [:resolve, 'b'], + [:prefix, [:resolve, 'c'], '++'], + [:block, []]]], + @parser.parse("for (a;\nb;\n++c){}")) + end + + def test_no_for_insertion_with_postfix_plus_plus + assert_sexp([[:for, [:resolve, 'a'], + [:resolve, 'b'], + [:postfix, [:resolve, 'c'], '++'], + [:block, []]]], + @parser.parse("for (a;\nb;\nc++){}")) + end + def assert_sexp(expected, node) assert_equal(expected, node.to_sexp) end From 6795093cce76409f13c57f1308f1893b3cc43cb9 Mon Sep 17 00:00:00 2001 From: Scott Whittaker Date: Sat, 18 Oct 2014 18:04:36 -0500 Subject: [PATCH 2/2] Parser: never add semicolons inside `for` loop headers This patch fixes a bug in RKelly's automatic semicolon insertion logic: though the ECMAScript standard specifies that automatic semicolon insertion never takes place within the header of a `for` loop, this valid code would fail to parse: for(a; b; ++c) { /* ... */ } ...because RKelly erroneously attempts to insert a semicolon after `b`, which would be a valid insertion anywhere outside the header of a `for` loop. To fix this, this modifies `RKelly::Parser` to explicitly track when we're inside the loop header, including any nested parenthetical sub-expressions, so it won't attempt to insert a semicolon. --- lib/rkelly/parser.rb | 15 +++++++++++++++ test/test_automatic_semicolon_insertion.rb | 8 -------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/rkelly/parser.rb b/lib/rkelly/parser.rb index 920de83..1b97901 100644 --- a/lib/rkelly/parser.rb +++ b/lib/rkelly/parser.rb @@ -27,6 +27,7 @@ def initialize @tokens = [] @logger = nil @terminator = false + @for_header_parentheses = 0 @prev_token = nil @comments = [] end @@ -88,7 +89,21 @@ def next_token end end while([:COMMENT, :S].include?(n_token.name)) + + if '(' == n_token.value + if 'for' == @prev_token.value + @for_header_parentheses = 1 + elsif @for_header_parentheses > 0 + @for_header_parentheses += 1 + end + end + + if ')' == n_token.value && @for_header_parentheses > 0 + @for_header_parentheses -= 1 + end + if @terminator && + 0 == @for_header_parentheses && ((@prev_token && %w[continue break return throw].include?(@prev_token.value)) || (n_token && %w[++ --].include?(n_token.value))) @position -= 1 diff --git a/test/test_automatic_semicolon_insertion.rb b/test/test_automatic_semicolon_insertion.rb index d1ff702..b998f0e 100644 --- a/test/test_automatic_semicolon_insertion.rb +++ b/test/test_automatic_semicolon_insertion.rb @@ -132,14 +132,6 @@ def test_no_for_insertion end def test_no_for_insertion_with_prefix_plus_plus - # RKelly erroneously inserts a semicolon after `b`: - assert_sexp([[:for, [:resolve, 'a'], - [:resolve, 'b'], - [:prefix, [:resolve, 'c'], '++'], - [:block, []]]], - @parser.parse("for (a;\nb\n++c){}")) - - # This correct code, on the other hand, fails to parse. assert_sexp([[:for, [:resolve, 'a'], [:resolve, 'b'], [:prefix, [:resolve, 'c'], '++'],