Reduce exprAllowed usage (#13431)
This commit is contained in:
@@ -1,35 +1,27 @@
|
||||
// @flow
|
||||
|
||||
// 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
|
||||
// The token context is used to track whether `}` matches
|
||||
// a template quasi `${` or other tokens containing `{`:
|
||||
// namely tt.braceL and tt.dollarBraceL
|
||||
|
||||
import { types as tt } from "./types";
|
||||
|
||||
export class TokContext {
|
||||
constructor(token: string, isExpr?: boolean, preserveSpace?: boolean) {
|
||||
constructor(token: string, preserveSpace?: boolean) {
|
||||
this.token = token;
|
||||
this.isExpr = !!isExpr;
|
||||
this.preserveSpace = !!preserveSpace;
|
||||
}
|
||||
|
||||
token: string;
|
||||
isExpr: boolean;
|
||||
preserveSpace: boolean;
|
||||
}
|
||||
|
||||
export const types: {
|
||||
[key: string]: TokContext,
|
||||
} = {
|
||||
braceStatement: new TokContext("{", false),
|
||||
braceExpression: new TokContext("{", true),
|
||||
recordExpression: new TokContext("#{", true),
|
||||
templateQuasi: new TokContext("${", false),
|
||||
parenStatement: new TokContext("(", false),
|
||||
parenExpression: new TokContext("(", true),
|
||||
template: new TokContext("`", true, true),
|
||||
functionExpression: new TokContext("function", true),
|
||||
functionStatement: new TokContext("function", false),
|
||||
brace: new TokContext("{"),
|
||||
templateQuasi: new TokContext("${"),
|
||||
template: new TokContext("`", true),
|
||||
};
|
||||
|
||||
// Token-specific context update code
|
||||
@@ -42,93 +34,25 @@ export const types: {
|
||||
// When `=>` is eaten, the context update of `yield` is executed, however,
|
||||
// `this.prodParam` still has `[Yield]` production because it is not yet updated
|
||||
|
||||
tt.parenR.updateContext = tt.braceR.updateContext = function () {
|
||||
if (this.state.context.length === 1) {
|
||||
this.state.exprAllowed = true;
|
||||
return;
|
||||
tt.braceR.updateContext = context => {
|
||||
if (context.length > 1) {
|
||||
context.pop();
|
||||
}
|
||||
|
||||
let out = this.state.context.pop();
|
||||
if (out === types.braceStatement && this.curContext().token === "function") {
|
||||
out = this.state.context.pop();
|
||||
}
|
||||
|
||||
this.state.exprAllowed = !out.isExpr;
|
||||
};
|
||||
|
||||
tt.name.updateContext = function (prevType) {
|
||||
let allowed = false;
|
||||
if (prevType !== tt.dot) {
|
||||
if (
|
||||
this.state.value === "of" &&
|
||||
!this.state.exprAllowed &&
|
||||
prevType !== tt._function &&
|
||||
prevType !== tt._class
|
||||
) {
|
||||
allowed = true;
|
||||
}
|
||||
}
|
||||
this.state.exprAllowed = allowed;
|
||||
};
|
||||
|
||||
tt.braceL.updateContext = function (prevType) {
|
||||
this.state.context.push(
|
||||
this.braceIsBlock(prevType) ? types.braceStatement : types.braceExpression,
|
||||
);
|
||||
this.state.exprAllowed = true;
|
||||
};
|
||||
|
||||
tt.dollarBraceL.updateContext = function () {
|
||||
this.state.context.push(types.templateQuasi);
|
||||
this.state.exprAllowed = true;
|
||||
};
|
||||
|
||||
tt.parenL.updateContext = function (prevType) {
|
||||
const statementParens =
|
||||
prevType === tt._if ||
|
||||
prevType === tt._for ||
|
||||
prevType === tt._with ||
|
||||
prevType === tt._while;
|
||||
this.state.context.push(
|
||||
statementParens ? types.parenStatement : types.parenExpression,
|
||||
);
|
||||
this.state.exprAllowed = true;
|
||||
};
|
||||
|
||||
tt.incDec.updateContext = function () {
|
||||
// tokExprAllowed stays unchanged
|
||||
};
|
||||
|
||||
tt._function.updateContext = tt._class.updateContext = function (prevType) {
|
||||
if (
|
||||
prevType.beforeExpr &&
|
||||
prevType !== tt.semi &&
|
||||
prevType !== tt._else &&
|
||||
!(prevType === tt._return && this.hasPrecedingLineBreak()) &&
|
||||
!(
|
||||
(prevType === tt.colon || prevType === tt.braceL) &&
|
||||
this.curContext() === types.braceStatement
|
||||
)
|
||||
) {
|
||||
this.state.context.push(types.functionExpression);
|
||||
} else {
|
||||
this.state.context.push(types.functionStatement);
|
||||
}
|
||||
|
||||
this.state.exprAllowed = false;
|
||||
};
|
||||
|
||||
tt.backQuote.updateContext = function () {
|
||||
if (this.curContext() === types.template) {
|
||||
this.state.context.pop();
|
||||
} else {
|
||||
this.state.context.push(types.template);
|
||||
}
|
||||
this.state.exprAllowed = false;
|
||||
};
|
||||
|
||||
// we don't need to update context for tt.braceBarL because we do not pop context for tt.braceBarR
|
||||
tt.braceHashL.updateContext = function () {
|
||||
this.state.context.push(types.recordExpression);
|
||||
this.state.exprAllowed = true; /* tt.braceHashL.beforeExpr */
|
||||
tt.braceL.updateContext = tt.braceHashL.updateContext = context => {
|
||||
context.push(types.brace);
|
||||
};
|
||||
|
||||
tt.dollarBraceL.updateContext = context => {
|
||||
context.push(types.templateQuasi);
|
||||
};
|
||||
|
||||
tt.backQuote.updateContext = context => {
|
||||
if (context[context.length - 1] === types.template) {
|
||||
context.pop();
|
||||
} else {
|
||||
context.push(types.template);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -190,7 +190,6 @@ export default class Tokenizer extends ParserErrors {
|
||||
end: state.end,
|
||||
lastTokEnd: state.end,
|
||||
context: [this.curContext()],
|
||||
exprAllowed: state.exprAllowed,
|
||||
inType: state.inType,
|
||||
};
|
||||
}
|
||||
@@ -525,16 +524,9 @@ export default class Tokenizer extends ParserErrors {
|
||||
}
|
||||
|
||||
readToken_slash(): void {
|
||||
// '/'
|
||||
if (this.state.exprAllowed && !this.state.inType) {
|
||||
++this.state.pos;
|
||||
this.readRegexp();
|
||||
return;
|
||||
}
|
||||
|
||||
const next = this.input.charCodeAt(this.state.pos + 1);
|
||||
if (next === charCodes.equalsTo) {
|
||||
this.finishOp(tt.assign, 2);
|
||||
this.finishOp(tt.slashAssign, 2);
|
||||
} else {
|
||||
this.finishOp(tt.slash, 1);
|
||||
}
|
||||
@@ -565,7 +557,6 @@ export default class Tokenizer extends ParserErrors {
|
||||
let type = code === charCodes.asterisk ? tt.star : tt.modulo;
|
||||
let width = 1;
|
||||
let next = this.input.charCodeAt(this.state.pos + 1);
|
||||
const exprAllowed = this.state.exprAllowed;
|
||||
|
||||
// Exponentiation operator **
|
||||
if (code === charCodes.asterisk && next === charCodes.asterisk) {
|
||||
@@ -574,7 +565,7 @@ export default class Tokenizer extends ParserErrors {
|
||||
type = tt.exponent;
|
||||
}
|
||||
|
||||
if (next === charCodes.equalsTo && !exprAllowed) {
|
||||
if (next === charCodes.equalsTo && !this.state.inType) {
|
||||
width++;
|
||||
type = tt.assign;
|
||||
}
|
||||
@@ -983,7 +974,7 @@ export default class Tokenizer extends ParserErrors {
|
||||
}
|
||||
|
||||
readRegexp(): void {
|
||||
const start = this.state.pos;
|
||||
const start = this.state.start + 1;
|
||||
let escaped, inClass;
|
||||
for (;;) {
|
||||
if (this.state.pos >= this.length) {
|
||||
@@ -1565,68 +1556,9 @@ export default class Tokenizer extends ParserErrors {
|
||||
}
|
||||
}
|
||||
|
||||
braceIsBlock(prevType: TokenType): boolean {
|
||||
const parent = this.curContext();
|
||||
if (parent === ct.functionExpression || parent === ct.functionStatement) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
prevType === tt.colon &&
|
||||
(parent === ct.braceStatement || parent === ct.braceExpression)
|
||||
) {
|
||||
return !parent.isExpr;
|
||||
}
|
||||
|
||||
// The check for `tt.name && exprAllowed` detects whether we are
|
||||
// after a `yield` or `of` construct. See the `updateContext` for
|
||||
// `tt.name`.
|
||||
if (
|
||||
prevType === tt._return ||
|
||||
(prevType === tt.name && this.state.exprAllowed)
|
||||
) {
|
||||
return this.hasPrecedingLineBreak();
|
||||
}
|
||||
|
||||
if (
|
||||
prevType === tt._else ||
|
||||
prevType === tt.semi ||
|
||||
prevType === tt.eof ||
|
||||
prevType === tt.parenR ||
|
||||
prevType === tt.arrow
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (prevType === tt.braceL) {
|
||||
return parent === ct.braceStatement;
|
||||
}
|
||||
|
||||
if (
|
||||
prevType === tt._var ||
|
||||
prevType === tt._const ||
|
||||
prevType === tt.name
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prevType === tt.relational) {
|
||||
// `class C<T> { ... }`
|
||||
return true;
|
||||
}
|
||||
|
||||
return !this.state.exprAllowed;
|
||||
}
|
||||
|
||||
// the prevType is required by the jsx plugin
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
updateContext(prevType: TokenType): void {
|
||||
const type = this.state.type;
|
||||
let update;
|
||||
|
||||
if (type.keyword && (prevType === tt.dot || prevType === tt.questionDot)) {
|
||||
this.state.exprAllowed = false;
|
||||
} else if ((update = type.updateContext)) {
|
||||
update.call(this, prevType);
|
||||
} else {
|
||||
this.state.exprAllowed = type.beforeExpr;
|
||||
}
|
||||
this.state.type.updateContext?.(this.state.context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ export default class State {
|
||||
// The context stack is used to superficially track syntactic
|
||||
// context to predict whether a regular expression is allowed in a
|
||||
// given position.
|
||||
context: Array<TokContext> = [ct.braceStatement];
|
||||
context: Array<TokContext> = [ct.brace];
|
||||
exprAllowed: boolean = true;
|
||||
|
||||
// Used to signal to callers of `readWord1` whether the word
|
||||
@@ -181,7 +181,6 @@ export type LookaheadState = {
|
||||
type: TokenType,
|
||||
start: number,
|
||||
end: number,
|
||||
/* Used only in readSlashToken */
|
||||
exprAllowed: boolean,
|
||||
/* Used only in readToken_mult_modulo */
|
||||
inType: boolean,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @flow
|
||||
|
||||
import type { TokContext } from "./context";
|
||||
// ## Token types
|
||||
|
||||
// The assignment of fine-grained, information-carrying type objects
|
||||
@@ -9,10 +9,9 @@
|
||||
// All token type variables start with an underscore, to make them
|
||||
// easy to recognize.
|
||||
|
||||
// The `beforeExpr` property is used to disambiguate between regular
|
||||
// expressions and divisions. It is set on all token types that can
|
||||
// be followed by an expression (thus, a slash after them would be a
|
||||
// regular expression).
|
||||
// The `beforeExpr` property is used to disambiguate between 1) binary
|
||||
// expression (<) and JSX Tag start (<name>); 2) object literal and JSX
|
||||
// texts. It is set on the `updateContext` function in the JSX plugin.
|
||||
|
||||
// The `startsExpr` property is used to determine whether an expression
|
||||
// may be the “argument” subexpression of a `yield` expression or
|
||||
@@ -53,7 +52,7 @@ export class TokenType {
|
||||
prefix: boolean;
|
||||
postfix: boolean;
|
||||
binop: ?number;
|
||||
updateContext: ?(prevType: TokenType) => void;
|
||||
updateContext: ?(context: Array<TokContext>) => void;
|
||||
|
||||
constructor(label: string, conf: TokenOptions = {}) {
|
||||
this.label = label;
|
||||
@@ -102,7 +101,7 @@ export const types: { [name: string]: TokenType } = {
|
||||
braceL: new TokenType("{", { beforeExpr, startsExpr }),
|
||||
braceBarL: new TokenType("{|", { beforeExpr, startsExpr }),
|
||||
braceHashL: new TokenType("#{", { beforeExpr, startsExpr }),
|
||||
braceR: new TokenType("}"),
|
||||
braceR: new TokenType("}", { beforeExpr }),
|
||||
braceBarR: new TokenType("|}"),
|
||||
parenL: new TokenType("(", { beforeExpr, startsExpr }),
|
||||
parenR: new TokenType(")"),
|
||||
@@ -140,6 +139,7 @@ export const types: { [name: string]: TokenType } = {
|
||||
|
||||
eq: new TokenType("=", { beforeExpr, isAssign }),
|
||||
assign: new TokenType("_=", { beforeExpr, isAssign }),
|
||||
slashAssign: new TokenType("_=", { beforeExpr, isAssign }),
|
||||
incDec: new TokenType("++/--", { prefix, postfix, startsExpr }),
|
||||
bang: new TokenType("!", { beforeExpr, prefix, startsExpr }),
|
||||
tilde: new TokenType("~", { beforeExpr, prefix, startsExpr }),
|
||||
|
||||
Reference in New Issue
Block a user