1
0
Fork 0
mirror of https://github.com/jashkenas/coffeescript.git synced 2022-11-09 12:23:24 -05:00

first draft of range literals back...

This commit is contained in:
Jeremy Ashkenas 2010-11-20 16:25:22 -05:00
parent 9f708ad0c8
commit 31f4214b5d
13 changed files with 708 additions and 562 deletions

View file

@ -58,7 +58,8 @@
printTasks = function() {
var desc, name, spaces, task, _ref;
console.log('');
for (name in _ref = tasks) {
_ref = tasks;
for (name in _ref) {
task = _ref[name];
spaces = 20 - name.length;
spaces = spaces > 0 ? Array(spaces + 1).join(' ') : '';

View file

@ -33,7 +33,7 @@
};
exports.nodes = function(source, options) {
if (typeof source === 'string') {
return parser.parse(lexer.tokenize(code, options));
return parser.parse(lexer.tokenize(source, options));
} else {
return parser.parse(source);
}

View file

@ -49,10 +49,11 @@
return compileScripts();
};
compileScripts = function() {
var _fn, _i, _len, _ref, _results;
var base, compile, source, _i, _len, _ref, _results;
_ref = sources;
_fn = function(source) {
var base, compile;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
source = _ref[_i];
base = path.join(source);
compile = function(source, topLevel) {
return path.exists(source, function(exists) {
@ -81,12 +82,7 @@
});
});
};
return compile(source, true);
};
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
source = _ref[_i];
_results.push(_fn(source));
_results.push(compile(source, true));
}
return _results;
};

View file

