// Copyright (c) 2017, 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 analyzer.parser;

/**
 * Proxy implementation of the analyzer parser, implemented in terms of the
 * Fasta parser.
 */
abstract class ParserAdapter implements Parser {
  @override
  Token currentToken;

  /**
   * The fasta parser being wrapped.
   */
  final fasta.Parser fastaParser;

  /**
   * The builder which creates the analyzer AST data structures
   * based on the Fasta parser.
   */
  final AstBuilder astBuilder;

  ParserAdapter(this.currentToken, ErrorReporter errorReporter, Uri fileUri,
      {bool allowNativeClause: false})
      : fastaParser = new fasta.Parser(null),
        astBuilder = new AstBuilder(errorReporter, fileUri, true) {
    fastaParser.listener = astBuilder;
    astBuilder.parser = fastaParser;
    astBuilder.allowNativeClause = allowNativeClause;
  }

  @override
  set allowNativeClause(bool value) {
    astBuilder.allowNativeClause = value;
  }

  @override
  bool get enableOptionalNewAndConst => false;

  @override
  void set enableOptionalNewAndConst(bool enable) {}

  @override
  void set parseFunctionBodies(bool parseFunctionBodies) {
    astBuilder.parseFunctionBodies = parseFunctionBodies;
  }

  @override
  set parseGenericMethods(_) {}

  /// Append the given token to the end of the token stream,
  /// and update the token's offset.
  appendToken(Token token, Token newToken) {
    while (!token.next.isEof) {
      token = token.next;
    }
    newToken
      ..offset = token.end
      ..setNext(token.next);
    token.setNext(newToken);
  }

  @override
  Expression parseAdditiveExpression() => parseExpression2();

  @override
  Annotation parseAnnotation() {
    currentToken = fastaParser
        .parseMetadata(fastaParser.syntheticPreviousToken(currentToken))
        .next;
    return astBuilder.pop();
  }

  @override
  Expression parseArgument() {
    currentToken = new SimpleToken(TokenType.OPEN_PAREN, 0)
      ..setNext(currentToken);
    appendToken(currentToken, new SimpleToken(TokenType.CLOSE_PAREN, 0));
    currentToken = fastaParser
        .parseArguments(fastaParser.syntheticPreviousToken(currentToken))
        .next;
    MethodInvocation invocation = astBuilder.pop();
    return invocation.argumentList.arguments[0];
  }

  @override
  ArgumentList parseArgumentList() {
    currentToken = fastaParser
        .parseArguments(fastaParser.syntheticPreviousToken(currentToken))
        .next;
    var result = astBuilder.pop();
    return result is MethodInvocation ? result.argumentList : result;
  }

  @override
  Expression parseAssignableExpression(bool primaryAllowed) =>
      parseExpression2();

  @override
  Expression parseBitwiseAndExpression() => parseExpression2();

  @override
  Expression parseBitwiseOrExpression() => parseExpression2();

  @override
  Expression parseBitwiseXorExpression() => parseExpression2();

  @override
  ClassMember parseClassMember(String className) {
    astBuilder.classDeclaration = astFactory.classDeclaration(
      null,
      null,
      null,
      new Token(Keyword.CLASS, 0),
      astFactory.simpleIdentifier(
          new fasta.StringToken.fromString(TokenType.IDENTIFIER, className, 6)),
      null,
      null,
      null,
      null,
      null /* leftBracket */,
      <ClassMember>[],
      null /* rightBracket */,
    );
    currentToken = fastaParser.parseClassOrMixinMember(currentToken);
    ClassDeclaration declaration = astBuilder.classDeclaration;
    astBuilder.classDeclaration = null;
    return declaration.members.isNotEmpty ? declaration.members[0] : null;
  }

  @override
  List<Combinator> parseCombinators() {
    currentToken = fastaParser
        .parseCombinatorStar(fastaParser.syntheticPreviousToken(currentToken))
        .next;
    return astBuilder.pop();
  }

  @override
  CompilationUnit parseCompilationUnit(Token token) {
    currentToken = token;
    return parseCompilationUnit2();
  }

  @override
  CompilationUnit parseCompilationUnit2() {
    currentToken = fastaParser.parseUnit(currentToken);
    CompilationUnitImpl compilationUnit = astBuilder.pop();
    compilationUnit.localDeclarations = astBuilder.localDeclarations;
    return compilationUnit;
  }

  @override
  Expression parseConditionalExpression() => parseExpression2();

  @override
  Configuration parseConfiguration() {
    currentToken = fastaParser
        .parseConditionalUri(fastaParser.syntheticPreviousToken(currentToken))
        .next;
    return astBuilder.pop();
  }

  @override
  Expression parseConstExpression() => parseExpression2();

  @override
  CompilationUnit parseDirectives(Token token) {
    currentToken = token;
    return parseDirectives2();
  }

  @override
  CompilationUnit parseDirectives2() {
    currentToken = fastaParser.parseDirectives(currentToken);
    return astBuilder.pop();
  }

  @override
  DottedName parseDottedName() {
    currentToken = fastaParser
        .parseDottedName(fastaParser.syntheticPreviousToken(currentToken))
        .next;
    return astBuilder.pop();
  }

  @override
  Expression parseEqualityExpression() => parseExpression2();

  @override
  Expression parseExpression(Token token) {
    currentToken = token;
    return parseExpression2();
  }

