diff options
author | Minteck <nekostarfan@gmail.com> | 2021-08-24 14:41:48 +0200 |
---|---|---|
committer | Minteck <nekostarfan@gmail.com> | 2021-08-24 14:41:48 +0200 |
commit | d25e11bee6ca5ca523884da132d18e1400e077b9 (patch) | |
tree | 8af39fde19f7ed640a60fb397c7edd647dff1c4c /node_modules/meriyah/src/parser.ts | |
download | kartik-iridium-d25e11bee6ca5ca523884da132d18e1400e077b9.tar.gz kartik-iridium-d25e11bee6ca5ca523884da132d18e1400e077b9.tar.bz2 kartik-iridium-d25e11bee6ca5ca523884da132d18e1400e077b9.zip |
Initial commit
Diffstat (limited to 'node_modules/meriyah/src/parser.ts')
-rw-r--r-- | node_modules/meriyah/src/parser.ts | 9165 |
1 files changed, 9165 insertions, 0 deletions
diff --git a/node_modules/meriyah/src/parser.ts b/node_modules/meriyah/src/parser.ts new file mode 100644 index 0000000..d5b4a51 --- /dev/null +++ b/node_modules/meriyah/src/parser.ts @@ -0,0 +1,9165 @@ +import { nextToken, skipHashBang } from './lexer'; +import { Token, KeywordDescTable } from './token'; +import * as ESTree from './estree'; +import { report, reportMessageAt, reportScopeError, Errors } from './errors'; +import { scanTemplateTail } from './lexer/template'; +import { scanJSXIdentifier, scanJSXToken, scanJSXAttributeValue } from './lexer/jsx'; +import { + Context, + ParserState, + PropertyKind, + Origin, + consumeOpt, + consume, + Flags, + OnComment, + OnToken, + pushComment, + pushToken, + reinterpretToPattern, + DestructuringKind, + AssignmentKind, + BindingKind, + validateBindingIdentifier, + validateFunctionName, + isStrictReservedWord, + optionalBit, + matchOrInsertSemicolon, + isPropertyWithPrivateFieldKey, + isValidLabel, + validateAndDeclareLabel, + finishNode, + HoistedClassFlags, + HoistedFunctionFlags, + createScope, + addChildScope, + ScopeKind, + ScopeState, + addVarName, + addBlockName, + addBindingToExports, + declareUnboundVariable, + isEqualTagName, + isValidStrictMode, + createArrowHeadParsingScope, + addVarOrBlock, + isValidIdentifier, + classifyIdentifier +} from './common'; + +/** + * Create a new parser instance + */ + +export function create( + source: string, + sourceFile: string | void, + onComment: OnComment | void, + onToken: OnToken | void +): ParserState { + return { + /** + * The source code to be parsed + */ + source, + + /** + * The mutable parser flags, in case any flags need passed by reference. + */ + flags: Flags.None, + + /** + * The current index + */ + index: 0, + + /** + * Beginning of current line + */ + line: 1, + + /** + * Beginning of current column + */ + column: 0, + + /** + * Start position of whitespace before current token + */ + startPos: 0, + + /** + * The end of the source code + */ + end: source.length, + + /** + * Start position of text of current token + */ + tokenPos: 0, + + /** + * Start position of the colum before newline + */ + startColumn: 0, + + /** + * Position in the input code of the first character after the last newline + */ + colPos: 0, + + /** + * The number of newlines + */ + linePos: 1, + + /** + * Start position of text of current token + */ + startLine: 1, + + /** + * Used together with source maps. File containing the code being parsed + */ + + sourceFile, + + /** + * Holds the scanned token value + */ + tokenValue: '', + + /** + * The current token in the stream to consume + */ + token: Token.EOF, + + /** + * Holds the raw text that have been scanned by the lexer + */ + tokenRaw: '', + + /** + * Holds the regExp info text that have been collected by the lexer + */ + tokenRegExp: void 0, + + /** + * The code point at the current index + */ + currentChar: source.charCodeAt(0), + + /** + * https://tc39.es/ecma262/#sec-module-semantics-static-semantics-exportednames + */ + exportedNames: [], + + /** + * https://tc39.es/ecma262/#sec-exports-static-semantics-exportedbindings + */ + + exportedBindings: [], + + /** + * Assignable state + */ + assignable: 1, + + /** + * Destructuring state + */ + destructible: 0, + + /** + * Holds either a function or array used on every comment + */ + onComment, + + /** + * Holds either a function or array used on every token + */ + onToken, + + /** + * Holds leading decorators before "export" or "class" keywords + */ + leadingDecorators: [] + }; +} + +/** + * The parser options. + */ +export interface Options { + // Allow module code + module?: boolean; + // Enable stage 3 support (ESNext) + next?: boolean; + // Enable start and end offsets to each node + ranges?: boolean; + // Enable web compatibility + webcompat?: boolean; + // Enable line/column location information to each node + loc?: boolean; + // Attach raw property to each literal and identifier node + raw?: boolean; + // Enabled directives + directives?: boolean; + // Allow return in the global scope + globalReturn?: boolean; + // Enable implied strict mode + impliedStrict?: boolean; + // Enable non-standard parenthesized expression node + preserveParens?: boolean; + // Enable lexical binding and scope tracking + lexical?: boolean; + // Adds a source attribute in every node’s loc object when the locations option is `true` + source?: string; + // Distinguish Identifier from IdentifierPattern + identifierPattern?: boolean; + // Enable React JSX parsing + jsx?: boolean; + // Allow edge cases that deviate from the spec + specDeviation?: boolean; + // Allows comment extraction. Accepts either a a callback function or an array + onComment?: OnComment; + // Allows token extraction. Accepts either a a callback function or an array + onToken?: OnToken; + // Creates unique key for in ObjectPattern when key value are same + uniqueKeyInPattern?: boolean; +} + +/** + * Consumes a sequence of tokens and produces an syntax tree + */ +export function parseSource(source: string, options: Options | void, context: Context): ESTree.Program { + let sourceFile = ''; + let onComment; + let onToken; + if (options != null) { + if (options.module) context |= Context.Module | Context.Strict; + if (options.next) context |= Context.OptionsNext; + if (options.loc) context |= Context.OptionsLoc; + if (options.ranges) context |= Context.OptionsRanges; + if (options.uniqueKeyInPattern) context |= Context.OptionsUniqueKeyInPattern; + if (options.lexical) context |= Context.OptionsLexical; + if (options.webcompat) context |= Context.OptionsWebCompat; + if (options.directives) context |= Context.OptionsDirectives | Context.OptionsRaw; + if (options.globalReturn) context |= Context.OptionsGlobalReturn; + if (options.raw) context |= Context.OptionsRaw; + if (options.preserveParens) context |= Context.OptionsPreserveParens; + if (options.impliedStrict) context |= Context.Strict; + if (options.jsx) context |= Context.OptionsJSX; + if (options.identifierPattern) context |= Context.OptionsIdentifierPattern; + if (options.specDeviation) context |= Context.OptionsSpecDeviation; + if (options.source) sourceFile = options.source; + // Accepts either a callback function to be invoked or an array to collect comments (as the node is constructed) + if (options.onComment != null) { + onComment = Array.isArray(options.onComment) ? pushComment(context, options.onComment) : options.onComment; + } + // Accepts either a callback function to be invoked or an array to collect tokens + if (options.onToken != null) { + onToken = Array.isArray(options.onToken) ? pushToken(context, options.onToken) : options.onToken; + } + } + + // Initialize parser state + const parser = create(source, sourceFile, onComment, onToken); + + // See: https://github.com/tc39/proposal-hashbang + if (context & Context.OptionsNext) skipHashBang(parser); + + const scope: ScopeState | undefined = context & Context.OptionsLexical ? createScope() : void 0; + + let body: (ESTree.Statement | ReturnType<typeof parseDirective | typeof parseModuleItem>)[] = []; + + // https://tc39.es/ecma262/#sec-scripts + // https://tc39.es/ecma262/#sec-modules + + let sourceType: 'module' | 'script' = 'script'; + + if (context & Context.Module) { + sourceType = 'module'; + body = parseModuleItemList(parser, context | Context.InGlobal, scope); + + if (scope) { + for (const key in parser.exportedBindings) { + if (key[0] === '#' && !(scope as any)[key]) report(parser, Errors.UndeclaredExportedBinding, key.slice(1)); + } + } + } else { + body = parseStatementList(parser, context | Context.InGlobal, scope); + } + + const node: ESTree.Program = { + type: 'Program', + sourceType, + body + }; + + if (context & Context.OptionsRanges) { + node.start = 0; + node.end = source.length; + node.range = [0, source.length]; + } + + if (context & Context.OptionsLoc) { + node.loc = { + start: { line: 1, column: 0 }, + end: { line: parser.line, column: parser.column } + }; + + if (parser.sourceFile) node.loc.source = sourceFile; + } + + return node; +} + +/** + * Parses statement list items + * + * @param parser Parser object + * @param context Context masks + */ +export function parseStatementList( + parser: ParserState, + context: Context, + scope: ScopeState | undefined +): ESTree.Statement[] { + // StatementList :: + // (StatementListItem)* <end_token> + + nextToken(parser, context | Context.AllowRegExp | Context.AllowEscapedKeyword); + + const statements: ESTree.Statement[] = []; + + while (parser.token === Token.StringLiteral) { + // "use strict" must be the exact literal without escape sequences or line continuation. + const { index, tokenPos, tokenValue, linePos, colPos, token } = parser; + const expr = parseLiteral(parser, context); + if (isValidStrictMode(parser, index, tokenPos, tokenValue)) context |= Context.Strict; + statements.push(parseDirective(parser, context, expr, token, tokenPos, linePos, colPos)); + } + + while (parser.token !== Token.EOF) { + statements.push(parseStatementListItem(parser, context, scope, Origin.TopLevel, {}) as ESTree.Statement); + } + return statements; +} + +/** + * Parse module item list + * + * @see [Link](https://tc39.github.io/ecma262/#prod-ModuleItemList) + * + * @param parser Parser object + * @param context Context masks + */ +export function parseModuleItemList( + parser: ParserState, + context: Context, + scope: ScopeState | undefined +): ReturnType<typeof parseDirective | typeof parseModuleItem>[] { + // ecma262/#prod-Module + // Module : + // ModuleBody? + // + // ecma262/#prod-ModuleItemList + // ModuleBody : + // ModuleItem* + + nextToken(parser, context | Context.AllowRegExp); + + const statements: ReturnType<typeof parseDirective | typeof parseModuleItem>[] = []; + + // Avoid this if we're not going to create any directive nodes. This is likely to be the case + // most of the time, considering the prevalence of strict mode and the fact modules + // are already in strict mode. + if (context & Context.OptionsDirectives) { + while (parser.token === Token.StringLiteral) { + const { tokenPos, linePos, colPos, token } = parser; + statements.push(parseDirective(parser, context, parseLiteral(parser, context), token, tokenPos, linePos, colPos)); + } + } + + while (parser.token !== Token.EOF) { + statements.push(parseModuleItem(parser, context, scope) as ESTree.Statement); + } + return statements; +} + +/** + * Parse module item + * + * @see [Link](https://tc39.github.io/ecma262/#prod-ModuleItem) + * + * @param parser Parser object + * @param context Context masks + * @param scope Scope object + */ + +export function parseModuleItem(parser: ParserState, context: Context, scope: ScopeState | undefined): any { + // Support legacy decorators before export keyword. + parser.leadingDecorators = parseDecorators(parser, context); + + // ecma262/#prod-ModuleItem + // ModuleItem : + // ImportDeclaration + // ExportDeclaration + // StatementListItem + + let moduleItem; + switch (parser.token) { + case Token.ExportKeyword: + moduleItem = parseExportDeclaration(parser, context, scope); + break; + case Token.ImportKeyword: + moduleItem = parseImportDeclaration(parser, context, scope); + break; + default: + moduleItem = parseStatementListItem(parser, context, scope, Origin.TopLevel, {}); + } + + if (parser.leadingDecorators.length) { + report(parser, Errors.InvalidLeadingDecorator); + } + return moduleItem; +} + +/** + * Parse statement list + * + * @param parser Parser object + * @param context Context masks + * @param scope Scope object + */ + +export function parseStatementListItem( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + origin: Origin, + labels: ESTree.Labels +): ESTree.Statement | ESTree.Decorator[] { + // ECMA 262 10th Edition + // StatementListItem[Yield, Return] : + // Statement[?Yield, ?Return] + // Declaration[?Yield] + // + // Declaration[Yield] : + // HoistableDeclaration[?Yield] + // ClassDeclaration[?Yield] + // LexicalDeclaration[In, ?Yield] + // + // HoistableDeclaration[Yield, Default] : + // FunctionDeclaration[?Yield, ?Default] + // GeneratorDeclaration[?Yield, ?Default] + // + // LexicalDeclaration[In, Yield] : + // LetOrConst BindingList[?In, ?Yield] ; + const start = parser.tokenPos; + const line = parser.linePos; + const column = parser.colPos; + + switch (parser.token) { + // HoistableDeclaration[?Yield, ~Default] + case Token.FunctionKeyword: + return parseFunctionDeclaration( + parser, + context, + scope, + origin, + 1, + HoistedFunctionFlags.None, + 0, + start, + line, + column + ); + // @decorator + case Token.Decorator: + // ClassDeclaration[?Yield, ~Default] + case Token.ClassKeyword: + return parseClassDeclaration(parser, context, scope, HoistedClassFlags.None, start, line, column); + // LexicalDeclaration[In, ?Yield] + // LetOrConst BindingList[?In, ?Yield] + case Token.ConstKeyword: + return parseLexicalDeclaration(parser, context, scope, BindingKind.Const, Origin.None, start, line, column); + case Token.LetKeyword: + return parseLetIdentOrVarDeclarationStatement(parser, context, scope, origin, start, line, column); + // ExportDeclaration + case Token.ExportKeyword: + report(parser, Errors.InvalidImportExportSloppy, 'export'); + // ImportDeclaration + case Token.ImportKeyword: + nextToken(parser, context); + switch (parser.token) { + case Token.LeftParen: + return parseImportCallDeclaration(parser, context, start, line, column); + case Token.Period: + return parseImportMetaDeclaration(parser, context, start, line, column); + default: + report(parser, Errors.InvalidImportExportSloppy, 'import'); + } + // async [no LineTerminator here] AsyncArrowBindingIdentifier ... + // async [no LineTerminator here] ArrowFormalParameters ... + case Token.AsyncKeyword: + return parseAsyncArrowOrAsyncFunctionDeclaration(parser, context, scope, origin, labels, 1, start, line, column); + default: + return parseStatement(parser, context, scope, origin, labels, 1, start, line, column); + } +} + +/** + * Parse statement + * + * @param parser Parser object + * @param context Context masks + * @param allowFuncDecl Allow / disallow func statement + */ + +export function parseStatement( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + origin: Origin, + labels: ESTree.Labels, + allowFuncDecl: 0 | 1, + start: number, + line: number, + column: number +): ESTree.Statement { + // Statement :: + // Block + // VariableStatement + // EmptyStatement + // ExpressionStatement + // IfStatement + // IterationStatement + // ContinueStatement + // BreakStatement + // ReturnStatement + // WithStatement + // LabelledStatement + // SwitchStatement + // ThrowStatement + // TryStatement + // DebuggerStatement + + switch (parser.token) { + // VariableStatement[?Yield] + case Token.VarKeyword: + return parseVariableStatement(parser, context, scope, Origin.None, start, line, column); + // [+Return] ReturnStatement[?Yield] + case Token.ReturnKeyword: + return parseReturnStatement(parser, context, start, line, column); + case Token.IfKeyword: + return parseIfStatement(parser, context, scope, labels, start, line, column); + case Token.ForKeyword: + return parseForStatement(parser, context, scope, labels, start, line, column); + // BreakableStatement[Yield, Return]: + // IterationStatement[?Yield, ?Return] + // SwitchStatement[?Yield, ?Return] + case Token.DoKeyword: + return parseDoWhileStatement(parser, context, scope, labels, start, line, column); + case Token.WhileKeyword: + return parseWhileStatement(parser, context, scope, labels, start, line, column); + case Token.SwitchKeyword: + return parseSwitchStatement(parser, context, scope, labels, start, line, column); + case Token.Semicolon: + // EmptyStatement + return parseEmptyStatement(parser, context, start, line, column); + // BlockStatement[?Yield, ?Return] + case Token.LeftBrace: + return parseBlock( + parser, + context, + scope ? addChildScope(scope, ScopeKind.Block) : scope, + labels, + start, + line, + column + ) as ESTree.Statement; + + // ThrowStatement[?Yield] + case Token.ThrowKeyword: + return parseThrowStatement(parser, context, start, line, column); + case Token.BreakKeyword: + // BreakStatement[?Yield] + return parseBreakStatement(parser, context, labels, start, line, column); + // ContinueStatement[?Yield] + case Token.ContinueKeyword: + return parseContinueStatement(parser, context, labels, start, line, column); + // TryStatement[?Yield, ?Return] + case Token.TryKeyword: + return parseTryStatement(parser, context, scope, labels, start, line, column); + // WithStatement[?Yield, ?Return] + case Token.WithKeyword: + return parseWithStatement(parser, context, scope, labels, start, line, column); + case Token.DebuggerKeyword: + // DebuggerStatement + return parseDebuggerStatement(parser, context, start, line, column); + case Token.AsyncKeyword: + return parseAsyncArrowOrAsyncFunctionDeclaration(parser, context, scope, origin, labels, 0, start, line, column); + // Miscellaneous error cases arguably better caught here than elsewhere + case Token.CatchKeyword: + report(parser, Errors.CatchWithoutTry); + case Token.FinallyKeyword: + report(parser, Errors.FinallyWithoutTry); + case Token.FunctionKeyword: + // FunctionDeclaration & ClassDeclaration is forbidden by lookahead + // restriction in an arbitrary statement position. + report( + parser, + context & Context.Strict + ? Errors.StrictFunction + : (context & Context.OptionsWebCompat) < 1 + ? Errors.WebCompatFunction + : Errors.SloppyFunction + ); + case Token.ClassKeyword: + report(parser, Errors.ClassForbiddenAsStatement); + + default: + return parseExpressionOrLabelledStatement( + parser, + context, + scope, + origin, + labels, + allowFuncDecl, + start, + line, + column + ); + } +} + +/** + * Parses either expression or labeled statement + * + * @param parser Parser object + * @param context Context masks + * @param allowFuncDecl Allow / disallow func statement + */ + +export function parseExpressionOrLabelledStatement( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + origin: Origin, + labels: ESTree.Labels, + allowFuncDecl: 0 | 1, + start: number, + line: number, + column: number +): ESTree.ExpressionStatement | ESTree.LabeledStatement { + // ExpressionStatement | LabelledStatement :: + // Expression ';' + // Identifier ':' Statement + // + // ExpressionStatement[Yield] : + // [lookahead notin {{, function, class, let [}] Expression[In, ?Yield] ; + + const { tokenValue, token } = parser; + + let expr: ESTree.Expression; + + switch (token) { + case Token.LetKeyword: + expr = parseIdentifier(parser, context, 0); + if (context & Context.Strict) report(parser, Errors.UnexpectedLetStrictReserved); + + // "let" followed by either "[", "{" or an identifier means a lexical + // declaration, which should not appear here. + // However, ASI may insert a line break before an identifier or a brace. + if (parser.token === Token.LeftBracket) report(parser, Errors.RestrictedLetProduction); + + break; + + default: + expr = parsePrimaryExpression( + parser, + context, + BindingKind.Empty, + 0, + 1, + 0, + 0, + 1, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + } + + /** LabelledStatement[Yield, Await, Return]: + * + * ExpressionStatement | LabelledStatement :: + * Expression ';' + * Identifier ':' Statement + * + * ExpressionStatement[Yield] : + * [lookahead notin {{, function, class, let [}] Expression[In, ?Yield] ; + */ + if (token & Token.IsIdentifier && parser.token === Token.Colon) { + return parseLabelledStatement( + parser, + context, + scope, + origin, + labels, + tokenValue, + expr, + token, + allowFuncDecl, + start, + line, + column + ); + } + /** MemberExpression : + * 1. PrimaryExpression + * 2. MemberExpression [ AssignmentExpression ] + * 3. MemberExpression . IdentifierName + * 4. MemberExpression TemplateLiteral + * + * CallExpression : + * 1. MemberExpression Arguments + * 2. CallExpression ImportCall + * 3. CallExpression Arguments + * 4. CallExpression [ AssignmentExpression ] + * 5. CallExpression . IdentifierName + * 6. CallExpression TemplateLiteral + * + * UpdateExpression :: + * ('++' | '--')? LeftHandSideExpression + * + */ + + expr = parseMemberOrUpdateExpression(parser, context, expr, 0, 0, start, line, column); + + /** AssignmentExpression : + * 1. ConditionalExpression + * 2. LeftHandSideExpression = AssignmentExpression + * + */ + + expr = parseAssignmentExpression(parser, context, 0, 0, start, line, column, expr as ESTree.ArgumentExpression); + + /** Sequence expression + * + * Note: The comma operator leads to a sequence expression which is not equivalent + * to the ES Expression, but it's part of the ESTree specs: + * + * https://github.com/estree/estree/blob/master/es5.md#sequenceexpression + * + */ + if (parser.token === Token.Comma) { + expr = parseSequenceExpression(parser, context, 0, start, line, column, expr); + } + + /** + * ExpressionStatement[Yield, Await]: + * [lookahead ∉ { {, function, async [no LineTerminator here] function, class, let [ }]Expression[+In, ?Yield, ?Await] + */ + + return parseExpressionStatement(parser, context, expr, start, line, column); +} + +/** + * Parses block statement + * + * @see [Link](https://tc39.github.io/ecma262/#prod-BlockStatement) + * @see [Link](https://tc39.github.io/ecma262/#prod-Block) + * + * @param parser Parser object + * @param context Context masks + * @param scope Scope object + * @param labels Labels object + * @param start + * @param line + * @param column + * + */ +export function parseBlock( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + labels: ESTree.Labels, + start: number, + line: number, + column: number +): ESTree.BlockStatement { + // Block :: + // '{' StatementList '}' + const body: ESTree.Statement[] = []; + consume(parser, context | Context.AllowRegExp, Token.LeftBrace); + while (parser.token !== Token.RightBrace) { + body.push(parseStatementListItem(parser, context, scope, Origin.BlockStatement, { $: labels }) as any); + } + + consume(parser, context | Context.AllowRegExp, Token.RightBrace); + + return finishNode(parser, context, start, line, column, { + type: 'BlockStatement', + body + }); +} + +/** + * Parses return statement + * + * @see [Link](https://tc39.github.io/ecma262/#prod-ReturnStatement) + * + * @param parser Parser object + * @param context Context masks + * @param start + * @param line + * @param column + */ +export function parseReturnStatement( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.ReturnStatement { + // ReturnStatement :: + // 'return' [no line terminator] Expression? ';' + if ((context & Context.OptionsGlobalReturn) < 1 && context & Context.InGlobal) report(parser, Errors.IllegalReturn); + + nextToken(parser, context | Context.AllowRegExp); + + const argument = + parser.flags & Flags.NewLine || parser.token & Token.IsAutoSemicolon + ? null + : parseExpressions(parser, context, 0, 1, parser.tokenPos, parser.line, parser.column); + + matchOrInsertSemicolon(parser, context | Context.AllowRegExp); + + return finishNode(parser, context, start, line, column, { + type: 'ReturnStatement', + argument + }); +} + +/** + * Parses an expression statement + * + * @see [Link](https://tc39.github.io/ecma262/#prod-ExpressionStatement) + * + * @param parser Parser object + * @param context Context masks + * @param expression AST node + * @param start + * @param line + * @param column + */ +export function parseExpressionStatement( + parser: ParserState, + context: Context, + expression: ESTree.Expression, + start: number, + line: number, + column: number +): ESTree.ExpressionStatement { + matchOrInsertSemicolon(parser, context | Context.AllowRegExp); + return finishNode(parser, context, start, line, column, { + type: 'ExpressionStatement', + expression + }); +} + +/** + * Parses either expression or labeled statement + * + * @param parser Parser object + * @param context Context masks + * @param expr ESTree AST node + * @param token Token to validate + * @param allowFuncDecl Allow / disallow func statement + * @param start + * @param line + * @param column + * + */ +export function parseLabelledStatement( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + origin: Origin, + labels: ESTree.Labels, + value: string, + expr: ESTree.Identifier | ESTree.Expression, + token: Token, + allowFuncDecl: 0 | 1, + start: number, + line: number, + column: number +): ESTree.LabeledStatement { + // LabelledStatement :: + // Expression ';' + // Identifier ':' Statement + + validateBindingIdentifier(parser, context, BindingKind.None, token, 1); + validateAndDeclareLabel(parser, labels, value); + + nextToken(parser, context | Context.AllowRegExp); // skip: ':' + + const body = + allowFuncDecl && + (context & Context.Strict) < 1 && + context & Context.OptionsWebCompat && + // In sloppy mode, Annex B.3.2 allows labelled function declarations. + // Otherwise it's a parse error. + parser.token === Token.FunctionKeyword + ? parseFunctionDeclaration( + parser, + context, + addChildScope(scope, ScopeKind.Block), + origin, + 0, + HoistedFunctionFlags.None, + 0, + parser.tokenPos, + parser.linePos, + parser.colPos + ) + : parseStatement( + parser, + context, + scope, + origin, + labels, + allowFuncDecl, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + + return finishNode(parser, context, start, line, column, { + type: 'LabeledStatement', + label: expr as ESTree.Identifier, + body + }); +} + +/** + * Parses either async ident, async function or async arrow in + * statement position + * + * @param parser Parser object + * @param context Context masks + * @param labels + * @param allowFuncDecl Allow / disallow func statement + * @param start Start position of current AST node + * @param start + * @param line + * @param column + */ + +export function parseAsyncArrowOrAsyncFunctionDeclaration( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + origin: Origin, + labels: ESTree.Labels, + allowFuncDecl: 0 | 1, + start: number, + line: number, + column: number +): ESTree.ExpressionStatement | ESTree.LabeledStatement | ESTree.FunctionDeclaration { + // AsyncArrowFunction[In, Yield, Await]: + // async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In] + // CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In] + // + // AsyncArrowBindingIdentifier[Yield]: + // BindingIdentifier[?Yield, +Await] + // + // CoverCallExpressionAndAsyncArrowHead[Yield, Await]: + // MemberExpression[?Yield, ?Await]Arguments[?Yield, ?Await] + // + // AsyncFunctionDeclaration[Yield, Await, Default]: + // async[no LineTerminator here]functionBindingIdentifier[?Yield, ?Await](FormalParameters[~Yield, +Await]){AsyncFunctionBody} + // [+Default]async[no LineTerminator here]function(FormalParameters[~Yield, +Await]){AsyncFunctionBody} + // + // AsyncFunctionBody: + // FunctionBody[~Yield, +Await] + + const { token, tokenValue } = parser; + + let expr: ESTree.Expression = parseIdentifier(parser, context, 0); + + if (parser.token === Token.Colon) { + return parseLabelledStatement( + parser, + context, + scope, + origin, + labels, + tokenValue, + expr, + token, + 1, + start, + line, + column + ); + } + + const asyncNewLine = parser.flags & Flags.NewLine; + + if (!asyncNewLine) { + // async function ... + if (parser.token === Token.FunctionKeyword) { + if (!allowFuncDecl) report(parser, Errors.AsyncFunctionInSingleStatementContext); + + return parseFunctionDeclaration( + parser, + context, + scope, + origin, + 1, + HoistedFunctionFlags.None, + 1, + start, + line, + column + ); + } + + // async Identifier => ... + if ((parser.token & Token.IsIdentifier) === Token.IsIdentifier) { + /** ArrowFunction[In, Yield, Await]: + * ArrowParameters[?Yield, ?Await][no LineTerminator here]=>ConciseBody[?In] + */ + expr = parseAsyncArrowAfterIdent(parser, context, /* assignable */ 1, start, line, column); + if (parser.token === Token.Comma) expr = parseSequenceExpression(parser, context, 0, start, line, column, expr); + + /** + * ExpressionStatement[Yield, Await]: + * [lookahead ∉ { {, function, async [no LineTerminator here] function, class, let [ }]Expression[+In, ?Yield, ?Await] + */ + return parseExpressionStatement(parser, context, expr, start, line, column); + } + } + + /** ArrowFunction[In, Yield, Await]: + * ArrowParameters[?Yield, ?Await][no LineTerminator here]=>ConciseBody[?In] + */ + if (parser.token === Token.LeftParen) { + expr = parseAsyncArrowOrCallExpression( + parser, + context, + expr, + 1, + BindingKind.ArgumentList, + Origin.None, + asyncNewLine, + start, + line, + column + ); + } else { + if (parser.token === Token.Arrow) { + classifyIdentifier(parser, context, token, /* isArrow */ 1); + expr = parseArrowFromIdentifier(parser, context, parser.tokenValue, expr, 0, 1, 0, start, line, column); + } + + parser.assignable = AssignmentKind.Assignable; + } + + /** MemberExpression : + * 1. PrimaryExpression + * 2. MemberExpression [ AssignmentExpression ] + * 3. MemberExpression . IdentifierName + * 4. MemberExpression TemplateLiteral + * + * CallExpression : + * 1. MemberExpression Arguments + * 2. CallExpression ImportCall + * 3. CallExpression Arguments + * 4. CallExpression [ AssignmentExpression ] + * 5. CallExpression . IdentifierName + * 6. CallExpression TemplateLiteral + * + * UpdateExpression :: + * ('++' | '--')? LeftHandSideExpression + */ + + expr = parseMemberOrUpdateExpression(parser, context, expr, 0, 0, start, line, column); + /** Sequence expression + * + * Note: The comma operator leads to a sequence expression which is not equivalent + * to the ES Expression, but it's part of the ESTree specs: + * + * https://github.com/estree/estree/blob/master/es5.md#sequenceexpression + * + */ + if (parser.token === Token.Comma) expr = parseSequenceExpression(parser, context, 0, start, line, column, expr); + + /** AssignmentExpression : + * + * 1. ConditionalExpression + * 2. LeftHandSideExpression = AssignmentExpression + * + */ + expr = parseAssignmentExpression(parser, context, 0, 0, start, line, column, expr as ESTree.ArgumentExpression); + + parser.assignable = AssignmentKind.Assignable; + + /** + * ExpressionStatement[Yield, Await]: + * [lookahead ∉ { {, function, async [no LineTerminator here] function, class, let [ }]Expression[+In, ?Yield, ?Await] + */ + return parseExpressionStatement(parser, context, expr, start, line, column); +} + +/** + * Parse directive node + * + * @see [Link](https://tc39.github.io/ecma262/#sec-directive-prologues-and-the-use-strict-directive) + * + * @param parser Parser object + * @param context Context masks + * @param expression AST expression node + * @param token + * @param start Start pos of node + * @param line + * @param column + */ + +export function parseDirective( + parser: ParserState, + context: Context, + expression: ESTree.ArgumentExpression | ESTree.SequenceExpression | ESTree.Expression, + token: Token, + start: number, + line: number, + column: number +): ESTree.ExpressionStatement { + if (token !== Token.Semicolon) { + parser.assignable = AssignmentKind.CannotAssign; + + expression = parseMemberOrUpdateExpression(parser, context, expression, 0, 0, start, line, column); + + if (parser.token !== Token.Semicolon) { + expression = parseAssignmentExpression(parser, context, 0, 0, start, line, column, expression); + + if (parser.token === Token.Comma) { + expression = parseSequenceExpression(parser, context, 0, start, line, column, expression); + } + } + + matchOrInsertSemicolon(parser, context | Context.AllowRegExp); + } + + return context & Context.OptionsDirectives && expression.type === 'Literal' && typeof expression.value === 'string' + ? finishNode(parser, context, start, line, column, { + type: 'ExpressionStatement', + expression, + // OptionsRaw is implicitly turned on by OptionsDirectives. + directive: (expression.raw as string).slice(1, -1) + }) + : finishNode(parser, context, start, line, column, { + type: 'ExpressionStatement', + expression + }); +} + +/** + * Parses empty statement + * + * @param parser Parser object + * @param context Context masks + * @param start Start pos of node + * @param line + * @param column + */ + +export function parseEmptyStatement( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.EmptyStatement { + nextToken(parser, context | Context.AllowRegExp); + return finishNode(parser, context, start, line, column, { + type: 'EmptyStatement' + }); +} + +/** + * Parses throw statement + * + * @see [Link](https://tc39.github.io/ecma262/#prod-ThrowStatement) + * + * @param parser Parser object + * @param context Context masks + * @param start Start pos of node +* @param line +* @param column + + */ +export function parseThrowStatement( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.ThrowStatement { + // ThrowStatement :: + // 'throw' Expression ';' + nextToken(parser, context | Context.AllowRegExp); + if (parser.flags & Flags.NewLine) report(parser, Errors.NewlineAfterThrow); + const argument: ESTree.Expression = parseExpressions( + parser, + context, + 0, + 1, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + matchOrInsertSemicolon(parser, context | Context.AllowRegExp); + return finishNode(parser, context, start, line, column, { + type: 'ThrowStatement', + argument + }); +} + +/** + * Parses an if statement with an optional else block + * + * @see [Link](https://tc39.github.io/ecma262/#sec-if-statement) + * + * @param parser Parser object + * @param context Context masks + * @param scope Scope instance + * @param start Start pos of node +* @param line +* @param column + + */ +export function parseIfStatement( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + labels: ESTree.Labels, + start: number, + line: number, + column: number +): ESTree.IfStatement { + // IfStatement :: + // 'if' '(' Expression ')' Statement ('else' Statement)? + nextToken(parser, context); + consume(parser, context | Context.AllowRegExp, Token.LeftParen); + parser.assignable = AssignmentKind.Assignable; + const test = parseExpressions(parser, context, 0, 1, parser.tokenPos, parser.line, parser.colPos); + consume(parser, context | Context.AllowRegExp, Token.RightParen); + const consequent = parseConsequentOrAlternative( + parser, + context, + scope, + labels, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + let alternate: ESTree.Statement | null = null; + if (parser.token === Token.ElseKeyword) { + nextToken(parser, context | Context.AllowRegExp); + alternate = parseConsequentOrAlternative( + parser, + context, + scope, + labels, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + } + + return finishNode(parser, context, start, line, column, { + type: 'IfStatement', + test, + consequent, + alternate + }); +} + +/** + * Parse either consequent or alternate. + * + * @param parser Parser object + * @param context Context masks + * @param start Start pos of node + * @param line + * @param column + */ +export function parseConsequentOrAlternative( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + labels: ESTree.Labels, + start: number, + line: number, + column: number +): ESTree.Statement | ESTree.FunctionDeclaration { + return context & Context.Strict || + // Disallow if web compatibility is off + (context & Context.OptionsWebCompat) < 1 || + parser.token !== Token.FunctionKeyword + ? parseStatement( + parser, + context, + scope, + Origin.None, + { $: labels }, + 0, + parser.tokenPos, + parser.linePos, + parser.colPos + ) + : parseFunctionDeclaration( + parser, + context, + addChildScope(scope, ScopeKind.Block), + Origin.None, + 0, + HoistedFunctionFlags.None, + 0, + start, + line, + column + ); +} + +/** + * Parses switch statement + * + * @see [Link](https://tc39.github.io/ecma262/#prod-SwitchStatement) + * + * @param parser Parser object + * @param context Context masks + * @param start Start pos of node + * @param line + * @param column + */ +export function parseSwitchStatement( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + labels: ESTree.Labels, + start: number, + line: number, + column: number +): ESTree.SwitchStatement { + // SwitchStatement :: + // 'switch' '(' Expression ')' '{' CaseClause* '}' + // CaseClause :: + // 'case' Expression ':' StatementList + // 'default' ':' StatementList + nextToken(parser, context); + consume(parser, context | Context.AllowRegExp, Token.LeftParen); + const discriminant = parseExpressions(parser, context, 0, 1, parser.tokenPos, parser.linePos, parser.colPos); + consume(parser, context, Token.RightParen); + consume(parser, context, Token.LeftBrace); + const cases: ESTree.SwitchCase[] = []; + let seenDefault: 0 | 1 = 0; + if (scope) scope = addChildScope(scope, ScopeKind.SwitchStatement); + while (parser.token !== Token.RightBrace) { + const { tokenPos, linePos, colPos } = parser; + let test: ESTree.Expression | null = null; + const consequent: ESTree.Statement[] = []; + if (consumeOpt(parser, context | Context.AllowRegExp, Token.CaseKeyword)) { + test = parseExpressions(parser, context, 0, 1, parser.tokenPos, parser.linePos, parser.colPos); + } else { + consume(parser, context | Context.AllowRegExp, Token.DefaultKeyword); + if (seenDefault) report(parser, Errors.MultipleDefaultsInSwitch); + seenDefault = 1; + } + consume(parser, context | Context.AllowRegExp, Token.Colon); + while ( + parser.token !== Token.CaseKeyword && + parser.token !== Token.RightBrace && + parser.token !== Token.DefaultKeyword + ) { + consequent.push( + parseStatementListItem(parser, context | Context.InSwitch, scope, Origin.BlockStatement, { + $: labels + }) as ESTree.Statement + ); + } + + cases.push( + finishNode(parser, context, tokenPos, linePos, colPos, { + type: 'SwitchCase', + test, + consequent + }) + ); + } + + consume(parser, context | Context.AllowRegExp, Token.RightBrace); + return finishNode(parser, context, start, line, column, { + type: 'SwitchStatement', + discriminant, + cases + }); +} + +/** + * Parses while statement + * + * @see [Link](https://tc39.github.io/ecma262/#prod-grammar-notation-WhileStatement) + * + * @param parser Parser object + * @param context Context masks + * @param start + * @param start Start pos of node + * @param line + * @param column + */ +export function parseWhileStatement( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + labels: ESTree.Labels, + start: number, + line: number, + column: number +): ESTree.WhileStatement { + // WhileStatement :: + // 'while' '(' Expression ')' Statement + nextToken(parser, context); + consume(parser, context | Context.AllowRegExp, Token.LeftParen); + const test = parseExpressions(parser, context, 0, 1, parser.tokenPos, parser.linePos, parser.colPos); + consume(parser, context | Context.AllowRegExp, Token.RightParen); + const body = parseIterationStatementBody(parser, context, scope, labels); + return finishNode(parser, context, start, line, column, { + type: 'WhileStatement', + test, + body + }); +} + +/** + * Parses iteration statement body + * + * @see [Link](https://tc39.es/ecma262/#sec-iteration-statements) + * + * @param parser Parser object + * @param context Context masks + * @param start Start pos of node + * @param line + * @param column + */ +export function parseIterationStatementBody( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + labels: ESTree.Labels +): ESTree.Statement { + return parseStatement( + parser, + ((context | Context.DisallowIn) ^ Context.DisallowIn) | Context.InIteration, + scope, + Origin.None, + { loop: 1, $: labels }, + 0, + parser.tokenPos, + parser.linePos, + parser.colPos + ); +} + +/** + * Parses the continue statement production + * + * @see [Link](https://tc39.github.io/ecma262/#prod-ContinueStatement) + * + * @param parser Parser object + * @param context Context masks + */ +export function parseContinueStatement( + parser: ParserState, + context: Context, + labels: ESTree.Labels, + start: number, + line: number, + column: number +): ESTree.ContinueStatement { + // ContinueStatement :: + // 'continue' Identifier? ';' + if ((context & Context.InIteration) < 1) report(parser, Errors.IllegalContinue); + nextToken(parser, context); + let label: ESTree.Identifier | undefined | null = null; + if ((parser.flags & Flags.NewLine) < 1 && parser.token & Token.IsIdentifier) { + const { tokenValue } = parser; + label = parseIdentifier(parser, context | Context.AllowRegExp, 0); + if (!isValidLabel(parser, labels, tokenValue, /* requireIterationStatement */ 1)) + report(parser, Errors.UnknownLabel, tokenValue); + } + matchOrInsertSemicolon(parser, context | Context.AllowRegExp); + return finishNode(parser, context, start, line, column, { + type: 'ContinueStatement', + label + }); +} + +/** + * Parses the break statement production + * + * @see [Link](https://tc39.github.io/ecma262/#prod-BreakStatement) + * + * @param parser Parser object + * @param context Context masks + * @param start Start pos of node + * @param line + * @param column + */ +export function parseBreakStatement( + parser: ParserState, + context: Context, + labels: ESTree.Labels, + start: number, + line: number, + column: number +): ESTree.BreakStatement { + // BreakStatement :: + // 'break' Identifier? ';' + nextToken(parser, context | Context.AllowRegExp); + let label: ESTree.Identifier | undefined | null = null; + if ((parser.flags & Flags.NewLine) < 1 && parser.token & Token.IsIdentifier) { + const { tokenValue } = parser; + label = parseIdentifier(parser, context | Context.AllowRegExp, 0); + if (!isValidLabel(parser, labels, tokenValue, /* requireIterationStatement */ 0)) + report(parser, Errors.UnknownLabel, tokenValue); + } else if ((context & (Context.InSwitch | Context.InIteration)) < 1) { + report(parser, Errors.IllegalBreak); + } + + matchOrInsertSemicolon(parser, context | Context.AllowRegExp); + return finishNode(parser, context, start, line, column, { + type: 'BreakStatement', + label + }); +} + +/** + * Parses with statement + * + * @see [Link](https://tc39.github.io/ecma262/#prod-WithStatement) + * + * @param parser Parser object + * @param context Context masks + * @param scope Scope instance + * @param start Start pos of node + * @param line + * @param column + */ +export function parseWithStatement( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + labels: ESTree.Labels, + start: number, + line: number, + column: number +): ESTree.WithStatement { + // WithStatement :: + // 'with' '(' Expression ')' Statement + + nextToken(parser, context); + + if (context & Context.Strict) report(parser, Errors.StrictWith); + + consume(parser, context | Context.AllowRegExp, Token.LeftParen); + const object = parseExpressions(parser, context, 0, 1, parser.tokenPos, parser.linePos, parser.colPos); + consume(parser, context | Context.AllowRegExp, Token.RightParen); + const body = parseStatement( + parser, + context, + scope, + Origin.BlockStatement, + labels, + 0, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + return finishNode(parser, context, start, line, column, { + type: 'WithStatement', + object, + body + }); +} + +/** + * Parses the debugger statement production + * + * @see [Link](https://tc39.github.io/ecma262/#prod-DebuggerStatement) + * + * @param parser Parser object + * @param context Context masks + * @param start Start pos of node + * @param line + * @param column + */ +export function parseDebuggerStatement( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.DebuggerStatement { + // DebuggerStatement :: + // 'debugger' ';' + nextToken(parser, context | Context.AllowRegExp); + matchOrInsertSemicolon(parser, context | Context.AllowRegExp); + return finishNode(parser, context, start, line, column, { + type: 'DebuggerStatement' + }); +} + +/** + * Parses try statement + * + * @see [Link](https://tc39.github.io/ecma262/#prod-TryStatement) + * + * @param parser Parser object + * @param context Context masks + * @param scope Scope instance + * @param start Start pos of node + * @param line + * @param column + */ +export function parseTryStatement( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + labels: ESTree.Labels, + start: number, + line: number, + column: number +): ESTree.TryStatement { + // TryStatement :: + // 'try' Block Catch + // 'try' Block Finally + // 'try' Block Catch Finally + // + // Catch :: + // 'catch' '(' Identifier ')' Block + // + // Finally :: + // 'finally' Block + + nextToken(parser, context | Context.AllowRegExp); + + const firstScope = scope ? addChildScope(scope, ScopeKind.TryStatement) : void 0; + + const block = parseBlock(parser, context, firstScope, { $: labels }, parser.tokenPos, parser.linePos, parser.colPos); + const { tokenPos, linePos, colPos } = parser; + const handler = consumeOpt(parser, context | Context.AllowRegExp, Token.CatchKeyword) + ? parseCatchBlock(parser, context, scope, labels, tokenPos, linePos, colPos) + : null; + + let finalizer: ESTree.BlockStatement | null = null; + + if (parser.token === Token.FinallyKeyword) { + nextToken(parser, context | Context.AllowRegExp); + const finalizerScope = firstScope ? addChildScope(scope, ScopeKind.CatchStatement) : void 0; + finalizer = parseBlock( + parser, + context, + finalizerScope, + { $: labels }, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + } + + if (!handler && !finalizer) { + report(parser, Errors.NoCatchOrFinally); + } + + return finishNode(parser, context, start, line, column, { + type: 'TryStatement', + block, + handler, + finalizer + }); +} + +/** + * Parses catch block + * + * @see [Link](https://tc39.github.io/ecma262/#prod-Catch) + * + * @param parser Parser object + * @param context Context masks + * @param scope Scope instance + * @param start Start pos of node + * @param line + * @param column + */ +export function parseCatchBlock( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + labels: ESTree.Labels, + start: number, + line: number, + column: number +): ESTree.CatchClause { + let param: ESTree.BindingPattern | ESTree.Identifier | null = null; + let additionalScope: ScopeState | undefined = scope; + + if (consumeOpt(parser, context, Token.LeftParen)) { + /* + * Create a lexical scope around the whole catch clause, + * including the head. + */ + if (scope) scope = addChildScope(scope, ScopeKind.CatchStatement); + + param = parseBindingPattern( + parser, + context, + scope, + (parser.token & Token.IsPatternStart) === Token.IsPatternStart + ? BindingKind.CatchPattern + : BindingKind.CatchIdentifier, + Origin.None, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + + if (parser.token === Token.Comma) { + report(parser, Errors.InvalidCatchParams); + } else if (parser.token === Token.Assign) { + report(parser, Errors.InvalidCatchParamDefault); + } + + consume(parser, context | Context.AllowRegExp, Token.RightParen); + // ES 13.15.7 CatchClauseEvaluation + // + // Step 8 means that the body of a catch block always has an additional + // lexical scope. + if (scope) additionalScope = addChildScope(scope, ScopeKind.CatchBlock); + } + + const body = parseBlock( + parser, + context, + additionalScope, + { $: labels }, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + + return finishNode(parser, context, start, line, column, { + type: 'CatchClause', + param, + body + }); +} + +/** + * Parses do while statement + * + * @param parser Parser object + * @param context Context masks + * @param scope Scope instance + * @param start Start pos of node + * @param line + * @param column + */ +export function parseDoWhileStatement( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + labels: ESTree.Labels, + start: number, + line: number, + column: number +): ESTree.DoWhileStatement { + // DoStatement :: + // 'do Statement while ( Expression ) ;' + + nextToken(parser, context | Context.AllowRegExp); + const body = parseIterationStatementBody(parser, context, scope, labels); + consume(parser, context, Token.WhileKeyword); + consume(parser, context | Context.AllowRegExp, Token.LeftParen); + const test = parseExpressions(parser, context, 0, 1, parser.tokenPos, parser.linePos, parser.colPos); + consume(parser, context | Context.AllowRegExp, Token.RightParen); + // ECMA-262, section 11.9 + // The previous token is ) and the inserted semicolon would then be parsed as the terminating semicolon of a do-while statement (13.7.2). + // This cannot be implemented in matchOrInsertSemicolon() because it doesn't know + // this RightRaren is the end of a do-while statement. + consumeOpt(parser, context, Token.Semicolon); + return finishNode(parser, context, start, line, column, { + type: 'DoWhileStatement', + body, + test + }); +} + +/** + * Because we are not doing any backtracking - this parses `let` as an identifier + * or a variable declaration statement. + * + * @see [Link](https://tc39.github.io/ecma262/#sec-declarations-and-the-variable-statement) + * + * @param parser Parser object + * @param context Context masks + * @param scope Scope object + * @param origin Binding origin + * @param start Start pos of node + * @param start Start pos of node + * @param line + * @param column + */ +export function parseLetIdentOrVarDeclarationStatement( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + origin: Origin, + start: number, + line: number, + column: number +): ESTree.VariableDeclaration | ESTree.LabeledStatement | ESTree.ExpressionStatement { + const { token, tokenValue } = parser; + let expr: ESTree.Identifier | ESTree.Expression = parseIdentifier(parser, context, 0); + + if (parser.token & (Token.IsIdentifier | Token.IsPatternStart)) { + /* VariableDeclarations :: + * ('let') (Identifier ('=' AssignmentExpression)?)+[','] + */ + + const declarations = parseVariableDeclarationList(parser, context, scope, BindingKind.Let, Origin.None); + + matchOrInsertSemicolon(parser, context | Context.AllowRegExp); + + return finishNode(parser, context, start, line, column, { + type: 'VariableDeclaration', + kind: 'let', + declarations + }); + } + // 'Let' as identifier + parser.assignable = AssignmentKind.Assignable; + + if (context & Context.Strict) report(parser, Errors.UnexpectedLetStrictReserved); + + /** LabelledStatement[Yield, Await, Return]: + * + * ExpressionStatement | LabelledStatement :: + * Expression ';' + * Identifier ':' Statement + * + * ExpressionStatement[Yield] : + * [lookahead notin {{, function, class, let [}] Expression[In, ?Yield] ; + */ + + if (parser.token === Token.Colon) { + return parseLabelledStatement(parser, context, scope, origin, {}, tokenValue, expr, token, 0, start, line, column); + } + + /** + * ArrowFunction : + * ArrowParameters => ConciseBody + * + * ConciseBody : + * [lookahead not {] AssignmentExpression + * { FunctionBody } + * + */ + if (parser.token === Token.Arrow) { + let scope: ScopeState | undefined = void 0; + + if (context & Context.OptionsLexical) scope = createArrowHeadParsingScope(parser, context, tokenValue); + + parser.flags = (parser.flags | Flags.SimpleParameterList) ^ Flags.SimpleParameterList; + + expr = parseArrowFunctionExpression(parser, context, scope, [expr], /* isAsync */ 0, start, line, column); + } else { + /** + * UpdateExpression :: + * ('++' | '--')? LeftHandSideExpression + * + * MemberExpression :: + * (PrimaryExpression | FunctionLiteral | ClassLiteral) + * ('[' Expression ']' | '.' Identifier | Arguments | TemplateLiteral)* + * + * CallExpression :: + * (SuperCall | ImportCall) + * ('[' Expression ']' | '.' Identifier | Arguments | TemplateLiteral)* + * + * LeftHandSideExpression :: + * (NewExpression | MemberExpression) ... + */ + + expr = parseMemberOrUpdateExpression(parser, context, expr, 0, 0, start, line, column); + + /** + * AssignmentExpression : + * 1. ConditionalExpression + * 2. LeftHandSideExpression = AssignmentExpression + * + */ + expr = parseAssignmentExpression(parser, context, 0, 0, start, line, column, expr as ESTree.ArgumentExpression); + } + + /** Sequence expression + */ + if (parser.token === Token.Comma) { + expr = parseSequenceExpression(parser, context, 0, start, line, column, expr); + } + + /** + * ExpressionStatement[Yield, Await]: + * [lookahead ∉ { {, function, async [no LineTerminator here] function, class, let [ }]Expression[+In, ?Yield, ?Await] + */ + return parseExpressionStatement(parser, context, expr, start, line, column); +} + +/** + * Parses a `const` or `let` lexical declaration statement + * + * @param parser Parser object + * @param context Context masks + * @param type Binding kind + * @param origin Binding origin + * @param type Binding kind + * @param start Start pos of node + * @param start Start pos of node + * @param line + * @param column + */ +function parseLexicalDeclaration( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + kind: BindingKind, + origin: Origin, + start: number, + line: number, + column: number +): ESTree.VariableDeclaration { + // BindingList :: + // LexicalBinding + // BindingIdentifier + // BindingPattern + nextToken(parser, context); + + const declarations = parseVariableDeclarationList(parser, context, scope, kind, origin); + + matchOrInsertSemicolon(parser, context | Context.AllowRegExp); + + return finishNode(parser, context, start, line, column, { + type: 'VariableDeclaration', + kind: kind & BindingKind.Let ? 'let' : 'const', + declarations + }); +} + +/** + * Parses a variable declaration statement + * + * @see [Link](https://tc39.github.io/ecma262/#prod-VariableStatement) + * + * @param parser Parser object + * @param context Context masks + * @param scope Scope object + * @param origin Binding origin + * @param start Start pos of node + * @param start Start pos of node + * @param line + * @param column + */ +export function parseVariableStatement( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + origin: Origin, + start: number, + line: number, + column: number +): ESTree.VariableDeclaration { + // VariableDeclarations :: + // ('var') (Identifier ('=' AssignmentExpression)?)+[','] + // + nextToken(parser, context); + const declarations = parseVariableDeclarationList(parser, context, scope, BindingKind.Variable, origin); + + matchOrInsertSemicolon(parser, context | Context.AllowRegExp); + return finishNode(parser, context, start, line, column, { + type: 'VariableDeclaration', + kind: 'var', + declarations + }); +} + +/** + * Parses variable declaration list + * + * @see [Link](https://tc39.github.io/ecma262/#prod-VariableDeclarationList) + * + * @param parser Parser object + * @param context Context masks + * @param type Binding kind + * @param origin Binding origin + */ +export function parseVariableDeclarationList( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + kind: BindingKind, + origin: Origin +): ESTree.VariableDeclarator[] { + let bindingCount = 1; + const list: ESTree.VariableDeclarator[] = [parseVariableDeclaration(parser, context, scope, kind, origin)]; + while (consumeOpt(parser, context, Token.Comma)) { + bindingCount++; + list.push(parseVariableDeclaration(parser, context, scope, kind, origin)); + } + + if (bindingCount > 1 && origin & Origin.ForStatement && parser.token & Token.IsInOrOf) { + report(parser, Errors.ForInOfLoopMultiBindings, KeywordDescTable[parser.token & Token.Type]); + } + return list; +} + +/** + * Parses variable declaration + * + * @see [Link](https://tc39.github.io/ecma262/#prod-VariableDeclaration) + * + * @param parser Parser object + * @param context Context masks + * @param start Start pos of node + * @param start Start pos of node + * @param line + * @param column + */ +function parseVariableDeclaration( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + kind: BindingKind, + origin: Origin +): ESTree.VariableDeclarator { + // VariableDeclaration : + // BindingIdentifier Initializeropt + // BindingPattern Initializer + // + // VariableDeclarationNoIn : + // BindingIdentifier InitializerNoInopt + // BindingPattern InitializerNoIn + + const { token, tokenPos, linePos, colPos } = parser; + + let init: ESTree.Expression | ESTree.BindingPattern | ESTree.Identifier | null = null; + + const id = parseBindingPattern(parser, context, scope, kind, origin, tokenPos, linePos, colPos); + + if (parser.token === Token.Assign) { + nextToken(parser, context | Context.AllowRegExp); + init = parseExpression(parser, context, 1, 0, 0, parser.tokenPos, parser.linePos, parser.colPos); + if (origin & Origin.ForStatement || (token & Token.IsPatternStart) < 1) { + // Lexical declarations in for-in / for-of loops can't be initialized + + if ( + parser.token === Token.OfKeyword || + (parser.token === Token.InKeyword && + (token & Token.IsPatternStart || (kind & BindingKind.Variable) < 1 || context & Context.Strict)) + ) { + reportMessageAt( + tokenPos, + parser.line, + parser.index - 3, + Errors.ForInOfLoopInitializer, + parser.token === Token.OfKeyword ? 'of' : 'in' + ); + } + } + // Normal const declarations, and const declarations in for(;;) heads, must be initialized. + } else if ( + (kind & BindingKind.Const || (token & Token.IsPatternStart) > 0) && + (parser.token & Token.IsInOrOf) !== Token.IsInOrOf + ) { + report(parser, Errors.DeclarationMissingInitializer, kind & BindingKind.Const ? 'const' : 'destructuring'); + } + + return finishNode(parser, context, tokenPos, linePos, colPos, { + type: 'VariableDeclarator', + id, + init + }); +} + +/** + * Parses either For, ForIn or ForOf statement + * + * @see [Link](https://tc39.github.io/ecma262/#sec-for-statement) + * @see [Link](https://tc39.github.io/ecma262/#sec-for-in-and-for-of-statements) + * + * @param parser Parser object + * @param context Context masks + * @param start Start pos of node + * @param line + * @param column + + */ +export function parseForStatement( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + labels: ESTree.Labels, + start: number, + line: number, + column: number +): ESTree.ForStatement | ESTree.ForInStatement | ESTree.ForOfStatement { + nextToken(parser, context); + + const forAwait = (context & Context.InAwaitContext) > 0 && consumeOpt(parser, context, Token.AwaitKeyword); + + consume(parser, context | Context.AllowRegExp, Token.LeftParen); + + if (scope) scope = addChildScope(scope, ScopeKind.ForStatement); + + let test: ESTree.Expression | null = null; + let update: ESTree.Expression | null = null; + let destructible: AssignmentKind | DestructuringKind = 0; + let init = null; + let isVarDecl = + parser.token === Token.VarKeyword || parser.token === Token.LetKeyword || parser.token === Token.ConstKeyword; + let right; + + const { token, tokenPos, linePos, colPos } = parser; + + if (isVarDecl) { + if (token === Token.LetKeyword) { + init = parseIdentifier(parser, context, 0); + if (parser.token & (Token.IsIdentifier | Token.IsPatternStart)) { + if (parser.token === Token.InKeyword) { + if (context & Context.Strict) report(parser, Errors.DisallowedLetInStrict); + } else { + init = finishNode(parser, context, tokenPos, linePos, colPos, { + type: 'VariableDeclaration', + kind: 'let', + declarations: parseVariableDeclarationList( + parser, + context | Context.DisallowIn, + scope, + BindingKind.Let, + Origin.ForStatement + ) + }); + } + + parser.assignable = AssignmentKind.Assignable; + } else if (context & Context.Strict) { + report(parser, Errors.DisallowedLetInStrict); + } else { + isVarDecl = false; + parser.assignable = AssignmentKind.Assignable; + init = parseMemberOrUpdateExpression(parser, context, init, 0, 0, tokenPos, linePos, colPos); + + // `for of` only allows LeftHandSideExpressions which do not start with `let`, and no other production matches + if (parser.token === Token.OfKeyword) report(parser, Errors.ForOfLet); + } + } else { + nextToken(parser, context); + + init = finishNode( + parser, + context, + tokenPos, + linePos, + colPos, + token === Token.VarKeyword + ? { + type: 'VariableDeclaration', + kind: 'var', + declarations: parseVariableDeclarationList( + parser, + context | Context.DisallowIn, + scope, + BindingKind.Variable, + Origin.ForStatement + ) + } + : { + type: 'VariableDeclaration', + kind: 'const', + declarations: parseVariableDeclarationList( + parser, + context | Context.DisallowIn, + scope, + BindingKind.Const, + Origin.ForStatement + ) + } + ); + + parser.assignable = AssignmentKind.Assignable; + } + } else if (token === Token.Semicolon) { + if (forAwait) report(parser, Errors.InvalidForAwait); + } else if ((token & Token.IsPatternStart) === Token.IsPatternStart) { + init = + token === Token.LeftBrace + ? parseObjectLiteralOrPattern( + parser, + context, + void 0, + 1, + 0, + 0, + BindingKind.Empty, + Origin.ForStatement, + tokenPos, + linePos, + colPos + ) + : parseArrayExpressionOrPattern( + parser, + context, + void 0, + 1, + 0, + 0, + BindingKind.Empty, + Origin.ForStatement, + tokenPos, + linePos, + colPos + ); + + destructible = parser.destructible; + + if (context & Context.OptionsWebCompat && destructible & DestructuringKind.SeenProto) { + report(parser, Errors.DuplicateProto); + } + + parser.assignable = + destructible & DestructuringKind.CannotDestruct ? AssignmentKind.CannotAssign : AssignmentKind.Assignable; + + init = parseMemberOrUpdateExpression( + parser, + context | Context.DisallowIn, + init as ESTree.Expression, + 0, + 0, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + } else { + init = parseLeftHandSideExpression(parser, context | Context.DisallowIn, 1, 0, 1, tokenPos, linePos, colPos); + } + + if ((parser.token & Token.IsInOrOf) === Token.IsInOrOf) { + if (parser.token === Token.OfKeyword) { + if (parser.assignable & AssignmentKind.CannotAssign) + report(parser, Errors.CantAssignToInOfForLoop, forAwait ? 'await' : 'of'); + + reinterpretToPattern(parser, init); + nextToken(parser, context | Context.AllowRegExp); + + // IterationStatement: + // for(LeftHandSideExpression of AssignmentExpression) Statement + // forawait(LeftHandSideExpression of AssignmentExpression) Statement + right = parseExpression(parser, context, 1, 0, 0, parser.tokenPos, parser.linePos, parser.colPos); + + consume(parser, context | Context.AllowRegExp, Token.RightParen); + + const body = parseIterationStatementBody(parser, context, scope, labels); + + return finishNode(parser, context, start, line, column, { + type: 'ForOfStatement', + left: init, + right, + body, + await: forAwait + }); + } + + if (parser.assignable & AssignmentKind.CannotAssign) report(parser, Errors.CantAssignToInOfForLoop, 'in'); + + reinterpretToPattern(parser, init); + nextToken(parser, context | Context.AllowRegExp); + + // `for await` only accepts the `for-of` type + if (forAwait) report(parser, Errors.InvalidForAwait); + + // IterationStatement: + // for(LeftHandSideExpression in Expression) Statement + right = parseExpressions(parser, context, 0, 1, parser.tokenPos, parser.linePos, parser.colPos); + + consume(parser, context | Context.AllowRegExp, Token.RightParen); + const body = parseIterationStatementBody(parser, context, scope, labels); + + return finishNode(parser, context, start, line, column, { + type: 'ForInStatement', + body, + left: init, + right + }); + } + + if (forAwait) report(parser, Errors.InvalidForAwait); + + if (!isVarDecl) { + if (destructible & DestructuringKind.HasToDestruct && parser.token !== Token.Assign) { + report(parser, Errors.CantAssignToInOfForLoop, 'loop'); + } + + init = parseAssignmentExpression(parser, context | Context.DisallowIn, 0, 0, tokenPos, linePos, colPos, init); + } + + if (parser.token === Token.Comma) + init = parseSequenceExpression(parser, context, 0, parser.tokenPos, parser.linePos, parser.colPos, init); + + consume(parser, context | Context.AllowRegExp, Token.Semicolon); + + if (parser.token !== Token.Semicolon) + test = parseExpressions(parser, context, 0, 1, parser.tokenPos, parser.linePos, parser.colPos); + + consume(parser, context | Context.AllowRegExp, Token.Semicolon); + + if (parser.token !== Token.RightParen) + update = parseExpressions(parser, context, 0, 1, parser.tokenPos, parser.linePos, parser.colPos); + + consume(parser, context | Context.AllowRegExp, Token.RightParen); + + const body = parseIterationStatementBody(parser, context, scope, labels); + + return finishNode(parser, context, start, line, column, { + type: 'ForStatement', + init, + test, + update, + body + }); +} + +/** + * Parses restricted identifier in import & export declaration + * + * @param parser Parser object + * @param context Context masks + * @param scope Scope object + */ + +export function parseRestrictedIdentifier( + parser: ParserState, + context: Context, + scope: ScopeState | undefined +): ESTree.Identifier { + if (!isValidIdentifier(context, parser.token)) report(parser, Errors.UnexpectedStrictReserved); + if ((parser.token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments) report(parser, Errors.StrictEvalArguments); + if (scope) addBlockName(parser, context, scope, parser.tokenValue, BindingKind.Let, Origin.None); + return parseIdentifier(parser, context, 0); +} + +/** + * Parse import declaration + * + * @see [Link](https://tc39.github.io/ecma262/#prod-ImportDeclaration) + * + * @param parser Parser object + * @param context Context masks + * @param scope Scope object + */ +function parseImportDeclaration( + parser: ParserState, + context: Context, + scope: ScopeState | undefined +): ESTree.ImportDeclaration | ESTree.ExpressionStatement { + // ImportDeclaration : + // 'import' ImportClause 'from' ModuleSpecifier ';' + // 'import' ModuleSpecifier ';' + // + // ImportClause : + // ImportedDefaultBinding + // NameSpaceImport + // NamedImports + // ImportedDefaultBinding ',' NameSpaceImport + // ImportedDefaultBinding ',' NamedImports + // + // NameSpaceImport : + // '*' 'as' ImportedBinding + const start = parser.tokenPos; + const line = parser.linePos; + const column = parser.colPos; + + nextToken(parser, context); + + let source: ESTree.Literal | null = null; + + const { tokenPos, linePos, colPos } = parser; + + let specifiers: (ESTree.ImportSpecifier | ESTree.ImportDefaultSpecifier | ESTree.ImportNamespaceSpecifier)[] = []; + + // 'import' ModuleSpecifier ';' + if (parser.token === Token.StringLiteral) { + source = parseLiteral(parser, context); + } else { + if (parser.token & Token.IsIdentifier) { + const local = parseRestrictedIdentifier(parser, context, scope); + specifiers = [ + finishNode(parser, context, tokenPos, linePos, colPos, { + type: 'ImportDefaultSpecifier', + local + }) + ]; + + // NameSpaceImport + if (consumeOpt(parser, context, Token.Comma)) { + switch (parser.token) { + case Token.Multiply: + specifiers.push(parseImportNamespaceSpecifier(parser, context, scope)); + break; + + case Token.LeftBrace: + parseImportSpecifierOrNamedImports(parser, context, scope, specifiers); + break; + + default: + report(parser, Errors.InvalidDefaultImport); + } + } + } else { + // Parse NameSpaceImport or NamedImports if present + switch (parser.token) { + case Token.Multiply: + specifiers = [parseImportNamespaceSpecifier(parser, context, scope)]; + break; + case Token.LeftBrace: + parseImportSpecifierOrNamedImports(parser, context, scope, specifiers); + break; + case Token.LeftParen: + return parseImportCallDeclaration(parser, context, start, line, column); + case Token.Period: + return parseImportMetaDeclaration(parser, context, start, line, column); + default: + report(parser, Errors.UnexpectedToken, KeywordDescTable[parser.token & Token.Type]); + } + } + + source = parseModuleSpecifier(parser, context); + } + + matchOrInsertSemicolon(parser, context | Context.AllowRegExp); + + return finishNode(parser, context, start, line, column, { + type: 'ImportDeclaration', + specifiers, + source + }); +} + +/** + * Parse binding identifier + * + * @see [Link](https://tc39.github.io/ecma262/#prod-NameSpaceImport) + * + * @param parser Parser object + * @param context Context masks + * @param specifiers Array of import specifiers + */ +function parseImportNamespaceSpecifier( + parser: ParserState, + context: Context, + scope: ScopeState | undefined +): ESTree.ImportNamespaceSpecifier { + // NameSpaceImport: + // * as ImportedBinding + const { tokenPos, linePos, colPos } = parser; + nextToken(parser, context); + consume(parser, context, Token.AsKeyword); + + // 'import * as class from "foo":' + if ((parser.token & Token.IsStringOrNumber) === Token.IsStringOrNumber) { + reportMessageAt( + tokenPos, + parser.line, + parser.index, + Errors.UnexpectedToken, + KeywordDescTable[parser.token & Token.Type] + ); + } + + return finishNode(parser, context, tokenPos, linePos, colPos, { + type: 'ImportNamespaceSpecifier', + local: parseRestrictedIdentifier(parser, context, scope) + }); +} + +/** + * Parse module specifier + * + * @see [Link](https://tc39.github.io/ecma262/#prod-ModuleSpecifier) + * + * @param parser Parser object + * @param context Context masks + */ +function parseModuleSpecifier(parser: ParserState, context: Context): ESTree.Literal { + // ModuleSpecifier : + // StringLiteral + consumeOpt(parser, context, Token.FromKeyword); + + if (parser.token !== Token.StringLiteral) report(parser, Errors.InvalidExportImportSource, 'Import'); + + return parseLiteral(parser, context); +} + +function parseImportSpecifierOrNamedImports( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + specifiers: (ESTree.ImportSpecifier | ESTree.ImportDefaultSpecifier | ESTree.ImportNamespaceSpecifier)[] +): (ESTree.ImportSpecifier | ESTree.ImportDefaultSpecifier | ESTree.ImportNamespaceSpecifier)[] { + // NamedImports : + // '{' '}' + // '{' ImportsList '}' + // '{' ImportsList ',' '}' + // + // ImportsList : + // ImportSpecifier + // ImportsList ',' ImportSpecifier + // + // ImportSpecifier : + // BindingIdentifier + // IdentifierName 'as' BindingIdentifier + + nextToken(parser, context); + + while (parser.token & Token.IsIdentifier) { + let { token, tokenValue, tokenPos, linePos, colPos } = parser; + const imported = parseIdentifier(parser, context, 0); + let local: ESTree.Identifier; + + if (consumeOpt(parser, context, Token.AsKeyword)) { + if ((parser.token & Token.IsStringOrNumber) === Token.IsStringOrNumber || parser.token === Token.Comma) { + report(parser, Errors.InvalidKeywordAsAlias); + } else { + validateBindingIdentifier(parser, context, BindingKind.Const, parser.token, 0); + } + tokenValue = parser.tokenValue; + local = parseIdentifier(parser, context, 0); + } else { + // Keywords cannot be bound to themselves, so an import name + // that is a keyword is a syntax error if it is not followed + // by the keyword 'as'. + // See the ImportSpecifier production in ES6 section 15.2.2. + validateBindingIdentifier(parser, context, BindingKind.Const, token, 0); + local = imported; + } + + if (scope) addBlockName(parser, context, scope, tokenValue, BindingKind.Let, Origin.None); + + specifiers.push( + finishNode(parser, context, tokenPos, linePos, colPos, { + type: 'ImportSpecifier', + local, + imported + }) + ); + + if (parser.token !== Token.RightBrace) consume(parser, context, Token.Comma); + } + + consume(parser, context, Token.RightBrace); + + return specifiers; +} + +/** + * Parse import meta declaration + * + * @param parser Parser object + * @param context Context masks + * @param meta ESTree AST node + * @param start + * @param line + * @param column + */ +export function parseImportMetaDeclaration( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.ExpressionStatement { + let expr: ESTree.Expression = parseImportMetaExpression( + parser, + context, + finishNode(parser, context, start, line, column, { + type: 'Identifier', + name: 'import' + }), + start, + line, + column + ); + + /** MemberExpression : + * 1. PrimaryExpression + * 2. MemberExpression [ AssignmentExpression ] + * 3. MemberExpression . IdentifierName + * 4. MemberExpression TemplateLiteral + * + * CallExpression : + * 1. MemberExpression Arguments + * 2. CallExpression ImportCall + * 3. CallExpression Arguments + * 4. CallExpression [ AssignmentExpression ] + * 5. CallExpression . IdentifierName + * 6. CallExpression TemplateLiteral + * + * UpdateExpression :: + * ('++' | '--')? LeftHandSideExpression + * + */ + + expr = parseMemberOrUpdateExpression(parser, context, expr, 0, 0, start, line, column); + + /** AssignmentExpression : + * 1. ConditionalExpression + * 2. LeftHandSideExpression = AssignmentExpression + */ + + expr = parseAssignmentExpression(parser, context, 0, 0, start, line, column, expr as ESTree.Expression); + + /** + * ExpressionStatement[Yield, Await]: + * [lookahead ∉ { {, function, async [no LineTerminator here] function, class, let [ }]Expression[+In, ?Yield, ?Await] + */ + return parseExpressionStatement(parser, context, expr, start, line, column); +} + +/** + * Parse dynamic import declaration + * + * @see [Link](https://github.com/tc39/proposal-dynamic-import) + * + * @param parser Parser object + * @param context Context masks + * @param number + */ +function parseImportCallDeclaration( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.ExpressionStatement { + let expr = parseImportExpression(parser, context, /* inGroup */ 0, start, line, column); + + /** MemberExpression : + * 1. PrimaryExpression + * 2. MemberExpression [ AssignmentExpression ] + * 3. MemberExpression . IdentifierName + * 4. MemberExpression TemplateLiteral + * + * CallExpression : + * 1. MemberExpression Arguments + * 2. CallExpression ImportCall + * 3. CallExpression Arguments + * 4. CallExpression [ AssignmentExpression ] + * 5. CallExpression . IdentifierName + * 6. CallExpression TemplateLiteral + * + * UpdateExpression :: + * ('++' | '--')? LeftHandSideExpression + * + */ + + expr = parseMemberOrUpdateExpression(parser, context, expr, 0, 0, start, line, column); + + /** + * ExpressionStatement[Yield, Await]: + * [lookahead ∉ { {, function, async [no LineTerminator here] function, class, let [ }]Expression[+In, ?Yield, ?Await] + */ + return parseExpressionStatement(parser, context, expr, start, line, column); +} + +/** + * Parse export declaration + * + * @see [Link](https://tc39.github.io/ecma262/#prod-ExportDeclaration) + * + * @param parser Parser object + * @param context Context masks + */ +function parseExportDeclaration( + parser: ParserState, + context: Context, + scope: ScopeState | undefined +): ESTree.ExportAllDeclaration | ESTree.ExportNamedDeclaration | ESTree.ExportDefaultDeclaration { + // ExportDeclaration: + // 'export' '*' 'from' ModuleSpecifier ';' + // 'export' '*' 'as' IdentifierName 'from' ModuleSpecifier ';' + // 'export' ExportClause ('from' ModuleSpecifier)? ';' + // 'export' VariableStatement + // 'export' Declaration + // 'export' 'default' + const start = parser.tokenPos; + const line = parser.linePos; + const column = parser.colPos; + + // https://tc39.github.io/ecma262/#sec-exports + nextToken(parser, context | Context.AllowRegExp); + + const specifiers: ESTree.ExportSpecifier[] = []; + + let declaration: ESTree.ExportDeclaration | ESTree.Expression | null = null; + let source: ESTree.Literal | null = null; + let key: string; + + if (consumeOpt(parser, context | Context.AllowRegExp, Token.DefaultKeyword)) { + // export default HoistableDeclaration[Default] + // export default ClassDeclaration[Default] + // export default [lookahead not-in {function, class}] AssignmentExpression[In] ; + + switch (parser.token) { + // export default HoistableDeclaration[Default] + case Token.FunctionKeyword: { + declaration = parseFunctionDeclaration( + parser, + context, + scope, + Origin.TopLevel, + 1, + HoistedFunctionFlags.Hoisted, + 0, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + break; + } + // export default ClassDeclaration[Default] + // export default @decl ClassDeclaration[Default] + case Token.Decorator: + case Token.ClassKeyword: + declaration = parseClassDeclaration( + parser, + context, + scope, + HoistedClassFlags.Hoisted, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + break; + + // export default HoistableDeclaration[Default] + case Token.AsyncKeyword: + const { tokenPos, linePos, colPos } = parser; + + declaration = parseIdentifier(parser, context, 0); + + const { flags } = parser; + + if ((flags & Flags.NewLine) < 1) { + if (parser.token === Token.FunctionKeyword) { + declaration = parseFunctionDeclaration( + parser, + context, + scope, + Origin.TopLevel, + 1, + HoistedFunctionFlags.Hoisted, + 1, + tokenPos, + linePos, + colPos + ); + } else { + if (parser.token === Token.LeftParen) { + declaration = parseAsyncArrowOrCallExpression( + parser, + context, + declaration, + 1, + BindingKind.ArgumentList, + Origin.None, + flags, + tokenPos, + linePos, + colPos + ); + declaration = parseMemberOrUpdateExpression( + parser, + context, + declaration as any, + 0, + 0, + tokenPos, + linePos, + colPos + ); + declaration = parseAssignmentExpression( + parser, + context, + 0, + 0, + tokenPos, + linePos, + colPos, + declaration as any + ); + } else if (parser.token & Token.IsIdentifier) { + if (scope) scope = createArrowHeadParsingScope(parser, context, parser.tokenValue); + + declaration = parseIdentifier(parser, context, 0); + declaration = parseArrowFunctionExpression( + parser, + context, + scope, + [declaration], + 1, + tokenPos, + linePos, + colPos + ); + } + } + } + break; + + default: + // export default [lookahead ∉ {function, class}] AssignmentExpression[In] ; + declaration = parseExpression(parser, context, 1, 0, 0, parser.tokenPos, parser.linePos, parser.colPos); + matchOrInsertSemicolon(parser, context | Context.AllowRegExp); + } + + // See: https://www.ecma-international.org/ecma-262/9.0/index.html#sec-exports-static-semantics-exportednames + if (scope) declareUnboundVariable(parser, 'default'); + + return finishNode(parser, context, start, line, column, { + type: 'ExportDefaultDeclaration', + declaration + }); + } + + switch (parser.token) { + case Token.Multiply: { + // + // 'export' '*' 'as' IdentifierName 'from' ModuleSpecifier ';' + // + // See: https://github.com/tc39/ecma262/pull/1174 + nextToken(parser, context); // Skips: '*' + + let exported: ESTree.Identifier | null = null; + const isNamedDeclaration = consumeOpt(parser, context, Token.AsKeyword); + + if (isNamedDeclaration) { + if (scope) declareUnboundVariable(parser, parser.tokenValue); + exported = parseIdentifier(parser, context, 0); + } + + consume(parser, context, Token.FromKeyword); + + if (parser.token !== Token.StringLiteral) report(parser, Errors.InvalidExportImportSource, 'Export'); + + source = parseLiteral(parser, context); + + matchOrInsertSemicolon(parser, context | Context.AllowRegExp); + + return finishNode(parser, context, start, line, column, { + type: 'ExportAllDeclaration', + source, + exported + } as any); + } + case Token.LeftBrace: { + // ExportClause : + // '{' '}' + // '{' ExportsList '}' + // '{' ExportsList ',' '}' + // + // ExportsList : + // ExportSpecifier + // ExportsList ',' ExportSpecifier + // + // ExportSpecifier : + // IdentifierName + // IdentifierName 'as' IdentifierName + + nextToken(parser, context); // Skips: '{' + + const tmpExportedNames: string[] = []; + const tmpExportedBindings: string[] = []; + + while (parser.token & Token.IsIdentifier) { + const { tokenPos, tokenValue, linePos, colPos } = parser; + const local = parseIdentifier(parser, context, 0); + + let exported: ESTree.Identifier | null; + + if (parser.token === Token.AsKeyword) { + nextToken(parser, context); + if ((parser.token & Token.IsStringOrNumber) === Token.IsStringOrNumber) { + report(parser, Errors.InvalidKeywordAsAlias); + } + if (scope) { + tmpExportedNames.push(parser.tokenValue); + tmpExportedBindings.push(tokenValue); + } + exported = parseIdentifier(parser, context, 0); + } else { + if (scope) { + tmpExportedNames.push(parser.tokenValue); + tmpExportedBindings.push(parser.tokenValue); + } + exported = local; + } + + specifiers.push( + finishNode(parser, context, tokenPos, linePos, colPos, { + type: 'ExportSpecifier', + local, + exported + }) + ); + + if (parser.token !== Token.RightBrace) consume(parser, context, Token.Comma); + } + + consume(parser, context, Token.RightBrace); + + if (consumeOpt(parser, context, Token.FromKeyword)) { + // The left hand side can't be a keyword where there is no + // 'from' keyword since it references a local binding. + if (parser.token !== Token.StringLiteral) report(parser, Errors.InvalidExportImportSource, 'Export'); + + source = parseLiteral(parser, context); + } else if (scope) { + let i = 0; + let iMax = tmpExportedNames.length; + for (; i < iMax; i++) { + declareUnboundVariable(parser, tmpExportedNames[i]); + } + i = 0; + iMax = tmpExportedBindings.length; + + for (; i < iMax; i++) { + addBindingToExports(parser, tmpExportedBindings[i]); + } + } + + matchOrInsertSemicolon(parser, context | Context.AllowRegExp); + + break; + } + + case Token.ClassKeyword: + declaration = parseClassDeclaration( + parser, + context, + scope, + HoistedClassFlags.Export, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + break; + case Token.FunctionKeyword: + declaration = parseFunctionDeclaration( + parser, + context, + scope, + Origin.TopLevel, + 1, + HoistedFunctionFlags.Export, + 0, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + break; + + case Token.LetKeyword: + declaration = parseLexicalDeclaration( + parser, + context, + scope, + BindingKind.Let, + Origin.Export, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + break; + case Token.ConstKeyword: + declaration = parseLexicalDeclaration( + parser, + context, + scope, + BindingKind.Const, + Origin.Export, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + break; + case Token.VarKeyword: + declaration = parseVariableStatement( + parser, + context, + scope, + Origin.Export, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + break; + case Token.AsyncKeyword: + const { tokenPos, linePos, colPos } = parser; + + nextToken(parser, context); + + if ((parser.flags & Flags.NewLine) < 1 && parser.token === Token.FunctionKeyword) { + declaration = parseFunctionDeclaration( + parser, + context, + scope, + Origin.TopLevel, + 1, + HoistedFunctionFlags.Export, + 1, + tokenPos, + linePos, + colPos + ); + if (scope) { + key = declaration.id ? declaration.id.name : ''; + declareUnboundVariable(parser, key); + } + break; + } + // falls through + default: + report(parser, Errors.UnexpectedToken, KeywordDescTable[parser.token & Token.Type]); + } + + return finishNode(parser, context, start, line, column, { + type: 'ExportNamedDeclaration', + declaration, + specifiers, + source + }); +} + +/** + * Parses an expression + * + * @param parser Parser object + * @param context Context masks + * @param assignable + */ +export function parseExpression( + parser: ParserState, + context: Context, + canAssign: 0 | 1, + isPattern: 0 | 1, + inGroup: 0 | 1, + start: number, + line: number, + column: number +): ESTree.Expression { + // Expression :: + // AssignmentExpression + // Expression ',' AssignmentExpression + + let expr = parsePrimaryExpression( + parser, + context, + BindingKind.Empty, + 0, + canAssign, + isPattern, + inGroup, + 1, + start, + line, + column + ); + + expr = parseMemberOrUpdateExpression(parser, context, expr, inGroup, 0, start, line, column); + + return parseAssignmentExpression(parser, context, inGroup, 0, start, line, column, expr); +} + +/** + * Parse sequence expression + * + * @param parser Parser object + * @param context Context masks + * @param expr ESTree AST node + */ +export function parseSequenceExpression( + parser: ParserState, + context: Context, + inGroup: 0 | 1, + start: number, + line: number, + column: number, + expr: ESTree.AssignmentExpression | ESTree.Expression +): ESTree.SequenceExpression { + // Expression :: + // AssignmentExpression + // Expression ',' AssignmentExpression + const expressions: ESTree.Expression[] = [expr]; + while (consumeOpt(parser, context | Context.AllowRegExp, Token.Comma)) { + expressions.push(parseExpression(parser, context, 1, 0, inGroup, parser.tokenPos, parser.linePos, parser.colPos)); + } + + return finishNode(parser, context, start, line, column, { + type: 'SequenceExpression', + expressions + }); +} + +/** + * Parse expression or sequence expression + * + * @param parser Parser object + * @param context Context masks + * @param canAssign + */ +export function parseExpressions( + parser: ParserState, + context: Context, + inGroup: 0 | 1, + canAssign: 0 | 1, + start: number, + line: number, + column: number +): ESTree.SequenceExpression | ESTree.Expression { + const expr = parseExpression(parser, context, canAssign, 0, inGroup, start, line, column); + return parser.token === Token.Comma + ? parseSequenceExpression(parser, context, inGroup, start, line, column, expr) + : expr; +} + +/** + * Parse assignment expression + * + * @param parser Parser object + * @param context Context masks + * @param inGroup + * @param isPattern + * @param start + * @param line + * @param column + * @param left + * + * * @param left ESTree AST node + */ +export function parseAssignmentExpression( + parser: ParserState, + context: Context, + inGroup: 0 | 1, + isPattern: 0 | 1, + start: number, + line: number, + column: number, + left: ESTree.ArgumentExpression | ESTree.Expression | null +): ESTree.ArgumentExpression | ESTree.Expression { + /** + * AssignmentExpression :: + * ConditionalExpression + * ArrowFunction + * AsyncArrowFunction + * YieldExpression + * LeftHandSideExpression AssignmentOperator AssignmentExpression + */ + + const { token } = parser; + + if ((token & Token.IsAssignOp) === Token.IsAssignOp) { + if (parser.assignable & AssignmentKind.CannotAssign) report(parser, Errors.CantAssignTo); + if ( + (!isPattern && token === Token.Assign && ((left as ESTree.Expression).type as string) === 'ArrayExpression') || + ((left as ESTree.Expression).type as string) === 'ObjectExpression' + ) { + reinterpretToPattern(parser, left); + } + + nextToken(parser, context | Context.AllowRegExp); + + const right = parseExpression(parser, context, 1, 1, inGroup, parser.tokenPos, parser.linePos, parser.colPos); + + parser.assignable = AssignmentKind.CannotAssign; + + return finishNode( + parser, + context, + start, + line, + column, + isPattern + ? { + type: 'AssignmentPattern', + left, + right + } + : ({ + type: 'AssignmentExpression', + left, + operator: KeywordDescTable[token & Token.Type], + right + } as any) + ); + } + + /** Binary expression + * + * https://tc39.github.io/ecma262/#sec-multiplicative-operators + * + */ + if ((token & Token.IsBinaryOp) === Token.IsBinaryOp) { + // We start using the binary expression parser for prec >= 4 only! + left = parseBinaryExpression(parser, context, inGroup, start, line, column, 4, token, left as ESTree.Expression); + } + + /** + * Conditional expression + * https://tc39.github.io/ecma262/#prod-ConditionalExpression + * + */ + if (consumeOpt(parser, context | Context.AllowRegExp, Token.QuestionMark)) { + left = parseConditionalExpression(parser, context, left as ESTree.Expression, start, line, column); + } + + return left as ESTree.Expression; +} + +/** + * Parse assignment expression or assignment pattern + * + * @param parser Parser object + * @param context Context masks + * @param inGroup + * @param isPattern + * @param start + * @param line + * @param column + * @param left + */ + +export function parseAssignmentExpressionOrPattern( + parser: ParserState, + context: Context, + inGroup: 0 | 1, + isPattern: 0 | 1, + start: number, + line: number, + column: number, + left: any +): any { + const { token } = parser; + + nextToken(parser, context | Context.AllowRegExp); + + const right = parseExpression(parser, context, 1, 1, inGroup, parser.tokenPos, parser.linePos, parser.colPos); + + left = finishNode( + parser, + context, + start, + line, + column, + isPattern + ? { + type: 'AssignmentPattern', + left, + right + } + : ({ + type: 'AssignmentExpression', + left, + operator: KeywordDescTable[token & Token.Type], + right + } as any) + ); + + parser.assignable = AssignmentKind.CannotAssign; + + return left as ESTree.Expression; +} + +/** + * Parse conditional expression + * + * @param parser Parser object + * @param context Context masks + * @param test ESTree AST node + */ +export function parseConditionalExpression( + parser: ParserState, + context: Context, + test: ESTree.Expression, + start: number, + line: number, + column: number +): ESTree.ConditionalExpression { + // ConditionalExpression :: + // LogicalOrExpression + // LogicalOrExpression '?' AssignmentExpression ':' AssignmentExpression + const consequent = parseExpression( + parser, + (context | Context.DisallowIn) ^ Context.DisallowIn, + 1, + 0, + 0, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + consume(parser, context | Context.AllowRegExp, Token.Colon); + parser.assignable = AssignmentKind.Assignable; + // In parsing the first assignment expression in conditional + // expressions we always accept the 'in' keyword; see ECMA-262, + // section 11.12, page 58. + const alternate = parseExpression(parser, context, 1, 0, 0, parser.tokenPos, parser.linePos, parser.colPos); + parser.assignable = AssignmentKind.CannotAssign; + return finishNode(parser, context, start, line, column, { + type: 'ConditionalExpression', + test, + consequent, + alternate + }); +} + +/** + * Parses binary and unary expressions recursively + * based on the precedence of the operators encountered. + * + * @param parser Parser object + * @param context Context masks + * @param minPrec The precedence of the last binary expression parsed + * @param left ESTree AST node + */ +export function parseBinaryExpression( + parser: ParserState, + context: Context, + inGroup: 0 | 1, + start: number, + line: number, + column: number, + minPrec: number, + operator: Token, + left: ESTree.ArgumentExpression | ESTree.Expression +): ESTree.ArgumentExpression | ESTree.Expression { + const bit = -((context & Context.DisallowIn) > 0) & Token.InKeyword; + let t: Token; + let prec: number; + + parser.assignable = AssignmentKind.CannotAssign; + + while (parser.token & Token.IsBinaryOp) { + t = parser.token; + prec = t & Token.Precedence; + + if ((t & Token.IsLogical && operator & Token.IsCoalesc) || (operator & Token.IsLogical && t & Token.IsCoalesc)) { + report(parser, Errors.InvalidCoalescing); + } + + // 0 precedence will terminate binary expression parsing + + if (prec + (((t === Token.Exponentiate) as any) << 8) - (((bit === t) as any) << 12) <= minPrec) break; + nextToken(parser, context | Context.AllowRegExp); + + left = finishNode(parser, context, start, line, column, { + type: t & Token.IsLogical || t & Token.IsCoalesc ? 'LogicalExpression' : 'BinaryExpression', + left, + right: parseBinaryExpression( + parser, + context, + inGroup, + parser.tokenPos, + parser.linePos, + parser.colPos, + prec, + t, + parseLeftHandSideExpression(parser, context, 0, inGroup, 1, parser.tokenPos, parser.linePos, parser.colPos) + ), + operator: KeywordDescTable[t & Token.Type] + }); + } + + if (parser.token === Token.Assign) report(parser, Errors.CantAssignTo); + + return left; +} + +/** + * Parses unary expression + * + * @param parser Parser object + * @param context Context masks + */ +export function parseUnaryExpression( + parser: ParserState, + context: Context, + isLHS: 0 | 1, + start: number, + line: number, + column: number, + inGroup: 0 | 1 +): ESTree.UnaryExpression { + /** + * UnaryExpression :: + * 1) UpdateExpression + * 2) delete UnaryExpression + * 3) void UnaryExpression + * 4) typeof UnaryExpression + * 5) + UnaryExpression + * 6) - UnaryExpression + * 7) ~ UnaryExpression + * 8) ! UnaryExpression + * 9) AwaitExpression + */ + if (!isLHS) report(parser, Errors.Unexpected); + const unaryOperator = parser.token; + nextToken(parser, context | Context.AllowRegExp); + const arg = parseLeftHandSideExpression( + parser, + context, + 0, + inGroup, + 1, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + if (parser.token === Token.Exponentiate) report(parser, Errors.InvalidExponentiationLHS); + if (context & Context.Strict && unaryOperator === Token.DeleteKeyword) { + if (arg.type === 'Identifier') { + report(parser, Errors.StrictDelete); + // Prohibit delete of private class elements + } else if (isPropertyWithPrivateFieldKey(arg)) { + report(parser, Errors.DeletePrivateField); + } + } + + parser.assignable = AssignmentKind.CannotAssign; + + return finishNode(parser, context, start, line, column, { + type: 'UnaryExpression', + operator: KeywordDescTable[unaryOperator & Token.Type] as ESTree.UnaryOperator, + argument: arg, + prefix: true + }); +} + +/** + * Parse async expression + * + * @param parser Parser object + * @param context Context masks + */ +export function parseAsyncExpression( + parser: ParserState, + context: Context, + inGroup: 0 | 1, + isLHS: 0 | 1, + canAssign: 0 | 1, + isPattern: 0 | 1, + inNew: 0 | 1, + start: number, + line: number, + column: number +): ESTree.FunctionExpression | ESTree.ArrowFunctionExpression | ESTree.CallExpression | ESTree.Identifier { + const { token } = parser; + const expr = parseIdentifier(parser, context, isPattern); + const { flags } = parser; + + if ((flags & Flags.NewLine) < 1) { + // async function ... + if (parser.token === Token.FunctionKeyword) { + return parseFunctionExpression(parser, context, /* isAsync */ 1, inGroup, start, line, column); + } + + // async Identifier => ... + if ((parser.token & Token.IsIdentifier) === Token.IsIdentifier) { + if (!isLHS) report(parser, Errors.Unexpected); + return parseAsyncArrowAfterIdent(parser, context, canAssign, start, line, column); + } + } + + // async (...) => ... + if (!inNew && parser.token === Token.LeftParen) { + return parseAsyncArrowOrCallExpression( + parser, + context, + expr, + canAssign, + BindingKind.ArgumentList, + Origin.None, + flags, + start, + line, + column + ); + } + + if (parser.token === Token.Arrow) { + classifyIdentifier(parser, context, token, /* isArrow */ 1); + if (inNew) report(parser, Errors.InvalidAsyncArrow); + return parseArrowFromIdentifier(parser, context, parser.tokenValue, expr, inNew, canAssign, 0, start, line, column); + } + return expr; +} + +/** + * Parse yield expression + * + * @param parser Parser object + * @param context Context masks + */ +export function parseYieldExpression( + parser: ParserState, + context: Context, + inGroup: 0 | 1, + canAssign: 0 | 1, + start: number, + line: number, + column: number +): ESTree.YieldExpression | ESTree.Identifier | ESTree.ArrowFunctionExpression { + // YieldExpression[In] : + // yield + // yield [no LineTerminator here] AssignmentExpression[?In, Yield] + // yield [no LineTerminator here] * AssignmentExpression[?In, Yield] + + if (inGroup) parser.destructible |= DestructuringKind.Yield; + if (context & Context.InYieldContext) { + nextToken(parser, context | Context.AllowRegExp); + if (context & Context.InArgumentList) report(parser, Errors.YieldInParameter); + if (!canAssign) report(parser, Errors.CantAssignTo); + if (parser.token === Token.QuestionMark) report(parser, Errors.InvalidTernaryYield); + + let argument: ESTree.Expression | null = null; + let delegate = false; // yield* + + if ((parser.flags & Flags.NewLine) < 1) { + delegate = consumeOpt(parser, context | Context.AllowRegExp, Token.Multiply); + if (parser.token & (Token.Contextual | Token.IsExpressionStart) || delegate) { + argument = parseExpression(parser, context, 1, 0, 0, parser.tokenPos, parser.linePos, parser.colPos); + } + } + + parser.assignable = AssignmentKind.CannotAssign; + + return finishNode(parser, context, start, line, column, { + type: 'YieldExpression', + argument, + delegate + }); + } + + if (context & Context.Strict) report(parser, Errors.DisallowedInContext, 'yield'); + + return parseIdentifierOrArrow(parser, context, start, line, column); +} + +/** + * Parse await expression + * + * @param parser Parser object + * @param context Context masks + * @param inNew + */ +export function parseAwaitExpression( + parser: ParserState, + context: Context, + inNew: 0 | 1, + inGroup: 0 | 1, + start: number, + line: number, + column: number +): ESTree.IdentifierOrExpression | ESTree.AwaitExpression { + if (inGroup) parser.destructible |= DestructuringKind.Await; + if (context & Context.InAwaitContext || (context & Context.Module && context & Context.InGlobal)) { + if (inNew) report(parser, Errors.Unexpected); + + if (context & Context.InArgumentList) { + reportMessageAt(parser.index, parser.line, parser.index, Errors.AwaitInParameter); + } + + nextToken(parser, context | Context.AllowRegExp); + + const argument = parseLeftHandSideExpression( + parser, + context, + 0, + 0, + 1, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + + if (parser.token === Token.Exponentiate) report(parser, Errors.InvalidExponentiationLHS); + + parser.assignable = AssignmentKind.CannotAssign; + + return finishNode(parser, context, start, line, column, { + type: 'AwaitExpression', + argument + }); + } + + if (context & Context.Module) report(parser, Errors.AwaitOutsideAsync); + return parseIdentifierOrArrow(parser, context, start, line, column); +} + +/** + * Parses function body + * + * @param parser Parser object + * @param context Context masks + * @param scope Scope object | null + * @param origin Binding origin + * @param firstRestricted + * @param scopeError + */ +export function parseFunctionBody( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + origin: Origin, + firstRestricted: Token | undefined, + scopeError: any +): ESTree.BlockStatement { + const { tokenPos, linePos, colPos } = parser; + + consume(parser, context | Context.AllowRegExp, Token.LeftBrace); + + const body: ESTree.Statement[] = []; + const prevContext = context; + + if (parser.token !== Token.RightBrace) { + while (parser.token === Token.StringLiteral) { + const { index, tokenPos, tokenValue, token } = parser; + const expr = parseLiteral(parser, context); + if (isValidStrictMode(parser, index, tokenPos, tokenValue)) { + context |= Context.Strict; + // TC39 deemed "use strict" directives to be an error when occurring + // in the body of a function with non-simple parameter list, on + // 29/7/2015. https://goo.gl/ueA7Ln + if (parser.flags & Flags.SimpleParameterList) { + reportMessageAt(parser.index, parser.line, parser.tokenPos, Errors.IllegalUseStrict); + } + + if (parser.flags & Flags.Octals) { + reportMessageAt(parser.index, parser.line, parser.tokenPos, Errors.StrictOctalLiteral); + } + } + body.push(parseDirective(parser, context, expr, token, tokenPos, parser.linePos, parser.colPos)); + } + if (context & Context.Strict) { + if (firstRestricted) { + if ((firstRestricted & Token.IsEvalOrArguments) === Token.IsEvalOrArguments) { + report(parser, Errors.StrictEvalArguments); + } + if ((firstRestricted & Token.FutureReserved) === Token.FutureReserved) { + report(parser, Errors.StrictFunctionName); + } + } + if (parser.flags & Flags.StrictEvalArguments) report(parser, Errors.StrictEvalArguments); + if (parser.flags & Flags.HasStrictReserved) report(parser, Errors.UnexpectedStrictReserved); + } + + if ( + context & Context.OptionsLexical && + scope && + scopeError !== void 0 && + (prevContext & Context.Strict) < 1 && + (context & Context.InGlobal) < 1 + ) { + reportScopeError(scopeError); + } + } + + parser.flags = + (parser.flags | Flags.StrictEvalArguments | Flags.HasStrictReserved | Flags.Octals) ^ + (Flags.StrictEvalArguments | Flags.HasStrictReserved | Flags.Octals); + + parser.destructible = (parser.destructible | DestructuringKind.Yield) ^ DestructuringKind.Yield; + + while (parser.token !== Token.RightBrace) { + body.push(parseStatementListItem(parser, context, scope, Origin.TopLevel, {}) as ESTree.Statement); + } + + consume( + parser, + origin & (Origin.Arrow | Origin.Declaration) ? context | Context.AllowRegExp : context, + Token.RightBrace + ); + + parser.flags &= ~(Flags.SimpleParameterList | Flags.Octals); + + if (parser.token === Token.Assign) report(parser, Errors.CantAssignTo); + + return finishNode(parser, context, tokenPos, linePos, colPos, { + type: 'BlockStatement', + body + }); +} + +/** + * Parse super expression + * + * @param parser Parser object + * @param context Context masks + */ +export function parseSuperExpression( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.Super { + nextToken(parser, context); + + switch (parser.token) { + case Token.QuestionMarkPeriod: + report(parser, Errors.OptionalChainingNoSuper); + case Token.LeftParen: { + // The super property has to be within a class constructor + if ((context & Context.SuperCall) < 1) report(parser, Errors.SuperNoConstructor); + if (context & Context.InClass) report(parser, Errors.UnexpectedPrivateField); + parser.assignable = AssignmentKind.CannotAssign; + break; + } + case Token.LeftBracket: + case Token.Period: { + // new super() is never allowed. + // super() is only allowed in derived constructor + if ((context & Context.SuperProperty) < 1) report(parser, Errors.InvalidSuperProperty); + if (context & Context.InClass) report(parser, Errors.UnexpectedPrivateField); + parser.assignable = AssignmentKind.Assignable; + break; + } + default: + report(parser, Errors.UnexpectedToken, 'super'); + } + + return finishNode(parser, context, start, line, column, { type: 'Super' }); +} + +/** + * Parses left hand side + * + * @param parser Parser object + * @param context Context masks + * @param canAssign + * @param start + * @param line + * @param column + */ +export function parseLeftHandSideExpression( + parser: ParserState, + context: Context, + canAssign: 0 | 1, + inGroup: 0 | 1, + isLHS: 0 | 1, + start: number, + line: number, + column: number +): ESTree.Expression { + // LeftHandSideExpression :: + // (PrimaryExpression | MemberExpression) ... + + const expression = parsePrimaryExpression( + parser, + context, + BindingKind.Empty, + 0, + canAssign, + 0, + inGroup, + isLHS, + start, + line, + column + ); + + return parseMemberOrUpdateExpression(parser, context, expression, inGroup, 0, start, line, column); +} + +/** + * Parse update expression + * + * @param parser Parser object + * @param context Context masks + * @param inNew + * @param start + * @param line + * @param column + */ +function parseUpdateExpression( + parser: ParserState, + context: Context, + expr: ESTree.Expression, + start: number, + line: number, + column: number +) { + if (parser.assignable & AssignmentKind.CannotAssign) report(parser, Errors.InvalidIncDecTarget); + + const { token } = parser; + + nextToken(parser, context); + + parser.assignable = AssignmentKind.CannotAssign; + + return finishNode(parser, context, start, line, column, { + type: 'UpdateExpression', + argument: expr, + operator: KeywordDescTable[token & Token.Type] as ESTree.UpdateOperator, + prefix: false + }); +} +/** + * Parses member or update expression + * + * @param parser Parser object + * @param context Context masks + * @param expr ESTree AST node + * @param inGroup + * @param start + * @param line + * @param column + */ +export function parseMemberOrUpdateExpression( + parser: ParserState, + context: Context, + expr: ESTree.Expression, + inGroup: 0 | 1, + inChain: 0 | 1, + start: number, + line: number, + column: number +): any { + if ((parser.token & Token.IsUpdateOp) === Token.IsUpdateOp && (parser.flags & Flags.NewLine) < 1) { + expr = parseUpdateExpression(parser, context, expr, start, line, column); + } else if ((parser.token & Token.IsMemberOrCallExpression) === Token.IsMemberOrCallExpression) { + context = (context | Context.DisallowIn | Context.InGlobal) ^ (Context.DisallowIn | Context.InGlobal); + + switch (parser.token) { + /* Property */ + case Token.Period: { + nextToken(parser, context | Context.AllowEscapedKeyword); + + parser.assignable = AssignmentKind.Assignable; + + const property = parsePropertyOrPrivatePropertyName(parser, context); + + expr = finishNode(parser, context, start, line, column, { + type: 'MemberExpression', + object: expr, + computed: false, + property + }); + break; + } + + /* Property */ + case Token.LeftBracket: { + let restoreHasOptionalChaining = false; + if ((parser.flags & Flags.HasOptionalChaining) === Flags.HasOptionalChaining) { + restoreHasOptionalChaining = true; + parser.flags = (parser.flags | Flags.HasOptionalChaining) ^ Flags.HasOptionalChaining; + } + + nextToken(parser, context | Context.AllowRegExp); + + const { tokenPos, linePos, colPos } = parser; + const property = parseExpressions(parser, context, inGroup, 1, tokenPos, linePos, colPos); + + consume(parser, context, Token.RightBracket); + + parser.assignable = AssignmentKind.Assignable; + + expr = finishNode(parser, context, start, line, column, { + type: 'MemberExpression', + object: expr, + computed: true, + property + }); + + if (restoreHasOptionalChaining) { + parser.flags |= Flags.HasOptionalChaining; + } + break; + } + + /* Call */ + case Token.LeftParen: { + if ((parser.flags & Flags.DisallowCall) === Flags.DisallowCall) { + parser.flags = (parser.flags | Flags.DisallowCall) ^ Flags.DisallowCall; + return expr; + } + + let restoreHasOptionalChaining = false; + if ((parser.flags & Flags.HasOptionalChaining) === Flags.HasOptionalChaining) { + restoreHasOptionalChaining = true; + parser.flags = (parser.flags | Flags.HasOptionalChaining) ^ Flags.HasOptionalChaining; + } + + const args = parseArguments(parser, context, inGroup); + + parser.assignable = AssignmentKind.CannotAssign; + + expr = finishNode(parser, context, start, line, column, { + type: 'CallExpression', + callee: expr, + arguments: args + }); + + if (restoreHasOptionalChaining) { + parser.flags |= Flags.HasOptionalChaining; + } + break; + } + + /* Optional chaining */ + case Token.QuestionMarkPeriod: { + nextToken(parser, context); // skips: '?.' + parser.flags |= Flags.HasOptionalChaining; + parser.assignable = AssignmentKind.CannotAssign; + expr = parseOptionalChain(parser, context, expr, start, line, column); + break; + } + + default: + if ((parser.flags & Flags.HasOptionalChaining) === Flags.HasOptionalChaining) { + report(parser, Errors.OptionalChainingNoTemplate); + } + /* Tagged Template */ + parser.assignable = AssignmentKind.CannotAssign; + + expr = finishNode(parser, context, start, line, column, { + type: 'TaggedTemplateExpression', + tag: expr, + quasi: + parser.token === Token.TemplateContinuation + ? parseTemplate(parser, context | Context.TaggedTemplate) + : parseTemplateLiteral(parser, context, parser.tokenPos, parser.linePos, parser.colPos) + }); + } + + expr = parseMemberOrUpdateExpression(parser, context, expr, 0, 1, start, line, column); + } + + // Finalize ChainExpression + // FIXME: current implementation does not invalidate destructuring like `({ a: x?.obj['a'] } = {})` + if (inChain === 0 && (parser.flags & Flags.HasOptionalChaining) === Flags.HasOptionalChaining) { + parser.flags = (parser.flags | Flags.HasOptionalChaining) ^ Flags.HasOptionalChaining; + + expr = finishNode(parser, context, start, line, column, { + type: 'ChainExpression', + expression: expr as ESTree.CallExpression | ESTree.MemberExpression + }); + } + + return expr; +} + +/** + * Parses optional chain + * + * @param parser Parser object + * @param context Context masks + * @param expr ESTree AST node + */ +export function parseOptionalChain( + parser: ParserState, + context: Context, + expr: ESTree.Expression, + start: number, + line: number, + column: number +): ESTree.MemberExpression | ESTree.CallExpression { + let restoreHasOptionalChaining = false; + let node; + if (parser.token === Token.LeftBracket || parser.token === Token.LeftParen) { + if ((parser.flags & Flags.HasOptionalChaining) === Flags.HasOptionalChaining) { + restoreHasOptionalChaining = true; + parser.flags = (parser.flags | Flags.HasOptionalChaining) ^ Flags.HasOptionalChaining; + } + } + if (parser.token === Token.LeftBracket) { + nextToken(parser, context | Context.AllowRegExp); + const { tokenPos, linePos, colPos } = parser; + const property = parseExpressions(parser, context, 0, 1, tokenPos, linePos, colPos); + consume(parser, context, Token.RightBracket); + parser.assignable = AssignmentKind.CannotAssign; + node = finishNode(parser, context, start, line, column, { + type: 'MemberExpression', + object: expr, + computed: true, + optional: true, + property + }); + } else if (parser.token === Token.LeftParen) { + const args = parseArguments(parser, context, 0); + + parser.assignable = AssignmentKind.CannotAssign; + + node = finishNode(parser, context, start, line, column, { + type: 'CallExpression', + callee: expr, + arguments: args, + optional: true + }); + } else { + if ((parser.token & (Token.IsIdentifier | Token.Keyword)) < 1) report(parser, Errors.InvalidDotProperty); + const property = parseIdentifier(parser, context, 0); + parser.assignable = AssignmentKind.CannotAssign; + node = finishNode(parser, context, start, line, column, { + type: 'MemberExpression', + object: expr, + computed: false, + optional: true, + property + }); + } + + if (restoreHasOptionalChaining) { + parser.flags |= Flags.HasOptionalChaining; + } + return node; +} + +/** + * Parses property or private property name + * + * @param parser Parser object + * @param context Context masks + */ +export function parsePropertyOrPrivatePropertyName(parser: ParserState, context: Context): any { + if ((parser.token & (Token.IsIdentifier | Token.Keyword)) < 1 && parser.token !== Token.PrivateField) { + report(parser, Errors.InvalidDotProperty); + } + + return context & Context.OptionsNext && parser.token === Token.PrivateField + ? parsePrivateIdentifier(parser, context, parser.tokenPos, parser.linePos, parser.colPos) + : parseIdentifier(parser, context, 0); +} + +/** + * Parse update expression + * + * @param parser Parser object + * @param context Context masks + * @param inNew + * @param start + * @param line + * @param column + */ +export function parseUpdateExpressionPrefixed( + parser: ParserState, + context: Context, + inNew: 0 | 1, + isLHS: 0 | 1, + start: number, + line: number, + column: number +): ESTree.UpdateExpression { + // UpdateExpression :: + // LeftHandSideExpression ('++' | '--')? + + if (inNew) report(parser, Errors.InvalidIncDecNew); + if (!isLHS) report(parser, Errors.Unexpected); + + const { token } = parser; + + nextToken(parser, context | Context.AllowRegExp); + + const arg = parseLeftHandSideExpression(parser, context, 0, 0, 1, parser.tokenPos, parser.linePos, parser.colPos); + + if (parser.assignable & AssignmentKind.CannotAssign) { + report(parser, Errors.InvalidIncDecTarget); + } + + parser.assignable = AssignmentKind.CannotAssign; + + return finishNode(parser, context, start, line, column, { + type: 'UpdateExpression', + argument: arg, + operator: KeywordDescTable[token & Token.Type] as ESTree.UpdateOperator, + prefix: true + }); +} + +/** + * Parses expressions such as a literal expression + * and update expression. + * + * @param parser Parser object + * @param context Context masks + * @param kind Binding kind + * @param inNew + * @param canAssign + * @param isPattern + * @param inGroup + * @param start + * @param line + * @param column + */ + +export function parsePrimaryExpression( + parser: ParserState, + context: Context, + kind: BindingKind, + inNew: 0 | 1, + canAssign: 0 | 1, + isPattern: 0 | 1, + inGroup: 0 | 1, + isLHS: 0 | 1, + start: number, + line: number, + column: number +): any { + // PrimaryExpression :: + // 'this' + // 'null' + // 'true' + // 'false' + // Identifier + // Number + // String + // BigIntLiteral + // ArrayLiteral + // ObjectLiteral + // RegExpLiteral + // ClassLiteral + // ImportCall + // ImportMeta + // '(' Expression ')' + // TemplateLiteral + // AsyncFunctionLiteral + // YieldExpression + // AwaitExpression + // PrivateField + // Decorator + // Intrinsic + // JSX + + if ((parser.token & Token.IsIdentifier) === Token.IsIdentifier) { + switch (parser.token) { + case Token.AwaitKeyword: + return parseAwaitExpression(parser, context, inNew, inGroup, start, line, column); + case Token.YieldKeyword: + return parseYieldExpression(parser, context, inGroup, canAssign, start, line, column); + case Token.AsyncKeyword: + return parseAsyncExpression(parser, context, inGroup, isLHS, canAssign, isPattern, inNew, start, line, column); + default: // ignore + } + + const { token, tokenValue } = parser; + + const expr = parseIdentifier(parser, context | Context.TaggedTemplate, isPattern); + + if (parser.token === Token.Arrow) { + if (!isLHS) report(parser, Errors.Unexpected); + classifyIdentifier(parser, context, token, /* isArrow */ 1); + return parseArrowFromIdentifier(parser, context, tokenValue, expr, inNew, canAssign, 0, start, line, column); + } + + if (context & Context.InClass && token === Token.Arguments) report(parser, Errors.InvalidClassFieldArgEval); + + // Only a "simple validation" is done here to handle 'let' edge cases + + if (token === Token.LetKeyword) { + if (context & Context.Strict) report(parser, Errors.StrictInvalidLetInExprPos); + if (kind & (BindingKind.Let | BindingKind.Const)) report(parser, Errors.InvalidLetConstBinding); + } + + parser.assignable = + context & Context.Strict && (token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments + ? AssignmentKind.CannotAssign + : AssignmentKind.Assignable; + + return expr; + } + + if ((parser.token & Token.IsStringOrNumber) === Token.IsStringOrNumber) { + return parseLiteral(parser, context); + } + + // Update + Unary + Primary expression + switch (parser.token) { + case Token.Increment: + case Token.Decrement: + return parseUpdateExpressionPrefixed(parser, context, inNew, isLHS, start, line, column); + case Token.DeleteKeyword: + case Token.Negate: + case Token.Complement: + case Token.Add: + case Token.Subtract: + case Token.TypeofKeyword: + case Token.VoidKeyword: + return parseUnaryExpression(parser, context, isLHS, start, line, column, inGroup); + case Token.FunctionKeyword: + return parseFunctionExpression(parser, context, /* isAsync */ 0, inGroup, start, line, column); + case Token.LeftBrace: + return parseObjectLiteral(parser, context, canAssign ? 0 : 1, inGroup, start, line, column); + case Token.LeftBracket: + return parseArrayLiteral(parser, context, canAssign ? 0 : 1, inGroup, start, line, column); + case Token.LeftParen: + return parseParenthesizedExpression( + parser, + context, + canAssign, + BindingKind.ArgumentList, + Origin.None, + start, + line, + column + ); + case Token.FalseKeyword: + case Token.TrueKeyword: + case Token.NullKeyword: + return parseNullOrTrueOrFalseLiteral(parser, context, start, line, column); + case Token.ThisKeyword: + return parseThisExpression(parser, context); + case Token.RegularExpression: + return parseRegExpLiteral(parser, context, start, line, column); + case Token.Decorator: + case Token.ClassKeyword: + return parseClassExpression(parser, context, inGroup, start, line, column); + case Token.SuperKeyword: + return parseSuperExpression(parser, context, start, line, column); + case Token.TemplateSpan: + return parseTemplateLiteral(parser, context, start, line, column); + case Token.TemplateContinuation: + return parseTemplate(parser, context); + case Token.NewKeyword: + return parseNewExpression(parser, context, inGroup, start, line, column); + case Token.BigIntLiteral: + return parseBigIntLiteral(parser, context, start, line, column); + case Token.PrivateField: + return parsePrivateIdentifier(parser, context, start, line, column); + case Token.ImportKeyword: + return parseImportCallOrMetaExpression(parser, context, inNew, inGroup, start, line, column); + case Token.LessThan: + if (context & Context.OptionsJSX) + return parseJSXRootElementOrFragment(parser, context, /*inJSXChild*/ 1, start, line, column); + default: + if (isValidIdentifier(context, parser.token)) return parseIdentifierOrArrow(parser, context, start, line, column); + report(parser, Errors.UnexpectedToken, KeywordDescTable[parser.token & Token.Type]); + } +} + +/** + * Parses Import call expression + * + * @param parser Parser object + * @param context Context masks + * @param inGroup + * @param start + * @param line + * @param column + */ +function parseImportCallOrMetaExpression( + parser: ParserState, + context: Context, + inNew: 0 | 1, + inGroup: 0 | 1, + start: number, + line: number, + column: number +): ESTree.ImportExpression | ESTree.MetaProperty { + // ImportCall[Yield, Await]: + // import(AssignmentExpression[+In, ?Yield, ?Await]) + + let expr: ESTree.Identifier | ESTree.ImportExpression = parseIdentifier(parser, context, 0); + + if (parser.token === Token.Period) { + return parseImportMetaExpression(parser, context, expr, start, line, column); + } + + if (inNew) report(parser, Errors.InvalidImportNew); + + expr = parseImportExpression(parser, context, inGroup, start, line, column); + + parser.assignable = AssignmentKind.CannotAssign; + + return parseMemberOrUpdateExpression(parser, context, expr, inGroup, 0, start, line, column); +} + +/** + * Parses import meta expression + * + * @param parser Parser object + * @param context Context masks + * @param meta ESTree AST node + * @param start + * @param line + * @param column + */ + +export function parseImportMetaExpression( + parser: ParserState, + context: Context, + meta: ESTree.Identifier, + start: number, + line: number, + column: number +): ESTree.MetaProperty { + if ((context & Context.Module) === 0) report(parser, Errors.ImportMetaOutsideModule); + + nextToken(parser, context); // skips: '.' + + if (parser.token !== Token.Meta && parser.tokenValue !== 'meta') + report(parser, Errors.UnexpectedToken, KeywordDescTable[parser.token & Token.Type]); + + parser.assignable = AssignmentKind.CannotAssign; + + return finishNode(parser, context, start, line, column, { + type: 'MetaProperty', + meta, + property: parseIdentifier(parser, context, 0) + }); +} + +/** + * Parses import expression + * + * @param parser Parser object + * @param context Context masks + * @param inGroup + * @param start + * @param line + * @param column + */ + +export function parseImportExpression( + parser: ParserState, + context: Context, + inGroup: 0 | 1, + start: number, + line: number, + column: number +): ESTree.ImportExpression { + consume(parser, context | Context.AllowRegExp, Token.LeftParen); + + if (parser.token === Token.Ellipsis) report(parser, Errors.InvalidSpreadInImport); + + const source = parseExpression(parser, context, 1, 0, inGroup, parser.tokenPos, parser.linePos, parser.colPos); + + consume(parser, context, Token.RightParen); + + return finishNode(parser, context, start, line, column, { + type: 'ImportExpression', + source + }); +} + +/** + * Parses BigInt literal + * + * @param parser Parser object + * @param context Context masks + */ +export function parseBigIntLiteral( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.BigIntLiteral { + const { tokenRaw, tokenValue } = parser; + nextToken(parser, context); + parser.assignable = AssignmentKind.CannotAssign; + return finishNode( + parser, + context, + start, + line, + column, + context & Context.OptionsRaw + ? { + type: 'Literal', + value: tokenValue, + bigint: tokenRaw.slice(0, -1), // without the ending "n" + raw: tokenRaw + } + : { + type: 'Literal', + value: tokenValue, + bigint: tokenRaw.slice(0, -1) // without the ending "n" + } + ); +} + +/** + * Parses template literal + * + * @param parser Parser object + * @param context Context masks + */ +export function parseTemplateLiteral( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.TemplateLiteral { + /** + * Template Literals + * + * Template :: + * FullTemplate + * TemplateHead + * + * FullTemplate :: + * `TemplateCharactersopt` + * + * TemplateHead :: + * ` TemplateCharactersopt ${ + * + * TemplateSubstitutionTail :: + * TemplateMiddle + * TemplateTail + * + * TemplateMiddle :: + * } TemplateCharactersopt ${ + * + * TemplateTail :: + * } TemplateCharactersopt ` + * + * TemplateCharacters :: + * TemplateCharacter TemplateCharactersopt + * + * TemplateCharacter :: + * SourceCharacter but not one of ` or \ or $ + * $ [lookahead not { ] + * \ EscapeSequence + * LineContinuation + */ + + parser.assignable = AssignmentKind.CannotAssign; + const { tokenValue, tokenRaw, tokenPos, linePos, colPos } = parser; + consume(parser, context, Token.TemplateSpan); + const quasis = [parseTemplateElement(parser, context, tokenValue, tokenRaw, tokenPos, linePos, colPos, true)]; + + return finishNode(parser, context, start, line, column, { + type: 'TemplateLiteral', + expressions: [], + quasis + }); +} + +/** + * Parses template + * + * @param parser Parser object + * @param context Context masks + */ +export function parseTemplate(parser: ParserState, context: Context): ESTree.TemplateLiteral { + context = (context | Context.DisallowIn) ^ Context.DisallowIn; + + const { tokenValue, tokenRaw, tokenPos, linePos, colPos } = parser; + consume(parser, context | Context.AllowRegExp, Token.TemplateContinuation); + const quasis = [ + parseTemplateElement(parser, context, tokenValue, tokenRaw, tokenPos, linePos, colPos, /* tail */ false) + ]; + + const expressions = [parseExpressions(parser, context, 0, 1, parser.tokenPos, parser.linePos, parser.colPos)]; + + if (parser.token !== Token.RightBrace) report(parser, Errors.InvalidTemplateContinuation); + + while ((parser.token = scanTemplateTail(parser, context)) !== Token.TemplateSpan) { + const { tokenValue, tokenRaw, tokenPos, linePos, colPos } = parser; + consume(parser, context | Context.AllowRegExp, Token.TemplateContinuation); + quasis.push( + parseTemplateElement(parser, context, tokenValue, tokenRaw, tokenPos, linePos, colPos, /* tail */ false) + ); + + expressions.push(parseExpressions(parser, context, 0, 1, parser.tokenPos, parser.linePos, parser.colPos)); + if (parser.token !== Token.RightBrace) report(parser, Errors.InvalidTemplateContinuation); + } + + { + const { tokenValue, tokenRaw, tokenPos, linePos, colPos } = parser; + consume(parser, context, Token.TemplateSpan); + quasis.push( + parseTemplateElement(parser, context, tokenValue, tokenRaw, tokenPos, linePos, colPos, /* tail */ true) + ); + } + + return finishNode(parser, context, tokenPos, linePos, colPos, { + type: 'TemplateLiteral', + expressions, + quasis + }); +} + +/** + * Parses template spans + * + * @param parser Parser object + * @param tail + */ +export function parseTemplateElement( + parser: ParserState, + context: Context, + cooked: string | null, + raw: string, + start: number, + line: number, + col: number, + tail: boolean +): ESTree.TemplateElement { + const node = finishNode(parser, context, start, line, col, { + type: 'TemplateElement', + value: { + cooked, + raw + }, + tail + }) as ESTree.TemplateElement; + + const tailSize = tail ? 1 : 2; + + // Patch range + if (context & Context.OptionsRanges) { + // skip the front "`" or "}" + (node.start as number) += 1; + (node.range as [number, number])[0] += 1; + // skip the tail "`" or "${" + (node.end as number) -= tailSize; + (node.range as [number, number])[1] -= tailSize; + } + + // Patch loc + if (context & Context.OptionsLoc) { + // skip the front "`" or "}" + (node.loc as ESTree.SourceLocation).start.column += 1; + // skip the tail "`" or "${" + (node.loc as ESTree.SourceLocation).end.column -= tailSize; + } + + return node; +} + +/** + * Parses spread element + * + * @param parser Parser object + * @param context Context masks + */ +function parseSpreadElement( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.SpreadElement { + context = (context | Context.DisallowIn) ^ Context.DisallowIn; + consume(parser, context | Context.AllowRegExp, Token.Ellipsis); + const argument = parseExpression(parser, context, 1, 0, 0, parser.tokenPos, parser.linePos, parser.colPos); + parser.assignable = AssignmentKind.Assignable; + return finishNode(parser, context, start, line, column, { + type: 'SpreadElement', + argument + }); +} + +/** + * Parses arguments + * + * @param parser Parser object + * @param context Context masks + */ +export function parseArguments( + parser: ParserState, + context: Context, + inGroup: 0 | 1 +): (ESTree.SpreadElement | ESTree.Expression)[] { + // Arguments :: + // '(' (AssignmentExpression)*[','] ')' + nextToken(parser, context | Context.AllowRegExp); + + const args: (ESTree.Expression | ESTree.SpreadElement)[] = []; + + if (parser.token === Token.RightParen) { + nextToken(parser, context); + return args; + } + + while (parser.token !== Token.RightParen) { + if (parser.token === Token.Ellipsis) { + args.push(parseSpreadElement(parser, context, parser.tokenPos, parser.linePos, parser.colPos)); + } else { + args.push(parseExpression(parser, context, 1, 0, inGroup, parser.tokenPos, parser.linePos, parser.colPos)); + } + + if (parser.token !== Token.Comma) break; + + nextToken(parser, context | Context.AllowRegExp); + + if (parser.token === Token.RightParen) break; + } + + consume(parser, context, Token.RightParen); + + return args; +} + +/** + * Parses an identifier expression + * + * @param parser Parser object + * @param context Context masks + */ +export function parseIdentifier(parser: ParserState, context: Context, isPattern: 0 | 1): ESTree.Identifier { + const { tokenValue, tokenPos, linePos, colPos } = parser; + nextToken(parser, context); + + return finishNode( + parser, + context, + tokenPos, + linePos, + colPos, + context & Context.OptionsIdentifierPattern + ? { + type: 'Identifier', + name: tokenValue, + pattern: isPattern === 1 + } + : { + type: 'Identifier', + name: tokenValue + } + ); +} + +/** + * Parses an literal expression such as string literal + * + * @param parser Parser object + * @param context Context masks + */ +export function parseLiteral(parser: ParserState, context: Context): ESTree.Literal { + const { tokenValue, tokenRaw, tokenPos, linePos, colPos } = parser; + if (parser.token === Token.BigIntLiteral) { + return parseBigIntLiteral(parser, context, tokenPos, linePos, colPos); + } + + nextToken(parser, context); + parser.assignable = AssignmentKind.CannotAssign; + return finishNode( + parser, + context, + tokenPos, + linePos, + colPos, + context & Context.OptionsRaw + ? { + type: 'Literal', + value: tokenValue, + raw: tokenRaw + } + : { + type: 'Literal', + value: tokenValue + } + ); +} + +/** + * Parses null and boolean expressions + * + * @param parser Parser object + * @param context Context masks + */ +export function parseNullOrTrueOrFalseLiteral( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.Literal { + const raw = KeywordDescTable[parser.token & Token.Type]; + const value = parser.token === Token.NullKeyword ? null : raw === 'true'; + + nextToken(parser, context); + parser.assignable = AssignmentKind.CannotAssign; + return finishNode( + parser, + context, + start, + line, + column, + context & Context.OptionsRaw + ? { + type: 'Literal', + value, + raw + } + : { + type: 'Literal', + value + } + ); +} + +/** + * Parses this expression + * + * @param parser Parser object + * @param context Context masks + */ +export function parseThisExpression(parser: ParserState, context: Context): ESTree.ThisExpression { + const { tokenPos, linePos, colPos } = parser; + nextToken(parser, context); + parser.assignable = AssignmentKind.CannotAssign; + return finishNode(parser, context, tokenPos, linePos, colPos, { + type: 'ThisExpression' + }); +} + +/** + * Parse function declaration + * + * @param parser Parser object + * @param context Context masks + * @param scope + * @param allowGen + * @param ExportDefault + * @param isAsync + * @param start + */ +export function parseFunctionDeclaration( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + origin: Origin, + allowGen: 0 | 1, + flags: HoistedFunctionFlags, + isAsync: 0 | 1, + start: number, + line: number, + column: number +): ESTree.FunctionDeclaration { + // FunctionDeclaration :: + // function BindingIdentifier ( FormalParameters ) { FunctionBody } + // function ( FormalParameters ) { FunctionBody } + // + // GeneratorDeclaration :: + // function * BindingIdentifier ( FormalParameters ) { FunctionBody } + // function * ( FormalParameters ) { FunctionBody } + // + // AsyncFunctionDeclaration :: + // async function BindingIdentifier ( FormalParameters ) { FunctionBody } + // async function ( FormalParameters ) { FunctionBody } + // + // AsyncGeneratorDeclaration :: + // async function * BindingIdentifier ( FormalParameters ) { FunctionBody } + // async function * ( FormalParameters ) { FunctionBody } + + nextToken(parser, context | Context.AllowRegExp); + + const isGenerator = allowGen ? optionalBit(parser, context, Token.Multiply) : 0; + + let id: ESTree.Identifier | null = null; + let firstRestricted: Token | undefined; + + // Create a new function scope + let functionScope = scope ? createScope() : void 0; + + if (parser.token === Token.LeftParen) { + if ((flags & HoistedClassFlags.Hoisted) < 1) report(parser, Errors.DeclNoName, 'Function'); + } else { + // In ES6, a function behaves as a lexical binding, except in + // a script scope, or the initial scope of eval or another function. + const kind = + origin & Origin.TopLevel && ((context & Context.InGlobal) < 1 || (context & Context.Module) < 1) + ? BindingKind.Variable + : BindingKind.FunctionLexical; + + validateFunctionName(parser, context | ((context & 0b0000000000000000000_1100_00000000) << 11), parser.token); + + if (scope) { + if (kind & BindingKind.Variable) { + addVarName(parser, context, scope as ScopeState, parser.tokenValue, kind); + } else { + addBlockName(parser, context, scope, parser.tokenValue, kind, origin); + } + + functionScope = addChildScope(functionScope, ScopeKind.FunctionRoot); + + if (flags) { + if (flags & HoistedClassFlags.Export) { + declareUnboundVariable(parser, parser.tokenValue); + } + } + } + + firstRestricted = parser.token; + + if (parser.token & Token.IsIdentifier) { + id = parseIdentifier(parser, context, 0); + } else { + report(parser, Errors.UnexpectedToken, KeywordDescTable[parser.token & Token.Type]); + } + } + + context = + ((context | 0b0000001111011000000_0000_00000000) ^ 0b0000001111011000000_0000_00000000) | + Context.AllowNewTarget | + ((isAsync * 2 + isGenerator) << 21) | + (isGenerator ? 0 : Context.AllowEscapedKeyword); + + if (scope) functionScope = addChildScope(functionScope, ScopeKind.FunctionParams); + + const params = parseFormalParametersOrFormalList( + parser, + context | Context.InArgumentList, + functionScope, + 0, + BindingKind.ArgumentList + ); + + const body = parseFunctionBody( + parser, + (context | Context.InGlobal | Context.InSwitch | Context.InIteration) ^ + (Context.InGlobal | Context.InSwitch | Context.InIteration), + scope ? addChildScope(functionScope, ScopeKind.FunctionBody) : functionScope, + Origin.Declaration, + firstRestricted, + scope ? (functionScope as ScopeState).scopeError : void 0 + ); + + return finishNode(parser, context, start, line, column, { + type: 'FunctionDeclaration', + id, + params, + body, + async: isAsync === 1, + generator: isGenerator === 1 + }); +} + +/** + * Parse function expression + * + * @param parser Parser object + * @param context Context masks + * @param isAsync + */ +export function parseFunctionExpression( + parser: ParserState, + context: Context, + isAsync: 0 | 1, + inGroup: 0 | 1, + start: number, + line: number, + column: number +): ESTree.FunctionExpression { + // GeneratorExpression: + // function* BindingIdentifier [Yield][opt](FormalParameters[Yield]){ GeneratorBody } + // + // FunctionExpression: + // function BindingIdentifier[opt](FormalParameters){ FunctionBody } + + nextToken(parser, context | Context.AllowRegExp); + + const isGenerator = optionalBit(parser, context, Token.Multiply); + const generatorAndAsyncFlags = (isAsync * 2 + isGenerator) << 21; + + let id: ESTree.Identifier | null = null; + let firstRestricted: Token | undefined; + + // Create a new function scope + let scope = context & Context.OptionsLexical ? createScope() : void 0; + + if ((parser.token & (Token.IsIdentifier | Token.Keyword | Token.FutureReserved)) > 0) { + validateFunctionName(parser, ((context | 0x1ec0000) ^ 0x1ec0000) | generatorAndAsyncFlags, parser.token); + + if (scope) scope = addChildScope(scope, ScopeKind.FunctionRoot); + + firstRestricted = parser.token; + id = parseIdentifier(parser, context, /* isPattern */ 0); + } + + context = + ((context | 0b0000001111011000000_0000_00000000) ^ 0b0000001111011000000_0000_00000000) | + Context.AllowNewTarget | + generatorAndAsyncFlags | + (isGenerator ? 0 : Context.AllowEscapedKeyword); + + if (scope) scope = addChildScope(scope, ScopeKind.FunctionParams); + + const params = parseFormalParametersOrFormalList( + parser, + context | Context.InArgumentList, + scope, + inGroup, + BindingKind.ArgumentList + ); + + const body = parseFunctionBody( + parser, + context & ~(0x8001000 | Context.InGlobal | Context.InSwitch | Context.InIteration | Context.InClass), + scope ? addChildScope(scope, ScopeKind.FunctionBody) : scope, + 0, + firstRestricted, + void 0 + ); + + parser.assignable = AssignmentKind.CannotAssign; + + return finishNode(parser, context, start, line, column, { + type: 'FunctionExpression', + id, + params, + body, + async: isAsync === 1, + generator: isGenerator === 1 + }); +} + +/** + * Parses array literal expression + * + * @param parser Parser object + * @param context Context masks + * @param skipInitializer + */ +function parseArrayLiteral( + parser: ParserState, + context: Context, + skipInitializer: 0 | 1, + inGroup: 0 | 1, + start: number, + line: number, + column: number +): ESTree.ArrayExpression { + /* ArrayLiteral : + * [ Elisionopt ] + * [ ElementList ] + * [ ElementList , Elisionopt ] + * + * ElementList : + * Elisionopt AssignmentExpression + * Elisionopt ... AssignmentExpression + * ElementList , Elisionopt AssignmentExpression + * ElementList , Elisionopt SpreadElement + * + * Elision : + * , + * Elision , + * + * SpreadElement : + * ... AssignmentExpression + * + */ + const expr = parseArrayExpressionOrPattern( + parser, + context, + void 0, + skipInitializer, + inGroup, + 0, + BindingKind.Empty, + Origin.None, + start, + line, + column + ); + + if (context & Context.OptionsWebCompat && parser.destructible & DestructuringKind.SeenProto) { + report(parser, Errors.DuplicateProto); + } + + if (parser.destructible & DestructuringKind.HasToDestruct) { + report(parser, Errors.InvalidShorthandPropInit); + } + + return expr as ESTree.ArrayExpression; +} + +/** + * Parse array expression or pattern + * + * @param parser Parser object + * @param context Context masks + * @param skipInitializer + * @param BindingKind + */ + +export function parseArrayExpressionOrPattern( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + skipInitializer: 0 | 1, + inGroup: 0 | 1, + isPattern: 0 | 1, + kind: BindingKind, + origin: Origin, + start: number, + line: number, + column: number +): ESTree.ArrayExpression | ESTree.ArrayPattern | ESTree.AssignmentExpression { + /* ArrayLiteral : + * [ Elisionopt ] + * [ ElementList ] + * [ ElementList , Elisionopt ] + * + * ElementList : + * Elisionopt AssignmentExpression + * Elisionopt ... AssignmentExpression + * ElementList , Elisionopt AssignmentExpression + * ElementList , Elisionopt SpreadElement + * + * Elision : + * , + * Elision , + * + * SpreadElement : + * ... AssignmentExpression + * + * ArrayAssignmentPattern[Yield] : + * [ Elisionopt AssignmentRestElement[?Yield]opt ] + * [ AssignmentElementList[?Yield] ] + * [ AssignmentElementList[?Yield] , Elisionopt AssignmentRestElement[?Yield]opt ] + * + * AssignmentRestElement[Yield] : + * ... DestructuringAssignmentTarget[?Yield] + * + * AssignmentElementList[Yield] : + * AssignmentElisionElement[?Yield] + * AssignmentElementList[?Yield] , AssignmentElisionElement[?Yield] + * + * AssignmentElisionElement[Yield] : + * Elisionopt AssignmentElement[?Yield] + * + * AssignmentElement[Yield] : + * DestructuringAssignmentTarget[?Yield] Initializer[In,?Yield]opt + * + * DestructuringAssignmentTarget[Yield] : + * LeftHandSideExpression[?Yield] + */ + + nextToken(parser, context | Context.AllowRegExp); + + const elements: (ESTree.Identifier | ESTree.AssignmentExpression | null)[] = []; + let destructible: AssignmentKind | DestructuringKind = 0; + + context = (context | Context.DisallowIn) ^ Context.DisallowIn; + + while (parser.token !== Token.RightBracket) { + if (consumeOpt(parser, context | Context.AllowRegExp, Token.Comma)) { + elements.push(null); + } else { + let left: any; + + const { token, tokenPos, linePos, colPos, tokenValue } = parser; + + if (token & Token.IsIdentifier) { + left = parsePrimaryExpression(parser, context, kind, 0, 1, 0, inGroup, 1, tokenPos, linePos, colPos); + + if (parser.token === Token.Assign) { + if (parser.assignable & AssignmentKind.CannotAssign) report(parser, Errors.CantAssignTo); + + nextToken(parser, context | Context.AllowRegExp); + + if (scope) addVarOrBlock(parser, context, scope, tokenValue, kind, origin); + + const right = parseExpression(parser, context, 1, 1, inGroup, parser.tokenPos, parser.linePos, parser.colPos); + + left = finishNode( + parser, + context, + tokenPos, + linePos, + colPos, + isPattern + ? { + type: 'AssignmentPattern', + left, + right + } + : ({ + type: 'AssignmentExpression', + operator: '=', + left, + right + } as any) + ); + + destructible |= + parser.destructible & DestructuringKind.Yield + ? DestructuringKind.Yield + : 0 | (parser.destructible & DestructuringKind.Await) + ? DestructuringKind.Await + : 0; + } else if (parser.token === Token.Comma || parser.token === Token.RightBracket) { + if (parser.assignable & AssignmentKind.CannotAssign) { + destructible |= DestructuringKind.CannotDestruct; + } else if (scope) { + addVarOrBlock(parser, context, scope, tokenValue, kind, origin); + } + destructible |= + parser.destructible & DestructuringKind.Yield + ? DestructuringKind.Yield + : 0 | (parser.destructible & DestructuringKind.Await) + ? DestructuringKind.Await + : 0; + } else { + destructible |= + kind & BindingKind.ArgumentList + ? DestructuringKind.Assignable + : (kind & BindingKind.Empty) < 1 + ? DestructuringKind.CannotDestruct + : 0; + + left = parseMemberOrUpdateExpression(parser, context, left, inGroup, 0, tokenPos, linePos, colPos); + + if (parser.token !== Token.Comma && parser.token !== Token.RightBracket) { + if (parser.token !== Token.Assign) destructible |= DestructuringKind.CannotDestruct; + left = parseAssignmentExpression(parser, context, inGroup, isPattern, tokenPos, linePos, colPos, left); + } else if (parser.token !== Token.Assign) { + destructible |= + parser.assignable & AssignmentKind.CannotAssign + ? DestructuringKind.CannotDestruct + : DestructuringKind.Assignable; + } + } + } else if (token & Token.IsPatternStart) { + left = + parser.token === Token.LeftBrace + ? parseObjectLiteralOrPattern( + parser, + context, + scope, + 0, + inGroup, + isPattern, + kind, + origin, + tokenPos, + linePos, + colPos + ) + : parseArrayExpressionOrPattern( + parser, + context, + scope, + 0, + inGroup, + isPattern, + kind, + origin, + tokenPos, + linePos, + colPos + ); + + destructible |= parser.destructible; + + parser.assignable = + parser.destructible & DestructuringKind.CannotDestruct + ? AssignmentKind.CannotAssign + : AssignmentKind.Assignable; + + if (parser.token === Token.Comma || parser.token === Token.RightBracket) { + if (parser.assignable & AssignmentKind.CannotAssign) { + destructible |= DestructuringKind.CannotDestruct; + } + } else if (parser.destructible & DestructuringKind.HasToDestruct) { + report(parser, Errors.InvalidDestructuringTarget); + } else { + left = parseMemberOrUpdateExpression(parser, context, left, inGroup, 0, tokenPos, linePos, colPos); + destructible = parser.assignable & AssignmentKind.CannotAssign ? DestructuringKind.CannotDestruct : 0; + + if (parser.token !== Token.Comma && parser.token !== Token.RightBracket) { + left = parseAssignmentExpression(parser, context, inGroup, isPattern, tokenPos, linePos, colPos, left); + } else if (parser.token !== Token.Assign) { + destructible |= + parser.assignable & AssignmentKind.CannotAssign + ? DestructuringKind.CannotDestruct + : DestructuringKind.Assignable; + } + } + } else if (token === Token.Ellipsis) { + left = parseSpreadOrRestElement( + parser, + context, + scope, + Token.RightBracket, + kind, + origin, + 0, + inGroup, + isPattern, + tokenPos, + linePos, + colPos + ); + destructible |= parser.destructible; + if (parser.token !== Token.Comma && parser.token !== Token.RightBracket) + report(parser, Errors.UnexpectedToken, KeywordDescTable[parser.token & Token.Type]); + } else { + left = parseLeftHandSideExpression(parser, context, 1, 0, 1, tokenPos, linePos, colPos); + + if (parser.token !== Token.Comma && parser.token !== Token.RightBracket) { + left = parseAssignmentExpression(parser, context, inGroup, isPattern, tokenPos, linePos, colPos, left); + if ((kind & (BindingKind.Empty | BindingKind.ArgumentList)) < 1 && token === Token.LeftParen) + destructible |= DestructuringKind.CannotDestruct; + } else if (parser.assignable & AssignmentKind.CannotAssign) { + destructible |= DestructuringKind.CannotDestruct; + } else if (token === Token.LeftParen) { + destructible |= + parser.assignable & AssignmentKind.Assignable && kind & (BindingKind.Empty | BindingKind.ArgumentList) + ? DestructuringKind.Assignable + : DestructuringKind.CannotDestruct; + } + } + + elements.push(left); + + if (consumeOpt(parser, context | Context.AllowRegExp, Token.Comma)) { + if (parser.token === Token.RightBracket) break; + } else break; + } + } + + consume(parser, context, Token.RightBracket); + + const node = finishNode(parser, context, start, line, column, { + type: isPattern ? 'ArrayPattern' : 'ArrayExpression', + elements + } as any); + + if (!skipInitializer && parser.token & Token.IsAssignOp) { + return parseArrayOrObjectAssignmentPattern( + parser, + context, + destructible, + inGroup, + isPattern, + start, + line, + column, + node + ); + } + + parser.destructible = destructible; + + return node; +} + +/** + * Parses array or object assignment pattern + * + * @param parser Parser object + * @param context Context masks + * @param destructible + * @param inGroup + * @param start Start index + * @param line Start line + * @param column Start of column + * @param node ESTree AST node + */ + +function parseArrayOrObjectAssignmentPattern( + parser: ParserState, + context: Context, + destructible: AssignmentKind | DestructuringKind, + inGroup: 0 | 1, + isPattern: 0 | 1, + start: number, + line: number, + column: number, + node: ESTree.ArrayExpression | ESTree.ObjectExpression | ESTree.ObjectPattern +): ESTree.AssignmentExpression { + // 12.15.5 Destructuring Assignment + // + // AssignmentElement[Yield, Await]: + // DestructuringAssignmentTarget[?Yield, ?Await] + // DestructuringAssignmentTarget[?Yield, ?Await] Initializer[+In, ?Yield, ?Await] + // + + if (parser.token !== Token.Assign) report(parser, Errors.CantAssignTo); + + nextToken(parser, context | Context.AllowRegExp); + + if (destructible & DestructuringKind.CannotDestruct) report(parser, Errors.CantAssignTo); + + if (!isPattern) reinterpretToPattern(parser, node); + + const { tokenPos, linePos, colPos } = parser; + + const right = parseExpression(parser, context, 1, 1, inGroup, tokenPos, linePos, colPos); + + parser.destructible = + ((destructible | DestructuringKind.SeenProto | DestructuringKind.HasToDestruct) ^ + (DestructuringKind.HasToDestruct | DestructuringKind.SeenProto)) | + (parser.destructible & DestructuringKind.Await ? DestructuringKind.Await : 0) | + (parser.destructible & DestructuringKind.Yield ? DestructuringKind.Yield : 0); + + return finishNode( + parser, + context, + start, + line, + column, + isPattern + ? { + type: 'AssignmentPattern', + left: node, + right + } + : ({ + type: 'AssignmentExpression', + left: node, + operator: '=', + right + } as any) + ); +} + +/** + * Parses rest or spread element + * + * @param parser Parser object + * @param context Context masks + * @param closingToken + * @param type Binding kind + * @param origin Binding origin + * @param isAsync + * @param isGroup + * @param start Start index + * @param line Start line + * @param column Start of column + */ +function parseSpreadOrRestElement( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + closingToken: Token, + kind: BindingKind, + origin: Origin, + isAsync: 0 | 1, + inGroup: 0 | 1, + isPattern: 0 | 1, + start: number, + line: number, + column: number +): ESTree.SpreadElement | ESTree.RestElement { + nextToken(parser, context | Context.AllowRegExp); // skip '...' + + let argument: ESTree.Expression | null = null; + let destructible: AssignmentKind | DestructuringKind = DestructuringKind.None; + + let { token, tokenValue, tokenPos, linePos, colPos } = parser; + + if (token & (Token.Keyword | Token.IsIdentifier)) { + parser.assignable = AssignmentKind.Assignable; + + argument = parsePrimaryExpression(parser, context, kind, 0, 1, 0, inGroup, 1, tokenPos, linePos, colPos); + + token = parser.token; + + argument = parseMemberOrUpdateExpression( + parser, + context, + argument as ESTree.Expression, + inGroup, + 0, + tokenPos, + linePos, + colPos + ); + + if (parser.token !== Token.Comma && parser.token !== closingToken) { + if (parser.assignable & AssignmentKind.CannotAssign && parser.token === Token.Assign) + report(parser, Errors.InvalidDestructuringTarget); + + destructible |= DestructuringKind.CannotDestruct; + + argument = parseAssignmentExpression(parser, context, inGroup, isPattern, tokenPos, linePos, colPos, argument); + } + + if (parser.assignable & AssignmentKind.CannotAssign) { + destructible |= DestructuringKind.CannotDestruct; + } else if (token === closingToken || token === Token.Comma) { + if (scope) addVarOrBlock(parser, context, scope, tokenValue, kind, origin); + } else { + destructible |= DestructuringKind.Assignable; + } + + destructible |= parser.destructible & DestructuringKind.Await ? DestructuringKind.Await : 0; + } else if (token === closingToken) { + report(parser, Errors.RestMissingArg); + } else if (token & Token.IsPatternStart) { + argument = + parser.token === Token.LeftBrace + ? parseObjectLiteralOrPattern( + parser, + context, + scope, + 1, + inGroup, + isPattern, + kind, + origin, + tokenPos, + linePos, + colPos + ) + : parseArrayExpressionOrPattern( + parser, + context, + scope, + 1, + inGroup, + isPattern, + kind, + origin, + tokenPos, + linePos, + colPos + ); + + token = parser.token; + + if (token !== Token.Assign && token !== closingToken && token !== Token.Comma) { + if (parser.destructible & DestructuringKind.HasToDestruct) report(parser, Errors.InvalidDestructuringTarget); + + argument = parseMemberOrUpdateExpression(parser, context, argument, inGroup, 0, tokenPos, linePos, colPos); + + destructible |= parser.assignable & AssignmentKind.CannotAssign ? DestructuringKind.CannotDestruct : 0; + + if ((parser.token & Token.IsAssignOp) === Token.IsAssignOp) { + if (parser.token !== Token.Assign) destructible |= DestructuringKind.CannotDestruct; + argument = parseAssignmentExpression(parser, context, inGroup, isPattern, tokenPos, linePos, colPos, argument); + } else { + if ((parser.token & Token.IsBinaryOp) === Token.IsBinaryOp) { + argument = parseBinaryExpression(parser, context, 1, tokenPos, linePos, colPos, 4, token, argument as any); + } + if (consumeOpt(parser, context | Context.AllowRegExp, Token.QuestionMark)) { + argument = parseConditionalExpression(parser, context, argument as any, tokenPos, linePos, colPos); + } + destructible |= + parser.assignable & AssignmentKind.CannotAssign + ? DestructuringKind.CannotDestruct + : DestructuringKind.Assignable; + } + } else { + destructible |= + closingToken === Token.RightBrace && token !== Token.Assign + ? DestructuringKind.CannotDestruct + : parser.destructible; + } + } else { + destructible |= DestructuringKind.Assignable; + + argument = parseLeftHandSideExpression( + parser, + context, + 1, + inGroup, + 1, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + + const { token, tokenPos, linePos, colPos } = parser; + + if (token === Token.Assign && token !== closingToken && token !== Token.Comma) { + if (parser.assignable & AssignmentKind.CannotAssign) report(parser, Errors.CantAssignTo); + + argument = parseAssignmentExpression(parser, context, inGroup, isPattern, tokenPos, linePos, colPos, argument); + + destructible |= DestructuringKind.CannotDestruct; + } else { + if (token === Token.Comma) { + destructible |= DestructuringKind.CannotDestruct; + } else if (token !== closingToken) { + argument = parseAssignmentExpression(parser, context, inGroup, isPattern, tokenPos, linePos, colPos, argument); + } + + destructible |= + parser.assignable & AssignmentKind.Assignable ? DestructuringKind.Assignable : DestructuringKind.CannotDestruct; + } + + parser.destructible = destructible; + + if (parser.token !== closingToken && parser.token !== Token.Comma) report(parser, Errors.UnclosedSpreadElement); + + return finishNode(parser, context, start, line, column, { + type: isPattern ? 'RestElement' : 'SpreadElement', + argument: argument as ESTree.SpreadArgument + } as any); + } + + if (parser.token !== closingToken) { + if (kind & BindingKind.ArgumentList) + destructible |= isAsync ? DestructuringKind.CannotDestruct : DestructuringKind.Assignable; + + if (consumeOpt(parser, context | Context.AllowRegExp, Token.Assign)) { + if (destructible & DestructuringKind.CannotDestruct) report(parser, Errors.CantAssignTo); + + reinterpretToPattern(parser, argument); + + const right = parseExpression(parser, context, 1, 1, inGroup, parser.tokenPos, parser.linePos, parser.colPos); + + argument = finishNode( + parser, + context, + tokenPos, + linePos, + colPos, + isPattern + ? { + type: 'AssignmentPattern', + left: argument as ESTree.SpreadArgument, + right + } + : ({ + type: 'AssignmentExpression', + left: argument as ESTree.SpreadArgument, + operator: '=', + right + } as any) + ); + + destructible = DestructuringKind.CannotDestruct; + } else { + // Note the difference between '|=' and '=' above + destructible |= DestructuringKind.CannotDestruct; + } + } + + parser.destructible = destructible; + + return finishNode(parser, context, start, line, column, { + type: isPattern ? 'RestElement' : 'SpreadElement', + argument: argument as ESTree.SpreadArgument + } as any); +} + +/** + * Parses method definition + * + * @param parser Parser object + * @param context Context masks + * @param kind + * @param inGroup + * @param start Start index + * @param line Start line + * @param column Start of column + */ +export function parseMethodDefinition( + parser: ParserState, + context: Context, + kind: PropertyKind, + inGroup: 0 | 1, + start: number, + line: number, + column: number +): ESTree.FunctionExpression { + const modifierFlags = + (kind & PropertyKind.Constructor) < 1 ? 0b0000001111010000000_0000_00000000 : 0b0000000111000000000_0000_00000000; + + context = + ((context | modifierFlags) ^ modifierFlags) | + ((kind & 0b0000000000000000000_0000_01011000) << 18) | + 0b0000110000001000000_0000_00000000; + + let scope = context & Context.OptionsLexical ? addChildScope(createScope(), ScopeKind.FunctionParams) : void 0; + + const params = parseMethodFormals( + parser, + context | Context.InArgumentList, + scope, + kind, + BindingKind.ArgumentList, + inGroup + ); + + if (scope) scope = addChildScope(scope, ScopeKind.FunctionBody); + + const body = parseFunctionBody(parser, context & ~(0x8001000 | Context.InGlobal), scope, Origin.None, void 0, void 0); + + return finishNode(parser, context, start, line, column, { + type: 'FunctionExpression', + params, + body, + async: (kind & PropertyKind.Async) > 0, + generator: (kind & PropertyKind.Generator) > 0, + id: null + }); +} + +/** + * Parse object literal or object pattern + * + * @param parser Parser object + * @param context Context masks + * @param skipInitializer + * @param inGroup + * @param start Start index + * @param line Start line + * @param column Start of column + + */ +function parseObjectLiteral( + parser: ParserState, + context: Context, + skipInitializer: 0 | 1, + inGroup: 0 | 1, + start: number, + line: number, + column: number +): ESTree.ObjectExpression { + /** + * ObjectLiteral + * {} + * {PropertyDefinitionList } + * + * {PropertyDefinitionList } + * + * PropertyDefinitionList: + * PropertyDefinition + * PropertyDefinitionList, PropertyDefinition + * + * PropertyDefinition: + * { IdentifierReference } + * { CoverInitializedName } + * { PropertyName:AssignmentExpression } + * + * MethodDefinition: + * ...AssignmentExpression + * + * PropertyName: + * LiteralPropertyName + * ComputedPropertyName + * LiteralPropertyName: + * IdentifierName + * StringLiteral + * NumericLiteral + * + * ComputedPropertyName: AssignmentExpression + * + * CoverInitializedName: + * IdentifierReference , Initializer + * + * Initializer: + * =AssignmentExpression + */ + const expr = parseObjectLiteralOrPattern( + parser, + context, + void 0, + skipInitializer, + inGroup, + 0, + BindingKind.Empty, + Origin.None, + start, + line, + column + ); + + if (context & Context.OptionsWebCompat && parser.destructible & DestructuringKind.SeenProto) { + report(parser, Errors.DuplicateProto); + } + + if (parser.destructible & DestructuringKind.HasToDestruct) { + report(parser, Errors.InvalidShorthandPropInit); + } + + return expr as ESTree.ObjectExpression; +} + +/** + * Parse object literal or object pattern + * + * @param parser Parser object + * @param context Context masks + * @param skipInitializer Context masks + * @param inGroup + * @param kind Binding kind + * @param origin Binding origin + * @param start + * @param line + * @param column + */ +export function parseObjectLiteralOrPattern( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + skipInitializer: 0 | 1, + inGroup: 0 | 1, + isPattern: 0 | 1, + kind: BindingKind, + origin: Origin, + start: number, + line: number, + column: number +): ESTree.ObjectExpression | ESTree.ObjectPattern | ESTree.AssignmentExpression { + /** + * + * ObjectLiteral : + * { } + * { PropertyDefinitionList } + * + * PropertyDefinitionList : + * PropertyDefinition + * PropertyDefinitionList, PropertyDefinition + * + * PropertyDefinition : + * IdentifierName + * PropertyName : AssignmentExpression + * + * PropertyName : + * IdentifierName + * StringLiteral + * NumericLiteral + * + * + * ObjectBindingPattern : + * {} + * { BindingPropertyList } + * { BindingPropertyList , } + * + * BindingPropertyList : + * BindingProperty + * BindingPropertyList , BindingProperty + * + * BindingProperty : + * SingleNameBinding + * PropertyName : BindingElement + * + * SingleNameBinding : + * BindingIdentifier Initializeropt + * + * PropertyDefinition : + * IdentifierName + * CoverInitializedName + * PropertyName : AssignmentExpression + * MethodDefinition + */ + + nextToken(parser, context); + + const properties: (ESTree.Property | ESTree.SpreadElement | ESTree.RestElement)[] = []; + let destructible: DestructuringKind | AssignmentKind = 0; + let prototypeCount = 0; + + context = (context | Context.DisallowIn) ^ Context.DisallowIn; + + while (parser.token !== Token.RightBrace) { + const { token, tokenValue, linePos, colPos, tokenPos } = parser; + + if (token === Token.Ellipsis) { + properties.push( + parseSpreadOrRestElement( + parser, + context, + scope, + Token.RightBrace, + kind, + origin, + 0, + inGroup, + isPattern, + tokenPos, + linePos, + colPos + ) + ); + } else { + let state = PropertyKind.None; + let key: ESTree.Expression | null = null; + let value; + const t = parser.token; + if (parser.token & (Token.IsIdentifier | Token.Keyword) || parser.token === Token.EscapedReserved) { + key = parseIdentifier(parser, context, 0); + + if (parser.token === Token.Comma || parser.token === Token.RightBrace || parser.token === Token.Assign) { + state |= PropertyKind.Shorthand; + + if (context & Context.Strict && (token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments) { + destructible |= DestructuringKind.CannotDestruct; + } else { + validateBindingIdentifier(parser, context, kind, token, 0); + } + + if (scope) addVarOrBlock(parser, context, scope, tokenValue, kind, origin); + + if (consumeOpt(parser, context | Context.AllowRegExp, Token.Assign)) { + destructible |= DestructuringKind.HasToDestruct; + + const right = parseExpression( + parser, + context, + 1, + 1, + inGroup, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + + destructible |= + parser.destructible & DestructuringKind.Yield + ? DestructuringKind.Yield + : 0 | (parser.destructible & DestructuringKind.Await) + ? DestructuringKind.Await + : 0; + + value = finishNode(parser, context, tokenPos, linePos, colPos, { + type: 'AssignmentPattern', + left: context & Context.OptionsUniqueKeyInPattern ? Object.assign({}, key) : key, + right + }); + } else { + destructible |= + (token === Token.AwaitKeyword ? DestructuringKind.Await : 0) | + (token === Token.EscapedReserved ? DestructuringKind.CannotDestruct : 0); + value = context & Context.OptionsUniqueKeyInPattern ? Object.assign({}, key) : key; + } + } else if (consumeOpt(parser, context | Context.AllowRegExp, Token.Colon)) { + const { tokenPos, linePos, colPos } = parser; + + if (tokenValue === '__proto__') prototypeCount++; + + if (parser.token & Token.IsIdentifier) { + const tokenAfterColon = parser.token; + const valueAfterColon = parser.tokenValue; + // A reserved word is an IdentifierName that cannot be used as an Identifier + destructible |= t === Token.EscapedReserved ? DestructuringKind.CannotDestruct : 0; + + value = parsePrimaryExpression(parser, context, kind, 0, 1, 0, inGroup, 1, tokenPos, linePos, colPos); + + const { token } = parser; + + value = parseMemberOrUpdateExpression(parser, context, value, inGroup, 0, tokenPos, linePos, colPos); + + if (parser.token === Token.Comma || parser.token === Token.RightBrace) { + if (token === Token.Assign || token === Token.RightBrace || token === Token.Comma) { + destructible |= parser.destructible & DestructuringKind.Await ? DestructuringKind.Await : 0; + if (parser.assignable & AssignmentKind.CannotAssign) { + destructible |= DestructuringKind.CannotDestruct; + } else if (scope && (tokenAfterColon & Token.IsIdentifier) === Token.IsIdentifier) { + addVarOrBlock(parser, context, scope, valueAfterColon, kind, origin); + } + } else { + destructible |= + parser.assignable & AssignmentKind.Assignable + ? DestructuringKind.Assignable + : DestructuringKind.CannotDestruct; + } + } else if ((parser.token & Token.IsAssignOp) === Token.IsAssignOp) { + if (parser.assignable & AssignmentKind.CannotAssign) { + destructible |= DestructuringKind.CannotDestruct; + } else if (token !== Token.Assign) { + destructible |= DestructuringKind.Assignable; + } else if (scope) { + addVarOrBlock(parser, context, scope, valueAfterColon, kind, origin); + } + value = parseAssignmentExpression(parser, context, inGroup, isPattern, tokenPos, linePos, colPos, value); + } else { + destructible |= DestructuringKind.CannotDestruct; + if ((parser.token & Token.IsBinaryOp) === Token.IsBinaryOp) { + value = parseBinaryExpression(parser, context, 1, tokenPos, linePos, colPos, 4, token, value); + } + if (consumeOpt(parser, context | Context.AllowRegExp, Token.QuestionMark)) { + value = parseConditionalExpression(parser, context, value, tokenPos, linePos, colPos); + } + } + } else if ((parser.token & Token.IsPatternStart) === Token.IsPatternStart) { + value = + parser.token === Token.LeftBracket + ? parseArrayExpressionOrPattern( + parser, + context, + scope, + 0, + inGroup, + isPattern, + kind, + origin, + tokenPos, + linePos, + colPos + ) + : parseObjectLiteralOrPattern( + parser, + context, + scope, + 0, + inGroup, + isPattern, + kind, + origin, + tokenPos, + linePos, + colPos + ); + + destructible = parser.destructible; + + parser.assignable = + destructible & DestructuringKind.CannotDestruct ? AssignmentKind.CannotAssign : AssignmentKind.Assignable; + + if (parser.token === Token.Comma || parser.token === Token.RightBrace) { + if (parser.assignable & AssignmentKind.CannotAssign) destructible |= DestructuringKind.CannotDestruct; + } else if (parser.destructible & DestructuringKind.HasToDestruct) { + report(parser, Errors.InvalidDestructuringTarget); + } else { + value = parseMemberOrUpdateExpression(parser, context, value, inGroup, 0, tokenPos, linePos, colPos); + + destructible = parser.assignable & AssignmentKind.CannotAssign ? DestructuringKind.CannotDestruct : 0; + + if ((parser.token & Token.IsAssignOp) === Token.IsAssignOp) { + value = parseAssignmentExpressionOrPattern( + parser, + context, + inGroup, + isPattern, + tokenPos, + linePos, + colPos, + value + ); + } else { + if ((parser.token & Token.IsBinaryOp) === Token.IsBinaryOp) { + value = parseBinaryExpression(parser, context, 1, tokenPos, linePos, colPos, 4, token, value); + } + if (consumeOpt(parser, context | Context.AllowRegExp, Token.QuestionMark)) { + value = parseConditionalExpression(parser, context, value, tokenPos, linePos, colPos); + } + destructible |= + parser.assignable & AssignmentKind.CannotAssign + ? DestructuringKind.CannotDestruct + : DestructuringKind.Assignable; + } + } + } else { + value = parseLeftHandSideExpression(parser, context, 1, inGroup, 1, tokenPos, linePos, colPos); + + destructible |= + parser.assignable & AssignmentKind.Assignable + ? DestructuringKind.Assignable + : DestructuringKind.CannotDestruct; + + if (parser.token === Token.Comma || parser.token === Token.RightBrace) { + if (parser.assignable & AssignmentKind.CannotAssign) destructible |= DestructuringKind.CannotDestruct; + } else { + value = parseMemberOrUpdateExpression(parser, context, value, inGroup, 0, tokenPos, linePos, colPos); + + destructible = parser.assignable & AssignmentKind.CannotAssign ? DestructuringKind.CannotDestruct : 0; + + if (parser.token !== Token.Comma && token !== Token.RightBrace) { + if (parser.token !== Token.Assign) destructible |= DestructuringKind.CannotDestruct; + value = parseAssignmentExpression( + parser, + context, + inGroup, + isPattern, + tokenPos, + linePos, + colPos, + value + ); + } + } + } + } else if (parser.token === Token.LeftBracket) { + destructible |= DestructuringKind.CannotDestruct; + if (token === Token.AsyncKeyword) state |= PropertyKind.Async; + + state |= + (token === Token.GetKeyword + ? PropertyKind.Getter + : token === Token.SetKeyword + ? PropertyKind.Setter + : PropertyKind.Method) | PropertyKind.Computed; + + key = parseComputedPropertyName(parser, context, inGroup); + destructible |= parser.assignable; + + value = parseMethodDefinition( + parser, + context, + state, + inGroup, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + } else if (parser.token & (Token.IsIdentifier | Token.Keyword)) { + destructible |= DestructuringKind.CannotDestruct; + if (token === Token.EscapedReserved) report(parser, Errors.InvalidEscapedKeyword); + if (token === Token.AsyncKeyword) { + if (parser.flags & Flags.NewLine) report(parser, Errors.AsyncRestrictedProd); + state |= PropertyKind.Async; + } + key = parseIdentifier(parser, context, 0); + + state |= + token === Token.GetKeyword + ? PropertyKind.Getter + : token === Token.SetKeyword + ? PropertyKind.Setter + : PropertyKind.Method; + + value = parseMethodDefinition( + parser, + context, + state, + inGroup, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + } else if (parser.token === Token.LeftParen) { + destructible |= DestructuringKind.CannotDestruct; + state |= PropertyKind.Method; + value = parseMethodDefinition( + parser, + context, + state, + inGroup, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + } else if (parser.token === Token.Multiply) { + destructible |= DestructuringKind.CannotDestruct; + + if (token === Token.GetKeyword || token === Token.SetKeyword) { + report(parser, Errors.InvalidGeneratorGetter); + } else if (token === Token.AnyIdentifier) { + report(parser, Errors.InvalidEscapedKeyword); + } + + nextToken(parser, context); + + state |= + PropertyKind.Generator | PropertyKind.Method | (token === Token.AsyncKeyword ? PropertyKind.Async : 0); + + if (parser.token & Token.IsIdentifier) { + key = parseIdentifier(parser, context, 0); + } else if ((parser.token & Token.IsStringOrNumber) === Token.IsStringOrNumber) { + key = parseLiteral(parser, context); + } else if (parser.token === Token.LeftBracket) { + state |= PropertyKind.Computed; + key = parseComputedPropertyName(parser, context, inGroup); + destructible |= parser.assignable; + } else { + report(parser, Errors.UnexpectedToken, KeywordDescTable[parser.token & Token.Type]); + } + value = parseMethodDefinition( + parser, + context, + state, + inGroup, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + } else if ((parser.token & Token.IsStringOrNumber) === Token.IsStringOrNumber) { + if (token === Token.AsyncKeyword) state |= PropertyKind.Async; + + state |= + token === Token.GetKeyword + ? PropertyKind.Getter + : token === Token.SetKeyword + ? PropertyKind.Setter + : PropertyKind.Method; + + destructible |= DestructuringKind.CannotDestruct; + + key = parseLiteral(parser, context); + + value = parseMethodDefinition( + parser, + context, + state, + inGroup, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + } else { + report(parser, Errors.UnexpectedCharAfterObjLit); + } + } else if ((parser.token & Token.IsStringOrNumber) === Token.IsStringOrNumber) { + key = parseLiteral(parser, context); + + if (parser.token === Token.Colon) { + consume(parser, context | Context.AllowRegExp, Token.Colon); + + const { tokenPos, linePos, colPos } = parser; + + if (tokenValue === '__proto__') prototypeCount++; + + if (parser.token & Token.IsIdentifier) { + value = parsePrimaryExpression(parser, context, kind, 0, 1, 0, inGroup, 1, tokenPos, linePos, colPos); + + const { token, tokenValue: valueAfterColon } = parser; + + value = parseMemberOrUpdateExpression(parser, context, value, inGroup, 0, tokenPos, linePos, colPos); + + if (parser.token === Token.Comma || parser.token === Token.RightBrace) { + if (token === Token.Assign || token === Token.RightBrace || token === Token.Comma) { + if (parser.assignable & AssignmentKind.CannotAssign) { + destructible |= DestructuringKind.CannotDestruct; + } else if (scope) { + addVarOrBlock(parser, context, scope, valueAfterColon, kind, origin); + } + } else { + destructible |= + parser.assignable & AssignmentKind.Assignable + ? DestructuringKind.Assignable + : DestructuringKind.CannotDestruct; + } + } else if (parser.token === Token.Assign) { + if (parser.assignable & AssignmentKind.CannotAssign) destructible |= DestructuringKind.CannotDestruct; + value = parseAssignmentExpression(parser, context, inGroup, isPattern, tokenPos, linePos, colPos, value); + } else { + destructible |= DestructuringKind.CannotDestruct; + value = parseAssignmentExpression(parser, context, inGroup, isPattern, tokenPos, linePos, colPos, value); + } + } else if ((parser.token & Token.IsPatternStart) === Token.IsPatternStart) { + value = + parser.token === Token.LeftBracket + ? parseArrayExpressionOrPattern( + parser, + context, + scope, + 0, + inGroup, + isPattern, + kind, + origin, + tokenPos, + linePos, + colPos + ) + : parseObjectLiteralOrPattern( + parser, + context, + scope, + 0, + inGroup, + isPattern, + kind, + origin, + tokenPos, + linePos, + colPos + ); + + destructible = parser.destructible; + + parser.assignable = + destructible & DestructuringKind.CannotDestruct ? AssignmentKind.CannotAssign : AssignmentKind.Assignable; + + if (parser.token === Token.Comma || parser.token === Token.RightBrace) { + if (parser.assignable & AssignmentKind.CannotAssign) { + destructible |= DestructuringKind.CannotDestruct; + } + } else if ((parser.destructible & DestructuringKind.HasToDestruct) !== DestructuringKind.HasToDestruct) { + value = parseMemberOrUpdateExpression(parser, context, value, inGroup, 0, tokenPos, linePos, colPos); + destructible = parser.assignable & AssignmentKind.CannotAssign ? DestructuringKind.CannotDestruct : 0; + + if ((parser.token & Token.IsAssignOp) === Token.IsAssignOp) { + value = parseAssignmentExpressionOrPattern( + parser, + context, + inGroup, + isPattern, + tokenPos, + linePos, + colPos, + value + ); + } else { + if ((parser.token & Token.IsBinaryOp) === Token.IsBinaryOp) { + value = parseBinaryExpression(parser, context, 1, tokenPos, linePos, colPos, 4, token, value); + } + if (consumeOpt(parser, context | Context.AllowRegExp, Token.QuestionMark)) { + value = parseConditionalExpression(parser, context, value, tokenPos, linePos, colPos); + } + destructible |= + parser.assignable & AssignmentKind.CannotAssign + ? DestructuringKind.CannotDestruct + : DestructuringKind.Assignable; + } + } + } else { + value = parseLeftHandSideExpression(parser, context, 1, 0, 1, tokenPos, linePos, colPos); + + destructible |= + parser.assignable & AssignmentKind.Assignable + ? DestructuringKind.Assignable + : DestructuringKind.CannotDestruct; + + if (parser.token === Token.Comma || parser.token === Token.RightBrace) { + if (parser.assignable & AssignmentKind.CannotAssign) { + destructible |= DestructuringKind.CannotDestruct; + } + } else { + value = parseMemberOrUpdateExpression(parser, context, value, inGroup, 0, tokenPos, linePos, colPos); + + destructible = parser.assignable & AssignmentKind.Assignable ? 0 : DestructuringKind.CannotDestruct; + + if (parser.token !== Token.Comma && parser.token !== Token.RightBrace) { + if (parser.token !== Token.Assign) destructible |= DestructuringKind.CannotDestruct; + value = parseAssignmentExpression( + parser, + context, + inGroup, + isPattern, + tokenPos, + linePos, + colPos, + value + ); + } + } + } + } else if (parser.token === Token.LeftParen) { + state |= PropertyKind.Method; + value = parseMethodDefinition( + parser, + context, + state, + inGroup, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + destructible = parser.assignable | DestructuringKind.CannotDestruct; + } else { + report(parser, Errors.InvalidObjLitKey); + } + } else if (parser.token === Token.LeftBracket) { + key = parseComputedPropertyName(parser, context, inGroup); + + destructible |= parser.destructible & DestructuringKind.Yield ? DestructuringKind.Yield : 0; + + state |= PropertyKind.Computed; + + if (parser.token === Token.Colon) { + nextToken(parser, context | Context.AllowRegExp); // skip ':' + + const { tokenPos, linePos, colPos, tokenValue, token: tokenAfterColon } = parser; + + if (parser.token & Token.IsIdentifier) { + value = parsePrimaryExpression(parser, context, kind, 0, 1, 0, inGroup, 1, tokenPos, linePos, colPos); + + const { token } = parser; + + value = parseMemberOrUpdateExpression(parser, context, value, inGroup, 0, tokenPos, linePos, colPos); + + if ((parser.token & Token.IsAssignOp) === Token.IsAssignOp) { + destructible |= + parser.assignable & AssignmentKind.CannotAssign + ? DestructuringKind.CannotDestruct + : token === Token.Assign + ? 0 + : DestructuringKind.Assignable; + value = parseAssignmentExpressionOrPattern( + parser, + context, + inGroup, + isPattern, + tokenPos, + linePos, + colPos, + value + ); + } else if (parser.token === Token.Comma || parser.token === Token.RightBrace) { + if (token === Token.Assign || token === Token.RightBrace || token === Token.Comma) { + if (parser.assignable & AssignmentKind.CannotAssign) { + destructible |= DestructuringKind.CannotDestruct; + } else if (scope && (tokenAfterColon & Token.IsIdentifier) === Token.IsIdentifier) { + addVarOrBlock(parser, context, scope, tokenValue, kind, origin); + } + } else { + destructible |= + parser.assignable & AssignmentKind.Assignable + ? DestructuringKind.Assignable + : DestructuringKind.CannotDestruct; + } + } else { + destructible |= DestructuringKind.CannotDestruct; + value = parseAssignmentExpression(parser, context, inGroup, isPattern, tokenPos, linePos, colPos, value); + } + } else if ((parser.token & Token.IsPatternStart) === Token.IsPatternStart) { + value = + parser.token === Token.LeftBracket + ? parseArrayExpressionOrPattern( + parser, + context, + scope, + 0, + inGroup, + isPattern, + kind, + origin, + tokenPos, + linePos, + colPos + ) + : parseObjectLiteralOrPattern( + parser, + context, + scope, + 0, + inGroup, + isPattern, + kind, + origin, + tokenPos, + linePos, + colPos + ); + + destructible = parser.destructible; + + parser.assignable = + destructible & DestructuringKind.CannotDestruct ? AssignmentKind.CannotAssign : AssignmentKind.Assignable; + + if (parser.token === Token.Comma || parser.token === Token.RightBrace) { + if (parser.assignable & AssignmentKind.CannotAssign) destructible |= DestructuringKind.CannotDestruct; + } else if (destructible & DestructuringKind.HasToDestruct) { + report(parser, Errors.InvalidShorthandPropInit); + } else { + value = parseMemberOrUpdateExpression(parser, context, value, inGroup, 0, tokenPos, linePos, colPos); + + destructible = + parser.assignable & AssignmentKind.CannotAssign ? destructible | DestructuringKind.CannotDestruct : 0; + + if ((parser.token & Token.IsAssignOp) === Token.IsAssignOp) { + if (parser.token !== Token.Assign) destructible |= DestructuringKind.CannotDestruct; + value = parseAssignmentExpressionOrPattern( + parser, + context, + inGroup, + isPattern, + tokenPos, + linePos, + colPos, + value + ); + } else { + if ((parser.token & Token.IsBinaryOp) === Token.IsBinaryOp) { + value = parseBinaryExpression(parser, context, 1, tokenPos, linePos, colPos, 4, token, value); + } + if (consumeOpt(parser, context | Context.AllowRegExp, Token.QuestionMark)) { + value = parseConditionalExpression(parser, context, value, tokenPos, linePos, colPos); + } + destructible |= + parser.assignable & AssignmentKind.CannotAssign + ? DestructuringKind.CannotDestruct + : DestructuringKind.Assignable; + } + } + } else { + value = parseLeftHandSideExpression(parser, context, 1, 0, 1, tokenPos, linePos, colPos); + + destructible |= + parser.assignable & AssignmentKind.Assignable + ? DestructuringKind.Assignable + : DestructuringKind.CannotDestruct; + + if (parser.token === Token.Comma || parser.token === Token.RightBrace) { + if (parser.assignable & AssignmentKind.CannotAssign) destructible |= DestructuringKind.CannotDestruct; + } else { + value = parseMemberOrUpdateExpression(parser, context, value, inGroup, 0, tokenPos, linePos, colPos); + + destructible = parser.assignable & AssignmentKind.Assignable ? 0 : DestructuringKind.CannotDestruct; + + if (parser.token !== Token.Comma && parser.token !== Token.RightBrace) { + if (parser.token !== Token.Assign) destructible |= DestructuringKind.CannotDestruct; + value = parseAssignmentExpression( + parser, + context, + inGroup, + isPattern, + tokenPos, + linePos, + colPos, + value + ); + } + } + } + } else if (parser.token === Token.LeftParen) { + state |= PropertyKind.Method; + + value = parseMethodDefinition(parser, context, state, inGroup, parser.tokenPos, linePos, colPos); + + destructible = DestructuringKind.CannotDestruct; + } else { + report(parser, Errors.InvalidComputedPropName); + } + } else if (token === Token.Multiply) { + consume(parser, context | Context.AllowRegExp, Token.Multiply); + + state |= PropertyKind.Generator; + + if (parser.token & Token.IsIdentifier) { + const { token, line, index } = parser; + + key = parseIdentifier(parser, context, 0); + + state |= PropertyKind.Method; + + if (parser.token === Token.LeftParen) { + destructible |= DestructuringKind.CannotDestruct; + value = parseMethodDefinition( + parser, + context, + state, + inGroup, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + } else { + reportMessageAt( + index, + line, + index, + token === Token.AsyncKeyword + ? Errors.InvalidAsyncGetter + : token === Token.GetKeyword || parser.token === Token.SetKeyword + ? Errors.InvalidGetSetGenerator + : Errors.InvalidGenMethodShorthand, + KeywordDescTable[token & Token.Type] + ); + } + } else if ((parser.token & Token.IsStringOrNumber) === Token.IsStringOrNumber) { + destructible |= DestructuringKind.CannotDestruct; + key = parseLiteral(parser, context); + state |= PropertyKind.Method; + value = parseMethodDefinition(parser, context, state, inGroup, tokenPos, linePos, colPos); + } else if (parser.token === Token.LeftBracket) { + destructible |= DestructuringKind.CannotDestruct; + state |= PropertyKind.Computed | PropertyKind.Method; + key = parseComputedPropertyName(parser, context, inGroup); + value = parseMethodDefinition( + parser, + context, + state, + inGroup, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + } else { + report(parser, Errors.InvalidObjLitKeyStar); + } + } else { + report(parser, Errors.UnexpectedToken, KeywordDescTable[token & Token.Type]); + } + + destructible |= parser.destructible & DestructuringKind.Await ? DestructuringKind.Await : 0; + + parser.destructible = destructible; + + properties.push( + finishNode(parser, context, tokenPos, linePos, colPos, { + type: 'Property', + key: key as ESTree.Expression, + value, + kind: !(state & PropertyKind.GetSet) ? 'init' : state & PropertyKind.Setter ? 'set' : 'get', + computed: (state & PropertyKind.Computed) > 0, + method: (state & PropertyKind.Method) > 0, + shorthand: (state & PropertyKind.Shorthand) > 0 + }) + ); + } + + destructible |= parser.destructible; + if (parser.token !== Token.Comma) break; + nextToken(parser, context); + } + + consume(parser, context, Token.RightBrace); + + if (prototypeCount > 1) destructible |= DestructuringKind.SeenProto; + + const node = finishNode(parser, context, start, line, column, { + type: isPattern ? 'ObjectPattern' : 'ObjectExpression', + properties + }); + + if (!skipInitializer && parser.token & Token.IsAssignOp) { + return parseArrayOrObjectAssignmentPattern( + parser, + context, + destructible, + inGroup, + isPattern, + start, + line, + column, + node + ); + } + + parser.destructible = destructible; + + return node; +} + +/** + * Parses method formals + * + * @param parser parser object + * @param context context masks + * @param kind + * @param type Binding kind + * @param inGroup + */ +export function parseMethodFormals( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + kind: PropertyKind, + type: BindingKind, + inGroup: 0 | 1 +): ESTree.Parameter[] { + // FormalParameter[Yield,GeneratorParameter] : + // BindingElement[?Yield, ?GeneratorParameter] + + consume(parser, context, Token.LeftParen); + + const params: (ESTree.AssignmentPattern | ESTree.Parameter)[] = []; + + parser.flags = (parser.flags | Flags.SimpleParameterList) ^ Flags.SimpleParameterList; + + if (parser.token === Token.RightParen) { + if (kind & PropertyKind.Setter) { + report(parser, Errors.AccessorWrongArgs, 'Setter', 'one', ''); + } + nextToken(parser, context); + return params; + } + + if (kind & PropertyKind.Getter) { + report(parser, Errors.AccessorWrongArgs, 'Getter', 'no', 's'); + } + if (kind & PropertyKind.Setter && parser.token === Token.Ellipsis) { + report(parser, Errors.BadSetterRestParameter); + } + + context = (context | Context.DisallowIn) ^ Context.DisallowIn; + + let setterArgs = 0; + let isSimpleParameterList: 0 | 1 = 0; + + while (parser.token !== Token.Comma) { + let left = null; + const { tokenPos, linePos, colPos } = parser; + + if (parser.token & Token.IsIdentifier) { + if ((context & Context.Strict) < 1) { + if ((parser.token & Token.FutureReserved) === Token.FutureReserved) { + parser.flags |= Flags.HasStrictReserved; + } + + if ((parser.token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments) { + parser.flags |= Flags.StrictEvalArguments; + } + } + + left = parseAndClassifyIdentifier( + parser, + context, + scope, + kind | BindingKind.ArgumentList, + Origin.None, + tokenPos, + linePos, + colPos + ); + } else { + if (parser.token === Token.LeftBrace) { + left = parseObjectLiteralOrPattern( + parser, + context, + scope, + 1, + inGroup, + 1, + type, + Origin.None, + tokenPos, + linePos, + colPos + ); + } else if (parser.token === Token.LeftBracket) { + left = parseArrayExpressionOrPattern( + parser, + context, + scope, + 1, + inGroup, + 1, + type, + Origin.None, + tokenPos, + linePos, + colPos + ); + } else if (parser.token === Token.Ellipsis) { + left = parseSpreadOrRestElement( + parser, + context, + scope, + Token.RightParen, + type, + Origin.None, + 0, + inGroup, + 1, + tokenPos, + linePos, + colPos + ); + } + + isSimpleParameterList = 1; + + if (parser.destructible & (DestructuringKind.Assignable | DestructuringKind.CannotDestruct)) + report(parser, Errors.InvalidBindingDestruct); + } + + if (parser.token === Token.Assign) { + nextToken(parser, context | Context.AllowRegExp); + + isSimpleParameterList = 1; + + const right = parseExpression(parser, context, 1, 1, 0, parser.tokenPos, parser.linePos, parser.colPos); + + left = finishNode(parser, context, tokenPos, linePos, colPos, { + type: 'AssignmentPattern', + left: left as ESTree.BindingPattern | ESTree.Identifier, + right + }); + } + + setterArgs++; + + params.push(left as any); + + if (!consumeOpt(parser, context, Token.Comma)) break; + if (parser.token === Token.RightParen) { + // allow the trailing comma + break; + } + } + + if (kind & PropertyKind.Setter && setterArgs !== 1) { + report(parser, Errors.AccessorWrongArgs, 'Setter', 'one', ''); + } + + if (scope && scope.scopeError !== void 0) reportScopeError(scope.scopeError); + + if (isSimpleParameterList) parser.flags |= Flags.SimpleParameterList; + + consume(parser, context, Token.RightParen); + + return params; +} + +/** + * Parse computed property name + * + * @param parser Parser object + * @param context Context masks + */ +export function parseComputedPropertyName(parser: ParserState, context: Context, inGroup: 0 | 1): ESTree.Expression { + // ComputedPropertyName : + // [ AssignmentExpression ] + nextToken(parser, context | Context.AllowRegExp); + const key = parseExpression( + parser, + (context | Context.DisallowIn) ^ Context.DisallowIn, + 1, + 0, + inGroup, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + consume(parser, context, Token.RightBracket); + return key; +} + +/** + * Parses an expression which has been parenthesised, or arrow head + * + * @param parser Parser object + * @param context Context masks + * @param assignable + * @param start Start index + * @param line Start line + * @param column Start of column + */ +export function parseParenthesizedExpression( + parser: ParserState, + context: Context, + canAssign: 0 | 1, + kind: BindingKind, + origin: Origin, + start: number, + line: number, + column: number +): any { + parser.flags = (parser.flags | Flags.SimpleParameterList) ^ Flags.SimpleParameterList; + + const { tokenPos: piStart, linePos: plStart, colPos: pcStart } = parser; + + nextToken(parser, context | Context.AllowRegExp | Context.AllowEscapedKeyword); + + const scope = context & Context.OptionsLexical ? addChildScope(createScope(), ScopeKind.ArrowParams) : void 0; + + context = (context | Context.DisallowIn) ^ Context.DisallowIn; + + if (consumeOpt(parser, context, Token.RightParen)) { + // Not valid expression syntax, but this is valid in an arrow function + // with no params: `() => body`. + return parseParenthesizedArrow(parser, context, scope, [], canAssign, 0, start, line, column); + } + + let destructible: AssignmentKind | DestructuringKind = 0; + + parser.destructible &= ~(DestructuringKind.Yield | DestructuringKind.Await); + + let expr; + let expressions: ESTree.Expression[] = []; + let isSequence: 0 | 1 = 0; + let isSimpleParameterList: 0 | 1 = 0; + + const { tokenPos: iStart, linePos: lStart, colPos: cStart } = parser; + + parser.assignable = AssignmentKind.Assignable; + + while (parser.token !== Token.RightParen) { + const { token, tokenPos, linePos, colPos } = parser; + + if (token & (Token.IsIdentifier | Token.Keyword)) { + if (scope) addBlockName(parser, context, scope, parser.tokenValue, BindingKind.ArgumentList, Origin.None); + + expr = parsePrimaryExpression(parser, context, kind, 0, 1, 0, 1, 1, tokenPos, linePos, colPos); + + if (parser.token === Token.RightParen || parser.token === Token.Comma) { + if (parser.assignable & AssignmentKind.CannotAssign) { + destructible |= DestructuringKind.CannotDestruct; + isSimpleParameterList = 1; + } else if ( + (token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments || + (token & Token.FutureReserved) === Token.FutureReserved + ) { + isSimpleParameterList = 1; + } + } else { + if (parser.token === Token.Assign) { + isSimpleParameterList = 1; + } else { + destructible |= DestructuringKind.CannotDestruct; + } + + expr = parseMemberOrUpdateExpression(parser, context, expr, /* inGroup */ 1, 0, tokenPos, linePos, colPos); + + if (parser.token !== Token.RightParen && parser.token !== Token.Comma) { + expr = parseAssignmentExpression(parser, context, 1, 0, tokenPos, linePos, colPos, expr); + } + } + } else if ((token & Token.IsPatternStart) === Token.IsPatternStart) { + expr = + token === Token.LeftBrace + ? parseObjectLiteralOrPattern( + parser, + context | Context.AllowEscapedKeyword, + scope, + 0, + 1, + 0, + kind, + origin, + tokenPos, + linePos, + colPos + ) + : parseArrayExpressionOrPattern( + parser, + context | Context.AllowEscapedKeyword, + scope, + 0, + 1, + 0, + kind, + origin, + tokenPos, + linePos, + colPos + ); + + destructible |= parser.destructible; + + isSimpleParameterList = 1; + + parser.assignable = AssignmentKind.CannotAssign; + + if (parser.token !== Token.RightParen && parser.token !== Token.Comma) { + if (destructible & DestructuringKind.HasToDestruct) report(parser, Errors.InvalidPatternTail); + + expr = parseMemberOrUpdateExpression(parser, context, expr, 0, 0, tokenPos, linePos, colPos); + + destructible |= DestructuringKind.CannotDestruct; + + if (parser.token !== Token.RightParen && parser.token !== Token.Comma) { + expr = parseAssignmentExpression(parser, context, 0, 0, tokenPos, linePos, colPos, expr); + } + } + } else if (token === Token.Ellipsis) { + expr = parseSpreadOrRestElement( + parser, + context, + scope, + Token.RightParen, + kind, + origin, + 0, + 1, + 0, + tokenPos, + linePos, + colPos + ); + + if (parser.destructible & DestructuringKind.CannotDestruct) report(parser, Errors.InvalidRestArg); + + isSimpleParameterList = 1; + + if (isSequence && (parser.token === Token.RightParen || parser.token === Token.Comma)) { + expressions.push(expr); + } + destructible |= DestructuringKind.HasToDestruct; + break; + } else { + destructible |= DestructuringKind.CannotDestruct; + + expr = parseExpression(parser, context, 1, 0, 1, tokenPos, linePos, colPos); + + if (isSequence && (parser.token === Token.RightParen || parser.token === Token.Comma)) { + expressions.push(expr); + } + + if (parser.token === Token.Comma) { + if (!isSequence) { + isSequence = 1; + expressions = [expr]; + } + } + + if (isSequence) { + while (consumeOpt(parser, context | Context.AllowRegExp, Token.Comma)) { + expressions.push(parseExpression(parser, context, 1, 0, 1, parser.tokenPos, parser.linePos, parser.colPos)); + } + + parser.assignable = AssignmentKind.CannotAssign; + + expr = finishNode(parser, context, iStart, lStart, cStart, { + type: 'SequenceExpression', + expressions + }); + } + + consume(parser, context, Token.RightParen); + + parser.destructible = destructible; + + return expr; + } + + if (isSequence && (parser.token === Token.RightParen || parser.token === Token.Comma)) { + expressions.push(expr); + } + + if (!consumeOpt(parser, context | Context.AllowRegExp, Token.Comma)) break; + + if (!isSequence) { + isSequence = 1; + expressions = [expr]; + } + + if (parser.token === Token.RightParen) { + destructible |= DestructuringKind.HasToDestruct; + break; + } + } + + if (isSequence) { + parser.assignable = AssignmentKind.CannotAssign; + + expr = finishNode(parser, context, iStart, lStart, cStart, { + type: 'SequenceExpression', + expressions + }); + } + + consume(parser, context, Token.RightParen); + + if (destructible & DestructuringKind.CannotDestruct && destructible & DestructuringKind.HasToDestruct) + report(parser, Errors.CantAssignToValidRHS); + + destructible |= + parser.destructible & DestructuringKind.Yield + ? DestructuringKind.Yield + : 0 | (parser.destructible & DestructuringKind.Await) + ? DestructuringKind.Await + : 0; + + if (parser.token === Token.Arrow) { + if (destructible & (DestructuringKind.Assignable | DestructuringKind.CannotDestruct)) + report(parser, Errors.InvalidArrowDestructLHS); + if (context & (Context.InAwaitContext | Context.Module) && destructible & DestructuringKind.Await) + report(parser, Errors.AwaitInParameter); + if (context & (Context.Strict | Context.InYieldContext) && destructible & DestructuringKind.Yield) { + report(parser, Errors.YieldInParameter); + } + if (isSimpleParameterList) parser.flags |= Flags.SimpleParameterList; + return parseParenthesizedArrow( + parser, + context, + scope, + isSequence ? expressions : [expr], + canAssign, + 0, + start, + line, + column + ); + } else if (destructible & DestructuringKind.HasToDestruct) { + report(parser, Errors.UncompleteArrow); + } + + parser.destructible = ((parser.destructible | DestructuringKind.Yield) ^ DestructuringKind.Yield) | destructible; + + return context & Context.OptionsPreserveParens + ? finishNode(parser, context, piStart, plStart, pcStart, { + type: 'ParenthesizedExpression', + expression: expr + }) + : expr; +} + +/** + * Parses either an identifier or an arrow function + * + * @param parser Parser object + * @param context Context masks + * @param start Start index + * @param line Start line + * @param column Start of column + + */ +export function parseIdentifierOrArrow( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.Identifier | ESTree.ArrowFunctionExpression { + const { tokenValue } = parser; + + const expr = parseIdentifier(parser, context, 0); + parser.assignable = AssignmentKind.Assignable; + if (parser.token === Token.Arrow) { + let scope: ScopeState | undefined = void 0; + + if (context & Context.OptionsLexical) scope = createArrowHeadParsingScope(parser, context, tokenValue); + + parser.flags = (parser.flags | Flags.SimpleParameterList) ^ Flags.SimpleParameterList; + + return parseArrowFunctionExpression(parser, context, scope, [expr], /* isAsync */ 0, start, line, column); + } + return expr; +} + +/** + * Parse arrow function expression + * + * @param parser Parser object + * @param context Context masks + * @param params + * @param isAsync + * @param start Start index + * @param line Start line + * @param column Start of column + */ +function parseArrowFromIdentifier( + parser: ParserState, + context: Context, + value: any, + expr: ESTree.Expression, + inNew: 0 | 1, + canAssign: 0 | 1, + isAsync: 0 | 1, + start: number, + line: number, + column: number +): ESTree.ArrowFunctionExpression { + if (!canAssign) report(parser, Errors.InvalidAssignmentTarget); + if (inNew) report(parser, Errors.InvalidAsyncArrow); + parser.flags &= ~Flags.SimpleParameterList; + const scope = context & Context.OptionsLexical ? createArrowHeadParsingScope(parser, context, value) : void 0; + + return parseArrowFunctionExpression(parser, context, scope, [expr], isAsync, start, line, column); +} + +/** + * Parse arrow function expression + * + * @param parser Parser object + * @param context Context masks + * @param params + * @param isAsync + * @param start Start index + * @param line Start line + * @param column Start of column + */ +function parseParenthesizedArrow( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + params: any, + canAssign: 0 | 1, + isAsync: 0 | 1, + start: number, + line: number, + column: number +): ESTree.ArrowFunctionExpression { + if (!canAssign) report(parser, Errors.InvalidAssignmentTarget); + + for (let i = 0; i < params.length; ++i) reinterpretToPattern(parser, params[i]); + + return parseArrowFunctionExpression(parser, context, scope, params, isAsync, start, line, column); +} + +/** + * Parse arrow function expression + * + * @param parser Parser object + * @param context Context masks + * @param params + * @param isAsync + * @param start Start index + * @param line Start line + * @param column Start of column + */ +export function parseArrowFunctionExpression( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + params: any, + isAsync: 0 | 1, + start: number, + line: number, + column: number +): ESTree.ArrowFunctionExpression { + /** + * ArrowFunction : + * ArrowParameters => ConciseBody + * + * ArrowParameters : + * BindingIdentifer + * CoverParenthesizedExpressionAndArrowParameterList + * + * CoverParenthesizedExpressionAndArrowParameterList : + * ( Expression ) + * ( ) + * ( ... BindingIdentifier ) + * ( Expression , ... BindingIdentifier ) + * + * ConciseBody : + * [lookahead not {] AssignmentExpression + * { FunctionBody } + * + */ + + if (parser.flags & Flags.NewLine) report(parser, Errors.InvalidLineBreak); + + consume(parser, context | Context.AllowRegExp, Token.Arrow); + + context = ((context | 0b0000000111100000000_0000_00000000) ^ 0b0000000111100000000_0000_00000000) | (isAsync << 22); + + const expression = parser.token !== Token.LeftBrace; + + let body: ESTree.BlockStatement | ESTree.Expression; + + if (scope && scope.scopeError !== void 0) { + reportScopeError(scope.scopeError); + } + + if (expression) { + // Single-expression body + body = parseExpression(parser, context, 1, 0, 0, parser.tokenPos, parser.linePos, parser.colPos); + } else { + if (scope) scope = addChildScope(scope, ScopeKind.FunctionBody); + + body = parseFunctionBody( + parser, + (context | 0b0001000000000000001_0000_00000000 | Context.InGlobal | Context.InClass) ^ + (0b0001000000000000001_0000_00000000 | Context.InGlobal | Context.InClass), + scope, + Origin.Arrow, + void 0, + void 0 + ); + + switch (parser.token) { + case Token.LeftBracket: + if ((parser.flags & Flags.NewLine) < 1) { + report(parser, Errors.InvalidInvokedBlockBodyArrow); + } + break; + case Token.Period: + case Token.TemplateSpan: + case Token.QuestionMark: + report(parser, Errors.InvalidAccessedBlockBodyArrow); + case Token.LeftParen: + if ((parser.flags & Flags.NewLine) < 1) { + report(parser, Errors.InvalidInvokedBlockBodyArrow); + } + parser.flags |= Flags.DisallowCall; + break; + default: // ignore + } + if ((parser.token & Token.IsBinaryOp) === Token.IsBinaryOp && (parser.flags & Flags.NewLine) < 1) + report(parser, Errors.UnexpectedToken, KeywordDescTable[parser.token & Token.Type]); + if ((parser.token & Token.IsUpdateOp) === Token.IsUpdateOp) report(parser, Errors.InvalidArrowPostfix); + } + + parser.assignable = AssignmentKind.CannotAssign; + + return finishNode(parser, context, start, line, column, { + type: 'ArrowFunctionExpression', + params, + body, + async: isAsync === 1, + expression + }); +} + +/** + * Parses formal parameters + * + * @param parser Parser object + * @param context Context masks + */ +export function parseFormalParametersOrFormalList( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + inGroup: 0 | 1, + kind: BindingKind +): ESTree.Parameter[] { + /** + * FormalParameter : + * BindingElement + * + * FormalParameterList : + * [empty] + * FunctionRestParameter + * FormalsList + * FormalsList , FunctionRestParameter + * + * FunctionRestParameter : + * ... BindingIdentifier + * + * FormalsList : + * FormalParameter + * FormalsList , FormalParameter + * + * FormalParameter : + * BindingElement + * + * BindingElement : + * SingleNameBinding + * BindingPattern Initializeropt + * + */ + consume(parser, context, Token.LeftParen); + + parser.flags = (parser.flags | Flags.SimpleParameterList) ^ Flags.SimpleParameterList; + + const params: ESTree.Parameter[] = []; + + if (consumeOpt(parser, context, Token.RightParen)) return params; + + context = (context | Context.DisallowIn) ^ Context.DisallowIn; + + let isSimpleParameterList: 0 | 1 = 0; + + while (parser.token !== Token.Comma) { + let left: any; + + const { tokenPos, linePos, colPos } = parser; + + if (parser.token & Token.IsIdentifier) { + if ((context & Context.Strict) < 1) { + if ((parser.token & Token.FutureReserved) === Token.FutureReserved) { + parser.flags |= Flags.HasStrictReserved; + } + if ((parser.token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments) { + parser.flags |= Flags.StrictEvalArguments; + } + } + + left = parseAndClassifyIdentifier( + parser, + context, + scope, + kind | BindingKind.ArgumentList, + Origin.None, + tokenPos, + linePos, + colPos + ); + } else { + if (parser.token === Token.LeftBrace) { + left = parseObjectLiteralOrPattern( + parser, + context, + scope, + 1, + inGroup, + 1, + kind, + Origin.None, + tokenPos, + linePos, + colPos + ); + } else if (parser.token === Token.LeftBracket) { + left = parseArrayExpressionOrPattern( + parser, + context, + scope, + 1, + inGroup, + 1, + kind, + Origin.None, + tokenPos, + linePos, + colPos + ); + } else if (parser.token === Token.Ellipsis) { + left = parseSpreadOrRestElement( + parser, + context, + scope, + Token.RightParen, + kind, + Origin.None, + 0, + inGroup, + 1, + tokenPos, + linePos, + colPos + ); + } else { + report(parser, Errors.UnexpectedToken, KeywordDescTable[parser.token & Token.Type]); + } + + isSimpleParameterList = 1; + + if (parser.destructible & (DestructuringKind.Assignable | DestructuringKind.CannotDestruct)) { + report(parser, Errors.InvalidBindingDestruct); + } + } + + if (parser.token === Token.Assign) { + nextToken(parser, context | Context.AllowRegExp); + + isSimpleParameterList = 1; + + const right = parseExpression(parser, context, 1, 1, inGroup, parser.tokenPos, parser.linePos, parser.colPos); + + left = finishNode(parser, context, tokenPos, linePos, colPos, { + type: 'AssignmentPattern', + left, + right + }); + } + + params.push(left); + + if (!consumeOpt(parser, context, Token.Comma)) break; + if (parser.token === Token.RightParen) { + // allow the trailing comma + break; + } + } + + if (isSimpleParameterList) parser.flags |= Flags.SimpleParameterList; + + if (scope && (isSimpleParameterList || context & Context.Strict) && scope.scopeError !== void 0) { + reportScopeError(scope.scopeError); + } + + consume(parser, context, Token.RightParen); + + return params; +} + +/** + * Parses member or update expression without call expression + * + * @param parser Parser object + * @param context Context masks + * @param expr ESTree AST node + * @param inGroup + * @param start + * @param line + * @param column + */ +export function parseMembeExpressionNoCall( + parser: ParserState, + context: Context, + expr: ESTree.Expression, + inGroup: 0 | 1, + start: number, + line: number, + column: number +): any { + const { token } = parser; + + if (token & Token.IsMemberOrCallExpression) { + /* Property */ + if (token === Token.Period) { + nextToken(parser, context | Context.AllowEscapedKeyword); + + parser.assignable = AssignmentKind.Assignable; + + const property = parsePropertyOrPrivatePropertyName(parser, context); + + return parseMembeExpressionNoCall( + parser, + context, + finishNode(parser, context, start, line, column, { + type: 'MemberExpression', + object: expr, + computed: false, + property + }), + 0, + start, + line, + column + ); + /* Property */ + } else if (token === Token.LeftBracket) { + nextToken(parser, context | Context.AllowRegExp); + + const { tokenPos, linePos, colPos } = parser; + + const property = parseExpressions(parser, context, inGroup, 1, tokenPos, linePos, colPos); + + consume(parser, context, Token.RightBracket); + + parser.assignable = AssignmentKind.Assignable; + + return parseMembeExpressionNoCall( + parser, + context, + finishNode(parser, context, start, line, column, { + type: 'MemberExpression', + object: expr, + computed: true, + property + }), + 0, + start, + line, + column + ); + /* Template */ + } else if (token === Token.TemplateContinuation || token === Token.TemplateSpan) { + parser.assignable = AssignmentKind.CannotAssign; + + return parseMembeExpressionNoCall( + parser, + context, + finishNode(parser, context, start, line, column, { + type: 'TaggedTemplateExpression', + tag: expr, + quasi: + parser.token === Token.TemplateContinuation + ? parseTemplate(parser, context | Context.TaggedTemplate) + : parseTemplateLiteral(parser, context, parser.tokenPos, parser.linePos, parser.colPos) + }), + 0, + start, + line, + column + ); + } + } + return expr; +} + +/** + * Parses new or new target expression + * + * @param parser Parser object + * @param context Context masks + * @returns {(ESTree.Expression | ESTree.MetaProperty)} + */ +export function parseNewExpression( + parser: ParserState, + context: Context, + inGroup: 0 | 1, + start: number, + line: number, + column: number +): ESTree.NewExpression | ESTree.Expression | ESTree.MetaProperty { + // NewExpression :: + // ('new')+ MemberExpression + // + // NewTarget :: + // 'new' '.' 'target' + // + // Examples of new expression: + // - new foo.bar().baz + // - new foo()() + // - new new foo()() + // - new new foo + // - new new foo() + // - new new foo().bar().baz + // - `new.target[await x]` + // - `new (foo);` + // - `new (foo)();` + // - `new foo()();` + // - `new (await foo);` + // - `new x(await foo);` + const id = parseIdentifier(parser, context | Context.AllowRegExp, 0); + const { tokenPos, linePos, colPos } = parser; + + if (consumeOpt(parser, context, Token.Period)) { + if (context & Context.AllowNewTarget && parser.token === Token.Target) { + parser.assignable = AssignmentKind.CannotAssign; + return parseMetaProperty(parser, context, id, start, line, column); + } + + report(parser, Errors.InvalidNewTarget); + } + + parser.assignable = AssignmentKind.CannotAssign; + + if ((parser.token & Token.IsUnaryOp) === Token.IsUnaryOp) { + report(parser, Errors.InvalidNewUnary, KeywordDescTable[parser.token & Token.Type]); + } + + const expr = parsePrimaryExpression( + parser, + context, + BindingKind.Empty, + 1, + 0, + 0, + inGroup, + 1, + tokenPos, + linePos, + colPos + ); + + context = (context | Context.DisallowIn) ^ Context.DisallowIn; + + if (parser.token === Token.QuestionMarkPeriod) report(parser, Errors.OptionalChainingNoNew); + + // NewExpression without arguments. + const callee = parseMembeExpressionNoCall(parser, context, expr, inGroup, tokenPos, linePos, colPos); + + parser.assignable = AssignmentKind.CannotAssign; + + return finishNode(parser, context, start, line, column, { + type: 'NewExpression', + callee, + arguments: parser.token === Token.LeftParen ? parseArguments(parser, context, inGroup) : [] + }); +} + +/** + * Parse meta property + * + * @see [Link](https://tc39.github.io/ecma262/#prod-StatementList) + * + * @param parser Parser object + * @param context Context masks + * @param meta ESTree AST node + */ +export function parseMetaProperty( + parser: ParserState, + context: Context, + meta: ESTree.Identifier, + start: number, + line: number, + column: number +): ESTree.MetaProperty { + const property = parseIdentifier(parser, context, 0); + return finishNode(parser, context, start, line, column, { + type: 'MetaProperty', + meta, + property + }); +} + +/** + * Parses async expression + * + * @param parser Parser object + * @param context Context masks + * @param inNew + * @param assignable + */ + +/** + * Parses async arrow after identifier + * + * @param parser Parser object + * @param context Context masks + * @param canAssign Either true or false + * @param start Start pos of node + * @param line Line pos of node + * @param column Column pos of node + */ +function parseAsyncArrowAfterIdent( + parser: ParserState, + context: Context, + canAssign: 0 | 1, + start: number, + line: number, + column: number +) { + if (parser.token === Token.AwaitKeyword) report(parser, Errors.AwaitInParameter); + + if (context & (Context.Strict | Context.InYieldContext) && parser.token === Token.YieldKeyword) { + report(parser, Errors.YieldInParameter); + } + + if ((parser.token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments) { + parser.flags |= Flags.StrictEvalArguments; + } + + return parseArrowFromIdentifier( + parser, + context, + parser.tokenValue, + parseIdentifier(parser, context, 0), + 0, + canAssign, + 1, + start, + line, + column + ); +} + +/** + * Parses async arrow or call expressions + * + * @param parser Parser object + * @param context Context masks + * @param callee ESTree AST node + * @param assignable + * @param kind Binding kind + * @param origin Binding origin + * @param flags Mutual parser flags + * @param start Start pos of node + * @param line Line pos of node + * @param column Column pos of node + */ + +export function parseAsyncArrowOrCallExpression( + parser: ParserState, + context: Context, + callee: ESTree.Identifier | void, + canAssign: 0 | 1, + kind: BindingKind, + origin: Origin, + flags: Flags, + start: number, + line: number, + column: number +): ESTree.CallExpression | ESTree.ArrowFunctionExpression { + nextToken(parser, context | Context.AllowRegExp); + + const scope = context & Context.OptionsLexical ? addChildScope(createScope(), ScopeKind.ArrowParams) : void 0; + + context = (context | Context.DisallowIn) ^ Context.DisallowIn; + + if (consumeOpt(parser, context, Token.RightParen)) { + if (parser.token === Token.Arrow) { + if (flags & Flags.NewLine) report(parser, Errors.InvalidLineBreak); + return parseParenthesizedArrow(parser, context, scope, [], canAssign, 1, start, line, column); + } + + return finishNode(parser, context, start, line, column, { + type: 'CallExpression', + callee, + arguments: [] + }); + } + + let destructible: AssignmentKind | DestructuringKind = 0; + let expr: ESTree.Expression | null = null; + let isSimpleParameterList: 0 | 1 = 0; + + parser.destructible = + (parser.destructible | DestructuringKind.Yield | DestructuringKind.Await) ^ + (DestructuringKind.Yield | DestructuringKind.Await); + + const params: ESTree.Expression[] = []; + + while (parser.token !== Token.RightParen) { + const { token, tokenPos, linePos, colPos } = parser; + + if (token & (Token.IsIdentifier | Token.Keyword)) { + if (scope) addBlockName(parser, context, scope, parser.tokenValue, kind, Origin.None); + + expr = parsePrimaryExpression(parser, context, kind, 0, 1, 0, 1, 1, tokenPos, linePos, colPos); + + if (parser.token === Token.RightParen || parser.token === Token.Comma) { + if (parser.assignable & AssignmentKind.CannotAssign) { + destructible |= DestructuringKind.CannotDestruct; + isSimpleParameterList = 1; + } else if ((token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments) { + parser.flags |= Flags.StrictEvalArguments; + } else if ((token & Token.FutureReserved) === Token.FutureReserved) { + parser.flags |= Flags.HasStrictReserved; + } + } else { + if (parser.token === Token.Assign) { + isSimpleParameterList = 1; + } else { + destructible |= DestructuringKind.CannotDestruct; + } + + expr = parseMemberOrUpdateExpression( + parser, + context, + expr as ESTree.Expression, + 1, + 0, + tokenPos, + linePos, + colPos + ); + + if (parser.token !== Token.RightParen && parser.token !== Token.Comma) { + expr = parseAssignmentExpression(parser, context, 1, 0, tokenPos, linePos, colPos, expr as ESTree.Expression); + } + } + } else if (token & Token.IsPatternStart) { + expr = + token === Token.LeftBrace + ? parseObjectLiteralOrPattern(parser, context, scope, 0, 1, 0, kind, origin, tokenPos, linePos, colPos) + : parseArrayExpressionOrPattern(parser, context, scope, 0, 1, 0, kind, origin, tokenPos, linePos, colPos); + + destructible |= parser.destructible; + + isSimpleParameterList = 1; + + if (parser.token !== Token.RightParen && parser.token !== Token.Comma) { + if (destructible & DestructuringKind.HasToDestruct) report(parser, Errors.InvalidPatternTail); + + expr = parseMemberOrUpdateExpression(parser, context, expr, 0, 0, tokenPos, linePos, colPos); + + destructible |= DestructuringKind.CannotDestruct; + + if ((parser.token & Token.IsBinaryOp) === Token.IsBinaryOp) { + expr = parseBinaryExpression(parser, context, 1, start, line, column, 4, token, expr as ESTree.Expression); + } + if (consumeOpt(parser, context | Context.AllowRegExp, Token.QuestionMark)) { + expr = parseConditionalExpression(parser, context, expr as ESTree.Expression, start, line, column); + } + } + } else if (token === Token.Ellipsis) { + expr = parseSpreadOrRestElement( + parser, + context, + scope, + Token.RightParen, + kind, + origin, + 1, + 1, + 0, + tokenPos, + linePos, + colPos + ); + + destructible |= (parser.token === Token.RightParen ? 0 : DestructuringKind.CannotDestruct) | parser.destructible; + + isSimpleParameterList = 1; + } else { + expr = parseExpression(parser, context, 1, 0, 0, tokenPos, linePos, colPos); + + destructible = parser.assignable; + + params.push(expr); + + while (consumeOpt(parser, context | Context.AllowRegExp, Token.Comma)) { + params.push(parseExpression(parser, context, 1, 0, 0, tokenPos, linePos, colPos)); + } + + destructible |= parser.assignable; + + consume(parser, context, Token.RightParen); + + parser.destructible = destructible | DestructuringKind.CannotDestruct; + + parser.assignable = AssignmentKind.CannotAssign; + + return finishNode(parser, context, start, line, column, { + type: 'CallExpression', + callee, + arguments: params + }); + } + + params.push(expr as ESTree.Expression); + + if (!consumeOpt(parser, context | Context.AllowRegExp, Token.Comma)) break; + } + + consume(parser, context, Token.RightParen); + + destructible |= + parser.destructible & DestructuringKind.Yield + ? DestructuringKind.Yield + : 0 | (parser.destructible & DestructuringKind.Await) + ? DestructuringKind.Await + : 0; + + if (parser.token === Token.Arrow) { + if (destructible & (DestructuringKind.Assignable | DestructuringKind.CannotDestruct)) + report(parser, Errors.InvalidLHSAsyncArrow); + if (parser.flags & Flags.NewLine || flags & Flags.NewLine) report(parser, Errors.InvalidLineBreak); + if (destructible & DestructuringKind.Await) report(parser, Errors.AwaitInParameter); + if (context & (Context.Strict | Context.InYieldContext) && destructible & DestructuringKind.Yield) + report(parser, Errors.YieldInParameter); + if (isSimpleParameterList) parser.flags |= Flags.SimpleParameterList; + + return parseParenthesizedArrow(parser, context, scope, params, canAssign, 1, start, line, column); + } else if (destructible & DestructuringKind.HasToDestruct) { + report(parser, Errors.InvalidShorthandPropInit); + } + + parser.assignable = AssignmentKind.CannotAssign; + + return finishNode(parser, context, start, line, column, { + type: 'CallExpression', + callee, + arguments: params + }); +} + +/** + * Parse regular expression literal + * + * @see [Link](https://tc39.github.io/ecma262/#sec-literals-regular-expression-literals) + * + * @param parser Parser object + * @param context Context masks + */ + +/** + * Parses reguar expression literal AST node + * + * @param parser Parser object + * @param context Context masks + */ +export function parseRegExpLiteral( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.RegExpLiteral { + const { tokenRaw, tokenRegExp, tokenValue } = parser; + nextToken(parser, context); + parser.assignable = AssignmentKind.CannotAssign; + return context & Context.OptionsRaw + ? finishNode(parser, context, start, line, column, { + type: 'Literal', + value: tokenValue, + regex: tokenRegExp as { pattern: string; flags: string }, + raw: tokenRaw + }) + : finishNode(parser, context, start, line, column, { + type: 'Literal', + value: tokenValue, + regex: tokenRegExp as { pattern: string; flags: string } + }); +} + +/** + * Parse class expression + * + * @param parser Parser object + * @param context Context masks + * @param ExportDefault + */ +export function parseClassDeclaration( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + flags: HoistedClassFlags, + start: number, + line: number, + column: number +): ESTree.ClassDeclaration { + // ClassDeclaration :: + // 'class' Identifier ('extends' LeftHandSideExpression)? '{' ClassBody '}' + // 'class' ('extends' LeftHandSideExpression)? '{' ClassBody '}' + // DecoratorList[?Yield, ?Await]optclassBindingIdentifier[?Yield, ?Await]ClassTail[?Yield, ?Await] + // DecoratorList[?Yield, ?Await]optclassClassTail[?Yield, ?Await] + // + context = (context | Context.InConstructor | Context.Strict) ^ Context.InConstructor; + + let decorators = parseDecorators(parser, context); + if (decorators.length) { + start = parser.tokenPos; + line = parser.linePos; + column = parser.colPos; + } + + if (parser.leadingDecorators.length) { + parser.leadingDecorators.push(...decorators); + decorators = parser.leadingDecorators; + parser.leadingDecorators = []; + } + + nextToken(parser, context); + let id: ESTree.Expression | null = null; + let superClass: ESTree.Expression | null = null; + + const { tokenValue } = parser; + + if (parser.token & Token.Keyword && parser.token !== Token.ExtendsKeyword) { + if (isStrictReservedWord(parser, context, parser.token)) { + report(parser, Errors.UnexpectedStrictReserved); + } + + if ((parser.token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments) { + report(parser, Errors.StrictEvalArguments); + } + + if (scope) { + // A named class creates a new lexical scope with a const binding of the + // class name for the "inner name". + addBlockName(parser, context, scope, tokenValue, BindingKind.Class, Origin.None); + + if (flags) { + if (flags & HoistedClassFlags.Export) { + declareUnboundVariable(parser, tokenValue); + } + } + } + + id = parseIdentifier(parser, context, 0); + } else { + // Only under the "export default" context, class declaration does not require the class name. + // + // ExportDeclaration: + // ... + // export default ClassDeclaration[~Yield, +Default] + // ... + // + // ClassDeclaration[Yield, Default]: + // ... + // [+Default] class ClassTail[?Yield] + // + if ((flags & HoistedClassFlags.Hoisted) < 1) report(parser, Errors.DeclNoName, 'Class'); + } + let inheritedContext = context; + + if (consumeOpt(parser, context | Context.AllowRegExp, Token.ExtendsKeyword)) { + superClass = parseLeftHandSideExpression(parser, context, 0, 0, 0, parser.tokenPos, parser.linePos, parser.colPos); + inheritedContext |= Context.SuperCall; + } else { + inheritedContext = (inheritedContext | Context.SuperCall) ^ Context.SuperCall; + } + + const body = parseClassBody(parser, inheritedContext, context, scope, BindingKind.Empty, Origin.Declaration, 0); + + return finishNode( + parser, + context, + start, + line, + column, + context & Context.OptionsNext + ? { + type: 'ClassDeclaration', + id, + superClass, + decorators, + body + } + : { + type: 'ClassDeclaration', + id, + superClass, + body + } + ); +} + +/** + * Parse class expression + * + * @param parser Parser object + * @param context Context masks + */ +export function parseClassExpression( + parser: ParserState, + context: Context, + inGroup: 0 | 1, + start: number, + line: number, + column: number +): ESTree.ClassExpression { + // ClassExpression :: + // 'class' Identifier ('extends' LeftHandSideExpression)? '{' ClassBody '}' + // 'class' ('extends' LeftHandSideExpression)? '{' ClassBody '}' + // DecoratorList[?Yield, ?Await]optclassBindingIdentifier[?Yield, ?Await]ClassTail[?Yield, ?Await] + // + + let id: ESTree.Expression | null = null; + let superClass: ESTree.Expression | null = null; + + // All class code is always strict mode implicitly + context = (context | Context.Strict | Context.InConstructor) ^ Context.InConstructor; + + const decorators = parseDecorators(parser, context); + if (decorators.length) { + start = parser.tokenPos; + line = parser.linePos; + column = parser.colPos; + } + + nextToken(parser, context); + + if (parser.token & Token.Keyword && parser.token !== Token.ExtendsKeyword) { + if (isStrictReservedWord(parser, context, parser.token)) report(parser, Errors.UnexpectedStrictReserved); + if ((parser.token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments) { + report(parser, Errors.StrictEvalArguments); + } + + id = parseIdentifier(parser, context, 0); + } + + // Second set of context masks to fix 'super' edge cases + let inheritedContext = context; + + if (consumeOpt(parser, context | Context.AllowRegExp, Token.ExtendsKeyword)) { + superClass = parseLeftHandSideExpression( + parser, + context, + 0, + inGroup, + 0, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + inheritedContext |= Context.SuperCall; + } else { + inheritedContext = (inheritedContext | Context.SuperCall) ^ Context.SuperCall; + } + + const body = parseClassBody(parser, inheritedContext, context, void 0, BindingKind.Empty, Origin.None, inGroup); + + parser.assignable = AssignmentKind.CannotAssign; + + return finishNode( + parser, + context, + start, + line, + column, + context & Context.OptionsNext + ? { + type: 'ClassExpression', + id, + superClass, + decorators, + body + } + : { + type: 'ClassExpression', + id, + superClass, + body + } + ); +} + +/** + * Parses a list of decorators + * + * @param parser Parser object + * @param context Context masks + */ +export function parseDecorators(parser: ParserState, context: Context): ESTree.Decorator[] { + const list: ESTree.Decorator[] = []; + + if (context & Context.OptionsNext) { + while (parser.token === Token.Decorator) { + list.push(parseDecoratorList(parser, context, parser.tokenPos, parser.linePos, parser.colPos)); + } + } + + return list; +} + +/** + * Parses a list of decorators + * + * @param parser Parser object + * @param context Context masks + * @param start + */ + +export function parseDecoratorList( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.Decorator { + nextToken(parser, context | Context.AllowRegExp); + + let expression = parsePrimaryExpression(parser, context, BindingKind.Empty, 0, 1, 0, 0, 1, start, line, column); + + expression = parseMemberOrUpdateExpression(parser, context, expression, 0, 0, start, line, column); + + return finishNode(parser, context, start, line, column, { + type: 'Decorator', + expression + }); +} +/** + * Parses class body + * + * @param parser Parser object + * @param context Context masks + * @param inheritedContext Second set of context masks + * @param type Binding kind + * @param origin Binding origin + * @param decorators + */ + +export function parseClassBody( + parser: ParserState, + context: Context, + inheritedContext: Context, + scope: ScopeState | undefined, + kind: BindingKind, + origin: Origin, + inGroup: 0 | 1 +): ESTree.ClassBody { + /** + * ClassElement : + * static MethodDefinition + * MethodDefinition + * DecoratorList + * DecoratorList static MethodDefinition + * DecoratorList PropertyDefinition + * DecoratorList static PropertyDefinition + * + * MethodDefinition : + * ClassElementName ( FormalParameterList ) { FunctionBody } + * get ClassElementName ( ) { FunctionBody } + * set ClassElementName ( PropertySetParameterList ) { FunctionBody } + * + * ClassElementName : + * PropertyName + * PrivateIdentifier + * + * PrivateIdentifier :: + * # IdentifierName + * + * IdentifierName :: + * IdentifierStart + * IdentifierName IdentifierPart + * + * IdentifierStart :: + * UnicodeIDStart + * $ + * _ + * \ UnicodeEscapeSequence + * IdentifierPart:: + * UnicodeIDContinue + * $ + * \ UnicodeEscapeSequence + * <ZWNJ> <ZWJ> + * + * UnicodeIDStart:: + * any Unicode code point with the Unicode property "ID_Start" + * + * UnicodeIDContinue:: + * any Unicode code point with the Unicode property "ID_Continue" + * + * GeneratorMethod : + * * ClassElementName ( UniqueFormalParameters ){GeneratorBody} + * + * AsyncMethod : + * async [no LineTerminator here] ClassElementName ( UniqueFormalParameters ) { AsyncFunctionBody } + * + * AsyncGeneratorMethod : + * async [no LineTerminator here]* ClassElementName ( UniqueFormalParameters ) { AsyncGeneratorBody } + */ + + const { tokenPos, linePos, colPos } = parser; + + consume(parser, context | Context.AllowRegExp, Token.LeftBrace); + context = (context | Context.DisallowIn) ^ Context.DisallowIn; + parser.flags = (parser.flags | Flags.HasConstructor) ^ Flags.HasConstructor; + + const body: (ESTree.MethodDefinition | ESTree.PropertyDefinition)[] = []; + let decorators: ESTree.Decorator[]; + + while (parser.token !== Token.RightBrace) { + let length = 0; + + // See: https://github.com/tc39/proposal-decorators + + decorators = parseDecorators(parser, context); + + length = decorators.length; + + if (length > 0 && parser.tokenValue === 'constructor') { + report(parser, Errors.GeneratorConstructor); + } + + if (parser.token === Token.RightBrace) report(parser, Errors.TrailingDecorators); + + if (consumeOpt(parser, context, Token.Semicolon)) { + if (length > 0) report(parser, Errors.InvalidDecoratorSemicolon); + continue; + } + body.push( + parseClassElementList( + parser, + context, + scope, + inheritedContext, + kind, + decorators, + 0, + inGroup, + parser.tokenPos, + parser.linePos, + parser.colPos + ) + ); + } + consume(parser, origin & Origin.Declaration ? context | Context.AllowRegExp : context, Token.RightBrace); + + return finishNode(parser, context, tokenPos, linePos, colPos, { + type: 'ClassBody', + body + }); +} + +/** + * Parses class element list + * + * @param parser Parser object + * @param context Context masks + * @param inheritedContext Second set of context masks + * @param type Binding type + * @param decorators + * @param isStatic + */ +function parseClassElementList( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + inheritedContext: Context, + type: BindingKind, + decorators: ESTree.Decorator[], + isStatic: 0 | 1, + inGroup: 0 | 1, + start: number, + line: number, + column: number +): ESTree.MethodDefinition | ESTree.PropertyDefinition { + let kind: PropertyKind = isStatic ? PropertyKind.Static : PropertyKind.None; + let key: ESTree.Expression | ESTree.PrivateIdentifier | null = null; + + const { token, tokenPos, linePos, colPos } = parser; + + if (token & (Token.IsIdentifier | Token.FutureReserved)) { + key = parseIdentifier(parser, context, 0); + + switch (token) { + case Token.StaticKeyword: + if (!isStatic && parser.token !== Token.LeftParen) { + return parseClassElementList( + parser, + context, + scope, + inheritedContext, + type, + decorators, + 1, + inGroup, + start, + line, + column + ); + } + break; + + case Token.AsyncKeyword: + if (parser.token !== Token.LeftParen && (parser.flags & Flags.NewLine) < 1) { + if (context & Context.OptionsNext && (parser.token & Token.IsClassField) === Token.IsClassField) { + return parsePropertyDefinition(parser, context, key, kind, decorators, tokenPos, linePos, colPos); + } + + kind |= PropertyKind.Async | (optionalBit(parser, context, Token.Multiply) ? PropertyKind.Generator : 0); + } + break; + + case Token.GetKeyword: + if (parser.token !== Token.LeftParen) { + if (context & Context.OptionsNext && (parser.token & Token.IsClassField) === Token.IsClassField) { + return parsePropertyDefinition(parser, context, key, kind, decorators, tokenPos, linePos, colPos); + } + kind |= PropertyKind.Getter; + } + break; + + case Token.SetKeyword: + if (parser.token !== Token.LeftParen) { + if (context & Context.OptionsNext && (parser.token & Token.IsClassField) === Token.IsClassField) { + return parsePropertyDefinition(parser, context, key, kind, decorators, tokenPos, linePos, colPos); + } + kind |= PropertyKind.Setter; + } + break; + + default: // ignore + } + } else if (token === Token.LeftBracket) { + kind |= PropertyKind.Computed; + key = parseComputedPropertyName(parser, inheritedContext, inGroup); + } else if ((token & Token.IsStringOrNumber) === Token.IsStringOrNumber) { + key = parseLiteral(parser, context); + } else if (token === Token.Multiply) { + kind |= PropertyKind.Generator; + nextToken(parser, context); // skip: '*' + } else if (context & Context.OptionsNext && parser.token === Token.PrivateField) { + kind |= PropertyKind.PrivateField; + key = parsePrivateIdentifier(parser, context, tokenPos, linePos, colPos); + context = context | Context.InClass; + } else if (context & Context.OptionsNext && (parser.token & Token.IsClassField) === Token.IsClassField) { + kind |= PropertyKind.ClassField; + context = context | Context.InClass; + } else if (token === Token.EscapedFutureReserved) { + key = parseIdentifier(parser, context, 0); + if (parser.token !== Token.LeftParen) + report(parser, Errors.UnexpectedToken, KeywordDescTable[parser.token & Token.Type]); + } else { + report(parser, Errors.UnexpectedToken, KeywordDescTable[parser.token & Token.Type]); + } + + if (kind & (PropertyKind.Generator | PropertyKind.Async | PropertyKind.GetSet)) { + if (parser.token & Token.IsIdentifier) { + key = parseIdentifier(parser, context, 0); + } else if ((parser.token & Token.IsStringOrNumber) === Token.IsStringOrNumber) { + key = parseLiteral(parser, context); + } else if (parser.token === Token.LeftBracket) { + kind |= PropertyKind.Computed; + key = parseComputedPropertyName(parser, context, /* inGroup */ 0); + } else if (parser.token === Token.EscapedFutureReserved) { + key = parseIdentifier(parser, context, 0); + } else if (context & Context.OptionsNext && parser.token === Token.PrivateField) { + kind |= PropertyKind.PrivateField; + key = parsePrivateIdentifier(parser, context, tokenPos, linePos, colPos); + } else report(parser, Errors.InvalidKeyToken); + } + + if ((kind & PropertyKind.Computed) < 1) { + if (parser.tokenValue === 'constructor') { + if ((parser.token & Token.IsClassField) === Token.IsClassField) { + report(parser, Errors.InvalidClassFieldConstructor); + } else if ((kind & PropertyKind.Static) < 1 && parser.token === Token.LeftParen) { + if (kind & (PropertyKind.GetSet | PropertyKind.Async | PropertyKind.ClassField | PropertyKind.Generator)) { + report(parser, Errors.InvalidConstructor, 'accessor'); + } else if ((context & Context.SuperCall) < 1) { + if (parser.flags & Flags.HasConstructor) report(parser, Errors.DuplicateConstructor); + else parser.flags |= Flags.HasConstructor; + } + } + kind |= PropertyKind.Constructor; + } else if ( + // Static Async Generator Private Methods can be named "#prototype" (class declaration) + (kind & PropertyKind.PrivateField) < 1 && + kind & (PropertyKind.Static | PropertyKind.GetSet | PropertyKind.Generator | PropertyKind.Async) && + parser.tokenValue === 'prototype' + ) { + report(parser, Errors.StaticPrototype); + } + } + + if (context & Context.OptionsNext && parser.token !== Token.LeftParen) { + return parsePropertyDefinition(parser, context, key, kind, decorators, tokenPos, linePos, colPos); + } + + const value = parseMethodDefinition(parser, context, kind, inGroup, parser.tokenPos, parser.linePos, parser.colPos); + + return finishNode( + parser, + context, + start, + line, + column, + context & Context.OptionsNext + ? { + type: 'MethodDefinition', + kind: + (kind & PropertyKind.Static) < 1 && kind & PropertyKind.Constructor + ? 'constructor' + : kind & PropertyKind.Getter + ? 'get' + : kind & PropertyKind.Setter + ? 'set' + : 'method', + static: (kind & PropertyKind.Static) > 0, + computed: (kind & PropertyKind.Computed) > 0, + key, + decorators, + value + } + : { + type: 'MethodDefinition', + kind: + (kind & PropertyKind.Static) < 1 && kind & PropertyKind.Constructor + ? 'constructor' + : kind & PropertyKind.Getter + ? 'get' + : kind & PropertyKind.Setter + ? 'set' + : 'method', + static: (kind & PropertyKind.Static) > 0, + computed: (kind & PropertyKind.Computed) > 0, + key, + value + } + ); +} + +/** + * Parses private name + * + * @param parser Parser object + * @param context Context masks + */ +function parsePrivateIdentifier( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.PrivateIdentifier { + // PrivateIdentifier:: + // #IdentifierName + nextToken(parser, context); // skip: '#' + const { tokenValue } = parser; + if (tokenValue === 'constructor') report(parser, Errors.InvalidStaticClassFieldConstructor); + nextToken(parser, context); + + return finishNode(parser, context, start, line, column, { + type: 'PrivateIdentifier', + name: tokenValue + }); +} + +/** + * Parses field definition + * + * @param parser Parser object + * @param context Context masks + * @param key ESTree AST node + * @param state + * @param decorators + */ + +export function parsePropertyDefinition( + parser: ParserState, + context: Context, + key: ESTree.PrivateIdentifier | ESTree.Expression | null, + state: PropertyKind, + decorators: ESTree.Decorator[] | null, + start: number, + line: number, + column: number +): ESTree.PropertyDefinition { + // ClassElement : + // MethodDefinition + // static MethodDefinition + // PropertyDefinition ; + // ; + let value: ESTree.Expression | null = null; + + if (state & PropertyKind.Generator) report(parser, Errors.Unexpected); + + if (parser.token === Token.Assign) { + nextToken(parser, context | Context.AllowRegExp); + + const { tokenPos, linePos, colPos } = parser; + + if (parser.token === Token.Arguments) report(parser, Errors.StrictEvalArguments); + + value = parsePrimaryExpression( + parser, + context | Context.InClass, + BindingKind.Empty, + 0, + 1, + 0, + 0, + 1, + tokenPos, + linePos, + colPos + ); + + if ((parser.token & Token.IsClassField) !== Token.IsClassField) { + value = parseMemberOrUpdateExpression( + parser, + context | Context.InClass, + value as ESTree.Expression, + 0, + 0, + tokenPos, + linePos, + colPos + ); + + value = parseAssignmentExpression(parser, context | Context.InClass, 0, 0, tokenPos, linePos, colPos, value); + if (parser.token === Token.Comma) { + value = parseSequenceExpression(parser, context, 0, start, line, column, value as any); + } + } + } + + return finishNode(parser, context, start, line, column, { + type: 'PropertyDefinition', + key, + value, + static: (state & PropertyKind.Static) > 0, + computed: (state & PropertyKind.Computed) > 0, + decorators + } as any); +} + +/** + * Parses binding pattern + * + * @param parser Parser object + * @param context Context masks + * @param type Binding kind + */ +export function parseBindingPattern( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + type: BindingKind, + origin: Origin, + start: number, + line: number, + column: number +): ESTree.BindingPattern { + // Pattern :: + // Identifier + // ArrayLiteral + // ObjectLiteral + + if (parser.token & Token.IsIdentifier) + return parseAndClassifyIdentifier(parser, context, scope, type, origin, start, line, column); + + if ((parser.token & Token.IsPatternStart) !== Token.IsPatternStart) + report(parser, Errors.UnexpectedToken, KeywordDescTable[parser.token & Token.Type]); + + const left: any = + parser.token === Token.LeftBracket + ? parseArrayExpressionOrPattern(parser, context, scope, 1, 0, 1, type, origin, start, line, column) + : parseObjectLiteralOrPattern(parser, context, scope, 1, 0, 1, type, origin, start, line, column); + + if (parser.destructible & DestructuringKind.CannotDestruct) report(parser, Errors.InvalidBindingDestruct); + + if (parser.destructible & DestructuringKind.Assignable) report(parser, Errors.InvalidBindingDestruct); + + return left; +} + +/** + * Classify and parse identifier if of valid type + * + * @param parser Parser object + * @param context Context masks + * @param type Binding kind + */ +function parseAndClassifyIdentifier( + parser: ParserState, + context: Context, + scope: ScopeState | undefined, + kind: BindingKind, + origin: Origin, + start: number, + line: number, + column: number +): ESTree.Identifier { + const { tokenValue, token } = parser; + + if (context & Context.Strict) { + if ((token & Token.IsEvalOrArguments) === Token.IsEvalOrArguments) { + report(parser, Errors.StrictEvalArguments); + } else if ((token & Token.FutureReserved) === Token.FutureReserved) { + report(parser, Errors.UnexpectedStrictReserved); + } + } + + if ((token & Token.Reserved) === Token.Reserved) { + report(parser, Errors.KeywordNotId); + } + + if (context & (Context.Module | Context.InYieldContext) && token === Token.YieldKeyword) { + report(parser, Errors.YieldInParameter); + } + if (token === Token.LetKeyword) { + if (kind & (BindingKind.Let | BindingKind.Const)) report(parser, Errors.InvalidLetConstBinding); + } + if (context & (Context.InAwaitContext | Context.Module) && token === Token.AwaitKeyword) { + report(parser, Errors.AwaitOutsideAsync); + } + + nextToken(parser, context); + + if (scope) addVarOrBlock(parser, context, scope, tokenValue, kind, origin); + + return finishNode(parser, context, start, line, column, { + type: 'Identifier', + name: tokenValue + }); +} + +/** + * Parses either a JSX element or JSX Fragment + * + * @param parser Parser object + * @param context Context masks + * @param inJSXChild + * @param start + * @param line + * @param column + */ + +function parseJSXRootElementOrFragment( + parser: ParserState, + context: Context, + inJSXChild: 0 | 1, + start: number, + line: number, + column: number +): ESTree.JSXElement | ESTree.JSXFragment { + nextToken(parser, context); + + // JSX fragments + if (parser.token === Token.GreaterThan) { + return finishNode(parser, context, start, line, column, { + type: 'JSXFragment', + openingFragment: parseOpeningFragment(parser, context, start, line, column), + children: parseJSXChildren(parser, context), + closingFragment: parseJSXClosingFragment( + parser, + context, + inJSXChild, + parser.tokenPos, + parser.linePos, + parser.colPos + ) + }); + } + + let closingElement: ESTree.JSXClosingElement | null = null; + let children: ESTree.JSXChild[] = []; + + const openingElement: ESTree.JSXOpeningElement = parseJSXOpeningFragmentOrSelfCloseElement( + parser, + context, + inJSXChild, + start, + line, + column + ); + + if (!openingElement.selfClosing) { + children = parseJSXChildren(parser, context); + closingElement = parseJSXClosingElement( + parser, + context, + inJSXChild, + parser.tokenPos, + parser.linePos, + parser.colPos + ); + const close = isEqualTagName(closingElement.name); + if (isEqualTagName(openingElement.name) !== close) report(parser, Errors.ExpectedJSXClosingTag, close); + } + + return finishNode(parser, context, start, line, column, { + type: 'JSXElement', + children, + openingElement, + closingElement + }); +} + +/** + * Parses JSX opening fragment + * + * @param parser Parser object + * @param context Context masks + * @param start + * @param line + * @param column + */ +export function parseOpeningFragment( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.JSXOpeningFragment { + scanJSXToken(parser, context); + return finishNode(parser, context, start, line, column, { + type: 'JSXOpeningFragment' + }); +} + +/** + * Parses JSX Closing element + * + * @param parser Parser object + * @param context Context masks + * @param inJSXChild + * @param start + * @param line + * @param column + */ +function parseJSXClosingElement( + parser: ParserState, + context: Context, + inJSXChild: 0 | 1, + start: number, + line: number, + column: number +): ESTree.JSXClosingElement { + consume(parser, context, Token.JSXClose); + const name = parseJSXElementName(parser, context, parser.tokenPos, parser.linePos, parser.colPos); + if (inJSXChild) { + consume(parser, context, Token.GreaterThan); + } else { + parser.token = scanJSXToken(parser, context); + } + + return finishNode(parser, context, start, line, column, { + type: 'JSXClosingElement', + name + }); +} + +/** + * Parses JSX closing fragment + * + * @param parser Parser object + * @param context Context masks + * @param inJSXChild + * @param start + * @param line + * @param column + */ +export function parseJSXClosingFragment( + parser: ParserState, + context: Context, + inJSXChild: 0 | 1, + start: number, + line: number, + column: number +): ESTree.JSXClosingFragment { + consume(parser, context, Token.JSXClose); + + if (inJSXChild) { + consume(parser, context, Token.GreaterThan); + } else { + consume(parser, context, Token.GreaterThan); + } + + return finishNode(parser, context, start, line, column, { + type: 'JSXClosingFragment' + }); +} + +/** + * Parses JSX children + * + * @param parser Parser object + * @param context Context masks + */ +export function parseJSXChildren(parser: ParserState, context: Context): ESTree.JSXChild[] { + const children: ESTree.JSXChild[] = []; + while (parser.token !== Token.JSXClose) { + parser.index = parser.tokenPos = parser.startPos; + parser.column = parser.colPos = parser.startColumn; + parser.line = parser.linePos = parser.startLine; + scanJSXToken(parser, context); + children.push(parseJSXChild(parser, context, parser.tokenPos, parser.linePos, parser.colPos)); + } + return children; +} + +/** + * Parses a JSX child node + * + * @param parser Parser object + * @param context Context masks + * @param start + * @param line + * @param column + */ +function parseJSXChild(parser: ParserState, context: Context, start: number, line: number, column: number): any { + if (parser.token === Token.JSXText) return parseJSXText(parser, context, start, line, column); + if (parser.token === Token.LeftBrace) + return parseJSXExpressionContainer(parser, context, /*inJSXChild*/ 0, /* isAttr */ 0, start, line, column); + if (parser.token === Token.LessThan) + return parseJSXRootElementOrFragment(parser, context, /*inJSXChild*/ 0, start, line, column); + report(parser, Errors.Unexpected); +} + +/** + * Parses JSX Text + * + * @param parser Parser object + * @param context Context masks + * @param start + * @param line + * @param column + */ +export function parseJSXText( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.JSXText { + scanJSXToken(parser, context); + + const node = { + type: 'JSXText', + value: parser.tokenValue as string + } as ESTree.JSXText; + + if (context & Context.OptionsRaw) { + node.raw = parser.tokenRaw; + } + + return finishNode(parser, context, start, line, column, node); +} + +/** + * Parses either a JSX element, JSX Fragment or JSX self close element + * + * @param parser Parser object + * @param context Context masks + * @param inJSXChild + * @param start + * @param line + * @param column + */ +function parseJSXOpeningFragmentOrSelfCloseElement( + parser: ParserState, + context: Context, + inJSXChild: 0 | 1, + start: number, + line: number, + column: number +): ESTree.JSXOpeningElement { + if ((parser.token & Token.IsIdentifier) !== Token.IsIdentifier && (parser.token & Token.Keyword) !== Token.Keyword) + report(parser, Errors.Unexpected); + + const tagName = parseJSXElementName(parser, context, parser.tokenPos, parser.linePos, parser.colPos); + const attributes = parseJSXAttributes(parser, context); + const selfClosing = parser.token === Token.Divide; + + if (parser.token === Token.GreaterThan) { + scanJSXToken(parser, context); + } else { + consume(parser, context, Token.Divide); + if (inJSXChild) { + consume(parser, context, Token.GreaterThan); + } else { + scanJSXToken(parser, context); + } + } + + return finishNode(parser, context, start, line, column, { + type: 'JSXOpeningElement', + name: tagName, + attributes, + selfClosing + }); +} + +/** + * Parses JSX element name + * + * @param parser Parser object + * @param context Context masks + * @param start + * @param line + * @param column + */ +function parseJSXElementName( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.JSXIdentifier | ESTree.JSXMemberExpression | ESTree.JSXNamespacedName { + scanJSXIdentifier(parser); + + let key: ESTree.JSXIdentifier | ESTree.JSXMemberExpression = parseJSXIdentifier(parser, context, start, line, column); + + // Namespace + if (parser.token === Token.Colon) return parseJSXNamespacedName(parser, context, key, start, line, column); + + // Member expression + while (consumeOpt(parser, context, Token.Period)) { + scanJSXIdentifier(parser); + key = parseJSXMemberExpression(parser, context, key, start, line, column); + } + return key; +} + +/** + * Parses JSX member expression + * + * @param parser Parser object + * @param context Context masks + * @param start + * @param line + * @param column + */ +export function parseJSXMemberExpression( + parser: ParserState, + context: Context, + object: ESTree.JSXIdentifier | ESTree.JSXMemberExpression, + start: number, + line: number, + column: number +): ESTree.JSXMemberExpression { + const property = parseJSXIdentifier(parser, context, parser.tokenPos, parser.linePos, parser.colPos); + return finishNode(parser, context, start, line, column, { + type: 'JSXMemberExpression', + object, + property + }); +} + +/** + * Parses JSX attributes + * + * @param parser Parser object + * @param context Context masks + * @param start + * @param line + * @param column + */ +export function parseJSXAttributes( + parser: ParserState, + context: Context +): (ESTree.JSXAttribute | ESTree.JSXSpreadAttribute)[] { + const attributes: (ESTree.JSXAttribute | ESTree.JSXSpreadAttribute)[] = []; + while (parser.token !== Token.Divide && parser.token !== Token.GreaterThan && parser.token !== Token.EOF) { + attributes.push(parseJsxAttribute(parser, context, parser.tokenPos, parser.linePos, parser.colPos)); + } + return attributes; +} + +/** + * Parses JSX Spread attribute + * + * @param parser Parser object + * @param context Context masks + * @param start + * @param line + * @param column + */ +export function parseJSXSpreadAttribute( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.JSXSpreadAttribute { + nextToken(parser, context); // skips: '{' + consume(parser, context, Token.Ellipsis); + const expression = parseExpression(parser, context, 1, 0, 0, parser.tokenPos, parser.linePos, parser.colPos); + consume(parser, context, Token.RightBrace); + return finishNode(parser, context, start, line, column, { + type: 'JSXSpreadAttribute', + argument: expression + }); +} + +/** + * Parses JSX attribute + * + * @param parser Parser object + * @param context Context masks + * @param start + * @param line + * @param column + */ +function parseJsxAttribute( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.JSXAttribute | ESTree.JSXSpreadAttribute { + if (parser.token === Token.LeftBrace) return parseJSXSpreadAttribute(parser, context, start, line, column); + scanJSXIdentifier(parser); + let value: ESTree.JSXAttributeValue | null = null; + let name: ESTree.JSXNamespacedName | ESTree.JSXIdentifier = parseJSXIdentifier(parser, context, start, line, column); + + if (parser.token === Token.Colon) { + name = parseJSXNamespacedName(parser, context, name, start, line, column); + } + + // HTML empty attribute + if (parser.token === Token.Assign) { + const token = scanJSXAttributeValue(parser, context); + const { tokenPos, linePos, colPos } = parser; + switch (token) { + case Token.StringLiteral: + value = parseLiteral(parser, context); + break; + case Token.LessThan: + value = parseJSXRootElementOrFragment(parser, context, /*inJSXChild*/ 1, tokenPos, linePos, colPos); + break; + case Token.LeftBrace: + value = parseJSXExpressionContainer(parser, context, 1, 1, tokenPos, linePos, colPos); + break; + default: + report(parser, Errors.InvalidJSXAttributeValue); + } + } + + return finishNode(parser, context, start, line, column, { + type: 'JSXAttribute', + value, + name + }); +} + +/** + * Parses JSX namespace name + * + * @param parser Parser object + * @param context Context masks + * @param namespace + * @param start + * @param line + * @param column + */ + +function parseJSXNamespacedName( + parser: ParserState, + context: Context, + namespace: ESTree.JSXIdentifier | ESTree.JSXMemberExpression, + start: number, + line: number, + column: number +): ESTree.JSXNamespacedName { + consume(parser, context, Token.Colon); + const name = parseJSXIdentifier(parser, context, parser.tokenPos, parser.linePos, parser.colPos); + return finishNode(parser, context, start, line, column, { + type: 'JSXNamespacedName', + namespace, + name + }); +} + +/** + * Parses JSX Expression container + * + * @param parser Parser object + * @param context Context masks + * @param inJSXChild + * @param start + * @param line + * @param column + */ +function parseJSXExpressionContainer( + parser: ParserState, + context: Context, + inJSXChild: 0 | 1, + isAttr: 0 | 1, + start: number, + line: number, + column: number +): ESTree.JSXExpressionContainer | ESTree.JSXSpreadChild { + nextToken(parser, context); + const { tokenPos, linePos, colPos } = parser; + if (parser.token === Token.Ellipsis) return parseJSXSpreadChild(parser, context, tokenPos, linePos, colPos); + + let expression: ESTree.Expression | ESTree.JSXEmptyExpression | null = null; + + if (parser.token === Token.RightBrace) { + // JSX attributes must only be assigned a non-empty 'expression' + if (isAttr) report(parser, Errors.InvalidNonEmptyJSXExpr); + expression = parseJSXEmptyExpression(parser, context, parser.startPos, parser.startLine, parser.startColumn); + } else { + expression = parseExpression(parser, context, 1, 0, 0, tokenPos, linePos, colPos); + } + if (inJSXChild) { + consume(parser, context, Token.RightBrace); + } else { + scanJSXToken(parser, context); + } + + return finishNode(parser, context, start, line, column, { + type: 'JSXExpressionContainer', + expression + }); +} + +/** + * Parses JSX spread child + * + * @param parser Parser object + * @param context Context masks + * @param start + * @param line + * @param column + */ +function parseJSXSpreadChild( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.JSXSpreadChild { + consume(parser, context, Token.Ellipsis); + const expression = parseExpression(parser, context, 1, 0, 0, parser.tokenPos, parser.linePos, parser.colPos); + consume(parser, context, Token.RightBrace); + return finishNode(parser, context, start, line, column, { + type: 'JSXSpreadChild', + expression + }); +} + +/** + * Parses JSX empty expression + * + * @param parser Parser object + * @param context Context masks + * @param start + * @param line + * @param column + */ +function parseJSXEmptyExpression( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.JSXEmptyExpression { + // Since " }" is treated as single token, we have to artificially break + // it into " " and "}". + // Move token start from beginning of whitespace(s) to beginning of "}", + // so JSXEmptyExpression can have correct end loc. + parser.startPos = parser.tokenPos; + parser.startLine = parser.linePos; + parser.startColumn = parser.colPos; + return finishNode(parser, context, start, line, column, { + type: 'JSXEmptyExpression' + }); +} + +/** + * Parses JSX Identifier + * + * @param parser Parser object + * @param context Context masks + * @param start + * @param line + * @param column + */ +export function parseJSXIdentifier( + parser: ParserState, + context: Context, + start: number, + line: number, + column: number +): ESTree.JSXIdentifier { + const { tokenValue } = parser; + nextToken(parser, context); + + return finishNode(parser, context, start, line, column, { + type: 'JSXIdentifier', + name: tokenValue + }); +} |