// 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) 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 () { if (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; };