state.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import {reservedWords, keywords} from "./identifier"
  2. import {types as tt} from "./tokentype"
  3. import {lineBreak} from "./whitespace"
  4. import {getOptions} from "./options"
  5. // Registered plugins
  6. export const plugins = {}
  7. function keywordRegexp(words) {
  8. return new RegExp("^(" + words.replace(/ /g, "|") + ")$")
  9. }
  10. export class Parser {
  11. constructor(options, input, startPos) {
  12. this.options = options = getOptions(options)
  13. this.sourceFile = options.sourceFile
  14. this.keywords = keywordRegexp(keywords[options.ecmaVersion >= 6 ? 6 : 5])
  15. let reserved = ""
  16. if (!options.allowReserved) {
  17. for (let v = options.ecmaVersion;; v--)
  18. if (reserved = reservedWords[v]) break
  19. if (options.sourceType == "module") reserved += " await"
  20. }
  21. this.reservedWords = keywordRegexp(reserved)
  22. let reservedStrict = (reserved ? reserved + " " : "") + reservedWords.strict
  23. this.reservedWordsStrict = keywordRegexp(reservedStrict)
  24. this.reservedWordsStrictBind = keywordRegexp(reservedStrict + " " + reservedWords.strictBind)
  25. this.input = String(input)
  26. // Used to signal to callers of `readWord1` whether the word
  27. // contained any escape sequences. This is needed because words with
  28. // escape sequences must not be interpreted as keywords.
  29. this.containsEsc = false
  30. // Load plugins
  31. this.loadPlugins(options.plugins)
  32. // Set up token state
  33. // The current position of the tokenizer in the input.
  34. if (startPos) {
  35. this.pos = startPos
  36. this.lineStart = this.input.lastIndexOf("\n", startPos - 1) + 1
  37. this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length
  38. } else {
  39. this.pos = this.lineStart = 0
  40. this.curLine = 1
  41. }
  42. // Properties of the current token:
  43. // Its type
  44. this.type = tt.eof
  45. // For tokens that include more information than their type, the value
  46. this.value = null
  47. // Its start and end offset
  48. this.start = this.end = this.pos
  49. // And, if locations are used, the {line, column} object
  50. // corresponding to those offsets
  51. 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
  55. // The context stack is used to superficially track syntactic
  56. // context to predict whether a regular expression is allowed in a
  57. // given position.
  58. this.context = this.initialContext()
  59. this.exprAllowed = true
  60. // Figure out if it's a module code.
  61. this.inModule = options.sourceType === "module"
  62. this.strict = this.inModule || this.strictDirective(this.pos)
  63. // Used to signify the start of a potential arrow function
  64. this.potentialArrowAt = -1
  65. // Flags to track whether we are in a function, a generator, an async function.
  66. this.inFunction = this.inGenerator = this.inAsync = false
  67. // Positions to delayed-check that yield/await does not exist in default parameters.
  68. this.yieldPos = this.awaitPos = 0
  69. // Labels in scope.
  70. this.labels = []
  71. // If enabled, skip leading hashbang line.
  72. if (this.pos === 0 && options.allowHashBang && this.input.slice(0, 2) === '#!')
  73. this.skipLineComment(2)
  74. }
  75. // DEPRECATED Kept for backwards compatibility until 3.0 in case a plugin uses them
  76. isKeyword(word) { return this.keywords.test(word) }
  77. isReservedWord(word) { return this.reservedWords.test(word) }
  78. extend(name, f) {
  79. this[name] = f(this[name])
  80. }
  81. loadPlugins(pluginConfigs) {
  82. for (let name in pluginConfigs) {
  83. let plugin = plugins[name]
  84. if (!plugin) throw new Error("Plugin '" + name + "' not found")
  85. plugin(this, pluginConfigs[name])
  86. }
  87. }
  88. parse() {
  89. let node = this.options.program || this.startNode()
  90. this.nextToken()
  91. return this.parseTopLevel(node)
  92. }
  93. }