Skip to content

Instantly share code, notes, and snippets.

@timbertson
Created April 26, 2010 05:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timbertson/379009 to your computer and use it in GitHub Desktop.
Save timbertson/379009 to your computer and use it in GitHub Desktop.
diff --git a/src/grammar.coffee b/src/grammar.coffee
index 0a8d950..219911d 100644
--- a/src/grammar.coffee
+++ b/src/grammar.coffee
@@ -457,8 +457,8 @@ grammar: {
# The CoffeeScript switch/when/else block replaces the JavaScript
# switch/case/default by compiling into an if-else chain.
Switch: [
- o "SWITCH Expression INDENT Whens OUTDENT", -> $4.rewrite_condition $2
- o "SWITCH Expression INDENT Whens ELSE Block OUTDENT", -> $4.rewrite_condition($2).add_else $6, true
+ o "SWITCH Expression INDENT Whens OUTDENT", -> $4.unwrap().switches_over $2
+ o "SWITCH Expression INDENT Whens ELSE Block OUTDENT", -> $4.switches_over($2).add_else $6, true
]
# The inner list of whens is left recursive. At code-generation time, the
diff --git a/src/nodes.coffee b/src/nodes.coffee
index 5d73d81..995af42 100644
--- a/src/nodes.coffee
+++ b/src/nodes.coffee
@@ -1152,63 +1152,74 @@ exports.IfNode: class IfNode extends BaseNode
constructor: (condition, body, else_body, tags) ->
@condition: condition
- @body: body and body.unwrap()
- @else_body: else_body and else_body.unwrap()
- @children: compact flatten [@condition, @body, @else_body]
+ @body: body
+ @else_body: else_body
+ @populate_children()
@tags: tags or {}
@multiple: true if @condition instanceof Array
@condition: new OpNode('!', new ParentheticalNode(@condition)) if @tags.invert
+ populate_children: ->
+ @children: compact flatten [@condition, @body, @else_body]
+
# Add a new *else* clause to this **IfNode**, or push it down to the bottom
# of the chain recursively.
push: (else_body) ->
- eb: else_body.unwrap()
- if @else_body then @else_body.push(eb) else @else_body: eb
+ else_expressions: else_body
+ if not (else_expressions instanceof Expressions)
+ else_expressions: new Expressions([else_expressions])
+ if @else_body then @else_body_node().push(else_body) else @else_body: else_expressions
this
+ body_node: -> @body?.unwrap()
+ else_body_node: -> @else_body?.unwrap()
+
force_statement: ->
@tags.statement: true
this
# Tag a chain of **IfNodes** with their object(s) to switch on for equality
# tests. `rewrite_switch` will perform the actual change at compile time.
- rewrite_condition: (expression) ->
- @switcher: expression
+ switches_over: (expression) ->
+ @switch_subject: expression
this
# Rewrite a chain of **IfNodes** with their switch condition for equality.
# Ensure that the switch expression isn't evaluated more than once.
rewrite_switch: (o) ->
- assigner: @switcher
- if not (@switcher.unwrap() instanceof LiteralNode)
+ assigner: @switch_subject
+ if not (@switch_subject.unwrap() instanceof LiteralNode)
variable: literal(o.scope.free_variable())
- assigner: new AssignNode(variable, @switcher)
- @switcher: variable
+ assigner: new AssignNode(variable, @switch_subject)
+ @children.push(assigner)
+ @switch_subject: variable
@condition: if @multiple
for cond, i in @condition
- new OpNode('==', (if i is 0 then assigner else @switcher), cond)
+ new OpNode('==', (if i is 0 then assigner else @switch_subject), cond)
else
new OpNode('==', assigner, @condition)
- @else_body.rewrite_condition(@switcher) if @is_chain()
+ @else_body_node().switches_over(@switch_subject) if @is_chain()
+ # prevent this rewrite from happening again
+ @switch_subject: undefined
this
# Rewrite a chain of **IfNodes** to add a default case as the final *else*.
add_else: (exprs, statement) ->
if @is_chain()
- @else_body.add_else exprs, statement
+ @else_body_node().add_else exprs, statement
else
- exprs: exprs.unwrap() unless statement
- @children.push @else_body: exprs
+ @else_body: exprs
+ @populate_children()
this
# If the `else_body` is an **IfNode** itself, then we've got an *if-else* chain.
is_chain: ->
- @chain: or @else_body and @else_body instanceof IfNode
+ @chain: or @else_body and @else_body_node() instanceof IfNode
# The **IfNode** only compiles into a statement if either of its bodies needs
# to be a statement. Otherwise a ternary is safe.
is_statement: ->
- @statement: or !!(@comment or @tags.statement or @body.is_statement() or (@else_body and @else_body.is_statement()))
+ @statement: or !!(@comment or @tags.statement or @body_node().is_statement() or (@else_body and @else_body_node().is_statement()))
compile_condition: (o) ->
(cond.compile(o) for cond in flatten([@condition])).join(' || ')
@@ -1217,14 +1228,14 @@ exports.IfNode: class IfNode extends BaseNode
if @is_statement() then @compile_statement(o) else @compile_ternary(o)
make_return: ->
- @body: and @body.make_return()
- @else_body: and @else_body.make_return()
+ @body: and @body_node().make_return()
+ @else_body: and @else_body_node().make_return()
this
# Compile the **IfNode** as a regular *if-else* statement. Flattened chains
# force inner *else* bodies into statement form.
compile_statement: (o) ->
- @rewrite_switch(o) if @switcher
+ @rewrite_switch(o) if @switch_subject
child: del o, 'chain_child'
cond_o: merge o
o.indent: @idt 1
@@ -1232,19 +1243,19 @@ exports.IfNode: class IfNode extends BaseNode
if_dent: if child then '' else @idt()
com_dent: if child then @idt() else ''
prefix: if @comment then "${ @comment.compile(cond_o) }\n$com_dent" else ''
- body: Expressions.wrap([@body]).compile(o)
+ body: @body.compile(o)
if_part: "$prefix${if_dent}if (${ @compile_condition(cond_o) }) {\n$body\n$@tab}"
return if_part unless @else_body
else_part: if @is_chain()
- ' else ' + @else_body.compile(merge(o, {indent: @idt(), chain_child: true}))
+ ' else ' + @else_body_node().compile(merge(o, {indent: @idt(), chain_child: true}))
else
- " else {\n${ Expressions.wrap([@else_body]).compile(o) }\n$@tab}"
+ " else {\n${ @else_body.compile(o) }\n$@tab}"
"$if_part$else_part"
# Compile the IfNode as a ternary operator.
compile_ternary: (o) ->
- if_part: @condition.compile(o) + ' ? ' + @body.compile(o)
- else_part: if @else_body then @else_body.compile(o) else 'null'
+ if_part: @condition.compile(o) + ' ? ' + @body_node().compile(o)
+ else_part: if @else_body then @else_body_node().compile(o) else 'null'
"$if_part : $else_part"
# Faux-Nodes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment