Merge branch 'upstream' into jsx

Conflicts:
	acorn.js
	test/run.js
This commit is contained in:
Ingvar Stepanyan 2014-11-13 19:25:00 +02:00
commit 42d21f5064
8 changed files with 1212 additions and 1986 deletions

7
.editorconfig Normal file
View File

@ -0,0 +1,7 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true

120
acorn.js
View File

@ -43,8 +43,9 @@
input = String(inpt); inputLen = input.length;
setOptions(opts);
initTokenState();
var startPos = options.locations ? [tokPos, new Position] : tokPos;
initParserState();
return parseTopLevel(options.program);
return parseTopLevel(options.program || startNodeAt(startPos));
};
// A second optional argument can be given to further configure
@ -135,9 +136,9 @@
};
function setOptions(opts) {
options = opts || {};
for (var opt in defaultOptions) if (!has(options, opt))
options[opt] = defaultOptions[opt];
options = {};
for (var opt in defaultOptions)
options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt];
sourceFile = options.sourceFile || null;
if (isArray(options.onToken)) {
var tokens = options.onToken;
@ -214,6 +215,7 @@
input = String(inpt); inputLen = input.length;
setOptions(opts);
initTokenState();
skipSpace();
function getToken(forceRegexp) {
lastEnd = tokEnd;
@ -234,6 +236,10 @@
tokRegexpAllowed = reAllowed;
skipSpace();
};
getToken.noRegexp = function() {
tokRegexpAllowed = false;
};
getToken.options = options;
return getToken;
};
@ -308,6 +314,7 @@
if (options.locations) lastEndLoc = new Position;
inFunction = inGenerator = strict = false;
labels = [];
skipSpace();
readToken();
}
@ -406,8 +413,9 @@
var _bracketL = {type: "[", beforeExpr: true}, _bracketR = {type: "]"}, _braceL = {type: "{", beforeExpr: true};
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: "."}, _ellipsis = {type: "..."}, _question = {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 _ellipsis = {type: "...", prefix: true, beforeExpr: true};
var _ltSlash = {type: "</"};
// Operators. These carry several kinds of properties to help the
@ -453,8 +461,8 @@
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,
xjsName: _xjsName, xjsText: _xjsText};
arrow: _arrow, bquote: _bquote, dollarBraceL: _dollarBraceL, star: _star,
assign: _assign, xjsName: _xjsName, xjsText: _xjsText};
for (var kw in keywordTypes) exports.tokTypes["_" + kw] = keywordTypes[kw];
// This is a trick taken from Esprima. It turns out that, on
@ -600,7 +608,6 @@
tokRegexpAllowed = true;
metParenL = 0;
inTemplate = inXJSChild = inXJSTag = false;
skipSpace();
}
// Called at the end of every token. Sets `tokEnd`, `tokVal`, and
@ -937,6 +944,10 @@
finishToken(type, str, shouldSkipSpace);
}
var regexpUnicodeSupport = false;
try { new RegExp("\uffff", "u"); regexpUnicodeSupport = true; }
catch(e) {}
// Parse a regular expression. Some context-awareness is necessary,
// since a '/' inside a '[]' set does not end the expression.
@ -959,14 +970,36 @@
// Need to use `readWord1` because '\uXXXX' sequences are allowed
// here (don't ask).
var mods = readWord1();
if (mods && !/^[gmsiy]*$/.test(mods)) raise(start, "Invalid regular expression flag");
var tmp = content;
if (mods) {
var validFlags = /^[gmsiy]*$/;
if (options.ecmaVersion >= 6) validFlags = /^[gmsiyu]*$/;
if (!validFlags.test(mods)) raise(start, "Invalid regular expression flag");
if (mods.indexOf('u') >= 0 && !regexpUnicodeSupport) {
// Replace each astral symbol and every Unicode code point
// escape sequence that represents such a symbol with a single
// ASCII symbol to avoid throwing on regular expressions that
// are only valid in combination with the `/u` flag.
tmp = tmp
.replace(/\\u\{([0-9a-fA-F]{5,6})\}/g, "x")
.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "x");
}
}
// Detect invalid regular expressions.
try {
var value = new RegExp(content, mods);
new RegExp(tmp);
} catch (e) {
if (e instanceof SyntaxError) raise(start, "Error parsing regular expression: " + e.message);
raise(e);
}
return finishToken(_regexp, value);
// Get a regular expression object for this pattern-flag pair, or `null` in
// case the current environment doesn't support the flags it uses.
try {
var value = new RegExp(content, mods);
} catch (err) {
value = null;
}
return finishToken(_regexp, {pattern: content, flags: mods, value: value});
}
// Read an integer in the given radix. Return null if zero digits
@ -1764,7 +1797,7 @@
// strict mode, init properties are also not allowed to be repeated.
function checkPropClash(prop, propHash) {
if (prop.computed) return;
if (options.ecmaVersion >= 6) return;
var key = prop.key, name;
switch (key.type) {
case "Identifier": name = key.name; break;
@ -1834,15 +1867,19 @@
// `program` argument. If present, the statements will be appended
// to its body instead of creating a new node.
function parseTopLevel(program) {
var node = program || startNode(), first = true;
if (!program) node.body = [];
function parseTopLevel(node) {
var first = true;
if (!node.body) node.body = [];
while (tokType !== _eof) {
var stmt = parseStatement();
node.body.push(stmt);
if (first && isUseStrict(stmt)) setStrict(true);
first = false;
}
lastStart = tokStart;
lastEnd = tokEnd;
lastEndLoc = tokEndLoc;
return finishNode(node, "Program");
}
@ -2279,9 +2316,14 @@
function parseMaybeUnary() {
if (tokType.prefix) {
var node = startNode(), update = tokType.isUpdate;
var node = startNode(), update = tokType.isUpdate, nodeType;
if (tokType === _ellipsis) {
nodeType = "SpreadElement";
} else {
nodeType = update ? "UpdateExpression" : "UnaryExpression";
node.operator = tokVal;
node.prefix = true;
}
tokRegexpAllowed = true;
next();
node.argument = parseMaybeUnary();
@ -2289,7 +2331,7 @@
else if (strict && node.operator === "delete" &&
node.argument.type === "Identifier")
raise(node.start, "Deleting local variable in strict mode");
return finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
return finishNode(node, nodeType);
}
var start = storeCurrentPos();
var expr = parseExprSubscripts();
@ -2362,7 +2404,15 @@
}
return id;
case _num: case _string: case _regexp: case _xjsText:
case _regexp:
var node = startNode();
node.regex = {pattern: tokVal.pattern, flags: tokVal.flags};
node.value = tokVal.value;
node.raw = input.slice(tokStart, tokEnd);
next();
return finishNode(node, "Literal");
case _num: case _string: case _xjsText:
var node = startNode();
node.value = tokVal;
node.raw = input.slice(tokStart, tokEnd);
@ -2378,10 +2428,10 @@
case _parenL:
var start = storeCurrentPos();
var tokStartLoc1 = tokStartLoc, tokStart1 = tokStart, val, exprList;
var val, exprList;
next();
// check whether this is generator comprehension or regular expression
if (options.ecmaVersion >= 6 && tokType === _for) {
if (options.ecmaVersion >= 7 && tokType === _for) {
val = parseComprehension(startNodeAt(start), true);
} else {
var oldParenL = ++metParenL;
@ -2418,7 +2468,7 @@
var node = startNode();
next();
// check whether this is array comprehension or regular array
if (options.ecmaVersion >= 6 && tokType === _for) {
if (options.ecmaVersion >= 7 && tokType === _for) {
return parseComprehension(node, false);
}
node.elements = parseExprList(_bracketR, true, true);
@ -2438,9 +2488,6 @@
case _new:
return parseNew();
case _ellipsis:
return parseSpread();
case _bquote:
return parseTemplate();
@ -2466,15 +2513,6 @@
return finishNode(node, "NewExpression");
}
// Parse spread element '...expr'
function parseSpread() {
var node = startNode();
next();
node.argument = parseExpression(true);
return finishNode(node, "SpreadElement");
}
// Parse template expression.
function parseTemplate() {
@ -2715,7 +2753,7 @@
next();
node.id = tokType === _name ? parseIdent() : isStatement ? unexpected() : null;
node.superClass = eat(_extends) ? parseExpression() : null;
var classBody = startNode(), methodHash = {}, staticMethodHash = {};
var classBody = startNode();
classBody.body = [];
expect(_braceL);
while (!eat(_braceR)) {
@ -2728,7 +2766,7 @@
}
var isGenerator = eat(_star);
parsePropertyName(method);
if (tokType === _name && !method.computed && method.key.type === "Identifier" &&
if (tokType !== _parenL && !method.computed && method.key.type === "Identifier" &&
(method.key.name === "get" || method.key.name === "set")) {
if (isGenerator) unexpected();
method.kind = method.key.name;
@ -2737,7 +2775,6 @@
method.kind = "";
}
method.value = parseMethod(isGenerator);
checkPropClash(method, method['static'] ? staticMethodHash : methodHash);
classBody.body.push(finishNode(method, "MethodDefinition"));
eat(_semi);
}
@ -2809,8 +2846,8 @@
node.source = null;
semicolon();
} else {
// export * from '...'
// export { x, y as z } [from '...']
// export * from '...';
// export { x, y as z } [from '...'];
var isBatch = tokType === _star;
node.declaration = null;
node['default'] = false;
@ -2822,6 +2859,7 @@
if (isBatch) unexpected();
node.source = null;
}
semicolon();
}
return finishNode(node, "ExportDeclaration");
}
@ -2845,7 +2883,7 @@
} else first = false;
var node = startNode();
node.id = parseIdent();
node.id = parseIdent(tokType === _default);
if (tokType === _name && tokVal === "as") {
next();
node.name = parseIdent(true);
@ -2876,6 +2914,7 @@
// (it doesn't support mixed default + named yet)
node.kind = node.specifiers[0]['default'] ? "default" : "named";
}
semicolon();
return finishNode(node, "ImportDeclaration");
}
@ -3134,7 +3173,8 @@
inXJSTag = false;
next();
var node = parseSpread();
if (tokType !== _ellipsis) unexpected();
var node = parseMaybeUnary();
inXJSTag = origInXJSTag;

View File

@ -40,14 +40,14 @@
var options, input, fetchToken, context;
acorn.defaultOptions.tabSize = 4;
exports.parse_dammit = function(inpt, opts) {
if (!opts) opts = {};
input = String(inpt);
if (/^#!.*/.test(input)) input = "//" + input.slice(2);
options = opts;
if (!opts.tabSize) opts.tabSize = 4;
fetchToken = acorn.tokenize(input, opts);
options = fetchToken.options;
sourceFile = options.sourceFile || null;
context = [];
nextLineStart = 0;
@ -59,15 +59,14 @@
var lastEnd, token = {start: 0, end: 0}, ahead = [];
var curLineStart, nextLineStart, curIndent, lastEndLoc, sourceFile;
function next() {
function next(forceRegexp) {
lastEnd = token.end;
if (options.locations)
lastEndLoc = token.endLoc;
if (forceRegexp)
ahead.length = 0;
if (ahead.length)
token = ahead.shift();
else
token = readToken();
token = ahead.shift() || readToken(forceRegexp);
if (token.start >= nextLineStart) {
while (token.start >= nextLineStart) {
@ -78,10 +77,16 @@
}
}
function readToken() {
function readToken(forceRegexp) {
for (;;) {
try {
return fetchToken();
var tok = fetchToken(forceRegexp);
if (tok.type === tt.dot && input.substr(tok.end, 1) === '.') {
tok = fetchToken();
tok.start--;
tok.type = tt.ellipsis;
}
return tok;
} catch(e) {
if (!(e instanceof SyntaxError)) throw e;
@ -256,6 +261,8 @@
if (token.type === type) {
next();
return true;
} else {
return false;
}
}
@ -263,7 +270,7 @@
return (token.type === tt.eof || token.type === tt.braceR || newline.test(input.slice(lastEnd, token.start)));
}
function semicolon() {
eat(tt.semi);
return eat(tt.semi);
}
function expect(type) {
@ -279,26 +286,45 @@
}
function checkLVal(expr) {
if (expr.type === "Identifier" || expr.type === "MemberExpression") return expr;
return dummyIdent();
if (!expr) return expr;
switch (expr.type) {
case "Identifier":
case "MemberExpression":
case "ObjectPattern":
case "ArrayPattern":
case "SpreadElement":
return expr;
default:
return dummyIdent();
}
}
function parseTopLevel() {
var node = startNode();
var node = startNodeAt(options.locations ? [0, acorn.getLineInfo(input, 0)] : 0);
node.body = [];
while (token.type !== tt.eof) node.body.push(parseStatement());
lastEnd = token.end;
lastEndLoc = token.endLoc;
return finishNode(node, "Program");
}
function parseStatement() {
if (token.type === tt.slash || token.type === tt.assign && token.value === "/=")
next(true);
var starttype = token.type, node = startNode();
switch (starttype) {
case tt._break: case tt._continue:
next();
var isBreak = starttype === tt._break;
node.label = token.type === tt.name ? parseIdent() : null;
semicolon();
if (semicolon() || canInsertSemicolon()) {
node.label = null;
} else {
node.label = token.type === tt.name ? parseIdent() : null;
semicolon();
}
return finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
case tt._debugger:
@ -318,16 +344,17 @@
pushCx();
expect(tt.parenL);
if (token.type === tt.semi) return parseFor(node, null);
if (token.type === tt._var) {
var init = startNode();
next();
parseVar(init, true);
if (init.declarations.length === 1 && eat(tt._in))
if (token.type === tt._var || token.type === tt._let) {
var init = parseVar(true);
if (init.declarations.length === 1 && (token.type === tt._in || token.type === tt.name && token.value === "of")) {
return parseForIn(node, init);
}
return parseFor(node, init);
}
var init = parseExpression(false, true);
if (eat(tt._in)) {return parseForIn(node, checkLVal(init));}
if (token.type === tt._in || token.type === tt.name && token.value === "of") {
return parseForIn(node, checkLVal(init));
}
return parseFor(node, init);
case tt._function:
@ -404,9 +431,9 @@
return finishNode(node, "TryStatement");
case tt._var:
next();
node = parseVar(node);
return node;
case tt._let:
case tt._const:
return parseVar();
case tt._while:
next();
@ -427,6 +454,15 @@
next();
return finishNode(node, "EmptyStatement");
case tt._class:
return parseObj(true, true);
case tt._import:
return parseImport();
case tt._export:
return parseExport();
default:
var expr = parseExpression();
if (isDummy(expr)) {
@ -470,24 +506,27 @@
}
function parseForIn(node, init) {
var type = token.type === tt._in ? "ForInStatement" : "ForOfStatement";
next();
node.left = init;
node.right = parseExpression();
popCx();
expect(tt.parenR);
node.body = parseStatement();
return finishNode(node, "ForInStatement");
return finishNode(node, type);
}
function parseVar(node, noIn) {
function parseVar(noIn) {
var node = startNode();
node.kind = token.type.keyword;
next();
node.declarations = [];
node.kind = "var";
while (token.type === tt.name) {
do {
var decl = startNode();
decl.id = parseIdent();
decl.id = options.ecmaVersion >= 6 ? toAssignable(parseExprAtom()) : parseIdent();
decl.init = eat(tt.eq) ? parseExpression(true, noIn) : null;
node.declarations.push(finishNode(decl, "VariableDeclarator"));
if (!eat(tt.comma)) break;
}
} while (eat(tt.comma));
if (!node.declarations.length) {
var decl = startNode();
decl.id = dummyIdent();
@ -524,7 +563,7 @@
if (token.type.isAssign) {
var node = startNodeAt(start);
node.operator = token.value;
node.left = checkLVal(left);
node.left = token.type === tt.eq ? toAssignable(left) : checkLVal(left);
next();
node.right = parseMaybeAssign(noIn);
return finishNode(node, "AssignmentExpression");
@ -575,13 +614,20 @@
function parseMaybeUnary(noIn) {
if (token.type.prefix) {
var node = startNode(), update = token.type.isUpdate;
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;
}
node.operator = token.value;
node.prefix = true;
next();
node.argument = parseMaybeUnary(noIn);
if (update) node.argument = checkLVal(node.argument);
return finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
return finishNode(node, nodeType);
}
var start = storeCurrentPos();
var expr = parseExprSubscripts();
@ -647,9 +693,22 @@
var node = startNode();
next();
return finishNode(node, "ThisExpression");
case tt.name:
return parseIdent();
case tt.num: case tt.string: case tt.regexp:
var start = storeCurrentPos();
var id = parseIdent();
return eat(tt.arrow) ? parseArrowExpression(startNodeAt(start), [id]) : id;
case tt.regexp:
var node = startNode();
var val = token.value;
node.regex = {pattern: val.pattern, flags: val.flags};
node.value = val.value;
node.raw = input.slice(token.start, token.end);
next();
return finishNode(node, "Literal");
case tt.num: case tt.string:
var node = startNode();
node.value = token.value;
node.raw = input.slice(token.start, token.end);
@ -664,20 +723,32 @@
return finishNode(node, "Literal");
case tt.parenL:
var start = storeCurrentPos();
next();
var val = parseExpression();
expect(tt.parenR);
if (eat(tt.arrow)) {
return parseArrowExpression(startNodeAt(start), val.expressions || (isDummy(val) ? [] : [val]));
}
if (options.preserveParens) {
var par = startNodeAt(start);
par.expression = val;
val = finishNode(par, "ParenthesizedExpression");
}
return val;
case tt.bracketL:
var node = startNode();
pushCx();
node.elements = parseExprList(tt.bracketR);
node.elements = parseExprList(tt.bracketR, true);
return finishNode(node, "ArrayExpression");
case tt.braceL:
return parseObj();
case tt._class:
return parseObj(true);
case tt._function:
var node = startNode();
next();
@ -686,6 +757,18 @@
case tt._new:
return parseNew();
case tt._yield:
var node = startNode();
next();
if (semicolon() || canInsertSemicolon()) {
node.delegate = false;
node.argument = null;
} else {
node.delegate = eat(tt.star);
node.argument = parseExpression(true);
}
return finishNode(node, "YieldExpression");
default:
return dummyIdent();
}
@ -705,41 +788,92 @@
return finishNode(node, "NewExpression");
}
function parseObj() {
function parseObj(isClass, isStatement) {
var node = startNode();
node.properties = [];
if (isClass) {
next();
if (token.type === tt.name) node.id = parseIdent();
else if (isStatement) node.id = dummyIdent();
node.superClass = eat(tt._extends) ? parseExpression() : null;
node.body = startNode();
node.body.body = [];
} else {
node.properties = [];
}
pushCx();
var indent = curIndent + 1, line = curLineStart;
next();
eat(tt.braceL);
if (curIndent + 1 < indent) { indent = curIndent; line = curLineStart; }
while (!closes(tt.braceR, indent, line)) {
var name = parsePropertyName();
if (!name) { if (isDummy(parseExpression(true))) next(); eat(tt.comma); continue; }
var prop = startNode();
prop.key = name;
if (eat(tt.colon)) {
prop.value = parseExpression(true);
var prop = startNode(), isGenerator;
if (options.ecmaVersion >= 6) {
if (isClass) {
if (prop['static'] = (token.type === tt.name && token.value === "static")) next();
} else {
prop.method = false;
prop.shorthand = false;
}
isGenerator = eat(tt.star);
}
parsePropertyName(prop);
if (isDummy(prop.key)) { if (isDummy(parseExpression(true))) next(); eat(tt.comma); continue; }
if (!isClass && eat(tt.colon)) {
prop.kind = "init";
prop.value = parseExpression(true);
} else if (options.ecmaVersion >= 6 && (token.type === tt.parenL || token.type === tt.braceL)) {
if (isClass) {
prop.kind = "";
} else {
prop.kind = "init";
prop.method = true;
}
prop.value = parseMethod(isGenerator);
} else if (options.ecmaVersion >= 5 && prop.key.type === "Identifier" &&
(prop.key.name === "get" || prop.key.name === "set")) {
prop.kind = prop.key.name;
prop.key = parsePropertyName() || dummyIdent();
prop.value = parseFunction(startNode(), false);
parsePropertyName(prop);
prop.value = parseMethod(false);
} else if (isClass) {
prop.kind = "";
prop.value = parseMethod(isGenerator);
} else {
prop.value = dummyIdent();
prop.kind = "init";
prop.value = options.ecmaVersion >= 6 ? prop.key : dummyIdent();
prop.shorthand = true;
}
node.properties.push(finishNode(prop, "Property"));
eat(tt.comma);
if (isClass) {
node.body.body.push(finishNode(prop, "MethodDefinition"));
semicolon();
} else {
node.properties.push(finishNode(prop, "Property"));
eat(tt.comma);
}
}
popCx();
eat(tt.braceR);
return finishNode(node, "ObjectExpression");
if (isClass) {
semicolon();
finishNode(node.body, "ClassBody");
return finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
} else {
return finishNode(node, "ObjectExpression");
}
}
function parsePropertyName() {
if (token.type === tt.num || token.type === tt.string) return parseExprAtom();
if (token.type === tt.name || token.type.keyword) return parseIdent();
function parsePropertyName(prop) {
if (options.ecmaVersion >= 6) {
if (eat(tt.bracketL)) {
prop.computed = true;
prop.key = parseExpression();
expect(tt.bracketR);
return;
} else {
prop.computed = false;
}
}
var key = (token.type === tt.num || token.type === tt.string) ? parseExprAtom() : parseIdent();
prop.key = key || dummyIdent();
}
function parsePropertyAccessor() {
@ -749,32 +883,212 @@
function parseIdent() {
var node = startNode();
node.name = token.type === tt.name ? token.value : token.type.keyword;
fetchToken.noRegexp();
next();
return finishNode(node, "Identifier");
}
function initFunction(node) {
node.id = null;
node.params = [];
if (options.ecmaVersion >= 6) {
node.defaults = [];
node.rest = null;
node.generator = false;
node.expression = false;
}
}
// Convert existing expression atom to assignable pattern
// if possible.
function toAssignable(node) {
if (options.ecmaVersion >= 6 && node) {
switch (node.type) {
case "ObjectExpression":
node.type = "ObjectPattern";
var props = node.properties;
for (var i = 0; i < props.length; i++) {
props[i].value = toAssignable(props[i].value);
}
break;
case "ArrayExpression":
node.type = "ArrayPattern";
var elms = node.elements;
for (var i = 0; i < elms.length; i++) {
elms[i] = toAssignable(elms[i]);
}
break;
case "SpreadElement":
node.argument = toAssignable(node.argument);
break;
}
}
return checkLVal(node);
}
function parseFunctionParams(node, params) {
var defaults = [], hasDefaults = false;
if (!params) {
pushCx();
params = parseExprList(tt.parenR);
}
for (var i = 0; i < params.length; i++) {
var param = params[i], defValue = null;
if (param.type === "AssignmentExpression") {
defValue = param.right;
param = param.left;
}
param = toAssignable(param);
if (param.type === "SpreadElement") {
param = param.argument;
if (i === params.length - 1) {
node.rest = param;
continue;
}
}
node.params.push(param);
defaults.push(defValue);
if (defValue) hasDefaults = true;
}
if (hasDefaults) node.defaults = defaults;
}
function parseFunction(node, isStatement) {
initFunction(node);
if (options.ecmaVersion >= 6) {
node.generator = eat(tt.star);
}
if (token.type === tt.name) node.id = parseIdent();
else if (isStatement) node.id = dummyIdent();
else node.id = null;
node.params = [];
pushCx();
expect(tt.parenL);
while (token.type == tt.name) {
node.params.push(parseIdent());
eat(tt.comma);
}
popCx();
eat(tt.parenR);
parseFunctionParams(node);
node.body = parseBlock();
return finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
}
function parseExprList(close) {
function parseMethod(isGenerator) {
var node = startNode();
initFunction(node);
parseFunctionParams(node);
node.generator = isGenerator || false;
node.expression = options.ecmaVersion >= 6 && token.type !== tt.braceL;
node.body = node.expression ? parseExpression(true) : parseBlock();
return finishNode(node, "FunctionExpression");
}
function parseArrowExpression(node, params) {
initFunction(node);
parseFunctionParams(node, params);
node.expression = token.type !== tt.braceL;
node.body = node.expression ? parseExpression(true) : parseBlock();
return finishNode(node, "ArrowFunctionExpression");
}
function parseExport() {
var node = startNode();
next();
node['default'] = eat(tt._default);
node.specifiers = node.source = null;
if (node['default']) {
node.declaration = parseExpression();
semicolon();
} else if (token.type.keyword) {
node.declaration = parseStatement();
} else {
node.declaration = null;
parseSpecifierList(node, "Export");
}
semicolon();
return finishNode(node, "ExportDeclaration");
}
function parseImport() {
var node = startNode();
next();
if (token.type === tt.string) {
node.specifiers = [];
node.source = parseExprAtom();
node.kind = '';
} else {
if (token.type === tt.name && token.value !== "from") {
var elt = startNode();
elt.id = parseIdent();
elt.name = null;
elt['default'] = true;
finishNode(elt, "ImportSpecifier");
eat(tt.comma);
}
parseSpecifierList(node, "Import");
var specs = node.specifiers;
for (var i = 0; i < specs.length; i++) specs[i]['default'] = false;
if (elt) node.specifiers.unshift(elt);
}
semicolon();
return finishNode(node, "ImportDeclaration");
}
function parseSpecifierList(node, prefix) {
var elts = node.specifiers = [];
if (token.type === tt.star) {
var elt = startNode();
next();
if (token.type === tt.name && token.value === "as") {
next();
elt.name = parseIdent();
}
elts.push(finishNode(elt, prefix + "BatchSpecifier"));
} else {
var indent = curIndent, line = curLineStart, continuedLine = nextLineStart;
pushCx();
eat(tt.braceL);
if (curLineStart > continuedLine) continuedLine = curLineStart;
while (!closes(tt.braceR, indent + (curLineStart <= continuedLine ? 1 : 0), line)) {
var elt = startNode();
if (token.type === tt.star) {
next();
if (token.type === tt.name && token.value === "as") {
next();
elt.name = parseIdent();
}
finishNode(elt, prefix + "BatchSpecifier");
} else {
if (token.type === tt.name && token.value === "from") break;
elt.id = parseIdent();
if (token.type === tt.name && token.value === "as") {
next();
elt.name = parseIdent();
} else {
elt.name = null;
}
finishNode(elt, prefix + "Specifier");
}
elts.push(elt);
eat(tt.comma);
}
eat(tt.braceR);
popCx();
}
if (token.type === tt.name && token.value === "from") {
next();
node.source = parseExprAtom();
} else {
node.source = null;
}
}
function parseExprList(close, allowEmpty) {
var indent = curIndent, line = curLineStart, elts = [], continuedLine = nextLineStart;
next(); // Opening bracket
if (curLineStart > continuedLine) continuedLine = curLineStart;
while (!closes(close, indent + (curLineStart <= continuedLine ? 1 : 0), line)) {
if (eat(tt.comma)) {
elts.push(allowEmpty ? null : dummyIdent());
continue;
}
var elt = parseExpression(true);
if (isDummy(elt)) {
if (closes(close, indent, line)) break;
@ -782,7 +1096,7 @@
} else {
elts.push(elt);
}
while (eat(tt.comma)) {}
eat(tt.comma);
}
popCx();
eat(close);

View File

@ -1,9 +1,8 @@
(function(exports) {
var tests = [];
var acorn = typeof require == "undefined" ? window.acorn : require("../acorn.js");
exports.test = function(code, ast, options, comments) {
tests.push({code: code, ast: ast, options: options, comments: comments});
exports.test = function(code, ast, options) {
tests.push({code: code, ast: ast, options: options});
};
exports.testFail = function(code, message, options) {
tests.push({code: code, error: message, options: options});
@ -12,30 +11,29 @@
tests.push({code: code, assert: assert, options: options});
};
exports.runTests = function(callback) {
var comments;
function onComment(block, text, start, end, startLoc, endLoc) {
comments.push({
block: block,
text: text,
start: start,
end: end,
startLoc: { line: startLoc.line, column: startLoc.column },
endLoc: { line: endLoc.line, column: endLoc.column }
});
}
var opts = {locations: true, onComment: onComment};
exports.runTests = function(config, callback) {
var parse = config.parse;
for (var i = 0; i < tests.length; ++i) {
var test = tests[i];
if (config.filter && !config.filter(test)) continue;
try {
comments = [];
if (test.options && !test.options.onComment) test.options.onComment = onComment;
var ast = acorn.parse(test.code, test.options || opts);
if (test.error) callback("fail", test.code,
"Expected error message: " + test.error + "\nBut parsing succeeded.");
var testOpts = test.options || {locations: true};
var expected = {};
if (expected.onComment = testOpts.onComment) {
testOpts.onComment = []
}
if (expected.onToken = testOpts.onToken) {
testOpts.onToken = [];
}
var ast = parse(test.code, testOpts);
if (test.error) {
if (config.loose) {
callback("ok", test.code);
} else {
callback("fail", test.code, "Expected error message: " + test.error + "\nBut parsing succeeded.");
}
}
else if (test.assert) {
var error = test.assert(ast);
if (error) callback("fail", test.code,
@ -43,12 +41,21 @@
else callback("ok", test.code);
} else {
var mis = misMatch(test.ast, ast);
if (!mis && test.comments) mis = misMatch(test.comments, comments);
for (var name in expected) {
if (mis) break;
if (expected[name]) {
mis = misMatch(expected[name], testOpts[name]);
testOpts[name] = expected[name];
}
}
if (mis) callback("fail", test.code, mis);
else callback("ok", test.code);
}
} catch(e) {
if (test.error && e instanceof SyntaxError) {
if (!(e instanceof SyntaxError)) {
throw e;
}
if (test.error) {
if (e.message == test.error) callback("ok", test.code);
else callback("fail", test.code,
"Expected error message: " + test.error + "\nGot error message: " + e.message);

View File

@ -3,24 +3,13 @@
<meta charset="utf-8">
<title>Acorn test suite</title>
<script src="../acorn.js"></script>
<script src="../acorn_loose.js"></script>
<script src="driver.js"></script>
<script src="tests-jsx.js" charset="utf-8"></script>
<script src="tests.js" charset="utf-8"></script>
<script src="tests-harmony.js" charset="utf-8"></script>
</head>
<script>
var testsRun = 0, failed = 0;
function report(state, code, message) {
if (state != "ok") {++failed; console.log(code, message);}
++testsRun;
}
window.onload = function(){
var t0 = +new Date;
runTests(report);
var out = testsRun + " tests run in " + (+new Date - t0) + "ms\n";
if (failed) out += failed + " failures.\n";
else out += "All passed.\n";
document.body.appendChild(document.createElement("pre")).appendChild(document.createTextNode(out));
};
</script>
<body>
<ul id="log"></ul>
<script src="run.js"></script>
</body>

View File

@ -1,23 +1,111 @@
var driver = require("./driver.js");
require("./tests.js");
require("./tests-harmony.js");
require("./tests-jsx.js");
(function() {
var driver;
if (typeof require !== "undefined") {
driver = require("./driver.js");
require("./tests.js");
require("./tests-harmony.js");
require("./tests-jsx.js");
} else {
driver = window;
}
var htmlLog = typeof document === "object" && document.getElementById('log');
var htmlGroup = htmlLog;
function group(name) {
if (htmlGroup) {
var parentGroup = htmlGroup;
htmlGroup = document.createElement("ul");
var item = document.createElement("li");
item.textContent = name;
item.appendChild(htmlGroup);
parentGroup.appendChild(item);
}
if (typeof console === "object" && console.group) {
console.group(name);
}
}
function groupEnd() {
if (htmlGroup) {
htmlGroup = htmlGroup.parentElement.parentElement;
}
if (typeof console === "object" && console.groupEnd) {
console.groupEnd(name);
}
}
function log(title, message) {
if (htmlGroup) {
var elem = document.createElement("li");
elem.innerHTML = "<b>" + title + "</b> " + message;
htmlGroup.appendChild(elem);
}
if (typeof console === "object") console.log(title, message);
}
var stats, modes = {
Normal: {
config: {
parse: (typeof require === "undefined" ? window.acorn : require("../acorn.js")).parse
}
},
Loose: {
config: {
parse: (typeof require === "undefined" ? window.acorn : require("../acorn_loose")).parse_dammit,
loose: true,
filter: function (test) {
if (/`/.test(test.code)) return false; // FIXME remove this when the loose parse supports template strings
var opts = test.options || {};
if (opts.loose === false) return false;
return (opts.ecmaVersion || 5) <= 6;
}
}
}
};
var testsRun = 0, failed = 0;
function report(state, code, message) {
if (state != "ok") {++failed; console.log(code, message);}
++testsRun;
if (state != "ok") {++stats.failed; log(code, message);}
++stats.testsRun;
}
var t0 = +new Date;
driver.runTests(report);
console.log(testsRun + " tests run in " + (+new Date - t0) + "ms");
group("Errors");
if (failed) {
console.log(failed + " failures.");
for (var name in modes) {
group(name);
var mode = modes[name];
stats = mode.stats = {testsRun: 0, failed: 0};
var t0 = +new Date;
driver.runTests(mode.config, report);
mode.stats.duration = +new Date - t0;
groupEnd();
}
groupEnd();
function outputStats(name, stats) {
log(name + ":", stats.testsRun + " tests run in " + stats.duration + "ms; " +
(stats.failed ? stats.failed + " failures." : "all passed."));
}
var total = {testsRun: 0, failed: 0, duration: 0};
group("Stats");
for (var name in modes) {
var stats = modes[name].stats;
outputStats(name + " parser", stats);
for (var key in stats) total[key] += stats[key];
}
outputStats("Total", total);
groupEnd();
if (total.failed && typeof process === "object") {
process.stdout.write("", function() {
process.exit(1);
});
} else {
console.log("All passed.");
}
}
})();

File diff suppressed because it is too large Load Diff

View File

@ -43,8 +43,8 @@ test("this\n", {
column: 0
},
end: {
line: 1,
column: 4
line: 2,
column: 0
}
}
});
@ -86,8 +86,8 @@ test("null\n", {
column: 0
},
end: {
line: 1,
column: 4
line: 2,
column: 0
}
}
});
@ -125,16 +125,70 @@ test("\n 42\n\n", {
],
loc: {
start: {
line: 2,
column: 4
line: 1,
column: 0
},
end: {
line: 2,
column: 6
line: 4,
column: 0
}
}
});
test("/foobar/", {
type: "Program",
body: [
{
type: "ExpressionStatement",
expression: {
type: "Literal",
value: /foobar/,
regex: {
pattern: "foobar",
flags: ""
},
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 1,
column: 8
}
}
}
}
]
});
test("/[a-z]/g", {
type: "Program",
body: [
{
type: "ExpressionStatement",
expression: {
type: "Literal",
value: /[a-z]/,
regex: {
pattern: "[a-z]",
flags: "g"
},
loc: {
start: {
line: 1,
column: 0
},
end: {
line: 1,
column: 8
}
}
}
}
]
});
test("(1 + 2 ) * 3", {
type: "Program",
body: [
@ -4862,7 +4916,7 @@ test("/* block comment */ 42", {
loc: {
start: {
line: 1,
column: 20
column: 0
},
end: {
line: 1,
@ -4909,7 +4963,7 @@ test("42 /*The*/ /*Answer*/", {
},
end: {
line: 1,
column: 2
column: 21
}
}
});
@ -4952,7 +5006,7 @@ test("42 /*the*/ /*answer*/", {
},
end: {
line: 1,
column: 2
column: 21
}
}
});
@ -4990,8 +5044,8 @@ test("/* multiline\ncomment\nshould\nbe\nignored */ 42", {
],
loc: {
start: {
line: 5,
column: 11
line: 1,
column: 0
},
end: {
line: 5,
@ -5033,8 +5087,8 @@ test("/*a\r\nb*/ 42", {
],
loc: {
start: {
line: 2,
column: 4
line: 1,
column: 0
},
end: {
line: 2,
@ -5076,8 +5130,8 @@ test("/*a\rb*/ 42", {
],
loc: {
start: {
line: 2,
column: 4
line: 1,
column: 0
},
end: {
line: 2,
@ -5119,8 +5173,8 @@ test("/*a\nb*/ 42", {
],
loc: {
start: {
line: 2,
column: 4
line: 1,
column: 0
},
end: {
line: 2,
@ -5162,8 +5216,8 @@ test("/*a\nc*/ 42", {
],
loc: {
start: {
line: 2,
column: 4
line: 1,
column: 0
},
end: {
line: 2,
@ -5205,7 +5259,7 @@ test("// line comment\n42", {
],
loc: {
start: {
line: 2,
line: 1,
column: 0
},
end: {
@ -5253,7 +5307,7 @@ test("42 // line comment", {
},
end: {
line: 1,
column: 2
column: 18
}
}
});
@ -5291,7 +5345,7 @@ test("// Hello, world!\n42", {
],
loc: {
start: {
line: 2,
line: 1,
column: 0
},
end: {
@ -5306,7 +5360,7 @@ test("// Hello, world!\n", {
body: [],
loc: {
start: {
line: 2,
line: 1,
column: 0
},
end: {
@ -5321,7 +5375,7 @@ test("// Hallo, world!\n", {
body: [],
loc: {
start: {
line: 2,
line: 1,
column: 0
},
end: {
@ -5364,7 +5418,7 @@ test("//\n42", {
],
loc: {
start: {
line: 2,
line: 1,
column: 0
},
end: {
@ -5380,7 +5434,7 @@ test("//", {
loc: {
start: {
line: 1,
column: 2
column: 0
},
end: {
line: 1,
@ -5395,7 +5449,7 @@ test("// ", {
loc: {
start: {
line: 1,
column: 3
column: 0
},
end: {
line: 1,
@ -5438,7 +5492,7 @@ test("/**/42", {
loc: {
start: {
line: 1,
column: 4
column: 0
},
end: {
line: 1,
@ -5480,7 +5534,7 @@ test("// Hello, world!\n\n// Another hello\n42", {
],
loc: {
start: {
line: 4,
line: 1,
column: 0
},
end: {
@ -28612,191 +28666,171 @@ testFail("for(x of a);", "Unexpected token (1:6)");
testFail("for(var x of a);", "Unexpected token (1:10)");
// Assertion Tests
(function() {
var actualComments = [],
expectedComments = [
" Bear class",
" Whatever",
[" 1",
" 2",
" 3"
].join('\n'),
"stuff"
];
testAssert(
function TestComments() {
// Bear class
function Bear(x,y,z) {
this.position = [x||0,y||0,z||0]
}
Bear.prototype.roar = function(message) {
return 'RAWWW: ' + message; // Whatever
};
function Cat() {
/* 1
2
3*/
}
Cat.prototype.roar = function(message) {
return 'MEOOWW: ' + /*stuff*/ message;
};
}.toString().replace(/\r\n/g, '\n'),
function assert(ast) {
if (actualComments.length !== expectedComments.length) {
return JSON.stringify(actualComments) + " !== " + JSON.stringify(expectedComments);
} else {
for (var i=0, n=actualComments.length; i < n; i++) {
if (actualComments[i] !== expectedComments[i])
return JSON.stringify(actualComments[i]) + ' !== ' + JSON.stringify(expectedComments[i]);
}
}
},
{
onComment: function(isMultiline, text) {
actualComments.push(text);
}
test(function TestComments() {
// Bear class
function Bear(x,y,z) {
this.position = [x||0,y||0,z||0]
}
);
})();
Bear.prototype.roar = function(message) {
return 'RAWWW: ' + message; // Whatever
};
function Cat() {
/* 1
2
3*/
}
Cat.prototype.roar = function(message) {
return 'MEOOWW: ' + /*stuff*/ message;
};
}.toString().replace(/\r\n/g, '\n'), {}, {
onComment: [
{type: "Line", value: " Bear class"},
{type: "Line", value: " Whatever"},
{type: "Block", value: [
" 1",
" 2",
" 3"
].join('\n')},
{type: "Block", value: "stuff"}
]
});
test("<!--\n;", {
type: "Program",
body: [
{
type: "EmptyStatement"
body: [{
type: "EmptyStatement"
}]
});
test("\nfunction plop() {\n'use strict';\n/* Comment */\n}", {}, {
locations: true,
onComment: [{
type: "Block",
value: " Comment ",
loc: {
start: { line: 4, column: 0 },
end: { line: 4, column: 13 }
}
]
}
);
}]
});
(function() {
test("\nfunction plop() {\n'use strict';\n/* Comment */\n}", {}, {locations: true},
[{
block: true,
text: " Comment ",
startLoc: { line: 4, column: 0 },
endLoc: { line: 4, column: 13 }
}]);
test("// line comment", {}, {
locations: true,
onComment: [{
type: "Line",
value: " line comment",
loc: {
start: { line: 1, column: 0 },
end: { line: 1, column: 15 }
}
}]
});
test("// line comment", {}, {locations: true},
[{
block: false,
text: " line comment",
startLoc: { line: 1, column: 0 },
endLoc: { line: 1, column: 15 }
}]);
test("<!-- HTML comment", {}, {
locations: true,
onComment: [{
type: "Line",
value: " HTML comment",
loc: {
start: { line: 1, column: 0 },
end: { line: 1, column: 17 }
}
}]
});
test("<!-- HTML comment", {}, {locations: true},
[{
block: false,
text: " HTML comment",
startLoc: { line: 1, column: 0 },
endLoc: { line: 1, column: 17 }
}]);
test(";\n--> HTML comment", {}, {
locations: true,
onComment: [{
type: "Line",
value: " HTML comment",
loc: {
start: { line: 2, column: 0 },
end: { line: 2, column: 16 }
}
}]
});
test(";\n--> HTML comment", {}, {locations: true},
[{
block: false,
text: " HTML comment",
startLoc: { line: 2, column: 0 },
endLoc: { line: 2, column: 16 }
}]);
})();
var tokTypes = acorn.tokTypes;
(function() {
var tokTypes = acorn.tokTypes;
var actualTokens = [],
expectedTokens = [
{
type: tokTypes._var,
value: "var",
loc: {
start: {line: 1, column: 0},
end: {line: 1, column: 3}
}
},
{
type: tokTypes.name,
value: "x",
loc: {
start: {line: 1, column: 4},
end: {line: 1, column: 5}
}
},
{
type: tokTypes.eq,
value: "=",
loc: {
start: {line: 1, column: 6},
end: {line: 1, column: 7}
}
},
{
type: tokTypes.parenL,
value: undefined,
loc: {
start: {line: 1, column: 8},
end: {line: 1, column: 9}
}
},
{
type: tokTypes.num,
value: 1,
loc: {
start: {line: 1, column: 9},
end: {line: 1, column: 10}
}
},
{
type: {binop: 9, prefix: true, beforeExpr: true},
value: "+",
loc: {
start: {line: 1, column: 11},
end: {line: 1, column: 12}
}
},
{
type: tokTypes.num,
value: 2,
loc: {
start: {line: 1, column: 13},
end: {line: 1, column: 14}
}
},
{
type: tokTypes.parenR,
value: undefined,
loc: {
start: {line: 1, column: 14},
end: {line: 1, column: 15}
}
},
{
type: tokTypes.eof,
value: undefined,
loc: {
start: {line: 1, column: 15},
end: {line: 1, column: 15}
}
}
];
testAssert('var x = (1 + 2)', function assert(ast) {
if (actualTokens.length !== expectedTokens.length) {
return "Bad token stream length: expected " + expectedTokens.length + ", got " + actualTokens.length;
} else {
for (var i=0, n=actualTokens.length; i < n; i++) {
var mis = misMatch(expectedTokens[i], actualTokens[i]);
if (mis) return mis;
test('var x = (1 + 2)', {}, {
locations: true,
onToken: [
{
type: tokTypes._var,
value: "var",
loc: {
start: {line: 1, column: 0},
end: {line: 1, column: 3}
}
},
{
type: tokTypes.name,
value: "x",
loc: {
start: {line: 1, column: 4},
end: {line: 1, column: 5}
}
},
{
type: tokTypes.eq,
value: "=",
loc: {
start: {line: 1, column: 6},
end: {line: 1, column: 7}
}
},
{
type: tokTypes.parenL,
value: undefined,
loc: {
start: {line: 1, column: 8},
end: {line: 1, column: 9}
}
},
{
type: tokTypes.num,
value: 1,
loc: {
start: {line: 1, column: 9},
end: {line: 1, column: 10}
}
},
{
type: {binop: 9, prefix: true, beforeExpr: true},
value: "+",
loc: {
start: {line: 1, column: 11},
end: {line: 1, column: 12}
}
},
{
type: tokTypes.num,
value: 2,
loc: {
start: {line: 1, column: 13},
end: {line: 1, column: 14}
}
},
{
type: tokTypes.parenR,
value: undefined,
loc: {
start: {line: 1, column: 14},
end: {line: 1, column: 15}
}
},
{
type: tokTypes.eof,
value: undefined,
loc: {
start: {line: 1, column: 15},
end: {line: 1, column: 15}
}
}
}, {
locations: true,
onToken: actualTokens
});
})();
]
});
test("function f(f) { 'use strict'; }", {});