From 7129f518a4b35292dde4772524a030be49ecb249 Mon Sep 17 00:00:00 2001 From: matehat Date: Wed, 17 Mar 2010 14:41:09 -0400 Subject: [PATCH] Added the ability for function declaration to have a splat at an arbitrary position, not just at the end. Still restrict their number to 1. Adjusted tests accordingly. --- lib/nodes.js | 50 ++++++++++++++++++++++++++++------------- src/nodes.coffee | 28 +++++++++++++++++------ test/test_splats.coffee | 9 +++++--- 3 files changed, 62 insertions(+), 25 deletions(-) diff --git a/lib/nodes.js b/lib/nodes.js index d8a053a0..1be3e84b 100644 --- a/lib/nodes.js +++ b/lib/nodes.js @@ -918,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); @@ -927,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" : ''; @@ -1011,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. diff --git a/src/nodes.coffee b/src/nodes.coffee index 640e6000..b8772122 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -717,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 '' @@ -768,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) -> diff --git a/test/test_splats.coffee b/test/test_splats.coffee index 070718e6..b8d1c4d3 100644 --- a/test/test_splats.coffee +++ b/test/test_splats.coffee @@ -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,6 +33,7 @@ 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() @@ -40,6 +42,7 @@ 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: {