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:
parent
9f708ad0c8
commit
31f4214b5d
13 changed files with 708 additions and 562 deletions
|
@ -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(' ') : '';
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
151
lib/grammar.js
151
lib/grammar.js
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
32
lib/lexer.js
32
lib/lexer.js
|
@ -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 = /^[-=]>/;
|
||||
|
|
350
lib/nodes.js
350
lib/nodes.js
|
@ -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';
|
||||
|
|
360
lib/parser.js
360
lib/parser.js
File diff suppressed because one or more lines are too long
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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']
|
||||
]
|
||||
|
|
|
@ -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]+/
|
||||
|
|
252
src/nodes.coffee
252
src/nodes.coffee
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue