Implement ES2015-like destructuring defaults
This let's you do things like: fullName = ({first = 'John', last = 'Doe'}) -> "#{first} #{last}" Note: CoffeeScrits treats `undefined` and `null` the same, and that's true in the case of destructuring defaults as well, as opposed to ES2015 which only uses the default value if the target is `undefined`. A similar ES2015 difference already exists for function parameter defaults. It is important for CoffeeScript to be consistent with itself. fullName2 = (first = 'John', last = 'Doe') -> "#{first} #{last}" assert fullName('Bob', null) is fullName2(first: 'Bob', last: null) Fixes #1558, #3288 and #4005.
This commit is contained in:
parent
66716cd730
commit
6d9553a016
|
@ -108,9 +108,18 @@
|
||||||
return new Assign(LOC(1)(new Value($1)), $3, 'object');
|
return new Assign(LOC(1)(new Value($1)), $3, 'object');
|
||||||
}), o('ObjAssignable : INDENT Expression OUTDENT', function() {
|
}), o('ObjAssignable : INDENT Expression OUTDENT', function() {
|
||||||
return new Assign(LOC(1)(new Value($1)), $4, 'object');
|
return new Assign(LOC(1)(new Value($1)), $4, 'object');
|
||||||
|
}), o('SimpleObjAssignable = Expression', function() {
|
||||||
|
return new Assign(LOC(1)(new Value($1)), $3, null, {
|
||||||
|
operatorToken: LOC(2)(new Literal($2))
|
||||||
|
});
|
||||||
|
}), o('SimpleObjAssignable = INDENT Expression OUTDENT', function() {
|
||||||
|
return new Assign(LOC(1)(new Value($1)), $4, null, {
|
||||||
|
operatorToken: LOC(2)(new Literal($2))
|
||||||
|
});
|
||||||
}), o('Comment')
|
}), o('Comment')
|
||||||
],
|
],
|
||||||
ObjAssignable: [o('Identifier'), o('AlphaNumeric'), o('ThisProperty')],
|
SimpleObjAssignable: [o('Identifier'), o('ThisProperty')],
|
||||||
|
ObjAssignable: [o('SimpleObjAssignable'), o('AlphaNumeric')],
|
||||||
Return: [
|
Return: [
|
||||||
o('RETURN Expression', function() {
|
o('RETURN Expression', function() {
|
||||||
return new Return($2);
|
return new Return($2);
|
||||||
|
|
|
@ -1310,8 +1310,13 @@
|
||||||
if (hasDynamic && i < dynamicIndex) {
|
if (hasDynamic && i < dynamicIndex) {
|
||||||
indent += TAB;
|
indent += TAB;
|
||||||
}
|
}
|
||||||
if (prop instanceof Assign && prop.variable instanceof Value && prop.variable.hasProperties()) {
|
if (prop instanceof Assign) {
|
||||||
prop.variable.error('invalid object key');
|
if (prop.context !== 'object') {
|
||||||
|
prop.operatorToken.error("unexpected " + prop.operatorToken.value);
|
||||||
|
}
|
||||||
|
if (prop.variable instanceof Value && prop.variable.hasProperties()) {
|
||||||
|
prop.variable.error('invalid object key');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (prop instanceof Value && prop["this"]) {
|
if (prop instanceof Value && prop["this"]) {
|
||||||
prop = new Assign(prop.properties[0].name, prop, 'object');
|
prop = new Assign(prop.properties[0].name, prop, 'object');
|
||||||
|
@ -1631,8 +1636,10 @@
|
||||||
this.variable = variable1;
|
this.variable = variable1;
|
||||||
this.value = value1;
|
this.value = value1;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.param = options && options.param;
|
if (options == null) {
|
||||||
this.subpattern = options && options.subpattern;
|
options = {};
|
||||||
|
}
|
||||||
|
this.param = options.param, this.subpattern = options.subpattern, this.operatorToken = options.operatorToken;
|
||||||
forbidden = (ref3 = (name = this.variable.unwrapAll().value), indexOf.call(STRICT_PROSCRIBED, ref3) >= 0);
|
forbidden = (ref3 = (name = this.variable.unwrapAll().value), indexOf.call(STRICT_PROSCRIBED, ref3) >= 0);
|
||||||
if (forbidden && this.context !== 'object') {
|
if (forbidden && this.context !== 'object') {
|
||||||
this.variable.error("variable name may not be \"" + name + "\"");
|
this.variable.error("variable name may not be \"" + name + "\"");
|
||||||
|
@ -1713,7 +1720,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
Assign.prototype.compilePatternMatch = function(o) {
|
Assign.prototype.compilePatternMatch = function(o) {
|
||||||
var acc, assigns, code, expandedIdx, fragments, i, idx, isObject, ivar, j, len1, name, obj, objects, olen, ref, ref3, ref4, ref5, ref6, ref7, ref8, rest, top, val, value, vvar, vvarText;
|
var acc, assigns, code, defaultValue, expandedIdx, fragments, i, idx, isObject, ivar, j, len1, name, obj, objects, olen, ref, ref3, ref4, ref5, ref6, ref7, rest, top, val, value, vvar, vvarText;
|
||||||
top = o.level === LEVEL_TOP;
|
top = o.level === LEVEL_TOP;
|
||||||
value = this.value;
|
value = this.value;
|
||||||
objects = this.variable.base.objects;
|
objects = this.variable.base.objects;
|
||||||
|
@ -1731,17 +1738,29 @@
|
||||||
}
|
}
|
||||||
isObject = this.variable.isObject();
|
isObject = this.variable.isObject();
|
||||||
if (top && olen === 1 && !(obj instanceof Splat)) {
|
if (top && olen === 1 && !(obj instanceof Splat)) {
|
||||||
if (obj instanceof Assign) {
|
defaultValue = null;
|
||||||
|
if (obj instanceof Assign && obj.context === 'object') {
|
||||||
ref3 = obj, (ref4 = ref3.variable, idx = ref4.base), obj = ref3.value;
|
ref3 = obj, (ref4 = ref3.variable, idx = ref4.base), obj = ref3.value;
|
||||||
|
if (obj instanceof Assign) {
|
||||||
|
defaultValue = obj.value;
|
||||||
|
obj = obj.variable;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (obj instanceof Assign) {
|
||||||
|
defaultValue = obj.value;
|
||||||
|
obj = obj.variable;
|
||||||
|
}
|
||||||
idx = isObject ? obj["this"] ? obj.properties[0].name : obj : new Literal(0);
|
idx = isObject ? obj["this"] ? obj.properties[0].name : obj : new Literal(0);
|
||||||
}
|
}
|
||||||
acc = IDENTIFIER.test(idx.unwrap().value || 0);
|
acc = IDENTIFIER.test(idx.unwrap().value);
|
||||||
value = new Value(value);
|
value = new Value(value);
|
||||||
value.properties.push(new (acc ? Access : Index)(idx));
|
value.properties.push(new (acc ? Access : Index)(idx));
|
||||||
if (ref5 = obj.unwrap().value, indexOf.call(RESERVED, ref5) >= 0) {
|
if (ref5 = obj.unwrap().value, indexOf.call(RESERVED, ref5) >= 0) {
|
||||||
obj.error("assignment to a reserved word: " + (obj.compile(o)));
|
obj.error("assignment to a reserved word: " + (obj.compile(o)));
|
||||||
}
|
}
|
||||||
|
if (defaultValue) {
|
||||||
|
value = new Op('?', value, defaultValue);
|
||||||
|
}
|
||||||
return new Assign(obj, value, null, {
|
return new Assign(obj, value, null, {
|
||||||
param: this.param
|
param: this.param
|
||||||
}).compileToFragments(o, LEVEL_TOP);
|
}).compileToFragments(o, LEVEL_TOP);
|
||||||
|
@ -1758,17 +1777,6 @@
|
||||||
for (i = j = 0, len1 = objects.length; j < len1; i = ++j) {
|
for (i = j = 0, len1 = objects.length; j < len1; i = ++j) {
|
||||||
obj = objects[i];
|
obj = objects[i];
|
||||||
idx = i;
|
idx = i;
|
||||||
if (isObject) {
|
|
||||||
if (obj instanceof Assign) {
|
|
||||||
ref6 = obj, (ref7 = ref6.variable, idx = ref7.base), obj = ref6.value;
|
|
||||||
} else {
|
|
||||||
if (obj.base instanceof Parens) {
|
|
||||||
ref8 = new Value(obj.unwrapAll()).cacheReference(o), obj = ref8[0], idx = ref8[1];
|
|
||||||
} else {
|
|
||||||
idx = obj["this"] ? obj.properties[0].name : obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!expandedIdx && obj instanceof Splat) {
|
if (!expandedIdx && obj instanceof Splat) {
|
||||||
name = obj.name.unwrap().value;
|
name = obj.name.unwrap().value;
|
||||||
obj = obj.unwrap();
|
obj = obj.unwrap();
|
||||||
|
@ -1798,17 +1806,29 @@
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
name = obj.unwrap().value;
|
|
||||||
if (obj instanceof Splat || obj instanceof Expansion) {
|
if (obj instanceof Splat || obj instanceof Expansion) {
|
||||||
obj.error("multiple splats/expansions are disallowed in an assignment");
|
obj.error("multiple splats/expansions are disallowed in an assignment");
|
||||||
}
|
}
|
||||||
if (typeof idx === 'number') {
|
defaultValue = null;
|
||||||
idx = new Literal(expandedIdx || idx);
|
if (obj instanceof Assign && obj.context === 'object') {
|
||||||
acc = false;
|
ref6 = obj, (ref7 = ref6.variable, idx = ref7.base), obj = ref6.value;
|
||||||
|
if (obj instanceof Assign) {
|
||||||
|
defaultValue = obj.value;
|
||||||
|
obj = obj.variable;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
acc = isObject && IDENTIFIER.test(idx.unwrap().value || 0);
|
if (obj instanceof Assign) {
|
||||||
|
defaultValue = obj.value;
|
||||||
|
obj = obj.variable;
|
||||||
|
}
|
||||||
|
idx = isObject ? obj["this"] ? obj.properties[0].name : obj : new Literal(expandedIdx || idx);
|
||||||
}
|
}
|
||||||
|
name = obj.unwrap().value;
|
||||||
|
acc = IDENTIFIER.test(idx.unwrap().value);
|
||||||
val = new Value(new Literal(vvarText), [new (acc ? Access : Index)(idx)]);
|
val = new Value(new Literal(vvarText), [new (acc ? Access : Index)(idx)]);
|
||||||
|
if (defaultValue) {
|
||||||
|
val = new Op('?', val, defaultValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ((name != null) && indexOf.call(RESERVED, name) >= 0) {
|
if ((name != null) && indexOf.call(RESERVED, name) >= 0) {
|
||||||
obj.error("assignment to a reserved word: " + (obj.compile(o)));
|
obj.error("assignment to a reserved word: " + (obj.compile(o)));
|
||||||
|
@ -2129,6 +2149,9 @@
|
||||||
ref3 = name.objects;
|
ref3 = name.objects;
|
||||||
for (j = 0, len1 = ref3.length; j < len1; j++) {
|
for (j = 0, len1 = ref3.length; j < len1; j++) {
|
||||||
obj = ref3[j];
|
obj = ref3[j];
|
||||||
|
if (obj instanceof Assign && (obj.context == null)) {
|
||||||
|
obj = obj.variable;
|
||||||
|
}
|
||||||
if (obj instanceof Assign) {
|
if (obj instanceof Assign) {
|
||||||
this.eachName(iterator, obj.value.unwrap());
|
this.eachName(iterator, obj.value.unwrap());
|
||||||
} else if (obj instanceof Splat) {
|
} else if (obj instanceof Splat) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -168,18 +168,27 @@ grammar =
|
||||||
# the ordinary **Assign** is that these allow numbers and strings as keys.
|
# the ordinary **Assign** is that these allow numbers and strings as keys.
|
||||||
AssignObj: [
|
AssignObj: [
|
||||||
o 'ObjAssignable', -> new Value $1
|
o 'ObjAssignable', -> new Value $1
|
||||||
o 'ObjAssignable : Expression', -> new Assign LOC(1)(new Value($1)), $3, 'object'
|
o 'ObjAssignable : Expression', -> new Assign LOC(1)(new Value $1), $3, 'object'
|
||||||
o 'ObjAssignable :
|
o 'ObjAssignable :
|
||||||
INDENT Expression OUTDENT', -> new Assign LOC(1)(new Value($1)), $4, 'object'
|
INDENT Expression OUTDENT', -> new Assign LOC(1)(new Value $1), $4, 'object'
|
||||||
|
o 'SimpleObjAssignable = Expression', -> new Assign LOC(1)(new Value $1), $3, null,
|
||||||
|
operatorToken: LOC(2)(new Literal $2)
|
||||||
|
o 'SimpleObjAssignable =
|
||||||
|
INDENT Expression OUTDENT', -> new Assign LOC(1)(new Value $1), $4, null,
|
||||||
|
operatorToken: LOC(2)(new Literal $2)
|
||||||
o 'Comment'
|
o 'Comment'
|
||||||
]
|
]
|
||||||
|
|
||||||
ObjAssignable: [
|
SimpleObjAssignable: [
|
||||||
o 'Identifier'
|
o 'Identifier'
|
||||||
o 'AlphaNumeric'
|
|
||||||
o 'ThisProperty'
|
o 'ThisProperty'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
ObjAssignable: [
|
||||||
|
o 'SimpleObjAssignable'
|
||||||
|
o 'AlphaNumeric'
|
||||||
|
]
|
||||||
|
|
||||||
# A return statement from a function body.
|
# A return statement from a function body.
|
||||||
Return: [
|
Return: [
|
||||||
o 'RETURN Expression', -> new Return $2
|
o 'RETURN Expression', -> new Return $2
|
||||||
|
|
|
@ -951,8 +951,11 @@ exports.Obj = class Obj extends Base
|
||||||
',\n'
|
',\n'
|
||||||
indent = if prop instanceof Comment then '' else idt
|
indent = if prop instanceof Comment then '' else idt
|
||||||
indent += TAB if hasDynamic and i < dynamicIndex
|
indent += TAB if hasDynamic and i < dynamicIndex
|
||||||
if prop instanceof Assign and prop.variable instanceof Value and prop.variable.hasProperties()
|
if prop instanceof Assign
|
||||||
prop.variable.error 'invalid object key'
|
if prop.context isnt 'object'
|
||||||
|
prop.operatorToken.error "unexpected #{prop.operatorToken.value}"
|
||||||
|
if prop.variable instanceof Value and prop.variable.hasProperties()
|
||||||
|
prop.variable.error 'invalid object key'
|
||||||
if prop instanceof Value and prop.this
|
if prop instanceof Value and prop.this
|
||||||
prop = new Assign prop.properties[0].name, prop, 'object'
|
prop = new Assign prop.properties[0].name, prop, 'object'
|
||||||
if prop not instanceof Comment
|
if prop not instanceof Comment
|
||||||
|
@ -1168,9 +1171,8 @@ exports.Class = class Class extends Base
|
||||||
# The **Assign** is used to assign a local variable to value, or to set the
|
# The **Assign** is used to assign a local variable to value, or to set the
|
||||||
# property of an object -- including within object literals.
|
# property of an object -- including within object literals.
|
||||||
exports.Assign = class Assign extends Base
|
exports.Assign = class Assign extends Base
|
||||||
constructor: (@variable, @value, @context, options) ->
|
constructor: (@variable, @value, @context, options = {}) ->
|
||||||
@param = options and options.param
|
{@param, @subpattern, @operatorToken} = options
|
||||||
@subpattern = options and options.subpattern
|
|
||||||
forbidden = (name = @variable.unwrapAll().value) in STRICT_PROSCRIBED
|
forbidden = (name = @variable.unwrapAll().value) in STRICT_PROSCRIBED
|
||||||
if forbidden and @context isnt 'object'
|
if forbidden and @context isnt 'object'
|
||||||
@variable.error "variable name may not be \"#{name}\""
|
@variable.error "variable name may not be \"#{name}\""
|
||||||
|
@ -1225,8 +1227,6 @@ exports.Assign = class Assign extends Base
|
||||||
|
|
||||||
# Brief implementation of recursive pattern matching, when assigning array or
|
# Brief implementation of recursive pattern matching, when assigning array or
|
||||||
# object literals to a value. Peeks at their properties to assign inner names.
|
# object literals to a value. Peeks at their properties to assign inner names.
|
||||||
# See the [ECMAScript Harmony Wiki](http://wiki.ecmascript.org/doku.php?id=harmony:destructuring)
|
|
||||||
# for details.
|
|
||||||
compilePatternMatch: (o) ->
|
compilePatternMatch: (o) ->
|
||||||
top = o.level is LEVEL_TOP
|
top = o.level is LEVEL_TOP
|
||||||
{value} = this
|
{value} = this
|
||||||
|
@ -1239,19 +1239,31 @@ exports.Assign = class Assign extends Base
|
||||||
obj.error 'Destructuring assignment has no target'
|
obj.error 'Destructuring assignment has no target'
|
||||||
isObject = @variable.isObject()
|
isObject = @variable.isObject()
|
||||||
if top and olen is 1 and obj not instanceof Splat
|
if top and olen is 1 and obj not instanceof Splat
|
||||||
# Unroll simplest cases: `{v} = x` -> `v = x.v`
|
# Pick the property straight off the value when there’s just one to pick
|
||||||
if obj instanceof Assign
|
# (no need to cache the value into a variable).
|
||||||
|
defaultValue = null
|
||||||
|
if obj instanceof Assign and obj.context is 'object'
|
||||||
|
# A regular object pattern-match.
|
||||||
{variable: {base: idx}, value: obj} = obj
|
{variable: {base: idx}, value: obj} = obj
|
||||||
|
if obj instanceof Assign
|
||||||
|
defaultValue = obj.value
|
||||||
|
obj = obj.variable
|
||||||
else
|
else
|
||||||
|
if obj instanceof Assign
|
||||||
|
defaultValue = obj.value
|
||||||
|
obj = obj.variable
|
||||||
idx = if isObject
|
idx = if isObject
|
||||||
|
# A shorthand `{a, b, @c} = val` pattern-match.
|
||||||
if obj.this then obj.properties[0].name else obj
|
if obj.this then obj.properties[0].name else obj
|
||||||
else
|
else
|
||||||
|
# A regular array pattern-match.
|
||||||
new Literal 0
|
new Literal 0
|
||||||
acc = IDENTIFIER.test idx.unwrap().value or 0
|
acc = IDENTIFIER.test idx.unwrap().value
|
||||||
value = new Value value
|
value = new Value value
|
||||||
value.properties.push new (if acc then Access else Index) idx
|
value.properties.push new (if acc then Access else Index) idx
|
||||||
if obj.unwrap().value in RESERVED
|
if obj.unwrap().value in RESERVED
|
||||||
obj.error "assignment to a reserved word: #{obj.compile o}"
|
obj.error "assignment to a reserved word: #{obj.compile o}"
|
||||||
|
value = new Op '?', value, defaultValue if defaultValue
|
||||||
return new Assign(obj, value, null, param: @param).compileToFragments o, LEVEL_TOP
|
return new Assign(obj, value, null, param: @param).compileToFragments o, LEVEL_TOP
|
||||||
vvar = value.compileToFragments o, LEVEL_LIST
|
vvar = value.compileToFragments o, LEVEL_LIST
|
||||||
vvarText = fragmentsToText vvar
|
vvarText = fragmentsToText vvar
|
||||||
|
@ -1263,18 +1275,7 @@ exports.Assign = class Assign extends Base
|
||||||
vvar = [@makeCode ref]
|
vvar = [@makeCode ref]
|
||||||
vvarText = ref
|
vvarText = ref
|
||||||
for obj, i in objects
|
for obj, i in objects
|
||||||
# A regular array pattern-match.
|
|
||||||
idx = i
|
idx = i
|
||||||
if isObject
|
|
||||||
if obj instanceof Assign
|
|
||||||
# A regular object pattern-match.
|
|
||||||
{variable: {base: idx}, value: obj} = obj
|
|
||||||
else
|
|
||||||
# A shorthand `{a, b, @c} = val` pattern-match.
|
|
||||||
if obj.base instanceof Parens
|
|
||||||
[obj, idx] = new Value(obj.unwrapAll()).cacheReference o
|
|
||||||
else
|
|
||||||
idx = if obj.this then obj.properties[0].name else obj
|
|
||||||
if not expandedIdx and obj instanceof Splat
|
if not expandedIdx and obj instanceof Splat
|
||||||
name = obj.name.unwrap().value
|
name = obj.name.unwrap().value
|
||||||
obj = obj.unwrap()
|
obj = obj.unwrap()
|
||||||
|
@ -1297,15 +1298,29 @@ exports.Assign = class Assign extends Base
|
||||||
assigns.push val.compileToFragments o, LEVEL_LIST
|
assigns.push val.compileToFragments o, LEVEL_LIST
|
||||||
continue
|
continue
|
||||||
else
|
else
|
||||||
name = obj.unwrap().value
|
|
||||||
if obj instanceof Splat or obj instanceof Expansion
|
if obj instanceof Splat or obj instanceof Expansion
|
||||||
obj.error "multiple splats/expansions are disallowed in an assignment"
|
obj.error "multiple splats/expansions are disallowed in an assignment"
|
||||||
if typeof idx is 'number'
|
defaultValue = null
|
||||||
idx = new Literal expandedIdx or idx
|
if obj instanceof Assign and obj.context is 'object'
|
||||||
acc = no
|
# A regular object pattern-match.
|
||||||
|
{variable: {base: idx}, value: obj} = obj
|
||||||
|
if obj instanceof Assign
|
||||||
|
defaultValue = obj.value
|
||||||
|
obj = obj.variable
|
||||||
else
|
else
|
||||||
acc = isObject and IDENTIFIER.test idx.unwrap().value or 0
|
if obj instanceof Assign
|
||||||
|
defaultValue = obj.value
|
||||||
|
obj = obj.variable
|
||||||
|
idx = if isObject
|
||||||
|
# A shorthand `{a, b, @c} = val` pattern-match.
|
||||||
|
if obj.this then obj.properties[0].name else obj
|
||||||
|
else
|
||||||
|
# A regular array pattern-match.
|
||||||
|
new Literal expandedIdx or idx
|
||||||
|
name = obj.unwrap().value
|
||||||
|
acc = IDENTIFIER.test idx.unwrap().value
|
||||||
val = new Value new Literal(vvarText), [new (if acc then Access else Index) idx]
|
val = new Value new Literal(vvarText), [new (if acc then Access else Index) idx]
|
||||||
|
val = new Op '?', val, defaultValue if defaultValue
|
||||||
if name? and name in RESERVED
|
if name? and name in RESERVED
|
||||||
obj.error "assignment to a reserved word: #{obj.compile o}"
|
obj.error "assignment to a reserved word: #{obj.compile o}"
|
||||||
assigns.push new Assign(obj, val, null, param: @param, subpattern: yes).compileToFragments o, LEVEL_LIST
|
assigns.push new Assign(obj, val, null, param: @param, subpattern: yes).compileToFragments o, LEVEL_LIST
|
||||||
|
@ -1503,6 +1518,9 @@ exports.Param = class Param extends Base
|
||||||
# * at-params `@foo`
|
# * at-params `@foo`
|
||||||
return atParam name if name instanceof Value
|
return atParam name if name instanceof Value
|
||||||
for obj in name.objects
|
for obj in name.objects
|
||||||
|
# * destructured parameter with default value
|
||||||
|
if obj instanceof Assign and not obj.context?
|
||||||
|
obj = obj.variable
|
||||||
# * assignments within destructured parameters `{foo:bar}`
|
# * assignments within destructured parameters `{foo:bar}`
|
||||||
if obj instanceof Assign
|
if obj instanceof Assign
|
||||||
@eachName iterator, obj.value.unwrap()
|
@eachName iterator, obj.value.unwrap()
|
||||||
|
|
|
@ -299,6 +299,99 @@ test "destructuring with dynamic keys", ->
|
||||||
eq 3, c
|
eq 3, c
|
||||||
throws -> CoffeeScript.compile '{"#{a}"} = b'
|
throws -> CoffeeScript.compile '{"#{a}"} = b'
|
||||||
|
|
||||||
|
test "simple array destructuring defaults", ->
|
||||||
|
[a = 1] = []
|
||||||
|
eq 1, a
|
||||||
|
[a = 2] = [undefined]
|
||||||
|
eq 2, a
|
||||||
|
[a = 3] = [null]
|
||||||
|
eq 3, a
|
||||||
|
[a = 4] = [0]
|
||||||
|
eq 0, a
|
||||||
|
arr = [a = 5]
|
||||||
|
eq 5, a
|
||||||
|
arrayEq [5], arr
|
||||||
|
|
||||||
|
test "simple object destructuring defaults", ->
|
||||||
|
{b = 1} = {}
|
||||||
|
eq b, 1
|
||||||
|
{b = 2} = {b: undefined}
|
||||||
|
eq b, 2
|
||||||
|
{b = 3} = {b: null}
|
||||||
|
eq b, 3
|
||||||
|
{b = 4} = {b: 0}
|
||||||
|
eq b, 0
|
||||||
|
|
||||||
|
{b: c = 1} = {}
|
||||||
|
eq c, 1
|
||||||
|
{b: c = 2} = {b: undefined}
|
||||||
|
eq c, 2
|
||||||
|
{b: c = 3} = {b: null}
|
||||||
|
eq c, 3
|
||||||
|
{b: c = 4} = {b: 0}
|
||||||
|
eq c, 0
|
||||||
|
|
||||||
|
test "multiple array destructuring defaults", ->
|
||||||
|
[a = 1, b = 2, c] = [null, 12, 13]
|
||||||
|
eq a, 1
|
||||||
|
eq b, 12
|
||||||
|
eq c, 13
|
||||||
|
[a, b = 2, c = 3] = [null, 12, 13]
|
||||||
|
eq a, null
|
||||||
|
eq b, 12
|
||||||
|
eq c, 13
|
||||||
|
[a = 1, b, c = 3] = [11, 12]
|
||||||
|
eq a, 11
|
||||||
|
eq b, 12
|
||||||
|
eq c, 3
|
||||||
|
|
||||||
|
test "multiple object destructuring defaults", ->
|
||||||
|
{a = 1, b: bb = 2, 'c': c = 3, "#{0}": d = 4} = {"#{'b'}": 12}
|
||||||
|
eq a, 1
|
||||||
|
eq bb, 12
|
||||||
|
eq c, 3
|
||||||
|
eq d, 4
|
||||||
|
|
||||||
|
test "array destructuring defaults with splats", ->
|
||||||
|
[..., a = 9] = []
|
||||||
|
eq a, 9
|
||||||
|
[..., b = 9] = [19]
|
||||||
|
eq b, 19
|
||||||
|
|
||||||
|
test "deep destructuring assignment with defaults", ->
|
||||||
|
[a, [{b = 1, c = 3}] = [c: 2]] = [0]
|
||||||
|
eq a, 0
|
||||||
|
eq b, 1
|
||||||
|
eq c, 2
|
||||||
|
|
||||||
|
test "destructuring assignment with context (@) properties and defaults", ->
|
||||||
|
a={}; b={}; c={}; d={}; e={}
|
||||||
|
obj =
|
||||||
|
fn: () ->
|
||||||
|
local = [a, {b, c: null}, d]
|
||||||
|
[@a, {b: @b = b, @c = c}, @d, @e = e] = local
|
||||||
|
eq undefined, obj[key] for key in ['a','b','c','d','e']
|
||||||
|
obj.fn()
|
||||||
|
eq a, obj.a
|
||||||
|
eq b, obj.b
|
||||||
|
eq c, obj.c
|
||||||
|
eq d, obj.d
|
||||||
|
eq e, obj.e
|
||||||
|
|
||||||
|
test "destructuring assignment with defaults single evaluation", ->
|
||||||
|
callCount = 0
|
||||||
|
fn = -> callCount++
|
||||||
|
[a = fn()] = []
|
||||||
|
eq 0, a
|
||||||
|
eq 1, callCount
|
||||||
|
[a = fn()] = [10]
|
||||||
|
eq 10, a
|
||||||
|
eq 1, callCount
|
||||||
|
{a = fn(), b: c = fn()} = {a: 20, b: null}
|
||||||
|
eq 20, a
|
||||||
|
eq c, 1
|
||||||
|
eq callCount, 2
|
||||||
|
|
||||||
|
|
||||||
# Existential Assignment
|
# Existential Assignment
|
||||||
|
|
||||||
|
|
|
@ -776,6 +776,22 @@ test "invalid object keys", ->
|
||||||
@a: 1
|
@a: 1
|
||||||
^^
|
^^
|
||||||
'''
|
'''
|
||||||
|
assertErrorFormat '''
|
||||||
|
{a=2}
|
||||||
|
''', '''
|
||||||
|
[stdin]:1:3: error: unexpected =
|
||||||
|
{a=2}
|
||||||
|
^
|
||||||
|
'''
|
||||||
|
|
||||||
|
test "invalid destructuring default target", ->
|
||||||
|
assertErrorFormat '''
|
||||||
|
{'a' = 2} = obj
|
||||||
|
''', '''
|
||||||
|
[stdin]:1:6: error: unexpected =
|
||||||
|
{'a' = 2} = obj
|
||||||
|
^
|
||||||
|
'''
|
||||||
|
|
||||||
test "#4070: lone expansion", ->
|
test "#4070: lone expansion", ->
|
||||||
assertErrorFormat '''
|
assertErrorFormat '''
|
||||||
|
|
|
@ -140,6 +140,33 @@ test "destructuring in function definition", ->
|
||||||
eq 2, c
|
eq 2, c
|
||||||
) {a: [1], c: 2}
|
) {a: [1], c: 2}
|
||||||
|
|
||||||
|
context = {}
|
||||||
|
(([{a: [b, c = 2], @d, e = 4}]...) ->
|
||||||
|
eq 1, b
|
||||||
|
eq 2, c
|
||||||
|
eq @d, 3
|
||||||
|
eq context.d, 3
|
||||||
|
eq e, 4
|
||||||
|
).call context, {a: [1], d: 3}
|
||||||
|
|
||||||
|
ajax = (url, {
|
||||||
|
async = true,
|
||||||
|
beforeSend = (->),
|
||||||
|
cache = true,
|
||||||
|
method = 'get',
|
||||||
|
data = {}
|
||||||
|
}) ->
|
||||||
|
{url, async, beforeSend, cache, method, data}
|
||||||
|
|
||||||
|
fn = ->
|
||||||
|
deepEqual ajax('/home', beforeSend: fn, cache: null, method: 'post'), {
|
||||||
|
url: '/home', async: true, beforeSend: fn, cache: true, method: 'post', data: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
test "#4005: `([a = {}]..., b) ->` weirdness", ->
|
||||||
|
fn = ([a = {}]..., b) -> [a, b]
|
||||||
|
deepEqual fn(5), [{}, 5]
|
||||||
|
|
||||||
test "default values", ->
|
test "default values", ->
|
||||||
nonceA = {}
|
nonceA = {}
|
||||||
nonceB = {}
|
nonceB = {}
|
||||||
|
|
|
@ -77,12 +77,18 @@ test "duplicate formal parameters are prohibited", ->
|
||||||
strict '(@_,@_)->', 'two @params'
|
strict '(@_,@_)->', 'two @params'
|
||||||
strict '(@case,@case)->', 'two @reserved'
|
strict '(@case,@case)->', 'two @reserved'
|
||||||
strict '(_,{_})->', 'param, {param}'
|
strict '(_,{_})->', 'param, {param}'
|
||||||
|
strict '(_,{_=true})->', 'param, {param=}'
|
||||||
strict '({_,_})->', '{param, param}'
|
strict '({_,_})->', '{param, param}'
|
||||||
|
strict '({_=true,_})->', '{param=, param}'
|
||||||
strict '(_,[_])->', 'param, [param]'
|
strict '(_,[_])->', 'param, [param]'
|
||||||
|
strict '(_,[_=true])->', 'param, [param=]'
|
||||||
strict '([_,_])->', '[param, param]'
|
strict '([_,_])->', '[param, param]'
|
||||||
|
strict '([_=true,_])->', '[param=, param]'
|
||||||
strict '(_,[_]=true)->', 'param, [param]='
|
strict '(_,[_]=true)->', 'param, [param]='
|
||||||
|
strict '(_,[_=true]=true)->', 'param, [param=]='
|
||||||
strict '(_,[@_,{_}])->', 'param, [@param, {param}]'
|
strict '(_,[@_,{_}])->', 'param, [@param, {param}]'
|
||||||
strict '(_,[_,{@_}])->', 'param, [param, {@param}]'
|
strict '(_,[_,{@_}])->', 'param, [param, {@param}]'
|
||||||
|
strict '(_,[_,{@_=true}])->', 'param, [param, {@param=}]'
|
||||||
strict '(_,[_,{_}])->', 'param, [param, {param}]'
|
strict '(_,[_,{_}])->', 'param, [param, {param}]'
|
||||||
strict '(_,[_,{__}])->', 'param, [param, {param2}]'
|
strict '(_,[_,{__}])->', 'param, [param, {param2}]'
|
||||||
strict '(_,[__,{_}])->', 'param, [param2, {param}]'
|
strict '(_,[__,{_}])->', 'param, [param2, {param}]'
|
||||||
|
@ -95,7 +101,9 @@ test "duplicate formal parameters are prohibited", ->
|
||||||
strictOk '(_,@_ = true)->'
|
strictOk '(_,@_ = true)->'
|
||||||
strictOk '(@_,{_})->'
|
strictOk '(@_,{_})->'
|
||||||
strictOk '({_,@_})->'
|
strictOk '({_,@_})->'
|
||||||
|
strictOk '({_,@_ = true})->'
|
||||||
strictOk '([_,@_])->'
|
strictOk '([_,@_])->'
|
||||||
|
strictOk '([_,@_ = true])->'
|
||||||
strictOk '({},_arg)->'
|
strictOk '({},_arg)->'
|
||||||
strictOk '({},{})->'
|
strictOk '({},{})->'
|
||||||
strictOk '([]...,_arg)->'
|
strictOk '([]...,_arg)->'
|
||||||
|
|
Loading…
Reference in New Issue