Parse assignment patterns in-place in certain contexts.

* Parsing assignables without extra transform step when possible (speed-up).
* Added support for shorthand defaults in such certain contexts (issue #181).

Conflicts:
	acorn.js
	acorn_loose.js
This commit is contained in:
Sebastian McKenzie 2015-01-09 05:54:16 +11:00
parent 8c25cb0d80
commit 5c0d9a0e61
3 changed files with 166 additions and 30 deletions

View File

@ -1828,7 +1828,8 @@
function has(obj, propName) {
return Object.prototype.hasOwnProperty.call(obj, propName);
}
// Convert existing expression atom to assignable pattern
// Convert existing expression atom to assignable pattern
// if possible.
function toAssignable(node, allowSpread, checkType) {
@ -1878,6 +1879,53 @@
return node;
}
// Parses lvalue (assignable) atom.
function parseAssignableAtom() {
if (options.ecmaVersion < 6) return parseIdent();
switch (tokType) {
case _name:
return parseIdent();
case _bracketL:
var node = startNode();
next();
var elts = node.elements = [], first = true;
while (!eat(_bracketR)) {
first ? first = false : expect(_comma);
if (tokType === _ellipsis) {
var spread = startNode();
next();
spread.argument = parseAssignableAtom();
checkSpreadAssign(spread.argument);
elts.push(finishNode(spread, "SpreadElement"));
expect(_bracketR);
break;
}
elts.push(tokType === _comma ? null : parseMaybeDefault());
}
return finishNode(node, "ArrayPattern");
case _braceL:
return parseObj(true);
default:
unexpected();
}
}
// Parses assignment pattern around given atom if possible.
function parseMaybeDefault(startPos, left) {
left = left || parseAssignableAtom();
if (!eat(_eq)) return left;
var node = startPos ? startNodeAt(startPos) : startNode();
node.operator = "=";
node.left = left;
node.right = parseMaybeAssign();
return finishNode(node, "AssignmentPattern");
}
// Checks if node can be assignable spread argument.
function checkSpreadAssign(node) {
@ -2361,7 +2409,7 @@
node.kind = kind;
for (;;) {
var decl = startNode();
decl.id = options.ecmaVersion >= 6 ? toAssignable(parseExprAtom()) : parseIdent();
decl.id = parseAssignableAtom();
checkLVal(decl.id, true);
if (tokType === _colon) {
@ -2788,7 +2836,7 @@
// Parse an object literal.
function parseObj() {
function parseObj(isPattern) {
var node = startNode(), first = true, propHash = {};
node.properties = [];
next();
@ -2798,7 +2846,7 @@
if (options.allowTrailingCommas && eat(_braceR)) break;
} else first = false;
var prop = startNode(), isGenerator = false, isAsync = false;
var prop = startNode(), start, isGenerator = false, isAsync = false;
if (options.ecmaVersion >= 7 && tokType === _ellipsis) {
prop = parseMaybeUnary();
prop.type = "SpreadProperty";
@ -2808,7 +2856,11 @@
if (options.ecmaVersion >= 6) {
prop.method = false;
prop.shorthand = false;
isGenerator = eat(_star);
if (isPattern) {
start = storeCurrentPos();
} else {
isGenerator = eat(_star);
}
}
if (options.ecmaVersion >= 7 && tokType === _name && tokVal === "async") {
var asyncId = parseIdent();
@ -2827,22 +2879,23 @@
if (tokType !== _parenL) unexpected();
}
if (eat(_colon)) {
prop.value = parseExpression(true);
prop.value = isPattern ? parseMaybeDefault(start) : parseMaybeAssign();
prop.kind = "init";
} else if (options.ecmaVersion >= 6 && tokType === _parenL) {
if (isPattern) unexpected();
prop.kind = "init";
prop.method = true;
prop.value = parseMethod(isGenerator, isAsync);
} else if (options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" &&
(prop.key.name === "get" || prop.key.name === "set"|| (options.playground && prop.key.name === "memo")) &&
(tokType != _comma && tokType != _braceR)) {
if (isGenerator || isAsync) unexpected();
if (isGenerator || isAsync || isPattern) unexpected();
prop.kind = prop.key.name;
parsePropertyName(prop);
prop.value = parseMethod(false, false);
} else if (options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {
prop.kind = "init";
prop.value = prop.key;
prop.value = isPattern ? parseMaybeDefault(start, prop.key) : prop.key;
prop.shorthand = true;
} else unexpected();
@ -2850,7 +2903,7 @@
checkPropClash(prop, propHash);
node.properties.push(finishNode(prop, "Property"));
}
return finishNode(node, "ObjectExpression");
return finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression");
}
function parsePropertyName(prop) {
@ -2960,7 +3013,7 @@
if (eat(_parenR)) {
break;
} else if (options.ecmaVersion >= 6 && eat(_ellipsis)) {
node.rest = toAssignable(parseExprAtom(), false, true);
node.rest = parseAssignableAtom();
checkSpreadAssign(node.rest);
parseFunctionParam(node.rest);
expect(_parenR);
@ -3358,7 +3411,7 @@
var block = startNode();
next();
expect(_parenL);
block.left = toAssignable(parseExprAtom());
block.left = parseAssignableAtom();
checkLVal(block.left, true);
if (tokType !== _name || tokVal !== "of") unexpected();
next();

View File

@ -983,6 +983,10 @@
case "SpreadElement":
node.argument = toAssignable(node.argument);
break;
case "AssignmentExpression":
node.type = "AssignmentPattern";
break;
}
}
return checkLVal(node);

View File

@ -14393,30 +14393,39 @@ test('var {get} = obj;', {
test("var {propName: localVar = defaultValue} = obj", {
type: "Program",
range: [0, 45],
body: [{
type: "VariableDeclaration",
range: [0, 45],
declarations: [{
type: "VariableDeclarator",
range: [4, 45],
id: {
type: "ObjectPattern",
range: [4, 39],
properties: [{
type: "Property",
range: [5, 38],
method: false,
shorthand: false,
computed: false,
key: {
type: "Identifier",
range: [5, 13],
name: "propName"
},
value: {
type: "AssignmentPattern",
range: [5, 38],
operator: "=",
left: {
type: "Identifier",
range: [15, 23],
name: "localVar"
},
right: {
type: "Identifier",
range: [26, 38],
name: "defaultValue"
}
},
@ -14425,44 +14434,114 @@ test("var {propName: localVar = defaultValue} = obj", {
},
init: {
type: "Identifier",
range: [42, 45],
name: "obj"
}
}],
kind: "var"
}]
}, {ecmaVersion: 6});
}, {
ecmaVersion: 6,
ranges: true,
locations: true,
loose: false
});
test("var [a = 1, b = 2] = arr", {
test("var {propName = defaultValue} = obj", {
type: "Program",
range: [0, 35],
body: [{
type: "VariableDeclaration",
range: [0, 35],
declarations: [{
type: "VariableDeclarator",
range: [4, 35],
id: {
type: "ArrayPattern",
elements: [
{
type: "AssignmentPattern",
operator: "=",
left: {type: "Identifier", name: "a"},
right: {
type: "Literal",
value: 1
}
type: "ObjectPattern",
range: [4, 29],
properties: [{
type: "Property",
range: [5, 28],
method: false,
shorthand: true,
computed: false,
key: {
type: "Identifier",
range: [5, 13],
name: "propName"
},
{
kind: "init",
value: {
type: "AssignmentPattern",
range: [5, 28],
operator: "=",
left: {type: "Identifier", name: "b"},
left: {
type: "Identifier",
range: [5, 13],
name: "propName"
},
right: {
type: "Literal",
value: 2
type: "Identifier",
range: [16, 28],
name: "defaultValue"
}
}
]
}]
},
init: {type: "Identifier", name: "arr"}
init: {
type: "Identifier",
range: [32, 35],
name: "obj"
}
}],
kind: "var"
}]
}, {ecmaVersion: 6});
}, {
ecmaVersion: 6,
ranges: true,
locations: true,
loose: false
});
test("var [localVar = defaultValue] = obj", {
type: "Program",
range: [0, 35],
body: [{
type: "VariableDeclaration",
range: [0, 35],
declarations: [{
type: "VariableDeclarator",
range: [4, 35],
id: {
type: "ArrayPattern",
range: [4, 29],
elements: [{
type: "AssignmentPattern",
range: [16, 28],
operator: "=",
left: {
type: "Identifier",
range: [5, 13],
name: "localVar"
},
right: {
type: "Identifier",
range: [16, 28],
name: "defaultValue"
}
}]
},
init: {
type: "Identifier",
range: [32, 35],
name: "obj"
}
}],
kind: "var"
}]
}, {
ecmaVersion: 6,
ranges: true,
locations: true,
loose: false
});