mirror of
https://github.com/jashkenas/coffeescript.git
synced 2022-11-09 12:23:24 -05:00
Added expansion to destructuring
This commit is contained in:
parent
5f31a3d60e
commit
369e0545c0
7 changed files with 277 additions and 168 deletions
|
@ -148,6 +148,8 @@
|
|||
return new Param($1, null, true);
|
||||
}), o('ParamVar = Expression', function() {
|
||||
return new Param($1, $3);
|
||||
}), o('...', function() {
|
||||
return new Expansion;
|
||||
})
|
||||
],
|
||||
ParamVar: [o('Identifier'), o('ThisProperty'), o('Array'), o('Object')],
|
||||
|
@ -327,7 +329,11 @@
|
|||
return $1.concat($4);
|
||||
})
|
||||
],
|
||||
Arg: [o('Expression'), o('Splat')],
|
||||
Arg: [
|
||||
o('Expression'), o('Splat'), o('...', function() {
|
||||
return new Expansion;
|
||||
})
|
||||
],
|
||||
SimpleArgs: [
|
||||
o('Expression'), o('SimpleArgs , Expression', function() {
|
||||
return [].concat($1, $3);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Generated by CoffeeScript 1.6.3
|
||||
(function() {
|
||||
var Access, Arr, Assign, Base, Block, Call, Class, Code, CodeFragment, Comment, Existence, Extends, For, HEXNUM, IDENTIFIER, IDENTIFIER_STR, IS_REGEX, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, NUMBER, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, isLiteralArguments, isLiteralThis, last, locationDataToString, merge, multident, parseNum, some, starts, throwSyntaxError, unfoldSoak, utility, _ref, _ref1,
|
||||
var Access, Arr, Assign, Base, Block, Call, Class, Code, CodeFragment, Comment, Existence, Expansion, Extends, For, HEXNUM, IDENTIFIER, IDENTIFIER_STR, IS_REGEX, IS_STRING, If, In, Index, LEVEL_ACCESS, LEVEL_COND, LEVEL_LIST, LEVEL_OP, LEVEL_PAREN, LEVEL_TOP, Literal, METHOD_DEF, NEGATE, NO, NUMBER, Obj, Op, Param, Parens, RESERVED, Range, Return, SIMPLENUM, STRICT_PROSCRIBED, Scope, Slice, Splat, Switch, TAB, THIS, Throw, Try, UTILITIES, Value, While, YES, addLocationDataFn, compact, del, ends, extend, flatten, fragmentsToText, isLiteralArguments, isLiteralThis, last, locationDataToString, merge, multident, parseNum, some, starts, throwSyntaxError, unfoldSoak, utility, _ref, _ref1,
|
||||
__hasProp = {}.hasOwnProperty,
|
||||
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
||||
__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; },
|
||||
|
@ -1637,7 +1637,7 @@
|
|||
};
|
||||
|
||||
Assign.prototype.compilePatternMatch = function(o) {
|
||||
var acc, assigns, code, fragments, i, idx, isObject, ivar, name, obj, objects, olen, ref, rest, splat, top, val, value, vvar, vvarText, _i, _len, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7;
|
||||
var acc, assigns, code, expandedIdx, fragments, i, idx, isObject, ivar, name, obj, objects, olen, ref, rest, top, val, value, vvar, vvarText, _i, _len, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7;
|
||||
top = o.level === LEVEL_TOP;
|
||||
value = this.value;
|
||||
objects = this.variable.base.objects;
|
||||
|
@ -1669,7 +1669,7 @@
|
|||
vvar = value.compileToFragments(o, LEVEL_LIST);
|
||||
vvarText = fragmentsToText(vvar);
|
||||
assigns = [];
|
||||
splat = false;
|
||||
expandedIdx = false;
|
||||
if (!IDENTIFIER.test(vvarText) || this.variable.assigns(vvarText)) {
|
||||
assigns.push([this.makeCode("" + (ref = o.scope.freeVariable('ref')) + " = ")].concat(__slice.call(vvar)));
|
||||
vvar = [this.makeCode(ref)];
|
||||
|
@ -1689,7 +1689,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!splat && obj instanceof Splat) {
|
||||
if (!expandedIdx && obj instanceof Splat) {
|
||||
name = obj.name.unwrap().value;
|
||||
obj = obj.unwrap();
|
||||
val = "" + olen + " <= " + vvarText + ".length ? " + (utility('slice')) + ".call(" + vvarText + ", " + i;
|
||||
|
@ -1700,14 +1700,26 @@
|
|||
val += ") : []";
|
||||
}
|
||||
val = new Literal(val);
|
||||
splat = "" + ivar + "++";
|
||||
expandedIdx = "" + ivar + "++";
|
||||
} else if (!expandedIdx && obj instanceof Expansion) {
|
||||
if (rest = olen - i - 1) {
|
||||
if (rest === 1) {
|
||||
expandedIdx = "" + vvarText + ".length - 1";
|
||||
} else {
|
||||
ivar = o.scope.freeVariable('i');
|
||||
val = new Literal("" + ivar + " = " + vvarText + ".length - " + rest);
|
||||
expandedIdx = "" + ivar + "++";
|
||||
assigns.push(val.compileToFragments(o, LEVEL_LIST));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
name = obj.unwrap().value;
|
||||
if (obj instanceof Splat) {
|
||||
obj.error("multiple splats are disallowed in an assignment");
|
||||
if (obj instanceof Splat || obj instanceof Expansion) {
|
||||
obj.error("multiple splats/expansions are disallowed in an assignment");
|
||||
}
|
||||
if (typeof idx === 'number') {
|
||||
idx = new Literal(splat || idx);
|
||||
idx = new Literal(expandedIdx || idx);
|
||||
acc = false;
|
||||
} else {
|
||||
acc = isObject && IDENTIFIER.test(idx.unwrap().value || 0);
|
||||
|
@ -1834,17 +1846,22 @@
|
|||
_ref3 = this.params;
|
||||
for (_i = 0, _len = _ref3.length; _i < _len; _i++) {
|
||||
param = _ref3[_i];
|
||||
o.scope.parameter(param.asReference(o));
|
||||
if (!(param instanceof Expansion)) {
|
||||
o.scope.parameter(param.asReference(o));
|
||||
}
|
||||
}
|
||||
_ref4 = this.params;
|
||||
for (_j = 0, _len1 = _ref4.length; _j < _len1; _j++) {
|
||||
param = _ref4[_j];
|
||||
if (!param.splat) {
|
||||
if (!(param.splat || param instanceof Expansion)) {
|
||||
continue;
|
||||
}
|
||||
_ref5 = this.params;
|
||||
for (_k = 0, _len2 = _ref5.length; _k < _len2; _k++) {
|
||||
p = _ref5[_k].name;
|
||||
if (!(!(param instanceof Expansion))) {
|
||||
continue;
|
||||
}
|
||||
if (p["this"]) {
|
||||
p = p.properties[0].name;
|
||||
}
|
||||
|
@ -2037,7 +2054,7 @@
|
|||
} else {
|
||||
iterator(obj.base.value, obj.base);
|
||||
}
|
||||
} else {
|
||||
} else if (!(obj instanceof Expansion)) {
|
||||
obj.error("illegal parameter " + (obj.compile()));
|
||||
}
|
||||
}
|
||||
|
@ -2117,6 +2134,29 @@
|
|||
|
||||
})(Base);
|
||||
|
||||
exports.Expansion = Expansion = (function(_super) {
|
||||
__extends(Expansion, _super);
|
||||
|
||||
function Expansion() {
|
||||
return Expansion.__super__.constructor.apply(this, arguments);
|
||||
}
|
||||
|
||||
Expansion.prototype.isComplex = NO;
|
||||
|
||||
Expansion.prototype.compileNode = function(o) {
|
||||
return this.error('Expansion must be used inside a destructuring assignment or parameter list');
|
||||
};
|
||||
|
||||
Expansion.prototype.asReference = function(o) {
|
||||
return this;
|
||||
};
|
||||
|
||||
Expansion.prototype.eachName = function(iterator) {};
|
||||
|
||||
return Expansion;
|
||||
|
||||
})(Base);
|
||||
|
||||
exports.While = While = (function(_super) {
|
||||
__extends(While, _super);
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -217,6 +217,7 @@ grammar =
|
|||
o 'ParamVar', -> new Param $1
|
||||
o 'ParamVar ...', -> new Param $1, null, on
|
||||
o 'ParamVar = Expression', -> new Param $1, $3
|
||||
o '...', -> new Expansion
|
||||
]
|
||||
|
||||
# Function Parameters
|
||||
|
@ -378,6 +379,7 @@ grammar =
|
|||
Arg: [
|
||||
o 'Expression'
|
||||
o 'Splat'
|
||||
o '...', -> new Expansion
|
||||
]
|
||||
|
||||
# Just simple, comma-separated, required arguments (no fancy syntax). We need
|
||||
|
|
|
@ -1203,10 +1203,10 @@ exports.Assign = class Assign extends Base
|
|||
if obj.unwrap().value in RESERVED
|
||||
obj.error "assignment to a reserved word: #{obj.compile o}"
|
||||
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
|
||||
assigns = []
|
||||
splat = false
|
||||
assigns = []
|
||||
expandedIdx = false
|
||||
# Make vvar into a simple variable if it isn't already.
|
||||
if not IDENTIFIER.test(vvarText) or @variable.assigns(vvarText)
|
||||
assigns.push [@makeCode("#{ ref = o.scope.freeVariable 'ref' } = "), vvar...]
|
||||
|
@ -1225,7 +1225,7 @@ exports.Assign = class Assign extends Base
|
|||
[obj, idx] = new Value(obj.unwrapAll()).cacheReference o
|
||||
else
|
||||
idx = if obj.this then obj.properties[0].name else obj
|
||||
if not splat and obj instanceof Splat
|
||||
if not expandedIdx and obj instanceof Splat
|
||||
name = obj.name.unwrap().value
|
||||
obj = obj.unwrap()
|
||||
val = "#{olen} <= #{vvarText}.length ? #{ utility 'slice' }.call(#{vvarText}, #{i}"
|
||||
|
@ -1235,13 +1235,23 @@ exports.Assign = class Assign extends Base
|
|||
else
|
||||
val += ") : []"
|
||||
val = new Literal val
|
||||
splat = "#{ivar}++"
|
||||
expandedIdx = "#{ivar}++"
|
||||
else if not expandedIdx and obj instanceof Expansion
|
||||
if rest = olen - i - 1
|
||||
if rest is 1
|
||||
expandedIdx = "#{vvarText}.length - 1"
|
||||
else
|
||||
ivar = o.scope.freeVariable 'i'
|
||||
val = new Literal "#{ivar} = #{vvarText}.length - #{rest}"
|
||||
expandedIdx = "#{ivar}++"
|
||||
assigns.push val.compileToFragments o, LEVEL_LIST
|
||||
continue
|
||||
else
|
||||
name = obj.unwrap().value
|
||||
if obj instanceof Splat
|
||||
obj.error "multiple splats are disallowed in an assignment"
|
||||
if obj instanceof Splat or obj instanceof Expansion
|
||||
obj.error "multiple splats/expansions are disallowed in an assignment"
|
||||
if typeof idx is 'number'
|
||||
idx = new Literal splat or idx
|
||||
idx = new Literal expandedIdx or idx
|
||||
acc = no
|
||||
else
|
||||
acc = isObject and IDENTIFIER.test idx.unwrap().value or 0
|
||||
|
@ -1336,10 +1346,10 @@ exports.Code = class Code extends Base
|
|||
delete o.isExistentialEquals
|
||||
params = []
|
||||
exprs = []
|
||||
for param in @params
|
||||
for param in @params when param not instanceof Expansion
|
||||
o.scope.parameter param.asReference o
|
||||
for param in @params when param.splat
|
||||
for {name: p} in @params
|
||||
for param in @params when param.splat or param instanceof Expansion
|
||||
for {name: p} in @params when param not instanceof Expansion
|
||||
if p.this then p = p.properties[0].name
|
||||
if p.value then o.scope.add p.value, 'var', yes
|
||||
splats = new Assign new Value(new Arr(p.asReference o for p in @params)),
|
||||
|
@ -1453,7 +1463,7 @@ exports.Param = class Param extends Base
|
|||
atParam obj
|
||||
# * simple destructured parameters {foo}
|
||||
else iterator obj.base.value, obj.base
|
||||
else
|
||||
else if obj not instanceof Expansion
|
||||
obj.error "illegal parameter #{obj.compile()}"
|
||||
return
|
||||
|
||||
|
@ -1504,6 +1514,22 @@ exports.Splat = class Splat extends Base
|
|||
concatPart = list[index].joinFragmentArrays args, ', '
|
||||
[].concat list[0].makeCode("["), base, list[index].makeCode("].concat("), concatPart, (last list).makeCode(")")
|
||||
|
||||
#### Expansion
|
||||
|
||||
# Used to skip values inside an array destructuring (pattern matching) or
|
||||
# parameter list.
|
||||
exports.Expansion = class Expansion extends Base
|
||||
|
||||
isComplex: NO
|
||||
|
||||
compileNode: (o) ->
|
||||
@error 'Expansion must be used inside a destructuring assignment or parameter list'
|
||||
|
||||
asReference: (o) ->
|
||||
this
|
||||
|
||||
eachName: (iterator) ->
|
||||
|
||||
#### While
|
||||
|
||||
# A while loop, the only sort of low-level loop exposed by CoffeeScript. From
|
||||
|
|
|
@ -268,6 +268,22 @@ test "#2055: destructuring assignment with `new`", ->
|
|||
{length} = new Array
|
||||
eq 0, length
|
||||
|
||||
test "#156: destructuring with expansion", ->
|
||||
array = [1..5]
|
||||
[first, ..., last] = array
|
||||
eq 1, first
|
||||
eq 5, last
|
||||
[..., lastButOne, last] = array
|
||||
eq 4, lastButOne
|
||||
eq 5, last
|
||||
[first, second, ..., last] = array
|
||||
eq 2, second
|
||||
[..., last] = 'strings as well -> x'
|
||||
eq 'x', last
|
||||
throws (-> CoffeeScript.compile "[1, ..., 3]"), null, "prohibit expansion outside of assignment"
|
||||
throws (-> CoffeeScript.compile "[..., a, b...] = c"), null, "prohibit expansion and a splat"
|
||||
throws (-> CoffeeScript.compile "[...] = c"), null, "prohibit lone expansion"
|
||||
|
||||
|
||||
# Existential Assignment
|
||||
|
||||
|
|
|
@ -178,6 +178,21 @@ test "default values with splatted arguments", ->
|
|||
eq 1, withSplats(1,1,1)
|
||||
eq 2, withSplats(1,1,1,1)
|
||||
|
||||
test "#156: parameter lists with expansion", ->
|
||||
expandArguments = (first, ..., lastButOne, last) ->
|
||||
eq 1, first
|
||||
eq 4, lastButOne
|
||||
last
|
||||
eq 5, expandArguments 1, 2, 3, 4, 5
|
||||
|
||||
throws (-> CoffeeScript.compile "(..., a, b...) ->"), null, "prohibit expansion and a splat"
|
||||
throws (-> CoffeeScript.compile "(...) ->"), null, "prohibit lone expansion"
|
||||
|
||||
test "#156: parameter lists with expansion in array destructuring", ->
|
||||
expandArray = (..., [..., last]) ->
|
||||
last
|
||||
eq 3, expandArray 1, 2, 3, [1, 2, 3]
|
||||
|
||||
test "default values with function calls", ->
|
||||
doesNotThrow -> CoffeeScript.compile "(x = f()) ->"
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue