mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
Merge pull request #1666 from geraldalewis/1643_splatted_access
#1643 splatted access
This commit is contained in:
commit
a0d4242da4
3 changed files with 65 additions and 32 deletions
|
@ -1044,6 +1044,7 @@
|
|||
this.value = value;
|
||||
this.context = context;
|
||||
this.param = options && options.param;
|
||||
this.subpattern = options && options.subpattern;
|
||||
}
|
||||
Assign.prototype.children = ['variable', 'value'];
|
||||
Assign.prototype.isStatement = function(o) {
|
||||
|
@ -1056,7 +1057,7 @@
|
|||
return unfoldSoak(o, this, 'variable');
|
||||
};
|
||||
Assign.prototype.compileNode = function(o) {
|
||||
var isValue, match, name, val, _ref2, _ref3, _ref4, _ref5;
|
||||
var isValue, match, name, val, varBase, _ref2, _ref3, _ref4, _ref5;
|
||||
if (isValue = this.variable instanceof Value) {
|
||||
if (this.variable.isArray() || this.variable.isObject()) {
|
||||
return this.compilePatternMatch(o);
|
||||
|
@ -1067,14 +1068,16 @@
|
|||
}
|
||||
}
|
||||
name = this.variable.compile(o, LEVEL_LIST);
|
||||
if (!(this.context || this.variable.isAssignable())) {
|
||||
throw SyntaxError("\"" + (this.variable.compile(o)) + "\" cannot be assigned.");
|
||||
}
|
||||
if (!(this.context || isValue && (this.variable.namespaced || this.variable.hasProperties()))) {
|
||||
if (this.param) {
|
||||
o.scope.add(name, 'var');
|
||||
} else {
|
||||
o.scope.find(name);
|
||||
if (!this.context) {
|
||||
if (!(varBase = this.variable.unwrapAll()).isAssignable()) {
|
||||
throw SyntaxError("\"" + (this.variable.compile(o)) + "\" cannot be assigned.");
|
||||
}
|
||||
if (!(typeof varBase.hasProperties === "function" ? varBase.hasProperties() : void 0)) {
|
||||
if (this.param) {
|
||||
o.scope.add(name, 'var');
|
||||
} else {
|
||||
o.scope.find(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.value instanceof Code && (match = METHOD_DEF.exec(name))) {
|
||||
|
@ -1091,7 +1094,7 @@
|
|||
}
|
||||
};
|
||||
Assign.prototype.compilePatternMatch = function(o) {
|
||||
var acc, assigns, code, i, idx, isObject, ivar, name, obj, objects, olen, ref, rest, splat, top, val, value, vvar, _len, _ref2, _ref3, _ref4, _ref5, _ref6;
|
||||
var acc, assigns, code, i, idx, isObject, ivar, name, obj, objects, olen, ref, rest, splat, top, val, value, vvar, _len, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8;
|
||||
top = o.level === LEVEL_TOP;
|
||||
value = this.value;
|
||||
objects = this.variable.base.objects;
|
||||
|
@ -1106,10 +1109,10 @@
|
|||
isObject = this.variable.isObject();
|
||||
if (top && olen === 1 && !((obj = objects[0]) instanceof Splat)) {
|
||||
if (obj instanceof Assign) {
|
||||
_ref2 = obj, idx = _ref2.variable.base, obj = _ref2.value;
|
||||
_ref2 = obj, (_ref3 = _ref2.variable, idx = _ref3.base), obj = _ref2.value;
|
||||
} else {
|
||||
if (obj.base instanceof Parens) {
|
||||
_ref3 = new Value(obj.unwrapAll()).cacheReference(o), obj = _ref3[0], idx = _ref3[1];
|
||||
_ref4 = new Value(obj.unwrapAll()).cacheReference(o), obj = _ref4[0], idx = _ref4[1];
|
||||
} else {
|
||||
idx = isObject ? obj["this"] ? obj.properties[0].name : obj : new Literal(0);
|
||||
}
|
||||
|
@ -1117,7 +1120,7 @@
|
|||
acc = IDENTIFIER.test(idx.unwrap().value || 0);
|
||||
value = new Value(value);
|
||||
value.properties.push(new (acc ? Access : Index)(idx));
|
||||
if (_ref4 = obj.unwrap().value, __indexOf.call(['arguments', 'eval'].concat(RESERVED), _ref4) >= 0) {
|
||||
if (_ref5 = obj.unwrap().value, __indexOf.call(['arguments', 'eval'].concat(RESERVED), _ref5) >= 0) {
|
||||
throw new SyntaxError("assignment to a reserved word: " + (obj.compile(o)) + " = " + (value.compile(o)));
|
||||
}
|
||||
return new Assign(obj, value, null, {
|
||||
|
@ -1136,20 +1139,18 @@
|
|||
idx = i;
|
||||
if (isObject) {
|
||||
if (obj instanceof Assign) {
|
||||
_ref5 = obj, idx = _ref5.variable.base, obj = _ref5.value;
|
||||
_ref6 = obj, (_ref7 = _ref6.variable, idx = _ref7.base), obj = _ref6.value;
|
||||
} else {
|
||||
if (obj.base instanceof Parens) {
|
||||
_ref6 = new Value(obj.unwrapAll()).cacheReference(o), obj = _ref6[0], idx = _ref6[1];
|
||||
_ref8 = new Value(obj.unwrapAll()).cacheReference(o), obj = _ref8[0], idx = _ref8[1];
|
||||
} else {
|
||||
idx = obj["this"] ? obj.properties[0].name : obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!splat && obj instanceof Splat) {
|
||||
if (!obj.name.unwrapAll().isAssignable()) {
|
||||
throw SyntaxError("\"" + (obj.name.compile(o)) + "\" cannot be assigned.");
|
||||
}
|
||||
name = obj.name.unwrap().value;
|
||||
obj = obj.unwrap();
|
||||
val = "" + olen + " <= " + vvar + ".length ? " + (utility('slice')) + ".call(" + vvar + ", " + i;
|
||||
if (rest = olen - i - 1) {
|
||||
ivar = o.scope.freeVariable('i');
|
||||
|
@ -1177,10 +1178,11 @@
|
|||
throw new SyntaxError("assignment to a reserved word: " + (obj.compile(o)) + " = " + (val.compile(o)));
|
||||
}
|
||||
assigns.push(new Assign(obj, val, null, {
|
||||
param: this.param
|
||||
}).compile(o, LEVEL_TOP));
|
||||
param: this.param,
|
||||
subpattern: true
|
||||
}).compile(o, LEVEL_LIST));
|
||||
}
|
||||
if (!top) assigns.push(vvar);
|
||||
if (!(top || this.subpattern)) assigns.push(vvar);
|
||||
code = assigns.join(', ');
|
||||
if (o.level < LEVEL_LIST) {
|
||||
return code;
|
||||
|
@ -1365,6 +1367,9 @@
|
|||
return this.name.compile(o);
|
||||
}
|
||||
};
|
||||
Splat.prototype.unwrap = function() {
|
||||
return this.name;
|
||||
};
|
||||
Splat.compileSplattedArray = function(o, list, apply) {
|
||||
var args, base, code, i, index, node, _len;
|
||||
index = -1;
|
||||
|
|
|
@ -919,6 +919,7 @@ exports.Class = class Class extends Base
|
|||
exports.Assign = class Assign extends Base
|
||||
constructor: (@variable, @value, @context, options) ->
|
||||
@param = options and options.param
|
||||
@subpattern = options and options.subpattern
|
||||
|
||||
children: ['variable', 'value']
|
||||
|
||||
|
@ -941,13 +942,14 @@ exports.Assign = class Assign extends Base
|
|||
return @compileSplice o if @variable.isSplice()
|
||||
return @compileConditional o if @context in ['||=', '&&=', '?=']
|
||||
name = @variable.compile o, LEVEL_LIST
|
||||
unless @context or @variable.isAssignable()
|
||||
throw SyntaxError "\"#{ @variable.compile o }\" cannot be assigned."
|
||||
unless @context or isValue and (@variable.namespaced or @variable.hasProperties())
|
||||
if @param
|
||||
o.scope.add name, 'var'
|
||||
else
|
||||
o.scope.find name
|
||||
unless @context
|
||||
unless (varBase = @variable.unwrapAll()).isAssignable()
|
||||
throw SyntaxError "\"#{ @variable.compile o }\" cannot be assigned."
|
||||
unless varBase.hasProperties?()
|
||||
if @param
|
||||
o.scope.add name, 'var'
|
||||
else
|
||||
o.scope.find name
|
||||
if @value instanceof Code and match = METHOD_DEF.exec name
|
||||
@value.klass = match[1] if match[1]
|
||||
@value.name = match[2] ? match[3] ? match[4] ? match[5]
|
||||
|
@ -1006,9 +1008,8 @@ exports.Assign = class Assign extends Base
|
|||
else
|
||||
idx = if obj.this then obj.properties[0].name else obj
|
||||
if not splat and obj instanceof Splat
|
||||
unless obj.name.unwrapAll().isAssignable()
|
||||
throw SyntaxError "\"#{ obj.name.compile(o) }\" cannot be assigned."
|
||||
name = obj.name.unwrap().value
|
||||
obj = obj.unwrap()
|
||||
val = "#{olen} <= #{vvar}.length ? #{ utility 'slice' }.call(#{vvar}, #{i}"
|
||||
if rest = olen - i - 1
|
||||
ivar = o.scope.freeVariable 'i'
|
||||
|
@ -1031,8 +1032,8 @@ exports.Assign = class Assign extends Base
|
|||
val = new Value new Literal(vvar), [new (if acc then Access else Index) idx]
|
||||
if name? and name in ['arguments','eval'].concat RESERVED
|
||||
throw new SyntaxError "assignment to a reserved word: #{obj.compile o} = #{val.compile o}"
|
||||
assigns.push new Assign(obj, val, null, param: @param).compile o, LEVEL_TOP
|
||||
assigns.push vvar unless top
|
||||
assigns.push new Assign(obj, val, null, param: @param, subpattern: yes).compile o, LEVEL_LIST
|
||||
assigns.push vvar unless top or @subpattern
|
||||
code = assigns.join ', '
|
||||
if o.level < LEVEL_LIST then code else "(#{code})"
|
||||
|
||||
|
@ -1176,6 +1177,8 @@ exports.Splat = class Splat extends Base
|
|||
|
||||
compile: (o) ->
|
||||
if @index? then @compileParam o else @name.compile o
|
||||
|
||||
unwrap: -> @name
|
||||
|
||||
# Utility function that converts an arbitrary number of elements, mixed with
|
||||
# splats, to a proper array.
|
||||
|
|
|
@ -306,3 +306,28 @@ test "#1591, #1101: splatted expressions in destructuring assignment must be ass
|
|||
nonce = {}
|
||||
for nonref in ['', '""', '0', 'f()', '(->)'].concat CoffeeScript.RESERVED
|
||||
eq nonce, (try CoffeeScript.compile "[#{nonref}...] = v" catch e then nonce)
|
||||
|
||||
test "#1643: splatted accesses in destructuring assignments should not be declared as variables", ->
|
||||
nonce = {}
|
||||
accesses = ['o.a', 'o["a"]', '(o.a)', '(o.a).a', '@o.a', 'C::a', 'C::', 'f().a', 'o?.a', 'o?.a.b', 'f?().a']
|
||||
for access in accesses
|
||||
for i,j in [1,2,3] #position can matter
|
||||
code =
|
||||
"""
|
||||
nonce = {}; nonce2 = {}; nonce3 = {};
|
||||
@o = o = new (class C then a:{}); f = -> o
|
||||
[#{new Array(i).join('x,')}#{access}...] = [#{new Array(i).join('0,')}nonce, nonce2, nonce3]
|
||||
unless #{access}[0] is nonce and #{access}[1] is nonce2 and #{access}[2] is nonce3 then throw new Error('[...]')
|
||||
"""
|
||||
eq nonce, unless (try CoffeeScript.run code, bare: true catch e then true) then nonce
|
||||
# subpatterns like `[[a]...]` and `[{a}...]`
|
||||
subpatterns = ['[sub, sub2, sub3]', '{0: sub, 1: sub2, 2: sub3}']
|
||||
for subpattern in subpatterns
|
||||
for i,j in [1,2,3]
|
||||
code =
|
||||
"""
|
||||
nonce = {}; nonce2 = {}; nonce3 = {};
|
||||
[#{new Array(i).join('x,')}#{subpattern}...] = [#{new Array(i).join('0,')}nonce, nonce2, nonce3]
|
||||
unless sub is nonce and sub2 is nonce2 and sub3 is nonce3 then throw new Error('[sub...]')
|
||||
"""
|
||||
eq nonce, unless (try CoffeeScript.run code, bare: true catch e then true) then nonce
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue