diff --git a/examples/code.cs b/examples/code.cs index 7e732c2d..a06dbb7e 100644 --- a/examples/code.cs +++ b/examples/code.cs @@ -42,6 +42,8 @@ multiline: [ if submarine.shields_up full_speed_ahead() fire_torpedos() +else if submarine.sinking + abandon_ship() else run_away(). diff --git a/lib/coffee_script/grammar.y b/lib/coffee_script/grammar.y index 661a02ee..766b90bd 100644 --- a/lib/coffee_script/grammar.y +++ b/lib/coffee_script/grammar.y @@ -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 diff --git a/lib/coffee_script/nodes.rb b/lib/coffee_script/nodes.rb index 75424e0b..af38b332 100644 --- a/lib/coffee_script/nodes.rb +++ b/lib/coffee_script/nodes.rb @@ -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