* Elision * test * improvements * grammar optimization * cleanup
This commit is contained in:
parent
64b8dd486a
commit
f14c7ffa3f
|
@ -17,10 +17,12 @@
|
|||
// from our rules and saves it into `lib/parser.js`.
|
||||
|
||||
// The only dependency is on the **Jison.Parser**.
|
||||
var Parser, alt, alternatives, grammar, name, o, operators, token, tokens, unwrap;
|
||||
var Parser, alt, alternatives, grammar, log, name, o, operators, token, tokens, unwrap;
|
||||
|
||||
({Parser} = require('jison'));
|
||||
|
||||
log = console.log;
|
||||
|
||||
// Jison DSL
|
||||
// ---------
|
||||
|
||||
|
@ -954,9 +956,14 @@
|
|||
function() {
|
||||
return new Arr([]);
|
||||
}),
|
||||
o('[ ArgList OptComma ]',
|
||||
o('[ Elisions ]',
|
||||
function() {
|
||||
return new Arr($2);
|
||||
}),
|
||||
o('[ ArgElisionList OptElisions ]',
|
||||
function() {
|
||||
return new Arr([].concat($2,
|
||||
$3));
|
||||
})
|
||||
],
|
||||
// Inclusive and exclusive range dots.
|
||||
|
@ -1006,8 +1013,7 @@
|
|||
$1);
|
||||
})
|
||||
],
|
||||
// The **ArgList** is both the list of objects passed into a function call,
|
||||
// as well as the contents of an array literal
|
||||
// The **ArgList** is the list of objects passed into a function call
|
||||
// (i.e. comma-separated expressions). Newlines work as well.
|
||||
ArgList: [
|
||||
o('Arg',
|
||||
|
@ -1040,6 +1046,66 @@
|
|||
return new Expansion;
|
||||
})
|
||||
],
|
||||
// The **ArgElisionList** is the list of objects, contents of an array literal
|
||||
// (i.e. comma-separated expressions and elisions). Newlines work as well.
|
||||
ArgElisionList: [
|
||||
o('ArgElision'),
|
||||
o('ArgElisionList , ArgElision',
|
||||
function() {
|
||||
return $1.concat($3);
|
||||
}),
|
||||
o('ArgElisionList OptElisions TERMINATOR ArgElision',
|
||||
function() {
|
||||
return $1.concat($2,
|
||||
$4);
|
||||
}),
|
||||
o('INDENT ArgElisionList OptElisions OUTDENT',
|
||||
function() {
|
||||
return $2.concat($3);
|
||||
}),
|
||||
o('ArgElisionList OptElisions INDENT ArgElisionList OptElisions OUTDENT',
|
||||
function() {
|
||||
return $1.concat($2,
|
||||
$4,
|
||||
$5);
|
||||
})
|
||||
],
|
||||
ArgElision: [
|
||||
o('Arg',
|
||||
function() {
|
||||
return [$1];
|
||||
}),
|
||||
o('Elisions Arg',
|
||||
function() {
|
||||
return $1.concat($2);
|
||||
})
|
||||
],
|
||||
OptElisions: [
|
||||
o('OptComma',
|
||||
function() {
|
||||
return [];
|
||||
}),
|
||||
o(', Elisions',
|
||||
function() {
|
||||
return [].concat($2);
|
||||
})
|
||||
],
|
||||
Elisions: [
|
||||
o('Elision',
|
||||
function() {
|
||||
return [$1];
|
||||
}),
|
||||
o('Elisions Elision',
|
||||
function() {
|
||||
return $1.concat($2);
|
||||
})
|
||||
],
|
||||
Elision: [
|
||||
o(',',
|
||||
function() {
|
||||
return new Elision;
|
||||
})
|
||||
],
|
||||
// Just simple, comma-separated, required arguments (no fancy syntax). We need
|
||||
// this to be separate from the **ArgList** for use in **Switch** blocks, where
|
||||
// having the newlines wouldn't make sense.
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// nodes are created as the result of actions in the [grammar](grammar.html),
|
||||
// but some are created by other nodes as a method of code generation. To convert
|
||||
// the syntax tree into a string of JavaScript code, call `compile()` on the root.
|
||||
var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, CSXTag, Call, Class, Code, CodeFragment, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, FuncGlyph, HereComment, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, JS_FORBIDDEN, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, LineComment, Literal, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, SIMPLENUM, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, TAB, THIS, TaggedTemplateCall, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addDataToNode, attachCommentsToNode, compact, del, ends, extend, flatten, fragmentsToText, hasLineComments, indentInitial, isLiteralArguments, isLiteralThis, isUnassignable, locationDataToString, merge, moveComments, multident, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility,
|
||||
var Access, Arr, Assign, AwaitReturn, Base, Block, BooleanLiteral, CSXTag, Call, Class, Code, CodeFragment, Elision, ExecutableClassBody, Existence, Expansion, ExportAllDeclaration, ExportDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExportSpecifier, ExportSpecifierList, Extends, For, FuncGlyph, HereComment, HoistTarget, IdentifierLiteral, If, ImportClause, ImportDeclaration, ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier, ImportSpecifierList, In, Index, InfinityLiteral, JS_FORBIDDEN, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, LineComment, Literal, ModuleDeclaration, ModuleSpecifier, ModuleSpecifierList, NEGATE, NO, NaNLiteral, NullLiteral, NumberLiteral, Obj, Op, Param, Parens, PassthroughLiteral, PropertyName, Range, RegexLiteral, RegexWithInterpolations, Return, SIMPLENUM, Scope, Slice, Splat, StatementLiteral, StringLiteral, StringWithInterpolations, Super, SuperCall, Switch, TAB, THIS, TaggedTemplateCall, ThisLiteral, Throw, Try, UTILITIES, UndefinedLiteral, Value, While, YES, YieldReturn, addDataToNode, attachCommentsToNode, compact, del, ends, extend, flatten, fragmentsToText, hasLineComments, indentInitial, isLiteralArguments, isLiteralThis, isUnassignable, locationDataToString, merge, moveComments, multident, shouldCacheOrIsAssignable, some, starts, throwSyntaxError, unfoldSoak, unshiftAfterComments, utility,
|
||||
indexOf = [].indexOf,
|
||||
splice = [].splice,
|
||||
slice = [].slice;
|
||||
|
@ -1364,6 +1364,13 @@
|
|||
return (this.base instanceof Obj) && (!onlyGenerated || this.base.generated);
|
||||
}
|
||||
|
||||
isElision() {
|
||||
if (!(this.base instanceof Arr)) {
|
||||
return false;
|
||||
}
|
||||
return this.base.hasElision();
|
||||
}
|
||||
|
||||
isSplice() {
|
||||
var lastProp, ref1;
|
||||
ref1 = this.properties, lastProp = ref1[ref1.length - 1];
|
||||
|
@ -2335,6 +2342,18 @@
|
|||
this.objects = objs || [];
|
||||
}
|
||||
|
||||
hasElision() {
|
||||
var j, len1, obj, ref1;
|
||||
ref1 = this.objects;
|
||||
for (j = 0, len1 = ref1.length; j < len1; j++) {
|
||||
obj = ref1[j];
|
||||
if (obj instanceof Elision) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isAssignable() {
|
||||
var i, j, len1, obj, ref1;
|
||||
if (!this.objects.length) {
|
||||
|
@ -2358,11 +2377,16 @@
|
|||
}
|
||||
|
||||
compileNode(o) {
|
||||
var answer, compiledObjs, fragment, fragmentIndex, fragments, includesLineCommentsOnNonFirstElement, index, j, k, l, len1, len2, len3, len4, len5, obj, objIndex, q, r, ref1, unwrappedObj;
|
||||
var answer, compiledObjs, fragment, fragmentIndex, fragmentIsElision, fragments, includesLineCommentsOnNonFirstElement, index, j, k, l, len1, len2, len3, len4, len5, obj, objIndex, olen, passedElision, q, r, ref1, unwrappedObj;
|
||||
if (!this.objects.length) {
|
||||
return [this.makeCode('[]')];
|
||||
}
|
||||
o.indent += TAB;
|
||||
fragmentIsElision = function(fragment) {
|
||||
return fragmentsToText(fragment).trim() === ',';
|
||||
};
|
||||
// Detect if `Elisions` at the beginning of the array are processed (e.g. [, , , a]).
|
||||
passedElision = false;
|
||||
answer = [];
|
||||
ref1 = this.objects;
|
||||
for (objIndex = j = 0, len1 = ref1.length; j < len1; objIndex = ++j) {
|
||||
|
@ -2393,6 +2417,7 @@
|
|||
}
|
||||
return results;
|
||||
}).call(this);
|
||||
olen = compiledObjs.length;
|
||||
// If `compiledObjs` includes newlines, we will output this as a multiline
|
||||
// array (i.e. with a newline and indentation after the `[`). If an element
|
||||
// contains line comments, that should also trigger multiline output since
|
||||
|
@ -2411,9 +2436,12 @@
|
|||
includesLineCommentsOnNonFirstElement = true;
|
||||
}
|
||||
}
|
||||
if (index !== 0) {
|
||||
// Add ', ' if all `Elisions` from the beginning of the array are processed (e.g. [, , , a]) and
|
||||
// element isn't `Elision` or last element is `Elision` (e.g. [a,,b,,])
|
||||
if (index !== 0 && passedElision && (!fragmentIsElision(fragments) || index === olen - 1)) {
|
||||
answer.push(this.makeCode(', '));
|
||||
}
|
||||
passedElision = passedElision || !fragmentIsElision(fragments);
|
||||
answer.push(...fragments);
|
||||
}
|
||||
if (includesLineCommentsOnNonFirstElement || indexOf.call(fragmentsToText(answer), '\n') >= 0) {
|
||||
|
@ -2421,7 +2449,7 @@
|
|||
fragment = answer[fragmentIndex];
|
||||
if (fragment.isHereComment) {
|
||||
fragment.code = `${multident(fragment.code, o.indent, false)}\n${o.indent}`;
|
||||
} else if (fragment.code === ', ') {
|
||||
} else if (fragment.code === ', ' && !(fragment != null ? fragment.isElision : void 0)) {
|
||||
fragment.code = `,\n${o.indent}`;
|
||||
}
|
||||
}
|
||||
|
@ -3571,10 +3599,14 @@
|
|||
obj.error(message);
|
||||
}
|
||||
}
|
||||
if (!(obj instanceof Elision)) {
|
||||
assigns.push(new Assign(obj, val, null, {
|
||||
param: this.param,
|
||||
subpattern: true
|
||||
}).compileToFragments(o, LEVEL_LIST));
|
||||
} else {
|
||||
assigns.push(idx.compileToFragments(o, LEVEL_LIST));
|
||||
}
|
||||
}
|
||||
if (!(top || this.subpattern)) {
|
||||
assigns.push(vvar);
|
||||
|
@ -4237,6 +4269,8 @@
|
|||
// * simple destructured parameters {foo}
|
||||
iterator(obj.base.value, obj.base, this);
|
||||
}
|
||||
} else if (obj instanceof Elision) {
|
||||
obj;
|
||||
} else if (!(obj instanceof Expansion)) {
|
||||
obj.error(`illegal parameter ${obj.compile()}`);
|
||||
}
|
||||
|
@ -4332,6 +4366,38 @@
|
|||
|
||||
})();
|
||||
|
||||
//### Elision
|
||||
|
||||
// Array elision element (for example, [,a, , , b, , c, ,]).
|
||||
exports.Elision = Elision = (function() {
|
||||
class Elision extends Base {
|
||||
compileToFragments(o, level) {
|
||||
var fragment;
|
||||
fragment = super.compileToFragments(o, level);
|
||||
fragment.isElision = true;
|
||||
return fragment;
|
||||
}
|
||||
|
||||
compileNode(o) {
|
||||
return [this.makeCode(', ')];
|
||||
}
|
||||
|
||||
asReference(o) {
|
||||
return this;
|
||||
}
|
||||
|
||||
eachName(iterator) {}
|
||||
|
||||
};
|
||||
|
||||
Elision.prototype.isAssignable = YES;
|
||||
|
||||
Elision.prototype.shouldCache = NO;
|
||||
|
||||
return Elision;
|
||||
|
||||
})();
|
||||
|
||||
//### While
|
||||
|
||||
// A while loop, the only sort of low-level loop exposed by CoffeeScript. From
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -493,7 +493,8 @@ grammar =
|
|||
# The array literal.
|
||||
Array: [
|
||||
o '[ ]', -> new Arr []
|
||||
o '[ ArgList OptComma ]', -> new Arr $2
|
||||
o '[ Elisions ]', -> new Arr $2
|
||||
o '[ ArgElisionList OptElisions ]', -> new Arr [].concat $2, $3
|
||||
]
|
||||
|
||||
# Inclusive and exclusive range dots.
|
||||
|
@ -515,8 +516,7 @@ grammar =
|
|||
o 'RangeDots', -> new Range null, null, $1
|
||||
]
|
||||
|
||||
# The **ArgList** is both the list of objects passed into a function call,
|
||||
# as well as the contents of an array literal
|
||||
# The **ArgList** is the list of objects passed into a function call
|
||||
# (i.e. comma-separated expressions). Newlines work as well.
|
||||
ArgList: [
|
||||
o 'Arg', -> [$1]
|
||||
|
@ -533,6 +533,35 @@ grammar =
|
|||
o '...', -> new Expansion
|
||||
]
|
||||
|
||||
# The **ArgElisionList** is the list of objects, contents of an array literal
|
||||
# (i.e. comma-separated expressions and elisions). Newlines work as well.
|
||||
ArgElisionList: [
|
||||
o 'ArgElision'
|
||||
o 'ArgElisionList , ArgElision', -> $1.concat $3
|
||||
o 'ArgElisionList OptElisions TERMINATOR ArgElision', -> $1.concat $2, $4
|
||||
o 'INDENT ArgElisionList OptElisions OUTDENT', -> $2.concat $3
|
||||
o 'ArgElisionList OptElisions INDENT ArgElisionList OptElisions OUTDENT', -> $1.concat $2, $4, $5
|
||||
]
|
||||
|
||||
ArgElision: [
|
||||
o 'Arg', -> [$1]
|
||||
o 'Elisions Arg', -> $1.concat $2
|
||||
]
|
||||
|
||||
OptElisions: [
|
||||
o 'OptComma', -> []
|
||||
o ', Elisions', -> [].concat $2
|
||||
]
|
||||
|
||||
Elisions: [
|
||||
o 'Elision', -> [$1]
|
||||
o 'Elisions Elision', -> $1.concat $2
|
||||
]
|
||||
|
||||
Elision: [
|
||||
o ',', -> new Elision
|
||||
]
|
||||
|
||||
# Just simple, comma-separated, required arguments (no fancy syntax). We need
|
||||
# this to be separate from the **ArgList** for use in **Switch** blocks, where
|
||||
# having the newlines wouldn't make sense.
|
||||
|
|
|
@ -910,6 +910,10 @@ exports.Value = class Value extends Base
|
|||
return no if @properties.length
|
||||
(@base instanceof Obj) and (not onlyGenerated or @base.generated)
|
||||
|
||||
isElision: ->
|
||||
return no unless @base instanceof Arr
|
||||
@base.hasElision()
|
||||
|
||||
isSplice: ->
|
||||
[..., lastProp] = @properties
|
||||
lastProp instanceof Slice
|
||||
|
@ -1561,6 +1565,10 @@ exports.Arr = class Arr extends Base
|
|||
|
||||
children: ['objects']
|
||||
|
||||
hasElision: ->
|
||||
return yes for obj in @objects when obj instanceof Elision
|
||||
no
|
||||
|
||||
isAssignable: ->
|
||||
return no unless @objects.length
|
||||
|
||||
|
@ -1575,6 +1583,9 @@ exports.Arr = class Arr extends Base
|
|||
compileNode: (o) ->
|
||||
return [@makeCode '[]'] unless @objects.length
|
||||
o.indent += TAB
|
||||
fragmentIsElision = (fragment) -> fragmentsToText(fragment).trim() is ','
|
||||
# Detect if `Elisions` at the beginning of the array are processed (e.g. [, , , a]).
|
||||
passedElision = no
|
||||
|
||||
answer = []
|
||||
for obj, objIndex in @objects
|
||||
|
@ -1590,6 +1601,7 @@ exports.Arr = class Arr extends Base
|
|||
unwrappedObj.lhs = yes if unwrappedObj instanceof Arr or unwrappedObj instanceof Obj
|
||||
|
||||
compiledObjs = (obj.compileToFragments o, LEVEL_LIST for obj in @objects)
|
||||
olen = compiledObjs.length
|
||||
# If `compiledObjs` includes newlines, we will output this as a multiline
|
||||
# array (i.e. with a newline and indentation after the `[`). If an element
|
||||
# contains line comments, that should also trigger multiline output since
|
||||
|
@ -1604,14 +1616,17 @@ exports.Arr = class Arr extends Base
|
|||
fragment.code = fragment.code.trim()
|
||||
else if index isnt 0 and includesLineCommentsOnNonFirstElement is no and hasLineComments fragment
|
||||
includesLineCommentsOnNonFirstElement = yes
|
||||
if index isnt 0
|
||||
# Add ', ' if all `Elisions` from the beginning of the array are processed (e.g. [, , , a]) and
|
||||
# element isn't `Elision` or last element is `Elision` (e.g. [a,,b,,])
|
||||
if index isnt 0 and passedElision and (not fragmentIsElision(fragments) or index is olen - 1)
|
||||
answer.push @makeCode ', '
|
||||
passedElision = passedElision or not fragmentIsElision fragments
|
||||
answer.push fragments...
|
||||
if includesLineCommentsOnNonFirstElement or '\n' in fragmentsToText(answer)
|
||||
for fragment, fragmentIndex in answer
|
||||
if fragment.isHereComment
|
||||
fragment.code = "#{multident(fragment.code, o.indent, no)}\n#{o.indent}"
|
||||
else if fragment.code is ', '
|
||||
else if fragment.code is ', ' and not fragment?.isElision
|
||||
fragment.code = ",\n#{o.indent}"
|
||||
answer.unshift @makeCode "[\n#{o.indent}"
|
||||
answer.push @makeCode "\n#{@tab}]"
|
||||
|
@ -2440,7 +2455,10 @@ exports.Assign = class Assign extends Base
|
|||
if name?
|
||||
message = isUnassignable name
|
||||
obj.error message if message
|
||||
unless obj instanceof Elision
|
||||
assigns.push new Assign(obj, val, null, param: @param, subpattern: yes).compileToFragments o, LEVEL_LIST
|
||||
else
|
||||
assigns.push idx.compileToFragments o, LEVEL_LIST
|
||||
|
||||
assigns.push vvar unless top or @subpattern
|
||||
fragments = @joinFragmentArrays assigns, ', '
|
||||
|
@ -2890,6 +2908,8 @@ exports.Param = class Param extends Base
|
|||
atParam obj
|
||||
# * simple destructured parameters {foo}
|
||||
else iterator obj.base.value, obj.base, @
|
||||
else if obj instanceof Elision
|
||||
obj
|
||||
else if obj not instanceof Expansion
|
||||
obj.error "illegal parameter #{obj.compile()}"
|
||||
return
|
||||
|
@ -2946,6 +2966,28 @@ exports.Expansion = class Expansion extends Base
|
|||
|
||||
eachName: (iterator) ->
|
||||
|
||||
#### Elision
|
||||
|
||||
# Array elision element (for example, [,a, , , b, , c, ,]).
|
||||
exports.Elision = class Elision extends Base
|
||||
|
||||
isAssignable: YES
|
||||
|
||||
shouldCache: NO
|
||||
|
||||
compileToFragments: (o, level) ->
|
||||
fragment = super o, level
|
||||
fragment.isElision = yes
|
||||
fragment
|
||||
|
||||
compileNode: (o) ->
|
||||
[@makeCode ', ']
|
||||
|
||||
asReference: (o) ->
|
||||
this
|
||||
|
||||
eachName: (iterator) ->
|
||||
|
||||
#### While
|
||||
|
||||
# A while loop, the only sort of low-level loop exposed by CoffeeScript. From
|
||||
|
|
|
@ -26,6 +26,94 @@ test "incorrect indentation without commas", ->
|
|||
ok result[0][0] is 'a'
|
||||
ok result[1]['b'] is 'c'
|
||||
|
||||
# Elisions
|
||||
test "array elisions", ->
|
||||
eq [,1].length, 2
|
||||
eq [,,1,2,,].length, 5
|
||||
arr = [1,,2]
|
||||
eq arr.length, 3
|
||||
eq arr[1], undefined
|
||||
eq [,,].length, 2
|
||||
|
||||
test "array elisions indentation and commas", ->
|
||||
arr1 = [
|
||||
, 1, 2, , , 3,
|
||||
4, 5, 6
|
||||
, , 8, 9,
|
||||
]
|
||||
eq arr1.length, 12
|
||||
eq arr1[5], 3
|
||||
eq arr1[9], undefined
|
||||
arr2 = [, , 1,
|
||||
2, , 3,
|
||||
, 4, 5
|
||||
6
|
||||
, , ,
|
||||
]
|
||||
eq arr2.length, 12
|
||||
eq arr2[8], 5
|
||||
eq arr2[1], undefined
|
||||
|
||||
test "array elisions destructuring", ->
|
||||
arr = [1,2,3,4,5,6,7,8,9]
|
||||
[,a] = arr
|
||||
[,,,b] = arr
|
||||
arrayEq [a,b], [2,4]
|
||||
[,a,,b,,c,,,d] = arr
|
||||
arrayEq [a,b,c,d], [2,4,6,9]
|
||||
[
|
||||
,e,
|
||||
,f,
|
||||
,g,
|
||||
,,h] = arr
|
||||
arrayEq [e,f,g,h], [2,4,6,9]
|
||||
|
||||
test "array elisions destructuring with splats and expansions", ->
|
||||
arr = [1,2,3,4,5,6,7,8,9]
|
||||
[,a,,,b...] = arr
|
||||
arrayEq [a,b], [2,[5,6,7,8,9]]
|
||||
[,c,...,,d,,e] = arr
|
||||
arrayEq [c,d,e], [2,7,9]
|
||||
[...,e,,,f,,,] = arr
|
||||
arrayEq [e,f], [4,7]
|
||||
|
||||
test "array elisions as function parameters", ->
|
||||
arr = [1,2,3,4,5,6,7,8,9]
|
||||
foo = ([,a]) -> a
|
||||
a = foo arr
|
||||
eq a, 2
|
||||
foo = ([,,,a]) -> a
|
||||
a = foo arr
|
||||
eq a, 4
|
||||
foo = ([,a,,b,,c,,,d]) -> [a,b,c,d]
|
||||
[a,b,c,d] = foo arr
|
||||
arrayEq [a,b,c,d], [2,4,6,9]
|
||||
|
||||
test "array elisions nested destructuring", ->
|
||||
arr = [
|
||||
1,
|
||||
[2,3, [4,5,6, [7,8,9] ] ]
|
||||
]
|
||||
[,a] = arr
|
||||
arrayEq a[2][3], [7,8,9]
|
||||
[,[,,[,b,,[,,c]]]] = arr
|
||||
eq b, 5
|
||||
eq c, 9
|
||||
aobj = [
|
||||
{},
|
||||
{x: 2},
|
||||
{},
|
||||
[
|
||||
{},
|
||||
{},
|
||||
{z:1, w:[1,2,4], p:3, q:4}
|
||||
{},
|
||||
{}
|
||||
]
|
||||
]
|
||||
[,d,,[,,{w}]] = aobj
|
||||
deepEqual d, {x:2}
|
||||
arrayEq w, [1,2,4]
|
||||
|
||||
# Splats in Array Literals
|
||||
|
||||
|
|
Loading…
Reference in New Issue