You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
105 lines
3.5 KiB
JavaScript
105 lines
3.5 KiB
JavaScript
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 = options.allowReserved ? "" :
|
|
reservedWords[options.ecmaVersion] + (options.sourceType == "module" ? " 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 = Math.max(0, this.input.lastIndexOf("\n", startPos))
|
|
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.strict = this.inModule = options.sourceType === "module"
|
|
|
|
// Used to signify the start of a potential arrow function
|
|
this.potentialArrowAt = -1
|
|
|
|
// Flags to track whether we are in a function, a generator.
|
|
this.inFunction = this.inGenerator = false
|
|
// 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)
|
|
}
|
|
}
|