Code style -- less semicolons, more let variables

This commit is contained in:
Marijn Haverbeke 2015-03-20 16:59:11 +01:00
parent 6dd254d999
commit f538706db3
15 changed files with 1966 additions and 1963 deletions

File diff suppressed because it is too large Load Diff

View File

@ -8,42 +8,42 @@
// It starts by sorting the words by length.
function makePredicate(words) {
words = words.split(" ");
var f = "", cats = [];
out: for (var i = 0; i < words.length; ++i) {
for (var j = 0; j < cats.length; ++j)
words = words.split(" ")
let f = "", cats = []
out: for (let i = 0; i < words.length; ++i) {
for (let j = 0; j < cats.length; ++j)
if (cats[j][0].length == words[i].length) {
cats[j].push(words[i]);
continue out;
cats[j].push(words[i])
continue out
}
cats.push([words[i]]);
cats.push([words[i]])
}
function compareTo(arr) {
if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";";
f += "switch(str){";
for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":";
f += "return true}return false;";
if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";"
f += "switch(str){"
for (let i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":"
f += "return true}return false;"
}
// When there are more than three length categories, an outer
// switch first dispatches on the lengths, to save on comparisons.
if (cats.length > 3) {
cats.sort(function(a, b) {return b.length - a.length;});
f += "switch(str.length){";
for (var i = 0; i < cats.length; ++i) {
var cat = cats[i];
f += "case " + cat[0].length + ":";
compareTo(cat);
cats.sort((a, b) => b.length - a.length)
f += "switch(str.length){"
for (let i = 0; i < cats.length; ++i) {
let cat = cats[i]
f += "case " + cat[0].length + ":"
compareTo(cat)
}
f += "}";
f += "}"
// Otherwise, simply generate a flat `switch` statement.
} else {
compareTo(words);
compareTo(words)
}
return new Function("str", f);
return new Function("str", f)
}
// Reserved word lists for various dialects of the language
@ -58,7 +58,7 @@ export const reservedWords = {
// And the keywords
var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this";
var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"
export const keywords = {
5: makePredicate(ecma5AndLessKeywords),
@ -86,44 +86,44 @@ nonASCIIidentifierStartChars = nonASCIIidentifierChars = null
// offset starts at 0x10000, and each pair of numbers represents an
// offset to the next range, and then a size of the range. They were
// generated by tools/generate-identifier-regex.js
var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,17,26,6,37,11,29,3,35,5,7,2,4,43,157,99,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,98,21,11,25,71,55,7,1,65,0,16,3,2,2,2,26,45,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,955,52,76,44,33,24,27,35,42,34,4,0,13,47,15,3,22,0,38,17,2,24,133,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,32,4,287,47,21,1,2,0,185,46,82,47,21,0,60,42,502,63,32,0,449,56,1288,920,104,110,2962,1070,13266,568,8,30,114,29,19,47,17,3,32,20,6,18,881,68,12,0,67,12,16481,1,3071,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,4149,196,1340,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42710,42,4148,12,221,16355,541];
var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,1306,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,52,0,13,2,49,13,16,9,83,11,168,11,6,9,8,2,57,0,2,6,3,1,3,2,10,0,11,1,3,6,4,4,316,19,13,9,214,6,3,8,112,16,16,9,82,12,9,9,535,9,20855,9,135,4,60,6,26,9,1016,45,17,3,19723,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,4305,6,792618,239];
var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,17,26,6,37,11,29,3,35,5,7,2,4,43,157,99,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,98,21,11,25,71,55,7,1,65,0,16,3,2,2,2,26,45,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,955,52,76,44,33,24,27,35,42,34,4,0,13,47,15,3,22,0,38,17,2,24,133,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,32,4,287,47,21,1,2,0,185,46,82,47,21,0,60,42,502,63,32,0,449,56,1288,920,104,110,2962,1070,13266,568,8,30,114,29,19,47,17,3,32,20,6,18,881,68,12,0,67,12,16481,1,3071,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,4149,196,1340,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42710,42,4148,12,221,16355,541]
var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,1306,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,52,0,13,2,49,13,16,9,83,11,168,11,6,9,8,2,57,0,2,6,3,1,3,2,10,0,11,1,3,6,4,4,316,19,13,9,214,6,3,8,112,16,16,9,82,12,9,9,535,9,20855,9,135,4,60,6,26,9,1016,45,17,3,19723,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,4305,6,792618,239]
// This has a complexity linear to the value of the code. The
// assumption is that looking up astral identifier characters is
// rare.
function isInAstralSet(code, set) {
var pos = 0x10000;
for (var i = 0; i < set.length; i += 2) {
pos += set[i];
if (pos > code) return false;
pos += set[i + 1];
if (pos >= code) return true;
let pos = 0x10000
for (let i = 0; i < set.length; i += 2) {
pos += set[i]
if (pos > code) return false
pos += set[i + 1]
if (pos >= code) return true
}
}
// Test whether a given character code starts an identifier.
export function isIdentifierStart(code, astral) {
if (code < 65) return code === 36;
if (code < 91) return true;
if (code < 97) return code === 95;
if (code < 123) return true;
if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
if (astral === false) return false;
return isInAstralSet(code, astralIdentifierStartCodes);
if (code < 65) return code === 36
if (code < 91) return true
if (code < 97) return code === 95
if (code < 123) return true
if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code))
if (astral === false) return false
return isInAstralSet(code, astralIdentifierStartCodes)
}
// Test whether a given character is part of an identifier.
export function isIdentifierChar(code, astral) {
if (code < 48) return code === 36;
if (code < 58) return true;
if (code < 65) return false;
if (code < 91) return true;
if (code < 97) return code === 95;
if (code < 123) return true;
if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
if (astral === false) return false;
return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes);
if (code < 48) return code === 36
if (code < 58) return true
if (code < 65) return false
if (code < 91) return true
if (code < 97) return code === 95
if (code < 123) return true
if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code))
if (astral === false) return false
return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes)
}

View File

@ -51,17 +51,17 @@ const pp = Parser.prototype
// message.
pp.raise = function(pos, message) {
var loc = getLineInfo(this.input, pos);
message += " (" + loc.line + ":" + loc.column + ")";
var err = new SyntaxError(message);
err.pos = pos; err.loc = loc; err.raisedAt = this.pos;
throw err;
};
let loc = getLineInfo(this.input, pos)
message += " (" + loc.line + ":" + loc.column + ")"
let err = new SyntaxError(message)
err.pos = pos; err.loc = loc; err.raisedAt = this.pos
throw err
}
pp.curPosition = function() {
return new Position(this.curLine, this.pos - this.lineStart);
};
return new Position(this.curLine, this.pos - this.lineStart)
}
pp.markPosition = function() {
return this.options.locations ? [this.start, this.startLoc] : this.start;
};
return this.options.locations ? [this.start, this.startLoc] : this.start
}

View File

