[Pkg-javascript-commits] [node-acorn-jsx] 388/484: Initial rewrite of JSX parser onto new tokenizer (all tests passing).

Bastien Roucariès rouca at moszumanska.debian.org
Sat Aug 19 14:21:00 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 802c4cd8cb41692582b76ae2dda4890449c6853f
Author: Ingvar Stepanyan <me at rreverser.com>
Date:   Wed Jan 14 12:18:08 2015 +0200

    Initial rewrite of JSX parser onto new tokenizer (all tests passing).
---
 acorn.js        | 654 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 test/index.html |   1 +
 test/run.js     |   1 +
 3 files changed, 643 insertions(+), 13 deletions(-)

diff --git a/acorn.js b/acorn.js
index 90eee06..3d952ef 100644
--- a/acorn.js
+++ b/acorn.js
@@ -242,6 +242,19 @@
       tokExprAllowed = !!exprAllowed;
       skipSpace();
     };
+    if (typeof Symbol !== 'undefined') {
+      getToken[Symbol.iterator] = function () {
+        return {
+          next: function () {
+            var token = getToken();
+            return {
+              done: token.type === _eof,
+              value: token
+            };
+          }
+        };
+      };
+    }
     getToken.options = options;
     return getToken;
   };
@@ -354,6 +367,7 @@
 
   var _num = {type: "num"}, _regexp = {type: "regexp"}, _string = {type: "string"};
   var _name = {type: "name"}, _eof = {type: "eof"};
+  var _xjsName = {type: "xjsName"};
 
   // Keyword tokens. The `keyword` property (also used in keyword-like
   // operators) indicates that the token originated from an
@@ -414,7 +428,9 @@
   var _braceR = {type: "}"}, _parenL = {type: "(", beforeExpr: true}, _parenR = {type: ")"};
   var _comma = {type: ",", beforeExpr: true}, _semi = {type: ";", beforeExpr: true};
   var _colon = {type: ":", beforeExpr: true}, _dot = {type: "."}, _question = {type: "?", beforeExpr: true};
-  var _arrow = {type: "=>", beforeExpr: true}, _template = {type: "template"}, _templateContinued = {type: "templateContinued"};
+  var _arrow = {type: "=>", beforeExpr: true};
+  var _template = {type: "template"}, _templateContinued = {type: "templateContinued"};
+  var _xjsText = {type: "xjsText"};
   var _ellipsis = {type: "...", prefix: true, beforeExpr: true};
 
   // Operators. These carry several kinds of properties to help the
@@ -450,6 +466,9 @@
   // '*' may be multiply or have special meaning in ES6
   var _star = {binop: 10, beforeExpr: true};
 
+  // JSX tag boundaries
+  var _xjsTagStart = {type: "xjsTagStart"}, _xjsTagEnd = {type: "xjsTagEnd"};
+
   // Provide access to the token types for external users of the
   // tokenizer.
 
@@ -549,6 +568,9 @@
   var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
   var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
 
+  var decimalNumber = /^\d+$/;
+  var hexNumber = /^[\da-fA-F]+$/;
+
   // Whether a single character denotes a newline.
 
   var newline = /[\n\r\u2028\u2029]/;
@@ -629,17 +651,26 @@
 
   var b_stat = {token: "{", isExpr: false}, b_expr = {token: "{", isExpr: true};
   var p_stat = {token: "(", isExpr: false}, p_expr = {token: "(", isExpr: true};
+  var j_oTag = {token: "<tag", isExpr: false}, j_cTag = {token: "</tag", isExpr: false}, j_expr = {token: "<tag>...</tag>", isExpr: true};
+
+  function curTokContext() {
+    return tokContext[tokContext.length - 1];
+  }
 
   function braceIsBlock(prevType) {
     var parent;
-    if (prevType === _colon && (parent = tokContext[tokContext.length - 1]).token == "{")
+    if (prevType === _colon && (parent = curTokContext()).token == "{")
       return !parent.isExpr;
     if (prevType === _return)
       return newline.test(input.slice(lastEnd, tokStart));
     if (prevType === _else || prevType === _semi || prevType === _eof)
       return true;
     if (prevType == _braceL)
-      return tokContext[tokContext.length - 1] === b_stat;
+      return curTokContext() === b_stat;
+    if (prevType === _xjsTagEnd || prevType === _xjsText)
+      return true;
+    if (prevType === _xjsName)
+      return false;
     return !tokExprAllowed;
   }
 
