mirror of
https://github.com/jashkenas/coffeescript.git
synced 2026-04-11 03:00:13 -04:00
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:
@@ -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
|
||||||
});
|
});
|
||||||
|
|||||||
93
lib/nodes.js
93
lib/nodes.js
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user