111 lines
3.2 KiB
JavaScript
111 lines
3.2 KiB
JavaScript
|
// 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
|
||
|
}
|