@@ -648,12 +679,11 @@
   // after the token, so that the next one's `tokStart` will point at
   // the right position.
 
-  function finishToken(type, val, shouldSkipSpace) {
+  function finishToken(type, val) {
     tokEnd = tokPos;
     if (options.locations) tokEndLoc = curPosition();
     var prevType = tokType;
     tokType = type;
-    if (shouldSkipSpace !== false) skipSpace();
     tokVal = val;
 
     // Update context info
@@ -672,10 +702,30 @@
     } else if (type.keyword && prevType == _dot) {
       tokExprAllowed = false;
     } else if (tokExprAllowed && type == _function) {
-      tokExprAllowed = null;
+      tokExprAllowed = false;
+    } else if (type === _xjsTagStart) {
+      tokContext.push(j_expr); // treat as beginning of JSX expression
+      tokContext.push(j_oTag); // start opening tag context
+      tokExprAllowed = false;
+    } else if (type === _xjsTagEnd) {
+      var out = tokContext.pop();
+      if (out === j_oTag && prevType === _slash || out === j_cTag) {
+        tokContext.pop();
+        tokExprAllowed = curTokContext() === j_expr;
+      } else {
+        tokExprAllowed = true;
+      }
+    } else if (type === _xjsText) {
+      tokExprAllowed = true;
+    } else if (type === _slash && prevType === _xjsTagStart) {
+      tokContext.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore
+      tokContext.push(j_cTag); // reconsider as closing tag context
+      tokExprAllowed = false;
     } else {
       tokExprAllowed = type.beforeExpr;
     }
+
+    if (curTokContext() !== j_expr) skipSpace();
   }
 
   function skipBlockComment() {
@@ -833,6 +883,17 @@
     }
     if (next === 61)
       size = input.charCodeAt(tokPos + 2) === 61 ? 3 : 2;
+    if (tokExprAllowed && code === 60) {
+      ++tokPos;
+      return finishToken(_xjsTagStart);
+    }
+    if (code === 62) {
+      var context = curTokContext();
+      if (context === j_oTag || context === j_cTag) {
+        ++tokPos;
+        return finishToken(_xjsTagEnd);
+      }
+    }
     return finishOp(_relational, size);
   }
 
@@ -860,11 +921,11 @@
     case 44: ++tokPos; return finishToken(_comma);
     case 91: ++tokPos; return finishToken(_bracketL);
     case 93: ++tokPos; return finishToken(_bracketR);
-    case 123:
+    case 123: // '{'
       ++tokPos;
       if (templates.length) ++templates[templates.length - 1];
       return finishToken(_braceL);
-    case 125:
+    case 125: // '}'
       ++tokPos;
       if (templates.length && --templates[templates.length - 1] === 0)
         return readTemplateString(_templateContinued);
@@ -934,10 +995,18 @@
     if (tokPos >= inputLen) return finishToken(_eof);
 
     var code = input.charCodeAt(tokPos);
+    var context = curTokContext();
 
-    // Identifier or keyword. '\uXXXX' sequences are allowed in
-    // identifiers, so '\' also dispatches to that.
-    if (isIdentifierStart(code) || code === 92 /* '\' */) return readWord();
+    if (context === j_oTag || context === j_cTag) {
+      // JSX identifier
+      if (isIdentifierStart(code)) return readJSXWord();
+    } else if (context === j_expr) {
+      return readXJSToken();
+    } else {
+      // Identifier or keyword. '\uXXXX' sequences are allowed in
+      // identifiers, so '\' also dispatches to that.
+      if (isIdentifierStart(code) || code === 92 /* '\' */) return readWord();
+    }
 
     var tok = getTokenFromCode(code);
 
