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 5import {Parser} from "./state" 6import {types as tt} from "./tokentype" 7import {lineBreak} from "./whitespace" 8 9export class TokContext { 10 constructor(token, isExpr, preserveSpace, override) { 11 this.token = token 12 this.isExpr = !!isExpr 13 this.preserveSpace = !!preserveSpace 14 this.override = override 15 } 16} 17 18export const types = { 19 b_stat: new TokContext("{", false), 20 b_expr: new TokContext("{", true), 21 b_tmpl: new TokContext("${", true), 22 p_stat: new TokContext("(", false), 23 p_expr: new TokContext("(", true), 24 q_tmpl: new TokContext("`", true, true, p => p.readTmplToken()), 25 f_expr: new TokContext("function", true) 26} 27 28const pp = Parser.prototype 29 30pp.initialContext = function() { 31 return [types.b_stat] 32} 33 34pp.braceIsBlock = function(prevType) { 35 if (prevType === tt.colon) { 36 let parent = this.curContext() 37 if (parent === types.b_stat || parent === types.b_expr) 38 return !parent.isExpr 39 } 40 if (prevType === tt._return) 41 return lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) 42 if (prevType === tt._else || prevType === tt.semi || prevType === tt.eof || prevType === tt.parenR) 43 return true 44 if (prevType == tt.braceL) 45 return this.curContext() === types.b_stat 46 return !this.exprAllowed 47} 48 49pp.updateContext = function(prevType) { 50 let update, type = this.type 51 if (type.keyword && prevType == tt.dot) 52 this.exprAllowed = false 53 else if (update = type.updateContext) 54 update.call(this, prevType) 55 else 56 this.exprAllowed = type.beforeExpr 57} 58 59// Token-specific context update code 60 61tt.parenR.updateContext = tt.braceR.updateContext = function() { 62 if (this.context.length == 1) { 63 this.exprAllowed = true 64 return 65 } 66 let out = this.context.pop() 67 if (out === types.b_stat && this.curContext() === types.f_expr) { 68 this.context.pop() 69 this.exprAllowed = false 70 } else if (out === types.b_tmpl) { 71 this.exprAllowed = true 72 } else { 73 this.exprAllowed = !out.isExpr 74 } 75} 76 77tt.braceL.updateContext = function(prevType) { 78 this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr) 79 this.exprAllowed = true 80} 81 82tt.dollarBraceL.updateContext = function() { 83 this.context.push(types.b_tmpl) 84 this.exprAllowed = true 85} 86 87tt.parenL.updateContext = function(prevType) { 88 let statementParens = prevType === tt._if || prevType === tt._for || prevType === tt._with || prevType === tt._while 89 this.context.push(statementParens ? types.p_stat : types.p_expr) 90 this.exprAllowed = true 91} 92 93tt.incDec.updateContext = function() { 94 // tokExprAllowed stays unchanged 95} 96 97tt._function.updateContext = function(prevType) { 98 if (prevType.beforeExpr && prevType !== tt.semi && prevType !== tt._else && 99 !((prevType === tt.colon || prevType === tt.braceL) && this.curContext() === types.b_stat)) 100 this.context.push(types.f_expr) 101 this.exprAllowed = false 102} 103 104tt.backQuote.updateContext = function() { 105 if (this.curContext() === types.q_tmpl) 106 this.context.pop() 107 else 108 this.context.push(types.q_tmpl) 109 this.exprAllowed = false 110} 111