satyr 1612b04 ... enabled break/continue in comprehensions.

This commit is contained in:
Jeremy Ashkenas 2010-11-01 23:53:49 -04:00
parent 6aaa2eb4d0
commit 5aa21c363d
3 changed files with 66 additions and 35 deletions

View File

@ -70,13 +70,15 @@
Base.prototype.makeReturn = function() {
return new Return(this);
};
Base.prototype.contains = function(block, arg) {
Base.prototype.contains = function(pred) {
var contains;
contains = false;
this.traverseChildren(false, function(node, arg) {
var rearg;
return (rearg = block(node, arg)) === true ? !(contains = true) : arg != null ? rearg : void 0;
}, arg);
this.traverseChildren(false, function(node) {
if (pred(node)) {
contains = true;
return false;
}
});
return contains;
};
Base.prototype.containsType = function(type) {
@ -85,11 +87,7 @@
});
};
Base.prototype.containsPureStatement = function() {
return this.isPureStatement() || this.contains(function(node, func) {
return func(node) || (node instanceof While || node instanceof For ? function(node) {
return node instanceof Return;
} : func);
}, function(node) {
return this.isPureStatement() || this.contains(function(node) {
return node.isPureStatement();
});
};
@ -136,12 +134,12 @@
});
return nodes;
};
Base.prototype.traverseChildren = function(crossScope, func, arg) {
Base.prototype.traverseChildren = function(crossScope, func) {
return this.eachChild(function(child) {
if ((arg = func(child, arg)) === false) {
if (func(child) === false) {
return false;
}
return child.traverseChildren(crossScope, func, arg);
return child.traverseChildren(crossScope, func);
});
};
Base.prototype.invert = function() {
@ -1199,14 +1197,31 @@
__extends(While, Base);
While.prototype.children = ['condition', 'guard', 'body'];
While.prototype.isStatement = YES;
While.prototype.addBody = function(body) {
this.body = body;
While.prototype.addBody = function(_arg) {
this.body = _arg;
return this;
};
While.prototype.makeReturn = function() {
this.returns = true;
return this;
};
While.prototype.containsPureStatement = function() {
var _ref2, expressions, i, ret;
expressions = this.body.expressions;
i = expressions.length;
if ((_ref2 = expressions[--i]) != null ? _ref2.containsPureStatement() : void 0) {
return true;
}
ret = function(node) {
return node instanceof Return;
};
while (i--) {
if (expressions[i].contains(ret)) {
return true;
}
}
return false;
};
While.prototype.compileNode = function(o) {
var body, code, rvar, set;
o.indent = this.idt(1);
@ -1488,12 +1503,12 @@
})();
exports.For = (function() {
For = (function() {
function For(_arg, head) {
this.body = _arg;
function For(body, head) {
if (head.index instanceof Value) {
throw SyntaxError('index cannot be a pattern matching expression');
}
extend(this, head);
this.body = Expressions.wrap([body]);
if (!this.object) {
this.step || (this.step = new Literal(1));
}
@ -1510,6 +1525,7 @@
this.returns = true;
return this;
};
For.prototype.containsPureStatement = While.prototype.containsPureStatement;
For.prototype.compileReturnValue = function(val, o) {
if (this.returns) {
return '\n' + new Return(new Literal(val)).compile(o);
@ -1522,11 +1538,11 @@
For.prototype.compileNode = function(o) {
var _ref2, _ref3, _ref4, _ref5, _ref6, body, code, cond, defPart, forPart, guardPart, idt, index, ivar, lvar, name, namePart, pvar, retPart, rvar, scope, sourcePart, step, svar, tail, tvar, varPart, vars;
scope = o.scope;
body = this.body;
name = !this.pattern && ((_ref2 = this.name) != null ? _ref2.compile(o) : void 0);
index = (_ref3 = this.index) != null ? _ref3.compile(o) : void 0;
ivar = !index ? scope.freeVariable('i') : index;
varPart = guardPart = defPart = retPart = '';
body = Expressions.wrap([this.body]);
idt = this.idt(1);
if (name) {
scope.find(name, {

View File

@ -85,11 +85,12 @@ exports.Base = class Base
# Recursively traverses down the *children* of the nodes, yielding to a block
# and returning true when the block finds a match. `contains` does not cross
# scope boundaries.
contains: (block, arg) ->
contains: (pred) ->
contains = no
@traverseChildren false, (node, arg) ->
if (rearg = block node, arg) is true then not contains = true else if arg? then rearg
, arg
@traverseChildren no, (node) ->
if pred node
contains = yes
return no
contains
# Is this node of a certain type, or does it contain the type?
@ -99,11 +100,7 @@ exports.Base = class Base
# Convenience for the most common use of contains. Does the node contain
# a pure statement?
containsPureStatement: ->
@isPureStatement() or @contains (node, func) ->
func(node) or if node instanceof While or node instanceof For
(node) -> node instanceof Return
else func
, (node) -> node.isPureStatement()
@isPureStatement() or @contains (node) -> node.isPureStatement()
# `toString` representation of the node, for inspecting the parse tree.
# This is what `coffee --nodes` prints out.
@ -125,10 +122,10 @@ exports.Base = class Base
@eachChild (node) -> nodes.push node
nodes
traverseChildren: (crossScope, func, arg) ->
traverseChildren: (crossScope, func) ->
@eachChild (child) ->
return false if (arg = func child, arg) is false
child.traverseChildren crossScope, func, arg
return false if func(child) is false
child.traverseChildren crossScope, func
invert: ->
new Op '!', this
@ -996,14 +993,21 @@ exports.While = class While extends Base
@condition = if options?.invert then condition.invert() else condition
@guard = options?.guard
addBody: (body) ->
@body = body
addBody: (@body) ->
this
makeReturn: ->
@returns = true
this
containsPureStatement: ->
{expressions} = @body
i = expressions.length
return true if expressions[--i]?.containsPureStatement()
ret = (node) -> node instanceof Return
return true while i-- when expressions[i].contains ret
false
# The main difference from a JavaScript *while* is that the CoffeeScript
# *while* can be used as a part of a larger expression -- while loops may
# return an array containing the computed result of each iteration.
@ -1259,10 +1263,11 @@ exports.For = class For extends Base
isStatement: YES
constructor: (@body, head) ->
constructor: (body, head) ->
if head.index instanceof Value
throw SyntaxError 'index cannot be a pattern matching expression'
extend this, head
@body = Expressions.wrap [body]
@step or= new Literal 1 unless @object
@pattern = @name instanceof Value
@returns = false
@ -1271,6 +1276,8 @@ exports.For = class For extends Base
@returns = true
this
containsPureStatement: While::containsPureStatement
compileReturnValue: (val, o) ->
return '\n' + new Return(new Literal val).compile o if @returns
return '\n' + val if val
@ -1282,11 +1289,11 @@ exports.For = class For extends Base
# some cannot.
compileNode: (o) ->
{scope} = o
{body} = this
name = not @pattern and @name?.compile o
index = @index?.compile o
ivar = if not index then scope.freeVariable 'i' else index
varPart = guardPart = defPart = retPart = ''
body = Expressions.wrap [@body]
idt = @idt 1
scope.find(name, immediate: yes) if name
scope.find(index, immediate: yes) if index

View File

@ -114,6 +114,14 @@ ok val[0] is i
# Comprehensions only wrap their last line in a closure, allowing other lines
# to have pure expressions in them.
func = -> for i in [1]
return if false
break if i is 2
j for j in [1]
ok func()[0][0] is 1
i = 6
odds = while i--
continue unless i & 1
i
ok odds.join(', ') is '5, 3, 1'