1 import {reservedWords, keywords} from "./identifier.js"
2 import {types as tt} from "./tokentype.js"
3 import {lineBreak} from "./whitespace.js"
4 import {getOptions} from "./options.js"
5 import {wordsRegexp} from "./util.js"
6 import {SCOPE_TOP, SCOPE_FUNCTION, SCOPE_ASYNC, SCOPE_GENERATOR, SCOPE_SUPER, SCOPE_DIRECT_SUPER, SCOPE_CLASS_STATIC_BLOCK} from "./scopeflags.js"
9 constructor(options, input, startPos) {
10 this.options = options = getOptions(options)
11 this.sourceFile = options.sourceFile
12 this.keywords = wordsRegexp(keywords[options.ecmaVersion >= 6 ? 6 : options.sourceType === "module" ? "5module" : 5])
14 if (options.allowReserved !== true) {
15 reserved = reservedWords[options.ecmaVersion >= 6 ? 6 : options.ecmaVersion === 5 ? 5 : 3]
16 if (options.sourceType === "module") reserved += " await"
18 this.reservedWords = wordsRegexp(reserved)
19 let reservedStrict = (reserved ? reserved + " " : "") + reservedWords.strict
20 this.reservedWordsStrict = wordsRegexp(reservedStrict)
21 this.reservedWordsStrictBind = wordsRegexp(reservedStrict + " " + reservedWords.strictBind)
22 this.input = String(input)
24 // Used to signal to callers of `readWord1` whether the word
25 // contained any escape sequences. This is needed because words with
26 // escape sequences must not be interpreted as keywords.
27 this.containsEsc = false
31 // The current position of the tokenizer in the input.
34 this.lineStart = this.input.lastIndexOf("\n", startPos - 1) + 1
35 this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length
37 this.pos = this.lineStart = 0
41 // Properties of the current token:
44 // For tokens that include more information than their type, the value
46 // Its start and end offset
47 this.start = this.end = this.pos
48 // And, if locations are used, the {line, column} object
49 // corresponding to those offsets
50 this.startLoc = this.endLoc = this.curPosition()
52 // Position information for the previous token
53 this.lastTokEndLoc = this.lastTokStartLoc = null
54 this.lastTokStart = this.lastTokEnd = this.pos
56 // The context stack is used to superficially track syntactic
57 // context to predict whether a regular expression is allowed in a
59 this.context = this.initialContext()
60 this.exprAllowed = true
62 // Figure out if it's a module code.
63 this.inModule = options.sourceType === "module"
64 this.strict = this.inModule || this.strictDirective(this.pos)
66 // Used to signify the start of a potential arrow function
67 this.potentialArrowAt = -1
68 this.potentialArrowInForAwait = false
70 // Positions to delayed-check that yield/await does not exist in default parameters.
71 this.yieldPos = this.awaitPos = this.awaitIdentPos = 0
74 // Thus-far undefined exports.
75 this.undefinedExports = Object.create(null)
77 // If enabled, skip leading hashbang line.
78 if (this.pos === 0 && options.allowHashBang && this.input.slice(0, 2) === "#!")
79 this.skipLineComment(2)
81 // Scope tracking for duplicate variable names (see scope.js)
83 this.enterScope(SCOPE_TOP)
85 // For RegExp validation
86 this.regexpState = null
88 // The stack of private names.
89 // Each element has two properties: 'declared' and 'used'.
90 // When it exited from the outermost class definition, all used private names must be declared.
91 this.privateNameStack = []
95 let node = this.options.program || this.startNode()
97 return this.parseTopLevel(node)
100 get inFunction() { return (this.currentVarScope().flags & SCOPE_FUNCTION) > 0 }
102 get inGenerator() { return (this.currentVarScope().flags & SCOPE_GENERATOR) > 0 && !this.currentVarScope().inClassFieldInit }
104 get inAsync() { return (this.currentVarScope().flags & SCOPE_ASYNC) > 0 && !this.currentVarScope().inClassFieldInit }
107 for (let i = this.scopeStack.length - 1; i >= 0; i--) {
108 let scope = this.scopeStack[i]
109 if (scope.inClassFieldInit || scope.flags & SCOPE_CLASS_STATIC_BLOCK) return false
110 if (scope.flags & SCOPE_FUNCTION) return (scope.flags & SCOPE_ASYNC) > 0
112 return (this.inModule && this.options.ecmaVersion >= 13) || this.options.allowAwaitOutsideFunction
116 const {flags, inClassFieldInit} = this.currentThisScope()
117 return (flags & SCOPE_SUPER) > 0 || inClassFieldInit || this.options.allowSuperOutsideMethod
120 get allowDirectSuper() { return (this.currentThisScope().flags & SCOPE_DIRECT_SUPER) > 0 }
122 get treatFunctionsAsVar() { return this.treatFunctionsAsVarInScope(this.currentScope()) }
124 get allowNewDotTarget() {
125 const {flags, inClassFieldInit} = this.currentThisScope()
126 return (flags & (SCOPE_FUNCTION | SCOPE_CLASS_STATIC_BLOCK)) > 0 || inClassFieldInit
129 get inClassStaticBlock() {
130 return (this.currentVarScope().flags & SCOPE_CLASS_STATIC_BLOCK) > 0
133 static extend(...plugins) {
135 for (let i = 0; i < plugins.length; i++) cls = plugins[i](cls)
139 static parse(input, options) {
140 return new this(options, input).parse()
143 static parseExpressionAt(input, pos, options) {
144 let parser = new this(options, input, pos)
146 return parser.parseExpression()
149 static tokenizer(input, options) {
150 return new this(options, input)