mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
closes #1669; loop results are now collected in the same way as auto-return
s are generated
This commit is contained in:
parent
2ea51b02cc
commit
ad1bc1e0b8
4 changed files with 95 additions and 96 deletions
|
@ -1,5 +1,5 @@
|
|||
(function() {
|
||||
var Access, Arr, Assign, Base, Block, Call, Class, Closure, Code, Comment, Existence, Extends, For, IDENTIFIER, IDENTIFIER_STR, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, Obj, Op, Param, Parens, Push, RESERVED, Range, Return, SIMPLENUM, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, compact, del, ends, extend, flatten, last, merge, multident, starts, unfoldSoak, utility, _ref;
|
||||
var Access, Arr, Assign, Base, Block, Call, Class, Closure, Code, Comment, Existence, Extends, For, IDENTIFIER, IDENTIFIER_STR, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, compact, del, ends, extend, flatten, last, merge, multident, starts, unfoldSoak, utility, _ref;
|
||||
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
|
||||
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
|
||||
function ctor() { this.constructor = child; }
|
||||
|
@ -74,8 +74,14 @@
|
|||
}
|
||||
return [src, tmp];
|
||||
};
|
||||
Base.prototype.makeReturn = function() {
|
||||
return new Return(this);
|
||||
Base.prototype.makeReturn = function(res) {
|
||||
var me;
|
||||
me = this.unwrapAll();
|
||||
if (res) {
|
||||
return new Call(new Literal("" + res + ".push"), [me]);
|
||||
} else {
|
||||
return new Return(me);
|
||||
}
|
||||
};
|
||||
Base.prototype.contains = function(pred) {
|
||||
var contains;
|
||||
|
@ -200,13 +206,13 @@
|
|||
if (exp.jumps(o)) return exp;
|
||||
}
|
||||
};
|
||||
Block.prototype.makeReturn = function() {
|
||||
Block.prototype.makeReturn = function(res) {
|
||||
var expr, len;
|
||||
len = this.expressions.length;
|
||||
while (len--) {
|
||||
expr = this.expressions[len];
|
||||
if (!(expr instanceof Comment)) {
|
||||
this.expressions[len] = expr.makeReturn();
|
||||
this.expressions[len] = expr.makeReturn(res);
|
||||
if (expr instanceof Return && !expr.expression) {
|
||||
this.expressions.splice(len, 1);
|
||||
}
|
||||
|
@ -310,7 +316,7 @@
|
|||
if (this.isStatement()) {
|
||||
return this;
|
||||
} else {
|
||||
return new Return(this);
|
||||
return Literal.__super__.makeReturn.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
Literal.prototype.isAssignable = function() {
|
||||
|
@ -423,13 +429,6 @@
|
|||
Value.prototype.isSplice = function() {
|
||||
return last(this.properties) instanceof Slice;
|
||||
};
|
||||
Value.prototype.makeReturn = function() {
|
||||
if (this.properties.length) {
|
||||
return Value.__super__.makeReturn.call(this);
|
||||
} else {
|
||||
return this.base.makeReturn();
|
||||
}
|
||||
};
|
||||
Value.prototype.unwrap = function() {
|
||||
if (this.properties.length) {
|
||||
return this;
|
||||
|
@ -817,13 +816,7 @@
|
|||
Obj.prototype.compileNode = function(o) {
|
||||
var i, idt, indent, join, lastNoncom, node, obj, prop, props, _i, _len;
|
||||
props = this.properties;
|
||||
if (!props.length) {
|
||||
if (this.front) {
|
||||
return '({})';
|
||||
} else {
|
||||
return '{}';
|
||||
}
|
||||
}
|
||||
if (!props.length) return (this.front ? '({})' : '{}');
|
||||
if (this.generated) {
|
||||
for (_i = 0, _len = props.length; _i < _len; _i++) {
|
||||
node = props[_i];
|
||||
|
@ -1415,9 +1408,13 @@
|
|||
}
|
||||
While.prototype.children = ['condition', 'guard', 'body'];
|
||||
While.prototype.isStatement = YES;
|
||||
While.prototype.makeReturn = function() {
|
||||
this.returns = true;
|
||||
return this;
|
||||
While.prototype.makeReturn = function(res) {
|
||||
if (res) {
|
||||
return While.__super__.makeReturn.apply(this, arguments);
|
||||
} else {
|
||||
this.returns = true;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
While.prototype.addBody = function(body) {
|
||||
this.body = body;
|
||||
|
@ -1443,10 +1440,9 @@
|
|||
if (body.isEmpty()) {
|
||||
body = '';
|
||||
} else {
|
||||
if (o.level > LEVEL_TOP || this.returns) {
|
||||
rvar = o.scope.freeVariable('results');
|
||||
if (this.returns) {
|
||||
body.makeReturn(rvar = o.scope.freeVariable('results'));
|
||||
set = "" + this.tab + rvar + " = [];\n";
|
||||
if (body) body = Push.wrap(rvar, body);
|
||||
}
|
||||
if (this.guard) {
|
||||
if (body.expressions.length > 1) {
|
||||
|
@ -1670,9 +1666,9 @@
|
|||
var _ref2;
|
||||
return this.attempt.jumps(o) || ((_ref2 = this.recovery) != null ? _ref2.jumps(o) : void 0);
|
||||
};
|
||||
Try.prototype.makeReturn = function() {
|
||||
if (this.attempt) this.attempt = this.attempt.makeReturn();
|
||||
if (this.recovery) this.recovery = this.recovery.makeReturn();
|
||||
Try.prototype.makeReturn = function(res) {
|
||||
if (this.attempt) this.attempt = this.attempt.makeReturn(res);
|
||||
if (this.recovery) this.recovery = this.recovery.makeReturn(res);
|
||||
return this;
|
||||
};
|
||||
Try.prototype.compileNode = function(o) {
|
||||
|
@ -1737,9 +1733,6 @@
|
|||
Parens.prototype.isComplex = function() {
|
||||
return this.body.isComplex();
|
||||
};
|
||||
Parens.prototype.makeReturn = function() {
|
||||
return this.body.makeReturn();
|
||||
};
|
||||
Parens.prototype.compileNode = function(o) {
|
||||
var bare, code, expr;
|
||||
expr = this.body.unwrap();
|
||||
|
@ -1758,7 +1751,7 @@
|
|||
return Parens;
|
||||
})();
|
||||
exports.For = For = (function() {
|
||||
__extends(For, Base);
|
||||
__extends(For, While);
|
||||
function For(body, source) {
|
||||
var _ref2;
|
||||
this.source = source.source, this.guard = source.guard, this.step = source.step, this.name = source.name, this.index = source.index;
|
||||
|
@ -1782,12 +1775,6 @@
|
|||
this.returns = false;
|
||||
}
|
||||
For.prototype.children = ['body', 'source', 'guard', 'step'];
|
||||
For.prototype.isStatement = YES;
|
||||
For.prototype.jumps = While.prototype.jumps;
|
||||
For.prototype.makeReturn = function() {
|
||||
this.returns = true;
|
||||
return this;
|
||||
};
|
||||
For.prototype.compileNode = function(o) {
|
||||
var body, defPart, forPart, forVarPart, guardPart, idt1, index, ivar, lastJumps, lvar, name, namePart, ref, resultPart, returnResult, rvar, scope, source, stepPart, stepvar, svar, varPart, _ref2;
|
||||
body = Block.wrap([this.body]);
|
||||
|
@ -1839,7 +1826,7 @@
|
|||
if (this.returns) {
|
||||
resultPart = "" + this.tab + rvar + " = [];\n";
|
||||
returnResult = "\n" + this.tab + "return " + rvar + ";";
|
||||
body = Push.wrap(rvar, body);
|
||||
body.makeReturn(rvar);
|
||||
}
|
||||
if (this.guard) {
|
||||
if (body.expressions.length > 1) {
|
||||
|
@ -1911,14 +1898,17 @@
|
|||
}
|
||||
return (_ref4 = this.otherwise) != null ? _ref4.jumps(o) : void 0;
|
||||
};
|
||||
Switch.prototype.makeReturn = function() {
|
||||
Switch.prototype.makeReturn = function(res) {
|
||||
var pair, _i, _len, _ref2, _ref3;
|
||||
_ref2 = this.cases;
|
||||
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
|
||||
pair = _ref2[_i];
|
||||
pair[1].makeReturn();
|
||||
pair[1].makeReturn(res);
|
||||
}
|
||||
if ((_ref3 = this.otherwise) != null) _ref3.makeReturn();
|
||||
if (res) {
|
||||
this.otherwise || (this.otherwise = new Block([new Literal('void 0')]));
|
||||
}
|
||||
if ((_ref3 = this.otherwise) != null) _ref3.makeReturn(res);
|
||||
return this;
|
||||
};
|
||||
Switch.prototype.compileNode = function(o) {
|
||||
|
@ -1993,9 +1983,12 @@
|
|||
return this.compileExpression(o);
|
||||
}
|
||||
};
|
||||
If.prototype.makeReturn = function() {
|
||||
this.body && (this.body = new Block([this.body.makeReturn()]));
|
||||
this.elseBody && (this.elseBody = new Block([this.elseBody.makeReturn()]));
|
||||
If.prototype.makeReturn = function(res) {
|
||||
if (res) {
|
||||
this.elseBody || (this.elseBody = new Block([new Literal('void 0')]));
|
||||
}
|
||||
this.body && (this.body = new Block([this.body.makeReturn(res)]));
|
||||
this.elseBody && (this.elseBody = new Block([this.elseBody.makeReturn(res)]));
|
||||
return this;
|
||||
};
|
||||
If.prototype.ensureBlock = function(node) {
|
||||
|
@ -2044,12 +2037,6 @@
|
|||
};
|
||||
return If;
|
||||
})();
|
||||
Push = {
|
||||
wrap: function(name, exps) {
|
||||
if (exps.isEmpty() || last(exps.expressions).jumps()) return exps;
|
||||
return exps.push(new Call(new Value(new Literal(name), [new Access(new Literal('push'))]), [exps.pop()]));
|
||||
}
|
||||
};
|
||||
Closure = {
|
||||
wrap: function(expressions, statement, noReturn) {
|
||||
var args, call, func, mentionsArgs, meth;
|
||||
|
|
|
@ -76,8 +76,12 @@ exports.Base = class Base
|
|||
# Construct a node that returns the current node's result.
|
||||
# Note that this is overridden for smarter behavior for
|
||||
# many statement nodes (e.g. If, For)...
|
||||
makeReturn: ->
|
||||
new Return this
|
||||
makeReturn: (res) ->
|
||||
me = @unwrapAll()
|
||||
if res
|
||||
new Call new Literal("#{res}.push"), [me]
|
||||
else
|
||||
new Return me
|
||||
|
||||
# Does this node, or any of its children, contain a node of a certain kind?
|
||||
# Recursively traverses down the *children* of the nodes, yielding to a block
|
||||
|
@ -191,12 +195,12 @@ exports.Block = class Block extends Base
|
|||
|
||||
# A Block node does not return its entire body, rather it
|
||||
# ensures that the final expression is returned.
|
||||
makeReturn: ->
|
||||
makeReturn: (res) ->
|
||||
len = @expressions.length
|
||||
while len--
|
||||
expr = @expressions[len]
|
||||
if expr not instanceof Comment
|
||||
@expressions[len] = expr.makeReturn()
|
||||
@expressions[len] = expr.makeReturn res
|
||||
@expressions.splice(len, 1) if expr instanceof Return and not expr.expression
|
||||
break
|
||||
this
|
||||
|
@ -281,7 +285,7 @@ exports.Literal = class Literal extends Base
|
|||
constructor: (@value) ->
|
||||
|
||||
makeReturn: ->
|
||||
if @isStatement() then this else new Return this
|
||||
if @isStatement() then this else super
|
||||
|
||||
isAssignable: ->
|
||||
IDENTIFIER.test @value
|
||||
|
@ -374,9 +378,6 @@ exports.Value = class Value extends Base
|
|||
isSplice: ->
|
||||
last(@properties) instanceof Slice
|
||||
|
||||
makeReturn: ->
|
||||
if @properties.length then super() else @base.makeReturn()
|
||||
|
||||
# The value can be unwrapped as its inner node, if there are no attached
|
||||
# properties.
|
||||
unwrap: ->
|
||||
|
@ -1210,9 +1211,12 @@ exports.While = class While extends Base
|
|||
|
||||
isStatement: YES
|
||||
|
||||
makeReturn: ->
|
||||
@returns = yes
|
||||
this
|
||||
makeReturn: (res) ->
|
||||
if res
|
||||
super
|
||||
else
|
||||
@returns = yes
|
||||
this
|
||||
|
||||
addBody: (@body) ->
|
||||
this
|
||||
|
@ -1234,10 +1238,9 @@ exports.While = class While extends Base
|
|||
if body.isEmpty()
|
||||
body = ''
|
||||
else
|
||||
if o.level > LEVEL_TOP or @returns
|
||||
rvar = o.scope.freeVariable 'results'
|
||||
if @returns
|
||||
body.makeReturn rvar = o.scope.freeVariable 'results'
|
||||
set = "#{@tab}#{rvar} = [];\n"
|
||||
body = Push.wrap rvar, body if body
|
||||
if @guard
|
||||
if body.expressions.length > 1
|
||||
body.expressions.unshift new If (new Parens @guard).invert(), new Literal "continue"
|
||||
|
@ -1419,9 +1422,9 @@ exports.Try = class Try extends Base
|
|||
|
||||
jumps: (o) -> @attempt.jumps(o) or @recovery?.jumps(o)
|
||||
|
||||
makeReturn: ->
|
||||
@attempt = @attempt .makeReturn() if @attempt
|
||||
@recovery = @recovery.makeReturn() if @recovery
|
||||
makeReturn: (res) ->
|
||||
@attempt = @attempt .makeReturn res if @attempt
|
||||
@recovery = @recovery.makeReturn res if @recovery
|
||||
this
|
||||
|
||||
# Compilation is more or less as you would expect -- the *finally* clause
|
||||
|
@ -1497,7 +1500,6 @@ exports.Parens = class Parens extends Base
|
|||
|
||||
unwrap : -> @body
|
||||
isComplex : -> @body.isComplex()
|
||||
makeReturn: -> @body.makeReturn()
|
||||
|
||||
compileNode: (o) ->
|
||||
expr = @body.unwrap()
|
||||
|
@ -1518,7 +1520,7 @@ exports.Parens = class Parens extends Base
|
|||
# Unlike Python array comprehensions, they can be multi-line, and you can pass
|
||||
# the current index of the loop as a second parameter. Unlike Ruby blocks,
|
||||
# you can map and filter in a single pass.
|
||||
exports.For = class For extends Base
|
||||
exports.For = class For extends While
|
||||
constructor: (body, source) ->
|
||||
{@source, @guard, @step, @name, @index} = source
|
||||
@body = Block.wrap [body]
|
||||
|
@ -1534,14 +1536,6 @@ exports.For = class For extends Base
|
|||
|
||||
children: ['body', 'source', 'guard', 'step']
|
||||
|
||||
isStatement: YES
|
||||
|
||||
jumps: While::jumps
|
||||
|
||||
makeReturn: ->
|
||||
@returns = yes
|
||||
this
|
||||
|
||||
# Welcome to the hairiest method in all of CoffeeScript. Handles the inner
|
||||
# loop, filtering, stepping, and result saving for array, object, and range
|
||||
# comprehensions. Some of the generated code can be shared in common, and
|
||||
|
@ -1582,7 +1576,7 @@ exports.For = class For extends Base
|
|||
if @returns
|
||||
resultPart = "#{@tab}#{rvar} = [];\n"
|
||||
returnResult = "\n#{@tab}return #{rvar};"
|
||||
body = Push.wrap rvar, body
|
||||
body.makeReturn rvar
|
||||
if @guard
|
||||
if body.expressions.length > 1
|
||||
body.expressions.unshift new If (new Parens @guard).invert(), new Literal "continue"
|
||||
|
@ -1636,9 +1630,10 @@ exports.Switch = class Switch extends Base
|
|||
return block if block.jumps o
|
||||
@otherwise?.jumps o
|
||||
|
||||
makeReturn: ->
|
||||
pair[1].makeReturn() for pair in @cases
|
||||
@otherwise?.makeReturn()
|
||||
makeReturn: (res) ->
|
||||
pair[1].makeReturn res for pair in @cases
|
||||
@otherwise or= new Block [new Literal 'void 0'] if res
|
||||
@otherwise?.makeReturn res
|
||||
this
|
||||
|
||||
compileNode: (o) ->
|
||||
|
@ -1696,9 +1691,10 @@ exports.If = class If extends Base
|
|||
compileNode: (o) ->
|
||||
if @isStatement o then @compileStatement o else @compileExpression o
|
||||
|
||||
makeReturn: ->
|
||||
@body and= new Block [@body.makeReturn()]
|
||||
@elseBody and= new Block [@elseBody.makeReturn()]
|
||||
makeReturn: (res) ->
|
||||
@elseBody or= new Block [new Literal 'void 0'] if res
|
||||
@body and= new Block [@body.makeReturn res]
|
||||
@elseBody and= new Block [@elseBody.makeReturn res]
|
||||
this
|
||||
|
||||
ensureBlock: (node) ->
|
||||
|
@ -1752,15 +1748,6 @@ exports.If = class If extends Base
|
|||
# Faux-nodes are never created by the grammar, but are used during code
|
||||
# generation to generate other combinations of nodes.
|
||||
|
||||
#### Push
|
||||
|
||||
# The **Push** creates the tree for `array.push(value)`,
|
||||
# which is helpful for recording the result arrays from comprehensions.
|
||||
Push =
|
||||
wrap: (name, exps) ->
|
||||
return exps if exps.isEmpty() or last(exps.expressions).jumps()
|
||||
exps.push new Call new Value(new Literal(name), [new Access new Literal 'push']), [exps.pop()]
|
||||
|
||||
#### Closure
|
||||
|
||||
# A faux-node used to wrap an expressions body in a closure.
|
||||
|
|
|
@ -428,3 +428,28 @@ test "#1326: `by` value is uncached", ->
|
|||
arrayEq a, rangeCompileSimple
|
||||
#exercises Range.compile
|
||||
eq "#{i for i in [0..2] by h()}", '0,1,2'
|
||||
|
||||
test "#1669: break/continue should skip the result only for that branch", ->
|
||||
ns = for n in [0..99]
|
||||
if n > 9
|
||||
break
|
||||
else if n & 1
|
||||
continue
|
||||
else
|
||||
n
|
||||
eq "#{ns}", '0,2,4,6,8'
|
||||
|
||||
# `else undefined` is implied.
|
||||
ns = for n in [1..9]
|
||||
if n % 2
|
||||
continue unless n % 5
|
||||
n
|
||||
eq "#{ns}", "1,,3,,,7,,9"
|
||||
|
||||
# Ditto.
|
||||
ns = for n in [1..9]
|
||||
switch
|
||||
when n % 2
|
||||
continue unless n % 5
|
||||
n
|
||||
eq "#{ns}", "1,,3,,,7,,9"
|
||||
|
|
|
@ -404,7 +404,7 @@ test "Switch with break as the return value of a loop.", ->
|
|||
when 1 then i
|
||||
when 0 then break
|
||||
|
||||
eq results.join(', '), '9, , 7, , 5, , 3, , 1, '
|
||||
eq results.join(', '), '9, 7, 5, 3, 1'
|
||||
|
||||
|
||||
test "Issue #997. Switch doesn't fallthrough.", ->
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue