[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