@ -151,6 +151,8 @@
return new Value($1);
}), o('Parenthetical', function() {
return new Value($1);
}), o('Range', function() {
return new Value($1);
}), o('This')
],
Accessor: [
@ -258,6 +260,27 @@
return new Arr($2);
})
],
RangeDots: [
o('..', function() {
return 'inclusive';
}), o('...', function() {
return 'exclusive';
})
],
Range: [
o('[ Expression RangeDots Expression ]', function() {
return new Range($2, $4, $3);
})
],
Slice: [
o('INDEX_START Expression RangeDots Expression INDEX_END', function() {
return new Range($2, $4, $3);
}), o('INDEX_START Expression RangeDots INDEX_END', function() {
return new Range($2, null, $3);
}), o('INDEX_START RangeDots Expression INDEX_END', function() {
return new Range(null, $3, $2);
})
],
ArgList: [
o('Arg', function() {
return [$1];
@ -341,11 +364,31 @@
],
For: [
o('Statement ForBody', function() {
return new For($1, $2);
return new For($1, $2, $2.vars[0], $2.vars[1]);
}), o('Expression ForBody', function() {
return new For($1, $2);
return new For($1, $2, $2.vars[0], $2.vars[1]);
}), o('ForBody Block', function() {
return new For($2, $1);
return new For($2, $1, $1.vars[0], $1.vars[1]);
})
],
ForBody: [
o('FOR Range', function() {
return {
source: new Value($2),
vars: []
};
}), o('ForStart ForSource', function() {
$2.raw = $1.raw;
$2.vars = $1;
return $2;
})
],
ForStart: [
o('FOR ForVariables', function() {
return $2;
}), o('FOR ALL ForVariables', function() {
$3.raw = true;
return $3;
})
],
ForValue: [
@ -355,21 +398,45 @@
return new Value($1);
})
],
ForIn: [
ForVariables: [
o('ForValue', function() {
return [$1];
}), o('ForValue , ForValue', function() {
return [$1, $3];
})
],
ForSource: [
o('FORIN Expression', function() {
return {
source: $2
};
}), o('FOROF Expression', function() {
return {
source: $2,
object: true
};
}), o('FORIN Expression WHEN Expression', function() {
return {
source: $2,
guard: $4
};
}), o('FOROF Expression WHEN Expression', function() {
return {
source: $2,
guard: $4,
object: true
};
}), o('FORIN Expression BY Expression', function() {
return {
source: $2,
step: $4
};
}), o('FORIN Expression WHEN Expression BY Expression', function() {
return {
source: $2,
guard: $4,
step: $6
};
}), o('FORIN Expression BY Expression WHEN Expression', function() {
return {
source: $2,
@ -378,80 +445,6 @@
};
})
],
ForOf: [
o('FOROF Expression', function() {
return {
object: true,
source: $2
};
}), o('FOROF Expression WHEN Expression', function() {
return {
object: true,
source: $2,
guard: $4
};
})
],
ForTo: [
o('TO Expression', function() {
return {
to: $2
};
}), o('TO Expression WHEN Expression', function() {
return {
to: $2,
guard: $4
};
}), o('TO Expression BY Expression', function() {
return {
to: $2,
step: $4
};
}), o('TO Expression BY Expression WHEN Expression', function() {
return {
to: $2,
step: $4,
guard: $6
};
})
],
ForBody: [
o('FOR ForValue ForIn', function() {
return extend($3, {
name: $2
});
}), o('FOR ForValue , Identifier ForIn', function() {
return extend($5, {
name: $2,
index: $4
});
}), o('FOR Identifier ForOf', function() {
return extend($3, {
index: $2
});
}), o('FOR ForValue , ForValue ForOf', function() {
return extend($5, {
index: $2,
name: $4
});
}), o('FOR ALL Identifier ForOf', function() {
return extend($4, {
raw: true,
index: $3
});
}), o('FOR ALL Identifier , ForValue ForOf', function() {
return extend($6, {
raw: true,
index: $3,
name: $5
});
}), o('FOR Identifier FROM Expression ForTo', function() {
return extend($5, {
index: $2,
from: $4
});
})
],
Switch: [
o('SWITCH Expression INDENT Whens OUTDENT', function() {
return new Switch($2, $4);
@ -559,7 +552,7 @@
})
]
};
operators = [['left', '.', '?.', '::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', 'LOGIC'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'FROM', 'TO', 'BY', 'WHEN'], ['right', 'IF', 'UNLESS', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS'], ['right', 'POST_IF', 'POST_UNLESS']];
operators = [['left', '.', '?.', '::'], ['left', 'CALL_START', 'CALL_END'], ['nonassoc', '++', '--'], ['left', '?'], ['right', 'UNARY'], ['left', 'MATH'], ['left', '+', '-'], ['left', 'SHIFT'], ['left', 'RELATION'], ['left', 'COMPARE'], ['left', 'LOGIC'], ['nonassoc', 'INDENT', 'OUTDENT'], ['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS'], ['right', 'FORIN', 'FOROF', 'BY', 'WHEN'], ['right', 'IF', 'UNLESS', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS'], ['right', 'POST_IF', 'POST_UNLESS']];
tokens = [];
for (name in grammar) {
alternatives = grammar[name];

View file

@ -1,7 +1,8 @@
(function() {
var key, val, _ref;
var __hasProp = Object.prototype.hasOwnProperty;
for (key in _ref = require('./coffee-script')) {
_ref = require('./coffee-script');
for (key in _ref) {
if (!__hasProp.call(_ref, key)) continue;
val = _ref[key];
exports[key] = val;

View file

@ -21,7 +21,6 @@
this.outdebt = 0;
this.indents = [];
this.tokens = [];
this.seenFor = this.seenFrom = false;
i = 0;
while (this.chunk = code.slice(i)) {
i += this.identifierToken() || this.commentToken() || this.whitespaceToken() || this.lineToken() || this.heredocToken() || this.stringToken() || this.numberToken() || this.regexToken() || this.jsToken() || this.literalToken();
@ -42,17 +41,6 @@
this.token('ALL', id);
return id.length;
}
if (id === 'from' && this.tag(1) === 'FOR') {
this.seenFor = false;
this.seenFrom = true;
this.token('FROM', id);
return id.length;
}
if (id === 'to' && this.seenFrom) {
this.seenFrom = false;
this.token('TO', id);
return id.length;
}
forcedIdentifier = colon || (prev = last(this.tokens)) && !prev.spaced && ((_ref = prev[0]) === '.' || _ref === '?.' || _ref === '@' || _ref === '::');
tag = 'IDENTIFIER';
if (__indexOf.call(JS_KEYWORDS, id) >= 0 || !forcedIdentifier && __indexOf.call(COFFEE_KEYWORDS, id) >= 0) {
@ -218,7 +206,7 @@
return regex.length;
};
Lexer.prototype.heregexToken = function(match) {
var body, flags, heregex, re, tag, tokens, value, _i, _len, _ref, _ref2, _this;
var body, flags, heregex, re, tag, tokens, value, _i, _len, _ref, _ref2, _ref3, _this;
heregex = match[0], body = match[1], flags = match[2];
if (0 > body.indexOf('#{')) {
re = body.replace(HEREGEX_OMIT, '').replace(/\//g, '\\/');
@ -228,12 +216,11 @@
this.token('IDENTIFIER', 'RegExp');
this.tokens.push(['CALL_START', '(']);
tokens = [];
for (_i = 0, _len = this.interpolateString(body, {
_ref = this.interpolateString(body, {
regex: true
}).length; _i < _len; _i++) {
_ref = this.interpolateString(body, {
regex: true
})[_i], tag = _ref[0], value = _ref[1];
});
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
_ref2 = _ref[_i], tag = _ref2[0], value = _ref2[1];
if (tag === 'TOKENS') {
tokens.push.apply(tokens, value);
} else {
@ -246,7 +233,7 @@
tokens.push(['+', '+']);
}
tokens.pop();
if (((_ref2 = tokens[0]) != null ? _ref2[0] : void 0) !== 'STRING') {
if (((_ref3 = tokens[0]) != null ? _ref3[0] : void 0) !== 'STRING') {
this.tokens.push(['STRING', '""'], ['+', '+']);
}
(_this = this.tokens).push.apply(_this, tokens);
@ -459,10 +446,10 @@
throw SyntaxError("Reserved word \"" + (this.value()) + "\" on line " + (this.line + 1) + " can't be assigned");
};
Lexer.prototype.balancedString = function(str, delimited, options) {
var i, open, pair, stack, _i, _len, _to;
var i, open, pair, stack, _i, _len, _ref;
options == null && (options = {});
stack = [delimited[0]];
for (i = 1, _to = str.length - 1; 1 <= _to ? i <= _to : i >= _to; 1 <= _to ? i++ : i--) {
for (i = 1, _ref = str.length; (1 <= _ref ? i < _ref : i > _ref); (1 <= _ref ? i += 1 : i -= 1)) {
switch (str.charAt(i)) {
case '\\':
i++;
@ -484,6 +471,7 @@
}
}
}
i--;
throw new Error("unterminated " + (stack.pop()[0]) + " on line " + (this.line + 1));
};
Lexer.prototype.interpolateString = function(str, options) {
@ -605,7 +593,7 @@
IDENTIFIER = /^([$A-Za-z_][$\w]*)([^\n\S]*:(?!:))?/;
NUMBER = /^0x[\da-f]+|^(?:\d+(\.\d+)?|\.\d+)(?:e[+-]?\d+)?/i;
HEREDOC = /^("""|''')([\s\S]*?)(?:\n[^\n\S]*)?\1/;
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{3})/;
OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})/;
WHITESPACE = /^[^\n\S]+/;
COMMENT = /^###([^#][\s\S]*?)(?:###[^\n\S]*\n|(?:###)?$)|^(?:\s*#(?!##[^#]).*)+/;
CODE = /^[-=]>/;

View file

@ -1,5 +1,5 @@
(function() {
var Accessor, Arr, Assign, Base, Call, Class, Closure, Code, Comment, Existence, Expressions, Extends, For, IDENTIFIER, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, NEGATE, NO, NUMBER, Obj, Op, Param, Parens, Push, Return, SIMPLENUM, Scope, Splat, Switch, TAB, THIS, TRAILING_WHITESPACE, Throw, Try, UTILITIES, Value, While, YES, compact, del, ends, extend, flatten, last, merge, multident, starts, unfoldSoak, utility, _ref;
var Accessor, Arr, Assign, Base, Call, Class, Closure, Code, Comment, Existence, Expressions, Extends, For, IDENTIFIER, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, NEGATE, NO, NUMBER, Obj, Op, Param, Parens, Push, Range, Return, SIMPLENUM, Scope, Slice, Splat, Switch, TAB, THIS, TRAILING_WHITESPACE, 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) {
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
@ -196,12 +196,12 @@
return false;
};
Expressions.prototype.makeReturn = function() {
var end, idx, _ref;
_ref = this.expressions;
for (idx = _ref.length - 1; idx >= 0; idx--) {
end = _ref[idx];
if (!(end instanceof Comment)) {
this.expressions[idx] = end.makeReturn();
var expr, len;
len = this.expressions.length;
while (len--) {
expr = this.expressions[len];
if (!(expr instanceof Comment)) {
this.expressions[len] = expr.makeReturn();
break;
}
}
@ -405,6 +405,9 @@
}
return (this.base instanceof Obj) && (!onlyGenerated || this.base.generated);
};
Value.prototype.isSplice = function() {
return last(this.properties) instanceof Slice;
};
Value.prototype.makeReturn = function() {
if (this.properties.length) {
return Value.__super__.makeReturn.call(this);
@ -670,6 +673,110 @@
};
return Index;
}();
exports.Range = Range = function() {
function Range(_arg, _arg2, tag) {
this.from = _arg;
this.to = _arg2;
this.exclusive = tag === 'exclusive';
this.equals = this.exclusive ? '' : '=';
}
__extends(Range, Base);
Range.prototype.children = ['from', 'to'];
Range.prototype.compileVariables = function(o) {
var parts, _ref, _ref2, _ref3;
o = merge(o, {
top: true
});
_ref = this.from.cache(o, LEVEL_LIST), this.from = _ref[0], this.fromVar = _ref[1];
_ref2 = this.to.cache(o, LEVEL_LIST), this.to = _ref2[0], this.toVar = _ref2[1];
_ref3 = [this.fromVar.match(SIMPLENUM), this.toVar.match(SIMPLENUM)], this.fromNum = _ref3[0], this.toNum = _ref3[1];
parts = [];
if (this.from !== this.fromVar) {
parts.push(this.from);
}
if (this.to !== this.toVar) {
return parts.push(this.to);
}
};
Range.prototype.compileNode = function(o) {
var compare, idx, incr, intro, step, stepPart, vars;
this.compileVariables(o);
if (!o.index) {
return this.compileArray(o);
}
if (this.fromNum && this.toNum) {
return this.compileSimple(o);
}
idx = del(o, 'index');
step = del(o, 'step');
vars = ("" + idx + " = " + this.from) + (this.to !== this.toVar ? ", " + this.to : '');
intro = "(" + this.fromVar + " <= " + this.toVar + " ? " + idx;
compare = "" + intro + " <" + this.equals + " " + this.toVar + " : " + idx + " >" + this.equals + " " + this.toVar + ")";
stepPart = step ? step.compile(o) : '1';
incr = step ? "" + idx + " += " + stepPart : "" + intro + " += " + stepPart + " : " + idx + " -= " + stepPart + ")";
return "" + vars + "; " + compare + "; " + incr;
};
Range.prototype.compileSimple = function(o) {
var from, idx, step, to, _ref;
_ref = [+this.fromNum, +this.toNum], from = _ref[0], to = _ref[1];
idx = del(o, 'index');
step = del(o, 'step');
step && (step = "" + idx + " += " + (step.compile(o)));
if (from <= to) {
return "" + idx + " = " + from + "; " + idx + " <" + this.equals + " " + to + "; " + (step || ("" + idx + "++"));
} else {
return "" + idx + " = " + from + "; " + idx + " >" + this.equals + " " + to + "; " + (step || ("" + idx + "--"));
}
};
Range.prototype.compileArray = function(o) {
var body, clause, i, idt, post, pre, range, result, vars, _i, _ref, _ref2, _results;
if (this.fromNum && this.toNum && Math.abs(this.fromNum - this.toNum) <= 20) {
range = (function() {
_results = [];
for (var _i = _ref = +this.fromNum, _ref2 = +this.toNum; _ref <= _ref2 ? _i <= _ref2 : _i >= _ref2; _ref <= _ref2 ? _i += 1 : _i -= 1){ _results.push(_i); }
return _results;
}).call(this);
if (this.exclusive) {
range.pop();
}
return "[" + (range.join(', ')) + "]";
}
idt = this.tab + TAB;
i = o.scope.freeVariable('i');
result = o.scope.freeVariable('results');
pre = "\n" + idt + result + " = [];";
if (this.fromNum && this.toNum) {
o.index = i;
body = this.compileSimple(o);
} else {
vars = ("" + i + " = " + this.from) + (this.to !== this.toVar ? ", " + this.to : '');
clause = "" + this.fromVar + " <= " + this.toVar + " ?";
body = "var " + vars + "; " + clause + " " + i + " <" + this.equals + " " + this.toVar + " : " + i + " >" + this.equals + " " + this.toVar + "; " + clause + " " + i + " += 1 : " + i + " -= 1";
}
post = "{ " + result + ".push(" + i + "); }\n" + idt + "return " + result + ";\n" + o.indent;
return "(function() {" + pre + "\n" + idt + "for (" + body + ")" + post + "}).call(this)";
};
return Range;
}();
exports.Slice = Slice = function() {
function Slice(_arg) {
this.range = _arg;
Slice.__super__.constructor.call(this);
}
__extends(Slice, Base);
Slice.prototype.children = ['range'];
Slice.prototype.compileNode = function(o) {
var from, to;
from = this.range.from ? this.range.from.compile(o) : '0';
to = this.range.to ? this.range.to.compile(o) : '';
to += !to || this.range.exclusive ? '' : ' + 1';
if (to) {
to = ', ' + to;
}
return ".slice(" + from + to + ")";
};
return Slice;
}();
exports.Obj = Obj = function() {
function Obj(props, _arg) {
this.generated = _arg != null ? _arg : false;
@ -1082,6 +1189,22 @@
_ref = this.variable.cacheReference(o), left = _ref[0], rite = _ref[1];
return new Op(this.context.slice(0, -1), left, new Assign(rite, this.value, '=')).compile(o);
};
Assign.prototype.compileSplice = function(o) {
var from, name, plus, range, ref, to, val;
range = this.variable.properties.pop().range;
name = this.variable.compile(o);
plus = range.exclusive ? '' : ' + 1';
from = range.from ? range.from.compile(o) : '0';
to = range.to ? range.to.compile(o) + ' - ' + from + plus : "" + name + ".length";
ref = o.scope.freeVariable('ref');
val = this.value.compile(o);
return "([].splice.apply(" + name + ", [" + from + ", " + to + "].concat(" + ref + " = " + val + ")), " + ref + ")";
};
Assign.prototype.compileConditional = function(o) {
var left, rite, _ref;
_ref = this.variable.cacheReference(o), left = _ref[0], rite = _ref[1];
return new Op(this.context.slice(0, -1), left, new Assign(rite, this.value)).compile(o);
};
return Assign;
}();
exports.Code = Code = function() {
@ -1601,149 +1724,109 @@
return Parens;
}();
exports.For = For = function() {
function For(body, head) {
if (head.index instanceof Value) {
function For(body, source, _arg, _arg2) {
var _ref;
this.name = _arg;
this.index = _arg2;
this.source = source.source, this.guard = source.guard, this.step = source.step;
this.body = Expressions.wrap([body]);
this.raw = !!source.raw;
this.object = !!source.object;
if (this.object) {
_ref = [this.index, this.name], this.name = _ref[0], this.index = _ref[1];
}
if (this.index instanceof Value) {
throw SyntaxError('index cannot be a pattern matching expression');
}
extend(this, head);
this.body = Expressions.wrap([body]);
this.range = this.source instanceof Value && this.source.base instanceof Range && !this.source.properties.length;
this.pattern = this.name instanceof Value;
if (this.range && this.pattern) {
throw SyntaxError('cannot pattern match a range loop');
}
this.returns = false;
}
__extends(For, Base);
For.prototype.children = ['body', 'source', 'guard', 'step', 'from', 'to'];
For.prototype.children = ['body', 'source', 'guard', 'step'];
For.prototype.isStatement = YES;
For.prototype.makeReturn = function() {
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);
}
if (val) {
return '\n' + val;
}
return '';
};
For.prototype.compileNode = function(o) {
var body, code, cond, defPart, forPart, fvar, guardPart, hasCode, head, idt, incr, index, intro, ivar, lvar, name, namePart, pvar, retPart, rvar, scope, sourcePart, step, svar, tail, tvar, varPart, vars, _ref, _ref2, _ref3, _ref4, _ref5, _ref6;
var body, forPart, guardPart, idt1, index, ivar, lvar, name, namePart, ref, resultPart, returnResult, rvar, scope, source, sourcePart, stepPart, svar, unstepPart, varPart, vars;
source = this.range ? this.source.base : this.source;
scope = o.scope;
body = this.body;
hasCode = this.body.contains(function(node) {
return node instanceof Code;
});
name = !this.pattern && ((_ref = this.name) != null ? _ref.compile(o) : void 0);
index = (_ref2 = this.index) != null ? _ref2.compile(o) : void 0;
ivar = !index ? scope.freeVariable('i') : index;
varPart = guardPart = defPart = retPart = '';
idt = o.indent + TAB;
if (!hasCode) {
if (name) {
scope.find(name, true);
}
if (index) {
scope.find(index, true);
}
name = this.name && this.name.compile(o, LEVEL_LIST);
index = this.index && this.index.compile(o, LEVEL_LIST);
if (name && !this.pattern) {
scope.find(name, {
immediate: true
});
}
if (this.step) {
_ref3 = this.step.compileLoopReference(o, 'step'), step = _ref3[0], pvar = _ref3[1];
if (index) {
scope.find(index, {
immediate: true
});
}
if (this.from) {
_ref4 = this.from.compileLoopReference(o, 'from'), head = _ref4[0], fvar = _ref4[1];
_ref5 = this.to.compileLoopReference(o, 'to'), tail = _ref5[0], tvar = _ref5[1];
vars = ivar + ' = ' + head;
if (tail !== tvar) {
vars += ', ' + tail;
}
if (SIMPLENUM.test(head) && SIMPLENUM.test(tail)) {
if (+head <= +tail) {
cond = "" + ivar + " <= " + tail;
} else {
pvar || (pvar = -1);
cond = "" + ivar + " >= " + tail;
}
} else {
if (+pvar) {
cond = "" + ivar + " " + (pvar < 0 ? '>' : '<') + "= " + tvar;
} else {
intro = "" + fvar + " <= " + tvar + " ? " + ivar;
cond = "" + intro + " <= " + tvar + " : " + ivar + " >= " + tvar;
incr = pvar ? "" + ivar + " += " + pvar : "" + intro + "++ : " + ivar + "--";
}
}
if (this.returns) {
rvar = scope.freeVariable('results');
}
ivar = (this.range ? name : index) || scope.freeVariable('i');
varPart = '';
guardPart = '';
unstepPart = '';
body = Expressions.wrap([this.body]);
idt1 = this.tab + TAB;
if (this.range) {
forPart = source.compile(merge(o, {
index: ivar,
step: this.step
}));
} else {
if (name || this.object && !this.raw) {
_ref6 = this.source.compileLoopReference(o, 'ref'), sourcePart = _ref6[0], svar = _ref6[1];
} else {
sourcePart = svar = this.source.compile(o, LEVEL_PAREN);
svar = this.source.compile(o, LEVEL_TOP);
if ((name || !this.raw) && !(IDENTIFIER.test(svar) && scope.check(svar, {
immediate: true
}))) {
sourcePart = "" + this.tab + (ref = scope.freeVariable('ref')) + " = " + svar + ";\n";
svar = ref;
}
namePart = this.pattern ? new Assign(this.name, new Literal("" + svar + "[" + ivar + "]")).compile(o, LEVEL_TOP) : name ? "" + name + " = " + svar + "[" + ivar + "]" : void 0;
if (!this.object) {
if (0 > pvar && (pvar | 0) === +pvar) {
vars = "" + ivar + " = " + svar + ".length - 1";
cond = "" + ivar + " >= 0";
} else {
lvar = scope.freeVariable('len');
vars = "" + ivar + " = 0, " + lvar + " = " + svar + ".length";
cond = "" + ivar + " < " + lvar;
}
lvar = scope.freeVariable('len');
stepPart = this.step ? "" + ivar + " += " + (this.step.compile(o, LEVEL_OP)) : "" + ivar + "++";
forPart = "" + ivar + " = 0, " + lvar + " = " + svar + ".length; " + ivar + " < " + lvar + "; " + stepPart;
}
}
if (this.object) {
forPart = ivar + ' in ' + sourcePart;
guardPart = this.raw ? '' : idt + ("if (!" + (utility('hasProp')) + ".call(" + svar + ", " + ivar + ")) continue;\n");
} else {
pvar || (pvar = 1);
if (step && (step !== pvar)) {
vars += ', ' + step;
}
if (svar !== sourcePart) {
defPart = this.tab + sourcePart + ';\n';
}
forPart = vars + ("; " + cond + "; ") + (incr || (ivar + (function() {
switch (+pvar) {
case 1:
return '++';
case -1:
return '--';
default:
if (pvar < 0) {
return ' -= ' + pvar.slice(1);
} else {
return ' += ' + pvar;
}
}
})()));
}
if (hasCode) {
body = Closure.wrap(body, true);
}
if (namePart) {
varPart = idt + namePart + ';\n';
}
if (!this.pattern) {
defPart += this.pluckDirectCall(o, body, name, index);
}
code = guardPart + varPart;
if (!body.isEmpty()) {
if (o.level > LEVEL_TOP || this.returns) {
rvar = scope.freeVariable('results');
defPart += this.tab + rvar + ' = [];\n';
retPart = this.compileReturnValue(rvar, o);
if (this.returns) {
resultPart = "" + this.tab + rvar + " = [];\n";
returnResult = '\n' + (new Return(new Literal(rvar)).compile(o, LEVEL_PAREN));
if (this.returns) {
body = Push.wrap(rvar, body);
}
if (this.guard) {
body = Expressions.wrap([new If(this.guard, body)]);
}
if (this.guard) {
body = Expressions.wrap([new If(this.guard, body)]);
}
if (namePart) {
varPart = "" + idt1 + namePart + ";\n";
}
if (forPart && name === ivar) {
unstepPart = this.step ? "" + name + " -= " + (this.step.compile(o)) + ";" : "" + name + "--;";
unstepPart = ("\n" + this.tab) + unstepPart;
}
if (this.object) {
forPart = "" + ivar + " in " + svar;
if (!this.raw) {
guardPart = "\n" + idt1 + "if (!" + (utility('hasProp')) + ".call(" + svar + ", " + ivar + ")) continue;";
}
o.indent = idt;
code += body.compile(o, LEVEL_TOP);
}
if (code) {
code = '\n' + code + '\n' + this.tab;
}
return defPart + this.tab + ("for (" + forPart + ") {" + code + "}") + retPart;
body = body.compile(merge(o, {
indent: idt1,
top: true
}));
vars = this.range ? name : "" + name + ", " + ivar;
return "" + (sourcePart || '') + (resultPart || '') + this.tab + "for (" + forPart + ") {" + guardPart + "\n" + varPart + body + "\n" + this.tab + "}" + unstepPart + (returnResult || '');
};
For.prototype.pluckDirectCall = function(o, body, name, index) {
var arg, args, base, defs, expr, fn, i, idx, ref, val, _len, _len2, _ref, _ref2, _ref3, _ref4, _ref5, _ref6;
@ -1803,15 +1886,16 @@
return this;
};
Switch.prototype.compileNode = function(o) {
var block, body, code, cond, conditions, expr, i, idt1, idt2, _i, _j, _len, _len2, _ref, _ref2, _ref3, _ref4;
var block, body, code, cond, conditions, expr, i, idt1, idt2, _i, _j, _len, _len2, _len3, _ref, _ref2, _ref3, _ref4, _ref5;
idt1 = o.indent + TAB;
idt2 = o.indent = idt1 + TAB;
code = this.tab + ("switch (" + (((_ref = this.subject) != null ? _ref.compile(o, LEVEL_PAREN) : void 0) || false) + ") {\n");
for (i = 0, _len = this.cases.length; i < _len; i++) {
_ref2 = this.cases[i], conditions = _ref2[0], block = _ref2[1];
_ref3 = flatten([conditions]);
for (_i = 0, _len2 = _ref3.length; _i < _len2; _i++) {
cond = _ref3[_i];
_ref2 = this.cases;
for (i = 0, _len = _ref2.length; i < _len; i++) {
_ref3 = _ref2[i], conditions = _ref3[0], block = _ref3[1];
_ref4 = flatten([conditions]);
for (_i = 0, _len2 = _ref4.length; _i < _len2; _i++) {
cond = _ref4[_i];
if (!this.subject) {
cond = cond.invert();
}
@ -1823,9 +1907,9 @@
if (i === this.cases.length - 1 && !this.otherwise) {
break;
}
_ref4 = block.expressions;
for (_j = _ref4.length - 1; _j >= 0; _j--) {
expr = _ref4[_j];
_ref5 = block.expressions;
for (_j = 0, _len3 = _ref5.length; _j < _len3; _j += -1) {
expr = _ref5[_j];
if (!(expr instanceof Comment)) {
if (!(expr instanceof Return)) {
code += idt2 + 'break;\n';

File diff suppressed because one or more lines are too long

View file

@ -52,9 +52,10 @@
return i - 1;
};
Rewriter.prototype.removeLeadingNewlines = function() {
var i, tag, _len;
for (i = 0, _len = this.tokens.length; i < _len; i++) {
tag = this.tokens[i][0];
var i, tag, _len, _ref;
_ref = this.tokens;
for (i = 0, _len = _ref.length; i < _len; i++) {
tag = _ref[i][0];
if (tag !== 'TERMINATOR') {
break;
}

View file

@ -43,7 +43,7 @@ exports.tokens = (code, options) ->
# or traverse it by using `.traverse()` with a callback.
exports.nodes = (source, options) ->
if typeof source is 'string'
parser.parse lexer.tokenize code, options
parser.parse lexer.tokenize source, options
else
parser.parse source

View file

@ -233,6 +233,7 @@ grammar =
o 'Assignable'
o 'Literal', -> new Value $1
o 'Parenthetical', -> new Value $1
o 'Range', -> new Value $1
o 'This'
]
@ -320,6 +321,24 @@ grammar =
o '[ ArgList OptComma ]', -> new Arr $2
]
# Inclusive and exclusive range dots.
RangeDots: [
o '..', -> 'inclusive'
o '...', -> 'exclusive'
]
# The CoffeeScript range literal.
Range: [
o '[ Expression RangeDots Expression ]', -> new Range $2, $4, $3
]
# Array slice literals.
Slice: [
o 'INDEX_START Expression RangeDots Expression INDEX_END', -> new Range $2, $4, $3
o 'INDEX_START Expression RangeDots INDEX_END', -> new Range $2, null, $3
o 'INDEX_START RangeDots Expression INDEX_END', -> new Range null, $3, $2
]
# The **ArgList** is both the list of objects passed into a function call,
# as well as the contents of an array literal
# (i.e. comma-separated expressions). Newlines work as well.
@ -397,49 +416,48 @@ grammar =
# Comprehensions can either be normal, with a block of expressions to execute,
# or postfix, with a single expression.
For: [
o 'Statement ForBody', -> new For $1, $2
o 'Expression ForBody', -> new For $1, $2
o 'ForBody Block', -> new For $2, $1
o 'Statement ForBody', -> new For $1, $2, $2.vars[0], $2.vars[1]
o 'Expression ForBody', -> new For $1, $2, $2.vars[0], $2.vars[1]
o 'ForBody Block', -> new For $2, $1, $1.vars[0], $1.vars[1]
]
# An array of all accepted values for a variable inside the loop. This
# enables support for pattern matching.
ForBody: [
o 'FOR Range', -> source: new Value($2), vars: []
o 'ForStart ForSource', -> $2.raw = $1.raw; $2.vars = $1; $2
]
ForStart: [
o 'FOR ForVariables', -> $2
o 'FOR ALL ForVariables', -> $3.raw = yes; $3
]
# An array of all accepted values for a variable inside the loop.
# This enables support for pattern matching.
ForValue: [
o 'Identifier'
o 'Array', -> new Value $1
o 'Object', -> new Value $1
]
ForIn: [
o 'FORIN Expression', -> source: $2
o 'FORIN Expression WHEN Expression', -> source: $2, guard: $4
o 'FORIN Expression BY Expression', -> source: $2, step: $4
o 'FORIN Expression BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6
]
ForOf: [
o 'FOROF Expression', -> object: on, source: $2
o 'FOROF Expression WHEN Expression', -> object: on, source: $2, guard: $4
]
ForTo: [
o 'TO Expression', -> to: $2
o 'TO Expression WHEN Expression', -> to: $2, guard: $4
o 'TO Expression BY Expression', -> to: $2, step: $4
o 'TO Expression BY Expression WHEN Expression', -> to: $2, step: $4, guard: $6
# An array or range comprehension has variables for the current element
# and (optional) reference to the current index. Or, *key, value*, in the case
# of object comprehensions.
ForVariables: [
o 'ForValue', -> [$1]
o 'ForValue , ForValue', -> [$1, $3]
]
# The source of a comprehension is an array or object with an optional guard
# clause. If it's an array comprehension, you can also choose to step through
# in fixed-size increments.
ForBody: [
o 'FOR ForValue ForIn', -> extend $3, name: $2
o 'FOR ForValue , Identifier ForIn', -> extend $5, name: $2, index: $4
o 'FOR Identifier ForOf', -> extend $3, index: $2
o 'FOR ForValue , ForValue ForOf', -> extend $5, index: $2, name: $4
o 'FOR ALL Identifier ForOf', -> extend $4, raw: on, index: $3
o 'FOR ALL Identifier , ForValue ForOf', -> extend $6, raw: on, index: $3, name: $5
o 'FOR Identifier FROM Expression ForTo', -> extend $5, index: $2, from: $4
ForSource: [
o 'FORIN Expression', -> source: $2
o 'FOROF Expression', -> source: $2, object: yes
o 'FORIN Expression WHEN Expression', -> source: $2, guard: $4
o 'FOROF Expression WHEN Expression', -> source: $2, guard: $4, object: yes
o 'FORIN Expression BY Expression', -> source: $2, step: $4
o 'FORIN Expression WHEN Expression BY Expression', -> source: $2, guard: $4, step: $6
o 'FORIN Expression BY Expression WHEN Expression', -> source: $2, step: $4, guard: $6
]
Switch: [
@ -545,7 +563,7 @@ operators = [
['left', 'LOGIC']
['nonassoc', 'INDENT', 'OUTDENT']
['right', '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS']
['right', 'FORIN', 'FOROF', 'FROM', 'TO', 'BY', 'WHEN']
['right', 'FORIN', 'FOROF', 'BY', 'WHEN']
['right', 'IF', 'UNLESS', 'ELSE', 'FOR', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS']
['right', 'POST_IF', 'POST_UNLESS']
]

View file

@ -41,7 +41,6 @@ exports.Lexer = class Lexer
@outdebt = 0 # The under-outdentation at the current level.
@indents = [] # The stack of all current indentation levels.
@tokens = [] # Stream of parsed tokens in the form `['TYPE', value, line]`.
@seenFor = @seenFrom = no # Flags for distinguishing `FORIN/FOROF/FROM/TO`.
# At every position, run through this list of attempted matches,
# short-circuiting if any of them succeed. Their order determines precedence:
@ -79,15 +78,6 @@ exports.Lexer = class Lexer
if id is 'all' and @tag() is 'FOR'
@token 'ALL', id
return id.length
if id is 'from' and @tag(1) is 'FOR'
@seenFor = no
@seenFrom = yes
@token 'FROM', id
return id.length
if id is 'to' and @seenFrom
@seenFrom = no
@token 'TO', id
return id.length
forcedIdentifier = colon or
(prev = last @tokens) and not prev.spaced and prev[0] in ['.', '?.', '@', '::']
tag = 'IDENTIFIER'
@ -399,7 +389,7 @@ exports.Lexer = class Lexer
# interpolations within strings, ad infinitum.
balancedString: (str, delimited, options = {}) ->
stack = [delimited[0]]
for i from 1 to str.length - 1
for i in [1...str.length]
switch str.charAt i
when '\\'
i++
@ -552,7 +542,7 @@ OPERATOR = /// ^ (
| ([-+:])\1 # doubles
| ([&|<>])\2=? # logic / shift
| \?\. # soak access
| \.{3} # splat
| \.{2,3} # range or splat
) ///
WHITESPACE = /^[^\n\S]+/

View file

@ -186,9 +186,12 @@ exports.Expressions = class Expressions extends Base
# An Expressions node does not return its entire body, rather it
# ensures that the final expression is returned.
makeReturn: ->
for end, idx in @expressions by -1 when end not instanceof Comment
@expressions[idx] = end.makeReturn()
break
len = @expressions.length
while len--
expr = @expressions[len]
if expr not instanceof Comment
@expressions[len] = expr.makeReturn()
break
this
# An **Expressions** is the only node that can serve as the root.
@ -342,6 +345,9 @@ exports.Value = class Value extends Base
return no if @properties.length
(@base instanceof Obj) and (not onlyGenerated or @base.generated)
isSplice: ->
last(@properties) instanceof Slice
makeReturn: ->
if @properties.length then super() else @base.makeReturn()
@ -559,6 +565,95 @@ exports.Index = class Index extends Base
isComplex: ->
@index.isComplex()
#### Range
# A range literal. Ranges can be used to extract portions (slices) of arrays,
# to specify a range for comprehensions, or as a value, to be expanded into the
# corresponding array of integers at runtime.
exports.Range = class Range extends Base
children: ['from', 'to']
constructor: (@from, @to, tag) ->
@exclusive = tag is 'exclusive'
@equals = if @exclusive then '' else '='
# Compiles the range's source variables -- where it starts and where it ends.
# But only if they need to be cached to avoid double evaluation.
compileVariables: (o) ->
o = merge(o, top: true)
[@from, @fromVar] = @from.cache o, LEVEL_LIST
[@to, @toVar] = @to.cache o, LEVEL_LIST
[@fromNum, @toNum] = [@fromVar.match(SIMPLENUM), @toVar.match(SIMPLENUM)]
parts = []
parts.push @from if @from isnt @fromVar
parts.push @to if @to isnt @toVar
# When compiled normally, the range returns the contents of the *for loop*
# needed to iterate over the values in the range. Used by comprehensions.
compileNode: (o) ->
@compileVariables o
return @compileArray(o) unless o.index
return @compileSimple(o) if @fromNum and @toNum
idx = del o, 'index'
step = del o, 'step'
vars = "#{idx} = #{@from}" + if @to isnt @toVar then ", #{@to}" else ''
intro = "(#{@fromVar} <= #{@toVar} ? #{idx}"
compare = "#{intro} <#{@equals} #{@toVar} : #{idx} >#{@equals} #{@toVar})"
stepPart = if step then step.compile(o) else '1'
incr = if step then "#{idx} += #{stepPart}" else "#{intro} += #{stepPart} : #{idx} -= #{stepPart})"
"#{vars}; #{compare}; #{incr}"
# Compile a simple range comprehension, with integers.
compileSimple: (o) ->
[from, to] = [+@fromNum, +@toNum]
idx = del o, 'index'
step = del o, 'step'
step and= "#{idx} += #{step.compile(o)}"
if from <= to
"#{idx} = #{from}; #{idx} <#{@equals} #{to}; #{step or "#{idx}++"}"
else
"#{idx} = #{from}; #{idx} >#{@equals} #{to}; #{step or "#{idx}--"}"
# When used as a value, expand the range into the equivalent array.
compileArray: (o) ->
if @fromNum and @toNum and Math.abs(@fromNum - @toNum) <= 20
range = [+@fromNum..+@toNum]
range.pop() if @exclusive
return "[#{ range.join(', ') }]"
idt = @tab + TAB
i = o.scope.freeVariable 'i'
result = o.scope.freeVariable 'results'
pre = "\n#{idt}#{result} = [];"
if @fromNum and @toNum
o.index = i
body = @compileSimple o
else
vars = "#{i} = #{@from}" + if @to isnt @toVar then ", #{@to}" else ''
clause = "#{@fromVar} <= #{@toVar} ?"
body = "var #{vars}; #{clause} #{i} <#{@equals} #{@toVar} : #{i} >#{@equals} #{@toVar}; #{clause} #{i} += 1 : #{i} -= 1"
post = "{ #{result}.push(#{i}); }\n#{idt}return #{result};\n#{o.indent}"
"(function() {#{pre}\n#{idt}for (#{body})#{post}}).call(this)"
#### Slice
# An array slice literal. Unlike JavaScript's `Array#slice`, the second parameter
# specifies the index of the end of the slice, just as the first parameter
# is the index of the beginning.
exports.Slice = class Slice extends Base
children: ['range']
constructor: (@range) ->
super()
compileNode: (o) ->
from = if @range.from then @range.from.compile(o) else '0'
to = if @range.to then @range.to.compile(o) else ''
to += if not to or @range.exclusive then '' else ' + 1'
to = ', ' + to if to
".slice(#{from}#{to})"
#### Obj
# An object literal, nothing fancy.
@ -866,6 +961,25 @@ exports.Assign = class Assign extends Base
[left, rite] = @variable.cacheReference o
new Op(@context.slice(0, -1), left, new Assign(rite, @value, '=')).compile o
# Compile the assignment from an array splice literal, using JavaScript's
# `Array#splice` method.
compileSplice: (o) ->
{range} = @variable.properties.pop()
name = @variable.compile o
plus = if range.exclusive then '' else ' + 1'
from = if range.from then range.from.compile(o) else '0'
to = if range.to then range.to.compile(o) + ' - ' + from + plus else "#{name}.length"
ref = o.scope.freeVariable 'ref'
val = @value.compile(o)
"([].splice.apply(#{name}, [#{from}, #{to}].concat(#{ref} = #{val})), #{ref})"
# When compiling a conditional assignment, take care to ensure that the
# operands are only evaluated once, even though we have to reference them
# more than once.
compileConditional: (o) ->
[left, rite] = @variable.cacheReference o
return new Op(@context.slice(0, -1), left, new Assign(rite, @value)).compile o
#### Code
# A function definition. This is the only node that creates a new Scope.
@ -1280,15 +1394,19 @@ exports.Parens = class Parens extends Base
# 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
constructor: (body, head) ->
if head.index instanceof Value
throw SyntaxError 'index cannot be a pattern matching expression'
extend this, head
constructor: (body, source, @name, @index) ->
{@source, @guard, @step} = source
@body = Expressions.wrap [body]
@raw = !!source.raw
@object = !!source.object
[@name, @index] = [@index, @name] if @object
throw SyntaxError 'index cannot be a pattern matching expression' if @index instanceof Value
@range = @source instanceof Value and @source.base instanceof Range and not @source.properties.length
@pattern = @name instanceof Value
throw SyntaxError 'cannot pattern match a range loop' if @range and @pattern
@returns = false
children: ['body', 'source', 'guard', 'step', 'from', 'to']
children: ['body', 'source', 'guard', 'step']
isStatement: YES
@ -1298,91 +1416,60 @@ exports.For = class For extends Base
containsPureStatement: While::containsPureStatement
compileReturnValue: (val, o) ->
return '\n' + new Return(new Literal val).compile o if @returns
return '\n' + val if val
''
# 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
# some cannot.
compileNode: (o) ->
{scope} = o
{body} = this
hasCode = @body.contains (node) -> node instanceof Code
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 = ''
idt = o.indent + TAB
unless hasCode
scope.find(name, yes) if name
scope.find(index, yes) if index
[step, pvar] = @step.compileLoopReference o, 'step' if @step
if @from
[head, fvar] = @from.compileLoopReference o, 'from'
[tail, tvar] = @to.compileLoopReference o, 'to'
vars = ivar + ' = ' + head
vars += ', ' + tail if tail isnt tvar
if SIMPLENUM.test(head) and SIMPLENUM.test(tail)
if +head <= +tail
cond = "#{ivar} <= #{tail}"
else
pvar or= -1
cond = "#{ivar} >= #{tail}"
else
if +pvar
cond = "#{ivar} #{ if pvar < 0 then '>' else '<' }= #{tvar}"
else
intro = "#{fvar} <= #{tvar} ? #{ivar}"
cond = "#{intro} <= #{tvar} : #{ivar} >= #{tvar}"
incr = if pvar then "#{ivar} += #{pvar}" else "#{intro}++ : #{ivar}--"
source = if @range then @source.base else @source
scope = o.scope
name = @name and @name.compile o, LEVEL_LIST
index = @index and @index.compile o, LEVEL_LIST
scope.find(name, immediate: yes) if name and not @pattern
scope.find(index, immediate: yes) if index
rvar = scope.freeVariable 'results' if @returns
ivar = (if @range then name else index) or scope.freeVariable 'i'
varPart = ''
guardPart = ''
unstepPart = ''
body = Expressions.wrap [@body]
idt1 = @tab + TAB
if @range
forPart = source.compile merge(o, {index: ivar, @step})
else
if name or @object and not @raw
[sourcePart, svar] = @source.compileLoopReference o, 'ref'
else
sourcePart = svar = @source.compile o, LEVEL_PAREN
svar = @source.compile o, LEVEL_TOP
if (name or not @raw) and
not (IDENTIFIER.test(svar) and scope.check svar, immediate: on)
sourcePart = "#{@tab}#{ref = scope.freeVariable 'ref'} = #{svar};\n"
svar = ref
namePart = if @pattern
new Assign(@name, new Literal "#{svar}[#{ivar}]").compile o, LEVEL_TOP
else if name
"#{name} = #{svar}[#{ivar}]"
unless @object
if 0 > pvar and (pvar | 0) is +pvar # negative int
vars = "#{ivar} = #{svar}.length - 1"
cond = "#{ivar} >= 0"
else
lvar = scope.freeVariable 'len'
vars = "#{ivar} = 0, #{lvar} = #{svar}.length"
cond = "#{ivar} < #{lvar}"
lvar = scope.freeVariable 'len'
stepPart = if @step then "#{ivar} += #{ @step.compile(o, LEVEL_OP) }" else "#{ivar}++"
forPart = "#{ivar} = 0, #{lvar} = #{svar}.length; #{ivar} < #{lvar}; #{stepPart}"
if @returns
resultPart = "#{@tab}#{rvar} = [];\n"
returnResult = '\n' + (new Return(new Literal(rvar)).compile o, LEVEL_PAREN)
body = Push.wrap rvar, body if @returns
if @guard
body = Expressions.wrap [new If @guard, body]
varPart = "#{idt1}#{namePart};\n" if namePart
if forPart and name is ivar
unstepPart = if @step then "#{name} -= #{ @step.compile(o) };" else "#{name}--;"
unstepPart = "\n#{@tab}" + unstepPart
if @object
forPart = ivar + ' in ' + sourcePart
guardPart = if @raw then '' else
idt + "if (!#{ utility 'hasProp' }.call(#{svar}, #{ivar})) continue;\n"
else
pvar or= 1
vars += ', ' + step if step and (step isnt pvar)
defPart = @tab + sourcePart + ';\n' if svar isnt sourcePart
forPart = vars + "; #{cond}; " + (incr or (ivar + switch +pvar
when 1 then '++'
when -1 then '--'
else (if pvar < 0 then ' -= ' + pvar.slice 1 else ' += ' + pvar)
))
body = Closure.wrap(body, yes) if hasCode
varPart = idt + namePart + ';\n' if namePart
defPart += @pluckDirectCall o, body, name, index unless @pattern
code = guardPart + varPart
unless body.isEmpty()
if o.level > LEVEL_TOP or @returns
rvar = scope.freeVariable 'results'
defPart += @tab + rvar + ' = [];\n'
retPart = @compileReturnValue rvar, o
body = Push.wrap rvar, body
body = Expressions.wrap [new If @guard, body] if @guard
o.indent = idt
code += body.compile o, LEVEL_TOP
code = '\n' + code + '\n' + @tab if code
defPart + @tab + "for (#{forPart}) {#{code}}" + retPart
forPart = "#{ivar} in #{svar}"
guardPart = "\n#{idt1}if (!#{utility('hasProp')}.call(#{svar}, #{ivar})) continue;" unless @raw
body = body.compile merge o, indent: idt1, top: true
vars = if @range then name else "#{name}, #{ivar}"
"""
#{sourcePart or ''}#{resultPart or ''}#{@tab}for (#{forPart}) {#{guardPart}
#{varPart}#{body}
#{@tab}}#{unstepPart}#{returnResult or ''}
"""
pluckDirectCall: (o, body, name, index) ->
defs = ''
@ -1526,8 +1613,7 @@ exports.If = class If extends Base
Push =
wrap: (name, exps) ->
return exps if exps.isEmpty() or last(exps.expressions).containsPureStatement()
exps.push new Call \
new Value(new Literal(name), [new Accessor new Literal 'push']), [exps.pop()]
exps.push new Call new Value(new Literal(name), [new Accessor new Literal 'push']), [exps.pop()]
#### Closure