1
0
Fork 0
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:
xixixao 2014-01-24 16:00:34 +00:00
parent 5f31a3d60e
commit 369e0545c0
7 changed files with 277 additions and 168 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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()) ->"