#pragma once #include "nasal.h" #include <cstring> #include <sstream> #include <iostream> #include <vector> #include <unordered_map> namespace nasal { enum class vm_type: u8 { /* none-gc object */ vm_none = 0, // error type vm_cnt, // counter for forindex/foreach loop vm_addr, // var* address vm_ret, // return addres(program counter) vm_nil, // nil vm_num, // number /* gc object */ vm_str, // string vm_vec, // vector vm_hash, // hashmap(dict) vm_func, // function(lambda) vm_upval, // upvalue vm_ghost, // ghost type vm_co, // coroutine vm_map, // for globals and namespaces /* mark type range */ vm_type_size_max }; // size of gc object type const u32 gc_type_size = static_cast<u32>(vm_type::vm_type_size_max) - static_cast<u32>(vm_type::vm_str); // basic types struct nas_vec; // vector struct nas_hash; // hashmap(dict) struct nas_func; // function(lambda) struct nas_upval; // upvalue struct nas_ghost; // objects struct nas_co; // coroutine struct nas_map; // mapper // union type struct nas_val; // nas_val includes gc-managed types struct var { public: vm_type type = vm_type::vm_none; union { u64 ret; i64 cnt; f64 num; var* addr; nas_val* gcobj; } val; private: var(vm_type t, u64 pc) {type = t; val.ret = pc;} var(vm_type t, i64 ct) {type = t; val.cnt = ct;} var(vm_type t, f64 n) {type = t; val.num = n;} var(vm_type t, var* p) {type = t; val.addr = p;} var(vm_type t, nas_val* p) {type = t; val.gcobj = p;} public: var() = default; var(const var&) = default; bool operator==(const var& nr) const { return type==nr.type && val.gcobj==nr.val.gcobj; } bool operator!=(const var& nr) const { return type!=nr.type || val.gcobj!=nr.val.gcobj; } // number and string can be translated to each other f64 to_num(); std::string to_str(); bool object_check(const std::string&); public: // create new var object static var none(); static var nil(); static var ret(u64); static var cnt(i64); static var num(f64); static var gcobj(nas_val*); static var addr(var*); public: // get value var* addr() const; u64 ret() const; i64& cnt(); f64 num() const; std::string& str(); nas_vec& vec(); nas_hash& hash(); nas_func& func(); nas_upval& upval(); nas_ghost& ghost(); nas_co& co(); nas_map& map(); public: bool is_none() const { return type==vm_type::vm_none; } bool is_cnt() const { return type==vm_type::vm_cnt; } bool is_addr() const { return type==vm_type::vm_addr; } bool is_ret() const { return type==vm_type::vm_ret; } bool is_nil() const { return type==vm_type::vm_nil; } bool is_num() const { return type==vm_type::vm_num; } bool is_str() const { return type==vm_type::vm_str; } bool is_vec() const { return type==vm_type::vm_vec; } bool is_hash() const { return type==vm_type::vm_hash; } bool is_func() const { return type==vm_type::vm_func; } bool is_upval() const { return type==vm_type::vm_upval; } bool is_ghost() const { return type==vm_type::vm_ghost; } bool is_coroutine() const { return type==vm_type::vm_co; } bool is_map() const { return type==vm_type::vm_map; } }; struct nas_vec { std::vector<var> elems; // mark if this is printed, avoid stack overflow bool printed = false; auto size() const { return elems.size(); } var get_value(const i32); var* get_memory(const i32); }; struct nas_hash { std::unordered_map<std::string, var> elems; // mark if this is printed, avoid stack overflow bool printed = false; auto size() const { return elems.size(); } var get_value(const std::string&); var* get_memory(const std::string&); }; struct nas_func { i64 dynamic_parameter_index; // dynamic parameter name index in hash. u64 entry; // pc will set to entry-1 to call this function u32 parameter_size; // used to load default parameters to a new function u64 local_size; // used to expand memory space for local values on stack std::vector<var> local; // local scope with default value(var) std::vector<var> upval; // closure // parameter table, u32 begins from 1 std::unordered_map<std::string, u32> keys; // dynamic parameter name std::string dynamic_parameter_name; nas_func(): dynamic_parameter_index(-1), entry(0), parameter_size(0), local_size(0), dynamic_parameter_name("") {} void clear(); }; struct nas_upval { public: /* on stack, use these variables */ bool on_stack; u64 size; var* stack_frame_offset; /* not on stack, use this */ std::vector<var> elems; public: nas_upval(): on_stack(true), size(0), stack_frame_offset(nullptr) {} var& operator[](usize n) { return on_stack? stack_frame_offset[n]:elems[n]; } void clear() { on_stack = true; elems.clear(); size = 0; } }; struct nas_ghost { private: using destructor = void (*)(void*); using marker = void (*)(void*, std::vector<var>*); public: std::string type_name; destructor destructor_function; marker gc_mark_function; void* pointer; public: nas_ghost(): type_name(""), destructor_function(nullptr), gc_mark_function(nullptr), pointer(nullptr) {} ~nas_ghost() { clear(); } void set(const std::string&, destructor, marker, void*); void clear(); public: const auto& get_ghost_name() const { return type_name; } public: template<typename T> T* get() { return static_cast<T*>(pointer); } template<typename T> T convert() const { return reinterpret_cast<T>(pointer); } }; struct context { u64 pc = 0; var* localr = nullptr; var* memr = nullptr; var funcr = var::nil(); var upvalr = var::nil(); var* canary = nullptr; var* stack = nullptr; var* top = nullptr; }; struct nas_co { enum class status:u32 { suspended, running, dead }; context ctx; status status; nas_co() { ctx.stack = new var[VM_STACK_DEPTH]; clear(); } ~nas_co() { delete[] ctx.stack; } void clear(); }; struct nas_map { bool printed = false; std::unordered_map<std::string, var*> mapper; public: void clear() { mapper.clear(); } auto size() const { return mapper.size(); } var get_value(const std::string&); var* get_memory(const std::string&); }; struct nas_val { enum class gc_status: u8 { uncollected = 0, collected, found }; gc_status mark; vm_type type; // value type u8 immutable; // used to mark if a string is immutable union { std::string* str; nas_vec* vec; nas_hash* hash; nas_func* func; nas_upval* upval; nas_ghost* obj; nas_co* co; nas_map* map; } ptr; nas_val(vm_type); ~nas_val(); void clear(); }; std::ostream& operator<<(std::ostream&, nas_vec&); std::ostream& operator<<(std::ostream&, nas_hash&); std::ostream& operator<<(std::ostream&, nas_func&); std::ostream& operator<<(std::ostream&, nas_map&); std::ostream& operator<<(std::ostream&, const nas_ghost&); std::ostream& operator<<(std::ostream&, const nas_co&); std::ostream& operator<<(std::ostream&, var&); const var zero = var::num(0); const var one = var::num(1); const var nil = var::nil(); // use to print error log and return error value var nas_err(const std::string&, const std::string&); }