  @override
  Expression parseExpression2() {
    currentToken = fastaParser
        .parseExpression(fastaParser.syntheticPreviousToken(currentToken))
        .next;
    return astBuilder.pop();
  }

  @override
  Expression parseExpressionWithoutCascade() => parseExpression2();

  @override
  FormalParameterList parseFormalParameterList({bool inFunctionType: false}) {
    currentToken = fastaParser
        .parseFormalParametersRequiredOpt(
            fastaParser.syntheticPreviousToken(currentToken),
            inFunctionType
                ? fasta.MemberKind.GeneralizedFunctionType
                : fasta.MemberKind.NonStaticMethod)
        .next;
    return astBuilder.pop();
  }

  @override
  FunctionBody parseFunctionBody(
      bool mayBeEmpty, ParserErrorCode emptyErrorCode, bool inExpression) {
    currentToken = fastaParser.parseAsyncModifierOpt(
        fastaParser.syntheticPreviousToken(currentToken));
    currentToken =
        fastaParser.parseFunctionBody(currentToken, inExpression, mayBeEmpty);
    return astBuilder.pop();
  }

  @override
  FunctionExpression parseFunctionExpression() => parseExpression2();

  @override
  Expression parseLogicalAndExpression() => parseExpression2();

  @override
  Expression parseLogicalOrExpression() => parseExpression2();

  @override
  Expression parseMultiplicativeExpression() => parseExpression2();

  @override
  InstanceCreationExpression parseNewExpression() => parseExpression2();

  @override
  Expression parsePostfixExpression() => parseExpression2();

  @override
  Identifier parsePrefixedIdentifier() => parseExpression2();

  @override
  Expression parsePrimaryExpression() {
    currentToken = fastaParser
        .parsePrimary(fastaParser.syntheticPreviousToken(currentToken),
            fasta.IdentifierContext.expression)
        .next;
    return astBuilder.pop();
  }

  @override
  Expression parseRelationalExpression() => parseExpression2();

  @override
  Expression parseRethrowExpression() => parseExpression2();

  @override
  Expression parseShiftExpression() => parseExpression2();

  @override
  SimpleIdentifier parseSimpleIdentifier(
          {bool allowKeyword: false, bool isDeclaration: false}) =>
      parseExpression2();

  @override
  Statement parseStatement(Token token) {
    currentToken = token;
    return parseStatement2();
  }

  @override
  Statement parseStatement2() {
    currentToken = fastaParser
        .parseStatement(fastaParser.syntheticPreviousToken(currentToken))
        .next;
    return astBuilder.pop();
  }

  @override
  StringLiteral parseStringLiteral() => parseExpression2();

  @override
  SymbolLiteral parseSymbolLiteral() => parseExpression2();

  @override
  Expression parseThrowExpression() => parseExpression2();

  @override
  Expression parseThrowExpressionWithoutCascade() => parseExpression2();

  AnnotatedNode parseTopLevelDeclaration(bool isDirective) {
    currentToken = fastaParser.parseTopLevelDeclaration(currentToken);
    return (isDirective ? astBuilder.directives : astBuilder.declarations)
        .removeLast();
  }

  @override
  TypeAnnotation parseTypeAnnotation(bool inExpression) {
    Token previous = fastaParser.syntheticPreviousToken(currentToken);
    currentToken = fasta
        .computeType(previous, true, !inExpression)
        .parseType(previous, fastaParser)
        .next;
    return astBuilder.pop();
  }

  @override
  TypeArgumentList parseTypeArgumentList() {
    Token previous = fastaParser.syntheticPreviousToken(currentToken);
    currentToken = fasta
        .computeTypeParamOrArg(previous)
        .parseArguments(previous, fastaParser)
        .next;
    return astBuilder.pop();
  }

  @override
  TypeName parseTypeName(bool inExpression) {
    Token previous = fastaParser.syntheticPreviousToken(currentToken);
    currentToken = fasta
        .computeType(previous, true, !inExpression)
        .parseType(previous, fastaParser)
        .next;
    return astBuilder.pop();
  }

  @override
  TypeParameter parseTypeParameter() {
    currentToken = new SyntheticBeginToken(TokenType.LT, 0)
      ..endGroup = new SyntheticToken(TokenType.GT, 0)
      ..setNext(currentToken);
    appendToken(currentToken, currentToken.endGroup);
    TypeParameterList typeParams = parseTypeParameterList();
    return typeParams.typeParameters[0];
  }

  @override
  TypeParameterList parseTypeParameterList() {
    Token token = fastaParser.syntheticPreviousToken(currentToken);
    currentToken = fasta
        .computeTypeParamOrArg(token, true)
        .parseVariables(token, fastaParser)
        .next;
    return astBuilder.pop();
  }

  @override
  Expression parseUnaryExpression() => parseExpression2();
}

/**
 * Replacement parser based on Fasta.
 */
class _Parser2 extends ParserAdapter {
  /**
   * The source being parsed.
   */
  final Source _source;

  @override
  bool enableUriInPartOf = true;

  factory _Parser2(Source source, AnalysisErrorListener errorListener,
      {bool allowNativeClause: false}) {
    var errorReporter = new ErrorReporter(errorListener, source);
    return new _Parser2._(source, errorReporter, source.uri,
        allowNativeClause: allowNativeClause);
  }

  _Parser2._(this._source, ErrorReporter errorReporter, Uri fileUri,
      {bool allowNativeClause: false})
      : super(null, errorReporter, fileUri,
            allowNativeClause: allowNativeClause);

  noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
