//
// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser
-import {types as tt} from "./tokentype"
-import {Parser} from "./state"
-import {DestructuringErrors} from "./parseutil"
-import {lineBreak} from "./whitespace"
-import {functionFlags, SCOPE_ARROW, SCOPE_SUPER, SCOPE_DIRECT_SUPER, BIND_OUTSIDE, BIND_VAR} from "./scopeflags"
+import {types as tt} from "./tokentype.js"
+import {types as tokenCtxTypes} from "./tokencontext.js"
+import {Parser} from "./state.js"
+import {DestructuringErrors} from "./parseutil.js"
+import {lineBreak} from "./whitespace.js"
+import {functionFlags, SCOPE_ARROW, SCOPE_SUPER, SCOPE_DIRECT_SUPER, BIND_OUTSIDE, BIND_VAR} from "./scopeflags.js"
// Nick
import CleanCSS from "@ndcode/clean-css"
if (this.options.ecmaVersion >= 6) {
if (name === "__proto__" && kind === "init") {
if (propHash.proto) {
- if (refDestructuringErrors && refDestructuringErrors.doubleProto < 0) refDestructuringErrors.doubleProto = key.start
- // Backwards-compat kludge. Can be removed in version 6.0
- else this.raiseRecoverable(key.start, "Redefinition of __proto__ property")
+ if (refDestructuringErrors) {
+ if (refDestructuringErrors.doubleProto < 0) {
+ refDestructuringErrors.doubleProto = key.start
+ }
+ } else {
+ this.raiseRecoverable(key.start, "Redefinition of __proto__ property")
+ }
}
propHash.proto = true
}
// and object pattern might appear (so it's possible to raise
// delayed syntax error at correct position).
-pp.parseExpression = function(noIn, refDestructuringErrors) {
+pp.parseExpression = function(forInit, refDestructuringErrors) {
let startPos = this.start, startLoc = this.startLoc
- let expr = this.parseMaybeAssign(noIn, refDestructuringErrors)
+ let expr = this.parseMaybeAssign(forInit, refDestructuringErrors)
if (this.type === tt.comma) {
let node = this.startNodeAt(startPos, startLoc)
node.expressions = [expr]
- while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn, refDestructuringErrors))
+ while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(forInit, refDestructuringErrors))
return this.finishNode(node, "SequenceExpression")
}
return expr
// Parse an assignment expression. This includes applications of
// operators like `+=`.
-pp.parseMaybeAssign = function(noIn, refDestructuringErrors, afterLeftParse) {
+pp.parseMaybeAssign = function(forInit, refDestructuringErrors, afterLeftParse) {
if (this.isContextual("yield")) {
- if (this.inGenerator) return this.parseYield()
+ if (this.inGenerator) return this.parseYield(forInit)
// The tokenizer will assume an expression is allowed after
// `yield`, but this isn't that kind of yield
else this.exprAllowed = false
}
- let ownDestructuringErrors = false, oldParenAssign = -1, oldTrailingComma = -1, oldShorthandAssign = -1
+ let ownDestructuringErrors = false, oldParenAssign = -1, oldTrailingComma = -1, oldDoubleProto = -1
if (refDestructuringErrors) {
oldParenAssign = refDestructuringErrors.parenthesizedAssign
oldTrailingComma = refDestructuringErrors.trailingComma
- oldShorthandAssign = refDestructuringErrors.shorthandAssign
- refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = refDestructuringErrors.shorthandAssign = -1
+ oldDoubleProto = refDestructuringErrors.doubleProto
+ refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = -1
} else {
refDestructuringErrors = new DestructuringErrors
ownDestructuringErrors = true
}
let startPos = this.start, startLoc = this.startLoc
- if (this.type === tt.parenL || this.type === tt.name)
+ if (this.type === tt.parenL || this.type === tt.name) {
this.potentialArrowAt = this.start
- let left = this.parseMaybeConditional(noIn, refDestructuringErrors)
+ this.potentialArrowInForAwait = forInit === "await"
+ }
+ let left = this.parseMaybeConditional(forInit, refDestructuringErrors)
if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc)
if (this.type.isAssign) {
let node = this.startNodeAt(startPos, startLoc)
node.operator = this.value
- node.left = this.type === tt.eq ? this.toAssignable(left, false, refDestructuringErrors) : left
- if (!ownDestructuringErrors) DestructuringErrors.call(refDestructuringErrors)
- refDestructuringErrors.shorthandAssign = -1 // reset because shorthand default was used correctly
- this.checkLVal(left)
+ if (this.type === tt.eq)
+ left = this.toAssignable(left, false, refDestructuringErrors)
+ if (!ownDestructuringErrors) {
+ refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = refDestructuringErrors.doubleProto = -1
+ }
+ if (refDestructuringErrors.shorthandAssign >= left.start)
+ refDestructuringErrors.shorthandAssign = -1 // reset because shorthand default was used correctly
+ if (this.type === tt.eq)
+ this.checkLValPattern(left)
+ else
+ this.checkLValSimple(left)
+ node.left = left
this.next()
- node.right = this.parseMaybeAssign(noIn)
+ node.right = this.parseMaybeAssign(forInit)
+ if (oldDoubleProto > -1) refDestructuringErrors.doubleProto = oldDoubleProto
return this.finishNode(node, "AssignmentExpression")
} else {
if (ownDestructuringErrors) this.checkExpressionErrors(refDestructuringErrors, true)
}
if (oldParenAssign > -1) refDestructuringErrors.parenthesizedAssign = oldParenAssign
if (oldTrailingComma > -1) refDestructuringErrors.trailingComma = oldTrailingComma
- if (oldShorthandAssign > -1) refDestructuringErrors.shorthandAssign = oldShorthandAssign
return left
}
// Parse a ternary conditional (`?:`) operator.
-pp.parseMaybeConditional = function(noIn, refDestructuringErrors) {
+pp.parseMaybeConditional = function(forInit, refDestructuringErrors) {
let startPos = this.start, startLoc = this.startLoc
- let expr = this.parseExprOps(noIn, refDestructuringErrors)
+ let expr = this.parseExprOps(forInit, refDestructuringErrors)
if (this.checkExpressionErrors(refDestructuringErrors)) return expr
if (this.eat(tt.question)) {
let node = this.startNodeAt(startPos, startLoc)
node.test = expr
node.consequent = this.parseMaybeAssign()
this.expect(tt.colon)
- node.alternate = this.parseMaybeAssign(noIn)
+ node.alternate = this.parseMaybeAssign(forInit)
return this.finishNode(node, "ConditionalExpression")
}
return expr
// Start the precedence parser.
-pp.parseExprOps = function(noIn, refDestructuringErrors) {
+pp.parseExprOps = function(forInit, refDestructuringErrors) {
let startPos = this.start, startLoc = this.startLoc
- let expr = this.parseMaybeUnary(refDestructuringErrors, false)
+ let expr = this.parseMaybeUnary(refDestructuringErrors, false, false, forInit)
if (this.checkExpressionErrors(refDestructuringErrors)) return expr
- return expr.start === startPos && expr.type === "ArrowFunctionExpression" ? expr : this.parseExprOp(expr, startPos, startLoc, -1, noIn)
+ return expr.start === startPos && expr.type === "ArrowFunctionExpression" ? expr : this.parseExprOp(expr, startPos, startLoc, -1, forInit)
}
// Parse binary operators with the operator precedence parsing
// defer further parser to one of its callers when it encounters an
// operator that has a lower precedence than the set it is parsing.
-pp.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) {
+pp.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, forInit) {
let prec = this.type.binop
- if (prec != null && (!noIn || this.type !== tt._in)) {
+ if (prec != null && (!forInit || this.type !== tt._in)) {
if (prec > minPrec) {
let logical = this.type === tt.logicalOR || this.type === tt.logicalAND
+ let coalesce = this.type === tt.coalesce
+ if (coalesce) {
+ // Handle the precedence of `tt.coalesce` as equal to the range of logical expressions.
+ // In other words, `node.right` shouldn't contain logical expressions in order to check the mixed error.
+ prec = tt.logicalAND.binop
+ }
let op = this.value
this.next()
let startPos = this.start, startLoc = this.startLoc
- let right = this.parseExprOp(this.parseMaybeUnary(null, false), startPos, startLoc, prec, noIn)
- let node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical)
- return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn)
+ let right = this.parseExprOp(this.parseMaybeUnary(null, false, false, forInit), startPos, startLoc, prec, forInit)
+ let node = this.buildBinary(leftStartPos, leftStartLoc, left, right, op, logical || coalesce)
+ if ((logical && this.type === tt.coalesce) || (coalesce && (this.type === tt.logicalOR || this.type === tt.logicalAND))) {
+ this.raiseRecoverable(this.start, "Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses")
+ }
+ return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, forInit)
}
}
return left
}
pp.buildBinary = function(startPos, startLoc, left, right, op, logical) {
+ if (right.type === "PrivateIdentifier") this.raise(right.start, "Private identifier can only be left side of binary expression")
let node = this.startNodeAt(startPos, startLoc)
node.left = left
node.operator = op
// Parse unary operators, both prefix and postfix.
-pp.parseMaybeUnary = function(refDestructuringErrors, sawUnary) {
+pp.parseMaybeUnary = function(refDestructuringErrors, sawUnary, incDec, forInit) {
let startPos = this.start, startLoc = this.startLoc, expr
- if (this.isContextual("await") && (this.inAsync || (!this.inFunction && this.options.allowAwaitOutsideFunction))) {
- expr = this.parseAwait()
+ if (this.isContextual("await") && this.canAwait) {
+ expr = this.parseAwait(forInit)
sawUnary = true
} else if (this.type.prefix) {
let node = this.startNode(), update = this.type === tt.incDec
node.operator = this.value
node.prefix = true
this.next()
- node.argument = this.parseMaybeUnary(null, true)
+ node.argument = this.parseMaybeUnary(null, true, update, forInit)
this.checkExpressionErrors(refDestructuringErrors, true)
- if (update) this.checkLVal(node.argument)
+ if (update) this.checkLValSimple(node.argument)
else if (this.strict && node.operator === "delete" &&
node.argument.type === "Identifier")
this.raiseRecoverable(node.start, "Deleting local variable in strict mode")
+ else if (node.operator === "delete" && isPrivateFieldAccess(node.argument))
+ this.raiseRecoverable(node.start, "Private fields can not be deleted")
else sawUnary = true
expr = this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression")
+ } else if (!sawUnary && this.type === tt.privateId) {
+ if (forInit || this.privateNameStack.length === 0) this.unexpected()
+ expr = this.parsePrivateIdent()
+ // only could be private fields in 'in', such as #x in obj
+ if (this.type !== tt._in) this.unexpected()
} else {
- expr = this.parseExprSubscripts(refDestructuringErrors)
+ expr = this.parseExprSubscripts(refDestructuringErrors, forInit)
if (this.checkExpressionErrors(refDestructuringErrors)) return expr
while (this.type.postfix && !this.canInsertSemicolon()) {
let node = this.startNodeAt(startPos, startLoc)
node.operator = this.value
node.prefix = false
node.argument = expr
- this.checkLVal(expr)
+ this.checkLValSimple(expr)
this.next()
expr = this.finishNode(node, "UpdateExpression")
}
}
- if (!sawUnary && this.eat(tt.starstar))
- return this.buildBinary(startPos, startLoc, expr, this.parseMaybeUnary(null, false), "**", false)
- else
+ if (!incDec && this.eat(tt.starstar)) {
+ if (sawUnary)
+ this.unexpected(this.lastTokStart)
+ else
+ return this.buildBinary(startPos, startLoc, expr, this.parseMaybeUnary(null, false, false, forInit), "**", false)
+ } else {
return expr
+ }
+}
+
+function isPrivateFieldAccess(node) {
+ return (
+ node.type === "MemberExpression" && node.property.type === "PrivateIdentifier" ||
+ node.type === "ChainExpression" && isPrivateFieldAccess(node.expression)
+ )
}
// Parse call, dot, and `[]`-subscript expressions.
-pp.parseExprSubscripts = function(refDestructuringErrors) {
+pp.parseExprSubscripts = function(refDestructuringErrors, forInit) {
let startPos = this.start, startLoc = this.startLoc
- let expr = this.parseExprAtom(refDestructuringErrors)
- let skipArrowSubscripts = expr.type === "ArrowFunctionExpression" && this.input.slice(this.lastTokStart, this.lastTokEnd) !== ")"
- if (this.checkExpressionErrors(refDestructuringErrors) || skipArrowSubscripts) return expr
- let result = this.parseSubscripts(expr, startPos, startLoc)
+ let expr = this.parseExprAtom(refDestructuringErrors, forInit)
+ if (expr.type === "ArrowFunctionExpression" && this.input.slice(this.lastTokStart, this.lastTokEnd) !== ")")
+ return expr
+ let result = this.parseSubscripts(expr, startPos, startLoc, false, forInit)
if (refDestructuringErrors && result.type === "MemberExpression") {
if (refDestructuringErrors.parenthesizedAssign >= result.start) refDestructuringErrors.parenthesizedAssign = -1
if (refDestructuringErrors.parenthesizedBind >= result.start) refDestructuringErrors.parenthesizedBind = -1
+ if (refDestructuringErrors.trailingComma >= result.start) refDestructuringErrors.trailingComma = -1
}
return result
}
-pp.parseSubscripts = function(base, startPos, startLoc, noCalls) {
+pp.parseSubscripts = function(base, startPos, startLoc, noCalls, forInit) {
let maybeAsyncArrow = this.options.ecmaVersion >= 8 && base.type === "Identifier" && base.name === "async" &&
- this.lastTokEnd === base.end && !this.canInsertSemicolon() && this.input.slice(base.start, base.end) === "async"
- for (let computed;;) {
- if ((computed = this.eat(tt.bracketL)) || this.eat(tt.dot)) {
- let node = this.startNodeAt(startPos, startLoc)
- node.object = base
- //node.property = computed ? this.parseExpression() : this.parseIdent(true)
- node.property = computed ? this.parseExpression() : this.type === tt.string ? this.parseLiteral(this.value) : this.parseIdent(true) // Nick
- node.computed = !!computed
- if (computed) this.expect(tt.bracketR)
- base = this.finishNode(node, "MemberExpression")
+ this.lastTokEnd === base.end && !this.canInsertSemicolon() && base.end - base.start === 5 &&
+ this.potentialArrowAt === base.start
+ let optionalChained = false
+
+ while (true) {
+ let element = this.parseSubscript(base, startPos, startLoc, noCalls, maybeAsyncArrow, optionalChained, forInit)
+
+ if (element.optional) optionalChained = true
+ if (element === base || element.type === "ArrowFunctionExpression") {
+ if (optionalChained) {
+ const chainNode = this.startNodeAt(startPos, startLoc)
+ chainNode.expression = element
+ element = this.finishNode(chainNode, "ChainExpression")
+ }
+ return element
+ }
+
+ base = element
+ }
+}
+
+pp.parseSubscript = function(base, startPos, startLoc, noCalls, maybeAsyncArrow, optionalChained, forInit) {
+ let optionalSupported = this.options.ecmaVersion >= 11
+ let optional = optionalSupported && this.eat(tt.questionDot)
+ if (noCalls && optional) this.raise(this.lastTokStart, "Optional chaining cannot appear in the callee of new expressions")
+
+ let computed = this.eat(tt.bracketL)
+ if (computed || (optional && this.type !== tt.parenL && this.type !== tt.backQuote) || this.eat(tt.dot)) {
+ let node = this.startNodeAt(startPos, startLoc)
+ node.object = base
+ if (computed) {
+ node.property = this.parseExpression()
+ this.expect(tt.bracketR)
+ } else if (this.type === tt.privateId && base.type !== "Super") {
+ node.property = this.parsePrivateIdent()
+ } else if (this.type === tt.string) { // Nick
+ node.property = this.parseLiteral(this.value)
+ } else {
+ node.property = this.parseIdent(this.options.allowReserved !== "never")
+ }
+ node.computed = !!computed
+ if (optionalSupported) {
+ node.optional = optional
+ }
+ base = this.finishNode(node, "MemberExpression")
} else if (this.eat(tt.hash)) { // Nick
let node = this.startNodeAt(startPos, startLoc)
node.object = base
//node.property = this.parseIdent(true)
node.property = this.type === tt.string ? this.parseLiteral(this.value) : this.parseIdent(true) // Nick
base = this.finishNode(node, "MemberExpressionHash")
- } else if (!noCalls && this.eat(tt.parenL)) {
- let refDestructuringErrors = new DestructuringErrors, oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos
- this.yieldPos = 0
- this.awaitPos = 0
- let exprList = this.parseExprList(tt.parenR, this.options.ecmaVersion >= 8, false, refDestructuringErrors)
- if (maybeAsyncArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) {
- this.checkPatternErrors(refDestructuringErrors, false)
- this.checkYieldAwaitInDefaultParams()
- this.yieldPos = oldYieldPos
- this.awaitPos = oldAwaitPos
- return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, true)
- }
- this.checkExpressionErrors(refDestructuringErrors, true)
- this.yieldPos = oldYieldPos || this.yieldPos
- this.awaitPos = oldAwaitPos || this.awaitPos
- let node = this.startNodeAt(startPos, startLoc)
- node.callee = base
- node.arguments = exprList
- base = this.finishNode(node, "CallExpression")
- } else if (this.type === tt.backQuote) {
- let node = this.startNodeAt(startPos, startLoc)
- node.tag = base
- node.quasi = this.parseTemplate({isTagged: true})
- base = this.finishNode(node, "TaggedTemplateExpression")
- } else if (this.type === tt.braceL) { // Nick
- let node = this.startNodeAt(startPos, startLoc)
- node.tag = base
- if (
- (
- base.type === 'Identifier' &&
- base.name === 'style'
- ) ||
- (
- base.type === 'CallExpression' &&
- base.callee.type === 'Identifier' &&
- base.callee.name === 'style'
- )
- ) {
- let render = clean_css.minifyEmbedded(this.input, this.pos)
- if (render.errors.length)
- throw render.errors
- for (let i = 0; i < render.warnings.length; ++i)
- console.log(`clean-css warning: ${render.warnings[i]}`)
- node.body = {
- type: 'BlockStatement',
- body: [
- {
- type: 'ExpressionStatement',
- expression: {
- type: 'Literal',
- value: render.styles // we simply assume it does not contain </style> tags, should HTML escape it??
- }
+ } else if (!noCalls && this.eat(tt.parenL)) {
+ let refDestructuringErrors = new DestructuringErrors, oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos
+ this.yieldPos = 0
+ this.awaitPos = 0
+ this.awaitIdentPos = 0
+ let exprList = this.parseExprList(tt.parenR, this.options.ecmaVersion >= 8, false, refDestructuringErrors)
+ if (maybeAsyncArrow && !optional && !this.canInsertSemicolon() && this.eat(tt.arrow)) {
+ this.checkPatternErrors(refDestructuringErrors, false)
+ this.checkYieldAwaitInDefaultParams()
+ if (this.awaitIdentPos > 0)
+ this.raise(this.awaitIdentPos, "Cannot use 'await' as identifier inside an async function")
+ this.yieldPos = oldYieldPos
+ this.awaitPos = oldAwaitPos
+ this.awaitIdentPos = oldAwaitIdentPos
+ return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, true, forInit)
+ }
+ this.checkExpressionErrors(refDestructuringErrors, true)
+ this.yieldPos = oldYieldPos || this.yieldPos
+ this.awaitPos = oldAwaitPos || this.awaitPos
+ this.awaitIdentPos = oldAwaitIdentPos || this.awaitIdentPos
+ let node = this.startNodeAt(startPos, startLoc)
+ node.callee = base
+ node.arguments = exprList
+ if (optionalSupported) {
+ node.optional = optional
+ }
+ base = this.finishNode(node, "CallExpression")
+ } else if (this.type === tt.backQuote) {
+ if (optional || optionalChained) {
+ this.raise(this.start, "Optional chaining cannot appear in the tag of tagged template expressions")
+ }
+ let node = this.startNodeAt(startPos, startLoc)
+ node.tag = base
+ node.quasi = this.parseTemplate({isTagged: true})
+ base = this.finishNode(node, "TaggedTemplateExpression")
+ } else if (this.type === tt.braceL) { // Nick
+ let node = this.startNodeAt(startPos, startLoc)
+ node.tag = base
+ if (
+ (
+ base.type === 'Identifier' &&
+ base.name === 'style'
+ ) ||
+ (
+ base.type === 'CallExpression' &&
+ base.callee.type === 'Identifier' &&
+ base.callee.name === 'style'
+ )
+ ) {
+ let render = clean_css.minifyEmbedded(this.input, this.pos)
+ if (render.errors.length)
+ throw render.errors
+ for (let i = 0; i < render.warnings.length; ++i)
+ console.log(`clean-css warning: ${render.warnings[i]}`)
+ node.body = {
+ type: 'BlockStatement',
+ body: [
+ {
+ type: 'ExpressionStatement',
+ expression: {
+ type: 'Literal',
+ value: render.styles // we simply assume it does not contain </style> tags, should HTML escape it??
}
- ]
- }
- this.pos = render.embeddedEnd
- this.next()
- this.expect(tt.braceR)
+ }
+ ]
}
- else
- node.body = this.parseBlock(false)
- base = this.finishNode(node, "HTMLExpression")
- } else {
- return base
+ this.pos = render.embeddedEnd
+ this.next()
+ this.expect(tt.braceR)
}
+ else
+ node.body = this.parseBlock(false)
+ base = this.finishNode(node, "HTMLExpression")
}
+ return base
}
// Parse an atomic expression — either a single token that is an
// `new`, or an expression wrapped in punctuation like `()`, `[]`,
// or `{}`.
-pp.parseExprAtom = function(refDestructuringErrors) {
+pp.parseExprAtom = function(refDestructuringErrors, forInit) {
+ // If a division operator appears in an expression position, the
+ // tokenizer got confused, and we force it to read a regexp instead.
+ if (this.type === tt.slash) this.readRegexp()
+
let node, canBeArrow = this.potentialArrowAt === this.start
switch (this.type) {
case tt._super:
// super [ Expression ]
// super . IdentifierName
// SuperCall:
- // super Arguments
+ // super ( Arguments )
if (this.type !== tt.dot && this.type !== tt.bracketL && this.type !== tt.parenL)
this.unexpected()
return this.finishNode(node, "Super")
case tt.name:
let startPos = this.start, startLoc = this.startLoc, containsEsc = this.containsEsc
- let id = this.parseIdent(this.type !== tt.name)
- if (this.options.ecmaVersion >= 8 && !containsEsc && id.name === "async" && !this.canInsertSemicolon() && this.eat(tt._function))
- return this.parseFunction(this.startNodeAt(startPos, startLoc), 0, false, true)
+ let id = this.parseIdent(false)
+ if (this.options.ecmaVersion >= 8 && !containsEsc && id.name === "async" && !this.canInsertSemicolon() && this.eat(tt._function)) {
+ this.overrideContext(tokenCtxTypes.f_expr)
+ return this.parseFunction(this.startNodeAt(startPos, startLoc), 0, false, true, forInit)
+ }
if (canBeArrow && !this.canInsertSemicolon()) {
if (this.eat(tt.arrow))
- return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], false)
- if (this.options.ecmaVersion >= 8 && id.name === "async" && this.type === tt.name && !containsEsc) {
- id = this.parseIdent()
+ return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], false, forInit)
+ if (this.options.ecmaVersion >= 8 && id.name === "async" && this.type === tt.name && !containsEsc &&
+ (!this.potentialArrowInForAwait || this.value !== "of" || this.containsEsc)) {
+ id = this.parseIdent(false)
if (this.canInsertSemicolon() || !this.eat(tt.arrow))
this.unexpected()
- return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], true)
+ return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id], true, forInit)
}
}
return id
return this.finishNode(node, "Literal")
case tt.parenL:
- let start = this.start, expr = this.parseParenAndDistinguishExpression(canBeArrow)
+ let start = this.start, expr = this.parseParenAndDistinguishExpression(canBeArrow, forInit)
if (refDestructuringErrors) {
if (refDestructuringErrors.parenthesizedAssign < 0 && !this.isSimpleAssignTarget(expr))
refDestructuringErrors.parenthesizedAssign = start
return this.finishNode(node, "ArrayExpression")
case tt.braceL:
+ this.overrideContext(tokenCtxTypes.b_expr)
return this.parseObj(false, refDestructuringErrors)
case tt._function:
case tt.backQuote:
return this.parseTemplate()
+ case tt._import:
+ if (this.options.ecmaVersion >= 11) {
+ return this.parseExprImport()
+ } else {
+ return this.unexpected()
+ }
+
default:
return this.parseIdent(true) // Nick this.unexpected()
}
}
+pp.parseExprImport = function() {
+ const node = this.startNode()
+
+ // Consume `import` as an identifier for `import.meta`.
+ // Because `this.parseIdent(true)` doesn't check escape sequences, it needs the check of `this.containsEsc`.
+ if (this.containsEsc) this.raiseRecoverable(this.start, "Escape sequence in keyword import")
+ const meta = this.parseIdent(true)
+
+ switch (this.type) {
+ case tt.parenL:
+ return this.parseDynamicImport(node)
+ case tt.dot:
+ node.meta = meta
+ return this.parseImportMeta(node)
+ default:
+ this.unexpected()
+ }
+}
+
+pp.parseDynamicImport = function(node) {
+ this.next() // skip `(`
+
+ // Parse node.source.
+ node.source = this.parseMaybeAssign()
+
+ // Verify ending.
+ if (!this.eat(tt.parenR)) {
+ const errorPos = this.start
+ if (this.eat(tt.comma) && this.eat(tt.parenR)) {
+ this.raiseRecoverable(errorPos, "Trailing comma is not allowed in import()")
+ } else {
+ this.unexpected(errorPos)
+ }
+ }
+
+ return this.finishNode(node, "ImportExpression")
+}
+
+pp.parseImportMeta = function(node) {
+ this.next() // skip `.`
+
+ const containsEsc = this.containsEsc
+ node.property = this.parseIdent(true)
+
+ if (node.property.name !== "meta")
+ this.raiseRecoverable(node.property.start, "The only valid meta property for import is 'import.meta'")
+ if (containsEsc)
+ this.raiseRecoverable(node.start, "'import.meta' must not contain escaped characters")
+ if (this.options.sourceType !== "module" && !this.options.allowImportExportEverywhere)
+ this.raiseRecoverable(node.start, "Cannot use 'import.meta' outside a module")
+
+ return this.finishNode(node, "MetaProperty")
+}
+
pp.parseLiteral = function(value) {
let node = this.startNode()
node.value = value
node.raw = this.input.slice(this.start, this.end)
+ if (node.raw.charCodeAt(node.raw.length - 1) === 110) node.bigint = node.raw.slice(0, -1).replace(/_/g, "")
this.next()
return this.finishNode(node, "Literal")
}
return val
}
-pp.parseParenAndDistinguishExpression = function(canBeArrow) {
+pp.parseParenAndDistinguishExpression = function(canBeArrow, forInit) {
let startPos = this.start, startLoc = this.startLoc, val, allowTrailingComma = this.options.ecmaVersion >= 8
if (this.options.ecmaVersion >= 6) {
this.next()
let refDestructuringErrors = new DestructuringErrors, oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, spreadStart
this.yieldPos = 0
this.awaitPos = 0
+ // Do not save awaitIdentPos to allow checking awaits nested in parameters
while (this.type !== tt.parenR) {
first ? first = false : this.expect(tt.comma)
if (allowTrailingComma && this.afterTrailingComma(tt.parenR, true)) {
exprList.push(this.parseMaybeAssign(false, refDestructuringErrors, this.parseParenItem))
}
}
- let innerEndPos = this.start, innerEndLoc = this.startLoc
+ let innerEndPos = this.lastTokEnd, innerEndLoc = this.lastTokEndLoc
this.expect(tt.parenR)
if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) {
this.checkYieldAwaitInDefaultParams()
this.yieldPos = oldYieldPos
this.awaitPos = oldAwaitPos
- return this.parseParenArrowList(startPos, startLoc, exprList)
+ return this.parseParenArrowList(startPos, startLoc, exprList, forInit)
}
if (!exprList.length || lastIsComma) this.unexpected(this.lastTokStart)
return item
}
-pp.parseParenArrowList = function(startPos, startLoc, exprList) {
- return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList)
+pp.parseParenArrowList = function(startPos, startLoc, exprList, forInit) {
+ return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList, false, forInit)
}
// New's precedence is slightly tricky. It must allow its argument to
const empty = []
pp.parseNew = function() {
+ if (this.containsEsc) this.raiseRecoverable(this.start, "Escape sequence in keyword new")
let node = this.startNode()
let meta = this.parseIdent(true)
if (this.options.ecmaVersion >= 6 && this.eat(tt.dot)) {
node.meta = meta
let containsEsc = this.containsEsc
node.property = this.parseIdent(true)
- if (node.property.name !== "target" || containsEsc)
- this.raiseRecoverable(node.property.start, "The only valid meta property for new is new.target")
- if (!this.inNonArrowFunction())
- this.raiseRecoverable(node.start, "new.target can only be used in functions")
+ if (node.property.name !== "target")
+ this.raiseRecoverable(node.property.start, "The only valid meta property for new is 'new.target'")
+ if (containsEsc)
+ this.raiseRecoverable(node.start, "'new.target' must not contain escaped characters")
+ if (!this.allowNewDotTarget)
+ this.raiseRecoverable(node.start, "'new.target' can only be used in functions and class static block")
return this.finishNode(node, "MetaProperty")
}
- let startPos = this.start, startLoc = this.startLoc
- node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true)
+ let startPos = this.start, startLoc = this.startLoc, isImport = this.type === tt._import
+ node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true, false)
+ if (isImport && node.callee.type === "ImportExpression") {
+ this.raise(startPos, "Cannot use new with import()")
+ }
if (this.eat(tt.parenL)) node.arguments = this.parseExprList(tt.parenR, this.options.ecmaVersion >= 8, false)
else node.arguments = empty
return this.finishNode(node, "NewExpression")
while (!this.eat(tt.braceR)) {
if (!first) {
this.expect(tt.comma)
- if (this.afterTrailingComma(tt.braceR)) break
+ if (this.options.ecmaVersion >= 5 && this.afterTrailingComma(tt.braceR)) break
} else first = false
const prop = this.parseProperty(isPattern, refDestructuringErrors)
} else if (!isPattern && !containsEsc &&
this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" &&
(prop.key.name === "get" || prop.key.name === "set") &&
- (this.type !== tt.comma && this.type !== tt.braceR)) {
+ (this.type !== tt.comma && this.type !== tt.braceR && this.type !== tt.eq)) {
if (isGenerator || isAsync) this.unexpected()
prop.kind = prop.key.name
this.parsePropertyName(prop)
this.raiseRecoverable(prop.value.params[0].start, "Setter cannot use rest params")
}
} else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {
+ if (isGenerator || isAsync) this.unexpected()
this.checkUnreserved(prop.key)
+ if (prop.key.name === "await" && !this.awaitIdentPos)
+ this.awaitIdentPos = startPos
prop.kind = "init"
if (isPattern) {
- prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key)
+ prop.value = this.parseMaybeDefault(startPos, startLoc, this.copyNode(prop.key))
} else if (this.type === tt.eq && refDestructuringErrors) {
if (refDestructuringErrors.shorthandAssign < 0)
refDestructuringErrors.shorthandAssign = this.start
- prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key)
+ prop.value = this.parseMaybeDefault(startPos, startLoc, this.copyNode(prop.key))
} else {
- prop.value = prop.key
+ prop.value = this.copyNode(prop.key)
}
prop.shorthand = true
} else this.unexpected()
prop.computed = false
}
}
- return prop.key = this.type === tt.num || this.type === tt.string ? this.parseExprAtom() : this.parseIdent(true)
+ return prop.key = this.type === tt.num || this.type === tt.string ? this.parseExprAtom() : this.parseIdent(this.options.allowReserved !== "never")
}
// Initialize empty function node.
// Parse object or class method.
pp.parseMethod = function(isGenerator, isAsync, allowDirectSuper) {
- let node = this.startNode(), oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos
+ let node = this.startNode(), oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos
this.initFunction(node)
if (this.options.ecmaVersion >= 6)
this.yieldPos = 0
this.awaitPos = 0
+ this.awaitIdentPos = 0
this.enterScope(functionFlags(isAsync, node.generator) | SCOPE_SUPER | (allowDirectSuper ? SCOPE_DIRECT_SUPER : 0))
this.expect(tt.parenL)
node.params = this.parseBindingList(tt.parenR, false, this.options.ecmaVersion >= 8)
this.checkYieldAwaitInDefaultParams()
- this.parseFunctionBody(node, false)
+ this.parseFunctionBody(node, false, true, false)
this.yieldPos = oldYieldPos
this.awaitPos = oldAwaitPos
+ this.awaitIdentPos = oldAwaitIdentPos
return this.finishNode(node, "FunctionExpression")
}
// Parse arrow function expression with given parameters.
-pp.parseArrowExpression = function(node, params, isAsync) {
- let oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos
+pp.parseArrowExpression = function(node, params, isAsync, forInit) {
+ let oldYieldPos = this.yieldPos, oldAwaitPos = this.awaitPos, oldAwaitIdentPos = this.awaitIdentPos
this.enterScope(functionFlags(isAsync, false) | SCOPE_ARROW)
this.initFunction(node)
this.yieldPos = 0
this.awaitPos = 0
+ this.awaitIdentPos = 0
node.params = this.toAssignableList(params, true)
- this.parseFunctionBody(node, true)
+ this.parseFunctionBody(node, true, false, forInit)
this.yieldPos = oldYieldPos
this.awaitPos = oldAwaitPos
+ this.awaitIdentPos = oldAwaitIdentPos
return this.finishNode(node, "ArrowFunctionExpression")
}
// Parse function body and check parameters.
-pp.parseFunctionBody = function(node, isArrowFunction) {
+pp.parseFunctionBody = function(node, isArrowFunction, isMethod, forInit) {
let isExpression = isArrowFunction && this.type !== tt.braceL
let oldStrict = this.strict, useStrict = false
if (isExpression) {
- node.body = this.parseMaybeAssign()
+ node.body = this.parseMaybeAssign(forInit)
node.expression = true
this.checkParams(node, false)
} else {
// Add the params to varDeclaredNames to ensure that an error is thrown
// if a let/const declaration in the function clashes with one of the params.
- this.checkParams(node, !oldStrict && !useStrict && !isArrowFunction && this.isSimpleParamList(node.params))
- node.body = this.parseBlock(false)
+ this.checkParams(node, !oldStrict && !useStrict && !isArrowFunction && !isMethod && this.isSimpleParamList(node.params))
+ // Ensure the function name isn't a forbidden identifier in strict mode, e.g. 'eval'
+ if (this.strict && node.id) this.checkLValSimple(node.id, BIND_OUTSIDE)
+ node.body = this.parseBlock(false, undefined, useStrict && !oldStrict)
node.expression = false
this.adaptDirectivePrologue(node.body.body)
this.labels = oldLabels
}
this.exitScope()
-
- // Ensure the function name isn't a forbidden identifier in strict mode, e.g. 'eval'
- if (this.strict && node.id) this.checkLVal(node.id, BIND_OUTSIDE)
- this.strict = oldStrict
}
pp.isSimpleParamList = function(params) {
// or "arguments" and duplicate parameters.
pp.checkParams = function(node, allowDuplicates) {
- let nameHash = {}
+ let nameHash = Object.create(null)
for (let param of node.params)
- this.checkLVal(param, BIND_VAR, allowDuplicates ? null : nameHash)
+ this.checkLValInnerPattern(param, BIND_VAR, allowDuplicates ? null : nameHash)
}
// Parses a comma-separated list of expressions, and returns them as
pp.checkUnreserved = function({start, end, name}) {
if (this.inGenerator && name === "yield")
- this.raiseRecoverable(start, "Can not use 'yield' as identifier inside a generator")
+ this.raiseRecoverable(start, "Cannot use 'yield' as identifier inside a generator")
if (this.inAsync && name === "await")
- this.raiseRecoverable(start, "Can not use 'await' as identifier inside an async function")
+ this.raiseRecoverable(start, "Cannot use 'await' as identifier inside an async function")
+ if (this.currentThisScope().inClassFieldInit && name === "arguments")
+ this.raiseRecoverable(start, "Cannot use 'arguments' in class field initializer")
+ if (this.inClassStaticBlock && (name === "arguments" || name === "await"))
+ this.raise(start, `Cannot use ${name} in class static initialization block`)
if (this.keywords.test(name))
this.raise(start, `Unexpected keyword '${name}'`)
if (this.options.ecmaVersion < 6 &&
const re = this.strict ? this.reservedWordsStrict : this.reservedWords
if (re.test(name)) {
if (!this.inAsync && name === "await")
- this.raiseRecoverable(start, "Can not use keyword 'await' outside an async function")
+ this.raiseRecoverable(start, "Cannot use keyword 'await' outside an async function")
this.raiseRecoverable(start, `The keyword '${name}' is reserved`)
}
}
pp.parseIdent = function(liberal, isBinding) {
let node = this.startNode()
- if (liberal && this.options.allowReserved === "never") liberal = false
if (this.type === tt.name) {
node.name = this.value
} else if (this.type.keyword) {
} else {
this.unexpected()
}
- this.next()
+ this.next(!!liberal)
this.finishNode(node, "Identifier")
- if (!liberal) this.checkUnreserved(node)
+ if (!liberal) {
+ this.checkUnreserved(node)
+ if (node.name === "await" && !this.awaitIdentPos)
+ this.awaitIdentPos = node.start
+ }
+ return node
+}
+
+pp.parsePrivateIdent = function() {
+ const node = this.startNode()
+ if (this.type === tt.privateId) {
+ node.name = this.value
+ } else {
+ this.unexpected()
+ }
+ this.next()
+ this.finishNode(node, "PrivateIdentifier")
+
+ // For validating existence
+ if (this.privateNameStack.length === 0) {
+ this.raise(node.start, `Private field '#${node.name}' must be declared in an enclosing class`)
+ } else {
+ this.privateNameStack[this.privateNameStack.length - 1].used.push(node)
+ }
+
return node
}
// Parses yield expression inside generator.
-pp.parseYield = function() {
+pp.parseYield = function(forInit) {
if (!this.yieldPos) this.yieldPos = this.start
let node = this.startNode()
node.argument = null
} else {
node.delegate = this.eat(tt.star)
- node.argument = this.parseMaybeAssign()
+ node.argument = this.parseMaybeAssign(forInit)
}
return this.finishNode(node, "YieldExpression")
}
-pp.parseAwait = function() {
+pp.parseAwait = function(forInit) {
if (!this.awaitPos) this.awaitPos = this.start
let node = this.startNode()
this.next()
- node.argument = this.parseMaybeUnary(null, true)
+ node.argument = this.parseMaybeUnary(null, true, false, forInit)
return this.finishNode(node, "AwaitExpression")
}