Improve support for binding pattern

Including improvements for parameters, variable assignment and
catch parameter.
This commit is contained in:
Anthony Van de Gejuchte
2016-08-20 22:32:29 +02:00
parent 1db50c3b16
commit 13ed445607
14 changed files with 727 additions and 312 deletions

View File

@@ -46,7 +46,7 @@
var KEYWORDS = 'break case catch class const continue debugger default delete do else export extends finally for function if in instanceof new return switch throw try typeof var let void while with import';
var KEYWORDS_ATOM = 'false null true';
var RESERVED_WORDS = 'enum implements interface package private protected public static super this' + KEYWORDS_ATOM + " " + KEYWORDS;
var RESERVED_WORDS = 'enum implements interface package private protected public static super this ' + KEYWORDS_ATOM + " " + KEYWORDS;
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case yield';
KEYWORDS = makePredicate(KEYWORDS);
@@ -1266,7 +1266,7 @@ function parse($TEXT, options) {
if (in_statement && !name)
unexpected();
var args = params_or_seq_().as_params(croak);
var args = parameters();
var body = _function_body(true, is_generator || is_generator_property);
return new ctor({
start : args.start,
@@ -1278,6 +1278,276 @@ function parse($TEXT, options) {
});
};
function track_used_binding_identifiers(is_parameter, strict) {
var parameters = {};
var duplicate = false;
var default_assignment = false;
var spread = false;
var strict_mode = !!strict;
var tracker = {
add_parameter: function(token) {
if (parameters["$" + token.value] !== undefined) {
if (duplicate === false) {
duplicate = token;
}
tracker.check_strict();
} else {
parameters["$" + token.value] = true;
if (is_parameter) {
switch (token.value) {
case "arguments":
case "eval":
case "yield":
if (strict_mode) {
token_error(token, "SyntaxError: Unexpected " + token.value + " identifier as parameter inside strict mode");
}
break;
default:
if (RESERVED_WORDS(token.value)) {
unexpected();
}
}
}
}
},
mark_default_assignment: function(token) {
if (default_assignment === false) {
default_assignment = token;
}
},
mark_spread: function(token) {
if (spread === false) {
spread = token;
}
},
mark_strict_mode: function() {
strict_mode = true;
},
is_strict: function() {
return default_assignment !== false || spread !== false || strict_mode
},
check_strict: function() {
if (tracker.is_strict() && duplicate !== false) {
token_error(duplicate, "SyntaxError: Parameter " + duplicate.value + " was used already");
}
}
};
return tracker;
}
function parameters() {
var start = S.token;
var first = true;
var params = [];
var used_parameters = track_used_binding_identifiers(true, S.input.has_directive("use strict"));
expect("(");
while (!is("punc", ")")) {
if (first) {
first = false;
} else {
expect(",");
}
var param = parameter(used_parameters);
params.push(param);
if (param instanceof AST_Expansion) {
break;
}
}
next();
return params;
}
function parameter(used_parameters, symbol_type) {
var param;
var expand = false;
if (used_parameters === undefined) {
used_parameters = track_used_binding_identifiers(true, S.input.has_directive("use strict"));
}
if (is("expand", "...")) {
expand = S.token;
used_parameters.mark_spread(S.token);
next();
}
param = binding_element(used_parameters, symbol_type);
if (is("operator", "=") && expand === false) {
used_parameters.mark_default_assignment(S.token);
next();
param.default = expression(false);
}
if (expand !== false) {
if (!is("punc", ")")) {
unexpected();
}
param = new AST_Expansion({
start: expand,
expression: param,
end: expand
});
}
used_parameters.check_strict();
return param;
}
function binding_element(used_parameters, symbol_type) {
var elements = [];
var first = true;
var is_expand = false;
var expand_token;
var first_token = S.token;
if (used_parameters === undefined) {
used_parameters = track_used_binding_identifiers(false, S.input.has_directive("use strict"));
}
symbol_type = symbol_type === undefined ? AST_SymbolFunarg : symbol_type;
if (is("punc", "[")) {
next();
while (!is("punc", "]")) {
if (first) {
first = false;
} else {
expect(",");
}
if (is("expand", "...")) {
is_expand = true;
expand_token = S.token;
used_parameters.mark_spread(S.token);
next();
}
if (is("punc")) {
switch (S.token.value) {
case ",":
case "]": // Last element
elements.push(new AST_Hole({
start: S.token,
end: S.token
}));
continue;
case "[":
case "{":
elements.push(binding_element(used_parameters, symbol_type));
break;
default:
unexpected();
}
} else if (is("name")) {
used_parameters.add_parameter(S.token);
elements.push(new symbol_type({
start: S.token,
name: S.token.value,
end: S.token
}));
next();
} else {
croak("SyntaxError: Invalid function parameter");
}
if (is("operator", "=") && is_expand === false) {
used_parameters.mark_default_assignment(S.token);
next();
elements[elements.length - 1].default = expression(false);
}
if (is_expand) {
if (!is("punc", "]")) {
unexpected(); // Must be last element
}
elements[elements.length - 1] = new AST_Expansion({
start: expand_token,
expression: elements[elements.length - 1],
end: expand_token
});
}
}
expect("]");
used_parameters.check_strict();
return new AST_Destructuring({
start: first_token,
names: elements,
is_array: true,
end: prev()
});
} else if (is("punc", "{")) {
next();
while (!is("punc", "}")) {
if (first) {
first = false;
} else {
expect(",");
}
if (is("name") && (is_token(peek(), "punc") || is_token(peek(), "operator")) && [",", "}", "="].indexOf(peek().value) !== -1) {
used_parameters.add_parameter(S.token);
elements.push(new symbol_type({
start: S.token,
name: S.token.value,
end: S.token
}));
next();
} else if (is("punc", "}")) {
continue; // Allow trailing hole
} else {
var property_token = S.token;
var property = as_property_name();
if (property === null) {
unexpected(prev());
} else if (prev().type === "name" && !is("punc", ":")) {
elements.push(new AST_SymbolFunarg({
start: prev(),
name: property_token,
end: prev()
}));
} else if (property instanceof AST_Node) {
expect(":");
elements.push(new AST_ObjectComputedKeyVal({
start: property_token,
key: property,
value: binding_element(used_parameters, symbol_type),
end: prev()
}));
} else {
expect(":");
elements.push(new AST_ObjectKeyVal({
start: property_token,
quote: property_token.quote,
end: prev(),
key: property,
value: binding_element(used_parameters, symbol_type)
}));
}
}
if (is("operator", "=")) {
used_parameters.mark_default_assignment(S.token);
next();
elements[elements.length - 1].default = expression(false);
}
}
expect("}");
used_parameters.check_strict();
return new AST_Destructuring({
start: first_token,
names: elements,
is_array: false,
end: prev()
});
} else if (is("name")) {
used_parameters.add_parameter(S.token);
next();
return new symbol_type({
start: prev(),
name: prev().value,
end: prev()
});
} else {
croak("SyntaxError: Invalid function parameter");
}
}
function params_or_seq_() {
var start = S.token
expect("(");
@@ -1434,7 +1704,7 @@ function parse($TEXT, options) {
var start = S.token;
next();
expect("(");
var name = as_symbol(AST_SymbolCatch);
var name = parameter(undefined, AST_SymbolCatch);
expect(")");
bcatch = new AST_Catch({
start : start,
@@ -1472,7 +1742,7 @@ function parse($TEXT, options) {
if (is("punc", "{") || is("punc", "[")) {
def = new AST_VarDef({
start: S.token,
name: destructuring_(sym_type),
name: binding_element(undefined ,sym_type),
value: is("operator", "=") ? (expect_token("operator", "="), expression(false, no_in)) : null,
end: prev()
});
@@ -1492,48 +1762,6 @@ function parse($TEXT, options) {
return a;
};
var destructuring_ = embed_tokens(function (sym_type) {
var is_array = is("punc", "[");
var closing = is_array ? ']' : '}';
var sym_type = sym_type || AST_SymbolRef;
next();
var first = true, children = [];
while (!is("punc", closing)) {
if (first) first = false; else expect(",");
if (is("punc", closing)) break;
if (is("punc", ",")) {
children.push(new AST_Hole({ start: S.token, end: S.token }));
} else if (is("punc", "[") || is("punc", "{")) {
children.push(destructuring_(sym_type));
} else if (is("expand", "...")) {
next();
var symbol = _make_symbol(sym_type);
children.push(new AST_Expansion({
start: prev(),
expression: symbol,
end: S.token
}));
next();
} else if (is("name")) {
children.push(new (sym_type)({
name : String(S.token.value),
start : S.token,
default: (next(), is("operator", "=")) ? (next(), expression(false)) : undefined,
end : S.token
}));
} else {
children.push(expression());
}
}
next()
return new AST_Destructuring({
names: children,
is_array: is_array
})
});
var var_ = function(no_in) {
return new AST_Var({
start : prev(),