1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

after a lot of grammar wrestling, got the if-else chains to parse unambiguously. Now you only need a single period to close chains of any length.

This commit is contained in:
Jeremy Ashkenas 2009-12-18 00:49:23 -05:00
parent a50446a0ca
commit 049358d006
3 changed files with 46 additions and 16 deletions

View file

@ -42,6 +42,8 @@ multiline: [
if submarine.shields_up
full_speed_ahead()
fire_torpedos()
else if submarine.sinking
abandon_ship()
else
run_away().

View file

@ -27,8 +27,7 @@ prechigh
right '-=' '+=' '/=' '*=' '||=' '&&='
right DELETE
right RETURN THROW FOR WHILE
left UNLESS
left IF
left UNLESS IF ELSE
nonassoc "."
preclow
@ -238,17 +237,6 @@ rule
| ArgList Terminator Expression { result = val[0] << val[2] }
;
# If statements, including post-fix ifs and unlesses.
If:
IF Expression
Then Expressions "." { result = IfNode.new(val[1], val[3]) }
| IF Expression
Then Expressions
ELSE Expressions "." { result = IfNode.new(val[1], val[3], val[5]) }
| Expression IF Expression { result = IfNode.new(val[2], Expressions.new([val[0]])) }
| Expression UNLESS Expression { result = IfNode.new(val[2], Expressions.new([val[0]]), nil, :invert) }
;
# Try/catch/finally exception handling blocks.
Try:
TRY Expressions CATCH IDENTIFIER
@ -311,6 +299,41 @@ rule
CASE Expression Then Expressions { result = IfNode.new(val[1], val[3]) }
;
# All of the following nutso if-else destructuring is to make the
# grammar expand unambiguously.
# An elsif portion of an if-else block.
ElsIf:
ELSE IF Expression
Then Expressions { result = IfNode.new(val[2], val[4]) }
;
# Multiple elsifs can be chained together.
ElsIfs:
ElsIf { result = val[0] }
| ElsIfs ElsIf { result = val[0].add_else(val[1]) }
;
# Terminating else bodies are strictly optional.
ElseBody
"." { result = nil }
| ELSE Expressions "." { result = val[1] }
;
# All the alternatives for ending an if-else block.
IfEnd:
ElseBody { result = val[0] }
| ElsIfs ElseBody { result = val[0].add_else(val[1]) }
;
# The full complement of if blocks, including postfix one-liner ifs and unlesses.
If:
IF Expression
Then Expressions IfEnd { result = IfNode.new(val[1], val[3], val[4]) }
| Expression IF Expression { result = IfNode.new(val[2], Expressions.new([val[0]])) }
| Expression UNLESS Expression { result = IfNode.new(val[2], Expressions.new([val[0]]), nil, :invert) }
;
end
---- header

View file

@ -464,20 +464,25 @@ module CoffeeScript
@is_statement ||= (@body.statement? || (@else_body && @else_body.statement?))
end
def custom_return?
statement?
end
def line_ending
statement? ? '' : ';'
end
def compile(indent, scope, opts={})
statement? ? compile_statement(indent, scope, opts) : compile_ternary(indent, scope)
opts[:statement] || statement? ? compile_statement(indent, scope, opts) : compile_ternary(indent, scope)
end
# Compile the IfNode as a regular if-else statement.
# Compile the IfNode as a regular if-else statement. Flattened chains
# force sub-else bodies into statement form.
def compile_statement(indent, scope, opts)
if_part = "if (#{@condition.compile(indent, scope, :no_paren => true)}) {\n#{Expressions.wrap(@body).compile(indent + TAB, scope, opts)}\n#{indent}}"
return if_part unless @else_body
else_part = chain? ?
" else #{@else_body.compile(indent, scope, opts)}" :
" else #{@else_body.compile(indent, scope, opts.merge(:statement => true))}" :
" else {\n#{Expressions.wrap(@else_body).compile(indent + TAB, scope, opts)}\n#{indent}}"
if_part + else_part
end