#pragma once #include #include #include "UserScript.h" #include "UserScript/parser.h" namespace scripting { class ByteCodeInterpreter final : public UserScript { std::map variables; std::map functions; std::vector execution_stack; public: struct function_tag { std::string name; size_t arity; std::shared_ptr location; }; struct variable_tag { std::string name; std::shared_ptr 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 element; std::shared_ptr location; }; std::optional> 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 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) { 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 &error); std::vector generate(std::vector &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 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 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> executeAtOnce(std::string code) final { std::vector 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 maybe_error; instruction_ptr = 0; while (instruction_ptr < bytecode.size()) { step(maybe_error); if (maybe_error) return std::vector({maybe_error.value()}); } auto v = resolve_and_pop(); execution_stack.clear(); return v; } std::vector prepare(std::string code) final { std::vector 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 stepOnce() final { std::optional error; while (not step(error)); return error; } bool step(std::optional &error); ~ByteCodeInterpreter() final {} }; }