From 9a946d2ac573c6c2480afd7fa38edd7d06e01714 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Wed, 30 Dec 2009 15:52:07 -0500 Subject: [PATCH] execution tests still pass -- more lexer block insertion and 2 shift/reduces in the grammar now --- lib/coffee_script/grammar.y | 15 +++++++------- lib/coffee_script/lexer.rb | 13 +++++++----- .../execution/test_assign_to_try_catch.coffee | 5 ++++- test/fixtures/execution/test_switch.coffee | 2 +- test/unit/test_parser.rb | 20 +++++++++---------- 5 files changed, 30 insertions(+), 25 deletions(-) diff --git a/lib/coffee_script/grammar.y b/lib/coffee_script/grammar.y index 1a14e4c7..fdbf88c5 100644 --- a/lib/coffee_script/grammar.y +++ b/lib/coffee_script/grammar.y @@ -1,7 +1,7 @@ class Parser # Declare tokens produced by the lexer -token IF ELSE THEN UNLESS +token IF ELSE UNLESS token NUMBER STRING REGEX token TRUE FALSE YES NO ON OFF token IDENTIFIER PROPERTY_ACCESS @@ -32,15 +32,15 @@ prechigh left '.' right INDENT left OUTDENT - right THROW FOR IN WHILE WHEN NEW SUPER THEN ELSE + right THROW FOR IN WHILE WHEN NEW SUPER ELSE left UNLESS EXTENDS IF left ASSIGN '||=' '&&=' right RETURN '=>' preclow -# We expect 4 shift/reduce errors for optional syntax. +# We expect 2 shift/reduce errors for optional syntax. # There used to be 252 -- greatly improved. -expect 4 +expect 2 rule @@ -344,9 +344,9 @@ rule # An individual when. When: - WHEN Expression Block { result = IfNode.new(val[1], val[2], nil, {:statement => true}) } - | WHEN Expression - THEN Expression Terminator { result = IfNode.new(val[1], val[3], nil, {:statement => true}) } + WHEN Expression Block { result = IfNode.new(val[1], val[2], nil, {:statement => true}) } + | WHEN Expression Block Terminator { result = IfNode.new(val[1], val[2], nil, {:statement => true}) } + | Comment ; # All of the following nutso if-else destructuring is to make the @@ -354,7 +354,6 @@ rule IfBlock: IF Expression Block { result = IfNode.new(val[1], val[2]) } - | IF Expression THEN Expression { result = IfNode.new(val[1], val[3]) } ; # An elsif portion of an if-else block. diff --git a/lib/coffee_script/lexer.rb b/lib/coffee_script/lexer.rb index 27f0795e..e7ac718b 100644 --- a/lib/coffee_script/lexer.rb +++ b/lib/coffee_script/lexer.rb @@ -27,7 +27,7 @@ module CoffeeScript COMMENT = /\A((#[^\n]*\s*)+)/m CODE = /\A(=>)/ REGEX = /\A(\/(.*?)[^\\]\/[imgy]{0,4})/ - MULTI_DENT = /\A((\n+([ \t]*(?=\S)))+)/ + MULTI_DENT = /\A((\n+([ \t]*(?=\S))?)+)/ LAST_DENT = /\n+([ \t]*)\Z/ # Token cleaning regexes. @@ -47,7 +47,8 @@ module CoffeeScript # Single-line flavors of block expressions that have unclosed endings. # The grammar can't disambiguate them, so we insert the implicit indentation. - SINGLE_LINERS = [:ELSE, "=>"] + SINGLE_LINERS = [:ELSE, "=>", :TRY, :FINALLY, :THEN] + SINGLE_CLOSERS = ["\n", :CATCH, :FINALLY, :ELSE] # The inverse mappings of token pairs we're trying to fix up. INVERSES = {:INDENT => :OUTDENT, :OUTDENT => :INDENT, '(' => ')', ')' => '('} @@ -257,13 +258,15 @@ module CoffeeScript if SINGLE_LINERS.include?(token[0]) && post[0] != :INDENT line = token[1].line @tokens.insert(i + 1, [:INDENT, Value.new(2, line)]) + idx = i + 1 loop do - i += 1 - if !@tokens[i] || @tokens[i][0] == "\n" - @tokens.insert(i, [:OUTDENT, Value.new(2, line)]) + idx += 1 + if !@tokens[idx] || SINGLE_CLOSERS.include?(@tokens[idx][0]) + @tokens.insert(idx, [:OUTDENT, Value.new(2, line)]) break end end + @tokens.delete_at(i) if token[0] == :THEN end end end diff --git a/test/fixtures/execution/test_assign_to_try_catch.coffee b/test/fixtures/execution/test_assign_to_try_catch.coffee index c635bbf0..9de8178f 100644 --- a/test/fixtures/execution/test_assign_to_try_catch.coffee +++ b/test/fixtures/execution/test_assign_to_try_catch.coffee @@ -2,4 +2,7 @@ result: try nonexistent * missing catch error true -print(result) \ No newline at end of file + +result2: try nonexistent * missing catch error then true + +print(result is true and result2 is true) \ No newline at end of file diff --git a/test/fixtures/execution/test_switch.coffee b/test/fixtures/execution/test_switch.coffee index 2fc7da81..dad43358 100644 --- a/test/fixtures/execution/test_switch.coffee +++ b/test/fixtures/execution/test_switch.coffee @@ -9,5 +9,5 @@ result: switch num when 10 then true when 11 then false else false - + print(result) diff --git a/test/unit/test_parser.rb b/test/unit/test_parser.rb index 88305e73..83d6ac64 100644 --- a/test/unit/test_parser.rb +++ b/test/unit/test_parser.rb @@ -8,7 +8,7 @@ class ParserTest < Test::Unit::TestCase def test_parsing_an_empty_string nodes = @par.parse("") - assert nodes.is_a? Expressions + assert nodes.is_a?(Expressions) assert nodes.expressions.empty? end @@ -16,14 +16,14 @@ class ParserTest < Test::Unit::TestCase nodes = @par.parse("a: 'one'").expressions assert nodes.length == 1 assign = nodes.first - assert assign.is_a? AssignNode + assert assign.is_a?(AssignNode) assert assign.variable.literal == 'a' end def test_parsing_an_object_literal nodes = @par.parse("{one : 1\ntwo : 2}").expressions obj = nodes.first.literal - assert obj.is_a? ObjectNode + assert obj.is_a?(ObjectNode) assert obj.properties.first.variable.literal.value == "one" assert obj.properties.last.variable.literal.value == "two" end @@ -32,21 +32,21 @@ class ParserTest < Test::Unit::TestCase code = @par.parse("x, y => x * y").expressions.first assert code.params == ['x', 'y'] body = code.body.expressions.first - assert body.is_a? OpNode + assert body.is_a?(OpNode) assert body.operator == '*' end def test_parsing_if_statement the_if = @par.parse("clap_your_hands() if happy").expressions.first - assert the_if.is_a? IfNode + assert the_if.is_a?(IfNode) assert the_if.condition.literal == 'happy' - assert the_if.body.is_a? CallNode + assert the_if.body.is_a?(CallNode) assert the_if.body.variable.literal == 'clap_your_hands' end def test_parsing_array_comprehension nodes = @par.parse("i for x, i in [10, 9, 8, 7, 6, 5] when i % 2 is 0").expressions - assert nodes.first.is_a? ForNode + assert nodes.first.is_a?(ForNode) assert nodes.first.body.literal == 'i' assert nodes.first.filter.operator == '===' assert nodes.first.source.literal.objects.last.literal.value == "5" @@ -54,7 +54,7 @@ class ParserTest < Test::Unit::TestCase def test_parsing_comment nodes = @par.parse("a: 1\n# comment\nb: 2").expressions - assert nodes[1].is_a? CommentNode + assert nodes[1].is_a?(CommentNode) end def test_parsing_inner_comments @@ -65,9 +65,9 @@ class ParserTest < Test::Unit::TestCase def test_parsing nodes = @par.parse(File.read('test/fixtures/generation/each.coffee')) assign = nodes.expressions[1] - assert assign.is_a? AssignNode + assert assign.is_a?(AssignNode) assert assign.variable.literal == '_' - assert assign.value.is_a? CodeNode + assert assign.value.is_a?(CodeNode) assert assign.value.params == ['obj', 'iterator', 'context'] assert nodes.compile == File.read('test/fixtures/generation/each.js') end