@ -5,7 +5,7 @@ import {tokTypes as tt} from ".."
const lp = LooseParser.prototype
lp.checkLVal = function(expr) {
if (!expr) return expr;
if (!expr) return expr
switch (expr.type) {
case "Identifier":
case "MemberExpression":
@ -13,400 +13,401 @@ lp.checkLVal = function(expr) {
case "ArrayPattern":
case "RestElement":
case "AssignmentPattern":
return expr;
return expr
default:
return this.dummyIdent();
return this.dummyIdent()
}
};
}
lp.parseExpression = function(noIn) {
var start = this.storeCurrentPos();
var expr = this.parseMaybeAssign(noIn);
let start = this.storeCurrentPos()
let expr = this.parseMaybeAssign(noIn)
if (this.tok.type === tt.comma) {
var node = this.startNodeAt(start);
node.expressions = [expr];
while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn));
return this.finishNode(node, "SequenceExpression");
let node = this.startNodeAt(start)
node.expressions = [expr]
while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn))
return this.finishNode(node, "SequenceExpression")
}
return expr;
};
return expr
}
lp.parseParenExpression = function() {
this.pushCx();
this.expect(tt.parenL);
var val = this.parseExpression();
this.popCx();
this.expect(tt.parenR);
return val;
};
this.pushCx()
this.expect(tt.parenL)
let val = this.parseExpression()
this.popCx()
this.expect(tt.parenR)
return val
}
lp.parseMaybeAssign = function(noIn) {
var start = this.storeCurrentPos();
var left = this.parseMaybeConditional(noIn);
let start = this.storeCurrentPos()
let left = this.parseMaybeConditional(noIn)
if (this.tok.type.isAssign) {
var node = this.startNodeAt(start);
node.operator = this.tok.value;
node.left = this.tok.type === tt.eq ? this.toAssignable(left) : this.checkLVal(left);
this.next();
node.right = this.parseMaybeAssign(noIn);
return this.finishNode(node, "AssignmentExpression");
let node = this.startNodeAt(start)
node.operator = this.tok.value
node.left = this.tok.type === tt.eq ? this.toAssignable(left) : this.checkLVal(left)
this.next()
node.right = this.parseMaybeAssign(noIn)
return this.finishNode(node, "AssignmentExpression")
}
return left;
};
return left
}
lp.parseMaybeConditional = function(noIn) {
var start = this.storeCurrentPos();
var expr = this.parseExprOps(noIn);
let start = this.storeCurrentPos()
let expr = this.parseExprOps(noIn)
if (this.eat(tt.question)) {
var node = this.startNodeAt(start);
node.test = expr;
node.consequent = this.parseMaybeAssign();
node.alternate = this.expect(tt.colon) ? this.parseMaybeAssign(noIn) : this.dummyIdent();
return this.finishNode(node, "ConditionalExpression");
let node = this.startNodeAt(start)
node.test = expr
node.consequent = this.parseMaybeAssign()
node.alternate = this.expect(tt.colon) ? this.parseMaybeAssign(noIn) : this.dummyIdent()
return this.finishNode(node, "ConditionalExpression")
}
return expr;
};
return expr
}
lp.parseExprOps = function(noIn) {
var start = this.storeCurrentPos();
var indent = this.curIndent, line = this.curLineStart;
return this.parseExprOp(this.parseMaybeUnary(noIn), start, -1, noIn, indent, line);
};
let start = this.storeCurrentPos()
let indent = this.curIndent, line = this.curLineStart
return this.parseExprOp(this.parseMaybeUnary(noIn), start, -1, noIn, indent, line)
}
lp.parseExprOp = function(left, start, minPrec, noIn, indent, line) {
if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) return left;
var prec = this.tok.type.binop;
if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) return left
let prec = this.tok.type.binop
if (prec != null && (!noIn || this.tok.type !== tt._in)) {
if (prec > minPrec) {
var node = this.startNodeAt(start);
node.left = left;
node.operator = this.tok.value;
this.next();
let node = this.startNodeAt(start)
node.left = left
node.operator = this.tok.value
this.next()
if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) {
node.right = this.dummyIdent();
node.right = this.dummyIdent()
} else {
var rightStart = this.storeCurrentPos();
node.right = this.parseExprOp(this.parseMaybeUnary(noIn), rightStart, prec, noIn, indent, line);
let rightStart = this.storeCurrentPos()
node.right = this.parseExprOp(this.parseMaybeUnary(noIn), rightStart, prec, noIn, indent, line)
}
this.finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression");
return this.parseExprOp(node, start, minPrec, noIn, indent, line);
this.finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression")
return this.parseExprOp(node, start, minPrec, noIn, indent, line)
}
}
return left;
};
return left
}
lp.parseMaybeUnary = function(noIn) {
if (this.tok.type.prefix) {
var node = this.startNode(), update = this.tok.type === tt.incDec;
node.operator = this.tok.value;
node.prefix = true;
this.next();
node.argument = this.parseMaybeUnary(noIn);
if (update) node.argument = this.checkLVal(node.argument);
return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
let node = this.startNode(), update = this.tok.type === tt.incDec
node.operator = this.tok.value
node.prefix = true
this.next()
node.argument = this.parseMaybeUnary(noIn)
if (update) node.argument = this.checkLVal(node.argument)
return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression")
} else if (this.tok.type === tt.ellipsis) {
var node = this.startNode();
this.next();
node.argument = this.parseMaybeUnary(noIn);
return this.finishNode(node, "SpreadElement");
let node = this.startNode()
this.next()
node.argument = this.parseMaybeUnary(noIn)
return this.finishNode(node, "SpreadElement")
}
var start = this.storeCurrentPos();
var expr = this.parseExprSubscripts();
let start = this.storeCurrentPos()
let expr = this.parseExprSubscripts()
while (this.tok.type.postfix && !this.canInsertSemicolon()) {
var node = this.startNodeAt(start);
node.operator = this.tok.value;
node.prefix = false;
node.argument = this.checkLVal(expr);
this.next();
expr = this.finishNode(node, "UpdateExpression");
let node = this.startNodeAt(start)
node.operator = this.tok.value
node.prefix = false
node.argument = this.checkLVal(expr)
this.next()
expr = this.finishNode(node, "UpdateExpression")
}
return expr;
};
return expr
}
lp.parseExprSubscripts = function() {
var start = this.storeCurrentPos();
return this.parseSubscripts(this.parseExprAtom(), start, false, this.curIndent, this.curLineStart);
};
let start = this.storeCurrentPos()
return this.parseSubscripts(this.parseExprAtom(), start, false, this.curIndent, this.curLineStart)
}
lp.parseSubscripts = function(base, start, noCalls, startIndent, line) {
for (;;) {
if (this.curLineStart != line && this.curIndent <= startIndent && this.tokenStartsLine()) {
if (this.tok.type == tt.dot && this.curIndent == startIndent)
--startIndent;
--startIndent
else
return base;
return base
}
if (this.eat(tt.dot)) {
var node = this.startNodeAt(start);
node.object = base;
let node = this.startNodeAt(start)
node.object = base
if (this.curLineStart != line && this.curIndent <= startIndent && this.tokenStartsLine())
node.property = this.dummyIdent();
node.property = this.dummyIdent()
else
node.property = this.parsePropertyAccessor() || this.dummyIdent();
node.computed = false;
base = this.finishNode(node, "MemberExpression");
node.property = this.parsePropertyAccessor() || this.dummyIdent()
node.computed = false
base = this.finishNode(node, "MemberExpression")
} else if (this.tok.type == tt.bracketL) {
this.pushCx();
this.next();
var node = this.startNodeAt(start);
node.object = base;
node.property = this.parseExpression();
node.computed = true;
this.popCx();
this.expect(tt.bracketR);
base = this.finishNode(node, "MemberExpression");
this.pushCx()
this.next()
let node = this.startNodeAt(start)
node.object = base
node.property = this.parseExpression()
node.computed = true
this.popCx()
this.expect(tt.bracketR)
base = this.finishNode(node, "MemberExpression")
} else if (!noCalls && this.tok.type == tt.parenL) {
this.pushCx();
var node = this.startNodeAt(start);
node.callee = base;
node.arguments = this.parseExprList(tt.parenR);
base = this.finishNode(node, "CallExpression");
this.pushCx()
let node = this.startNodeAt(start)
node.callee = base
node.arguments = this.parseExprList(tt.parenR)
base = this.finishNode(node, "CallExpression")
} else if (this.tok.type == tt.backQuote) {
var node = this.startNodeAt(start);
node.tag = base;
node.quasi = this.parseTemplate();
base = this.finishNode(node, "TaggedTemplateExpression");
let node = this.startNodeAt(start)
node.tag = base
node.quasi = this.parseTemplate()
base = this.finishNode(node, "TaggedTemplateExpression")
} else {
return base;
return base
}
}
};
}
lp.parseExprAtom = function() {
let node
switch (this.tok.type) {
case tt._this:
case tt._super:
var type = this.tok.type === tt._this ? "ThisExpression" : "SuperExpression";
var node = this.startNode();
this.next();
return this.finishNode(node, type);
let type = this.tok.type === tt._this ? "ThisExpression" : "SuperExpression"
node = this.startNode()
this.next()
return this.finishNode(node, type)
case tt.name:
var start = this.storeCurrentPos();
var id = this.parseIdent();
return this.eat(tt.arrow) ? this.parseArrowExpression(this.startNodeAt(start), [id]) : id;
let start = this.storeCurrentPos()
let id = this.parseIdent()
return this.eat(tt.arrow) ? this.parseArrowExpression(this.startNodeAt(start), [id]) : id
case tt.regexp:
var node = this.startNode();
var val = this.tok.value;
node.regex = {pattern: val.pattern, flags: val.flags};
node.value = val.value;
node.raw = this.input.slice(this.tok.start, this.tok.end);
this.next();
return this.finishNode(node, "Literal");
node = this.startNode()
let val = this.tok.value
node.regex = {pattern: val.pattern, flags: val.flags}
node.value = val.value
node.raw = this.input.slice(this.tok.start, this.tok.end)
this.next()
return this.finishNode(node, "Literal")
case tt.num: case tt.string:
var node = this.startNode();
node.value = this.tok.value;
node.raw = this.input.slice(this.tok.start, this.tok.end);
this.next();
return this.finishNode(node, "Literal");
node = this.startNode()
node.value = this.tok.value
node.raw = this.input.slice(this.tok.start, this.tok.end)
this.next()
return this.finishNode(node, "Literal")
case tt._null: case tt._true: case tt._false:
var node = this.startNode();
node.value = this.tok.type === tt._null ? null : this.tok.type === tt._true;
node.raw = this.tok.type.keyword;
this.next();
return this.finishNode(node, "Literal");
node = this.startNode()
node.value = this.tok.type === tt._null ? null : this.tok.type === tt._true
node.raw = this.tok.type.keyword
this.next()
return this.finishNode(node, "Literal")
case tt.parenL:
var start = this.storeCurrentPos();
this.next();
var val = this.parseExpression();
this.expect(tt.parenR);
let parenStart = this.storeCurrentPos()
this.next()
let inner = this.parseExpression()
this.expect(tt.parenR)
if (this.eat(tt.arrow)) {
return this.parseArrowExpression(this.startNodeAt(start), val.expressions || (isDummy(val) ? [] : [val]));
return this.parseArrowExpression(this.startNodeAt(parenStart), inner.expressions || (isDummy(inner) ? [] : [inner]))
}
if (this.options.preserveParens) {
var par = this.startNodeAt(start);
par.expression = val;
val = this.finishNode(par, "ParenthesizedExpression");
let par = this.startNodeAt(parenStart)
par.expression = inner
inner = this.finishNode(par, "ParenthesizedExpression")
}
return val;
return inner
case tt.bracketL:
var node = this.startNode();
this.pushCx();
node.elements = this.parseExprList(tt.bracketR, true);
return this.finishNode(node, "ArrayExpression");
node = this.startNode()
this.pushCx()
node.elements = this.parseExprList(tt.bracketR, true)
return this.finishNode(node, "ArrayExpression")
case tt.braceL:
return this.parseObj();
return this.parseObj()
case tt._class:
return this.parseClass();
return this.parseClass()
case tt._function:
var node = this.startNode();
this.next();
return this.parseFunction(node, false);
node = this.startNode()
this.next()
return this.parseFunction(node, false)
case tt._new:
return this.parseNew();
return this.parseNew()
case tt._yield:
var node = this.startNode();
this.next();
node = this.startNode()
this.next()
if (this.semicolon() || this.canInsertSemicolon() || (this.tok.type != tt.star && !this.tok.type.startsExpr)) {
node.delegate = false;
node.argument = null;
node.delegate = false
node.argument = null
} else {
node.delegate = this.eat(tt.star);
node.argument = this.parseMaybeAssign();
node.delegate = this.eat(tt.star)
node.argument = this.parseMaybeAssign()
}
return this.finishNode(node, "YieldExpression");
return this.finishNode(node, "YieldExpression")
case tt.backQuote:
return this.parseTemplate();
return this.parseTemplate()
default:
return this.dummyIdent();
return this.dummyIdent()
}
};
}
lp.parseNew = function() {
var node = this.startNode(), startIndent = this.curIndent, line = this.curLineStart;
this.next();
var start = this.storeCurrentPos();
node.callee = this.parseSubscripts(this.parseExprAtom(), start, true, startIndent, line);
let node = this.startNode(), startIndent = this.curIndent, line = this.curLineStart
this.next()
let start = this.storeCurrentPos()
node.callee = this.parseSubscripts(this.parseExprAtom(), start, true, startIndent, line)
if (this.tok.type == tt.parenL) {
this.pushCx();
node.arguments = this.parseExprList(tt.parenR);
this.pushCx()
node.arguments = this.parseExprList(tt.parenR)
} else {
node.arguments = [];
node.arguments = []
}
return this.finishNode(node, "NewExpression");
};
return this.finishNode(node, "NewExpression")
}
lp.parseTemplateElement = function() {
var elem = this.startNode();
let elem = this.startNode()
elem.value = {
raw: this.input.slice(this.tok.start, this.tok.end),
cooked: this.tok.value
};
this.next();
elem.tail = this.tok.type === tt.backQuote;
return this.finishNode(elem, "TemplateElement");
};
}
this.next()
elem.tail = this.tok.type === tt.backQuote
return this.finishNode(elem, "TemplateElement")
}
lp.parseTemplate = function() {
var node = this.startNode();
this.next();
node.expressions = [];
var curElt = this.parseTemplateElement();
node.quasis = [curElt];
let node = this.startNode()
this.next()
node.expressions = []
let curElt = this.parseTemplateElement()
node.quasis = [curElt]
while (!curElt.tail) {
this.next();
node.expressions.push(this.parseExpression());
this.next()
node.expressions.push(this.parseExpression())
if (this.expect(tt.braceR)) {
curElt = this.parseTemplateElement();
curElt = this.parseTemplateElement()
} else {
curElt = this.startNode();
curElt.value = {cooked: '', raw: ''};
curElt.tail = true;
curElt = this.startNode()
curElt.value = {cooked: '', raw: ''}
curElt.tail = true
}
node.quasis.push(curElt);
node.quasis.push(curElt)
}
this.expect(tt.backQuote);
return this.finishNode(node, "TemplateLiteral");
};
this.expect(tt.backQuote)
return this.finishNode(node, "TemplateLiteral")
}
lp.parseObj = function() {
var node = this.startNode();
node.properties = [];
this.pushCx();
var indent = this.curIndent + 1, line = this.curLineStart;
this.eat(tt.braceL);
if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart; }
let node = this.startNode()
node.properties = []
this.pushCx()
let indent = this.curIndent + 1, line = this.curLineStart
this.eat(tt.braceL)
if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart }
while (!this.closes(tt.braceR, indent, line)) {
var prop = this.startNode(), isGenerator, start;
let prop = this.startNode(), isGenerator, start
if (this.options.ecmaVersion >= 6) {
start = this.storeCurrentPos();
prop.method = false;
prop.shorthand = false;
isGenerator = this.eat(tt.star);
start = this.storeCurrentPos()
prop.method = false
prop.shorthand = false
isGenerator = this.eat(tt.star)
}
this.parsePropertyName(prop);
if (isDummy(prop.key)) { if (isDummy(this.parseMaybeAssign())) this.next(); this.eat(tt.comma); continue; }
this.parsePropertyName(prop)
if (isDummy(prop.key)) { if (isDummy(this.parseMaybeAssign())) this.next(); this.eat(tt.comma); continue }
if (this.eat(tt.colon)) {
prop.kind = "init";
prop.value = this.parseMaybeAssign();
prop.kind = "init"
prop.value = this.parseMaybeAssign()
} else if (this.options.ecmaVersion >= 6 && (this.tok.type === tt.parenL || this.tok.type === tt.braceL)) {
prop.kind = "init";
prop.method = true;
prop.value = this.parseMethod(isGenerator);
prop.kind = "init"
prop.method = true
prop.value = this.parseMethod(isGenerator)
} else if (this.options.ecmaVersion >= 5 && prop.key.type === "Identifier" &&
!prop.computed && (prop.key.name === "get" || prop.key.name === "set") &&
(this.tok.type != tt.comma && this.tok.type != tt.braceR)) {
prop.kind = prop.key.name;
this.parsePropertyName(prop);
prop.value = this.parseMethod(false);
prop.kind = prop.key.name
this.parsePropertyName(prop)
prop.value = this.parseMethod(false)
} else {
prop.kind = "init";
prop.kind = "init"
if (this.options.ecmaVersion >= 6) {
if (this.eat(tt.eq)) {
var assign = this.startNodeAt(start);
assign.operator = "=";
assign.left = prop.key;
assign.right = this.parseMaybeAssign();
prop.value = this.finishNode(assign, "AssignmentExpression");
let assign = this.startNodeAt(start)
assign.operator = "="
assign.left = prop.key
assign.right = this.parseMaybeAssign()
prop.value = this.finishNode(assign, "AssignmentExpression")
} else {
prop.value = prop.key;
prop.value = prop.key
}
} else {
prop.value = this.dummyIdent();
prop.value = this.dummyIdent()
}
prop.shorthand = true;
prop.shorthand = true
}
node.properties.push(this.finishNode(prop, "Property"));
this.eat(tt.comma);
node.properties.push(this.finishNode(prop, "Property"))
this.eat(tt.comma)
}
this.popCx();
this.popCx()
if (!this.eat(tt.braceR)) {
// If there is no closing brace, make the node span to the start
// of the next token (this is useful for Tern)
this.last.end = this.tok.start;
if (this.options.locations) this.last.loc.end = this.tok.loc.start;
this.last.end = this.tok.start
if (this.options.locations) this.last.loc.end = this.tok.loc.start
}
return this.finishNode(node, "ObjectExpression");
};
return this.finishNode(node, "ObjectExpression")
}
lp.parsePropertyName = function(prop) {
if (this.options.ecmaVersion >= 6) {
if (this.eat(tt.bracketL)) {
prop.computed = true;
prop.key = this.parseExpression();
this.expect(tt.bracketR);
return;
prop.computed = true
prop.key = this.parseExpression()
this.expect(tt.bracketR)
return
} else {
prop.computed = false;
prop.computed = false
}
}
var key = (this.tok.type === tt.num || this.tok.type === tt.string) ? this.parseExprAtom() : this.parseIdent();
prop.key = key || this.dummyIdent();
};
let key = (this.tok.type === tt.num || this.tok.type === tt.string) ? this.parseExprAtom() : this.parseIdent()
prop.key = key || this.dummyIdent()
}
lp.parsePropertyAccessor = function() {
if (this.tok.type === tt.name || this.tok.type.keyword) return this.parseIdent();
};
if (this.tok.type === tt.name || this.tok.type.keyword) return this.parseIdent()
}
lp.parseIdent = function() {
var node = this.startNode();
node.name = this.tok.type === tt.name ? this.tok.value : this.tok.type.keyword;
this.next();
return this.finishNode(node, "Identifier");
};
let node = this.startNode()
node.name = this.tok.type === tt.name ? this.tok.value : this.tok.type.keyword
this.next()
return this.finishNode(node, "Identifier")
}
lp.initFunction = function(node) {
node.id = null;
node.params = [];
node.id = null
node.params = []
if (this.options.ecmaVersion >= 6) {
node.generator = false;
node.expression = false;
node.generator = false
node.expression = false
}
};
}
// Convert existing expression atom to assignable pattern
// if possible.
@ -415,189 +416,83 @@ lp.toAssignable = function(node) {
if (this.options.ecmaVersion >= 6 && node) {
switch (node.type) {
case "ObjectExpression":
node.type = "ObjectPattern";
var props = node.properties;
for (var i = 0; i < props.length; i++)
this.toAssignable(props[i].value);
break;
node.type = "ObjectPattern"
let props = node.properties
for (let i = 0; i < props.length; i++)
this.toAssignable(props[i].value)
break
case "ArrayExpression":
node.type = "ArrayPattern";
this.toAssignableList(node.elements);
break;
node.type = "ArrayPattern"
this.toAssignableList(node.elements)
break
case "SpreadElement":
node.type = "RestElement";
node.argument = this.toAssignable(node.argument);
break;
node.type = "RestElement"
node.argument = this.toAssignable(node.argument)
break
case "AssignmentExpression":
node.type = "AssignmentPattern";
break;
node.type = "AssignmentPattern"
break
}
}
return this.checkLVal(node);
};
return this.checkLVal(node)
}
lp.toAssignableList = function(exprList) {
for (var i = 0; i < exprList.length; i++)
this.toAssignable(exprList[i]);
return exprList;
};
for (let i = 0; i < exprList.length; i++)
this.toAssignable(exprList[i])
return exprList
}
lp.parseFunctionParams = function(params) {
this.pushCx();
params = this.parseExprList(tt.parenR);
return this.toAssignableList(params);
};
this.pushCx()
params = this.parseExprList(tt.parenR)
return this.toAssignableList(params)
}
lp.parseMethod = function(isGenerator) {
var node = this.startNode();
this.initFunction(node);
node.params = this.parseFunctionParams();
node.generator = isGenerator || false;
node.expression = this.options.ecmaVersion >= 6 && this.tok.type !== tt.braceL;
node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock();
return this.finishNode(node, "FunctionExpression");
};
let node = this.startNode()
this.initFunction(node)
node.params = this.parseFunctionParams()
node.generator = isGenerator || false
node.expression = this.options.ecmaVersion >= 6 && this.tok.type !== tt.braceL
node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock()
return this.finishNode(node, "FunctionExpression")
}
lp.parseArrowExpression = function(node, params) {
this.initFunction(node);
node.params = this.toAssignableList(params);
node.expression = this.tok.type !== tt.braceL;
node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock();
return this.finishNode(node, "ArrowFunctionExpression");
};
lp.parseExport = function() {
var node = this.startNode();
this.next();
if (this.eat(tt.star)) {
node.source = this.eatContextual("from") ? this.parseExprAtom() : null;
return this.finishNode(node, "ExportAllDeclaration");
}
if (this.eat(tt._default)) {
var expr = this.parseMaybeAssign();
if (expr.id) {
switch (expr.type) {
case "FunctionExpression": expr.type = "FunctionDeclaration"; break;
case "ClassExpression": expr.type = "ClassDeclaration"; break;
}
}
node.declaration = expr;
this.semicolon();
return this.finishNode(node, "ExportDefaultDeclaration");
}
if (this.tok.type.keyword) {
node.declaration = this.parseStatement();
node.specifiers = [];
node.source = null;
} else {
node.declaration = null;
node.specifiers = this.parseExportSpecifierList();
node.source = this.eatContextual("from") ? this.parseExprAtom() : null;
this.semicolon();
}
return this.finishNode(node, "ExportNamedDeclaration");
};
lp.parseImport = function() {
var node = this.startNode();
this.next();
if (this.tok.type === tt.string) {
node.specifiers = [];
node.source = this.parseExprAtom();
node.kind = '';
} else {
if (this.tok.type === tt.name && this.tok.value !== "from") {
var elt = this.startNode();
elt.local = this.parseIdent();
this.finishNode(elt, "ImportDefaultSpecifier");
this.eat(tt.comma);
}
node.specifiers = this.parseImportSpecifierList();
node.source = this.eatContextual("from") ? this.parseExprAtom() : null;
if (elt) node.specifiers.unshift(elt);
}
this.semicolon();
return this.finishNode(node, "ImportDeclaration");
};
lp.parseImportSpecifierList = function() {
var elts = [];
if (this.tok.type === tt.star) {
var elt = this.startNode();
this.next();
if (this.eatContextual("as")) elt.local = this.parseIdent();
elts.push(this.finishNode(elt, "ImportNamespaceSpecifier"));
} else {
var indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart;
this.pushCx();
this.eat(tt.braceL);
if (this.curLineStart > continuedLine) continuedLine = this.curLineStart;
while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
var elt = this.startNode();
if (this.eat(tt.star)) {
if (this.eatContextual("as")) elt.local = this.parseIdent();
this.finishNode(elt, "ImportNamespaceSpecifier");
} else {
if (this.isContextual("from")) break;
elt.imported = this.parseIdent();
elt.local = this.eatContextual("as") ? this.parseIdent() : elt.imported;
this.finishNode(elt, "ImportSpecifier");
}
elts.push(elt);
this.eat(tt.comma);
}
this.eat(tt.braceR);
this.popCx();
}
return elts;
};
lp.parseExportSpecifierList = function() {
var elts = [];
var indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart;
this.pushCx();
this.eat(tt.braceL);
if (this.curLineStart > continuedLine) continuedLine = this.curLineStart;
while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
if (this.isContextual("from")) break;
var elt = this.startNode();
elt.local = this.parseIdent();
elt.exported = this.eatContextual("as") ? this.parseIdent() : elt.local;
this.finishNode(elt, "ExportSpecifier");
elts.push(elt);
this.eat(tt.comma);
}
this.eat(tt.braceR);
this.popCx();
return elts;
};
this.initFunction(node)
node.params = this.toAssignableList(params)
node.expression = this.tok.type !== tt.braceL
node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock()
return this.finishNode(node, "ArrowFunctionExpression")
}
lp.parseExprList = function(close, allowEmpty) {
var indent = this.curIndent, line = this.curLineStart, elts = [];
let indent = this.curIndent, line = this.curLineStart, elts = []
this.next(); // Opening bracket
while (!this.closes(close, indent + 1, line)) {
if (this.eat(tt.comma)) {
elts.push(allowEmpty ? null : this.dummyIdent());
continue;
elts.push(allowEmpty ? null : this.dummyIdent())
continue
}
var elt = this.parseMaybeAssign();
let elt = this.parseMaybeAssign()
if (isDummy(elt)) {
if (this.closes(close, indent, line)) break;
this.next();
if (this.closes(close, indent, line)) break
this.next()
} else {
elts.push(elt);
elts.push(elt)
}
this.eat(tt.comma);
this.eat(tt.comma)
}
this.popCx();
this.popCx()
if (!this.eat(close)) {
// If there is no closing brace, make the node span to the start
// of the next token (this is useful for Tern)
this.last.end = this.tok.start;
if (this.options.locations) this.last.loc.end = this.tok.loc.start;
this.last.end = this.tok.start
if (this.options.locations) this.last.loc.end = this.tok.loc.start
}
return elts;
};
return elts
}

