//===--- ASTVisitor.h - Decl, Expr and Stmt Visitor -------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the ASTVisitor class, and the DeclVisitor, ExprVisitor, and
// StmtVisitor template typedefs.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_AST_ASTVISITOR_H
#define SWIFT_AST_ASTVISITOR_H

#include "swift/AST/Decl.h"
#include "swift/AST/Expr.h"
#include "swift/AST/Module.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/Stmt.h"
#include "swift/AST/TypeRepr.h"
#include "llvm/Support/ErrorHandling.h"

namespace swift {
  class ParameterList;
  
/// ASTVisitor - This is a simple visitor class for Swift expressions.
template<typename ImplClass,
         typename ExprRetTy = void,
         typename StmtRetTy = void,
         typename DeclRetTy = void,
         typename PatternRetTy = void,
         typename TypeReprRetTy = void,
         typename AttributeRetTy = void,
         typename... Args>
class ASTVisitor {
public:
  typedef ASTVisitor ASTVisitorType;

  DeclRetTy visit(Decl *D, Args... AA) {
    switch (D->getKind()) {
#define DECL(CLASS, PARENT) \
    case DeclKind::CLASS: \
      return static_cast<ImplClass*>(this) \
        ->visit##CLASS##Decl(static_cast<CLASS##Decl*>(D), \
                             ::std::forward<Args>(AA)...);
#include "swift/AST/DeclNodes.def"
    }
    llvm_unreachable("Not reachable, all cases handled");
  }

  ExprRetTy visit(Expr *E, Args... AA) {
    switch (E->getKind()) {

#define EXPR(CLASS, PARENT) \
    case ExprKind::CLASS: \
      return static_cast<ImplClass*>(this) \
        ->visit##CLASS##Expr(static_cast<CLASS##Expr*>(E), \
                             ::std::forward<Args>(AA)...);
#include "swift/AST/ExprNodes.def"

    }
    llvm_unreachable("Not reachable, all cases handled");
  }
  
  // Provide default implementations of abstract "visit" implementations that
  // just chain to their base class.  This allows visitors to just implement
  // the base behavior and handle all subclasses if they desire.  Since this is
  // a template, it will only instantiate cases that are used and thus we still
  // require full coverage of the AST nodes by the visitor.
#define ABSTRACT_EXPR(CLASS, PARENT)                                \
  ExprRetTy visit##CLASS##Expr(CLASS##Expr *E, Args... AA) {  \
     return static_cast<ImplClass*>(this)->visit##PARENT(E, \
                                                ::std::forward<Args>(AA)...);  \
  }
#define EXPR(CLASS, PARENT) ABSTRACT_EXPR(CLASS, PARENT)
#include "swift/AST/ExprNodes.def"

  StmtRetTy visit(Stmt *S, Args... AA) {
    switch (S->getKind()) {

#define STMT(CLASS, PARENT) \
    case StmtKind::CLASS: \
      return static_cast<ImplClass*>(this) \
        ->visit##CLASS##Stmt(static_cast<CLASS##Stmt*>(S), \
                             ::std::forward<Args>(AA)...);
#include "swift/AST/StmtNodes.def"

    }
    llvm_unreachable("Not reachable, all cases handled");
  }

#define DECL(CLASS, PARENT) \
  DeclRetTy visit##CLASS##Decl(CLASS##Decl *D, Args... AA) {\
    return static_cast<ImplClass*>(this)->visit##PARENT(D, \
                                                 ::std::forward<Args>(AA)...); \
  }
#define ABSTRACT_DECL(CLASS, PARENT) DECL(CLASS, PARENT)
#include "swift/AST/DeclNodes.def"

  PatternRetTy visit(Pattern *P, Args... AA) {
    switch (P->getKind()) {
#define PATTERN(CLASS, PARENT) \
    case PatternKind::CLASS: \
      return static_cast<ImplClass*>(this) \
        ->visit##CLASS##Pattern(static_cast<CLASS##Pattern*>(P), \
                                ::std::forward<Args>(AA)...);
#include "swift/AST/PatternNodes.def"
    }
    llvm_unreachable("Not reachable, all cases handled");
  }

  TypeReprRetTy visit(TypeRepr *T, Args... AA) {
    switch (T->getKind()) {
#define TYPEREPR(CLASS, PARENT) \
    case TypeReprKind::CLASS: \
      return static_cast<ImplClass*>(this) \
        ->visit##CLASS##TypeRepr(static_cast<CLASS##TypeRepr*>(T), \
                                 ::std::forward<Args>(AA)...);
#include "swift/AST/TypeReprNodes.def"
    }
    llvm_unreachable("Not reachable, all cases handled");
  }

#define TYPEREPR(CLASS, PARENT) \
  TypeReprRetTy visit##CLASS##TypeRepr(CLASS##TypeRepr *T, Args... AA) {\
    return static_cast<ImplClass*>(this)->visit##PARENT(T, \
                                                 ::std::forward<Args>(AA)...); \
  }
#define ABSTRACT_TYPEREPR(CLASS, PARENT) TYPEREPR(CLASS, PARENT)
#include "swift/AST/TypeReprNodes.def"

  AttributeRetTy visit(DeclAttribute *A, Args... AA) {
    switch (A->getKind()) {
#define DECL_ATTR(_, CLASS, ...)                           \
    case DAK_##CLASS:                                              \
      return static_cast<ImplClass*>(this)                        \
               ->visit##CLASS##Attr(static_cast<CLASS##Attr*>(A), \
                                    ::std::forward<Args>(AA)...);
#include "swift/AST/Attr.def"

    case DAK_Count:
      llvm_unreachable("Not an attribute kind");
    }
  }

#define DECL_ATTR(NAME,CLASS,...) \
  AttributeRetTy visit##CLASS##Attr(CLASS##Attr *A, Args... AA) { \
    return static_cast<ImplClass*>(this)->visitDeclAttribute(       \
             A, ::std::forward<Args>(AA)...);                       \
  }
#include "swift/AST/Attr.def"
  
  bool visit(ParameterList *PL) {
    return static_cast<ImplClass*>(this)->visitParameterList(PL);
  }
  
  bool visitParameterList(ParameterList *PL) { return false; }
};
  
  
template<typename ImplClass, typename ExprRetTy = void, typename... Args>
using ExprVisitor = ASTVisitor<ImplClass, ExprRetTy, void, void, void, void,
                               void, Args...>;

template<typename ImplClass, typename StmtRetTy = void, typename... Args>
using StmtVisitor = ASTVisitor<ImplClass, void, StmtRetTy, void, void, void,
                               void, Args...>;

template<typename ImplClass, typename DeclRetTy = void, typename... Args>
using DeclVisitor = ASTVisitor<ImplClass, void, void, DeclRetTy, void, void,
                               void, Args...>;

template<typename ImplClass, typename PatternRetTy = void, typename... Args>
using PatternVisitor = ASTVisitor<ImplClass, void,void,void, PatternRetTy, void,
                                  void, Args...>;

template<typename ImplClass, typename TypeReprRetTy = void, typename... Args>
using TypeReprVisitor = ASTVisitor<ImplClass, void,void,void,void,TypeReprRetTy,
                                   void, Args...>;

template<typename ImplClass, typename AttributeRetTy = void, typename... Args>
using AttributeVisitor = ASTVisitor<ImplClass, void,void,void,void,void,
                                    AttributeRetTy, Args...>;

} // end namespace swift

#endif
