merging satyr/defarg

This commit is contained in:
Jeremy Ashkenas 2010-11-01 21:33:07 -04:00
commit c17efcf422
13 changed files with 413 additions and 407 deletions

View File

@ -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)) {

View File

@ -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) {

View File

@ -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);

View File

@ -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('+', '+');
}

View File

@ -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;

File diff suppressed because one or more lines are too long

View File

@ -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',

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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()

View File

@ -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