First draft of real switch statements for CoffeeScript switch statements.

This commit is contained in:
Jeremy Ashkenas 2010-09-15 23:46:01 -04:00
parent 60f80e2698
commit d8465ce767
6 changed files with 128 additions and 38 deletions

View File

@ -497,29 +497,25 @@
],
Switch: [
o("SWITCH Expression INDENT Whens OUTDENT", function() {
return $4.switchesOver($2);
return new SwitchNode($2, $4);
}), o("SWITCH Expression INDENT Whens ELSE Block OUTDENT", function() {
return $4.switchesOver($2).addElse($6, true);
return new SwitchNode($2, $4, $6);
}), o("SWITCH INDENT Whens OUTDENT", function() {
return $3;
return new SwitchNode(null, $3);
}), o("SWITCH INDENT Whens ELSE Block OUTDENT", function() {
return $3.addElse($5, true);
return new SwitchNode(null, $3, $5);
})
],
Whens: [
o("When"), o("Whens When", function() {
return $1.addElse($2);
return $1.concat($2);
})
],
When: [
o("LEADING_WHEN SimpleArgs Block", function() {
return new IfNode($2, $3, {
statement: true
});
return [[$2, $3]];
}), o("LEADING_WHEN SimpleArgs Block TERMINATOR", function() {
return new IfNode($2, $3, {
statement: true
});
return [[$2, $3]];
})
],
IfBlock: [

View File

@ -416,7 +416,7 @@
return doc.replace(MULTILINER, "\\n").replace(new RegExp(options.quote, 'g'), "\\" + (options.quote));
};
Lexer.prototype.tagParameters = function() {
var _d, i, tok;
var i, tok;
if (this.tag() !== ')') {
return null;
}
@ -427,11 +427,15 @@
if (!tok) {
return null;
}
if ((_d = tok[0]) === 'IDENTIFIER') {
switch (tok[0]) {
case 'IDENTIFIER':
tok[0] = 'PARAM';
} else if (_d === ')') {
break;
case ')':
tok[0] = 'PARAM_END';
} else if (_d === '(' || _d === 'CALL_START') {
break;
case '(':
case 'CALL_START':
return (tok[0] = 'PARAM_START');
}
}

View File

@ -1,5 +1,5 @@
(function() {
var AccessorNode, ArrayNode, AssignNode, BaseNode, CallNode, ClassNode, ClosureNode, CodeNode, CommentNode, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IS_STRING, IfNode, InNode, IndexNode, LiteralNode, NUMBER, ObjectNode, OpNode, ParamNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, SIMPLENUM, Scope, SliceNode, SplatNode, TAB, TRAILING_WHITESPACE, ThrowNode, TryNode, UTILITIES, ValueNode, WhileNode, _a, compact, del, ends, flatten, helpers, include, indexOf, literal, merge, starts, utility;
var AccessorNode, ArrayNode, AssignNode, BaseNode, CallNode, ClassNode, ClosureNode, CodeNode, CommentNode, ExistenceNode, Expressions, ExtendsNode, ForNode, IDENTIFIER, IS_STRING, IfNode, InNode, IndexNode, LiteralNode, NUMBER, ObjectNode, OpNode, ParamNode, ParentheticalNode, PushNode, RangeNode, ReturnNode, SIMPLENUM, Scope, SliceNode, SplatNode, SwitchNode, TAB, TRAILING_WHITESPACE, ThrowNode, TryNode, UTILITIES, ValueNode, WhileNode, _a, compact, del, ends, flatten, helpers, include, indexOf, literal, merge, starts, utility;
var __extends = function(child, parent) {
var ctor = function(){};
ctor.prototype = parent.prototype;
@ -1728,6 +1728,67 @@
};
return ForNode;
})();
exports.SwitchNode = (function() {
SwitchNode = function(_b, _c, _d) {
this.otherwise = _d;
this.cases = _c;
this.subject = _b;
SwitchNode.__super__.constructor.call(this);
this.tags.subjectless = !this.subject;
this.subject || (this.subject = literal('true'));
return this;
};
__extends(SwitchNode, BaseNode);
SwitchNode.prototype["class"] = 'SwitchNode';
SwitchNode.prototype.children = ['subject', 'cases', 'otherwise'];
SwitchNode.prototype.isStatement = function() {
return true;
};
SwitchNode.prototype.makeReturn = function() {
var _b, _c, _d, pair;
_c = this.cases;
for (_b = 0, _d = _c.length; _b < _d; _b++) {
pair = _c[_b];
pair[1].makeReturn();
}
if (this.otherwise) {
this.otherwise.makeReturn();
}
return this;
};
SwitchNode.prototype.compileNode = function(o) {
var _b, _c, _d, _e, _f, _g, _h, block, code, condition, conditions, exprs, idt, pair;
idt = (o.indent = this.idt(1));
o.top = true;
code = ("" + (this.tab) + "switch (" + (this.subject.compile(o)) + ") {");
_c = this.cases;
for (_b = 0, _d = _c.length; _b < _d; _b++) {
pair = _c[_b];
_e = pair;
conditions = _e[0];
block = _e[1];
exprs = block.expressions;
_g = flatten([conditions]);
for (_f = 0, _h = _g.length; _f < _h; _f++) {
condition = _g[_f];
if (this.tags.subjectless) {
condition = new OpNode('!!', new ParentheticalNode(condition));
}
code += ("\n" + (this.tab) + "case " + (condition.compile(o)) + ":");
}
code += ("\n" + (block.compile(o)));
if (!(exprs[exprs.length - 1] instanceof ReturnNode)) {
code += ("\n" + (idt) + "break;");
}
}
if (this.otherwise) {
code += ("\n" + (this.tab) + "default:\n" + (this.otherwise.compile(o)));
}
code += ("\n" + (this.tab) + "}");
return code;
};
return SwitchNode;
})();
exports.IfNode = (function() {
IfNode = function(_b, _c, _d) {
this.tags = _d;

View File

@ -409,25 +409,21 @@ case 177:this.$ = {
guard: $$[$0-6+6-1]
};
break;
case 178:this.$ = $$[$0-5+4-1].switchesOver($$[$0-5+2-1]);
case 178:this.$ = new SwitchNode($$[$0-5+2-1], $$[$0-5+4-1]);
break;
case 179:this.$ = $$[$0-7+4-1].switchesOver($$[$0-7+2-1]).addElse($$[$0-7+6-1], true);
case 179:this.$ = new SwitchNode($$[$0-7+2-1], $$[$0-7+4-1], $$[$0-7+6-1]);
break;
case 180:this.$ = $$[$0-4+3-1];
case 180:this.$ = new SwitchNode(null, $$[$0-4+3-1]);
break;
case 181:this.$ = $$[$0-6+3-1].addElse($$[$0-6+5-1], true);
case 181:this.$ = new SwitchNode(null, $$[$0-6+3-1], $$[$0-6+5-1]);
break;
case 182:this.$ = $$[$0-1+1-1];
break;
case 183:this.$ = $$[$0-2+1-1].addElse($$[$0-2+2-1]);
case 183:this.$ = $$[$0-2+1-1].concat($$[$0-2+2-1]);
break;
case 184:this.$ = new IfNode($$[$0-3+2-1], $$[$0-3+3-1], {
statement: true
});
case 184:this.$ = [[$$[$0-3+2-1], $$[$0-3+3-1]]];
break;
case 185:this.$ = new IfNode($$[$0-4+2-1], $$[$0-4+3-1], {
statement: true
});
case 185:this.$ = [[$$[$0-4+2-1], $$[$0-4+3-1]]];
break;
case 186:this.$ = new IfNode($$[$0-3+2-1], $$[$0-3+3-1]);
break;

View File

@ -485,26 +485,22 @@ grammar =
o "IN Expression BY Expression WHEN Expression", -> source: $2, step: $4, guard: $6
]
# 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.switchesOver $2
o "SWITCH Expression INDENT Whens ELSE Block OUTDENT", -> $4.switchesOver($2).addElse $6, true
o "SWITCH INDENT Whens OUTDENT", -> $3
o "SWITCH INDENT Whens ELSE Block OUTDENT", -> $3.addElse $5, true
o "SWITCH Expression INDENT Whens OUTDENT", -> new SwitchNode $2, $4
o "SWITCH Expression INDENT Whens ELSE Block OUTDENT", -> new SwitchNode $2, $4, $6
o "SWITCH INDENT Whens OUTDENT", -> new SwitchNode null, $3
o "SWITCH INDENT Whens ELSE Block OUTDENT", -> new SwitchNode null, $3, $5
]
# The inner list of whens is left recursive. At code-generation time, the
# IfNode will rewrite them into a proper chain.
Whens: [
o "When"
o "Whens When", -> $1.addElse $2
o "Whens When", -> $1.concat $2
]
# An individual **When** clause, with action.
When: [
o "LEADING_WHEN SimpleArgs Block", -> new IfNode $2, $3, statement: true
o "LEADING_WHEN SimpleArgs Block TERMINATOR", -> new IfNode $2, $3, statement: true
o "LEADING_WHEN SimpleArgs Block", -> [[$2, $3]]
o "LEADING_WHEN SimpleArgs Block TERMINATOR", -> [[$2, $3]]
]
# The most basic form of *if* is a condition and an action. The following

View File

@ -1422,6 +1422,43 @@ exports.ForNode = class ForNode extends BaseNode
vars = if range then name else "#{name}, #{ivar}"
"#{sourcePart}for (#{forPart}) {#{guardPart}\n#{varPart}#{body}\n#{@tab}}#{returnResult}"
#### SwitchNode
# A JavaScript *switch* statement. Converts into a returnable expression on-demand.
exports.SwitchNode = class SwitchNode extends BaseNode
class: 'SwitchNode'
children: ['subject', 'cases', 'otherwise']
isStatement: -> yes
constructor: (@subject, @cases, @otherwise) ->
super()
@tags.subjectless = !@subject
@subject or= literal 'true'
makeReturn: ->
pair[1].makeReturn() for pair in @cases
@otherwise.makeReturn() if @otherwise
this
compileNode: (o) ->
idt = o.indent = @idt 1
o.top = yes
code = "#{ @tab }switch (#{ @subject.compile o }) {"
for pair in @cases
[conditions, block] = pair
exprs = block.expressions
for condition in flatten [conditions]
condition = new OpNode '!!', new ParentheticalNode condition if @tags.subjectless
code += "\n#{ @tab }case #{ condition.compile o }:"
code += "\n#{ block.compile o }"
code += "\n#{ idt }break;" unless exprs[exprs.length - 1] instanceof ReturnNode
if @otherwise
code += "\n#{ @tab }default:\n#{ @otherwise.compile o }"
code += "\n#{ @tab }}"
code
#### IfNode
# *If/else* statements. Our *switch/when* will be compiled into this. Acts as an