From 663595ba9414577e65076c06830e365250f351d7 Mon Sep 17 00:00:00 2001 From: Chris Connelly Date: Sun, 6 Nov 2016 16:30:04 +0000 Subject: [PATCH] Compile splats in arrays and function calls to ES2015 splats (#4353) Rather than compiling splats to arrays built using `Array#concat`, splats are now compiled directly to ES2015 splats, e.g. f foo, arguments..., bar [ foo, arguments..., bar ] Which used to be compiled to: f.apply(null, [foo].concat(slice.call(arguments), [bar])); [foo].concat(slice.call(arguments), [bar]); Is now compiled to: f(foo, ...arguments, bar); [ foo, ...arguments, bar ]; --- lib/coffee-script/browser.js | 4 +- lib/coffee-script/lexer.js | 11 ++- lib/coffee-script/nodes.js | 152 ++++++++-------------------------- lib/coffee-script/optparse.js | 2 +- lib/coffee-script/rewriter.js | 7 +- src/nodes.coffee | 76 +---------------- test/arrays.coffee | 7 ++ 7 files changed, 56 insertions(+), 203 deletions(-) diff --git a/lib/coffee-script/browser.js b/lib/coffee-script/browser.js index 6cb84d2361..39bde36aba 100644 --- a/lib/coffee-script/browser.js +++ b/lib/coffee-script/browser.js @@ -47,7 +47,7 @@ if ((ref = xhr.status) === 0 || ref === 200) { param = [xhr.responseText, options]; if (!hold) { - CoffeeScript.run.apply(CoffeeScript, param); + CoffeeScript.run(...param); } } else { throw new Error("Could not load " + url); @@ -80,7 +80,7 @@ var param; param = coffees[index]; if (param instanceof Array) { - CoffeeScript.run.apply(CoffeeScript, param); + CoffeeScript.run(...param); index++; return execute(); } diff --git a/lib/coffee-script/lexer.js b/lib/coffee-script/lexer.js index 509f73d9fa..2f6a6f75f2 100644 --- a/lib/coffee-script/lexer.js +++ b/lib/coffee-script/lexer.js @@ -1,8 +1,7 @@ // Generated by CoffeeScript 2.0.0-alpha (function() { var BOM, BOOL, CALLABLE, CODE, COFFEE_ALIASES, COFFEE_ALIAS_MAP, COFFEE_KEYWORDS, COMMENT, COMPARE, COMPOUND_ASSIGN, HERECOMMENT_ILLEGAL, HEREDOC_DOUBLE, HEREDOC_INDENT, HEREDOC_SINGLE, HEREGEX, HEREGEX_OMIT, IDENTIFIER, INDENTABLE_CLOSERS, INDEXABLE, INVALID_ESCAPE, INVERSES, JSTOKEN, JS_KEYWORDS, LEADING_BLANK_LINE, LINE_BREAK, LINE_CONTINUER, Lexer, MATH, MULTI_DENT, NOT_REGEX, NUMBER, OPERATOR, POSSIBLY_DIVISION, REGEX, REGEX_FLAGS, REGEX_ILLEGAL, RELATION, RESERVED, Rewriter, SHIFT, SIMPLE_STRING_OMIT, STRICT_PROSCRIBED, STRING_DOUBLE, STRING_OMIT, STRING_SINGLE, STRING_START, TRAILING_BLANK_LINE, TRAILING_SPACES, UNARY, UNARY_MATH, VALID_FLAGS, WHITESPACE, compact, count, invertLiterate, isUnassignable, key, locationDataToString, ref, ref1, repeat, starts, throwSyntaxError, - indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, - slice = [].slice; + indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; ref = require('./rewriter'), Rewriter = ref.Rewriter, INVERSES = ref.INVERSES; @@ -549,7 +548,7 @@ } tag = value; ref2 = this.tokens, prev = ref2[ref2.length - 1]; - if (prev && indexOf.call(['='].concat(slice.call(COMPOUND_ASSIGN)), value) >= 0) { + if (prev && indexOf.call(['=', ...COMPOUND_ASSIGN], value) >= 0) { skipToken = false; if (value === '=' && ((ref3 = prev[1]) === '||' || ref3 === '&&') && !prev.spaced) { prev[0] = 'COMPOUND_ASSIGN'; @@ -719,7 +718,7 @@ }; Lexer.prototype.mergeInterpolationTokens = function(tokens, options, fn) { - var converted, firstEmptyStringIndex, firstIndex, i, j, lastToken, len, locationToken, lparen, plusToken, ref2, rparen, tag, token, tokensToPush, value; + var converted, firstEmptyStringIndex, firstIndex, i, j, lastToken, len, locationToken, lparen, plusToken, rparen, tag, token, tokensToPush, value; if (tokens.length > 1) { lparen = this.token('STRING_START', '(', 0, 0); } @@ -761,7 +760,7 @@ last_column: locationToken[2].first_column }; } - (ref2 = this.tokens).push.apply(ref2, tokensToPush); + this.tokens.push(...tokensToPush); } if (lparen) { lastToken = tokens[tokens.length - 1]; @@ -934,7 +933,7 @@ isUnassignable = function(name, displayName = name) { switch (false) { - case indexOf.call(slice.call(JS_KEYWORDS).concat(slice.call(COFFEE_KEYWORDS)), name) < 0: + case indexOf.call([...JS_KEYWORDS, ...COFFEE_KEYWORDS], name) < 0: return "keyword '" + displayName + "' can't be assigned"; case indexOf.call(STRICT_PROSCRIBED, name) < 0: return "'" + displayName + "' can't be assigned"; diff --git a/lib/coffee-script/nodes.js b/lib/coffee-script/nodes.js index fcd3253ca2..85829be718 100644 --- a/lib/coffee-script/nodes.js +++ b/lib/coffee-script/nodes.js @@ -531,7 +531,7 @@ }; Literal.prototype.toString = function() { - return " " + (this.isStatement() ? Literal.__super__.toString.apply(this, arguments) : this.constructor.name) + ": " + this.value; + return " " + (this.isStatement() ? Literal.__super__.toString.call(this, ...arguments) : this.constructor.name) + ": " + this.value; }; return Literal; @@ -777,7 +777,7 @@ if (o.scope.parent == null) { this.error('yield can only occur inside functions'); } - return YieldReturn.__super__.compileNode.apply(this, arguments); + return YieldReturn.__super__.compileNode.call(this, ...arguments); }; return YieldReturn; @@ -795,7 +795,7 @@ if (o.scope.parent == null) { this.error('await can only occur inside functions'); } - return AwaitReturn.__super__.compileNode.apply(this, arguments); + return AwaitReturn.__super__.compileNode.call(this, ...arguments); }; return AwaitReturn; @@ -958,21 +958,21 @@ } for (j = 0, len1 = props.length; j < len1; j++) { prop = props[j]; - fragments.push.apply(fragments, prop.compileToFragments(o)); + fragments.push(...prop.compileToFragments(o)); } return fragments; }; Value.prototype.unfoldSoak = function(o) { return this.unfoldedSoak != null ? this.unfoldedSoak : this.unfoldedSoak = (() => { - var fst, i, ifn, j, len1, prop, ref, ref3, ref4, snd; + var fst, i, ifn, j, len1, prop, ref, ref3, snd; if (ifn = this.base.unfoldSoak(o)) { - (ref3 = ifn.body.properties).push.apply(ref3, this.properties); + ifn.body.properties.push(...this.properties); return ifn; } - ref4 = this.properties; - for (i = j = 0, len1 = ref4.length; j < len1; i = ++j) { - prop = ref4[i]; + ref3 = this.properties; + for (i = j = 0, len1 = ref3.length; j < len1; i = ++j) { + prop = ref3[i]; if (!prop.soak) { continue; } @@ -1099,14 +1099,10 @@ }; Call.prototype.compileNode = function(o) { - var arg, argIndex, compiledArgs, compiledArray, fragments, j, len1, preface, ref3, ref4; + var arg, argIndex, compiledArgs, fragments, j, len1, preface, ref3, ref4; if ((ref3 = this.variable) != null) { ref3.front = this.front; } - compiledArray = Splat.compileSplattedArray(o, this.args, true); - if (compiledArray.length) { - return this.compileSplat(o, compiledArray); - } compiledArgs = []; ref4 = this.args; for (argIndex = j = 0, len1 = ref4.length; j < len1; argIndex = ++j) { @@ -1114,7 +1110,7 @@ if (argIndex) { compiledArgs.push(this.makeCode(", ")); } - compiledArgs.push.apply(compiledArgs, arg.compileToFragments(o, LEVEL_LIST)); + compiledArgs.push(...arg.compileToFragments(o, LEVEL_LIST)); } fragments = []; if (this instanceof SuperCall) { @@ -1127,44 +1123,14 @@ if (this.isNew) { fragments.push(this.makeCode('new ')); } - fragments.push.apply(fragments, this.variable.compileToFragments(o, LEVEL_ACCESS)); + fragments.push(...this.variable.compileToFragments(o, LEVEL_ACCESS)); fragments.push(this.makeCode("(")); } - fragments.push.apply(fragments, compiledArgs); + fragments.push(...compiledArgs); fragments.push(this.makeCode(")")); return fragments; }; - Call.prototype.compileSplat = function(o, splatArgs) { - var answer, base, fun, idt, name, ref; - if (this instanceof SuperCall) { - return [].concat(this.makeCode((this.superReference(o)) + ".apply(" + (this.superThis(o)) + ", "), splatArgs, this.makeCode(")")); - } - if (this.isNew) { - idt = this.tab + TAB; - return [].concat(this.makeCode("(function(func, args, ctor) {\n" + idt + "ctor.prototype = func.prototype;\n" + idt + "var child = new ctor, result = func.apply(child, args);\n" + idt + "return Object(result) === result ? result : child;\n" + this.tab + "})("), this.variable.compileToFragments(o, LEVEL_LIST), this.makeCode(", "), splatArgs, this.makeCode(", function(){})")); - } - answer = []; - base = new Value(this.variable); - if ((name = base.properties.pop()) && base.isComplex()) { - ref = o.scope.freeVariable('ref'); - answer = answer.concat(this.makeCode("(" + ref + " = "), base.compileToFragments(o, LEVEL_LIST), this.makeCode(")"), name.compileToFragments(o)); - } else { - fun = base.compileToFragments(o, LEVEL_ACCESS); - if (SIMPLENUM.test(fragmentsToText(fun))) { - fun = this.wrapInBraces(fun); - } - if (name) { - ref = fragmentsToText(fun); - fun.push.apply(fun, name.compileToFragments(o)); - } else { - ref = 'null'; - } - answer = answer.concat(fun); - } - return answer = answer.concat(this.makeCode(".apply(" + ref + ", "), splatArgs, this.makeCode(")")); - }; - return Call; })(Base); @@ -1262,12 +1228,12 @@ node = this.name.unwrap(); if (node instanceof PropertyName) { if (ref3 = node.value, indexOf.call(JS_FORBIDDEN, ref3) >= 0) { - return [this.makeCode('["')].concat(slice.call(name), [this.makeCode('"]')]); + return [this.makeCode('["'), ...name, this.makeCode('"]')]; } else { - return [this.makeCode('.')].concat(slice.call(name)); + return [this.makeCode('.'), ...name]; } } else { - return [this.makeCode('[')].concat(slice.call(name), [this.makeCode(']')]); + return [this.makeCode('['), ...name, this.makeCode(']')]; } }; @@ -1482,7 +1448,7 @@ if (indent) { answer.push(this.makeCode(indent)); } - answer.push.apply(answer, prop.compileToFragments(o, LEVEL_TOP)); + answer.push(...prop.compileToFragments(o, LEVEL_TOP)); if (join) { answer.push(this.makeCode(join)); } @@ -1528,10 +1494,6 @@ return [this.makeCode('[]')]; } o.indent += TAB; - answer = Splat.compileSplattedArray(o, this.objects); - if (answer.length) { - return answer; - } answer = []; compiledObjs = (function() { var j, len1, ref3, results; @@ -1548,7 +1510,7 @@ if (index) { answer.push(this.makeCode(", ")); } - answer.push.apply(answer, fragments); + answer.push(...fragments); } if (fragmentsToText(answer).indexOf('\n') >= 0) { answer.unshift(this.makeCode("[\n" + o.indent)); @@ -1736,7 +1698,7 @@ }; Class.prototype.compileNode = function(o) { - var args, argumentsNode, func, jumpNode, klass, lname, name, ref3, superClass; + var args, argumentsNode, func, jumpNode, klass, lname, name, superClass; if (jumpNode = this.body.jumps()) { jumpNode.error('Class bodies cannot contain pure statements'); } @@ -1763,7 +1725,7 @@ func.params.push(new Param(superClass)); args.push(this.parent); } - (ref3 = this.body.expressions).unshift.apply(ref3, this.directives); + this.body.expressions.unshift(...this.directives); klass = new Parens(new Call(func, args)); if (this.variable) { klass = new Assign(this.variable, klass, null, { @@ -1824,7 +1786,7 @@ code = []; code.push(this.makeCode(this.tab + "import ")); if (this.clause != null) { - code.push.apply(code, this.clause.compileNode(o)); + code.push(...this.clause.compileNode(o)); } if (((ref3 = this.source) != null ? ref3.value : void 0) != null) { if (this.clause !== null) { @@ -1854,13 +1816,13 @@ var code; code = []; if (this.defaultBinding != null) { - code.push.apply(code, this.defaultBinding.compileNode(o)); + code.push(...this.defaultBinding.compileNode(o)); if (this.namedImports != null) { code.push(this.makeCode(', ')); } } if (this.namedImports != null) { - code.push.apply(code, this.namedImports.compileNode(o)); + code.push(...this.namedImports.compileNode(o)); } return code; }; @@ -1970,7 +1932,7 @@ if (index) { code.push(this.makeCode(",\n" + o.indent)); } - code.push.apply(code, fragments); + code.push(...fragments); } code.push(this.makeCode("\n}")); } else { @@ -2238,7 +2200,7 @@ assigns = []; expandedIdx = false; if (!(value.unwrap() instanceof IdentifierLiteral) || this.variable.assigns(vvarText)) { - assigns.push([this.makeCode((ref = o.scope.freeVariable('ref')) + " = ")].concat(slice.call(vvar))); + assigns.push([this.makeCode((ref = o.scope.freeVariable('ref')) + " = "), ...vvar]); vvar = [this.makeCode(ref)]; vvarText = ref; } @@ -2419,7 +2381,7 @@ }; Code.prototype.compileNode = function(o) { - var answer, code, condition, exprs, haveSplatParam, i, ifTrue, j, k, len1, len2, param, paramNames, params, paramsAfterSplat, ref, ref3, ref4, ref5, ref6, splatParamName, val, wasEmpty; + var answer, code, condition, exprs, haveSplatParam, i, ifTrue, j, k, len1, len2, param, paramNames, params, paramsAfterSplat, ref, ref3, ref4, ref5, splatParamName, val, wasEmpty; if (this.bound) { if ((ref3 = o.scope.method) != null ? ref3.bound : void 0) { this.context = o.scope.method.context; @@ -2497,7 +2459,8 @@ } } if (paramsAfterSplat.length !== 0) { - exprs.unshift(new Assign(new Value(new Arr([new Splat(new IdentifierLiteral(splatParamName))].concat(slice.call((function() { + exprs.unshift(new Assign(new Value(new Arr([ + new Splat(new IdentifierLiteral(splatParamName)), ...(function() { var k, len2, results; results = []; for (k = 0, len2 = paramsAfterSplat.length; k < len2; k++) { @@ -2505,11 +2468,12 @@ results.push(param.asReference(o)); } return results; - })())))), new Value(new IdentifierLiteral(splatParamName)))); + })() + ])), new Value(new IdentifierLiteral(splatParamName)))); } wasEmpty = this.body.isEmpty(); if (exprs.length) { - (ref6 = this.body.expressions).unshift.apply(ref6, exprs); + this.body.expressions.unshift(...exprs); } if (!(wasEmpty || this.noReturn)) { this.body.makeReturn(); @@ -2537,7 +2501,7 @@ if (haveSplatParam && i === params.length - 1) { answer.push(this.makeCode('...')); } - answer.push.apply(answer, param.compileToFragments(o)); + answer.push(...param.compileToFragments(o)); } answer.push(this.makeCode(!this.bound ? ') {' : ') => {')); if (!this.body.isEmpty()) { @@ -2545,7 +2509,7 @@ } answer.push(this.makeCode('}')); if (this.ctor) { - return [this.makeCode(this.tab)].concat(slice.call(answer)); + return [this.makeCode(this.tab), ...answer]; } if (this.front || (o.level >= LEVEL_ACCESS)) { return this.wrapInBraces(answer); @@ -2682,57 +2646,13 @@ }; Splat.prototype.compileToFragments = function(o) { - return this.name.compileToFragments(o); + return [this.makeCode('...'), ...this.name.compileToFragments(o)]; }; Splat.prototype.unwrap = function() { return this.name; }; - Splat.compileSplattedArray = function(o, list, apply) { - var args, base, compiledNode, concatPart, fragments, i, index, j, last, len1, node; - index = -1; - while ((node = list[++index]) && !(node instanceof Splat)) { - continue; - } - if (index >= list.length) { - return []; - } - if (list.length === 1) { - node = list[0]; - fragments = node.compileToFragments(o, LEVEL_LIST); - if (apply) { - return fragments; - } - return [].concat(node.makeCode((utility('slice', o)) + ".call("), fragments, node.makeCode(")")); - } - args = list.slice(index); - for (i = j = 0, len1 = args.length; j < len1; i = ++j) { - node = args[i]; - compiledNode = node.compileToFragments(o, LEVEL_LIST); - args[i] = node instanceof Splat ? [].concat(node.makeCode((utility('slice', o)) + ".call("), compiledNode, node.makeCode(")")) : [].concat(node.makeCode("["), compiledNode, node.makeCode("]")); - } - if (index === 0) { - node = list[0]; - concatPart = node.joinFragmentArrays(args.slice(1), ', '); - return args[0].concat(node.makeCode(".concat("), concatPart, node.makeCode(")")); - } - base = (function() { - var k, len2, ref3, results; - ref3 = list.slice(0, index); - results = []; - for (k = 0, len2 = ref3.length; k < len2; k++) { - node = ref3[k]; - results.push(node.compileToFragments(o, LEVEL_LIST)); - } - return results; - })(); - base = list[0].joinFragmentArrays(base, ', '); - concatPart = list[index].joinFragmentArrays(args, ', '); - last = list[list.length - 1]; - return [].concat(list[0].makeCode("["), base, list[index].makeCode("].concat("), concatPart, last.makeCode(")")); - }; - return Splat; })(Base); @@ -2774,7 +2694,7 @@ While.prototype.makeReturn = function(res) { if (res) { - return While.__super__.makeReturn.apply(this, arguments); + return While.__super__.makeReturn.call(this, ...arguments); } else { this.returns = !this.jumps({ loop: true @@ -3588,7 +3508,7 @@ fragments.push(cond.makeCode(idt2 + 'break;\n')); } if (this.otherwise && this.otherwise.expressions.length) { - fragments.push.apply(fragments, [this.makeCode(idt1 + "default:\n")].concat(slice.call(this.otherwise.compileToFragments(o, LEVEL_TOP)), [this.makeCode("\n")])); + fragments.push(this.makeCode(idt1 + "default:\n"), ...this.otherwise.compileToFragments(o, LEVEL_TOP), this.makeCode("\n")); } fragments.push(this.makeCode(this.tab + '}')); return fragments; diff --git a/lib/coffee-script/optparse.js b/lib/coffee-script/optparse.js index 4ff3f60b33..c8dab1e715 100644 --- a/lib/coffee-script/optparse.js +++ b/lib/coffee-script/optparse.js @@ -95,7 +95,7 @@ if (tuple.length < 3) { tuple.unshift(null); } - results.push(buildRule.apply(null, tuple)); + results.push(buildRule(...tuple)); } return results; }; diff --git a/lib/coffee-script/rewriter.js b/lib/coffee-script/rewriter.js index 20183e5f73..cec8b8b334 100644 --- a/lib/coffee-script/rewriter.js +++ b/lib/coffee-script/rewriter.js @@ -1,8 +1,7 @@ // Generated by CoffeeScript 2.0.0-alpha (function() { var BALANCED_PAIRS, CALL_CLOSERS, EXPRESSION_CLOSE, EXPRESSION_END, EXPRESSION_START, IMPLICIT_CALL, IMPLICIT_END, IMPLICIT_FUNC, IMPLICIT_UNSPACED_CALL, INVERSES, LINEBREAKS, SINGLE_CLOSERS, SINGLE_LINERS, generate, k, left, len, ref, rite, - indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, - slice = [].slice; + indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; generate = function(tag, value, origin) { var tok; @@ -401,7 +400,7 @@ tag = token[0]; if (tag === 'TERMINATOR') { if (this.tag(i + 1) === 'ELSE' && this.tag(i - 1) !== 'OUTDENT') { - tokens.splice.apply(tokens, [i, 1].concat(slice.call(this.indentation()))); + tokens.splice(i, 1, ...this.indentation()); return 1; } if (ref = this.tag(i + 1), indexOf.call(EXPRESSION_CLOSE, ref) >= 0) { @@ -414,7 +413,7 @@ if (!((ref1 = this.tag(i + j)) === 'OUTDENT' || ref1 === 'TERMINATOR' || ref1 === 'FINALLY')) { continue; } - tokens.splice.apply(tokens, [i + j, 0].concat(slice.call(this.indentation()))); + tokens.splice(i + j, 0, ...this.indentation()); return 2 + j; } } diff --git a/src/nodes.coffee b/src/nodes.coffee index 2ce5c2d043..12c1f64f3f 100644 --- a/src/nodes.coffee +++ b/src/nodes.coffee @@ -690,9 +690,6 @@ exports.Call = class Call extends Base # Compile a vanilla function call. compileNode: (o) -> @variable?.front = @front - compiledArray = Splat.compileSplattedArray o, @args, true - if compiledArray.length - return @compileSplat o, compiledArray compiledArgs = [] for arg, argIndex in @args if argIndex then compiledArgs.push @makeCode ", " @@ -711,47 +708,6 @@ exports.Call = class Call extends Base fragments.push @makeCode ")" fragments - # 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. - # If it's a constructor, then things get real tricky. We have to inject an - # inner constructor in order to be able to pass the varargs. - # - # splatArgs is an array of CodeFragments to put into the 'apply'. - compileSplat: (o, splatArgs) -> - if this instanceof SuperCall - return [].concat @makeCode("#{ @superReference o }.apply(#{@superThis(o)}, "), - splatArgs, @makeCode(")") - - if @isNew - idt = @tab + TAB - return [].concat @makeCode(""" - (function(func, args, ctor) { - #{idt}ctor.prototype = func.prototype; - #{idt}var child = new ctor, result = func.apply(child, args); - #{idt}return Object(result) === result ? result : child; - #{@tab}})("""), - (@variable.compileToFragments o, LEVEL_LIST), - @makeCode(", "), splatArgs, @makeCode(", function(){})") - - answer = [] - base = new Value @variable - if (name = base.properties.pop()) and base.isComplex() - ref = o.scope.freeVariable 'ref' - answer = answer.concat @makeCode("(#{ref} = "), - (base.compileToFragments o, LEVEL_LIST), - @makeCode(")"), - name.compileToFragments(o) - else - fun = base.compileToFragments o, LEVEL_ACCESS - fun = @wrapInBraces fun if SIMPLENUM.test fragmentsToText fun - if name - ref = fragmentsToText fun - fun.push (name.compileToFragments o)... - else - ref = 'null' - answer = answer.concat fun - answer = answer.concat @makeCode(".apply(#{ref}, "), splatArgs, @makeCode(")") - #### Super # Takes care of converting `super()` calls into calls against the prototype's @@ -1042,8 +998,6 @@ exports.Arr = class Arr extends Base compileNode: (o) -> return [@makeCode '[]'] unless @objects.length o.indent += TAB - answer = Splat.compileSplattedArray o, @objects - return answer if answer.length answer = [] compiledObjs = (obj.compileToFragments o, LEVEL_LIST for obj in @objects) @@ -1847,37 +1801,11 @@ exports.Splat = class Splat extends Base @name.assigns name compileToFragments: (o) -> - @name.compileToFragments o + [ @makeCode('...') + @name.compileToFragments(o)... ] unwrap: -> @name - # Utility function that converts an arbitrary number of elements, mixed with - # splats, to a proper array. - @compileSplattedArray: (o, list, apply) -> - index = -1 - continue while (node = list[++index]) and node not instanceof Splat - return [] if index >= list.length - if list.length is 1 - node = list[0] - fragments = node.compileToFragments o, LEVEL_LIST - return fragments if apply - return [].concat node.makeCode("#{ utility 'slice', o }.call("), fragments, node.makeCode(")") - args = list[index..] - for node, i in args - compiledNode = node.compileToFragments o, LEVEL_LIST - args[i] = if node instanceof Splat - then [].concat node.makeCode("#{ utility 'slice', o }.call("), compiledNode, node.makeCode(")") - else [].concat node.makeCode("["), compiledNode, node.makeCode("]") - if index is 0 - node = list[0] - concatPart = (node.joinFragmentArrays args[1..], ', ') - return args[0].concat node.makeCode(".concat("), concatPart, node.makeCode(")") - base = (node.compileToFragments o, LEVEL_LIST for node in list[...index]) - base = list[0].joinFragmentArrays base, ', ' - concatPart = list[index].joinFragmentArrays args, ', ' - [..., last] = list - [].concat list[0].makeCode("["), base, list[index].makeCode("].concat("), concatPart, last.makeCode(")") - #### Expansion # Used to skip values inside an array destructuring (pattern matching) or diff --git a/test/arrays.coffee b/test/arrays.coffee index 5947dbd8ce..4f5686bf4d 100644 --- a/test/arrays.coffee +++ b/test/arrays.coffee @@ -107,3 +107,10 @@ test "regex interpolation in array", -> eq 2, arr.length eq 'ab', arr[0].source eq 'value', arr[1].key + +test "splat extraction from generators", -> + gen = -> + yield 1 + yield 2 + yield 3 + arrayEq [ gen()... ], [ 1, 2, 3 ]