|
|
- #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();
- }
-
- int32_t var_count() final {
- return variables.size();
- }
-
- void var_clear() final {
- variables.clear();
- }
-
- std::optional<script_value> var_by_idx(int32_t idx) final {
- if(idx >= var_count() || idx < 0) return std::nullopt;
- for(auto& val : variables) {
- if(not idx--) {
- return val.second;
- }
- }
- return std::nullopt;
- }
-
- std::optional<script_value> varname_by_idx(int32_t idx) final {
- if(idx >= var_count() || idx < 0) return std::nullopt;
- for(auto& val : variables) {
- if(not idx--) {
- return val.first;
- }
- }
- return std::nullopt;
- }
-
- 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, bool repeating) 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, repeating);
- }
-
- 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 {}
- };
- }
|