Nasal-Interpreter/src/nasal_builtin.cpp

1066 lines
31 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "nasal_builtin.h"
#include <chrono>
var builtin_print(var* local, gc& ngc) {
for(auto& i : local[1].vec().elems) {
std::cout << i;
}
std::cout << std::flush;
return nil;
}
var builtin_println(var* local, gc& ngc) {
for(auto& i : local[1].vec().elems) {
std::cout << i;
}
std::cout << std::endl;
return nil;
}
var builtin_exit(var* local, gc& ngc) {
std::exit(local[1].num());
return nil;
}
var builtin_abort(var* local, gc& ngc) {
std::abort();
return nil;
}
var builtin_append(var* local, gc& ngc) {
var vec = local[1];
var elem = local[2];
if (vec.type!=vm_vec) {
return nas_err("append", "\"vec\" must be vector");
}
auto& v = vec.vec().elems;
for(auto& i : elem.vec().elems) {
v.push_back(i);
}
return nil;
}
var builtin_setsize(var* local, gc& ngc) {
var vec = local[1];
var size = local[2];
if (vec.type!=vm_vec) {
return nas_err("setsize", "\"vec\" must be vector");
}
if (size.type!=vm_num || size.num()<0) {
return nil;
}
vec.vec().elems.resize((i64)size.num(), nil);
return nil;
}
var builtin_system(var* local, gc& ngc) {
var str = local[1];
if (str.type!=vm_str) {
return var::num(-1);
}
return var::num((f64)system(str.str().c_str()));
}
var builtin_input(var* local, gc& ngc) {
var end = local[1];
var ret = ngc.alloc(vm_str);
if (end.type!=vm_str || end.str().length()>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 << in.rdbuf();
}
return ngc.newstr(rd.str());
}
var builtin_fout(var* local, gc& ngc) {
var val = local[1];
var str = local[2];
if (val.type!=vm_str) {
return nas_err("io::fout", "\"filename\" must be string");
}
std::ofstream out(val.str());
if (out.fail()) {
return nas_err("io::fout", "cannot open <"+val.str()+">");
}
out << str;
return nil;
}
var builtin_split(var* local, gc& ngc) {
var delimeter = local[1];
var str = local[2];
if (delimeter.type!=vm_str) {
return nas_err("split", "\"separator\" must be string");
}
if (str.type!=vm_str) {
return nas_err("split", "\"str\" must be string");
}
const auto& deli = delimeter.str();
const auto& s = str.str();
// avoid being sweeped
var res = ngc.temp = ngc.alloc(vm_vec);
auto& vec = res.vec().elems;
if (!deli.length()) {
for(auto i : s) {
vec.push_back(ngc.newstr(i));
}
ngc.temp = nil;
return res;
}
usize last = 0;
usize pos = s.find(deli, 0);
while(pos!=std::string::npos) {
if (pos>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" << std::hex << (u64)val.val.gcobj << std::dec;
}
return ngc.newstr(ss.str());
}
var builtin_int(var* local, gc& ngc) {
var val = local[1];
if (val.type!=vm_num && val.type!=vm_str) {
return nil;
}
return var::num(f64((i32)val.tonum()));
}
var builtin_floor(var* local, gc& ngc) {
var val = local[1];
return var::num(std::floor(val.num()));
}
var builtin_num(var* local, gc& ngc) {
var val = local[1];
if (val.type==vm_num) {
return val;
}
if (val.type!=vm_str) {
return nil;
}
f64 res = val.tonum();
if (std::isnan(res)) {
return nil;
}
return var::num(res);
}
var builtin_pop(var* local, gc& ngc) {
var val = local[1];
if (val.type!=vm_vec) {
return nas_err("pop", "\"vec\" must be vector");
}
auto& vec = val.vec().elems;
if (vec.size()) {
var tmp = vec.back();
vec.pop_back();
return tmp;
}
return nil;
}
var builtin_str(var* local, gc& ngc) {
return ngc.newstr(local[1].tostr());
}
var builtin_size(var* local, gc& ngc) {
var val = local[1];
f64 num = 0;
switch(val.type) {
case vm_num: num = val.num(); break;
case vm_str: num = val.str().length(); break;
case vm_vec: num = val.vec().size(); break;
case vm_hash: num = val.hash().size(); break;
case vm_map: num = val.map().mapper.size(); break;
}
return var::num(num);
}
var builtin_time(var* local, gc& ngc) {
var val = local[1];
if (val.type!=vm_num) {
return nas_err("time", "\"begin\" must be number");
}
time_t begin = (time_t)val.num();
return var::num((f64)time(&begin));
}
var builtin_contains(var* local, gc& ngc) {
var hash = local[1];
var key = local[2];
if (hash.type!=vm_hash || key.type!=vm_str) {
return zero;
}
return hash.hash().elems.count(key.str())? one:zero;
}
var builtin_delete(var* local, gc& ngc) {
var hash = local[1];
var key = local[2];
if (hash.type!=vm_hash) {
return nas_err("delete", "\"hash\" must be hash");
}
if (key.type!=vm_str) {
return nil;
}
if (hash.hash().elems.count(key.str())) {
hash.hash().elems.erase(key.str());
}
return nil;
}
var builtin_keys(var* local, gc& ngc) {
var hash = local[1];
if (hash.type!=vm_hash && hash.type!=vm_map) {
return nas_err("keys", "\"hash\" must be hash");
}
// avoid being sweeped
var res = ngc.temp = ngc.alloc(vm_vec);
auto& vec = res.vec().elems;
if (hash.type==vm_hash) {
for(const auto& iter : hash.hash().elems) {
vec.push_back(ngc.newstr(iter.first));
}
} else {
for(const auto& iter : hash.map().mapper) {
vec.push_back(ngc.newstr(iter.first));
}
}
ngc.temp=nil;
return res;
}
var builtin_die(var* local, gc& ngc) {
return nas_err("error", local[1].tostr());
}
var builtin_find(var* local, gc& ngc) {
var needle = local[1];
var haystack = local[2];
usize pos = haystack.tostr().find(needle.tostr());
if (pos==std::string::npos) {
return var::num((f64)-1);
}
return var::num((f64)pos);
}
var builtin_type(var* local, gc& ngc) {
switch(local[1].type) {
case vm_none: return ngc.newstr("undefined");
case vm_nil: return ngc.newstr("nil");
case vm_num: return ngc.newstr("num");
case vm_str: return ngc.newstr("str");
case vm_vec: return ngc.newstr("vec");
case vm_hash: return ngc.newstr("hash");
case vm_func: return ngc.newstr("func");
case vm_obj: return ngc.newstr("obj");
case vm_co: return ngc.newstr("coroutine");
case vm_map: return ngc.newstr("namespace");
}
return nil;
}
var builtin_substr(var* local, gc& ngc) {
var str = local[1];
var beg = local[2];
var len = local[3];
if (str.type!=vm_str) {
return nas_err("substr", "\"str\" must be string");
}
if (beg.type!=vm_num || beg.num()<0) {
return nas_err("substr", "\"begin\" should be number >= 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_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 <get> 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<u32> buff;
usize num = ((src.length()+8)>>6)+1;
usize buffsize = num<<4;
buff.resize(buffsize,0);
for(usize i = 0; i<src.length(); i++) {
buff[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; i<buffsize; i += 16) {
u32 f, a = atmp, b = btmp, c = ctmp, d = dtmp;
for(u32 j = 0; j<64; j++) {
if (j<16) f = md5f(b, c, d);
else if (j<32) f = md5g(b, c, d);
else if (j<48) f = md5h(b, c, d);
else f = md5i(b, c, d);
u32 tmp = d;
d = c;
c = b;
b = b+shift((a+f+k[j]+buff[i+idx[j]]),s[j]);
a = tmp;
}
atmp += a;
btmp += b;
ctmp += c;
dtmp += d;
}
return tohex(atmp)+tohex(btmp)+tohex(ctmp)+tohex(dtmp);
}
var builtin_md5(var* local, gc& ngc) {
var str = local[1];
if (str.type!=vm_str) {
return nas_err("md5", "\"str\" must be string");
}
return ngc.newstr(md5(str.str()));
}
var builtin_millisec(var* local, gc& ngc) {
f64 res = std::chrono::duration_cast<std::chrono::milliseconds>
(std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
return var::num(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_gcinfo(var* local, gc& ngc) {
auto den = std::chrono::high_resolution_clock::duration::period::den;
var res = ngc.alloc(vm_hash);
double total = 0;
for(u32 i = 0; i<gc_type_size; ++i) {
total += ngc.gcnt[i];
}
// using ms
auto& map = res.hash().elems;
map["total"] = var::num(ngc.worktime*1.0/den*1000);
map["average"] = var::num(ngc.worktime*1.0/den*1000/total);
map["max_gc"] = var::num(ngc.max_time*1.0/den*1000);
map["max_mark"] = var::num(ngc.max_mark_time*1.0/den*1000);
map["max_sweep"] = var::num(ngc.max_sweep_time*1.0/den*1000);
return res;
}
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);
}
nasal_builtin_table 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},
{"__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},
{"__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},
{"__millisec", builtin_millisec},
{"__gcextd", builtin_gcextend},
{"__gcinfo", builtin_gcinfo},
{"__logtime", builtin_logtime},
{"__ghosttype", builtin_ghosttype},
{nullptr, nullptr}
};