123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- import {reservedWords, keywords} from "./identifier"
- import {types as tt} from "./tokentype"
- import {lineBreak} from "./whitespace"
- import {getOptions} from "./options"
- // Registered plugins
- export const plugins = {}
- function keywordRegexp(words) {
- return new RegExp("^(" + words.replace(/ /g, "|") + ")$")
- }
- export class Parser {
- constructor(options, input, startPos) {
- this.options = options = getOptions(options)
- this.sourceFile = options.sourceFile
- this.keywords = keywordRegexp(keywords[options.ecmaVersion >= 6 ? 6 : 5])
- let reserved = ""
- if (!options.allowReserved) {
- for (let v = options.ecmaVersion;; v--)
- if (reserved = reservedWords[v]) break
- if (options.sourceType == "module") reserved += " await"
- }
- this.reservedWords = keywordRegexp(reserved)
- let reservedStrict = (reserved ? reserved + " " : "") + reservedWords.strict
- this.reservedWordsStrict = keywordRegexp(reservedStrict)
- this.reservedWordsStrictBind = keywordRegexp(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
- // Load plugins
- this.loadPlugins(options.plugins)
- // Set up token state
- // The current position of the tokenizer in the input.
- if (startPos) {
- this.pos = startPos
- this.lineStart = this.input.lastIndexOf("\n", startPos - 1) + 1
- this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length
- } else {
- this.pos = this.lineStart = 0
- this.curLine = 1
- }
- // 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 || this.strictDirective(this.pos)
- // Used to signify the start of a potential arrow function
- this.potentialArrowAt = -1
- // Flags to track whether we are in a function, a generator, an async function.
- this.inFunction = this.inGenerator = this.inAsync = false
- // Positions to delayed-check that yield/await does not exist in default parameters.
- this.yieldPos = this.awaitPos = 0
- // Labels in scope.
- this.labels = []
- // If enabled, skip leading hashbang line.
- if (this.pos === 0 && options.allowHashBang && this.input.slice(0, 2) === '#!')
- this.skipLineComment(2)
- }
- // DEPRECATED Kept for backwards compatibility until 3.0 in case a plugin uses them
- isKeyword(word) { return this.keywords.test(word) }
- isReservedWord(word) { return this.reservedWords.test(word) }
- extend(name, f) {
- this[name] = f(this[name])
- }
- loadPlugins(pluginConfigs) {
- for (let name in pluginConfigs) {
- let plugin = plugins[name]
- if (!plugin) throw new Error("Plugin '" + name + "' not found")
- plugin(this, pluginConfigs[name])
- }
- }
- parse() {
- let node = this.options.program || this.startNode()
- this.nextToken()
- return this.parseTopLevel(node)
- }
- }
|