Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(372)

Unified Diff: pkg/compiler/lib/src/parser/parser.dart

Issue 1723443003: First step of support for parsing and ignoring generic methods. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Moved text from description to dartdoc Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/compiler/lib/src/parser/parser.dart
diff --git a/pkg/compiler/lib/src/parser/parser.dart b/pkg/compiler/lib/src/parser/parser.dart
index f2e0577937e91946b398e1e8040ae978ff95a8ae..5180873c2d9c74a804953ad1c4ecdef08eed3916 100644
--- a/pkg/compiler/lib/src/parser/parser.dart
+++ b/pkg/compiler/lib/src/parser/parser.dart
@@ -39,6 +39,8 @@ import '../tokens/token_constants.dart' show
EOF_TOKEN,
EQ_TOKEN,
FUNCTION_TOKEN,
+ GT_TOKEN,
+ GT_GT_TOKEN,
HASH_TOKEN,
HEXADECIMAL_TOKEN,
IDENTIFIER_TOKEN,
@@ -94,16 +96,35 @@ class FormalParameterType {
* matches, "star" means zero or more matches. For example,
* [parseMetadataStar] corresponds to this grammar snippet: [:
* metadata* :], and [parseTypeOpt] corresponds to: [: type? :].
+ *
+ * On the extension to support generic methods:
+ *
+ * The syntactic conflict associated with relational expressions is resolved
+ * in favor of generic invocations (for instance, with `foo(m<B, C>(3))` we
+ * parse it as an invocation of `m` with type arguments `B, C` and value
+ * argument `3`, not as two boolean arguments passed to `foo`. It is expected
+ * that this situation is occurs rarely in practice, especially because a comma
+ * or ')' will delimit the expression where we have `3` in the example,
+ * which means that it won't need to be wrapped in parentheses. The work-around
+ * is to add parentheses around the last relational expression (i.e., `(C>(3))`
+ * in the example).
+ *
+ * Relative to the DEP #22 proposal, there is no support for currying
+ * invocations (that is, we cannot evalate a type like `myMethod<T,S>` to
+ * obtain a closure which is non-generic and which will pass the actual values
+ * of `T` and `S` in the given context as type arguments).
*/
class Parser {
final Listener listener;
bool mayParseFunctionExpressions = true;
final bool enableConditionalDirectives;
+ final bool enableGenericMethods;
bool asyncAwaitKeywordsEnabled;
Parser(this.listener,
{this.enableConditionalDirectives: false,
- this.asyncAwaitKeywordsEnabled: false});
+ this.asyncAwaitKeywordsEnabled: false,
+ this.enableGenericMethods: false});
Token parseUnit(Token token) {
listener.beginCompilationUnit(token);
@@ -524,6 +545,68 @@ class Parser {
return false;
}
+ bool isValidTypeArguments(Token token, {bool requireParenthesis: false}) {
+ // To avoid changing the token stream during an investigation of whether
+ // the parser is looking at a `typeArguments` construct, we do not replace
+ // one `>>` token by two `>` tokens (which would destroy a correct
+ // expression like `foo(a < 0, b >> 2)`). Instead, we count levels for the
+ // '<'/'>' brackets directly. This makes sense because the type argument
+ // sublanguage is so tiny:
+ //
+ // typeArguments ::= '<' typeList '>'
+ // typeList ::= type (',' type)*
+ // type ::= typeName typeArguments?
+ // typeName ::= qualified
+ // qualified ::= identifier ('.' identifier)?
+ //
+ // The point is that we check for parenthesis correctness for '<'/'>' plus
+ // membership of the intersection between the following regular languages:
+ //
+ // anything starting with '<' and ending with '>'
+ // (identifier | (identifier '.' identifier) | ',' | '<' | '>' | '>>')*
+ //
+ // Obviously, every correct `typeArguments` will pass this test. However,
+ // some incorrect ones will also pass, e.g., `<K,<<><>>,>`. This should be
+ // safe, but the real solution is of course the following:
+ //
+ // TODO(eernst): Prove that if a token sequence is accepted by this test,
+ // but it is not a correct `typeArguments` then it is a syntax error rather
+ // than a correct continuation of the program that does not contain a
+ // `typeArguments` construct at this point.
+ // If that property does not hold then we will reject some correct programs.
+ if (optional('<', token)) {
Johnni Winther 2016/02/29 10:18:45 Negate this to avoid the else branch: if (!option
eernst 2016/03/09 16:28:13 Done.
+ int angleBracketLevel = 1;
+ token = token.next;
+ while (!identical(token.kind, EOF_TOKEN) && angleBracketLevel > 0) {
+ // Invariant: `angleBracketLevel` is #'<' - #'>' from initial token
+ // to `token`, both included, where #'x' is the number of tokens with
+ // `stringValue` 'x'; in this computation, we consider the one-element
+ // token sequence '>>' equivalent to the two element sequence '>', '>'.
+ final int kind = token.kind;
+ switch (kind) {
+ case IDENTIFIER_TOKEN:
+ if (optional('.', token.next)) {
+ token = token.next.next;
+ if (token.kind != IDENTIFIER_TOKEN) return false;
+ }
+ break;
+ case COMMA_TOKEN: break;
+ case LT_TOKEN: angleBracketLevel++; break;
+ case GT_TOKEN: angleBracketLevel--; break;
+ case GT_GT_TOKEN: angleBracketLevel -= 2; break;
+ default: return false;
+ }
+ token = token.next;
+ }
+ if (angleBracketLevel != 0) return false;
+ return requireParenthesis
+ ? token.kind == OPEN_PAREN_TOKEN
+ : true;
+ } else {
+ return false;
+ }
+ }
+
Token parseQualified(Token token) {
token = parseIdentifier(token);
while (optional('.', token)) {
@@ -540,6 +623,14 @@ class Parser {
}
}
+ Token parseAndIgnoreQualifiedRestOpt(Token token) {
+ if (optional('.', token)) {
+ return parseAndIgnoreQualifiedRest(token);
+ } else {
+ return token;
+ }
+ }
+
Token parseQualifiedRest(Token token) {
assert(optional('.', token));
Token period = token;
@@ -548,6 +639,12 @@ class Parser {
return token;
}
+ Token parseAndIgnoreQualifiedRest(Token token) {
+ assert(optional('.', token));
+ token = parseAndIgnoreIdentifier(token.next);
+ return token;
+ }
+
Token skipBlock(Token token) {
if (!optional('{', token)) {
return listener.expectedBlockToSkip(token);
@@ -679,6 +776,13 @@ class Parser {
return token.next;
}
+ Token parseAndIgnoreIdentifier(Token token) {
+ if (!token.isIdentifier()) {
+ token = listener.expectedIdentifier(token);
+ }
+ return token.next;
+ }
+
Token expect(String string, Token token) {
if (!identical(string, token.stringValue)) {
return listener.expected(string, token);
@@ -698,6 +802,14 @@ class Parser {
return token;
}
+ Token parseAndIgnoreTypeParameter(Token token) {
+ token = parseAndIgnoreIdentifier(token);
+ if (optional('extends', token)) {
+ token = parseAndIgnoreType(token.next);
+ }
+ return token;
+ }
+
/**
* Returns true if the stringValue of the [token] is [value].
*/
@@ -748,6 +860,17 @@ class Parser {
return token;
}
+ Token parseAndIgnoreType(Token token) {
+ if (isValidTypeReference(token)) {
+ token = parseAndIgnoreIdentifier(token);
+ token = parseAndIgnoreQualifiedRestOpt(token);
+ } else {
+ token = listener.expectedType(token);
+ }
+ token = parseAndIgnoreTypeArgumentsOpt(token);
+ return token;
+ }
+
Token parseTypeArgumentsOpt(Token token) {
return parseStuff(token,
(t) => listener.beginTypeArguments(t),
@@ -756,6 +879,19 @@ class Parser {
(t) => listener.handleNoTypeArguments(t));
}
+ Token parseAndIgnoreTypeArgumentsOpt(Token token,
+ {bool requireParenthesis: false}) {
+ if (isValidTypeArguments(token, requireParenthesis: requireParenthesis)) {
+ return parseStuff(token,
+ (t) {},
+ (t) => parseAndIgnoreType(t),
+ (c, bt, et) {},
+ (t) {});
+ } else {
+ return token;
+ }
+ }
+
Token parseTypeVariablesOpt(Token token) {
return parseStuff(token,
(t) => listener.beginTypeVariables(t),
@@ -764,6 +900,14 @@ class Parser {
(t) => listener.handleNoTypeVariables(t));
}
+ Token parseAndIgnoreTypeParametersOpt(Token token) {
+ return parseStuff(token,
+ (t) {},
+ (t) => parseAndIgnoreTypeParameter(t),
+ (c, bt, et) {},
+ (t) {});
+ }
+
// TODO(ahe): Clean this up.
Token parseStuff(Token token, Function beginStuff, Function stuffParser,
Function endStuff, Function handleNoStuff) {
@@ -780,10 +924,6 @@ class Parser {
token = new SymbolToken(GT_INFO, token.charOffset);
token.next = new SymbolToken(GT_INFO, token.charOffset + 1);
token.next.next = next;
- } else if (identical(token.stringValue, '>>>')) {
- token = new SymbolToken(GT_INFO, token.charOffset);
- token.next = new SymbolToken(GT_GT_INFO, token.charOffset + 1);
- token.next.next = next;
}
endStuff(count, begin, token);
return expect('>', token);
@@ -1014,6 +1154,9 @@ class Parser {
}
Token token = parseIdentifier(name);
+ if (enableGenericMethods && getOrSet == null) {
+ token = parseAndIgnoreTypeParametersOpt(token);
+ }
token = parseFormalParametersOpt(token);
bool previousAsyncAwaitKeywordsEnabled = asyncAwaitKeywordsEnabled;
token = parseAsyncModifier(token);
@@ -1322,7 +1465,8 @@ class Parser {
// error handling.
final String value = token.stringValue;
if ((identical(value, '(')) || (identical(value, '.'))
- || (identical(value, '{')) || (identical(value, '=>'))) {
+ || (identical(value, '{')) || (identical(value, '=>'))
+ || (enableGenericMethods && identical(value, '<'))) {
isField = false;
break;
} else if (identical(value, ';')) {
@@ -1423,6 +1567,9 @@ class Parser {
}
token = parseQualifiedRestOpt(token);
+ if (enableGenericMethods && getOrSet == null) {
+ token = parseAndIgnoreTypeParametersOpt(token);
+ }
token = parseFormalParametersOpt(token);
token = parseInitializersOpt(token);
bool previousAsyncAwaitKeywordsEnabled = asyncAwaitKeywordsEnabled;
@@ -1509,6 +1656,9 @@ class Parser {
}
token = parseQualifiedRestOpt(token);
listener.endFunctionName(token);
+ if (enableGenericMethods && getOrSet == null) {
+ token = parseAndIgnoreTypeParametersOpt(token);
+ }
token = parseFormalParametersOpt(token);
token = parseInitializersOpt(token);
bool previousAsyncAwaitKeywordsEnabled = asyncAwaitKeywordsEnabled;
@@ -2338,6 +2488,9 @@ class Parser {
Token parseSend(Token token) {
listener.beginSend(token);
token = parseIdentifier(token);
+ if (enableGenericMethods) {
+ token = parseAndIgnoreTypeArgumentsOpt(token, requireParenthesis: true);
+ }
token = parseArgumentsOpt(token);
listener.endSend(token);
return token;

Powered by Google App Engine
This is Rietveld 408576698