diff --git a/lib/lexer.js b/lib/lexer.js index 46f16b18..2262216f 100644 --- a/lib/lexer.js +++ b/lib/lexer.js @@ -355,7 +355,7 @@ // parameter identifiers in order to avoid this. Also, parameter lists can // make use of splats. lex.prototype.tag_parameters = function tag_parameters() { - var i, tok; + var _a, i, tok; if (this.tag() !== ')') { return null; } @@ -366,11 +366,11 @@ if (!tok) { return null; } - if (tok[0] === 'IDENTIFIER') { + if ((_a = tok[0]) === 'IDENTIFIER') { tok[0] = 'PARAM'; - } else if (tok[0] === ')') { + } else if (_a === ')') { tok[0] = 'PARAM_END'; - } else if (tok[0] === '(') { + } else if (_a === '(') { return (tok[0] = 'PARAM_START'); } } diff --git a/lib/nodes.js b/lib/nodes.js index 43737ff6..623d6fb2 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -1216,23 +1216,34 @@ this.tags.statement = true; return this; }, - // Rewrite a chain of IfNodes with their switch condition for equality. + // Tag a chain of IfNodes with their switch condition for equality. rewrite_condition: function rewrite_condition(expression) { - var _a, _b, _c, cond; + this.switcher = expression; + return this; + }, + // Rewrite a chain of IfNodes with their switch condition for equality. + rewrite_switch: function rewrite_switch(o) { + var _a, _b, assigner, cond, i, variable; + assigner = this.switcher; + if (!(this.switcher.unwrap() instanceof LiteralNode)) { + variable = new LiteralNode(o.scope.free_variable()); + assigner = new AssignNode(variable, this.switcher); + this.switcher = variable; + } this.condition = (function() { if (this.multiple) { _a = []; _b = this.condition; - for (_c = 0; _c < _b.length; _c++) { - cond = _b[_c]; - _a.push(new OpNode('is', expression, cond)); + for (i = 0; i < _b.length; i++) { + cond = _b[i]; + _a.push(new OpNode('is', (i === 0 ? assigner : this.switcher), cond)); } return _a; } else { - return new OpNode('is', expression, this.condition); + return new OpNode('is', assigner, this.condition); } }).call(this); if (this.is_chain()) { - this.else_body.rewrite_condition(expression); + this.else_body.rewrite_condition(this.switcher); } return this; }, @@ -1269,6 +1280,9 @@ // force sub-else bodies into statement form. compile_statement: function compile_statement(o) { var body, child, com_dent, cond_o, else_part, if_dent, if_part, prefix; + if (this.switcher) { + this.rewrite_switch(o); + } child = del(o, 'chain_child'); cond_o = merge(o); del(cond_o, 'returns'); diff --git a/lib/rewriter.js b/lib/rewriter.js index 0971822f..63414570 100644 --- a/lib/rewriter.js +++ b/lib/rewriter.js @@ -156,22 +156,23 @@ brackets = [0]; return this.scan_tokens((function(__this) { var __func = function(prev, token, post, i) { - if (token[0] === 'CALL_START') { + var _i; + if ((_i = token[0]) === 'CALL_START') { parens.push(0); - } else if (token[0] === 'INDEX_START') { + } else if (_i === 'INDEX_START') { brackets.push(0); - } else if (token[0] === '(') { + } else if (_i === '(') { parens[parens.length - 1] += 1; - } else if (token[0] === '[') { + } else if (_i === '[') { brackets[brackets.length - 1] += 1; - } else if (token[0] === ')') { + } else if (_i === ')') { if (parens[parens.length - 1] === 0) { parens.pop(); token[0] = 'CALL_END'; } else { parens[parens.length - 1] -= 1; } - } else if (token[0] === ']') { + } else if (_i === ']') { if (brackets[brackets.length - 1] === 0) { brackets.pop(); token[0] = 'INDEX_END'; diff --git a/src/nodes.coffee b/src/nodes.coffee index e5d2dd1f..9f5b1412 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -970,13 +970,24 @@ IfNode: exports.IfNode: inherit Node, { @tags.statement: true this - # Rewrite a chain of IfNodes with their switch condition for equality. + # Tag a chain of IfNodes with their switch condition for equality. rewrite_condition: (expression) -> + @switcher: expression + this + + # Rewrite a chain of IfNodes with their switch condition for equality. + rewrite_switch: (o) -> + assigner: @switcher + if not (@switcher.unwrap() instanceof LiteralNode) + variable: new LiteralNode(o.scope.free_variable()) + assigner: new AssignNode(variable, @switcher) + @switcher: variable @condition: if @multiple - new OpNode('is', expression, cond) for cond in @condition + for cond, i in @condition + new OpNode('is', (if i is 0 then assigner else @switcher), cond) else - new OpNode('is', expression, @condition) - @else_body.rewrite_condition(expression) if @is_chain() + new OpNode('is', assigner, @condition) + @else_body.rewrite_condition(@switcher) if @is_chain() this # Rewrite a chain of IfNodes to add a default case as the final else. @@ -1003,6 +1014,7 @@ IfNode: exports.IfNode: inherit Node, { # Compile the IfNode as a regular if-else statement. Flattened chains # force sub-else bodies into statement form. compile_statement: (o) -> + @rewrite_switch(o) if @switcher child: del o, 'chain_child' cond_o: merge o del cond_o, 'returns' diff --git a/test/test_switch.coffee b/test/test_switch.coffee index 705a0cc4..7f2fc000 100644 --- a/test/test_switch.coffee +++ b/test/test_switch.coffee @@ -29,3 +29,15 @@ ok func(2) ok func(6) ok !func(3) ok !func(8) + + +# Should cache the switch value, if anything fancier than a literal. +num: 5 +result: switch num += 5 + when 5 then false + when 15 then false + when 10 then true + else false + +ok result +