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:
Huáng Jùnliàng
2021-05-19 16:00:24 -04:00
committed by GitHub
parent 053f94fc77
commit 461ba2531a
11 changed files with 308 additions and 101 deletions

View File

@@ -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;

View File

@@ -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",