View File

@ -4,123 +4,123 @@ import {Node, SourceLocation, lineBreak, isNewLine, tokTypes as tt} from ".."
const lp = LooseParser.prototype
lp.startNode = function() {
var node = new Node;
node.start = this.tok.start;
let node = new Node
node.start = this.tok.start
if (this.options.locations)
node.loc = new SourceLocation(this.toks, this.tok.loc.start);
node.loc = new SourceLocation(this.toks, this.tok.loc.start)
if (this.options.directSourceFile)
node.sourceFile = this.options.directSourceFile;
node.sourceFile = this.options.directSourceFile
if (this.options.ranges)
node.range = [this.tok.start, 0];
return node;
};
node.range = [this.tok.start, 0]
return node
}
lp.storeCurrentPos = function() {
return this.options.locations ? [this.tok.start, this.tok.loc.start] : this.tok.start;
};
return this.options.locations ? [this.tok.start, this.tok.loc.start] : this.tok.start
}
lp.startNodeAt = function(pos) {
var node = new Node;
let node = new Node
if (this.options.locations) {
node.start = pos[0];
node.loc = new SourceLocation(this.toks, pos[1]);
pos = pos[0];
node.start = pos[0]
node.loc = new SourceLocation(this.toks, pos[1])
pos = pos[0]
} else {
node.start = pos;
node.start = pos
}
if (this.options.directSourceFile)
node.sourceFile = this.options.directSourceFile;
node.sourceFile = this.options.directSourceFile
if (this.options.ranges)
node.range = [pos, 0];
return node;
};
node.range = [pos, 0]
return node
}
lp.finishNode = function(node, type) {
node.type = type;
node.end = this.last.end;
node.type = type
node.end = this.last.end
if (this.options.locations)
node.loc.end = this.last.loc.end;
node.loc.end = this.last.loc.end
if (this.options.ranges)
node.range[1] = this.last.end;
return node;
};
node.range[1] = this.last.end
return node
}
lp.dummyIdent = function() {
var dummy = this.startNode();
dummy.name = "✖";
return this.finishNode(dummy, "Identifier");
};
let dummy = this.startNode()
dummy.name = "✖"
return this.finishNode(dummy, "Identifier")
}
export function isDummy(node) { return node.name == "✖"; }
export function isDummy(node) { return node.name == "✖" }
lp.eat = function(type) {
if (this.tok.type === type) {
this.next();
return true;
this.next()
return true
} else {
return false;
return false
}
};
}
lp.isContextual = function(name) {
return this.tok.type === tt.name && this.tok.value === name;
};
return this.tok.type === tt.name && this.tok.value === name
}
lp.eatContextual = function(name) {
return this.tok.value === name && this.eat(tt.name);
};
return this.tok.value === name && this.eat(tt.name)
}
lp.canInsertSemicolon = function() {
return this.tok.type === tt.eof || this.tok.type === tt.braceR ||
lineBreak.test(this.input.slice(this.last.end, this.tok.start));
};
lineBreak.test(this.input.slice(this.last.end, this.tok.start))
}
lp.semicolon = function() {
return this.eat(tt.semi);
};
return this.eat(tt.semi)
}
lp.expect = function(type) {
if (this.eat(type)) return true;
for (var i = 1; i <= 2; i++) {
if (this.eat(type)) return true
for (let i = 1; i <= 2; i++) {
if (this.lookAhead(i).type == type) {
for (var j = 0; j < i; j++) this.next();
return true;
for (let j = 0; j < i; j++) this.next()
return true
}
}
};
}
lp.pushCx = function() {
this.context.push(this.curIndent);
};
this.context.push(this.curIndent)
}
lp.popCx = function() {
this.curIndent = this.context.pop();
};
this.curIndent = this.context.pop()
}
lp.lineEnd = function(pos) {
while (pos < this.input.length && !isNewLine(this.input.charCodeAt(pos))) ++pos;
return pos;
};
while (pos < this.input.length && !isNewLine(this.input.charCodeAt(pos))) ++pos
return pos
}
lp.indentationAfter = function(pos) {
for (var count = 0;; ++pos) {
var ch = this.input.charCodeAt(pos);
if (ch === 32) ++count;
else if (ch === 9) count += this.options.tabSize;
else return count;
for (let count = 0;; ++pos) {
let ch = this.input.charCodeAt(pos)
if (ch === 32) ++count
else if (ch === 9) count += this.options.tabSize
else return count
}
};
}
lp.closes = function(closeTok, indent, line, blockHeuristic) {
if (this.tok.type === closeTok || this.tok.type === tt.eof) return true;
if (this.tok.type === closeTok || this.tok.type === tt.eof) return true
return line != this.curLineStart && this.curIndent < indent && this.tokenStartsLine() &&
(!blockHeuristic || this.nextLineStart >= this.input.length ||
this.indentationAfter(this.nextLineStart) < indent);
};
this.indentationAfter(this.nextLineStart) < indent)
}
lp.tokenStartsLine = function() {
for (var p = this.tok.start - 1; p >= this.curLineStart; --p) {
var ch = this.input.charCodeAt(p);
if (ch !== 9 && ch !== 32) return false;
for (let p = this.tok.start - 1; p >= this.curLineStart; --p) {
let ch = this.input.charCodeAt(p)
if (ch !== 9 && ch !== 32) return false
}
return true;
};
return true
}

View File

@ -2,16 +2,16 @@ import {tokenizer, SourceLocation, tokTypes as tt} from ".."
export function LooseParser(input, options) {
this.toks = tokenizer(input, options)
this.options = this.toks.options;
this.input = this.toks.input;
this.tok = this.last = {type: tt.eof, start: 0, end: 0};
this.options = this.toks.options
this.input = this.toks.input
this.tok = this.last = {type: tt.eof, start: 0, end: 0}
if (this.options.locations) {
var here = this.toks.curPosition();
this.tok.loc = new SourceLocation(this.toks, here, here);
let here = this.toks.curPosition()
this.tok.loc = new SourceLocation(this.toks, here, here)
}
this.ahead = []; // Tokens ahead
this.context = []; // Indentation contexted
this.curIndent = 0;
this.curLineStart = 0;
this.nextLineStart = this.lineEnd(this.curLineStart) + 1;
this.curIndent = 0
this.curLineStart = 0
this.nextLineStart = this.lineEnd(this.curLineStart) + 1
}

View File

@ -5,307 +5,415 @@ import {getLineInfo, tokTypes as tt} from ".."
const lp = LooseParser.prototype
lp.parseTopLevel = function() {
var node = this.startNodeAt(this.options.locations ? [0, getLineInfo(this.input, 0)] : 0);
node.body = [];
while (this.tok.type !== tt.eof) node.body.push(this.parseStatement());
this.last = this.tok;
let node = this.startNodeAt(this.options.locations ? [0, getLineInfo(this.input, 0)] : 0)
node.body = []
while (this.tok.type !== tt.eof) node.body.push(this.parseStatement())
this.last = this.tok
if (this.options.ecmaVersion >= 6) {
node.sourceType = this.options.sourceType;
node.sourceType = this.options.sourceType
}
return this.finishNode(node, "Program");
};
return this.finishNode(node, "Program")
}
lp.parseStatement = function() {
var starttype = this.tok.type, node = this.startNode();
let starttype = this.tok.type, node = this.startNode()
switch (starttype) {
case tt._break: case tt._continue:
this.next();
var isBreak = starttype === tt._break;
this.next()
let isBreak = starttype === tt._break
if (this.semicolon() || this.canInsertSemicolon()) {
node.label = null;
node.label = null
} else {
node.label = this.tok.type === tt.name ? this.parseIdent() : null;
this.semicolon();
node.label = this.tok.type === tt.name ? this.parseIdent() : null
this.semicolon()
}
return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement")
case tt._debugger:
this.next();
this.semicolon();
return this.finishNode(node, "DebuggerStatement");
this.next()
this.semicolon()
return this.finishNode(node, "DebuggerStatement")
case tt._do:
this.next();
node.body = this.parseStatement();
node.test = this.eat(tt._while) ? this.parseParenExpression() : this.dummyIdent();
this.semicolon();
return this.finishNode(node, "DoWhileStatement");
this.next()
node.body = this.parseStatement()
node.test = this.eat(tt._while) ? this.parseParenExpression() : this.dummyIdent()
this.semicolon()
return this.finishNode(node, "DoWhileStatement")
case tt._for:
this.next();
this.pushCx();
this.expect(tt.parenL);
if (this.tok.type === tt.semi) return this.parseFor(node, null);
this.next()
this.pushCx()
this.expect(tt.parenL)
if (this.tok.type === tt.semi) return this.parseFor(node, null)
if (this.tok.type === tt._var || this.tok.type === tt._let || this.tok.type === tt._const) {
var init = this.parseVar(true);
let init = this.parseVar(true)
if (init.declarations.length === 1 && (this.tok.type === tt._in || this.isContextual("of"))) {
return this.parseForIn(node, init);
return this.parseForIn(node, init)
}
return this.parseFor(node, init);
return this.parseFor(node, init)
}
var init = this.parseExpression(true);
let init = this.parseExpression(true)
if (this.tok.type === tt._in || this.isContextual("of"))
return this.parseForIn(node, this.toAssignable(init));
return this.parseFor(node, init);
return this.parseForIn(node, this.toAssignable(init))
return this.parseFor(node, init)
case tt._function:
this.next();
return this.parseFunction(node, true);
this.next()
return this.parseFunction(node, true)
case tt._if:
this.next();
node.test = this.parseParenExpression();
node.consequent = this.parseStatement();
node.alternate = this.eat(tt._else) ? this.parseStatement() : null;
return this.finishNode(node, "IfStatement");
this.next()
node.test = this.parseParenExpression()
node.consequent = this.parseStatement()
node.alternate = this.eat(tt._else) ? this.parseStatement() : null
return this.finishNode(node, "IfStatement")
case tt._return:
this.next();
if (this.eat(tt.semi) || this.canInsertSemicolon()) node.argument = null;
else { node.argument = this.parseExpression(); this.semicolon(); }
return this.finishNode(node, "ReturnStatement");
this.next()
if (this.eat(tt.semi) || this.canInsertSemicolon()) node.argument = null
else { node.argument = this.parseExpression(); this.semicolon() }
return this.finishNode(node, "ReturnStatement")
case tt._switch:
var blockIndent = this.curIndent, line = this.curLineStart;
this.next();
node.discriminant = this.parseParenExpression();
node.cases = [];
this.pushCx();
this.expect(tt.braceL);
let blockIndent = this.curIndent, line = this.curLineStart
this.next()
node.discriminant = this.parseParenExpression()
node.cases = []
this.pushCx()
this.expect(tt.braceL)
for (var cur; !this.closes(tt.braceR, blockIndent, line, true);) {
let cur
while (!this.closes(tt.braceR, blockIndent, line, true)) {
if (this.tok.type === tt._case || this.tok.type === tt._default) {
var isCase = this.tok.type === tt._case;
if (cur) this.finishNode(cur, "SwitchCase");
node.cases.push(cur = this.startNode());
cur.consequent = [];
this.next();
if (isCase) cur.test = this.parseExpression();
else cur.test = null;
this.expect(tt.colon);
let isCase = this.tok.type === tt._case
if (cur) this.finishNode(cur, "SwitchCase")
node.cases.push(cur = this.startNode())
cur.consequent = []
this.next()
if (isCase) cur.test = this.parseExpression()
else cur.test = null
this.expect(tt.colon)
} else {
if (!cur) {
node.cases.push(cur = this.startNode());
cur.consequent = [];
cur.test = null;
node.cases.push(cur = this.startNode())
cur.consequent = []
cur.test = null
}
cur.consequent.push(this.parseStatement());
cur.consequent.push(this.parseStatement())
}
}
if (cur) this.finishNode(cur, "SwitchCase");
this.popCx();
this.eat(tt.braceR);
return this.finishNode(node, "SwitchStatement");
if (cur) this.finishNode(cur, "SwitchCase")
this.popCx()
this.eat(tt.braceR)
return this.finishNode(node, "SwitchStatement")
case tt._throw:
this.next();
node.argument = this.parseExpression();
this.semicolon();
return this.finishNode(node, "ThrowStatement");
this.next()
node.argument = this.parseExpression()
this.semicolon()
return this.finishNode(node, "ThrowStatement")
case tt._try:
this.next();
node.block = this.parseBlock();
node.handler = null;
this.next()
node.block = this.parseBlock()
node.handler = null
if (this.tok.type === tt._catch) {
var clause = this.startNode();
this.next();
this.expect(tt.parenL);
clause.param = this.toAssignable(this.parseExprAtom());
this.expect(tt.parenR);
clause.guard = null;
clause.body = this.parseBlock();
node.handler = this.finishNode(clause, "CatchClause");
let clause = this.startNode()
this.next()
this.expect(tt.parenL)
clause.param = this.toAssignable(this.parseExprAtom())
this.expect(tt.parenR)
clause.guard = null
clause.body = this.parseBlock()
node.handler = this.finishNode(clause, "CatchClause")
}
node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null;
if (!node.handler && !node.finalizer) return node.block;
return this.finishNode(node, "TryStatement");
node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null
if (!node.handler && !node.finalizer) return node.block
return this.finishNode(node, "TryStatement")
case tt._var:
case tt._let:
case tt._const:
return this.parseVar();
return this.parseVar()
case tt._while:
this.next();
node.test = this.parseParenExpression();
node.body = this.parseStatement();
return this.finishNode(node, "WhileStatement");
this.next()
node.test = this.parseParenExpression()
node.body = this.parseStatement()
return this.finishNode(node, "WhileStatement")
case tt._with:
this.next();
node.object = this.parseParenExpression();
node.body = this.parseStatement();
return this.finishNode(node, "WithStatement");
this.next()
node.object = this.parseParenExpression()
node.body = this.parseStatement()
return this.finishNode(node, "WithStatement")
case tt.braceL:
return this.parseBlock();
return this.parseBlock()
case tt.semi:
this.next();
return this.finishNode(node, "EmptyStatement");
this.next()
return this.finishNode(node, "EmptyStatement")
case tt._class:
return this.parseClass(true);
return this.parseClass(true)
case tt._import:
return this.parseImport();
return this.parseImport()
case tt._export:
return this.parseExport();
return this.parseExport()
default:
var expr = this.parseExpression();
let expr = this.parseExpression()
if (isDummy(expr)) {
this.next();
if (this.tok.type === tt.eof) return this.finishNode(node, "EmptyStatement");
return this.parseStatement();
this.next()
if (this.tok.type === tt.eof) return this.finishNode(node, "EmptyStatement")
return this.parseStatement()
} else if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon)) {
node.body = this.parseStatement();
node.label = expr;
return this.finishNode(node, "LabeledStatement");
node.body = this.parseStatement()
node.label = expr
return this.finishNode(node, "LabeledStatement")
} else {
node.expression = expr;
this.semicolon();
return this.finishNode(node, "ExpressionStatement");
node.expression = expr
this.semicolon()
return this.finishNode(node, "ExpressionStatement")
}
}
};
}
lp.parseBlock = function() {
var node = this.startNode();
this.pushCx();
this.expect(tt.braceL);
var blockIndent = this.curIndent, line = this.curLineStart;
node.body = [];
let node = this.startNode()
this.pushCx()
this.expect(tt.braceL)
let blockIndent = this.curIndent, line = this.curLineStart
node.body = []
while (!this.closes(tt.braceR, blockIndent, line, true))
node.body.push(this.parseStatement());
this.popCx();
this.eat(tt.braceR);
return this.finishNode(node, "BlockStatement");
};
node.body.push(this.parseStatement())
this.popCx()
this.eat(tt.braceR)
return this.finishNode(node, "BlockStatement")
}
lp.parseFor = function(node, init) {
node.init = init;
node.test = node.update = null;
if (this.eat(tt.semi) && this.tok.type !== tt.semi) node.test = this.parseExpression();
if (this.eat(tt.semi) && this.tok.type !== tt.parenR) node.update = this.parseExpression();
this.popCx();
this.expect(tt.parenR);
node.body = this.parseStatement();
return this.finishNode(node, "ForStatement");
};
node.init = init
node.test = node.update = null
if (this.eat(tt.semi) && this.tok.type !== tt.semi) node.test = this.parseExpression()
if (this.eat(tt.semi) && this.tok.type !== tt.parenR) node.update = this.parseExpression()
this.popCx()
this.expect(tt.parenR)
node.body = this.parseStatement()
return this.finishNode(node, "ForStatement")
}
lp.parseForIn = function(node, init) {
var type = this.tok.type === tt._in ? "ForInStatement" : "ForOfStatement";
this.next();
node.left = init;
node.right = this.parseExpression();
this.popCx();
this.expect(tt.parenR);
node.body = this.parseStatement();
return this.finishNode(node, type);
};
let type = this.tok.type === tt._in ? "ForInStatement" : "ForOfStatement"
this.next()
node.left = init
node.right = this.parseExpression()
this.popCx()
this.expect(tt.parenR)
node.body = this.parseStatement()
return this.finishNode(node, type)
}
lp.parseVar = function(noIn) {
var node = this.startNode();
node.kind = this.tok.type.keyword;
this.next();
node.declarations = [];
let node = this.startNode()
node.kind = this.tok.type.keyword
this.next()
node.declarations = []
do {
var decl = this.startNode();
decl.id = this.options.ecmaVersion >= 6 ? this.toAssignable(this.parseExprAtom()) : this.parseIdent();
decl.init = this.eat(tt.eq) ? this.parseMaybeAssign(noIn) : null;
node.declarations.push(this.finishNode(decl, "VariableDeclarator"));
} while (this.eat(tt.comma));
let decl = this.startNode()
decl.id = this.options.ecmaVersion >= 6 ? this.toAssignable(this.parseExprAtom()) : this.parseIdent()
decl.init = this.eat(tt.eq) ? this.parseMaybeAssign(noIn) : null
node.declarations.push(this.finishNode(decl, "VariableDeclarator"))
} while (this.eat(tt.comma))
if (!node.declarations.length) {
var decl = this.startNode();
decl.id = this.dummyIdent();
node.declarations.push(this.finishNode(decl, "VariableDeclarator"));
let decl = this.startNode()
decl.id = this.dummyIdent()
node.declarations.push(this.finishNode(decl, "VariableDeclarator"))
}
if (!noIn) this.semicolon();
return this.finishNode(node, "VariableDeclaration");
};
if (!noIn) this.semicolon()
return this.finishNode(node, "VariableDeclaration")
}
lp.parseClass = function(isStatement) {
var node = this.startNode();
this.next();
if (this.tok.type === tt.name) node.id = this.parseIdent();
else if (isStatement) node.id = this.dummyIdent();
else node.id = null;
node.superClass = this.eat(tt._extends) ? this.parseExpression() : null;
node.body = this.startNode();
node.body.body = [];
this.pushCx();
var indent = this.curIndent + 1, line = this.curLineStart;
this.eat(tt.braceL);
if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart; }
let node = this.startNode()
this.next()
if (this.tok.type === tt.name) node.id = this.parseIdent()
else if (isStatement) node.id = this.dummyIdent()
else node.id = null
node.superClass = this.eat(tt._extends) ? this.parseExpression() : null
node.body = this.startNode()
node.body.body = []
this.pushCx()
let indent = this.curIndent + 1, line = this.curLineStart
this.eat(tt.braceL)
if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart }
while (!this.closes(tt.braceR, indent, line)) {
if (this.semicolon()) continue;
var method = this.startNode(), isGenerator, start;
if (this.semicolon()) continue
let method = this.startNode(), isGenerator, start
if (this.options.ecmaVersion >= 6) {
method['static'] = false;
isGenerator = this.eat(tt.star);
method['static'] = false
isGenerator = this.eat(tt.star)
}
this.parsePropertyName(method);
if (isDummy(method.key)) { if (isDummy(this.parseMaybeAssign())) this.next(); this.eat(tt.comma); continue; }
this.parsePropertyName(method)
if (isDummy(method.key)) { if (isDummy(this.parseMaybeAssign())) this.next(); this.eat(tt.comma); continue }
if (method.key.type === "Identifier" && !method.computed && method.key.name === "static" &&
(this.tok.type != tt.parenL && this.tok.type != tt.braceL)) {
method['static'] = true;
isGenerator = this.eat(tt.star);
this.parsePropertyName(method);
method['static'] = true
isGenerator = this.eat(tt.star)
this.parsePropertyName(method)
} else {
method['static'] = false;
method['static'] = false
}
if (this.options.ecmaVersion >= 5 && method.key.type === "Identifier" &&
!method.computed && (method.key.name === "get" || method.key.name === "set") &&
this.tok.type !== tt.parenL && this.tok.type !== tt.braceL) {
method.kind = method.key.name;
this.parsePropertyName(method);
method.value = this.parseMethod(false);
method.kind = method.key.name
this.parsePropertyName(method)
method.value = this.parseMethod(false)
} else {
if (!method.computed && !method['static'] && !isGenerator && (
method.key.type === "Identifier" && method.key.name === "constructor" ||
method.key.type === "Literal" && method.key.value === "constructor")) {
method.kind = "constructor";
method.kind = "constructor"
} else {
method.kind = "method";
method.kind = "method"
}
method.value = this.parseMethod(isGenerator);
method.value = this.parseMethod(isGenerator)
}
node.body.body.push(this.finishNode(method, "MethodDefinition"));
node.body.body.push(this.finishNode(method, "MethodDefinition"))
}
this.popCx();
this.popCx()
if (!this.eat(tt.braceR)) {
// If there is no closing brace, make the node span to the start
// of the next token (this is useful for Tern)
this.last.end = this.tok.start;
if (this.options.locations) this.last.loc.end = this.tok.loc.start;
this.last.end = this.tok.start
if (this.options.locations) this.last.loc.end = this.tok.loc.start
}
this.semicolon();
this.finishNode(node.body, "ClassBody");
return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
};
this.semicolon()
this.finishNode(node.body, "ClassBody")
return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
}
lp.parseFunction = function(node, isStatement) {
this.initFunction(node);
this.initFunction(node)
if (this.options.ecmaVersion >= 6) {
node.generator = this.eat(tt.star);
node.generator = this.eat(tt.star)
}
if (this.tok.type === tt.name) node.id = this.parseIdent();
else if (isStatement) node.id = this.dummyIdent();
node.params = this.parseFunctionParams();
node.body = this.parseBlock();
return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
};
if (this.tok.type === tt.name) node.id = this.parseIdent()
else if (isStatement) node.id = this.dummyIdent()
node.params = this.parseFunctionParams()
node.body = this.parseBlock()
return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression")
}
lp.parseExport = function() {
let node = this.startNode()
this.next()
if (this.eat(tt.star)) {
node.source = this.eatContextual("from") ? this.parseExprAtom() : null
return this.finishNode(node, "ExportAllDeclaration")
}
if (this.eat(tt._default)) {
let expr = this.parseMaybeAssign()
if (expr.id) {
switch (expr.type) {
case "FunctionExpression": expr.type = "FunctionDeclaration"; break
case "ClassExpression": expr.type = "ClassDeclaration"; break
}
}
node.declaration = expr
this.semicolon()
return this.finishNode(node, "ExportDefaultDeclaration")
}
if (this.tok.type.keyword) {
node.declaration = this.parseStatement()
node.specifiers = []
node.source = null
} else {
node.declaration = null
node.specifiers = this.parseExportSpecifierList()
node.source = this.eatContextual("from") ? this.parseExprAtom() : null
this.semicolon()
}
return this.finishNode(node, "ExportNamedDeclaration")
}
lp.parseImport = function() {
let node = this.startNode()
this.next()
if (this.tok.type === tt.string) {
node.specifiers = []
node.source = this.parseExprAtom()
node.kind = ''
} else {
let elt
if (this.tok.type === tt.name && this.tok.value !== "from") {
elt = this.startNode()
elt.local = this.parseIdent()
this.finishNode(elt, "ImportDefaultSpecifier")
this.eat(tt.comma)
}
node.specifiers = this.parseImportSpecifierList()
node.source = this.eatContextual("from") ? this.parseExprAtom() : null
if (elt) node.specifiers.unshift(elt)
}
this.semicolon()
return this.finishNode(node, "ImportDeclaration")
}
lp.parseImportSpecifierList = function() {
let elts = []
if (this.tok.type === tt.star) {
let elt = this.startNode()
this.next()
if (this.eatContextual("as")) elt.local = this.parseIdent()
elts.push(this.finishNode(elt, "ImportNamespaceSpecifier"))
} else {
let indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart
this.pushCx()
this.eat(tt.braceL)
if (this.curLineStart > continuedLine) continuedLine = this.curLineStart
while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
let elt = this.startNode()
if (this.eat(tt.star)) {
if (this.eatContextual("as")) elt.local = this.parseIdent()
this.finishNode(elt, "ImportNamespaceSpecifier")
} else {
if (this.isContextual("from")) break
elt.imported = this.parseIdent()
elt.local = this.eatContextual("as") ? this.parseIdent() : elt.imported
this.finishNode(elt, "ImportSpecifier")
}
elts.push(elt)
this.eat(tt.comma)
}
this.eat(tt.braceR)
this.popCx()
}
return elts
}
lp.parseExportSpecifierList = function() {
let elts = []
let indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart
this.pushCx()
this.eat(tt.braceL)
if (this.curLineStart > continuedLine) continuedLine = this.curLineStart
while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
if (this.isContextual("from")) break
let elt = this.startNode()
elt.local = this.parseIdent()
elt.exported = this.eatContextual("as") ? this.parseIdent() : elt.local
this.finishNode(elt, "ExportSpecifier")
elts.push(elt)
this.eat(tt.comma)
}
this.eat(tt.braceR)
this.popCx()
return elts
}

