=> with destructuring arguments. Requires a lot of parser changes

This commit is contained in:
Fábio Santos
2015-08-04 00:14:18 +01:00
parent fa5c4f2d03
commit a68953c491
4 changed files with 193 additions and 98 deletions

View File

@@ -387,6 +387,11 @@ var AST_ArrowParametersOrSeq = DEFNODE("ArrowParametersOrSeq", "expressions", {
start: ex.start,
end: ex.end
});
} else if (ex instanceof AST_Destructuring) {
if (ex.names.length == 0)
croak("Invalid destructuring function parameter", ex.start.line, ex.start.col);
ex.names = ex.names.map(to_fun_args);
return ex;
} else if (ex instanceof AST_SymbolRef) {
return new AST_SymbolFunarg({
name: ex.name,

View File

@@ -989,35 +989,17 @@ function parse($TEXT, options) {
var arrow_function = function(args) {
expect_token("arrow", "=>");
if (args instanceof AST_SymbolRef) {
args = [args];
} else if (args instanceof AST_Seq) {
args = args.to_array();
} else if (args instanceof AST_Node) {
croak("Invalid syntax", args.start.line, args.start.col);
}
var argnames = args.as_params(croak);
for (var i = 0; i < args.length; i++) {
if (!(args[i] instanceof AST_SymbolRef)) {
croak("Invalid parameter for an arrow function", args[i].start.line, args[i].start.col);
}
args[i] = new AST_SymbolFunarg({
name: args[i].name,
start: args[i].start,
end: args[i].end
})
}
var body = is("punc", "{") ?
_function_body(true) :
_function_body(false);
return new AST_Arrow({
argnames: args,
body: (function(){
if (is("punc", "{")) {
return _function_body();
} else {
return expression(true);
}
})()
start : args.start,
end : body.end,
argnames : argnames,
body : body
});
};
@@ -1030,7 +1012,7 @@ function parse($TEXT, options) {
unexpected();
var args = params_or_seq_().as_params(croak);
var body = _function_body();
var body = _function_body(true);
return new ctor({
start : args.start,
end : body.end,
@@ -1060,14 +1042,18 @@ function parse($TEXT, options) {
});
}
function _function_body() {
function _function_body(block) {
var loop = S.in_loop;
var labels = S.labels;
++S.in_function;
S.in_directives = true;
if (block)
S.in_directives = true;
S.in_loop = 0;
S.labels = [];
var a = block_();
if (block)
var a = block_();
else
var a = expression(false);
--S.in_function;
S.in_loop = loop;
S.labels = labels;
@@ -1263,16 +1249,17 @@ function parse($TEXT, options) {
if (is("punc")) {
switch (start.value) {
case "(":
next();
var ex = expression(true);
var ex = params_or_seq_();
ex.start = start;
ex.end = S.token;
expect(")");
return subscripts(ex, allow_calls);
if (is("arrow", "=>")) {
return arrow_function(ex);
}
return subscripts(ex.as_expr(croak), allow_calls);
case "[":
return subscripts(array_(), allow_calls);
case "{":
return subscripts(object_(), allow_calls);
return subscripts(object_or_object_destructuring_(), allow_calls);
}
unexpected();
}
@@ -1311,68 +1298,101 @@ function parse($TEXT, options) {
});
});
var object_ = embed_tokens(function() {
var object_or_object_destructuring_ = embed_tokens(function() {
var start = S.token;
expect("{");
var first = true, a = [];
while (!is("punc", "}")) {
if (first) first = false; else expect(",");
if (!options.strict && is("punc", "}"))
// allow trailing comma
break;
var start = S.token;
var type = start.type;
var name = as_property_name();
if (type == "name" && !is("punc", ":")) {
if (name == "get") {
a.push(new AST_ObjectGetter({
start : start,
key : as_atom_node(),
value : function_(AST_Accessor),
end : prev()
}));
continue;
function try_an_object() {
var first = true, a = [];
while (!is("punc", "}")) {
if (first) first = false; else expect(",");
if (!options.strict && is("punc", "}"))
// allow trailing comma
break;
var start = S.token;
var type = start.type;
var name = as_property_name();
if (type == "name" && !is("punc", ":")) {
if (name == "get") {
a.push(new AST_ObjectGetter({
start : start,
key : as_atom_node(),
value : function_(AST_Accessor),
end : prev()
}));
continue;
}
if (name == "set") {
a.push(new AST_ObjectSetter({
start : start,
key : as_atom_node(),
value : function_(AST_Accessor),
end : prev()
}));
continue;
}
}
if (name == "set") {
a.push(new AST_ObjectSetter({
start : start,
key : as_atom_node(),
value : function_(AST_Accessor),
end : prev()
}));
continue;
}
}
if (!is("punc", ":")) {
// It's one of those object destructurings, the value is its own name
if (!S.in_parameters) {
croak("Invalid syntax", S.token.line, S.token.col);
}
a.push(new AST_ObjectSymbol({
start: start,
end: start,
symbol: new AST_SymbolRef({
if (!is("punc", ":")) {
// It's one of those object destructurings, the value is its own name
if (!S.in_parameters) {
croak("Invalid syntax", S.token.line, S.token.col);
}
a.push(new AST_ObjectSymbol({
start: start,
end: start,
name: name
})
}));
} else {
if (S.in_parameters) {
croak("Cannot destructure", S.token.line, S.token.col);
symbol: new AST_SymbolRef({
start: start,
end: start,
name: name
})
}));
} else {
if (S.in_parameters) {
croak("Cannot destructure", S.token.line, S.token.col);
}
expect(":");
a.push(new AST_ObjectKeyVal({
start : start,
key : name,
value : expression(false),
end : prev()
}));
}
expect(":");
a.push(new AST_ObjectKeyVal({
start : start,
key : name,
value : expression(false),
end : prev()
}));
}
next();
return new AST_Object({ properties: a })
}
next();
return new AST_Object({ properties: a });
var obj = try_an_object();
if (obj instanceof AST_Object) { return obj; }
if (!S.in_parameters) {
croak("Cannot destructure", S.token.line, S.token.col);
}
var firstName = obj;
var namesInDestructuring = [];
namesInDestructuring.push( new AST_SymbolRef({
start : prev(),
end : prev(),
name : firstName
}));
while (!is("punc", "}")) {
expect(",");
namesInDestructuring.push(as_symbol(AST_SymbolRef))
}
expect('}');
return new AST_Destructuring({
start : start,
end : S.token,
names : namesInDestructuring,
is_array : false
})
});
function as_property_name() {
@@ -1530,16 +1550,13 @@ function parse($TEXT, options) {
return (expr instanceof AST_PropAccess || expr instanceof AST_Symbol);
};
// In ES6, AssignmentExpression can also be an ArrowFunction
var maybe_assign = function(no_in) {
var start = S.token;
if (start.value == "(" && peek().value == ")") {
next(); // (
next(); // )
return arrow_function([]);
}
var left = maybe_conditional(no_in);
var val = S.token.value;
var left = maybe_conditional(no_in), val = S.token.value;
if (is("operator") && ASSIGNMENT(val)) {
if (is_assignable(left)) {
next();
@@ -1553,15 +1570,20 @@ function parse($TEXT, options) {
}
croak("Invalid assignment");
}
if (is("arrow")) {
return arrow_function(left)
}
return left;
};
var expression = function(commas, no_in) {
var start = S.token;
var expr = maybe_assign(no_in);
if (expr instanceof AST_SymbolRef && is("arrow", "=>")) {
expr = new AST_ArrowParametersOrSeq({
start: expr.start,
end: expr.end,
expressions: [expr]
});
return arrow_function(expr);
}
if (commas && is("punc", ",")) {
next();
return new AST_Seq({

View File

@@ -35,3 +35,65 @@ regression_arrow_functions_and_hoist: {
}
expect_exact: "a=>b;"
}
destructuring_arguments: {
input: {
(function ( a ) { });
(function ( [ a ] ) { });
(function ( [ a, b ] ) { });
(function ( [ [ a ] ] ) { });
(function ( [ [ a, b ] ] ) { });
(function ( [ a, [ b ] ] ) { });
(function ( [ [ b ], a ] ) { });
(function ( { a } ) { });
(function ( { a, b } ) { });
(function ( [ { a } ] ) { });
(function ( [ { a, b } ] ) { });
(function ( [ a, { b } ] ) { });
(function ( [ { b }, a ] ) { });
( [ a ] ) => { };
( [ a, b ] ) => { };
( { a } ) => { };
( { a, b, c, d, e } ) => { };
( [ a ] ) => b;
( [ a, b ] ) => c;
( { a } ) => b;
( { a, b } ) => c;
}
expect: {
(function(a){});
(function([a]){});
(function([a,b]){});
(function([[a]]){});
(function([[a,b]]){});
(function([a,[b]]){});
(function([[b],a]){});
(function({a}){});
(function({a,b}){});
(function([{a}]){});
(function([{a,b}]){});
(function([a,{b}]){});
(function([{b},a]){});
([a])=>{};
([a,b])=>{};
({a})=>{};
({a,b,c,d,e})=>{};
([a])=>b;
([a,b])=>c;
({a})=>b;
({a,b})=>c;
}
}

View File

@@ -21,6 +21,12 @@ module.exports = function () {
ok.equal(destr_fun1.argnames.length, 1);
ok.equal(destr_fun2.argnames.length, 1);
var destr_fun1 = UglifyJS.parse('({a, b}) => null').body[0].body;
var destr_fun2 = UglifyJS.parse('([a, [b]]) => null').body[0].body;
ok.equal(destr_fun1.argnames.length, 1);
ok.equal(destr_fun2.argnames.length, 1);
var destruct1 = destr_fun1.argnames[0];
var destruct2 = destr_fun2.argnames[0];