Spread attribute support and small optimizations.
Conflicts: acorn.js docs/acorn.html package.json test/tests.js
This commit is contained in:
parent
fe6cafa141
commit
35c8f104bd
@ -1,5 +1,7 @@
|
||||
# Acorn (JSX edition)
|
||||
|
||||
[](https://travis-ci.org/RReverser/acorn)
|
||||
|
||||
This is modification of [Acorn][acorn] - a tiny, fast JavaScript parser, written completely in JavaScript.
|
||||
|
||||
It was forked to create experimental, alternative, faster [React.js JSX][jsx] parser by integrating pieces
|
||||
|
||||
236
acorn.js
236
acorn.js
@ -351,6 +351,7 @@
|
||||
var _comma = {type: ",", beforeExpr: true}, _semi = {type: ";", beforeExpr: true};
|
||||
var _colon = {type: ":", beforeExpr: true}, _dot = {type: "."}, _ellipsis = {type: "..."}, _question = {type: "?", beforeExpr: true};
|
||||
var _arrow = {type: "=>", beforeExpr: true}, _bquote = {type: "`"}, _dollarBraceL = {type: "${", beforeExpr: true};
|
||||
var _ltSlash = {type: "</"};
|
||||
|
||||
// Operators. These carry several kinds of properties to help the
|
||||
// parser use them properly (the presence of these properties is
|
||||
@ -385,6 +386,9 @@
|
||||
// '*' may be multiply or have special meaning in ES6
|
||||
var _star = {binop: 10, beforeExpr: true};
|
||||
|
||||
// '<', '>' may be relational or have special meaning in JSX
|
||||
var _lt = {binop: 7, beforeExpr: true}, _gt = {binop: 7, beforeExpr: true};
|
||||
|
||||
// Provide access to the token types for external users of the
|
||||
// tokenizer.
|
||||
|
||||
@ -544,14 +548,14 @@
|
||||
tokEnd = tokPos;
|
||||
if (options.locations) tokEndLoc = new Position;
|
||||
tokType = type;
|
||||
if (shouldSkipSpace !== false && !(inXJSTag && val === '>') && !(inXJSChild && tokType !== _braceL)) {
|
||||
if (shouldSkipSpace !== false && !(inXJSTag && type === _gt) && !(inXJSChild && tokType !== _braceL)) {
|
||||
skipSpace();
|
||||
}
|
||||
tokVal = val;
|
||||
tokRegexpAllowed = type.beforeExpr;
|
||||
if (options.onToken) {
|
||||
options.onToken(getCurrentToken());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function skipBlockComment() {
|
||||
@ -712,9 +716,16 @@
|
||||
skipSpace();
|
||||
return readToken();
|
||||
}
|
||||
if (next === 61)
|
||||
if (next === 61) {
|
||||
size = input.charCodeAt(tokPos + 2) === 61 ? 3 : 2;
|
||||
return finishOp(_relational, size);
|
||||
return finishOp(_relational, size);
|
||||
}
|
||||
if (next === 47) {
|
||||
// '</', beginning of JSX closing element
|
||||
size = 2;
|
||||
return finishOp(_ltSlash, size);
|
||||
}
|
||||
return finishOp(code === 60 ? _lt : _gt, size);
|
||||
}
|
||||
|
||||
function readToken_eq_excl(code) { // '=!', '=>'
|
||||
@ -732,34 +743,34 @@
|
||||
function getTemplateToken(code) {
|
||||
// '`' and '${' have special meanings, but they should follow
|
||||
// string (can be empty)
|
||||
if (tokType === _string) {
|
||||
if (code === 96) { // '`'
|
||||
++tokPos;
|
||||
return finishToken(_bquote);
|
||||
if (tokType === _string) {
|
||||
if (code === 96) { // '`'
|
||||
++tokPos;
|
||||
return finishToken(_bquote);
|
||||
} else
|
||||
if (code === 36 && input.charCodeAt(tokPos + 1) === 123) { // '${'
|
||||
tokPos += 2;
|
||||
return finishToken(_dollarBraceL);
|
||||
if (code === 36 && input.charCodeAt(tokPos + 1) === 123) { // '${'
|
||||
tokPos += 2;
|
||||
return finishToken(_dollarBraceL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (code === 125) { // '}'
|
||||
++tokPos;
|
||||
return finishToken(_braceR, undefined, false);
|
||||
}
|
||||
|
||||
// anything else is considered string literal
|
||||
// anything else is considered string literal
|
||||
return readTmplString();
|
||||
}
|
||||
}
|
||||
|
||||
function getTokenFromCode(code) {
|
||||
switch (code) {
|
||||
// The interpretation of a dot depends on whether it is followed
|
||||
// by a digit or another two dots.
|
||||
switch(code) {
|
||||
// The interpretation of a dot depends on whether it is followed
|
||||
// by a digit or another two dots.
|
||||
case 46: // '.'
|
||||
return readToken_dot();
|
||||
|
||||
// Punctuation tokens.
|
||||
// Punctuation tokens.
|
||||
case 40: ++tokPos; return finishToken(_parenL);
|
||||
case 41: ++tokPos; return finishToken(_parenR);
|
||||
case 59: ++tokPos; return finishToken(_semi);
|
||||
@ -784,12 +795,12 @@
|
||||
if (next === 111 || next === 79) return readRadixNumber(8); // '0o', '0O' - octal number
|
||||
if (next === 98 || next === 66) return readRadixNumber(2); // '0b', '0B' - binary number
|
||||
}
|
||||
// Anything else beginning with a digit is an integer, octal
|
||||
// number, or float.
|
||||
// Anything else beginning with a digit is an integer, octal
|
||||
// number, or float.
|
||||
case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // 1-9
|
||||
return readNumber(false);
|
||||
|
||||
// Quotes produce strings.
|
||||
// Quotes produce strings.
|
||||
case 34: case 39: // '"', "'"
|
||||
return inXJSTag ? readXJSStringLiteral() : readString(code);
|
||||
|
||||
@ -993,9 +1004,9 @@
|
||||
++tokPos;
|
||||
if (newline.test(String.fromCharCode(ch))) {
|
||||
raise(tokStart, "Unterminated string constant");
|
||||
}
|
||||
}
|
||||
out += String.fromCharCode(ch); // '\'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1011,15 +1022,15 @@
|
||||
} else {
|
||||
++tokPos;
|
||||
if (newline.test(String.fromCharCode(ch))) {
|
||||
if (ch === 13 && input.charCodeAt(tokPos) === 10) {
|
||||
++tokPos;
|
||||
ch = 10;
|
||||
}
|
||||
if (options.locations) {
|
||||
++tokCurLine;
|
||||
tokLineStart = tokPos;
|
||||
}
|
||||
}
|
||||
if (ch === 13 && input.charCodeAt(tokPos) === 10) {
|
||||
++tokPos;
|
||||
ch = 10;
|
||||
}
|
||||
if (options.locations) {
|
||||
++tokCurLine;
|
||||
tokLineStart = tokPos;
|
||||
}
|
||||
}
|
||||
out += String.fromCharCode(ch); // '\'
|
||||
}
|
||||
}
|
||||
@ -1611,7 +1622,7 @@
|
||||
function has(obj, propName) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, propName);
|
||||
}
|
||||
// Convert existing expression atom to assignable pattern
|
||||
// Convert existing expression atom to assignable pattern
|
||||
// if possible.
|
||||
|
||||
function toAssignable(node, allowSpread, checkType) {
|
||||
@ -1746,8 +1757,8 @@
|
||||
break;
|
||||
|
||||
default:
|
||||
raise(expr.start, "Assigning to rvalue");
|
||||
}
|
||||
raise(expr.start, "Assigning to rvalue");
|
||||
}
|
||||
}
|
||||
|
||||
// ### Statement parsing
|
||||
@ -2304,28 +2315,28 @@
|
||||
if (options.ecmaVersion >= 6 && tokType === _for) {
|
||||
val = parseComprehension(startNode(), 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)) {
|
||||
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(startNode(), 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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val.start = tokStart1;
|
||||
val.end = lastEnd;
|
||||
if (options.locations) {
|
||||
@ -2343,7 +2354,7 @@
|
||||
// check whether this is array comprehension or regular array
|
||||
if (options.ecmaVersion >= 6 && tokType === _for) {
|
||||
return parseComprehension(node, false);
|
||||
}
|
||||
}
|
||||
node.elements = parseExprList(_bracketR, true, true);
|
||||
return finishNode(node, "ArrayExpression");
|
||||
|
||||
@ -2367,10 +2378,8 @@
|
||||
case _bquote:
|
||||
return parseTemplate();
|
||||
|
||||
case _relational:
|
||||
if (tokVal === '<') {
|
||||
return parseXJSElement();
|
||||
}
|
||||
case _lt:
|
||||
return parseXJSElement();
|
||||
|
||||
default:
|
||||
unexpected();
|
||||
@ -2467,9 +2476,9 @@
|
||||
|
||||
checkPropClash(prop, propHash);
|
||||
node.properties.push(finishNode(prop, "Property"));
|
||||
}
|
||||
}
|
||||
return finishNode(node, "ObjectExpression");
|
||||
}
|
||||
}
|
||||
|
||||
function parsePropertyName(prop) {
|
||||
if (options.ecmaVersion >= 6) {
|
||||
@ -2494,7 +2503,7 @@
|
||||
node.defaults = [];
|
||||
node.rest = null;
|
||||
node.generator = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse a function declaration or literal (depending on the
|
||||
@ -2602,11 +2611,11 @@
|
||||
node.body = parseExpression(true);
|
||||
node.expression = true;
|
||||
} else {
|
||||
// Start a new scope with regard to labels and the `inFunction`
|
||||
// flag (restore them to their old value afterwards).
|
||||
// Start a new scope with regard to labels and the `inFunction`
|
||||
// flag (restore them to their old value afterwards).
|
||||
var oldInFunc = inFunction, oldInGen = inGenerator, oldLabels = labels;
|
||||
inFunction = true; inGenerator = node.generator; labels = [];
|
||||
node.body = parseBlock(true);
|
||||
node.body = parseBlock(true);
|
||||
node.expression = false;
|
||||
inFunction = oldInFunc; inGenerator = oldInGen; labels = oldLabels;
|
||||
}
|
||||
@ -2640,9 +2649,9 @@
|
||||
if (tokType === _name && tokVal === "static") {
|
||||
next();
|
||||
method['static'] = true;
|
||||
} else {
|
||||
} else {
|
||||
method['static'] = false;
|
||||
}
|
||||
}
|
||||
var isGenerator = eat(_star);
|
||||
parsePropertyName(method);
|
||||
if (tokType === _name && !method.computed && method.key.type === "Identifier" &&
|
||||
@ -2851,8 +2860,8 @@
|
||||
node.delegate = false;
|
||||
node.argument = null;
|
||||
} else {
|
||||
node.delegate = eat(_star);
|
||||
node.argument = parseExpression(true);
|
||||
node.delegate = eat(_star);
|
||||
node.argument = parseExpression(true);
|
||||
}
|
||||
return finishNode(node, "YieldExpression");
|
||||
}
|
||||
@ -2972,24 +2981,27 @@
|
||||
// Parses any type of JSX attribute value.
|
||||
|
||||
function parseXJSAttributeValue() {
|
||||
var node;
|
||||
if (tokType === _braceL) {
|
||||
node = parseXJSExpressionContainer();
|
||||
if (node.expression.type === "XJSEmptyExpression") {
|
||||
raise(
|
||||
node.start,
|
||||
'XJS attributes must only be assigned a non-empty ' +
|
||||
'expression'
|
||||
);
|
||||
}
|
||||
} else if (tokVal === '<') {
|
||||
node = parseXJSElement();
|
||||
} else if (tokType === _xjsText) {
|
||||
node = parseExprAtom();
|
||||
} else {
|
||||
raise(tokStart, "XJS value should be either an expression or a quoted XJS text");
|
||||
switch (tokType) {
|
||||
case _braceL:
|
||||
var node = parseXJSExpressionContainer();
|
||||
if (node.expression.type === "XJSEmptyExpression") {
|
||||
raise(
|
||||
node.start,
|
||||
'XJS attributes must only be assigned a non-empty ' +
|
||||
'expression'
|
||||
);
|
||||
}
|
||||
return node;
|
||||
|
||||
case _lt:
|
||||
return parseXJSElement();
|
||||
|
||||
case _xjsText:
|
||||
return parseExprAtom();
|
||||
|
||||
default:
|
||||
raise(tokStart, "XJS value should be either an expression or a quoted XJS text");
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
// XJSEmptyExpression is unique type since it doesn't actually parse anything,
|
||||
@ -3022,28 +3034,54 @@
|
||||
var origInXJSTag = inXJSTag;
|
||||
inXJSTag = false;
|
||||
|
||||
expect(_braceL);
|
||||
|
||||
next();
|
||||
node.expression = tokType === _braceR ? parseXJSEmptyExpression() : parseExpression();
|
||||
|
||||
inXJSTag = origInXJSTag;
|
||||
|
||||
expect(_braceR);
|
||||
|
||||
return finishNode(node, "XJSExpressionContainer");
|
||||
}
|
||||
|
||||
// Parses following JSX attribute name-value pair.
|
||||
|
||||
function parseXJSAttribute() {
|
||||
var node = startNode();
|
||||
if (tokType === _braceL) {
|
||||
var tokStart1 = tokStart, tokStartLoc1 = tokStartLoc;
|
||||
|
||||
var origInXJSTag = inXJSTag;
|
||||
inXJSTag = false;
|
||||
|
||||
next();
|
||||
var node = parseSpread();
|
||||
|
||||
inXJSTag = origInXJSTag;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
var node = startNode();
|
||||
node.name = parseXJSAttributeName();
|
||||
|
||||
// HTML empty attribute
|
||||
if (tokVal === "=") {
|
||||
if (tokType === _eq) {
|
||||
next();
|
||||
node.value = parseXJSAttributeValue();
|
||||
} else {
|
||||
node.value = null;
|
||||
}
|
||||
|
||||
return finishNode(node, "XJSAttribute");
|
||||
@ -3074,11 +3112,11 @@
|
||||
inXJSChild = false;
|
||||
inXJSTag = true;
|
||||
|
||||
expectChar('<');
|
||||
next();
|
||||
|
||||
node.name = parseXJSElementName();
|
||||
|
||||
while (tokType !== _eof && tokType !== _slash && tokVal !== '>') {
|
||||
while (tokType !== _eof && tokType !== _slash && tokType !== _gt) {
|
||||
attributes.push(parseXJSAttribute());
|
||||
}
|
||||
|
||||
@ -3091,7 +3129,7 @@
|
||||
inXJSChild = true;
|
||||
}
|
||||
|
||||
expectChar('>');
|
||||
expect(_gt);
|
||||
|
||||
return finishNode(node, "XJSOpeningElement");
|
||||
}
|
||||
@ -3105,8 +3143,7 @@
|
||||
inXJSChild = false;
|
||||
inXJSTag = true;
|
||||
tokRegexpAllowed = false;
|
||||
expectChar('<');
|
||||
expect(_slash);
|
||||
expect(_ltSlash);
|
||||
node.name = parseXJSElementName();
|
||||
skipSpace();
|
||||
// A valid token is expected after >, so parser needs to know
|
||||
@ -3114,7 +3151,7 @@
|
||||
inXJSChild = origInXJSChild;
|
||||
inXJSTag = origInXJSTag;
|
||||
tokRegexpAllowed = false;
|
||||
expectChar('>');
|
||||
expect(_gt);
|
||||
return finishNode(node, "XJSClosingElement");
|
||||
}
|
||||
|
||||
@ -3127,14 +3164,15 @@
|
||||
|
||||
var origInXJSChild = inXJSChild;
|
||||
var openingElement = parseXJSOpeningElement();
|
||||
var closingElement = null;
|
||||
|
||||
if (!openingElement.selfClosing) {
|
||||
while (tokType !== _eof && !(tokVal === '<' && nextChar() === '/')) {
|
||||
while (tokType !== _eof && tokType !== _ltSlash) {
|
||||
inXJSChild = true;
|
||||
children.push(parseXJSChild());
|
||||
}
|
||||
inXJSChild = origInXJSChild;
|
||||
var closingElement = parseXJSClosingElement();
|
||||
closingElement = parseXJSClosingElement();
|
||||
if (getQualifiedXJSName(closingElement.name) !== getQualifiedXJSName(openingElement.name)) {
|
||||
raise(
|
||||
closingElement.start,
|
||||
@ -3152,7 +3190,7 @@
|
||||
// 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 && tokVal === '<') {
|
||||
if (!origInXJSChild && tokType === _lt) {
|
||||
raise(tokStart, "Adjacent XJS elements must be wrapped in an enclosing tag");
|
||||
}
|
||||
|
||||
|
||||
2034
docs/acorn.html
2034
docs/acorn.html
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
"name": "acorn-jsx",
|
||||
"description": "Alternative React JSX parser",
|
||||
"main": "acorn.js",
|
||||
"version": "0.7.1",
|
||||
"version": "0.7.1-1",
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "Marijn Haverbeke",
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
var driver = require("./driver.js");
|
||||
require("./tests.js");
|
||||
require("./tests-harmony.js");
|
||||
require("./tests-jsx.js");
|
||||
|
||||
var testsRun = 0, failed = 0;
|
||||
function report(state, code, message) {
|
||||
|
||||
3801
test/tests-jsx.js
3801
test/tests-jsx.js
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,6 @@ if (typeof exports != "undefined") {
|
||||
var testFail = require("./driver.js").testFail;
|
||||
var testAssert = require("./driver.js").testAssert;
|
||||
var acorn = require("..");
|
||||
require("./tests-jsx.js");
|
||||
}
|
||||
|
||||
test("this\n", {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user