JS(extractor): improve parser support for flowtype syntax

This commit is contained in:
Esben Sparre Andreasen
2018-11-22 09:01:36 +01:00
parent 733acaccfa
commit 8c7ca38b8d
12 changed files with 1636 additions and 20 deletions

View File

@@ -3040,6 +3040,10 @@ public class Parser {
return parseClassPropertyBody(pi, hadConstructor, isStatic);
}
protected boolean atGetterSetterName(PropertyInfo pi) {
return !pi.isGenerator && !pi.isAsync && pi.key instanceof Identifier && this.type != TokenType.parenL && (((Identifier) pi.key).getName().equals("get") || ((Identifier) pi.key).getName().equals("set"));
}
/**
* Parse a method declaration in a class, assuming that its name has already been consumed.
*/
@@ -3047,7 +3051,7 @@ public class Parser {
pi.kind = "method";
boolean isGetSet = false;
if (!pi.computed) {
if (!pi.isGenerator && !pi.isAsync && pi.key instanceof Identifier && this.type != TokenType.parenL && (((Identifier) pi.key).getName().equals("get") || ((Identifier) pi.key).getName().equals("set"))) {
if (atGetterSetterName(pi)) {
isGetSet = true;
pi.kind = ((Identifier) pi.key).getName();
this.parsePropertyName(pi);

View File

@@ -146,6 +146,10 @@ public class FlowParser extends ESNextParser {
boolean oldInType = inType;
inType = true;
this.expect(tok == null ? TokenType.colon : tok);
if (this.type == TokenType.modulo) {// an annotation like '%checks' without a preceeding type
inType = oldInType;
return;
}
if (allowLeadingPipeOrAnd) {
if (this.type == TokenType.bitwiseAND || this.type == TokenType.bitwiseOR) {
this.next();
@@ -223,14 +227,29 @@ public class FlowParser extends ESNextParser {
while (this.type != TokenType.braceR) {
Position stmtStart = startLoc;
// todo: declare check
this.next();
if (this.eat(TokenType._import)) {
this.flowParseDeclareImport(stmtStart);
} else {
// todo: declare check
this.next();
this.flowParseDeclare(stmtStart);
this.flowParseDeclare(stmtStart);
}
}
this.expect(TokenType.braceR);
}
private void flowParseDeclareImport(Position stmtStart) {
String kind = flowParseImportSpecifiers();
if (kind == null) {
this.raise(stmtStart, "Imports within a `declare module` body must always be `import type` or `import typeof`.");
}
this.expect(TokenType.name);
this.expectContextual("from");
this.expect(TokenType.string);
this.semicolon();
}
private void flowParseDeclareModuleExports() {
this.expectContextual("module");
this.expect(TokenType.dot);
@@ -737,7 +756,7 @@ public class FlowParser extends ESNextParser {
private void flowParsePostfixType() {
this.flowParsePrimaryType();
if (this.type == TokenType.bracketL) {
while (this.type == TokenType.bracketL) {
this.expect(TokenType.bracketL);
this.expect(TokenType.bracketR);
}
@@ -807,11 +826,20 @@ public class FlowParser extends ESNextParser {
// if allowExpression is true then we're parsing an arrow function and if
// there's a return type then it's been handled elsewhere
this.flowParseTypeAnnotation();
this.flowParseChecksAnnotation();
}
return super.parseFunctionBody(id, params, isArrowFunction);
}
private void flowParseChecksAnnotation() {
// predicate functions with the special '%checks' annotation
if (this.type == TokenType.modulo && lookaheadIsIdent("checks", true)) {
this.next();
this.next();
}
}
// interfaces
@Override
protected Statement parseStatement(boolean declaration, boolean topLevel, Set<String> exports) {
@@ -975,24 +1003,30 @@ public class FlowParser extends ESNextParser {
return param;
}
private String flowParseImportSpecifiers() {
String kind = null;
if (this.type == TokenType._typeof) {
kind = "typeof";
} else if (this.isContextual("type")) {
kind = "type";
}
if (kind != null) {
String lh = lookahead(4);
if (!lh.isEmpty()) {
int c = lh.codePointAt(0);
if ((Identifiers.isIdentifierStart(c, true) && !"from".equals(lh)) || c == '{' || c == '*') {
this.next();
}
}
}
return kind;
}
@Override
protected List<ImportSpecifier> parseImportSpecifiers() {
String kind = null;
if (flow()) {
if (this.type == TokenType._typeof) {
kind = "typeof";
} else if (this.isContextual("type")) {
kind = "type";
}
if (kind != null) {
String lh = lookahead(4);
if (!lh.isEmpty()) {
int c = lh.codePointAt(0);
if ((Identifiers.isIdentifierStart(c, true) && !"from".equals(lh)) || c == '{' || c == '*') {
this.next();
}
}
}
kind = flowParseImportSpecifiers();
}
List<ImportSpecifier> specs = super.parseImportSpecifiers();
@@ -1102,6 +1136,7 @@ public class FlowParser extends ESNextParser {
boolean oldNoAnonFunctionType = noAnonFunctionType;
noAnonFunctionType = true;
flowParseTypeAnnotation();
flowParseChecksAnnotation();
noAnonFunctionType = oldNoAnonFunctionType;
if (this.type != TokenType.arrow)
unexpected();
@@ -1158,4 +1193,12 @@ public class FlowParser extends ESNextParser {
this.eat(TokenType.plusMin);
super.parsePropertyName(result);
}
@Override
protected boolean atGetterSetterName(PropertyInfo pi) {
if (flow() && this.isRelational("<"))
return false;
return super.atGetterSetterName(pi);
}
}

View File

@@ -41,7 +41,7 @@ public class Main {
* such a way that it may produce different tuples for the same file under the same
* {@link ExtractorConfig}.
*/
public static final String EXTRACTOR_VERSION = "2018-11-20";
public static final String EXTRACTOR_VERSION = "2018-11-22_a";
public static final Pattern NEWLINE = Pattern.compile("\n");