#pragma once
|
|
#include <stack>
|
|
#include <map>
|
|
#include "UserScript.h"
|
|
#include "UserScript/parser.h"
|
|
|
|
namespace scripting {
|
|
class ByteCodeInterpreter final : public UserScript {
|
|
std::map<std::string, script_value> variables;
|
|
std::map<std::string, function> functions;
|
|
std::vector<argument> execution_stack;
|
|
|
|
public:
|
|
struct function_tag {
|
|
std::string name;
|
|
size_t arity;
|
|
std::shared_ptr<const code_location> location;
|
|
};
|
|
|
|
struct variable_tag {
|
|
std::string name;
|
|
std::shared_ptr<const code_location> location;
|
|
};
|
|
|
|
enum class operator_t : uint8_t {
|
|
logical_not,
|
|
binary_not,
|
|
unary_plus,
|
|
unary_minus,
|
|
divide,
|
|
modulo,
|
|
multiply,
|
|
subtract,
|
|
add,
|
|
bitshift_left,
|
|
bitshift_right,
|
|
rotate_left,
|
|
rotate_right,
|
|
less_than,
|
|
greater_than,
|
|
less_or_equal_than,
|
|
greater_or_equal_than,
|
|
equals,
|
|
different,
|
|
binary_and,
|
|
binary_or,
|
|
binary_xor,
|
|
logical_and,
|
|
logical_or,
|
|
INTERNAL_jump,
|
|
INTERNAL_jump_if,
|
|
INTERNAL_stack_cls,
|
|
};
|
|
|
|
struct operand {
|
|
std::variant<script_value, function_tag, variable_tag, operator_t> element;
|
|
std::shared_ptr<const code_location> location;
|
|
};
|
|
|
|
std::optional<std::reference_wrapper<script_value>> getValue(const std::string &name) {
|
|
if (auto var = variables.find(name); var != variables.end()) {
|
|
return var->second;
|
|
} else {
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
bool setValue(const std::string &name, script_value value) {
|
|
if (auto var = variables.find(name); var != variables.end()) {
|
|
var->second = value;
|
|
return true;
|
|
} else {
|
|
variables.emplace(std::make_pair(name, value));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::vector<operand> bytecode;
|
|
size_t instruction_ptr;
|
|
|
|
script_value resolve(const std::string &name) final {
|
|
auto it = variables.find(name);
|
|
if (it == variables.end()) {
|
|
return script_value{};
|
|
}
|
|
return (*it).second;
|
|
}
|
|
|
|
script_value resolve_and_pop() {
|
|
if (execution_stack.empty()) return script_value{};
|
|
auto value = std::move(execution_stack.back());
|
|
auto resolved = std::visit([&](auto v) -> script_value {
|
|
if constexpr (std::is_same_v<script_variable, decltype(v)>) {
|
|
auto it = variables.find(v.name);
|
|
if (it == variables.end()) {
|
|
return script_value{};
|
|
}
|
|
return (*it).second;
|
|
} else {
|
|
return v;
|
|
}
|
|
}, value);
|
|
execution_stack.pop_back();
|
|
return resolved;
|
|
}
|
|
|
|
void big_f_ing_switch(operand &op, std::optional<script_error> &error);
|
|
|
|
std::vector<ByteCodeInterpreter::operand>
|
|
generate(std::vector<script_error> &errors, ast::block &tree, bool loop = true);
|
|
|
|
void registerFunction(std::string name, function fn) final {
|
|
functions.insert_or_assign(name, std::move(fn));
|
|
}
|
|
|
|
std::variant<script_value, std::vector<script_error>> executeAtOnce(std::string code) final {
|
|
std::vector<script_error> errors;
|
|
auto lexed = ast::lex(code, errors);
|
|
auto parsed = ast::parse(lexed, errors);
|
|
if (not errors.empty()) return errors;
|
|
bytecode = generate(errors, parsed, false);
|
|
if (not errors.empty()) return errors;
|
|
std::optional<script_error> maybe_error;
|
|
instruction_ptr = 0;
|
|
while (instruction_ptr < bytecode.size()) {
|
|
step(maybe_error);
|
|
if (maybe_error) return std::vector<script_error>({maybe_error.value()});
|
|
}
|
|
auto v = resolve_and_pop();
|
|
execution_stack.clear();
|
|
return v;
|
|
}
|
|
|
|
std::vector<script_error> prepare(std::string code) final {
|
|
std::vector<script_error> errors;
|
|
auto lexed = ast::lex(code, errors);
|
|
auto parsed = ast::parse(lexed, errors);
|
|
if (errors.empty()) {
|
|
bytecode = generate(errors, parsed, true);
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
std::optional<script_error> stepOnce() final {
|
|
std::optional<script_error> error;
|
|
while (not step(error));
|
|
return error;
|
|
}
|
|
|
|
bool step(std::optional<script_error> &error);
|
|
|
|
~ByteCodeInterpreter() final {}
|
|
};
|
|
}
|