@@ -1096,6 +1165,7 @@
   }
 
   function readString(quote) {
+    var isJSX = curTokContext() === j_oTag;
     ++tokPos;
     var out = "";
     for (;;) {
@@ -1105,8 +1175,10 @@
         ++tokPos;
         return finishToken(_string, out);
       }
-      if (ch === 92) { // '\'
+      if (ch === 92 && !isJSX) { // '\'
         out += readEscapedChar();
+      } else if (ch === 38 && isJSX) { // '&'
+        out += readXJSEntity();
       } else {
         ++tokPos;
         if (isNewLine(ch)) {
@@ -1149,6 +1221,332 @@
     }
   }
 
+  var XHTMLEntities = {
+    quot: '\u0022',
+    amp: '&',
+    apos: '\u0027',
+    lt: '<',
+    gt: '>',
+    nbsp: '\u00A0',
+    iexcl: '\u00A1',
+    cent: '\u00A2',
+    pound: '\u00A3',
+    curren: '\u00A4',
+    yen: '\u00A5',
+    brvbar: '\u00A6',
+    sect: '\u00A7',
+    uml: '\u00A8',
+    copy: '\u00A9',
+    ordf: '\u00AA',
+    laquo: '\u00AB',
+    not: '\u00AC',
+    shy: '\u00AD',
+    reg: '\u00AE',
+    macr: '\u00AF',
+    deg: '\u00B0',
+    plusmn: '\u00B1',
+    sup2: '\u00B2',
+    sup3: '\u00B3',
+    acute: '\u00B4',
+    micro: '\u00B5',
+    para: '\u00B6',
+    middot: '\u00B7',
+    cedil: '\u00B8',
+    sup1: '\u00B9',
+    ordm: '\u00BA',
+    raquo: '\u00BB',
+    frac14: '\u00BC',
+    frac12: '\u00BD',
+    frac34: '\u00BE',
+    iquest: '\u00BF',
+    Agrave: '\u00C0',
+    Aacute: '\u00C1',
+    Acirc: '\u00C2',
+    Atilde: '\u00C3',
+    Auml: '\u00C4',
+    Aring: '\u00C5',
+    AElig: '\u00C6',
+    Ccedil: '\u00C7',
+    Egrave: '\u00C8',
+    Eacute: '\u00C9',
+    Ecirc: '\u00CA',
+    Euml: '\u00CB',
+    Igrave: '\u00CC',
+    Iacute: '\u00CD',
+    Icirc: '\u00CE',
+    Iuml: '\u00CF',
+    ETH: '\u00D0',
+    Ntilde: '\u00D1',
+    Ograve: '\u00D2',
+    Oacute: '\u00D3',
+    Ocirc: '\u00D4',
+    Otilde: '\u00D5',
+    Ouml: '\u00D6',
+    times: '\u00D7',
+    Oslash: '\u00D8',
+    Ugrave: '\u00D9',
+    Uacute: '\u00DA',
+    Ucirc: '\u00DB',
+    Uuml: '\u00DC',
+    Yacute: '\u00DD',
+    THORN: '\u00DE',
+    szlig: '\u00DF',
+    agrave: '\u00E0',
+    aacute: '\u00E1',
+    acirc: '\u00E2',
+    atilde: '\u00E3',
+    auml: '\u00E4',
+    aring: '\u00E5',
+    aelig: '\u00E6',
+    ccedil: '\u00E7',
+    egrave: '\u00E8',
+    eacute: '\u00E9',
+    ecirc: '\u00EA',
+    euml: '\u00EB',
+    igrave: '\u00EC',
+    iacute: '\u00ED',
+    icirc: '\u00EE',
+    iuml: '\u00EF',
+    eth: '\u00F0',
+    ntilde: '\u00F1',
+    ograve: '\u00F2',
+    oacute: '\u00F3',
+    ocirc: '\u00F4',
+    otilde: '\u00F5',
+    ouml: '\u00F6',
+    divide: '\u00F7',
+    oslash: '\u00F8',
+    ugrave: '\u00F9',
+    uacute: '\u00FA',
+    ucirc: '\u00FB',
+    uuml: '\u00FC',
+    yacute: '\u00FD',
+    thorn: '\u00FE',
+    yuml: '\u00FF',
+    OElig: '\u0152',
+    oelig: '\u0153',
+    Scaron: '\u0160',
+    scaron: '\u0161',
+    Yuml: '\u0178',
+    fnof: '\u0192',
+    circ: '\u02C6',
+    tilde: '\u02DC',
+    Alpha: '\u0391',
+    Beta: '\u0392',
+    Gamma: '\u0393',
+    Delta: '\u0394',
+    Epsilon: '\u0395',
+    Zeta: '\u0396',
+    Eta: '\u0397',
+    Theta: '\u0398',
+    Iota: '\u0399',
+    Kappa: '\u039A',
+    Lambda: '\u039B',
+    Mu: '\u039C',
+    Nu: '\u039D',
+    Xi: '\u039E',
+    Omicron: '\u039F',
+    Pi: '\u03A0',
+    Rho: '\u03A1',
+    Sigma: '\u03A3',
+    Tau: '\u03A4',
+    Upsilon: '\u03A5',
+    Phi: '\u03A6',
+    Chi: '\u03A7',
+    Psi: '\u03A8',
+    Omega: '\u03A9',
+    alpha: '\u03B1',
+    beta: '\u03B2',
+    gamma: '\u03B3',
+    delta: '\u03B4',
+    epsilon: '\u03B5',
+    zeta: '\u03B6',
+    eta: '\u03B7',
+    theta: '\u03B8',
+    iota: '\u03B9',
+    kappa: '\u03BA',
+    lambda: '\u03BB',
+    mu: '\u03BC',
+    nu: '\u03BD',
+    xi: '\u03BE',
+    omicron: '\u03BF',
+    pi: '\u03C0',
+    rho: '\u03C1',
+    sigmaf: '\u03C2',
+    sigma: '\u03C3',
+    tau: '\u03C4',
+    upsilon: '\u03C5',
+    phi: '\u03C6',
+    chi: '\u03C7',
+    psi: '\u03C8',
+    omega: '\u03C9',
+    thetasym: '\u03D1',
+    upsih: '\u03D2',
+    piv: '\u03D6',
+    ensp: '\u2002',
+    emsp: '\u2003',
+    thinsp: '\u2009',
+    zwnj: '\u200C',
+    zwj: '\u200D',
+    lrm: '\u200E',
+    rlm: '\u200F',
+    ndash: '\u2013',
+    mdash: '\u2014',
+    lsquo: '\u2018',
+    rsquo: '\u2019',
+    sbquo: '\u201A',
+    ldquo: '\u201C',
+    rdquo: '\u201D',
+    bdquo: '\u201E',
+    dagger: '\u2020',
+    Dagger: '\u2021',
+    bull: '\u2022',
+    hellip: '\u2026',
+    permil: '\u2030',
+    prime: '\u2032',
+    Prime: '\u2033',
+    lsaquo: '\u2039',
+    rsaquo: '\u203A',
+    oline: '\u203E',
+    frasl: '\u2044',
+    euro: '\u20AC',
+    image: '\u2111',
+    weierp: '\u2118',
+    real: '\u211C',
+    trade: '\u2122',
+    alefsym: '\u2135',
+    larr: '\u2190',
+    uarr: '\u2191',
+    rarr: '\u2192',
+    darr: '\u2193',
+    harr: '\u2194',
+    crarr: '\u21B5',
+    lArr: '\u21D0',
+    uArr: '\u21D1',
+    rArr: '\u21D2',
+    dArr: '\u21D3',
+    hArr: '\u21D4',
+    forall: '\u2200',
+    part: '\u2202',
+    exist: '\u2203',
+    empty: '\u2205',
+    nabla: '\u2207',
+    isin: '\u2208',
+    notin: '\u2209',
+    ni: '\u220B',
+    prod: '\u220F',
+    sum: '\u2211',
+    minus: '\u2212',
+    lowast: '\u2217',
+    radic: '\u221A',
+    prop: '\u221D',
+    infin: '\u221E',
+    ang: '\u2220',
+    and: '\u2227',
+    or: '\u2228',
+    cap: '\u2229',
+    cup: '\u222A',
+    'int': '\u222B',
+    there4: '\u2234',
+    sim: '\u223C',
+    cong: '\u2245',
+    asymp: '\u2248',
+    ne: '\u2260',
+    equiv: '\u2261',
+    le: '\u2264',
+    ge: '\u2265',
+    sub: '\u2282',
+    sup: '\u2283',
+    nsub: '\u2284',
+    sube: '\u2286',
+    supe: '\u2287',
+    oplus: '\u2295',
+    otimes: '\u2297',
+    perp: '\u22A5',
+    sdot: '\u22C5',
+    lceil: '\u2308',
+    rceil: '\u2309',
+    lfloor: '\u230A',
+    rfloor: '\u230B',
+    lang: '\u2329',
+    rang: '\u232A',
+    loz: '\u25CA',
+    spades: '\u2660',
+    clubs: '\u2663',
+    hearts: '\u2665',
+    diams: '\u2666'
+  };
+
+  function readXJSEntity() {
+    var str = '', count = 0, entity;
+    var ch = input[tokPos];
+    if (ch !== '&') raise(tokPos, "Entity must start with an ampersand");
+    var startPos = ++tokPos;
+    while (tokPos < inputLen && count++ < 10) {
+      ch = input[tokPos++];
+      if (ch === ';') {
+        if (str[0] === '#') {
+          if (str[1] === 'x') {
+            str = str.substr(2);
+            if (hexNumber.test(str)) {
+              entity = String.fromCharCode(parseInt(str, 16));
+            }
+          } else {
+            str = str.substr(1);
+            if (decimalNumber.test(str)) {
+              entity = String.fromCharCode(parseInt(str, 10));
+            }
+          }
+        } else {
+          entity = XHTMLEntities[str];
+        }
+        break;
+      }
+      str += ch;
+    }
+    if (!entity) {
+      tokPos = startPos;
+      return '&';
+    }
+    return entity;
+  }
+
+  // Reads inline JSX contents token.
+
+  function readXJSToken() {
+    var out = "", start = tokPos;
+    for (;;) {
+      if (tokPos >= inputLen) raise(tokStart, "Unterminated JSX contents");
+      var ch = input.charAt(tokPos);
+      switch (ch) {
+        case "{":
+        case "<":
+          if (tokPos === start) {
+            return getTokenFromCode(ch.charCodeAt(0));
+          }
+          return finishToken(_xjsText, out);
+
+        case "&":
+          out += readXJSEntity();
+          break;
+
+        default:
+          ++tokPos;
+          if (newline.test(ch)) {
+            if (ch === "\r" && input.charCodeAt(tokPos) === 10) {
+              ++tokPos;
+              ch = "\n";
+            }
+            if (options.locations) {
+              ++tokCurLine;
+              tokLineStart = tokPos;
+            }
+          }
+          out += ch;
+      }
+    }
+  }
+
   // Used to read escaped characters
 
   function readEscapedChar() {
@@ -1241,6 +1639,21 @@
     return finishToken(type, word);
   }
 
+  // Read a JSX identifier (valid tag or attribute name).
+  //
+  // Optimized version since JSX identifiers can't contain
+  // escape characters and so can be read as single slice.
+  // Also assumes that first character was already checked
+  // by isIdentifierStart in readToken.
+
+  function readJSXWord() {
+    var ch, start = tokPos;
+    do {
+      ch = input.charCodeAt(++tokPos);
+    } while (isIdentifierChar(ch) || ch === 45); // '-'
+    return finishToken(_xjsName, input.slice(start, tokPos));
+  }
+
   // ## Parser
 
   // A recursive descent parser operates by defining functions for all
@@ -2165,7 +2578,7 @@
       next();
       return finishNode(node, "Literal");
 
-    case _num: case _string:
+    case _num: case _string: case _xjsText:
       var node = startNode();
       node.value = tokVal;
       node.raw = input.slice(tokStart, tokEnd);
@@ -2244,6 +2657,9 @@
     case _template:
       return parseTemplate();
 
+    case _xjsTagStart:
+      return parseXJSElement();
+
     default:
       unexpected();
     }
@@ -2755,4 +3171,216 @@
     node.generator = isGenerator;
     return finishNode(node, "ComprehensionExpression");
   }
