#include #include "UserScript/interpreter.h" namespace scripting { // Replace with constexpr vector & find ? static const std::map mappings = { {ast::operator_t::logical_not, ByteCodeInterpreter::operator_t::logical_not}, {ast::operator_t::binary_not, ByteCodeInterpreter::operator_t::binary_not}, {ast::operator_t::divide, ByteCodeInterpreter::operator_t::divide}, {ast::operator_t::modulo, ByteCodeInterpreter::operator_t::modulo}, {ast::operator_t::multiply, ByteCodeInterpreter::operator_t::multiply}, {ast::operator_t::subtract, ByteCodeInterpreter::operator_t::subtract}, {ast::operator_t::add, ByteCodeInterpreter::operator_t::add}, {ast::operator_t::bitshift_left, ByteCodeInterpreter::operator_t::bitshift_left}, {ast::operator_t::bitshift_right, ByteCodeInterpreter::operator_t::bitshift_right}, {ast::operator_t::rotate_left, ByteCodeInterpreter::operator_t::rotate_left}, {ast::operator_t::rotate_right, ByteCodeInterpreter::operator_t::rotate_right}, {ast::operator_t::less_than, ByteCodeInterpreter::operator_t::less_than}, {ast::operator_t::greater_than, ByteCodeInterpreter::operator_t::greater_than}, {ast::operator_t::less_or_equal_than, ByteCodeInterpreter::operator_t::less_or_equal_than}, {ast::operator_t::greater_or_equal_than, ByteCodeInterpreter::operator_t::greater_or_equal_than}, {ast::operator_t::equals, ByteCodeInterpreter::operator_t::equals}, {ast::operator_t::different, ByteCodeInterpreter::operator_t::different}, {ast::operator_t::binary_and, ByteCodeInterpreter::operator_t::binary_and}, {ast::operator_t::binary_or, ByteCodeInterpreter::operator_t::binary_or}, {ast::operator_t::binary_xor, ByteCodeInterpreter::operator_t::binary_xor}, {ast::operator_t::logical_and, ByteCodeInterpreter::operator_t::logical_and}, {ast::operator_t::logical_or, ByteCodeInterpreter::operator_t::logical_or}, }; /// GENERATION HANDLERS DECLARATIONS template void handle(std::vector &, std::vector &, T &); template<> void handle(std::vector &ctx, std::vector &errors, ast::block &block); template<> void handle(std::vector &ctx, std::vector &errors, ast::command_expression &cmd); template<> void handle(std::vector &ctx, std::vector &errors, ast::binary_algebraic_expression &cmd); template<> void handle(std::vector &ctx, std::vector &errors, ast::unary_algebraic_expression &cmd); template<> void handle(std::vector &ctx, std::vector &errors, ast::paren_expression &cmd); template<> void handle(std::vector &ctx, std::vector &errors, ast::conditional &cmd); template<> void handle(std::vector &ctx, std::vector &errors, ast::while_loop &cmd); template<> void handle(std::vector &ctx, std::vector &errors, ast::expression &cmd); template<> void handle(std::vector &ctx, std::vector &errors, ast::variable_expression &cmd); template<> void handle(std::vector &ctx, std::vector &errors, ast::literal_int_expression &cmd); template<> void handle(std::vector &ctx, std::vector &errors, ast::literal_string_expression &cmd); /// GENERATION HANDLERS DEFINITIONS template<> void handle(std::vector &ctx, std::vector &errors, ast::block &block) { for (auto &elem: block.contents) { std::visit([&](auto &v) { handle(ctx, errors, *v); }, elem.contents); ctx.push_back(ByteCodeInterpreter::operand{.element = ByteCodeInterpreter::operator_t::INTERNAL_stack_cls}); } } template<> void handle(std::vector &ctx, std::vector &errors, ast::command_expression &cmd) { for (auto& argument : std::ranges::reverse_view(cmd.arguments)) { std::visit([&](auto &v) { handle(ctx, errors, *v); }, argument->contents); } ctx.push_back( ByteCodeInterpreter::operand{.element = ByteCodeInterpreter::function_tag{.name = cmd.name.value, .arity = cmd.arguments.size()}, .location = cmd.location}); } template<> void handle(std::vector &ctx, std::vector &errors, ast::paren_expression &expr) { std::visit([&](auto &v) { handle(ctx, errors, *v); }, expr.content); } template<> void handle(std::vector &ctx, std::vector &errors, ast::expression &expr) { std::visit([&](auto &v) { handle(ctx, errors, *v); }, expr.contents); } template<> void handle(std::vector &ctx, std::vector &errors, ast::binary_algebraic_expression &expr) { handle(ctx, errors, *expr.lhs); handle(ctx, errors, *expr.rhs); ctx.push_back(ByteCodeInterpreter::operand{.element = mappings.at(expr.op), .location = expr.location}); } template<> void handle(std::vector &ctx, std::vector &errors, ast::unary_algebraic_expression &expr) { handle(ctx, errors, *expr.content); ctx.push_back(ByteCodeInterpreter::operand{.element = mappings.at(expr.op), .location = expr.location}); } template<> void handle(std::vector &ctx, std::vector &errors, ast::variable_expression &expr) { ctx.push_back(ByteCodeInterpreter::operand{ ByteCodeInterpreter::variable_tag{.name = expr.name.value, .location = expr.location}}); } template<> void handle(std::vector &ctx, std::vector &errors, ast::literal_int_expression &expr) { ctx.push_back(ByteCodeInterpreter::operand{script_value{expr.value}}); } template<> void handle(std::vector &ctx, std::vector &errors, ast::literal_string_expression &expr) { ctx.push_back(ByteCodeInterpreter::operand{script_value{expr.value}}); } template<> void handle(std::vector &ctx, std::vector &errors, ast::conditional &cond) { /// some basic documentation (from before the reference stability bug but things are the same): /// https://app.excalidraw.com/s/hxPegpAmTX/2c8KKzinqeg std::visit([&](auto &v) { handle(ctx, errors, *v); }, cond.condition->contents); ctx.push_back(ByteCodeInterpreter::operand{.element = script_value{}, .location = cond.location}); /// As you can see, being smart is dumb, be a fucking monkey that comes from the 70s and use 70s technology:tm: to your advantage /// More seriously, WTF (?) we do this because we used to have a bug with unreliable references to these locations, which makes sense since we /// don't have reference stability auto else_side_idx = ctx.size() - 1; ctx.push_back( ByteCodeInterpreter::operand{.element = ByteCodeInterpreter::operator_t::INTERNAL_jump_if, .location = cond.location}); handle(ctx, errors, *cond.on_condition); if (cond.otherwise) { ctx.push_back(ByteCodeInterpreter::operand{.element = script_value{}, .location = cond.location}); auto end_side_idx = ctx.size() - 1; ctx.push_back( ByteCodeInterpreter::operand{.element = ByteCodeInterpreter::operator_t::INTERNAL_jump, .location = cond.location}); ctx[else_side_idx].element = static_cast(ctx.size()) - 1; ctx[else_side_idx].location = cond.location; handle(ctx, errors, *cond.otherwise); ctx[end_side_idx].element = static_cast(ctx.size()) - 1; ctx[end_side_idx].location = cond.location; } else { ctx[else_side_idx].element = static_cast(ctx.size()) - 1; ctx[else_side_idx].location = cond.location; } } template<> void handle(std::vector &ctx, std::vector &errors, ast::while_loop &cond) { auto beforewhile_side_idx = static_cast(ctx.size()) - 1; std::visit([&](auto &v) { handle(ctx, errors, *v); }, cond.condition->contents); ctx.push_back(ByteCodeInterpreter::operand{.element = script_value{}, .location = cond.location}); auto endwhile_side_idx = ctx.size() - 1; ctx.push_back( ByteCodeInterpreter::operand{.element = ByteCodeInterpreter::operator_t::INTERNAL_jump_if, .location = cond.location}); handle(ctx, errors, *cond.on_condition); ctx.push_back( ByteCodeInterpreter::operand{.element = script_value{beforewhile_side_idx}, .location = cond.location}); ctx.push_back( ByteCodeInterpreter::operand{.element = ByteCodeInterpreter::operator_t::INTERNAL_jump, .location = cond.location}); ctx[endwhile_side_idx].element = static_cast(ctx.size()) - 1; ctx[endwhile_side_idx].location = cond.location; } std::vector ByteCodeInterpreter::generate(std::vector &errors, ast::block &tree, bool loop) { std::vector code; handle(code, errors, tree); if (loop) { // Here we have to deal with the quirks of jumping before the increments happens again code.push_back(ByteCodeInterpreter::operand{.element = script_value{-1}, .location = tree.location}); code.push_back( ByteCodeInterpreter::operand{.element = ByteCodeInterpreter::operator_t::INTERNAL_jump, .location = tree.location}); } return code; } }