merging satyr/defarg
This commit is contained in:
commit
c17efcf422
|
@ -6,9 +6,8 @@
|
|||
return eval(CoffeeScript.compile(code, options));
|
||||
};
|
||||
CoffeeScript.run = function(code, options) {
|
||||
if (options != null) {
|
||||
options.bare = true;
|
||||
}
|
||||
options != null ? options : options = {};
|
||||
options.bare = true;
|
||||
return Function(CoffeeScript.compile(code, options))();
|
||||
};
|
||||
if (!(typeof window !== "undefined" && window !== null)) {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
exports.VERSION = '0.9.4';
|
||||
exports.helpers = require('./helpers');
|
||||
exports.compile = compile = function(code, options) {
|
||||
options || (options = {});
|
||||
options != null ? options : options = {};
|
||||
try {
|
||||
return (parser.parse(lexer.tokenize(code))).compile(options);
|
||||
} catch (err) {
|
||||
|
|
|
@ -124,16 +124,15 @@
|
|||
})
|
||||
],
|
||||
Param: [
|
||||
o('PARAM', function() {
|
||||
return new Literal($1);
|
||||
}), o('PARAM ...', function() {
|
||||
return new Param($1, false, true);
|
||||
}), o('@ PARAM', function() {
|
||||
return new Param($2, true);
|
||||
}), o('@ PARAM ...', function() {
|
||||
return new Param($2, true, true);
|
||||
o('ParamVar', function() {
|
||||
return new Param($1);
|
||||
}), o('ParamVar ...', function() {
|
||||
return new Param($1, null, true);
|
||||
}), o('ParamVar = Expression', function() {
|
||||
return new Param($1, $3);
|
||||
})
|
||||
],
|
||||
ParamVar: [o('Identifier'), o('ThisProperty'), o('Array'), o('Object')],
|
||||
Splat: [
|
||||
o('Expression ...', function() {
|
||||
return new Splat($1);
|
||||
|
|
31
lib/lexer.js
31
lib/lexer.js
|
@ -402,23 +402,27 @@
|
|||
return doc;
|
||||
};
|
||||
Lexer.prototype.tagParameters = function() {
|
||||
var i, tok;
|
||||
var i, stack, tok, tokens;
|
||||
if (this.tag() !== ')') {
|
||||
return this;
|
||||
}
|
||||
i = this.tokens.length;
|
||||
while (tok = this.tokens[--i]) {
|
||||
stack = [];
|
||||
tokens = this.tokens;
|
||||
i = tokens.length;
|
||||
tokens[--i][0] = 'PARAM_END';
|
||||
while (tok = tokens[--i]) {
|
||||
switch (tok[0]) {
|
||||
case 'IDENTIFIER':
|
||||
tok[0] = 'PARAM';
|
||||
break;
|
||||
case ')':
|
||||
tok[0] = 'PARAM_END';
|
||||
stack.push(tok);
|
||||
break;
|
||||
case '(':
|
||||
case 'CALL_START':
|
||||
tok[0] = 'PARAM_START';
|
||||
return true;
|
||||
if (stack.length) {
|
||||
stack.pop();
|
||||
} else {
|
||||
tok[0] = 'PARAM_START';
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
|
@ -434,7 +438,7 @@
|
|||
};
|
||||
Lexer.prototype.balancedString = function(str, delimited, options) {
|
||||
var _i, _len, close, i, levels, open, pair, slen;
|
||||
options || (options = {});
|
||||
options != null ? options : options = {};
|
||||
levels = [];
|
||||
i = 0;
|
||||
slen = str.length;
|
||||
|
@ -471,8 +475,9 @@
|
|||
return i && str.slice(0, i);
|
||||
};
|
||||
Lexer.prototype.interpolateString = function(str, options) {
|
||||
var _len, _ref2, _ref3, _this, expr, heredoc, i, inner, interpolated, letter, nested, pi, regex, tag, tokens, value;
|
||||
_ref2 = options || (options = {}), heredoc = _ref2.heredoc, regex = _ref2.regex;
|
||||
var _len, _ref2, _this, expr, heredoc, i, inner, interpolated, letter, nested, pi, regex, tag, tokens, value;
|
||||
options != null ? options : options = {};
|
||||
heredoc = options.heredoc, regex = options.regex;
|
||||
tokens = [];
|
||||
pi = 0;
|
||||
i = -1;
|
||||
|
@ -519,7 +524,7 @@
|
|||
this.token('(', '(');
|
||||
}
|
||||
for (i = 0, _len = tokens.length; i < _len; i++) {
|
||||
_ref3 = tokens[i], tag = _ref3[0], value = _ref3[1];
|
||||
_ref2 = tokens[i], tag = _ref2[0], value = _ref2[1];
|
||||
if (i) {
|
||||
this.token('+', '+');
|
||||
}
|
||||
|
|
221
lib/nodes.js
221
lib/nodes.js
|
@ -96,7 +96,7 @@
|
|||
};
|
||||
Base.prototype.toString = function(idt, override) {
|
||||
var _i, _len, _ref2, _result, child, children, klass;
|
||||
idt || (idt = '');
|
||||
idt != null ? idt : idt = '';
|
||||
children = ((function() {
|
||||
_ref2 = this.collectChildren();
|
||||
_result = [];
|
||||
|
@ -209,7 +209,7 @@
|
|||
return this;
|
||||
};
|
||||
Expressions.prototype.compile = function(o, level) {
|
||||
o || (o = {});
|
||||
o != null ? o : o = {};
|
||||
return o.scope ? Expressions.__super__.compile.call(this, o, level) : this.compileRoot(o);
|
||||
};
|
||||
Expressions.prototype.compileNode = function(o) {
|
||||
|
@ -464,13 +464,12 @@
|
|||
exports.Call = (function() {
|
||||
Call = (function() {
|
||||
function Call(variable, _arg, _arg2) {
|
||||
this.args = _arg != null ? _arg : [];
|
||||
this.soak = _arg2;
|
||||
this.args = _arg;
|
||||
Call.__super__.constructor.call(this);
|
||||
this.isNew = false;
|
||||
this.isSuper = variable === 'super';
|
||||
this.variable = this.isSuper ? null : variable;
|
||||
this.args || (this.args = []);
|
||||
return this;
|
||||
}
|
||||
return Call;
|
||||
|
@ -595,8 +594,8 @@
|
|||
exports.Extends = (function() {
|
||||
Extends = (function() {
|
||||
function Extends(_arg, _arg2) {
|
||||
this.parent = _arg2;
|
||||
this.child = _arg;
|
||||
this.parent = _arg2;
|
||||
Extends.__super__.constructor.call(this);
|
||||
return this;
|
||||
}
|
||||
|
@ -792,8 +791,8 @@
|
|||
exports.Class = (function() {
|
||||
Class = (function() {
|
||||
function Class(_arg, _arg2, props) {
|
||||
this.parent = _arg2;
|
||||
this.variable = _arg;
|
||||
this.parent = _arg2;
|
||||
Class.__super__.constructor.call(this);
|
||||
this.properties = props || [];
|
||||
this.returns = false;
|
||||
|
@ -892,9 +891,9 @@
|
|||
exports.Assign = (function() {
|
||||
Assign = (function() {
|
||||
function Assign(_arg, _arg2, _arg3) {
|
||||
this.context = _arg3;
|
||||
this.value = _arg2;
|
||||
this.variable = _arg;
|
||||
this.value = _arg2;
|
||||
this.context = _arg3;
|
||||
Assign.__super__.constructor.call(this);
|
||||
return this;
|
||||
}
|
||||
|
@ -938,15 +937,15 @@
|
|||
return o.level <= LEVEL_LIST ? val : "(" + val + ")";
|
||||
};
|
||||
Assign.prototype.compilePatternMatch = function(o) {
|
||||
var _len, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, acc, assigns, code, i, idx, isObject, obj, objects, olength, ref, splat, top, val, valVar, value;
|
||||
var _len, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, acc, assigns, code, i, idx, isObject, ivar, obj, objects, olen, ref, rest, splat, top, val, value, vvar;
|
||||
top = o.level === LEVEL_TOP;
|
||||
value = this.value;
|
||||
objects = this.variable.base.objects;
|
||||
if (!(olength = objects.length)) {
|
||||
if (!(olen = objects.length)) {
|
||||
return value.compile(o);
|
||||
}
|
||||
isObject = this.variable.isObject();
|
||||
if (top && olength === 1 && !((obj = objects[0]) instanceof Splat)) {
|
||||
if (top && olen === 1 && !((obj = objects[0]) instanceof Splat)) {
|
||||
if (obj instanceof Assign) {
|
||||
_ref2 = obj, (_ref3 = _ref2.variable, idx = _ref3.base, _ref3), obj = _ref2.value;
|
||||
} else {
|
||||
|
@ -961,12 +960,12 @@
|
|||
value.properties.push(new (acc ? Accessor : Index)(idx));
|
||||
return new Assign(obj, value).compile(o);
|
||||
}
|
||||
valVar = value.compile(o, LEVEL_LIST);
|
||||
vvar = value.compile(o, LEVEL_LIST);
|
||||
assigns = [];
|
||||
splat = false;
|
||||
if (!IDENTIFIER.test(valVar) || this.variable.assigns(valVar)) {
|
||||
assigns.push("" + (ref = o.scope.freeVariable('ref')) + " = " + valVar);
|
||||
valVar = ref;
|
||||
if (!IDENTIFIER.test(vvar) || this.variable.assigns(vvar)) {
|
||||
assigns.push("" + (ref = o.scope.freeVariable('ref')) + " = " + vvar);
|
||||
vvar = ref;
|
||||
}
|
||||
for (i = 0, _len = objects.length; i < _len; i++) {
|
||||
obj = objects[i];
|
||||
|
@ -983,21 +982,32 @@
|
|||
}
|
||||
}
|
||||
if (!splat && obj instanceof Splat) {
|
||||
val = new Literal(obj.compileValue(o, valVar, i, olength - i - 1));
|
||||
splat = true;
|
||||
val = "" + olen + " <= " + vvar + ".length ? " + (utility('slice')) + ".call(" + vvar + ", " + i;
|
||||
if (rest = olen - i - 1) {
|
||||
ivar = o.scope.freeVariable('i');
|
||||
val += ", " + ivar + " = " + vvar + ".length - " + rest + ") : (" + ivar + " = " + i + ", [])";
|
||||
} else {
|
||||
val += ") : []";
|
||||
}
|
||||
val = new Literal(val);
|
||||
splat = "" + ivar + "++";
|
||||
} else {
|
||||
if (typeof idx !== 'object') {
|
||||
idx = new Literal(splat ? "" + valVar + ".length - " + (olength - idx) : idx);
|
||||
if (obj instanceof Splat) {
|
||||
obj = obj.name.compile(o);
|
||||
throw SyntaxError("multiple splats are disallowed in an assignment: " + obj + " ...");
|
||||
}
|
||||
if (typeof idx === 'number') {
|
||||
idx = new Literal(splat || idx);
|
||||
acc = false;
|
||||
} else {
|
||||
acc = isObject && IDENTIFIER.test(idx.unwrap().value || 0);
|
||||
}
|
||||
val = new Value(new Literal(valVar), [new (acc ? Accessor : Index)(idx)]);
|
||||
val = new Value(new Literal(vvar), [new (acc ? Accessor : Index)(idx)]);
|
||||
}
|
||||
assigns.push(new Assign(obj, val).compile(o, LEVEL_LIST));
|
||||
}
|
||||
if (!top) {
|
||||
assigns.push(valVar);
|
||||
assigns.push(vvar);
|
||||
}
|
||||
code = assigns.join(', ');
|
||||
return o.level < LEVEL_LIST ? code : "(" + code + ")";
|
||||
|
@ -1011,10 +1021,10 @@
|
|||
})();
|
||||
exports.Code = (function() {
|
||||
Code = (function() {
|
||||
function Code(params, body, tag) {
|
||||
function Code(_arg, _arg2, tag) {
|
||||
this.params = _arg != null ? _arg : [];
|
||||
this.body = _arg2 != null ? _arg2 : new Expressions;
|
||||
Code.__super__.constructor.call(this);
|
||||
this.params = params || [];
|
||||
this.body = body || new Expressions;
|
||||
this.bound = tag === 'boundfunc';
|
||||
if (this.bound) {
|
||||
this.context = 'this';
|
||||
|
@ -1026,68 +1036,77 @@
|
|||
__extends(Code, Base);
|
||||
Code.prototype.children = ['params', 'body'];
|
||||
Code.prototype.compileNode = function(o) {
|
||||
var _i, _len, _len2, _ref2, _ref3, _result, close, code, comm, empty, func, i, idt, open, param, params, scope, sharedScope, splat, value;
|
||||
var _i, _j, _len, _len2, _len3, _ref2, _ref3, _result, _this, close, code, comm, exprs, func, i, idt, open, p, param, ref, scope, sharedScope, splats, v, vars, wasEmpty;
|
||||
sharedScope = del(o, 'sharedScope');
|
||||
o.scope = scope = sharedScope || new Scope(o.scope, this.body, this);
|
||||
o.indent = this.idt(1);
|
||||
empty = this.body.expressions.length === 0;
|
||||
delete o.bare;
|
||||
delete o.globals;
|
||||
splat = void 0;
|
||||
params = [];
|
||||
vars = [];
|
||||
exprs = [];
|
||||
_ref2 = this.params;
|
||||
for (i = 0, _len = _ref2.length; i < _len; i++) {
|
||||
param = _ref2[i];
|
||||
if (splat) {
|
||||
if (param.attach) {
|
||||
param.assign = new Assign(new Value(new Literal('this'), [new Accessor(param.value)]));
|
||||
this.body.expressions.splice(splat.index + 1, 0, param.assign);
|
||||
}
|
||||
splat.trailings.push(param);
|
||||
for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
|
||||
param = _ref2[_i];
|
||||
if (param.splat) {
|
||||
splats = new Assign(new Value(new Arr((function() {
|
||||
_ref3 = this.params;
|
||||
_result = [];
|
||||
for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) {
|
||||
p = _ref3[_j];
|
||||
_result.push(p.asReference(o));
|
||||
}
|
||||
return _result;
|
||||
}).call(this))), new Value(new Literal('arguments')));
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ref3 = this.params;
|
||||
for (_j = 0, _len2 = _ref3.length; _j < _len2; _j++) {
|
||||
param = _ref3[_j];
|
||||
if (param.isComplex()) {
|
||||
ref = param.asReference(o);
|
||||
exprs.push(new Assign(new Value(param.name), param.value ? new Op('?', ref, param.value) : ref));
|
||||
} else {
|
||||
if (param.attach) {
|
||||
value = param.value;
|
||||
_ref3 = [new Literal(scope.freeVariable('arg')), param.splat], param = _ref3[0], param.splat = _ref3[1];
|
||||
this.body.unshift(new Assign(new Value(new Literal('this'), [new Accessor(value)]), param));
|
||||
}
|
||||
if (param.splat) {
|
||||
splat = new Splat(param.value);
|
||||
splat.index = i;
|
||||
splat.trailings = [];
|
||||
splat.arglength = this.params.length;
|
||||
this.body.unshift(splat);
|
||||
} else {
|
||||
params.push(param);
|
||||
ref = param;
|
||||
if (param.value) {
|
||||
exprs.push(new Assign(new Value(param.name), param.value, '?='));
|
||||
}
|
||||
}
|
||||
if (!splats) {
|
||||
vars.push(ref);
|
||||
}
|
||||
}
|
||||
scope.startLevel();
|
||||
if (!(empty || this.noReturn)) {
|
||||
wasEmpty = this.body.isEmpty();
|
||||
if (splats) {
|
||||
exprs.unshift(splats);
|
||||
}
|
||||
if (exprs.length) {
|
||||
(_this = this.body.expressions).splice.apply(_this, [0, 0].concat(exprs));
|
||||
}
|
||||
if (!(wasEmpty || this.noReturn)) {
|
||||
this.body.makeReturn();
|
||||
}
|
||||
params = (function() {
|
||||
_result = [];
|
||||
for (_i = 0, _len2 = params.length; _i < _len2; _i++) {
|
||||
param = params[_i];
|
||||
scope.parameter(param = param.compile(o));
|
||||
_result.push(param);
|
||||
if (!splats) {
|
||||
for (i = 0, _len3 = vars.length; i < _len3; i++) {
|
||||
v = vars[i];
|
||||
scope.parameter(vars[i] = v.compile(o));
|
||||
}
|
||||
return _result;
|
||||
})();
|
||||
}
|
||||
comm = this.comment ? this.comment.compile(o) + '\n' : '';
|
||||
if (this.className) {
|
||||
o.indent = this.idt(2);
|
||||
}
|
||||
idt = this.idt(1);
|
||||
code = this.body.expressions.length ? "\n" + (this.body.compileWithDeclarations(o)) + "\n" : '';
|
||||
code = this.body.isEmpty() ? '' : "\n" + (this.body.compileWithDeclarations(o)) + "\n";
|
||||
if (this.className) {
|
||||
open = "(function() {\n" + comm + idt + "function " + this.className + "(";
|
||||
close = "" + (code && idt) + "}\n" + idt + "return " + this.className + ";\n" + this.tab + "})()";
|
||||
} else {
|
||||
open = "function(";
|
||||
open = 'function(';
|
||||
close = "" + (code && this.tab) + "}";
|
||||
}
|
||||
func = "" + open + (params.join(', ')) + ") {" + code + close;
|
||||
func = "" + open + (vars.join(', ')) + ") {" + code + close;
|
||||
scope.endLevel();
|
||||
if (this.bound) {
|
||||
return "" + (utility('bind')) + "(" + func + ", " + this.context + ")";
|
||||
|
@ -1101,30 +1120,34 @@
|
|||
})();
|
||||
exports.Param = (function() {
|
||||
Param = (function() {
|
||||
function Param(name, _arg, _arg2) {
|
||||
this.splat = _arg2;
|
||||
this.attach = _arg;
|
||||
function Param(_arg, _arg2, _arg3) {
|
||||
this.name = _arg;
|
||||
this.value = _arg2;
|
||||
this.splat = _arg3;
|
||||
Param.__super__.constructor.call(this);
|
||||
this.value = new Literal(this.name = name);
|
||||
return this;
|
||||
}
|
||||
return Param;
|
||||
})();
|
||||
__extends(Param, Base);
|
||||
Param.prototype.children = ['name'];
|
||||
Param.prototype.children = ['name', 'value'];
|
||||
Param.prototype.compile = function(o) {
|
||||
return this.value.compile(o, LEVEL_LIST);
|
||||
return this.name.compile(o, LEVEL_LIST);
|
||||
};
|
||||
Param.prototype.toString = function() {
|
||||
var name;
|
||||
name = this.name;
|
||||
if (this.attach) {
|
||||
name = '@' + name;
|
||||
Param.prototype.asReference = function(o) {
|
||||
var node;
|
||||
if (this.reference) {
|
||||
return this.reference;
|
||||
}
|
||||
node = this.isComplex() ? new Literal(o.scope.freeVariable('arg')) : this.name;
|
||||
node = new Value(node);
|
||||
if (this.splat) {
|
||||
name += '...';
|
||||
node = new Splat(node);
|
||||
}
|
||||
return new Literal(name).toString();
|
||||
return this.reference = node;
|
||||
};
|
||||
Param.prototype.isComplex = function() {
|
||||
return this.name.isComplex();
|
||||
};
|
||||
return Param;
|
||||
})();
|
||||
|
@ -1146,36 +1169,6 @@
|
|||
Splat.prototype.compile = function(o) {
|
||||
return this.index != null ? this.compileParam(o) : this.name.compile(o);
|
||||
};
|
||||
Splat.prototype.compileParam = function(o) {
|
||||
var _len, _ref2, assign, end, idx, len, name, pos, trailing, variadic;
|
||||
name = this.name.compile(o);
|
||||
o.scope.find(name);
|
||||
end = '';
|
||||
if (this.trailings.length) {
|
||||
len = o.scope.freeVariable('len');
|
||||
o.scope.assign(len, 'arguments.length');
|
||||
variadic = o.scope.freeVariable('result');
|
||||
o.scope.assign(variadic, len + ' >= ' + this.arglength);
|
||||
end = this.trailings.length ? ", " + len + " - " + this.trailings.length : void 0;
|
||||
_ref2 = this.trailings;
|
||||
for (idx = 0, _len = _ref2.length; idx < _len; idx++) {
|
||||
trailing = _ref2[idx];
|
||||
if (trailing.attach) {
|
||||
assign = trailing.assign;
|
||||
trailing = new Literal(o.scope.freeVariable('arg'));
|
||||
assign.value = trailing;
|
||||
}
|
||||
pos = this.trailings.length - idx;
|
||||
o.scope.assign(trailing.compile(o), "arguments[" + variadic + " ? " + len + " - " + pos + " : " + (this.index + idx) + "]");
|
||||
}
|
||||
}
|
||||
return "" + name + " = " + (utility('slice')) + ".call(arguments, " + this.index + end + ")";
|
||||
};
|
||||
Splat.prototype.compileValue = function(o, name, index, trailings) {
|
||||
var trail;
|
||||
trail = trailings ? ', -' + trailings : '';
|
||||
return utility('slice') + (".call(" + name + ", " + index + trail + ")");
|
||||
};
|
||||
Splat.compileSplattedArray = function(list, o) {
|
||||
var _len, arg, args, code, end, i, prev;
|
||||
args = [];
|
||||
|
@ -1357,8 +1350,8 @@
|
|||
exports.In = (function() {
|
||||
In = (function() {
|
||||
function In(_arg, _arg2) {
|
||||
this.array = _arg2;
|
||||
this.object = _arg;
|
||||
this.array = _arg2;
|
||||
In.__super__.constructor.call(this);
|
||||
return this;
|
||||
}
|
||||
|
@ -1407,10 +1400,10 @@
|
|||
exports.Try = (function() {
|
||||
Try = (function() {
|
||||
function Try(_arg, _arg2, _arg3, _arg4) {
|
||||
this.ensure = _arg4;
|
||||
this.recovery = _arg3;
|
||||
this.error = _arg2;
|
||||
this.attempt = _arg;
|
||||
this.error = _arg2;
|
||||
this.recovery = _arg3;
|
||||
this.ensure = _arg4;
|
||||
Try.__super__.constructor.call(this);
|
||||
return this;
|
||||
}
|
||||
|
@ -1636,9 +1629,9 @@
|
|||
exports.Switch = (function() {
|
||||
Switch = (function() {
|
||||
function Switch(_arg, _arg2, _arg3) {
|
||||
this.otherwise = _arg3;
|
||||
this.cases = _arg2;
|
||||
this.subject = _arg;
|
||||
this.cases = _arg2;
|
||||
this.otherwise = _arg3;
|
||||
Switch.__super__.constructor.call(this);
|
||||
return this;
|
||||
}
|
||||
|
@ -1700,13 +1693,13 @@
|
|||
})();
|
||||
exports.If = (function() {
|
||||
If = (function() {
|
||||
function If(condition, _arg, tags) {
|
||||
function If(condition, _arg, _arg2) {
|
||||
this.body = _arg;
|
||||
this.tags = tags || (tags = {});
|
||||
this.condition = tags.invert ? condition.invert() : condition;
|
||||
this.soak = tags.soak;
|
||||
this.tags = _arg2 != null ? _arg2 : {};
|
||||
this.condition = this.tags.invert ? condition.invert() : condition;
|
||||
this.elseBody = null;
|
||||
this.isChain = false;
|
||||
this.soak = this.tags.soak;
|
||||
return this;
|
||||
}
|
||||
return If;
|
||||
|
|
312
lib/parser.js
312
lib/parser.js
File diff suppressed because one or more lines are too long
|
@ -4,9 +4,9 @@
|
|||
exports.Scope = (function() {
|
||||
Scope = (function() {
|
||||
function Scope(_arg, _arg2, _arg3) {
|
||||
this.method = _arg3;
|
||||
this.expressions = _arg2;
|
||||
this.parent = _arg;
|
||||
this.expressions = _arg2;
|
||||
this.method = _arg3;
|
||||
this.variables = [
|
||||
{
|
||||
name: 'arguments',
|
||||
|
|
|
@ -8,8 +8,8 @@ CoffeeScript.eval = (code, options) ->
|
|||
eval CoffeeScript.compile code, options
|
||||
|
||||
# Running code does not provide access to this scope.
|
||||
CoffeeScript.run = (code, options) ->
|
||||
options?.bare = on
|
||||
CoffeeScript.run = (code, options = {}) ->
|
||||
options.bare = on
|
||||
Function(CoffeeScript.compile code, options)()
|
||||
|
||||
# If we're not in a browser environment, we're finished with the public API.
|
||||
|
|
|
@ -27,8 +27,7 @@ exports.helpers = require './helpers'
|
|||
|
||||
# Compile a string of CoffeeScript code to JavaScript, using the Coffee/Jison
|
||||
# compiler.
|
||||
exports.compile = compile = (code, options) ->
|
||||
options or= {}
|
||||
exports.compile = compile = (code, options = {}) ->
|
||||
try
|
||||
(parser.parse lexer.tokenize code).compile options
|
||||
catch err
|
||||
|
|
|
@ -200,10 +200,16 @@ grammar =
|
|||
# A single parameter in a function definition can be ordinary, or a splat
|
||||
# that hoovers up the remaining arguments.
|
||||
Param: [
|
||||
o 'PARAM', -> new Literal $1
|
||||
o 'PARAM ...', -> new Param $1, false, true
|
||||
o '@ PARAM', -> new Param $2, true
|
||||
o '@ PARAM ...', -> new Param $2, true, true
|
||||
o 'ParamVar', -> new Param $1
|
||||
o 'ParamVar ...', -> new Param $1, null, on
|
||||
o 'ParamVar = Expression', -> new Param $1, $3
|
||||
]
|
||||
|
||||
ParamVar: [
|
||||
o 'Identifier'
|
||||
o 'ThisProperty'
|
||||
o 'Array'
|
||||
o 'Object'
|
||||
]
|
||||
|
||||
# A splat that occurs outside of a parameter list.
|
||||
|
|
|
@ -355,12 +355,19 @@ exports.Lexer = class Lexer
|
|||
# parameters specially in order to make things easier for the parser.
|
||||
tagParameters: ->
|
||||
return this if @tag() isnt ')'
|
||||
i = @tokens.length
|
||||
while tok = @tokens[--i]
|
||||
stack = []
|
||||
{tokens} = this
|
||||
i = tokens.length
|
||||
tokens[--i][0] = 'PARAM_END'
|
||||
while tok = tokens[--i]
|
||||
switch tok[0]
|
||||
when 'IDENTIFIER' then tok[0] = 'PARAM'
|
||||
when ')' then tok[0] = 'PARAM_END'
|
||||
when '(', 'CALL_START' then tok[0] = 'PARAM_START'; return true
|
||||
when ')'
|
||||
stack.push tok
|
||||
when '(', 'CALL_START'
|
||||
if stack.length then stack.pop()
|
||||
else
|
||||
tok[0] = 'PARAM_START'
|
||||
return this
|
||||
this
|
||||
|
||||
# Close up all remaining open blocks at the end of the file.
|
||||
|
@ -381,8 +388,7 @@ exports.Lexer = class Lexer
|
|||
# a series of delimiters, all of which must be nested correctly within the
|
||||
# contents of the string. This method allows us to have strings within
|
||||
# interpolations within strings, ad infinitum.
|
||||
balancedString: (str, delimited, options) ->
|
||||
options or= {}
|
||||
balancedString: (str, delimited, options = {}) ->
|
||||
levels = []
|
||||
i = 0
|
||||
slen = str.length
|
||||
|
@ -415,8 +421,8 @@ exports.Lexer = class Lexer
|
|||
# If it encounters an interpolation, this method will recursively create a
|
||||
# new Lexer, tokenize the interpolated contents, and merge them into the
|
||||
# token stream.
|
||||
interpolateString: (str, options) ->
|
||||
{heredoc, regex} = options or= {}
|
||||
interpolateString: (str, options = {}) ->
|
||||
{heredoc, regex} = options
|
||||
tokens = []
|
||||
pi = 0
|
||||
i = -1
|
||||
|
|
157
src/nodes.coffee
157
src/nodes.coffee
|
@ -108,8 +108,7 @@ exports.Base = class Base
|
|||
|
||||
# `toString` representation of the node, for inspecting the parse tree.
|
||||
# This is what `coffee --nodes` prints out.
|
||||
toString: (idt, override) ->
|
||||
idt or= ''
|
||||
toString: (idt = '', override) ->
|
||||
children = (child.toString idt + TAB for child in @collectChildren()).join('')
|
||||
klass = override or @constructor.name + if @soak then '?' else ''
|
||||
'\n' + idt + klass + children
|
||||
|
@ -199,8 +198,7 @@ exports.Expressions = class Expressions extends Base
|
|||
this
|
||||
|
||||
# An **Expressions** is the only node that can serve as the root.
|
||||
compile: (o, level) ->
|
||||
o or= {}
|
||||
compile: (o = {}, level) ->
|
||||
if o.scope then super o, level else @compileRoot o
|
||||
|
||||
compileNode: (o) ->
|
||||
|
@ -406,12 +404,11 @@ exports.Call = class Call extends Base
|
|||
|
||||
children: ['variable', 'args']
|
||||
|
||||
constructor: (variable, @args, @soak) ->
|
||||
constructor: (variable, @args = [], @soak) ->
|
||||
super()
|
||||
@isNew = false
|
||||
@isSuper = variable is 'super'
|
||||
@variable = if @isSuper then null else variable
|
||||
@args or= []
|
||||
|
||||
# Tag this invocation as creating a new instance.
|
||||
newInstance: ->
|
||||
|
@ -778,9 +775,9 @@ exports.Assign = class Assign extends Base
|
|||
top = o.level is LEVEL_TOP
|
||||
{value} = this
|
||||
{objects} = @variable.base
|
||||
return value.compile o unless olength = objects.length
|
||||
return value.compile o unless olen = objects.length
|
||||
isObject = @variable.isObject()
|
||||
if top and olength is 1 and (obj = objects[0]) not instanceof Splat
|
||||
if top and olen is 1 and (obj = objects[0]) not instanceof Splat
|
||||
# Unroll simplest cases: `{v} = x` -> `v = x.v`
|
||||
if obj instanceof Assign
|
||||
{variable: {base: idx}, value: obj} = obj
|
||||
|
@ -796,12 +793,12 @@ exports.Assign = class Assign extends Base
|
|||
value = new Value value
|
||||
value.properties.push new (if acc then Accessor else Index) idx
|
||||
return new Assign(obj, value).compile o
|
||||
valVar = value.compile o, LEVEL_LIST
|
||||
vvar = value.compile o, LEVEL_LIST
|
||||
assigns = []
|
||||
splat = false
|
||||
if not IDENTIFIER.test(valVar) or @variable.assigns(valVar)
|
||||
assigns.push "#{ ref = o.scope.freeVariable 'ref' } = #{valVar}"
|
||||
valVar = ref
|
||||
if not IDENTIFIER.test(vvar) or @variable.assigns(vvar)
|
||||
assigns.push "#{ ref = o.scope.freeVariable 'ref' } = #{vvar}"
|
||||
vvar = ref
|
||||
for obj, i in objects
|
||||
# A regular array pattern-match.
|
||||
idx = i
|
||||
|
@ -816,17 +813,27 @@ exports.Assign = class Assign extends Base
|
|||
else
|
||||
idx = if obj.tags.this then obj.properties[0].name else obj
|
||||
if not splat and obj instanceof Splat
|
||||
val = new Literal obj.compileValue o, valVar, i, olength - i - 1
|
||||
splat = true
|
||||
val = "#{olen} <= #{vvar}.length ? #{ utility 'slice' }.call(#{vvar}, #{i}"
|
||||
if rest = olen - i - 1
|
||||
ivar = o.scope.freeVariable 'i'
|
||||
val += ", #{ivar} = #{vvar}.length - #{rest}) : (#{ivar} = #{i}, [])"
|
||||
else
|
||||
val += ") : []"
|
||||
val = new Literal val
|
||||
splat = "#{ivar}++"
|
||||
else
|
||||
if typeof idx isnt 'object'
|
||||
idx = new Literal(if splat then "#{valVar}.length - #{ olength - idx }" else idx)
|
||||
if obj instanceof Splat
|
||||
obj = obj.name.compile o
|
||||
throw SyntaxError \
|
||||
"multiple splats are disallowed in an assignment: #{obj} ..."
|
||||
if typeof idx is 'number'
|
||||
idx = new Literal splat or idx
|
||||
acc = no
|
||||
else
|
||||
acc = isObject and IDENTIFIER.test idx.unwrap().value or 0
|
||||
val = new Value new Literal(valVar), [new (if acc then Accessor else Index) idx]
|
||||
val = new Value new Literal(vvar), [new (if acc then Accessor else Index) idx]
|
||||
assigns.push new Assign(obj, val).compile o, LEVEL_LIST
|
||||
assigns.push valVar unless top
|
||||
assigns.push vvar unless top
|
||||
code = assigns.join ', '
|
||||
if o.level < LEVEL_LIST then code else "(#{code})"
|
||||
|
||||
|
@ -846,10 +853,8 @@ exports.Code = class Code extends Base
|
|||
|
||||
children: ['params', 'body']
|
||||
|
||||
constructor: (params, body, tag) ->
|
||||
constructor: (@params = [], @body = new Expressions, tag) ->
|
||||
super()
|
||||
@params = params or []
|
||||
@body = body or new Expressions
|
||||
@bound = tag is 'boundfunc'
|
||||
@context = 'this' if @bound
|
||||
|
||||
|
@ -862,46 +867,41 @@ exports.Code = class Code extends Base
|
|||
sharedScope = del o, 'sharedScope'
|
||||
o.scope = scope = sharedScope or new Scope o.scope, @body, this
|
||||
o.indent = @idt 1
|
||||
empty = @body.expressions.length is 0
|
||||
delete o.bare
|
||||
delete o.globals
|
||||
splat = undefined
|
||||
params = []
|
||||
for param, i in @params
|
||||
if splat
|
||||
if param.attach
|
||||
param.assign = new Assign new Value new Literal('this'), [new Accessor param.value]
|
||||
@body.expressions.splice splat.index + 1, 0, param.assign
|
||||
splat.trailings.push param
|
||||
vars = []
|
||||
exprs = []
|
||||
for param in @params when param.splat
|
||||
splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
|
||||
new Value new Literal 'arguments'
|
||||
break
|
||||
for param in @params
|
||||
if param.isComplex()
|
||||
ref = param.asReference o
|
||||
exprs.push new Assign new Value(param.name),
|
||||
if param.value then new Op '?', ref, param.value else ref
|
||||
else
|
||||
if param.attach
|
||||
{value} = param
|
||||
[param, param.splat] = [new Literal(scope.freeVariable 'arg'), param.splat]
|
||||
@body.unshift new Assign new Value(new Literal('this'), [new Accessor value]), param
|
||||
if param.splat
|
||||
splat = new Splat param.value
|
||||
splat.index = i
|
||||
splat.trailings = []
|
||||
splat.arglength = @params.length
|
||||
@body.unshift splat
|
||||
else
|
||||
params.push param
|
||||
ref = param
|
||||
exprs.push new Assign new Value(param.name), param.value, '?=' if param.value
|
||||
vars.push ref unless splats
|
||||
scope.startLevel()
|
||||
@body.makeReturn() unless empty or @noReturn
|
||||
params = for param in params
|
||||
scope.parameter param = param.compile o
|
||||
param
|
||||
comm = if @comment then @comment.compile(o) + '\n' else ''
|
||||
wasEmpty = @body.isEmpty()
|
||||
exprs.unshift splats if splats
|
||||
@body.expressions.splice 0, 0, exprs... if exprs.length
|
||||
@body.makeReturn() unless wasEmpty or @noReturn
|
||||
scope.parameter vars[i] = v.compile o for v, i in vars unless splats
|
||||
comm = if @comment then @comment.compile(o) + '\n' else ''
|
||||
o.indent = @idt 2 if @className
|
||||
idt = @idt 1
|
||||
code = if @body.expressions.length then "\n#{ @body.compileWithDeclarations o }\n" else ''
|
||||
idt = @idt 1
|
||||
code = if @body.isEmpty() then ''
|
||||
else "\n#{ @body.compileWithDeclarations o }\n"
|
||||
if @className
|
||||
open = "(function() {\n#{comm}#{idt}function #{@className}("
|
||||
close = "#{ code and idt }}\n#{idt}return #{@className};\n#{@tab}})()"
|
||||
else
|
||||
open = "function("
|
||||
open = 'function('
|
||||
close = "#{ code and @tab }}"
|
||||
func = "#{open}#{ params.join ', ' }) {#{code}#{close}"
|
||||
func = "#{open}#{ vars.join ', ' }) {#{code}#{close}"
|
||||
scope.endLevel()
|
||||
return "#{ utility 'bind' }(#{func}, #{@context})" if @bound
|
||||
if @tags.front then "(#{func})" else func
|
||||
|
@ -917,19 +917,21 @@ exports.Code = class Code extends Base
|
|||
# as well as be a splat, gathering up a group of parameters into an array.
|
||||
exports.Param = class Param extends Base
|
||||
|
||||
children: ['name']
|
||||
children: ['name', 'value']
|
||||
|
||||
constructor: (name, @attach, @splat) ->
|
||||
constructor: (@name, @value, @splat) ->
|
||||
super()
|
||||
@value = new Literal @name = name
|
||||
|
||||
compile: (o) -> @value.compile o, LEVEL_LIST
|
||||
compile: (o) -> @name.compile o, LEVEL_LIST
|
||||
|
||||
toString: ->
|
||||
{name} = this
|
||||
name = '@' + name if @attach
|
||||
name += '...' if @splat
|
||||
new Literal(name).toString()
|
||||
asReference: (o) ->
|
||||
return @reference if @reference
|
||||
node = if @isComplex() then new Literal o.scope.freeVariable 'arg' else @name
|
||||
node = new Value node
|
||||
node = new Splat node if @splat
|
||||
@reference = node
|
||||
|
||||
isComplex: -> @name.isComplex()
|
||||
|
||||
#### Splat
|
||||
|
||||
|
@ -949,34 +951,6 @@ exports.Splat = class Splat extends Base
|
|||
|
||||
compile: (o) -> if @index? then @compileParam o else @name.compile o
|
||||
|
||||
# Compiling a parameter splat means recovering the parameters that succeed
|
||||
# the splat in the parameter list, by slicing the arguments object.
|
||||
compileParam: (o) ->
|
||||
name = @name.compile o
|
||||
o.scope.find name
|
||||
end = ''
|
||||
if @trailings.length
|
||||
len = o.scope.freeVariable 'len'
|
||||
o.scope.assign len, 'arguments.length'
|
||||
variadic = o.scope.freeVariable 'result'
|
||||
o.scope.assign variadic, len + ' >= ' + @arglength
|
||||
end = if @trailings.length then ", #{len} - #{@trailings.length}"
|
||||
for trailing, idx in @trailings
|
||||
if trailing.attach
|
||||
assign = trailing.assign
|
||||
trailing = new Literal o.scope.freeVariable 'arg'
|
||||
assign.value = trailing
|
||||
pos = @trailings.length - idx
|
||||
o.scope.assign trailing.compile(o),
|
||||
"arguments[#{variadic} ? #{len} - #{pos} : #{ @index + idx }]"
|
||||
"#{name} = #{ utility 'slice' }.call(arguments, #{@index}#{end})"
|
||||
|
||||
# A compiling a splat as a destructuring assignment means slicing arguments
|
||||
# from the right-hand-side's corresponding array.
|
||||
compileValue: (o, name, index, trailings) ->
|
||||
trail = if trailings then ', -' + trailings else ''
|
||||
utility('slice') + ".call(#{name}, #{index}#{trail})"
|
||||
|
||||
# Utility function that converts arbitrary number of elements, mixed with
|
||||
# splats, to a proper array
|
||||
@compileSplattedArray: (list, o) ->
|
||||
|
@ -1398,12 +1372,11 @@ exports.If = class If extends Base
|
|||
|
||||
children: ['condition', 'body', 'elseBody']
|
||||
|
||||
constructor: (condition, @body, tags) ->
|
||||
@tags = tags or= {}
|
||||
@condition = if tags.invert then condition.invert() else condition
|
||||
@soak = tags.soak
|
||||
constructor: (condition, @body, @tags = {}) ->
|
||||
@condition = if @tags.invert then condition.invert() else condition
|
||||
@elseBody = null
|
||||
@isChain = false
|
||||
{@soak} = @tags
|
||||
|
||||
bodyNode: -> @body?.unwrap()
|
||||
elseBodyNode: -> @elseBody?.unwrap()
|
||||
|
|
|
@ -29,15 +29,35 @@ ok sumOfArgs(1, 2, 3, 4, 5) is 15
|
|||
ok context.arg is 1
|
||||
|
||||
((splat..., @arg) ->).call context, 1, 2, 3
|
||||
ok context.arg is 3
|
||||
eq context.arg, 3
|
||||
|
||||
((@arg...) ->).call context, 1, 2, 3
|
||||
ok context.arg.join ' ' is '1 2 3'
|
||||
eq context.arg.join(' '), '1 2 3'
|
||||
|
||||
class Klass
|
||||
constructor: (@one, @two) ->
|
||||
|
||||
obj = new Klass 1, 2
|
||||
|
||||
ok obj.one is 1
|
||||
ok obj.two is 2
|
||||
eq obj.one, 1
|
||||
eq obj.two, 2
|
||||
|
||||
|
||||
# Destructuring.
|
||||
(([{a: [b], c}]...) ->
|
||||
eq b, 123
|
||||
eq c, 456
|
||||
) {a: [123], c: 456}
|
||||
|
||||
|
||||
# Default values.
|
||||
obj = f: (q = 123, @p = 456) -> q
|
||||
eq obj.f(), 123
|
||||
eq obj.p , 456
|
||||
|
||||
withSplats = (a = 2, b..., c = 3, d = 5) -> a * (b.length + 1) * c * d
|
||||
eq 30, withSplats()
|
||||
eq 15, withSplats 1
|
||||
eq 5, withSplats 1, 1
|
||||
eq 1, withSplats 1, 1, 1
|
||||
eq 2, withSplats 1, 1, 1, 1
|
||||
|
|
Loading…
Reference in New Issue