//===-- gen/recursivevisitor.h - Code Coverage Analysis ---------*- C++ -*-===//
//
//                         LDC – the LLVM D compiler
//
//
// The RecursiveWalker is based on apply.c from DMD, with the following license:
//
// Compiler implementation of the D programming language
// Copyright (c) 1999-2014 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// Distributed under the Boost Software License, Version 1.0.
// http://www.boost.org/LICENSE_1_0.txt
// https://github.com/D-Programming-Language/dmd/blob/master/src/apply.c
//
// See the LICENSE file for more details.
//
//===----------------------------------------------------------------------===//
//
// This file contains a RecursiveVisitor with default implementations that
// recursively visits all statements unless overridden.
// It also contains a RecursiveWalker, that recursively walks the tree
// (depth-first) and calls the visitor for each node before recursing deeper
// down the tree.
//
//===----------------------------------------------------------------------===//

#ifndef LDC_GEN_RECURSIVEVISITOR_H
#define LDC_GEN_RECURSIVEVISITOR_H

#include "ddmd/attrib.h"
#include "ddmd/declaration.h"
#include "ddmd/init.h"
#include "ddmd/statement.h"
#include "ddmd/visitor.h"

class RecursiveVisitor : public Visitor {
public:
  template <class T> void recurse(T *s) {
    if (s)
      s->accept(this);
  }

  template <class T> void recurse(Array<T> *stmts) {
    if (stmts) {
      for (auto s : *stmts) {
        recurse(s);
      }
    }
  }

  using Visitor::visit;

  void visit(CompoundStatement *stmt) override {
    for (auto s : *stmt->statements) {
      recurse(s);
    }
  }

  void visit(ReturnStatement *stmt) override { recurse(stmt->exp); }

  void visit(ExpStatement *stmt) override { recurse(stmt->exp); }

  void visit(IfStatement *stmt) override {
    recurse(stmt->condition);
    recurse(stmt->ifbody);
    recurse(stmt->elsebody);
  }

  void visit(ScopeStatement *stmt) override { recurse(stmt->statement); }

  void visit(WhileStatement *stmt) override {
    recurse(stmt->condition);
    recurse(stmt->_body);
  }

  void visit(DoStatement *stmt) override {
    recurse(stmt->_body);
    recurse(stmt->condition);
  }

  void visit(ForStatement *stmt) override {
    recurse(stmt->_init);
    recurse(stmt->condition);
    recurse(stmt->_body);
    recurse(stmt->increment);
  }

  void visit(ForeachStatement *stmt) override {
    recurse(stmt->aggr);
    recurse(stmt->_body);
  }

  void visit(ForeachRangeStatement *stmt) override {
    recurse(stmt->lwr);
    recurse(stmt->upr);
    recurse(stmt->_body);
  }

  void visit(OnScopeStatement *stmt) override {
    stmt->error("Internal Compiler Error: OnScopeStatement should have been "
                "lowered by frontend.");
    fatal();
  }

  void visit(ThrowStatement *stmt) override {
    recurse(stmt);
    recurse(stmt->exp);
  }

  void visit(TryFinallyStatement *stmt) override {
    recurse(stmt->_body);
    recurse(stmt->finalbody);
  }

  void visit(TryCatchStatement *stmt) override {
    recurse(stmt->_body);
    for (auto c : *stmt->catches) {
      recurse(c->handler);
    }
  }

  void visit(SwitchStatement *stmt) override {
    recurse(stmt->condition);
    recurse(stmt->_body);
    for (auto cs : *stmt->cases) {
      recurse(cs);
    }
  }

  void visit(CaseStatement *stmt) override { recurse(stmt->statement); }

  void visit(DefaultStatement *stmt) override { recurse(stmt->statement); }

  void visit(UnrolledLoopStatement *stmt) override {
    recurse(stmt->statements);
  }

  void visit(LabelStatement *stmt) override { recurse(stmt->statement); }

  void visit(SynchronizedStatement *stmt) override {
    recurse(stmt->exp);
    recurse(stmt->_body);
  }

  void visit(WithStatement *stmt) override {
    recurse(stmt->wthis);
    recurse(stmt->exp);
    recurse(stmt->_body);
  }

  void visit(DebugStatement *stmt) override {
    recurse(stmt);
    recurse(stmt->statement);
  }