View File

@ -8,101 +8,101 @@ function isSpace(ch) {
}
lp.next = function() {
this.last = this.tok;
this.last = this.tok
if (this.ahead.length)
this.tok = this.ahead.shift();
this.tok = this.ahead.shift()
else
this.tok = this.readToken();
this.tok = this.readToken()
if (this.tok.start >= this.nextLineStart) {
while (this.tok.start >= this.nextLineStart) {
this.curLineStart = this.nextLineStart;
this.nextLineStart = this.lineEnd(this.curLineStart) + 1;
this.curLineStart = this.nextLineStart
this.nextLineStart = this.lineEnd(this.curLineStart) + 1
}
this.curIndent = this.indentationAfter(this.curLineStart);
this.curIndent = this.indentationAfter(this.curLineStart)
}
};
}
lp.readToken = function() {
for (;;) {
try {
this.toks.next();
this.toks.next()
if (this.toks.type === tt.dot &&
this.input.substr(this.toks.end, 1) === "." &&
this.options.ecmaVersion >= 6) {
this.toks.end++;
this.toks.type = tt.ellipsis;
this.toks.end++
this.toks.type = tt.ellipsis
}
return new Token(this.toks);
return new Token(this.toks)
} catch(e) {
if (!(e instanceof SyntaxError)) throw e;
if (!(e instanceof SyntaxError)) throw e
// Try to skip some text, based on the error message, and then continue
var msg = e.message, pos = e.raisedAt, replace = true;
let msg = e.message, pos = e.raisedAt, replace = true
if (/unterminated/i.test(msg)) {
pos = this.lineEnd(e.pos + 1);
pos = this.lineEnd(e.pos + 1)
if (/string/.test(msg)) {
replace = {start: e.pos, end: pos, type: tt.string, value: this.input.slice(e.pos + 1, pos)};
replace = {start: e.pos, end: pos, type: tt.string, value: this.input.slice(e.pos + 1, pos)}
} else if (/regular expr/i.test(msg)) {
var re = this.input.slice(e.pos, pos);
try { re = new RegExp(re); } catch(e) {}
replace = {start: e.pos, end: pos, type: tt.regexp, value: re};
let re = this.input.slice(e.pos, pos)
try { re = new RegExp(re) } catch(e) {}
replace = {start: e.pos, end: pos, type: tt.regexp, value: re}
} else if (/template/.test(msg)) {
replace = {start: e.pos, end: pos,
type: tt.template,
value: this.input.slice(e.pos, pos)};
value: this.input.slice(e.pos, pos)}
} else {
replace = false;
replace = false
}
} else if (/invalid (unicode|regexp|number)|expecting unicode|octal literal|is reserved|directly after number/i.test(msg)) {
while (pos < this.input.length && !isSpace(this.input.charCodeAt(pos))) ++pos;
while (pos < this.input.length && !isSpace(this.input.charCodeAt(pos))) ++pos
} else if (/character escape|expected hexadecimal/i.test(msg)) {
while (pos < this.input.length) {
var ch = this.input.charCodeAt(pos++);
if (ch === 34 || ch === 39 || isNewLine(ch)) break;
let ch = this.input.charCodeAt(pos++)
if (ch === 34 || ch === 39 || isNewLine(ch)) break
}
} else if (/unexpected character/i.test(msg)) {
pos++;
replace = false;
pos++
replace = false
} else if (/regular expression/i.test(msg)) {
replace = true;
replace = true
} else {
throw e;
throw e
}
this.resetTo(pos);
if (replace === true) replace = {start: pos, end: pos, type: tt.name, value: "✖"};
this.resetTo(pos)
if (replace === true) replace = {start: pos, end: pos, type: tt.name, value: "✖"}
if (replace) {
if (this.options.locations)
replace.loc = new SourceLocation(
this.toks,
getLineInfo(this.input, replace.start),
getLineInfo(this.input, replace.end));
return replace;
getLineInfo(this.input, replace.end))
return replace
}
}
}
};
}
lp.resetTo = function(pos) {
this.toks.pos = pos;
var ch = this.input.charAt(pos - 1);
this.toks.pos = pos
let ch = this.input.charAt(pos - 1)
this.toks.exprAllowed = !ch || /[\[\{\(,;:?\/*=+\-~!|&%^<>]/.test(ch) ||
/[enwfd]/.test(ch) &&
/\b(keywords|case|else|return|throw|new|in|(instance|type)of|delete|void)$/.test(this.input.slice(pos - 10, pos));
/\b(keywords|case|else|return|throw|new|in|(instance|type)of|delete|void)$/.test(this.input.slice(pos - 10, pos))
if (this.options.locations) {
this.toks.curLine = 1;
this.toks.lineStart = lineBreakG.lastIndex = 0;
var match;
this.toks.curLine = 1
this.toks.lineStart = lineBreakG.lastIndex = 0
let match
while ((match = lineBreakG.exec(this.input)) && match.index < pos) {
++this.toks.curLine;
this.toks.lineStart = match.index + match[0].length;
++this.toks.curLine
this.toks.lineStart = match.index + match[0].length
}
}
};
}
lp.lookAhead = function(n) {
while (n > this.ahead.length)
this.ahead.push(this.readToken());
return this.ahead[n - 1];
};
this.ahead.push(this.readToken())
return this.ahead[n - 1]
}

View File

@ -15,134 +15,134 @@ pp.toAssignable = function(node, isBinding) {
case "ObjectPattern":
case "ArrayPattern":
case "AssignmentPattern":
break;
break
case "ObjectExpression":
node.type = "ObjectPattern";
for (var i = 0; i < node.properties.length; i++) {
var prop = node.properties[i];
if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter");
this.toAssignable(prop.value, isBinding);
node.type = "ObjectPattern"
for (let i = 0; i < node.properties.length; i++) {
let prop = node.properties[i]
if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter")
this.toAssignable(prop.value, isBinding)
}
break;
break
case "ArrayExpression":
node.type = "ArrayPattern";
this.toAssignableList(node.elements, isBinding);
break;
node.type = "ArrayPattern"
this.toAssignableList(node.elements, isBinding)
break
case "AssignmentExpression":
if (node.operator === "=") {
node.type = "AssignmentPattern";
node.type = "AssignmentPattern"
} else {
this.raise(node.left.end, "Only '=' operator can be used for specifying default value.");
this.raise(node.left.end, "Only '=' operator can be used for specifying default value.")
}
break;
break
case "MemberExpression":
if (!isBinding) break;
if (!isBinding) break
default:
this.raise(node.start, "Assigning to rvalue");
this.raise(node.start, "Assigning to rvalue")
}
}
return node;
};
return node
}
// Convert list of expression atoms to binding list.
pp.toAssignableList = function(exprList, isBinding) {
var end = exprList.length;
let end = exprList.length
if (end) {
var last = exprList[end - 1];
let last = exprList[end - 1]
if (last && last.type == "RestElement") {
--end;
--end
} else if (last && last.type == "SpreadElement") {
last.type = "RestElement";
var arg = last.argument;
this.toAssignable(arg, isBinding);
last.type = "RestElement"
let arg = last.argument
this.toAssignable(arg, isBinding)
if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern")
this.unexpected(arg.start);
--end;
this.unexpected(arg.start)
--end
}
}
for (var i = 0; i < end; i++) {
var elt = exprList[i];
if (elt) this.toAssignable(elt, isBinding);
for (let i = 0; i < end; i++) {
let elt = exprList[i]
if (elt) this.toAssignable(elt, isBinding)
}
return exprList;
};
return exprList
}
// Parses spread element.
pp.parseSpread = function(refShorthandDefaultPos) {
var node = this.startNode();
this.next();
node.argument = this.parseMaybeAssign(refShorthandDefaultPos);
return this.finishNode(node, "SpreadElement");
};
let node = this.startNode()
this.next()
node.argument = this.parseMaybeAssign(refShorthandDefaultPos)
return this.finishNode(node, "SpreadElement")
}
pp.parseRest = function() {
var node = this.startNode();
this.next();
node.argument = this.type === tt.name || this.type === tt.bracketL ? this.parseBindingAtom() : this.unexpected();
return this.finishNode(node, "RestElement");
};
let node = this.startNode()
this.next()
node.argument = this.type === tt.name || this.type === tt.bracketL ? this.parseBindingAtom() : this.unexpected()
return this.finishNode(node, "RestElement")
}
// Parses lvalue (assignable) atom.
pp.parseBindingAtom = function() {
if (this.options.ecmaVersion < 6) return this.parseIdent();
if (this.options.ecmaVersion < 6) return this.parseIdent()
switch (this.type) {
case tt.name:
return this.parseIdent();
return this.parseIdent()
case tt.bracketL:
var node = this.startNode();
this.next();
node.elements = this.parseBindingList(tt.bracketR, true, true);
return this.finishNode(node, "ArrayPattern");
let node = this.startNode()
this.next()
node.elements = this.parseBindingList(tt.bracketR, true, true)
return this.finishNode(node, "ArrayPattern")
case tt.braceL:
return this.parseObj(true);
return this.parseObj(true)
default:
this.unexpected();
this.unexpected()
}
};
}
pp.parseBindingList = function(close, allowEmpty, allowTrailingComma) {
var elts = [], first = true;
let elts = [], first = true
while (!this.eat(close)) {
if (first) first = false;
else this.expect(tt.comma);
if (first) first = false
else this.expect(tt.comma)
if (allowEmpty && this.type === tt.comma) {
elts.push(null);
elts.push(null)
} else if (allowTrailingComma && this.afterTrailingComma(close)) {
break;
break
} else if (this.type === tt.ellipsis) {
elts.push(this.parseRest());
this.expect(close);
break;
elts.push(this.parseRest())
this.expect(close)
break
} else {
elts.push(this.parseMaybeDefault());
elts.push(this.parseMaybeDefault())
}
}
return elts;
};
return elts
}
// Parses assignment pattern around given atom if possible.
pp.parseMaybeDefault = function(startPos, left) {
startPos = startPos || this.markPosition();
left = left || this.parseBindingAtom();
if (!this.eat(tt.eq)) return left;
var node = this.startNodeAt(startPos);
node.operator = "=";
node.left = left;
node.right = this.parseMaybeAssign();
return this.finishNode(node, "AssignmentPattern");
};
startPos = startPos || this.markPosition()
left = left || this.parseBindingAtom()
if (!this.eat(tt.eq)) return left
let node = this.startNodeAt(startPos)
node.operator = "="
node.left = left
node.right = this.parseMaybeAssign()
return this.finishNode(node, "AssignmentPattern")
}
// Verify that a node is an lval — something that can be assigned
// to.
@ -151,39 +151,39 @@ pp.checkLVal = function(expr, isBinding, checkClashes) {
switch (expr.type) {
case "Identifier":
if (this.strict && (reservedWords.strictBind(expr.name) || reservedWords.strict(expr.name)))
this.raise(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode");
this.raise(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode")
if (checkClashes) {
if (has(checkClashes, expr.name))
this.raise(expr.start, "Argument name clash in strict mode");
checkClashes[expr.name] = true;
this.raise(expr.start, "Argument name clash in strict mode")
checkClashes[expr.name] = true
}
break;
break
case "MemberExpression":
if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression");
break;
if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression")
break
case "ObjectPattern":
for (var i = 0; i < expr.properties.length; i++)
this.checkLVal(expr.properties[i].value, isBinding, checkClashes);
break;
for (let i = 0; i < expr.properties.length; i++)
this.checkLVal(expr.properties[i].value, isBinding, checkClashes)
break
case "ArrayPattern":
for (var i = 0; i < expr.elements.length; i++) {
var elem = expr.elements[i];
if (elem) this.checkLVal(elem, isBinding, checkClashes);
for (let i = 0; i < expr.elements.length; i++) {
let elem = expr.elements[i]
if (elem) this.checkLVal(elem, isBinding, checkClashes)
}
break;
break
case "AssignmentPattern":
this.checkLVal(expr.left, isBinding, checkClashes);
break;
this.checkLVal(expr.left, isBinding, checkClashes)
break
case "RestElement":
this.checkLVal(expr.argument, isBinding, checkClashes);
break;
this.checkLVal(expr.argument, isBinding, checkClashes)
break
default:
this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue");
this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue")
}
};
}

View File

@ -8,50 +8,50 @@ const pp = Parser.prototype
export class Node {}
pp.startNode = function() {
var node = new Node;
node.start = this.start;
let node = new Node
node.start = this.start
if (this.options.locations)
node.loc = new SourceLocation(this, this.startLoc);
node.loc = new SourceLocation(this, this.startLoc)
if (this.options.directSourceFile)
node.sourceFile = this.options.directSourceFile;
node.sourceFile = this.options.directSourceFile
if (this.options.ranges)
node.range = [this.start, 0];
return node;
};
node.range = [this.start, 0]
return node
}
pp.startNodeAt = function(pos) {
var node = new Node, start = pos;
let node = new Node, start = pos
if (this.options.locations) {
node.loc = new SourceLocation(this, start[1]);
start = pos[0];
node.loc = new SourceLocation(this, start[1])
start = pos[0]
}
node.start = start;
node.start = start
if (this.options.directSourceFile)
node.sourceFile = this.options.directSourceFile;
node.sourceFile = this.options.directSourceFile
if (this.options.ranges)
node.range = [start, 0];
return node;
};
node.range = [start, 0]
return node
}
// Finish an AST node, adding `type` and `end` properties.
pp.finishNode = function(node, type) {
node.type = type;
node.end = this.lastTokEnd;
node.type = type
node.end = this.lastTokEnd
if (this.options.locations)
node.loc.end = this.lastTokEndLoc;
node.loc.end = this.lastTokEndLoc
if (this.options.ranges)
node.range[1] = this.lastTokEnd;
return node;
};
node.range[1] = this.lastTokEnd
return node
}
// Finish node at given position
pp.finishNodeAt = function(node, type, pos) {
if (this.options.locations) { node.loc.end = pos[1]; pos = pos[0]; }
node.type = type;
node.end = pos;
if (this.options.locations) { node.loc.end = pos[1]; pos = pos[0] }
node.type = type
node.end = pos
if (this.options.ranges)
node.range[1] = pos;
return node;
};
node.range[1] = pos
return node
}

View File

@ -92,13 +92,13 @@ export function getOptions(opts) {
options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt]
if (isArray(options.onToken)) {
var tokens = options.onToken;
let tokens = options.onToken
options.onToken = (token) => tokens.push(token)
}
if (isArray(options.onComment))
options.onComment = pushComment(options, options.onComment);
options.onComment = pushComment(options, options.onComment)
return options;
return options
}
function pushComment(options, array) {

View File

@ -10,38 +10,38 @@ const pp = Parser.prototype
pp.isUseStrict = function(stmt) {
return this.options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" &&
stmt.expression.type === "Literal" && stmt.expression.value === "use strict";
};
stmt.expression.type === "Literal" && stmt.expression.value === "use strict"
}
// Predicate that tests whether the next token is of the given
// type, and if yes, consumes it as a side effect.
pp.eat = function(type) {
if (this.type === type) {
this.next();
return true;
this.next()
return true
} else {
return false;
return false
}
};
}
// Tests whether parsed token is a contextual keyword.
pp.isContextual = function(name) {
return this.type === tt.name && this.value === name;
};
return this.type === tt.name && this.value === name
}
// Consumes contextual keyword if possible.
pp.eatContextual = function(name) {
return this.value === name && this.eat(tt.name);
};
return this.value === name && this.eat(tt.name)
}
// Asserts that following token is given contextual keyword.
pp.expectContextual = function(name) {
if (!this.eatContextual(name)) this.unexpected();
};
if (!this.eatContextual(name)) this.unexpected()
}
// Test whether a semicolon can be inserted at the current position.
@ -49,41 +49,41 @@ pp.canInsertSemicolon = function() {
return this.type === tt.eof ||
this.type === tt.braceR ||
lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
};
}
pp.insertSemicolon = function() {
if (this.canInsertSemicolon()) {
if (this.options.onInsertedSemicolon)
this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc)
return true;
return true
}
};
}
// Consume a semicolon, or, failing that, see if we are allowed to
// pretend that there is a semicolon at this position.
pp.semicolon = function() {
if (!this.eat(tt.semi) && !this.insertSemicolon()) this.unexpected();
};
if (!this.eat(tt.semi) && !this.insertSemicolon()) this.unexpected()
}
pp.afterTrailingComma = function(tokType) {
if (this.type == tokType) {
if (this.options.onTrailingComma)
this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc);
this.next();
return true;
this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc)
this.next()
return true
}
};
}
// Expect a token of a given type. If found, consume it, otherwise,
// raise an unexpected token error.
pp.expect = function(type) {
this.eat(type) || this.unexpected();
};
this.eat(type) || this.unexpected()
}
// Raise an unexpected token error.
pp.unexpected = function(pos) {
this.raise(pos != null ? pos : this.start, "Unexpected token");
};
this.raise(pos != null ? pos : this.start, "Unexpected token")
}

View File

@ -12,68 +12,68 @@ const pp = Parser.prototype
// to its body instead of creating a new node.
pp.parseTopLevel = function(node) {
var first = true;
if (!node.body) node.body = [];
let first = true
if (!node.body) node.body = []
while (this.type !== tt.eof) {
var stmt = this.parseStatement(true, true);
node.body.push(stmt);
if (first && this.isUseStrict(stmt)) this.setStrict(true);
first = false;
let stmt = this.parseStatement(true, true)
node.body.push(stmt)
if (first && this.isUseStrict(stmt)) this.setStrict(true)
first = false
}
this.next();
this.next()
if (this.options.ecmaVersion >= 6) {
node.sourceType = this.options.sourceType;
node.sourceType = this.options.sourceType
}
return this.finishNode(node, "Program");
};
return this.finishNode(node, "Program")
}
var loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"};
const loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"}
// Parse a single statement.
//
// If expecting a statement and finding a slash operator, parse a
// regular expression literal. This is to handle cases like
// `if (foo) /blah/.exec(foo);`, where looking at the previous token
// `if (foo) /blah/.exec(foo)`, where looking at the previous token
// does not help.
pp.parseStatement = function(declaration, topLevel) {
var starttype = this.type, node = this.startNode();
let starttype = this.type, node = this.startNode()
// Most types of statements are recognized by the keyword they
// start with. Many are trivial to parse, some require a bit of
// complexity.
switch (starttype) {
case tt._break: case tt._continue: return this.parseBreakContinueStatement(node, starttype.keyword);
case tt._debugger: return this.parseDebuggerStatement(node);
case tt._do: return this.parseDoStatement(node);
case tt._for: return this.parseForStatement(node);
case tt._break: case tt._continue: return this.parseBreakContinueStatement(node, starttype.keyword)
case tt._debugger: return this.parseDebuggerStatement(node)
case tt._do: return this.parseDoStatement(node)
case tt._for: return this.parseForStatement(node)
case tt._function:
if (!declaration && this.options.ecmaVersion >= 6) this.unexpected();
return this.parseFunctionStatement(node);
if (!declaration && this.options.ecmaVersion >= 6) this.unexpected()
return this.parseFunctionStatement(node)
case tt._class:
if (!declaration) this.unexpected();
return this.parseClass(node, true);
case tt._if: return this.parseIfStatement(node);
case tt._return: return this.parseReturnStatement(node);
case tt._switch: return this.parseSwitchStatement(node);
case tt._throw: return this.parseThrowStatement(node);
case tt._try: return this.parseTryStatement(node);
case tt._let: case tt._const: if (!declaration) this.unexpected(); // NOTE: falls through to _var
case tt._var: return this.parseVarStatement(node, starttype);
case tt._while: return this.parseWhileStatement(node);
case tt._with: return this.parseWithStatement(node);
case tt.braceL: return this.parseBlock(); // no point creating a function for this
case tt.semi: return this.parseEmptyStatement(node);
if (!declaration) this.unexpected()
return this.parseClass(node, true)
case tt._if: return this.parseIfStatement(node)
case tt._return: return this.parseReturnStatement(node)
case tt._switch: return this.parseSwitchStatement(node)
case tt._throw: return this.parseThrowStatement(node)
case tt._try: return this.parseTryStatement(node)
case tt._let: case tt._const: if (!declaration) this.unexpected() // NOTE: falls through to _var
case tt._var: return this.parseVarStatement(node, starttype)
case tt._while: return this.parseWhileStatement(node)
case tt._with: return this.parseWithStatement(node)
case tt.braceL: return this.parseBlock()
case tt.semi: return this.parseEmptyStatement(node)
case tt._export:
case tt._import:
if (!this.options.allowImportExportEverywhere) {
if (!topLevel)
this.raise(this.start, "'import' and 'export' may only appear at the top level");
this.raise(this.start, "'import' and 'export' may only appear at the top level")
if (!this.inModule)
this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'");
this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'")
}
return starttype === tt._import ? this.parseImport(node) : this.parseExport(node);
return starttype === tt._import ? this.parseImport(node) : this.parseExport(node)
// If the statement does not start with a statement keyword or a
// brace, it's an ExpressionStatement or LabeledStatement. We
@ -81,55 +81,55 @@ pp.parseStatement = function(declaration, topLevel) {
// next token is a colon and the expression was a simple
// Identifier node, we switch to interpreting it as a label.
default:
var maybeName = this.value, expr = this.parseExpression();
let maybeName = this.value, expr = this.parseExpression()
if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon))
return this.parseLabeledStatement(node, maybeName, expr);
else return this.parseExpressionStatement(node, expr);
return this.parseLabeledStatement(node, maybeName, expr)
else return this.parseExpressionStatement(node, expr)
}
};
}
pp.parseBreakContinueStatement = function(node, keyword) {
var isBreak = keyword == "break";
this.next();
if (this.eat(tt.semi) || this.insertSemicolon()) node.label = null;
else if (this.type !== tt.name) this.unexpected();
let isBreak = keyword == "break"
this.next()
if (this.eat(tt.semi) || this.insertSemicolon()) node.label = null
else if (this.type !== tt.name) this.unexpected()
else {
node.label = this.parseIdent();
this.semicolon();
node.label = this.parseIdent()
this.semicolon()
}
// Verify that there is an actual destination to break or
// continue to.
for (var i = 0; i < this.labels.length; ++i) {
var lab = this.labels[i];
let lab = this.labels[i]
if (node.label == null || lab.name === node.label.name) {
if (lab.kind != null && (isBreak || lab.kind === "loop")) break;
if (node.label && isBreak) break;
if (lab.kind != null && (isBreak || lab.kind === "loop")) break
if (node.label && isBreak) break
}
}
if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword);
return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
};
if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword)
return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement")
}
pp.parseDebuggerStatement = function(node) {
this.next();
this.semicolon();
return this.finishNode(node, "DebuggerStatement");
};
this.next()
this.semicolon()
return this.finishNode(node, "DebuggerStatement")
}
pp.parseDoStatement = function(node) {
this.next();
this.labels.push(loopLabel);
node.body = this.parseStatement(false);
this.labels.pop();
this.expect(tt._while);
node.test = this.parseParenExpression();
this.next()
this.labels.push(loopLabel)
node.body = this.parseStatement(false)
this.labels.pop()
this.expect(tt._while)
node.test = this.parseParenExpression()
if (this.options.ecmaVersion >= 6)
this.eat(tt.semi);
this.eat(tt.semi)
else
this.semicolon();
return this.finishNode(node, "DoWhileStatement");
};
this.semicolon()
return this.finishNode(node, "DoWhileStatement")
}
// Disambiguating between a `for` and a `for`/`in` or `for`/`of`
// loop is non-trivial. Basically, we have to parse the init `var`
@ -140,65 +140,65 @@ pp.parseDoStatement = function(node) {
// is a regular `for` loop.
pp.parseForStatement = function(node) {
this.next();
this.labels.push(loopLabel);
this.expect(tt.parenL);
if (this.type === tt.semi) return this.parseFor(node, null);
this.next()
this.labels.push(loopLabel)
this.expect(tt.parenL)
if (this.type === tt.semi) return this.parseFor(node, null)
if (this.type === tt._var || this.type === tt._let || this.type === tt._const) {
var init = this.startNode(), varKind = this.type;
this.next();
this.parseVar(init, true, varKind);
this.finishNode(init, "VariableDeclaration");
let init = this.startNode(), varKind = this.type
this.next()
this.parseVar(init, true, varKind)
this.finishNode(init, "VariableDeclaration")
if ((this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) && init.declarations.length === 1 &&
!(varKind !== tt._var && init.declarations[0].init))
return this.parseForIn(node, init);
return this.parseFor(node, init);
return this.parseForIn(node, init)
return this.parseFor(node, init)
}
var refShorthandDefaultPos = {start: 0};
var init = this.parseExpression(true, refShorthandDefaultPos);
let refShorthandDefaultPos = {start: 0}
let init = this.parseExpression(true, refShorthandDefaultPos)
if (this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) {
this.toAssignable(init);
this.checkLVal(init);
return this.parseForIn(node, init);
this.toAssignable(init)
this.checkLVal(init)
return this.parseForIn(node, init)
} else if (refShorthandDefaultPos.start) {
this.unexpected(refShorthandDefaultPos.start);
this.unexpected(refShorthandDefaultPos.start)
}
return this.parseFor(node, init);
};
return this.parseFor(node, init)
}
pp.parseFunctionStatement = function(node) {
this.next();
return this.parseFunction(node, true);
};
this.next()
return this.parseFunction(node, true)
}
pp.parseIfStatement = function(node) {
this.next();
node.test = this.parseParenExpression();
node.consequent = this.parseStatement(false);
node.alternate = this.eat(tt._else) ? this.parseStatement(false) : null;
return this.finishNode(node, "IfStatement");
};
this.next()
node.test = this.parseParenExpression()
node.consequent = this.parseStatement(false)
node.alternate = this.eat(tt._else) ? this.parseStatement(false) : null
return this.finishNode(node, "IfStatement")
}
pp.parseReturnStatement = function(node) {
if (!this.inFunction && !this.options.allowReturnOutsideFunction)
this.raise(this.start, "'return' outside of function");
this.next();
this.raise(this.start, "'return' outside of function")
this.next()
// In `return` (and `break`/`continue`), the keywords with
// optional arguments, we eagerly look for a semicolon or the
// possibility to insert one.
if (this.eat(tt.semi) || this.insertSemicolon()) node.argument = null;
else { node.argument = this.parseExpression(); this.semicolon(); }
return this.finishNode(node, "ReturnStatement");
};
if (this.eat(tt.semi) || this.insertSemicolon()) node.argument = null
else { node.argument = this.parseExpression(); this.semicolon() }
return this.finishNode(node, "ReturnStatement")
}
pp.parseSwitchStatement = function(node) {
this.next();
node.discriminant = this.parseParenExpression();
node.cases = [];
this.expect(tt.braceL);
this.labels.push(switchLabel);
this.next()
node.discriminant = this.parseParenExpression()
node.cases = []
this.expect(tt.braceL)
this.labels.push(switchLabel)
// Statements under must be grouped (by label) in SwitchCase
// nodes. `cur` is used to keep the node that we are currently
@ -206,241 +206,357 @@ pp.parseSwitchStatement = function(node) {
for (var cur, sawDefault; this.type != tt.braceR;) {
if (this.type === tt._case || this.type === tt._default) {
var isCase = this.type === tt._case;
if (cur) this.finishNode(cur, "SwitchCase");
node.cases.push(cur = this.startNode());
cur.consequent = [];
this.next();
if (isCase) cur.test = this.parseExpression();
else {
if (sawDefault) this.raise(this.lastTokStart, "Multiple default clauses"); sawDefault = true;
cur.test = null;
let isCase = this.type === tt._case
if (cur) this.finishNode(cur, "SwitchCase")
node.cases.push(cur = this.startNode())
cur.consequent = []
this.next()
if (isCase) {
cur.test = this.parseExpression()
} else {
if (sawDefault) this.raise(this.lastTokStart, "Multiple default clauses")
sawDefault = true
cur.test = null
}
this.expect(tt.colon);
this.expect(tt.colon)
} else {
if (!cur) this.unexpected();
cur.consequent.push(this.parseStatement(true));
if (!cur) this.unexpected()
cur.consequent.push(this.parseStatement(true))
}
}
if (cur) this.finishNode(cur, "SwitchCase");
this.next(); // Closing brace
this.labels.pop();
return this.finishNode(node, "SwitchStatement");
};
if (cur) this.finishNode(cur, "SwitchCase")
this.next() // Closing brace
this.labels.pop()
return this.finishNode(node, "SwitchStatement")
}
pp.parseThrowStatement = function(node) {
this.next();
this.next()
if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start)))
this.raise(this.lastTokEnd, "Illegal newline after throw");
node.argument = this.parseExpression();
this.semicolon();
return this.finishNode(node, "ThrowStatement");
};
this.raise(this.lastTokEnd, "Illegal newline after throw")
node.argument = this.parseExpression()
this.semicolon()
return this.finishNode(node, "ThrowStatement")
}
// Reused empty array added for node fields that are always empty.
const empty = []
pp.parseTryStatement = function(node) {
this.next();
node.block = this.parseBlock();
node.handler = null;
this.next()
node.block = this.parseBlock()
node.handler = null
if (this.type === tt._catch) {
var clause = this.startNode();
this.next();
this.expect(tt.parenL);
clause.param = this.parseBindingAtom();
this.checkLVal(clause.param, true);
this.expect(tt.parenR);
clause.guard = null;
clause.body = this.parseBlock();
node.handler = this.finishNode(clause, "CatchClause");
let clause = this.startNode()
this.next()
this.expect(tt.parenL)
clause.param = this.parseBindingAtom()
this.checkLVal(clause.param, true)
this.expect(tt.parenR)
clause.guard = null
clause.body = this.parseBlock()
node.handler = this.finishNode(clause, "CatchClause")
}
node.guardedHandlers = empty;
node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null;
node.guardedHandlers = empty
node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null
if (!node.handler && !node.finalizer)
this.raise(node.start, "Missing catch or finally clause");
return this.finishNode(node, "TryStatement");
};
this.raise(node.start, "Missing catch or finally clause")
return this.finishNode(node, "TryStatement")
}
pp.parseVarStatement = function(node, kind) {
this.next();
this.parseVar(node, false, kind);
this.semicolon();
return this.finishNode(node, "VariableDeclaration");
};
this.next()
this.parseVar(node, false, kind)
this.semicolon()
return this.finishNode(node, "VariableDeclaration")
}
pp.parseWhileStatement = function(node) {
this.next();
node.test = this.parseParenExpression();
this.labels.push(loopLabel);
node.body = this.parseStatement(false);
this.labels.pop();
return this.finishNode(node, "WhileStatement");
};
this.next()
node.test = this.parseParenExpression()
this.labels.push(loopLabel)
node.body = this.parseStatement(false)
this.labels.pop()
return this.finishNode(node, "WhileStatement")
}
pp.parseWithStatement = function(node) {
if (this.strict) this.raise(this.start, "'with' in strict mode");
this.next();
node.object = this.parseParenExpression();
node.body = this.parseStatement(false);
return this.finishNode(node, "WithStatement");
};
if (this.strict) this.raise(this.start, "'with' in strict mode")
this.next()
node.object = this.parseParenExpression()
node.body = this.parseStatement(false)
return this.finishNode(node, "WithStatement")
}
pp.parseEmptyStatement = function(node) {
this.next();
return this.finishNode(node, "EmptyStatement");
};
this.next()
return this.finishNode(node, "EmptyStatement")
}
pp.parseLabeledStatement = function(node, maybeName, expr) {
for (var i = 0; i < this.labels.length; ++i)
if (this.labels[i].name === maybeName) this.raise(expr.start, "Label '" + maybeName + "' is already declared");
var kind = this.type.isLoop ? "loop" : this.type === tt._switch ? "switch" : null;
this.labels.push({name: maybeName, kind: kind});
node.body = this.parseStatement(true);
this.labels.pop();
node.label = expr;
return this.finishNode(node, "LabeledStatement");
};
for (let i = 0; i < this.labels.length; ++i)
if (this.labels[i].name === maybeName) this.raise(expr.start, "Label '" + maybeName + "' is already declared")
let kind = this.type.isLoop ? "loop" : this.type === tt._switch ? "switch" : null
this.labels.push({name: maybeName, kind: kind})
node.body = this.parseStatement(true)
this.labels.pop()
node.label = expr
return this.finishNode(node, "LabeledStatement")
}
pp.parseExpressionStatement = function(node, expr) {
node.expression = expr;
this.semicolon();
return this.finishNode(node, "ExpressionStatement");
};
node.expression = expr
this.semicolon()
return this.finishNode(node, "ExpressionStatement")
}
// Parse a semicolon-enclosed block of statements, handling `"use
// strict"` declarations when `allowStrict` is true (used for
// function bodies).
pp.parseBlock = function(allowStrict) {
var node = this.startNode(), first = true, oldStrict;
node.body = [];
this.expect(tt.braceL);
let node = this.startNode(), first = true, oldStrict
node.body = []
this.expect(tt.braceL)
while (!this.eat(tt.braceR)) {
var stmt = this.parseStatement(true);
node.body.push(stmt);
let stmt = this.parseStatement(true)
node.body.push(stmt)
if (first && allowStrict && this.isUseStrict(stmt)) {
oldStrict = this.strict;
this.setStrict(this.strict = true);
oldStrict = this.strict
this.setStrict(this.strict = true)
}
first = false;
first = false
}
if (oldStrict === false) this.setStrict(false);
return this.finishNode(node, "BlockStatement");
};
if (oldStrict === false) this.setStrict(false)
return this.finishNode(node, "BlockStatement")
}
// Parse a regular `for` loop. The disambiguation code in
// `parseStatement` will already have parsed the init statement or
// expression.
pp.parseFor = function(node, init) {
node.init = init;
this.expect(tt.semi);
node.test = this.type === tt.semi ? null : this.parseExpression();
this.expect(tt.semi);
node.update = this.type === tt.parenR ? null : this.parseExpression();
this.expect(tt.parenR);
node.body = this.parseStatement(false);
this.labels.pop();
return this.finishNode(node, "ForStatement");
};
node.init = init
this.expect(tt.semi)
node.test = this.type === tt.semi ? null : this.parseExpression()
this.expect(tt.semi)
node.update = this.type === tt.parenR ? null : this.parseExpression()
this.expect(tt.parenR)
node.body = this.parseStatement(false)
this.labels.pop()
return this.finishNode(node, "ForStatement")
}
// Parse a `for`/`in` and `for`/`of` loop, which are almost
// same from parser's perspective.
pp.parseForIn = function(node, init) {
var type = this.type === tt._in ? "ForInStatement" : "ForOfStatement";
this.next();
node.left = init;
node.right = this.parseExpression();
this.expect(tt.parenR);
node.body = this.parseStatement(false);
this.labels.pop();
return this.finishNode(node, type);
};
let type = this.type === tt._in ? "ForInStatement" : "ForOfStatement"
this.next()
node.left = init
node.right = this.parseExpression()
this.expect(tt.parenR)
node.body = this.parseStatement(false)
this.labels.pop()
return this.finishNode(node, type)
}
// Parse a list of variable declarations.
pp.parseVar = function(node, noIn, kind) {
node.declarations = [];
node.kind = kind.keyword;
node.declarations = []
node.kind = kind.keyword
for (;;) {
var decl = this.startNode();
decl.id = this.parseBindingAtom();
this.checkLVal(decl.id, true);
let decl = this.startNode()
decl.id = this.parseBindingAtom()
this.checkLVal(decl.id, true)
if (this.eat(tt.eq)) {
decl.init = this.parseMaybeAssign(noIn);
decl.init = this.parseMaybeAssign(noIn)
} else if (kind === tt._const && !(this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of")))) {
this.unexpected();
this.unexpected()
} else if (decl.id.type != "Identifier") {
this.raise(this.lastTokEnd, "Complex binding patterns require an initialization value")
} else {
decl.init = null;
decl.init = null
}
node.declarations.push(this.finishNode(decl, "VariableDeclarator"));
if (!this.eat(tt.comma)) break;
node.declarations.push(this.finishNode(decl, "VariableDeclarator"))
if (!this.eat(tt.comma)) break
}
return node;
};
return node
}
// Parse a function declaration or literal (depending on the
// `isStatement` parameter).
pp.parseFunction = function(node, isStatement, allowExpressionBody) {
this.initFunction(node);
if (this.options.ecmaVersion >= 6) {
node.generator = this.eat(tt.star);
}
if (isStatement || this.type === tt.name) {
node.id = this.parseIdent();
}
this.expect(tt.parenL);
node.params = this.parseBindingList(tt.parenR, false, false);
this.parseFunctionBody(node, allowExpressionBody);
return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
};
this.initFunction(node)
if (this.options.ecmaVersion >= 6)
node.generator = this.eat(tt.star)
if (isStatement || this.type === tt.name)
node.id = this.parseIdent()
this.expect(tt.parenL)
node.params = this.parseBindingList(tt.parenR, false, false)
this.parseFunctionBody(node, allowExpressionBody)
return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression")
}
// Parse a class declaration or literal (depending on the
// `isStatement` parameter).
pp.parseClass = function(node, isStatement) {
this.next();
node.id = this.type === tt.name ? this.parseIdent() : isStatement ? this.unexpected() : null;
node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null;
var classBody = this.startNode();
classBody.body = [];
this.expect(tt.braceL);
this.next()
node.id = this.type === tt.name ? this.parseIdent() : isStatement ? this.unexpected() : null
node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null
let classBody = this.startNode()
classBody.body = []
this.expect(tt.braceL)
while (!this.eat(tt.braceR)) {
if (this.eat(tt.semi)) continue;
var method = this.startNode();
var isGenerator = this.eat(tt.star);
this.parsePropertyName(method);
if (this.eat(tt.semi)) continue
let method = this.startNode()
let isGenerator = this.eat(tt.star)
this.parsePropertyName(method)
if (this.type !== tt.parenL && !method.computed && method.key.type === "Identifier" &&
method.key.name === "static") {
if (isGenerator) this.unexpected();
method['static'] = true;
isGenerator = this.eat(tt.star);
this.parsePropertyName(method);
if (isGenerator) this.unexpected()
method['static'] = true
isGenerator = this.eat(tt.star)
this.parsePropertyName(method)
} else {
method['static'] = false;
method['static'] = false
}
method.kind = "method";
method.kind = "method"
if (!method.computed && !isGenerator) {
if (method.key.type === "Identifier") {
if (this.type !== tt.parenL && (method.key.name === "get" || method.key.name === "set")) {
method.kind = method.key.name;
this.parsePropertyName(method);
method.kind = method.key.name
this.parsePropertyName(method)
} else if (!method['static'] && method.key.name === "constructor") {
method.kind = "constructor";
method.kind = "constructor"
}
} else if (!method['static'] && method.key.type === "Literal" && method.key.value === "constructor") {
method.kind = "constructor";
method.kind = "constructor"
}
}
method.value = this.parseMethod(isGenerator);
classBody.body.push(this.finishNode(method, "MethodDefinition"));
method.value = this.parseMethod(isGenerator)
classBody.body.push(this.finishNode(method, "MethodDefinition"))
}
node.body = this.finishNode(classBody, "ClassBody");
return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
};
node.body = this.finishNode(classBody, "ClassBody")
return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
}
// Parses module export declaration.
pp.parseExport = function(node) {
this.next()
// export * from '...'
if (this.eat(tt.star)) {
this.expectContextual("from")
node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()
this.semicolon()
return this.finishNode(node, "ExportAllDeclaration")
}
if (this.eat(tt._default)) { // export default ...
let expr = this.parseMaybeAssign()
if (expr.id) {
switch (expr.type) {
case "FunctionExpression": expr.type = "FunctionDeclaration"; break
case "ClassExpression": expr.type = "ClassDeclaration"; break
}
}
node.declaration = expr
this.semicolon()
return this.finishNode(node, "ExportDefaultDeclaration")
}
// export var|const|let|function|class ...
if (this.type.keyword) {
node.declaration = this.parseStatement(true)
node.specifiers = []
node.source = null
} else { // export { x, y as z } [from '...']
node.declaration = null
node.specifiers = this.parseExportSpecifiers()
if (this.eatContextual("from")) {
node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()
} else {
node.source = null
}
this.semicolon()
}
return this.finishNode(node, "ExportNamedDeclaration")
}
// Parses a comma-separated list of module exports.
pp.parseExportSpecifiers = function() {
let nodes = [], first = true
// export { x, y as z } [from '...']
this.expect(tt.braceL)
while (!this.eat(tt.braceR)) {
if (!first) {
this.expect(tt.comma)
if (this.afterTrailingComma(tt.braceR)) break
} else first = false
let node = this.startNode()
node.local = this.parseIdent(this.type === tt._default)
node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local
nodes.push(this.finishNode(node, "ExportSpecifier"))
}
return nodes
}
// Parses import declaration.
pp.parseImport = function(node) {
this.next()
// import '...'
if (this.type === tt.string) {
node.specifiers = empty
node.source = this.parseExprAtom()
node.kind = ""
} else {
node.specifiers = this.parseImportSpecifiers()
this.expectContextual("from")
node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()
}
this.semicolon()
return this.finishNode(node, "ImportDeclaration")
}
// Parses a comma-separated list of module imports.
pp.parseImportSpecifiers = function() {
let nodes = [], first = true
if (this.type === tt.name) {
// import defaultObj, { x, y as z } from '...'
let node = this.startNode()
node.local = this.parseIdent()
this.checkLVal(node.local, true)
nodes.push(this.finishNode(node, "ImportDefaultSpecifier"))
if (!this.eat(tt.comma)) return nodes
}
if (this.type === tt.star) {
let node = this.startNode()
this.next()
this.expectContextual("as")
node.local = this.parseIdent()
this.checkLVal(node.local, true)
nodes.push(this.finishNode(node, "ImportNamespaceSpecifier"))
return nodes
}
this.expect(tt.braceL)
while (!this.eat(tt.braceR)) {
if (!first) {
this.expect(tt.comma)
if (this.afterTrailingComma(tt.braceR)) break
} else first = false
let node = this.startNode()
node.imported = this.parseIdent(true)
node.local = this.eatContextual("as") ? this.parseIdent() : node.imported
this.checkLVal(node.local, true)
nodes.push(this.finishNode(node, "ImportSpecifier"))
}
return nodes
}

View File

@ -32,72 +32,72 @@ pp.initialContext = function() {
}
pp.braceIsBlock = function(prevType) {
var parent;
let parent
if (prevType === tt.colon && (parent = this.curContext()).token == "{")
return !parent.isExpr;
return !parent.isExpr
if (prevType === tt._return)
return lineBreak.test(this.input.slice(this.lastTokEnd, this.start));
return lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
if (prevType === tt._else || prevType === tt.semi || prevType === tt.eof)
return true;
return true
if (prevType == tt.braceL)
return this.curContext() === types.b_stat;
return !this.exprAllowed;
};
return this.curContext() === types.b_stat
return !this.exprAllowed
}
pp.updateContext = function(prevType) {
var update, type = this.type;
let update, type = this.type
if (type.keyword && prevType == tt.dot)
this.exprAllowed = false;
this.exprAllowed = false
else if (update = type.updateContext)
update.call(this, prevType);
update.call(this, prevType)
else
this.exprAllowed = type.beforeExpr;
};
this.exprAllowed = type.beforeExpr
}
// Token-specific context update code
tt.parenR.updateContext = tt.braceR.updateContext = function() {
var out = this.context.pop();
let out = this.context.pop()
if (out === types.b_stat && this.curContext() === types.f_expr) {
this.context.pop();
this.exprAllowed = false;
this.context.pop()
this.exprAllowed = false
} else if (out === types.b_tmpl) {
this.exprAllowed = true;
this.exprAllowed = true
} else {
this.exprAllowed = !(out && out.isExpr);
this.exprAllowed = !(out && out.isExpr)
}
};
}
tt.braceL.updateContext = function(prevType) {
this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr);
this.exprAllowed = true;
};
this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr)
this.exprAllowed = true
}
tt.dollarBraceL.updateContext = function() {
this.context.push(types.b_tmpl);
this.exprAllowed = true;
};
this.context.push(types.b_tmpl)
this.exprAllowed = true
}
tt.parenL.updateContext = function(prevType) {
var statementParens = prevType === tt._if || prevType === tt._for || prevType === tt._with || prevType === tt._while;
this.context.push(statementParens ? types.p_stat : types.p_expr);
this.exprAllowed = true;
};
let statementParens = prevType === tt._if || prevType === tt._for || prevType === tt._with || prevType === tt._while
this.context.push(statementParens ? types.p_stat : types.p_expr)
this.exprAllowed = true
}
tt.incDec.updateContext = function() {
// tokExprAllowed stays unchanged
};
}
tt._function.updateContext = function() {
if (this.curContext() !== types.b_stat)
this.context.push(types.f_expr);
this.exprAllowed = false;
};
this.context.push(types.f_expr)
this.exprAllowed = false
}
tt.backQuote.updateContext = function() {
if (this.curContext() === types.q_tmpl)
this.context.pop();
this.context.pop()
else
this.context.push(types.q_tmpl);
this.exprAllowed = false;
};
this.context.push(types.q_tmpl)
this.exprAllowed = false
}

View File

@ -10,14 +10,14 @@ import {lineBreak, lineBreakG, isNewLine, nonASCIIwhitespace} from "./whitespace
export class Token {
constructor(p) {
this.type = p.type;
this.value = p.value;
this.start = p.start;
this.end = p.end;
this.type = p.type
this.value = p.value
this.start = p.start
this.end = p.end
if (p.options.locations)
this.loc = new SourceLocation(p, p.startLoc, p.endLoc);
this.loc = new SourceLocation(p, p.startLoc, p.endLoc)
if (p.options.ranges)
this.range = [p.start, p.end];
this.range = [p.start, p.end]
}
}
@ -29,52 +29,52 @@ const pp = Parser.prototype
pp.next = function() {
if (this.options.onToken)
this.options.onToken(new Token(this));
this.options.onToken(new Token(this))
this.lastTokEnd = this.end;
this.lastTokStart = this.start;
this.lastTokEndLoc = this.endLoc;
this.lastTokStartLoc = this.startLoc;
this.nextToken();
};
this.lastTokEnd = this.end
this.lastTokStart = this.start
this.lastTokEndLoc = this.endLoc
this.lastTokStartLoc = this.startLoc
this.nextToken()
}
pp.getToken = function() {
this.next();
return new Token(this);
};
this.next()
return new Token(this)
}
// If we're in an ES6 environment, make parsers iterable
if (typeof Symbol !== "undefined")
pp[Symbol.iterator] = function () {
var self = this;
let self = this
return {next: function () {
var token = self.getToken();
let token = self.getToken()
return {
done: token.type === tt.eof,
value: token
};
}};
};
}
}}
}
// Toggle strict mode. Re-reads the next number or string to please
// pedantic tests (`"use strict"; 010;` should fail).
pp.setStrict = function(strict) {
this.strict = strict;
if (this.type !== tt.num && this.type !== tt.string) return;
this.pos = this.start;
this.strict = strict
if (this.type !== tt.num && this.type !== tt.string) return
this.pos = this.start
if (this.options.locations) {
while (this.pos < this.lineStart) {
this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1;
--this.curLine;
this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1
--this.curLine
}
}
this.nextToken();
};
this.nextToken()
}
pp.curContext = function() {
return this.context[this.context.length - 1];
};
return this.context[this.context.length - 1]
}
// Read a single token, updating the parser object's token-related
// properties.
@ -95,91 +95,91 @@ pp.readToken = function(code) {
// Identifier or keyword. '\uXXXX' sequences are allowed in
// identifiers, so '\' also dispatches to that.
if (isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\' */)
return this.readWord();
return this.readWord()
return this.getTokenFromCode(code);
};
return this.getTokenFromCode(code)
}
pp.fullCharCodeAtPos = function() {
var code = this.input.charCodeAt(this.pos);
if (code <= 0xd7ff || code >= 0xe000) return code;
var next = this.input.charCodeAt(this.pos + 1);
return (code << 10) + next - 0x35fdc00;
};
let code = this.input.charCodeAt(this.pos)
if (code <= 0xd7ff || code >= 0xe000) return code
let next = this.input.charCodeAt(this.pos + 1)
return (code << 10) + next - 0x35fdc00
}
pp.skipBlockComment = function() {
var startLoc = this.options.onComment && this.options.locations && this.curPosition();
var start = this.pos, end = this.input.indexOf("*/", this.pos += 2);
if (end === -1) this.raise(this.pos - 2, "Unterminated comment");
this.pos = end + 2;
let startLoc = this.options.onComment && this.options.locations && this.curPosition()
let start = this.pos, end = this.input.indexOf("*/", this.pos += 2)
if (end === -1) this.raise(this.pos - 2, "Unterminated comment")
this.pos = end + 2
if (this.options.locations) {
lineBreakG.lastIndex = start;
var match;
lineBreakG.lastIndex = start
let match
while ((match = lineBreakG.exec(this.input)) && match.index < this.pos) {
++this.curLine;
this.lineStart = match.index + match[0].length;
++this.curLine
this.lineStart = match.index + match[0].length
}
}
if (this.options.onComment)
this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos,
startLoc, this.options.locations && this.curPosition());
};
startLoc, this.options.locations && this.curPosition())
}
pp.skipLineComment = function(startSkip) {
var start = this.pos;
var startLoc = this.options.onComment && this.options.locations && this.curPosition();
var ch = this.input.charCodeAt(this.pos+=startSkip);
let start = this.pos
let startLoc = this.options.onComment && this.options.locations && this.curPosition()
let ch = this.input.charCodeAt(this.pos+=startSkip)
while (this.pos < this.input.length && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) {
++this.pos;
ch = this.input.charCodeAt(this.pos);
++this.pos
ch = this.input.charCodeAt(this.pos)
}
if (this.options.onComment)
this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos,
startLoc, this.options.locations && this.curPosition());
};
startLoc, this.options.locations && this.curPosition())
}
// Called at the start of the parse and after every token. Skips
// whitespace and comments, and.
pp.skipSpace = function() {
while (this.pos < this.input.length) {
var ch = this.input.charCodeAt(this.pos);
let ch = this.input.charCodeAt(this.pos)
if (ch === 32) { // ' '
++this.pos;
++this.pos
} else if (ch === 13) {
++this.pos;
var next = this.input.charCodeAt(this.pos);
++this.pos
let next = this.input.charCodeAt(this.pos)
if (next === 10) {
++this.pos;
++this.pos
}
if (this.options.locations) {
++this.curLine;
this.lineStart = this.pos;
++this.curLine
this.lineStart = this.pos
}
} else if (ch === 10 || ch === 8232 || ch === 8233) {
++this.pos;
++this.pos
if (this.options.locations) {
++this.curLine;
this.lineStart = this.pos;
++this.curLine
this.lineStart = this.pos
}
} else if (ch > 8 && ch < 14) {
++this.pos;
++this.pos
} else if (ch === 47) { // '/'
var next = this.input.charCodeAt(this.pos + 1);
let next = this.input.charCodeAt(this.pos + 1)
if (next === 42) { // '*'
this.skipBlockComment();
this.skipBlockComment()
} else if (next === 47) { // '/'
this.skipLineComment(2);
} else break;
this.skipLineComment(2)
} else break
} else if (ch === 160) { // '\xa0'
++this.pos;
++this.pos
} else if (ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {
++this.pos;
++this.pos
} else {
break;
break
}
}
};
}
// Called at the end of every token. Sets `end`, `val`, and
// maintains `context` and `exprAllowed`, and skips the space after
@ -187,14 +187,14 @@ pp.skipSpace = function() {
// right position.
pp.finishToken = function(type, val) {
this.end = this.pos;
if (this.options.locations) this.endLoc = this.curPosition();
var prevType = this.type;
this.type = type;
this.value = val;
this.end = this.pos
if (this.options.locations) this.endLoc = this.curPosition()
let prevType = this.type
this.type = type
this.value = val
this.updateContext(prevType);
};
this.updateContext(prevType)
}
// ### Token reading
@ -206,117 +206,117 @@ pp.finishToken = function(type, val) {
// All in the name of speed.
//
pp.readToken_dot = function() {
var next = this.input.charCodeAt(this.pos + 1);
if (next >= 48 && next <= 57) return this.readNumber(true);
var next2 = this.input.charCodeAt(this.pos + 2);
let next = this.input.charCodeAt(this.pos + 1)
if (next >= 48 && next <= 57) return this.readNumber(true)
let next2 = this.input.charCodeAt(this.pos + 2)
if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) { // 46 = dot '.'
this.pos += 3;
return this.finishToken(tt.ellipsis);
this.pos += 3
return this.finishToken(tt.ellipsis)
} else {
++this.pos;
return this.finishToken(tt.dot);
++this.pos
return this.finishToken(tt.dot)
}
};
}
pp.readToken_slash = function() { // '/'
var next = this.input.charCodeAt(this.pos + 1);
let next = this.input.charCodeAt(this.pos + 1)
if (this.exprAllowed) {++this.pos; return this.readRegexp();}
if (next === 61) return this.finishOp(tt.assign, 2);
return this.finishOp(tt.slash, 1);
};
if (next === 61) return this.finishOp(tt.assign, 2)
return this.finishOp(tt.slash, 1)
}
pp.readToken_mult_modulo = function(code) { // '%*'
var next = this.input.charCodeAt(this.pos + 1);
if (next === 61) return this.finishOp(tt.assign, 2);
return this.finishOp(code === 42 ? tt.star : tt.modulo, 1);
};
let next = this.input.charCodeAt(this.pos + 1)
if (next === 61) return this.finishOp(tt.assign, 2)
return this.finishOp(code === 42 ? tt.star : tt.modulo, 1)
}
pp.readToken_pipe_amp = function(code) { // '|&'
var next = this.input.charCodeAt(this.pos + 1);
if (next === code) return this.finishOp(code === 124 ? tt.logicalOR : tt.logicalAND, 2);
if (next === 61) return this.finishOp(tt.assign, 2);
return this.finishOp(code === 124 ? tt.bitwiseOR : tt.bitwiseAND, 1);
};
let next = this.input.charCodeAt(this.pos + 1)
if (next === code) return this.finishOp(code === 124 ? tt.logicalOR : tt.logicalAND, 2)
if (next === 61) return this.finishOp(tt.assign, 2)
return this.finishOp(code === 124 ? tt.bitwiseOR : tt.bitwiseAND, 1)
}
pp.readToken_caret = function() { // '^'
var next = this.input.charCodeAt(this.pos + 1);
if (next === 61) return this.finishOp(tt.assign, 2);
return this.finishOp(tt.bitwiseXOR, 1);
};
let next = this.input.charCodeAt(this.pos + 1)
if (next === 61) return this.finishOp(tt.assign, 2)
return this.finishOp(tt.bitwiseXOR, 1)
}
pp.readToken_plus_min = function(code) { // '+-'
var next = this.input.charCodeAt(this.pos + 1);
let next = this.input.charCodeAt(this.pos + 1)
if (next === code) {
if (next == 45 && this.input.charCodeAt(this.pos + 2) == 62 &&
lineBreak.test(this.input.slice(this.lastTokEnd, this.pos))) {
// A `-->` line comment
this.skipLineComment(3);
this.skipSpace();
return this.nextToken();
this.skipLineComment(3)
this.skipSpace()
return this.nextToken()
}
return this.finishOp(tt.incDec, 2);
return this.finishOp(tt.incDec, 2)
}
if (next === 61) return this.finishOp(tt.assign, 2);
return this.finishOp(tt.plusMin, 1);
};
if (next === 61) return this.finishOp(tt.assign, 2)
return this.finishOp(tt.plusMin, 1)
}
pp.readToken_lt_gt = function(code) { // '<>'
var next = this.input.charCodeAt(this.pos + 1);
var size = 1;
let next = this.input.charCodeAt(this.pos + 1)
let size = 1
if (next === code) {
size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2;
if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(tt.assign, size + 1);
return this.finishOp(tt.bitShift, size);
size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2
if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(tt.assign, size + 1)
return this.finishOp(tt.bitShift, size)
}
if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 &&
this.input.charCodeAt(this.pos + 3) == 45) {
if (this.inModule) unexpected();
if (this.inModule) unexpected()
// `<!--`, an XML-style comment that should be interpreted as a line comment
this.skipLineComment(4);
this.skipSpace();
return this.nextToken();
this.skipLineComment(4)
this.skipSpace()
return this.nextToken()
}
if (next === 61)
size = this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2;
return this.finishOp(tt.relational, size);
};
size = this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2
return this.finishOp(tt.relational, size)
}
pp.readToken_eq_excl = function(code) { // '=!'
var next = this.input.charCodeAt(this.pos + 1);
if (next === 61) return this.finishOp(tt.equality, this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2);
let next = this.input.charCodeAt(this.pos + 1)
if (next === 61) return this.finishOp(tt.equality, this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2)
if (code === 61 && next === 62 && this.options.ecmaVersion >= 6) { // '=>'
this.pos += 2;
return this.finishToken(tt.arrow);
this.pos += 2
return this.finishToken(tt.arrow)
}
return this.finishOp(code === 61 ? tt.eq : tt.prefix, 1);
};
return this.finishOp(code === 61 ? tt.eq : tt.prefix, 1)
}
pp.getTokenFromCode = function(code) {
switch (code) {
// The interpretation of a dot depends on whether it is followed
// by a digit or another two dots.
case 46: // '.'
return this.readToken_dot();
return this.readToken_dot()
// Punctuation tokens.
case 40: ++this.pos; return this.finishToken(tt.parenL);
case 41: ++this.pos; return this.finishToken(tt.parenR);
case 59: ++this.pos; return this.finishToken(tt.semi);
case 44: ++this.pos; return this.finishToken(tt.comma);
case 91: ++this.pos; return this.finishToken(tt.bracketL);
case 93: ++this.pos; return this.finishToken(tt.bracketR);
case 123: ++this.pos; return this.finishToken(tt.braceL);
case 125: ++this.pos; return this.finishToken(tt.braceR);
case 58: ++this.pos; return this.finishToken(tt.colon);
case 63: ++this.pos; return this.finishToken(tt.question);
case 40: ++this.pos; return this.finishToken(tt.parenL)
case 41: ++this.pos; return this.finishToken(tt.parenR)
case 59: ++this.pos; return this.finishToken(tt.semi)
case 44: ++this.pos; return this.finishToken(tt.comma)
case 91: ++this.pos; return this.finishToken(tt.bracketL)
case 93: ++this.pos; return this.finishToken(tt.bracketR)
case 123: ++this.pos; return this.finishToken(tt.braceL)
case 125: ++this.pos; return this.finishToken(tt.braceR)
case 58: ++this.pos; return this.finishToken(tt.colon)
case 63: ++this.pos; return this.finishToken(tt.question)
case 96: // '`'
if (this.options.ecmaVersion < 6) break;
++this.pos;
return this.finishToken(tt.backQuote);
if (this.options.ecmaVersion < 6) break
++this.pos
return this.finishToken(tt.backQuote)
case 48: // '0'
var next = this.input.charCodeAt(this.pos + 1);
let next = this.input.charCodeAt(this.pos + 1)
if (next === 120 || next === 88) return this.readRadixNumber(16); // '0x', '0X' - hex number
if (this.options.ecmaVersion >= 6) {
if (next === 111 || next === 79) return this.readRadixNumber(8); // '0o', '0O' - octal number
@ -325,11 +325,11 @@ pp.getTokenFromCode = function(code) {
// 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 this.readNumber(false);
return this.readNumber(false)
// Quotes produce strings.
case 34: case 39: // '"', "'"
return this.readString(code);
return this.readString(code)
// Operators are parsed inline in tiny state machines. '=' (61) is
// often referred to. `finishOp` simply skips the amount of
@ -337,70 +337,70 @@ pp.getTokenFromCode = function(code) {
// of the type given by its first argument.
case 47: // '/'
return this.readToken_slash();
return this.readToken_slash()
case 37: case 42: // '%*'
return this.readToken_mult_modulo(code);
return this.readToken_mult_modulo(code)
case 124: case 38: // '|&'
return this.readToken_pipe_amp(code);
return this.readToken_pipe_amp(code)
case 94: // '^'
return this.readToken_caret();
return this.readToken_caret()
case 43: case 45: // '+-'
return this.readToken_plus_min(code);
return this.readToken_plus_min(code)
case 60: case 62: // '<>'
return this.readToken_lt_gt(code);
return this.readToken_lt_gt(code)
case 61: case 33: // '=!'
return this.readToken_eq_excl(code);
return this.readToken_eq_excl(code)
case 126: // '~'
return this.finishOp(tt.prefix, 1);
return this.finishOp(tt.prefix, 1)
}
this.raise(this.pos, "Unexpected character '" + codePointToString(code) + "'");
};
this.raise(this.pos, "Unexpected character '" + codePointToString(code) + "'")
}
pp.finishOp = function(type, size) {
var str = this.input.slice(this.pos, this.pos + size);
this.pos += size;
return this.finishToken(type, str);
};
let str = this.input.slice(this.pos, this.pos + size)
this.pos += size
return this.finishToken(type, str)
}
var regexpUnicodeSupport = false;
try { new RegExp("\uffff", "u"); regexpUnicodeSupport = true; }
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.
pp.readRegexp = function() {
var content = "", escaped, inClass, start = this.pos;
let escaped, inClass, start = this.pos
for (;;) {
if (this.pos >= this.input.length) this.raise(start, "Unterminated regular expression");
var ch = this.input.charAt(this.pos);
if (lineBreak.test(ch)) this.raise(start, "Unterminated regular expression");
if (this.pos >= this.input.length) this.raise(start, "Unterminated regular expression")
let ch = this.input.charAt(this.pos)
if (lineBreak.test(ch)) this.raise(start, "Unterminated regular expression")
if (!escaped) {
if (ch === "[") inClass = true;
else if (ch === "]" && inClass) inClass = false;
else if (ch === "/" && !inClass) break;
escaped = ch === "\\";
} else escaped = false;
++this.pos;
if (ch === "[") inClass = true
else if (ch === "]" && inClass) inClass = false
else if (ch === "/" && !inClass) break
escaped = ch === "\\"
} else escaped = false
++this.pos
}
var content = this.input.slice(start, this.pos);
++this.pos;
let content = this.input.slice(start, this.pos)
++this.pos
// Need to use `readWord1` because '\uXXXX' sequences are allowed
// here (don't ask).
var mods = this.readWord1();
var tmp = content;
let mods = this.readWord1()
let tmp = content
if (mods) {
var validFlags = /^[gmsiy]*$/;
if (this.options.ecmaVersion >= 6) validFlags = /^[gmsiyu]*$/;
if (!validFlags.test(mods)) this.raise(start, "Invalid regular expression flag");
let validFlags = /^[gmsiy]*$/
if (this.options.ecmaVersion >= 6) validFlags = /^[gmsiyu]*$/
if (!validFlags.test(mods)) this.raise(start, "Invalid regular expression flag")
if (mods.indexOf('u') >= 0 && !regexpUnicodeSupport) {
// Replace each astral symbol and every Unicode escape sequence that
// possibly represents an astral symbol or a paired surrogate with a
@ -415,177 +415,178 @@ pp.readRegexp = function() {
}
// Detect invalid regular expressions.
try {
new RegExp(tmp);
new RegExp(tmp)
} catch (e) {
if (e instanceof SyntaxError) this.raise(start, "Error parsing regular expression: " + e.message);
this.raise(e);
if (e instanceof SyntaxError) this.raise(start, "Error parsing regular expression: " + e.message)
this.raise(e)
}
// Get a regular expression object for this pattern-flag pair, or `null` in
// case the current environment doesn't support the flags it uses.
let value
try {
var value = new RegExp(content, mods);
value = new RegExp(content, mods)
} catch (err) {
value = null;
value = null
}
return this.finishToken(tt.regexp, {pattern: content, flags: mods, value: value});
};
return this.finishToken(tt.regexp, {pattern: content, flags: mods, value: value})
}
// Read an integer in the given radix. Return null if zero digits
// were read, the integer value otherwise. When `len` is given, this
// will return `null` unless the integer has exactly `len` digits.
pp.readInt = function(radix, len) {
var start = this.pos, total = 0;
for (var i = 0, e = len == null ? Infinity : len; i < e; ++i) {
var code = this.input.charCodeAt(this.pos), val;
let start = this.pos, total = 0
for (let i = 0, e = len == null ? Infinity : len; i < e; ++i) {
let code = this.input.charCodeAt(this.pos), val
if (code >= 97) val = code - 97 + 10; // a
else if (code >= 65) val = code - 65 + 10; // A
else if (code >= 48 && code <= 57) val = code - 48; // 0-9
else val = Infinity;
if (val >= radix) break;
++this.pos;
total = total * radix + val;
else val = Infinity
if (val >= radix) break
++this.pos
total = total * radix + val
}
if (this.pos === start || len != null && this.pos - start !== len) return null;
if (this.pos === start || len != null && this.pos - start !== len) return null
return total;
};
return total
}
pp.readRadixNumber = function(radix) {
this.pos += 2; // 0x
var val = this.readInt(radix);
if (val == null) this.raise(this.start + 2, "Expected number in radix " + radix);
if (isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number");
return this.finishToken(tt.num, val);
};
let val = this.readInt(radix)
if (val == null) this.raise(this.start + 2, "Expected number in radix " + radix)
if (isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number")
return this.finishToken(tt.num, val)
}
// Read an integer, octal integer, or floating-point number.
pp.readNumber = function(startsWithDot) {
var start = this.pos, isFloat = false, octal = this.input.charCodeAt(this.pos) === 48;
if (!startsWithDot && this.readInt(10) === null) this.raise(start, "Invalid number");
let start = this.pos, isFloat = false, octal = this.input.charCodeAt(this.pos) === 48
if (!startsWithDot && this.readInt(10) === null) this.raise(start, "Invalid number")
if (this.input.charCodeAt(this.pos) === 46) {
++this.pos;
this.readInt(10);
isFloat = true;
++this.pos
this.readInt(10)
isFloat = true
}
var next = this.input.charCodeAt(this.pos);
let next = this.input.charCodeAt(this.pos)
if (next === 69 || next === 101) { // 'eE'
next = this.input.charCodeAt(++this.pos);
next = this.input.charCodeAt(++this.pos)
if (next === 43 || next === 45) ++this.pos; // '+-'
if (this.readInt(10) === null) this.raise(start, "Invalid number");
isFloat = true;
if (this.readInt(10) === null) this.raise(start, "Invalid number")
isFloat = true
}
if (isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number");
if (isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number")
var str = this.input.slice(start, this.pos), val;
if (isFloat) val = parseFloat(str);
else if (!octal || str.length === 1) val = parseInt(str, 10);
else if (/[89]/.test(str) || this.strict) this.raise(start, "Invalid number");
else val = parseInt(str, 8);
return this.finishToken(tt.num, val);
};
let str = this.input.slice(start, this.pos), val
if (isFloat) val = parseFloat(str)
else if (!octal || str.length === 1) val = parseInt(str, 10)
else if (/[89]/.test(str) || this.strict) this.raise(start, "Invalid number")
else val = parseInt(str, 8)
return this.finishToken(tt.num, val)
}
// Read a string value, interpreting backslash-escapes.
pp.readCodePoint = function() {
var ch = this.input.charCodeAt(this.pos), code;
let ch = this.input.charCodeAt(this.pos), code
if (ch === 123) {
if (this.options.ecmaVersion < 6) this.unexpected();
++this.pos;
code = this.readHexChar(this.input.indexOf('}', this.pos) - this.pos);
++this.pos;
if (code > 0x10FFFF) this.unexpected();
if (this.options.ecmaVersion < 6) this.unexpected()
++this.pos
code = this.readHexChar(this.input.indexOf('}', this.pos) - this.pos)
++this.pos
if (code > 0x10FFFF) this.unexpected()
} else {
code = this.readHexChar(4);
code = this.readHexChar(4)
}
return code;
};
return code
}
function codePointToString(code) {
// UTF-16 Decoding
if (code <= 0xFFFF) return String.fromCharCode(code);
if (code <= 0xFFFF) return String.fromCharCode(code)
return String.fromCharCode(((code - 0x10000) >> 10) + 0xD800,
((code - 0x10000) & 1023) + 0xDC00);
((code - 0x10000) & 1023) + 0xDC00)
}
pp.readString = function(quote) {
var out = "", chunkStart = ++this.pos;
let out = "", chunkStart = ++this.pos
for (;;) {
if (this.pos >= this.input.length) this.raise(this.start, "Unterminated string constant");
var ch = this.input.charCodeAt(this.pos);
if (ch === quote) break;
if (this.pos >= this.input.length) this.raise(this.start, "Unterminated string constant")
let ch = this.input.charCodeAt(this.pos)
if (ch === quote) break
if (ch === 92) { // '\'
out += this.input.slice(chunkStart, this.pos);
out += this.readEscapedChar();
chunkStart = this.pos;
out += this.input.slice(chunkStart, this.pos)
out += this.readEscapedChar()
chunkStart = this.pos
} else {
if (isNewLine(ch)) this.raise(this.start, "Unterminated string constant");
++this.pos;
if (isNewLine(ch)) this.raise(this.start, "Unterminated string constant")
++this.pos
}
}
out += this.input.slice(chunkStart, this.pos++);
return this.finishToken(tt.string, out);
};
out += this.input.slice(chunkStart, this.pos++)
return this.finishToken(tt.string, out)
}
// Reads template string tokens.
pp.readTmplToken = function() {
var out = "", chunkStart = this.pos;
let out = "", chunkStart = this.pos
for (;;) {
if (this.pos >= this.input.length) this.raise(this.start, "Unterminated template");
var ch = this.input.charCodeAt(this.pos);
if (this.pos >= this.input.length) this.raise(this.start, "Unterminated template")
let ch = this.input.charCodeAt(this.pos)
if (ch === 96 || ch === 36 && this.input.charCodeAt(this.pos + 1) === 123) { // '`', '${'
if (this.pos === this.start && this.type === tt.template) {
if (ch === 36) {
this.pos += 2;
return this.finishToken(tt.dollarBraceL);
this.pos += 2
return this.finishToken(tt.dollarBraceL)
} else {
++this.pos;
return this.finishToken(tt.backQuote);
++this.pos
return this.finishToken(tt.backQuote)
}
}
out += this.input.slice(chunkStart, this.pos);
return this.finishToken(tt.template, out);
out += this.input.slice(chunkStart, this.pos)
return this.finishToken(tt.template, out)
}
if (ch === 92) { // '\'
out += this.input.slice(chunkStart, this.pos);
out += this.readEscapedChar();
chunkStart = this.pos;
out += this.input.slice(chunkStart, this.pos)
out += this.readEscapedChar()
chunkStart = this.pos
} else if (isNewLine(ch)) {
out += this.input.slice(chunkStart, this.pos);
++this.pos;
out += this.input.slice(chunkStart, this.pos)
++this.pos
if (ch === 13 && this.input.charCodeAt(this.pos) === 10) {
++this.pos;
out += "\n";
++this.pos
out += "\n"
} else {
out += String.fromCharCode(ch);
out += String.fromCharCode(ch)
}
if (this.options.locations) {
++this.curLine;
this.lineStart = this.pos;
++this.curLine
this.lineStart = this.pos
}
chunkStart = this.pos;
chunkStart = this.pos
} else {
++this.pos;
++this.pos
}
}
};
}
// Used to read escaped characters
pp.readEscapedChar = function() {
var ch = this.input.charCodeAt(++this.pos);
var octal = /^[0-7]+/.exec(this.input.slice(this.pos, this.pos + 3));
if (octal) octal = octal[0];
while (octal && parseInt(octal, 8) > 255) octal = octal.slice(0, -1);
if (octal === "0") octal = null;
++this.pos;
let ch = this.input.charCodeAt(++this.pos)
let octal = /^[0-7]+/.exec(this.input.slice(this.pos, this.pos + 3))
if (octal) octal = octal[0]
while (octal && parseInt(octal, 8) > 255) octal = octal.slice(0, -1)
if (octal === "0") octal = null
++this.pos
if (octal) {
if (this.strict) this.raise(this.pos - 2, "Octal literal in strict mode");
this.pos += octal.length - 1;
return String.fromCharCode(parseInt(octal, 8));
if (this.strict) this.raise(this.pos - 2, "Octal literal in strict mode")
this.pos += octal.length - 1
return String.fromCharCode(parseInt(octal, 8))
} else {
switch (ch) {
case 110: return "\n"; // 'n' -> '\n'
@ -599,26 +600,26 @@ pp.readEscapedChar = function() {
case 48: return "\0"; // 0 -> '\0'
case 13: if (this.input.charCodeAt(this.pos) === 10) ++this.pos; // '\r\n'
case 10: // ' \n'
if (this.options.locations) { this.lineStart = this.pos; ++this.curLine; }
return "";
default: return String.fromCharCode(ch);
if (this.options.locations) { this.lineStart = this.pos; ++this.curLine }
return ""
default: return String.fromCharCode(ch)
}
}
};
}
// Used to read character escape sequences ('\x', '\u', '\U').
pp.readHexChar = function(len) {
var n = this.readInt(16, len);
if (n === null) this.raise(this.start, "Bad character escape sequence");
return n;
};
let n = this.readInt(16, len)
if (n === null) this.raise(this.start, "Bad character escape sequence")
return n
}
// Used to signal to callers of `readWord1` whether the word
// contained any escape sequences. This is needed because words with
// escape sequences must not be interpreted as keywords.
var containsEsc;
var containsEsc
// Read an identifier, and return it as a string. Sets `containsEsc`
// to whether the word contained a '\u' escape.
@ -627,40 +628,40 @@ var containsEsc;
// as a micro-optimization.
pp.readWord1 = function() {
containsEsc = false;
var word = "", first = true, chunkStart = this.pos;
var astral = this.options.ecmaVersion >= 6;
containsEsc = false
let word = "", first = true, chunkStart = this.pos
let astral = this.options.ecmaVersion >= 6
while (this.pos < this.input.length) {
var ch = this.fullCharCodeAtPos();
let ch = this.fullCharCodeAtPos()
if (isIdentifierChar(ch, astral)) {
this.pos += ch <= 0xffff ? 1 : 2;
this.pos += ch <= 0xffff ? 1 : 2
} else if (ch === 92) { // "\"
containsEsc = true;
word += this.input.slice(chunkStart, this.pos);
var escStart = this.pos;
containsEsc = true
word += this.input.slice(chunkStart, this.pos)
let escStart = this.pos
if (this.input.charCodeAt(++this.pos) != 117) // "u"
this.raise(this.pos, "Expecting Unicode escape sequence \\uXXXX");
++this.pos;
var esc = this.readCodePoint();
this.raise(this.pos, "Expecting Unicode escape sequence \\uXXXX")
++this.pos
let esc = this.readCodePoint()
if (!(first ? isIdentifierStart : isIdentifierChar)(esc, astral))
this.raise(escStart, "Invalid Unicode escape");
word += codePointToString(esc);
chunkStart = this.pos;
this.raise(escStart, "Invalid Unicode escape")
word += codePointToString(esc)
chunkStart = this.pos
} else {
break;
break
}
first = false;
first = false
}
return word + this.input.slice(chunkStart, this.pos);
};
return word + this.input.slice(chunkStart, this.pos)
}
// Read an identifier or keyword token. Will check for reserved
// words when necessary.
pp.readWord = function() {
var word = this.readWord1();
var type = tt.name;
let word = this.readWord1()
let type = tt.name
if ((this.options.ecmaVersion >= 6 || !containsEsc) && this.isKeyword(word))
type = keywordTypes[word];
return this.finishToken(type, word);
};
type = keywordTypes[word]
return this.finishToken(type, word)
}