|
|
- #include "UserScript/interpreter.h"
-
- namespace scripting {
- void to_null(script_value& value, auto on_value, auto on_error) {
- if(std::holds_alternative<null>(value)) {
- on_value(std::get<null>(value));
- } else {
- on_error(value);
- }
- }
- void to_int(script_value& value, auto on_value, auto on_error) {
- if(std::holds_alternative<int32_t>(value)) {
- on_value(std::get<int32_t>(value));
- } else {
- on_error(value);
- }
- }
- void to_string(script_value& value, auto on_value, auto on_error) {
- if(std::holds_alternative<std::string>(value)) {
- on_value(std::get<std::string>(value));
- } else {
- on_error(value);
- }
- }
- void to_array(script_value& value, auto on_value, auto on_error) {
- if(std::holds_alternative<array>(value)) {
- on_value(std::get<array>(value));
- } else {
- on_error(value);
- }
- }
-
- namespace wizardry {
- // taken from cppreference: https://en.cppreference.com/w/cpp/utility/variant/visit
- template<class... Ts>
- struct overloaded : Ts ... {
- using Ts::operator()...;
- };
- // explicit deduction guide (not needed as of C++20)
- template<class... Ts>
- overloaded(Ts...) -> overloaded<Ts...>;
- }
-
- bool ByteCodeInterpreter::step(std::optional<script_error>& error) {
- if(instruction_ptr >= bytecode.size()) return true;
-
- auto& curr_op = bytecode[instruction_ptr];
- auto& instr = curr_op.element;
-
- bool ret = std::visit(wizardry::overloaded{
- [&](script_value& v){
- execution_stack.push_back(v);
- return false;
- },
- [&](variable_tag& v){
- execution_stack.push_back(script_variable{.name = v.name});
- return false;
- },
- [&](ByteCodeInterpreter::operator_t& v){
- big_f_ing_switch(curr_op, error);
- return v == ByteCodeInterpreter::operator_t::INTERNAL_jump or v == ByteCodeInterpreter::operator_t::INTERNAL_jump_if;
- },
- [&](ByteCodeInterpreter::function_tag& v){
- if(v.arity > execution_stack.size()) {
- error = script_error{.location = bytecode[instruction_ptr].location, .message = "INTERNAL ERROR: invalid amount of argument found in stack, please warn the devs, this is bad and should never happen"};
- return true;
- }
- auto it = functions.find(v.name);
- if(it == functions.end()) {
- for(auto arity = v.arity; arity != 0; arity--) {
- execution_stack.pop_back();
- }
- execution_stack.push_back(argument{script_value{}});
- // Invalid function is not an error
- return true;
- }
- auto args_in_situ = std::span{execution_stack}.subspan(execution_stack.size() - v.arity);
- std::vector<argument> arguments{args_in_situ.begin(), args_in_situ.end()};
- auto item = (*it).second->apply(this, arguments, error);
- if(item) {
- execution_stack.emplace_back(item.value());
- }
- return true;
- },
-
- }, instr);
- instruction_ptr = instruction_ptr + 1;
- return instruction_ptr >= bytecode.size() || error || ret;
- }
-
- std::unique_ptr<UserScript> prepare_interpreter(const std::string& code) {
- auto script = std::make_unique<ByteCodeInterpreter>();
- script->prepare(code);
- return script;
- }
-
- /// BIG FUCKING SWITCH
-
- void ByteCodeInterpreter::big_f_ing_switch(operand& op, std::optional<script_error>& error) {
- switch (get<ByteCodeInterpreter::operator_t>(op.element)) {
- case operator_t::logical_not: {
- auto v = resolve_and_pop();
- // TODO: strings and arrays to booleans?
- to_int(
- v,
- [&](int32_t &value) {
- execution_stack.push_back(script_value{int32_t(!value)});
- },
- [&](auto &other) {
- to_null(
- other,
- [&](null &) {
- execution_stack.push_back(script_value{int32_t(1)});
- },
- [&](auto &) {
- error = script_error{
- op.location,
- "! operator requires an integer or null"
- };
- execution_stack.push_back(script_value{});
- }
- );
- }
- );
- break;
- }
- case operator_t::binary_not: {
- auto v = resolve_and_pop();
- to_int(
- v,
- [&](int32_t &value) {
- execution_stack.push_back(script_value{int32_t(~value)});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "~ operator requires an integer"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::unary_plus: {
- auto v = resolve_and_pop();
- error = script_error{
- op.location,
- "unary + operator is unimplemented"
- };
- execution_stack.push_back(script_value{});
- break;
- }
- case operator_t::unary_minus: {
- auto rhs = resolve_and_pop();
- error = script_error{
- op.location,
- "unary - operator is unimplemented"
- };
- execution_stack.push_back(script_value{});
- break;
- }
- case operator_t::divide: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- if (value_rhs == 0) {
- error = script_error{
- op.location,
- "Division by zero: / operator requires an non-zero integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- return;
- }// TODO: this should be `value_` versions of the variables
- execution_stack.push_back(script_value{value_lhs / value_rhs});
- },
- [&](auto &other) {
- error = script_error {
- op.location,
- "/ operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "/ operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::modulo: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- if (value_rhs == 0) {
- error = script_error{
- op.location,
- "Division by zero: % operator requires an non-zero integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- return;
- }
- execution_stack.push_back(script_value{value_lhs % value_rhs});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "% operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "% operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::multiply: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- execution_stack.push_back(script_value{value_lhs * value_rhs});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "* operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "* operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::subtract: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- execution_stack.push_back(script_value{value_lhs - value_rhs});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "- operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "- operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::add: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- // TODO: strings and arrays concats?
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- execution_stack.push_back(script_value{value_lhs + value_rhs});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "+ operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "+ operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::bitshift_left: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- // TODO: strings and arrays shifts and rotates?
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- if (value_rhs < 0 or value_rhs > 32) {
- error = script_error{
- op.location,
- "bad shift: shift must be between 0 and 32 bits"
- };
- execution_stack.push_back(script_value{});
- return;
- }
- uint32_t true_lhs = *reinterpret_cast<uint32_t *>(&value_lhs), true_rhs = *reinterpret_cast<uint32_t *>(&value_rhs);
- execution_stack.push_back(script_value{static_cast<int32_t>(true_lhs << true_rhs)});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "<< operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "<< operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::bitshift_right: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- // TODO: strings and arrays shifts and rotates?
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- if (value_rhs < 0 or value_rhs > 32) {
- error = script_error{
- op.location,
- "bad shift: shift must be between 0 and 32 bits"
- };
- execution_stack.push_back(script_value{});
- return;
- }
- uint32_t true_lhs = *reinterpret_cast<uint32_t *>(&lhs), true_rhs = *reinterpret_cast<uint32_t *>(&rhs);
- execution_stack.push_back(script_value{static_cast<int32_t>(true_lhs >> true_rhs)});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- ">> operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- ">> operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::rotate_left: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- // TODO: strings and arrays shifts and rotates?
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- if (value_rhs < 0 or value_rhs > 32) {
- error = script_error{
- op.location,
- "bad rotate: rotate must be between 0 and 32 bits"
- };
- execution_stack.push_back(script_value{});
- return;
- }
- uint32_t true_lhs = *reinterpret_cast<uint32_t *>(&value_lhs), true_rhs = *reinterpret_cast<uint32_t *>(&value_rhs);
- execution_stack.push_back(script_value{static_cast<int32_t>(std::rotl(true_lhs, true_rhs))});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "<<< operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "<<< operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::rotate_right: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- // TODO: strings and arrays shifts and rotates?
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- if (value_rhs < 0 or value_rhs > 32) {
- error = script_error{
- op.location,
- "bad rotate: rotate must be between 0 and 32 bits"
- };
- execution_stack.push_back(script_value{});
- return;
- }
- uint32_t true_lhs = *reinterpret_cast<uint32_t *>(&value_lhs), true_rhs = *reinterpret_cast<uint32_t *>(&value_rhs);
- execution_stack.push_back(script_value{static_cast<int32_t>(std::rotr(true_lhs, true_rhs))});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- ">>> operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- ">>> operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::less_than: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- // TODO: lexicographical strings and arrays shifts comparisons
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- execution_stack.push_back(script_value{value_lhs < value_rhs});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "< operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "< operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::greater_than: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- // TODO: lexicographical strings and arrays shifts comparisons
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- execution_stack.push_back(script_value{value_lhs > value_rhs});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "> operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "> operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::less_or_equal_than: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- // TODO: lexicographical strings and arrays shifts comparisons
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- execution_stack.push_back(script_value{value_lhs <= value_rhs});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "<= operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "<= operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::greater_or_equal_than: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- // TODO: lexicographical strings and arrays shifts comparisons
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- execution_stack.push_back(script_value{value_lhs >= value_rhs});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- ">= operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- ">= operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::equals: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- // TODO: error reporting (got degraded)
- // TODO: compare arrays
- if(rhs.index() != lhs.index()) {
- execution_stack.push_back(script_value{0});
- } else if(holds_alternative<null>(rhs)) {
- execution_stack.push_back(script_value{1});
- } else if(holds_alternative<int32_t>(rhs)) {
- execution_stack.push_back(script_value{get<int32_t>(rhs) == get<int32_t>(lhs)});
- } else if(holds_alternative<std::string>(rhs)) {
- execution_stack.push_back(script_value{get<std::string>(rhs) == get<std::string>(lhs)});
- } else if(holds_alternative<array>(rhs)) {
- execution_stack.push_back(script_value{0});
- //execution_stack.push_back(script_value{get<array>(rhs).value == get<array>(lhs).value});
- }
- break;
- }
- case operator_t::different: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- // TODO: strings and arrays different
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- execution_stack.push_back(script_value{value_lhs != value_rhs});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "!= operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "!= operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::binary_and: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- uint32_t true_lhs = *reinterpret_cast<uint32_t *>(&value_lhs), true_rhs = *reinterpret_cast<uint32_t *>(&value_rhs);
- execution_stack.push_back(script_value{static_cast<int32_t>(true_lhs & true_rhs)});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "& operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "& operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::binary_or: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- uint32_t true_lhs = *reinterpret_cast<uint32_t *>(&value_lhs), true_rhs = *reinterpret_cast<uint32_t *>(&value_rhs);
- execution_stack.push_back(script_value{static_cast<int32_t>(true_lhs | true_rhs)});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "| operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "| operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::binary_xor: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- // TODO: XORing strings maybe?
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- uint32_t true_lhs = *reinterpret_cast<uint32_t *>(&value_lhs), true_rhs = *reinterpret_cast<uint32_t *>(&value_rhs);
- execution_stack.push_back(script_value{static_cast<int32_t>(true_lhs ^ true_rhs)});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "^ operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "^ operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::logical_and: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- // TODO: strings and arrays to booleans?
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- uint32_t true_lhs = *reinterpret_cast<uint32_t *>(&value_lhs), true_rhs = *reinterpret_cast<uint32_t *>(&value_rhs);
- execution_stack.push_back(script_value{static_cast<int32_t>(true_lhs && true_rhs)});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "&& operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "&& operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::logical_or: {
- auto rhs = resolve_and_pop();
- auto lhs = resolve_and_pop();
- // TODO: strings and arrays to booleans?
- to_int(
- rhs,
- [&](int32_t &value_rhs) {
- to_int(
- lhs,
- [&](int32_t &value_lhs) {
- uint32_t true_lhs = *reinterpret_cast<uint32_t *>(&value_lhs), true_rhs = *reinterpret_cast<uint32_t *>(&value_rhs);
- execution_stack.push_back(script_value{static_cast<int32_t>(true_lhs || true_rhs)});
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "|| operator requires an integer as left hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- },
- [&](auto &other) {
- error = script_error{
- op.location,
- "|| operator requires an integer as right hand side"
- };
- execution_stack.push_back(script_value{});
- }
- );
- break;
- }
- case operator_t::INTERNAL_jump: {
- auto location = resolve_and_pop();
- to_int(
- location,
- [&](int32_t &instruction_target) {
- instruction_ptr = instruction_target;
- },
- [&](auto &instruction_target) {
- error = script_error{
- op.location,
- "Jump to invalid location"
- };
- }
- );
- break;
- }
- case operator_t::INTERNAL_jump_if: {
- auto location = resolve_and_pop();
- auto condition = resolve_and_pop();
- // TODO: handle null as the condition
- to_int(
- condition,
- [&](int32_t &condition_value) {
- // CAUTION: the condition is inverted, this should really be called jump_if_not
- // TODO: rename to jump_if_not appropriately
- if (not condition_value) {
- to_int(
- location,
- [&](int32_t &instruction_target) {
- instruction_ptr = instruction_target;
- },
- [&](auto &instruction_target) {
- error = script_error{
- op.location,
- "JumpIf to invalid location "
- };
- }
- );
- }
- },
- [&](auto &instruction_target) {
- error = script_error{
- op.location,
- "Condition is not an integer"
- };
- }
- );
- break;
- }
- case operator_t::INTERNAL_stack_cls: {
- execution_stack.clear();
- break;
- }
- }
- }
- }
|