Nasal-Interpreter/nasal_builtin.h

1233 lines
35 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.

#pragma once
#include "nasal_gc.h"
#ifndef _MSC_VER
#include <unistd.h>
#include <dirent.h>
#else
#pragma warning (disable:4566) // i know i'm using utf-8, fuck you
#define _CRT_SECURE_NO_DEPRECATE 1
#define _CRT_NONSTDC_NO_DEPRECATE 1
#include <io.h>
#include <direct.h>
#endif
#include <sstream>
#include <cmath>
#include <thread>
#include <sys/stat.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#include <sys/wait.h>
#endif
#if defined __APPLE__
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#endif
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 {vm_num,(f64)-1};
return {vm_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_fin(var* local,gc& ngc)
{
var val=local[1];
if(val.type!=vm_str)
return nas_err("io::fin","\"filename\" must be string");
std::ifstream fin(val.str(),std::ios::binary);
std::stringstream rd;
if(!fin.fail())
rd<<fin.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 fout(val.str());
if(fout.fail())
return nas_err("io::fout","cannot open <"+val.str()+">");
fout<<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 string& deli=delimeter.str();
const string& 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,pos=s.find(deli,0);
while(pos!=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 {vm_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 {vm_num,f64((i32)val.tonum())};
}
var builtin_floor(var* local,gc& ngc)
{
var val=local[1];
return {vm_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 {vm_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;
}
return {vm_num,num};
}
var builtin_i32xor(var* local,gc& ngc)
{
return {vm_num,(f64)(i32(local[1].num())^i32(local[2].num()))};
}
var builtin_i32and(var* local,gc& ngc)
{
return {vm_num,(f64)(i32(local[1].num())&i32(local[2].num()))};
}
var builtin_i32or(var* local,gc& ngc)
{
return {vm_num,(f64)(i32(local[1].num())|i32(local[2].num()))};
}
var builtin_i32nand(var* local,gc& ngc)
{
return {vm_num,(f64)(~(i32(local[1].num())&i32(local[2].num())))};
}
var builtin_i32not(var* local,gc& ngc)
{
return {vm_num,(f64)(~i32(local[1].num()))};
}
var builtin_u32xor(var* local,gc& ngc)
{
return {vm_num,(f64)(u32(local[1].num())^u32(local[2].num()))};
}
var builtin_u32and(var* local,gc& ngc)
{
return {vm_num,(f64)(u32(local[1].num())&u32(local[2].num()))};
}
var builtin_u32or(var* local,gc& ngc)
{
return {vm_num,(f64)(u32(local[1].num())|u32(local[2].num()))};
}
var builtin_u32nand(var* local,gc& ngc)
{
return {vm_num,(f64)(u32)(~(u32(local[1].num())&u32(local[2].num())))};
}
var builtin_u32not(var* local,gc& ngc)
{
return {vm_num,(f64)(u32)(~u32(local[1].num()))};
}
var builtin_pow(var* local,gc& ngc)
{
var x=local[1];
var y=local[2];
if(x.type!=vm_num || y.type!=vm_num)
return {vm_num,std::nan("")};
return {vm_num,std::pow(x.num(),y.num())};
}
var builtin_sin(var* local,gc& ngc)
{
var val=local[1];
return {vm_num,val.type==vm_num?sin(val.num()):std::nan("")};
}
var builtin_cos(var* local,gc& ngc)
{
var val=local[1];
return {vm_num,val.type==vm_num?cos(val.num()):std::nan("")};
}
var builtin_tan(var* local,gc& ngc)
{
var val=local[1];
return {vm_num,val.type==vm_num?tan(val.num()):std::nan("")};
}
var builtin_exp(var* local,gc& ngc)
{
var val=local[1];
return {vm_num,val.type==vm_num?exp(val.num()):std::nan("")};
}
var builtin_lg(var* local,gc& ngc)
{
var val=local[1];
return {vm_num,val.type==vm_num?log(val.num())/log(10.0):std::nan("")};
}
var builtin_ln(var* local,gc& ngc)
{
var val=local[1];
return {vm_num,val.type==vm_num?log(val.num()):std::nan("")};
}
var builtin_sqrt(var* local,gc& ngc)
{
var val=local[1];
return {vm_num,val.type==vm_num?sqrt(val.num()):std::nan("")};
}
var builtin_atan2(var* local,gc& ngc)
{
var x=local[1];
var y=local[2];
if(x.type!=vm_num || y.type!=vm_num)
return {vm_num,std::nan("")};
return {vm_num,atan2(y.num(),x.num())};
}
var builtin_isnan(var* local,gc& ngc)
{
var x=local[1];
return (x.type==vm_num && std::isnan(x.num()))?one:zero;
}
var builtin_time(var* local,gc& ngc)
{
var val=local[1];
if(val.type!=vm_num)
return nas_err("time","\"begin_time\" must be number");
time_t begin_time=(time_t)val.num();
return {vm_num,(f64)time(&begin_time)};
}
var builtin_contains(var* local,gc& ngc)
{
var hash=local[1];
var key=local[2];
if(hash.type!=vm_hash)
return nas_err("contains","\"hash\" must be hash");
if(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)
return nas_err("keys","\"hash\" must be hash");
// avoid being sweeped
var res=ngc.temp=ngc.alloc(vm_vec);
auto& vec=res.vec().elems;
for(auto& iter:hash.hash().elems)
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==string::npos)
return {vm_num,(f64)-1};
return {vm_num,(f64)pos};
}
var builtin_type(var* local,gc& ngc)
{
switch(local[1].type)
{
case vm_none: return ngc.newstr("undefined");break;
case vm_nil: return ngc.newstr("nil"); break;
case vm_num: return ngc.newstr("num"); break;
case vm_str: return ngc.newstr("str"); break;
case vm_vec: return ngc.newstr("vec"); break;
case vm_hash: return ngc.newstr("hash"); break;
case vm_func: return ngc.newstr("func"); break;
case vm_obj: return ngc.newstr("obj"); break;
case vm_co: return ngc.newstr("coroutine");break;
}
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() || begin+length>str.str().length())
return nas_err("susbtr","index out of range");
return ngc.newstr(str.str().substr(begin,length));
}
var builtin_streq(var* local,gc& ngc)
{
var a=local[1];
var b=local[2];
return {vm_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 {vm_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_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;
}
void obj_file_dtor(void* ptr)
{
fclose((FILE*)ptr);
}
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(nas_obj::file,res,obj_file_dtor);
return ret;
}
var builtin_close(var* local,gc& ngc)
{
var fd=local[1];
if(!fd.objchk(nas_obj::file))
return nas_err("close","not a valid filehandle");
fclose((FILE*)fd.obj().ptr);
fd.obj().ptr=nullptr;
return nil;
}
var builtin_read(var* local,gc& ngc)
{
var fd=local[1];
var buf=local[2];
var len=local[3];
if(!fd.objchk(nas_obj::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 {vm_num,res};
}
var builtin_write(var* local,gc& ngc)
{
var fd=local[1];
var str=local[2];
if(!fd.objchk(nas_obj::file))
return nas_err("write","not a valid filehandle");
if(str.type!=vm_str)
return nas_err("write","\"str\" must be string");
return {vm_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(nas_obj::file))
return nas_err("seek","not a valid filehandle");
return {vm_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(nas_obj::file))
return nas_err("tell","not a valid filehandle");
return {vm_num,(f64)ftell((FILE*)fd.obj().ptr)};
}
var builtin_readln(var* local,gc& ngc)
{
var fd=local[1];
if(!fd.objchk(nas_obj::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={
{vm_num,(f64)buf.st_dev},
{vm_num,(f64)buf.st_ino},
{vm_num,(f64)buf.st_mode},
{vm_num,(f64)buf.st_nlink},
{vm_num,(f64)buf.st_uid},
{vm_num,(f64)buf.st_gid},
{vm_num,(f64)buf.st_rdev},
{vm_num,(f64)buf.st_size},
{vm_num,(f64)buf.st_atime},
{vm_num,(f64)buf.st_mtime},
{vm_num,(f64)buf.st_ctime}
};
return ret;
}
var builtin_eof(var* local,gc& ngc)
{
var fd=local[1];
if(!fd.objchk(nas_obj::file))
return nas_err("readln","not a valid filehandle");
return {vm_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<bit+len;++i)
if(s[i>>3]&(1<<(7-(i&7))))
res|=1<<(bit+len-i-1);
return {vm_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<bit+len;++i)
if(s[i>>3]&(1<<(7-(i&7))))
res|=1<<(bit+len-i-1);
if(res&(1<<(len-1)))
res|=~((1<<len)-1);
return {vm_num,(f64)((i32)res)};
}
var builtin_setfld(var* local,gc& ngc)
{
// bits.setfld(s,0,8,69);
// set 01000101(69) to string will get this:
// 10100010(162)
// so s[0]=162
var str=local[1];
var startbit=local[2];
var length=local[3];
var value=local[4];
if(str.type!=vm_str || str.val.gcobj->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<bit+len;++i)
{
if(val&(1<<(i-bit)))
s[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 nas_err("sleep","\"duration\" must be number");
std::this_thread::sleep_for(std::chrono::microseconds(i64(val.num()*1e6)));
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({vm_num,(f64)fd[0]});
res.vec().elems.push_back({vm_num,(f64)fd[1]});
return res;
#endif
return nas_err("pipe","not supported for windows");
}
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 {vm_num,(f64)res};
#endif
return nas_err("fork","not supported for windows");
}
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({vm_num,(f64)ret_pid});
vec.vec().elems.push_back({vm_num,(f64)status});
return vec;
#endif
return nas_err("waitpid","not supported for windows");
}
void obj_dir_dtor(void* ptr)
{
#ifndef _MSC_VER
closedir((DIR*)ptr);
#else
FindClose(ptr);
#endif
}
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(nas_obj::dir,p,obj_dir_dtor);
return ret;
}
var builtin_readdir(var* local,gc& ngc)
{
var handle=local[1];
if(!handle.objchk(nas_obj::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(nas_obj::dir))
return nas_err("closedir","not a valid dir handle");
#ifndef _MSC_VER
closedir((DIR*)handle.obj().ptr);
#else
FindClose(handle.obj().ptr);
#endif
handle.obj().ptr=nullptr;
return nil;
}
var builtin_chdir(var* local,gc& ngc)
{
var path=local[1];
if(path.type!=vm_str)
return {vm_num,(f64)-1};
return {vm_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;
}
void dylib_dtor(void* ptr)
{
#ifdef _WIN32
FreeLibrary((HMODULE)ptr);
#else
dlclose(ptr);
#endif
}
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.alloc(vm_obj);
ret.obj().set(nas_obj::dylib,ptr,dylib_dtor);
return ret;
}
var builtin_dlsym(var* local,gc& ngc)
{
var lib=local[1];
var sym=local[2];
if(!lib.objchk(nas_obj::dylib))
return nas_err("dlsym","\"lib\" is not a valid dynamic lib");
if(sym.type!=vm_str)
return nas_err("dlsym","\"sym\" must be string");
// "get" function
#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("dlsym","cannot find get function");
// get function pointer by name
void* ptr=(void*)((getptr)func)(sym.str().c_str());
if(!ptr)
return nas_err("dlsym","cannot find function <"+sym.str()+">");
var ret=ngc.alloc(vm_obj);
ret.obj().set(nas_obj::faddr,ptr);
return ret;
}
var builtin_dlclose(var* local,gc& ngc)
{
var libptr=local[1];
if(!libptr.objchk(nas_obj::dylib))
return nas_err("dlclose","\"lib\" is not a valid dynamic lib");
#ifdef _WIN32
FreeLibrary((HMODULE)libptr.obj().ptr);
#else
dlclose(libptr.obj().ptr);
#endif
libptr.obj().ptr=nullptr;
return nil;
}
var builtin_dlcall(var* local,gc& ngc)
{
var fp=local[1];
var args=local[2];
if(!fp.objchk(nas_obj::faddr))
return nas_err("dlcall","\"funcptr\" is not a valid function pointer");
auto& vec=args.vec().elems;
return ((mod)fp.obj().ptr)(vec.data(),vec.size(),&ngc);
}
var builtin_platform(var* local,gc& ngc)
{
#if defined _WIN32 || defined _WIN64
return ngc.newstr("windows");
#elif defined __linux__
return ngc.newstr("linux");
#elif defined __APPLE__
return ngc.newstr("macOS");
#endif
return ngc.newstr("unknown");
}
// md5 related functions
string tohex(u32 num)
{
const char str16[]="0123456789abcdef";
string str="";
for(u32 i=0;i<4;i++,num>>=8)
{
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;
}
string md5(const 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_cocreate(var* local,gc& ngc)
{
// +-----------------+
// | old pc | <- top[0]
// +-----------------+
// | old localr | <- top[-1]
// +-----------------+
// | old upvalr | <- top[-2]
// +-----------------+
// | local scope |
// | ... |
// +-----------------+ <- local pointer stored in localr
// | old funcr | <- old function stored in funcr
// +-----------------+
var func=local[1];
if(func.type!=vm_func)
return nas_err("coroutine::create","must use a function to create coroutine");
if(ngc.cort)
return nas_err("coroutine::create","cannot create another coroutine in a coroutine");
var co=ngc.alloc(vm_co);
nas_co& cort=co.co();
cort.pc=func.func().entry-1;
cort.top[0]=nil;
cort.localr=cort.top+1;
cort.top=cort.localr+func.func().lsize;
cort.localr[0]=func.func().local[0];
cort.top[0]=nil; // old upvalr
cort.top++;
cort.top[0]={vm_addr,(var*)nullptr}; // old localr
cort.top++;
cort.top[0]={vm_ret,(u32)0}; // old pc, set to zero to make op_ret recognizing this as coroutine function
cort.funcr=func; // make sure the coroutine function can use correct upvalues
cort.status=nas_co::suspended;
return co;
}
var builtin_coresume(var* local,gc& ngc)
{
if(ngc.cort)
return nas_err("coroutine::resume","cannot start another coroutine when one is running");
var co=local[1];
if(co.type!=vm_co)
return nas_err("coroutine::resume","must use a coroutine object");
if(co.co().status==nas_co::dead)
return nil;
ngc.ctxchg(co.co());
return ngc.top[0];
}
var builtin_coyield(var* local,gc& ngc)
{
if(!ngc.cort)
return nas_err("coroutine::yield","no coroutine is running");
ngc.ctxreserve();
// this will set to main stack top
// then builtin_coresume will return it
return local[1];
}
var builtin_costatus(var* local,gc& ngc)
{
var co=local[1];
if(co.type!=vm_co)
return nas_err("coroutine::status","must use a coroutine object");
switch(co.co().status)
{
case nas_co::suspended: return ngc.newstr("suspended");break;
case nas_co::running: return ngc.newstr("running"); break;
case nas_co::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::milliseconds>
(std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
return {vm_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_logtime(var* local,gc& ngc)
{
time_t t=time(nullptr);
tm* tm_t=localtime(&t);
char s[32];
sprintf(s,"%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);
}
// 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 },
{"__fin", builtin_fin },
{"__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 },
{"__i32xor", builtin_i32xor },
{"__i32and", builtin_i32and },
{"__i32or", builtin_i32or },
{"__i32nand", builtin_i32nand },
{"__i32not", builtin_i32not },
{"__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 },
{"__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 },
{"__dlsym", builtin_dlsym },
{"__dlclose", builtin_dlclose },
{"__dlcall", builtin_dlcall },
{"__platform",builtin_platform},
{"__md5", builtin_md5 },
{"__cocreate",builtin_cocreate},
{"__coresume",builtin_coresume},
{"__coyield", builtin_coyield },
{"__costatus",builtin_costatus},
{"__corun" ,builtin_corun },
{"__millisec",builtin_millisec},
{"__sysargv", builtin_sysargv },
{"__logtime", builtin_logtime },
{nullptr, nullptr }
};