tokencontext.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // The algorithm used to determine whether a regexp can appear at a
  2. // given point in the program is loosely based on sweet.js' approach.
  3. // See https://github.com/mozilla/sweet.js/wiki/design
  4. import {Parser} from "./state"
  5. import {types as tt} from "./tokentype"
  6. import {lineBreak} from "./whitespace"
  7. export class TokContext {
  8. constructor(token, isExpr, preserveSpace, override) {
  9. this.token = token
  10. this.isExpr = !!isExpr
  11. this.preserveSpace = !!preserveSpace
  12. this.override = override
  13. }
  14. }
  15. export const types = {
  16. b_stat: new TokContext("{", false),
  17. b_expr: new TokContext("{", true),
  18. b_tmpl: new TokContext("${", true),
  19. p_stat: new TokContext("(", false),
  20. p_expr: new TokContext("(", true),
  21. q_tmpl: new TokContext("`", true, true, p => p.readTmplToken()),
  22. f_expr: new TokContext("function", true)
  23. }
  24. const pp = Parser.prototype
  25. pp.initialContext = function() {
  26. return [types.b_stat]
  27. }
  28. pp.braceIsBlock = function(prevType) {
  29. if (prevType === tt.colon) {
  30. let parent = this.curContext()
  31. if (parent === types.b_stat || parent === types.b_expr)
  32. return !parent.isExpr
  33. }
  34. if (prevType === tt._return)
  35. return lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
  36. if (prevType === tt._else || prevType === tt.semi || prevType === tt.eof || prevType === tt.parenR)
  37. return true
  38. if (prevType == tt.braceL)
  39. return this.curContext() === types.b_stat
  40. return !this.exprAllowed
  41. }
  42. pp.updateContext = function(prevType) {
  43. let update, type = this.type
  44. if (type.keyword && prevType == tt.dot)
  45. this.exprAllowed = false
  46. else if (update = type.updateContext)
  47. update.call(this, prevType)
  48. else
  49. this.exprAllowed = type.beforeExpr
  50. }
  51. // Token-specific context update code
  52. tt.parenR.updateContext = tt.braceR.updateContext = function() {
  53. if (this.context.length == 1) {
  54. this.exprAllowed = true
  55. return
  56. }
  57. let out = this.context.pop()
  58. if (out === types.b_stat && this.curContext() === types.f_expr) {
  59. this.context.pop()
  60. this.exprAllowed = false
  61. } else if (out === types.b_tmpl) {
  62. this.exprAllowed = true
  63. } else {
  64. this.exprAllowed = !out.isExpr
  65. }
  66. }
  67. tt.braceL.updateContext = function(prevType) {
  68. this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr)
  69. this.exprAllowed = true
  70. }
  71. tt.dollarBraceL.updateContext = function() {
  72. this.context.push(types.b_tmpl)
  73. this.exprAllowed = true
  74. }
  75. tt.parenL.updateContext = function(prevType) {
  76. let statementParens = prevType === tt._if || prevType === tt._for || prevType === tt._with || prevType === tt._while
  77. this.context.push(statementParens ? types.p_stat : types.p_expr)
  78. this.exprAllowed = true
  79. }
  80. tt.incDec.updateContext = function() {
  81. // tokExprAllowed stays unchanged
  82. }
  83. tt._function.updateContext = function(prevType) {
  84. if (prevType.beforeExpr && prevType !== tt.semi && prevType !== tt._else &&
  85. !((prevType === tt.colon || prevType === tt.braceL) && this.curContext() === types.b_stat))
  86. this.context.push(types.f_expr)
  87. this.exprAllowed = false
  88. }
  89. tt.backQuote.updateContext = function() {
  90. if (this.curContext() === types.q_tmpl)
  91. this.context.pop()
  92. else
  93. this.context.push(types.q_tmpl)
  94. this.exprAllowed = false
  95. }