diff --git a/Rakefile b/Rakefile index 6f508bde..82536e6a 100644 --- a/Rakefile +++ b/Rakefile @@ -13,8 +13,8 @@ end namespace :build do desc "Recompile the Racc parser (pass -v and -g for verbose debugging)" - task :parser, :extra_args do |t, args| - sh "racc #{args[:extra_args]} -o lib/coffee_script/parser.rb lib/coffee_script/grammar.y" + task :parser, :racc_args do |t, args| + sh "racc #{args[:racc_args]} -o lib/coffee_script/parser.rb lib/coffee_script/grammar.y" end desc "Compile the Narwhal interface for --interactive and --run" diff --git a/lib/coffee_script/grammar.y b/lib/coffee_script/grammar.y index d0e82c25..f9c4c2df 100644 --- a/lib/coffee_script/grammar.y +++ b/lib/coffee_script/grammar.y @@ -28,10 +28,10 @@ prechigh left '&&' '||' AND OR right '-=' '+=' '/=' '*=' '%=' right DELETE INSTANCEOF TYPEOF - left "." + left '.' right THROW FOR IN WHILE NEW SUPER left UNLESS IF ELSE EXTENDS - left ':' '=' '||=' '&&=' + left ASSIGN '||=' '&&=' right RETURN preclow @@ -115,14 +115,13 @@ rule # Assignment to a variable. Assign: - Value ":" Expression { result = AssignNode.new(val[0], val[2]) } - | Value "=" Expression { result = AssignNode.new(val[0], val[2]) } + Value ASSIGN Expression { result = AssignNode.new(val[0], val[2]) } ; # Assignment within an object literal. AssignObj: - IDENTIFIER ":" Expression { result = AssignNode.new(val[0], val[2], :object) } - | STRING ":" Expression { result = AssignNode.new(val[0], val[2], :object) } + IDENTIFIER ASSIGN Expression { result = AssignNode.new(val[0], val[2], :object) } + | STRING ASSIGN Expression { result = AssignNode.new(val[0], val[2], :object) } | Comment { result = val[0] } ; diff --git a/lib/coffee_script/lexer.rb b/lib/coffee_script/lexer.rb index 324c4e57..96a31877 100644 --- a/lib/coffee_script/lexer.rb +++ b/lib/coffee_script/lexer.rb @@ -40,6 +40,9 @@ module CoffeeScript # Tokens that always constitute the end of an expression. EXP_END = ['}', ')', ']'] + # Assignment tokens. + ASSIGN = [':', '='] + # Scan by attempting to match tokens one character at a time. Slow and steady. def tokenize(code) @code = code.chomp # Cleanup code by remove extra line breaks @@ -139,7 +142,8 @@ module CoffeeScript value ||= @chunk[0,1] skip_following_newlines if EXP_START.include?(value) remove_leading_newlines if EXP_END.include?(value) - token(value, value) + tag = ASSIGN.include?(value) ? :ASSIGN : value + token(tag, value) @i += value.length end diff --git a/test/fixtures/each.tokens b/test/fixtures/each.tokens index 4304bef4..149a3170 100644 --- a/test/fixtures/each.tokens +++ b/test/fixtures/each.tokens @@ -1 +1 @@ -[[:COMMENT, [" The cornerstone, an each implementation.", " Handles objects implementing forEach, arrays, and raw objects."]], ["\n", "\n"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "each"], [":", ":"], [:PARAM, "obj"], [",", ","], [:PARAM, "iterator"], [",", ","], [:PARAM, "context"], ["=>", "=>"], ["\n", "\n"], [:IDENTIFIER, "index"], [":", ":"], [:NUMBER, "0"], ["\n", "\n"], [:TRY, "try"], ["\n", "\n"], [:IF, "if"], [:IDENTIFIER, "obj"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "forEach"], ["\n", "\n"], [:IDENTIFIER, "obj"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "forEach"], ["(", "("], [:IDENTIFIER, "iterator"], [",", ","], [:IDENTIFIER, "context"], [")", ")"], ["\n", "\n"], [:ELSE, "else"], [:IF, "if"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "isArray"], ["(", "("], [:IDENTIFIER, "obj"], [")", ")"], [:OR, "or"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "isArguments"], ["(", "("], [:IDENTIFIER, "obj"], [")", ")"], ["\n", "\n"], [:IDENTIFIER, "iterator"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "call"], ["(", "("], [:IDENTIFIER, "context"], [",", ","], [:IDENTIFIER, "item"], [",", ","], [:IDENTIFIER, "i"], [",", ","], [:IDENTIFIER, "obj"], [")", ")"], [:FOR, "for"], [:IDENTIFIER, "item"], [",", ","], [:IDENTIFIER, "i"], [:IN, "in"], [:IDENTIFIER, "obj"], [".", "."], ["\n", "\n"], [:ELSE, "else"], ["\n", "\n"], [:IDENTIFIER, "iterator"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "call"], ["(", "("], [:IDENTIFIER, "context"], [",", ","], [:IDENTIFIER, "obj"], ["[", "["], [:IDENTIFIER, "key"], ["]", "]"], [",", ","], [:IDENTIFIER, "key"], [",", ","], [:IDENTIFIER, "obj"], [")", ")"], [:FOR, "for"], [:IDENTIFIER, "key"], [:IN, "in"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "keys"], ["(", "("], [:IDENTIFIER, "obj"], [")", ")"], [".", "."], [".", "."], ["\n", "\n"], [:CATCH, "catch"], [:IDENTIFIER, "e"], ["\n", "\n"], [:THROW, "throw"], [:IDENTIFIER, "e"], [:IF, "if"], [:IDENTIFIER, "e"], [:ISNT, "isnt"], [:IDENTIFIER, "breaker"], [".", "."], ["\n", "\n"], [:IDENTIFIER, "obj"], [".", "."]] \ No newline at end of file +[[:COMMENT, [" The cornerstone, an each implementation.", " Handles objects implementing forEach, arrays, and raw objects."]], ["\n", "\n"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "each"], [:ASSIGN, ":"], [:PARAM, "obj"], [",", ","], [:PARAM, "iterator"], [",", ","], [:PARAM, "context"], ["=>", "=>"], ["\n", "\n"], [:IDENTIFIER, "index"], [:ASSIGN, ":"], [:NUMBER, "0"], ["\n", "\n"], [:TRY, "try"], ["\n", "\n"], [:IF, "if"], [:IDENTIFIER, "obj"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "forEach"], ["\n", "\n"], [:IDENTIFIER, "obj"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "forEach"], ["(", "("], [:IDENTIFIER, "iterator"], [",", ","], [:IDENTIFIER, "context"], [")", ")"], ["\n", "\n"], [:ELSE, "else"], [:IF, "if"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "isArray"], ["(", "("], [:IDENTIFIER, "obj"], [")", ")"], [:OR, "or"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "isArguments"], ["(", "("], [:IDENTIFIER, "obj"], [")", ")"], ["\n", "\n"], [:IDENTIFIER, "iterator"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "call"], ["(", "("], [:IDENTIFIER, "context"], [",", ","], [:IDENTIFIER, "item"], [",", ","], [:IDENTIFIER, "i"], [",", ","], [:IDENTIFIER, "obj"], [")", ")"], [:FOR, "for"], [:IDENTIFIER, "item"], [",", ","], [:IDENTIFIER, "i"], [:IN, "in"], [:IDENTIFIER, "obj"], [".", "."], ["\n", "\n"], [:ELSE, "else"], ["\n", "\n"], [:IDENTIFIER, "iterator"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "call"], ["(", "("], [:IDENTIFIER, "context"], [",", ","], [:IDENTIFIER, "obj"], ["[", "["], [:IDENTIFIER, "key"], ["]", "]"], [",", ","], [:IDENTIFIER, "key"], [",", ","], [:IDENTIFIER, "obj"], [")", ")"], [:FOR, "for"], [:IDENTIFIER, "key"], [:IN, "in"], [:IDENTIFIER, "_"], [:PROPERTY_ACCESS, "."], [:IDENTIFIER, "keys"], ["(", "("], [:IDENTIFIER, "obj"], [")", ")"], [".", "."], [".", "."], ["\n", "\n"], [:CATCH, "catch"], [:IDENTIFIER, "e"], ["\n", "\n"], [:THROW, "throw"], [:IDENTIFIER, "e"], [:IF, "if"], [:IDENTIFIER, "e"], [:ISNT, "isnt"], [:IDENTIFIER, "breaker"], [".", "."], ["\n", "\n"], [:IDENTIFIER, "obj"], [".", "."]] \ No newline at end of file diff --git a/test/fixtures/execution/test_everything.cs b/test/fixtures/execution/test_everything.cs index 827c80e1..f0a866dc 100644 --- a/test/fixtures/execution/test_everything.cs +++ b/test/fixtures/execution/test_everything.cs @@ -16,7 +16,11 @@ func: => else c.text + '---'. - c.list: l for l in c.text.split('') if l is '-'. + d = { + text = c.text + } + + c.list: l for l in d.text.split('') if l is '-'. c.single: c.list[1, 1][0]. diff --git a/test/unit/test_lexer.rb b/test/unit/test_lexer.rb index 0f3297b4..b626e4f3 100644 --- a/test/unit/test_lexer.rb +++ b/test/unit/test_lexer.rb @@ -12,14 +12,14 @@ class LexerTest < Test::Unit::TestCase def test_lexing_basic_assignment code = "a: 'one'; b: [1, 2]" - assert @lex.tokenize(code) == [[:IDENTIFIER, "a"], [":", ":"], - [:STRING, "'one'"], [";", ";"], [:IDENTIFIER, "b"], [":", ":"], + assert @lex.tokenize(code) == [[:IDENTIFIER, "a"], [:ASSIGN, ":"], + [:STRING, "'one'"], [";", ";"], [:IDENTIFIER, "b"], [:ASSIGN, ":"], ["[", "["], [:NUMBER, "1"], [",", ","], [:NUMBER, "2"], ["]", "]"]] end def test_lexing_object_literal code = "{one : 1}" - assert @lex.tokenize(code) == [["{", "{"], [:IDENTIFIER, "one"], [":", ":"], + assert @lex.tokenize(code) == [["{", "{"], [:IDENTIFIER, "one"], [:ASSIGN, ":"], [:NUMBER, "1"], ["}", "}"]] end @@ -37,9 +37,9 @@ class LexerTest < Test::Unit::TestCase def test_lexing_comment code = "a: 1\n # comment\n # on two lines\nb: 2" - assert @lex.tokenize(code) == [[:IDENTIFIER, "a"], [":", ":"], [:NUMBER, "1"], + assert @lex.tokenize(code) == [[:IDENTIFIER, "a"], [:ASSIGN, ":"], [:NUMBER, "1"], ["\n", "\n"], [:COMMENT, [" comment", " on two lines"]], ["\n", "\n"], - [:IDENTIFIER, "b"], [":", ":"], [:NUMBER, "2"]] + [:IDENTIFIER, "b"], [:ASSIGN, ":"], [:NUMBER, "2"]] end def test_lexing