diff --git a/CMakeLists.txt b/CMakeLists.txt index ef2d704..151e760 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,6 +36,7 @@ set(NASAL_OBJECT_SOURCE_FILE ${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp ${CMAKE_SOURCE_DIR}/src/nasal_opcode.cpp ${CMAKE_SOURCE_DIR}/src/nasal_parse.cpp + ${CMAKE_SOURCE_DIR}/src/nasal_type.cpp ${CMAKE_SOURCE_DIR}/src/nasal_vm.cpp ${CMAKE_SOURCE_DIR}/src/optimizer.cpp ${CMAKE_SOURCE_DIR}/src/symbol_finder.cpp diff --git a/makefile b/makefile index 8e0c434..7317864 100644 --- a/makefile +++ b/makefile @@ -23,6 +23,7 @@ NASAL_HEADER=\ src/nasal_opcode.h\ src/nasal_parse.h\ src/nasal_vm.h\ + src/nasal_type.h\ src/nasal.h\ src/optimizer.h\ src/symbol_finder.h\ @@ -57,6 +58,7 @@ NASAL_OBJECT=\ build/unix_lib.o\ build/dylib_lib.o\ build/coroutine.o\ + build/nasal_type.o\ build/nasal_vm.o\ build/nasal_dbg.o\ build/repl.o\ @@ -89,7 +91,10 @@ build/repl.o: $(NASAL_HEADER) src/repl.h src/repl.cpp | build build/nasal_err.o: src/nasal.h src/repl.h src/nasal_err.h src/nasal_err.cpp | build $(CXX) $(CXXFLAGS) src/nasal_err.cpp -o build/nasal_err.o -build/nasal_gc.o: src/nasal.h src/nasal_gc.h src/nasal_gc.cpp | build +build/nasal_type.o: src/nasal.h src/nasal_type.h src/nasal_type.cpp | build + $(CXX) $(CXXFLAGS) src/nasal_type.cpp -o build/nasal_type.o + +build/nasal_gc.o: src/nasal.h src/nasal_type.h src/nasal_gc.h src/nasal_gc.cpp | build $(CXX) $(CXXFLAGS) src/nasal_gc.cpp -o build/nasal_gc.o build/nasal_import.o: \ @@ -115,6 +120,7 @@ build/nasal_ast.o: \ build/nasal_builtin.o: \ src/nasal.h\ + src/nasal_type.h\ src/nasal_gc.h\ src/nasal_builtin.h src/nasal_builtin.cpp | build $(CXX) $(CXXFLAGS) src/nasal_builtin.cpp -o build/nasal_builtin.o diff --git a/module/fib.cpp b/module/fib.cpp index 9d570ef..aec5dcb 100644 --- a/module/fib.cpp +++ b/module/fib.cpp @@ -2,6 +2,8 @@ #include #include "../src/nasal.h" +#include "../src/nasal_type.h" +#include "../src/nasal_gc.h" namespace nasal { namespace fib_module { diff --git a/module/keyboard.cpp b/module/keyboard.cpp index 0cdeb72..8586c58 100644 --- a/module/keyboard.cpp +++ b/module/keyboard.cpp @@ -1,4 +1,6 @@ #include "../src/nasal.h" +#include "../src/nasal_type.h" +#include "../src/nasal_gc.h" #include #ifndef _MSC_VER diff --git a/module/makefile b/module/makefile index 5c9ab1c..d80702e 100644 --- a/module/makefile +++ b/module/makefile @@ -3,8 +3,8 @@ dynamic_libs_so = libfib.so libkey.so libnasock.so libmat.so dynamic_libs_dll = libfib.dll libkey.dll libnasock.dll libmat.dll -used_header = ../src/nasal.h ../src/nasal_gc.h -used_object = ../build/nasal_misc.o ../build/nasal_gc.o +used_header = ../src/nasal.h ../src/nasal_type.h ../src/nasal_gc.h +used_object = ../build/nasal_misc.o ../build/nasal_type.o ../build/nasal_gc.o STD = c++17 diff --git a/module/matrix.cpp b/module/matrix.cpp index 8f67f13..5481af4 100644 --- a/module/matrix.cpp +++ b/module/matrix.cpp @@ -1,4 +1,6 @@ #include "../src/nasal.h" +#include "../src/nasal_type.h" +#include "../src/nasal_gc.h" #include namespace nasal { diff --git a/module/nasocket.cpp b/module/nasocket.cpp index 6208321..2ac1069 100644 --- a/module/nasocket.cpp +++ b/module/nasocket.cpp @@ -1,4 +1,6 @@ #include "../src/nasal.h" +#include "../src/nasal_type.h" +#include "../src/nasal_gc.h" #ifndef _MSC_VER #include diff --git a/src/bits_lib.cpp b/src/bits_lib.cpp index a4000d8..f33c79b 100644 --- a/src/bits_lib.cpp +++ b/src/bits_lib.cpp @@ -41,7 +41,7 @@ var builtin_fld(var* local, gc& ngc) { var str = local[1]; var startbit = local[2]; var length = local[3]; - if (str.type!=vm_str || str.val.gcobj->unmut) { + if (str.type!=vm_str || str.val.gcobj->unmutable) { return nas_err("fld", "\"str\" must be mutable string"); } if (startbit.type!=vm_num || length.type!=vm_num) { @@ -70,7 +70,7 @@ var builtin_sfld(var* local, gc& ngc) { var str = local[1]; var startbit = local[2]; var length = local[3]; - if (str.type!=vm_str || str.val.gcobj->unmut) { + if (str.type!=vm_str || str.val.gcobj->unmutable) { return nas_err("sfld", "\"str\" must be mutable string"); } if (startbit.type!=vm_num || length.type!=vm_num) { @@ -103,7 +103,7 @@ var builtin_setfld(var* local, gc& ngc) { var startbit = local[2]; var length = local[3]; var value = local[4]; - if (str.type!=vm_str || str.val.gcobj->unmut) { + if (str.type!=vm_str || str.val.gcobj->unmutable) { return nas_err("setfld", "\"str\" must be mutable string"); } if (startbit.type!=vm_num || length.type!=vm_num || value.type!=vm_num) { diff --git a/src/io_lib.cpp b/src/io_lib.cpp index 406ab17..63c5dee 100644 --- a/src/io_lib.cpp +++ b/src/io_lib.cpp @@ -42,7 +42,7 @@ var builtin_exists(var* local, gc& ngc) { if (local[1].type!=vm_str) { return zero; } - return access(local[1].str().c_str(), F_OK)!=-1?one:zero; + return access(local[1].str().c_str(), F_OK)!=-1? one:zero; } var builtin_open(var* local, gc& ngc) { @@ -79,7 +79,7 @@ var builtin_read(var* local, gc& ngc) { if (!fd.objchk(file_type_name)) { return nas_err("read", "not a valid filehandle"); } - if (buf.type!=vm_str || buf.val.gcobj->unmut) { + if (buf.type!=vm_str || buf.val.gcobj->unmutable) { return nas_err("read", "\"buf\" must be mutable string"); } if (len.type!=vm_num) { @@ -94,7 +94,7 @@ var builtin_read(var* local, gc& ngc) { } f64 res = fread(buff, 1, len.num(), static_cast(fd.obj().pointer)); buf.str() = buff; - buf.val.gcobj->unmut = true; + buf.val.gcobj->unmutable = true; delete []buff; return var::num(res); } diff --git a/src/io_lib.h b/src/io_lib.h index 6740984..3ea800d 100644 --- a/src/io_lib.h +++ b/src/io_lib.h @@ -6,6 +6,12 @@ #include +#ifndef _MSC_VER +#include +#else +#include +#endif + #ifdef _MSC_VER #define F_OK 0 // fuck msc #endif diff --git a/src/main.cpp b/src/main.cpp index e8ec9b1..1867f67 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,6 @@ #include "nasal.h" +#include "nasal_type.h" +#include "nasal_gc.h" #include "nasal_err.h" #include "nasal_lexer.h" #include "nasal_ast.h" diff --git a/src/math_lib.cpp b/src/math_lib.cpp index 2804e1d..e751636 100644 --- a/src/math_lib.cpp +++ b/src/math_lib.cpp @@ -57,7 +57,7 @@ var builtin_atan2(var* local, gc& ngc) { var builtin_isnan(var* local, gc& ngc) { var x = local[1]; - return (x.type==vm_num && std::isnan(x.num()))?one:zero; + return (x.type==vm_num && std::isnan(x.num()))? one:zero; } nasal_builtin_table math_lib_native[] = { diff --git a/src/nasal.h b/src/nasal.h index 7b43271..6ad8084 100644 --- a/src/nasal.h +++ b/src/nasal.h @@ -9,7 +9,6 @@ #include #include #include -#include // abbreviation of some useful basic type using i32 = std::int32_t; @@ -54,8 +53,6 @@ f64 dec2f(const char*); f64 str2num(const char*); i32 utf8_hdchk(const char); std::string chrhex(const char); -std::string rawstr(const std::string&, const usize maxlen=0); +std::string rawstr(const std::string&, const usize maxlen = 0); -} - -#include "nasal_gc.h" +} \ No newline at end of file diff --git a/src/nasal_builtin.h b/src/nasal_builtin.h index c436a52..cf7d6ea 100644 --- a/src/nasal_builtin.h +++ b/src/nasal_builtin.h @@ -1,8 +1,13 @@ #pragma once #include "nasal.h" +#include "nasal_type.h" #include "nasal_gc.h" +#ifdef _WIN32 +#include +#endif + #ifdef _MSC_VER #pragma warning (disable:4566) // i know i'm using utf-8, fuck you #pragma warning (disable:4244) diff --git a/src/nasal_gc.cpp b/src/nasal_gc.cpp index 0dda3b8..688feb4 100644 --- a/src/nasal_gc.cpp +++ b/src/nasal_gc.cpp @@ -2,348 +2,6 @@ namespace nasal { -var nas_vec::get_val(const i32 n) { - i32 size = elems.size(); - if (n<-size || n>=size) { - return var::none(); - } - return elems[n>=0? n:n+size]; -} - -var* nas_vec::get_mem(const i32 n) { - i32 size = elems.size(); - if (n<-size || n>=size) { - return nullptr; - } - return &elems[n>=0? n:n+size]; -} - -std::ostream& operator<<(std::ostream& out, nas_vec& vec) { - if (!vec.elems.size() || vec.printed) { - out << (vec.elems.size()? "[..]":"[]"); - return out; - } - vec.printed = true; - usize iter = 0, size = vec.elems.size(); - out << "["; - for(auto& i:vec.elems) { - out << i << ",]"[(++iter)==size]; - } - vec.printed = false; - return out; -} - -var nas_hash::get_val(const std::string& key) { - if (elems.count(key)) { - return elems.at(key); - } else if (!elems.count("parents")) { - return var::none(); - } - var ret = var::none(); - var val = elems.at("parents"); - if (val.type!=vm_vec) { - return ret; - } - for(auto& i : val.vec().elems) { - if (i.type==vm_hash) { - ret = i.hash().get_val(key); - } - if (ret.type!=vm_none) { - return ret; - } - } - return ret; -} - -var* nas_hash::get_mem(const std::string& key) { - if (elems.count(key)) { - return &elems.at(key); - } else if (!elems.count("parents")) { - return nullptr; - } - var* addr = nullptr; - var val = elems.at("parents"); - if (val.type!=vm_vec) { - return addr; - } - for(auto& i : val.vec().elems) { - if (i.type==vm_hash) { - addr = i.hash().get_mem(key); - } - if (addr) { - return addr; - } - } - return addr; -} - -std::ostream& operator<<(std::ostream& out, nas_hash& hash) { - if (!hash.elems.size() || hash.printed) { - out << (hash.elems.size()? "{..}":"{}"); - return out; - } - hash.printed = true; - usize iter = 0, size = hash.elems.size(); - out << "{"; - for(auto& i : hash.elems) { - out << i.first << ":" << i.second << ",}"[(++iter)==size]; - } - hash.printed = false; - return out; -} - -void nas_func::clear() { - dynamic_parameter_index = -1; - local.clear(); - upval.clear(); - keys.clear(); -} - -void nas_ghost::set( - const std::string& ghost_type_name, - destructor destructor_pointer, - void* ghost_pointer) { - type_name = ghost_type_name; - destructor_function = destructor_pointer; - pointer = ghost_pointer; -} - -void nas_ghost::clear() { - // do nothing if pointer is null - if (!pointer) { - return; - } - - // do clear pointer if destructor function pointer is null - if (!destructor_function) { - type_name = ""; - pointer = nullptr; - return; - } - - // do destruction - destructor_function(pointer); - type_name = ""; - pointer = nullptr; - destructor_function = nullptr; -} - -std::ostream& operator<<(std::ostream& out, const nas_ghost& ghost) { - out << "(ghost.pointer) << std::dec << ">"; - return out; -} - -void nas_co::clear() { - if (!ctx.stack) { - return; - } - for(u32 i = 0; i(&co) << std::dec << ">"; - return out; -} - -var nas_map::get_val(const std::string& key) { - if (mapper.count(key)) { - return *mapper.at(key); - } - return var::none(); -} - -var* nas_map::get_mem(const std::string& key) { - if (mapper.count(key)) { - return mapper.at(key); - } - return nullptr; -} - -std::ostream& operator<<(std::ostream& out, nas_map& mp) { - if (!mp.mapper.size() || mp.printed) { - out << (mp.mapper.size()? "{..}":"{}"); - return out; - } - mp.printed = true; - usize iter = 0, size = mp.mapper.size(); - out << "{"; - for(auto& i : mp.mapper) { - out << i.first << ":" << *i.second << ",}"[(++iter)==size]; - } - mp.printed = false; - return out; -} - -nas_val::nas_val(u8 val_type) { - mark = gc_status::collected; - type = val_type; - unmut = 0; - switch(val_type) { - case vm_str: ptr.str = new std::string; break; - case vm_vec: ptr.vec = new nas_vec; break; - case vm_hash: ptr.hash = new nas_hash; break; - case vm_func: ptr.func = new nas_func; break; - case vm_upval: ptr.upval = new nas_upval; break; - case vm_obj: ptr.obj = new nas_ghost; break; - case vm_co: ptr.co = new nas_co; break; - case vm_map: ptr.map = new nas_map; break; - } -} - -nas_val::~nas_val() { - switch(type) { - case vm_str: delete ptr.str; break; - case vm_vec: delete ptr.vec; break; - case vm_hash: delete ptr.hash; break; - case vm_func: delete ptr.func; break; - case vm_upval:delete ptr.upval;break; - case vm_obj: delete ptr.obj; break; - case vm_co: delete ptr.co; break; - case vm_map: delete ptr.map; break; - } - type=vm_nil; -} - -void nas_val::clear() { - switch(type) { - case vm_str: ptr.str->clear(); break; - case vm_vec: ptr.vec->elems.clear(); break; - case vm_hash: ptr.hash->elems.clear();break; - case vm_func: ptr.func->clear(); break; - case vm_upval:ptr.upval->clear(); break; - case vm_obj: ptr.obj->clear(); break; - case vm_co: ptr.co->clear(); break; - case vm_map: ptr.map->clear(); break; - } -} - -f64 var::tonum() { - return type!=vm_str? val.num:str2num(str().c_str()); -} - -std::string var::tostr() { - if (type==vm_str) { - return str(); - } else if (type==vm_num) { - std::string tmp=std::to_string(num()); - tmp.erase(tmp.find_last_not_of('0')+1, std::string::npos); - tmp.erase(tmp.find_last_not_of('.')+1, std::string::npos); - return tmp; - } - return ""; -} - -std::ostream& operator<<(std::ostream& out, var& ref) { - switch(ref.type) { - case vm_none: out << "undefined"; break; - case vm_nil: out << "nil"; break; - case vm_num: out << ref.val.num; break; - case vm_str: out << ref.str(); break; - case vm_vec: out << ref.vec(); break; - case vm_hash: out << ref.hash(); break; - case vm_func: out << "func(..) {..}"; break; - case vm_obj: out << ref.obj(); break; - case vm_co: out << ref.co(); break; - case vm_map: out << ref.map(); break; - } - return out; -} - -bool var::objchk(const std::string& name) { - return type==vm_obj && obj().type_name==name && obj().pointer; -} - -var var::none() { - return {vm_none, static_cast(0)}; -} - -var var::nil() { - return {vm_nil, static_cast(0)}; -} - -var var::ret(u32 pc) { - return {vm_ret, pc}; -} - -var var::cnt(i64 n) { - return {vm_cnt, n}; -} - -var var::num(f64 n) { - return {vm_num, n}; -} - -var var::gcobj(nas_val* p) { - return {p->type, p}; -} - -var var::addr(var* p) { - return {vm_addr, p}; -} - -var* var::addr() { - return val.addr; -} - -u32 var::ret() { - return val.ret; -} - -i64& var::cnt() { - return val.cnt; -} - -f64 var::num() { - return val.num; -} - -std::string& var::str() { - return *val.gcobj->ptr.str; -} - -nas_vec& var::vec() { - return *val.gcobj->ptr.vec; -} - -nas_hash& var::hash() { - return *val.gcobj->ptr.hash; -} - -nas_func& var::func() { - return *val.gcobj->ptr.func; -} - -nas_upval& var::upval() { - return *val.gcobj->ptr.upval; -} - -nas_ghost& var::obj() { - return *val.gcobj->ptr.obj; -} - -nas_co& var::co() { - return *val.gcobj->ptr.co; -} - -nas_map& var::map() { - return *val.gcobj->ptr.map; -} - void gc::do_mark_sweep() { using clk = std::chrono::high_resolution_clock; auto begin = clk::now(); @@ -556,7 +214,7 @@ void gc::init( continue; } strs[i] = var::gcobj(new nas_val(vm_str)); - strs[i].val.gcobj->unmut = 1; + strs[i].val.gcobj->unmutable = 1; strs[i].str() = constant_strings[i]; } @@ -568,7 +226,7 @@ void gc::init( continue; } env_argv[i] = var::gcobj(new nas_val(vm_str)); - env_argv[i].val.gcobj->unmut = 1; + env_argv[i].val.gcobj->unmutable = 1; env_argv[i].str() = argv[i]; } } @@ -724,9 +382,4 @@ void gc::ctxreserve() { cort = nullptr; } -var nas_err(const std::string& error_function_name, const std::string& info) { - std::cerr << "[vm] " << error_function_name << ": " << info << "\n"; - return var::none(); -} - } diff --git a/src/nasal_gc.h b/src/nasal_gc.h index 906700f..f5b4497 100644 --- a/src/nasal_gc.h +++ b/src/nasal_gc.h @@ -7,288 +7,18 @@ #pragma warning (disable:4102) #endif -#ifndef _MSC_VER -#include -#include -#else -#include -#include -#endif - -#ifdef _WIN32 -#include -#else -#include -#endif - #include #include -#include #include -#include #include #include #include #include "nasal.h" +#include "nasal_type.h" namespace nasal { -enum vm_type:u8 { - /* none-gc object */ - vm_none = 0, - vm_cnt, - vm_addr, - vm_ret, - vm_nil, - vm_num, - /* gc object */ - vm_str, - vm_vec, - vm_hash, - vm_func, - vm_upval, - vm_obj, - vm_co, - vm_map // for globals and namespaces -}; - -const u32 gc_type_size = vm_map-vm_str+1; - -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 -struct nas_val; // nas_val includes gc-managed types - -struct var { -public: - u8 type = vm_none; - union { - u32 ret; - i64 cnt; - f64 num; - var* addr; - nas_val* gcobj; - } val; - -private: - var(u8 t, u32 pc) {type = t; val.ret = pc;} - var(u8 t, i64 ct) {type = t; val.cnt = ct;} - var(u8 t, f64 n) {type = t; val.num = n;} - var(u8 t, var* p) {type = t; val.addr = p;} - var(u8 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 tonum(); - std::string tostr(); - bool objchk(const std::string&); - - // create new var object - static var none(); - static var nil(); - static var ret(u32); - static var cnt(i64); - static var num(f64); - static var gcobj(nas_val*); - static var addr(var*); - - // get content - var* addr(); - u32 ret(); - i64& cnt(); - f64 num(); - std::string& str(); - nas_vec& vec(); - nas_hash& hash(); - nas_func& func(); - nas_upval& upval(); - nas_ghost& obj(); - nas_co& co(); - nas_map& map(); -}; - -struct nas_vec { - std::vector elems; - - // mark if this is printed, avoid stackoverflow - bool printed; - - nas_vec():printed(false) {} - usize size() const {return elems.size();} - var get_val(const i32); - var* get_mem(const i32); -}; - -struct nas_hash { - std::unordered_map elems; - - // mark if this is printed, avoid stackoverflow - bool printed; - - nas_hash(): printed(false) {} - usize size() const {return elems.size();} - var get_val(const std::string&); - var* get_mem(const std::string&); -}; - -struct nas_func { - i32 dynamic_parameter_index; // dynamic parameter name index in hash. - u32 entry; // pc will set to entry-1 to call this function - u32 parameter_size; // used to load default parameters to a new function - u32 local_size; // used to expand memory space for local values on stack - std::vector local; // local scope with default value(var) - std::vector upval; // closure - - // parameter table, u32 begins from 1 - std::unordered_map keys; - - nas_func(): - dynamic_parameter_index(-1), entry(0), - parameter_size(0), local_size(0) {} - void clear(); -}; - -struct nas_upval { -public: - /* on stack, use these variables */ - bool on_stack; - u32 size; - var* stk; - - /* not on stack, use this */ - std::vector elems; - -public: - nas_upval(): on_stack(true), size(0), stk(nullptr) {} - - var& operator[](usize n) { - return on_stack? stk[n]:elems[n]; - } - - void clear() { - on_stack = true; - elems.clear(); - size = 0; - } -}; - -struct nas_ghost { -private: - using destructor = void (*)(void*); - -public: - std::string type_name; - destructor destructor_function; - void* pointer; - -public: - nas_ghost(): - type_name(""), destructor_function(nullptr), pointer(nullptr) {} - ~nas_ghost() {clear();} - void set(const std::string&, destructor, void*); - void clear(); - -public: - const std::string& get_ghost_name() const { - return type_name; - } -}; - -struct context { - u32 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[STACK_DEPTH]; - clear(); - } - ~nas_co() { - delete[] ctx.stack; - } - void clear(); -}; - -struct nas_map { - bool printed = false; - std::unordered_map mapper; - - nas_map() {} - void clear() { - mapper.clear(); - } - - var get_val(const std::string&); - var* get_mem(const std::string&); -}; - -struct nas_val { - enum class gc_status:u8 { - uncollected = 0, - collected, - found - }; - - gc_status mark; - u8 type; // value type - u8 unmut; // used to mark if a string is unmutable - 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(u8); - ~nas_val(); - void clear(); -}; - -std::ostream& operator<<(std::ostream&, nas_vec&); -std::ostream& operator<<(std::ostream&, nas_hash&); -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(); - struct gc { /* main context temporary storage */ context main_context; @@ -370,7 +100,7 @@ public: var newstr(const char* buff) { var s = alloc(vm_str); - s.str() = buff; + s.str() = std::string(buff); return s; } @@ -381,9 +111,6 @@ public: } }; -// use to print error log and return error value -var nas_err(const std::string&, const std::string&); - // module function type typedef var (*module_func)(var*, usize, gc*); diff --git a/src/nasal_type.cpp b/src/nasal_type.cpp new file mode 100644 index 0000000..829020a --- /dev/null +++ b/src/nasal_type.cpp @@ -0,0 +1,352 @@ +#include "nasal_type.h" + +namespace nasal { + +var nas_vec::get_val(const i32 n) { + i32 size = elems.size(); + if (n<-size || n>=size) { + return var::none(); + } + return elems[n>=0? n:n+size]; +} + +var* nas_vec::get_mem(const i32 n) { + i32 size = elems.size(); + if (n<-size || n>=size) { + return nullptr; + } + return &elems[n>=0? n:n+size]; +} + +std::ostream& operator<<(std::ostream& out, nas_vec& vec) { + if (!vec.elems.size() || vec.printed) { + out << (vec.elems.size()? "[..]":"[]"); + return out; + } + vec.printed = true; + usize iter = 0, size = vec.elems.size(); + out << "["; + for(auto& i:vec.elems) { + out << i << ",]"[(++iter)==size]; + } + vec.printed = false; + return out; +} + +var nas_hash::get_val(const std::string& key) { + if (elems.count(key)) { + return elems.at(key); + } else if (!elems.count("parents")) { + return var::none(); + } + var ret = var::none(); + var val = elems.at("parents"); + if (val.type!=vm_vec) { + return ret; + } + for(auto& i : val.vec().elems) { + if (i.type==vm_hash) { + ret = i.hash().get_val(key); + } + if (ret.type!=vm_none) { + return ret; + } + } + return ret; +} + +var* nas_hash::get_mem(const std::string& key) { + if (elems.count(key)) { + return &elems.at(key); + } else if (!elems.count("parents")) { + return nullptr; + } + var* addr = nullptr; + var val = elems.at("parents"); + if (val.type!=vm_vec) { + return addr; + } + for(auto& i : val.vec().elems) { + if (i.type==vm_hash) { + addr = i.hash().get_mem(key); + } + if (addr) { + return addr; + } + } + return addr; +} + +std::ostream& operator<<(std::ostream& out, nas_hash& hash) { + if (!hash.elems.size() || hash.printed) { + out << (hash.elems.size()? "{..}":"{}"); + return out; + } + hash.printed = true; + usize iter = 0, size = hash.elems.size(); + out << "{"; + for(auto& i : hash.elems) { + out << i.first << ":" << i.second << ",}"[(++iter)==size]; + } + hash.printed = false; + return out; +} + +void nas_func::clear() { + dynamic_parameter_index = -1; + local.clear(); + upval.clear(); + keys.clear(); +} + +void nas_ghost::set( + const std::string& ghost_type_name, + destructor destructor_pointer, + void* ghost_pointer) { + type_name = ghost_type_name; + destructor_function = destructor_pointer; + pointer = ghost_pointer; +} + +void nas_ghost::clear() { + // do nothing if pointer is null + if (!pointer) { + return; + } + + // do clear pointer if destructor function pointer is null + if (!destructor_function) { + type_name = ""; + pointer = nullptr; + return; + } + + // do destruction + destructor_function(pointer); + type_name = ""; + pointer = nullptr; + destructor_function = nullptr; +} + +std::ostream& operator<<(std::ostream& out, const nas_ghost& ghost) { + out << "(ghost.pointer) << std::dec << ">"; + return out; +} + +void nas_co::clear() { + if (!ctx.stack) { + return; + } + for(u32 i = 0; i(&co) << std::dec << ">"; + return out; +} + +var nas_map::get_val(const std::string& key) { + if (mapper.count(key)) { + return *mapper.at(key); + } + return var::none(); +} + +var* nas_map::get_mem(const std::string& key) { + if (mapper.count(key)) { + return mapper.at(key); + } + return nullptr; +} + +std::ostream& operator<<(std::ostream& out, nas_map& mp) { + if (!mp.mapper.size() || mp.printed) { + out << (mp.mapper.size()? "{..}":"{}"); + return out; + } + mp.printed = true; + usize iter = 0, size = mp.mapper.size(); + out << "{"; + for(auto& i : mp.mapper) { + out << i.first << ":" << *i.second << ",}"[(++iter)==size]; + } + mp.printed = false; + return out; +} + +nas_val::nas_val(u8 val_type) { + mark = gc_status::collected; + type = val_type; + unmutable = 0; + switch(val_type) { + case vm_str: ptr.str = new std::string; break; + case vm_vec: ptr.vec = new nas_vec; break; + case vm_hash: ptr.hash = new nas_hash; break; + case vm_func: ptr.func = new nas_func; break; + case vm_upval: ptr.upval = new nas_upval; break; + case vm_obj: ptr.obj = new nas_ghost; break; + case vm_co: ptr.co = new nas_co; break; + case vm_map: ptr.map = new nas_map; break; + } +} + +nas_val::~nas_val() { + switch(type) { + case vm_str: delete ptr.str; break; + case vm_vec: delete ptr.vec; break; + case vm_hash: delete ptr.hash; break; + case vm_func: delete ptr.func; break; + case vm_upval:delete ptr.upval;break; + case vm_obj: delete ptr.obj; break; + case vm_co: delete ptr.co; break; + case vm_map: delete ptr.map; break; + } + type=vm_nil; +} + +void nas_val::clear() { + switch(type) { + case vm_str: ptr.str->clear(); break; + case vm_vec: ptr.vec->elems.clear(); break; + case vm_hash: ptr.hash->elems.clear();break; + case vm_func: ptr.func->clear(); break; + case vm_upval:ptr.upval->clear(); break; + case vm_obj: ptr.obj->clear(); break; + case vm_co: ptr.co->clear(); break; + case vm_map: ptr.map->clear(); break; + } +} + +f64 var::tonum() { + return type!=vm_str? val.num:str2num(str().c_str()); +} + +std::string var::tostr() { + if (type==vm_str) { + return str(); + } else if (type==vm_num) { + std::string tmp=std::to_string(num()); + tmp.erase(tmp.find_last_not_of('0')+1, std::string::npos); + tmp.erase(tmp.find_last_not_of('.')+1, std::string::npos); + return tmp; + } + return ""; +} + +std::ostream& operator<<(std::ostream& out, var& ref) { + switch(ref.type) { + case vm_none: out << "undefined"; break; + case vm_nil: out << "nil"; break; + case vm_num: out << ref.val.num; break; + case vm_str: out << ref.str(); break; + case vm_vec: out << ref.vec(); break; + case vm_hash: out << ref.hash(); break; + case vm_func: out << "func(..) {..}"; break; + case vm_obj: out << ref.obj(); break; + case vm_co: out << ref.co(); break; + case vm_map: out << ref.map(); break; + } + return out; +} + +bool var::objchk(const std::string& name) { + return type==vm_obj && obj().type_name==name && obj().pointer; +} + +var var::none() { + return {vm_none, static_cast(0)}; +} + +var var::nil() { + return {vm_nil, static_cast(0)}; +} + +var var::ret(u32 pc) { + return {vm_ret, pc}; +} + +var var::cnt(i64 n) { + return {vm_cnt, n}; +} + +var var::num(f64 n) { + return {vm_num, n}; +} + +var var::gcobj(nas_val* p) { + return {p->type, p}; +} + +var var::addr(var* p) { + return {vm_addr, p}; +} + +var* var::addr() { + return val.addr; +} + +u32 var::ret() { + return val.ret; +} + +i64& var::cnt() { + return val.cnt; +} + +f64 var::num() { + return val.num; +} + +std::string& var::str() { + return *val.gcobj->ptr.str; +} + +nas_vec& var::vec() { + return *val.gcobj->ptr.vec; +} + +nas_hash& var::hash() { + return *val.gcobj->ptr.hash; +} + +nas_func& var::func() { + return *val.gcobj->ptr.func; +} + +nas_upval& var::upval() { + return *val.gcobj->ptr.upval; +} + +nas_ghost& var::obj() { + return *val.gcobj->ptr.obj; +} + +nas_co& var::co() { + return *val.gcobj->ptr.co; +} + +nas_map& var::map() { + return *val.gcobj->ptr.map; +} + +var nas_err(const std::string& error_function_name, const std::string& info) { + std::cerr << "[vm] " << error_function_name << ": " << info << "\n"; + return var::none(); +} + +} \ No newline at end of file diff --git a/src/nasal_type.h b/src/nasal_type.h new file mode 100644 index 0000000..b3f6151 --- /dev/null +++ b/src/nasal_type.h @@ -0,0 +1,272 @@ +#pragma once + +#include "nasal.h" + +#include +#include + +namespace nasal { + +enum 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_obj, // ghost type + vm_co, // coroutine + vm_map // for globals and namespaces +}; + +// size of gc object type +const u32 gc_type_size = vm_map-vm_str+1; + +// 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: + u8 type = vm_none; + union { + u32 ret; + i64 cnt; + f64 num; + var* addr; + nas_val* gcobj; + } val; + +private: + var(u8 t, u32 pc) {type = t; val.ret = pc;} + var(u8 t, i64 ct) {type = t; val.cnt = ct;} + var(u8 t, f64 n) {type = t; val.num = n;} + var(u8 t, var* p) {type = t; val.addr = p;} + var(u8 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 tonum(); + std::string tostr(); + bool objchk(const std::string&); + + // create new var object + static var none(); + static var nil(); + static var ret(u32); + static var cnt(i64); + static var num(f64); + static var gcobj(nas_val*); + static var addr(var*); + + // get value + var* addr(); + u32 ret(); + i64& cnt(); + f64 num(); + std::string& str(); + nas_vec& vec(); + nas_hash& hash(); + nas_func& func(); + nas_upval& upval(); + nas_ghost& obj(); + nas_co& co(); + nas_map& map(); +}; + +struct nas_vec { + std::vector elems; + + // mark if this is printed, avoid stackoverflow + bool printed; + + nas_vec():printed(false) {} + usize size() const {return elems.size();} + var get_val(const i32); + var* get_mem(const i32); +}; + +struct nas_hash { + std::unordered_map elems; + + // mark if this is printed, avoid stackoverflow + bool printed; + + nas_hash(): printed(false) {} + usize size() const {return elems.size();} + var get_val(const std::string&); + var* get_mem(const std::string&); +}; + +struct nas_func { + i32 dynamic_parameter_index; // dynamic parameter name index in hash. + u32 entry; // pc will set to entry-1 to call this function + u32 parameter_size; // used to load default parameters to a new function + u32 local_size; // used to expand memory space for local values on stack + std::vector local; // local scope with default value(var) + std::vector upval; // closure + + // parameter table, u32 begins from 1 + std::unordered_map keys; + + nas_func(): + dynamic_parameter_index(-1), entry(0), + parameter_size(0), local_size(0) {} + void clear(); +}; + +struct nas_upval { +public: + /* on stack, use these variables */ + bool on_stack; + u32 size; + var* stack_frame_offset; + + /* not on stack, use this */ + std::vector 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*); + +public: + std::string type_name; + destructor destructor_function; + void* pointer; + +public: + nas_ghost(): + type_name(""), destructor_function(nullptr), pointer(nullptr) {} + ~nas_ghost() {clear();} + void set(const std::string&, destructor, void*); + void clear(); + +public: + const std::string& get_ghost_name() const { + return type_name; + } +}; + +struct context { + u32 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[STACK_DEPTH]; + clear(); + } + ~nas_co() { + delete[] ctx.stack; + } + void clear(); +}; + +struct nas_map { + bool printed = false; + std::unordered_map mapper; + + nas_map() {} + void clear() { + mapper.clear(); + } + + var get_val(const std::string&); + var* get_mem(const std::string&); +}; + +struct nas_val { + enum class gc_status:u8 { + uncollected = 0, + collected, + found + }; + + gc_status mark; + u8 type; // value type + u8 unmutable; // used to mark if a string is unmutable + 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(u8); + ~nas_val(); + void clear(); +}; + +std::ostream& operator<<(std::ostream&, nas_vec&); +std::ostream& operator<<(std::ostream&, nas_hash&); +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&); + +} \ No newline at end of file diff --git a/src/nasal_vm.h b/src/nasal_vm.h index 278cb3d..930b845 100644 --- a/src/nasal_vm.h +++ b/src/nasal_vm.h @@ -262,7 +262,7 @@ inline void vm::o_newf() { // so this size & stk setting has no problem var upval = (ctx.upvalr.type==vm_nil)? ngc.alloc(vm_upval):ctx.upvalr; upval.upval().size = ctx.funcr.func().local_size; - upval.upval().stk = ctx.localr; + upval.upval().stack_frame_offset = ctx.localr; func.upval.push_back(upval); ctx.upvalr = upval; } @@ -507,7 +507,7 @@ inline void vm::o_neq() { #define op_cmp(type)\ --ctx.top;\ - ctx.top[0] = (ctx.top[0].tonum() type ctx.top[1].tonum())?one:zero; + ctx.top[0] = (ctx.top[0].tonum() type ctx.top[1].tonum())? one:zero; inline void vm::o_less() {op_cmp(<);} inline void vm::o_leq() {op_cmp(<=);} @@ -515,7 +515,7 @@ inline void vm::o_grt() {op_cmp(>);} inline void vm::o_geq() {op_cmp(>=);} #define op_cmp_const(type)\ - ctx.top[0] = (ctx.top[0].tonum() type cnum[imm[ctx.pc]])?one:zero; + ctx.top[0] = (ctx.top[0].tonum() type cnum[imm[ctx.pc]])? one:zero; inline void vm::o_lessc() {op_cmp_const(<);} inline void vm::o_leqc() {op_cmp_const(<=);} diff --git a/tools/file2ppm.nas b/tools/file2ppm.nas new file mode 100644 index 0000000..b2498dc --- /dev/null +++ b/tools/file2ppm.nas @@ -0,0 +1,28 @@ + +var ppm = func(filename, buffer) { + # P3 use ASCII number + # P6 use binary character + var width = 256; + var height = int(size(buffer)/3/width); # ppm use 3 chars for one pixel + println("width ", width, ", height ", height); + + var fd = io.open(filename, "wb"); + io.write(fd, "P6\n"~width~" "~height~"\n255\n"); + io.write(fd, buffer); + io.close(fd); +} + +if (size(arg)<1) { + println("need input file and output file"); + exit(-1); +} + +var content = io.readfile(arg[0], "r"); +var tail_len = 0; +while(math.mod(size(content), 256*3)!=0) { + content ~= "A"; + tail_len += 1; +} +println("filled ", tail_len); +println("size ", size(content)); +ppm(size(arg)==2? arg[1]:"out.ppm", content); \ No newline at end of file