Spread attribute support and small optimizations.

Conflicts:
	acorn.js
	docs/acorn.html
	package.json
	test/tests.js
This commit is contained in:
Ingvar Stepanyan 2014-07-27 02:56:41 +03:00
parent fe6cafa141
commit 35c8f104bd
7 changed files with 3910 additions and 2227 deletions

View File

@ -1,5 +1,7 @@
# Acorn (JSX edition)
[![Build Status](https://travis-ci.org/RReverser/acorn.svg?branch=master)](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

View File

@ -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,7 +548,7 @@
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;
@ -712,10 +716,17 @@
skipSpace();
return readToken();
}
if (next === 61)
if (next === 61) {
size = input.charCodeAt(tokPos + 2) === 61 ? 3 : 2;
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) { // '=!', '=>'
var next = input.charCodeAt(tokPos + 1);
@ -2367,10 +2378,8 @@
case _bquote:
return parseTemplate();
case _relational:
if (tokVal === '<') {
case _lt:
return parseXJSElement();
}
default:
unexpected();
@ -2972,9 +2981,9 @@
// Parses any type of JSX attribute value.
function parseXJSAttributeValue() {
var node;
if (tokType === _braceL) {
node = parseXJSExpressionContainer();
switch (tokType) {
case _braceL:
var node = parseXJSExpressionContainer();
if (node.expression.type === "XJSEmptyExpression") {
raise(
node.start,
@ -2982,14 +2991,17 @@
'expression'
);
}
} else if (tokVal === '<') {
node = parseXJSElement();
} else if (tokType === _xjsText) {
node = parseExprAtom();
} else {
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");
}

File diff suppressed because it is too large Load Diff

View File

@ -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",

View File

@ -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) {

View File

@ -1,11 +1,8 @@
// React JSX tests
if (typeof exports !== "undefined") {
var test = require("./driver.js").test;
}
// Simply taken from esprima-fb/fbtest.js
var fbTestFixture = {
// Taken and adapted from esprima-fb/fbtest.js.
'XJS': {
'<a />': {
type: "ExpressionStatement",
expression: {
@ -29,6 +26,7 @@ var fbTestFixture = {
end: { line: 1, column: 5 }
}
},
closingElement: null,
children: [],
range: [0, 5],
loc: {
@ -103,6 +101,7 @@ var fbTestFixture = {
end: { line: 1, column: 8 }
}
},
value: null,
range: [5, 8],
loc: {
start: { line: 1, column: 5 },
@ -115,6 +114,7 @@ var fbTestFixture = {
end: { line: 1, column: 11 }
}
},
closingElement: null,
children: [],
range: [0, 11],
loc: {
@ -304,6 +304,7 @@ var fbTestFixture = {
end: { line: 1, column: 32 }
}
},
closingElement: null,
children: [],
range: [27, 32],
loc: {
@ -442,6 +443,7 @@ var fbTestFixture = {
end: { line: 1, column: 29 }
}
},
closingElement: null,
children: [],
range: [0, 29],
loc: {
@ -496,6 +498,7 @@ var fbTestFixture = {
}
}
},
closingElement: null,
children: [],
range: [
0,
@ -635,7 +638,7 @@ var fbTestFixture = {
}
},
'<AbC-def\n test="&#x0026;&#38;">\nbar\nbaz\r\n</AbC-def>': {
'<AbC-def\n test="&#x0026;&#38;">\nbar\nbaz\n</AbC-def>': {
type: "ExpressionStatement",
expression: {
type: "XJSElement",
@ -737,8 +740,8 @@ var fbTestFixture = {
type: "XJSIdentifier",
name: "AbC-def",
range: [
44,
51
43,
50
],
loc: {
start: {
@ -752,8 +755,8 @@ var fbTestFixture = {
}
},
range: [
42,
52
41,
51
],
loc: {
start: {
@ -769,11 +772,11 @@ var fbTestFixture = {
children: [
{
type: "Literal",
value: "\nbar\nbaz\r\n",
raw: "\nbar\nbaz\r\n",
value: "\nbar\nbaz\n",
raw: "\nbar\nbaz\n",
range: [
32,
42
41
],
loc: {
start: {
@ -789,7 +792,7 @@ var fbTestFixture = {
],
range: [
0,
52
51
],
loc: {
start: {
@ -804,7 +807,7 @@ var fbTestFixture = {
},
range: [
0,
52
51
],
loc: {
start: {
@ -925,6 +928,7 @@ var fbTestFixture = {
}
}
},
closingElement: null,
children: [],
range: [
10,
@ -980,6 +984,7 @@ var fbTestFixture = {
}
}
},
closingElement: null,
children: [],
range: [
18,
@ -1057,6 +1062,7 @@ var fbTestFixture = {
}
}
},
closingElement: null,
children: [],
range: [
0,
@ -1405,6 +1411,7 @@ var fbTestFixture = {
}
}
},
closingElement: null,
children: [],
range: [
5,
@ -1555,6 +1562,7 @@ var fbTestFixture = {
}
}
},
closingElement: null,
"children": [],
"range": [
16,
@ -1747,6 +1755,7 @@ var fbTestFixture = {
}
}
},
closingElement: null,
"children": [],
"range": [
0,
@ -2018,6 +2027,7 @@ var fbTestFixture = {
end: { line: 1, column: 8 }
}
},
closingElement: null,
children: [],
range: [0, 9],
loc: {
@ -2045,12 +2055,547 @@ var fbTestFixture = {
start: { line: 1, column: 0 },
end: { line: 1, column: 14 }
}
},
'<div {...props} />': {
"type": "ExpressionStatement",
"expression": {
"type": "XJSElement",
"openingElement": {
"type": "XJSOpeningElement",
"name": {
"type": "XJSIdentifier",
"name": "div",
"range": [
1,
4
],
"loc": {
"start": {
"line": 1,
"column": 1
},
"end": {
"line": 1,
"column": 4
}
}
},
"selfClosing": true,
"attributes": [
{
"type": "XJSSpreadAttribute",
"argument": {
"type": "Identifier",
"name": "props",
"range": [
9,
14
],
"loc": {
"start": {
"line": 1,
"column": 9
},
"end": {
"line": 1,
"column": 14
}
}
},
"range": [
5,
15
],
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 15
}
}
}
],
"range": [
0,
18
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 18
}
}
},
closingElement: null,
"children": [],
"range": [
0,
18
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 18
}
}
},
"range": [
0,
18
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 18
}
}
},
'<div {...props} post="attribute" />': {
"type": "ExpressionStatement",
"expression": {
"type": "XJSElement",
"openingElement": {
"type": "XJSOpeningElement",
"name": {
"type": "XJSIdentifier",
"name": "div",
"range": [
1,
4
],
"loc": {
"start": {
"line": 1,
"column": 1
},
"end": {
"line": 1,
"column": 4
}
}
},
"selfClosing": true,
"attributes": [
{
"type": "XJSSpreadAttribute",
"argument": {
"type": "Identifier",
"name": "props",
"range": [
9,
14
],
"loc": {
"start": {
"line": 1,
"column": 9
},
"end": {
"line": 1,
"column": 14
}
}
},
"range": [
5,
15
],
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 15
}
}
},
{
"type": "XJSAttribute",
"name": {
"type": "XJSIdentifier",
"name": "post",
"range": [
16,
20
],
"loc": {
"start": {
"line": 1,
"column": 16
},
"end": {
"line": 1,
"column": 20
}
}
},
"value": {
"type": "Literal",
"value": "attribute",
"raw": "\"attribute\"",
"range": [
21,
32
],
"loc": {
"start": {
"line": 1,
"column": 21
},
"end": {
"line": 1,
"column": 32
}
}
},
"range": [
16,
32
],
"loc": {
"start": {
"line": 1,
"column": 16
},
"end": {
"line": 1,
"column": 32
}
}
}
],
"range": [
0,
35
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 35
}
}
},
closingElement: null,
"children": [],
"range": [
0,
35
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 35
}
}
},
"range": [
0,
35
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 35
}
}
},
'<div pre="leading" pre2="attribute" {...props}></div>': {
"type": "ExpressionStatement",
"expression": {
"type": "XJSElement",
"openingElement": {
"type": "XJSOpeningElement",
"name": {
"type": "XJSIdentifier",
"name": "div",
"range": [
1,
4
],
"loc": {
"start": {
"line": 1,
"column": 1
},
"end": {
"line": 1,
"column": 4
}
}
},
"selfClosing": false,
"attributes": [
{
"type": "XJSAttribute",
"name": {
"type": "XJSIdentifier",
"name": "pre",
"range": [
5,
8
],
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 8
}
}
},
"value": {
"type": "Literal",
"value": "leading",
"raw": "\"leading\"",
"range": [
9,
18
],
"loc": {
"start": {
"line": 1,
"column": 9
},
"end": {
"line": 1,
"column": 18
}
}
},
"range": [
5,
18
],
"loc": {
"start": {
"line": 1,
"column": 5
},
"end": {
"line": 1,
"column": 18
}
}
},
{
"type": "XJSAttribute",
"name": {
"type": "XJSIdentifier",
"name": "pre2",
"range": [
19,
23
],
"loc": {
"start": {
"line": 1,
"column": 19
},
"end": {
"line": 1,
"column": 23
}
}
},
"value": {
"type": "Literal",
"value": "attribute",
"raw": "\"attribute\"",
"range": [
24,
35
],
"loc": {
"start": {
"line": 1,
"column": 24
},
"end": {
"line": 1,
"column": 35
}
}
},
"range": [
19,
35
],
"loc": {
"start": {
"line": 1,
"column": 19
},
"end": {
"line": 1,
"column": 35
}
}
},
{
"type": "XJSSpreadAttribute",
"argument": {
"type": "Identifier",
"name": "props",
"range": [
40,
45
],
"loc": {
"start": {
"line": 1,
"column": 40
},
"end": {
"line": 1,
"column": 45
}
}
},
"range": [
36,
46
],
"loc": {
"start": {
"line": 1,
"column": 36
},
"end": {
"line": 1,
"column": 46
}
}
}
],
"range": [
0,
47
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 47
}
}
},
"closingElement": {
"type": "XJSClosingElement",
"name": {
"type": "XJSIdentifier",
"name": "div",
"range": [
49,
52
],
"loc": {
"start": {
"line": 1,
"column": 49
},
"end": {
"line": 1,
"column": 52
}
}
},
"range": [
47,
53
],
"loc": {
"start": {
"line": 1,
"column": 47
},
"end": {
"line": 1,
"column": 53
}
}
},
"children": [],
"range": [
0,
53
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 53
}
}
},
"range": [
0,
53
],
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 53
}
}
}
}
};
for (var code in fbTestFixture) {
if (typeof exports !== "undefined") {
var test = require("./driver.js").test;
}
for (var code in fbTestFixture.XJS) {
test(code, {
type: 'Program',
body: [fbTestFixture[code]]
}, {locations: true, ranges: true});
body: [fbTestFixture.XJS[code]]
}, {
ecmaVersion: 6,
locations: true,
ranges: true
});
}

View File

@ -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", {