Merge branch 'splats' of git://github.com/matehat/coffee-script

This commit is contained in:
Jeremy Ashkenas
2010-03-17 21:07:31 -04:00
3 changed files with 132 additions and 51 deletions

View File

@@ -431,17 +431,21 @@
};
// Compile a vanilla function call.
CallNode.prototype.compile_node = function compile_node(o) {
var _a, _b, _c, _d, arg, args;
if (this.args[this.args.length - 1] instanceof SplatNode) {
return this.compile_splat(o);
var _a, _b, _c, _d, _e, _f, _g, arg, args;
_a = this.args;
for (_b = 0, _c = _a.length; _b < _c; _b++) {
arg = _a[_b];
if (arg instanceof SplatNode) {
return this.compile_splat(o);
}
}
args = (function() {
_a = []; _b = this.args;
for (_c = 0, _d = _b.length; _c < _d; _c++) {
arg = _b[_c];
_a.push(arg.compile(o));
_d = []; _e = this.args;
for (_f = 0, _g = _e.length; _f < _g; _f++) {
arg = _e[_f];
_d.push(arg.compile(o));
}
return _a;
return _d;
}).call(this).join(', ');
if (this.variable === 'super') {
return this.compile_super(args, o);
@@ -459,7 +463,7 @@
// If you call a function with a splat, it's converted into a JavaScript
// `.apply()` call to allow an array of arguments to be passed.
CallNode.prototype.compile_splat = function compile_splat(o) {
var _a, _b, _c, arg, args, code, i, meth, obj, temp;
var meth, obj, temp;
meth = this.variable.compile(o);
obj = this.variable.source || 'this';
if (obj.match(/\(/)) {
@@ -467,19 +471,34 @@
obj = temp;
meth = "(" + temp + " = " + (this.variable.source) + ")" + (this.variable.last);
}
args = (function() {
_a = []; _b = this.args;
for (i = 0, _c = _b.length; i < _c; i++) {
arg = _b[i];
_a.push((function() {
code = arg.compile(o);
code = arg instanceof SplatNode ? code : "[" + code + "]";
return i === 0 ? code : ".concat(" + code + ")";
}).call(this));
return '' + this.prefix + (meth) + ".apply(" + obj + ", " + (this.compile_splat_arguments(o)) + ")";
};
// Converts arbitrary number of arguments, mixed with splats, to
// a proper array to pass to an `.apply()` call
CallNode.prototype.compile_splat_arguments = function compile_splat_arguments(o) {
var _a, _b, _c, arg, args, code, i, prev;
args = [];
i = 0;
_a = this.args;
for (_b = 0, _c = _a.length; _b < _c; _b++) {
arg = _a[_b];
code = arg.compile(o);
if (!(arg instanceof SplatNode)) {
prev = args[i - 1];
if (i === 1 && prev[0] === '[' && prev[prev.length - 1] === ']') {
args[i - 1] = '' + (prev.slice(0, prev.length - 1)) + ", " + code + "]";
continue;
} else if (i > 1 && prev[8] === '[' && prev[prev.length - 2] === ']') {
args[i - 1] = '' + (prev.slice(0, prev.length - 2)) + ", " + code + "])";
continue;
} else {
code = "[" + code + "]";
}
}
return _a;
}).call(this);
return '' + this.prefix + (meth) + ".apply(" + obj + ", " + (args.join('')) + ")";
args.push(i === 0 ? code : ".concat(" + code + ")");
i += 1;
}
return args.join('');
};
return CallNode;
}).call(this);
@@ -899,7 +918,7 @@
// arrow, generates a wrapper that saves the current value of `this` through
// a closure.
CodeNode.prototype.compile_node = function compile_node(o) {
var _a, _b, _c, _d, _e, _f, _g, code, func, inner, name_part, param, params, shared_scope, splat, top;
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, code, func, i, inner, name_part, param, params, shared_scope, splat, top;
shared_scope = del(o, 'shared_scope');
top = del(o, 'top');
o.scope = shared_scope || new Scope(o.scope, this.body, this);
@@ -908,22 +927,35 @@
o.indent = this.idt(this.bound ? 2 : 1);
del(o, 'no_wrap');
del(o, 'globals');
if (this.params[this.params.length - 1] instanceof SplatNode) {
splat = this.params.pop();
splat.index = this.params.length;
this.body.unshift(splat);
i = 0;
splat = undefined;
params = [];
_a = this.params;
for (_b = 0, _c = _a.length; _b < _c; _b++) {
param = _a[_b];
if (param instanceof SplatNode && !(typeof splat !== "undefined" && splat !== null)) {
splat = param;
splat.index = i;
this.body.unshift(splat);
splat.trailings = [];
} else if ((typeof splat !== "undefined" && splat !== null)) {
splat.trailings.push(param);
} else {
params.push(param);
}
i += 1;
}
params = (function() {
_a = []; _b = this.params;
for (_c = 0, _d = _b.length; _c < _d; _c++) {
param = _b[_c];
_a.push(param.compile(o));
_d = []; _e = params;
for (_f = 0, _g = _e.length; _f < _g; _f++) {
param = _e[_f];
_d.push(param.compile(o));
}
return _a;
return _d;
}).call(this);
_e = params;
for (_f = 0, _g = _e.length; _f < _g; _f++) {
param = _e[_f];
_h = params;
for (_i = 0, _j = _h.length; _i < _j; _i++) {
param = _h[_i];
(o.scope.parameter(param));
}
code = this.body.expressions.length ? "\n" + (this.body.compile_with_declarations(o)) + "\n" : '';
@@ -992,10 +1024,17 @@
// Compiling a parameter splat means recovering the parameters that succeed
// the splat in the parameter list, by slicing the arguments object.
SplatNode.prototype.compile_param = function compile_param(o) {
var name;
var _a, _b, _c, i, name, trailing;
name = this.name.compile(o);
o.scope.find(name);
return '' + name + " = Array.prototype.slice.call(arguments, " + this.index + ")";
i = 0;
_a = this.trailings;
for (_b = 0, _c = _a.length; _b < _c; _b++) {
trailing = _a[_b];
o.scope.assign(trailing.compile(o), "arguments[arguments.length - " + this.trailings.length + " + " + i + "]");
i += 1;
}
return '' + name + " = Array.prototype.slice.call(arguments, " + this.index + ", arguments.length - " + (this.trailings.length) + ")";
};
// A compiling a splat as a destructuring assignment means slicing arguments
// from the right-hand-side's corresponding array.

View File

@@ -338,7 +338,8 @@ exports.CallNode: class CallNode extends BaseNode
# Compile a vanilla function call.
compile_node: (o) ->
return @compile_splat(o) if @args[@args.length - 1] instanceof SplatNode
for arg in @args
return @compile_splat(o) if arg instanceof SplatNode
args: (arg.compile(o) for arg in @args).join(', ')
return @compile_super(args, o) if @variable is 'super'
"$@prefix${@variable.compile(o)}($args)"
@@ -362,11 +363,28 @@ exports.CallNode: class CallNode extends BaseNode
temp: o.scope.free_variable()
obj: temp
meth: "($temp = ${ @variable.source })${ @variable.last }"
args: for arg, i in @args
"$@prefix${meth}.apply($obj, ${ @compile_splat_arguments(o) })"
# Converts arbitrary number of arguments, mixed with splats, to
# a proper array to pass to an `.apply()` call
compile_splat_arguments: (o) ->
args: []
i: 0
for arg in @args
code: arg.compile o
code: if arg instanceof SplatNode then code else "[$code]"
if i is 0 then code else ".concat($code)"
"$@prefix${meth}.apply($obj, ${ args.join('') })"
if not (arg instanceof SplatNode)
prev: args[i - 1]
if i is 1 and prev[0] is '[' and prev[prev.length - 1] is ']'
args[i - 1] = "${prev[0...prev.length - 1]}, $code]"
continue
else if i > 1 and prev[8] is '[' and prev[prev.length - 2] is ']'
args[i - 1] = "${prev[0...prev.length - 2]}, $code])"
continue
else
code: "[$code]"
args.push(if i is 0 then code else ".concat($code)")
i += 1
args.join('')
#### ExtendsNode
@@ -699,11 +717,21 @@ exports.CodeNode: class CodeNode extends BaseNode
o.indent: @idt(if @bound then 2 else 1)
del o, 'no_wrap'
del o, 'globals'
if @params[@params.length - 1] instanceof SplatNode
splat: @params.pop()
splat.index: @params.length
@body.unshift(splat)
params: (param.compile(o) for param in @params)
i: 0
splat: undefined
params: []
for param in @params
if param instanceof SplatNode and not splat?
splat: param
splat.index: i
@body.unshift(splat)
splat.trailings: []
else if splat?
splat.trailings.push(param)
else
params.push(param)
i += 1
params: (param.compile(o) for param in params)
(o.scope.parameter(param)) for param in params
code: if @body.expressions.length then "\n${ @body.compile_with_declarations(o) }\n" else ''
name_part: if @name then ' ' + @name else ''
@@ -750,8 +778,12 @@ exports.SplatNode: class SplatNode extends BaseNode
compile_param: (o) ->
name: @name.compile(o)
o.scope.find name
"$name = Array.prototype.slice.call(arguments, $@index)"
i: 0
for trailing in @trailings
o.scope.assign(trailing.compile(o), "arguments[arguments.length - $@trailings.length + $i]")
i += 1
"$name = Array.prototype.slice.call(arguments, $@index, arguments.length - ${@trailings.length})"
# A compiling a splat as a destructuring assignment means slicing arguments
# from the right-hand-side's corresponding array.
compile_value: (o, name, index) ->

View File

@@ -6,13 +6,14 @@ result: func 1, 2, 3, 4, 5
ok result is "3 4 5"
gold: silver: bronze: the_field: null
gold: silver: bronze: the_field: last: null
medalists: (first, second, third, rest...) ->
medalists: (first, second, third, rest..., unlucky) ->
gold: first
silver: second
bronze: third
the_field: rest
the_field: rest.concat([last])
last: unlucky
contenders: [
"Michael Phelps"
@@ -32,8 +33,17 @@ medalists "Mighty Mouse", contenders...
ok gold is "Mighty Mouse"
ok silver is "Michael Phelps"
ok bronze is "Liu Xiang"
ok last is "Usain Bolt"
ok the_field.length is 8
contenders.reverse()
medalists contenders[0...2]..., "Mighty Mouse", contenders[2...contenders.length]...
ok gold is "Usain Bolt"
ok silver is "Asafa Powell"
ok bronze is "Mighty Mouse"
ok last is "Michael Phelps"
ok the_field.length is 8
obj: {
name: 'bob'