support generator functions (#4620)

This commit is contained in:
Alex Lam S.L
2021-02-07 22:44:20 +00:00
committed by GitHub
parent c44b6399c3
commit fd4caf7a9c
12 changed files with 1186 additions and 182 deletions

View File

@@ -112,13 +112,18 @@ var OPERATORS = makePredicate([
var NEWLINE_CHARS = "\n\r\u2028\u2029";
var OPERATOR_CHARS = "+-*&%=<>!?|~^";
var PUNC_BEFORE_EXPRESSION = "[{(,;:";
var PUNC_CHARS = PUNC_BEFORE_EXPRESSION + "`)}]";
var PUNC_OPENERS = "[{(";
var PUNC_SEPARATORS = ",;:";
var PUNC_CLOSERS = ")}]";
var PUNC_AFTER_EXPRESSION = PUNC_SEPARATORS + PUNC_CLOSERS;
var PUNC_BEFORE_EXPRESSION = PUNC_OPENERS + PUNC_SEPARATORS;
var PUNC_CHARS = PUNC_BEFORE_EXPRESSION + "`" + PUNC_CLOSERS;
var WHITESPACE_CHARS = NEWLINE_CHARS + " \u00a0\t\f\u000b\u200b\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF";
var NON_IDENTIFIER_CHARS = makePredicate(characters("./'\"" + OPERATOR_CHARS + PUNC_CHARS + WHITESPACE_CHARS));
NEWLINE_CHARS = makePredicate(characters(NEWLINE_CHARS));
OPERATOR_CHARS = makePredicate(characters(OPERATOR_CHARS));
PUNC_AFTER_EXPRESSION = makePredicate(characters(PUNC_AFTER_EXPRESSION));
PUNC_BEFORE_EXPRESSION = makePredicate(characters(PUNC_BEFORE_EXPRESSION));
PUNC_CHARS = makePredicate(characters(PUNC_CHARS));
WHITESPACE_CHARS = makePredicate(characters(WHITESPACE_CHARS));
@@ -692,6 +697,7 @@ function parse($TEXT, options) {
in_directives : true,
in_funarg : -1,
in_function : 0,
in_generator : false,
in_loop : 0,
labels : [],
peeked : null,
@@ -829,12 +835,17 @@ function parse($TEXT, options) {
if (is_token(peek(), "keyword", "function")) {
next();
next();
return function_(AST_AsyncDefun);
if (!is("operator", "*")) return function_(AST_AsyncDefun);
next();
return function_(AST_AsyncGeneratorDefun);
}
break;
case "await":
if (S.in_async) return simple_statement();
break;
case "yield":
if (S.in_generator) return simple_statement();
break;
}
return is_token(peek(), "punc", ":")
? labeled_statement()
@@ -905,7 +916,9 @@ function parse($TEXT, options) {
case "function":
next();
return function_(AST_Defun);
if (!is("operator", "*")) return function_(AST_Defun);
next();
return function_(AST_GeneratorDefun);
case "if":
next();
@@ -1181,15 +1194,15 @@ function parse($TEXT, options) {
var function_ = function(ctor) {
var was_async = S.in_async;
var was_gen = S.in_generator;
var name;
if (ctor === AST_AsyncDefun) {
if (/Defun$/.test(ctor.TYPE)) {
name = as_symbol(AST_SymbolDefun);
S.in_async = true;
} else if (ctor === AST_Defun) {
name = as_symbol(AST_SymbolDefun);
S.in_async = false;
S.in_async = /^Async/.test(ctor.TYPE);
S.in_generator = /Generator/.test(ctor.TYPE);
} else {
S.in_async = ctor === AST_AsyncFunction;
S.in_async = /^Async/.test(ctor.TYPE);
S.in_generator = /Generator/.test(ctor.TYPE);
name = as_symbol(AST_SymbolLambda, true);
}
if (name && ctor !== AST_Accessor && !(name instanceof AST_SymbolDeclaration))
@@ -1218,6 +1231,7 @@ function parse($TEXT, options) {
--S.in_function;
S.in_loop = loop;
S.labels = labels;
S.in_generator = was_gen;
S.in_async = was_async;
return new ctor({
name: name,
@@ -1481,7 +1495,13 @@ function parse($TEXT, options) {
}
if (is("keyword", "function")) {
next();
var func = function_(AST_Function);
var func;
if (is("operator", "*")) {
next();
func = function_(AST_GeneratorFunction);
} else {
func = function_(AST_Function);
}
func.start = start;
func.end = prev();
return subscripts(func, allow_calls);
@@ -1492,7 +1512,13 @@ function parse($TEXT, options) {
if (sym.name == "async") {
if (is("keyword", "function")) {
next();
var func = function_(AST_AsyncFunction);
var func;
if (is("operator", "*")) {
next();
func = function_(AST_AsyncGeneratorFunction);
} else {
func = function_(AST_AsyncFunction);
}
func.start = start;
func.end = prev();
return subscripts(func, allow_calls);
@@ -1567,6 +1593,21 @@ function parse($TEXT, options) {
// allow trailing comma
if (!options.strict && is("punc", "}")) break;
var start = S.token;
if (is("operator", "*")) {
next();
var key = as_property_key();
var gen_start = S.token;
var gen = function_(AST_GeneratorFunction);
gen.start = gen_start;
gen.end = prev();
a.push(new AST_ObjectKeyVal({
start: start,
key: key,
value: gen,
end: prev(),
}));
continue;
}
if (is("operator", "...")) {
next();
a.push(new AST_Spread({
@@ -1628,9 +1669,10 @@ function parse($TEXT, options) {
}
if (start.type == "name") switch (key) {
case "async":
var is_gen = is("operator", "*") && next();
key = as_property_key();
var func_start = S.token;
var func = function_(AST_AsyncFunction);
var func = function_(is_gen ? AST_AsyncGeneratorFunction : AST_AsyncFunction);
func.start = func_start;
func.end = prev();
a.push(new AST_ObjectKeyVal({
@@ -1694,6 +1736,7 @@ function parse($TEXT, options) {
function _make_symbol(type, token) {
var name = token.value;
if (name === "await" && S.in_async) unexpected(token);
if (name === "yield" && S.in_generator) unexpected(token);
return new (name === "this" ? AST_This : type)({
name: "" + name,
start: token,
@@ -1870,12 +1913,42 @@ function parse($TEXT, options) {
return expr;
};
function maybe_unary() {
function maybe_unary(no_in) {
var start = S.token;
if (S.in_async && is("name", "await")) {
if (S.in_funarg === S.in_function) croak("Invalid use of await in function argument");
S.input.context().regex_allowed = true;
next();
return new AST_Await({
start: start,
expression: maybe_unary(no_in),
end: prev(),
});
}
if (S.in_generator && is("name", "yield")) {
if (S.in_funarg === S.in_function) croak("Invalid use of yield in function argument");
S.input.context().regex_allowed = true;
next();
var exp = null;
var nested = false;
if (is("operator", "*")) {
next();
exp = maybe_assign(no_in);
nested = true;
} else if (is("punc") ? !PUNC_AFTER_EXPRESSION[S.token.value] : !can_insert_semicolon()) {
exp = maybe_assign(no_in);
}
return new AST_Yield({
start: start,
expression: exp,
nested: nested,
end: prev(),
});
}
if (is("operator") && UNARY_PREFIX[start.value]) {
next();
handle_regexp();
var ex = make_unary(AST_UnaryPrefix, start, maybe_await());
var ex = make_unary(AST_UnaryPrefix, start, maybe_unary(no_in));
ex.start = start;
ex.end = prev();
return ex;
@@ -1906,26 +1979,13 @@ function parse($TEXT, options) {
return new ctor({ operator: op, expression: expr });
}
function maybe_await() {
var start = S.token;
if (!(S.in_async && is("name", "await"))) return maybe_unary();
if (S.in_funarg === S.in_function) croak("Invalid use of await in function argument");
S.input.context().regex_allowed = true;
next();
return new AST_Await({
start: start,
expression: maybe_await(),
end: prev(),
});
}
var expr_op = function(left, min_prec, no_in) {
var op = is("operator") ? S.token.value : null;
if (op == "in" && no_in) op = null;
var prec = op != null ? PRECEDENCE[op] : null;
if (prec != null && prec > min_prec) {
next();
var right = expr_op(maybe_await(), op == "**" ? prec - 1 : prec, no_in);
var right = expr_op(maybe_unary(no_in), op == "**" ? prec - 1 : prec, no_in);
return expr_op(new AST_Binary({
start : left.start,
left : left,
@@ -1938,7 +1998,7 @@ function parse($TEXT, options) {
};
function expr_ops(no_in) {
return expr_op(maybe_await(), 0, no_in);
return expr_op(maybe_unary(no_in), 0, no_in);
}
var maybe_conditional = function(no_in) {