[Pkg-javascript-commits] [node-acorn-jsx] 13/484: Partial parser comments
Bastien Roucariès
rouca at moszumanska.debian.org
Sat Aug 19 14:19:57 UTC 2017
This is an automated email from the git hooks/post-receive script.
rouca pushed a commit to branch master
in repository node-acorn-jsx.
commit 32e62f1cfcd7d90f7d68b0496759ef4f2a3bf38c
Author: Marijn Haverbeke <marijnh at gmail.com>
Date: Tue Oct 2 09:29:52 2012 +0200
Partial parser comments
---
acorn.js | 604 +++++++++++++++++++++++++++++++++++++---------------------
test/tests.js | 6 +-
2 files changed, 392 insertions(+), 218 deletions(-)
diff --git a/acorn.js b/acorn.js
index 9d6e6ab..18b2e9f 100644
--- a/acorn.js
+++ b/acorn.js
@@ -748,12 +748,37 @@
// ## Parser
+ // A recursive descent parser operates by defining functions for all
+ // syntactic elements, and recursively calling those, each function
+ // advancing the input stream and returning an AST node. Precedence
+ // of constructs (for example, the fact that `!x[1]` means `!(x[1])`
+ // instead of `(!x)[1]` is handled by the fact that the parser
+ // function that parses unary prefix operators is called first, and
+ // in turn calls the function that parses `[]` subscripts — that
+ // way, it'll receive the node for `x[1]` already parsed, and wraps
+ // *that* in the unary operator node.
+ //
+ // Acorn uses an [operator precedence parser][opp] to handle binary
+ // operator precedence, because it is much more compact than using
+ // the technique outlined above, which uses different, nesting
+ // functions to specify precedence, for all of the ten binary
+ // precedence levels that JavaScript defines.
+ //
+ // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser
+
+ // ### Parser utilities
+
+ // Continue to the next token.
+
function next() {
lastStart = tokStart;
lastEnd = tokEnd;
readToken();
}
+ // Enter strict mode. Re-reads the next token to please pedantic
+ // tests ("use strict"; 010; -- should fail).
+
function setStrict(strct) {
strict = strct;
tokPos = options.linePositions ? lastEnd.offset : lastEnd;
@@ -761,8 +786,11 @@
readToken();
}
+ // Start an AST node, attaching a start offset and optionally a
+ // `commentsBefore` property to it.
+
function startNode() {
- var node = {type: null, start: tokStart};
+ var node = {type: null, start: tokStart, end: null};
if (options.trackComments && tokCommentsBefore) {
node.commentsBefore = tokCommentsBefore;
tokCommentsBefore = null;
@@ -770,6 +798,11 @@
return node;
}
+ // Start a node whose start offset/comments information should be
+ // based on the start of another node. For example, a binary
+ // operator node is only started after its left-hand side has
+ // already been parsed.
+
function startNodeFrom(other) {
var node = {type: null, start: other.start};
if (other.commentsBefore) {
@@ -779,37 +812,42 @@
return node;
}
+ // Finish an AST node, adding `type`, `end`, and `commentsAfter`
+ // properties.
+ //
+ // We keep track of the last node that we finished, in order
+ // 'bubble' `commentsAfter` properties up to the biggest node. I.e.
+ // in '`1 + 1 // foo', the comment should be attached to the binary
+ // operator node, not the second literal node.
+
+ var lastFinishedNode;
+
function finishNode(node, type) {
- if (type != null) node.type = type;
+ node.type = type;
node.end = lastEnd;
- if (options.trackComments && tokCommentsAfter)
- node.commentsAfter = tokCommentsAfter;
+ if (options.trackComments) {
+ if (tokCommentsAfter) {
+ node.commentsAfter = tokCommentsAfter;
+ tokCommentsAfter = null;
+ } else if (lastFinishedNode && lastFinishedNode.end === lastEnd) {
+ node.commentsAfter = lastFinishedNode.commentsAfter;
+ lastFinishedNode.commentsAfter = null;
+ }
+ lastFinishedNode = node;
+ }
return node;
}
- function parseTopLevel() {
- initTokenState();
- lastStart = lastEnd = options.linePositions ? curLinePos() : tokPos;
- inFunction = strict = null;
- labels = [];
- readToken();
-
- var node = startNode(), first = true;
- node.body = [];
- while (tokType !== _eof) {
- var stmt = parseStatement();
- node.body.push(stmt);
- if (first && isUseStrict(stmt)) setStrict(true);
- first = false;
- }
- return finishNode(node, "Program");
- };
+ // Test whether a statement node is the string literal `"use strict"`.
function isUseStrict(stmt) {
return options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" &&
stmt.expression.type === "Literal" && stmt.expression.value === "use strict";
}
+ // Predicate that tests whether the next token is of the given
+ // type, and if yes, consumes it as a side effect.
+
function eat(type) {
if (tokType === type) {
next();
@@ -817,6 +855,8 @@
}
}
+ // Test whether a semicolon can be inserted at the current position.
+
function canInsertSemicolon() {
return tokType === _eof || tokType === _braceR ||
!options.strictSemicolons &&
@@ -824,27 +864,79 @@
: input.slice(lastEnd, tokStart));
}
+ // Consume a semicolon, or, failing that, see if we are allowed to
+ // pretend that there is a semicolon at this position.
+
function semicolon() {
if (!eat(_semi) && !canInsertSemicolon()) unexpected();
}
+ // Expect a token of a given type. If found, consume it, otherwise,
+ // raise an unexpected token error.
+
function expect(type) {
if (tokType === type) next();
else unexpected();
}
+ // Raise an unexpected token error.
+
function unexpected() {
raise(tokStart, "Unexpected token");
}
+ // Verify that a node is an lval — something that can be assigned
+ // to.
+
+ function checkLVal(expr) {
+ if (expr.type !== "Identifier" && expr.type !== "MemberExpression")
+ raise(expr.start, "Assigning to rvalue");
+ if (strict && expr.type === "Identifier" && isStrictBadIdWord(expr.name))
+ raise(expr.start, "Assigning to " + expr.name + " in strict mode");
+ }
+
+ // ### Statement parsing
+
+ // Parse a program. Initializes the parser, reads any number of
+ // statements, and wraps them in a Program node.
+
+ function parseTopLevel() {
+ initTokenState();
+ lastStart = lastEnd = options.linePositions ? curLinePos() : tokPos;
+ inFunction = strict = null;
+ labels = [];
+ readToken();
+
+ var node = startNode(), first = true;
+ node.body = [];
+ while (tokType !== _eof) {
+ var stmt = parseStatement();
+ node.body.push(stmt);
+ if (first && isUseStrict(stmt)) setStrict(true);
+ first = false;
+ }
+ return finishNode(node, "Program");
+ };
+
+ var loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"};
+
+ // Parse a single statement.
+ //
+ // If expecting a statement and finding a slash operator, parse a
+ // regular expression literal. This is to handle cases like
+ // `if (foo) /blah/.exec(foo);`, where looking at the previous token
+ // does not help.
+
function parseStatement() {
- // if expecting a statement and found a slash as operator,
- // it must be a literal regexp.
if (tokType === _slash)
readToken(true);
var starttype = tokType, node = startNode();
+ // Most types of statements are recognized by the keyword they
+ // start with. Many are trivial to parse, some require a bit of
+ // complexity.
+
switch (starttype) {
case _break: case _continue:
next();
@@ -855,6 +947,9 @@
node.label = parseIdent();
semicolon();
}
+
+ // Verify that there is an actual destination to break or
+ // continue to.
for (var i = 0; i < labels.length; ++i) {
var lab = labels[i];
if ((node.label == null || lab.name === node.label.name) &&
@@ -869,15 +964,24 @@
case _do:
next();
- labels.push({kind: "loop"});
+ labels.push(loopLabel);
node.body = parseStatement();
labels.pop();
expect(_while);
node.test = parseParenExpression();
return finishNode(node, "DoWhileStatement");
+ // Disambiguating between a `for` and a `for`/`in` loop is
+ // non-trivial. Basically, we have to parse the init `var`
+ // statement or expression, disallowing the `in` operator (see
+ // the second parameter to `parseExpression`), and then check
+ // whether the next token is `in`. When there is no init part
+ // (semicolon immediately after the opening parenthesis), it is
+ // a regular `for` loop.
+
case _for:
next();
+ labels.push(loopLabel);
expect(_parenL);
if (tokType === _semi) return parseFor(node, null);
if (tokType === _var) {
@@ -906,6 +1010,11 @@
case _return:
if (!inFunction) raise(tokStart, "'return' outside of function");
next();
+
+ // In `return` (and `break`/`continue`), the keywords with
+ // optional arguments, we eagerly look for a semicolon or the
+ // possibility to insert one.
+
if (eat(_semi) || canInsertSemicolon()) node.argument = null;
else { node.argument = parseExpression(); semicolon(); }
return finishNode(node, "ReturnStatement");
@@ -915,8 +1024,13 @@
node.discriminant = parseParenExpression();
node.cases = [];
expect(_braceL);
- labels.push({kind: "switch"});
- for (var cur, sawDefault; !eat(_braceR);) {
+ labels.push(switchLabel);
+
+ // Statements under must be grouped (by label) in SwitchCase
+ // nodes. `cur` is used to keep the node that we are currently
+ // adding statements to.
+
+ for (var cur, sawDefault; tokType != _braceR;) {
if (tokType === _case || tokType === _default) {
var isCase = tokType === _case;
if (cur) finishNode(cur, "SwitchCase");
@@ -935,6 +1049,7 @@
}
}
if (cur) finishNode(cur, "SwitchCase");
+ next(); // Closing brace
labels.pop();
return finishNode(node, "SwitchStatement");
@@ -973,7 +1088,7 @@
case _while:
next();
node.test = parseParenExpression();
- labels.push({kind: "loop"});
+ labels.push(loopLabel);
node.body = parseStatement();
labels.pop();
return finishNode(node, "WhileStatement");
@@ -992,6 +1107,12 @@
next();
return finishNode(node, "EmptyStatement");
+ // If the statement does not start with a statement keyword or a
+ // brace, it's an ExpressionStatement or LabeledStatement. We
+ // simply start parsing an expression, and afterwards, if the
+ // next token is a colon and the expression was a simple
+ // Identifier node, we switch to interpreting it as a label.
+
default:
var maybeName = tokVal, expr = parseExpression();
if (starttype === _name && expr.type === "Identifier" && eat(_colon)) {
@@ -1010,6 +1131,9 @@
}
}
+ // Used for constructs like `switch` and `if` that insist on
+ // parentheses around their expression.
+
function parseParenExpression() {
expect(_parenL);
var val = parseExpression();
@@ -1017,6 +1141,10 @@
return val;
}
+ // Parse a semicolon-enclosed block of statements, handling `"use
+ // strict"` declarations when `allowStrict` is true (used for
+ // function bodies).
+
function parseBlock(allowStrict) {
var node = startNode(), first = true, strict = false, oldStrict;
node.body = [];
@@ -1034,6 +1162,10 @@
return finishNode(node, "BlockStatement");
}
+ // Parse a regular `for` loop. The disambiguation code in
+ // `parseStatement` will already have parsed the init statement or
+ // expression.
+
function parseFor(node, init) {
node.init = init;
expect(_semi);
@@ -1041,22 +1173,24 @@
expect(_semi);
node.update = tokType === _parenR ? null : parseExpression();
expect(_parenR);
- labels.push({kind: "loop"});
node.body = parseStatement();
labels.pop();
return finishNode(node, "ForStatement");
}
+ // Parse a `for`/`in` loop.
+
function parseForIn(node, init) {
node.left = init;
node.right = parseExpression();
expect(_parenR);
- labels.push({kind: "loop"});
node.body = parseStatement();
labels.pop();
return finishNode(node, "ForInStatement");
}
+ // Parse a list of variable declarations.
+
function parseVar(node, noIn) {
node.declarations = [];
node.kind = "var";
@@ -1072,113 +1206,150 @@
return finishNode(node, "VariableDeclaration");
}
- function parsePropertyName() {
- if (tokType === _num || tokType === _string) return parseExprAtom();
- return parseIdent(true);
- }
+ // ### Expression parsing
- function parseIdent(liberal) {
- var node = startNode();
- if (tokType !== _name) {
- if (liberal && tokType.keyword) node.name = tokType.keyword;
- else unexpected();
- } else node.name = tokVal;
- next();
- return finishNode(node, "Identifier");
- }
+ // These nest, from the most general expression type at the top to
+ // 'atomic', nondivisible expression types at the bottom. Most of
+ // the functions will simply let the function(s) below them parse,
+ // and, *if* the syntactic construct they handle is present, wrap
+ // the AST node that the inner parser gave them in another node.
- function parseFunction(node, isStatement) {
- if (tokType === _name) node.id = parseIdent();
- else if (isStatement) unexpected();
- else node.id = null;
- node.params = [];
- var first = true;
- expect(_parenL);
- while (!eat(_parenR)) {
- if (!first) expect(_comma); else first = false;
- node.params.push(parseIdent());
- }
+ // Parse a full expression. The arguments are used to forbid comma
+ // sequences (in argument lists, array literals, or object literals)
+ // or the `in` operator (in for loops initalization expressions).
- var oldInFunc = inFunction, oldLabels = labels;
- inFunction = true; labels = [];
- node.body = parseBlock(true);
- if (strict || node.body.body.length && isUseStrict(node.body.body[0])) {
- for (var i = node.id ? -1 : 0; i < node.params.length; ++i) {
- var id = i < 0 ? node.id : node.params[i];
- if (isStrictReservedWord(id.name) || isStrictBadIdWord(id.name))
- raise(id.start, "Defining '" + id.name + "' in strict mode");
- if (i >= 0) for (var j = 0; j < i; ++j) if (id.name === node.params[j].name)
- raise(id.start, "Argument name clash in strict mode");
- }
+ function parseExpression(noComma, noIn) {
+ var expr = parseMaybeAssign(noIn);
+ if (!noComma && tokType === _comma) {
+ var node = startNodeFrom(expr);
+ node.expressions = [expr];
+ while (eat(_comma)) node.expressions.push(parseMaybeAssign(noIn));
+ return finishNode(node, "SequenceExpression");
}
-
- inFunction = oldInFunc; labels = oldLabels;
- return finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
+ return expr;
}
- // EXPRESSION PARSING
+ // Parse an assignment expression. This includes applications of
+ // operators like `+=`.
- function parseExprList(close, allowTrailingComma, allowEmpty) {
- var elts = [], first = true;
- while (!eat(close)) {
- if (!first) {
- expect(_comma);
- if (allowTrailingComma && options.allowTrailingCommas && eat(close)) break;
- } else first = false;
+ function parseMaybeAssign(noIn) {
+ var left = parseMaybeConditional(noIn);
+ if (tokType.isAssign) {
+ var node = startNodeFrom(left);
+ node.operator = tokVal;
+ node.left = left;
+ next();
+ node.right = parseMaybeAssign(noIn);
+ checkLVal(left);
+ return finishNode(node, "AssignmentExpression");
+ }
+ return left;
+ }
- if (allowEmpty && tokType === _comma) elts.push(null);
- else elts.push(parseExpression(true));
+ // Parse a ternary conditional (`?:`) operator.
+
+ function parseMaybeConditional(noIn) {
+ var expr = parseExprOps(noIn);
+ if (eat(_question)) {
+ var node = startNodeFrom(expr);
+ node.test = expr;
+ node.consequent = parseExpression(true);
+ expect(_colon);
+ node.alternate = parseExpression(true, noIn);
+ return finishNode(node, "ConditionalExpression");
}
- return elts;
+ return expr;
}
- function parseNew() {
- var node = startNode();
- next();
- node.callee = parseSubscripts(parseExprAtom(false), false);
- if (eat(_parenL)) node.arguments = parseExprList(_parenR, false);
- else node.arguments = [];
- return finishNode(node, "NewExpression");
+ // Start the precedence parser.
+
+ function parseExprOps(noIn) {
+ return parseExprOp(parseMaybeUnary(noIn), -1, noIn);
}
- function parseObj() {
- var node = startNode(), first = true;
- node.properties = [];
- next();
- while (!eat(_braceR)) {
- if (!first) {
- expect(_comma);
- if (options.allowTrailingCommas && eat(_braceR)) break;
- } else first = false;
+ // Parse binary operators with the operator precedence parsing
+ // algorithm. `left` is the left-hand side of the operator.
+ // `minPrec` provides context that allows the function to stop and
+ // defer further parser to one of its callers when it encounters an
+ // operator that has a lower precedence than the set it is parsing.
- var prop = {key: parsePropertyName()}, isGetSet, kind;
- if (eat(_colon)) {
- prop.value = parseExpression(true);
- kind = prop.kind = "init";
- } else if (options.ecmaVersion >= 5 && prop.key.type === "Identifier" &&
- (prop.key.name === "get" || prop.key.name === "set")) {
- isGetSet = true;
- kind = prop.kind = prop.key.name;
- prop.key = parsePropertyName();
- if (!tokType === _parenL) unexpected();
- prop.value = parseFunction(startNode(), false);
- } else unexpected();
- if (prop.key.type === "Identifier" && (strict || isGetSet)) {
- for (var i = 0; i < node.properties.length; ++i) {
- var other = node.properties[i];
- if (other.key.name === prop.key.name) {
- var conflict = kind == other.kind || isGetSet && other.kind === "init" ||
- kind === "init" && (other.kind === "get" || other.kind === "set");
- if (conflict && !strict && kind === "init" && other.kind === "init") conflict = false;
- if (conflict) raise(prop.key.start, "Redefinition of property");
- }
- }
+ function parseExprOp(left, minPrec, noIn) {
+ var prec = tokType.binop;
+ if (prec != null && (!noIn || tokType !== _in)) {
+ if (prec > minPrec) {
+ var node = startNodeFrom(left);
+ node.left = left;
+ node.operator = tokVal;
+ next();
+ node.right = parseExprOp(parseMaybeUnary(noIn), prec, noIn);
+ var node = finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression");
+ return parseExprOp(node, minPrec, noIn);
}
- node.properties.push(prop);
}
- return finishNode(node, "ObjectExpression");
+ return left;
+ }
+
+ // Parse unary operators, both prefix and postfix.
+
+ function parseMaybeUnary(noIn) {
+ if (tokType.prefix) {
+ var node = startNode(), update = tokType.isUpdate;
+ node.operator = tokVal;
+ node.prefix = true;
+ next();
+ node.argument = parseMaybeUnary(noIn);
+ if (update) checkLVal(node.argument);
+ else if (strict && node.operator === "delete" &&
+ node.argument.type === "Identifier")
+ raise(node.start, "Deleting local variable in strict mode");
+ return finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
+ }
+ var expr = parseExprSubscripts();
+ while (tokType.postfix && !canInsertSemicolon()) {
+ var node = startNodeFrom(expr);
+ node.operator = tokVal;
+ node.prefix = false;
+ node.argument = expr;
+ checkLVal(expr);
+ next();
+ expr = finishNode(node, "UpdateExpression");
+ }
+ return expr;
+ }
+
+ // Parse call, dot, and `[]`-subscript expressions.
+
+ function parseExprSubscripts() {
+ return parseSubscripts(parseExprAtom());
+ }
+
+ function parseSubscripts(base, noCalls) {
+ if (eat(_dot)) {
+ var node = startNodeFrom(base);
+ node.object = base;
+ node.property = parseIdent(true);
+ node.computed = false;
+ return parseSubscripts(finishNode(node, "MemberExpression"), noCalls);
+ } else if (eat(_bracketL)) {
+ var node = startNodeFrom(base);
+ node.object = base;
+ node.property = parseExpression();
+ node.computed = true;
+ expect(_bracketR);
+ return parseSubscripts(finishNode(node, "MemberExpression"), noCalls);
+ } else if (!noCalls && eat(_parenL)) {
+ var node = startNodeFrom(base);
+ node.callee = base;
+ node.arguments = parseExprList(_parenR, false);
+ return parseSubscripts(finishNode(node, "CallExpression"), noCalls);
+ } else return base;
}
+ // Parse an atomic expression — either a single token that is an
+ // expression, an expression started by a keyword like `function` or
+ // `new`, or an expression wrapped in punctuation like `()`, `[]`,
+ // or `{}`.
+
function parseExprAtom() {
switch (tokType) {
case _name:
@@ -1228,120 +1399,123 @@
}
}
- function parseSubscripts(base, allowCalls) {
- if (eat(_dot)) {
- var node = startNodeFrom(base);
- node.object = base;
- node.property = parseIdent(true);
- node.computed = false;
- return parseSubscripts(finishNode(node, "MemberExpression"), allowCalls);
- } else if (eat(_bracketL)) {
- var node = startNodeFrom(base);
- node.object = base;
- node.property = parseExpression();
- node.computed = true;
- expect(_bracketR);
- return parseSubscripts(finishNode(node, "MemberExpression"), allowCalls);
- } else if (allowCalls && eat(_parenL)) {
- var node = startNodeFrom(base);
- node.callee = base;
- node.arguments = parseExprList(_parenR, false);
- return parseSubscripts(finishNode(node, "CallExpression"), allowCalls);
- } else return base;
- }
+ // New's precedence is slightly tricky. It must allow its argument
+ // to be a `[]` or dot subscript expression, but not a call — at
+ // least, not without wrapping it in parentheses. Thus, it uses the
- function parseExprSubscripts(allowCalls) {
- return parseSubscripts(parseExprAtom(allowCalls), allowCalls);
+ function parseNew() {
+ var node = startNode();
+ next();
+ node.callee = parseSubscripts(parseExprAtom(false), true);
+ if (eat(_parenL)) node.arguments = parseExprList(_parenR, false);
+ else node.arguments = [];
+ return finishNode(node, "NewExpression");
}
- function parseMaybeUnary(noIn) {
- if (tokType.prefix) {
- var node = startNode(), update = tokType.isUpdate;
- node.operator = tokVal;
- node.prefix = true;
- next();
- node.argument = parseMaybeUnary(noIn);
- if (update) checkLVal(node.argument);
- else if (strict && node.operator === "delete" &&
- node.argument.type === "Identifier")
- raise(node.start, "Deleting local variable in strict mode");
- return finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
- }
- var expr = parseExprSubscripts(true);
- while (tokType.postfix && !canInsertSemicolon()) {
- var node = startNodeFrom(expr);
- node.operator = tokVal;
- node.prefix = false;
- node.argument = expr;
- checkLVal(expr);
- next();
- expr = finishNode(node, "UpdateExpression");
- }
- return expr;
- }
+ // Parse an object literal.
- function parseExprOp(left, minPrec, noIn) {
- var prec = tokType.binop;
- if (prec != null && (!noIn || tokType !== _in)) {
- if (prec > minPrec) {
- var node = startNodeFrom(left);
- node.left = left;
- node.operator = tokVal;
- next();
- node.right = parseExprOp(parseMaybeUnary(noIn), prec, noIn);
- var node = finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression");
- return parseExprOp(node, minPrec, noIn);
+ function parseObj() {
+ var node = startNode(), first = true, sawGetSet = false;
+ node.properties = [];
+ next();
+ while (!eat(_braceR)) {
+ if (!first) {
+ expect(_comma);
+ if (options.allowTrailingCommas && eat(_braceR)) break;
+ } else first = false;
+
+ var prop = {key: parsePropertyName()}, isGetSet = false, kind;
+ if (eat(_colon)) {
+ prop.value = parseExpression(true);
+ kind = prop.kind = "init";
+ } else if (options.ecmaVersion >= 5 && prop.key.type === "Identifier" &&
+ (prop.key.name === "get" || prop.key.name === "set")) {
+ isGetSet = sawGetSet = true;
+ kind = prop.kind = prop.key.name;
+ prop.key = parsePropertyName();
+ if (!tokType === _parenL) unexpected();
+ prop.value = parseFunction(startNode(), false);
+ } else unexpected();
+
+ // getters and setters are not allowed to clash — either with
+ // each other or with an init property — and in strict mode,
+ // init properties are also not allowed to be repeated.
+
+ if (prop.key.type === "Identifier" && (strict || sawGetSet)) {
+ for (var i = 0; i < node.properties.length; ++i) {
+ var other = node.properties[i];
+ if (other.key.name === prop.key.name) {
+ var conflict = kind == other.kind || isGetSet && other.kind === "init" ||
+ kind === "init" && (other.kind === "get" || other.kind === "set");
+ if (conflict && !strict && kind === "init" && other.kind === "init") conflict = false;
+ if (conflict) raise(prop.key.start, "Redefinition of property");
+ }
+ }
}
+ node.properties.push(prop);
}
- return left;
+ return finishNode(node, "ObjectExpression");
}
- function parseExprOps(noIn) {
- return parseExprOp(parseMaybeUnary(noIn), -1, noIn);
+ function parsePropertyName() {
+ if (tokType === _num || tokType === _string) return parseExprAtom();
+ return parseIdent(true);
}
- function parseMaybeConditional(noIn) {
- var expr = parseExprOps(noIn);
- if (eat(_question)) {
- var node = startNodeFrom(expr);
- node.test = expr;
- node.consequent = parseExpression(true);
- expect(_colon);
- node.alternate = parseExpression(true, noIn);
- return finishNode(node, "ConditionalExpression");
+ // Parse a function declaration or literal (depending on the
+ // `isStatement` parameter).
+
+ function parseFunction(node, isStatement) {
+ if (tokType === _name) node.id = parseIdent();
+ else if (isStatement) unexpected();
+ else node.id = null;
+ node.params = [];
+ var first = true;
+ expect(_parenL);
+ while (!eat(_parenR)) {
+ if (!first) expect(_comma); else first = false;
+ node.params.push(parseIdent());
}
- return expr;
- }
- function parseMaybeAssign(noIn) {
- var left = parseMaybeConditional(noIn);
- if (tokType.isAssign) {
- var node = startNodeFrom(left);
- node.operator = tokVal;
- node.left = left;
- next();
- node.right = parseMaybeAssign(noIn);
- checkLVal(left);
- return finishNode(node, "AssignmentExpression");
+ var oldInFunc = inFunction, oldLabels = labels;
+ inFunction = true; labels = [];
+ node.body = parseBlock(true);
+ if (strict || node.body.body.length && isUseStrict(node.body.body[0])) {
+ for (var i = node.id ? -1 : 0; i < node.params.length; ++i) {
+ var id = i < 0 ? node.id : node.params[i];
+ if (isStrictReservedWord(id.name) || isStrictBadIdWord(id.name))
+ raise(id.start, "Defining '" + id.name + "' in strict mode");
+ if (i >= 0) for (var j = 0; j < i; ++j) if (id.name === node.params[j].name)
+ raise(id.start, "Argument name clash in strict mode");
+ }
}
- return left;
+
+ inFunction = oldInFunc; labels = oldLabels;
+ return finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
}
- function parseExpression(noComma, noIn) {
- var expr = parseMaybeAssign(noIn);
- if (!noComma && tokType === _comma) {
- var node = startNodeFrom(expr);
- node.expressions = [expr];
- while (eat(_comma)) node.expressions.push(parseMaybeAssign(noIn));
- return finishNode(node, "SequenceExpression");
+ function parseExprList(close, allowTrailingComma, allowEmpty) {
+ var elts = [], first = true;
+ while (!eat(close)) {
+ if (!first) {
+ expect(_comma);
+ if (allowTrailingComma && options.allowTrailingCommas && eat(close)) break;
+ } else first = false;
+
+ if (allowEmpty && tokType === _comma) elts.push(null);
+ else elts.push(parseExpression(true));
}
- return expr;
+ return elts;
}
- function checkLVal(expr) {
- if (expr.type !== "Identifier" && expr.type !== "MemberExpression")
- raise(expr.start, "Assigning to rvalue");
- if (strict && expr.type === "Identifier" && isStrictBadIdWord(expr.name))
- raise(expr.start, "Assigning to " + expr.name + " in strict mode");
+ function parseIdent(liberal) {
+ var node = startNode();
+ if (tokType !== _name) {
+ if (liberal && tokType.keyword) node.name = tokType.keyword;
+ else unexpected();
+ } else node.name = tokVal;
+ next();
+ return finishNode(node, "Identifier");
}
+
})(typeof exports === "undefined" ? (window.acorn = {}) : exports);
diff --git a/test/tests.js b/test/tests.js
index d3d7779..0960482 100644
--- a/test/tests.js
+++ b/test/tests.js
@@ -2495,7 +2495,7 @@ test("switch (answer) { case 42: /* perfect */ bingo() }", {
value: 42,
end: {line: 1, column: 25}
},
- end: {line: 1, column: 50}
+ end: {line: 1, column: 48}
}
],
end: {line: 1, column: 50}
@@ -9390,7 +9390,7 @@ test("switch (answer) { case 42: hi(); break; }", {
value: 42,
end: {line: 1, column: 25}
},
- end: {line: 1, column: 41}
+ end: {line: 1, column: 39}
}
],
end: {line: 1, column: 41}
@@ -9461,7 +9461,7 @@ test("switch (answer) { case 42: hi(); break; default: break }", {
}
],
test: null,
- end: {line: 1, column: 56}
+ end: {line: 1, column: 54}
}
],
end: {line: 1, column: 56}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-acorn-jsx.git
More information about the Pkg-javascript-commits
mailing list