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 ];
This commit is contained in:
Chris Connelly 2016-11-06 16:30:04 +00:00 committed by Geoffrey Booth
parent 496fd5d3d3
commit 663595ba94
7 changed files with 56 additions and 203 deletions

View File

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

View File

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

View File

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

View File

@ -95,7 +95,7 @@
if (tuple.length < 3) {
tuple.unshift(null);
}
results.push(buildRule.apply(null, tuple));
results.push(buildRule(...tuple));
}
return results;
};

View File

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

View File

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

View File

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