support arrow function (#4385)
This commit is contained in:
156
lib/parse.js
156
lib/parse.js
@@ -569,6 +569,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
}
|
||||
if (is_digit(code)) return read_num();
|
||||
if (PUNC_CHARS[ch]) return token("punc", next());
|
||||
if (looking_at("=>")) return token("punc", next() + next());
|
||||
if (OPERATOR_CHARS[ch]) return read_operator();
|
||||
if (code == 92 || !NON_IDENTIFIER_CHARS[ch]) return read_word();
|
||||
break;
|
||||
@@ -634,7 +635,7 @@ var PRECEDENCE = function(a, ret) {
|
||||
["*", "/", "%"]
|
||||
], {});
|
||||
|
||||
var ATOMIC_START_TOKEN = makePredicate("atom num string regexp name");
|
||||
var ATOMIC_START_TOKEN = makePredicate("atom num regexp string");
|
||||
|
||||
/* -----[ Parser ]----- */
|
||||
|
||||
@@ -741,7 +742,7 @@ function parse($TEXT, options) {
|
||||
|
||||
function parenthesised() {
|
||||
expect("(");
|
||||
var exp = expression(true);
|
||||
var exp = expression();
|
||||
expect(")");
|
||||
return exp;
|
||||
}
|
||||
@@ -769,7 +770,7 @@ function parse($TEXT, options) {
|
||||
switch (S.token.type) {
|
||||
case "string":
|
||||
var dir = S.in_directives;
|
||||
var body = expression(true);
|
||||
var body = expression();
|
||||
if (dir) {
|
||||
if (body instanceof AST_String) {
|
||||
var value = body.start.raw.slice(1, -1);
|
||||
@@ -887,7 +888,7 @@ function parse($TEXT, options) {
|
||||
if (is("punc", ";")) {
|
||||
next();
|
||||
} else if (!can_insert_semicolon()) {
|
||||
value = expression(true);
|
||||
value = expression();
|
||||
semicolon();
|
||||
}
|
||||
return new AST_Return({
|
||||
@@ -905,7 +906,7 @@ function parse($TEXT, options) {
|
||||
next();
|
||||
if (has_newline_before(S.token))
|
||||
croak("Illegal newline after 'throw'");
|
||||
var value = expression(true);
|
||||
var value = expression();
|
||||
semicolon();
|
||||
return new AST_Throw({
|
||||
value: value
|
||||
@@ -956,9 +957,7 @@ function parse($TEXT, options) {
|
||||
// https://github.com/mishoo/UglifyJS/issues/287
|
||||
label.references.forEach(function(ref) {
|
||||
if (ref instanceof AST_Continue) {
|
||||
ref = ref.label.start;
|
||||
croak("Continue label `" + label.name + "` refers to non-IterationStatement.",
|
||||
ref.line, ref.col, ref.pos);
|
||||
token_error(ref.label.start, "Continue label `" + label.name + "` must refer to IterationStatement");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -966,7 +965,7 @@ function parse($TEXT, options) {
|
||||
}
|
||||
|
||||
function simple_statement() {
|
||||
var body = expression(true);
|
||||
var body = expression();
|
||||
semicolon();
|
||||
return new AST_SimpleStatement({ body: body });
|
||||
}
|
||||
@@ -980,7 +979,7 @@ function parse($TEXT, options) {
|
||||
ldef = find_if(function(l) {
|
||||
return l.name == label.name;
|
||||
}, S.labels);
|
||||
if (!ldef) croak("Undefined label " + label.name);
|
||||
if (!ldef) token_error(label.start, "Undefined label " + label.name);
|
||||
label.thedef = ldef;
|
||||
} else if (S.in_loop == 0) croak(type.TYPE + " not inside a loop or switch");
|
||||
semicolon();
|
||||
@@ -999,13 +998,14 @@ function parse($TEXT, options) {
|
||||
? (next(), let_(true))
|
||||
: is("keyword", "var")
|
||||
? (next(), var_(true))
|
||||
: expression(true, true);
|
||||
: expression(true);
|
||||
if (is("operator", "in")) {
|
||||
if (init instanceof AST_Definitions) {
|
||||
if (init.definitions.length > 1)
|
||||
croak("Only one variable declaration allowed in for..in loop", init.start.line, init.start.col, init.start.pos);
|
||||
if (init.definitions.length > 1) {
|
||||
token_error(init.start, "Only one variable declaration allowed in for..in loop");
|
||||
}
|
||||
} else if (!(is_assignable(init) || (init = to_destructured(init)) instanceof AST_Destructured)) {
|
||||
croak("Invalid left-hand side in for..in loop", init.start.line, init.start.col, init.start.pos);
|
||||
token_error(init.start, "Invalid left-hand side in for..in loop");
|
||||
}
|
||||
next();
|
||||
return for_in(init);
|
||||
@@ -1016,9 +1016,9 @@ function parse($TEXT, options) {
|
||||
|
||||
function regular_for(init) {
|
||||
expect(";");
|
||||
var test = is("punc", ";") ? null : expression(true);
|
||||
var test = is("punc", ";") ? null : expression();
|
||||
expect(";");
|
||||
var step = is("punc", ")") ? null : expression(true);
|
||||
var step = is("punc", ")") ? null : expression();
|
||||
expect(")");
|
||||
return new AST_For({
|
||||
init : init,
|
||||
@@ -1029,7 +1029,7 @@ function parse($TEXT, options) {
|
||||
}
|
||||
|
||||
function for_in(init) {
|
||||
var obj = expression(true);
|
||||
var obj = expression();
|
||||
expect(")");
|
||||
return new AST_ForIn({
|
||||
init : init,
|
||||
@@ -1038,6 +1038,71 @@ function parse($TEXT, options) {
|
||||
});
|
||||
}
|
||||
|
||||
function to_funarg(node) {
|
||||
if (node instanceof AST_Array) return new AST_DestructuredArray({
|
||||
start: node.start,
|
||||
elements: node.elements.map(function(node) {
|
||||
return node instanceof AST_Hole ? node : to_funarg(node);
|
||||
}),
|
||||
end: node.end,
|
||||
});
|
||||
if (node instanceof AST_Object) return new AST_DestructuredObject({
|
||||
start: node.start,
|
||||
properties: node.properties.map(function(prop) {
|
||||
if (!(prop instanceof AST_ObjectKeyVal)) token_error(prop.start, "Invalid destructuring assignment");
|
||||
return new AST_DestructuredKeyVal({
|
||||
start: prop.start,
|
||||
key: prop.key,
|
||||
value: to_funarg(prop.value),
|
||||
end: prop.end,
|
||||
});
|
||||
}),
|
||||
end: node.end,
|
||||
});
|
||||
if (node instanceof AST_SymbolRef) return new AST_SymbolFunarg(node);
|
||||
token_error(node.start, "Invalid arrow parameter");
|
||||
}
|
||||
|
||||
function arrow(exprs, start) {
|
||||
var was_async = S.in_async;
|
||||
S.in_async = false;
|
||||
var was_funarg = S.in_funarg;
|
||||
S.in_funarg = S.in_function;
|
||||
var argnames = exprs.map(to_funarg);
|
||||
S.in_funarg = was_funarg;
|
||||
expect("=>");
|
||||
var body, value;
|
||||
var loop = S.in_loop;
|
||||
var labels = S.labels;
|
||||
++S.in_function;
|
||||
S.in_directives = true;
|
||||
S.input.push_directives_stack();
|
||||
S.in_loop = 0;
|
||||
S.labels = [];
|
||||
if (is("punc", "{")) {
|
||||
body = block_();
|
||||
value = null;
|
||||
if (S.input.has_directive("use strict")) {
|
||||
argnames.forEach(strict_verify_symbol);
|
||||
}
|
||||
} else {
|
||||
body = [];
|
||||
value = maybe_assign();
|
||||
}
|
||||
S.input.pop_directives_stack();
|
||||
--S.in_function;
|
||||
S.in_loop = loop;
|
||||
S.labels = labels;
|
||||
S.in_async = was_async;
|
||||
return new AST_Arrow({
|
||||
start: start,
|
||||
argnames: argnames,
|
||||
body: body,
|
||||
value: value,
|
||||
end: prev(),
|
||||
});
|
||||
}
|
||||
|
||||
var function_ = function(ctor) {
|
||||
var was_async = S.in_async;
|
||||
var name;
|
||||
@@ -1118,7 +1183,7 @@ function parse($TEXT, options) {
|
||||
cur = [];
|
||||
branch = new AST_Case({
|
||||
start : (tmp = S.token, next(), tmp),
|
||||
expression : expression(true),
|
||||
expression : expression(),
|
||||
body : cur
|
||||
});
|
||||
a.push(branch);
|
||||
@@ -1187,7 +1252,7 @@ function parse($TEXT, options) {
|
||||
var value = null;
|
||||
if (is("operator", "=")) {
|
||||
next();
|
||||
value = expression(false, no_in);
|
||||
value = maybe_assign(no_in);
|
||||
} else if (!no_in && (type === AST_SymbolConst || name instanceof AST_Destructured)) {
|
||||
croak("Missing initializer in declaration");
|
||||
}
|
||||
@@ -1251,9 +1316,6 @@ function parse($TEXT, options) {
|
||||
function as_atom_node() {
|
||||
var tok = S.token, ret;
|
||||
switch (tok.type) {
|
||||
case "name":
|
||||
ret = _make_symbol(AST_SymbolRef, tok);
|
||||
break;
|
||||
case "num":
|
||||
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
|
||||
break;
|
||||
@@ -1295,7 +1357,11 @@ function parse($TEXT, options) {
|
||||
switch (start.value) {
|
||||
case "(":
|
||||
next();
|
||||
var ex = expression(true);
|
||||
if (is("punc", ")")) {
|
||||
next();
|
||||
return arrow([], start);
|
||||
}
|
||||
var ex = expression(false, true);
|
||||
var len = start.comments_before.length;
|
||||
[].unshift.apply(ex.start.comments_before, start.comments_before);
|
||||
start.comments_before.length = 0;
|
||||
@@ -1318,6 +1384,7 @@ function parse($TEXT, options) {
|
||||
end.comments_after = ex.end.comments_after;
|
||||
ex.end = end;
|
||||
if (ex instanceof AST_Call) mark_pure(ex);
|
||||
if (is("punc", "=>")) return arrow(ex instanceof AST_Sequence ? ex.expressions : [ ex ], start);
|
||||
return subscripts(ex, allow_calls);
|
||||
case "[":
|
||||
return subscripts(array_(), allow_calls);
|
||||
@@ -1340,6 +1407,11 @@ function parse($TEXT, options) {
|
||||
func.end = prev();
|
||||
return subscripts(func, allow_calls);
|
||||
}
|
||||
if (is("name")) {
|
||||
var sym = _make_symbol(AST_SymbolRef, start);
|
||||
next();
|
||||
return is("punc", "=>") ? arrow([ sym ], start) : subscripts(sym, allow_calls);
|
||||
}
|
||||
if (ATOMIC_START_TOKEN[S.token.type]) {
|
||||
return subscripts(as_atom_node(), allow_calls);
|
||||
}
|
||||
@@ -1347,14 +1419,14 @@ function parse($TEXT, options) {
|
||||
};
|
||||
|
||||
function expr_list(closing, allow_trailing_comma, allow_empty, parser) {
|
||||
if (!parser) parser = expression;
|
||||
if (!parser) parser = maybe_assign;
|
||||
var first = true, a = [];
|
||||
while (!is("punc", closing)) {
|
||||
if (first) first = false; else expect(",");
|
||||
if (allow_trailing_comma && is("punc", closing)) break;
|
||||
if (allow_empty && is("punc", ",")) {
|
||||
a.push(new AST_Hole({ start: S.token, end: S.token }));
|
||||
} else if (parser === expression && is("operator", "...")) {
|
||||
} else if (parser === maybe_assign && is("operator", "...")) {
|
||||
a.push(new AST_Spread({
|
||||
start: S.token,
|
||||
expression: (next(), parser()),
|
||||
@@ -1391,7 +1463,7 @@ function parse($TEXT, options) {
|
||||
next();
|
||||
a.push(new AST_Spread({
|
||||
start: start,
|
||||
expression: expression(false),
|
||||
expression: maybe_assign(),
|
||||
end: prev(),
|
||||
}));
|
||||
continue;
|
||||
@@ -1440,7 +1512,7 @@ function parse($TEXT, options) {
|
||||
a.push(new AST_ObjectKeyVal({
|
||||
start: start,
|
||||
key: key,
|
||||
value: expression(false),
|
||||
value: maybe_assign(),
|
||||
end: prev(),
|
||||
}));
|
||||
}
|
||||
@@ -1463,7 +1535,7 @@ function parse($TEXT, options) {
|
||||
case "punc":
|
||||
if (tmp.value != "[") unexpected();
|
||||
next();
|
||||
var key = expression(false);
|
||||
var key = maybe_assign();
|
||||
expect("]");
|
||||
return key;
|
||||
default:
|
||||
@@ -1490,7 +1562,7 @@ function parse($TEXT, options) {
|
||||
|
||||
function strict_verify_symbol(sym) {
|
||||
if (sym.name == "arguments" || sym.name == "eval")
|
||||
croak("Unexpected " + sym.name + " in strict mode", sym.start.line, sym.start.col, sym.start.pos);
|
||||
token_error(sym.start, "Unexpected " + sym.name + " in strict mode");
|
||||
}
|
||||
|
||||
function as_symbol(type, noerror) {
|
||||
@@ -1580,7 +1652,7 @@ function parse($TEXT, options) {
|
||||
}
|
||||
if (is("punc", "[")) {
|
||||
next();
|
||||
var prop = expression(true);
|
||||
var prop = expression();
|
||||
expect("]");
|
||||
return subscripts(new AST_Sub({
|
||||
start : start,
|
||||
@@ -1629,11 +1701,11 @@ function parse($TEXT, options) {
|
||||
case "++":
|
||||
case "--":
|
||||
if (!is_assignable(expr))
|
||||
croak("Invalid use of " + op + " operator", token.line, token.col, token.pos);
|
||||
token_error(token, "Invalid use of " + op + " operator");
|
||||
break;
|
||||
case "delete":
|
||||
if (expr instanceof AST_SymbolRef && S.input.has_directive("use strict"))
|
||||
croak("Calling delete on expression not allowed in strict mode", expr.start.line, expr.start.col, expr.start.pos);
|
||||
token_error(expr.start, "Calling delete on expression not allowed in strict mode");
|
||||
break;
|
||||
}
|
||||
return new ctor({ operator: op, expression: expr });
|
||||
@@ -1679,13 +1751,13 @@ function parse($TEXT, options) {
|
||||
var expr = expr_ops(no_in);
|
||||
if (is("operator", "?")) {
|
||||
next();
|
||||
var yes = expression(false);
|
||||
var yes = maybe_assign();
|
||||
expect(":");
|
||||
return new AST_Conditional({
|
||||
start : start,
|
||||
condition : expr,
|
||||
consequent : yes,
|
||||
alternative : expression(false, no_in),
|
||||
alternative : maybe_assign(no_in),
|
||||
end : prev()
|
||||
});
|
||||
}
|
||||
@@ -1728,7 +1800,7 @@ function parse($TEXT, options) {
|
||||
});
|
||||
}
|
||||
|
||||
var maybe_assign = function(no_in) {
|
||||
function maybe_assign(no_in) {
|
||||
var start = S.token;
|
||||
var left = maybe_conditional(no_in), val = S.token.value;
|
||||
if (is("operator") && ASSIGNMENT[val]) {
|
||||
@@ -1745,23 +1817,23 @@ function parse($TEXT, options) {
|
||||
croak("Invalid assignment");
|
||||
}
|
||||
return left;
|
||||
};
|
||||
}
|
||||
|
||||
var expression = function(commas, no_in) {
|
||||
function expression(no_in, maybe_arrow) {
|
||||
var start = S.token;
|
||||
var exprs = [];
|
||||
while (true) {
|
||||
exprs.push(maybe_assign(no_in));
|
||||
if (!commas || !is("punc", ",")) break;
|
||||
if (!is("punc", ",")) break;
|
||||
next();
|
||||
commas = true;
|
||||
if (maybe_arrow && is("punc", ")") && is_token(peek(), "punc", "=>")) break;
|
||||
}
|
||||
return exprs.length == 1 ? exprs[0] : new AST_Sequence({
|
||||
start : start,
|
||||
expressions : exprs,
|
||||
end : peek()
|
||||
end : prev()
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function in_loop(cont) {
|
||||
++S.in_loop;
|
||||
@@ -1772,7 +1844,7 @@ function parse($TEXT, options) {
|
||||
|
||||
if (options.expression) {
|
||||
handle_regexp();
|
||||
return expression(true);
|
||||
return expression();
|
||||
}
|
||||
|
||||
return function() {
|
||||
|
||||
Reference in New Issue
Block a user