#include "nasal_builtin.h" #include namespace nasal { 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(static_cast(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(static_cast(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_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(static_cast(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; ss << reinterpret_cast(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(static_cast(static_cast(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(static_cast(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(-1.0); } return var::num(static_cast(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 = static_cast(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(static_cast(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_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_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; i (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; itm_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(reinterpret_cast(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}, {"__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}, {"__sleep", builtin_sleep}, {"__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} }; }