//===-- lib/Semantics/program-tree.h ----------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef FORTRAN_SEMANTICS_PROGRAM_TREE_H_
#define FORTRAN_SEMANTICS_PROGRAM_TREE_H_

#include "symbol.h"
#include "flang/Parser/parse-tree.h"
#include <list>
#include <variant>

// A ProgramTree represents a tree of program units and their contained
// subprograms. The root nodes represent: main program, function, subroutine,
// module subprogram, module, or submodule.
// Each node of the tree consists of:
//   - the statement that introduces the program unit
//   - the specification part
//   - the execution part if applicable (not for module or submodule)
//   - a child node for each contained subprogram

namespace Fortran::semantics {

class Scope;
class SemanticsContext;

class ProgramTree {
public:
  using EntryStmtList = std::list<common::Reference<const parser::EntryStmt>>;
  using GenericSpecList =
      std::list<common::Reference<const parser::GenericSpec>>;

  // Build the ProgramTree rooted at one of these program units.
  static ProgramTree &Build(const parser::ProgramUnit &, SemanticsContext &);
  static std::optional<ProgramTree> Build(
      const parser::MainProgram &, SemanticsContext &);
  static std::optional<ProgramTree> Build(
      const parser::FunctionSubprogram &, SemanticsContext &);
  static std::optional<ProgramTree> Build(
      const parser::SubroutineSubprogram &, SemanticsContext &);
  static std::optional<ProgramTree> Build(
      const parser::SeparateModuleSubprogram &, SemanticsContext &);
  static std::optional<ProgramTree> Build(
      const parser::Module &, SemanticsContext &);
  static std::optional<ProgramTree> Build(
      const parser::Submodule &, SemanticsContext &);
  static std::optional<ProgramTree> Build(
      const parser::BlockData &, SemanticsContext &);
  static std::optional<ProgramTree> Build(
      const parser::CompilerDirective &, SemanticsContext &);
  static std::optional<ProgramTree> Build(
      const parser::OpenACCRoutineConstruct &, SemanticsContext &);

  ENUM_CLASS(Kind, // kind of node
      Program, Function, Subroutine, MpSubprogram, Module, Submodule, BlockData)
  using Stmt = std::variant< // the statement that introduces the program unit
      const parser::Statement<parser::ProgramStmt> *,
      const parser::Statement<parser::FunctionStmt> *,
      const parser::Statement<parser::SubroutineStmt> *,
      const parser::Statement<parser::MpSubprogramStmt> *,
      const parser::Statement<parser::ModuleStmt> *,
      const parser::Statement<parser::SubmoduleStmt> *,
      const parser::Statement<parser::BlockDataStmt> *>;

  ProgramTree(const parser::Name &name, const parser::SpecificationPart &spec,
      const parser::ExecutionPart *exec = nullptr)
      : name_{name}, spec_{spec}, exec_{exec} {}

  const parser::Name &name() const { return name_; }
  Kind GetKind() const;
  const Stmt &stmt() const { return stmt_; }
  bool isSpecificationPartResolved() const {
    return isSpecificationPartResolved_;
  }
  void set_isSpecificationPartResolved(bool yes = true) {
    isSpecificationPartResolved_ = yes;
  }
  const parser::ParentIdentifier &GetParentId() const; // only for Submodule
  const parser::SpecificationPart &spec() const { return spec_; }
  const parser::ExecutionPart *exec() const { return exec_; }
  std::list<ProgramTree> &children() { return children_; }
  const std::list<ProgramTree> &children() const { return children_; }
  const EntryStmtList &entryStmts() const { return entryStmts_; }
  const GenericSpecList &genericSpecs() const { return genericSpecs_; }

  Symbol::Flag GetSubpFlag() const;
  bool IsModule() const; // Module or Submodule
  bool HasModulePrefix() const; // in function or subroutine stmt
  Scope *scope() const { return scope_; }
  void set_scope(Scope &);
  const parser::LanguageBindingSpec *bindingSpec() const {
    return bindingSpec_;
  }
  ProgramTree &set_bindingSpec(const parser::LanguageBindingSpec *spec) {
    bindingSpec_ = spec;
    return *this;
  }
  void AddChild(ProgramTree &&);
  void AddEntry(const parser::EntryStmt &);
  void AddGeneric(const parser::GenericSpec &);

  template <typename T>
  ProgramTree &set_stmt(const parser::Statement<T> &stmt) {
    stmt_ = &stmt;
    return *this;
  }
  template <typename T>
  ProgramTree &set_endStmt(const parser::Statement<T> &stmt) {
    endStmt_ = &stmt.source;
    return *this;
  }

private:
  const parser::Name &name_;
  Stmt stmt_{
      static_cast<const parser::Statement<parser::ProgramStmt> *>(nullptr)};
  const parser::SpecificationPart &spec_;
  const parser::ExecutionPart *exec_{nullptr};
  std::list<ProgramTree> children_;
  EntryStmtList entryStmts_;
  GenericSpecList genericSpecs_;
  Scope *scope_{nullptr};
  const parser::CharBlock *endStmt_{nullptr};
  bool isSpecificationPartResolved_{false};
  const parser::LanguageBindingSpec *bindingSpec_{nullptr};
};

} // namespace Fortran::semantics
#endif // FORTRAN_SEMANTICS_PROGRAM_TREE_H_
