Skip to content
Snippets Groups Projects
Select Git revision
  • 6e1438e36d0b7e01709662dd33f89d5b6b63e3f5
  • develop default protected
  • feature/advection
  • feature/composite-scheme-other-fluxes
  • origin/stage/bouguettaia
  • save_clemence
  • feature/local-dt-fsi
  • feature/variational-hydro
  • feature/gmsh-reader
  • feature/reconstruction
  • feature/kinetic-schemes
  • feature/composite-scheme-sources
  • feature/serraille
  • feature/composite-scheme
  • hyperplastic
  • feature/polynomials
  • feature/gks
  • feature/implicit-solver-o2
  • feature/coupling_module
  • feature/implicit-solver
  • feature/merge-local-dt-fsi
  • v0.5.0 protected
  • v0.4.1 protected
  • v0.4.0 protected
  • v0.3.0 protected
  • v0.2.0 protected
  • v0.1.0 protected
  • Kidder
  • v0.0.4 protected
  • v0.0.3 protected
  • v0.0.2 protected
  • v0 protected
  • v0.0.1 protected
33 results

MeshFaceBoundary.cpp

Blame
  • PugsParser.cpp 56.23 KiB
    #include <PugsOStream.hpp>
    #include <PugsParser.hpp>
    
    #include <PugsAssert.hpp>
    
    #include <fstream>
    #include <unordered_map>
    #include <variant>
    
    #include <rang.hpp>
    
    #include <pegtl.hpp>
    #include <pegtl/analyze.hpp>
    #include <pegtl/contrib/parse_tree.hpp>
    #include <pegtl/contrib/parse_tree_to_dot.hpp>
    
    using namespace TAO_PEGTL_NAMESPACE;
    
    namespace language
    {
    // clang-format off
    
    struct slashslash : TAO_PEGTL_STRING("//") {};
    struct slashstar  : TAO_PEGTL_STRING("/*") {};
    struct starslash  : TAO_PEGTL_STRING("*/") {};
    
    struct comment
        : sor< if_must< slashslash, until< eolf > >,
               try_catch< slashstar, until< starslash> >,
               // error management
               if_must<at<slashstar>,raise<slashstar>, until< eof> > > {};
    
    struct ignored : star< sor< space, comment>  >{};
    
    struct integer
        : seq< opt< one< '+', '-' > >, plus< digit > >{};
    struct INTEGER : seq< integer, ignored >{};
    
    struct real
        : seq< opt< one< '+',
                         '-' >
                    >,
               sor< seq<
                      plus< digit >,
                      one < '.' >,
                      star< digit >
                      >,
                    seq<
                      one < '.' >,
                      plus< digit >
                      >
                    >,
               opt<
                 seq<
                   one< 'E',
                        'e' >,
                   opt< one< '+',
                             '-' >
                        >,
                   plus<digit>
                   >
                 >
               >{};
    
    struct semicol : one< ';' >{};
    
    struct REAL : seq< real, ignored >{};
    
    struct B_set : TAO_PEGTL_KEYWORD("B") {};
    struct R_set :TAO_PEGTL_KEYWORD("R") {};
    struct Z_set : TAO_PEGTL_KEYWORD("Z") {};
    
    struct basic_type : sor < B_set, R_set, Z_set > {};
    
    struct TYPESPECIFIER : seq< basic_type, ignored> {};
    
    struct and_kw :  TAO_PEGTL_KEYWORD("and") {};
    struct or_kw :  TAO_PEGTL_KEYWORD("or") {};
    
    struct not_kw :  TAO_PEGTL_KEYWORD("not") {};
    struct true_kw :  TAO_PEGTL_KEYWORD("true") {};
    struct false_kw :  TAO_PEGTL_KEYWORD("false") {};
    
    struct BOOL : seq< sor<true_kw, false_kw>, ignored > {};
    
    struct do_kw : TAO_PEGTL_KEYWORD("do") {};
    struct DO : seq < do_kw, ignored > {};
    
    struct while_kw : TAO_PEGTL_KEYWORD("while") {};
    struct WHILE : seq < while_kw, ignored > {};
    
    struct for_kw : TAO_PEGTL_KEYWORD("for") {};
    struct FOR : seq < for_kw, ignored > {};
    
    struct if_kw : TAO_PEGTL_KEYWORD("if") {};
    struct IF : seq < if_kw, ignored > {};
    
    struct else_kw : TAO_PEGTL_KEYWORD("else") {};
    struct ELSE : seq < else_kw, ignored > {};
    
    struct break_kw : TAO_PEGTL_KEYWORD("break") {};
    struct BREAK : seq < break_kw, ignored > {};
    
    struct continue_kw : TAO_PEGTL_KEYWORD("continue") {};
    struct CONTINUE : seq < continue_kw, ignored > {};
    
    struct keywork : sor < basic_type, true_kw, false_kw, do_kw, while_kw, for_kw, if_kw, else_kw, and_kw, or_kw , break_kw, continue_kw > {};
    
    struct name : minus< identifier, keywork >  {};
    struct NAME : seq < name, ignored > {};
    
    struct SEMICOL : seq< semicol , ignored > {};
    
    struct open_parent : seq< one< '(' >, ignored > {};
    struct close_parent : seq< one< ')' >, ignored > {};
    
    struct expression;
    struct parented_expression : if_must< open_parent, expression, close_parent > {};
    
    struct primary_expression : sor< BOOL, REAL, INTEGER , NAME, parented_expression > {};
    
    struct unary_plus :  one< '+' > {};
    struct unary_minus : one< '-' > {};
    
    struct unary_not : sor< one< '!'> , not_kw > {};
    
    struct unary_operator : seq< sor< unary_plus, unary_minus, unary_not>, ignored > {};
    
    struct unary_expression : sor< seq< unary_operator, unary_expression >,
                                   primary_expression> {};
    
    struct and_op : seq< sor<TAO_PEGTL_STRING("&&"), and_kw>, ignored > {};
    struct or_op : seq< sor<TAO_PEGTL_STRING("||"), or_kw>, ignored > {};
    
    struct eq_op : seq< TAO_PEGTL_STRING("=="), ignored > {};
    struct not_eq_op : seq< TAO_PEGTL_STRING("!="), ignored > {};
    
    struct lesser_op : seq< one< '<' >, ignored > {};
    struct lesser_or_eq_op : seq< TAO_PEGTL_STRING("<="), ignored > {};
    struct greater_op : seq< one< '>' >, ignored > {};
    struct greater_or_eq_op : seq< TAO_PEGTL_STRING(">="), ignored > {};
    
    struct plus_op : seq< one< '+' >, ignored > {};
    struct minus_op : seq< one< '-' >, ignored > {};
    struct multiply_op : seq< one< '*' >, ignored > {};
    struct divide_op : seq< one< '/' >, ignored > {};
    
    struct product : list_must< unary_expression, sor< multiply_op, divide_op > > {};
    
    struct sum : list_must< product, sor< plus_op, minus_op > > {};
    
    struct compare : sor< seq< sum, sor< lesser_or_eq_op, greater_or_eq_op, lesser_op, greater_op >, sum >,
                          sum > {};
    
    struct equality : sor< seq< compare, sor< eq_op, not_eq_op >, compare >,
                           compare > {};
    
    struct logical : sor< seq< equality, sor< and_op, or_op >, equality >,
                          equality > {};
    
    struct expression : logical {};
    
    struct AFFECT_OP : seq< one< '=' >, ignored > {};
    
    struct affectation : if_must<NAME , AFFECT_OP, expression> {};
    
    struct declaration : if_must<TYPESPECIFIER, NAME, opt<if_must<AFFECT_OP, expression>> >{};
    
    struct open_brace : seq< one< '{' >, ignored > {};
    struct close_brace : seq< one< '}' > > {};
    
    struct instruction_list;
    
    struct braced_instruction_list
        :  sor<try_catch< open_brace, instruction_list, close_brace >,
               // non matching braces management
               if_must< at< one< '{' > >, raise<open_brace>, until<eof> > >{};
    
    struct bloc : braced_instruction_list {};
    
    struct statement_bloc;
    
    struct if_statement : if_must< IF, parented_expression,  statement_bloc, opt< if_must<ELSE, statement_bloc > > > {};
    
    struct do_while_statement : if_must< DO, statement_bloc, WHILE, parented_expression > {};
    
    struct while_statement : if_must< WHILE, parented_expression, statement_bloc> {};
    
    struct for_init : opt< sor< declaration, affectation, expression > > {};
    struct for_test : opt< expression > {};
    struct for_post : opt< sor< affectation, expression > > {};
    struct for_statement_bloc;
    
    struct for_statement : if_must< FOR, open_parent, for_init, SEMICOL, for_test, SEMICOL, for_post, close_parent, for_statement_bloc > {};
    
    struct instruction
        : sor<if_must< declaration, semicol >,
              if_must< affectation, semicol >,
              if_must< expression, semicol >,
              if_statement,
              if_must<do_while_statement, semicol>,
              while_statement,
              for_statement,
              if_must< BREAK, semicol >,
              if_must< CONTINUE, semicol >,
              bloc>
    {};
    
    struct INSTRUCTION : seq<instruction, ignored> {};
    
    struct statement_bloc : INSTRUCTION {};
    
    struct for_statement_bloc : sor< braced_instruction_list,
                                     instruction > {};
    
    struct instruction_list : star< sor< INSTRUCTION,
                                         SEMICOL> > {};
    struct grammar : must<ignored, instruction_list, eof>{};
    
    template <typename Rule>
    struct errors : public normal<Rule>
    {
      static const std::string error_message;
    
      template <typename Input, typename... States>
      static void
      raise(const Input& in, States&&... /*unused*/)
      {
        throw parse_error(error_message, std::vector{in.position()});
      }
    };
    
    template <typename Rule>
    inline const std::string errors<Rule>::error_message = "parse error matching "+ internal::demangle< Rule >();
    
    template <>
    inline const std::string errors<language::SEMICOL>::error_message = "parse error, missing ';'";
    
    template <>
    inline const std::string errors<language::expression>::error_message = "parse error, expecting expression";
    
    template <>
    inline const std::string errors<language::slashstar>::error_message = "bloc comment was never terminated, missing '*/'";
    
    template <>
    inline const std::string errors<language::open_brace>::error_message = "open brace was never closed, missing '}'";
    
    // clang-format on
    
    enum class DataType
    {
      undefined_t = -1,
      bool_t      = 0,
      int_t       = 1,
      double_t    = 2,
      typename_t  = 10,
      void_t      = 9999
    };
    
    std::string
    dataTypeName(const DataType& data_type)
    {
      std::string name;
      switch (data_type) {
      case DataType::undefined_t:
        name = "undefined";
        break;
      case DataType::bool_t:
        name = "bool";
        break;
      case DataType::int_t:
        name = "int";
        break;
      case DataType::double_t:
        name = "double";
        break;
      case DataType::typename_t:
        name = "typename";
        break;
      case DataType::void_t:
        name = "void";
        break;
      }
      return name;
    }
    
    DataType
    dataTypePromotion(const DataType& data_type_1, const DataType& data_type_2)
    {
      if (data_type_1 == data_type_2) {
        return data_type_1;
      } else if ((std::max(data_type_1, data_type_2) <= DataType::double_t) and
                 (std::min(data_type_1, data_type_2) >= DataType::bool_t)) {
        return std::max(data_type_1, data_type_2);
      } else {
        return DataType::undefined_t;
      }
    }
    
    using DataVariant = std::variant<std::monostate, bool, int64_t, double>;
    
    struct [[deprecated]] ExecAll{PUGS_INLINE bool exec() const {return true;
    }   // namespace language
    }
    ;
    
    struct ExecUntilBreakOrContinue
    {
      enum class JumpType
      {
        no_jump,
        break_jump,
        continue_jump
      };
    
     private:
      JumpType m_jump_type{JumpType::no_jump};
      bool m_exec{true};
    
     public:
      PUGS_INLINE
      bool
      exec() const
      {
        return m_exec;
      }
    
      PUGS_INLINE
      JumpType
      jumpType() const
      {
        return m_jump_type;
      }
    
      ExecUntilBreakOrContinue& operator=(const ExecUntilBreakOrContinue&) = delete;
      ExecUntilBreakOrContinue& operator=(ExecUntilBreakOrContinue&&) = default;
    
      ExecUntilBreakOrContinue() = default;
    
      constexpr ExecUntilBreakOrContinue(const JumpType& jump_type)
        : m_jump_type(jump_type), m_exec((jump_type == JumpType::no_jump))
      {
        ;
      }
    };
    
    class SymbolTable;
    class INodeProcessor
    {
     public:
      virtual void execute(ExecUntilBreakOrContinue& exec_policy) = 0;
    
      INodeProcessor(const INodeProcessor& node) = delete;
    
      INodeProcessor() {}
    
      ~INodeProcessor() {}
    };
    
    struct Node : public parse_tree::basic_node<Node>
    {
      enum class Type
      {
        root,
    
        bloc,
    
        affectation,
        declaration,
    
        real,
        integer,
        name,
    
        unary_minus,
        unary_not,
    
        multiply_op,
        divide_op,
        plus_op,
        minus_op,
    
        or_op,
        and_op,
    
        greater_op,
        greater_or_eq_op,
        lesser_op,
        lesser_or_eq_op,
    
        eq_op,
        not_eq_op,
    
        B_set,
        Z_set,
        R_set,
    
        if_statement,
        do_while_statement,
        while_statement,
        for_statement,
        for_statement_bloc,
    
        for_init,
        for_post,
        for_test,
    
        continue_kw,
        break_kw,
        true_kw,
        false_kw,
    
        undefined
      };
    
      std::shared_ptr<SymbolTable> m_symbol_table;
    
      Type m_type{Type::undefined};
    
      std::shared_ptr<INodeProcessor> m_node_processor;
    
      PUGS_INLINE
      void
      execute(ExecUntilBreakOrContinue& exec_policy)
      {
    #warning remove_content
        if (not m_node_processor) {
          throw parse_error("undefined execution", std::vector{this->begin()});
        }
        if (exec_policy.exec()) {
          m_node_processor->execute(exec_policy);
        }
      }
    
      DataType m_data_type{DataType::undefined_t};
      DataVariant m_value;
    };
    
    struct rearrange : parse_tree::apply<rearrange>
    {
      template <typename... States>
      static void
      transform(std::unique_ptr<Node>& n, States&&... st)
      {
        if (n->children.size() == 1) {
          n = std::move(n->children.back());
        } else {
          // First we rearrange tree
          {
            n->remove_content();
            auto& children = n->children;
            auto rhs       = std::move(children.back());
            children.pop_back();
            auto op = std::move(children.back());
            children.pop_back();
            op->children.emplace_back(std::move(n));
            op->children.emplace_back(std::move(rhs));
            n = std::move(op);
            transform(n->children.front(), st...);
          }
          // Then we eventually simplify operations
          {
            if (n->is<language::minus_op>()) {
              Assert(n->children.size() == 2);
              auto& rhs = n->children[1];
              if (rhs->is<language::unary_minus>()) {
                rhs->remove_content();
                n->id = typeid(language::plus_op);
                rhs   = std::move(rhs->children[0]);
              }
            } else if (n->is<language::plus_op>()) {
              Assert(n->children.size() == 2);
              auto& rhs = n->children[1];
              if (rhs->is<language::unary_minus>()) {
                rhs->remove_content();
                n->id = typeid(language::minus_op);
                rhs   = std::move(rhs->children[0]);
              }
            }
          }
        }
      }
    };
    
    struct simplify_unary : parse_tree::apply<simplify_unary>
    {
      template <typename... States>
      static void
      transform(std::unique_ptr<Node>& n, States&&... st)
      {
        if (n->children.size() == 1) {
          if (n->is<unary_expression>()) {
            n->remove_content();
            n = std::move(n->children.back());
            transform(n, st...);
          } else if (n->is<unary_minus>()) {
            auto& child = n->children[0];
            if (child->is<unary_minus>()) {
              n->remove_content();
              child->remove_content();
              n = std::move(child->children[0]);
              transform(n, st...);
            }
          } else if (n->is<unary_not>()) {
            auto& child = n->children[0];
            if (child->is<unary_not>()) {
              n->remove_content();
              child->remove_content();
              n = std::move(child->children[0]);
              transform(n, st...);
            }
          }
        } else if (n->children.size() == 2) {
          if (n->children[0]->is<language::unary_plus>()) {
            n->remove_content();
            n = std::move(n->children[1]);
            transform(n, st...);
          } else if (n->children[0]->is<language::unary_minus>() or n->children[0]->is<language::unary_not>()) {
            n->remove_content();
            auto expression     = std::move(n->children[1]);
            auto unary_operator = std::move(n->children[0]);
            unary_operator->children.emplace_back(std::move(expression));
            n = std::move(unary_operator);
            n->remove_content();
            transform(n, st...);
          }
        }
      }
    };
    
    struct simplify_statement_bloc : parse_tree::apply<simplify_statement_bloc>
    {
      template <typename... States>
      static void
      transform(std::unique_ptr<Node>& n, States&&... st)
      {
        if (n->children.size() == 1) {
          if (not n->children[0]->is<language::declaration>()) {
            n->remove_content();
            n = std::move(n->children.back());
            transform(n, st...);
          } else {
            n->id = typeid(language::bloc);
          }
        }
      }
    };
    
    struct simplify_for_statement_bloc : parse_tree::apply<simplify_for_statement_bloc>
    {
      template <typename... States>
      static void
      transform(std::unique_ptr<Node>& n, States&&... st)
      {
        if (n->children.size() == 1) {
          n->remove_content();
          n = std::move(n->children.back());
          transform(n, st...);
        }
      }
    };
    
    struct simplify_for_init : parse_tree::apply<simplify_for_init>
    {
      template <typename... States>
      static void
      transform(std::unique_ptr<Node>& n, States&&...)
      {
        Assert(n->children.size() <= 1);
        if (n->children.size() == 1) {
          n->remove_content();
          n = std::move(n->children.back());
        }
      }
    };
    
    struct simplify_for_test : parse_tree::apply<simplify_for_test>
    {
      template <typename... States>
      static void
      transform(std::unique_ptr<Node>& n, States&&...)
      {
        Assert(n->children.size() <= 1);
        if (n->children.size() == 1) {
          n->remove_content();
          n = std::move(n->children.back());
        }
      }
    };
    
    struct simplify_for_post : parse_tree::apply<simplify_for_post>
    {
      template <typename... States>
      static void
      transform(std::unique_ptr<Node>& n, States&&...)
      {
        Assert(n->children.size() <= 1);
        if (n->children.size() == 1) {
          n->remove_content();
          n = std::move(n->children.back());
        }
      }
    };
    
    template <typename Rule>
    using selector = parse_tree::selector<Rule,
                                          parse_tree::store_content::on<true_kw,
                                                                        false_kw,
                                                                        integer,
                                                                        real,
                                                                        name,
                                                                        B_set,
                                                                        R_set,
                                                                        Z_set,
                                                                        declaration,
                                                                        affectation,
                                                                        if_statement,
                                                                        do_while_statement,
                                                                        while_statement,
                                                                        for_statement,
                                                                        break_kw,
                                                                        continue_kw>,
                                          simplify_unary::on<unary_minus, unary_plus, unary_not, unary_expression>,
                                          parse_tree::remove_content::on<plus_op,
                                                                         minus_op,
                                                                         multiply_op,
                                                                         divide_op,
                                                                         lesser_op,
                                                                         lesser_or_eq_op,
                                                                         greater_op,
                                                                         greater_or_eq_op,
                                                                         eq_op,
                                                                         not_eq_op,
                                                                         and_op,
                                                                         or_op>,
                                          simplify_for_statement_bloc::on<for_statement_bloc>,
                                          parse_tree::discard_empty::on<ignored, semicol, bloc>,
                                          simplify_statement_bloc::on<statement_bloc>,
                                          simplify_for_init::on<for_init>,
                                          simplify_for_test::on<for_test>,
                                          simplify_for_post::on<for_post>,
                                          rearrange::on<product, expression>>;
    
    class SymbolTable
    {
      class Attributes
      {
        bool m_is_initialized{false};
        DataType m_data_type{DataType::undefined_t};
        DataVariant m_value;
    
       public:
        auto&
        value()
        {
          return m_value;
        }
    
        const auto&
        value() const
        {
          return m_value;
        }
    
        const bool&
        isInitialized() const
        {
          return m_is_initialized;
        }
    
        void
        setIsInitialized()
        {
          m_is_initialized = true;
        }
    
        const DataType&
        dataType() const
        {
          return m_data_type;
        }
    
        void
        setDataType(const DataType& data_type)
        {
          m_data_type = data_type;
        }
    
        friend std::ostream&
        operator<<(std::ostream& os, const Attributes& attributes)
        {
          std::visit(
            [&](const auto& value) {
              using T = std::decay_t<decltype(value)>;
              if constexpr (std::is_same_v<T, std::monostate>) {
                os << "--";
              } else {
                os << value;
              }
            },
            attributes.m_value);
    
          return os;
        }
      };
    
     private:
      std::vector<std::pair<std::string, Attributes>> m_symbol_list;
      std::shared_ptr<SymbolTable> m_parent_table;
    
     public:
      friend std::ostream&
      operator<<(std::ostream& os, const SymbolTable& symbol_table)
      {
        os << "-- Symbol table state -- parent : " << symbol_table.m_parent_table.get() << "\n";
        for (auto i_symbol : symbol_table.m_symbol_list) {
          os << ' ' << i_symbol.first << ": " << std::boolalpha << i_symbol.second << '\n';
        }
        os << "------------------------\n";
        return os;
      }
    
      auto
      find(const std::string& symbol)
      {
        auto i_symbol = m_symbol_list.end();
    
        for (auto i_stored_symbol = m_symbol_list.begin(); i_stored_symbol != m_symbol_list.end(); ++i_stored_symbol) {
          if (i_stored_symbol->first == symbol) {
            i_symbol = i_stored_symbol;
            break;
          }
        }
    
        if (i_symbol != m_symbol_list.end()) {
          return std::make_pair(i_symbol, true);
        } else {
          if (m_parent_table) {
            return m_parent_table->find(symbol);
          } else {
            return std::make_pair(i_symbol, false);
          }
        }
      }
    
      auto
      add(const std::string& symbol)
      {
        for (auto i_stored_symbol = m_symbol_list.begin(); i_stored_symbol != m_symbol_list.end(); ++i_stored_symbol) {
          if (i_stored_symbol->first == symbol) {
            return std::make_pair(i_stored_symbol, false);
          }
        }
        return std::make_pair(m_symbol_list.emplace(m_symbol_list.end(), std::make_pair(symbol, Attributes())), true);
      }
    
      SymbolTable(const std::shared_ptr<SymbolTable>& parent_table = nullptr) : m_parent_table(parent_table)
      {
        ;
      }
    };
    
    auto affect_to_symbol = [](const Node& n, const auto given_value, auto i_symbol) {
      switch (i_symbol->second.dataType()) {
      case DataType::double_t: {
        i_symbol->second.value() = static_cast<double>(std::visit(
          [](const auto& value) -> double {
            using T = std::decay_t<decltype(value)>;
            if constexpr (std::is_same_v<T, std::monostate>) {
              return 0;
            } else {
              return value;
            }
          },
          given_value));
        break;
      }
      case DataType::int_t: {
        i_symbol->second.value() = static_cast<int64_t>(std::visit(
          [](const auto& value) -> int64_t {
            using T = std::decay_t<decltype(value)>;
            if constexpr (std::is_same_v<T, std::monostate>) {
              return 0;
            } else {
              return value;
            }
          },
          given_value));
        break;
      }
      case DataType::bool_t: {
        i_symbol->second.value() = static_cast<bool>(std::visit(
          [](const auto& value) -> bool {
            using T = std::decay_t<decltype(value)>;
            if constexpr (std::is_same_v<T, std::monostate>) {
              return false;
            } else {
              return value;
            }
          },
          given_value));
        break;
      }
      default: {
        throw parse_error("unknown affectation type", std::vector{n.begin()});
      }
      }
    };
    
    class NodeList final : public INodeProcessor
    {
      Node& m_node;
    
     public:
      NodeList(Node& node) : m_node{node} {}
      void
      execute(ExecUntilBreakOrContinue& exec_policy)
      {
        for (auto& child : m_node.children) {
          child->execute(exec_policy);
        }
      }
    };
    
    class [[deprecated]] Declaration final : public INodeProcessor
    {
      Node& m_node;
    
     public:
      Declaration(Node & node) : m_node{node} {}
      void execute(ExecUntilBreakOrContinue & exec_policy)
      {
        if (m_node.children.size() > 2) {
          const std::string& symbol = m_node.children[1]->string();
          auto [i_symbol, found]    = m_node.m_symbol_table->find(symbol);
          Assert(found);
          m_node.children[2]->execute(exec_policy);
          affect_to_symbol(m_node, m_node.children[2]->m_value, i_symbol);
        }
      }
    };
    
    class Affectation final : public INodeProcessor
    {
      Node& m_node;
    
     public:
      Affectation(Node& node) : m_node{node} {}
      [[deprecated]] void
      execute(ExecUntilBreakOrContinue& exec_policy)
      {
        const std::string& symbol = m_node.children[0]->string();
        auto [i_symbol, found]    = m_node.m_symbol_table->find(symbol);
        Assert(found);
        m_node.children[1]->execute(exec_policy);
        affect_to_symbol(m_node, m_node.children[1]->m_value, i_symbol);
      }
    };
    
    class Constant final : public INodeProcessor
    {
     public:
      Constant() {}
    
      PUGS_INLINE
      void
      execute(ExecUntilBreakOrContinue&)
      {
        ;
      }
    };
    
    template <typename Op>
    struct UnaryOp;
    
    template <>
    struct UnaryOp<language::unary_minus>
    {
      template <typename A>
      A
      eval(const A& a)
      {
        return -a;
      }
    };
    
    template <>
    struct UnaryOp<language::unary_not>
    {
      template <typename A>
      bool
      eval(const A& a)
      {
        return not a;
      }
    };
    
    template <typename UnaryOpT>
    class UnaryExpression final : public INodeProcessor
    {
      Node& m_node;
    
      template <typename CastType, typename op>
      CastType
      eval(const DataVariant& a)
      {
        return std::visit(
          [](const auto& a) -> double {
            using A = std::decay_t<decltype(a)>;
            if constexpr (std::is_same_v<A, std::monostate>) {
              return 0;
            } else {
              return UnaryOp<op>().eval(a);
            }
          },
          a);
      }
    
      template <typename op>
      void
      eval_node_unary(Node& n, const DataVariant& a)
      {
        switch (n.m_data_type) {
        case DataType::double_t: {
          n.m_value = eval<double, op>(a);
          break;
        }
        case DataType::int_t: {
          n.m_value = eval<int64_t, op>(a);
          break;
        }
        case DataType::bool_t: {
          n.m_value = eval<bool, op>(a);
          break;
        }
        default: {
          throw parse_error("unknown unary operation type", std::vector{n.begin()});
        }
        }
      }
    
     public:
      UnaryExpression(Node& node) : m_node{node} {}
      void
      execute(ExecUntilBreakOrContinue& exec_policy)
      {
        m_node.children[0]->execute(exec_policy);
        eval_node_unary<UnaryOpT>(m_node, m_node.children[0]->m_value);
      }
    };
    
    template <typename Op>
    struct BinOp;
    
    template <>
    struct BinOp<language::and_op>
    {
      template <typename A, typename B>
      auto
      eval(const A& a, const B& b) -> decltype(a and b)
      {
        return a and b;
      }
    };
    
    template <>
    struct BinOp<language::or_op>
    {
      template <typename A, typename B>
      auto
      eval(const A& a, const B& b) -> decltype(a or b)
      {
        return a or b;
      }
    };
    
    template <>
    struct BinOp<language::eq_op>
    {
      template <typename A, typename B>
      auto
      eval(const A& a, const B& b) -> decltype(a == b)
      {
        return a == b;
      }
    };
    
    template <>
    struct BinOp<language::not_eq_op>
    {
      template <typename A, typename B>
      auto
      eval(const A& a, const B& b) -> decltype(a != b)
      {
        return a != b;
      }
    };
    
    template <>
    struct BinOp<language::lesser_op>
    {
      template <typename A, typename B>
      auto
      eval(const A& a, const B& b) -> decltype(a < b)
      {
        return a < b;
      }
    };
    
    template <>
    struct BinOp<language::lesser_or_eq_op>
    {
      template <typename A, typename B>
      auto
      eval(const A& a, const B& b) -> decltype(a <= b)
      {
        return a <= b;
      }
    };
    
    template <>
    struct BinOp<language::greater_op>
    {
      template <typename A, typename B>
      auto
      eval(const A& a, const B& b) -> decltype(a > b)
      {
        return a > b;
      }
    };
    
    template <>
    struct BinOp<language::greater_or_eq_op>
    {
      template <typename A, typename B>
      auto
      eval(const A& a, const B& b) -> decltype(a >= b)
      {
        return a >= b;
      }
    };
    
    template <>
    struct BinOp<language::plus_op>
    {
      template <typename A, typename B>
      auto
      eval(const A& a, const B& b) -> decltype(a + b)
      {
        return a + b;
      }
    };
    
    template <>
    struct BinOp<language::minus_op>
    {
      template <typename A, typename B>
      auto
      eval(const A& a, const B& b) -> decltype(a - b)
      {
        return a - b;
      }
    };
    
    template <>
    struct BinOp<language::multiply_op>
    {
      template <typename A, typename B>
      auto
      eval(const A& a, const B& b) -> decltype(a * b)
      {
        return a * b;
      }
    };
    
    template <>
    struct BinOp<language::divide_op>
    {
      template <typename A, typename B>
      auto
      eval(const A& a, const B& b) -> decltype(a * b)
      {
        return a / b;
      }
    };
    
    template <typename BinaryOpT>
    class BinaryExpression final : public INodeProcessor
    {
      Node& m_node;
      template <typename op, typename A, typename B>
      auto
      eval(const BinOp<op>& bin_op, const A& a, const B& b)
      {
        return bin_op.eval(a, b);
      }
    
      template <typename CastType, typename op>
      CastType
      eval(const DataVariant& a, const DataVariant& b)
      {
        return std::visit(
          [](const auto& a, const auto& b) -> double {
            using A = std::decay_t<decltype(a)>;
            using B = std::decay_t<decltype(b)>;
            if constexpr ((std::is_same_v<A, std::monostate>) or (std::is_same_v<B, std::monostate>)) {
              return 0;
            } else {
              return BinOp<op>().eval(a, b);
            }
          },
          a, b);
      }
    
      template <typename op>
      void
      eval_node_op(Node& n, const DataVariant& a, const DataVariant& b)
      {
        switch (n.m_data_type) {
        case DataType::double_t: {
          n.m_value = eval<double, op>(a, b);
          break;
        }
        case DataType::int_t: {
          n.m_value = eval<int64_t, op>(a, b);
          break;
        }
        case DataType::bool_t: {
          n.m_value = eval<bool, op>(a, b);
          break;
        }
        default: {
          throw parse_error("unknown operation type", std::vector{n.begin()});
        }
        }
      }
    
     public:
      BinaryExpression(Node& node) : m_node{node} {}
      void
      execute(ExecUntilBreakOrContinue& exec_policy)
      {
        m_node.children[0]->execute(exec_policy);
        m_node.children[1]->execute(exec_policy);
    
        eval_node_op<BinaryOpT>(m_node, m_node.children[0]->m_value, m_node.children[1]->m_value);
      }
    };
    
    class IfStatement final : public INodeProcessor
    {
      Node& m_node;
    
     public:
      IfStatement(Node& node) : m_node{node} {}
    
      void
      execute(ExecUntilBreakOrContinue& exec_policy)
      {
        m_node.children[0]->execute(exec_policy);
        const bool is_true = static_cast<bool>(std::visit(
          [](const auto& value) -> bool {
            using T = std::decay_t<decltype(value)>;
            if constexpr (std::is_same_v<T, std::monostate>) {
              return false;
            } else {
              return value;
            }
          },
          m_node.children[0]->m_value));
        if (is_true) {
          Assert(m_node.children[1] != nullptr);
          m_node.children[1]->execute(exec_policy);
        } else {
          if (m_node.children.size() == 3) {
            // else statement
            Assert(m_node.children[2] != nullptr);
            m_node.children[2]->execute(exec_policy);
          }
        }
      }
    };
    
    class DoWhileStatement final : public INodeProcessor
    {
      Node& m_node;
    
     public:
      DoWhileStatement(Node& node) : m_node{node} {}
    
      [[deprecated]] void
      execute(ExecUntilBreakOrContinue& exec_policy)
      {
        bool continuation_test = true;
        ExecUntilBreakOrContinue exec_until_jump;
        do {
          m_node.children[0]->execute(exec_until_jump);
          if (not exec_until_jump.exec()) {
            if (exec_until_jump.jumpType() == ExecUntilBreakOrContinue::JumpType::break_jump) {
              break;
            } else if (exec_until_jump.jumpType() == ExecUntilBreakOrContinue::JumpType::continue_jump) {
              exec_until_jump = ExecUntilBreakOrContinue{};   // getting ready for next loop traversal
            }
          }
          m_node.children[1]->execute(exec_policy);
          continuation_test = static_cast<bool>(std::visit(
            [](const auto& value) -> bool {
              using T = std::decay_t<decltype(value)>;
              if constexpr (std::is_same_v<T, std::monostate>) {
                return false;
              } else {
                return value;
              }
            },
            m_node.children[1]->m_value));
        } while (continuation_test);
      }
    };
    
    class WhileStatement final : public INodeProcessor
    {
      Node& m_node;
    
     public:
      WhileStatement(Node& node) : m_node{node} {}
    
      [[deprecated]] void
      execute(ExecUntilBreakOrContinue& exec_policy)
      {
        ExecUntilBreakOrContinue exec_until_jump;
        while ([&]() {
          m_node.children[0]->execute(exec_policy);
          return static_cast<bool>(std::visit(
            [](const auto& value) -> bool {
              using T = std::decay_t<decltype(value)>;
              if constexpr (std::is_same_v<T, std::monostate>) {
                return false;
              } else {
                return value;
              }
            },
            m_node.children[0]->m_value));
        }()) {
          m_node.children[1]->execute(exec_until_jump);
          if (not exec_until_jump.exec()) {
            if (exec_until_jump.jumpType() == ExecUntilBreakOrContinue::JumpType::break_jump) {
              break;
            } else if (exec_until_jump.jumpType() == ExecUntilBreakOrContinue::JumpType::continue_jump) {
              exec_until_jump = ExecUntilBreakOrContinue{};   // getting ready for next loop traversal
            }
          }
        }
      }
    };
    
    class ForStatement final : public INodeProcessor
    {
      Node& m_node;
    
     public:
      ForStatement(Node& node) : m_node{node} {}
    
      [[deprecated]] void
      execute(ExecUntilBreakOrContinue& exec_policy)
      {
        ExecUntilBreakOrContinue exec_until_jump;
        m_node.children[0]->execute(exec_policy);
        while ([&]() {
          m_node.children[1]->execute(exec_policy);
          return static_cast<bool>(std::visit(
            [](const auto& value) -> bool {
              using T = std::decay_t<decltype(value)>;
              if constexpr (std::is_same_v<T, std::monostate>) {
                return false;
              } else {
                return value;
              }
            },
            m_node.children[1]->m_value));
        }()) {
          m_node.children[3]->execute(exec_until_jump);
          if (not exec_until_jump.exec()) {
            if (exec_until_jump.jumpType() == ExecUntilBreakOrContinue::JumpType::break_jump) {
              break;
            } else if (exec_until_jump.jumpType() == ExecUntilBreakOrContinue::JumpType::continue_jump) {
              exec_until_jump = ExecUntilBreakOrContinue{};   // getting ready for next loop traversal
            }
          }
    
          m_node.children[2]->execute(exec_policy);
        }
      }
    };
    
    class NameExpression final : public INodeProcessor
    {
      Node& m_node;
    
     public:
      NameExpression(Node& node) : m_node{node} {}
    
      [[deprecated]] void
      execute(ExecUntilBreakOrContinue&)
      {
        const std::string& symbol = m_node.string();
        auto [i_symbol, found]    = m_node.m_symbol_table->find(symbol);
        Assert(found);
        m_node.m_value = i_symbol->second.value();
      }
    };
    
    class BreakExpression final : public INodeProcessor
    {
     public:
      BreakExpression() {}
    
      void
      execute(ExecUntilBreakOrContinue& exec_policy)
      {
        exec_policy = ExecUntilBreakOrContinue(ExecUntilBreakOrContinue::JumpType::break_jump);
      }
    };
    
    class ContinueExpression final : public INodeProcessor
    {
     public:
      ContinueExpression() {}
    
      void
      execute(ExecUntilBreakOrContinue& exec_policy)
      {
        exec_policy = ExecUntilBreakOrContinue(ExecUntilBreakOrContinue::JumpType::continue_jump);
      }
    };
    
    namespace internal
    {
    void
    build_node_type(Node& n)
    {
      if (n.is_root()) {
        n.m_type           = Node::Type::root;
        n.m_node_processor = std::make_shared<NodeList>(n);
      } else if (n.is<language::bloc>()) {
        n.m_type           = Node::Type::bloc;
        n.m_node_processor = std::make_shared<NodeList>(n);
      } else if (n.is<language::declaration>()) {
        n.m_type           = Node::Type::declaration;
        n.m_node_processor = std::make_shared<Declaration>(n);
      } else if (n.is<language::affectation>()) {
        n.m_type           = Node::Type::affectation;
        n.m_node_processor = std::make_shared<Affectation>(n);
    
      } else if (n.is<language::real>()) {
        n.m_type           = Node::Type::real;
        n.m_node_processor = std::make_shared<Constant>();
      } else if (n.is<language::integer>()) {
        n.m_type           = Node::Type::integer;
        n.m_node_processor = std::make_shared<Constant>();
    
      } else if (n.is<language::name>()) {
        n.m_type           = Node::Type::name;
        n.m_node_processor = std::make_shared<NameExpression>(n);
    
      } else if (n.is<language::unary_minus>()) {
        n.m_type           = Node::Type::unary_minus;
        n.m_node_processor = std::make_shared<UnaryExpression<language::unary_minus>>(n);
      } else if (n.is<language::unary_not>()) {
        n.m_type           = Node::Type::unary_not;
        n.m_node_processor = std::make_shared<UnaryExpression<language::unary_not>>(n);
    
      } else if (n.is<language::multiply_op>()) {
        n.m_type           = Node::Type::multiply_op;
        n.m_node_processor = std::make_shared<BinaryExpression<language::multiply_op>>(n);
      } else if (n.is<language::divide_op>()) {
        n.m_type           = Node::Type::divide_op;
        n.m_node_processor = std::make_shared<BinaryExpression<language::divide_op>>(n);
      } else if (n.is<language::plus_op>()) {
        n.m_type           = Node::Type::plus_op;
        n.m_node_processor = std::make_shared<BinaryExpression<language::plus_op>>(n);
      } else if (n.is<language::minus_op>()) {
        n.m_type           = Node::Type::minus_op;
        n.m_node_processor = std::make_shared<BinaryExpression<language::minus_op>>(n);
    
      } else if (n.is<language::or_op>()) {
        n.m_type           = Node::Type::or_op;
        n.m_node_processor = std::make_shared<BinaryExpression<language::or_op>>(n);
      } else if (n.is<language::and_op>()) {
        n.m_type           = Node::Type::and_op;
        n.m_node_processor = std::make_shared<BinaryExpression<language::and_op>>(n);
    
      } else if (n.is<language::greater_op>()) {
        n.m_type           = Node::Type::greater_op;
        n.m_node_processor = std::make_shared<BinaryExpression<language::greater_op>>(n);
      } else if (n.is<language::greater_or_eq_op>()) {
        n.m_type           = Node::Type::greater_or_eq_op;
        n.m_node_processor = std::make_shared<BinaryExpression<language::greater_or_eq_op>>(n);
      } else if (n.is<language::lesser_op>()) {
        n.m_type           = Node::Type::lesser_op;
        n.m_node_processor = std::make_shared<BinaryExpression<language::lesser_op>>(n);
      } else if (n.is<language::lesser_or_eq_op>()) {
        n.m_type           = Node::Type::lesser_or_eq_op;
        n.m_node_processor = std::make_shared<BinaryExpression<language::lesser_or_eq_op>>(n);
    
      } else if (n.is<language::eq_op>()) {
        n.m_type           = Node::Type::eq_op;
        n.m_node_processor = std::make_shared<BinaryExpression<language::eq_op>>(n);
      } else if (n.is<language::not_eq_op>()) {
        n.m_type           = Node::Type::not_eq_op;
        n.m_node_processor = std::make_shared<BinaryExpression<language::not_eq_op>>(n);
    
      } else if (n.is<language::B_set>()) {
        n.m_type = Node::Type::B_set;
      } else if (n.is<language::Z_set>()) {
        n.m_type = Node::Type::Z_set;
      } else if (n.is<language::R_set>()) {
        n.m_type = Node::Type::R_set;
    
      } else if (n.is<language::if_statement>()) {
        n.m_type           = Node::Type::if_statement;
        n.m_node_processor = std::make_shared<IfStatement>(n);
      } else if (n.is<language::do_while_statement>()) {
        n.m_type           = Node::Type::do_while_statement;
        n.m_node_processor = std::make_shared<DoWhileStatement>(n);
      } else if (n.is<language::while_statement>()) {
        n.m_type           = Node::Type::while_statement;
        n.m_node_processor = std::make_shared<WhileStatement>(n);
      } else if (n.is<language::for_statement>()) {
        n.m_type           = Node::Type::for_statement;
        n.m_node_processor = std::make_shared<ForStatement>(n);
      } else if (n.is<language::for_statement_bloc>()) {
        n.m_type           = Node::Type::for_statement_bloc;
        n.m_node_processor = std::make_shared<NodeList>(n);
    
      } else if (n.is<language::for_init>()) {
        n.m_type = Node::Type::for_init;
      } else if (n.is<language::for_post>()) {
        n.m_type = Node::Type::for_post;
      } else if (n.is<language::for_test>()) {
        n.m_type = Node::Type::for_test;
    
      } else if (n.is<language::break_kw>()) {
        n.m_type           = Node::Type::break_kw;
        n.m_node_processor = std::make_shared<BreakExpression>();
      } else if (n.is<language::continue_kw>()) {
        n.m_type           = Node::Type::continue_kw;
        n.m_node_processor = std::make_shared<ContinueExpression>();
      } else if (n.is<language::true_kw>()) {
        n.m_type           = Node::Type::true_kw;
        n.m_node_processor = std::make_shared<Constant>();
      } else if (n.is<language::false_kw>()) {
        perr() << "setting constant value\n";
        n.m_type           = Node::Type::false_kw;
        n.m_node_processor = std::make_shared<Constant>();
      }
    
      for (auto& child : n.children) {
        internal::build_node_type(*child);
      }
    
      if (n.m_type == Node::Type::undefined) {
        std::ostringstream error_message;
        error_message << "undefined node type '" << rang::fgB::red << n.name() << rang::fg::reset << "'";
        throw parse_error{error_message.str(), std::vector{n.begin()}};
      }
    }
    }   // namespace internal
    
    void
    build_node_type(Node& n)
    {
      Assert(n.is_root());
      Assert(n.is<void>());
      n.m_type           = Node::Type::root;
      n.m_node_processor = std::make_shared<NodeList>(n);
      for (auto& child : n.children) {
        internal::build_node_type(*child);
      }
      pout() << " - build node types\n";
    }
    
    namespace internal
    {
    void
    print_dot(std::ostream& os, const Node& n)
    {
      if (n.is_root()) {
        os << "  x" << &n << " [ label=\"root \\n" << dataTypeName(n.m_data_type) << "\" ]\n";
      } else {
        if (n.has_content()) {
          os << "  x" << &n << " [ label=\"" << n.name() << "\\n"
             << n.string_view() << "\\n"
             << dataTypeName(n.m_data_type) << "\" ]\n";
        } else {
          os << "  x" << &n << " [ label=\"" << n.name() << "\\n" << dataTypeName(n.m_data_type) << "\" ]\n";
        }
      }
      if (!n.children.empty()) {
        os << "  x" << &n << " -> { ";
        for (auto& child : n.children) {
          os << "x" << child.get() << ((child == n.children.back()) ? " }\n" : ", ");
        }
        for (auto& child : n.children) {
          print_dot(os, *child);
        }
      }
    }
    
    }   // namespace internal
    
    void
    print_dot(std::ostream& os, const Node& n)
    {
      Assert(n.is_root());
      os << "digraph parse_tree\n{\n";
      internal::print_dot(os, n);
      os << "}\n";
    }
    
    namespace internal
    {
    void
    build_symbol_table_and_check_declarations(Node& n, std::shared_ptr<SymbolTable>& symbol_table)
    {
      if (n.is<language::bloc>() or (n.is<language::for_statement>())) {
        if (!n.children.empty()) {
          std::shared_ptr bloc_symbol_table = std::make_shared<SymbolTable>(symbol_table);
          n.m_symbol_table                  = bloc_symbol_table;
          for (auto& child : n.children) {
            build_symbol_table_and_check_declarations(*child, bloc_symbol_table);
          }
        }
      } else {
        n.m_symbol_table = symbol_table;
        if (n.has_content()) {
          if (n.is<language::declaration>()) {
            const std::string& symbol = n.children[1]->string();
            auto [i_symbol, success]  = symbol_table->add(symbol);
            if (not success) {
              std::ostringstream error_message;
              error_message << "symbol '" << rang::fg::red << symbol << rang::fg::reset << '\'' << " was already defined!";
              throw parse_error(error_message.str(), std::vector{n.begin()});
            }
          } else if (n.is<language::name>()) {
            auto [i_symbol, found] = symbol_table->find(n.string());
            if (not found) {
              std::ostringstream error_message;
              error_message << "undefined symbol '" << rang::fg::red << n.string() << rang::fg::reset << '\'';
              throw parse_error(error_message.str(), std::vector{n.begin()});
            }
          }
        }
    
        for (auto& child : n.children) {
          build_symbol_table_and_check_declarations(*child, symbol_table);
        }
      }
    }
    }   // namespace internal
    
    void
    build_symbol_table_and_check_declarations(Node& n)
    {
      Assert(n.is_root());
      std::shared_ptr symbol_table = std::make_shared<SymbolTable>();
      n.m_symbol_table             = symbol_table;
      internal::build_symbol_table_and_check_declarations(n, symbol_table);
      pout() << " - checked symbols declaration\n";
    }
    
    namespace internal
    {
    void
    check_symbol_initialization(const Node& n, std::shared_ptr<SymbolTable>& symbol_table)
    {
      if (n.is<language::bloc>() or n.is<language::for_statement>()) {
        if (!n.children.empty()) {
          std::shared_ptr bloc_symbol_table = std::make_shared<SymbolTable>(symbol_table);
          for (auto& child : n.children) {
            check_symbol_initialization(*child, bloc_symbol_table);
          }
        }
      } else {
        if (n.has_content()) {
          if (n.is<language::declaration>()) {
            const std::string& symbol = n.children[1]->string();
            auto [i_symbol, success]  = symbol_table->add(symbol);
            Assert(success, "unexpected error, should have been detected through declaration checking");
            if (n.children.size() == 3) {
              check_symbol_initialization(*n.children[2], symbol_table);
            }
            i_symbol->second.setIsInitialized();
          } else if (n.is<language::affectation>()) {
            // first checks for right hand side
            check_symbol_initialization(*n.children[1], symbol_table);
            // then marks left hand side as initialized
            const std::string& symbol = n.children[0]->string();
            auto [i_symbol, found]    = symbol_table->find(symbol);
            Assert(found, "unexpected error, should have been detected through declaration checking");
            i_symbol->second.setIsInitialized();
          } else if (n.is<language::name>()) {
            auto [i_symbol, found] = symbol_table->find(n.string());
            Assert(found, "unexpected error, should have been detected through declaration checking");
            if (not i_symbol->second.isInitialized()) {
              std::ostringstream error_message;
              error_message << "uninitialized symbol '" << rang::fg::red << n.string() << rang::fg::reset << '\'';
              throw parse_error(error_message.str(), std::vector{n.begin()});
            }
          }
        }
    
        if ((not n.is<language::declaration>()) and (not n.is<language::affectation>())) {
          for (auto& child : n.children) {
            check_symbol_initialization(*child, symbol_table);
          }
        }
      }
    }
    }   // namespace internal
    
    void
    check_symbol_initialization(const Node& n)
    {
      perr() << rang::fgB::yellow << "warning:" << rang::fg::reset << " symbol initialization checking not working!\n";
      Assert(n.is_root());
      std::shared_ptr symbol_table = std::make_shared<SymbolTable>();
      internal::check_symbol_initialization(n, symbol_table);
      pout() << " - checked symbols initialization\n";
    }
    
    namespace internal
    {
    void
    build_node_data_types(Node& n)
    {
      if (n.is<language::bloc>() or n.is<for_statement>()) {
        if (!n.children.empty()) {
          for (auto& child : n.children) {
            build_node_data_types(*child);
          }
        }
        n.m_data_type = DataType::void_t;
      } else {
        if (n.has_content()) {
          if (n.is<language::true_kw>() or n.is<language::false_kw>() or n.is<language::do_kw>()) {
            n.m_data_type = DataType::bool_t;
          } else if (n.is<language::real>()) {
            n.m_data_type = DataType::double_t;
          } else if (n.is<language::integer>()) {
            n.m_data_type = DataType::int_t;
          } else if (n.is<language::affectation>()) {
            n.m_data_type = DataType::void_t;
          } else if (n.is<language::declaration>()) {
            auto& type_node = *(n.children[0]);
            DataType data_type{DataType::undefined_t};
            if (type_node.is<language::B_set>()) {
              data_type = DataType::bool_t;
            } else if (type_node.is<language::R_set>()) {
              data_type = DataType::double_t;
            } else if (type_node.is<language::Z_set>()) {
              data_type = DataType::int_t;
            }
            if (data_type == DataType::undefined_t) {
              throw parse_error("unexpected error: invalid datatype", type_node.begin());
            }
            type_node.m_data_type      = DataType::void_t;
            n.children[1]->m_data_type = data_type;
            const std::string& symbol  = n.children[1]->string();
    
            std::shared_ptr<SymbolTable>& symbol_table = n.m_symbol_table;
    
            auto [i_symbol, found] = symbol_table->find(symbol);
            Assert(found);
            i_symbol->second.setDataType(data_type);
            n.m_data_type = DataType::void_t;
          } else if (n.is<language::name>()) {
            std::shared_ptr<SymbolTable>& symbol_table = n.m_symbol_table;
    
            auto [i_symbol, found] = symbol_table->find(n.string());
            Assert(found);
            n.m_data_type = i_symbol->second.dataType();
          }
        }
        for (auto& child : n.children) {
          build_node_data_types(*child);
        }
    
        if (n.is<language::break_kw>() or n.is<language::continue_kw>()) {
          n.m_data_type = DataType::void_t;
        } else if (n.is<language::for_statement>()) {
          n.m_data_type = DataType::void_t;
        } else if (n.is<language::for_post>() or n.is<language::for_init>() or n.is<language::for_statement_bloc>()) {
          n.m_data_type = DataType::void_t;
        } else if (n.is<language::for_test>()) {
          n.m_data_type = DataType::bool_t;
        } else if (n.is<language::if_statement>() or n.is<language::while_statement>()) {
          n.m_data_type = DataType::void_t;
          if ((n.children[0]->m_data_type > DataType::double_t) or (n.children[0]->m_data_type < DataType::bool_t)) {
            const DataType type_0 = n.children[0]->m_data_type;
            std::ostringstream message;
            message << "Cannot convert data type to boolean value\n"
                    << "note: incompatible operand '" << n.children[0]->string() << " of type ' (" << dataTypeName(type_0)
                    << ')' << std::ends;
            throw parse_error(message.str(), n.children[0]->begin());
          }
        } else if (n.is<language::do_while_statement>()) {
          n.m_data_type = DataType::void_t;
          if ((n.children[1]->m_data_type > DataType::double_t) or (n.children[1]->m_data_type < DataType::bool_t)) {
            const DataType type_0 = n.children[1]->m_data_type;
            std::ostringstream message;
            message << "Cannot convert data type to boolean value\n"
                    << "note: incompatible operand '" << n.children[1]->string() << " of type ' (" << dataTypeName(type_0)
                    << ')' << std::ends;
            throw parse_error(message.str(), n.children[1]->begin());
          }
        } else if (n.is<language::unary_not>() or n.is<language::lesser_op>() or n.is<language::lesser_or_eq_op>() or
                   n.is<language::greater_op>() or n.is<language::greater_or_eq_op>() or n.is<language::eq_op>() or
                   n.is<language::not_eq_op>() or n.is<language::and_op>() or n.is<language::or_op>()) {
          n.m_data_type = DataType::bool_t;
        } else if (n.is<language::unary_minus>() or n.is<language::unary_plus>()) {
          n.m_data_type = n.children[0]->m_data_type;
        } else if (n.is<language::plus_op>() or n.is<language::minus_op>() or n.is<language::multiply_op>() or
                   n.is<language::divide_op>()) {
          const DataType type_0 = n.children[0]->m_data_type;
          const DataType type_1 = n.children[1]->m_data_type;
    
          n.m_data_type = dataTypePromotion(type_0, type_1);
          if (n.m_data_type == DataType::undefined_t) {
            std::ostringstream message;
            message << "undefined binary operator\n"
                    << "note: incompatible operand types " << n.children[0]->string() << " (" << dataTypeName(type_0)
                    << ") and " << n.children[1]->string() << " (" << dataTypeName(type_1) << ')' << std::ends;
            throw parse_error(message.str(), n.begin());
          }
        }
      }
    }
    }   // namespace internal
    
    void
    build_node_data_types(Node& n)
    {
      Assert(n.is_root());
      n.m_data_type = DataType::void_t;
    
      internal::build_node_data_types(n);
      pout() << " - build node data types\n";
    }
    
    namespace internal
    {
    void
    check_node_data_types(const Node& n)
    {
      if (n.m_data_type == DataType::undefined_t) {
        throw parse_error("unexpected error: undefined datatype for AST node for " + n.name(), n.begin());
      }
    
      for (auto& child : n.children) {
        check_node_data_types(*child);
      }
    }
    }   // namespace internal
    
    void
    check_node_data_types(const Node& n)
    {
      Assert(n.is_root());
      internal::check_node_data_types(n);
      pout() << " - checked node data types\n";
    }
    
    namespace internal
    {
    void
    build_node_values(Node& n, std::shared_ptr<SymbolTable>& symbol_table)
    {
      if (n.is<language::bloc>()) {
        if (!n.children.empty()) {
          std::shared_ptr bloc_symbol_table = std::make_shared<SymbolTable>(symbol_table);
          for (auto& child : n.children) {
            build_node_values(*child, bloc_symbol_table);
          }
        }
        n.m_data_type = DataType::void_t;
      } else {
        for (auto& child : n.children) {
          build_node_values(*child, symbol_table);
        }
    
        if (n.has_content()) {
          if (n.is<language::real>()) {
            std::stringstream ss(n.string());
            double v;
            ss >> v;
            n.m_value = v;
          } else if (n.is<language::integer>()) {
            std::stringstream ss(n.string());
            int64_t v;
            ss >> v;
            n.m_value = v;
          } else if (n.is<language::for_test>()) {
            // if AST contains a for_test statement, it means that no test were
            // given to the for-loop, so its value is always true
            n.m_value = true;
          } else if (n.is<language::true_kw>()) {
            n.m_value = true;
          } else if (n.is<language::false_kw>()) {
            n.m_value = false;
          }
        }
      }
    }
    }   // namespace internal
    
    void
    build_node_values(Node& n)
    {
      Assert(n.is_root());
      n.m_data_type                = DataType::void_t;
      std::shared_ptr symbol_table = std::make_shared<SymbolTable>();
      internal::build_node_values(n, symbol_table);
      pout() << " - build node data types\n";
    }
    
    namespace internal
    {
    void
    check_break_or_continue_placement(const Node& n, bool is_inside_loop)
    {
      if (n.is<language::for_statement>() or n.is<language::do_while_statement>() or n.is<language::while_statement>()) {
        for (auto& child : n.children) {
          check_break_or_continue_placement(*child, true);
        }
      } else if (n.is<language::break_kw>() or n.is<language::continue_kw>()) {
        if (not is_inside_loop) {
          std::ostringstream error_message;
          error_message << "unexpected '" << rang::fgB::red << n.string() << rang::fg::reset
                        << "' outside of loop or switch statement";
          throw parse_error(error_message.str(), std::vector{n.begin()});
        }
      } else {
        for (auto& child : n.children) {
          check_break_or_continue_placement(*child, is_inside_loop);
        }
      }
    }
    }   // namespace internal
    
    void
    check_break_or_continue_placement(const Node& n)
    {
      Assert(n.is_root());
      internal::check_break_or_continue_placement(n, false);
    }
    
    void print(const Node& n);
    
    std::string prefix;
    std::vector<int> last_prefix_size;
    const std::string T_junction(" \u251c\u2500\u2500");
    const std::string L_junction(" \u2514\u2500\u2500");
    
    const std::string pipe_space(" \u2502  ");
    const std::string space_space("    ");
    
    template <typename NodeVector>
    void
    print(const NodeVector& node_list)
    {
      for (size_t i_child = 0; i_child < node_list.size(); ++i_child) {
        if (i_child != node_list.size() - 1) {
          pout() << rang::fgB::green << prefix << T_junction << rang::fg::reset;
        } else {
          pout() << rang::fgB::green << prefix << L_junction << rang::fg::reset;
        }
        auto& child = *(node_list[i_child]);
        if (not child.children.empty()) {
          last_prefix_size.push_back(prefix.size());
          if (i_child != node_list.size() - 1) {
            prefix += pipe_space;
          } else {
            prefix += space_space;
          }
    
          print(*(node_list[i_child]));
    
          prefix.resize(last_prefix_size[last_prefix_size.size() - 1]);
          last_prefix_size.pop_back();
        } else {
          print(*(node_list[i_child]));
        }
      }
    }
    
    void
    print(const Node& n)
    {
      pout() << '(' << rang::fgB::yellow;
      if (n.is_root()) {
        pout() << "root";
      } else {
        pout() << n.name();
      }
      pout() << rang::fg::reset << ':';
      pout() << dataTypeName(n.m_data_type) << ':';
    
      pout() << rang::fgB::cyan;
      std::visit(
        [](const auto& value) {
          using T = std::decay_t<decltype(value)>;
          if constexpr (std::is_same_v<T, std::monostate>) {
            pout() << "--";
          } else {
            pout() << value;
          }
        },
        n.m_value);
      pout() << rang::fg::reset;
    
      pout() << ")\n";
    
      if (not n.children.empty()) {
        print(n.children);
      }
    }
    
    }   // namespace language
    
    void
    parser(const std::string& filename)
    {
      const size_t grammar_issues = analyze<language::grammar>();
    
      pout() << rang::fgB::yellow << "grammar_issues=" << rang::fg::reset << grammar_issues << '\n';
    
      pout() << rang::style::bold << "Parsing file " << rang::style::reset << rang::style::underline << filename
             << rang::style::reset << " ...\n";
    
      std::unique_ptr<language::Node> root_node;
      read_input in(filename);
      try {
        root_node = parse_tree::parse<language::grammar, language::Node, language::selector, nothing, language::errors>(in);
        pout() << " - AST is built ...... [done]\n";
        language::build_node_type(*root_node);
    
        language::build_symbol_table_and_check_declarations(*root_node);
        language::check_symbol_initialization(*root_node);
        {
          std::string dot_filename{"parse_tree.dot"};
          std::ofstream fout(dot_filename);
          language::print_dot(fout, *root_node);
          pout() << "   AST dot file: " << dot_filename << '\n';
        }
    
        language::build_node_data_types(*root_node);
    
        language::check_node_data_types(*root_node);
        language::build_node_values(*root_node);
    
        language::check_break_or_continue_placement(*root_node);
    
        // language::print(*root_node);
    
        language::ExecUntilBreakOrContinue exec_all;
        root_node->execute(exec_all);
        pout() << *(root_node->m_symbol_table) << '\n';
      }
      catch (const parse_error& e) {
        const auto p = e.positions.front();
        perr() << rang::style::bold << p.source << ':' << p.line << ':' << p.byte_in_line << ": " << rang::style::reset
               << rang::fgB::red << "error: " << rang::fg::reset << rang::style::bold << e.what() << rang::style::reset
               << '\n'
               << in.line_at(p) << '\n'
               << std::string(p.byte_in_line, ' ') << rang::fgB::yellow << '^' << rang::fg::reset << std::endl;
        std::exit(1);
      }
    
      pout() << "Parsed: " << filename << '\n';
    }