diff --git a/.gitignore b/.gitignore index 3250e4e3e9..213edda45f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ /node_modules /.idea /.tern-port -acorn_csp.js +/acorn_csp.js diff --git a/acorn.js b/acorn.js index e0d4dcf074..8de369f153 100644 --- a/acorn.js +++ b/acorn.js @@ -231,12 +231,12 @@ initTokenState(); skipSpace(); - function getToken(forceRegexp) { + function getToken() { lastEnd = tokEnd; - readToken(forceRegexp); + readToken(); return new Token(); } - getToken.jumpTo = function(pos, reAllowed) { + getToken.jumpTo = function(pos, exprAllowed) { tokPos = pos; if (options.locations) { tokCurLine = 1; @@ -247,12 +247,23 @@ tokLineStart = match.index + match[0].length; } } - tokRegexpAllowed = reAllowed; + tokExprAllowed = !!exprAllowed; skipSpace(); }; - getToken.noRegexp = function() { - tokRegexpAllowed = false; - }; + getToken.current = function() { return new Token(); }; + 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; }; @@ -285,12 +296,13 @@ // Internal state for the tokenizer. To distinguish between division // operators and regular expressions, it remembers whether the last - // token was one that is allowed to be followed by an expression. - // (If it is, a slash is probably a regexp, if it isn't it's a - // division operator. See the `parseStatement` function for a - // caveat.) + // token was one that is allowed to be followed by an expression. In + // some cases, notably after ')' or '}' tokens, the situation + // depends on the context before the matching opening bracket, so + // tokContext keeps a stack of information about current bracketed + // forms. - var tokRegexpAllowed; + var tokContext, tokExprAllowed; // When `options.locations` is true, these are used to keep // track of the current line, and know when a new line has been @@ -312,18 +324,6 @@ var inFunction, inGenerator, inAsync, labels, strict, inXJSChild, inXJSTag, inType; - // This counter is used for checking that arrow expressions did - // not contain nested parentheses in argument list. - - var metParenL; - - // This is used by the tokenizer to track the template strings it is - // inside, and count the amount of open braces seen inside them, to - // be able to switch back to a template token when the } to match ${ - // is encountered. It will hold an array of integers. - - var templates; - function initParserState() { lastStart = lastEnd = tokPos; if (options.locations) lastEndLoc = new Position; @@ -365,6 +365,7 @@ var _num = {type: "num"}, _regexp = {type: "regexp"}, _string = {type: "string"}; var _name = {type: "name"}, _eof = {type: "eof"}; + var _jsxName = {type: "jsxName"}; // These are JSX-specific token types @@ -429,10 +430,10 @@ 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}, _bquote = {type: "`"}, _dollarBraceL = {type: "${", beforeExpr: true}; - var _ltSlash = {type: "", beforeExpr: true}, _template = {type: "template"}, _templateContinued = {type: "templateContinued"}; - var _ellipsis = {type: "...", prefix: true, beforeExpr: true}; + var _arrow = {type: "=>", beforeExpr: true}, _template = {type: "template"}; + var _ellipsis = {type: "...", beforeExpr: true}; + var _backQuote = {type: "`"}, _dollarBraceL = {type: "${", beforeExpr: true}; + var _jsxText = {type: "jsxText"}; var _paamayimNekudotayim = { type: "::", beforeExpr: true }; var _at = { type: '@' }; var _hash = { type: '#' }; @@ -474,6 +475,9 @@ // '<', '>' may be relational or have special meaning in JSX var _lt = {binop: 7, beforeExpr: true}, _gt = {binop: 7, beforeExpr: true}; + // JSX tag boundaries + var _jsxTagStart = {type: "jsxTagStart"}, _jsxTagEnd = {type: "jsxTagEnd"}; + // Provide access to the token types for external users of the // tokenizer. @@ -481,10 +485,9 @@ parenL: _parenL, parenR: _parenR, comma: _comma, semi: _semi, colon: _colon, dot: _dot, ellipsis: _ellipsis, question: _question, slash: _slash, eq: _eq, name: _name, eof: _eof, num: _num, regexp: _regexp, string: _string, - arrow: _arrow, bquote: _bquote, dollarBraceL: _dollarBraceL, star: _star, - assign: _assign, xjsName: _xjsName, xjsText: _xjsText, paamayimNekudotayim: _paamayimNekudotayim, exponent: _exponent, at: _at, hash: _hash, - template: _template, templateContinued: _templateContinued}; + arrow: _arrow, template: _template, star: _star, assign: _assign, + backQuote: _backQuote, dollarBraceL: _dollarBraceL}; for (var kw in keywordTypes) exports.tokTypes["_" + kw] = keywordTypes[kw]; // This is a trick taken from Esprima. It turns out that, on @@ -627,7 +630,7 @@ Position.prototype.offset = function(n) { return new Position(this.line, this.column + n); - } + }; function curPosition() { return new Position(tokCurLine, tokPos - tokLineStart); @@ -644,29 +647,109 @@ tokCurLine = 1; tokPos = tokLineStart = 0; } - tokRegexpAllowed = true; - metParenL = 0; - inType = inXJSChild = inXJSTag = false; - templates = []; + tokType = _eof; + tokContext = []; + tokExprAllowed = true; + inType = false; if (tokPos === 0 && options.allowHashBang && input.slice(0, 2) === '#!') { skipLineComment(2); } } - // Called at the end of every token. Sets `tokEnd`, `tokVal`, and - // `tokRegexpAllowed`, and skips the space after the token, so that - // the next one's `tokStart` will point at the right position. + // The algorithm used to determine whether a regexp can appear at a + // given point in the program is loosely based on sweet.js' approach. + // See https://github.com/mozilla/sweet.js/wiki/design - function finishToken(type, val, shouldSkipSpace) { + var b_stat = {token: "{", isExpr: false}, b_expr = {token: "{", isExpr: true}, b_tmpl = {token: "${", isExpr: true}; + var p_stat = {token: "(", isExpr: false}, p_expr = {token: "(", isExpr: true}; + var q_tmpl = {token: "`", isExpr: true}; + var j_oTag = {token: "...", isExpr: true}; + + function curTokContext() { + return tokContext[tokContext.length - 1]; + } + + function braceIsBlock(prevType) { + var parent; + 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 curTokContext() === b_stat; + if (prevType === _jsxTagEnd || prevType === _jsxText) + return true; + if (prevType === _jsxName) + return false; + return !tokExprAllowed; + } + + // Called at the end of every token. Sets `tokEnd`, `tokVal`, and + // maintains `tokContext` and `tokExprAllowed`, and skips the space + // after the token, so that the next one's `tokStart` will point at + // the right position. + + function finishToken(type, val) { tokEnd = tokPos; if (options.locations) tokEndLoc = curPosition(); + var prevType = tokType, preserveSpace = false; tokType = type; - if (shouldSkipSpace !== false) skipSpace(); tokVal = val; - tokRegexpAllowed = type.beforeExpr; - if (options.onToken) { - options.onToken(new Token()); + + // Update context info + if (type === _parenR || type === _braceR) { + var out = tokContext.pop(); + tokExprAllowed = !(out && out.isExpr); + preserveSpace = out === b_tmpl || curTokContext() === j_expr; + } else if (type === _braceL) { + tokContext.push(braceIsBlock(prevType) ? b_stat : b_expr); + tokExprAllowed = true; + } else if (type === _dollarBraceL) { + tokContext.push(b_tmpl); + tokExprAllowed = true; + } else if (type == _parenL) { + var statementParens = prevType === _if || prevType === _for || prevType === _with || prevType === _while; + tokContext.push(statementParens ? p_stat : p_expr); + tokExprAllowed = true; + } else if (type == _incDec) { + // tokExprAllowed stays unchanged + } else if (type.keyword && prevType == _dot) { + tokExprAllowed = false; + } else if (tokExprAllowed && type == _function) { + tokExprAllowed = false; + } else if (type === _backQuote) { + if (curTokContext() === q_tmpl) { + tokContext.pop(); + } else { + tokContext.push(q_tmpl); + preserveSpace = true; + } + tokExprAllowed = false; + } else if (type === _jsxTagStart) { + tokContext.push(j_expr); // treat as beginning of JSX expression + tokContext.push(j_oTag); // start opening tag context + tokExprAllowed = false; + } else if (type === _jsxTagEnd) { + var out = tokContext.pop(); + if (out === j_oTag && prevType === _slash || out === j_cTag) { + tokContext.pop(); + preserveSpace = tokExprAllowed = curTokContext() === j_expr; + } else { + preserveSpace = tokExprAllowed = true; + } + } else if (type === _jsxText) { + preserveSpace = tokExprAllowed = true; + } else if (type === _slash && prevType === _jsxTagStart) { + 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 (!preserveSpace) skipSpace(); } function skipBlockComment() { @@ -752,9 +835,6 @@ // // All in the name of speed. // - // The `forceRegexp` parameter is used in the one case where the - // `tokRegexpAllowed` trick does not work. See `parseStatement`. - function readToken_dot() { var next = input.charCodeAt(tokPos + 1); if (next >= 48 && next <= 57) return readNumber(true); @@ -773,7 +853,7 @@ function readToken_slash() { // '/' var next = input.charCodeAt(tokPos + 1); - if (tokRegexpAllowed) {++tokPos; return readRegexp();} + if (tokExprAllowed) {++tokPos; return readRegexp();} if (next === 61) return finishOp(_assign, 2); return finishOp(_slash, 1); } @@ -838,7 +918,7 @@ function readToken_lt_gt(code) { // '<>' var next = input.charCodeAt(tokPos + 1); var size = 1; - if (!inType && next === code) { + if (next === code) { size = code === 62 && input.charCodeAt(tokPos + 2) === 62 ? 3 : 2; if (input.charCodeAt(tokPos + size) === 61) return finishOp(_assign, size + 1); return finishOp(_bitShift, size); @@ -850,16 +930,20 @@ skipSpace(); return readToken(); } - if (next === 61) { + if (tokExprAllowed && code === 60) { + ++tokPos; + return finishToken(_jsxTagStart); + } + if (code === 62) { + var context = curTokContext(); + if (context === j_oTag || context === j_cTag) { + ++tokPos; + return finishToken(_jsxTagEnd); + } + } + if (next === 61) size = input.charCodeAt(tokPos + 2) === 61 ? 3 : 2; - return finishOp(_relational, size); - } - if (code === 60 && next === 47) { - // '' @@ -904,16 +988,8 @@ case 44: ++tokPos; return finishToken(_comma); case 91: ++tokPos; return finishToken(_bracketL); case 93: ++tokPos; return finishToken(_bracketR); - case 123: - ++tokPos; - if (templates.length) ++templates[templates.length - 1]; - return finishToken(_braceL); - case 125: - ++tokPos; - if (templates.length && --templates[templates.length - 1] === 0) - return readTemplateString(_templateContinued); - else - return finishToken(_braceR); + case 123: ++tokPos; return finishToken(_braceL); + case 125: ++tokPos; return finishToken(_braceR); case 63: ++tokPos; return finishToken(_question); case 64: @@ -942,7 +1018,9 @@ case 96: // '`' if (options.ecmaVersion >= 6) { ++tokPos; - return readTemplateString(_template); + return finishToken(_backQuote); + } else { + return false; } case 48: // '0' @@ -997,23 +1075,32 @@ return false; } - function readToken(forceRegexp) { - if (!forceRegexp) tokStart = tokPos; - else tokPos = tokStart + 1; + function readToken() { + tokStart = tokPos; if (options.locations) tokStartLoc = curPosition(); - if (forceRegexp) return readRegexp(); if (tokPos >= inputLen) return finishToken(_eof); - var code = input.charCodeAt(tokPos); + var context = curTokContext(); - // JSX content - either simple text, start of or {expression} - if (inXJSChild && tokType !== _braceL && code !== 60 && code !== 123 && code !== 125) { - return readXJSText(['<', '{']); + if (context === q_tmpl) { + return readTmplToken(); } - // Identifier or keyword. '\uXXXX' sequences are allowed in - // identifiers, so '\' also dispatches to that. - if (isIdentifierStart(code) || code === 92 /* '\' */) return readWord(); + if (context === j_expr) { + return readJSXToken(); + } + + var code = input.charCodeAt(tokPos); + if (context === j_oTag || context === j_cTag) { + // JSX identifier + if (isIdentifierStart(code)) return readJSXWord(); + } else if (context === j_expr) { + return readJSXToken(); + } 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); @@ -1173,6 +1260,7 @@ } function readString(quote) { + var isJSX = curTokContext() === j_oTag; ++tokPos; var out = ""; for (;;) { @@ -1182,11 +1270,13 @@ ++tokPos; return finishToken(_string, out); } - if (ch === 92) { // '\' + if (ch === 92 && !isJSX) { // '\' out += readEscapedChar(); + } else if (ch === 38 && isJSX) { // '&' + out += readJSXEntity(); } else { ++tokPos; - if (newline.test(String.fromCharCode(ch))) { + if (isNewLine(ch)) { raise(tokStart, "Unterminated string constant"); } out += String.fromCharCode(ch); // '\' @@ -1194,34 +1284,366 @@ } } - function readTemplateString(type) { - if (type == _templateContinued) templates.pop(); - var out = "", start = tokPos;; + // Reads template string tokens. + + function readTmplToken() { + var out = "", start = tokPos; for (;;) { if (tokPos >= inputLen) raise(tokStart, "Unterminated template"); - var ch = input.charAt(tokPos); - if (ch === "`" || ch === "$" && input.charCodeAt(tokPos + 1) === 123) { // '`', '${' - var raw = input.slice(start, tokPos); - ++tokPos; - if (ch == "$") { ++tokPos; templates.push(1); } - return finishToken(type, {cooked: out, raw: raw}); + var ch = input.charCodeAt(tokPos); + if (ch === 96 || ch === 36 && input.charCodeAt(tokPos + 1) === 123) { // '`', '${' + if (tokPos === start && tokType === _template) { + if (ch === 36) { + tokPos += 2; + return finishToken(_dollarBraceL); + } else { + ++tokPos; + return finishToken(_backQuote); + } + } + return finishToken(_template, out); } - - if (ch === "\\") { // '\' + if (ch === 92) { // '\' out += readEscapedChar(); } else { ++tokPos; - if (newline.test(ch)) { - if (ch === "\r" && input.charCodeAt(tokPos) === 10) { + if (isNewLine(ch)) { + if (ch === 13 && input.charCodeAt(tokPos) === 10) { ++tokPos; - ch = "\n"; + ch = 10; } if (options.locations) { ++tokCurLine; tokLineStart = tokPos; } } - out += ch; + out += String.fromCharCode(ch); + } + } + } + + 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 readJSXEntity() { + 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 readJSXToken() { + var out = "", start = tokPos; + for (;;) { + if (tokPos >= inputLen) raise(tokStart, "Unterminated JSX contents"); + var ch = input.charCodeAt(tokPos); + switch (ch) { + case 123: // '{' + case 60: // '<' + if (tokPos === start) { + return getTokenFromCode(ch); + } + return finishToken(_jsxText, out); + + case 38: // '&' + out += readJSXEntity(); + break; + + default: + ++tokPos; + if (isNewLine(ch)) { + if (ch === 13 && input.charCodeAt(tokPos) === 10) { + ++tokPos; + ch = 10; + } + if (options.locations) { + ++tokCurLine; + tokLineStart = tokPos; + } + } + out += String.fromCharCode(ch); } } } @@ -1655,6 +2077,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(_jsxName, input.slice(start, tokPos)); + } + // ## Parser // A recursive descent parser operates by defining functions for all @@ -1680,17 +2117,21 @@ // Continue to the next token. function next() { + if (options.onToken) + options.onToken(new Token()); + lastStart = tokStart; lastEnd = tokEnd; lastEndLoc = tokEndLoc; readToken(); } - // Enter strict mode. Re-reads the next token to please pedantic - // tests ("use strict"; 010; -- should fail). + // Enter strict mode. Re-reads the next number or string to + // please pedantic tests ("use strict"; 010; -- should fail). function setStrict(strct) { strict = strct; + if (tokType !== _num && tokType !== _string) return; tokPos = tokStart; if (options.locations) { while (tokPos < tokLineStart) { @@ -1765,6 +2206,8 @@ return node; } + // Finish node at given position + function finishNodeAt(node, type, pos) { if (options.locations) { node.loc.end = pos[1]; pos = pos[0]; } node.type = type; @@ -1840,7 +2283,10 @@ switch (node.type) { case "Identifier": case "MemberExpression": - break; + case "ObjectPattern": + case "ArrayPattern": + case "AssignmentPattern": + break; case "ObjectExpression": node.type = "ObjectPattern"; @@ -1882,6 +2328,21 @@ return node; } + // Parses spread element. + + function parseSpread(isBinding) { + var spread = startNode(); + next(); + if (isBinding) { + var arg = parseAssignableAtom(); + checkSpreadAssign(arg); + spread.argument = arg; + } else { + spread.argument = parseMaybeAssign(); + } + return finishNode(spread, "SpreadElement"); + } + // Parses lvalue (assignable) atom. function parseAssignableAtom() { @@ -1897,11 +2358,7 @@ 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")); + elts.push(parseSpread(true)); expect(_bracketR); break; } @@ -2043,15 +2500,13 @@ var first = true; if (!node.body) node.body = []; while (tokType !== _eof) { - var stmt = parseStatement(true); + var stmt = parseStatement(true, true); node.body.push(stmt); if (first && isUseStrict(stmt)) setStrict(true); first = false; } - lastStart = tokStart; - lastEnd = tokEnd; - lastEndLoc = tokEndLoc; + next(); return finishNode(node, "Program"); } @@ -2064,11 +2519,8 @@ // `if (foo) /blah/.exec(foo);`, where looking at the previous token // does not help. - function parseStatement(topLevel) { - if (tokType === _slash || tokType === _assign && tokVal == "/=") - readToken(true); - - var starttype = tokType, node = startNode(), start = storeCurrentPos(); + function parseStatement(declaration, topLevel) { + 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 @@ -2079,14 +2531,19 @@ case _debugger: return parseDebuggerStatement(node); case _do: return parseDoStatement(node); case _for: return parseForStatement(node); - case _function: return parseFunctionStatement(node); - case _class: return parseClass(node, true); + case _function: + if (!declaration && options.ecmaVersion >= 6) unexpected(); + return parseFunctionStatement(node); + case _class: + if (!declaration) unexpected(); + return parseClass(node, true); case _if: return parseIfStatement(node); case _return: return parseReturnStatement(node); case _switch: return parseSwitchStatement(node); case _throw: return parseThrowStatement(node); case _try: return parseTryStatement(node); - case _var: case _let: case _const: return parseVarStatement(node, starttype.keyword); + case _let: case _const: if (!declaration) unexpected(); // NOTE: falls through to _var + case _var: return parseVarStatement(node, starttype.keyword); case _while: return parseWhileStatement(node); case _with: return parseWithStatement(node); case _braceL: return parseBlock(); // no point creating a function for this @@ -2162,7 +2619,7 @@ function parseDoStatement(node) { next(); labels.push(loopLabel); - node.body = parseStatement(); + node.body = parseStatement(false); labels.pop(); expect(_while); node.test = parseParenExpression(); @@ -2212,8 +2669,8 @@ function parseIfStatement(node) { next(); node.test = parseParenExpression(); - node.consequent = parseStatement(); - node.alternate = eat(_else) ? parseStatement() : null; + node.consequent = parseStatement(false); + node.alternate = eat(_else) ? parseStatement(false) : null; return finishNode(node, "IfStatement"); } @@ -2257,7 +2714,7 @@ expect(_colon); } else { if (!cur) unexpected(); - cur.consequent.push(parseStatement()); + cur.consequent.push(parseStatement(true)); } } if (cur) finishNode(cur, "SwitchCase"); @@ -2283,9 +2740,8 @@ var clause = startNode(); next(); expect(_parenL); - clause.param = parseIdent(); - if (strict && isStrictBadIdWord(clause.param.name)) - raise(clause.param.start, "Binding " + clause.param.name + " in strict mode"); + clause.param = parseAssignableAtom(); + checkLVal(clause.param, true); expect(_parenR); clause.guard = null; clause.body = parseBlock(); @@ -2309,7 +2765,7 @@ next(); node.test = parseParenExpression(); labels.push(loopLabel); - node.body = parseStatement(); + node.body = parseStatement(false); labels.pop(); return finishNode(node, "WhileStatement"); } @@ -2318,7 +2774,7 @@ if (strict) raise(tokStart, "'with' in strict mode"); next(); node.object = parseParenExpression(); - node.body = parseStatement(); + node.body = parseStatement(false); return finishNode(node, "WithStatement"); } @@ -2332,7 +2788,7 @@ if (labels[i].name === maybeName) raise(expr.start, "Label '" + maybeName + "' is already declared"); var kind = tokType.isLoop ? "loop" : tokType === _switch ? "switch" : null; labels.push({name: maybeName, kind: kind}); - node.body = parseStatement(); + node.body = parseStatement(true); labels.pop(); node.label = expr; return finishNode(node, "LabeledStatement"); @@ -2363,7 +2819,7 @@ node.body = []; expect(_braceL); while (!eat(_braceR)) { - var stmt = parseStatement(); + var stmt = parseStatement(true); node.body.push(stmt); if (first && allowStrict && isUseStrict(stmt)) { oldStrict = strict; @@ -2386,7 +2842,7 @@ expect(_semi); node.update = tokType === _parenR ? null : parseExpression(); expect(_parenR); - node.body = parseStatement(); + node.body = parseStatement(false); labels.pop(); return finishNode(node, "ForStatement"); } @@ -2400,7 +2856,7 @@ node.left = init; node.right = parseExpression(); expect(_parenR); - node.body = parseStatement(); + node.body = parseStatement(false); labels.pop(); return finishNode(node, type); } @@ -2527,22 +2983,16 @@ function parseMaybeUnary(isStatement) { if (tokType.prefix) { - var node = startNode(), update = tokType.isUpdate, nodeType; - if (tokType === _ellipsis) { - nodeType = "SpreadElement"; - } else { - nodeType = update ? "UpdateExpression" : "UnaryExpression"; + var node = startNode(), update = tokType.isUpdate; node.operator = tokVal; node.prefix = true; - } - tokRegexpAllowed = true; next(); node.argument = parseMaybeUnary(); 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, nodeType); + return finishNode(node, update ? "UpdateExpression" : "UnaryExpression"); } var start = storeCurrentPos(); var expr = parseExprSubscripts(isStatement); @@ -2599,7 +3049,7 @@ node.callee = base; node.arguments = parseExprList(_parenR, false); return parseSubscripts(finishNode(node, "CallExpression"), start, noCalls); - } else if (tokType === _template) { + } else if (tokType === _backQuote) { var node = startNodeAt(start); node.tag = base; node.quasi = parseTemplate(); @@ -2643,16 +3093,16 @@ // arrow functions if (tokType === _parenL) { next(); - var oldParenL = ++metParenL; + var exprList; if (tokType !== _parenR) { - val = parseExpression(); + var val = parseExpression(); exprList = val.type === "SequenceExpression" ? val.expressions : [val]; } else { exprList = []; } expect(_parenR); // if '=>' follows '(...)', convert contents to arguments - if (metParenL === oldParenL && eat(_arrow)) { + if (eat(_arrow)) { return parseArrowExpression(node, exprList, true); } else { node.callee = id; @@ -2693,7 +3143,7 @@ next(); return finishNode(node, "Literal"); - case _num: case _string: case _xjsText: + case _num: case _string: case _jsxText: var node = startNode(); node.value = tokVal; node.raw = input.slice(tokStart, tokEnd); @@ -2708,42 +3158,7 @@ return finishNode(node, "Literal"); case _parenL: - var start = storeCurrentPos(); - var val, exprList; - next(); - // check whether this is generator comprehension or regular expression - if (options.ecmaVersion >= 7 && tokType === _for) { - val = parseComprehension(startNodeAt(start), true); - } else { - var oldParenL = ++metParenL; - if (tokType !== _parenR) { - val = parseExpression(); - exprList = val.type === "SequenceExpression" ? val.expressions : [val]; - } else { - exprList = []; - } - expect(_parenR); - // if '=>' follows '(...)', convert contents to arguments - if (metParenL === oldParenL && eat(_arrow)) { - val = parseArrowExpression(startNodeAt(start), exprList); - } else { - // forbid '()' before everything but '=>' - if (!val) unexpected(lastStart); - // forbid '...' in sequence expressions - if (options.ecmaVersion >= 6) { - for (var i = 0; i < exprList.length; i++) { - if (exprList[i].type === "SpreadElement") unexpected(); - } - } - - if (options.preserveParens) { - var par = startNodeAt(start); - par.expression = val; - val = finishNode(par, "ParenthesizedExpression"); - } - } - } - return val; + return parseParenAndDistinguishExpression(); case _bracketL: var node = startNode(); @@ -2769,15 +3184,15 @@ case _new: return parseNew(); - case _template: + case _backQuote: return parseTemplate(); - case _lt: - return parseXJSElement(); - case _hash: return parseBindFunctionExpression(); + case _jsxTagStart: + return parseJSXElement(); + default: unexpected(); } @@ -2799,6 +3214,60 @@ return finishNode(node, "BindFunctionExpression"); } + function parseParenAndDistinguishExpression() { + var start = storeCurrentPos(), val; + if (options.ecmaVersion >= 6) { + next(); + + if (options.ecmaVersion >= 7 && tokType === _for) { + return parseComprehension(startNodeAt(start), true); + } + + var innerStart = storeCurrentPos(), exprList = [], first = true, spreadStart, innerParenStart; + while (tokType !== _parenR) { + first ? first = false : expect(_comma); + if (tokType === _ellipsis) { + spreadStart = tokStart; + exprList.push(parseSpread(true)); + break; + } else { + if (tokType === _parenL && !innerParenStart) { + innerParenStart = tokStart; + } + exprList.push(parseMaybeAssign()); + } + } + var innerEnd = storeCurrentPos(); + expect(_parenR); + + if (eat(_arrow)) { + if (innerParenStart) unexpected(innerParenStart); + return parseArrowExpression(startNodeAt(start), exprList); + } + + if (!exprList.length) unexpected(lastStart); + if (spreadStart) unexpected(spreadStart); + + if (exprList.length > 1) { + val = startNodeAt(innerStart); + val.expressions = exprList; + finishNodeAt(val, "SequenceExpression", innerEnd); + } else { + val = exprList[0]; + } + } else { + val = parseParenExpression(); + } + + if (options.preserveParens) { + var par = startNodeAt(start); + par.expression = val; + return finishNode(par, "ParenthesizedExpression"); + } else { + return val; + } + } + // 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 @@ -2816,28 +3285,33 @@ // Parse template expression. function parseTemplateElement() { - var elem = startNodeAt(options.locations ? [tokStart + 1, tokStartLoc.offset(1)] : tokStart + 1); - elem.value = tokVal; - elem.tail = input.charCodeAt(tokEnd - 1) !== 123; // '{' + var elem = startNode(); + elem.value = { + raw: input.slice(tokStart, tokEnd), + cooked: tokVal + }; next(); - var endOff = elem.tail ? 1 : 2; - return finishNodeAt(elem, "TemplateElement", options.locations ? [lastEnd - endOff, lastEndLoc.offset(-endOff)] : lastEnd - endOff); + elem.tail = tokType === _backQuote; + return finishNode(elem, "TemplateElement"); } function parseTemplate() { var node = startNode(); + next(); node.expressions = []; var curElt = parseTemplateElement(); node.quasis = [curElt]; while (!curElt.tail) { + expect(_dollarBraceL); node.expressions.push(parseExpression()); - if (tokType !== _templateContinued) unexpected(); + expect(_braceR); node.quasis.push(curElt = parseTemplateElement()); } + next(); return finishNode(node, "TemplateLiteral"); } - // Parse an object literal. + // Parse an object literal or binding pattern. function parseObj(isPattern) { var node = startNode(), first = true, propHash = {}; @@ -2851,7 +3325,7 @@ var prop = startNode(), start, isGenerator = false, isAsync = false; if (options.ecmaVersion >= 7 && tokType === _ellipsis) { - prop = parseMaybeUnary(); + prop = parseSpread(); prop.type = "SpreadProperty"; node.properties.push(prop); continue; @@ -2890,7 +3364,7 @@ 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")) && + (prop.key.name === "get" || prop.key.name === "set" || (options.playground && prop.key.name === "memo")) && (tokType != _comma && tokType != _braceR)) { if (isGenerator || isAsync || isPattern) unexpected(); prop.kind = prop.key.name; @@ -3015,7 +3489,7 @@ for (;;) { if (eat(_parenR)) { break; - } else if (options.ecmaVersion >= 6 && eat(_ellipsis)) { + } else if (eat(_ellipsis)) { node.rest = parseAssignableAtom(); checkSpreadAssign(node.rest); parseFunctionParam(node.rest); @@ -3026,7 +3500,6 @@ var param = parseAssignableAtom(); parseFunctionParam(param); node.params.push(param); - if (options.ecmaVersion >= 6) { if (eat(_eq)) { hasDefaults = true; @@ -3110,70 +3583,45 @@ function parseClass(node, isStatement) { next(); node.id = tokType === _name ? parseIdent() : isStatement ? unexpected() : null; - if (tokType === _lt) { - node.typeParameters = parseTypeParameterDeclaration(); - } - node.superClass = eat(_extends) ? parseMaybeAssign(false, true) : null; - if (node.superClass && tokType === _lt) { - node.superTypeParameters = parseTypeParameterInstantiation(); - } - if (tokType === _name && tokVal === "implements") { - next(); - node.implements = parseClassImplements(); - } + node.superClass = eat(_extends) ? parseExprSubscripts() : null; var classBody = startNode(); classBody.body = []; expect(_braceL); while (!eat(_braceR)) { - while (eat(_semi)); - if (tokType === _braceR) continue; + if (eat(_semi)) continue; var method = startNode(); if (options.ecmaVersion >= 7 && tokType === _name && tokVal === "private") { next(); classBody.body.push(parsePrivate(method)); continue; } - if (tokType === _name && tokVal === "static") { - next(); - method['static'] = true; - } else { - method['static'] = false; - } - var isAsync = false; var isGenerator = eat(_star); - if (options.ecmaVersion >= 7 && !isGenerator && tokType === _name && tokVal === "async") { - var asyncId = parseIdent(); - if (tokType === _colon || tokType === _parenL) { - method.key = asyncId; - } else { - isAsync = true; - parsePropertyName(method); - } - } else { + var isAsync = false; + parsePropertyName(method); + if (tokType !== _parenL && !method.computed && method.key.type === "Identifier" && + method.key.name === "async") { + isAsync = true; parsePropertyName(method); } if (tokType !== _parenL && !method.computed && method.key.type === "Identifier" && - (method.key.name === "get" || method.key.name === "set" || (options.playground && method.key.name === "memo"))) { + method.key.name === "static") { + if (isGenerator || isAsync) unexpected(); + method['static'] = true; + isGenerator = eat(_star); + parsePropertyName(method); + } else { + method['static'] = false; + } + if (tokType !== _parenL && !method.computed && method.key.type === "Identifier" && + (method.key.name === "get" || method.key.name === "set") || (options.playground && method.key.name === "memo")) { if (isGenerator || isAsync) unexpected(); method.kind = method.key.name; parsePropertyName(method); } else { method.kind = ""; } - if (tokType === _colon) { - if (isGenerator || isAsync) unexpected(); - method.typeAnnotation = parseTypeAnnotation(); - semicolon(); - classBody.body.push(finishNode(method, "ClassProperty")); - } else { - var typeParameters; - if (tokType === _lt) { - typeParameters = parseTypeParameterDeclaration(); - } - method.value = parseMethod(isGenerator, isAsync); - method.value.typeParameters = typeParameters; - classBody.body.push(finishNode(method, "MethodDefinition")); - } + method.value = parseMethod(isGenerator, isAsync); + classBody.body.push(finishNode(method, "MethodDefinition")); } node.body = finishNode(classBody, "ClassBody"); return finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression"); @@ -3210,8 +3658,11 @@ if (allowTrailingComma && options.allowTrailingCommas && eat(close)) break; } else first = false; - if (allowEmpty && tokType === _comma) elts.push(null); - else elts.push(parseExpression(true)); + if (allowEmpty && tokType === _comma) { + elts.push(null); + } else { + elts.push(tokType === _ellipsis ? parseSpread() : parseMaybeAssign()); + } } return elts; } @@ -3236,7 +3687,6 @@ } else { unexpected(); } - tokRegexpAllowed = false; next(); return finishNode(node, "Identifier"); } @@ -3247,7 +3697,7 @@ next(); // export var|const|let|function|class ...; if (tokType === _var || tokType === _const || tokType === _let || tokType === _function || tokType === _class || tokType === _name && tokVal === 'async') { - node.declaration = parseStatement(); + node.declaration = parseStatement(true); node['default'] = false; node.specifiers = null; node.source = null; @@ -3342,11 +3792,10 @@ if (tokType === _name) { // import defaultObj, { x, y as z } from '...' var node = startNode(); - node.id = startNode(); - node.name = parseIdent(); - checkLVal(node.name, true); - node.id.name = "default"; - finishNode(node.id, "Identifier"); + node.id = parseIdent(); + checkLVal(node.id, true); + node.name = null; + node['default'] = true; nodes.push(finishNode(node, "ImportSpecifier")); if (!eat(_comma)) return nodes; } @@ -3420,9 +3869,6 @@ checkLVal(block.left, true); if (tokType !== _name || tokVal !== "of") unexpected(); next(); - // `of` property is here for compatibility with Esprima's AST - // which also supports deprecated [for (... in ...) expr] - block.of = true; block.right = parseExpression(); expect(_parenR); node.blocks.push(finishNode(block, "ComprehensionBlock")); @@ -3436,122 +3882,95 @@ // Transforms JSX element name to string. - function getQualifiedXJSName(object) { - if (object.type === "XJSIdentifier") { + function getQualifiedJSXName(object) { + if (object.type === "JSXIdentifier") { return object.name; } - if (object.type === "XJSNamespacedName") { + if (object.type === "JSXNamespacedName") { return object.namespace.name + ':' + object.name.name; } - if (object.type === "XJSMemberExpression") { + if (object.type === "JSXMemberExpression") { return ( - getQualifiedXJSName(object.object) + '.' + - getQualifiedXJSName(object.property) + getQualifiedJSXName(object.object) + '.' + + getQualifiedJSXName(object.property) ); } } // Parse next token as JSX identifier - function parseXJSIdentifier() { + function parseJSXIdentifier() { var node = startNode(); - if (tokType === _xjsName) { + if (tokType === _jsxName) { node.name = tokVal; } else if (tokType.keyword) { node.name = tokType.keyword; } else { unexpected(); } - tokRegexpAllowed = false; next(); - return finishNode(node, "XJSIdentifier"); + return finishNode(node, "JSXIdentifier"); } // Parse namespaced identifier. - function parseXJSNamespacedName() { - var node = startNode(); - - node.namespace = parseXJSIdentifier(); - expect(_colon); - node.name = parseXJSIdentifier(); - - return finishNode(node, "XJSNamespacedName"); + function parseJSXNamespacedName() { + var start = storeCurrentPos(); + var name = parseJSXIdentifier(); + if (!eat(_colon)) return name; + var node = startNodeAt(start); + node.namespace = name; + node.name = parseJSXIdentifier(); + return finishNode(node, "JSXNamespacedName"); } - // Parse JSX object. + // Parses element name in any form - namespaced, member + // or single identifier. - function parseXJSMemberExpression() { + function parseJSXElementName() { var start = storeCurrentPos(); - var node = parseXJSIdentifier(); - + var node = parseJSXNamespacedName(); while (eat(_dot)) { var newNode = startNodeAt(start); newNode.object = node; - newNode.property = parseXJSIdentifier(); - node = finishNode(newNode, "XJSMemberExpression"); + newNode.property = parseJSXIdentifier(); + node = finishNode(newNode, "JSXMemberExpression"); } - return node; } - // Parses element name in any form - namespaced, object - // or single identifier. - - function parseXJSElementName() { - switch (nextChar()) { - case ':': - return parseXJSNamespacedName(); - - case '.': - return parseXJSMemberExpression(); - - default: - return parseXJSIdentifier(); - } - } - - // Parses attribute name as optionally namespaced identifier. - - function parseXJSAttributeName() { - if (nextChar() === ':') { - return parseXJSNamespacedName(); - } - - return parseXJSIdentifier(); - } - // Parses any type of JSX attribute value. - function parseXJSAttributeValue() { + function parseJSXAttributeValue() { switch (tokType) { case _braceL: - var node = parseXJSExpressionContainer(); - if (node.expression.type === "XJSEmptyExpression") { + var node = parseJSXExpressionContainer(); + if (node.expression.type === "JSXEmptyExpression") { raise( node.start, - 'XJS attributes must only be assigned a non-empty ' + + 'JSX attributes must only be assigned a non-empty ' + 'expression' ); } return node; - case _lt: - return parseXJSElement(); + case _jsxTagStart: + return parseJSXElement(); - case _xjsText: + case _jsxText: + case _string: return parseExprAtom(); default: - raise(tokStart, "XJS value should be either an expression or a quoted XJS text"); + raise(tokStart, "JSX value should be either an expression or a quoted JSX text"); } } - // XJSEmptyExpression is unique type since it doesn't actually parse anything, + // JSXEmptyExpression 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() { + function parseJSXEmptyExpression() { if (tokType !== _braceR) { unexpected(); } @@ -3566,193 +3985,111 @@ tokStartLoc = lastEndLoc; lastEndLoc = tmp; - return finishNode(startNode(), "XJSEmptyExpression"); + return finishNode(startNode(), "JSXEmptyExpression"); } // Parses JSX expression enclosed into curly brackets. - function parseXJSExpressionContainer() { + function parseJSXExpressionContainer() { var node = startNode(); - - var origInXJSTag = inXJSTag, - origInXJSChild = inXJSChild; - - inXJSTag = false; - inXJSChild = false; - next(); - node.expression = tokType === _braceR ? parseXJSEmptyExpression() : parseExpression(); - - inXJSTag = origInXJSTag; - inXJSChild = origInXJSChild; - - if (inXJSChild) { - tokPos = tokEnd; - } + node.expression = tokType === _braceR ? parseJSXEmptyExpression() : parseExpression(); expect(_braceR); - return finishNode(node, "XJSExpressionContainer"); + return finishNode(node, "JSXExpressionContainer"); } // Parses following JSX attribute name-value pair. - function parseXJSAttribute() { - if (tokType === _braceL) { - var tokStart1 = tokStart, tokStartLoc1 = tokStartLoc; - - var origInXJSTag = inXJSTag; - inXJSTag = false; - - next(); - if (tokType !== _ellipsis) unexpected(); - var node = parseMaybeUnary(); - - inXJSTag = origInXJSTag; - + function parseJSXAttribute() { + var node = startNode(); + if (eat(_braceL)) { + expect(_ellipsis); + node.argument = parseMaybeAssign(); expect(_braceR); - node.type = "XJSSpreadAttribute"; - - node.start = tokStart1; - node.end = lastEnd; - if (options.locations) { - node.loc.start = tokStartLoc1; - node.loc.end = lastEndLoc; - } - if (options.ranges) { - node.range = [tokStart1, lastEnd]; - } - - return node; + return finishNode(node, "JSXSpreadAttribute"); } - - var node = startNode(); - node.name = parseXJSAttributeName(); - - // HTML empty attribute - if (tokType === _eq) { - next(); - node.value = parseXJSAttributeValue(); - } else { - node.value = null; - } - - return finishNode(node, "XJSAttribute"); + node.name = parseJSXNamespacedName(); + node.value = eat(_eq) ? parseJSXAttributeValue() : null; + return finishNode(node, "JSXAttribute"); } - // Parses any type of JSX contents (expression, text or another tag). + // Parses JSX opening tag starting after '<'. - function parseXJSChild() { - switch (tokType) { - case _braceL: - return parseXJSExpressionContainer(); - - case _xjsText: - return parseExprAtom(); - - default: - return parseXJSElement(); + function parseJSXOpeningElementAt(start) { + var node = startNodeAt(start); + node.attributes = []; + node.name = parseJSXElementName(); + while (tokType !== _slash && tokType !== _jsxTagEnd) { + node.attributes.push(parseJSXAttribute()); } + node.selfClosing = eat(_slash); + expect(_jsxTagEnd); + return finishNode(node, "JSXOpeningElement"); } - // Parses JSX open tag. + // Parses JSX closing tag starting after ', so parser needs to know - // whether to look for a standard JS token or an XJS text node - inXJSChild = origInXJSChild; - inXJSTag = origInXJSTag; - tokRegexpAllowed = false; - if (inXJSChild) { - tokPos = tokEnd; - } - expect(_gt); - return finishNode(node, "XJSClosingElement"); - } - - // Parses entire JSX element, including it's opening tag, - // attributes, contents and closing tag. - - function parseXJSElement() { - var node = startNode(); + // Parses entire JSX element, including it's opening tag + // (starting after '<'), attributes, contents and closing tag. + function parseJSXElementAt(start) { + var node = startNodeAt(start); var children = []; - - var origInXJSChild = inXJSChild; - var openingElement = parseXJSOpeningElement(); + var openingElement = parseJSXOpeningElementAt(start); var closingElement = null; if (!openingElement.selfClosing) { - while (tokType !== _eof && tokType !== _ltSlash) { - inXJSChild = true; - children.push(parseXJSChild()); + contents:for (;;) { + switch (tokType) { + case _jsxTagStart: + start = storeCurrentPos(); + next(); + if (eat(_slash)) { + closingElement = parseJSXClosingElementAt(start); + break contents; + } + children.push(parseJSXElementAt(start)); + break; + + case _jsxText: + children.push(parseExprAtom()); + break; + + case _braceL: + children.push(parseJSXExpressionContainer()); + break; + + default: + unexpected(); + } } - inXJSChild = origInXJSChild; - closingElement = parseXJSClosingElement(); - if (getQualifiedXJSName(closingElement.name) !== getQualifiedXJSName(openingElement.name)) { + if (getQualifiedJSXName(closingElement.name) !== getQualifiedJSXName(openingElement.name)) { raise( closingElement.start, - "Expected corresponding XJS closing tag for '" + getQualifiedXJSName(openingElement.name) + "'" + "Expected corresponding JSX closing tag for <" + getQualifiedJSXName(openingElement.name) + ">" ); } } - // When (erroneously) writing two adjacent tags like - // - // var x =
one
two
; - // - // the default error message is a bit incomprehensible. Since it's - // rarely (never?) useful to write a less-than sign after an XJS - // element, we disallow it here in the parser in order to provide a - // better error message. (In the rare case that the less-than operator - // was intended, the left tag can be wrapped in parentheses.) - if (!origInXJSChild && tokType === _lt) { - raise(tokStart, "Adjacent XJS elements must be wrapped in an enclosing tag"); - } - node.openingElement = openingElement; node.closingElement = closingElement; node.children = children; - return finishNode(node, "XJSElement"); + return finishNode(node, "JSXElement"); + } + + // Parses entire JSX element from current position. + + function parseJSXElement() { + var start = storeCurrentPos(); + next(); + return parseJSXElementAt(start); } // Declare diff --git a/acorn_loose.js b/acorn_loose.js index 2f41edd588..23ae3a7ed1 100644 --- a/acorn_loose.js +++ b/acorn_loose.js @@ -66,6 +66,8 @@ ahead.length = 0; token = ahead.shift() || readToken(forceRegexp); + if (options.onToken) + options.onToken(token); if (token.start >= nextLineStart) { while (token.start >= nextLineStart) { @@ -101,8 +103,10 @@ replace = {start: e.pos, end: pos, type: tt.regexp, value: re}; } else if (/template/.test(msg)) { replace = {start: e.pos, end: pos, - type: input.charAt(e.pos) == "`" ? tt.template : tt.templateContinued, - value: input.slice(e.pos + 1, pos)}; + type: tt.template, + value: input.slice(e.pos, pos)}; + } else if (/comment/.test(msg)) { + replace = fetchToken.current(); } else { replace = false; } @@ -253,14 +257,6 @@ return node; } - function finishNodeAt(node, type, pos) { - if (options.locations) { node.loc.end = pos[1]; pos = pos[0]; } - node.type = type; - node.end = pos; - if (options.ranges) node.range[1] = pos; - return node; - } - function dummyIdent() { var dummy = startNode(); dummy.name = "✖"; @@ -304,6 +300,7 @@ case "ObjectPattern": case "ArrayPattern": case "SpreadElement": + case "AssignmentPattern": return expr; default: @@ -625,20 +622,18 @@ function parseMaybeUnary(noIn) { if (token.type.prefix) { - var node = startNode(), update = token.type.isUpdate, nodeType; - if (token.type === tt.ellipsis) { - nodeType = "SpreadElement"; - } else { - nodeType = update ? "UpdateExpression" : "UnaryExpression"; - node.operator = token.value; - node.prefix = true; - } + var node = startNode(), update = token.type.isUpdate; node.operator = token.value; node.prefix = true; next(); node.argument = parseMaybeUnary(noIn); if (update) node.argument = checkLVal(node.argument); - return finishNode(node, nodeType); + return finishNode(node, update ? "UpdateExpression" : "UnaryExpression"); + } else if (token.type === tt.ellipsis) { + var node = startNode(); + next(); + node.argument = parseMaybeUnary(noIn); + return finishNode(node, "SpreadElement"); } var start = storeCurrentPos(); var expr = parseExprSubscripts(); @@ -692,7 +687,7 @@ node.callee = base; node.arguments = parseExprList(tt.parenR); base = finishNode(node, "CallExpression"); - } else if (token.type == tt.template) { + } else if (token.type == tt.backQuote) { var node = startNodeAt(start); node.tag = base; node.quasi = parseTemplate(); @@ -785,7 +780,7 @@ } return finishNode(node, "YieldExpression"); - case tt.template: + case tt.backQuote: return parseTemplate(); default: @@ -808,36 +803,35 @@ } function parseTemplateElement() { - var elem = startNodeAt(options.locations ? [token.start + 1, token.startLoc.offset(1)] : token.start + 1); - elem.value = token.value; - elem.tail = input.charCodeAt(token.end - 1) !== 123; // '{' - var endOff = elem.tail ? 1 : 2; - var endPos = options.locations ? [token.end - endOff, token.endLoc.offset(-endOff)] : token.end - endOff; + var elem = startNode(); + elem.value = { + raw: input.slice(token.start, token.end), + cooked: token.value + }; next(); - return finishNodeAt(elem, "TemplateElement", endPos); + elem.tail = token.type === tt.backQuote; + return finishNode(elem, "TemplateElement"); } function parseTemplate() { var node = startNode(); + next(); node.expressions = []; var curElt = parseTemplateElement(); node.quasis = [curElt]; while (!curElt.tail) { - var next = parseExpression(); - if (isDummy(next)) { - node.quasis[node.quasis.length - 1].tail = true; - break; - } - node.expressions.push(next); - if (token.type === tt.templateContinued) { - node.quasis.push(curElt = parseTemplateElement()); + next(); + node.expressions.push(parseExpression()); + if (expect(tt.braceR)) { + curElt = parseTemplateElement(); } else { curElt = startNode(); - curElt.value = {cooked: "", raw: ""}; + curElt.value = {cooked: '', raw: ''}; curElt.tail = true; - node.quasis.push(curElt); } + node.quasis.push(curElt); } + expect(tt.backQuote); return finishNode(node, "TemplateLiteral"); } @@ -858,10 +852,11 @@ eat(tt.braceL); if (curIndent + 1 < indent) { indent = curIndent; line = curLineStart; } while (!closes(tt.braceR, indent, line)) { + if (isClass && semicolon()) continue; var prop = startNode(), isGenerator; if (options.ecmaVersion >= 6) { if (isClass) { - if (prop['static'] = (token.type === tt.name && token.value === "static")) next(); + prop['static'] = false; } else { prop.method = false; prop.shorthand = false; @@ -870,6 +865,16 @@ } parsePropertyName(prop); if (isDummy(prop.key)) { if (isDummy(parseExpression(true))) next(); eat(tt.comma); continue; } + if (isClass) { + if (prop.key.type === "Identifier" && !prop.computed && prop.key.name === "static" && + (token.type != tt.parenL && token.type != tt.braceL)) { + prop['static'] = true; + isGenerator = eat(tt.star); + parsePropertyName(prop); + } else { + prop['static'] = false; + } + } if (!isClass && eat(tt.colon)) { prop.kind = "init"; prop.value = parseExpression(true); @@ -882,7 +887,7 @@ } prop.value = parseMethod(isGenerator); } else if (options.ecmaVersion >= 5 && prop.key.type === "Identifier" && - (prop.key.name === "get" || prop.key.name === "set") && + !prop.computed && (prop.key.name === "get" || prop.key.name === "set") && (token.type != tt.comma && token.type != tt.braceR)) { prop.kind = prop.key.name; parsePropertyName(prop); @@ -898,7 +903,6 @@ if (isClass) { node.body.body.push(finishNode(prop, "MethodDefinition")); - semicolon(); } else { node.properties.push(finishNode(prop, "Property")); eat(tt.comma); @@ -942,7 +946,6 @@ function parseIdent() { var node = startNode(); node.name = token.type === tt.name ? token.value : token.type.keyword; - fetchToken.noRegexp(); next(); return finishNode(node, "Identifier"); } diff --git a/bin/acorn b/bin/acorn index dd5baea012..b80ad29d51 100755 --- a/bin/acorn +++ b/bin/acorn @@ -4,12 +4,12 @@ var path = require("path"); var fs = require("fs"); var acorn = require("../acorn.js"); -var infile, parsed, options = {}, silent = false, compact = false; +var infile, parsed, tokens, options = {}, silent = false, compact = false, tokenize = false; function help(status) { var print = (status == 0) ? console.log : console.error; print("usage: " + path.basename(process.argv[1]) + " [--ecma3|--ecma5|--ecma6] [--strictSemicolons]"); - print(" [--locations] [--compact] [--silent] [--help] [--] infile"); + print(" [--tokenize] [--locations] [--compact] [--silent] [--help] [--] infile"); process.exit(status); } @@ -26,16 +26,29 @@ for (var i = 2; i < process.argv.length; ++i) { else if (arg == "--silent") silent = true; else if (arg == "--compact") compact = true; else if (arg == "--help") help(0); + else if (arg == "--tokenize") tokenize = true; else help(1); } try { var code = fs.readFileSync(infile, "utf8"); - parsed = acorn.parse(code, options); + + if (!tokenize) + parsed = acorn.parse(code, options); + else { + var get = acorn.tokenize(code, options); + tokens = []; + while (true) { + var token = get(); + tokens.push(token); + if (token.type.type == "eof") + break; + } + } } catch(e) { console.log(e.message); process.exit(1); } if (!silent) - console.log(JSON.stringify(parsed, null, compact ? null : 2)); + console.log(JSON.stringify(tokenize ? tokens : parsed, null, compact ? null : 2)); diff --git a/test/index.html b/test/index.html index bb4e45513e..da28c65435 100644 --- a/test/index.html +++ b/test/index.html @@ -8,6 +8,7 @@ + diff --git a/test/tests-harmony.js b/test/tests-harmony.js index fa19a011b6..07bf912d01 100644 --- a/test/tests-harmony.js +++ b/test/tests-harmony.js @@ -3328,8 +3328,7 @@ test("[for (x of array) x]", { loc: { start: {line: 1, column: 1}, end: {line: 1, column: 17} - }, - of: true + } }], body: { type: "Identifier", @@ -3412,8 +3411,7 @@ test("[for (x of array) for (y of array2) if (x === test) x]", { loc: { start: {line: 1, column: 1}, end: {line: 1, column: 17} - }, - of: true + } }, { type: "ComprehensionBlock", @@ -3436,8 +3434,7 @@ test("[for (x of array) for (y of array2) if (x === test) x]", { loc: { start: {line: 1, column: 18}, end: {line: 1, column: 35} - }, - of: true + } } ], body: { @@ -3521,8 +3518,7 @@ test("(for (x of array) for (y of array2) if (x === test) x)", { loc: { start: {line: 1, column: 1}, end: {line: 1, column: 17} - }, - of: true + } }, { type: "ComprehensionBlock", @@ -3545,8 +3541,7 @@ test("(for (x of array) for (y of array2) if (x === test) x)", { loc: { start: {line: 1, column: 18}, end: {line: 1, column: 35} - }, - of: true + } } ], body: { @@ -3617,8 +3612,7 @@ test("[for ([,x] of array) for ({[start.x]: x, [start.y]: y} of array2) x]", { loc: { start: {line: 1, column: 1}, end: {line: 1, column: 20} - }, - of: true + } }, { type: "ComprehensionBlock", @@ -3728,8 +3722,7 @@ test("[for ([,x] of array) for ({[start.x]: x, [start.y]: y} of array2) x]", { loc: { start: {line: 1, column: 21}, end: {line: 1, column: 65} - }, - of: true + } } ], body: { @@ -5278,14 +5271,6 @@ test("import $ from \"jquery\"", { specifiers: [{ type: "ImportSpecifier", id: { - type: "Identifier", - name: "default", - loc: { - start: {line: 1, column: 7}, - end: {line: 1, column: 8} - } - }, - name: { type: "Identifier", name: "$", loc: { @@ -5293,6 +5278,7 @@ test("import $ from \"jquery\"", { end: {line: 1, column: 8} } }, + name: null, loc: { start: {line: 1, column: 7}, end: {line: 1, column: 8} @@ -5455,21 +5441,15 @@ test("import crypto, { decrypt, encrypt as enc } from \"crypto\"", { end: {line: 1, column: 13} }, id: { - type: "Identifier", - loc: { - start: {line: 1, column: 7}, - end: {line: 1, column: 13} - }, - name: "default" - }, - name: { type: "Identifier", loc: { start: {line: 1, column: 7}, end: {line: 1, column: 13} }, name: "crypto" - } + }, + name: null, + default: true }, { type: "ImportSpecifier", @@ -6478,6 +6458,72 @@ test("var A = class extends B {}", { locations: true }); +test("class A extends class B extends C {} {}", { + type: "Program", + body: [{ + type: "ClassDeclaration", + id: { + type: "Identifier", + name: "A", + loc: { + start: {line: 1, column: 6}, + end: {line: 1, column: 7} + } + }, + superClass: { + type: "ClassExpression", + id: { + type: "Identifier", + name: "B", + loc: { + start: {line: 1, column: 22}, + end: {line: 1, column: 23} + } + }, + superClass: { + type: "Identifier", + name: "C", + loc: { + start: {line: 1, column: 32}, + end: {line: 1, column: 33} + } + }, + body: { + type: "ClassBody", + body: [], + loc: { + start: {line: 1, column: 34}, + end: {line: 1, column: 36} + } + }, + loc: { + start: {line: 1, column: 16}, + end: {line: 1, column: 36} + } + }, + body: { + type: "ClassBody", + body: [], + loc: { + start: {line: 1, column: 37}, + end: {line: 1, column: 39} + } + }, + loc: { + start: {line: 1, column: 0}, + end: {line: 1, column: 39} + } + }], + loc: { + start: {line: 1, column: 0}, + end: {line: 1, column: 39} + } +}, { + ecmaVersion: 6, + ranges: true, + locations: true +}); + test("class A {get() {}}", { type: "Program", body: [{ @@ -8813,6 +8859,42 @@ test("class A { foo() {} get foo() {} }",{ locations: true }); +test("class Semicolon { ; }", { + type: "Program", + loc: { + start: {line: 1, column: 0}, + end: {line: 1, column: 21} + }, + body: [{ + type: "ClassDeclaration", + loc: { + start: {line: 1, column: 0}, + end: {line: 1, column: 21} + }, + id: { + type: "Identifier", + loc: { + start: {line: 1, column: 6}, + end: {line: 1, column: 15} + }, + name: "Semicolon" + }, + superClass: null, + body: { + type: "ClassBody", + loc: { + start: {line: 1, column: 16}, + end: {line: 1, column: 21} + }, + body: [] + } + }] +}, { + ecmaVersion: 6, + ranges: true, + locations: true +}); + // ES6: Computed Properties test("({[x]: 10})", { @@ -13809,9 +13891,9 @@ testFail("import { foo, bar }", "Unexpected token (1:19)", {ecmaVersion: 6}); testFail("import foo from bar", "Unexpected token (1:16)", {ecmaVersion: 6}); -testFail("((a)) => 42", "Unexpected token (1:6)", {ecmaVersion: 6}); +testFail("((a)) => 42", "Unexpected token (1:1)", {ecmaVersion: 6}); -testFail("(a, (b)) => 42", "Unexpected token (1:9)", {ecmaVersion: 6}); +testFail("(a, (b)) => 42", "Unexpected token (1:4)", {ecmaVersion: 6}); testFail("\"use strict\"; (eval = 10) => 42", "Assigning to eval in strict mode (1:15)", {ecmaVersion: 6}); @@ -14031,7 +14113,7 @@ testFail("class A extends yield B { }", "Unexpected token (1:22)", {ecmaVersion: testFail("class default", "Unexpected token (1:6)", {ecmaVersion: 6}); -testFail("`test", "Unterminated template (1:0)", {ecmaVersion: 6}); +testFail("`test", "Unterminated template (1:1)", {ecmaVersion: 6}); testFail("switch `test`", "Unexpected token (1:7)", {ecmaVersion: 6}); @@ -14069,7 +14151,7 @@ testFail("\"use strict\"; function x({ b: { a } }, [{ b: { a } }]){}", "Argument testFail("\"use strict\"; function x(a, ...[a]){}", "Argument name clash in strict mode (1:32)", {ecmaVersion: 6}); -testFail("(...a, b) => {}", "Unexpected token (1:1)", {ecmaVersion: 6}); +testFail("(...a, b) => {}", "Unexpected token (1:5)", {ecmaVersion: 6}); testFail("([ 5 ]) => {}", "Unexpected token (1:3)", {ecmaVersion: 6}); @@ -14144,9 +14226,9 @@ test("[...a, ] = b", { locations: true }); -testFail("if (b,...a, );", "Unexpected token (1:12)", {ecmaVersion: 6}); +testFail("if (b,...a, );", "Unexpected token (1:6)", {ecmaVersion: 6}); -testFail("(b, ...a)", "Unexpected token (1:9)", {ecmaVersion: 6}); +testFail("(b, ...a)", "Unexpected token (1:4)", {ecmaVersion: 6}); testFail("switch (cond) { case 10: let a = 20; ", "Unexpected token (1:37)", {ecmaVersion: 6}); @@ -14290,13 +14372,11 @@ test("import foo, * as bar from 'baz';", { { type: "ImportSpecifier", id: { - type: "Identifier", - name: "default" - }, - name: { type: "Identifier", name: "foo" - } + }, + name: null, + default: true }, { type: "ImportBatchSpecifier", @@ -14545,3 +14625,259 @@ test("var [localVar = defaultValue] = obj", { locations: true, loose: false }); + +// https://github.com/marijnh/acorn/issues/191 + +test("try {} catch ({message}) {}", { + type: "Program", + range: [0, 27], + body: [{ + type: "TryStatement", + range: [0, 27], + block: { + type: "BlockStatement", + range: [4, 6], + body: [] + }, + handler: { + type: "CatchClause", + range: [7, 27], + param: { + type: "ObjectPattern", + range: [14, 23], + properties: [{ + type: "Property", + range: [15, 22], + method: false, + shorthand: true, + computed: false, + key: { + type: "Identifier", + range: [15, 22], + name: "message" + }, + kind: "init", + value: { + type: "Identifier", + range: [15, 22], + name: "message" + } + }] + }, + guard: null, + body: { + type: "BlockStatement", + range: [25, 27], + body: [] + } + }, + guardedHandlers: [], + finalizer: null + }] +}, { + ecmaVersion: 6, + ranges: true, + locations: true, + loose: false +}); + +// https://github.com/marijnh/acorn/issues/192 + +test("class A { static() {} }", { + type: "Program", + range: [0, 23], + body: [{ + type: "ClassDeclaration", + range: [0, 23], + id: { + type: "Identifier", + range: [6, 7], + name: "A" + }, + superClass: null, + body: { + type: "ClassBody", + range: [8, 23], + body: [{ + type: "MethodDefinition", + range: [10, 21], + computed: false, + key: { + type: "Identifier", + range: [10, 16], + name: "static" + }, + static: false, + kind: "", + value: { + type: "FunctionExpression", + range: [16, 21], + id: null, + params: [], + defaults: [], + rest: null, + generator: false, + body: { + type: "BlockStatement", + range: [19, 21], + body: [] + }, + expression: false + } + }] + } + }] +}, { + ecmaVersion: 6, + ranges: true, + locations: true +}); + +test("class A { *static() {} }", { + type: "Program", + range: [0, 24], + body: [{ + type: "ClassDeclaration", + range: [0, 24], + id: { + type: "Identifier", + range: [6, 7], + name: "A" + }, + superClass: null, + body: { + type: "ClassBody", + range: [8, 24], + body: [{ + type: "MethodDefinition", + range: [10, 22], + computed: false, + key: { + type: "Identifier", + range: [11, 17], + name: "static" + }, + static: false, + kind: "", + value: { + type: "FunctionExpression", + range: [17, 22], + id: null, + params: [], + defaults: [], + rest: null, + generator: true, + body: { + type: "BlockStatement", + range: [20, 22], + body: [] + }, + expression: false + } + }] + } + }] +}, { + ecmaVersion: 6, + ranges: true, + locations: true +}); + +test("`${/\d/.exec('1')[0]}`", { + "type": "Program", + "start": 0, + "end": 21, + "body": [ + { + "type": "ExpressionStatement", + "start": 0, + "end": 21, + "expression": { + "type": "TemplateLiteral", + "start": 0, + "end": 21, + "expressions": [ + { + "type": "MemberExpression", + "start": 3, + "end": 19, + "object": { + "type": "CallExpression", + "start": 3, + "end": 16, + "callee": { + "type": "MemberExpression", + "start": 3, + "end": 11, + "object": { + "type": "Literal", + "start": 3, + "end": 6, + "regex": { + "pattern": "d", + "flags": "" + }, + "value": {}, + "raw": "/d/" + }, + "property": { + "type": "Identifier", + "start": 7, + "end": 11, + "name": "exec" + }, + "computed": false + }, + "arguments": [ + { + "type": "Literal", + "start": 12, + "end": 15, + "value": "1", + "raw": "'1'" + } + ] + }, + "property": { + "type": "Literal", + "start": 17, + "end": 18, + "value": 0, + "raw": "0" + }, + "computed": true + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 1, + "end": 1, + "value": { + "raw": "", + "cooked": "" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 20, + "end": 20, + "value": { + "raw": "", + "cooked": "" + }, + "tail": true + } + ] + } + } + ] +}, { + ecmaVersion: 6 +}); + +testFail("if (1) let x = 10;", "Unexpected token (1:7)", {ecmaVersion: 6}); +testFail("for (;;) const x = 10;", "Unexpected token (1:9)", {ecmaVersion: 6}); +testFail("while (1) function foo(){}", "Unexpected token (1:10)", {ecmaVersion: 6}); +testFail("if (1) ; else class Cls {}", "Unexpected token (1:14)", {ecmaVersion: 6}); diff --git a/test/tests-jsx.js b/test/tests-jsx.js index cb61494198..b952a4f680 100644 --- a/test/tests-jsx.js +++ b/test/tests-jsx.js @@ -2,15 +2,15 @@ var fbTestFixture = { // Taken and adapted from esprima-fb/fbtest.js. - 'XJS': { + 'JSX': { '': { type: "ExpressionStatement", expression: { - type: "XJSElement", + type: "JSXElement", openingElement: { - type: "XJSOpeningElement", + type: "JSXOpeningElement", name: { - type: "XJSIdentifier", + type: "JSXIdentifier", name: "a", range: [1, 2], loc: { @@ -43,13 +43,13 @@ var fbTestFixture = { '': { type: 'ExpressionStatement', expression: { - type: 'XJSElement', + type: 'JSXElement', openingElement: { - type: 'XJSOpeningElement', + type: 'JSXOpeningElement', name: { - type: 'XJSNamespacedName', + type: 'JSXNamespacedName', namespace: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'n', range: [1, 2], loc: { @@ -58,7 +58,7 @@ var fbTestFixture = { } }, name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'a', range: [3, 4], loc: { @@ -74,11 +74,11 @@ var fbTestFixture = { }, selfClosing: true, attributes: [{ - type: 'XJSAttribute', + type: 'JSXAttribute', name: { - type: 'XJSNamespacedName', + type: 'JSXNamespacedName', namespace: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'n', range: [5, 6], loc: { @@ -87,7 +87,7 @@ var fbTestFixture = { } }, name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'v', range: [7, 8], loc: { @@ -131,11 +131,11 @@ var fbTestFixture = { ' {value} ': { type: 'ExpressionStatement', expression: { - type: 'XJSElement', + type: 'JSXElement', openingElement: { - type: 'XJSOpeningElement', + type: 'JSXOpeningElement', name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'a', range: [1, 2], loc: { @@ -145,11 +145,11 @@ var fbTestFixture = { }, selfClosing: false, attributes: [{ - type: 'XJSAttribute', + type: 'JSXAttribute', name: { - type: 'XJSNamespacedName', + type: 'JSXNamespacedName', namespace: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'n', range: [3, 4], loc: { @@ -158,7 +158,7 @@ var fbTestFixture = { } }, name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'foo', range: [5, 8], loc: { @@ -195,9 +195,9 @@ var fbTestFixture = { } }, closingElement: { - type: 'XJSClosingElement', + type: 'JSXClosingElement', name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'a', range: [38, 39], loc: { @@ -221,7 +221,7 @@ var fbTestFixture = { end: { line: 1, column: 16 } } }, { - type: 'XJSExpressionContainer', + type: 'JSXExpressionContainer', expression: { type: 'Identifier', name: 'value', @@ -246,11 +246,11 @@ var fbTestFixture = { end: { line: 1, column: 24 } } }, { - type: 'XJSElement', + type: 'JSXElement', openingElement: { - type: 'XJSOpeningElement', + type: 'JSXOpeningElement', name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'b', range: [25, 26], loc: { @@ -267,9 +267,9 @@ var fbTestFixture = { } }, closingElement: { - type: 'XJSClosingElement', + type: 'JSXClosingElement', name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'b', range: [34, 35], loc: { @@ -284,11 +284,11 @@ var fbTestFixture = { } }, children: [{ - type: 'XJSElement', + type: 'JSXElement', openingElement: { - type: 'XJSOpeningElement', + type: 'JSXOpeningElement', name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'c', range: [28, 29], loc: { @@ -333,25 +333,25 @@ var fbTestFixture = { '': { type: "ExpressionStatement", expression: { - type: "XJSElement", + type: "JSXElement", openingElement: { - type: "XJSOpeningElement", + type: "JSXOpeningElement", name: { - type: "XJSIdentifier", + type: "JSXIdentifier", name: "a", range: [1, 2] }, selfClosing: true, attributes: [ { - type: "XJSAttribute", + type: "JSXAttribute", name: { - type: "XJSIdentifier", + type: "JSXIdentifier", name: "b", range: [3, 4] }, value: { - type: "XJSExpressionContainer", + type: "JSXExpressionContainer", expression: { type: "Literal", value: " ", @@ -363,9 +363,9 @@ var fbTestFixture = { range: [3, 10] }, { - type: "XJSAttribute", + type: "JSXAttribute", name: { - type: "XJSIdentifier", + type: "JSXIdentifier", name: "c", range: [11, 12] }, @@ -378,9 +378,9 @@ var fbTestFixture = { range: [11, 16] }, { - type: "XJSAttribute", + type: "JSXAttribute", name: { - type: "XJSIdentifier", + type: "JSXIdentifier", name: "d", range: [17, 18] }, @@ -393,9 +393,9 @@ var fbTestFixture = { range: [17, 26] }, { - type: "XJSAttribute", + type: "JSXAttribute", name: { - type: "XJSIdentifier", + type: "JSXIdentifier", name: "e", range: [27, 28] }, @@ -419,11 +419,11 @@ var fbTestFixture = { '': { type: "ExpressionStatement", expression: { - type: "XJSElement", + type: "JSXElement", openingElement: { - type: "XJSOpeningElement", + type: "JSXOpeningElement", name: { - type: "XJSIdentifier", + type: "JSXIdentifier", name: "a", range: [ 1, @@ -492,11 +492,11 @@ var fbTestFixture = { '<日本語>': { type: "ExpressionStatement", expression: { - type: "XJSElement", + type: "JSXElement", openingElement: { - type: "XJSOpeningElement", + type: "JSXOpeningElement", name: { - type: "XJSIdentifier", + type: "JSXIdentifier", name: "日本語", range: [ 1, @@ -531,9 +531,9 @@ var fbTestFixture = { } }, closingElement: { - type: "XJSClosingElement", + type: "JSXClosingElement", name: { - type: "XJSIdentifier", + type: "JSXIdentifier", name: "日本語", range: [ 7, @@ -600,11 +600,11 @@ var fbTestFixture = { '\nbar\nbaz\n': { type: "ExpressionStatement", expression: { - type: "XJSElement", + type: "JSXElement", openingElement: { - type: "XJSOpeningElement", + type: "JSXOpeningElement", name: { - type: "XJSIdentifier", + type: "JSXIdentifier", name: "AbC-def", range: [ 1, @@ -624,9 +624,9 @@ var fbTestFixture = { selfClosing: false, attributes: [ { - type: "XJSAttribute", + type: "JSXAttribute", name: { - type: "XJSIdentifier", + type: "JSXIdentifier", name: "test", range: [ 11, @@ -694,9 +694,9 @@ var fbTestFixture = { } }, closingElement: { - type: "XJSClosingElement", + type: "JSXClosingElement", name: { - type: "XJSIdentifier", + type: "JSXIdentifier", name: "AbC-def", range: [ 43, @@ -783,11 +783,11 @@ var fbTestFixture = { ' : } />': { type: "ExpressionStatement", expression: { - type: "XJSElement", + type: "JSXElement", openingElement: { - type: "XJSOpeningElement", + type: "JSXOpeningElement", name: { - type: "XJSIdentifier", + type: "JSXIdentifier", name: "a", range: [ 1, @@ -807,9 +807,9 @@ var fbTestFixture = { selfClosing: true, attributes: [ { - type: "XJSAttribute", + type: "JSXAttribute", name: { - type: "XJSIdentifier", + type: "JSXIdentifier", name: "b", range: [ 3, @@ -827,7 +827,7 @@ var fbTestFixture = { } }, value: { - type: "XJSExpressionContainer", + type: "JSXExpressionContainer", expression: { type: "ConditionalExpression", test: { @@ -849,11 +849,11 @@ var fbTestFixture = { } }, consequent: { - type: "XJSElement", + type: "JSXElement", openingElement: { - type: "XJSOpeningElement", + type: "JSXOpeningElement", name: { - type: "XJSIdentifier", + type: "JSXIdentifier", name: "c", range: [ 11, @@ -905,11 +905,11 @@ var fbTestFixture = { } }, alternate: { - type: "XJSElement", + type: "JSXElement", openingElement: { - type: "XJSOpeningElement", + type: "JSXOpeningElement", name: { - type: "XJSIdentifier", + type: "JSXIdentifier", name: "d", range: [ 19, @@ -1057,11 +1057,11 @@ var fbTestFixture = { '{}': { type: 'ExpressionStatement', expression: { - type: 'XJSElement', + type: 'JSXElement', openingElement: { - type: 'XJSOpeningElement', + type: 'JSXOpeningElement', name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'a', range: [1, 2], loc: { @@ -1078,9 +1078,9 @@ var fbTestFixture = { } }, closingElement: { - type: 'XJSClosingElement', + type: 'JSXClosingElement', name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'a', range: [7, 8], loc: { @@ -1095,9 +1095,9 @@ var fbTestFixture = { } }, children: [{ - type: 'XJSExpressionContainer', + type: 'JSXExpressionContainer', expression: { - type: 'XJSEmptyExpression', + type: 'JSXEmptyExpression', range: [4, 4], loc: { start: { line: 1, column: 4 }, @@ -1126,11 +1126,11 @@ var fbTestFixture = { '{/* this is a comment */}': { type: 'ExpressionStatement', expression: { - type: 'XJSElement', + type: 'JSXElement', openingElement: { - type: 'XJSOpeningElement', + type: 'JSXOpeningElement', name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'a', range: [1, 2], loc: { @@ -1147,9 +1147,9 @@ var fbTestFixture = { } }, closingElement: { - type: 'XJSClosingElement', + type: 'JSXClosingElement', name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'a', range: [30, 31], loc: { @@ -1164,9 +1164,9 @@ var fbTestFixture = { } }, children: [{ - type: 'XJSExpressionContainer', + type: 'JSXExpressionContainer', expression: { - type: 'XJSEmptyExpression', + type: 'JSXEmptyExpression', range: [4, 27], loc: { start: { line: 1, column: 4 }, @@ -1195,11 +1195,11 @@ var fbTestFixture = { '
@test content
': { type: 'ExpressionStatement', expression: { - type: 'XJSElement', + type: 'JSXElement', openingElement: { - type: 'XJSOpeningElement', + type: 'JSXOpeningElement', name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'div', range: [1, 4], loc: { @@ -1216,9 +1216,9 @@ var fbTestFixture = { } }, closingElement: { - type: 'XJSClosingElement', + type: 'JSXClosingElement', name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'div', range: [20, 23], loc: { @@ -1258,11 +1258,11 @@ var fbTestFixture = { '

