Repository navigation
Expand file tree
/
Copy pathstate.js
More file actions
184 lines (153 loc) · 6.43 KB
/
Copy pathstate.js
File metadata and controls
184 lines (153 loc) · 6.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
import {reservedWords, keywords} from "./identifier.js"
import {types as tt} from "./tokentype.js"
import {lineBreak} from "./whitespace.js"
import {getOptions} from "./options.js"
import {wordsRegexp} from "./util.js"
import {
SCOPE_TOP, SCOPE_FUNCTION, SCOPE_ASYNC, SCOPE_GENERATOR, SCOPE_SUPER, SCOPE_DIRECT_SUPER,
SCOPE_ARROW, SCOPE_CLASS_STATIC_BLOCK, SCOPE_CLASS_FIELD_INIT, SCOPE_SWITCH
} from "./scopeflags.js"
export class Parser {
constructor(options, input, startPos) {
this.options = options = getOptions(options)
this.sourceFile = options.sourceFile
this.keywords = wordsRegexp(keywords[options.ecmaVersion >= 6 ? 6 : options.sourceType === "module" ? "5module" : 5])
let reserved = ""
if (options.allowReserved !== true) {
reserved = reservedWords[options.ecmaVersion >= 6 ? 6 : options.ecmaVersion === 5 ? 5 : 3]
if (options.sourceType === "module") reserved += " await"
}
this.reservedWords = wordsRegexp(reserved)
let reservedStrict = (reserved ? reserved + " " : "") + reservedWords.strict
this.reservedWordsStrict = wordsRegexp(reservedStrict)
this.reservedWordsStrictBind = wordsRegexp(reservedStrict + " " + reservedWords.strictBind)
this.input = String(input)
// Used to signal to callers of `readWord1` whether the word
// contained any escape sequences. This is needed because words with
// escape sequences must not be interpreted as keywords.
this.containsEsc = false
// Set up token state
// The current position of the tokenizer in the input.
this.pos = startPos || 0
this.curLine = 1
if (options.startLocation) {
this.lineStart = this.pos - options.startLocation.column
this.curLine = options.startLocation.line
} else if (startPos) {
this.lineStart = this.input.lastIndexOf("\n", startPos - 1) + 1
if (this.options.locations)
this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length
} else {
this.lineStart = 0
}
// Properties of the current token:
// Its type
this.type = tt.eof
// For tokens that include more information than their type, the value
this.value = null
// Its start and end offset
this.start = this.end = this.pos
// And, if locations are used, the {line, column} object
// corresponding to those offsets
this.startLoc = this.endLoc = this.curPosition()
// Position information for the previous token
this.lastTokEndLoc = this.lastTokStartLoc = null
this.lastTokStart = this.lastTokEnd = this.pos
// The context stack is used to superficially track syntactic
// context to predict whether a regular expression is allowed in a
// given position.
this.context = this.initialContext()
this.exprAllowed = true
// Figure out if it's a module code.
this.inModule = options.sourceType === "module"
this.strict = this.inModule || options.strict === true || this.strictDirective(this.pos)
// Used to signify the start of a potential arrow function
this.potentialArrowAt = -1
this.potentialArrowInForAwait = false
// Used to distinguish potential statement labels
this.statementStart = -1
// Positions to delayed-check that yield/await does not exist in default parameters.
this.yieldPos = this.awaitPos = this.awaitIdentPos = 0
// Labels in scope.
this.labels = []
// Thus-far undefined exports.
this.undefinedExports = Object.create(null)
// If enabled, skip leading hashbang line.
if (this.pos === 0 && options.allowHashBang && this.input.slice(0, 2) === "#!")
this.skipLineComment(2)
// Scope tracking for duplicate variable names (see scope.js)
this.scopeStack = []
this.enterScope(
this.options.sourceType === "commonjs"
// In commonjs, the top-level scope behaves like a function scope
? SCOPE_FUNCTION
: SCOPE_TOP
)
// For RegExp validation
this.regexpState = null
// The stack of private names.
// Each element has two properties: 'declared' and 'used'.
// When it exited from the outermost class definition, all used private names must be declared.
this.privateNameStack = []
}
parse() {
let node = this.options.program || this.startNode()
this.nextToken()
return this.catchStackOverflow(() => this.parseTopLevel(node))
}
get inFunction() { return (this.currentVarScope().flags & SCOPE_FUNCTION) > 0 }
get inGenerator() { return (this.currentVarScope().flags & SCOPE_GENERATOR) > 0 }
get inAsync() { return (this.currentVarScope().flags & SCOPE_ASYNC) > 0 }
get canAwait() {
for (let i = this.scopeStack.length - 1; i >= 0; i--) {
let {flags} = this.scopeStack[i]
if (flags & (SCOPE_CLASS_STATIC_BLOCK | SCOPE_CLASS_FIELD_INIT)) return false
if (flags & SCOPE_FUNCTION) return (flags & SCOPE_ASYNC) > 0
}
return (this.inModule && this.options.ecmaVersion >= 13) || this.options.allowAwaitOutsideFunction
}
get allowReturn() {
if (this.inFunction) return true
if (this.options.allowReturnOutsideFunction && this.currentVarScope().flags & SCOPE_TOP) return true
return false
}
get allowSuper() {
const {flags} = this.currentThisScope()
return (flags & SCOPE_SUPER) > 0 || this.options.allowSuperOutsideMethod
}
get allowDirectSuper() { return (this.currentThisScope().flags & SCOPE_DIRECT_SUPER) > 0 }
get treatFunctionsAsVar() { return this.treatFunctionsAsVarInScope(this.currentScope()) }
get allowNewDotTarget() {
for (let i = this.scopeStack.length - 1; i >= 0; i--) {
let {flags} = this.scopeStack[i]
if (flags & (SCOPE_CLASS_STATIC_BLOCK | SCOPE_CLASS_FIELD_INIT) ||
((flags & SCOPE_FUNCTION) && !(flags & SCOPE_ARROW))) return true
}
return false
}
get allowUsing() {
const {flags} = this.currentScope()
if (flags & SCOPE_SWITCH) return false
if (!this.inModule && flags & SCOPE_TOP) return false
return true
}
get inClassStaticBlock() {
return (this.currentVarScope().flags & SCOPE_CLASS_STATIC_BLOCK) > 0
}
static extend(...plugins) {
let cls = this
for (let i = 0; i < plugins.length; i++) cls = plugins[i](cls)
return cls
}
static parse(input, options) {
return new this(options, input).parse()
}
static parseExpressionAt(input, pos, options) {
let parser = new this(options, input, pos)
parser.nextToken()
return parser.parseExpression()
}
static tokenizer(input, options) {
return new this(options, input)
}
}