     1→import { type AssignmentKind, type DestructuringKind, Flags, type Location } from '../common';
     2→import { Errors, ParseError } from '../errors';
     3→import type * as ESTree from '../estree';
     4→import { convertTokenType } from '../lexer';
     5→import { type NormalizedOptions, normalizeOptions, type OnComment, type OnToken, type Options } from '../options';
     6→import { Token } from '../token';
     7→import { PrivateScope } from './private-scope';
     8→import { Scope, type ScopeKind } from './scope';
     9→
    10→export class Parser {
    11→  private lastOnToken: [string, number, number, ESTree.SourceLocation] | null = null;
    12→
    13→  options: NormalizedOptions;
    14→
    15→  token = Token.EOF;
    16→  /**
    17→   * The mutable parser flags, in case any flags need passed by reference.
    18→   */
    19→  flags = Flags.None;
    20→  /**
    21→   * The current index
    22→   */
    23→  index = 0;
    24→  /**
    25→   * Beginning of current line
    26→   */
    27→  line = 1;
    28→
    29→  /**
    30→   * Beginning of current column
    31→   */
    32→  column = 0;
    33→
    34→  /**
    35→   * Start position of whitespace/comment before current token
    36→   */
    37→  startIndex = 0;
    38→
    39→  /**
    40→   * The end of the source code
    41→   */
    42→  end = 0;
    43→
    44→  /**
    45→   * Start position of text of current token
    46→   */
    47→  tokenIndex = 0;
    48→
    49→  /**
    50→   * Start position of the column before newline
    51→   */
    52→  startColumn = 0;
    53→
    54→  /**
    55→   * Position in the input code of the first character after the last newline
    56→   */
    57→  tokenColumn = 0;
    58→
    59→  /**
    60→   * The number of newlines
    61→   */
    62→  tokenLine = 1;
    63→
    64→  /**
    65→   * Start position of text of current token
    66→   */
    67→  startLine = 1;
    68→
    69→  /**
    70→   * Holds the scanned token value
    71→   */
    72→  tokenValue: any = '';
    73→
    74→  /**
    75→   * Holds the raw text that have been scanned by the lexer
    76→   */
    77→  tokenRaw = '';
    78→
    79→  /**
    80→   * Holds the regExp info text that have been collected by the lexer
    81→   */
    82→  tokenRegExp: void | {
    83→    pattern: string;
    84→    flags: string;
    85→  } = void 0;
    86→
    87→  /**
    88→   * The code point at the current index
    89→   */
    90→  currentChar = 0;
    91→
    92→  /**
    93→   *  https://tc39.es/ecma262/#sec-module-semantics-static-semantics-exportednames
    94→   */
    95→  exportedNames = new Set<string>();
    96→
    97→  /**
    98→   * https://tc39.es/ecma262/#sec-exports-static-semantics-exportedbindings
    99→   */
   100→
   101→  exportedBindings = new Set<string>();
   102→
   103→  /**
   104→   * Assignable state
   105→   */
   106→  assignable: AssignmentKind | DestructuringKind = 1;
   107→
   108→  /**
   109→   * Destructuring state
   110→   */
   111→  destructible: AssignmentKind | DestructuringKind = 0;
   112→
   113→  /**
   114→   * Holds leading decorators before "export" or "class" keywords
   115→   */
   116→  leadingDecorators: {
   117→    start?: Location;
   118→    decorators: ESTree.Decorator[];
   119→  } = { decorators: [] };
   120→
   121→  constructor(
   122→    /**
   123→     * The source code to be parsed
   124→     */
   125→    public readonly source: string,
   126→    rawOptions: Options = {},
   127→  ) {
   128→    this.end = source.length;
   129→    this.currentChar = source.charCodeAt(0);
   130→    this.options = normalizeOptions(rawOptions);
   131→
   132→    // Accepts either a callback function to be invoked or an array to collect comments (as the node is constructed)
   133→    if (Array.isArray(this.options.onComment)) {
   134→      this.options.onComment = pushComment(this.options.onComment, this.options);
   135→    }
   136→
   137→    // Accepts either a callback function to be invoked or an array to collect tokens
   138→    if (Array.isArray(this.options.onToken)) {
   139→      this.options.onToken = pushToken(this.options.onToken, this.options);
   140→    }
   141→  }
   142→
   143→  /**
   144→   * Get the current token in the stream to consume
   145→   * This function exists as workaround for TS issue
   146→   * https://github.com/microsoft/TypeScript/issues/9998
   147→   */
   148→  getToken() {
   149→    return this.token;
   150→  }
   151→
   152→  /**
   153→   * Set the current token in the stream to consume
   154→   * This function exists as workaround for TS issue
   155→   * https://github.com/microsoft/TypeScript/issues/9998
   156→   */
   157→  setToken(value: Token, replaceLast = false) {
   158→    this.token = value;
   159→
   160→    const { onToken } = this.options;
   161→
   162→    if (onToken) {
   163→      if (value !== Token.EOF) {
   164→        const loc = {
   165→          start: {
   166→            line: this.tokenLine,
   167→            column: this.tokenColumn,
   168→          },
   169→          end: {
   170→            line: this.line,
   171→            column: this.column,
   172→          },
   173→        };
   174→
   175→        if (!replaceLast && this.lastOnToken) {
   176→          onToken(...this.lastOnToken);
   177→        }
   178→        this.lastOnToken = [convertTokenType(value), this.tokenIndex, this.index, loc];
   179→      } else {
   180→        if (this.lastOnToken) {
   181→          onToken(...this.lastOnToken);
   182→          this.lastOnToken = null;
   183→        }
   184→      }
   185→    }
   186→    return value;
   187→  }
   188→
   189→  get tokenStart(): Location {
   190→    return {
   191→      index: this.tokenIndex,
   192→      line: this.tokenLine,
   193→      column: this.tokenColumn,
   194→    };
   195→  }
   196→
   197→  get currentLocation(): Location {
   198→    return { index: this.index, line: this.line, column: this.column };
   199→  }
   200→
   201→  finishNode<T extends ESTree.Node>(node: T, start: Location, end: Location | void): T {
   202→    if (this.options.ranges) {
   203→      node.start = start.index;
   204→      const endIndex = end ? end.index : this.startIndex;
   205→      node.end = endIndex;
   206→      node.range = [start.index, endIndex];
   207→    }
   208→
   209→    if (this.options.loc) {
   210→      node.loc = {
   211→        start: {
   212→          line: start.line,
   213→          column: start.column,
   214→        },
   215→        end: end ? { line: end.line, column: end.column } : { line: this.startLine, column: this.startColumn },
   216→      };
   217→
   218→      if (this.options.source) {
   219→        node.loc.source = this.options.source;
   220→      }
   221→    }
   222→
   223→    return node;
   224→  }
   225→
   226→  /**
   227→   * Appends a name to the `ExportedBindings` of the `ExportsList`,
   228→   *
   229→   * @see [Link](https://tc39.es/ecma262/$sec-exports-static-semantics-exportedbindings)
   230→   *
   231→   * @param name Exported binding name
   232→   */
   233→  addBindingToExports(name: string): void {
   234→    this.exportedBindings.add(name);
   235→  }
   236→
   237→  /**
   238→   * Appends a name to the `ExportedNames` of the `ExportsList`, and checks
   239→   * for duplicates
   240→   *
   241→   * @see [Link](https://tc39.github.io/ecma262/$sec-exports-static-semantics-exportednames)
   242→   *
   243→   * @param name Exported name
   244→   */
   245→  declareUnboundVariable(name: string): void {
   246→    const { exportedNames } = this;
   247→
   248→    if (exportedNames.has(name)) {
   249→      this.report(Errors.DuplicateExportBinding, name);
   250→    }
   251→
   252→    exportedNames.add(name);
   253→  }
   254→
   255→  /**
   256→   * Throws an error
   257→   *
   258→   * @export
   259→   * @param {Errors} type
   260→   * @param {...string[]} params
   261→   * @returns {never}
   262→   */
   263→  report(type: Errors, ...params: string[]): never {
   264→    throw new ParseError(this.tokenStart, this.currentLocation, type, ...params);
   265→  }
   266→
   267→  createScopeIfLexical(type?: ScopeKind, parent?: Scope) {
   268→    if (this.options.lexical) {
   269→      return this.createScope(type, parent);
   270→    }
   271→
   272→    return undefined;
   273→  }
   274→
   275→  createScope(type?: ScopeKind, parent?: Scope) {
   276→    return new Scope(this, type, parent);
   277→  }
   278→
   279→  createPrivateScopeIfLexical(parent?: PrivateScope) {
   280→    if (this.options.lexical) {
   281→      return new PrivateScope(this, parent);
   282→    }
   283→
   284→    return undefined;
   285→  }
   286→
   287→  cloneIdentifier(original: ESTree.Identifier): ESTree.Identifier {
   288→    return this.cloneLocationInformation({ ...original }, original);
   289→  }
   290→
   291→  cloneStringLiteral(original: ESTree.StringLiteral): ESTree.StringLiteral {
   292→    return this.cloneLocationInformation({ ...original }, original);
   293→  }
   294→
   295→  private cloneLocationInformation<T extends ESTree.Node>(node: T, original: T) {
   296→    if (this.options.ranges) {
   297→      node.range = [...original.range!];
   298→    }
   299→
   300→    if (this.options.loc) {
   301→      node.loc = {
   302→        ...original.loc,
   303→        start: { ...original.loc!.start },
   304→        end: { ...original.loc!.end },
   305→      };
   306→    }
   307→
   308→    return node;
   309→  }
   310→}
   311→
   312→function pushComment(comments: ESTree.Comment[], options: NormalizedOptions): OnComment {
   313→  return function (type: ESTree.CommentType, value: string, start: number, end: number, loc: ESTree.SourceLocation) {
   314→    const comment: ESTree.Comment = {
   315→      type,
   316→      value,
   317→    };
   318→
   319→    if (options.ranges) {
   320→      comment.start = start;
   321→      comment.end = end;
   322→      comment.range = [start, end];
   323→    }
   324→    if (options.loc) {
   325→      comment.loc = loc;
   326→    }
   327→    comments.push(comment);
   328→  };
   329→}
   330→
   331→function pushToken(tokens: Token[], options: NormalizedOptions): OnToken {
   332→  return function (type: string, start: number, end: number, loc: ESTree.SourceLocation) {
   333→    const token: any = {
   334→      token: type,
   335→    };
   336→
   337→    if (options.ranges) {
   338→      token.start = start;
   339→      token.end = end;
   340→      token.range = [start, end];
   341→    }
   342→
   343→    if (options.loc) {
   344→      token.loc = loc;
   345→    }
   346→    tokens.push(token);
   347→  };
   348→}
