| 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
|
| deleted file mode 100644
|
| index 0ef9cb8373d292e82d3f55ef70d7bd8d86addb3b..0000000000000000000000000000000000000000
|
| --- a/pkg/compiler/lib/src/parser/parser.dart
|
| +++ /dev/null
|
| @@ -1,3007 +0,0 @@
|
| -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -library dart2js.parser;
|
| -
|
| -import '../common.dart';
|
| -import '../tokens/keyword.dart' show Keyword;
|
| -import '../tokens/precedence.dart' show PrecedenceInfo;
|
| -import '../tokens/precedence_constants.dart'
|
| - show
|
| - AS_INFO,
|
| - ASSIGNMENT_PRECEDENCE,
|
| - CASCADE_PRECEDENCE,
|
| - EQUALITY_PRECEDENCE,
|
| - GT_INFO,
|
| - IS_INFO,
|
| - MINUS_MINUS_INFO,
|
| - OPEN_PAREN_INFO,
|
| - OPEN_SQUARE_BRACKET_INFO,
|
| - PERIOD_INFO,
|
| - PLUS_PLUS_INFO,
|
| - POSTFIX_PRECEDENCE,
|
| - QUESTION_INFO,
|
| - QUESTION_PERIOD_INFO,
|
| - RELATIONAL_PRECEDENCE;
|
| -import '../tokens/token.dart'
|
| - show
|
| - BeginGroupToken,
|
| - isUserDefinableOperator,
|
| - KeywordToken,
|
| - SymbolToken,
|
| - Token;
|
| -import '../tokens/token_constants.dart'
|
| - show
|
| - BAD_INPUT_TOKEN,
|
| - COMMA_TOKEN,
|
| - DOUBLE_TOKEN,
|
| - EOF_TOKEN,
|
| - EQ_TOKEN,
|
| - FUNCTION_TOKEN,
|
| - GT_TOKEN,
|
| - GT_GT_TOKEN,
|
| - HASH_TOKEN,
|
| - HEXADECIMAL_TOKEN,
|
| - IDENTIFIER_TOKEN,
|
| - INT_TOKEN,
|
| - KEYWORD_TOKEN,
|
| - LT_TOKEN,
|
| - OPEN_CURLY_BRACKET_TOKEN,
|
| - OPEN_PAREN_TOKEN,
|
| - OPEN_SQUARE_BRACKET_TOKEN,
|
| - PERIOD_TOKEN,
|
| - SEMICOLON_TOKEN,
|
| - STRING_INTERPOLATION_IDENTIFIER_TOKEN,
|
| - STRING_INTERPOLATION_TOKEN,
|
| - STRING_TOKEN;
|
| -import '../util/characters.dart' as Characters show $CLOSE_CURLY_BRACKET;
|
| -import '../util/util.dart' show Link;
|
| -import 'listener.dart' show Listener;
|
| -
|
| -class FormalParameterType {
|
| - final String type;
|
| - const FormalParameterType(this.type);
|
| - bool get isRequired => this == REQUIRED;
|
| - bool get isPositional => this == POSITIONAL;
|
| - bool get isNamed => this == NAMED;
|
| - static final REQUIRED = const FormalParameterType('required');
|
| - static final POSITIONAL = const FormalParameterType('positional');
|
| - static final NAMED = const FormalParameterType('named');
|
| -}
|
| -
|
| -/**
|
| - * An event generating parser of Dart programs. This parser expects
|
| - * all tokens in a linked list (aka a token stream).
|
| - *
|
| - * The class [Scanner] is used to generate a token stream. See the
|
| - * file scanner.dart.
|
| - *
|
| - * Subclasses of the class [Listener] are used to listen to events.
|
| - *
|
| - * Most methods of this class belong in one of two major categories:
|
| - * parse metods and peek methods. Parse methods all have the prefix
|
| - * parse, and peek methods all have the prefix peek.
|
| - *
|
| - * Parse methods generate events (by calling methods on [listener])
|
| - * and return the next token to parse. Peek methods do not generate
|
| - * events (except for errors) and may return null.
|
| - *
|
| - * Parse methods are generally named parseGrammarProductionSuffix. The
|
| - * suffix can be one of "opt", or "star". "opt" means zero or one
|
| - * matches, "star" means zero or more matches. For example,
|
| - * [parseMetadataStar] corresponds to this grammar snippet: [:
|
| - * metadata* :], and [parseTypeOpt] corresponds to: [: type? :].
|
| - */
|
| -class Parser {
|
| - final Listener listener;
|
| - bool mayParseFunctionExpressions = true;
|
| - bool asyncAwaitKeywordsEnabled;
|
| -
|
| - Parser(this.listener, {this.asyncAwaitKeywordsEnabled: false});
|
| -
|
| - Token parseUnit(Token token) {
|
| - listener.beginCompilationUnit(token);
|
| - int count = 0;
|
| - while (!identical(token.kind, EOF_TOKEN)) {
|
| - token = parseTopLevelDeclaration(token);
|
| - count++;
|
| - }
|
| - listener.endCompilationUnit(count, token);
|
| - return token;
|
| - }
|
| -
|
| - Token parseTopLevelDeclaration(Token token) {
|
| - token = _parseTopLevelDeclaration(token);
|
| - listener.endTopLevelDeclaration(token);
|
| - return token;
|
| - }
|
| -
|
| - Token _parseTopLevelDeclaration(Token token) {
|
| - token = parseMetadataStar(token);
|
| - final String value = token.stringValue;
|
| - if ((identical(value, 'abstract') && optional('class', token.next)) ||
|
| - identical(value, 'class')) {
|
| - return parseClassOrNamedMixinApplication(token);
|
| - } else if (identical(value, 'enum')) {
|
| - return parseEnum(token);
|
| - } else if (identical(value, 'typedef')) {
|
| - return parseTypedef(token);
|
| - } else if (identical(value, 'library')) {
|
| - return parseLibraryName(token);
|
| - } else if (identical(value, 'import')) {
|
| - return parseImport(token);
|
| - } else if (identical(value, 'export')) {
|
| - return parseExport(token);
|
| - } else if (identical(value, 'part')) {
|
| - return parsePartOrPartOf(token);
|
| - } else {
|
| - return parseTopLevelMember(token);
|
| - }
|
| - }
|
| -
|
| - /// library qualified ';'
|
| - Token parseLibraryName(Token token) {
|
| - Token libraryKeyword = token;
|
| - listener.beginLibraryName(libraryKeyword);
|
| - assert(optional('library', token));
|
| - token = parseQualified(token.next);
|
| - Token semicolon = token;
|
| - token = expect(';', token);
|
| - listener.endLibraryName(libraryKeyword, semicolon);
|
| - return token;
|
| - }
|
| -
|
| - /// import uri (if (test) uri)* (as identifier)? combinator* ';'
|
| - Token parseImport(Token token) {
|
| - Token importKeyword = token;
|
| - listener.beginImport(importKeyword);
|
| - assert(optional('import', token));
|
| - token = parseLiteralStringOrRecoverExpression(token.next);
|
| - token = parseConditionalUris(token);
|
| - Token deferredKeyword;
|
| - if (optional('deferred', token)) {
|
| - deferredKeyword = token;
|
| - token = token.next;
|
| - }
|
| - Token asKeyword;
|
| - if (optional('as', token)) {
|
| - asKeyword = token;
|
| - token = parseIdentifier(token.next);
|
| - }
|
| - token = parseCombinators(token);
|
| - Token semicolon = token;
|
| - token = expect(';', token);
|
| - listener.endImport(importKeyword, deferredKeyword, asKeyword, semicolon);
|
| - return token;
|
| - }
|
| -
|
| - /// if (test) uri
|
| - Token parseConditionalUris(Token token) {
|
| - listener.beginConditionalUris(token);
|
| - int count = 0;
|
| - while (optional('if', token)) {
|
| - count++;
|
| - token = parseConditionalUri(token);
|
| - }
|
| - listener.endConditionalUris(count);
|
| - return token;
|
| - }
|
| -
|
| - Token parseConditionalUri(Token token) {
|
| - listener.beginConditionalUri(token);
|
| - Token ifKeyword = token;
|
| - token = expect('if', token);
|
| - token = expect('(', token);
|
| - token = parseDottedName(token);
|
| - Token equalitySign;
|
| - if (optional('==', token)) {
|
| - equalitySign = token;
|
| - token = parseLiteralStringOrRecoverExpression(token.next);
|
| - }
|
| - token = expect(')', token);
|
| - token = parseLiteralStringOrRecoverExpression(token);
|
| - listener.endConditionalUri(ifKeyword, equalitySign);
|
| - return token;
|
| - }
|
| -
|
| - Token parseDottedName(Token token) {
|
| - listener.beginDottedName(token);
|
| - Token firstIdentifier = token;
|
| - token = parseIdentifier(token);
|
| - int count = 1;
|
| - while (optional('.', token)) {
|
| - token = parseIdentifier(token.next);
|
| - count++;
|
| - }
|
| - listener.endDottedName(count, firstIdentifier);
|
| - return token;
|
| - }
|
| -
|
| - /// export uri conditional-uris* combinator* ';'
|
| - Token parseExport(Token token) {
|
| - Token exportKeyword = token;
|
| - listener.beginExport(exportKeyword);
|
| - assert(optional('export', token));
|
| - token = parseLiteralStringOrRecoverExpression(token.next);
|
| - token = parseConditionalUris(token);
|
| - token = parseCombinators(token);
|
| - Token semicolon = token;
|
| - token = expect(';', token);
|
| - listener.endExport(exportKeyword, semicolon);
|
| - return token;
|
| - }
|
| -
|
| - Token parseCombinators(Token token) {
|
| - listener.beginCombinators(token);
|
| - int count = 0;
|
| - while (true) {
|
| - String value = token.stringValue;
|
| - if (identical('hide', value)) {
|
| - token = parseHide(token);
|
| - } else if (identical('show', value)) {
|
| - token = parseShow(token);
|
| - } else {
|
| - listener.endCombinators(count);
|
| - break;
|
| - }
|
| - count++;
|
| - }
|
| - return token;
|
| - }
|
| -
|
| - /// hide identifierList
|
| - Token parseHide(Token token) {
|
| - Token hideKeyword = token;
|
| - listener.beginHide(hideKeyword);
|
| - assert(optional('hide', token));
|
| - token = parseIdentifierList(token.next);
|
| - listener.endHide(hideKeyword);
|
| - return token;
|
| - }
|
| -
|
| - /// show identifierList
|
| - Token parseShow(Token token) {
|
| - Token showKeyword = token;
|
| - listener.beginShow(showKeyword);
|
| - assert(optional('show', token));
|
| - token = parseIdentifierList(token.next);
|
| - listener.endShow(showKeyword);
|
| - return token;
|
| - }
|
| -
|
| - /// identifier (, identifier)*
|
| - Token parseIdentifierList(Token token) {
|
| - listener.beginIdentifierList(token);
|
| - token = parseIdentifier(token);
|
| - int count = 1;
|
| - while (optional(',', token)) {
|
| - token = parseIdentifier(token.next);
|
| - count++;
|
| - }
|
| - listener.endIdentifierList(count);
|
| - return token;
|
| - }
|
| -
|
| - /// type (, type)*
|
| - Token parseTypeList(Token token) {
|
| - listener.beginTypeList(token);
|
| - token = parseType(token);
|
| - int count = 1;
|
| - while (optional(',', token)) {
|
| - token = parseType(token.next);
|
| - count++;
|
| - }
|
| - listener.endTypeList(count);
|
| - return token;
|
| - }
|
| -
|
| - Token parsePartOrPartOf(Token token) {
|
| - assert(optional('part', token));
|
| - if (optional('of', token.next)) {
|
| - return parsePartOf(token);
|
| - } else {
|
| - return parsePart(token);
|
| - }
|
| - }
|
| -
|
| - Token parsePart(Token token) {
|
| - Token partKeyword = token;
|
| - listener.beginPart(token);
|
| - assert(optional('part', token));
|
| - token = parseLiteralStringOrRecoverExpression(token.next);
|
| - Token semicolon = token;
|
| - token = expect(';', token);
|
| - listener.endPart(partKeyword, semicolon);
|
| - return token;
|
| - }
|
| -
|
| - Token parsePartOf(Token token) {
|
| - listener.beginPartOf(token);
|
| - assert(optional('part', token));
|
| - assert(optional('of', token.next));
|
| - Token partKeyword = token;
|
| - token = parseQualified(token.next.next);
|
| - Token semicolon = token;
|
| - token = expect(';', token);
|
| - listener.endPartOf(partKeyword, semicolon);
|
| - return token;
|
| - }
|
| -
|
| - Token parseMetadataStar(Token token, {bool forParameter: false}) {
|
| - listener.beginMetadataStar(token);
|
| - int count = 0;
|
| - while (optional('@', token)) {
|
| - token = parseMetadata(token);
|
| - count++;
|
| - }
|
| - listener.endMetadataStar(count, forParameter);
|
| - return token;
|
| - }
|
| -
|
| - /**
|
| - * Parse
|
| - * [: '@' qualified (‘.’ identifier)? (arguments)? :]
|
| - */
|
| - Token parseMetadata(Token token) {
|
| - listener.beginMetadata(token);
|
| - Token atToken = token;
|
| - assert(optional('@', token));
|
| - token = parseIdentifier(token.next);
|
| - token = parseQualifiedRestOpt(token);
|
| - token = parseTypeArgumentsOpt(token);
|
| - Token period = null;
|
| - if (optional('.', token)) {
|
| - period = token;
|
| - token = parseIdentifier(token.next);
|
| - }
|
| - token = parseArgumentsOpt(token);
|
| - listener.endMetadata(atToken, period, token);
|
| - return token;
|
| - }
|
| -
|
| - 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);
|
| - return expect(';', token);
|
| - }
|
| -
|
| - Token parseMixinApplication(Token token) {
|
| - listener.beginMixinApplication(token);
|
| - token = parseType(token);
|
| - token = expect('with', token);
|
| - token = parseTypeList(token);
|
| - listener.endMixinApplication();
|
| - return token;
|
| - }
|
| -
|
| - Token parseReturnTypeOpt(Token token) {
|
| - if (identical(token.stringValue, 'void')) {
|
| - listener.handleVoidKeyword(token);
|
| - return token.next;
|
| - } else {
|
| - return parseTypeOpt(token);
|
| - }
|
| - }
|
| -
|
| - Token parseFormalParametersOpt(Token token) {
|
| - if (optional('(', token)) {
|
| - return parseFormalParameters(token);
|
| - } else {
|
| - listener.handleNoFormalParameters(token);
|
| - return token;
|
| - }
|
| - }
|
| -
|
| - Token parseFormalParameters(Token token) {
|
| - Token begin = token;
|
| - listener.beginFormalParameters(begin);
|
| - expect('(', token);
|
| - int parameterCount = 0;
|
| - do {
|
| - token = token.next;
|
| - if (optional(')', token)) {
|
| - break;
|
| - }
|
| - ++parameterCount;
|
| - String value = token.stringValue;
|
| - if (identical(value, '[')) {
|
| - token = parseOptionalFormalParameters(token, false);
|
| - break;
|
| - } else if (identical(value, '{')) {
|
| - token = parseOptionalFormalParameters(token, true);
|
| - break;
|
| - }
|
| - token = parseFormalParameter(token, FormalParameterType.REQUIRED);
|
| - } while (optional(',', token));
|
| - listener.endFormalParameters(parameterCount, begin, token);
|
| - return expect(')', token);
|
| - }
|
| -
|
| - Token parseFormalParameter(Token token, FormalParameterType type) {
|
| - token = parseMetadataStar(token, forParameter: true);
|
| - listener.beginFormalParameter(token);
|
| -
|
| - // Skip over `covariant` token, if the next token is an identifier or
|
| - // modifier.
|
| - // This enables the case where `covariant` is the name of the parameter:
|
| - // void foo(covariant);
|
| - if (identical(token.stringValue, 'covariant') &&
|
| - (token.next.isIdentifier() || isModifier(token.next))) {
|
| - token = token.next;
|
| - }
|
| - token = parseModifiers(token);
|
| - // TODO(ahe): Validate that there are formal parameters if void.
|
| - token = parseReturnTypeOpt(token);
|
| - 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);
|
| - }
|
| - 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);
|
| - }
|
| - String value = token.stringValue;
|
| - if ((identical('=', value)) || (identical(':', value))) {
|
| - // TODO(ahe): Validate that these are only used for optional parameters.
|
| - Token equal = token;
|
| - token = parseExpression(token.next);
|
| - listener.handleValuedFormalParameter(equal, token);
|
| - if (type.isRequired) {
|
| - listener.reportError(
|
| - equal, MessageKind.REQUIRED_PARAMETER_WITH_DEFAULT);
|
| - } else if (type.isPositional && identical(':', value)) {
|
| - listener.reportError(
|
| - equal, MessageKind.POSITIONAL_PARAMETER_WITH_EQUALS);
|
| - }
|
| - }
|
| - listener.endFormalParameter(thisKeyword);
|
| - return token;
|
| - }
|
| -
|
| - Token parseOptionalFormalParameters(Token token, bool isNamed) {
|
| - Token begin = token;
|
| - listener.beginOptionalFormalParameters(begin);
|
| - assert((isNamed && optional('{', token)) || optional('[', token));
|
| - int parameterCount = 0;
|
| - do {
|
| - token = token.next;
|
| - if (isNamed && optional('}', token)) {
|
| - break;
|
| - } else if (!isNamed && optional(']', token)) {
|
| - break;
|
| - }
|
| - var type =
|
| - isNamed ? FormalParameterType.NAMED : FormalParameterType.POSITIONAL;
|
| - token = parseFormalParameter(token, type);
|
| - ++parameterCount;
|
| - } while (optional(',', token));
|
| - if (parameterCount == 0) {
|
| - listener.reportError(
|
| - token,
|
| - isNamed
|
| - ? MessageKind.EMPTY_NAMED_PARAMETER_LIST
|
| - : MessageKind.EMPTY_OPTIONAL_PARAMETER_LIST);
|
| - }
|
| - listener.endOptionalFormalParameters(parameterCount, begin, token);
|
| - if (isNamed) {
|
| - return expect('}', token);
|
| - } else {
|
| - return expect(']', token);
|
| - }
|
| - }
|
| -
|
| - Token parseTypeOpt(Token token) {
|
| - Token peek = peekAfterIfType(token);
|
| - if (peek != null && (peek.isIdentifier() || optional('this', peek))) {
|
| - return parseType(token);
|
| - }
|
| - listener.handleNoType(token);
|
| - return token;
|
| - }
|
| -
|
| - bool isValidTypeReference(Token token) {
|
| - final kind = token.kind;
|
| - if (identical(kind, IDENTIFIER_TOKEN)) return true;
|
| - if (identical(kind, KEYWORD_TOKEN)) {
|
| - Keyword keyword = (token as KeywordToken).keyword;
|
| - String value = keyword.syntax;
|
| - return keyword.isPseudo ||
|
| - (identical(value, 'dynamic')) ||
|
| - (identical(value, 'void'));
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - /// Returns true if [token] matches '<' type (',' type)* '>' '(', and
|
| - /// otherwise returns false. The final '(' is not part of the grammar
|
| - /// construct `typeArguments`, but it is required here such that type
|
| - /// arguments in generic method invocations can be recognized, and as few as
|
| - /// possible other constructs will pass (e.g., 'a < C, D > 3').
|
| - bool isValidMethodTypeArguments(Token token) {
|
| - return tryParseMethodTypeArguments(token) != null;
|
| - }
|
| -
|
| - /// Returns token after match if [token] matches '<' type (',' type)* '>' '(',
|
| - /// and otherwise returns null. Does not produce listener events. With respect
|
| - /// to the final '(', please see the description of
|
| - /// [isValidMethodTypeArguments].
|
| - Token tryParseMethodTypeArguments(Token token) {
|
| - if (!identical(token.kind, LT_TOKEN)) return null;
|
| - BeginGroupToken beginToken = token;
|
| - Token endToken = beginToken.endGroup;
|
| - if (endToken == null || !identical(endToken.next.kind, OPEN_PAREN_TOKEN)) {
|
| - return null;
|
| - }
|
| - token = tryParseType(token.next);
|
| - while (token != null && identical(token.kind, COMMA_TOKEN)) {
|
| - token = tryParseType(token.next);
|
| - }
|
| - if (token == null || !identical(token.kind, GT_TOKEN)) return null;
|
| - return token.next;
|
| - }
|
| -
|
| - /// Returns token after match if [token] matches typeName typeArguments?, and
|
| - /// otherwise returns null. Does not produce listener events.
|
| - Token tryParseType(Token token) {
|
| - token = tryParseQualified(token);
|
| - if (token == null) return null;
|
| - Token tokenAfterQualified = token;
|
| - token = tryParseNestedTypeArguments(token);
|
| - return token == null ? tokenAfterQualified : token;
|
| - }
|
| -
|
| - /// Returns token after match if [token] matches identifier ('.' identifier)?,
|
| - /// and otherwise returns null. Does not produce listener events.
|
| - Token tryParseQualified(Token token) {
|
| - if (!isValidTypeReference(token)) return null;
|
| - token = token.next;
|
| - if (!identical(token.kind, PERIOD_TOKEN)) return token;
|
| - token = token.next;
|
| - if (!identical(token.kind, IDENTIFIER_TOKEN)) return null;
|
| - return token.next;
|
| - }
|
| -
|
| - /// Returns token after match if [token] matches '<' type (',' type)* '>',
|
| - /// and otherwise returns null. Does not produce listener events. The final
|
| - /// '>' may be the first character in a '>>' token, in which case a synthetic
|
| - /// '>' token is created and returned, representing the second '>' in the
|
| - /// '>>' token.
|
| - Token tryParseNestedTypeArguments(Token token) {
|
| - if (!identical(token.kind, LT_TOKEN)) return null;
|
| - // If the initial '<' matches the first '>' in a '>>' token, we will have
|
| - // `token.endGroup == null`, so we cannot rely on `token.endGroup == null`
|
| - // to imply that the match must fail. Hence no `token.endGroup == null`
|
| - // test here.
|
| - token = tryParseType(token.next);
|
| - while (token != null && identical(token.kind, COMMA_TOKEN)) {
|
| - token = tryParseType(token.next);
|
| - }
|
| - if (token == null) return null;
|
| - if (identical(token.kind, GT_TOKEN)) return token.next;
|
| - if (!identical(token.kind, GT_GT_TOKEN)) return null;
|
| - // [token] is '>>' of which the final '>' that we are parsing is the first
|
| - // character. In order to keep the parsing process on track we must return
|
| - // a synthetic '>' corresponding to the second character of that '>>'.
|
| - Token syntheticToken = new SymbolToken(GT_INFO, token.charOffset + 1);
|
| - syntheticToken.next = token.next;
|
| - return syntheticToken;
|
| - }
|
| -
|
| - Token parseQualified(Token token) {
|
| - token = parseIdentifier(token);
|
| - while (optional('.', token)) {
|
| - token = parseQualifiedRest(token);
|
| - }
|
| - return token;
|
| - }
|
| -
|
| - Token parseQualifiedRestOpt(Token token) {
|
| - if (optional('.', token)) {
|
| - return parseQualifiedRest(token);
|
| - } else {
|
| - return token;
|
| - }
|
| - }
|
| -
|
| - Token parseQualifiedRest(Token token) {
|
| - assert(optional('.', token));
|
| - Token period = token;
|
| - token = parseIdentifier(token.next);
|
| - listener.handleQualified(period);
|
| - return token;
|
| - }
|
| -
|
| - Token skipBlock(Token token) {
|
| - if (!optional('{', token)) {
|
| - return listener.expectedBlockToSkip(token);
|
| - }
|
| - BeginGroupToken beginGroupToken = token;
|
| - Token endGroup = beginGroupToken.endGroup;
|
| - if (endGroup == null) {
|
| - return listener.unmatched(beginGroupToken);
|
| - } else if (!identical(endGroup.kind, Characters.$CLOSE_CURLY_BRACKET)) {
|
| - return listener.unmatched(beginGroupToken);
|
| - }
|
| - return beginGroupToken.endGroup;
|
| - }
|
| -
|
| - Token parseEnum(Token token) {
|
| - listener.beginEnum(token);
|
| - Token enumKeyword = token;
|
| - token = parseIdentifier(token.next);
|
| - token = expect('{', token);
|
| - int count = 0;
|
| - if (!optional('}', token)) {
|
| - token = parseIdentifier(token);
|
| - count++;
|
| - while (optional(',', token)) {
|
| - token = token.next;
|
| - if (optional('}', token)) break;
|
| - token = parseIdentifier(token);
|
| - count++;
|
| - }
|
| - }
|
| - Token endBrace = token;
|
| - token = expect('}', token);
|
| - listener.endEnum(enumKeyword, endBrace, count);
|
| - return token;
|
| - }
|
| -
|
| - Token parseClassOrNamedMixinApplication(Token token) {
|
| - Token begin = token;
|
| - Token abstractKeyword;
|
| - if (optional('abstract', token)) {
|
| - abstractKeyword = token;
|
| - token = token.next;
|
| - }
|
| - Token classKeyword = token;
|
| - var isMixinApplication = optional('=', peekAfterType(token.next));
|
| - if (isMixinApplication) {
|
| - listener.beginNamedMixinApplication(begin);
|
| - } else {
|
| - listener.beginClassDeclaration(begin);
|
| - }
|
| -
|
| - int modifierCount = 0;
|
| - if (abstractKeyword != null) {
|
| - parseModifier(abstractKeyword);
|
| - modifierCount++;
|
| - }
|
| - listener.handleModifiers(modifierCount);
|
| -
|
| - if (isMixinApplication) {
|
| - token = parseIdentifier(token.next);
|
| - token = parseTypeVariablesOpt(token);
|
| - token = expect('=', token);
|
| - return parseNamedMixinApplication(token, classKeyword);
|
| - } else {
|
| - return parseClass(begin, classKeyword);
|
| - }
|
| - }
|
| -
|
| - Token parseNamedMixinApplication(Token token, Token classKeyword) {
|
| - token = parseMixinApplication(token);
|
| - Token implementsKeyword = null;
|
| - if (optional('implements', token)) {
|
| - implementsKeyword = token;
|
| - token = parseTypeList(token.next);
|
| - }
|
| - listener.endNamedMixinApplication(classKeyword, implementsKeyword, token);
|
| - return expect(';', token);
|
| - }
|
| -
|
| - Token parseClass(Token begin, Token classKeyword) {
|
| - Token token = parseIdentifier(classKeyword.next);
|
| - token = parseTypeVariablesOpt(token);
|
| - Token extendsKeyword;
|
| - if (optional('extends', token)) {
|
| - extendsKeyword = token;
|
| - if (optional('with', peekAfterType(token.next))) {
|
| - token = parseMixinApplication(token.next);
|
| - } else {
|
| - token = parseType(token.next);
|
| - }
|
| - } else {
|
| - extendsKeyword = null;
|
| - listener.handleNoType(token);
|
| - }
|
| - Token implementsKeyword;
|
| - int interfacesCount = 0;
|
| - if (optional('implements', token)) {
|
| - implementsKeyword = token;
|
| - do {
|
| - token = parseType(token.next);
|
| - ++interfacesCount;
|
| - } while (optional(',', token));
|
| - }
|
| - token = parseClassBody(token);
|
| - listener.endClassDeclaration(
|
| - interfacesCount, begin, extendsKeyword, implementsKeyword, token);
|
| - return token.next;
|
| - }
|
| -
|
| - Token parseStringPart(Token token) {
|
| - if (identical(token.kind, STRING_TOKEN)) {
|
| - listener.handleStringPart(token);
|
| - return token.next;
|
| - } else {
|
| - return listener.expected('string', token);
|
| - }
|
| - }
|
| -
|
| - Token parseIdentifier(Token token) {
|
| - if (!token.isIdentifier()) {
|
| - token = listener.expectedIdentifier(token);
|
| - }
|
| - listener.handleIdentifier(token);
|
| - return token.next;
|
| - }
|
| -
|
| - Token expect(String string, Token token) {
|
| - if (!identical(string, token.stringValue)) {
|
| - return listener.expected(string, token);
|
| - }
|
| - return token.next;
|
| - }
|
| -
|
| - Token parseTypeVariable(Token token) {
|
| - listener.beginTypeVariable(token);
|
| - token = parseIdentifier(token);
|
| - Token extendsOrSuper = null;
|
| - if (optional('extends', token) || optional('super', token)) {
|
| - extendsOrSuper = token;
|
| - token = parseType(token.next);
|
| - } else {
|
| - listener.handleNoType(token);
|
| - }
|
| - listener.endTypeVariable(token, extendsOrSuper);
|
| - return token;
|
| - }
|
| -
|
| - /**
|
| - * Returns true if the stringValue of the [token] is [value].
|
| - */
|
| - bool optional(String value, Token token) {
|
| - return identical(value, token.stringValue);
|
| - }
|
| -
|
| - /**
|
| - * Returns true if the stringValue of the [token] is either [value1],
|
| - * [value2], or [value3].
|
| - */
|
| - bool isOneOf3(Token token, String value1, String value2, String value3) {
|
| - String stringValue = token.stringValue;
|
| - return value1 == stringValue ||
|
| - value2 == stringValue ||
|
| - value3 == stringValue;
|
| - }
|
| -
|
| - /**
|
| - * Returns true if the stringValue of the [token] is either [value1],
|
| - * [value2], [value3], or [value4].
|
| - */
|
| - bool isOneOf4(
|
| - Token token, String value1, String value2, String value3, String value4) {
|
| - String stringValue = token.stringValue;
|
| - return value1 == stringValue ||
|
| - value2 == stringValue ||
|
| - value3 == stringValue ||
|
| - value4 == stringValue;
|
| - }
|
| -
|
| - bool notEofOrValue(String value, Token token) {
|
| - return !identical(token.kind, EOF_TOKEN) &&
|
| - !identical(value, token.stringValue);
|
| - }
|
| -
|
| - Token parseType(Token token) {
|
| - Token begin = token;
|
| - if (isValidTypeReference(token)) {
|
| - token = parseIdentifier(token);
|
| - token = parseQualifiedRestOpt(token);
|
| - } else {
|
| - token = listener.expectedType(token);
|
| - }
|
| - token = parseTypeArgumentsOpt(token);
|
| - listener.endType(begin, token);
|
| - return token;
|
| - }
|
| -
|
| - Token parseTypeArgumentsOpt(Token token) {
|
| - return parseStuff(
|
| - token,
|
| - (t) => listener.beginTypeArguments(t),
|
| - (t) => parseType(t),
|
| - (c, bt, et) => listener.endTypeArguments(c, bt, et),
|
| - (t) => listener.handleNoTypeArguments(t));
|
| - }
|
| -
|
| - Token parseTypeVariablesOpt(Token token) {
|
| - return parseStuff(
|
| - token,
|
| - (t) => listener.beginTypeVariables(t),
|
| - (t) => parseTypeVariable(t),
|
| - (c, bt, et) => listener.endTypeVariables(c, bt, et),
|
| - (t) => listener.handleNoTypeVariables(t));
|
| - }
|
| -
|
| - // TODO(ahe): Clean this up.
|
| - Token parseStuff(Token token, Function beginStuff, Function stuffParser,
|
| - Function endStuff, Function handleNoStuff) {
|
| - if (optional('<', token)) {
|
| - Token begin = token;
|
| - beginStuff(begin);
|
| - int count = 0;
|
| - do {
|
| - token = stuffParser(token.next);
|
| - ++count;
|
| - } while (optional(',', token));
|
| - Token next = token.next;
|
| - if (identical(token.stringValue, '>>')) {
|
| - token = new SymbolToken(GT_INFO, token.charOffset);
|
| - token.next = new SymbolToken(GT_INFO, token.charOffset + 1);
|
| - token.next.next = next;
|
| - }
|
| - endStuff(count, begin, token);
|
| - return expect('>', token);
|
| - }
|
| - handleNoStuff(token);
|
| - return token;
|
| - }
|
| -
|
| - Token parseTopLevelMember(Token token) {
|
| - Token start = token;
|
| - listener.beginTopLevelMember(token);
|
| -
|
| - Link<Token> identifiers = findMemberName(token);
|
| - if (identifiers.isEmpty) {
|
| - return listener.expectedDeclaration(start);
|
| - }
|
| - Token afterName = identifiers.head;
|
| - identifiers = identifiers.tail;
|
| -
|
| - if (identifiers.isEmpty) {
|
| - return listener.expectedDeclaration(start);
|
| - }
|
| - Token name = identifiers.head;
|
| - identifiers = identifiers.tail;
|
| - Token getOrSet;
|
| - if (!identifiers.isEmpty) {
|
| - String value = identifiers.head.stringValue;
|
| - if ((identical(value, 'get')) || (identical(value, 'set'))) {
|
| - getOrSet = identifiers.head;
|
| - identifiers = identifiers.tail;
|
| - }
|
| - }
|
| - Token type;
|
| - if (!identifiers.isEmpty) {
|
| - if (isValidTypeReference(identifiers.head)) {
|
| - type = identifiers.head;
|
| - identifiers = identifiers.tail;
|
| - }
|
| - }
|
| -
|
| - token = afterName;
|
| - bool isField;
|
| - while (true) {
|
| - // Loop to allow the listener to rewrite the token stream for
|
| - // error handling.
|
| - final String value = token.stringValue;
|
| - if ((identical(value, '(')) ||
|
| - (identical(value, '{')) ||
|
| - (identical(value, '=>'))) {
|
| - isField = false;
|
| - break;
|
| - } else if ((identical(value, '=')) || (identical(value, ','))) {
|
| - isField = true;
|
| - break;
|
| - } else if (identical(value, ';')) {
|
| - if (getOrSet != null) {
|
| - // If we found a "get" keyword, this must be an abstract
|
| - // getter.
|
| - isField = (!identical(getOrSet.stringValue, 'get'));
|
| - // TODO(ahe): This feels like a hack.
|
| - } else {
|
| - isField = true;
|
| - }
|
| - break;
|
| - } else {
|
| - token = listener.unexpected(token);
|
| - if (identical(token.kind, EOF_TOKEN)) return token;
|
| - }
|
| - }
|
| - var modifiers = identifiers.reverse();
|
| - return isField
|
| - ? parseFields(start, modifiers, type, getOrSet, name, true)
|
| - : parseTopLevelMethod(start, modifiers, type, getOrSet, name);
|
| - }
|
| -
|
| - bool isVarFinalOrConst(Token token) {
|
| - String value = token.stringValue;
|
| - return identical('var', value) ||
|
| - identical('final', value) ||
|
| - identical('const', value);
|
| - }
|
| -
|
| - Token expectVarFinalOrConst(
|
| - Link<Token> modifiers, bool hasType, bool allowStatic) {
|
| - int modifierCount = 0;
|
| - Token staticModifier;
|
| - if (allowStatic &&
|
| - !modifiers.isEmpty &&
|
| - optional('static', modifiers.head)) {
|
| - staticModifier = modifiers.head;
|
| - modifierCount++;
|
| - parseModifier(staticModifier);
|
| - modifiers = modifiers.tail;
|
| - }
|
| - if (modifiers.isEmpty) {
|
| - listener.handleModifiers(modifierCount);
|
| - return null;
|
| - }
|
| - if (modifiers.tail.isEmpty) {
|
| - Token modifier = modifiers.head;
|
| - if (isVarFinalOrConst(modifier)) {
|
| - modifierCount++;
|
| - parseModifier(modifier);
|
| - listener.handleModifiers(modifierCount);
|
| - // TODO(ahe): The caller checks for "var Type name", perhaps we should
|
| - // check here instead.
|
| - return modifier;
|
| - }
|
| - }
|
| -
|
| - // Slow case to report errors.
|
| - List<Token> modifierList = modifiers.toList();
|
| - Token varFinalOrConst =
|
| - modifierList.firstWhere(isVarFinalOrConst, orElse: () => null);
|
| - if (allowStatic && staticModifier == null) {
|
| - staticModifier = modifierList.firstWhere(
|
| - (modifier) => optional('static', modifier),
|
| - orElse: () => null);
|
| - if (staticModifier != null) {
|
| - modifierCount++;
|
| - parseModifier(staticModifier);
|
| - modifierList.remove(staticModifier);
|
| - }
|
| - }
|
| - bool hasTypeOrModifier = hasType;
|
| - if (varFinalOrConst != null) {
|
| - parseModifier(varFinalOrConst);
|
| - modifierCount++;
|
| - hasTypeOrModifier = true;
|
| - modifierList.remove(varFinalOrConst);
|
| - }
|
| - listener.handleModifiers(modifierCount);
|
| - var kind = hasTypeOrModifier
|
| - ? MessageKind.EXTRANEOUS_MODIFIER
|
| - : MessageKind.EXTRANEOUS_MODIFIER_REPLACE;
|
| - for (Token modifier in modifierList) {
|
| - listener.reportError(modifier, kind, {'modifier': modifier});
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - /// Removes the optional `covariant` token from the modifiers, if there
|
| - /// is no `static` in the list, and `covariant` is the first modifier.
|
| - Link<Token> removeOptCovariantTokenIfNotStatic(Link<Token> modifiers) {
|
| - if (modifiers.isEmpty ||
|
| - !identical(modifiers.first.stringValue, 'covariant')) {
|
| - return modifiers;
|
| - }
|
| - for (Token modifier in modifiers.tail) {
|
| - if (identical(modifier.stringValue, 'static')) {
|
| - return modifiers;
|
| - }
|
| - }
|
| - return modifiers.tail;
|
| - }
|
| -
|
| - Token parseFields(Token start, Link<Token> modifiers, Token type,
|
| - Token getOrSet, Token name, bool isTopLevel) {
|
| - bool hasType = type != null;
|
| -
|
| - if (getOrSet == null && !isTopLevel) {
|
| - modifiers = removeOptCovariantTokenIfNotStatic(modifiers);
|
| - }
|
| -
|
| - Token varFinalOrConst =
|
| - expectVarFinalOrConst(modifiers, hasType, !isTopLevel);
|
| - bool isVar = false;
|
| - bool hasModifier = false;
|
| - if (varFinalOrConst != null) {
|
| - hasModifier = true;
|
| - isVar = optional('var', varFinalOrConst);
|
| - }
|
| -
|
| - if (getOrSet != null) {
|
| - var kind = (hasModifier || hasType)
|
| - ? MessageKind.EXTRANEOUS_MODIFIER
|
| - : MessageKind.EXTRANEOUS_MODIFIER_REPLACE;
|
| - listener.reportError(getOrSet, kind, {'modifier': getOrSet});
|
| - }
|
| -
|
| - if (!hasType) {
|
| - listener.handleNoType(name);
|
| - } else if (optional('void', type)) {
|
| - listener.handleNoType(name);
|
| - // TODO(ahe): This error is reported twice, second time is from
|
| - // [parseVariablesDeclarationMaybeSemicolon] via
|
| - // [PartialFieldListElement.parseNode].
|
| - listener.reportError(type, MessageKind.VOID_NOT_ALLOWED);
|
| - } else {
|
| - parseType(type);
|
| - if (isVar) {
|
| - listener.reportError(modifiers.head, MessageKind.EXTRANEOUS_MODIFIER,
|
| - {'modifier': modifiers.head});
|
| - }
|
| - }
|
| -
|
| - Token token = parseIdentifier(name);
|
| -
|
| - int fieldCount = 1;
|
| - token = parseVariableInitializerOpt(token);
|
| - while (optional(',', token)) {
|
| - token = parseIdentifier(token.next);
|
| - token = parseVariableInitializerOpt(token);
|
| - ++fieldCount;
|
| - }
|
| - Token semicolon = token;
|
| - token = expectSemicolon(token);
|
| - if (isTopLevel) {
|
| - listener.endTopLevelFields(fieldCount, start, semicolon);
|
| - } else {
|
| - listener.endFields(fieldCount, start, semicolon);
|
| - }
|
| - return token;
|
| - }
|
| -
|
| - Token parseTopLevelMethod(Token start, Link<Token> modifiers, Token type,
|
| - Token getOrSet, Token name) {
|
| - Token externalModifier;
|
| - // TODO(johnniwinther): Move error reporting to resolution to give more
|
| - // specific error messages.
|
| - for (Token modifier in modifiers) {
|
| - if (externalModifier == null && optional('external', modifier)) {
|
| - externalModifier = modifier;
|
| - } else {
|
| - listener.reportError(
|
| - modifier, MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier});
|
| - }
|
| - }
|
| - if (externalModifier != null) {
|
| - parseModifier(externalModifier);
|
| - listener.handleModifiers(1);
|
| - } else {
|
| - listener.handleModifiers(0);
|
| - }
|
| -
|
| - if (type == null) {
|
| - listener.handleNoType(name);
|
| - } else {
|
| - parseReturnTypeOpt(type);
|
| - }
|
| - Token token = parseIdentifier(name);
|
| -
|
| - if (getOrSet == null) {
|
| - token = parseTypeVariablesOpt(token);
|
| - } else {
|
| - listener.handleNoTypeVariables(token);
|
| - }
|
| - token = parseFormalParametersOpt(token);
|
| - bool previousAsyncAwaitKeywordsEnabled = asyncAwaitKeywordsEnabled;
|
| - token = parseAsyncModifier(token);
|
| - token = parseFunctionBody(token, false, externalModifier != null);
|
| - asyncAwaitKeywordsEnabled = previousAsyncAwaitKeywordsEnabled;
|
| - Token endToken = token;
|
| - token = token.next;
|
| - if (token.kind == BAD_INPUT_TOKEN) {
|
| - token = listener.unexpected(token);
|
| - }
|
| - listener.endTopLevelMethod(start, getOrSet, endToken);
|
| - return token;
|
| - }
|
| -
|
| - /// Looks ahead to find the name of a member. Returns a link of the modifiers,
|
| - /// set/get, (operator) name, and either the start of the method body or the
|
| - /// end of the declaration.
|
| - ///
|
| - /// Examples:
|
| - ///
|
| - /// int get foo;
|
| - /// results in
|
| - /// [';', 'foo', 'get', 'int']
|
| - ///
|
| - ///
|
| - /// static const List<int> foo = null;
|
| - /// results in
|
| - /// ['=', 'foo', 'List', 'const', 'static']
|
| - ///
|
| - ///
|
| - /// get foo async* { return null }
|
| - /// results in
|
| - /// ['{', 'foo', 'get']
|
| - ///
|
| - ///
|
| - /// operator *(arg) => null;
|
| - /// results in
|
| - /// ['(', '*', 'operator']
|
| - ///
|
| - Link<Token> findMemberName(Token token) {
|
| - Link<Token> identifiers = const Link<Token>();
|
| -
|
| - // `true` if 'get' has been seen.
|
| - bool isGetter = false;
|
| - // `true` if an identifier has been seen after 'get'.
|
| - bool hasName = false;
|
| -
|
| - while (token.kind != EOF_TOKEN) {
|
| - String value = token.stringValue;
|
| - if (value == 'get') {
|
| - isGetter = true;
|
| - } else if (hasName && (value == 'sync' || value == 'async')) {
|
| - // Skip.
|
| - token = token.next;
|
| - value = token.stringValue;
|
| - if (value == '*') {
|
| - // Skip.
|
| - token = token.next;
|
| - }
|
| - continue;
|
| - } else if (value == '(' || value == '{' || value == '=>') {
|
| - // A method.
|
| - identifiers = identifiers.prepend(token);
|
| - return identifiers;
|
| - } else if (value == '=' || value == ';' || value == ',') {
|
| - // A field or abstract getter.
|
| - identifiers = identifiers.prepend(token);
|
| - return identifiers;
|
| - } else if (isGetter) {
|
| - 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 (optional('<', token.next)) {
|
| - if (token.next is BeginGroupToken) {
|
| - BeginGroupToken beginGroup = token.next;
|
| - if (beginGroup.endGroup == null) {
|
| - listener.unmatched(beginGroup);
|
| - }
|
| - token = beginGroup.endGroup;
|
| - }
|
| - }
|
| - }
|
| - token = token.next;
|
| - }
|
| - return const Link<Token>();
|
| - }
|
| -
|
| - Token parseVariableInitializerOpt(Token token) {
|
| - if (optional('=', token)) {
|
| - Token assignment = token;
|
| - listener.beginInitializer(token);
|
| - token = parseExpression(token.next);
|
| - listener.endInitializer(assignment);
|
| - }
|
| - return token;
|
| - }
|
| -
|
| - Token parseInitializersOpt(Token token) {
|
| - if (optional(':', token)) {
|
| - return parseInitializers(token);
|
| - } else {
|
| - listener.handleNoInitializers();
|
| - return token;
|
| - }
|
| - }
|
| -
|
| - Token parseInitializers(Token token) {
|
| - Token begin = token;
|
| - listener.beginInitializers(begin);
|
| - expect(':', token);
|
| - int count = 0;
|
| - bool old = mayParseFunctionExpressions;
|
| - mayParseFunctionExpressions = false;
|
| - do {
|
| - token = parseExpression(token.next);
|
| - ++count;
|
| - } while (optional(',', token));
|
| - mayParseFunctionExpressions = old;
|
| - listener.endInitializers(count, begin, token);
|
| - return token;
|
| - }
|
| -
|
| - Token parseLiteralStringOrRecoverExpression(Token token) {
|
| - if (identical(token.kind, STRING_TOKEN)) {
|
| - return parseLiteralString(token);
|
| - } else {
|
| - listener.recoverableError(token, "unexpected");
|
| - return parseExpression(token);
|
| - }
|
| - }
|
| -
|
| - Token expectSemicolon(Token token) {
|
| - return expect(';', token);
|
| - }
|
| -
|
| - bool isModifier(Token token) {
|
| - final String value = token.stringValue;
|
| - return (identical('final', value)) ||
|
| - (identical('var', value)) ||
|
| - (identical('const', value)) ||
|
| - (identical('abstract', value)) ||
|
| - (identical('static', value)) ||
|
| - (identical('external', value));
|
| - }
|
| -
|
| - Token parseModifier(Token token) {
|
| - assert(isModifier(token));
|
| - listener.handleModifier(token);
|
| - return token.next;
|
| - }
|
| -
|
| - void parseModifierList(Link<Token> tokens) {
|
| - int count = 0;
|
| - for (; !tokens.isEmpty; tokens = tokens.tail) {
|
| - Token token = tokens.head;
|
| - if (isModifier(token)) {
|
| - parseModifier(token);
|
| - } else {
|
| - listener.unexpected(token);
|
| - // Skip the remaining modifiers.
|
| - break;
|
| - }
|
| - count++;
|
| - }
|
| - listener.handleModifiers(count);
|
| - }
|
| -
|
| - Token parseModifiers(Token token) {
|
| - int count = 0;
|
| - while (identical(token.kind, KEYWORD_TOKEN)) {
|
| - if (!isModifier(token)) break;
|
| - token = parseModifier(token);
|
| - count++;
|
| - }
|
| - listener.handleModifiers(count);
|
| - return token;
|
| - }
|
| -
|
| - /**
|
| - * 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.next;
|
| - if (identical(peek.kind, PERIOD_TOKEN)) {
|
| - if (peek.next.isIdentifier()) {
|
| - // Look past a library prefix.
|
| - peek = peek.next.next;
|
| - }
|
| - }
|
| - // We are looking at "qualified ...".
|
| - if (identical(peek.kind, LT_TOKEN)) {
|
| - // Possibly generic type.
|
| - // We are looking at "qualified '<'".
|
| - BeginGroupToken beginGroupToken = peek;
|
| - Token gtToken = beginGroupToken.endGroup;
|
| - if (gtToken != null) {
|
| - // We are looking at "qualified '<' ... '>' ...".
|
| - return gtToken.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.
|
| - */
|
| - Token peekAfterIfType(Token token) {
|
| - if (!optional('void', token) && !token.isIdentifier()) {
|
| - return null;
|
| - }
|
| - return peekAfterType(token);
|
| - }
|
| -
|
| - Token parseClassBody(Token token) {
|
| - Token begin = token;
|
| - listener.beginClassBody(token);
|
| - if (!optional('{', token)) {
|
| - token = listener.expectedClassBody(token);
|
| - }
|
| - token = token.next;
|
| - int count = 0;
|
| - while (notEofOrValue('}', token)) {
|
| - token = parseMember(token);
|
| - ++count;
|
| - }
|
| - expect('}', token);
|
| - listener.endClassBody(count, begin, token);
|
| - return token;
|
| - }
|
| -
|
| - bool isGetOrSet(Token token) {
|
| - final String value = token.stringValue;
|
| - return (identical(value, 'get')) || (identical(value, 'set'));
|
| - }
|
| -
|
| - bool isFactoryDeclaration(Token token) {
|
| - if (optional('external', token)) token = token.next;
|
| - if (optional('const', token)) token = token.next;
|
| - return optional('factory', token);
|
| - }
|
| -
|
| - Token parseMember(Token token) {
|
| - token = parseMetadataStar(token);
|
| - Token start = token;
|
| - listener.beginMember(token);
|
| - if (isFactoryDeclaration(token)) {
|
| - token = parseFactoryMethod(token);
|
| - listener.endMember();
|
| - assert(token != null);
|
| - return token;
|
| - }
|
| -
|
| - Link<Token> identifiers = findMemberName(token);
|
| - if (identifiers.isEmpty) {
|
| - return listener.expectedDeclaration(start);
|
| - }
|
| - Token afterName = identifiers.head;
|
| - identifiers = identifiers.tail;
|
| -
|
| - if (identifiers.isEmpty) {
|
| - return listener.expectedDeclaration(start);
|
| - }
|
| - Token name = identifiers.head;
|
| - identifiers = identifiers.tail;
|
| - if (!identifiers.isEmpty) {
|
| - if (optional('operator', identifiers.head)) {
|
| - name = identifiers.head;
|
| - identifiers = identifiers.tail;
|
| - }
|
| - }
|
| - Token getOrSet;
|
| - if (!identifiers.isEmpty) {
|
| - if (isGetOrSet(identifiers.head)) {
|
| - getOrSet = identifiers.head;
|
| - identifiers = identifiers.tail;
|
| - }
|
| - }
|
| - Token type;
|
| - if (!identifiers.isEmpty) {
|
| - if (isValidTypeReference(identifiers.head)) {
|
| - type = identifiers.head;
|
| - identifiers = identifiers.tail;
|
| - }
|
| - }
|
| -
|
| - token = afterName;
|
| - bool isField;
|
| - while (true) {
|
| - // Loop to allow the listener to rewrite the token stream for
|
| - // error handling.
|
| - final String value = token.stringValue;
|
| - if ((identical(value, '(')) ||
|
| - (identical(value, '.')) ||
|
| - (identical(value, '{')) ||
|
| - (identical(value, '=>')) ||
|
| - (identical(value, '<'))) {
|
| - isField = false;
|
| - break;
|
| - } else if (identical(value, ';')) {
|
| - if (getOrSet != null) {
|
| - // If we found a "get" keyword, this must be an abstract
|
| - // getter.
|
| - isField = (!identical(getOrSet.stringValue, 'get'));
|
| - // TODO(ahe): This feels like a hack.
|
| - } else {
|
| - isField = true;
|
| - }
|
| - break;
|
| - } else if ((identical(value, '=')) || (identical(value, ','))) {
|
| - isField = true;
|
| - break;
|
| - } else {
|
| - token = listener.unexpected(token);
|
| - if (identical(token.kind, EOF_TOKEN)) {
|
| - // TODO(ahe): This is a hack, see parseTopLevelMember.
|
| - listener.endFields(1, start, token);
|
| - listener.endMember();
|
| - return token;
|
| - }
|
| - }
|
| - }
|
| -
|
| - var modifiers = identifiers.reverse();
|
| - token = isField
|
| - ? parseFields(start, modifiers, type, getOrSet, name, false)
|
| - : parseMethod(start, modifiers, type, getOrSet, name);
|
| - listener.endMember();
|
| - return token;
|
| - }
|
| -
|
| - Token parseMethod(Token start, Link<Token> modifiers, Token type,
|
| - Token getOrSet, Token name) {
|
| - Token externalModifier;
|
| - Token staticModifier;
|
| - Token constModifier;
|
| - int modifierCount = 0;
|
| - int allowedModifierCount = 1;
|
| - // TODO(johnniwinther): Move error reporting to resolution to give more
|
| - // specific error messages.
|
| - for (Token modifier in modifiers) {
|
| - if (externalModifier == null && optional('external', modifier)) {
|
| - modifierCount++;
|
| - externalModifier = modifier;
|
| - if (modifierCount != allowedModifierCount) {
|
| - listener.reportError(modifier, MessageKind.EXTRANEOUS_MODIFIER,
|
| - {'modifier': modifier});
|
| - }
|
| - allowedModifierCount++;
|
| - } else if (staticModifier == null && optional('static', modifier)) {
|
| - modifierCount++;
|
| - staticModifier = modifier;
|
| - if (modifierCount != allowedModifierCount) {
|
| - listener.reportError(modifier, MessageKind.EXTRANEOUS_MODIFIER,
|
| - {'modifier': modifier});
|
| - }
|
| - } else if (constModifier == null && optional('const', modifier)) {
|
| - modifierCount++;
|
| - constModifier = modifier;
|
| - if (modifierCount != allowedModifierCount) {
|
| - listener.reportError(modifier, MessageKind.EXTRANEOUS_MODIFIER,
|
| - {'modifier': modifier});
|
| - }
|
| - } else {
|
| - listener.reportError(
|
| - modifier, MessageKind.EXTRANEOUS_MODIFIER, {'modifier': modifier});
|
| - }
|
| - }
|
| - if (getOrSet != null && constModifier != null) {
|
| - listener.reportError(constModifier, MessageKind.EXTRANEOUS_MODIFIER,
|
| - {'modifier': constModifier});
|
| - }
|
| - parseModifierList(modifiers);
|
| -
|
| - if (type == null) {
|
| - listener.handleNoType(name);
|
| - } else {
|
| - parseReturnTypeOpt(type);
|
| - }
|
| - Token token;
|
| - if (optional('operator', name)) {
|
| - token = parseOperatorName(name);
|
| - if (staticModifier != null) {
|
| - listener.reportError(staticModifier, MessageKind.EXTRANEOUS_MODIFIER,
|
| - {'modifier': staticModifier});
|
| - }
|
| - } else {
|
| - token = parseIdentifier(name);
|
| - }
|
| -
|
| - token = parseQualifiedRestOpt(token);
|
| - if (getOrSet == null) {
|
| - token = parseTypeVariablesOpt(token);
|
| - } else {
|
| - listener.handleNoTypeVariables(token);
|
| - }
|
| - token = parseFormalParametersOpt(token);
|
| - token = parseInitializersOpt(token);
|
| - bool previousAsyncAwaitKeywordsEnabled = asyncAwaitKeywordsEnabled;
|
| - token = parseAsyncModifier(token);
|
| - if (optional('=', token)) {
|
| - token = parseRedirectingFactoryBody(token);
|
| - } else {
|
| - token = parseFunctionBody(
|
| - token, false, staticModifier == null || externalModifier != null);
|
| - }
|
| - asyncAwaitKeywordsEnabled = previousAsyncAwaitKeywordsEnabled;
|
| - listener.endMethod(getOrSet, start, token);
|
| - return token.next;
|
| - }
|
| -
|
| - Token parseFactoryMethod(Token token) {
|
| - assert(isFactoryDeclaration(token));
|
| - Token start = token;
|
| - Token externalModifier;
|
| - if (identical(token.stringValue, 'external')) {
|
| - externalModifier = token;
|
| - token = token.next;
|
| - }
|
| - if (optional('const', token)) {
|
| - token = token.next; // Skip const.
|
| - }
|
| - Token factoryKeyword = token;
|
| - listener.beginFactoryMethod(factoryKeyword);
|
| - token = token.next; // Skip 'factory'.
|
| - token = parseConstructorReference(token);
|
| - token = parseFormalParameters(token);
|
| - token = parseAsyncModifier(token);
|
| - if (optional('=', token)) {
|
| - token = parseRedirectingFactoryBody(token);
|
| - } else {
|
| - token = parseFunctionBody(token, false, externalModifier != null);
|
| - }
|
| - listener.endFactoryMethod(start, token);
|
| - return token.next;
|
| - }
|
| -
|
| - Token parseOperatorName(Token token) {
|
| - assert(optional('operator', token));
|
| - if (isUserDefinableOperator(token.next.stringValue)) {
|
| - Token operator = token;
|
| - token = token.next;
|
| - listener.handleOperatorName(operator, token);
|
| - return token.next;
|
| - } else {
|
| - return parseIdentifier(token);
|
| - }
|
| - }
|
| -
|
| - Token parseFunction(Token token, Token getOrSet) {
|
| - listener.beginFunction(token);
|
| - token = parseModifiers(token);
|
| - if (identical(getOrSet, token)) {
|
| - // get <name> => ...
|
| - token = token.next;
|
| - listener.handleNoType(token);
|
| - listener.beginFunctionName(token);
|
| - if (optional('operator', token)) {
|
| - token = parseOperatorName(token);
|
| - } else {
|
| - token = parseIdentifier(token);
|
| - }
|
| - } else if (optional('operator', token)) {
|
| - // operator <op> (...
|
| - listener.handleNoType(token);
|
| - listener.beginFunctionName(token);
|
| - token = parseOperatorName(token);
|
| - } else {
|
| - // <type>? <get>? <name>
|
| - token = parseReturnTypeOpt(token);
|
| - if (identical(getOrSet, token)) {
|
| - token = token.next;
|
| - }
|
| - listener.beginFunctionName(token);
|
| - if (optional('operator', token)) {
|
| - token = parseOperatorName(token);
|
| - } else {
|
| - token = parseIdentifier(token);
|
| - }
|
| - }
|
| - token = parseQualifiedRestOpt(token);
|
| - listener.endFunctionName(token);
|
| - if (getOrSet == null) {
|
| - token = parseTypeVariablesOpt(token);
|
| - } else {
|
| - listener.handleNoTypeVariables(token);
|
| - }
|
| - token = parseFormalParametersOpt(token);
|
| - token = parseInitializersOpt(token);
|
| - bool previousAsyncAwaitKeywordsEnabled = asyncAwaitKeywordsEnabled;
|
| - token = parseAsyncModifier(token);
|
| - if (optional('=', token)) {
|
| - token = parseRedirectingFactoryBody(token);
|
| - } else {
|
| - token = parseFunctionBody(token, false, true);
|
| - }
|
| - asyncAwaitKeywordsEnabled = previousAsyncAwaitKeywordsEnabled;
|
| - listener.endFunction(getOrSet, token);
|
| - return token.next;
|
| - }
|
| -
|
| - Token parseUnnamedFunction(Token token) {
|
| - listener.beginUnnamedFunction(token);
|
| - token = parseFormalParameters(token);
|
| - bool previousAsyncAwaitKeywordsEnabled = asyncAwaitKeywordsEnabled;
|
| - token = parseAsyncModifier(token);
|
| - bool isBlock = optional('{', token);
|
| - token = parseFunctionBody(token, true, false);
|
| - asyncAwaitKeywordsEnabled = previousAsyncAwaitKeywordsEnabled;
|
| - listener.endUnnamedFunction(token);
|
| - return isBlock ? token.next : token;
|
| - }
|
| -
|
| - Token parseFunctionDeclaration(Token token) {
|
| - listener.beginFunctionDeclaration(token);
|
| - token = parseFunction(token, null);
|
| - listener.endFunctionDeclaration(token);
|
| - return token;
|
| - }
|
| -
|
| - Token parseFunctionExpression(Token token) {
|
| - listener.beginFunction(token);
|
| - listener.handleModifiers(0);
|
| - token = parseReturnTypeOpt(token);
|
| - listener.beginFunctionName(token);
|
| - token = parseIdentifier(token);
|
| - listener.endFunctionName(token);
|
| - token = parseTypeVariablesOpt(token);
|
| - token = parseFormalParameters(token);
|
| - listener.handleNoInitializers();
|
| - bool previousAsyncAwaitKeywordsEnabled = asyncAwaitKeywordsEnabled;
|
| - token = parseAsyncModifier(token);
|
| - bool isBlock = optional('{', token);
|
| - token = parseFunctionBody(token, true, false);
|
| - asyncAwaitKeywordsEnabled = previousAsyncAwaitKeywordsEnabled;
|
| - listener.endFunction(null, token);
|
| - return isBlock ? token.next : token;
|
| - }
|
| -
|
| - Token parseConstructorReference(Token token) {
|
| - Token start = token;
|
| - listener.beginConstructorReference(start);
|
| - token = parseIdentifier(token);
|
| - token = parseQualifiedRestOpt(token);
|
| - token = parseTypeArgumentsOpt(token);
|
| - Token period = null;
|
| - if (optional('.', token)) {
|
| - period = token;
|
| - token = parseIdentifier(token.next);
|
| - }
|
| - listener.endConstructorReference(start, period, token);
|
| - return token;
|
| - }
|
| -
|
| - Token parseRedirectingFactoryBody(Token token) {
|
| - listener.beginRedirectingFactoryBody(token);
|
| - assert(optional('=', token));
|
| - Token equals = token;
|
| - token = parseConstructorReference(token.next);
|
| - Token semicolon = token;
|
| - expectSemicolon(token);
|
| - listener.endRedirectingFactoryBody(equals, semicolon);
|
| - return token;
|
| - }
|
| -
|
| - Token parseFunctionBody(Token token, bool isExpression, bool allowAbstract) {
|
| - if (optional(';', token)) {
|
| - if (!allowAbstract) {
|
| - listener.reportError(token, MessageKind.BODY_EXPECTED);
|
| - }
|
| - listener.endFunctionBody(0, null, token);
|
| - return token;
|
| - } else if (optional('=>', token)) {
|
| - Token begin = token;
|
| - token = parseExpression(token.next);
|
| - if (!isExpression) {
|
| - expectSemicolon(token);
|
| - listener.endReturnStatement(true, begin, token);
|
| - } else {
|
| - listener.endReturnStatement(true, begin, null);
|
| - }
|
| - return token;
|
| - }
|
| - Token begin = token;
|
| - int statementCount = 0;
|
| - if (!optional('{', token)) {
|
| - return listener.expectedFunctionBody(token);
|
| - }
|
| -
|
| - listener.beginFunctionBody(begin);
|
| - token = token.next;
|
| - while (notEofOrValue('}', token)) {
|
| - token = parseStatement(token);
|
| - ++statementCount;
|
| - }
|
| - listener.endFunctionBody(statementCount, begin, token);
|
| - expect('}', token);
|
| - return token;
|
| - }
|
| -
|
| - Token parseAsyncModifier(Token token) {
|
| - Token async;
|
| - Token star;
|
| - asyncAwaitKeywordsEnabled = false;
|
| - if (optional('async', token)) {
|
| - asyncAwaitKeywordsEnabled = true;
|
| - async = token;
|
| - token = token.next;
|
| - if (optional('*', token)) {
|
| - star = token;
|
| - token = token.next;
|
| - }
|
| - } else if (optional('sync', token)) {
|
| - async = token;
|
| - token = token.next;
|
| - if (optional('*', token)) {
|
| - asyncAwaitKeywordsEnabled = true;
|
| - star = token;
|
| - token = token.next;
|
| - } else {
|
| - listener.reportError(async, MessageKind.INVALID_SYNC_MODIFIER);
|
| - }
|
| - }
|
| - listener.handleAsyncModifier(async, star);
|
| - return token;
|
| - }
|
| -
|
| - Token parseStatement(Token token) {
|
| - final value = token.stringValue;
|
| - if (identical(token.kind, IDENTIFIER_TOKEN)) {
|
| - return parseExpressionStatementOrDeclaration(token);
|
| - } else if (identical(value, '{')) {
|
| - return parseBlock(token);
|
| - } else if (identical(value, 'return')) {
|
| - return parseReturnStatement(token);
|
| - } else if (identical(value, 'var') || identical(value, 'final')) {
|
| - return parseVariablesDeclaration(token);
|
| - } else if (identical(value, 'if')) {
|
| - return parseIfStatement(token);
|
| - } else if (asyncAwaitKeywordsEnabled && identical(value, 'await')) {
|
| - if (identical(token.next.stringValue, 'for')) {
|
| - return parseForStatement(token, token.next);
|
| - } else {
|
| - return parseExpressionStatement(token);
|
| - }
|
| - } else if (identical(value, 'for')) {
|
| - return parseForStatement(null, token);
|
| - } else if (identical(value, 'rethrow')) {
|
| - return parseRethrowStatement(token);
|
| - } else if (identical(value, 'throw') && optional(';', token.next)) {
|
| - // TODO(kasperl): Stop dealing with throw here.
|
| - return parseRethrowStatement(token);
|
| - } else if (identical(value, 'void')) {
|
| - return parseExpressionStatementOrDeclaration(token);
|
| - } else if (identical(value, 'while')) {
|
| - return parseWhileStatement(token);
|
| - } else if (identical(value, 'do')) {
|
| - return parseDoWhileStatement(token);
|
| - } else if (identical(value, 'try')) {
|
| - return parseTryStatement(token);
|
| - } else if (identical(value, 'switch')) {
|
| - return parseSwitchStatement(token);
|
| - } else if (identical(value, 'break')) {
|
| - return parseBreakStatement(token);
|
| - } else if (identical(value, 'continue')) {
|
| - return parseContinueStatement(token);
|
| - } else if (identical(value, 'assert')) {
|
| - return parseAssertStatement(token);
|
| - } else if (identical(value, ';')) {
|
| - return parseEmptyStatement(token);
|
| - } else if (asyncAwaitKeywordsEnabled && identical(value, 'yield')) {
|
| - return parseYieldStatement(token);
|
| - } else if (identical(value, 'const')) {
|
| - return parseExpressionStatementOrConstDeclaration(token);
|
| - } else if (token.isIdentifier()) {
|
| - return parseExpressionStatementOrDeclaration(token);
|
| - } else {
|
| - return parseExpressionStatement(token);
|
| - }
|
| - }
|
| -
|
| - Token parseYieldStatement(Token token) {
|
| - Token begin = token;
|
| - listener.beginYieldStatement(begin);
|
| - assert(identical('yield', token.stringValue));
|
| - token = token.next;
|
| - Token starToken;
|
| - if (optional('*', token)) {
|
| - starToken = token;
|
| - token = token.next;
|
| - }
|
| - token = parseExpression(token);
|
| - listener.endYieldStatement(begin, starToken, token);
|
| - return expectSemicolon(token);
|
| - }
|
| -
|
| - Token parseReturnStatement(Token token) {
|
| - Token begin = token;
|
| - listener.beginReturnStatement(begin);
|
| - assert(identical('return', token.stringValue));
|
| - token = token.next;
|
| - if (optional(';', token)) {
|
| - listener.endReturnStatement(false, begin, token);
|
| - } else {
|
| - token = parseExpression(token);
|
| - listener.endReturnStatement(true, begin, token);
|
| - }
|
| - return expectSemicolon(token);
|
| - }
|
| -
|
| - Token peekIdentifierAfterType(Token token) {
|
| - Token peek = peekAfterType(token);
|
| - if (peek != null && peek.isIdentifier()) {
|
| - // We are looking at "type identifier".
|
| - return peek;
|
| - } else {
|
| - return null;
|
| - }
|
| - }
|
| -
|
| - Token peekIdentifierAfterOptionalType(Token token) {
|
| - Token peek = peekAfterIfType(token);
|
| - if (peek != null && peek.isIdentifier()) {
|
| - // We are looking at "type identifier".
|
| - return peek;
|
| - } else if (token.isIdentifier()) {
|
| - // We are looking at "identifier".
|
| - return token;
|
| - } else {
|
| - return null;
|
| - }
|
| - }
|
| -
|
| - Token parseExpressionStatementOrDeclaration(Token token) {
|
| - assert(token.isIdentifier() || identical(token.stringValue, 'void'));
|
| - Token identifier = peekIdentifierAfterType(token);
|
| - if (identifier != null) {
|
| - assert(identifier.isIdentifier());
|
| - Token afterId = identifier.next;
|
| - int afterIdKind = afterId.kind;
|
| - if (identical(afterIdKind, EQ_TOKEN) ||
|
| - identical(afterIdKind, SEMICOLON_TOKEN) ||
|
| - identical(afterIdKind, COMMA_TOKEN)) {
|
| - // We are looking at "type identifier" followed by '=', ';', ','.
|
| - return parseVariablesDeclaration(token);
|
| - } else if (identical(afterIdKind, OPEN_PAREN_TOKEN)) {
|
| - // We are looking at "type identifier '('".
|
| - BeginGroupToken beginParen = afterId;
|
| - Token endParen = beginParen.endGroup;
|
| - // TODO(eernst): Check for NPE as described in issue 26252.
|
| - Token afterParens = endParen.next;
|
| - if (optional('{', afterParens) ||
|
| - optional('=>', afterParens) ||
|
| - optional('async', afterParens) ||
|
| - optional('sync', afterParens)) {
|
| - // We are looking at "type identifier '(' ... ')'" followed
|
| - // by '{', '=>', 'async', or 'sync'.
|
| - return parseFunctionDeclaration(token);
|
| - }
|
| - } else if (identical(afterIdKind, LT_TOKEN)) {
|
| - // We are looking at "type identifier '<'".
|
| - BeginGroupToken beginAngle = afterId;
|
| - Token endAngle = beginAngle.endGroup;
|
| - if (endAngle != null &&
|
| - identical(endAngle.next.kind, OPEN_PAREN_TOKEN)) {
|
| - BeginGroupToken beginParen = endAngle.next;
|
| - Token endParen = beginParen.endGroup;
|
| - if (endParen != null) {
|
| - Token afterParens = endParen.next;
|
| - if (optional('{', afterParens) ||
|
| - optional('=>', afterParens) ||
|
| - optional('async', afterParens) ||
|
| - optional('sync', afterParens)) {
|
| - // We are looking at "type identifier '<' ... '>' '(' ... ')'"
|
| - // followed by '{', '=>', 'async', or 'sync'.
|
| - return parseFunctionDeclaration(token);
|
| - }
|
| - }
|
| - }
|
| - }
|
| - // Fall-through to expression statement.
|
| - } else {
|
| - if (optional(':', token.next)) {
|
| - return parseLabeledStatement(token);
|
| - } else if (optional('(', token.next)) {
|
| - BeginGroupToken begin = token.next;
|
| - // TODO(eernst): Check for NPE as described in issue 26252.
|
| - String afterParens = begin.endGroup.next.stringValue;
|
| - if (identical(afterParens, '{') ||
|
| - identical(afterParens, '=>') ||
|
| - identical(afterParens, 'async') ||
|
| - identical(afterParens, 'sync')) {
|
| - return parseFunctionDeclaration(token);
|
| - }
|
| - } else if (optional('<', token.next)) {
|
| - BeginGroupToken beginAngle = token.next;
|
| - Token endAngle = beginAngle.endGroup;
|
| - if (endAngle != null &&
|
| - identical(endAngle.next.kind, OPEN_PAREN_TOKEN)) {
|
| - BeginGroupToken beginParen = endAngle.next;
|
| - Token endParen = beginParen.endGroup;
|
| - if (endParen != null) {
|
| - String afterParens = endParen.next.stringValue;
|
| - if (identical(afterParens, '{') ||
|
| - identical(afterParens, '=>') ||
|
| - identical(afterParens, 'async') ||
|
| - identical(afterParens, 'sync')) {
|
| - return parseFunctionDeclaration(token);
|
| - }
|
| - }
|
| - }
|
| - // Fall through to expression statement.
|
| - }
|
| - }
|
| - return parseExpressionStatement(token);
|
| - }
|
| -
|
| - Token parseExpressionStatementOrConstDeclaration(Token token) {
|
| - assert(identical(token.stringValue, 'const'));
|
| - if (isModifier(token.next)) {
|
| - return parseVariablesDeclaration(token);
|
| - }
|
| - Token identifier = peekIdentifierAfterOptionalType(token.next);
|
| - if (identifier != null) {
|
| - assert(identifier.isIdentifier());
|
| - Token afterId = identifier.next;
|
| - int afterIdKind = afterId.kind;
|
| - if (identical(afterIdKind, EQ_TOKEN) ||
|
| - identical(afterIdKind, SEMICOLON_TOKEN) ||
|
| - identical(afterIdKind, COMMA_TOKEN)) {
|
| - // We are looking at "const type identifier" followed by '=', ';', or
|
| - // ','.
|
| - return parseVariablesDeclaration(token);
|
| - }
|
| - // Fall-through to expression statement.
|
| - }
|
| -
|
| - return parseExpressionStatement(token);
|
| - }
|
| -
|
| - Token parseLabel(Token token) {
|
| - token = parseIdentifier(token);
|
| - Token colon = token;
|
| - token = expect(':', token);
|
| - listener.handleLabel(colon);
|
| - return token;
|
| - }
|
| -
|
| - Token parseLabeledStatement(Token token) {
|
| - int labelCount = 0;
|
| - do {
|
| - token = parseLabel(token);
|
| - labelCount++;
|
| - } while (token.isIdentifier() && optional(':', token.next));
|
| - listener.beginLabeledStatement(token, labelCount);
|
| - token = parseStatement(token);
|
| - listener.endLabeledStatement(labelCount);
|
| - return token;
|
| - }
|
| -
|
| - Token parseExpressionStatement(Token token) {
|
| - listener.beginExpressionStatement(token);
|
| - token = parseExpression(token);
|
| - listener.endExpressionStatement(token);
|
| - return expectSemicolon(token);
|
| - }
|
| -
|
| - Token parseExpression(Token token) {
|
| - return optional('throw', token)
|
| - ? parseThrowExpression(token, true)
|
| - : parsePrecedenceExpression(token, ASSIGNMENT_PRECEDENCE, true);
|
| - }
|
| -
|
| - Token parseExpressionWithoutCascade(Token token) {
|
| - return optional('throw', token)
|
| - ? parseThrowExpression(token, false)
|
| - : parsePrecedenceExpression(token, ASSIGNMENT_PRECEDENCE, false);
|
| - }
|
| -
|
| - Token parseConditionalExpressionRest(Token token) {
|
| - assert(optional('?', token));
|
| - Token question = token;
|
| - token = parseExpressionWithoutCascade(token.next);
|
| - Token colon = token;
|
| - token = expect(':', token);
|
| - token = parseExpressionWithoutCascade(token);
|
| - listener.handleConditionalExpression(question, colon);
|
| - return token;
|
| - }
|
| -
|
| - Token parsePrecedenceExpression(
|
| - Token token, int precedence, bool allowCascades) {
|
| - assert(precedence >= 1);
|
| - assert(precedence <= POSTFIX_PRECEDENCE);
|
| - token = parseUnaryExpression(token, allowCascades);
|
| - PrecedenceInfo info = token.info;
|
| - int tokenLevel = info.precedence;
|
| - for (int level = tokenLevel; level >= precedence; --level) {
|
| - while (identical(tokenLevel, level)) {
|
| - Token operator = token;
|
| - if (identical(tokenLevel, CASCADE_PRECEDENCE)) {
|
| - if (!allowCascades) {
|
| - return token;
|
| - }
|
| - token = parseCascadeExpression(token);
|
| - } else if (identical(tokenLevel, ASSIGNMENT_PRECEDENCE)) {
|
| - // Right associative, so we recurse at the same precedence
|
| - // level.
|
| - token = parsePrecedenceExpression(token.next, level, allowCascades);
|
| - listener.handleAssignmentExpression(operator);
|
| - } else if (identical(tokenLevel, POSTFIX_PRECEDENCE)) {
|
| - if (identical(info, PERIOD_INFO) ||
|
| - identical(info, QUESTION_PERIOD_INFO)) {
|
| - // Left associative, so we recurse at the next higher
|
| - // precedence level. However, POSTFIX_PRECEDENCE is the
|
| - // highest level, so we just call parseUnaryExpression
|
| - // directly.
|
| - token = parseUnaryExpression(token.next, allowCascades);
|
| - listener.handleBinaryExpression(operator);
|
| - } else if ((identical(info, OPEN_PAREN_INFO)) ||
|
| - (identical(info, OPEN_SQUARE_BRACKET_INFO))) {
|
| - token = parseArgumentOrIndexStar(token);
|
| - } else if ((identical(info, PLUS_PLUS_INFO)) ||
|
| - (identical(info, MINUS_MINUS_INFO))) {
|
| - listener.handleUnaryPostfixAssignmentExpression(token);
|
| - token = token.next;
|
| - } else {
|
| - token = listener.unexpected(token);
|
| - }
|
| - } else if (identical(info, IS_INFO)) {
|
| - token = parseIsOperatorRest(token);
|
| - } else if (identical(info, AS_INFO)) {
|
| - token = parseAsOperatorRest(token);
|
| - } else if (identical(info, QUESTION_INFO)) {
|
| - token = parseConditionalExpressionRest(token);
|
| - } else {
|
| - // Left associative, so we recurse at the next higher
|
| - // precedence level.
|
| - token =
|
| - parsePrecedenceExpression(token.next, level + 1, allowCascades);
|
| - listener.handleBinaryExpression(operator);
|
| - }
|
| - info = token.info;
|
| - tokenLevel = info.precedence;
|
| - if (level == EQUALITY_PRECEDENCE || level == RELATIONAL_PRECEDENCE) {
|
| - // We don't allow (a == b == c) or (a < b < c).
|
| - // Continue the outer loop if we have matched one equality or
|
| - // relational operator.
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - return token;
|
| - }
|
| -
|
| - Token parseCascadeExpression(Token token) {
|
| - listener.beginCascade(token);
|
| - assert(optional('..', token));
|
| - Token cascadeOperator = token;
|
| - token = token.next;
|
| - if (optional('[', token)) {
|
| - token = parseArgumentOrIndexStar(token);
|
| - } else if (token.isIdentifier()) {
|
| - token = parseSend(token);
|
| - listener.handleBinaryExpression(cascadeOperator);
|
| - } else {
|
| - return listener.unexpected(token);
|
| - }
|
| - Token mark;
|
| - do {
|
| - mark = token;
|
| - if (optional('.', token)) {
|
| - Token period = token;
|
| - token = parseSend(token.next);
|
| - listener.handleBinaryExpression(period);
|
| - }
|
| - token = parseArgumentOrIndexStar(token);
|
| - } while (!identical(mark, token));
|
| -
|
| - if (identical(token.info.precedence, ASSIGNMENT_PRECEDENCE)) {
|
| - Token assignment = token;
|
| - token = parseExpressionWithoutCascade(token.next);
|
| - listener.handleAssignmentExpression(assignment);
|
| - }
|
| - listener.endCascade();
|
| - return token;
|
| - }
|
| -
|
| - Token parseUnaryExpression(Token token, bool allowCascades) {
|
| - String value = token.stringValue;
|
| - // Prefix:
|
| - if (asyncAwaitKeywordsEnabled && optional('await', token)) {
|
| - return parseAwaitExpression(token, allowCascades);
|
| - } else if (identical(value, '+')) {
|
| - // Dart no longer allows prefix-plus.
|
| - listener.reportError(token, MessageKind.UNSUPPORTED_PREFIX_PLUS);
|
| - return parseUnaryExpression(token.next, allowCascades);
|
| - } else if ((identical(value, '!')) ||
|
| - (identical(value, '-')) ||
|
| - (identical(value, '~'))) {
|
| - Token operator = token;
|
| - // Right associative, so we recurse at the same precedence
|
| - // level.
|
| - token = parsePrecedenceExpression(
|
| - token.next, POSTFIX_PRECEDENCE, allowCascades);
|
| - listener.handleUnaryPrefixExpression(operator);
|
| - } else if ((identical(value, '++')) || identical(value, '--')) {
|
| - // TODO(ahe): Validate this is used correctly.
|
| - Token operator = token;
|
| - // Right associative, so we recurse at the same precedence
|
| - // level.
|
| - token = parsePrecedenceExpression(
|
| - token.next, POSTFIX_PRECEDENCE, allowCascades);
|
| - listener.handleUnaryPrefixAssignmentExpression(operator);
|
| - } else {
|
| - token = parsePrimary(token);
|
| - }
|
| - return token;
|
| - }
|
| -
|
| - Token parseArgumentOrIndexStar(Token token) {
|
| - while (true) {
|
| - if (optional('[', token)) {
|
| - Token openSquareBracket = token;
|
| - bool old = mayParseFunctionExpressions;
|
| - mayParseFunctionExpressions = true;
|
| - token = parseExpression(token.next);
|
| - mayParseFunctionExpressions = old;
|
| - listener.handleIndexedExpression(openSquareBracket, token);
|
| - token = expect(']', token);
|
| - } else if (optional('(', token)) {
|
| - listener.handleNoTypeArguments(token);
|
| - token = parseArguments(token);
|
| - listener.endSend(token);
|
| - } else {
|
| - break;
|
| - }
|
| - }
|
| - return token;
|
| - }
|
| -
|
| - Token parsePrimary(Token token) {
|
| - final kind = token.kind;
|
| - if (kind == IDENTIFIER_TOKEN) {
|
| - return parseSendOrFunctionLiteral(token);
|
| - } else if (kind == INT_TOKEN || kind == HEXADECIMAL_TOKEN) {
|
| - return parseLiteralInt(token);
|
| - } else if (kind == DOUBLE_TOKEN) {
|
| - return parseLiteralDouble(token);
|
| - } else if (kind == STRING_TOKEN) {
|
| - return parseLiteralString(token);
|
| - } else if (kind == HASH_TOKEN) {
|
| - return parseLiteralSymbol(token);
|
| - } else if (kind == KEYWORD_TOKEN) {
|
| - final value = token.stringValue;
|
| - if (value == 'true' || value == 'false') {
|
| - return parseLiteralBool(token);
|
| - } else if (value == 'null') {
|
| - return parseLiteralNull(token);
|
| - } else if (value == 'this') {
|
| - return parseThisExpression(token);
|
| - } else if (value == 'super') {
|
| - return parseSuperExpression(token);
|
| - } else if (value == 'new') {
|
| - return parseNewExpression(token);
|
| - } else if (value == 'const') {
|
| - return parseConstExpression(token);
|
| - } else if (value == 'void') {
|
| - return parseFunctionExpression(token);
|
| - } else if (asyncAwaitKeywordsEnabled &&
|
| - (value == 'yield' || value == 'async')) {
|
| - return listener.expectedExpression(token);
|
| - } else if (token.isIdentifier()) {
|
| - return parseSendOrFunctionLiteral(token);
|
| - } else {
|
| - return listener.expectedExpression(token);
|
| - }
|
| - } else if (kind == OPEN_PAREN_TOKEN) {
|
| - return parseParenthesizedExpressionOrFunctionLiteral(token);
|
| - } else if (kind == OPEN_SQUARE_BRACKET_TOKEN || token.stringValue == '[]') {
|
| - listener.handleNoTypeArguments(token);
|
| - return parseLiteralListSuffix(token, null);
|
| - } else if (kind == OPEN_CURLY_BRACKET_TOKEN) {
|
| - listener.handleNoTypeArguments(token);
|
| - return parseLiteralMapSuffix(token, null);
|
| - } else if (kind == LT_TOKEN) {
|
| - return parseLiteralListOrMapOrFunction(token, null);
|
| - } else {
|
| - return listener.expectedExpression(token);
|
| - }
|
| - }
|
| -
|
| - Token parseParenthesizedExpressionOrFunctionLiteral(Token token) {
|
| - BeginGroupToken beginGroup = token;
|
| - // TODO(eernst): Check for NPE as described in issue 26252.
|
| - Token nextToken = beginGroup.endGroup.next;
|
| - int kind = nextToken.kind;
|
| - if (mayParseFunctionExpressions &&
|
| - (identical(kind, FUNCTION_TOKEN) ||
|
| - identical(kind, OPEN_CURLY_BRACKET_TOKEN) ||
|
| - (identical(kind, KEYWORD_TOKEN) &&
|
| - (nextToken.value == 'async' || nextToken.value == 'sync')))) {
|
| - listener.handleNoTypeVariables(token);
|
| - return parseUnnamedFunction(token);
|
| - } else {
|
| - bool old = mayParseFunctionExpressions;
|
| - mayParseFunctionExpressions = true;
|
| - token = parseParenthesizedExpression(token);
|
| - mayParseFunctionExpressions = old;
|
| - return token;
|
| - }
|
| - }
|
| -
|
| - Token parseParenthesizedExpression(Token token) {
|
| - // We expect [begin] to be of type [BeginGroupToken], but we don't know for
|
| - // sure until after calling expect.
|
| - var begin = token;
|
| - token = expect('(', token);
|
| - // [begin] is now known to have type [BeginGroupToken].
|
| - token = parseExpression(token);
|
| - if (!identical(begin.endGroup, token)) {
|
| - listener.unexpected(token);
|
| - token = begin.endGroup;
|
| - }
|
| - listener.handleParenthesizedExpression(begin);
|
| - return expect(')', token);
|
| - }
|
| -
|
| - Token parseThisExpression(Token token) {
|
| - listener.handleThisExpression(token);
|
| - token = token.next;
|
| - if (optional('(', token)) {
|
| - // Constructor forwarding.
|
| - listener.handleNoTypeArguments(token);
|
| - token = parseArguments(token);
|
| - listener.endSend(token);
|
| - }
|
| - return token;
|
| - }
|
| -
|
| - Token parseSuperExpression(Token token) {
|
| - listener.handleSuperExpression(token);
|
| - token = token.next;
|
| - if (optional('(', token)) {
|
| - // Super constructor.
|
| - listener.handleNoTypeArguments(token);
|
| - token = parseArguments(token);
|
| - listener.endSend(token);
|
| - }
|
| - return token;
|
| - }
|
| -
|
| - /// '[' (expressionList ','?)? ']'.
|
| - ///
|
| - /// Provide [constKeyword] if preceded by 'const', null if not.
|
| - /// This is a suffix parser because it is assumed that type arguments have
|
| - /// been parsed, or `listener.handleNoTypeArguments(..)` has been executed.
|
| - Token parseLiteralListSuffix(Token token, Token constKeyword) {
|
| - assert(optional('[', token) || optional('[]', token));
|
| - Token beginToken = token;
|
| - int count = 0;
|
| - if (optional('[', token)) {
|
| - bool old = mayParseFunctionExpressions;
|
| - mayParseFunctionExpressions = true;
|
| - do {
|
| - if (optional(']', token.next)) {
|
| - token = token.next;
|
| - break;
|
| - }
|
| - token = parseExpression(token.next);
|
| - ++count;
|
| - } while (optional(',', token));
|
| - mayParseFunctionExpressions = old;
|
| - listener.handleLiteralList(count, beginToken, constKeyword, token);
|
| - return expect(']', token);
|
| - }
|
| - // Looking at '[]'.
|
| - listener.handleLiteralList(0, token, constKeyword, token);
|
| - return token.next;
|
| - }
|
| -
|
| - /// '{' (mapLiteralEntry (',' mapLiteralEntry)* ','?)? '}'.
|
| - ///
|
| - /// Provide token for [constKeyword] if preceded by 'const', null if not.
|
| - /// This is a suffix parser because it is assumed that type arguments have
|
| - /// been parsed, or `listener.handleNoTypeArguments(..)` has been executed.
|
| - Token parseLiteralMapSuffix(Token token, Token constKeyword) {
|
| - assert(optional('{', token));
|
| - Token beginToken = token;
|
| - int count = 0;
|
| - bool old = mayParseFunctionExpressions;
|
| - mayParseFunctionExpressions = true;
|
| - do {
|
| - if (optional('}', token.next)) {
|
| - token = token.next;
|
| - break;
|
| - }
|
| - token = parseMapLiteralEntry(token.next);
|
| - ++count;
|
| - } while (optional(',', token));
|
| - mayParseFunctionExpressions = old;
|
| - listener.handleLiteralMap(count, beginToken, constKeyword, token);
|
| - return expect('}', token);
|
| - }
|
| -
|
| - /// formalParameterList functionBody.
|
| - ///
|
| - /// This is a suffix parser because it is assumed that type arguments have
|
| - /// been parsed, or `listener.handleNoTypeArguments(..)` has been executed.
|
| - Token parseLiteralFunctionSuffix(Token token) {
|
| - assert(optional('(', token));
|
| - BeginGroupToken beginGroup = token;
|
| - if (beginGroup.endGroup != null) {
|
| - Token nextToken = beginGroup.endGroup.next;
|
| - int kind = nextToken.kind;
|
| - if (identical(kind, FUNCTION_TOKEN) ||
|
| - identical(kind, OPEN_CURLY_BRACKET_TOKEN) ||
|
| - (identical(kind, KEYWORD_TOKEN) &&
|
| - (nextToken.value == 'async' || nextToken.value == 'sync'))) {
|
| - return parseUnnamedFunction(token);
|
| - }
|
| - // Fall through.
|
| - }
|
| - listener.unexpected(token);
|
| - return null;
|
| - }
|
| -
|
| - /// genericListLiteral | genericMapLiteral | genericFunctionLiteral.
|
| - ///
|
| - /// Where
|
| - /// genericListLiteral ::= typeArguments '[' (expressionList ','?)? ']'
|
| - /// genericMapLiteral ::=
|
| - /// typeArguments '{' (mapLiteralEntry (',' mapLiteralEntry)* ','?)? '}'
|
| - /// genericFunctionLiteral ::=
|
| - /// typeParameters formalParameterList functionBody
|
| - /// Provide token for [constKeyword] if preceded by 'const', null if not.
|
| - Token parseLiteralListOrMapOrFunction(Token token, Token constKeyword) {
|
| - assert(optional('<', token));
|
| - BeginGroupToken begin = token;
|
| - if (constKeyword == null &&
|
| - begin.endGroup != null &&
|
| - identical(begin.endGroup.next.kind, OPEN_PAREN_TOKEN)) {
|
| - token = parseTypeVariablesOpt(token);
|
| - return parseLiteralFunctionSuffix(token);
|
| - } else {
|
| - token = parseTypeArgumentsOpt(token);
|
| - if (optional('{', token)) {
|
| - return parseLiteralMapSuffix(token, constKeyword);
|
| - } else if ((optional('[', token)) || (optional('[]', token))) {
|
| - return parseLiteralListSuffix(token, constKeyword);
|
| - }
|
| - listener.unexpected(token);
|
| - return null;
|
| - }
|
| - }
|
| -
|
| - Token parseMapLiteralEntry(Token token) {
|
| - listener.beginLiteralMapEntry(token);
|
| - // Assume the listener rejects non-string keys.
|
| - token = parseExpression(token);
|
| - Token colon = token;
|
| - token = expect(':', token);
|
| - token = parseExpression(token);
|
| - listener.endLiteralMapEntry(colon, token);
|
| - return token;
|
| - }
|
| -
|
| - Token parseSendOrFunctionLiteral(Token token) {
|
| - if (!mayParseFunctionExpressions) return parseSend(token);
|
| - Token peek = peekAfterIfType(token);
|
| - if (peek != null &&
|
| - identical(peek.kind, IDENTIFIER_TOKEN) &&
|
| - isFunctionDeclaration(peek.next)) {
|
| - return parseFunctionExpression(token);
|
| - } else if (isFunctionDeclaration(token.next)) {
|
| - return parseFunctionExpression(token);
|
| - } else {
|
| - return parseSend(token);
|
| - }
|
| - }
|
| -
|
| - bool isFunctionDeclaration(Token token) {
|
| - if (optional('<', token)) {
|
| - BeginGroupToken begin = token;
|
| - if (begin.endGroup == null) return false;
|
| - token = begin.endGroup.next;
|
| - }
|
| - if (optional('(', token)) {
|
| - BeginGroupToken begin = token;
|
| - // TODO(eernst): Check for NPE as described in issue 26252.
|
| - String afterParens = begin.endGroup.next.stringValue;
|
| - if (identical(afterParens, '{') ||
|
| - identical(afterParens, '=>') ||
|
| - identical(afterParens, 'async') ||
|
| - identical(afterParens, 'sync')) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - Token parseRequiredArguments(Token token) {
|
| - if (optional('(', token)) {
|
| - token = parseArguments(token);
|
| - } else {
|
| - listener.handleNoArguments(token);
|
| - token = listener.unexpected(token);
|
| - }
|
| - return token;
|
| - }
|
| -
|
| - Token parseNewExpression(Token token) {
|
| - Token newKeyword = token;
|
| - token = expect('new', token);
|
| - token = parseConstructorReference(token);
|
| - token = parseRequiredArguments(token);
|
| - listener.handleNewExpression(newKeyword);
|
| - return token;
|
| - }
|
| -
|
| - Token parseConstExpression(Token token) {
|
| - Token constKeyword = token;
|
| - token = expect('const', token);
|
| - final String value = token.stringValue;
|
| - if ((identical(value, '[')) || (identical(value, '[]'))) {
|
| - listener.handleNoTypeArguments(token);
|
| - return parseLiteralListSuffix(token, constKeyword);
|
| - }
|
| - if (identical(value, '{')) {
|
| - listener.handleNoTypeArguments(token);
|
| - return parseLiteralMapSuffix(token, constKeyword);
|
| - }
|
| - if (identical(value, '<')) {
|
| - return parseLiteralListOrMapOrFunction(token, constKeyword);
|
| - }
|
| - token = parseConstructorReference(token);
|
| - token = parseRequiredArguments(token);
|
| - listener.handleConstExpression(constKeyword);
|
| - return token;
|
| - }
|
| -
|
| - Token parseLiteralInt(Token token) {
|
| - listener.handleLiteralInt(token);
|
| - return token.next;
|
| - }
|
| -
|
| - Token parseLiteralDouble(Token token) {
|
| - listener.handleLiteralDouble(token);
|
| - return token.next;
|
| - }
|
| -
|
| - Token parseLiteralString(Token token) {
|
| - bool old = mayParseFunctionExpressions;
|
| - mayParseFunctionExpressions = true;
|
| - token = parseSingleLiteralString(token);
|
| - int count = 1;
|
| - while (identical(token.kind, STRING_TOKEN)) {
|
| - token = parseSingleLiteralString(token);
|
| - count++;
|
| - }
|
| - if (count > 1) {
|
| - listener.handleStringJuxtaposition(count);
|
| - }
|
| - mayParseFunctionExpressions = old;
|
| - return token;
|
| - }
|
| -
|
| - Token parseLiteralSymbol(Token token) {
|
| - Token hashToken = token;
|
| - listener.beginLiteralSymbol(hashToken);
|
| - token = token.next;
|
| - if (isUserDefinableOperator(token.stringValue)) {
|
| - listener.handleOperator(token);
|
| - listener.endLiteralSymbol(hashToken, 1);
|
| - return token.next;
|
| - } else {
|
| - int count = 1;
|
| - token = parseIdentifier(token);
|
| - while (identical(token.stringValue, '.')) {
|
| - count++;
|
| - token = parseIdentifier(token.next);
|
| - }
|
| - listener.endLiteralSymbol(hashToken, count);
|
| - return token;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Only called when [:token.kind === STRING_TOKEN:].
|
| - */
|
| - Token parseSingleLiteralString(Token token) {
|
| - listener.beginLiteralString(token);
|
| - // Parsing the prefix, for instance 'x of 'x${id}y${id}z'
|
| - token = token.next;
|
| - int interpolationCount = 0;
|
| - var kind = token.kind;
|
| - while (kind != EOF_TOKEN) {
|
| - if (identical(kind, STRING_INTERPOLATION_TOKEN)) {
|
| - // Parsing ${expression}.
|
| - token = token.next;
|
| - token = parseExpression(token);
|
| - token = expect('}', token);
|
| - } else if (identical(kind, STRING_INTERPOLATION_IDENTIFIER_TOKEN)) {
|
| - // Parsing $identifier.
|
| - token = token.next;
|
| - token = parseExpression(token);
|
| - } else {
|
| - break;
|
| - }
|
| - ++interpolationCount;
|
| - // Parsing the infix/suffix, for instance y and z' of 'x${id}y${id}z'
|
| - token = parseStringPart(token);
|
| - kind = token.kind;
|
| - }
|
| - listener.endLiteralString(interpolationCount);
|
| - return token;
|
| - }
|
| -
|
| - Token parseLiteralBool(Token token) {
|
| - listener.handleLiteralBool(token);
|
| - return token.next;
|
| - }
|
| -
|
| - Token parseLiteralNull(Token token) {
|
| - listener.handleLiteralNull(token);
|
| - return token.next;
|
| - }
|
| -
|
| - Token parseSend(Token token) {
|
| - listener.beginSend(token);
|
| - token = parseIdentifier(token);
|
| - if (isValidMethodTypeArguments(token)) {
|
| - token = parseTypeArgumentsOpt(token);
|
| - } else {
|
| - listener.handleNoTypeArguments(token);
|
| - }
|
| - token = parseArgumentsOpt(token);
|
| - listener.endSend(token);
|
| - return token;
|
| - }
|
| -
|
| - Token parseArgumentsOpt(Token token) {
|
| - if (!optional('(', token)) {
|
| - listener.handleNoArguments(token);
|
| - return token;
|
| - } else {
|
| - return parseArguments(token);
|
| - }
|
| - }
|
| -
|
| - Token parseArguments(Token token) {
|
| - Token begin = token;
|
| - listener.beginArguments(begin);
|
| - assert(identical('(', token.stringValue));
|
| - int argumentCount = 0;
|
| - if (optional(')', token.next)) {
|
| - listener.endArguments(argumentCount, begin, token.next);
|
| - return token.next.next;
|
| - }
|
| - bool old = mayParseFunctionExpressions;
|
| - mayParseFunctionExpressions = true;
|
| - do {
|
| - if (optional(')', token.next)) {
|
| - token = token.next;
|
| - break;
|
| - }
|
| - Token colon = null;
|
| - if (optional(':', token.next.next)) {
|
| - token = parseIdentifier(token.next);
|
| - colon = token;
|
| - }
|
| - token = parseExpression(token.next);
|
| - if (colon != null) listener.handleNamedArgument(colon);
|
| - ++argumentCount;
|
| - } while (optional(',', token));
|
| - mayParseFunctionExpressions = old;
|
| - listener.endArguments(argumentCount, begin, token);
|
| - return expect(')', token);
|
| - }
|
| -
|
| - Token parseIsOperatorRest(Token token) {
|
| - assert(optional('is', token));
|
| - Token operator = token;
|
| - Token not = null;
|
| - if (optional('!', token.next)) {
|
| - token = token.next;
|
| - not = token;
|
| - }
|
| - token = parseType(token.next);
|
| - listener.handleIsOperator(operator, not, token);
|
| - String value = token.stringValue;
|
| - if (identical(value, 'is') || identical(value, 'as')) {
|
| - // The is- and as-operators cannot be chained, but they can take part of
|
| - // expressions like: foo is Foo || foo is Bar.
|
| - listener.unexpected(token);
|
| - }
|
| - return token;
|
| - }
|
| -
|
| - Token parseAsOperatorRest(Token token) {
|
| - assert(optional('as', token));
|
| - Token operator = token;
|
| - token = parseType(token.next);
|
| - listener.handleAsOperator(operator, token);
|
| - String value = token.stringValue;
|
| - if (identical(value, 'is') || identical(value, 'as')) {
|
| - // The is- and as-operators cannot be chained.
|
| - listener.unexpected(token);
|
| - }
|
| - return token;
|
| - }
|
| -
|
| - Token parseVariablesDeclaration(Token token) {
|
| - return parseVariablesDeclarationMaybeSemicolon(token, true);
|
| - }
|
| -
|
| - Token parseVariablesDeclarationNoSemicolon(Token token) {
|
| - // Only called when parsing a for loop, so this is for parsing locals.
|
| - return parseVariablesDeclarationMaybeSemicolon(token, false);
|
| - }
|
| -
|
| - Token parseVariablesDeclarationMaybeSemicolon(
|
| - Token token, bool endWithSemicolon) {
|
| - int count = 1;
|
| - listener.beginVariablesDeclaration(token);
|
| - token = parseModifiers(token);
|
| - token = parseTypeOpt(token);
|
| - token = parseOptionallyInitializedIdentifier(token);
|
| - while (optional(',', token)) {
|
| - token = parseOptionallyInitializedIdentifier(token.next);
|
| - ++count;
|
| - }
|
| - if (endWithSemicolon) {
|
| - Token semicolon = token;
|
| - token = expectSemicolon(semicolon);
|
| - listener.endVariablesDeclaration(count, semicolon);
|
| - return token;
|
| - } else {
|
| - listener.endVariablesDeclaration(count, null);
|
| - return token;
|
| - }
|
| - }
|
| -
|
| - Token parseOptionallyInitializedIdentifier(Token token) {
|
| - listener.beginInitializedIdentifier(token);
|
| - token = parseIdentifier(token);
|
| - token = parseVariableInitializerOpt(token);
|
| - listener.endInitializedIdentifier();
|
| - return token;
|
| - }
|
| -
|
| - Token parseIfStatement(Token token) {
|
| - Token ifToken = token;
|
| - listener.beginIfStatement(ifToken);
|
| - token = expect('if', token);
|
| - token = parseParenthesizedExpression(token);
|
| - token = parseStatement(token);
|
| - Token elseToken = null;
|
| - if (optional('else', token)) {
|
| - elseToken = token;
|
| - token = parseStatement(token.next);
|
| - }
|
| - listener.endIfStatement(ifToken, elseToken);
|
| - return token;
|
| - }
|
| -
|
| - Token parseForStatement(Token awaitToken, Token token) {
|
| - Token forToken = token;
|
| - listener.beginForStatement(forToken);
|
| - token = expect('for', token);
|
| - token = expect('(', token);
|
| - token = parseVariablesDeclarationOrExpressionOpt(token);
|
| - if (optional('in', token)) {
|
| - return parseForInRest(awaitToken, forToken, token);
|
| - } else {
|
| - if (awaitToken != null) {
|
| - listener.reportError(awaitToken, MessageKind.INVALID_AWAIT_FOR);
|
| - }
|
| - return parseForRest(forToken, token);
|
| - }
|
| - }
|
| -
|
| - Token parseVariablesDeclarationOrExpressionOpt(Token token) {
|
| - final String value = token.stringValue;
|
| - if (identical(value, ';')) {
|
| - listener.handleNoExpression(token);
|
| - return token;
|
| - } else if (isOneOf3(token, 'var', 'final', 'const')) {
|
| - return parseVariablesDeclarationNoSemicolon(token);
|
| - }
|
| - Token identifier = peekIdentifierAfterType(token);
|
| - if (identifier != null) {
|
| - assert(identifier.isIdentifier());
|
| - if (isOneOf4(identifier.next, '=', ';', ',', 'in')) {
|
| - return parseVariablesDeclarationNoSemicolon(token);
|
| - }
|
| - }
|
| - return parseExpression(token);
|
| - }
|
| -
|
| - Token parseForRest(Token forToken, Token token) {
|
| - token = expectSemicolon(token);
|
| - if (optional(';', token)) {
|
| - token = parseEmptyStatement(token);
|
| - } else {
|
| - token = parseExpressionStatement(token);
|
| - }
|
| - int expressionCount = 0;
|
| - while (true) {
|
| - if (optional(')', token)) break;
|
| - token = parseExpression(token);
|
| - ++expressionCount;
|
| - if (optional(',', token)) {
|
| - token = token.next;
|
| - } else {
|
| - break;
|
| - }
|
| - }
|
| - token = expect(')', token);
|
| - token = parseStatement(token);
|
| - listener.endForStatement(expressionCount, forToken, token);
|
| - return token;
|
| - }
|
| -
|
| - Token parseForInRest(Token awaitToken, Token forToken, Token token) {
|
| - assert(optional('in', token));
|
| - Token inKeyword = token;
|
| - token = parseExpression(token.next);
|
| - token = expect(')', token);
|
| - token = parseStatement(token);
|
| - listener.endForIn(awaitToken, forToken, inKeyword, token);
|
| - return token;
|
| - }
|
| -
|
| - Token parseWhileStatement(Token token) {
|
| - Token whileToken = token;
|
| - listener.beginWhileStatement(whileToken);
|
| - token = expect('while', token);
|
| - token = parseParenthesizedExpression(token);
|
| - token = parseStatement(token);
|
| - listener.endWhileStatement(whileToken, token);
|
| - return token;
|
| - }
|
| -
|
| - Token parseDoWhileStatement(Token token) {
|
| - Token doToken = token;
|
| - listener.beginDoWhileStatement(doToken);
|
| - token = expect('do', token);
|
| - token = parseStatement(token);
|
| - Token whileToken = token;
|
| - token = expect('while', token);
|
| - token = parseParenthesizedExpression(token);
|
| - listener.endDoWhileStatement(doToken, whileToken, token);
|
| - return expectSemicolon(token);
|
| - }
|
| -
|
| - Token parseBlock(Token token) {
|
| - Token begin = token;
|
| - listener.beginBlock(begin);
|
| - int statementCount = 0;
|
| - token = expect('{', token);
|
| - while (notEofOrValue('}', token)) {
|
| - token = parseStatement(token);
|
| - ++statementCount;
|
| - }
|
| - listener.endBlock(statementCount, begin, token);
|
| - return expect('}', token);
|
| - }
|
| -
|
| - Token parseAwaitExpression(Token token, bool allowCascades) {
|
| - Token awaitToken = token;
|
| - listener.beginAwaitExpression(awaitToken);
|
| - token = expect('await', token);
|
| - token = parsePrecedenceExpression(token, POSTFIX_PRECEDENCE, allowCascades);
|
| - listener.endAwaitExpression(awaitToken, token);
|
| - return token;
|
| - }
|
| -
|
| - Token parseThrowExpression(Token token, bool allowCascades) {
|
| - Token throwToken = token;
|
| - listener.beginThrowExpression(throwToken);
|
| - token = expect('throw', token);
|
| - token = allowCascades
|
| - ? parseExpression(token)
|
| - : parseExpressionWithoutCascade(token);
|
| - listener.endThrowExpression(throwToken, token);
|
| - return token;
|
| - }
|
| -
|
| - Token parseRethrowStatement(Token token) {
|
| - Token throwToken = token;
|
| - listener.beginRethrowStatement(throwToken);
|
| - // TODO(kasperl): Disallow throw here.
|
| - if (identical(throwToken.stringValue, 'throw')) {
|
| - token = expect('throw', token);
|
| - } else {
|
| - token = expect('rethrow', token);
|
| - }
|
| - listener.endRethrowStatement(throwToken, token);
|
| - return expectSemicolon(token);
|
| - }
|
| -
|
| - Token parseTryStatement(Token token) {
|
| - assert(optional('try', token));
|
| - Token tryKeyword = token;
|
| - listener.beginTryStatement(tryKeyword);
|
| - token = parseBlock(token.next);
|
| - int catchCount = 0;
|
| -
|
| - String value = token.stringValue;
|
| - while (identical(value, 'catch') || identical(value, 'on')) {
|
| - var onKeyword = null;
|
| - if (identical(value, 'on')) {
|
| - // on qualified catchPart?
|
| - onKeyword = token;
|
| - token = parseType(token.next);
|
| - value = token.stringValue;
|
| - }
|
| - Token catchKeyword = null;
|
| - if (identical(value, 'catch')) {
|
| - catchKeyword = token;
|
| - // TODO(ahe): Validate the "parameters".
|
| - token = parseFormalParameters(token.next);
|
| - }
|
| - token = parseBlock(token);
|
| - ++catchCount;
|
| - listener.handleCatchBlock(onKeyword, catchKeyword);
|
| - value = token.stringValue; // while condition
|
| - }
|
| -
|
| - Token finallyKeyword = null;
|
| - if (optional('finally', token)) {
|
| - finallyKeyword = token;
|
| - token = parseBlock(token.next);
|
| - listener.handleFinallyBlock(finallyKeyword);
|
| - }
|
| - listener.endTryStatement(catchCount, tryKeyword, finallyKeyword);
|
| - return token;
|
| - }
|
| -
|
| - Token parseSwitchStatement(Token token) {
|
| - assert(optional('switch', token));
|
| - Token switchKeyword = token;
|
| - listener.beginSwitchStatement(switchKeyword);
|
| - token = parseParenthesizedExpression(token.next);
|
| - token = parseSwitchBlock(token);
|
| - listener.endSwitchStatement(switchKeyword, token);
|
| - return token.next;
|
| - }
|
| -
|
| - Token parseSwitchBlock(Token token) {
|
| - Token begin = token;
|
| - listener.beginSwitchBlock(begin);
|
| - token = expect('{', token);
|
| - int caseCount = 0;
|
| - while (!identical(token.kind, EOF_TOKEN)) {
|
| - if (optional('}', token)) {
|
| - break;
|
| - }
|
| - token = parseSwitchCase(token);
|
| - ++caseCount;
|
| - }
|
| - listener.endSwitchBlock(caseCount, begin, token);
|
| - expect('}', token);
|
| - return token;
|
| - }
|
| -
|
| - /**
|
| - * Peek after the following labels (if any). The following token
|
| - * is used to determine if the labels belong to a statement or a
|
| - * switch case.
|
| - */
|
| - Token peekPastLabels(Token token) {
|
| - while (token.isIdentifier() && optional(':', token.next)) {
|
| - token = token.next.next;
|
| - }
|
| - return token;
|
| - }
|
| -
|
| - /**
|
| - * Parse a group of labels, cases and possibly a default keyword and
|
| - * the statements that they select.
|
| - */
|
| - Token parseSwitchCase(Token token) {
|
| - Token begin = token;
|
| - Token defaultKeyword = null;
|
| - int expressionCount = 0;
|
| - int labelCount = 0;
|
| - Token peek = peekPastLabels(token);
|
| - while (true) {
|
| - // Loop until we find something that can't be part of a switch case.
|
| - String value = peek.stringValue;
|
| - if (identical(value, 'default')) {
|
| - while (!identical(token, peek)) {
|
| - token = parseLabel(token);
|
| - labelCount++;
|
| - }
|
| - defaultKeyword = token;
|
| - token = expect(':', token.next);
|
| - peek = token;
|
| - break;
|
| - } else if (identical(value, 'case')) {
|
| - while (!identical(token, peek)) {
|
| - token = parseLabel(token);
|
| - labelCount++;
|
| - }
|
| - Token caseKeyword = token;
|
| - token = parseExpression(token.next);
|
| - Token colonToken = token;
|
| - token = expect(':', token);
|
| - listener.handleCaseMatch(caseKeyword, colonToken);
|
| - expressionCount++;
|
| - peek = peekPastLabels(token);
|
| - } else {
|
| - if (expressionCount == 0) {
|
| - listener.expected("case", token);
|
| - }
|
| - break;
|
| - }
|
| - }
|
| - // Finally zero or more statements.
|
| - int statementCount = 0;
|
| - while (!identical(token.kind, EOF_TOKEN)) {
|
| - String value = peek.stringValue;
|
| - if ((identical(value, 'case')) ||
|
| - (identical(value, 'default')) ||
|
| - ((identical(value, '}')) && (identical(token, peek)))) {
|
| - // A label just before "}" will be handled as a statement error.
|
| - break;
|
| - } else {
|
| - token = parseStatement(token);
|
| - }
|
| - statementCount++;
|
| - peek = peekPastLabels(token);
|
| - }
|
| - listener.handleSwitchCase(labelCount, expressionCount, defaultKeyword,
|
| - statementCount, begin, token);
|
| - return token;
|
| - }
|
| -
|
| - Token parseBreakStatement(Token token) {
|
| - assert(optional('break', token));
|
| - Token breakKeyword = token;
|
| - token = token.next;
|
| - bool hasTarget = false;
|
| - if (token.isIdentifier()) {
|
| - token = parseIdentifier(token);
|
| - hasTarget = true;
|
| - }
|
| - listener.handleBreakStatement(hasTarget, breakKeyword, token);
|
| - return expectSemicolon(token);
|
| - }
|
| -
|
| - Token parseAssertStatement(Token token) {
|
| - Token assertKeyword = token;
|
| - Token commaToken = null;
|
| - token = expect('assert', token);
|
| - token = expect('(', token);
|
| - bool old = mayParseFunctionExpressions;
|
| - mayParseFunctionExpressions = true;
|
| - token = parseExpression(token);
|
| - if (optional(',', token)) {
|
| - commaToken = token;
|
| - token = token.next;
|
| - token = parseExpression(token);
|
| - }
|
| - token = expect(')', token);
|
| - mayParseFunctionExpressions = old;
|
| - listener.handleAssertStatement(assertKeyword, commaToken, token);
|
| - return expectSemicolon(token);
|
| - }
|
| -
|
| - Token parseContinueStatement(Token token) {
|
| - assert(optional('continue', token));
|
| - Token continueKeyword = token;
|
| - token = token.next;
|
| - bool hasTarget = false;
|
| - if (token.isIdentifier()) {
|
| - token = parseIdentifier(token);
|
| - hasTarget = true;
|
| - }
|
| - listener.handleContinueStatement(hasTarget, continueKeyword, token);
|
| - return expectSemicolon(token);
|
| - }
|
| -
|
| - Token parseEmptyStatement(Token token) {
|
| - listener.handleEmptyStatement(token);
|
| - return expectSemicolon(token);
|
| - }
|
| -}
|
|
|