// 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

}