import { TV, PropName } from '../static-semantics/all.mjs'; import { Token, TokenPrecedence, isPropertyOrCall, isMember, isKeyword, isKeywordRaw, isReservedWordStrict, } from './tokens.mjs'; import { isLineTerminator } from './Lexer.mjs'; import { FunctionParser, FunctionKind } from './FunctionParser.mjs'; import { RegExpParser } from './RegExpParser.mjs'; export class ExpressionParser extends FunctionParser { // Expression : // AssignmentExpression // Expression `,` AssignmentExpression parseExpression() { const node = this.startNode(); const AssignmentExpression = this.parseAssignmentExpression(); if (this.eat(Token.COMMA)) { node.ExpressionList = [AssignmentExpression]; do { node.ExpressionList.push(this.parseAssignmentExpression()); } while (this.eat(Token.COMMA)); return this.finishNode(node, 'CommaOperator'); } return AssignmentExpression; } // AssignmentExpression : // ConditionalExpression // [+Yield] YieldExpression // ArrowFunction // AsyncArrowFunction // LeftHandSideExpression `=` AssignmentExpression // LeftHandSideExpression AssignmentOperator AssignmentExpression // LeftHandSideExpression LogicalAssignmentOperator AssignmentExpression // // AssignmentOperator : one of // *= /= %= += -= <<= >>= >>>= &= ^= |= **= // // LogicalAssignmentOperator : one of // &&= ||= ??= parseAssignmentExpression() { if (this.test(Token.YIELD) && this.scope.hasYield()) { return this.parseYieldExpression(); } const node = this.startNode(); this.scope.pushAssignmentInfo('assign'); const left = this.parseConditionalExpression(); const assignmentInfo = this.scope.popAssignmentInfo(); if (left.type === 'IdentifierReference') { // `async` [no LineTerminator here] IdentifierReference [no LineTerminator here] `=>` if (left.name === 'async' && this.test(Token.IDENTIFIER) && !this.peek().hadLineTerminatorBefore && this.testAhead(Token.ARROW) && !this.peekAhead().hadLineTerminatorBefore) { assignmentInfo.clear(); return this.parseArrowFunction(node, { Arguments: [this.parseIdentifierReference()], }, FunctionKind.ASYNC); } // IdentifierReference [no LineTerminator here] `=>` if (this.test(Token.ARROW) && !this.peek().hadLineTerminatorBefore) { assignmentInfo.clear(); return this.parseArrowFunction(node, { Arguments: [left] }, FunctionKind.NORMAL); } } // `async` [no LineTerminator here] Arguments [no LineTerminator here] `=>` if (left.type === 'CallExpression' && left.arrowInfo && this.test(Token.ARROW) && !this.peek().hadLineTerminatorBefore) { const last = left.Arguments[left.Arguments.length - 1]; if (!left.arrowInfo.trailingComma || (last && last.type !== 'AssignmentRestElement')) { assignmentInfo.clear(); return this.parseArrowFunction(node, left, FunctionKind.ASYNC); } } if (left.type === 'CoverParenthesizedExpressionAndArrowParameterList') { assignmentInfo.clear(); return this.parseArrowFunction(node, left, FunctionKind.NORMAL); } switch (this.peek().type) { case Token.ASSIGN: case Token.ASSIGN_MUL: case Token.ASSIGN_DIV: case Token.ASSIGN_MOD: case Token.ASSIGN_ADD: case Token.ASSIGN_SUB: case Token.ASSIGN_SHL: case Token.ASSIGN_SAR: case Token.ASSIGN_SHR: case Token.ASSIGN_BIT_AND: case Token.ASSIGN_BIT_XOR: case Token.ASSIGN_BIT_OR: case Token.ASSIGN_EXP: case Token.ASSIGN_AND: case Token.ASSIGN_OR: case Token.ASSIGN_NULLISH: assignmentInfo.clear(); this.validateAssignmentTarget(left); node.LeftHandSideExpression = left; node.AssignmentOperator = this.next().value; node.AssignmentExpression = this.parseAssignmentExpression(); return this.finishNode(node, 'AssignmentExpression'); default: return left; } } validateAssignmentTarget(node) { switch (node.type) { case 'IdentifierReference': if (this.isStrictMode() && (node.name === 'eval' || node.name === 'arguments')) { break; } return; case 'CoverInitializedName': this.validateAssignmentTarget(node.IdentifierReference); return; case 'MemberExpression': return; case 'SuperProperty': return; case 'ParenthesizedExpression': this.validateAssignmentTarget(node.Expression); return; case 'ArrayLiteral': node.ElementList.forEach((p, i) => { if (p.type === 'SpreadElement' && (i !== node.ElementList.length - 1 || node.hasTrailingComma)) { this.raiseEarly('InvalidAssignmentTarget', p); } this.validateAssignmentTarget(p); }); return; case 'ObjectLiteral': node.PropertyDefinitionList.forEach((p, i) => { if (p.type === 'PropertyDefinition' && !p.PropertyName && i !== node.PropertyDefinitionList.length - 1) { this.raiseEarly('InvalidAssignmentTarget', p); } this.validateAssignmentTarget(p); }); return; case 'PropertyDefinition': this.validateAssignmentTarget(node.AssignmentExpression); return; case 'AssignmentExpression': this.validateAssignmentTarget(node.LeftHandSideExpression); return; case 'Elision': return; case 'SpreadElement': if (node.AssignmentExpression.type === 'AssignmentExpression') { break; } this.validateAssignmentTarget(node.AssignmentExpression); return; default: break; } this.raiseEarly('InvalidAssignmentTarget', node); } // YieldExpression : // `yield` // `yield` [no LineTerminator here] AssignmentExpression // `yield` [no LineTerminator here] `*` AssignmentExpression parseYieldExpression() { if (this.scope.inParameters()) { this.raiseEarly('YieldInFormalParameters'); } const node = this.startNode(); this.expect(Token.YIELD); if (this.peek().hadLineTerminatorBefore) { node.hasStar = false; node.AssignmentExpression = null; } else { node.hasStar = this.eat(Token.MUL); if (node.hasStar) { node.AssignmentExpression = this.parseAssignmentExpression(); } else { switch (this.peek().type) { case Token.EOS: case Token.SEMICOLON: case Token.RBRACE: case Token.RBRACK: case Token.RPAREN: case Token.COLON: case Token.COMMA: case Token.IN: node.AssignmentExpression = null; break; default: node.AssignmentExpression = this.parseAssignmentExpression(); } } } if (this.scope.arrowInfoStack.length > 0) { this.scope.arrowInfoStack[this.scope.arrowInfoStack.length - 1].yieldExpressions.push(node); } return this.finishNode(node, 'YieldExpression'); } // ConditionalExpression : // ShortCircuitExpression // ShortCircuitExpression `?` AssignmentExpression `:` AssignmentExpression parseConditionalExpression() { const node = this.startNode(); const ShortCircuitExpression = this.parseShortCircuitExpression(); if (this.eat(Token.CONDITIONAL)) { node.ShortCircuitExpression = ShortCircuitExpression; this.scope.with({ in: true }, () => { node.AssignmentExpression_a = this.parseAssignmentExpression(); }); this.expect(Token.COLON); node.AssignmentExpression_b = this.parseAssignmentExpression(); return this.finishNode(node, 'ConditionalExpression'); } return ShortCircuitExpression; } // ShortCircuitExpression : // LogicalORExpression // CoalesceExpression // // CoalesceExpression : // CoalesceExpressionHead `??` BitwiseORExpression // // CoalesceExpressionHead : // CoalesceExpression // BitwiseORExpression parseShortCircuitExpression() { // Start parse at BIT_OR, right above AND/OR/NULLISH const expression = this.parseBinaryExpression(TokenPrecedence[Token.BIT_OR]); switch (this.peek().type) { case Token.AND: case Token.OR: // Drop into normal binary chain starting at OR return this.parseBinaryExpression(TokenPrecedence[Token.OR], expression); case Token.NULLISH: { let x = expression; while (this.eat(Token.NULLISH)) { const node = this.startNode(); node.CoalesceExpressionHead = x; node.BitwiseORExpression = this.parseBinaryExpression(TokenPrecedence[Token.BIT_OR]); x = this.finishNode(node, 'CoalesceExpression'); } return x; } default: return expression; } } parseBinaryExpression(precedence, x = this.parseUnaryExpression()) { let p = TokenPrecedence[this.peek().type]; if (p >= precedence) { do { while (TokenPrecedence[this.peek().type] === p) { const left = x; const node = this.startNode(left); if (this.peek().type === Token.IN && !this.scope.hasIn()) { return left; } const op = this.next(); const nextP = op.type === Token.EXP ? p : p + 1; const right = this.parseBinaryExpression(nextP); let name; switch (op.type) { case Token.EXP: name = 'ExponentiationExpression'; node.UpdateExpression = left; node.ExponentiationExpression = right; break; case Token.MUL: case Token.DIV: case Token.MOD: name = 'MultiplicativeExpression'; node.MultiplicativeExpression = left; node.MultiplicativeOperator = op.value; node.ExponentiationExpression = right; break; case Token.ADD: case Token.SUB: name = 'AdditiveExpression'; node.AdditiveExpression = left; node.MultiplicativeExpression = right; node.operator = op.value; break; case Token.SHL: case Token.SAR: case Token.SHR: name = 'ShiftExpression'; node.ShiftExpression = left; node.AdditiveExpression = right; node.operator = op.value; break; case Token.LT: case Token.GT: case Token.LTE: case Token.GTE: case Token.INSTANCEOF: case Token.IN: name = 'RelationalExpression'; node.RelationalExpression = left; node.ShiftExpression = right; node.operator = op.value; break; case Token.EQ: case Token.NE: case Token.EQ_STRICT: case Token.NE_STRICT: name = 'EqualityExpression'; node.EqualityExpression = left; node.RelationalExpression = right; node.operator = op.value; break; case Token.BIT_AND: name = 'BitwiseANDExpression'; node.A = left; node.operator = op.value; node.B = right; break; case Token.BIT_XOR: name = 'BitwiseXORExpression'; node.A = left; node.operator = op.value; node.B = right; break; case Token.BIT_OR: name = 'BitwiseORExpression'; node.A = left; node.operator = op.value; node.B = right; break; case Token.AND: name = 'LogicalANDExpression'; node.LogicalANDExpression = left; node.BitwiseORExpression = right; break; case Token.OR: name = 'LogicalORExpression'; node.LogicalORExpression = left; node.LogicalANDExpression = right; break; default: this.unexpected(op); } x = this.finishNode(node, name); } p -= 1; } while (p >= precedence); } return x; } // UnaryExpression : // UpdateExpression // `delete` UnaryExpression // `void` UnaryExpression // `typeof` UnaryExpression // `+` UnaryExpression // `-` UnaryExpression // `~` UnaryExpression // `!` UnaryExpression // [+Await] AwaitExpression parseUnaryExpression() { return this.scope.with({ in: true }, () => { if (this.test(Token.AWAIT) && this.scope.hasAwait()) { return this.parseAwaitExpression(); } const node = this.startNode(); switch (this.peek().type) { case Token.DELETE: case Token.VOID: case Token.TYPEOF: case Token.ADD: case Token.SUB: case Token.BIT_NOT: case Token.NOT: node.operator = this.next().value; node.UnaryExpression = this.parseUnaryExpression(); if (this.isStrictMode() && node.operator === 'delete' && node.UnaryExpression.type === 'IdentifierReference') { this.raiseEarly('DeleteIdentifier', node.UnaryExpression); } if (this.test(Token.EXP)) { this.unexpected(); } return this.finishNode(node, 'UnaryExpression'); default: return this.parseUpdateExpression(); } }); } // AwaitExpression : `await` UnaryExpression parseAwaitExpression() { if (this.scope.inParameters()) { this.raiseEarly('AwaitInFormalParameters'); } const node = this.startNode(); this.expect(Token.AWAIT); node.UnaryExpression = this.parseUnaryExpression(); if (this.scope.arrowInfoStack.length > 0) { this.scope.arrowInfoStack[this.scope.arrowInfoStack.length - 1].awaitExpressions.push(node); } else if (!this.scope.hasReturn()) { this.state.hasTopLevelAwait = true; } return this.finishNode(node, 'AwaitExpression'); } // UpdateExpression : // LeftHandSideExpression // LeftHandSideExpression [no LineTerminator here] `++` // LeftHandSideExpression [no LineTerminator here] `--` // `++` UnaryExpression // `--` UnaryExpression parseUpdateExpression() { if (this.test(Token.INC) || this.test(Token.DEC)) { const node = this.startNode(); node.operator = this.next().value; node.LeftHandSideExpression = null; node.UnaryExpression = this.parseUnaryExpression(); this.validateAssignmentTarget(node.UnaryExpression); return this.finishNode(node, 'UpdateExpression'); } const argument = this.parseLeftHandSideExpression(); if (!this.peek().hadLineTerminatorBefore) { if (this.test(Token.INC) || this.test(Token.DEC)) { this.validateAssignmentTarget(argument); const node = this.startNode(); node.operator = this.next().value; node.LeftHandSideExpression = argument; node.UnaryExpression = null; return this.finishNode(node, 'UpdateExpression'); } } return argument; } // LeftHandSideExpression parseLeftHandSideExpression(allowCalls = true) { let result; switch (this.peek().type) { case Token.NEW: result = this.parseNewExpression(); break; case Token.SUPER: { const node = this.startNode(); this.next(); if (this.test(Token.LPAREN)) { if (!this.scope.hasSuperCall()) { this.raiseEarly('InvalidSuperCall'); } node.Arguments = this.parseArguments().Arguments; result = this.finishNode(node, 'SuperCall'); } else { if (!this.scope.hasSuperProperty()) { this.raiseEarly('InvalidSuperProperty'); } if (this.eat(Token.LBRACK)) { node.Expression = this.parseExpression(); this.expect(Token.RBRACK); node.IdentifierName = null; } else { this.expect(Token.PERIOD); node.Expression = null; node.IdentifierName = this.parseIdentifierName(); } result = this.finishNode(node, 'SuperProperty'); } break; } case Token.IMPORT: { const node = this.startNode(); this.next(); if (this.scope.hasImportMeta() && this.eat(Token.PERIOD)) { this.expect('meta'); result = this.finishNode(node, 'ImportMeta'); } else { if (!allowCalls) { this.unexpected(); } this.expect(Token.LPAREN); node.AssignmentExpression = this.parseAssignmentExpression(); this.expect(Token.RPAREN); result = this.finishNode(node, 'ImportCall'); } break; } default: result = this.parsePrimaryExpression(); break; } const check = allowCalls ? isPropertyOrCall : isMember; while (check(this.peek().type)) { const node = this.startNode(result); switch (this.peek().type) { case Token.LBRACK: { this.next(); node.MemberExpression = result; node.IdentifierName = null; node.Expression = this.parseExpression(); result = this.finishNode(node, 'MemberExpression'); this.expect(Token.RBRACK); break; } case Token.PERIOD: this.next(); node.MemberExpression = result; node.IdentifierName = this.parseIdentifierName(); node.Expression = null; result = this.finishNode(node, 'MemberExpression'); break; case Token.LPAREN: { // `async` [no LineTerminator here] `(` const couldBeArrow = this.matches('async', this.currentToken) && result.type === 'IdentifierReference' && !this.peek().hadLineTerminatorBefore; if (couldBeArrow) { this.scope.pushArrowInfo(true); } const { Arguments, trailingComma } = this.parseArguments(); node.CallExpression = result; node.Arguments = Arguments; if (couldBeArrow) { node.arrowInfo = this.scope.popArrowInfo(); node.arrowInfo.trailingComma = trailingComma; } result = this.finishNode(node, 'CallExpression'); break; } case Token.OPTIONAL: node.MemberExpression = result; node.OptionalChain = this.parseOptionalChain(); result = this.finishNode(node, 'OptionalExpression'); break; case Token.TEMPLATE: node.MemberExpression = result; node.TemplateLiteral = this.parseTemplateLiteral(true); result = this.finishNode(node, 'TaggedTemplateExpression'); break; default: this.unexpected(); } } return result; } // OptionalChain parseOptionalChain() { this.expect(Token.OPTIONAL); let base = this.startNode(); base.OptionalChain = null; if (this.test(Token.LPAREN)) { base.Arguments = this.parseArguments().Arguments; } else if (this.eat(Token.LBRACK)) { base.Expression = this.parseExpression(); this.expect(Token.RBRACK); } else if (this.test(Token.TEMPLATE)) { this.raise('TemplateInOptionalChain'); } else { base.IdentifierName = this.parseIdentifierName(); } base = this.finishNode(base, 'OptionalChain'); while (true) { const node = this.startNode(); if (this.test(Token.LPAREN)) { node.OptionalChain = base; node.Arguments = this.parseArguments().Arguments; base = this.finishNode(node, 'OptionalChain'); } else if (this.eat(Token.LBRACK)) { node.OptionalChain = base; node.Expression = this.parseExpression(); this.expect(Token.RBRACK); base = this.finishNode(node, 'OptionalChain'); } else if (this.test(Token.TEMPLATE)) { this.raise('TemplateInOptionalChain'); } else if (this.eat(Token.PERIOD)) { node.OptionalChain = base; node.IdentifierName = this.parseIdentifierName(); base = this.finishNode(node, 'OptionalChain'); } else { return base; } } } // NewExpression parseNewExpression() { const node = this.startNode(); this.expect(Token.NEW); if (this.scope.hasNewTarget() && this.eat(Token.PERIOD)) { this.expect('target'); return this.finishNode(node, 'NewTarget'); } node.MemberExpression = this.parseLeftHandSideExpression(false); if (this.test(Token.LPAREN)) { node.Arguments = this.parseArguments().Arguments; } else { node.Arguments = null; } return this.finishNode(node, 'NewExpression'); } // PrimaryExpression : // ... parsePrimaryExpression() { switch (this.peek().type) { case Token.IDENTIFIER: case Token.ESCAPED_KEYWORD: case Token.YIELD: case Token.AWAIT: // `async` [no LineTerminator here] `function` if (this.test('async') && this.testAhead(Token.FUNCTION) && !this.peekAhead().hadLineTerminatorBefore) { return this.parseFunctionExpression(FunctionKind.ASYNC); } return this.parseIdentifierReference(); case Token.THIS: { const node = this.startNode(); this.next(); return this.finishNode(node, 'ThisExpression'); } case Token.NUMBER: case Token.BIGINT: return this.parseNumericLiteral(); case Token.STRING: return this.parseStringLiteral(); case Token.NULL: { const node = this.startNode(); this.next(); return this.finishNode(node, 'NullLiteral'); } case Token.TRUE: case Token.FALSE: return this.parseBooleanLiteral(); case Token.LBRACK: return this.parseArrayLiteral(); case Token.LBRACE: return this.parseObjectLiteral(); case Token.FUNCTION: return this.parseFunctionExpression(FunctionKind.NORMAL); case Token.CLASS: return this.parseClassExpression(); case Token.TEMPLATE: return this.parseTemplateLiteral(); case Token.DIV: case Token.ASSIGN_DIV: return this.parseRegularExpressionLiteral(); case Token.LPAREN: return this.parseCoverParenthesizedExpressionAndArrowParameterList(); default: return this.unexpected(); } } // NumericLiteral parseNumericLiteral() { const node = this.startNode(); if (!this.test(Token.NUMBER) && !this.test(Token.BIGINT)) { this.unexpected(); } node.value = this.next().value; return this.finishNode(node, 'NumericLiteral'); } // StringLiteral parseStringLiteral() { const node = this.startNode(); if (!this.test(Token.STRING)) { this.unexpected(); } node.value = this.next().value; return this.finishNode(node, 'StringLiteral'); } // BooleanLiteral : // `true` // `false` parseBooleanLiteral() { const node = this.startNode(); switch (this.peek().type) { case Token.TRUE: this.next(); node.value = true; break; case Token.FALSE: this.next(); node.value = false; break; default: this.unexpected(); } return this.finishNode(node, 'BooleanLiteral'); } // ArrayLiteral : // `[` `]` // `[` Elision `]` // `[` ElementList `]` // `[` ElementList `,` `]` // `[` ElementList `,` Elision `]` parseArrayLiteral() { const node = this.startNode(); this.expect(Token.LBRACK); node.ElementList = []; node.hasTrailingComma = false; while (true) { while (this.test(Token.COMMA)) { const elision = this.startNode(); this.next(); node.ElementList.push(this.finishNode(elision, 'Elision')); } if (this.eat(Token.RBRACK)) { break; } if (this.test(Token.ELLIPSIS)) { const spread = this.startNode(); this.next(); spread.AssignmentExpression = this.parseAssignmentExpression(); node.ElementList.push(this.finishNode(spread, 'SpreadElement')); } else { node.ElementList.push(this.parseAssignmentExpression()); } if (this.eat(Token.RBRACK)) { node.hasTrailingComma = false; break; } node.hasTrailingComma = true; this.expect(Token.COMMA); } return this.finishNode(node, 'ArrayLiteral'); } // ObjectLiteral : // `{` `}` // `{` PropertyDefinitionList `}` // `{` PropertyDefinitionList `,` `}` parseObjectLiteral() { const node = this.startNode(); this.expect(Token.LBRACE); node.PropertyDefinitionList = []; while (true) { if (this.eat(Token.RBRACE)) { break; } node.PropertyDefinitionList.push(this.parsePropertyDefinition()); if (this.eat(Token.RBRACE)) { break; } this.expect(Token.COMMA); } return this.finishNode(node, 'ObjectLiteral'); } parsePropertyDefinition() { return this.parseBracketedDefinition('property'); } parseFunctionExpression(kind) { return this.parseFunction(true, kind); } parseArguments() { this.expect(Token.LPAREN); if (this.eat(Token.RPAREN)) { return { Arguments: [], trailingComma: false }; } const Arguments = []; let trailingComma = false; while (true) { const node = this.startNode(); if (this.eat(Token.ELLIPSIS)) { node.AssignmentExpression = this.parseAssignmentExpression(); Arguments.push(this.finishNode(node, 'AssignmentRestElement')); } else { Arguments.push(this.parseAssignmentExpression()); } if (this.eat(Token.RPAREN)) { break; } this.expect(Token.COMMA); if (this.eat(Token.RPAREN)) { trailingComma = true; break; } } return { Arguments, trailingComma }; } // #sec-class-definitions // ClassDeclaration : // `class` BindingIdentifier ClassTail // [+Default] `class` ClassTail // // ClassExpression : // `class` BindingIdentifier? ClassTail parseClass(isExpression) { const node = this.startNode(); this.expect(Token.CLASS); this.scope.with({ strict: true }, () => { if (!this.test(Token.LBRACE) && !this.test(Token.EXTENDS)) { node.BindingIdentifier = this.parseBindingIdentifier(); if (!isExpression) { this.scope.declare(node.BindingIdentifier, 'lexical'); } } else if (isExpression === false && !this.scope.isDefault()) { this.raise('ClassMissingBindingIdentifier'); } else { node.BindingIdentifier = null; } node.ClassTail = this.scope.with({ default: false }, () => this.parseClassTail()); }); return this.finishNode(node, isExpression ? 'ClassExpression' : 'ClassDeclaration'); } // ClassTail : ClassHeritage? `{` ClassBody? `}` // ClassHeritage : `extends` LeftHandSideExpression // ClassBody : ClassElementList parseClassTail() { const node = this.startNode(); if (this.eat(Token.EXTENDS)) { node.ClassHeritage = this.parseLeftHandSideExpression(); } else { node.ClassHeritage = null; } this.expect(Token.LBRACE); if (this.eat(Token.RBRACE)) { node.ClassBody = null; } else { this.scope.with({ superCall: !!node.ClassHeritage }, () => { node.ClassBody = []; let hasConstructor = false; while (!this.eat(Token.RBRACE)) { const m = this.parseClassElement(); node.ClassBody.push(m); const name = PropName(m.MethodDefinition); const isActualConstructor = !m.static && !!m.MethodDefinition.UniqueFormalParameters && m.MethodDefinition.type === 'MethodDefinition' && name === 'constructor'; if (isActualConstructor) { if (hasConstructor) { this.raiseEarly('DuplicateConstructor', m); } else { hasConstructor = true; } } if ((m.static && name === 'prototype') || (!m.static && !isActualConstructor && name === 'constructor')) { this.raiseEarly('InvalidMethodName', m, name); } } }); } return this.finishNode(node, 'ClassTail'); } // ClassElement : // `static` MethodDefinition // MethodDefinition parseClassElement() { const node = this.startNode(); node.static = this.eat('static'); node.MethodDefinition = this.parseMethodDefinition(node.static); while (this.eat(Token.SEMICOLON)) { // nothing } return this.finishNode(node, 'ClassElement'); } parseMethodDefinition(isStatic) { return this.parseBracketedDefinition('method', isStatic); } parseClassExpression() { return this.parseClass(true); } parseTemplateLiteral(tagged = false) { const node = this.startNode(); node.TemplateSpanList = []; node.ExpressionList = []; let buffer = ''; while (true) { if (this.position >= this.source.length) { this.raise('UnterminatedTemplate', this.position); } const c = this.source[this.position]; switch (c) { case '`': this.position += 1; node.TemplateSpanList.push(buffer); this.next(); if (!tagged) { node.TemplateSpanList.forEach((s) => { if (TV(s) === undefined) { this.raise('InvalidTemplateEscape'); } }); } return this.finishNode(node, 'TemplateLiteral'); case '$': this.position += 1; if (this.source[this.position] === '{') { this.position += 1; node.TemplateSpanList.push(buffer); buffer = ''; this.next(); node.ExpressionList.push(this.parseExpression()); break; } buffer += c; break; default: { if (c === '\\') { buffer += c; this.position += 1; } const l = this.source[this.position]; this.position += 1; if (isLineTerminator(l)) { if (l === '\r' && this.source[this.position] === '\n') { this.position += 1; } if (l === '\u{2028}' || l === '\u{2029}') { buffer += l; } else { buffer += '\n'; } this.line += 1; this.columnOffset = this.position; } else { buffer += l; } break; } } } } // RegularExpressionLiteral : // `/` RegularExpressionBody `/` RegularExpressionFlags parseRegularExpressionLiteral() { const node = this.startNode(); this.scanRegularExpressionBody(); node.RegularExpressionBody = this.scannedValue; this.scanRegularExpressionFlags(); node.RegularExpressionFlags = this.scannedValue; try { const parse = (flags) => { const p = new RegExpParser(node.RegularExpressionBody); return p.scope(flags, () => p.parsePattern()); }; if (node.RegularExpressionFlags.includes('u')) { parse({ U: true, N: true }); } else { const pattern = parse({ U: false, N: false }); if (pattern.groupSpecifiers.size > 0) { parse({ U: false, N: true }); } } } catch (e) { if (e instanceof SyntaxError) { this.raise('Raw', node.location.startIndex + e.position + 1, e.message); } else { throw e; } } const fakeToken = { endIndex: this.position - 1, line: this.line - 1, column: this.position - this.columnOffset, }; this.next(); this.currentToken = fakeToken; return this.finishNode(node, 'RegularExpressionLiteral'); } // CoverParenthesizedExpressionAndArrowParameterList : // `(` Expression `)` // `(` Expression `,` `)` // `(` `)` // `(` `...` BindingIdentifier `)` // `(` `...` BindingPattern `)` // `(` Expression `,` `...` BindingIdentifier `)` // `(` Expression `.` `...` BindingPattern `)` parseCoverParenthesizedExpressionAndArrowParameterList() { const node = this.startNode(); const commaOp = this.startNode(); this.expect(Token.LPAREN); if (this.test(Token.RPAREN)) { if (!this.testAhead(Token.ARROW) || this.peekAhead().hadLineTerminatorBefore) { this.unexpected(); } this.next(); node.Arguments = []; return this.finishNode(node, 'CoverParenthesizedExpressionAndArrowParameterList'); } this.scope.pushArrowInfo(); this.scope.pushAssignmentInfo('arrow'); const expressions = []; let rparenAfterComma; while (true) { if (this.test(Token.ELLIPSIS)) { const inner = this.startNode(); this.next(); switch (this.peek().type) { case Token.LBRACE: case Token.LBRACK: inner.BindingPattern = this.parseBindingPattern(); break; default: inner.BindingIdentifier = this.parseBindingIdentifier(); break; } expressions.push(this.finishNode(inner, 'BindingRestElement')); this.expect(Token.RPAREN); break; } expressions.push(this.parseAssignmentExpression()); if (this.eat(Token.COMMA)) { if (this.eat(Token.RPAREN)) { rparenAfterComma = this.currentToken; break; } } else { this.expect(Token.RPAREN); break; } } const arrowInfo = this.scope.popArrowInfo(); const assignmentInfo = this.scope.popAssignmentInfo(); // ArrowParameters : // CoverParenthesizedExpressionAndArrowParameterList if (this.test(Token.ARROW) && !this.peek().hadLineTerminatorBefore) { node.Arguments = expressions; node.arrowInfo = arrowInfo; assignmentInfo.clear(); return this.finishNode(node, 'CoverParenthesizedExpressionAndArrowParameterList'); } // ParenthesizedExpression : // `(` Expression `)` if (expressions[expressions.length - 1].type === 'BindingRestElement') { this.unexpected(expressions[expressions.length - 1]); } if (rparenAfterComma) { this.unexpected(rparenAfterComma); } if (expressions.length === 1) { node.Expression = expressions[0]; } else { commaOp.ExpressionList = expressions; node.Expression = this.finishNode(commaOp, 'CommaOperator'); } return this.finishNode(node, 'ParenthesizedExpression'); } // PropertyName : // LiteralPropertyName // ComputedPropertyName // LiteralPropertyName : // IdentifierName // StringLiteral // NumericLiteral // ComputedPropertyName : // `[` AssignmentExpression `]` parsePropertyName() { if (this.test(Token.LBRACK)) { const node = this.startNode(); this.next(); node.ComputedPropertyName = this.parseAssignmentExpression(); this.expect(Token.RBRACK); return this.finishNode(node, 'PropertyName'); } if (this.test(Token.STRING)) { return this.parseStringLiteral(); } if (this.test(Token.NUMBER) || this.test(Token.BIGINT)) { return this.parseNumericLiteral(); } return this.parseIdentifierName(); } // PropertyDefinition : // IdentifierReference // CoverInitializedName // PropertyName `:` AssignmentExpression // MethodDefinition // `...` AssignmentExpression // MethodDefinition : // PropertyName `(` UniqueFormalParameters `)` `{` FunctionBody `}` // GeneratorMethod // AsyncMethod // AsyncGeneratorMethod // `get` PropertyName `(` `)` `{` FunctionBody `}` // `set` PropertyName `(` PropertySetParameterList `)` `{` FunctionBody `}` // GeneratorMethod : // `*` PropertyName `(` UniqueFormalParameters `)` `{` GeneratorBody `}` // AsyncMethod : // `async` [no LineTerminator here] PropertyName `(` UniqueFormalParameters `)` `{` AsyncFunctionBody `}` // AsyncGeneratorMethod : // `async` [no LineTerminator here] `*` Propertyname `(` UniqueFormalParameters `)` `{` AsyncGeneratorBody `}` parseBracketedDefinition(type, isStatic = false) { const node = this.startNode(); if (type === 'property' && this.eat(Token.ELLIPSIS)) { node.PropertyName = null; node.AssignmentExpression = this.parseAssignmentExpression(); return this.finishNode(node, 'PropertyDefinition'); } let isGenerator = this.eat(Token.MUL); let isGetter = false; let isSetter = false; let isAsync = false; if (!isGenerator) { if (this.test('get')) { isGetter = true; } else if (this.test('set')) { isSetter = true; } else if (this.test('async') && !this.peekAhead().hadLineTerminatorBefore) { isAsync = true; } } const firstName = this.parsePropertyName(); if (!isGenerator && !isGetter && !isSetter) { isGenerator = this.eat(Token.MUL); } const isSpecialMethod = isGenerator || ((isSetter || isGetter || isAsync) && !this.test(Token.LPAREN)); if (!isGenerator && type === 'property') { if (this.eat(Token.COLON)) { node.PropertyName = firstName; node.AssignmentExpression = this.parseAssignmentExpression(); return this.finishNode(node, 'PropertyDefinition'); } if (this.scope.assignmentInfoStack.length > 0 && this.test(Token.ASSIGN)) { node.IdentifierReference = firstName; node.IdentifierReference.type = 'IdentifierReference'; node.Initializer = this.parseInitializerOpt(); this.finishNode(node, 'CoverInitializedName'); this.scope.registerCoverInitializedName(node); return node; } if (!isSpecialMethod && firstName.type === 'IdentifierName' && !this.test(Token.LPAREN) && !isKeyword(firstName.name)) { firstName.type = 'IdentifierReference'; if (firstName.name === 'await' && (this.isStrictMode() || this.scope.hasAwait())) { this.raiseEarly('UnexpectedReservedWordStrict', firstName); } if (firstName.name === 'yield' && (this.isStrictMode() || this.scope.hasYield())) { this.raiseEarly('UnexpectedReservedWordStrict', firstName); } if (firstName.name !== 'yield' && firstName.name !== 'await' && isKeywordRaw(firstName.name)) { this.raiseEarly('UnexpectedToken', firstName); } if (this.isStrictMode() && isReservedWordStrict(firstName.name)) { this.raiseEarly('UnexpectedReservedWordStrict', firstName); } return firstName; } } node.PropertyName = (isSpecialMethod && (!isGenerator || isAsync)) ? this.parsePropertyName() : firstName; this.scope.with({ lexical: true, variable: true, superProperty: true, await: isAsync, yield: isGenerator, }, () => { if (isSpecialMethod && isGetter) { this.expect(Token.LPAREN); this.expect(Token.RPAREN); node.PropertySetParameterList = null; node.UniqueFormalParameters = null; } else if (isSpecialMethod && isSetter) { this.expect(Token.LPAREN); node.PropertySetParameterList = [this.parseFormalParameter()]; this.expect(Token.RPAREN); node.UniqueFormalParameters = null; } else { node.PropertySetParameterList = null; node.UniqueFormalParameters = this.parseUniqueFormalParameters(); } this.scope.with({ superCall: !isSpecialMethod && !isStatic && (node.PropertyName.name === 'constructor' || node.PropertyName.value === 'constructor') && this.scope.hasSuperCall(), }, () => { const body = this.parseFunctionBody(isAsync, isGenerator, false); node[`${isAsync ? 'Async' : ''}${isGenerator ? 'Generator' : 'Function'}Body`] = body; if (node.UniqueFormalParameters || node.PropertySetParameterList) { this.validateFormalParameters(node.UniqueFormalParameters || node.PropertySetParameterList, body, true); } }); }); const name = `${isAsync ? 'Async' : ''}${isGenerator ? 'Generator' : ''}Method${isAsync || isGenerator ? '' : 'Definition'}`; return this.finishNode(node, name); } }