Cleaned up IfNodes

- renamed rewrite_condition() to switches_over(),
   and @switcher to @switch_subject
 - removed unused else_body constructor parameter, as well
   as unnecessary push() method
 - ensure both @body and @else_body are always Expressions
   (previously they could be either Expressions or IfNode)
This commit is contained in:
gfxmonk
2010-04-27 23:32:45 +10:00
parent b47188763c
commit c8e0f8b149
5 changed files with 122 additions and 109 deletions

View File

@@ -570,26 +570,26 @@
// switch/case/default by compiling into an if-else chain. // switch/case/default by compiling into an if-else chain.
Switch: [ Switch: [
o("SWITCH Expression INDENT Whens OUTDENT", function() { o("SWITCH Expression INDENT Whens OUTDENT", function() {
return $4.rewrite_condition($2); return $4.switches_over($2);
}), o("SWITCH Expression INDENT Whens ELSE Block OUTDENT", function() { }), o("SWITCH Expression INDENT Whens ELSE Block OUTDENT", function() {
return $4.rewrite_condition($2).add_else($6, true); return $4.switches_over($2).add_else($6, true);
}) })
], ],
// The inner list of whens is left recursive. At code-generation time, the // The inner list of whens is left recursive. At code-generation time, the
// IfNode will rewrite them into a proper chain. // IfNode will rewrite them into a proper chain.
Whens: [ Whens: [
o("When"), o("Whens When", function() { o("When"), o("Whens When", function() {
return $1.push($2); return $1.add_else($2);
}) })
], ],
// An individual **When** clause, with action. // An individual **When** clause, with action.
When: [ When: [
o("LEADING_WHEN SimpleArgs Block", function() { o("LEADING_WHEN SimpleArgs Block", function() {
return new IfNode($2, $3, null, { return new IfNode($2, $3, {
statement: true statement: true
}); });
}), o("LEADING_WHEN SimpleArgs Block TERMINATOR", function() { }), o("LEADING_WHEN SimpleArgs Block TERMINATOR", function() {
return new IfNode($2, $3, null, { return new IfNode($2, $3, {
statement: true statement: true
}); });
}), o("Comment TERMINATOR When", function() { }), o("Comment TERMINATOR When", function() {
@@ -604,7 +604,7 @@
o("IF Expression Block", function() { o("IF Expression Block", function() {
return new IfNode($2, $3); return new IfNode($2, $3);
}), o("UNLESS Expression Block", function() { }), o("UNLESS Expression Block", function() {
return new IfNode($2, $3, null, { return new IfNode($2, $3, {
invert: true invert: true
}); });
}), o("IfStart ElsIf", function() { }), o("IfStart ElsIf", function() {
@@ -627,20 +627,20 @@
// *if* and *unless*. // *if* and *unless*.
If: [ If: [
o("IfBlock"), o("Statement IF Expression", function() { o("IfBlock"), o("Statement IF Expression", function() {
return new IfNode($3, Expressions.wrap([$1]), null, { return new IfNode($3, Expressions.wrap([$1]), {
statement: true statement: true
}); });
}), o("Expression IF Expression", function() { }), o("Expression IF Expression", function() {
return new IfNode($3, Expressions.wrap([$1]), null, { return new IfNode($3, Expressions.wrap([$1]), {
statement: true statement: true
}); });
}), o("Statement UNLESS Expression", function() { }), o("Statement UNLESS Expression", function() {
return new IfNode($3, Expressions.wrap([$1]), null, { return new IfNode($3, Expressions.wrap([$1]), {
statement: true, statement: true,
invert: true invert: true
}); });
}), o("Expression UNLESS Expression", function() { }), o("Expression UNLESS Expression", function() {
return new IfNode($3, Expressions.wrap([$1]), null, { return new IfNode($3, Expressions.wrap([$1]), {
statement: true, statement: true,
invert: true invert: true
}); });

View File

@@ -1532,8 +1532,10 @@
indent: this.idt(1), indent: this.idt(1),
top: true top: true
})) + "\n"; })) + "\n";
} else if (name) { } else {
var_part = ("" + body_dent + name + " = " + svar + "[" + ivar + "];\n"); if (name) {
var_part = ("" + body_dent + name + " = " + svar + "[" + ivar + "];\n");
}
} }
if (!(this.object)) { if (!(this.object)) {
lvar = scope.free_variable(); lvar = scope.free_variable();
@@ -1570,11 +1572,11 @@
// Single-expression **IfNodes** are compiled into ternary operators if possible, // Single-expression **IfNodes** are compiled into ternary operators if possible,
// because ternaries are already proper expressions, and don't need conversion. // because ternaries are already proper expressions, and don't need conversion.
exports.IfNode = (function() { exports.IfNode = (function() {
IfNode = function IfNode(condition, body, else_body, tags) { IfNode = function IfNode(condition, body, tags) {
this.condition = condition; this.condition = condition;
this.body = body && body.unwrap(); this.body = body;
this.else_body = else_body && else_body.unwrap(); this.else_body = null;
this.children = compact(flatten([this.condition, this.body, this.else_body])); this.populate_children();
this.tags = tags || {}; this.tags = tags || {};
if (this.condition instanceof Array) { if (this.condition instanceof Array) {
this.multiple = true; this.multiple = true;
@@ -1582,16 +1584,19 @@
if (this.tags.invert) { if (this.tags.invert) {
this.condition = new OpNode('!', new ParentheticalNode(this.condition)); this.condition = new OpNode('!', new ParentheticalNode(this.condition));
} }
this.is_chain = false;
return this; return this;
}; };
__extends(IfNode, BaseNode); __extends(IfNode, BaseNode);
// Add a new *else* clause to this **IfNode**, or push it down to the bottom IfNode.prototype.populate_children = function populate_children() {
// of the chain recursively. this.children = compact(flatten([this.condition, this.body, this.else_body]));
IfNode.prototype.push = function push(else_body) { return this.children;
var eb; };
eb = else_body.unwrap(); IfNode.prototype.body_node = function body_node() {
this.else_body ? this.else_body.push(eb) : (this.else_body = eb); return this.body == undefined ? undefined : this.body.unwrap();
return this; };
IfNode.prototype.else_body_node = function else_body_node() {
return this.else_body == undefined ? undefined : this.else_body.unwrap();
}; };
IfNode.prototype.force_statement = function force_statement() { IfNode.prototype.force_statement = function force_statement() {
this.tags.statement = true; this.tags.statement = true;
@@ -1599,57 +1604,55 @@
}; };
// Tag a chain of **IfNodes** with their object(s) to switch on for equality // 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. // tests. `rewrite_switch` will perform the actual change at compile time.
IfNode.prototype.rewrite_condition = function rewrite_condition(expression) { IfNode.prototype.switches_over = function switches_over(expression) {
this.switcher = expression; this.switch_subject = expression;
return this; return this;
}; };
// Rewrite a chain of **IfNodes** with their switch condition for equality. // Rewrite a chain of **IfNodes** with their switch condition for equality.
// Ensure that the switch expression isn't evaluated more than once. // Ensure that the switch expression isn't evaluated more than once.
IfNode.prototype.rewrite_switch = function rewrite_switch(o) { IfNode.prototype.rewrite_switch = function rewrite_switch(o) {
var _b, _c, _d, assigner, cond, i, variable; var _b, _c, _d, assigner, cond, i, variable;
assigner = this.switcher; assigner = this.switch_subject;
if (!(this.switcher.unwrap() instanceof LiteralNode)) { if (!((this.switch_subject.unwrap() instanceof LiteralNode))) {
variable = literal(o.scope.free_variable()); variable = literal(o.scope.free_variable());
assigner = new AssignNode(variable, this.switcher); assigner = new AssignNode(variable, this.switch_subject);
this.switcher = variable; this.children.push(assigner);
this.switch_subject = variable;
} }
this.condition = (function() { this.condition = (function() {
if (this.multiple) { if (this.multiple) {
_b = []; _c = this.condition; _b = []; _c = this.condition;
for (i = 0, _d = _c.length; i < _d; i++) { for (i = 0, _d = _c.length; i < _d; i++) {
cond = _c[i]; cond = _c[i];
_b.push(new OpNode('==', (i === 0 ? assigner : this.switcher), cond)); _b.push(new OpNode('==', (i === 0 ? assigner : this.switch_subject), cond));
} }
return _b; return _b;
} else { } else {
return new OpNode('==', assigner, this.condition); return new OpNode('==', assigner, this.condition);
} }
}).call(this); }).call(this);
if (this.is_chain()) { if (this.is_chain) {
this.else_body.rewrite_condition(this.switcher); this.else_body_node().switches_over(this.switch_subject);
} }
// prevent this rewrite from happening again
this.switch_subject = undefined;
return this; return this;
}; };
// Rewrite a chain of **IfNodes** to add a default case as the final *else*. // Rewrite a chain of **IfNodes** to add a default case as the final *else*.
IfNode.prototype.add_else = function add_else(exprs, statement) { IfNode.prototype.add_else = function add_else(else_body, statement) {
if (this.is_chain()) { if (this.is_chain) {
this.else_body.add_else(exprs, statement); this.else_body_node().add_else(else_body, statement);
} else { } else {
if (!(statement)) { this.is_chain = else_body instanceof IfNode;
exprs = exprs.unwrap(); this.else_body = this.ensure_expressions(else_body);
} this.populate_children();
this.children.push((this.else_body = exprs));
} }
return this; return this;
}; };
// If the `else_body` is an **IfNode** itself, then we've got an *if-else* chain.
IfNode.prototype.is_chain = function is_chain() {
return this.chain = this.chain || this.else_body && this.else_body instanceof IfNode;
};
// The **IfNode** only compiles into a statement if either of its bodies needs // The **IfNode** only compiles into a statement if either of its bodies needs
// to be a statement. Otherwise a ternary is safe. // to be a statement. Otherwise a ternary is safe.
IfNode.prototype.is_statement = function is_statement() { IfNode.prototype.is_statement = function is_statement() {
return this.statement = this.statement || !!(this.comment || this.tags.statement || this.body.is_statement() || (this.else_body && this.else_body.is_statement())); return this.statement = this.statement || !!(this.comment || this.tags.statement || this.body_node().is_statement() || (this.else_body && this.else_body_node().is_statement()));
}; };
IfNode.prototype.compile_condition = function compile_condition(o) { IfNode.prototype.compile_condition = function compile_condition(o) {
var _b, _c, _d, _e, cond; var _b, _c, _d, _e, cond;
@@ -1670,15 +1673,21 @@
} }
}; };
IfNode.prototype.make_return = function make_return() { IfNode.prototype.make_return = function make_return() {
this.body = this.body && this.body.make_return(); this.body = this.body && this.ensure_expressions(this.body.make_return());
this.else_body = this.else_body && this.else_body.make_return(); this.else_body = this.else_body && this.ensure_expressions(this.else_body.make_return());
return this; return this;
}; };
IfNode.prototype.ensure_expressions = function ensure_expressions(node) {
if (!(node instanceof Expressions)) {
node = new Expressions([node]);
}
return node;
};
// Compile the **IfNode** as a regular *if-else* statement. Flattened chains // Compile the **IfNode** as a regular *if-else* statement. Flattened chains
// force inner *else* bodies into statement form. // force inner *else* bodies into statement form.
IfNode.prototype.compile_statement = function compile_statement(o) { IfNode.prototype.compile_statement = function compile_statement(o) {
var body, child, com_dent, cond_o, else_part, if_dent, if_part, prefix; var body, child, com_dent, cond_o, else_part, if_dent, if_part, prefix;
if (this.switcher) { if (this.switch_subject) {
this.rewrite_switch(o); this.rewrite_switch(o);
} }
child = del(o, 'chain_child'); child = del(o, 'chain_child');
@@ -1688,22 +1697,22 @@
if_dent = child ? '' : this.idt(); if_dent = child ? '' : this.idt();
com_dent = child ? this.idt() : ''; com_dent = child ? this.idt() : '';
prefix = this.comment ? ("" + (this.comment.compile(cond_o)) + "\n" + com_dent) : ''; prefix = this.comment ? ("" + (this.comment.compile(cond_o)) + "\n" + com_dent) : '';
body = Expressions.wrap([this.body]).compile(o); body = this.body.compile(o);
if_part = ("" + prefix + (if_dent) + "if (" + (this.compile_condition(cond_o)) + ") {\n" + body + "\n" + this.tab + "}"); if_part = ("" + prefix + (if_dent) + "if (" + (this.compile_condition(cond_o)) + ") {\n" + body + "\n" + this.tab + "}");
if (!(this.else_body)) { if (!(this.else_body)) {
return if_part; return if_part;
} }
else_part = this.is_chain() ? ' else ' + this.else_body.compile(merge(o, { else_part = this.is_chain ? ' else ' + this.else_body_node().compile(merge(o, {
indent: this.idt(), indent: this.idt(),
chain_child: true chain_child: true
})) : (" else {\n" + (Expressions.wrap([this.else_body]).compile(o)) + "\n" + this.tab + "}"); })) : (" else {\n" + (this.else_body.compile(o)) + "\n" + this.tab + "}");
return "" + if_part + else_part; return "" + if_part + else_part;
}; };
// Compile the IfNode as a ternary operator. // Compile the IfNode as a ternary operator.
IfNode.prototype.compile_ternary = function compile_ternary(o) { IfNode.prototype.compile_ternary = function compile_ternary(o) {
var else_part, if_part; var else_part, if_part;
if_part = this.condition.compile(o) + ' ? ' + this.body.compile(o); if_part = this.condition.compile(o) + ' ? ' + this.body_node().compile(o);
else_part = this.else_body ? this.else_body.compile(o) : 'null'; else_part = this.else_body ? this.else_body_node().compile(o) : 'null';
return "" + if_part + " : " + else_part; return "" + if_part + " : " + else_part;
}; };
return IfNode; return IfNode;

View File

@@ -365,19 +365,19 @@ case 160:this.$ = {
guard: $$[$0-6+6-1] guard: $$[$0-6+6-1]
}; };
break; break;
case 161:this.$ = $$[$0-5+4-1].rewrite_condition($$[$0-5+2-1]); case 161:this.$ = $$[$0-5+4-1].switches_over($$[$0-5+2-1]);
break; break;
case 162:this.$ = $$[$0-7+4-1].rewrite_condition($$[$0-7+2-1]).add_else($$[$0-7+6-1], true); case 162:this.$ = $$[$0-7+4-1].switches_over($$[$0-7+2-1]).add_else($$[$0-7+6-1], true);
break; break;
case 163:this.$ = $$[$0-1+1-1]; case 163:this.$ = $$[$0-1+1-1];
break; break;
case 164:this.$ = $$[$0-2+1-1].push($$[$0-2+2-1]); case 164:this.$ = $$[$0-2+1-1].add_else($$[$0-2+2-1]);
break; break;
case 165:this.$ = new IfNode($$[$0-3+2-1], $$[$0-3+3-1], null, { case 165:this.$ = new IfNode($$[$0-3+2-1], $$[$0-3+3-1], {
statement: true statement: true
}); });
break; break;
case 166:this.$ = new IfNode($$[$0-4+2-1], $$[$0-4+3-1], null, { case 166:this.$ = new IfNode($$[$0-4+2-1], $$[$0-4+3-1], {
statement: true statement: true
}); });
break; break;
@@ -388,7 +388,7 @@ case 167:this.$ = (function () {
break; break;
case 168:this.$ = new IfNode($$[$0-3+2-1], $$[$0-3+3-1]); case 168:this.$ = new IfNode($$[$0-3+2-1], $$[$0-3+3-1]);
break; break;
case 169:this.$ = new IfNode($$[$0-3+2-1], $$[$0-3+3-1], null, { case 169:this.$ = new IfNode($$[$0-3+2-1], $$[$0-3+3-1], {
invert: true invert: true
}); });
break; break;
@@ -402,20 +402,20 @@ case 173:this.$ = (new IfNode($$[$0-4+3-1], $$[$0-4+4-1])).force_statement();
break; break;
case 174:this.$ = $$[$0-1+1-1]; case 174:this.$ = $$[$0-1+1-1];
break; break;
case 175:this.$ = new IfNode($$[$0-3+3-1], Expressions.wrap([$$[$0-3+1-1]]), null, { case 175:this.$ = new IfNode($$[$0-3+3-1], Expressions.wrap([$$[$0-3+1-1]]), {
statement: true statement: true
}); });
break; break;
case 176:this.$ = new IfNode($$[$0-3+3-1], Expressions.wrap([$$[$0-3+1-1]]), null, { case 176:this.$ = new IfNode($$[$0-3+3-1], Expressions.wrap([$$[$0-3+1-1]]), {
statement: true statement: true
}); });
break; break;
case 177:this.$ = new IfNode($$[$0-3+3-1], Expressions.wrap([$$[$0-3+1-1]]), null, { case 177:this.$ = new IfNode($$[$0-3+3-1], Expressions.wrap([$$[$0-3+1-1]]), {
statement: true, statement: true,
invert: true invert: true
}); });
break; break;
case 178:this.$ = new IfNode($$[$0-3+3-1], Expressions.wrap([$$[$0-3+1-1]]), null, { case 178:this.$ = new IfNode($$[$0-3+3-1], Expressions.wrap([$$[$0-3+1-1]]), {
statement: true, statement: true,
invert: true invert: true
}); });

View File

@@ -467,21 +467,21 @@ grammar: {
# The CoffeeScript switch/when/else block replaces the JavaScript # The CoffeeScript switch/when/else block replaces the JavaScript
# switch/case/default by compiling into an if-else chain. # switch/case/default by compiling into an if-else chain.
Switch: [ Switch: [
o "SWITCH Expression INDENT Whens OUTDENT", -> $4.rewrite_condition $2 o "SWITCH Expression INDENT Whens OUTDENT", -> $4.switches_over $2
o "SWITCH Expression INDENT Whens ELSE Block OUTDENT", -> $4.rewrite_condition($2).add_else $6, true 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 # The inner list of whens is left recursive. At code-generation time, the
# IfNode will rewrite them into a proper chain. # IfNode will rewrite them into a proper chain.
Whens: [ Whens: [
o "When" o "When"
o "Whens When", -> $1.push $2 o "Whens When", -> $1.add_else $2
] ]
# An individual **When** clause, with action. # An individual **When** clause, with action.
When: [ When: [
o "LEADING_WHEN SimpleArgs Block", -> new IfNode $2, $3, null, {statement: true} o "LEADING_WHEN SimpleArgs Block", -> new IfNode $2, $3, {statement: true}
o "LEADING_WHEN SimpleArgs Block TERMINATOR", -> new IfNode $2, $3, null, {statement: true} o "LEADING_WHEN SimpleArgs Block TERMINATOR", -> new IfNode $2, $3, {statement: true}
o "Comment TERMINATOR When", -> $3.comment: $1; $3 o "Comment TERMINATOR When", -> $3.comment: $1; $3
] ]
@@ -490,7 +490,7 @@ grammar: {
# ambiguity. # ambiguity.
IfStart: [ IfStart: [
o "IF Expression Block", -> new IfNode $2, $3 o "IF Expression Block", -> new IfNode $2, $3
o "UNLESS Expression Block", -> new IfNode $2, $3, null, {invert: true} o "UNLESS Expression Block", -> new IfNode $2, $3, {invert: true}
o "IfStart ElsIf", -> $1.add_else $2 o "IfStart ElsIf", -> $1.add_else $2
] ]
@@ -509,10 +509,10 @@ grammar: {
# *if* and *unless*. # *if* and *unless*.
If: [ If: [
o "IfBlock" o "IfBlock"
o "Statement IF Expression", -> new IfNode $3, Expressions.wrap([$1]), null, {statement: true} o "Statement IF Expression", -> new IfNode $3, Expressions.wrap([$1]), {statement: true}
o "Expression IF Expression", -> new IfNode $3, Expressions.wrap([$1]), null, {statement: true} o "Expression IF Expression", -> new IfNode $3, Expressions.wrap([$1]), {statement: true}
o "Statement UNLESS Expression", -> new IfNode $3, Expressions.wrap([$1]), null, {statement: true, invert: true} o "Statement UNLESS Expression", -> new IfNode $3, Expressions.wrap([$1]), {statement: true, invert: true}
o "Expression UNLESS Expression", -> new IfNode $3, Expressions.wrap([$1]), null, {statement: true, invert: true} o "Expression UNLESS Expression", -> new IfNode $3, Expressions.wrap([$1]), {statement: true, invert: true}
] ]
# Arithmetic and logical operators, working on one or more operands. # Arithmetic and logical operators, working on one or more operands.

View File

@@ -1156,21 +1156,21 @@ statement ForNode
# because ternaries are already proper expressions, and don't need conversion. # because ternaries are already proper expressions, and don't need conversion.
exports.IfNode: class IfNode extends BaseNode exports.IfNode: class IfNode extends BaseNode
constructor: (condition, body, else_body, tags) -> constructor: (condition, body, tags) ->
@condition: condition @condition: condition
@body: body and body.unwrap() @body: body
@else_body: else_body and else_body.unwrap() @else_body: null
@children: compact flatten [@condition, @body, @else_body] @populate_children()
@tags: tags or {} @tags: tags or {}
@multiple: true if @condition instanceof Array @multiple: true if @condition instanceof Array
@condition: new OpNode('!', new ParentheticalNode(@condition)) if @tags.invert @condition: new OpNode('!', new ParentheticalNode(@condition)) if @tags.invert
@is_chain: false
# Add a new *else* clause to this **IfNode**, or push it down to the bottom populate_children: ->
# of the chain recursively. @children: compact flatten [@condition, @body, @else_body]
push: (else_body) ->
eb: else_body.unwrap() body_node: -> @body?.unwrap()
if @else_body then @else_body.push(eb) else @else_body: eb else_body_node: -> @else_body?.unwrap()
this
force_statement: -> force_statement: ->
@tags.statement: true @tags.statement: true
@@ -1178,43 +1178,43 @@ exports.IfNode: class IfNode extends BaseNode
# Tag a chain of **IfNodes** with their object(s) to switch on for equality # 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. # tests. `rewrite_switch` will perform the actual change at compile time.
rewrite_condition: (expression) -> switches_over: (expression) ->
@switcher: expression @switch_subject: expression
this this
# Rewrite a chain of **IfNodes** with their switch condition for equality. # Rewrite a chain of **IfNodes** with their switch condition for equality.
# Ensure that the switch expression isn't evaluated more than once. # Ensure that the switch expression isn't evaluated more than once.
rewrite_switch: (o) -> rewrite_switch: (o) ->
assigner: @switcher assigner: @switch_subject
unless @switcher.unwrap() instanceof LiteralNode unless (@switch_subject.unwrap() instanceof LiteralNode)
variable: literal(o.scope.free_variable()) variable: literal(o.scope.free_variable())
assigner: new AssignNode(variable, @switcher) assigner: new AssignNode(variable, @switch_subject)
@switcher: variable @children.push(assigner)
@switch_subject: variable
@condition: if @multiple @condition: if @multiple
for cond, i in @condition 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 else
new OpNode('==', assigner, @condition) 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 this
# Rewrite a chain of **IfNodes** to add a default case as the final *else*. # Rewrite a chain of **IfNodes** to add a default case as the final *else*.
add_else: (exprs, statement) -> add_else: (else_body, statement) ->
if @is_chain() if @is_chain
@else_body.add_else exprs, statement @else_body_node().add_else else_body, statement
else else
exprs: exprs.unwrap() unless statement @is_chain: else_body instanceof IfNode
@children.push @else_body: exprs @else_body: @ensure_expressions else_body
@populate_children()
this 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
# The **IfNode** only compiles into a statement if either of its bodies needs # The **IfNode** only compiles into a statement if either of its bodies needs
# to be a statement. Otherwise a ternary is safe. # to be a statement. Otherwise a ternary is safe.
is_statement: -> 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) -> compile_condition: (o) ->
(cond.compile(o) for cond in flatten([@condition])).join(' || ') (cond.compile(o) for cond in flatten([@condition])).join(' || ')
@@ -1223,14 +1223,18 @@ exports.IfNode: class IfNode extends BaseNode
if @is_statement() then @compile_statement(o) else @compile_ternary(o) if @is_statement() then @compile_statement(o) else @compile_ternary(o)
make_return: -> make_return: ->
@body: and @body.make_return() @body: and @ensure_expressions(@body.make_return())
@else_body: and @else_body.make_return() @else_body: and @ensure_expressions(@else_body.make_return())
this this
ensure_expressions: (node) ->
node: new Expressions([node]) unless node instanceof Expressions
node
# Compile the **IfNode** as a regular *if-else* statement. Flattened chains # Compile the **IfNode** as a regular *if-else* statement. Flattened chains
# force inner *else* bodies into statement form. # force inner *else* bodies into statement form.
compile_statement: (o) -> compile_statement: (o) ->
@rewrite_switch(o) if @switcher @rewrite_switch(o) if @switch_subject
child: del o, 'chain_child' child: del o, 'chain_child'
cond_o: merge o cond_o: merge o
o.indent: @idt 1 o.indent: @idt 1
@@ -1238,19 +1242,19 @@ exports.IfNode: class IfNode extends BaseNode
if_dent: if child then '' else @idt() if_dent: if child then '' else @idt()
com_dent: if child then @idt() else '' com_dent: if child then @idt() else ''
prefix: if @comment then "${ @comment.compile(cond_o) }\n$com_dent" 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}" if_part: "$prefix${if_dent}if (${ @compile_condition(cond_o) }) {\n$body\n$@tab}"
return if_part unless @else_body return if_part unless @else_body
else_part: if @is_chain() 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
" else {\n${ Expressions.wrap([@else_body]).compile(o) }\n$@tab}" " else {\n${ @else_body.compile(o) }\n$@tab}"
"$if_part$else_part" "$if_part$else_part"
# Compile the IfNode as a ternary operator. # Compile the IfNode as a ternary operator.
compile_ternary: (o) -> compile_ternary: (o) ->
if_part: @condition.compile(o) + ' ? ' + @body.compile(o) if_part: @condition.compile(o) + ' ? ' + @body_node().compile(o)
else_part: if @else_body then @else_body.compile(o) else 'null' else_part: if @else_body then @else_body_node().compile(o) else 'null'
"$if_part : $else_part" "$if_part : $else_part"
# Faux-Nodes # Faux-Nodes