  void visit(NewExp *e) override {
    recurse(e->thisexp);
    recurse(e->newargs);
    recurse(e->argprefix);
    recurse(e->arguments);
  }

  void visit(NewAnonClassExp *e) override {
    recurse(e->thisexp);
    recurse(e->newargs);
    recurse(e->arguments);
  }

  void visit(UnaExp *e) override { recurse(e->e1); }

  void visit(BinExp *e) override {
    recurse(e->e1);
    recurse(e->e2);
  }

  void visit(AssertExp *e) override {
    recurse(e->e1);
    recurse(e->msg);
  }

  void visit(CallExp *e) override {
    recurse(e->e1);
    recurse(e->arguments);
  }

  void visit(ArrayExp *e) override {
    recurse(e->e1);
    recurse(e->arguments);
  }

  void visit(SliceExp *e) override {
    recurse(e->e1);
    recurse(e->lwr);
    recurse(e->upr);
  }

  void visit(ArrayLiteralExp *e) override { recurse(e->elements); }

  void visit(AssocArrayLiteralExp *e) override {
    recurse(e->keys);
    recurse(e->values);
  }

  void visit(StructLiteralExp *e) override {
    // use stageflags to prevent infinite recursion
    if (e->stageflags & 0x2000)
      return;
    int old = e->stageflags;
    e->stageflags |= 0x2000;
    recurse(e->elements);
    e->stageflags = old;
  }

  void visit(TupleExp *e) override {
    recurse(e->e0);
    recurse(e->exps);
  }

  void visit(CondExp *e) override {
    recurse(e->econd);
    recurse(e->e1);
    recurse(e->e2);
  }

  void visit(DeclarationExp *e) override { recurse(e->declaration); }

  void visit(VarDeclaration *decl) override {
    recurse(decl->_init);
    recurse(decl->edtor);
  }

  void visit(ExpInitializer *init) override { recurse(init->exp); }

  // Override the default assert(0) behavior of Visitor:
  void visit(Statement *) override {}   // do nothing
  void visit(Expression *) override {}  // do nothing
  void visit(Declaration *) override {} // do nothing
  void visit(Initializer *) override {} // do nothing
  void visit(Dsymbol *) override {}     // do nothing
};

///////////////////////////////////////////////////////////////////////////////

/// A recursive AST walker, that walks both Statements and Expressions
/// The recursion stops at a depth where the visitor sets stop to true.
/// If `continueAfterStop` is true, the visitor's stop is reset to false and
/// traversal continues at the next node in the hierarchy at the same level as
/// where stop was set.
class RecursiveWalker : public Visitor {
public:
  StoppableVisitor *v;
  bool continueAfterStop;

  RecursiveWalker(StoppableVisitor *visitor, bool _continueAfterStop = true)
      : v(visitor), continueAfterStop(_continueAfterStop) {}

  template <class T> bool recurse(T *stmt) {
    if (stmt) {
      stmt->accept(this);
    }
    return v->stop;
  }

  template <class T> bool recurse(Array<T> *stmts) {
    if (stmts) {
      for (auto s : *stmts) {
        if (recurse(s))
          break;
      }
    }
    return v->stop;
  }

  template <class T> bool call_visitor(T *obj) {
    obj->accept(v);
    if (v->stop && continueAfterStop) {
      // Reset stop to false, so that traversion continues at neighboring node
      // in the tree.
      v->stop = false;
      return true;
    }
    return v->stop;
  }

  using Visitor::visit;

  void visit(AttribDeclaration* ad) override {
    call_visitor(ad) || recurse(ad->decl);
  }

  void visit(FuncDeclaration *fd) override {
    call_visitor(fd) || recurse(fd->fbody);
  }

