#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;
|
|
}
|
|
}
|
|
}
|
|
}
|