+
+  // Transforms JSX element name to string.
+
+  function getQualifiedXJSName(object) {
+    if (object.type === "XJSIdentifier") {
+      return object.name;
+    }
+    if (object.type === "XJSNamespacedName") {
+      return object.namespace.name + ':' + object.name.name;
+    }
+    if (object.type === "XJSMemberExpression") {
+      return (
+        getQualifiedXJSName(object.object) + '.' +
+        getQualifiedXJSName(object.property)
+      );
+    }
+  }
+
+  // Parse next token as JSX identifier
+
+  function parseXJSIdentifier() {
+    var node = startNode();
+    if (tokType === _xjsName) {
+      node.name = tokVal;
+    } else if (tokType.keyword) {
+      node.name = tokType.keyword;
+    } else {
+      unexpected();
+    }
+    next();
+    return finishNode(node, "XJSIdentifier");
+  }
+
+  // Parse namespaced identifier.
+
+  function parseXJSNamespacedName() {
+    var start = storeCurrentPos();
+    var name = parseXJSIdentifier();
+    if (!eat(_colon)) return name;
+    var node = startNodeAt(start);
+    node.namespace = name;
+    node.name = parseXJSIdentifier();
+    return finishNode(node, "XJSNamespacedName");
+  }
+
+  // Parses element name in any form - namespaced, member
+  // or single identifier.
+
+  function parseXJSElementName() {
+    var start = storeCurrentPos();
+    var node = parseXJSNamespacedName();
+    while (eat(_dot)) {
+      var newNode = startNodeAt(start);
+      newNode.object = node;
+      newNode.property = parseXJSIdentifier();
+      node = finishNode(newNode, "XJSMemberExpression");
+    }
+    return node;
+  }
+
+  // Parses any type of JSX attribute value.
+
+  function parseXJSAttributeValue() {
+    switch (tokType) {
+      case _braceL:
+        var node = parseXJSExpressionContainer();
+        if (node.expression.type === "XJSEmptyExpression") {
+          raise(
+            node.start,
+              'XJS attributes must only be assigned a non-empty ' +
+              'expression'
+          );
+        }
+        return node;
+
+      case _xjsTagStart:
+        return parseXJSElement();
+
+      case _xjsText:
+      case _string:
+        return parseExprAtom();
+
+      default:
+        raise(tokStart, "XJS value should be either an expression or a quoted XJS text");
+    }
+  }
+
+  // XJSEmptyExpression is unique type since it doesn't actually parse anything,
+  // and so it should start at the end of last read token (left brace) and finish
+  // at the beginning of the next one (right brace).
+
+  function parseXJSEmptyExpression() {
+    if (tokType !== _braceR) {
+      unexpected();
+    }
+
+    var tmp;
+
+    tmp = tokStart;
+    tokStart = lastEnd;
+    lastEnd = tmp;
+
+    tmp = tokStartLoc;
+    tokStartLoc = lastEndLoc;
+    lastEndLoc = tmp;
+
+    return finishNode(startNode(), "XJSEmptyExpression");
+  }
+
+  // Parses JSX expression enclosed into curly brackets.
+
+  function parseXJSExpressionContainer() {
+    var node = startNode();
+    next();
+    node.expression = tokType === _braceR ? parseXJSEmptyExpression() : parseExpression();
+    expect(_braceR);
+    return finishNode(node, "XJSExpressionContainer");
+  }
+
+  // Parses following JSX attribute name-value pair.
+
+  function parseXJSAttribute() {
+    var node = startNode();
+    if (eat(_braceL)) {
+      if (tokType !== _ellipsis) unexpected();
+      node.argument = parseMaybeUnary().argument;
+      expect(_braceR);
+      return finishNode(node, "XJSSpreadAttribute");
+    }
+    node.name = parseXJSNamespacedName();
+    node.value = eat(_eq) ? parseXJSAttributeValue() : null;
+    return finishNode(node, "XJSAttribute");
+  }
+
+  // Parses JSX opening tag starting after '<'.
+
+  function parseXJSOpeningElementAt(start) {
+    var node = startNodeAt(start);
+    node.attributes = [];
+    node.name = parseXJSElementName();
+    while (tokType !== _slash && tokType !== _xjsTagEnd) {
+      node.attributes.push(parseXJSAttribute());
+    }
+    node.selfClosing = eat(_slash);
+    expect(_xjsTagEnd);
+    return finishNode(node, "XJSOpeningElement");
+  }
+
+  // Parses JSX closing tag starting after '</'.
+
+  function parseXJSClosingElementAt(start) {
+    var node = startNodeAt(start);
+    node.name = parseXJSElementName();
+    expect(_xjsTagEnd);
+    return finishNode(node, "XJSClosingElement");
+  }
+
+  // Parses entire JSX element, including it's opening tag
+  // (starting after '<'), attributes, contents and closing tag.
+
+  function parseXJSElementAt(start) {
+    var node = startNodeAt(start);
+    var children = [];
+    var openingElement = parseXJSOpeningElementAt(start);
+    var closingElement = null;
+
+    if (!openingElement.selfClosing) {
+      contents:for (;;) {
+        switch (tokType) {
+          case _xjsTagStart:
+            start = storeCurrentPos();
+            next();
+            if (eat(_slash)) {
+              closingElement = parseXJSClosingElementAt(start);
+              break contents;
+            }
+            children.push(parseXJSElementAt(start));
+            break;
+
+          case _xjsText:
+            children.push(parseExprAtom());
+            break;
+
+          case _braceL:
+            children.push(parseXJSExpressionContainer());
+            break;
+
+          default:
+            unexpected();
+        }
+      }
+      if (getQualifiedXJSName(closingElement.name) !== getQualifiedXJSName(openingElement.name)) {
+        raise(
+          closingElement.start,
+          "Expected corresponding XJS closing tag for <" + getQualifiedXJSName(openingElement.name) + ">"
+        );
+      }
+    }
+
+    node.openingElement = openingElement;
+    node.closingElement = closingElement;
+    node.children = children;
+    return finishNode(node, "XJSElement");
+  }
+
+  // Parses entire JSX element from current position.
+
+  function parseXJSElement() {
+    var start = storeCurrentPos();
+    next();
+    return parseXJSElementAt(start);
+  }
 });
diff --git a/test/index.html b/test/index.html
index ef80eb7..29489e7 100644
--- a/test/index.html
+++ b/test/index.html
@@ -7,6 +7,7 @@
   <script src="driver.js"></script>
   <script src="tests.js" charset="utf-8"></script>
   <script src="tests-harmony.js" charset="utf-8"></script>
+  <script src="tests-jsx.js" charset="utf-8"></script>
 </head>
 <body>
   <ul id="log"></ul>
diff --git a/test/run.js b/test/run.js
index 04f752d..a11cddd 100644
--- a/test/run.js
+++ b/test/run.js
@@ -5,6 +5,7 @@
     driver = require("./driver.js");
     require("./tests.js");
     require("./tests-harmony.js");
+    require("./tests-jsx.js");
   } else {
     driver = window;
   }

-- 
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