  void visit(CompoundStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->statements);
  }

  void visit(ReturnStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->exp);
  }

  void visit(ExpStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->exp);
  }

  void visit(IfStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->condition) || recurse(stmt->ifbody) ||
        recurse(stmt->elsebody);
  }

  void visit(ScopeStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->statement);
  }

  void visit(WhileStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->condition) || recurse(stmt->_body);
  }

  void visit(DoStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->_body) || recurse(stmt->condition);
  }

  void visit(ForStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->_init) || recurse(stmt->condition) ||
        recurse(stmt->_body) || recurse(stmt->increment);
  }

  void visit(ForeachStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->aggr) || recurse(stmt->_body);
  }

  void visit(ForeachRangeStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->lwr) || recurse(stmt->upr) ||
        recurse(stmt->_body);
  }

  void visit(OnScopeStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->statement);
  }

  void visit(ThrowStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->exp);
  }

  void visit(TryFinallyStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->_body) || recurse(stmt->finalbody);
  }

  void visit(TryCatchStatement *stmt) override {
    if (call_visitor(stmt) || recurse(stmt->_body))
      return;

    for (auto c : *stmt->catches) {
      if (recurse(c->handler))
        return;
    }
  }

  void visit(SwitchStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->condition) || recurse(stmt->_body);
  }

  void visit(CaseStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->statement);
  }

  void visit(DefaultStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->statement);
  }

  void visit(UnrolledLoopStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->statements);
  }

  void visit(LabelStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->statement);
  }

  void visit(SynchronizedStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->exp) || recurse(stmt->_body);
  }

  void visit(WithStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->wthis) || recurse(stmt->exp) ||
        recurse(stmt->_body);
  }

  void visit(PragmaStatement* stmt) override {
    call_visitor(stmt) || recurse(stmt->_body);
  }
  void visit(DebugStatement *stmt) override {
    call_visitor(stmt) || recurse(stmt->statement);
  }

  void visit(NewExp *e) override {
    call_visitor(e) || recurse(e->thisexp) || recurse(e->newargs) ||
        recurse(e->argprefix) || recurse(e->arguments);
  }

  void visit(NewAnonClassExp *e) override {
    call_visitor(e) || recurse(e->thisexp) || recurse(e->newargs) ||
        recurse(e->arguments);
  }

  void visit(UnaExp *e) override { call_visitor(e) || recurse(e->e1); }

  void visit(BinExp *e) override {
    call_visitor(e) || recurse(e->e1) || recurse(e->e2);
  }

  void visit(AssertExp *e) override {
    call_visitor(e) || recurse(e->e1) || recurse(e->msg);
  }

  void visit(CallExp *e) override {
    call_visitor(e) || recurse(e->e1) || recurse(e->arguments);
  }

  void visit(ArrayExp *e) override {
    call_visitor(e) || recurse(e->e1) || recurse(e->arguments);
  }

  void visit(SliceExp *e) override {
    call_visitor(e) || recurse(e->e1) || recurse(e->lwr) || recurse(e->upr);
  }

  void visit(ArrayLiteralExp *e) override {
    call_visitor(e) || recurse(e->elements);
  }

  void visit(AssocArrayLiteralExp *e) override {
    call_visitor(e) || recurse(e->keys) || recurse(e->values);
  }

  void visit(StructLiteralExp *e) override {
    // use stageflags to prevent infinite recursion
    if (e->stageflags & 0x2000)
      return;
    int old = e->stageflags;
    e->stageflags |= 0x2000;
    call_visitor(e) || recurse(e->elements);
    e->stageflags = old;
  }

  void visit(TupleExp *e) override {
    call_visitor(e) || recurse(e->e0) || recurse(e->exps);
  }

  void visit(CondExp *e) override {
    call_visitor(e) || recurse(e->econd) || recurse(e->e1) || recurse(e->e2);
  }

  void visit(DeclarationExp *e) override {
    call_visitor(e) || recurse(e->declaration);
  }

  void visit(VarDeclaration *decl) override {
    call_visitor(decl) || recurse(decl->_init) || recurse(decl->edtor);
  }

  void visit(ExpInitializer *init) override {
    call_visitor(init) || recurse(init->exp);
  }

  void visit(ScopeDsymbol *s) override {
    call_visitor(s) || recurse(s->members);
  }

  // Override the default assert(0) behavior of Visitor:
  void visit(Statement *stmt) override { call_visitor(stmt); }
  void visit(Expression *exp) override { call_visitor(exp); }
  void visit(Declaration *decl) override { call_visitor(decl); }
  void visit(Initializer *init) override { call_visitor(init); }
  void visit(Dsymbol *init) override {}
};

#endif // LDC_GEN_RECURSIVEVISITOR_H
