// 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 https://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 }