execution tests still pass -- more lexer block insertion and 2 shift/reduces in the grammar now

This commit is contained in:
Jeremy Ashkenas 2009-12-30 15:52:07 -05:00
parent c822f96145
commit 9a946d2ac5
5 changed files with 30 additions and 25 deletions

View File

@ -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.

View File

@ -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

View File

@ -2,4 +2,7 @@ result: try
nonexistent * missing
catch error
true
print(result)
result2: try nonexistent * missing catch error then true
print(result is true and result2 is true)

View File

@ -9,5 +9,5 @@ result: switch num
when 10 then true
when 11 then false
else false
print(result)

View File

@ -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