Chromium Code Reviews| 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 988cbf32c63d165be72ac4250dba5fc1b794c509..26fa3b1c30a7cb95bb893884052edaeee269d10c 100644 |
| --- a/pkg/compiler/lib/src/parser/parser.dart |
| +++ b/pkg/compiler/lib/src/parser/parser.dart |
| @@ -362,12 +362,20 @@ class Parser { |
| Token parseTypedef(Token token) { |
| Token typedefKeyword = token; |
| - listener.beginFunctionTypeAlias(token); |
| - token = parseReturnTypeOpt(token.next); |
| - token = parseIdentifier(token); |
| - token = parseTypeVariablesOpt(token); |
| - token = parseFormalParameters(token); |
| - listener.endFunctionTypeAlias(typedefKeyword, token); |
| + listener.beginTypedef(token); |
| + Token equals; |
| + if (optional('=', peekAfterNominalType(token.next))) { |
| + token = parseIdentifier(token.next); |
| + token = parseTypeVariablesOpt(token); |
| + equals = expect('=', token); |
|
Johnni Winther
2017/01/02 12:46:53
We are only using [equals] as a marker, but with t
floitsch
2017/01/02 13:48:48
Right. Good catch.
done.
|
| + token = parseType(equals); |
| + } else { |
| + token = parseReturnTypeOpt(token.next); |
| + token = parseIdentifier(token); |
| + token = parseTypeVariablesOpt(token); |
| + token = parseFormalParameters(token); |
| + } |
| + listener.endTypedef(typedefKeyword, equals, token); |
| return expect(';', token); |
| } |
| @@ -398,7 +406,11 @@ class Parser { |
| } |
| } |
| - Token parseFormalParameters(Token token) { |
| + /// Parses the formal parameter list of a function. |
| + /// |
| + /// If [inFunctionType] is true, then the names may be omitted (except for |
| + /// named arguments). If it is false, then the types may be omitted. |
| + Token parseFormalParameters(Token token, {bool inFunctionType: false}) { |
| Token begin = token; |
| listener.beginFormalParameters(begin); |
| expect('(', token); |
| @@ -411,40 +423,64 @@ class Parser { |
| ++parameterCount; |
| String value = token.stringValue; |
| if (identical(value, '[')) { |
| - token = parseOptionalFormalParameters(token, false); |
| + token = parseOptionalFormalParameters( |
| + token, false, inFunctionType: inFunctionType); |
| break; |
| } else if (identical(value, '{')) { |
| - token = parseOptionalFormalParameters(token, true); |
| + token = parseOptionalFormalParameters( |
| + token, true, inFunctionType: inFunctionType); |
| break; |
| } |
| - token = parseFormalParameter(token, FormalParameterType.REQUIRED); |
| + token = parseFormalParameter(token, FormalParameterType.REQUIRED, |
| + inFunctionType: inFunctionType); |
| } while (optional(',', token)); |
| listener.endFormalParameters(parameterCount, begin, token); |
| return expect(')', token); |
| } |
| - Token parseFormalParameter(Token token, FormalParameterType type) { |
| + Token parseFormalParameter(Token token, FormalParameterType type, |
| + {bool inFunctionType}) { |
| token = parseMetadataStar(token, forParameter: true); |
| listener.beginFormalParameter(token); |
| token = parseModifiers(token); |
| - // TODO(ahe): Validate that there are formal parameters if void. |
| - token = parseReturnTypeOpt(token); |
| + bool isNamedParameter = type == FormalParameterType.NAMED; |
| + |
| Token thisKeyword = null; |
| - if (optional('this', token)) { |
| - thisKeyword = token; |
| - // TODO(ahe): Validate field initializers are only used in |
| - // constructors, and not for function-typed arguments. |
| - token = expect('.', token.next); |
| + if (inFunctionType && isNamedParameter) { |
| + token = parseTypeOpt(token); |
| + token = parseIdentifier(token); |
| + } else if (inFunctionType) { |
| + token = parseType(token); |
| + if (token.isIdentifier()) { |
| + token = parseIdentifier(token); |
| + } else { |
| + listener.handleNoName(token); |
| + } |
| + } else { |
| + // TODO(ahe): Validate that there are formal parameters if void. |
|
Johnni Winther
2017/01/02 12:46:53
Remove. This is checked in resolution.
floitsch
2017/01/02 13:48:48
Done.
|
| + token = parseReturnTypeOpt(token); |
| + if (optional('this', token)) { |
| + thisKeyword = token; |
| + // TODO(ahe): Validate field initializers are only used in |
| + // constructors, and not for function-typed arguments. |
|
Johnni Winther
2017/01/02 12:46:53
Ditto.
floitsch
2017/01/02 13:48:48
Done.
|
| + token = expect('.', token.next); |
| + } |
| + token = parseIdentifier(token); |
| } |
| - token = parseIdentifier(token); |
| - if (optional('(', token)) { |
| - listener.handleNoTypeVariables(token); |
| - token = parseFormalParameters(token); |
| - listener.handleFunctionTypedFormalParameter(token); |
| - } else if (optional('<', token)) { |
| - token = parseTypeVariablesOpt(token); |
| - token = parseFormalParameters(token); |
| - listener.handleFunctionTypedFormalParameter(token); |
| + |
| + // Generalized function types don't allow inline function types. |
| + // The following isn't allowed: |
| + // int Function(int bar(String x)). |
| + if (!inFunctionType) { |
| + if (optional('(', token)) { |
| + listener.handleNoTypeVariables(token); |
| + token = parseFormalParameters(token); |
| + listener.handleFunctionTypedFormalParameter(token); |
| + } else if (optional('<', token)) { |
| + token = parseTypeVariablesOpt(token); |
| + token = parseFormalParameters(token); |
| + listener.handleFunctionTypedFormalParameter(token); |
| + } |
| } |
| String value = token.stringValue; |
| if ((identical('=', value)) || (identical(':', value))) { |
| @@ -464,7 +500,8 @@ class Parser { |
| return token; |
| } |
| - Token parseOptionalFormalParameters(Token token, bool isNamed) { |
| + Token parseOptionalFormalParameters(Token token, bool isNamed, |
| + {bool inFunctionType}) { |
| Token begin = token; |
| listener.beginOptionalFormalParameters(begin); |
| assert((isNamed && optional('{', token)) || optional('[', token)); |
| @@ -478,7 +515,8 @@ class Parser { |
| } |
| var type = |
| isNamed ? FormalParameterType.NAMED : FormalParameterType.POSITIONAL; |
| - token = parseFormalParameter(token, type); |
| + token = |
| + parseFormalParameter(token, type, inFunctionType: inFunctionType); |
| ++parameterCount; |
| } while (optional(',', token)); |
| if (parameterCount == 0) { |
| @@ -497,6 +535,10 @@ class Parser { |
| } |
| Token parseTypeOpt(Token token) { |
| + if (isGeneralizedFunctionType(token)) { |
| + // Function type without return type. |
| + return parseType(token); |
| + } |
| Token peek = peekAfterIfType(token); |
| if (peek != null && (peek.isIdentifier() || optional('this', peek))) { |
| return parseType(token); |
| @@ -661,7 +703,7 @@ class Parser { |
| token = token.next; |
| } |
| Token classKeyword = token; |
| - var isMixinApplication = optional('=', peekAfterType(token.next)); |
| + var isMixinApplication = optional('=', peekAfterNominalType(token.next)); |
| if (isMixinApplication) { |
| listener.beginNamedMixinApplication(begin); |
| } else { |
| @@ -702,7 +744,7 @@ class Parser { |
| Token extendsKeyword; |
| if (optional('extends', token)) { |
| extendsKeyword = token; |
| - if (optional('with', peekAfterType(token.next))) { |
| + if (optional('with', peekAfterNominalType(token.next))) { |
| token = parseMixinApplication(token.next); |
| } else { |
| token = parseType(token.next); |
| @@ -800,16 +842,53 @@ class Parser { |
| !identical(value, token.stringValue); |
| } |
| + bool isGeneralizedFunctionType(Token token) { |
| + // TODO(floitsch): don't use string comparison, but the keyword-state |
| + // table is currently not set up to deal with upper-case characters. |
| + return (optional('<', token.next) || optional('(', token.next)) && |
| + token.value == "Function"; |
| + } |
| + |
| Token parseType(Token token) { |
| Token begin = token; |
| - if (isValidTypeReference(token)) { |
| - token = parseIdentifier(token); |
| - token = parseQualifiedRestOpt(token); |
| + if (isGeneralizedFunctionType(token)) { |
| + // A function type without return type. |
| + // Push the non-existing return type first. The loop below will |
| + // generate the full type. |
| + listener.handleNoType(token); |
| } else { |
| - token = listener.expectedType(token); |
| + if (isValidTypeReference(token)) { |
| + token = parseIdentifier(token); |
| + token = parseQualifiedRestOpt(token); |
| + } else { |
| + token = listener.expectedType(token); |
| + } |
| + token = parseTypeArgumentsOpt(token); |
| + listener.endType(begin, token); |
| + } |
| + |
| + // While we see a `Function(` treat the pushed type as return type. |
| + // For example: `int Function() Function(int) Function(String x)`. |
| + while (isGeneralizedFunctionType(token)) { |
| + token = parseFunctionType(token); |
| } |
| - token = parseTypeArgumentsOpt(token); |
| - listener.endType(begin, token); |
| + return token; |
| + } |
| + |
| + /// Parses a generalized function type. |
| + /// |
| + /// The return type must already be pushed. |
| + Token parseFunctionType(Token token) { |
| + // TODO(floitsch): don't use string comparison, but the keyword-state |
| + // table is currently not set up to deal with upper-case characters. |
| + if (token.value != "Function") { |
| + return listener.expected("Function", token); |
| + } |
| + Token functionToken = token; |
| + token = token.next; |
| + token = parseTypeVariablesOpt(token); |
| + token = parseFormalParameters(token, inFunctionType: true); |
| + listener.endFunctionType(functionToken, token); |
| return token; |
| } |
| @@ -1146,26 +1225,55 @@ class Parser { |
| hasName = true; |
| } |
| identifiers = identifiers.prepend(token); |
| - if (isValidTypeReference(token)) { |
| - // type ... |
| - if (optional('.', token.next)) { |
| - // type '.' ... |
| - if (token.next.next.isIdentifier()) { |
| - // type '.' identifier |
| - token = token.next.next; |
| + |
| + if (!isGeneralizedFunctionType(token)) { |
| + // Read a potential return type. |
| + if (isValidTypeReference(token)) { |
| + // type ... |
| + if (optional('.', token.next)) { |
| + // type '.' ... |
| + if (token.next.next.isIdentifier()) { |
| + // type '.' identifier |
| + token = token.next.next; |
| + } |
| + } |
| + if (optional('<', token.next)) { |
| + if (token.next is BeginGroupToken) { |
| + BeginGroupToken beginGroup = token.next; |
| + if (beginGroup.endGroup == null) { |
| + listener.unmatched(beginGroup); |
| + } |
| + token = beginGroup.endGroup; |
| + } |
| } |
| } |
| - if (optional('<', token.next)) { |
| - if (token.next is BeginGroupToken) { |
| - BeginGroupToken beginGroup = token.next; |
| + token = token.next; |
| + } |
| + while (isGeneralizedFunctionType(token)) { |
| + token = token.next; |
| + if (optional('<', token)) { |
| + if (token is BeginGroupToken) { |
| + BeginGroupToken beginGroup = token; |
| if (beginGroup.endGroup == null) { |
| listener.unmatched(beginGroup); |
| } |
| - token = beginGroup.endGroup; |
| + token = beginGroup.endGroup.next; |
| + } |
| + } |
| + if (!optional('(', token)) { |
| + if (optional(';', token)) { |
| + listener.recoverableError(token, "expected '('"); |
| } |
| + token = listener.unexpected(token); |
| + } |
| + if (token is BeginGroupToken) { |
| + BeginGroupToken beginGroup = token; |
| + if (beginGroup.endGroup == null) { |
| + listener.unmatched(beginGroup); |
| + } |
| + token = beginGroup.endGroup.next; |
| } |
| } |
| - token = token.next; |
| } |
| return const Link<Token>(); |
| } |
| @@ -1263,11 +1371,31 @@ class Parser { |
| /** |
| * Returns the first token after the type starting at [token]. |
| + * |
| * This method assumes that [token] is an identifier (or void). |
| * Use [peekAfterIfType] if [token] isn't known to be an identifier. |
| */ |
| Token peekAfterType(Token token) { |
| // We are looking at "identifier ...". |
| + Token peek = token; |
| + if (!isGeneralizedFunctionType(token)) { |
| + peek = peekAfterNominalType(token); |
| + } |
| + |
| + // We might have just skipped over the return value of the function type. |
| + // Check again, if we are now at a function type position. |
| + while (isGeneralizedFunctionType(peek)) { |
| + peek = peekAfterFunctionType(peek); |
| + } |
| + return peek; |
| + } |
| + |
| + /** |
| + * Returns the first token after the nominal type starting at [token]. |
| + * |
| + * This method assumes that [token] is an identifier (or void). |
| + */ |
| + Token peekAfterNominalType(Token token) { |
| Token peek = token.next; |
| if (identical(peek.kind, PERIOD_TOKEN)) { |
| if (peek.next.isIdentifier()) { |
| @@ -1283,13 +1411,57 @@ class Parser { |
| Token gtToken = beginGroupToken.endGroup; |
| if (gtToken != null) { |
| // We are looking at "qualified '<' ... '>' ...". |
| - return gtToken.next; |
| + peek = gtToken.next; |
| } |
| } |
| return peek; |
| } |
| /** |
| + * Returns the first token after the function type starting at [token]. |
| + * |
| + * The token must be at the `Function` token position. That is, the return |
| + * type must have already been skipped. |
| + * |
| + * This function only skips over one function type syntax. |
| + * If necessary, this function must be called multiple times. |
| + * |
| + * Example: |
| + * ``` |
| + * int Function() Function<T>(int) |
| + * ^ ^ |
| + * A call to this function must be at one of the `Function` tokens. |
| + * If `token` pointed to the first `Function` token, then the returned |
| + * token points to the second `Function` token. |
| + */ |
| + Token peekAfterFunctionType(Token token) { |
| + // Possible inputs are: |
| + // Function( ... ) |
| + // Function< ... >( ... ) |
| + |
| + Token peek = token.next; // Skip over the Function token. |
| + // If there is a generic argument to the function, skip over that one first. |
| + if (identical(peek.kind, LT_TOKEN)) { |
| + BeginGroupToken beginGroupToken = peek; |
| + Token closeToken = beginGroupToken.endGroup; |
| + if (closeToken != null) { |
| + peek = closeToken.next; |
| + } |
| + } |
| + |
| + // Now we just need to skip over the formals. |
| + expect('(', peek); |
| + |
| + BeginGroupToken beginGroupToken = peek; |
| + Token closeToken = beginGroupToken.endGroup; |
| + if (closeToken != null) { |
| + peek = closeToken.next; |
| + } |
| + |
| + return peek; |
| + } |
| + |
| + /** |
| * If [token] is the start of a type, returns the token after that type. |
| * If [token] is not the start of a type, null is returned. |
| */ |