content: // 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.
  
  part of scanner;
  
  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;
  
    Parser(this.listener);
  
    Token parseUnit(Token token) {
      listener.beginCompilationUnit(token);
      int count = 0;
      while (!identical(token.kind, EOF_TOKEN)) {
        token = parseTopLevelDeclaration(token);
        listener.endTopLevelDeclaration(token);
        count++;
      }
      listener.endCompilationUnit(count, 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, '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 (as identifier)? combinator* ';'
    Token parseImport(Token token) {
      Token importKeyword = token;
      listener.beginImport(importKeyword);
      assert(optional('import', token));
      token = parseLiteralStringOrRecoverExpression(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, asKeyword, semicolon);
      return token;
    }
  
    /// export uri combinator* ';'
    Token parseExport(Token token) {
      Token exportKeyword = token;
      listener.beginExport(exportKeyword);
      assert(optional('export', token));
      token = parseLiteralStringOrRecoverExpression(token.next);
      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);
          return token;
        }
        count++;
      }
    }
  
    /// 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) {
      while (optional('@', token)) {
        token = parseMetadata(token);
      }
      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;
      if (optional('=', peekAfterType(token.next))) {
        // TODO(aprelev@gmail.com): Remove deprecated 'typedef' mixin application,
        // remove corresponding diagnostic from members.dart.
        listener.beginNamedMixinApplication(token);
        token = parseIdentifier(token.next);
        token = parseTypeVariablesOpt(token);
        token = expect('=', token);
        token = parseModifiers(token);
        token = parseMixinApplication(token);
        Token implementsKeyword = null;
        if (optional('implements', token)) {
          implementsKeyword = token;
          token = parseTypeList(token.next);
        }
        listener.endNamedMixinApplication(
            typedefKeyword, implementsKeyword, token);
      } else {
        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;
      if (optional(')', token.next)) {
        listener.endFormalParameters(parameterCount, begin, token.next);
        return token.next.next;
      }
      do {
        ++parameterCount;
        token = token.next;
        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) {
      listener.beginFormalParameter(token);
      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)) {
        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.isNamed && identical('=', value)) {
          listener.reportError(equal, MessageKind.NAMED_PARAMETER_WITH_EQUALS);
        } 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;
        var type = isNamed ? FormalParameterType.NAMED
                           : FormalParameterType.POSITIONAL;
        token = parseFormalParameter(token, type);
        ++parameterCount;
      } while (optional(',', token));
      listener.endOptionalFormalParameters(parameterCount, begin, token);
      if (isNamed) {
        return expect('}', token);
      } else {
        return expect(']', token);
      }
    }
  
    Token parseTypeOpt(Token token) {
      String value = token.stringValue;
      if (!identical(value, 'this')) {
        Token peek = peekAfterExpectedType(token);
        if (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;
    }
  
    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;
    }
  
    bool isDefaultKeyword(Token token) {
      String value = token.stringValue;
      if (identical(value, 'default')) return true;
      if (identical(value, 'factory')) {
        listener.recoverableError("expected 'default'", token: token);
        return true;
      }
      return false;
    }
  
    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, $CLOSE_CURLY_BRACKET)) {
        return listener.unmatched(beginGroupToken);
      }
      return beginGroupToken.endGroup;
    }
  
    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);
        token = parseIdentifier(token.next);
        token = parseTypeVariablesOpt(token);
        token = expect('=', token);
      } else {
        listener.beginClassDeclaration(begin);
      }
  
      // TODO(aprelev@gmail.com): Once 'typedef' named mixin application is
      // removed, move modifiers for named mixin application to the bottom of
      // listener stack. This is so stacks for class declaration and named
      // mixin application look similar.
      int modifierCount = 0;
      if (abstractKeyword != null) {
        parseModifier(abstractKeyword);
        modifierCount++;
      }
      listener.handleModifiers(modifierCount);
  
      if (isMixinApplication) {
        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()) {
        listener.handleIdentifier(token);
      } else {
        listener.expectedIdentifier(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);
      if (optional('extends', token)) {
        token = parseType(token.next);
      } else {
        listener.handleNoType(token);
      }
      listener.endTypeVariable(token);
      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], [value3], or [value4].
     */
    bool isOneOf4(Token token,
                  String value1, String value2, String value3, String value4) {
      String stringValue = token.stringValue;
      return identical(value1, stringValue) ||
             identical(value2, stringValue) ||
             identical(value3, stringValue) ||
             identical(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;
        } else if (identical(token.stringValue, '>>>')) {
          token = new SymbolToken(GT_INFO, token.charOffset);
          token.next = new SymbolToken(GT_GT_INFO, token.charOffset + 1);
          token.next.next = next;
        }
        endStuff(count, begin, token);
        return expect('>', token);
      }
      handleNoStuff(token);
      return token;
    }
  
    Token parseTopLevelMember(Token token) {
      Token start = token;
      listener.beginTopLevelMember(token);
  
      Link<Token> identifiers = findMemberName(token);
      if (identifiers.isEmpty) {
        return listener.unexpected(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 = name.next;
      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;
    }
  
    Token parseFields(Token start,
                      Link<Token> modifiers,
                      Token type,
                      Token getOrSet,
                      Token name,
                      bool isTopLevel) {
      bool hasType = type != null;
      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;
      }
      expectSemicolon(token);
      if (isTopLevel) {
        listener.endTopLevelFields(fieldCount, start, token);
      } else {
        listener.endFields(fieldCount, start, token);
      }
      return token.next;
    }
  
    Token parseTopLevelMethod(Token start,
                              Link<Token> modifiers,
                              Token type,
                              Token getOrSet,
                              Token name) {
      Token externalModifier;
      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);
  
      token = parseFormalParametersOpt(token);
      token = parseFunctionBody(token, false, externalModifier != null);
      listener.endTopLevelMethod(start, getOrSet, token);
      return token.next;
    }
  
    Link<Token> findMemberName(Token token) {
      Token start = token;
      Link<Token> identifiers = const Link<Token>();
      while (!identical(token.kind, EOF_TOKEN)) {
        String value = token.stringValue;
        if ((identical(value, '(')) || (identical(value, '{'))
            || (identical(value, '=>'))) {
          // A method.
          return identifiers;
        } else if ((identical(value, '=')) || (identical(value, ';'))
            || (identical(value, ','))) {
          // A field or abstract getter.
          return identifiers;
        }
        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 listener.expectedDeclaration(start);
    }
  
    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("unexpected", token: token);
        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);
        }
        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;
    }
  
    Token peekAfterType(Token token) {
      // TODO(ahe): Also handle var?
      // 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;
    }
  
    /**
     * Returns the token after the type which is expected to begin at [token].
     * If [token] is not the start of a type, [Listener.expectedType] is called.
     */
    Token peekAfterExpectedType(Token token) {
      if (!identical('void', token.stringValue) && !token.isIdentifier()) {
        return listener.expectedType(token);
      }
      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);
      String value = token.stringValue;
      if (isFactoryDeclaration(token)) {
        return parseFactoryMethod(token);
      }
      Token start = token;
      listener.beginMember(token);
  
      Link<Token> identifiers = findMemberName(token);
      if (identifiers.isEmpty) {
        return listener.unexpected(start);
      }
      Token name = identifiers.head;
      Token afterName = name.next;
      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, '=>'))) {
          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);
            return token;
          }
        }
      }
  
      var modifiers = identifiers.reverse();
      return isField
          ? parseFields(start, modifiers, type, getOrSet, name, false)
          : parseMethod(start, modifiers, type, getOrSet, name);
  
    }
  
    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;
      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});
        }
      }
      parseModifierList(modifiers);
  
      if (type == null) {
        listener.handleNoType(name);
      } else {
        parseReturnTypeOpt(type);
      }
      Token token;
      if (optional('operator', name)) {
        token = parseOperatorName(name);
        if (staticModifier != null) {
          // TODO(ahe): Consider a more specific error message.
          listener.reportError(
              staticModifier, MessageKind.EXTRANEOUS_MODIFIER,
              {'modifier': staticModifier});
        }
      } else {
        token = parseIdentifier(name);
      }
  
      token = parseQualifiedRestOpt(token);
      token = parseFormalParametersOpt(token);
      token = parseInitializersOpt(token);
      if (optional('=', token)) {
        token = parseRedirectingFactoryBody(token);
      } else {
        token = parseFunctionBody(
            token, false, staticModifier == null || externalModifier != null);
      }
      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;
      }
      Token constKeyword = null;
      if (optional('const', token)) {
        constKeyword = token;
        token = token.next;
      }
      Token factoryKeyword = token;
      listener.beginFactoryMethod(factoryKeyword);
      token = token.next; // Skip 'factory'.
      token = parseConstructorReference(token);
      token = parseFormalParameters(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)) token = token.next;
      if (optional('operator', token)) {
        listener.handleNoType(token);
        listener.beginFunctionName(token);
        token = parseOperatorName(token);
      } else {
        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);
      token = parseFormalParametersOpt(token);
      token = parseInitializersOpt(token);
      if (optional('=', token)) {
        token = parseRedirectingFactoryBody(token);
      } else {
        token = parseFunctionBody(token, false, true);
      }
      listener.endFunction(getOrSet, token);
      return token.next;
    }
  
    Token parseUnamedFunction(Token token) {
      listener.beginUnamedFunction(token);
      token = parseFormalParameters(token);
      bool isBlock = optional('{', token);
      token = parseFunctionBody(token, true, false);
      listener.endUnamedFunction(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 = parseFormalParameters(token);
      listener.handleNoInitializers();
      bool isBlock = optional('{', token);
      token = parseFunctionBody(token, true, false);
      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 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 (identical(value, 'for')) {
        return parseForStatement(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 (identical(value, 'const')) {
        return parseExpressionStatementOrConstDeclaration(token);
      } else if (token.isIdentifier()) {
        return parseExpressionStatementOrDeclaration(token);
      } else {
        return parseExpressionStatement(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 = peekIdentifierAfterType(token);
      if (peek != null) {
        // 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;
          Token afterParens = endParen.next;
          if (optional('{', afterParens) || optional('=>', afterParens)) {
            // We are looking at "type identifier '(' ... ')'" followed
            // by '=>' or '{'.
            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;
          String afterParens = begin.endGroup.next.stringValue;
          if (identical(afterParens, '{') || identical(afterParens, '=>')) {
            return parseFunctionDeclaration(token);
          }
        }
      }
      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)) {
              // 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 (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)) {
          token = parseArguments(token);
          listener.endSend(token);
        } else {
          break;
        }
      }
      return token;
    }
  
    Token parsePrimary(Token token) {
      final kind = token.kind;
      if (identical(kind, IDENTIFIER_TOKEN)) {
        return parseSendOrFunctionLiteral(token);
      } else if (identical(kind, INT_TOKEN)
          || identical(kind, HEXADECIMAL_TOKEN)) {
        return parseLiteralInt(token);
      } else if (identical(kind, DOUBLE_TOKEN)) {
        return parseLiteralDouble(token);
      } else if (identical(kind, STRING_TOKEN)) {
        return parseLiteralString(token);
      } else if (identical(kind, HASH_TOKEN)) {
        return parseLiteralSymbol(token);
      } else if (identical(kind, KEYWORD_TOKEN)) {
        final value = token.stringValue;
        if ((identical(value, 'true')) || (identical(value, 'false'))) {
          return parseLiteralBool(token);
        } else if (identical(value, 'null')) {
          return parseLiteralNull(token);
        } else if (identical(value, 'this')) {
          return parseThisExpression(token);
        } else if (identical(value, 'super')) {
          return parseSuperExpression(token);
        } else if (identical(value, 'new')) {
          return parseNewExpression(token);
        } else if (identical(value, 'const')) {
          return parseConstExpression(token);
        } else if (identical(value, 'void')) {
          return parseFunctionExpression(token);
        } else if (token.isIdentifier()) {
          return parseSendOrFunctionLiteral(token);
        } else {
          return listener.expectedExpression(token);
        }
      } else if (identical(kind, OPEN_PAREN_TOKEN)) {
        return parseParenthesizedExpressionOrFunctionLiteral(token);
      } else if ((identical(kind, LT_TOKEN)) ||
                 (identical(kind, OPEN_SQUARE_BRACKET_TOKEN)) ||
                 (identical(kind, OPEN_CURLY_BRACKET_TOKEN)) ||
                 identical(token.stringValue, '[]')) {
        return parseLiteralListOrMap(token);
      } else {
        return listener.expectedExpression(token);
      }
    }
  
    Token parseParenthesizedExpressionOrFunctionLiteral(Token token) {
      BeginGroupToken beginGroup = token;
      int kind = beginGroup.endGroup.next.kind;
      if (mayParseFunctionExpressions &&
          (identical(kind, FUNCTION_TOKEN)
              || identical(kind, OPEN_CURLY_BRACKET_TOKEN))) {
        return parseUnamedFunction(token);
      } else {
        bool old = mayParseFunctionExpressions;
        mayParseFunctionExpressions = true;
        token = parseParenthesizedExpression(token);
        mayParseFunctionExpressions = old;
        return token;
      }
    }
  
    Token parseParenthesizedExpression(Token token) {
      var begin = (token as BeginGroupToken);
      token = expect('(', token);
      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.
        token = parseArguments(token);
        listener.endSend(token);
      }
      return token;
    }
  
    Token parseSuperExpression(Token token) {
      listener.handleSuperExpression(token);
      token = token.next;
      if (optional('(', token)) {
        // Super constructor.
        token = parseArguments(token);
        listener.endSend(token);
      }
      return token;
    }
  
    Token parseLiteralListOrMap(Token token) {
      Token constKeyword = null;
      if (optional('const', token)) {
        constKeyword = token;
        token = token.next;
      }
      token = parseTypeArgumentsOpt(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 = parseMapLiteralEntry(token.next);
          ++count;
        } while (optional(',', token));
        mayParseFunctionExpressions = old;
        listener.handleLiteralMap(count, beginToken, constKeyword, token);
        return expect('}', token);
      } else 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);
      } else if (optional('[]', token)) {
        listener.handleLiteralList(0, token, constKeyword, token);
        return token.next;
      } else {
        listener.unexpected(token);
      }
    }
  
    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 = peekAfterExpectedType(token);
      if (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;
        String afterParens = begin.endGroup.next.stringValue;
        if (identical(afterParens, '{') || identical(afterParens, '=>')) {
          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, '[')) ||
          (identical(value, '[]')) ||
          (identical(value, '{'))) {
        return parseLiteralListOrMap(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) {
      token = parseSingleLiteralString(token);
      int count = 1;
      while (identical(token.kind, STRING_TOKEN)) {
        token = parseSingleLiteralString(token);
        count++;
      }
      if (count > 1) {
        listener.handleStringJuxtaposition(count);
      }
      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);
      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 {
        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 token) {
      Token forToken = token;
      listener.beginForStatement(forToken);
      token = expect('for', token);
      token = expect('(', token);
      token = parseVariablesDeclarationOrExpressionOpt(token);
      if (optional('in', token)) {
        return parseForInRest(forToken, token);
      } else {
        return parseForRest(forToken, token);
      }
    }
  
    Token parseVariablesDeclarationOrExpressionOpt(Token token) {
      final String value = token.stringValue;
      if (identical(value, ';')) {
        listener.handleNoExpression(token);
        return token;
      } else if ((identical(value, 'var')) || (identical(value, 'final'))) {
        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 forToken, Token token) {
      assert(optional('in', token));
      Token inKeyword = token;
      token = parseExpression(token.next);
      token = expect(')', token);
      token = parseStatement(token);
      listener.endForIn(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 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 = expect('assert', token);
      expect('(', token);
      token = parseArguments(token);
      listener.handleAssertStatement(assertKeyword, 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);
    }
  }
  