You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
4.0 KiB

#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));
}
void clear_variables() final {
variables.clear();
}
int32_t op_count() final {
return bytecode.size();
}
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 {}
};
}