diff --git a/ast/nasal_new_builtin.cpp b/ast/nasal_new_builtin.cpp new file mode 100644 index 0000000..a1bea78 --- /dev/null +++ b/ast/nasal_new_builtin.cpp @@ -0,0 +1,1266 @@ +#include "nasal_new_builtin.h" + +var builtin_print(var* local, gc& ngc) { + for(auto& i:local[1].vec().elems) { + std::cout<1 || !end.str().length()) { + std::cin>>ret.str(); + } else { + std::getline(std::cin, ret.str(), end.str()[0]); + } + return ret; +} + +var builtin_readfile(var* local, gc& ngc) { + var val=local[1]; + if (val.type!=vm_str) { + return nas_err("io::readfile", "\"filename\" must be string"); + } + std::ifstream in(val.str(), std::ios::binary); + std::stringstream rd; + if (!in.fail()) { + rd<"); + } + out<last) { + vec.push_back(ngc.newstr(s.substr(last, pos-last))); + } + last=pos+deli.length(); + pos=s.find(deli, last); + } + if (last!=s.length()) { + vec.push_back(ngc.newstr(s.substr(last))); + } + ngc.temp=nil; + return res; +} + +var builtin_rand(var* local, gc& ngc) { + var val=local[1]; + if (val.type!=vm_num && val.type!=vm_nil) { + return nas_err("rand", "\"seed\" must be nil or number"); + } + if (val.type==vm_num) { + srand((u32)val.num()); + return nil; + } + f64 num=0; + for(u32 i=0;i<5;++i) { + num=(num+rand())*(1.0/(RAND_MAX+1.0)); + } + return var::num(num); +} + +var builtin_id(var* local, gc& ngc) { + var val=local[1]; + std::stringstream ss; + ss<<"0"; + if (val.type>vm_num) { + ss<<"x"<= 0"); + } + if (len.type!=vm_num || len.num()<0) { + return nas_err("substr", "\"length\" should be number >= 0"); + } + usize begin=(usize)beg.num(); + usize length=(usize)len.num(); + if (begin>=str.str().length()) { + return nas_err("susbtr", "begin index out of range: "+std::to_string(begin)); + } + return ngc.newstr(str.str().substr(begin,length)); +} + +var builtin_streq(var* local, gc& ngc) { + var a=local[1]; + var b=local[2]; + return var::num(f64((a.type!=vm_str || b.type!=vm_str)?0:(a.str()==b.str()))); +} + +var builtin_left(var* local, gc& ngc) { + var str=local[1]; + var len=local[2]; + if (str.type!=vm_str) { + return nas_err("left", "\"string\" must be string"); + } + if (len.type!=vm_num) { + return nas_err("left", "\"length\" must be number"); + } + if (len.num()<0) { + return ngc.newstr(""); + } + return ngc.newstr(str.str().substr(0,len.num())); +} + +var builtin_right(var* local, gc& ngc) { + var str=local[1]; + var len=local[2]; + if (str.type!=vm_str) { + return nas_err("right", "\"string\" must be string"); + } + if (len.type!=vm_num) { + return nas_err("right", "\"length\" must be number"); + } + i32 length=(i32)len.num(); + i32 srclen=str.str().length(); + if (length>srclen) { + length=srclen; + } + if (length<0) { + length=0; + } + return ngc.newstr(str.str().substr(srclen-length, srclen)); +} + +var builtin_cmp(var* local, gc& ngc) { + var a=local[1]; + var b=local[2]; + if (a.type!=vm_str || b.type!=vm_str) { + return nas_err("cmp", "\"a\" and \"b\" must be string"); + } + return var::num((f64)strcmp(a.str().c_str(), b.str().c_str())); +} + +var builtin_chr(var* local, gc& ngc) { + const char* extend[]={ + "€"," ","‚","ƒ","„","…","†","‡", + "ˆ","‰","Š","‹","Œ"," ","Ž"," ", + " ","‘","’","“","”","•","–","—", + "˜","™","š","›","œ"," ","ž","Ÿ", + " ","¡","¢","£","¤","¥","¦","§", + "¨","©","ª","«","¬"," ","®","¯", + "°","±","²","³","´","µ","¶","·", + "¸","¹","º","»","¼","½","¾","¿", + "À","Á","Â","Ã","Ä","Å","Æ","Ç", + "È","É","Ê","Ë","Ì","Í","Î","Ï", + "Ð","Ñ","Ò","Ó","Ô","Õ","Ö","×", + "Ø","Ù","Ú","Û","Ü","Ý","Þ","ß", + "à","á","â","ã","ä","å","æ","ç", + "è","é","ê","ë","ì","í","î","ï", + "ð","ñ","ò","ó","ô","õ","ö","÷", + "ø","ù","ú","û","ü","ý","þ","ÿ" + }; + i32 num=local[1].num(); + if (0<=num && num<128) { + return ngc.newstr((char)num); + } else if (128<=num && num<256) { + return ngc.newstr(extend[num-128]); + } + return ngc.newstr(" "); +} + +var builtin_char(var* local, gc& ngc) { + return ngc.newstr((unsigned char)local[1].num()); +} + +var builtin_values(var* local, gc& ngc) { + var hash=local[1]; + if (hash.type!=vm_hash) { + return nas_err("values", "\"hash\" must be hash"); + } + var vec=ngc.alloc(vm_vec); + auto& v=vec.vec().elems; + for(auto& i:hash.hash().elems) { + v.push_back(i.second); + } + return vec; +} + +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; +} + +var builtin_open(var* local, gc& ngc) { + var name=local[1]; + var mode=local[2]; + if (name.type!=vm_str) { + return nas_err("open", "\"filename\" must be string"); + } + if (mode.type!=vm_str) { + return nas_err("open", "\"mode\" must be string"); + } + FILE* res=fopen(name.str().c_str(), mode.str().c_str()); + if (!res) { + return nas_err("open", "failed to open file <"+name.str()+">"); + } + var ret=ngc.alloc(vm_obj); + ret.obj().set(ngc.global_ghost_type_table.ghost_file, res, &ngc.global_ghost_type_table); + return ret; +} + +var builtin_close(var* local, gc& ngc) { + var fd=local[1]; + if (!fd.objchk(ngc.global_ghost_type_table.ghost_file)) { + return nas_err("close", "not a valid filehandle"); + } + fd.obj().clear(); + return nil; +} + +var builtin_read(var* local, gc& ngc) { + var fd=local[1]; + var buf=local[2]; + var len=local[3]; + if (!fd.objchk(ngc.global_ghost_type_table.ghost_file)) { + return nas_err("read", "not a valid filehandle"); + } + if (buf.type!=vm_str || buf.val.gcobj->unmut) { + return nas_err("read", "\"buf\" must be mutable string"); + } + if (len.type!=vm_num) { + return nas_err("read", "\"len\" must be number"); + } + if (len.num()<=0 || len.num()>=(1<<30)) { + return nas_err("read", "\"len\" less than 1 or too large"); + } + char* buff=new char[(usize)len.num()+1]; + if (!buff) { + return nas_err("read", "malloc failed"); + } + f64 res=fread(buff,1,len.num(), (FILE*)fd.obj().ptr); + buf.str()=buff; + buf.val.gcobj->unmut=true; + delete []buff; + return var::num(res); +} + +var builtin_write(var* local, gc& ngc) { + var fd=local[1]; + var str=local[2]; + if (!fd.objchk(ngc.global_ghost_type_table.ghost_file)) { + return nas_err("write", "not a valid filehandle"); + } + if (str.type!=vm_str) { + return nas_err("write", "\"str\" must be string"); + } + return var::num((f64)fwrite(str.str().c_str(), 1, str.str().length(), (FILE*)fd.obj().ptr)); +} + +var builtin_seek(var* local, gc& ngc) { + var fd=local[1]; + var pos=local[2]; + var whence=local[3]; + if (!fd.objchk(ngc.global_ghost_type_table.ghost_file)) { + return nas_err("seek", "not a valid filehandle"); + } + return var::num((f64)fseek((FILE*)fd.obj().ptr, pos.num(), whence.num())); +} + +var builtin_tell(var* local, gc& ngc) { + var fd=local[1]; + if (!fd.objchk(ngc.global_ghost_type_table.ghost_file)) { + return nas_err("tell", "not a valid filehandle"); + } + return var::num((f64)ftell((FILE*)fd.obj().ptr)); +} + +var builtin_readln(var* local, gc& ngc) { + var fd=local[1]; + if (!fd.objchk(ngc.global_ghost_type_table.ghost_file)) { + return nas_err("readln", "not a valid filehandle"); + } + var str=ngc.alloc(vm_str); + char c; + while((c=fgetc((FILE*)fd.obj().ptr))!=EOF) { + if (c=='\r') { + continue; + } + if (c=='\n') { + return str; + } + str.str()+=c; + } + if (str.str().length()) { + return str; + } + return nil; +} + +var builtin_stat(var* local, gc& ngc) { + var name=local[1]; + if (name.type!=vm_str) { + return nas_err("stat", "\"filename\" must be string"); + } + struct stat buf; + if (stat(name.str().c_str(),&buf)<0) { + return nas_err("stat", "failed to open file <"+name.str()+">"); + } + var ret=ngc.alloc(vm_vec); + ret.vec().elems={ + var::num((f64)buf.st_dev), + var::num((f64)buf.st_ino), + var::num((f64)buf.st_mode), + var::num((f64)buf.st_nlink), + var::num((f64)buf.st_uid), + var::num((f64)buf.st_gid), + var::num((f64)buf.st_rdev), + var::num((f64)buf.st_size), + var::num((f64)buf.st_atime), + var::num((f64)buf.st_mtime), + var::num((f64)buf.st_ctime) + }; + return ret; +} + +var builtin_eof(var* local, gc& ngc) { + var fd=local[1]; + if (!fd.objchk(ngc.global_ghost_type_table.ghost_file)) { + return nas_err("readln", "not a valid filehandle"); + } + return var::num((f64)feof((FILE*)fd.obj().ptr)); +} + +var builtin_fld(var* local, gc& ngc) { + // bits.fld(s,0,3); + // if s stores 10100010(162) + // will get 101(5) + var str=local[1]; + var startbit=local[2]; + var length=local[3]; + if (str.type!=vm_str || str.val.gcobj->unmut) { + return nas_err("fld", "\"str\" must be mutable string"); + } + if (startbit.type!=vm_num || length.type!=vm_num) { + return nas_err("fld", "\"startbit\",\"len\" must be number"); + } + u32 bit=(u32)startbit.num(); + u32 len=(u32)length.num(); + if (bit+len>8*str.str().length()) { + return nas_err("fld", "bitfield out of bounds"); + } + u32 res=0; + auto& s=str.str(); + for(u32 i=bit;i>3]&(1<<(7-(i&7)))) { + res|=1<<(bit+len-i-1); + } + } + return var::num((f64)res); +} + +var builtin_sfld(var* local, gc& ngc) { + // bits.sfld(s,0,3); + // if s stores 10100010(162) + // will get 101(5) then this will be signed extended to + // 11111101(-3) + var str=local[1]; + var startbit=local[2]; + var length=local[3]; + if (str.type!=vm_str || str.val.gcobj->unmut) { + return nas_err("sfld", "\"str\" must be mutable string"); + } + if (startbit.type!=vm_num || length.type!=vm_num) { + return nas_err("sfld", "\"startbit\",\"len\" must be number"); + } + u32 bit=(u32)startbit.num(); + u32 len=(u32)length.num(); + if (bit+len>8*str.str().length()) { + return nas_err("sfld", "bitfield out of bounds"); + } + u32 res=0; + auto& s=str.str(); + for(u32 i=bit;i>3]&(1<<(7-(i&7)))) { + res|=1<<(bit+len-i-1); + } + } + if (res&(1<<(len-1))) { + res|=~((1<unmut) { + return nas_err("setfld", "\"str\" must be mutable string"); + } + if (startbit.type!=vm_num || length.type!=vm_num || value.type!=vm_num) { + return nas_err("setfld", "\"startbit\",\"len\",\"val\" must be number"); + } + u32 bit=(u32)startbit.num(); + u32 len=(u32)length.num(); + u64 val=(u64)value.num(); + if (bit+len>8*str.str().length()) { + return nas_err("setfld", "bitfield out of bounds"); + } + auto& s=str.str(); + for(u32 i=bit;i>3]|=(1<<(7-(i&7))); + } else { + s[i>>3]&=~(1<<(7-(i&7))); + } + } + return nil; +} + +var builtin_buf(var* local, gc& ngc) { + var length=local[1]; + if (length.type!=vm_num || length.num()<=0) { + return nas_err("buf", "\"len\" must be number greater than 0"); + } + var str=ngc.alloc(vm_str); + auto& s=str.str(); + s.resize(length.num(), '\0'); + return str; +} + +var builtin_sleep(var* local, gc& ngc) { + var val=local[1]; + if (val.type!=vm_num) { + return nil; + } +#if defined(_WIN32) && !defined(_GLIBCXX_HAS_GTHREADS) + // mingw-w64 win32 thread model has no std::this_thread + // also msvc will use this + Sleep(i64(val.num()*1e3)); +#else + std::this_thread::sleep_for(std::chrono::microseconds(i64(val.num()*1e6))); +#endif + return nil; +} + +var builtin_pipe(var* local, gc& ngc) { +#ifndef _WIN32 + i32 fd[2]; + var res=ngc.alloc(vm_vec); + if (pipe(fd)==-1) { + return nas_err("pipe", "failed to create pipe"); + } + res.vec().elems.push_back(var::num((f64)fd[0])); + res.vec().elems.push_back(var::num((f64)fd[1])); + return res; +#endif + return nas_err("pipe", "not supported"); +} + +var builtin_fork(var* local, gc& ngc) { +#ifndef _WIN32 + f64 res=fork(); + if (res<0) { + return nas_err("fork", "failed to fork a process"); + } + return var::num((f64)res); +#endif + return nas_err("fork", "not supported"); +} + +var builtin_waitpid(var* local, gc& ngc) { + var pid=local[1]; + var nohang=local[2]; + if (pid.type!=vm_num || nohang.type!=vm_num) { + return nas_err("waitpid", "pid and nohang must be number"); + } +#ifndef _WIN32 + i32 ret_pid,status; + ret_pid=waitpid(pid.num(),&status,nohang.num()==0?0:WNOHANG); + var vec=ngc.alloc(vm_vec); + vec.vec().elems.push_back(var::num((f64)ret_pid)); + vec.vec().elems.push_back(var::num((f64)status)); + return vec; +#endif + return nas_err("waitpid", "not supported"); +} + +var builtin_opendir(var* local, gc& ngc) { + var path=local[1]; + if (path.type!=vm_str) { + return nas_err("opendir", "\"path\" must be string"); + } +#ifdef _MSC_VER + WIN32_FIND_DATAA data; + HANDLE p; + p=FindFirstFileA((path.str()+"\\*.*").c_str(),&data); + if (p==INVALID_HANDLE_VALUE) { + return nas_err("opendir", "cannot open dir <"+path.str()+">"); + } +#else + DIR* p=opendir(path.str().c_str()); + if (!p) { + return nas_err("opendir", "cannot open dir <"+path.str()+">"); + } +#endif + var ret=ngc.alloc(vm_obj); + ret.obj().set(ngc.global_ghost_type_table.ghost_dir, p, &ngc.global_ghost_type_table); + return ret; +} + +var builtin_readdir(var* local, gc& ngc) { + var handle=local[1]; + if (!handle.objchk(ngc.global_ghost_type_table.ghost_dir)) { + return nas_err("readdir", "not a valid dir handle"); + } +#ifdef _MSC_VER + WIN32_FIND_DATAA data; + if (!FindNextFileA(handle.obj().ptr,&data)) { + return nil; + } + return ngc.newstr(data.cFileName); +#else + dirent* p=readdir((DIR*)handle.obj().ptr); + return p?ngc.newstr(p->d_name):nil; +#endif +} + +var builtin_closedir(var* local, gc& ngc) { + var handle=local[1]; + if (!handle.objchk(ngc.global_ghost_type_table.ghost_dir)) { + return nas_err("closedir", "not a valid dir handle"); + } + handle.obj().clear(); + return nil; +} + +var builtin_chdir(var* local, gc& ngc) { + var path=local[1]; + if (path.type!=vm_str) { + return var::num((f64)-1); + } + return var::num((f64)chdir(path.str().c_str())); +} + +var builtin_environ(var* local, gc& ngc) { + var res=ngc.temp=ngc.alloc(vm_vec); + auto& vec=res.vec().elems; + for(char** env=environ;*env;++env) { + vec.push_back(ngc.newstr(*env)); + } + ngc.temp=nil; + return res; +} + +var builtin_getcwd(var* local, gc& ngc) { + char buf[1024]; + if (!getcwd(buf,sizeof(buf))) { + return nil; + } + return ngc.newstr(buf); +} + +var builtin_getenv(var* local, gc& ngc) { + var envvar=local[1]; + if (envvar.type!=vm_str) { + return nas_err("getenv", "\"envvar\" must be string"); + } + char* res=getenv(envvar.str().c_str()); + return res?ngc.newstr(res):nil; +} + +var builtin_dlopen(var* local, gc& ngc) { + var dlname=local[1]; + if (dlname.type!=vm_str) { + return nas_err("dlopen", "\"libname\" must be string"); + } +#ifdef _WIN32 + wchar_t* str=new wchar_t[dlname.str().size()+1]; + if (!str) { + return nas_err("dlopen", "malloc failed"); + } + memset(str, 0, sizeof(wchar_t)*dlname.str().size()+1); + mbstowcs(str, dlname.str().c_str(),dlname.str().size()+1); + void* ptr=LoadLibraryA(dlname.str().c_str()); + delete []str; +#else + void* ptr=dlopen(dlname.str().c_str(), RTLD_LOCAL|RTLD_LAZY); +#endif + if (!ptr) { + return nas_err("dlopen", "cannot open dynamic lib <"+dlname.str()+">"); + } + var ret=ngc.temp=ngc.alloc(vm_hash); + var lib=ngc.alloc(vm_obj); + lib.obj().set(ngc.global_ghost_type_table.ghost_dylib, ptr, &ngc.global_ghost_type_table); + ret.hash().elems["lib"]=lib; + +#ifdef _WIN32 + void* func=(void*)GetProcAddress((HMODULE)lib.obj().ptr, "get"); +#else + void* func=dlsym(lib.obj().ptr, "get"); +#endif + if (!func) { + return nas_err("dlopen", "cannot find function"); + } + // get function pointer by name + module_func_info* tbl=((get_func_ptr)func)(&ngc.global_ghost_type_table); + if (!tbl) { + return nas_err("dlopen", "failed to get module functions"); + } + for(u32 i=0;tbl[i].name;++i) { + void* p=(void*)tbl[i].fd; + var tmp=ngc.alloc(vm_obj); + tmp.obj().set(ngc.global_ghost_type_table.ghost_faddr, p, &ngc.global_ghost_type_table); + ret.hash().elems[tbl[i].name]=tmp; + } + + ngc.temp=nil; + return ret; +} + +var builtin_dlclose(var* local, gc& ngc) { + var libptr=local[1]; + if (!libptr.objchk(ngc.global_ghost_type_table.ghost_dylib)) { + return nas_err("dlclose", "\"lib\" is not a valid dynamic lib"); + } + libptr.obj().clear(); + return nil; +} + +var builtin_dlcallv(var* local, gc& ngc) { + var fp=local[1]; + var args=local[2]; + if (!fp.objchk(ngc.global_ghost_type_table.ghost_faddr)) { + return nas_err("dlcall", "\"ptr\" is not a valid function pointer"); + } + auto& vec=args.vec().elems; + return ((module_func)fp.obj().ptr)( + vec.data(), + vec.size(), + &ngc + ); +} + +var builtin_dlcall(var* local, gc& ngc) { + var fp=local[1]; + if (!fp.objchk(ngc.global_ghost_type_table.ghost_faddr)) { + return nas_err("dlcall", "\"ptr\" is not a valid function pointer"); + } + + var* local_frame_start=local+2; + usize local_frame_size=ngc.rctx->top-local_frame_start; + // arguments' stored place begins at local +2 + return ((module_func)fp.obj().ptr)( + local_frame_start, + local_frame_size, + &ngc + ); +} + +var builtin_platform(var* local, gc& ngc) { + if (is_windows()) { + return ngc.newstr("windows"); + } else if (is_linux()) { + return ngc.newstr("linux"); + } else if (is_macos()) { + return ngc.newstr("macOS"); + } + return ngc.newstr("unknown"); +} + +var builtin_arch(var* local, gc& ngc) { + if (is_x86()) { + return ngc.newstr("x86"); + } else if (is_x86_64()) { + return ngc.newstr("x86-64"); + } else if (is_amd64()) { + return ngc.newstr("amd64"); + } else if (is_arm()) { + return ngc.newstr("arm"); + } else if (is_aarch64()) { + return ngc.newstr("aarch64"); + } else if (is_ia64()) { + return ngc.newstr("ia64"); + } else if (is_powerpc()) { + return ngc.newstr("powerpc"); + } else if (is_superh()) { + return ngc.newstr("superh"); + } + return ngc.newstr("unknown"); +} + +// md5 related functions +std::string tohex(u32 num) { + const char str16[]="0123456789abcdef"; + std::string str=""; + for(u32 i=0;i<4;i++, num>>=8) { + std::string tmp=""; + for(u32 j=0,b=num&0xff;j<2;j++,b>>=4) { + tmp.insert(0,1,str16[b&0xf]); + } + str+=tmp; + } + return str; +} + +std::string md5(const std::string& src) { + std::vector buff; + usize num=((src.length()+8)>>6)+1; + usize buffsize=num<<4; + buff.resize(buffsize,0); + for(usize i=0;i>2]|=((u8)src[i])<<((i&0x3)<<3); + } + buff[src.length()>>2]|=0x80<<(((src.length()%4))<<3); + buff[buffsize-2]=(src.length()<<3)&0xffffffff; + buff[buffsize-1]=((src.length()<<3)>>32)&0xffffffff; + + // u32(abs(sin(i+1))*(2pow32)) + const u32 k[]={ + 0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501, + 0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,0xa679438e,0x49b40821, + 0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8, + 0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a, + 0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70, + 0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665, + 0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1, + 0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391 + }; + + // left shift bits + const u32 s[]={ + 7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22, + 5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20, + 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23, + 6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21 + }; + + // index + const u32 idx[]={ + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // g=i + 1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12, // g=(5*i+1)%16; + 5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2, // g=(3*i+5)%16; + 0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9 // g=(7*i)%16; + }; + +#define shift(x,n) (((x)<<(n))|((x)>>(32-(n)))) // cycle left shift +#define md5f(x,y,z) (((x)&(y))|((~x)&(z))) +#define md5g(x,y,z) (((x)&(z))|((y)&(~z))) +#define md5h(x,y,z) ((x)^(y)^(z)) +#define md5i(x,y,z) ((y)^((x)|(~z))) + + u32 atmp=0x67452301,btmp=0xefcdab89; + u32 ctmp=0x98badcfe,dtmp=0x10325476; + for(u32 i=0;itop[0].type==vm_ret) { + // when first calling this coroutine, the stack top must be vm_ret + return ngc.rctx->top[0]; + } + + // after first calling the coroutine, each time coroutine.yield triggered + // a new space will be reserved on stack with value nil + // so we could fill this place with args + + // the coroutine seems like coroutine.yield returns the value + // but in fact coroutine.yield stop the coroutine + // until main context calls the coroutine.resume + return local[2]; +} + +var builtin_coyield(var* local, gc& ngc) { + if (!ngc.cort) { + return nas_err("coroutine::yield", "no coroutine is running"); + } + + // this will set to main stack top + ngc.ctxreserve(); + + // then this will return value to main's stack top[0] + // the procedure seems like coroutine.resume returns the value + // but in fact coroutine.resume stop the main context + // until coroutine calls the coroutine.yield + return local[1]; +} + +var builtin_costatus(var* local, gc& ngc) { + var co=local[1]; + if (co.type!=vm_co) { + return ngc.newstr("error"); + } + switch(co.co().status) { + case coroutine_status::suspended: return ngc.newstr("suspended");break; + case coroutine_status::running: return ngc.newstr("running"); break; + case coroutine_status::dead: return ngc.newstr("dead"); break; + } + return nil; +} + +var builtin_corun(var* local, gc& ngc) { + return ngc.cort?one:zero; +} + +var builtin_millisec(var* local, gc& ngc) { + f64 res=std::chrono::duration_cast + (std::chrono::high_resolution_clock::now().time_since_epoch()) + .count(); + return var::num(res); +} + +var builtin_sysargv(var* local, gc& ngc) { + var res=ngc.alloc(vm_vec); + res.vec().elems=ngc.env_argv; + return res; +} + +var builtin_gcextend(var* local, gc& ngc) { + var type=local[1]; + if (type.type!=vm_str) { + return nil; + } + auto& s=type.str(); + if (s=="str") { + ngc.extend(vm_str); + } else if (s=="vec") { + ngc.extend(vm_vec); + } else if (s=="hash") { + ngc.extend(vm_hash); + } else if (s=="func") { + ngc.extend(vm_func); + } else if (s=="upval") { + ngc.extend(vm_upval); + } else if (s=="obj") { + ngc.extend(vm_obj); + } else if (s=="co") { + ngc.extend(vm_co); + } + return nil; +} + +var builtin_logtime(var* local, gc& ngc) { + time_t t=time(nullptr); + tm* tm_t=localtime(&t); + char s[64]; + snprintf( + s,64,"%d-%.2d-%.2d %.2d:%.2d:%.2d", + tm_t->tm_year+1900, + tm_t->tm_mon+1, + tm_t->tm_mday, + tm_t->tm_hour, + tm_t->tm_min, + tm_t->tm_sec + ); + return ngc.newstr(s); +} + +var builtin_ghosttype(var* local, gc& ngc) { + var arg = local[1]; + if (arg.type!=vm_obj) { + return nas_err("ghosttype", "this is not a ghost object."); + } + const auto& name = arg.obj().get_ghost_name(); + if (!name.length()) { + return var::num((u64)arg.obj().ptr); + } + return ngc.newstr(name); +} \ No newline at end of file diff --git a/ast/nasal_new_builtin.h b/ast/nasal_new_builtin.h new file mode 100644 index 0000000..ec983f8 --- /dev/null +++ b/ast/nasal_new_builtin.h @@ -0,0 +1,230 @@ +#pragma once + +#include "nasal_new_header.h" +#include "nasal_new_gc.h" + +#ifndef _MSC_VER +#include +#include +#else +#pragma warning (disable:4566) // i know i'm using utf-8, fuck you +#pragma warning (disable:4244) +#pragma warning (disable:4267) +#pragma warning (disable:4996) +#define _CRT_SECURE_NO_DEPRECATE 1 +#define _CRT_NONSTDC_NO_DEPRECATE 1 +#include +#include +#endif + +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#if defined __APPLE__ +#include +#define environ (*_NSGetEnviron()) +#endif + +var builtin_print(var*, gc&); +var builtin_println(var*, gc&); +var builtin_exit(var*, gc&); +var builtin_abort(var*, gc&); +var builtin_append(var*, gc&); +var builtin_setsize(var*, gc&); +var builtin_system(var*, gc&); +var builtin_input(var*, gc&); +var builtin_readfile(var*, gc&); +var builtin_fout(var*, gc&); +var builtin_split(var*, gc&); +var builtin_rand(var*, gc&); +var builtin_id(var*, gc&); +var builtin_int(var*, gc&); +var builtin_floor(var*, gc&); +var builtin_num(var*, gc&); +var builtin_pop(var*, gc&); +var builtin_str(var*, gc&); +var builtin_size(var*, gc&); +var builtin_u32xor(var*, gc&); +var builtin_u32and(var*, gc&); +var builtin_u32or(var*, gc&); +var builtin_u32nand(var*, gc&); +var builtin_u32not(var*, gc&); +var builtin_pow(var*, gc&); +var builtin_sin(var*, gc&); +var builtin_cos(var*, gc&); +var builtin_tan(var*, gc&); +var builtin_exp(var*, gc&); +var builtin_lg(var*, gc&); +var builtin_ln(var*, gc&); +var builtin_sqrt(var*, gc&); +var builtin_atan2(var*, gc&); +var builtin_isnan(var*, gc&); +var builtin_time(var*, gc&); +var builtin_contains(var*, gc&); +var builtin_delete(var*, gc&); +var builtin_keys(var*, gc&); +var builtin_die(var*, gc&); +var builtin_find(var*, gc&); +var builtin_type(var*, gc&); +var builtin_substr(var*, gc&); +var builtin_streq(var*, gc&); +var builtin_left(var*, gc&); +var builtin_right(var*, gc&); +var builtin_cmp(var*, gc&); +var builtin_chr(var*, gc&); +var builtin_char(var*, gc&); +var builtin_values(var*, gc&); +var builtin_exists(var*, gc&); +var builtin_open(var*, gc&); +var builtin_close(var*, gc&); +var builtin_read(var*, gc&); +var builtin_write(var*, gc&); +var builtin_seek(var*, gc&); +var builtin_tell(var*, gc&); +var builtin_readln(var*, gc&); +var builtin_stat(var*, gc&); +var builtin_eof(var*, gc&); +var builtin_fld(var*, gc&); +var builtin_sfld(var*, gc&); +var builtin_setfld(var*, gc&); +var builtin_buf(var*, gc&); +var builtin_sleep(var*, gc&); +var builtin_pipe(var*, gc&); +var builtin_fork(var*, gc&); +var builtin_waitpid(var*, gc&); +var builtin_opendir(var*, gc&); +var builtin_readdir(var*, gc&); +var builtin_closedir(var*, gc&); +var builtin_chdir(var*, gc&); +var builtin_environ(var*, gc&); +var builtin_getcwd(var*, gc&); +var builtin_getenv(var*, gc&); +var builtin_dlopen(var*, gc&); +var builtin_dlclose(var*, gc&); +var builtin_dlcallv(var*, gc&); +var builtin_dlcall(var*, gc&); +var builtin_platform(var*, gc&); +var builtin_arch(var*, gc&); +// md5 related functions +std::string tohex(u32); +std::string md5(const std::string&); +var builtin_md5(var*, gc&); +var builtin_cocreate(var*, gc&); +var builtin_coresume(var*, gc&); +var builtin_coyield(var*, gc&); +var builtin_costatus(var*, gc&); +var builtin_corun(var*, gc&); +var builtin_millisec(var*, gc&); +var builtin_sysargv(var*, gc&); +var builtin_gcextend(var*, gc&); +var builtin_logtime(var*, gc&); +var builtin_ghosttype(var*, gc&); + +// register builtin function's name and it's address here in this table below +// this table must end with {nullptr,nullptr} +struct { + const char* name; + var (*func)(var*,gc&); +} builtin[]= { + {"__print", builtin_print }, + {"__println", builtin_println }, + {"__exit", builtin_exit }, + {"__abort", builtin_abort }, + {"__append", builtin_append }, + {"__setsize", builtin_setsize }, + {"__system", builtin_system }, + {"__input", builtin_input }, + {"__readfile",builtin_readfile}, + {"__fout", builtin_fout }, + {"__split", builtin_split }, + {"__rand", builtin_rand }, + {"__id", builtin_id }, + {"__int", builtin_int }, + {"__floor", builtin_floor }, + {"__num", builtin_num }, + {"__pop", builtin_pop }, + {"__str", builtin_str }, + {"__size", builtin_size }, + {"__u32xor", builtin_u32xor }, + {"__u32and", builtin_u32and }, + {"__u32or", builtin_u32or }, + {"__u32nand", builtin_u32nand }, + {"__u32not", builtin_u32not }, + {"__pow", builtin_pow }, + {"__sin", builtin_sin }, + {"__cos", builtin_cos }, + {"__tan", builtin_tan }, + {"__exp", builtin_exp }, + {"__lg", builtin_lg }, + {"__ln", builtin_ln }, + {"__sqrt", builtin_sqrt }, + {"__atan2", builtin_atan2 }, + {"__isnan", builtin_isnan }, + {"__time", builtin_time }, + {"__contains",builtin_contains}, + {"__delete", builtin_delete }, + {"__keys", builtin_keys }, + {"__die", builtin_die }, + {"__find", builtin_find }, + {"__type", builtin_type }, + {"__substr", builtin_substr }, + {"__streq", builtin_streq }, + {"__left", builtin_left }, + {"__right", builtin_right }, + {"__cmp", builtin_cmp }, + {"__chr", builtin_chr }, + {"__char", builtin_char }, + {"__values", builtin_values }, + {"__exists", builtin_exists }, + {"__open", builtin_open }, + {"__close", builtin_close }, + {"__read", builtin_read }, + {"__write", builtin_write }, + {"__seek", builtin_seek }, + {"__tell", builtin_tell }, + {"__readln", builtin_readln }, + {"__stat", builtin_stat }, + {"__eof", builtin_eof }, + {"__fld", builtin_fld }, + {"__sfld", builtin_sfld }, + {"__setfld", builtin_setfld }, + {"__buf", builtin_buf }, + {"__sleep", builtin_sleep }, + {"__pipe", builtin_pipe }, + {"__fork", builtin_fork }, + {"__waitpid", builtin_waitpid }, + {"__opendir", builtin_opendir }, + {"__readdir", builtin_readdir }, + {"__closedir",builtin_closedir}, + {"__chdir", builtin_chdir }, + {"__environ", builtin_environ }, + {"__getcwd", builtin_getcwd }, + {"__getenv", builtin_getenv }, + {"__dlopen", builtin_dlopen }, + {"__dlclose", builtin_dlclose }, + {"__dlcallv", builtin_dlcallv }, + {"__dlcall", builtin_dlcall }, + {"__platform",builtin_platform}, + {"__arch", builtin_arch }, + {"__md5", builtin_md5 }, + {"__cocreate",builtin_cocreate}, + {"__coresume",builtin_coresume}, + {"__coyield", builtin_coyield }, + {"__costatus",builtin_costatus}, + {"__corun" ,builtin_corun }, + {"__millisec",builtin_millisec}, + {"__sysargv", builtin_sysargv }, + {"__gcextd", builtin_gcextend}, + {"__logtime", builtin_logtime }, + {"__ghosttype", builtin_ghosttype}, + {nullptr, nullptr } +}; diff --git a/ast/nasal_new_gc.cpp b/ast/nasal_new_gc.cpp new file mode 100644 index 0000000..ecd7ab2 --- /dev/null +++ b/ast/nasal_new_gc.cpp @@ -0,0 +1,563 @@ +#include "nasal_new_gc.h" + +void filehandle_destructor(void* ptr) { + if ((FILE*)ptr==stdin) { + return; + } + fclose((FILE*)ptr); +} + +void dir_entry_destructor(void* ptr) { +#ifndef _MSC_VER + closedir((DIR*)ptr); +#else + FindClose(ptr); +#endif +} + +void dylib_destructor(void* ptr) { +#ifdef _WIN32 + FreeLibrary((HMODULE)ptr); +#else + dlclose(ptr); +#endif +} + +void func_addr_destructor(void* ptr) {} + +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<destructor(type)(ptr); + ptr=nullptr; +} + +void nas_co::clear() { + for(u32 i=0;iclear(); 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; + } +} + +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<"; break; + } + return out; +} + +bool var::objchk(usize obj_type) { + return type==vm_obj && obj().type==obj_type && obj().ptr; +} + +var var::none() { + return {vm_none, (u32)0}; +} + +var var::nil() { + return {vm_nil, (u32)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; +} + +void gc::mark() { + std::vector bfs; + mark_context(bfs); + + while(!bfs.empty()) { + var value=bfs.back(); + bfs.pop_back(); + if (value.type<=vm_num || + value.val.gcobj->mark!=gc_status::uncollected) { + continue; + } + mark_var(bfs, value); + } +} + +void gc::mark_context(std::vector& bfs_queue) { + + // scan now running context, this context maybe related to coroutine or main + for(var* i=rctx->stack;i<=rctx->top;++i) { + bfs_queue.push_back(*i); + } + bfs_queue.push_back(rctx->funcr); + bfs_queue.push_back(rctx->upvalr); + bfs_queue.push_back(temp); + + if (!cort) { + return; + } + + // coroutine is running, so scan main process stack from mctx + for(var* i=mctx.stack;i<=mctx.top;++i) { + bfs_queue.push_back(*i); + } + bfs_queue.push_back(mctx.funcr); + bfs_queue.push_back(mctx.upvalr); +} + +void gc::mark_var(std::vector& bfs_queue, var& value) { + value.val.gcobj->mark=gc_status::found; + switch(value.type) { + case vm_vec: mark_vec(bfs_queue, value.vec()); break; + case vm_hash: mark_hash(bfs_queue, value.hash()); break; + case vm_func: mark_func(bfs_queue, value.func()); break; + case vm_upval: mark_upval(bfs_queue, value.upval()); break; + case vm_co: mark_co(bfs_queue, value.co()); break; + default: break; + } +} + +void gc::mark_vec(std::vector& bfs_queue, nas_vec& vec) { + for(auto& i:vec.elems) { + bfs_queue.push_back(i); + } +} + +void gc::mark_hash(std::vector& bfs_queue, nas_hash& hash) { + for(auto& i:hash.elems) { + bfs_queue.push_back(i.second); + } +} + +void gc::mark_func(std::vector& bfs_queue, nas_func& function) { + for(auto& i:function.local) { + bfs_queue.push_back(i); + } + for(auto& i:function.upval) { + bfs_queue.push_back(i); + } +} + +void gc::mark_upval(std::vector& bfs_queue, nas_upval& upval) { + for(auto& i:upval.elems) { + bfs_queue.push_back(i); + } +} + +void gc::mark_co(std::vector& bfs_queue, nas_co& co) { + bfs_queue.push_back(co.ctx.funcr); + bfs_queue.push_back(co.ctx.upvalr); + for(var* i=co.stack;i<=co.ctx.top;++i) { + bfs_queue.push_back(*i); + } +} + +void gc::sweep() { + for(auto i:memory) { + if (i->mark==gc_status::uncollected) { + i->clear(); + unused[i->type-vm_str].push_back(i); + i->mark=gc_status::collected; + } else if (i->mark==gc_status::found) { + i->mark=gc_status::uncollected; + } + } +} + +void gc::extend(u8 type) { + const u8 index=type-vm_str; + size[index]+=incr[index]; + + for(u32 i=0;i& s, const std::vector& argv) { + // initialize function register + rctx->funcr=nil; + worktime=0; + + // initialize counters + for(u8 i=0;iunmut=1; + strs[i].str()=s[i]; + } + + // record arguments + env_argv.resize(argv.size()); + for(usize i=0;iunmut=1; + env_argv[i].str()=argv[i]; + } +} + +void gc::clear() { + for(auto i:memory) { + delete i; + } + memory.clear(); + for(u8 i=0;imark=gc_status::uncollected; + unused[index].pop_back(); + return ret; +} + +void gc::ctxchg(nas_co& co) { + // store running state to main context + mctx=*rctx; + + // restore coroutine context state + *rctx=co.ctx; + + // set coroutine pointer + cort=&co; + + // set coroutine state to running + cort->status=coroutine_status::running; +} + +void gc::ctxreserve() { + // pc=0 means this coroutine is finished + cort->status=rctx->pc? + coroutine_status::suspended: + coroutine_status::dead; + + // store running state to coroutine + cort->ctx=*rctx; + + // restore main context state + *rctx=mctx; + + // set coroutine pointer to nullptr + cort=nullptr; +} + +var nas_err(const std::string& error_function_name, const std::string& info) { + std::cerr<<"[vm] "< +#include +#else +#include +#include +#endif + +#ifdef _WIN32 +#include +#else +#include +#endif + +#include +#include +#include +#include +#include + +#include "nasal_new_header.h" +#include "nasal_new_err.h" + +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 +}; + +const u32 gc_type_size=vm_co-vm_str+1; + +enum class coroutine_status:u32 { + suspended, + running, + dead +}; + +enum class gc_status:u8 { + uncollected=0, + collected, + found +}; + +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_val; // nas_val includes gc-managed types + +struct var { +public: + u8 type; + 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; + } + friend std::ostream& operator<<(std::ostream&, var&); + + // number and string can be translated to each other + f64 tonum(); + std::string tostr(); + bool objchk(usize); + + // 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(); +}; + +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 dpara; // dynamic parameter name index in hash. + u32 entry; // pc will set to entry-1 to call this function + u32 psize; // used to load default parameters to a new function + u32 lsize; // used to expand memory space for local values on stack + std::vector local; // local scope with default value(var) + std::vector upval; // closure + std::unordered_map keys; // parameter table, u32 begins from 1 + + nas_func(): dpara(-1), entry(0), psize(0), lsize(0) {} + void clear(); +}; + +struct nas_upval { +public: + /* on stack, use these variables */ + bool onstk; + u32 size; + var* stk; + + /* not on stack, use this */ + std::vector elems; + +public: + nas_upval(): onstk(true), size(0), stk(nullptr) {} + + var& operator[](usize n) { + return onstk? stk[n]:elems[n]; + } + + void clear() { + onstk=true; + elems.clear(); + size=0; + } +}; + +void filehandle_destructor(void*); +void dir_entry_destructor(void*); +void dylib_destructor(void*); +void func_addr_destructor(void*); + +struct ghost_register_table { +private: + using dtor=void (*)(void*); + +private: + std::unordered_map mapper; + std::vector ghost_name; + std::vector destructors; + +public: + // reserved ghost type only for native functions + usize ghost_file; + usize ghost_dir; + usize ghost_dylib; + usize ghost_faddr; + +public: + ghost_register_table() { + ghost_file = register_ghost_type("file", filehandle_destructor); + ghost_dir = register_ghost_type("dir", dir_entry_destructor); + ghost_dylib = register_ghost_type("dylib", dylib_destructor); + ghost_faddr = register_ghost_type("faddr", func_addr_destructor); + } + + bool exists(const std::string& name) const { + return mapper.count(name); + } + + usize get_ghost_type_index(const std::string& name) const { + return mapper.at(name); + } + + const std::string& get_ghost_name(usize index) const { + return ghost_name.at(index); + } + + usize register_ghost_type(const std::string& name, dtor ptr) { + if (mapper.count(name)) { + std::cerr<<"nasal_gc.h: ghost_register_table::register_ghost_type: "; + std::cerr<<"ghost type \""<get_ghost_name(ghost.type); + out<<" at 0x"<"; + return out; + } + + const std::string& get_ghost_name() const { + return ghost_type_table->get_ghost_name(type); + } +}; + +struct context { + u32 pc; + var* localr; + var* memr; + var funcr; + var upvalr; + var* canary; + var* stack; + var* top; +}; + +struct nas_co { + var stack[STACK_DEPTH]; + context ctx; + coroutine_status status; + + nas_co() {clear();} + void clear(); +}; + +struct nas_val { + 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; + } 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&, var&); + +const var zero = var::num(0); +const var one = var::num(1); +const var nil = var::nil(); + +struct gc { + ghost_register_table global_ghost_type_table; + /* main context temporary storage */ + context mctx; + + /* runtime context */ + context* rctx; + nas_co* cort=nullptr; // running coroutine + + /* temporary space used in builtin/module functions */ + var temp=nil; + + /* constants and memory pool */ + std::vector strs; // reserved address for const vm_str + std::vector env_argv; // command line arguments + std::vector memory; // gc memory + std::vector unused[gc_type_size]; // gc free list + + /* heap increase size */ + u32 incr[gc_type_size]={ + 128, // vm_str + 128, // vm_vec + 64, // vm_hash + 128, // vm_func + 256, // vm_upval + 16, // vm_obj + 16 // vm_co + }; + + /* values for analysis */ + u64 size[gc_type_size]; + u64 gcnt[gc_type_size]; + u64 acnt[gc_type_size]; + i64 worktime=0; + + gc(context* _ctx): rctx(_ctx) {} + +private: + /* gc functions */ + void mark(); + void mark_context(std::vector&); + void mark_var(std::vector&, var&); + inline void mark_vec(std::vector&, nas_vec&); + inline void mark_hash(std::vector&, nas_hash&); + inline void mark_func(std::vector&, nas_func&); + inline void mark_upval(std::vector&, nas_upval&); + inline void mark_co(std::vector&, nas_co&); + void sweep(); + +public: + void extend(u8); + void init(const std::vector&, const std::vector&); + void clear(); + void info(); + var alloc(const u8); + void ctxchg(nas_co&); + void ctxreserve(); + +public: + var newstr(char c) { + var s=alloc(vm_str); + s.str()=c; + return s; + } + + var newstr(const char* buff) { + var s=alloc(vm_str); + s.str()=buff; + return s; + } + + var newstr(const std::string& buff) { + var s=alloc(vm_str); + s.str()=buff; + return s; + } +}; + +// 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*); + +// module function stores in tables with this type, end with {nullptr,nullptr} +struct module_func_info { + const char* name; + module_func fd; +}; + +// module function "get" type +typedef module_func_info* (*get_func_ptr)(ghost_register_table*); diff --git a/ast/nasal_new_import.cpp b/ast/nasal_new_import.cpp index 535ee37..da52eb2 100644 --- a/ast/nasal_new_import.cpp +++ b/ast/nasal_new_import.cpp @@ -137,9 +137,16 @@ void linker::link(code_block* new_tree_root, code_block* old_tree_root) { code_block* linker::import_regular_file(call_expr* node) { lexer lex(err); parse par(err); - // get filename and set node to ast_null + // get filename auto filename = get_path(node); - // node.clear(); + // clear this node + for(auto i : node->get_calls()) { + delete i; + } + node->get_calls().clear(); + auto location = node->get_first()->get_location(); + delete node->get_first(); + node->set_first(new nil_expr(location)); // avoid infinite loading loop filename = find_file(filename); diff --git a/ast/nasal_new_parse.cpp b/ast/nasal_new_parse.cpp index a306be5..5660f36 100644 --- a/ast/nasal_new_parse.cpp +++ b/ast/nasal_new_parse.cpp @@ -284,9 +284,8 @@ void parse::params(function* func_node) { match(tok::lcurve); while(!lookahead(tok::rcurve)) { auto param = new parameter(toks[ptr].loc); - auto id_node = id(); - param->set_parameter_name(id_node->get_name()); - delete id_node; + param->set_parameter_name(toks[ptr].str); + match(tok::id); if (lookahead(tok::eq)) { match(tok::eq); param->set_parameter_type(parameter::param_type::default_parameter); diff --git a/makefile b/makefile index 915b1d8..52fbc9c 100644 --- a/makefile +++ b/makefile @@ -76,9 +76,11 @@ test:nasal NASAL_NEW_AST=\ nasal_new_misc.o\ nasal_new_err.o\ + nasal_new_gc.o\ nasal_new_import.o\ nasal_new_lexer.o\ nasal_new_ast.o\ + nasal_new_builtin.o\ nasal_new_parse.o\ ast_visitor.o\ ast_dumper.o\ @@ -86,7 +88,7 @@ NASAL_NEW_AST=\ # for test nnew: $(NASAL_NEW_AST) - $(CXX) $(NASAL_NEW_AST) -o nnew + $(CXX) $(NASAL_NEW_AST) -o nnew -ldl @ echo "build done" nasal_new_main.o: ast/nasal_new_main.cpp @@ -98,15 +100,21 @@ nasal_new_misc.o: ast/nasal_new_header.h ast/nasal_new_misc.cpp nasal_new_err.o: ast/nasal_new_err.h ast/nasal_new_err.cpp $(CXX) -std=$(STD) -c -O3 ast/nasal_new_err.cpp -fno-exceptions -fPIC -o nasal_new_err.o -I . +nasal_new_gc.o: ast/nasal_new_gc.h ast/nasal_new_gc.cpp + $(CXX) -std=$(STD) -c -O3 ast/nasal_new_gc.cpp -fno-exceptions -fPIC -o nasal_new_gc.o -I . + nasal_new_import.o: ast/nasal_new_import.h ast/nasal_new_import.cpp - $(CXX) --std=$(STD) -c -O3 ast/nasal_new_import.cpp -fno-exceptions -fPIC -o nasal_new_import.o -I . + $(CXX) -std=$(STD) -c -O3 ast/nasal_new_import.cpp -fno-exceptions -fPIC -o nasal_new_import.o -I . nasal_new_lexer.o: ast/nasal_new_lexer.h ast/nasal_new_lexer.cpp - $(CXX) --std=$(STD) -c -O3 ast/nasal_new_lexer.cpp -fno-exceptions -fPIC -o nasal_new_lexer.o -I . + $(CXX) -std=$(STD) -c -O3 ast/nasal_new_lexer.cpp -fno-exceptions -fPIC -o nasal_new_lexer.o -I . nasal_new_ast.o: ast/nasal_new_ast.h ast/nasal_new_ast.cpp $(CXX) -std=$(STD) -c -O3 ast/nasal_new_ast.cpp -fno-exceptions -fPIC -o nasal_new_ast.o -I . +nasal_new_builtin.o: ast/nasal_new_builtin.h ast/nasal_new_builtin.cpp + $(CXX) -std=$(STD) -c -O3 ast/nasal_new_builtin.cpp -fno-exceptions -fPIC -o nasal_new_builtin.o -I . + nasal_new_parse.o: ast/nasal_new_parse.h ast/nasal_new_parse.cpp ast/nasal_new_ast.h $(CXX) -std=$(STD) -c -O3 ast/nasal_new_parse.cpp -fno-exceptions -fPIC -o nasal_new_parse.o -I . diff --git a/nasal_builtin.h b/nasal_builtin.h index cb492fd..d800bd3 100644 --- a/nasal_builtin.h +++ b/nasal_builtin.h @@ -1287,6 +1287,18 @@ var builtin_logtime(var* local, gc& ngc) { return ngc.newstr(s); } +var builtin_ghosttype(var* local, gc& ngc) { + var arg = local[1]; + if (arg.type!=vm_obj) { + return nas_err("ghosttype", "this is not a ghost object."); + } + const auto& name = arg.obj().get_ghost_name(); + if (!name.length()) { + return var::num((u64)arg.obj().ptr); + } + return ngc.newstr(name); +} + // register builtin function's name and it's address here in this table below // this table must end with {nullptr,nullptr} struct { @@ -1383,5 +1395,6 @@ struct { {"__sysargv", builtin_sysargv }, {"__gcextd", builtin_gcextend}, {"__logtime", builtin_logtime }, + {"__ghosttype", builtin_ghosttype}, {nullptr, nullptr } }; diff --git a/nasal_gc.h b/nasal_gc.h index a889d63..d66bf25 100644 --- a/nasal_gc.h +++ b/nasal_gc.h @@ -283,6 +283,10 @@ public: out<<" at 0x"<"; return out; } + + const std::string& get_ghost_name() const { + return ghost_type_table->get_ghost_name(type); + } }; struct context { diff --git a/stl/lib.nas b/stl/lib.nas index d8e3ba3..ff8e774 100644 --- a/stl/lib.nas +++ b/stl/lib.nas @@ -250,6 +250,9 @@ var isvec=func(v){ return typeof(v)=="vec"; } +var ghosttype=func(ghost_object) { + return __ghosttype(ghost_object); +} # get the index of val in the vec var vecindex=func(vec,val){