25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 

869 satır
24 KiB

#include "UserScript/interpreter.h"
#include "UserScriptWizardry.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);
}
}
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.emplace_back(v);
return false;
},
[&](variable_tag& v){
execution_stack.emplace_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);
for(auto arity = v.arity; arity != 0; arity--) {
execution_stack.pop_back();
}
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;
}
}
}
}