// The algorithm used to determine whether a regexp can appear at a // given point in the program is loosely based on sweet.js' approach. // See github.com/mozilla/sweet.js/wiki/design
import {Parser} from “./state” import {types as tt} from “./tokentype” import {lineBreak} from “./whitespace”
export class TokContext {
constructor(token, isExpr, preserveSpace, override) { this.token = token this.isExpr = !!isExpr this.preserveSpace = !!preserveSpace this.override = override }
}
export const types = {
b_stat: new TokContext("{", false), b_expr: new TokContext("{", true), b_tmpl: new TokContext("${", true), p_stat: new TokContext("(", false), p_expr: new TokContext("(", true), q_tmpl: new TokContext("`", true, true, p => p.readTmplToken()), f_expr: new TokContext("function", true)
}
const pp = Parser.prototype
pp.initialContext = function() {
return [types.b_stat]
}
pp.braceIsBlock = function(prevType) {
if (prevType === tt.colon) { let parent = this.curContext() if (parent === types.b_stat || parent === types.b_expr) return !parent.isExpr } if (prevType === tt._return) return lineBreak.test(this.input.slice(this.lastTokEnd, this.start)) if (prevType === tt._else || prevType === tt.semi || prevType === tt.eof || prevType === tt.parenR) return true if (prevType == tt.braceL) return this.curContext() === types.b_stat return !this.exprAllowed
}
pp.updateContext = function(prevType) {
let update, type = this.type if (type.keyword && prevType == tt.dot) this.exprAllowed = false else if (update = type.updateContext) update.call(this, prevType) else this.exprAllowed = type.beforeExpr
}
// Token-specific context update code
tt.parenR.updateContext = tt.braceR.updateContext = function() {
if (this.context.length == 1) { this.exprAllowed = true return } let out = this.context.pop() if (out === types.b_stat && this.curContext() === types.f_expr) { this.context.pop() this.exprAllowed = false } else if (out === types.b_tmpl) { this.exprAllowed = true } else { this.exprAllowed = !out.isExpr }
}
tt.braceL.updateContext = function(prevType) {
this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr) this.exprAllowed = true
}
tt.dollarBraceL.updateContext = function() {
this.context.push(types.b_tmpl) this.exprAllowed = true
}
tt.parenL.updateContext = function(prevType) {
let statementParens = prevType === tt._if || prevType === tt._for || prevType === tt._with || prevType === tt._while this.context.push(statementParens ? types.p_stat : types.p_expr) this.exprAllowed = true
}
tt.incDec.updateContext = function() {
// tokExprAllowed stays unchanged
}
tt._function.updateContext = function(prevType) {
if (prevType.beforeExpr && prevType !== tt.semi && prevType !== tt._else && !((prevType === tt.colon || prevType === tt.braceL) && this.curContext() === types.b_stat)) this.context.push(types.f_expr) this.exprAllowed = false
}
tt.backQuote.updateContext = function() {
if (this.curContext() === types.q_tmpl) this.context.pop() else this.context.push(types.q_tmpl) this.exprAllowed = false
}