Select Git revision
Connectivity.hpp
-
Stéphane Del Pino authored
Now use const semantic "a la" Kokkos in SubItemValuePerItem
Stéphane Del Pino authoredNow use const semantic "a la" Kokkos in SubItemValuePerItem
PugsParser.cpp 77.41 KiB
#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';
}