#include <PugsParser.hpp> #include <PugsAssert.hpp> #include <fstream> #include <iostream> #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 : plus< digit > {}; struct INTEGER : seq< integer, ignored >{}; struct real : seq< 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 : one< 'B' >{}; struct N_set : one< 'N' >{}; struct Z_set : one< 'Z' >{}; struct R_set : one< 'R' >{}; struct basic_type : sor < B_set, R_set, Z_set, N_set > {}; struct TYPESPECIFIER : seq< basic_type, ignored> {}; struct and_kw : TAO_PEGTL_KEYWORD("and") {}; struct or_kw : TAO_PEGTL_KEYWORD("or") {}; struct xor_kw : TAO_PEGTL_KEYWORD("xor") {}; struct bitand_kw : TAO_PEGTL_KEYWORD("bitand") {}; struct bitor_kw : TAO_PEGTL_KEYWORD("bitor") {}; struct and_eq_kw : TAO_PEGTL_KEYWORD("and_eq") {}; struct xor_eq_kw : TAO_PEGTL_KEYWORD("xor_eq") {}; struct or_eq_kw : TAO_PEGTL_KEYWORD("or_eq") {}; 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 cout_kw : TAO_PEGTL_KEYWORD("cout") {}; struct cerr_kw : TAO_PEGTL_KEYWORD("cerr") {}; struct clog_kw : TAO_PEGTL_KEYWORD("clog") {}; struct keywork : sor < basic_type, true_kw, false_kw, do_kw, while_kw, for_kw, if_kw, else_kw, and_kw, or_kw, xor_kw, bitand_kw, bitor_kw, and_eq_kw, xor_eq_kw, or_eq_kw, break_kw, continue_kw, cout_kw, cerr_kw, clog_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_plusplus : TAO_PEGTL_STRING("++") {}; struct unary_minusminus : TAO_PEGTL_STRING("--") {}; struct unary_plus : one< '+' > {}; struct unary_minus : one< '-' > {}; struct unary_not : sor< one< '!'> , not_kw > {}; struct unary_operator : seq< sor< unary_plusplus, unary_minusminus, unary_plus, unary_minus, unary_not>, ignored > {}; struct post_plusplus : TAO_PEGTL_STRING("++") {}; struct post_minusminus : TAO_PEGTL_STRING("--") {}; struct postfix_operator : seq< sor< post_plusplus, post_minusminus>, ignored > {}; struct postfix_expression : seq< primary_expression, star<postfix_operator> > {}; struct unary_expression : sor< seq< unary_operator, unary_expression >, postfix_expression > {}; struct and_op : seq< sor<TAO_PEGTL_STRING("&&"), and_kw>, ignored > {}; struct or_op : seq< sor<TAO_PEGTL_STRING("||"), or_kw>, ignored > {}; struct xor_op : seq< sor< one< '^' >, xor_kw>, ignored >{}; struct bitand_op : seq< sor< seq< one< '&' >, not_at< one< '&' > > >, bitand_kw>, ignored >{}; struct bitor_op : seq< sor< seq< one< '|' >, not_at< one< '|' > > >, bitor_kw>, ignored >{}; struct eqeq_op : seq< TAO_PEGTL_STRING("=="), ignored > {}; struct not_eq_op : seq< TAO_PEGTL_STRING("!="), ignored > {}; struct lesser_op : seq< one< '<' >, not_at< one< '<' > >, ignored > {}; struct lesser_or_eq_op : seq< TAO_PEGTL_STRING("<="), ignored > {}; struct greater_op : seq< one< '>' >, not_at< one< '>' > >, ignored > {}; struct greater_or_eq_op : seq< TAO_PEGTL_STRING(">="), ignored > {}; struct shift_left_op : seq< TAO_PEGTL_STRING("<<"), ignored > {}; struct shift_right_op : seq< TAO_PEGTL_STRING(">>"), ignored > {}; struct plus_op : seq< one< '+' >, not_at< one< '+' > >, ignored > {}; struct minus_op : seq< one< '-' >, not_at< one< '-' > >, ignored > {}; struct multiply_op : seq< one< '*' >, ignored > {}; struct divide_op : seq< one< '/' >, ignored > {}; struct eq_op : seq< one<'='>, not_at< one< '=' > >, ignored > {}; struct multiplyeq_op : seq< TAO_PEGTL_STRING("*="), ignored > {}; struct divideeq_op : seq< TAO_PEGTL_STRING("/="), ignored > {}; struct pluseq_op : seq< TAO_PEGTL_STRING("+="), ignored > {}; struct minuseq_op : seq< TAO_PEGTL_STRING("-="), ignored > {}; struct bit_andeq_op : seq< sor< TAO_PEGTL_STRING("&="), and_eq_kw >, ignored > {}; struct bit_xoreq_op : seq< sor< TAO_PEGTL_STRING("^="), xor_eq_kw >, ignored > {}; struct bit_oreq_op : seq< sor< TAO_PEGTL_STRING("|="), or_eq_kw >, ignored > {}; struct product : list_must< unary_expression, sor< multiply_op, divide_op > > {}; struct sum : list_must< product, sor< plus_op, minus_op > > {}; struct compare : list_must<sum, sor< lesser_or_eq_op, greater_or_eq_op, lesser_op, greater_op > >{}; struct equality : list_must< compare, sor< eqeq_op, not_eq_op > >{}; struct bitwise_and : list_must < equality, bitand_op >{}; struct bitwise_xor : list_must< bitwise_and, xor_op >{}; struct bitwise_or : list_must< bitwise_xor, bitor_op >{}; struct logical_and : list_must< bitwise_or, and_op >{}; struct logical_or : list_must< logical_and, or_op >{}; struct expression : logical_or {}; struct affect_op : sor< eq_op, multiplyeq_op, divideeq_op, pluseq_op, minuseq_op, bit_andeq_op, bit_xoreq_op, bit_oreq_op > {}; struct affectation : seq< NAME , if_must< affect_op, expression > >{}; struct declaration : if_must<TYPESPECIFIER, NAME, opt<if_must<seq<one<'='>,ignored>, expression>> >{}; struct open_brace : seq< one< '{' >, ignored >{}; struct close_brace : seq< one< '}' >, ignored >{}; 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< sor< affectation, 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 ostream_object : seq< sor< cout_kw, cerr_kw, clog_kw >, ignored >{}; struct ostream_statement : seq< ostream_object, star< if_must< shift_left_op, expression, ignored > > >{}; struct instruction : sor<if_must< declaration, semicol >, if_must< affectation, semicol >, if_statement, if_must<do_while_statement, semicol>, while_statement, for_statement, if_must< ostream_statement, semicol >, if_must< BREAK, semicol >, if_must< CONTINUE, semicol >, if_must< expression, semicol >, bloc, semicol> {}; struct INSTRUCTION : seq<instruction, ignored> {}; struct statement_bloc : seq< sor< bloc, instruction >, ignored >{}; struct for_statement_bloc : seq< sor< braced_instruction_list, instruction >, ignored >{}; struct instruction_list : star< INSTRUCTION >{}; 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::SEMICOL>::error_message = "parse error, missing ';'"; template <> inline const std::string errors<language::name>::error_message = "parse error, missing identifier"; template <> inline const std::string errors<language::NAME>::error_message = "parse error, missing identifier"; template <> inline const std::string errors<language::expression>::error_message = "parse error, missing expression"; template <> inline const std::string errors<language::product>::error_message = "parse error, missing expression"; template <> inline const std::string errors<language::sum>::error_message = "parse error, missing expression"; template <> inline const std::string errors<language::compare>::error_message = "parse error, missing expression"; template <> inline const std::string errors<language::equality>::error_message = "parse error, missing expression"; template <> inline const std::string errors<language::bitwise_and>::error_message = "parse error, missing expression"; template <> inline const std::string errors<language::bitwise_xor>::error_message = "parse error, missing expression"; template <> inline const std::string errors<language::bitwise_or>::error_message = "parse error, missing expression"; template <> inline const std::string errors<language::logical_and>::error_message = "parse error, missing expression"; template <> inline const std::string errors<language::logical_or>::error_message = "parse error, missing expression"; template <> inline const std::string errors<language::parented_expression>::error_message = "parse error, missing parented expression"; template <> inline const std::string errors<language::statement_bloc>::error_message = "parse error, missing instruction"; template <> inline const std::string errors<language::WHILE>::error_message = "parse error, missing 'while' statement"; template <> inline const std::string errors<language::while_kw>::error_message = "parse error, missing 'while' statement"; template <> inline const std::string errors<language::open_parent>::error_message = "parse error, missing open parent '('"; template <> inline const std::string errors<language::for_statement_bloc>::error_message = "parse error, missing for-loop body"; 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, unsigned_int_t = 1, int_t = 2, double_t = 3, 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 = "B"; break; case DataType::unsigned_int_t: name = "N"; break; case DataType::int_t: name = "Z"; break; case DataType::double_t: name = "R"; 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, uint64_t, int64_t, double>; 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() {} virtual ~INodeProcessor() {} }; struct Node : public parse_tree::basic_node<Node> { std::shared_ptr<SymbolTable> m_symbol_table; std::unique_ptr<INodeProcessor> m_node_processor; PUGS_INLINE void execute(ExecUntilBreakOrContinue& exec_policy) { Assert(static_cast<bool>(m_node_processor)); 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>() or n->children[0]->is<language::unary_minusminus>() or n->children[0]->is<language::unary_plusplus>()) { 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...); } else if (n->children[1]->is<language::post_minusminus>() or n->children[1]->is<language::post_plusplus>()) { n->remove_content(); auto expression = std::move(n->children[0]); auto unary_operator = std::move(n->children[1]); 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()); } } }; struct simplify_stream_statement : parse_tree::apply<simplify_stream_statement> { template <typename... States> static void transform(std::unique_ptr<Node>& n, States&&...) { for (size_t i = 1; i < n->children.size(); ++i) { n->children[0]->children.emplace_back(std::move(n->children[i])); } n->remove_content(); n = std::move(n->children[0]); } }; template <typename Rule> using selector = parse_tree::selector<Rule, parse_tree::store_content::on<true_kw, false_kw, integer, real, name, B_set, N_set, Z_set, R_set, cout_kw, cerr_kw, clog_kw, declaration, if_statement, do_while_statement, while_statement, for_statement, break_kw, continue_kw>, rearrange::on<product, affectation, expression>, 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, eqeq_op, not_eq_op, and_op, or_op, xor_op, bitand_op, bitor_op, eq_op, multiplyeq_op, divideeq_op, pluseq_op, minuseq_op, // shift_left_op, // shift_right_op, bit_andeq_op, bit_xoreq_op, bit_oreq_op, unary_plusplus, unary_minusminus, post_minusminus, post_plusplus>, 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>, simplify_stream_statement::on<ostream_statement>>; 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) { ; } }; 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); } } }; template <typename Op> struct AffOp; template <> struct AffOp<language::eq_op> { template <typename A, typename B> PUGS_INLINE void eval(A& a, const B& b) { a = b; } }; template <> struct AffOp<language::multiplyeq_op> { template <typename A, typename B> PUGS_INLINE void eval(A& a, const B& b) { a *= b; } }; template <> struct AffOp<language::divideeq_op> { template <typename A, typename B> PUGS_INLINE void eval(A& a, const B& b) { a /= b; } }; template <> struct AffOp<language::pluseq_op> { template <typename A, typename B> PUGS_INLINE void eval(A& a, const B& b) { a += b; } }; template <> struct AffOp<language::minuseq_op> { template <typename A, typename B> PUGS_INLINE void eval(A& a, const B& b) { a -= b; } }; template <> struct AffOp<language::bit_andeq_op> { template <typename A, typename B> PUGS_INLINE void eval(A& a, const B& b) { a &= b; } }; template <> struct AffOp<language::bit_xoreq_op> { template <typename A, typename B> PUGS_INLINE void eval(A& a, const B& b) { a ^= b; } }; template <> struct AffOp<language::bit_oreq_op> { template <typename A, typename B> PUGS_INLINE void eval(A& a, const B& b) { a |= b; } }; template <typename OperatorT, typename ValueT, typename DataT> class AffectationProcessor final : public INodeProcessor { private: Node& m_node; DataVariant* p_value{nullptr}; static inline const bool _is_defined{[] { if constexpr (std::is_same_v<OperatorT, language::bit_andeq_op> or std::is_same_v<OperatorT, language::bit_xoreq_op> or std::is_same_v<OperatorT, language::bit_oreq_op>) { return std::is_same_v<std::decay_t<ValueT>, std::decay_t<DataT>> and std::is_integral_v<std::decay_t<ValueT>>; } else if constexpr (std::is_same_v<std::decay_t<ValueT>, bool>) { if constexpr (not std::is_same_v<OperatorT, language::eq_op>) { return false; } } return true; }()}; public: AffectationProcessor(Node& node) : m_node{node} { if constexpr (_is_defined) { const std::string& symbol = m_node.children[0]->string(); auto [i_symbol, found] = m_node.m_symbol_table->find(symbol); Assert(found); p_value = &i_symbol->second.value(); } else { throw parse_error("invalid operands to binary expression", std::vector{m_node.begin()}); } } void execute(ExecUntilBreakOrContinue& exec_policy) { if constexpr (_is_defined) { m_node.children[1]->execute(exec_policy); if constexpr (std::is_same_v<OperatorT, language::eq_op>) { if constexpr (std::is_same_v<ValueT, DataT>) { *p_value = m_node.children[1]->m_value; } else { *p_value = static_cast<ValueT>(std::get<DataT>(m_node.children[1]->m_value)); } } else { AffOp<OperatorT>().eval(std::get<ValueT>(*p_value), std::get<DataT>(m_node.children[1]->m_value)); } } } }; class NoProcess final : public INodeProcessor { public: NoProcess() {} PUGS_INLINE void execute(ExecUntilBreakOrContinue&) { ; } }; template <typename Op> struct UnaryOp; template <> struct UnaryOp<language::unary_minus> { template <typename A> PUGS_INLINE A eval(const A& a) { return -a; } }; template <> struct UnaryOp<language::unary_not> { template <typename A> PUGS_INLINE bool eval(const A& a) { return not a; } }; template <typename UnaryOpT, typename ValueT, typename DataT> class UnaryExpressionProcessor final : public INodeProcessor { Node& m_node; public: PUGS_INLINE ValueT eval(const DataVariant& a) { return UnaryOp<UnaryOpT>().eval(std::get<DataT>(a)); } public: UnaryExpressionProcessor(Node& node) : m_node{node} {} void execute(ExecUntilBreakOrContinue& exec_policy) { m_node.children[0]->execute(exec_policy); m_node.m_value = eval(m_node.children[0]->m_value); } }; template <typename Op> struct IncDecOp; template <> struct IncDecOp<language::unary_minusminus> { template <typename A> PUGS_INLINE A eval(A& a) { return --a; } }; template <> struct IncDecOp<language::unary_plusplus> { template <typename A> PUGS_INLINE A eval(A& a) { return ++a; } }; template <> struct IncDecOp<language::post_minusminus> { template <typename A> PUGS_INLINE A eval(A& a) { return a--; } }; template <> struct IncDecOp<language::post_plusplus> { template <typename A> PUGS_INLINE A eval(A& a) { return a++; } }; template <typename IncDecOpT, typename ValueT, typename DataT> class IncDecExpressionProcessor final : public INodeProcessor { Node& m_node; DataVariant* p_value{nullptr}; static inline const bool _is_defined{[] { if constexpr (std::is_same_v<IncDecOpT, language::unary_minusminus> or std::is_same_v<IncDecOpT, language::unary_plusplus> or std::is_same_v<IncDecOpT, language::post_minusminus> or std::is_same_v<IncDecOpT, language::post_plusplus>) { return not std::is_same_v<std::decay_t<DataT>, bool>; } return true; }()}; public: IncDecExpressionProcessor(Node& node) : m_node{node} { if constexpr (_is_defined) { Assert(m_node.children[0]->is<language::name>()); // It is sure at this point that children 0 is a variable name const std::string& symbol = m_node.children[0]->string(); auto [i_symbol, found] = m_node.m_symbol_table->find(symbol); Assert(found); p_value = &i_symbol->second.value(); } else { throw parse_error("invalid operand to unary operator", std::vector{m_node.begin()}); } } void execute(ExecUntilBreakOrContinue&) { if constexpr (_is_defined) { m_node.m_value = IncDecOp<IncDecOpT>().eval(std::get<DataT>(*p_value)); } } }; template <typename Op> struct BinOp; template <> struct BinOp<language::and_op> { template <typename A, typename B> PUGS_INLINE 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> PUGS_INLINE auto eval(const A& a, const B& b) -> decltype(a or b) { return a or b; } }; template <> struct BinOp<language::xor_op> { template <typename A, typename B> PUGS_INLINE auto eval(const A& a, const B& b) -> decltype(a xor b) { return a xor b; } }; template <> struct BinOp<language::bitand_op> { template <typename A, typename B> PUGS_INLINE auto eval(const A& a, const B& b) -> decltype(a & b) { return a & b; } }; template <> struct BinOp<language::bitor_op> { template <typename A, typename B> PUGS_INLINE auto eval(const A& a, const B& b) -> decltype(a | b) { return a | b; } }; template <> struct BinOp<language::eqeq_op> { template <typename A, typename B> PUGS_INLINE 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> PUGS_INLINE 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> PUGS_INLINE 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> PUGS_INLINE 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> PUGS_INLINE 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> PUGS_INLINE 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> PUGS_INLINE 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> PUGS_INLINE 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> PUGS_INLINE 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> PUGS_INLINE auto eval(const A& a, const B& b) -> decltype(a / b) { return a / b; } }; template <typename BinaryOpT, typename ValueT, typename A_DataT, typename B_DataT> class BinaryExpressionProcessor final : public INodeProcessor { Node& m_node; PUGS_INLINE ValueT eval(const DataVariant& a, const DataVariant& b) { // Add 'signed' when necessary to avoid signed/unsigned comparison warnings if constexpr ((not(std::is_same_v<A_DataT, bool> or std::is_same_v<B_DataT, bool>)) and (std::is_same_v<BinaryOpT, language::and_op> or std::is_same_v<BinaryOpT, language::or_op> or std::is_same_v<BinaryOpT, language::xor_op> or std::is_same_v<BinaryOpT, language::bitand_op> or std::is_same_v<BinaryOpT, language::bitor_op> or std::is_same_v<BinaryOpT, language::eqeq_op> or std::is_same_v<BinaryOpT, language::not_eq_op> or std::is_same_v<BinaryOpT, language::lesser_op> or std::is_same_v<BinaryOpT, language::lesser_or_eq_op> or std::is_same_v<BinaryOpT, language::greater_op> or std::is_same_v<BinaryOpT, language::greater_or_eq_op>) and (std::is_signed_v<A_DataT> xor std::is_signed_v<B_DataT>)) { if constexpr (std::is_unsigned_v<A_DataT>) { using signed_A_DataT = std::make_signed_t<A_DataT>; const signed_A_DataT signed_a = static_cast<signed_A_DataT>(std::get<A_DataT>(a)); return BinOp<BinaryOpT>().eval(signed_a, std::get<B_DataT>(b)); } else { using signed_B_DataT = std::make_signed_t<B_DataT>; const signed_B_DataT signed_b = static_cast<signed_B_DataT>(std::get<B_DataT>(b)); return BinOp<BinaryOpT>().eval(std::get<A_DataT>(a), signed_b); } } else { return BinOp<BinaryOpT>().eval(std::get<A_DataT>(a), std::get<B_DataT>(b)); } } static inline const bool _is_defined{[] { if constexpr (std::is_same_v<BinaryOpT, language::bitand_op> or std::is_same_v<BinaryOpT, language::xor_op> or std::is_same_v<BinaryOpT, language::bitor_op>) { return std::is_same_v<std::decay_t<A_DataT>, std::decay_t<B_DataT>> and std::is_integral_v<std::decay_t<A_DataT>>; } return true; }()}; public: BinaryExpressionProcessor(Node& node) : m_node{node} { if constexpr (not _is_defined) { throw parse_error("invalid operands to binary expression", std::vector{m_node.begin()}); } } void execute(ExecUntilBreakOrContinue& exec_policy) { if constexpr (_is_defined) { m_node.children[0]->execute(exec_policy); m_node.children[1]->execute(exec_policy); m_node.m_value = eval(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} {} 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} {} 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} {} 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; DataVariant* p_value{nullptr}; public: NameExpression(Node& node) : m_node{node} { const std::string& symbol = m_node.string(); auto [i_symbol, found] = m_node.m_symbol_table->find(symbol); Assert(found); p_value = &(i_symbol->second.value()); } void execute(ExecUntilBreakOrContinue&) { m_node.m_value = *p_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); } }; class OStreamObject final : public INodeProcessor { Node& m_node; std::ostream& m_os; public: OStreamObject(Node& node, std::ostream& os) : m_node{node}, m_os(os) { ; } void execute(ExecUntilBreakOrContinue& exec_policy) { for (size_t i = 0; i < m_node.children.size(); ++i) { m_node.children[i]->execute(exec_policy); std::visit( [&](auto&& value) { using ValueT = std::decay_t<decltype(value)>; if constexpr (not std::is_same_v<std::monostate, ValueT>) { if constexpr (std::is_same_v<bool, ValueT>) { m_os << std::boolalpha << value; } else { m_os << value; } } }, m_node.children[i]->m_value); } } }; namespace internal { void build_node_type(Node& n) { auto set_unary_operator_processor = [](Node& n, const auto& operator_v) { auto set_unary_operator_processor_for_data = [&](const auto& value, const DataType& data_type) { using OperatorT = std::decay_t<decltype(operator_v)>; using ValueT = std::decay_t<decltype(value)>; switch (data_type) { case DataType::bool_t: { n.m_node_processor = std::make_unique<UnaryExpressionProcessor<OperatorT, ValueT, bool>>(n); break; } case DataType::unsigned_int_t: { n.m_node_processor = std::make_unique<UnaryExpressionProcessor<OperatorT, ValueT, uint64_t>>(n); break; } case DataType::int_t: { n.m_node_processor = std::make_unique<UnaryExpressionProcessor<OperatorT, ValueT, int64_t>>(n); break; } case DataType::double_t: { n.m_node_processor = std::make_unique<UnaryExpressionProcessor<OperatorT, ValueT, double>>(n); break; } default: { throw parse_error("undefined operand type for unary operator", std::vector{n.children[0]->begin()}); } } }; auto set_unary_operator_processor_for_value = [&](const DataType& value_type) { const DataType data_type = n.children[0]->m_data_type; switch (value_type) { case DataType::bool_t: { set_unary_operator_processor_for_data(bool{}, data_type); break; } case DataType::unsigned_int_t: { set_unary_operator_processor_for_data(uint64_t{}, data_type); break; } case DataType::int_t: { set_unary_operator_processor_for_data(int64_t{}, data_type); break; } case DataType::double_t: { set_unary_operator_processor_for_data(double{}, data_type); break; } default: { throw parse_error("undefined value type for unary operator", std::vector{n.begin()}); } } }; set_unary_operator_processor_for_value(n.m_data_type); }; auto set_inc_dec_operator_processor = [](Node& n, const auto& operator_v) { auto set_inc_dec_operator_processor_for_data = [&](const auto& value, const DataType& data_type) { using OperatorT = std::decay_t<decltype(operator_v)>; using ValueT = std::decay_t<decltype(value)>; switch (data_type) { case DataType::bool_t: { n.m_node_processor = std::make_unique<IncDecExpressionProcessor<OperatorT, ValueT, bool>>(n); break; } case DataType::unsigned_int_t: { n.m_node_processor = std::make_unique<IncDecExpressionProcessor<OperatorT, ValueT, uint64_t>>(n); break; } case DataType::int_t: { n.m_node_processor = std::make_unique<IncDecExpressionProcessor<OperatorT, ValueT, int64_t>>(n); break; } case DataType::double_t: { n.m_node_processor = std::make_unique<IncDecExpressionProcessor<OperatorT, ValueT, double>>(n); break; } default: { throw parse_error("undefined operand type for unary operator", std::vector{n.children[0]->begin()}); } } }; auto set_inc_dec_processor_for_value = [&](const DataType& value_type) { const DataType data_type = n.children[0]->m_data_type; switch (value_type) { case DataType::bool_t: { set_inc_dec_operator_processor_for_data(bool{}, data_type); break; } case DataType::unsigned_int_t: { set_inc_dec_operator_processor_for_data(uint64_t{}, data_type); break; } case DataType::int_t: { set_inc_dec_operator_processor_for_data(int64_t{}, data_type); break; } case DataType::double_t: { set_inc_dec_operator_processor_for_data(double{}, data_type); break; } default: { throw parse_error("undefined value type for unary operator", std::vector{n.begin()}); } } }; if (not n.children[0]->is<language::name>()) { throw parse_error("invalid operand type for unary operator", std::vector{n.begin()}); } set_inc_dec_processor_for_value(n.m_data_type); }; auto set_binary_operator_processor = [](Node& n, const auto& operator_v) { auto set_binary_operator_processor_for_data_b = [&](const auto value, const auto data_a, const DataType& data_type_b) { using OperatorT = std::decay_t<decltype(operator_v)>; using ValueT = std::decay_t<decltype(value)>; using DataTA = std::decay_t<decltype(data_a)>; switch (data_type_b) { case DataType::bool_t: { n.m_node_processor = std::make_unique<BinaryExpressionProcessor<OperatorT, ValueT, DataTA, bool>>(n); break; } case DataType::unsigned_int_t: { n.m_node_processor = std::make_unique<BinaryExpressionProcessor<OperatorT, ValueT, DataTA, uint64_t>>(n); break; } case DataType::int_t: { n.m_node_processor = std::make_unique<BinaryExpressionProcessor<OperatorT, ValueT, DataTA, int64_t>>(n); break; } case DataType::double_t: { n.m_node_processor = std::make_unique<BinaryExpressionProcessor<OperatorT, ValueT, DataTA, double>>(n); break; } default: { throw parse_error("undefined operand type for binary operator", std::vector{n.children[1]->begin()}); } } }; auto set_binary_operator_processor_for_data_a = [&](const auto value, const DataType& data_type_a) { const DataType data_type_b = n.children[1]->m_data_type; switch (data_type_a) { case DataType::bool_t: { set_binary_operator_processor_for_data_b(value, bool{}, data_type_b); break; } case DataType::unsigned_int_t: { set_binary_operator_processor_for_data_b(value, uint64_t{}, data_type_b); break; } case DataType::int_t: { set_binary_operator_processor_for_data_b(value, int64_t{}, data_type_b); break; } case DataType::double_t: { set_binary_operator_processor_for_data_b(value, double{}, data_type_b); break; } default: { throw parse_error("undefined operand type for binary operator", std::vector{n.children[0]->begin()}); } } }; auto set_binary_operator_processor_for_value = [&](const DataType& value_type) { const DataType data_type_a = n.children[0]->m_data_type; switch (value_type) { case DataType::bool_t: { set_binary_operator_processor_for_data_a(bool{}, data_type_a); break; } case DataType::unsigned_int_t: { set_binary_operator_processor_for_data_a(uint64_t{}, data_type_a); break; } case DataType::int_t: { set_binary_operator_processor_for_data_a(int64_t{}, data_type_a); break; } case DataType::double_t: { set_binary_operator_processor_for_data_a(double{}, data_type_a); break; } default: { throw parse_error("undefined value type for binary operator", std::vector{n.begin()}); } } }; set_binary_operator_processor_for_value(n.m_data_type); }; auto set_affectation_processor = [](Node& n, const auto& operator_v) { auto set_affectation_processor_for_data = [&](const auto& value, const DataType& data_type) { using OperatorT = std::decay_t<decltype(operator_v)>; using ValueT = std::decay_t<decltype(value)>; switch (data_type) { case DataType::bool_t: { n.m_node_processor = std::make_unique<AffectationProcessor<OperatorT, ValueT, bool>>(n); break; } case DataType::unsigned_int_t: { n.m_node_processor = std::make_unique<AffectationProcessor<OperatorT, ValueT, uint64_t>>(n); break; } case DataType::int_t: { n.m_node_processor = std::make_unique<AffectationProcessor<OperatorT, ValueT, int64_t>>(n); break; } case DataType::double_t: { n.m_node_processor = std::make_unique<AffectationProcessor<OperatorT, ValueT, double>>(n); break; } default: { throw parse_error("undefined operand type for affectation", std::vector{n.children[0]->begin()}); } } }; auto set_affectation_processor_for_value = [&](const DataType& value_type) { const DataType data_type = n.children[1]->m_data_type; switch (value_type) { case DataType::bool_t: { set_affectation_processor_for_data(bool{}, data_type); break; } case DataType::unsigned_int_t: { set_affectation_processor_for_data(uint64_t{}, data_type); break; } case DataType::int_t: { set_affectation_processor_for_data(int64_t{}, data_type); break; } case DataType::double_t: { set_affectation_processor_for_data(double{}, data_type); break; } default: { throw parse_error("undefined value type for affectation", std::vector{n.begin()}); } } }; set_affectation_processor_for_value(n.m_data_type); }; if (n.is_root()) { n.m_node_processor = std::make_unique<NodeList>(n); } else if (n.is<language::bloc>()) { n.m_node_processor = std::make_unique<NodeList>(n); } else if (n.is<language::declaration>()) { n.m_node_processor = std::make_unique<NoProcess>(); } else if (n.is<language::eq_op>()) { set_affectation_processor(n, language::eq_op{}); } else if (n.is<language::multiplyeq_op>()) { set_affectation_processor(n, language::multiplyeq_op{}); } else if (n.is<language::divideeq_op>()) { set_affectation_processor(n, language::divideeq_op{}); } else if (n.is<language::pluseq_op>()) { set_affectation_processor(n, language::pluseq_op{}); } else if (n.is<language::minuseq_op>()) { set_affectation_processor(n, language::minuseq_op{}); } else if (n.is<language::bit_andeq_op>()) { set_affectation_processor(n, language::bit_andeq_op{}); } else if (n.is<language::bit_xoreq_op>()) { set_affectation_processor(n, language::bit_xoreq_op{}); } else if (n.is<language::bit_oreq_op>()) { set_affectation_processor(n, language::bit_oreq_op{}); } else if (n.is<language::real>()) { n.m_node_processor = std::make_unique<NoProcess>(); } else if (n.is<language::integer>()) { n.m_node_processor = std::make_unique<NoProcess>(); } else if (n.is<language::name>()) { n.m_node_processor = std::make_unique<NameExpression>(n); } else if (n.is<language::unary_minus>()) { set_unary_operator_processor(n, language::unary_minus{}); } else if (n.is<language::unary_not>()) { set_unary_operator_processor(n, language::unary_not{}); } else if (n.is<language::unary_minusminus>()) { set_inc_dec_operator_processor(n, language::unary_minusminus{}); } else if (n.is<language::unary_plusplus>()) { set_inc_dec_operator_processor(n, language::unary_plusplus{}); } else if (n.is<language::post_minusminus>()) { set_inc_dec_operator_processor(n, language::post_minusminus{}); } else if (n.is<language::post_plusplus>()) { set_inc_dec_operator_processor(n, language::post_plusplus{}); } else if (n.is<language::multiply_op>()) { set_binary_operator_processor(n, language::multiply_op{}); } else if (n.is<language::divide_op>()) { set_binary_operator_processor(n, language::divide_op{}); } else if (n.is<language::plus_op>()) { set_binary_operator_processor(n, language::plus_op{}); } else if (n.is<language::minus_op>()) { set_binary_operator_processor(n, language::minus_op{}); } else if (n.is<language::or_op>()) { set_binary_operator_processor(n, language::or_op{}); } else if (n.is<language::and_op>()) { set_binary_operator_processor(n, language::and_op{}); } else if (n.is<language::xor_op>()) { set_binary_operator_processor(n, language::xor_op{}); } else if (n.is<language::bitand_op>()) { set_binary_operator_processor(n, language::bitand_op{}); } else if (n.is<language::bitor_op>()) { set_binary_operator_processor(n, language::bitor_op{}); } else if (n.is<language::greater_op>()) { set_binary_operator_processor(n, language::greater_op{}); } else if (n.is<language::greater_or_eq_op>()) { set_binary_operator_processor(n, language::greater_or_eq_op{}); } else if (n.is<language::lesser_op>()) { set_binary_operator_processor(n, language::lesser_op{}); } else if (n.is<language::lesser_or_eq_op>()) { set_binary_operator_processor(n, language::lesser_or_eq_op{}); } else if (n.is<language::eqeq_op>()) { set_binary_operator_processor(n, language::eqeq_op{}); } else if (n.is<language::not_eq_op>()) { set_binary_operator_processor(n, language::not_eq_op{}); } else if (n.is<language::B_set>()) { } else if (n.is<language::N_set>()) { } else if (n.is<language::Z_set>()) { } else if (n.is<language::cout_kw>()) { n.m_node_processor = std::make_unique<OStreamObject>(n, std::cout); } else if (n.is<language::cerr_kw>()) { n.m_node_processor = std::make_unique<OStreamObject>(n, std::cerr); } else if (n.is<language::clog_kw>()) { n.m_node_processor = std::make_unique<OStreamObject>(n, std::clog); } else if (n.is<language::R_set>()) { } else if (n.is<language::if_statement>()) { n.m_node_processor = std::make_unique<IfStatement>(n); } else if (n.is<language::statement_bloc>()) { n.m_node_processor = std::make_unique<NodeList>(n); } else if (n.is<language::do_while_statement>()) { n.m_node_processor = std::make_unique<DoWhileStatement>(n); } else if (n.is<language::while_statement>()) { n.m_node_processor = std::make_unique<WhileStatement>(n); } else if (n.is<language::for_statement>()) { n.m_node_processor = std::make_unique<ForStatement>(n); } else if (n.is<language::for_statement_bloc>()) { n.m_node_processor = std::make_unique<NodeList>(n); } else if (n.is<language::for_init>()) { n.m_node_processor = std::make_unique<NoProcess>(); } else if (n.is<language::for_post>()) { n.m_node_processor = std::make_unique<NoProcess>(); } else if (n.is<language::for_test>()) { n.m_node_processor = std::make_unique<NoProcess>(); } else if (n.is<language::break_kw>()) { n.m_node_processor = std::make_unique<BreakExpression>(); } else if (n.is<language::continue_kw>()) { n.m_node_processor = std::make_unique<ContinueExpression>(); } else if (n.is<language::true_kw>()) { n.m_node_processor = std::make_unique<NoProcess>(); } else if (n.is<language::false_kw>()) { n.m_node_processor = std::make_unique<NoProcess>(); } else { 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()}}; } for (auto& child : n.children) { internal::build_node_type(*child); } } } // namespace internal void build_node_type(Node& n) { Assert(n.is_root()); Assert(n.is<void>()); n.m_node_processor = std::make_unique<NodeList>(n); for (auto& child : n.children) { internal::build_node_type(*child); } std::cout << " - 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); std::cout << " - 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.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::eq_op>()) { // 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::eq_op>())) { for (auto& child : n.children) { check_symbol_initialization(*child, symbol_table); } } } } } // namespace internal void check_symbol_initialization(const Node& n) { std::cerr << rang::fgB::yellow << "warning:" << rang::fg::reset << " symbol initialization checking not finished" "if and loops statements are not correctly evaluated\n"; Assert(n.is_root()); std::shared_ptr symbol_table = std::make_shared<SymbolTable>(); internal::check_symbol_initialization(n, symbol_table); std::cout << " - 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::cout_kw>() or n.is<language::cerr_kw>() or n.is<language::clog_kw>()) { 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::Z_set>()) { data_type = DataType::int_t; } else if (type_node.is<language::N_set>()) { data_type = DataType::unsigned_int_t; } else if (type_node.is<language::R_set>()) { data_type = DataType::double_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 = data_type; } 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::eq_op>() or n.is<language::multiplyeq_op>() or n.is<language::divideeq_op>() or n.is<language::pluseq_op>() or n.is<language::minuseq_op>() or n.is<language::bit_andeq_op>() or n.is<language::bit_xoreq_op>() or n.is<language::bit_oreq_op>()) { n.m_data_type = n.children[0]->m_data_type; } 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::statement_bloc>()) { n.m_data_type = DataType::void_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::eqeq_op>() or n.is<language::not_eq_op>() or n.is<language::and_op>() or n.is<language::or_op>() or n.is<language::xor_op>() or n.is<language::bitand_op>() or n.is<language::bitor_op>()) { n.m_data_type = DataType::bool_t; } else if (n.is<language::unary_minus>() or n.is<language::unary_plus>() or n.is<language::unary_plusplus>() or n.is<language::unary_minusminus>()) { 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); std::cout << " - 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); std::cout << " - 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); std::cout << " - 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); } namespace internal { void simplify_declarations(Node& n) { if (n.is<language::declaration>()) { if (n.children.size() == 3) { n.children[0] = std::move(n.children[1]); n.children[1] = std::move(n.children[2]); n.children.resize(2); n.id = typeid(language::eq_op); } } else { for (auto& child : n.children) { simplify_declarations(*child); } } } } // namespace internal void simplify_declarations(Node& n) { Assert(n.is_root()); internal::simplify_declarations(n); } 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) { std::cout << rang::fgB::green << prefix << T_junction << rang::fg::reset; } else { std::cout << 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) { std::cout << '(' << rang::fgB::yellow; if (n.is_root()) { std::cout << "root"; } else { std::cout << n.name(); } std::cout << rang::fg::reset << ':'; std::cout << dataTypeName(n.m_data_type) << ':'; std::cout << rang::fgB::cyan; std::visit( [](const auto& value) { using T = std::decay_t<decltype(value)>; if constexpr (std::is_same_v<T, std::monostate>) { std::cout << "--"; } else { std::cout << value; } }, n.m_value); std::cout << rang::fg::reset; std::cout << ")\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>(); std::cout << rang::fgB::yellow << "grammar_issues=" << rang::fg::reset << grammar_issues << '\n'; std::cout << 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); std::cout << " - AST is built ...... [done]\n"; 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); std::cout << " 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); // optimizations language::simplify_declarations(*root_node); language::build_node_type(*root_node); language::print(*root_node); language::ExecUntilBreakOrContinue exec_all; root_node->execute(exec_all); std::cout << *(root_node->m_symbol_table) << '\n'; } catch (const parse_error& e) { const auto p = e.positions.front(); std::cerr << 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); } std::cout << "Parsed: " << filename << '\n'; }