7x invalid-js-identifier
': { type: 'ExpressionStatement', expression: { - type: 'XJSElement', + type: 'JSXElement', openingElement: { - type: 'XJSOpeningElement', + type: 'JSXOpeningElement', name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'div', range: [ 1, @@ -1297,9 +1297,9 @@ var fbTestFixture = { } }, closingElement: { - type: 'XJSClosingElement', + type: 'JSXClosingElement', name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'div', range: [ 37, @@ -1332,11 +1332,11 @@ var fbTestFixture = { } }, children: [{ - type: 'XJSElement', + type: 'JSXElement', openingElement: { - type: 'XJSOpeningElement', + type: 'JSXOpeningElement', name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'br', range: [ 6, @@ -1439,11 +1439,11 @@ var fbTestFixture = { ' right=monkeys /> gorillas />': { "type": "ExpressionStatement", "expression": { - "type": "XJSElement", + "type": "JSXElement", "openingElement": { - "type": "XJSOpeningElement", + "type": "JSXOpeningElement", "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "name": "LeftRight", "range": [ 1, @@ -1463,9 +1463,9 @@ var fbTestFixture = { "selfClosing": true, "attributes": [ { - "type": "XJSAttribute", + "type": "JSXAttribute", "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "name": "left", "range": [ 11, @@ -1483,11 +1483,11 @@ var fbTestFixture = { } }, "value": { - "type": "XJSElement", + "type": "JSXElement", "openingElement": { - "type": "XJSOpeningElement", + "type": "JSXOpeningElement", "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "name": "a", "range": [ 17, @@ -1554,9 +1554,9 @@ var fbTestFixture = { } }, { - "type": "XJSAttribute", + "type": "JSXAttribute", "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "name": "right", "range": [ 22, @@ -1574,11 +1574,11 @@ var fbTestFixture = { } }, "value": { - "type": "XJSElement", + "type": "JSXElement", "openingElement": { - "type": "XJSOpeningElement", + "type": "JSXOpeningElement", "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "name": "b", "range": [ 29, @@ -1613,9 +1613,9 @@ var fbTestFixture = { } }, "closingElement": { - "type": "XJSClosingElement", + "type": "JSXClosingElement", "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "name": "b", "range": [ 52, @@ -1750,13 +1750,13 @@ var fbTestFixture = { '': { type: 'ExpressionStatement', expression: { - type: 'XJSElement', + type: 'JSXElement', openingElement: { - type: 'XJSOpeningElement', + type: 'JSXOpeningElement', name: { - type: 'XJSMemberExpression', + type: 'JSXMemberExpression', object: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'a', range: [1, 2], loc: { @@ -1765,7 +1765,7 @@ var fbTestFixture = { } }, property: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'b', range: [3, 4], loc: { @@ -1788,11 +1788,11 @@ var fbTestFixture = { } }, closingElement: { - type: 'XJSClosingElement', + type: 'JSXClosingElement', name: { - type: 'XJSMemberExpression', + type: 'JSXMemberExpression', object: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'a', range: [7, 8], loc: { @@ -1801,7 +1801,7 @@ var fbTestFixture = { } }, property: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'b', range: [9, 10], loc: { @@ -1838,15 +1838,15 @@ var fbTestFixture = { '': { type: 'ExpressionStatement', expression: { - type: 'XJSElement', + type: 'JSXElement', openingElement: { - type: 'XJSOpeningElement', + type: 'JSXOpeningElement', name: { - type: 'XJSMemberExpression', + type: 'JSXMemberExpression', object: { - type: 'XJSMemberExpression', + type: 'JSXMemberExpression', object: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'a', range: [1, 2], loc: { @@ -1855,7 +1855,7 @@ var fbTestFixture = { } }, property: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'b', range: [3, 4], loc: { @@ -1870,7 +1870,7 @@ var fbTestFixture = { } }, property: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'c', range: [5, 6], loc: { @@ -1893,13 +1893,13 @@ var fbTestFixture = { } }, closingElement: { - type: 'XJSClosingElement', + type: 'JSXClosingElement', name: { - type: 'XJSMemberExpression', + type: 'JSXMemberExpression', object: { - type: 'XJSMemberExpression', + type: 'JSXMemberExpression', object: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'a', range: [9, 10], loc: { @@ -1908,7 +1908,7 @@ var fbTestFixture = { } }, property: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'b', range: [11, 12], loc: { @@ -1923,7 +1923,7 @@ var fbTestFixture = { } }, property: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'c', range: [13, 14], loc: { @@ -1958,7 +1958,7 @@ var fbTestFixture = { }, // In order to more useful parse errors, we disallow following an - // XJSElement by a less-than symbol. In the rare case that the binary + // JSXElement by a less-than symbol. In the rare case that the binary // operator was intended, the tag can be wrapped in parentheses: '(
) < x;': { type: 'ExpressionStatement', @@ -1966,11 +1966,11 @@ var fbTestFixture = { type: 'BinaryExpression', operator: '<', left: { - type: 'XJSElement', + type: 'JSXElement', openingElement: { - type: 'XJSOpeningElement', + type: 'JSXOpeningElement', name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', name: 'div', range: [2, 5], loc: { @@ -2019,11 +2019,11 @@ var fbTestFixture = { '
': { "type": "ExpressionStatement", "expression": { - "type": "XJSElement", + "type": "JSXElement", "openingElement": { - "type": "XJSOpeningElement", + "type": "JSXOpeningElement", "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "name": "div", "range": [ 1, @@ -2043,7 +2043,7 @@ var fbTestFixture = { "selfClosing": true, "attributes": [ { - "type": "XJSSpreadAttribute", + "type": "JSXSpreadAttribute", "argument": { "type": "Identifier", "name": "props", @@ -2129,11 +2129,11 @@ var fbTestFixture = { '
': { "type": "ExpressionStatement", "expression": { - "type": "XJSElement", + "type": "JSXElement", "openingElement": { - "type": "XJSOpeningElement", + "type": "JSXOpeningElement", "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "name": "div", "range": [ 1, @@ -2153,7 +2153,7 @@ var fbTestFixture = { "selfClosing": true, "attributes": [ { - "type": "XJSSpreadAttribute", + "type": "JSXSpreadAttribute", "argument": { "type": "Identifier", "name": "props", @@ -2188,9 +2188,9 @@ var fbTestFixture = { } }, { - "type": "XJSAttribute", + "type": "JSXAttribute", "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "name": "post", "range": [ 16, @@ -2293,11 +2293,11 @@ var fbTestFixture = { '
': { "type": "ExpressionStatement", "expression": { - "type": "XJSElement", + "type": "JSXElement", "openingElement": { - "type": "XJSOpeningElement", + "type": "JSXOpeningElement", "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "name": "div", "range": [ 1, @@ -2317,9 +2317,9 @@ var fbTestFixture = { "selfClosing": false, "attributes": [ { - "type": "XJSAttribute", + "type": "JSXAttribute", "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "name": "pre", "range": [ 5, @@ -2371,9 +2371,9 @@ var fbTestFixture = { } }, { - "type": "XJSAttribute", + "type": "JSXAttribute", "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "name": "pre2", "range": [ 19, @@ -2425,7 +2425,7 @@ var fbTestFixture = { } }, { - "type": "XJSSpreadAttribute", + "type": "JSXSpreadAttribute", "argument": { "type": "Identifier", "name": "props", @@ -2476,9 +2476,9 @@ var fbTestFixture = { } }, "closingElement": { - "type": "XJSClosingElement", + "type": "JSXClosingElement", "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "name": "div", "range": [ 49, @@ -2560,7 +2560,7 @@ var fbTestFixture = { 52 ], "expression": { - "type": "XJSElement", + "type": "JSXElement", "start": 0, "end": 52, "loc": { @@ -2578,7 +2578,7 @@ var fbTestFixture = { 52 ], "openingElement": { - "type": "XJSOpeningElement", + "type": "JSXOpeningElement", "start": 0, "end": 31, "loc": { @@ -2597,7 +2597,7 @@ var fbTestFixture = { ], "attributes": [ { - "type": "XJSAttribute", + "type": "JSXAttribute", "start": 3, "end": 16, "loc": { @@ -2615,7 +2615,7 @@ var fbTestFixture = { 16 ], "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "start": 3, "end": 5, "loc": { @@ -2635,7 +2635,7 @@ var fbTestFixture = { "name": "aa" }, "value": { - "type": "XJSExpressionContainer", + "type": "JSXExpressionContainer", "start": 6, "end": 16, "loc": { @@ -2755,7 +2755,7 @@ var fbTestFixture = { } }, { - "type": "XJSAttribute", + "type": "JSXAttribute", "start": 17, "end": 30, "loc": { @@ -2773,7 +2773,7 @@ var fbTestFixture = { 30 ], "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "start": 17, "end": 19, "loc": { @@ -2793,7 +2793,7 @@ var fbTestFixture = { "name": "bb" }, "value": { - "type": "XJSExpressionContainer", + "type": "JSXExpressionContainer", "start": 20, "end": 30, "loc": { @@ -2914,7 +2914,7 @@ var fbTestFixture = { } ], "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "start": 1, "end": 2, "loc": { @@ -2936,7 +2936,7 @@ var fbTestFixture = { "selfClosing": false }, "closingElement": { - "type": "XJSClosingElement", + "type": "JSXClosingElement", "start": 48, "end": 52, "loc": { @@ -2954,7 +2954,7 @@ var fbTestFixture = { 52 ], "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "start": 50, "end": 51, "loc": { @@ -2976,7 +2976,7 @@ var fbTestFixture = { }, "children": [ { - "type": "XJSElement", + "type": "JSXElement", "start": 31, "end": 48, "loc": { @@ -2994,7 +2994,7 @@ var fbTestFixture = { 48 ], "openingElement": { - "type": "XJSOpeningElement", + "type": "JSXOpeningElement", "start": 31, "end": 36, "loc": { @@ -3013,7 +3013,7 @@ var fbTestFixture = { ], "attributes": [], "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "start": 32, "end": 35, "loc": { @@ -3035,7 +3035,7 @@ var fbTestFixture = { "selfClosing": false }, "closingElement": { - "type": "XJSClosingElement", + "type": "JSXClosingElement", "start": 42, "end": 48, "loc": { @@ -3053,7 +3053,7 @@ var fbTestFixture = { 48 ], "name": { - "type": "XJSIdentifier", + "type": "JSXIdentifier", "start": 44, "end": 47, "loc": { @@ -3075,7 +3075,7 @@ var fbTestFixture = { }, "children": [ { - "type": "XJSExpressionContainer", + "type": "JSXExpressionContainer", "start": 36, "end": 42, "loc": { @@ -3165,16 +3165,16 @@ var fbTestFixture = { start: 0, end: 40, expression: { - type: "XJSElement", + type: "JSXElement", start: 0, end: 38, openingElement: { - type: "XJSOpeningElement", + type: "JSXOpeningElement", start: 0, end: 3, attributes: [], name: { - type: "XJSIdentifier", + type: "JSXIdentifier", start: 1, end: 2, name: "p" @@ -3182,11 +3182,11 @@ var fbTestFixture = { selfClosing: false }, closingElement: { - type: "XJSClosingElement", + type: "JSXClosingElement", start: 34, end: 38, name: { - type: "XJSIdentifier", + type: "JSXIdentifier", start: 36, end: 37, name: "p" @@ -3201,19 +3201,19 @@ var fbTestFixture = { raw: "foo " }, { - type: "XJSElement", + type: "JSXElement", start: 7, end: 30, openingElement: { - type: "XJSOpeningElement", + type: "JSXOpeningElement", start: 7, end: 22, attributes: [{ - type: "XJSAttribute", + type: "JSXAttribute", start: 10, end: 21, name: { - type: "XJSIdentifier", + type: "JSXIdentifier", start: 10, end: 14, name: "href" @@ -3227,7 +3227,7 @@ var fbTestFixture = { } }], name: { - type: "XJSIdentifier", + type: "JSXIdentifier", start: 8, end: 9, name: "a" @@ -3235,11 +3235,11 @@ var fbTestFixture = { selfClosing: false }, closingElement: { - type: "XJSClosingElement", + type: "JSXClosingElement", start: 26, end: 30, name: { - type: "XJSIdentifier", + type: "JSXIdentifier", start: 28, end: 29, name: "a" @@ -3269,16 +3269,16 @@ var fbTestFixture = { start: 0, end: 30, expression: { - type: 'XJSElement', + type: 'JSXElement', start: 0, end: 30, openingElement: { - type: 'XJSOpeningElement', + type: 'JSXOpeningElement', start: 0, end: 5, attributes: [], name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', start: 1, end: 4, name: 'div' @@ -3286,31 +3286,31 @@ var fbTestFixture = { selfClosing: false }, closingElement: { - type: 'XJSClosingElement', + type: 'JSXClosingElement', start: 24, end: 30, name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', start: 26, end: 29, name: 'div' } }, children: [{ - type: 'XJSExpressionContainer', + type: 'JSXExpressionContainer', start: 5, end: 24, expression: { - type: 'XJSElement', + type: 'JSXElement', start: 6, end: 23, openingElement: { - type: 'XJSOpeningElement', + type: 'JSXOpeningElement', start: 6, end: 23, attributes: [ { - type: 'XJSSpreadAttribute', + type: 'JSXSpreadAttribute', start: 11, end: 20, argument: { @@ -3322,7 +3322,7 @@ var fbTestFixture = { } ], name: { - type: 'XJSIdentifier', + type: 'JSXIdentifier', start: 7, end: 10, name: 'div' @@ -3341,16 +3341,16 @@ var fbTestFixture = { start: 0, end: 18, expression: { - type: "XJSElement", + type: "JSXElement", start: 0, end: 18, openingElement: { - type: "XJSOpeningElement", + type: "JSXOpeningElement", start: 0, end: 5, attributes: [], name: { - type: "XJSIdentifier", + type: "JSXIdentifier", start: 1, end: 4, name: "div" @@ -3358,18 +3358,18 @@ var fbTestFixture = { selfClosing: false }, closingElement: { - type: "XJSClosingElement", + type: "JSXClosingElement", start: 12, end: 18, name: { - type: "XJSIdentifier", + type: "JSXIdentifier", start: 14, end: 17, name: "div" } }, children: [{ - type: "XJSExpressionContainer", + type: "JSXExpressionContainer", start: 5, end: 12, expression: { @@ -3407,16 +3407,16 @@ var fbTestFixture = { start: 0, end: 16, expression: { - type: "XJSElement", + type: "JSXElement", start: 0, end: 16, openingElement: { - type: "XJSOpeningElement", + type: "JSXOpeningElement", start: 0, end: 5, attributes: [], name: { - type: "XJSIdentifier", + type: "JSXIdentifier", start: 1, end: 4, name: "div" @@ -3424,11 +3424,11 @@ var fbTestFixture = { selfClosing: false }, closingElement: { - type: "XJSClosingElement", + type: "JSXClosingElement", start: 10, end: 16, name: { - type: "XJSIdentifier", + type: "JSXIdentifier", start: 12, end: 15, name: "div" @@ -3459,7 +3459,8 @@ for (var ns in fbTestFixture) { }, { ecmaVersion: 6, locations: true, - ranges: true + ranges: true, + loose: false }); } } diff --git a/test/tests.js b/test/tests.js index f6df2edc5e..172eb1fb87 100644 --- a/test/tests.js +++ b/test/tests.js @@ -26687,6 +26687,22 @@ test("a.in / b", { ] }); +// A number of slash-disambiguation corner cases +test("return {} / 2", {}, {allowReturnOutsideFunction: true}); +test("return\n{}\n/foo/", {}, {allowReturnOutsideFunction: true}); +test("+{} / 2", {}); +test("{}\n/foo/", {}); +test("x++\n{}\n/foo/", {}); +test("{{}\n/foo/}", {}); +test("while (1) /foo/", {}); +test("(1) / 2", {}); +test("({a: [1]}+[]) / 2", {}); +test("{[1]}\n/foo/", {}); +test("switch(a) { case 1: {}\n/foo/ }", {}); +test("({1: {} / 2})", {}); +test("+x++ / 2", {}); +test("foo.in\n{}\n/foo/", {}); + test("{}/=/", { type: "Program", body: [ @@ -28757,6 +28773,7 @@ var tokTypes = acorn.tokTypes; test('var x = (1 + 2)', {}, { locations: true, + loose: false, onToken: [ { type: tokTypes._var, diff --git a/util/walk.js b/util/walk.js index 117f56f93c..da7ca9f7e1 100644 --- a/util/walk.js +++ b/util/walk.js @@ -285,7 +285,15 @@ c(node.object, st, "Expression"); if (node.computed) c(node.property, st, "Expression"); }; - base.Identifier = base.Literal = base.ExportDeclaration = base.ImportDeclaration = ignore; + base.ExportDeclaration = function (node, st, c) { + c(node.declaration, st); + }; + base.ImportDeclaration = function (node, st, c) { + node.specifiers.forEach(function (specifier) { + c(specifier, st); + }); + }; + base.ImportSpecifier = base.ImportBatchSpecifier = base.Identifier = base.Literal = ignore; base.TaggedTemplateExpression = function(node, st, c) { c(node.tag, st, "Expression");