refactor: add parse*Literal parser routines (#13333)
* refactor: simplify parseLiteral interface * refactor: extract specific methods on parsing literals * fix: avoid StringLiteral type comparison * add test cases * fix: remove redundant node * Update packages/babel-parser/src/plugins/flow/index.js Co-authored-by: Federico Ciardi <fed.ciardi@gmail.com> * update test fixtures * fix: refine parseLiteral typings Co-authored-by: Federico Ciardi <fed.ciardi@gmail.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import { types as tt, TokenType } from "../tokenizer/types";
|
||||
import { TokenType } from "../tokenizer/types";
|
||||
import type Parser from "../parser";
|
||||
import type { ExpressionErrors } from "../parser/util";
|
||||
import * as N from "../types";
|
||||
@@ -9,7 +9,7 @@ import { Errors } from "../parser/error";
|
||||
|
||||
export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
class extends superClass {
|
||||
estreeParseRegExpLiteral({ pattern, flags }: N.RegExpLiteral): N.Node {
|
||||
parseRegExpLiteral({ pattern, flags }): N.Node {
|
||||
let regex = null;
|
||||
try {
|
||||
regex = new RegExp(pattern, flags);
|
||||
@@ -17,13 +17,13 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
// In environments that don't support these flags value will
|
||||
// be null as the regex can't be represented natively.
|
||||
}
|
||||
const node = this.estreeParseLiteral(regex);
|
||||
const node = this.estreeParseLiteral<N.EstreeRegExpLiteral>(regex);
|
||||
node.regex = { pattern, flags };
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
estreeParseBigIntLiteral(value: any): N.Node {
|
||||
parseBigIntLiteral(value: any): N.Node {
|
||||
// https://github.com/estree/estree/blob/master/es2020.md#bigintliteral
|
||||
let bigInt;
|
||||
try {
|
||||
@@ -32,13 +32,13 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
} catch {
|
||||
bigInt = null;
|
||||
}
|
||||
const node = this.estreeParseLiteral(bigInt);
|
||||
const node = this.estreeParseLiteral<N.EstreeBigIntLiteral>(bigInt);
|
||||
node.bigint = String(node.value || value);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
estreeParseDecimalLiteral(value: any): N.Node {
|
||||
parseDecimalLiteral(value: any): N.Node {
|
||||
// https://github.com/estree/estree/blob/master/experimental/decimal.md
|
||||
// todo: use BigDecimal when node supports it.
|
||||
const decimal = null;
|
||||
@@ -48,8 +48,24 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
return node;
|
||||
}
|
||||
|
||||
estreeParseLiteral(value: any): N.Node {
|
||||
return this.parseLiteral(value, "Literal");
|
||||
estreeParseLiteral<T: N.Node>(value: any) {
|
||||
return this.parseLiteral<T>(value, "Literal");
|
||||
}
|
||||
|
||||
parseStringLiteral(value: any): N.Node {
|
||||
return this.estreeParseLiteral(value);
|
||||
}
|
||||
|
||||
parseNumericLiteral(value: any): any {
|
||||
return this.estreeParseLiteral(value);
|
||||
}
|
||||
|
||||
parseNullLiteral(): N.Node {
|
||||
return this.estreeParseLiteral(null);
|
||||
}
|
||||
|
||||
parseBooleanLiteral(value: boolean): N.BooleanLiteral {
|
||||
return this.estreeParseLiteral(value);
|
||||
}
|
||||
|
||||
directiveToStmt(directive: N.Directive): N.ExpressionStatement {
|
||||
@@ -165,35 +181,6 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
classBody.body.push(method);
|
||||
}
|
||||
|
||||
parseExprAtom(refExpressionErrors?: ?ExpressionErrors): N.Expression {
|
||||
switch (this.state.type) {
|
||||
case tt.num:
|
||||
case tt.string:
|
||||
return this.estreeParseLiteral(this.state.value);
|
||||
|
||||
case tt.regexp:
|
||||
return this.estreeParseRegExpLiteral(this.state.value);
|
||||
|
||||
case tt.bigint:
|
||||
return this.estreeParseBigIntLiteral(this.state.value);
|
||||
|
||||
case tt.decimal:
|
||||
return this.estreeParseDecimalLiteral(this.state.value);
|
||||
|
||||
case tt._null:
|
||||
return this.estreeParseLiteral(null);
|
||||
|
||||
case tt._true:
|
||||
return this.estreeParseLiteral(true);
|
||||
|
||||
case tt._false:
|
||||
return this.estreeParseLiteral(false);
|
||||
|
||||
default:
|
||||
return super.parseExprAtom(refExpressionErrors);
|
||||
}
|
||||
}
|
||||
|
||||
parseMaybePrivateName(...args: [boolean]): any {
|
||||
const node = super.parseMaybePrivateName(...args);
|
||||
if (
|
||||
@@ -230,13 +217,8 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
return node.name;
|
||||
}
|
||||
|
||||
parseLiteral<T: N.Literal>(
|
||||
value: any,
|
||||
type: /*T["kind"]*/ string,
|
||||
startPos?: number,
|
||||
startLoc?: Position,
|
||||
): T {
|
||||
const node = super.parseLiteral(value, type, startPos, startLoc);
|
||||
parseLiteral<T: N.Node>(value: any, type: $ElementType<T, "type">): T {
|
||||
const node = super.parseLiteral<T>(value, type);
|
||||
node.raw = node.extra.raw;
|
||||
delete node.extra;
|
||||
|
||||
|
||||
@@ -1536,7 +1536,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
return this.finishNode(node, "FunctionTypeAnnotation");
|
||||
|
||||
case tt.string:
|
||||
return this.parseLiteral(
|
||||
return this.parseLiteral<N.StringLiteralTypeAnnotation>(
|
||||
this.state.value,
|
||||
"StringLiteralTypeAnnotation",
|
||||
);
|
||||
@@ -1545,26 +1545,27 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
case tt._false:
|
||||
node.value = this.match(tt._true);
|
||||
this.next();
|
||||
return this.finishNode(node, "BooleanLiteralTypeAnnotation");
|
||||
return this.finishNode<N.BooleanLiteralTypeAnnotation>(
|
||||
node,
|
||||
"BooleanLiteralTypeAnnotation",
|
||||
);
|
||||
|
||||
case tt.plusMin:
|
||||
if (this.state.value === "-") {
|
||||
this.next();
|
||||
if (this.match(tt.num)) {
|
||||
return this.parseLiteral(
|
||||
return this.parseLiteralAtNode<N.NumberLiteralTypeAnnotation>(
|
||||
-this.state.value,
|
||||
"NumberLiteralTypeAnnotation",
|
||||
node.start,
|
||||
node.loc.start,
|
||||
node,
|
||||
);
|
||||
}
|
||||
|
||||
if (this.match(tt.bigint)) {
|
||||
return this.parseLiteral(
|
||||
return this.parseLiteralAtNode<N.BigIntLiteralTypeAnnotation>(
|
||||
-this.state.value,
|
||||
"BigIntLiteralTypeAnnotation",
|
||||
node.start,
|
||||
node.loc.start,
|
||||
node,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2669,7 +2670,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
// parse import-type/typeof shorthand
|
||||
parseImportSpecifier(node: N.ImportDeclaration): void {
|
||||
const specifier = this.startNode();
|
||||
const firstIdentLoc = this.state.start;
|
||||
const firstIdentIsString = this.match(tt.string);
|
||||
const firstIdent = this.parseModuleExportName();
|
||||
|
||||
let specifierTypeKind = null;
|
||||
@@ -2713,13 +2714,15 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
specifier.local = specifier.imported.__clone();
|
||||
}
|
||||
} else {
|
||||
if (firstIdent.type === "StringLiteral") {
|
||||
if (firstIdentIsString) {
|
||||
/*:: invariant(firstIdent instanceof N.StringLiteral) */
|
||||
throw this.raise(
|
||||
specifier.start,
|
||||
Errors.ImportBindingIsString,
|
||||
firstIdent.value,
|
||||
);
|
||||
}
|
||||
/*:: invariant(firstIdent instanceof N.Node) */
|
||||
isBinding = true;
|
||||
specifier.imported = firstIdent;
|
||||
specifier.importKind = null;
|
||||
@@ -2731,7 +2734,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
|
||||
if (nodeIsTypeImport && specifierIsTypeImport) {
|
||||
this.raise(
|
||||
firstIdentLoc,
|
||||
specifier.start,
|
||||
FlowErrors.ImportTypeShorthandOnlyInPureImport,
|
||||
);
|
||||
}
|
||||
@@ -3388,14 +3391,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
const endOfInit = () => this.match(tt.comma) || this.match(tt.braceR);
|
||||
switch (this.state.type) {
|
||||
case tt.num: {
|
||||
const literal = this.parseLiteral(this.state.value, "NumericLiteral");
|
||||
const literal = this.parseNumericLiteral(this.state.value);
|
||||
if (endOfInit()) {
|
||||
return { type: "number", pos: literal.start, value: literal };
|
||||
}
|
||||
return { type: "invalid", pos: startPos };
|
||||
}
|
||||
case tt.string: {
|
||||
const literal = this.parseLiteral(this.state.value, "StringLiteral");
|
||||
const literal = this.parseStringLiteral(this.state.value);
|
||||
if (endOfInit()) {
|
||||
return { type: "string", pos: literal.start, value: literal };
|
||||
}
|
||||
@@ -3403,7 +3406,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
}
|
||||
case tt._true:
|
||||
case tt._false: {
|
||||
const literal = this.parseBooleanLiteral();
|
||||
const literal = this.parseBooleanLiteral(this.match(tt._true));
|
||||
if (endOfInit()) {
|
||||
return {
|
||||
type: "boolean",
|
||||
|
||||
Reference in New Issue
Block a user