Nasal-Interpreter/nasal_builtin.h

1159 lines
36 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.

#ifndef __NASAL_BUILTIN_H__
#define __NASAL_BUILTIN_H__
#if defined __APPLE__
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#endif
/*
builtin functions must be called inside a function like this:
var print=func(elems...){
return __builtin_print(elems);
}
builtin function __builtin_print is wrapped up by print
*/
// declaration of builtin functions
// to add new builtin function, declare it here and write the definition below
#define nas_native(name) nasal_ref name(nasal_ref*,nasal_gc&)
nas_native(builtin_print);
nas_native(builtin_append);
nas_native(builtin_setsize);
nas_native(builtin_system);
nas_native(builtin_input);
nas_native(builtin_fin);
nas_native(builtin_fout);
nas_native(builtin_split);
nas_native(builtin_rand);
nas_native(builtin_id);
nas_native(builtin_int);
nas_native(builtin_num);
nas_native(builtin_pop);
nas_native(builtin_str);
nas_native(builtin_size);
nas_native(builtin_xor);
nas_native(builtin_and);
nas_native(builtin_or);
nas_native(builtin_nand);
nas_native(builtin_not);
nas_native(builtin_sin);
nas_native(builtin_cos);
nas_native(builtin_tan);
nas_native(builtin_exp);
nas_native(builtin_lg);
nas_native(builtin_ln);
nas_native(builtin_sqrt);
nas_native(builtin_atan2);
nas_native(builtin_isnan);
nas_native(builtin_time);
nas_native(builtin_contains);
nas_native(builtin_delete);
nas_native(builtin_keys);
nas_native(builtin_import);
nas_native(builtin_die);
nas_native(builtin_type);
nas_native(builtin_substr);
nas_native(builtin_streq);
nas_native(builtin_left);
nas_native(builtin_right);
nas_native(builtin_cmp);
nas_native(builtin_chr);
nas_native(builtin_open);
nas_native(builtin_close);
nas_native(builtin_read);
nas_native(builtin_write);
nas_native(builtin_seek);
nas_native(builtin_tell);
nas_native(builtin_readln);
nas_native(builtin_stat);
nas_native(builtin_eof);
nas_native(builtin_fld);
nas_native(builtin_sfld);
nas_native(builtin_setfld);
nas_native(builtin_buf);
nas_native(builtin_sleep);
nas_native(builtin_pipe);
nas_native(builtin_fork);
nas_native(builtin_opendir);
nas_native(builtin_readdir);
nas_native(builtin_closedir);
nas_native(builtin_chdir);
nas_native(builtin_environ);
nas_native(builtin_getcwd);
nas_native(builtin_getenv);
nas_native(builtin_dlopen);
nas_native(builtin_dlsym);
nas_native(builtin_dlclose);
nas_native(builtin_dlcall);
nas_native(builtin_platform);
nas_native(builtin_gc);
nasal_ref builtin_err(const char* func_name,std::string info)
{
std::cerr<<"[vm] "<<func_name<<": "<<info<<".\n";
return {vm_none};
}
// 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;
nasal_ref (*func)(nasal_ref*,nasal_gc&);
} builtin[]=
{
{"__builtin_print", builtin_print },
{"__builtin_append", builtin_append },
{"__builtin_setsize", builtin_setsize },
{"__builtin_system", builtin_system },
{"__builtin_input", builtin_input },
{"__builtin_fin", builtin_fin },
{"__builtin_fout", builtin_fout },
{"__builtin_split", builtin_split },
{"__builtin_rand", builtin_rand },
{"__builtin_id", builtin_id },
{"__builtin_int", builtin_int },
{"__builtin_num", builtin_num },
{"__builtin_pop", builtin_pop },
{"__builtin_str", builtin_str },
{"__builtin_size", builtin_size },
{"__builtin_xor", builtin_xor },
{"__builtin_and", builtin_and },
{"__builtin_or", builtin_or },
{"__builtin_nand", builtin_nand },
{"__builtin_not", builtin_not },
{"__builtin_sin", builtin_sin },
{"__builtin_cos", builtin_cos },
{"__builtin_tan", builtin_tan },
{"__builtin_exp", builtin_exp },
{"__builtin_lg", builtin_lg },
{"__builtin_ln", builtin_ln },
{"__builtin_sqrt", builtin_sqrt },
{"__builtin_atan2", builtin_atan2 },
{"__builtin_isnan", builtin_isnan },
{"__builtin_time", builtin_time },
{"__builtin_contains",builtin_contains},
{"__builtin_delete", builtin_delete },
{"__builtin_keys", builtin_keys },
{"__builtin_import", builtin_import },
{"__builtin_die", builtin_die },
{"__builtin_type", builtin_type },
{"__builtin_substr", builtin_substr },
{"__builtin_streq", builtin_streq },
{"__builtin_left", builtin_left },
{"__builtin_right", builtin_right },
{"__builtin_cmp", builtin_cmp },
{"__builtin_chr", builtin_chr },
{"__builtin_open", builtin_open },
{"__builtin_close", builtin_close },
{"__builtin_read", builtin_read },
{"__builtin_write", builtin_write },
{"__builtin_seek", builtin_seek },
{"__builtin_tell", builtin_tell },
{"__builtin_readln", builtin_readln },
{"__builtin_stat", builtin_stat },
{"__builtin_eof", builtin_eof },
{"__builtin_fld", builtin_fld },
{"__builtin_sfld", builtin_sfld },
{"__builtin_setfld", builtin_setfld },
{"__builtin_buf", builtin_buf },
{"__builtin_sleep", builtin_sleep },
{"__builtin_pipe", builtin_pipe },
{"__builtin_fork", builtin_fork },
{"__builtin_opendir", builtin_opendir },
{"__builtin_readdir", builtin_readdir },
{"__builtin_closedir",builtin_closedir},
{"__builtin_chdir", builtin_chdir },
{"__builtin_environ", builtin_environ },
{"__builtin_getcwd", builtin_getcwd },
{"__builtin_getenv", builtin_getenv },
{"__builtin_dlopen", builtin_dlopen },
{"__builtin_dlsym", builtin_dlsym },
{"__builtin_dlclose", builtin_dlclose },
{"__builtin_dlcall", builtin_dlcall },
{"__builtin_platform",builtin_platform},
{"__builtin_gc", builtin_gc },
{nullptr, nullptr }
};
nasal_ref builtin_print(nasal_ref* local,nasal_gc& gc)
{
// get arguments
// local[0] is reserved for 'me'
nasal_ref vec=local[1];
// main process
for(auto& i:vec.vec().elems)
switch(i.type)
{
case vm_none: std::cout<<"null"; break;
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<i.num(); break;
case vm_str: std::cout<<i.str(); break;
case vm_vec: i.vec().print(); break;
case vm_hash: i.hash().print(); break;
case vm_func: std::cout<<"func(...){...}"; break;
case vm_obj: std::cout<<"<object>"; break;
}
std::cout<<std::flush;
// generate return value
return nil;
}
nasal_ref builtin_append(nasal_ref* local,nasal_gc& gc)
{
nasal_ref vec=local[1];
nasal_ref elem=local[2];
if(vec.type!=vm_vec)
return builtin_err("append","\"vector\" must be vector");
auto& ref_vec=vec.vec().elems;
for(auto& i:elem.vec().elems)
ref_vec.push_back(i);
return nil;
}
nasal_ref builtin_setsize(nasal_ref* local,nasal_gc& gc)
{
nasal_ref vec=local[1];
nasal_ref size=local[2];
if(vec.type!=vm_vec)
return builtin_err("setsize","\"vector\" must be vector");
if(size.type!=vm_num)
return builtin_err("setsize","\"size\" is not a number");
int num=(int)size.num();
if(num<0)
return builtin_err("setsize","\"size\" must be greater than -1");
vec.vec().elems.resize(num,nil);
return nil;
}
nasal_ref builtin_system(nasal_ref* local,nasal_gc& gc)
{
nasal_ref str=local[1];
if(str.type!=vm_str)
return builtin_err("system","\"str\" must be string");
return {vm_num,(double)system(str.str().c_str())};
}
nasal_ref builtin_input(nasal_ref* local,nasal_gc& gc)
{
nasal_ref ret=gc.alloc(vm_str);
std::cin>>ret.str();
return ret;
}
nasal_ref builtin_fin(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
if(val.type!=vm_str)
return builtin_err("io.fin","\"filename\" must be string");
std::string& filename=val.str();
std::ifstream fin(filename);
if(!fin.fail())
{
nasal_ref ret=gc.alloc(vm_str);
std::stringstream rd;
rd<<fin.rdbuf();
ret.str()=rd.str();
return ret;
}
return builtin_err("io.fin","cannot open \""+filename+"\"");
}
nasal_ref builtin_fout(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
nasal_ref str=local[2];
if(val.type!=vm_str)
return builtin_err("io.fout","\"filename\" must be string");
if(str.type!=vm_str)
return builtin_err("io.fout","\"str\" must be string");
std::ofstream fout(val.str());
if(fout.fail())
return builtin_err("io.fout","cannot open \""+val.str()+"\"");
fout<<str.str();
return nil;
}
nasal_ref builtin_split(nasal_ref* local,nasal_gc& gc)
{
nasal_ref deli_val=local[1];
nasal_ref str_val=local[2];
if(deli_val.type!=vm_str)
return builtin_err("split","\"separator\" must be string");
if(str_val.type!=vm_str)
return builtin_err("split","\"str\" must be string");
std::string& delimeter=deli_val.str();
std::string& source=str_val.str();
size_t delimeter_len=delimeter.length();
size_t source_len=source.length();
// push it to local scope to avoid being sweeped
if(gc.top+1>=gc.stack+STACK_MAX_DEPTH-1)
builtin_err("split","expand temporary space error:stackoverflow");
(++gc.top)[0]=gc.alloc(vm_vec);
std::vector<nasal_ref>& vec=gc.top[0].vec().elems;
if(!delimeter_len)
{
for(int i=0;i<source_len;++i)
{
vec.push_back(gc.alloc(vm_str));
vec.back().str()=source[i];
}
--gc.top;
return gc.top[1];
}
std::string tmp="";
for(int i=0;i<source_len;++i)
{
bool check_delimeter=false;
if(source[i]==delimeter[0])
for(int j=0;j<delimeter_len;++j)
{
if(i+j>=source_len || source[i+j]!=delimeter[j])
break;
if(j==delimeter_len-1)
check_delimeter=true;
}
if(check_delimeter)
{
if(tmp.length())
{
vec.push_back(gc.alloc(vm_str));
vec.back().str()=tmp;
tmp="";
}
i+=delimeter_len-1;
}
else
tmp+=source[i];
}
if(tmp.length())
{
vec.push_back(gc.alloc(vm_str));
vec.back().str()=tmp;
tmp="";
}
--gc.top;
return gc.top[1];
}
nasal_ref builtin_rand(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
if(val.type!=vm_num && val.type!=vm_nil)
return builtin_err("rand","\"seed\" must be nil or number");
if(val.type==vm_num)
{
srand((unsigned int)val.num());
return nil;
}
double num=0;
for(int i=0;i<5;++i)
num=(num+rand())*(1.0/(RAND_MAX+1.0));
return {vm_num,num};
}
nasal_ref builtin_id(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
nasal_ref ret=gc.alloc(vm_str);
char buf[32];
if(val.type>vm_num)
sprintf(buf,"%p",val.value.gcobj);
else
sprintf(buf,"0");
ret.str()=buf;
return ret;
}
nasal_ref builtin_int(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
if(val.type!=vm_num)
return nil;
int number=(int)val.num();
return {vm_num,(double)number};
}
nasal_ref builtin_num(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
if(val.type==vm_num)
return val;
if(val.type!=vm_str)
return nil;
double res=val.to_number();
if(std::isnan(res))
return nil;
return {vm_num,res};
}
nasal_ref builtin_pop(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
if(val.type!=vm_vec)
return builtin_err("pop","\"vector\" must be vector");
auto& vec=val.vec().elems;
if(vec.size())
{
nasal_ref tmp=vec.back();
vec.pop_back();
return tmp;
}
return nil;
}
nasal_ref builtin_str(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
if(val.type!=vm_num)
return builtin_err("str","\"number\" must be number");
std::string tmp=std::to_string(val.num());
tmp.erase(tmp.find_last_not_of('0')+1,std::string::npos);
tmp.erase(tmp.find_last_not_of('.')+1,std::string::npos);
nasal_ref ret=gc.alloc(vm_str);
ret.str()=tmp;
return ret;
}
nasal_ref builtin_size(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
double num;
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};
}
nasal_ref builtin_xor(nasal_ref* local,nasal_gc& gc)
{
nasal_ref a=local[1];
nasal_ref b=local[2];
if(a.type!=vm_num)
return builtin_err("xor","\"a\" must be number");
if(b.type!=vm_num)
return builtin_err("xor","\"b\" must be number");
int number_a=(int)a.num();
int number_b=(int)b.num();
return {vm_num,(double)(number_a^number_b)};
}
nasal_ref builtin_and(nasal_ref* local,nasal_gc& gc)
{
nasal_ref a=local[1];
nasal_ref b=local[2];
if(a.type!=vm_num)
return builtin_err("and","\"a\" must be number");
if(b.type!=vm_num)
return builtin_err("and","\"b\" must be number");
int number_a=(int)a.num();
int number_b=(int)b.num();
return {vm_num,(double)(number_a&number_b)};
}
nasal_ref builtin_or(nasal_ref* local,nasal_gc& gc)
{
nasal_ref a=local[1];
nasal_ref b=local[2];
if(a.type!=vm_num)
return builtin_err("or","\"a\" must be number");
if(b.type!=vm_num)
return builtin_err("or","\"b\" must be number");
int number_a=(int)a.num();
int number_b=(int)b.num();
return {vm_num,(double)(number_a|number_b)};
}
nasal_ref builtin_nand(nasal_ref* local,nasal_gc& gc)
{
nasal_ref a=local[1];
nasal_ref b=local[2];
if(a.type!=vm_num)
return builtin_err("nand","\"a\" must be number");
if(b.type!=vm_num)
return builtin_err("nand","\"b\" must be number");
int number_a=(int)a.num();
int number_b=(int)b.num();
return {vm_num,(double)(~(number_a&number_b))};
}
nasal_ref builtin_not(nasal_ref* local,nasal_gc& gc)
{
nasal_ref a=local[1];
if(a.type!=vm_num)
return builtin_err("not","\"a\" must be number");
int number=(int)a.num();
return {vm_num,(double)(~number)};
}
nasal_ref builtin_sin(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
if(val.type!=vm_num)
return builtin_err("sin","\"x\" must be number");
return {vm_num,sin(val.num())};
}
nasal_ref builtin_cos(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
if(val.type!=vm_num)
return builtin_err("cos","\"x\" must be number");
return {vm_num,cos(val.num())};
}
nasal_ref builtin_tan(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
if(val.type!=vm_num)
return builtin_err("tan","\"x\" must be number");
return {vm_num,tan(val.num())};
}
nasal_ref builtin_exp(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
if(val.type!=vm_num)
return builtin_err("exp","\"x\" must be number");
return {vm_num,exp(val.num())};
}
nasal_ref builtin_lg(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
if(val.type!=vm_num)
return builtin_err("ln","\"x\" must be number");
return {vm_num,log(val.num())/log(10.0)};
}
nasal_ref builtin_ln(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
if(val.type!=vm_num)
return builtin_err("ln","\"x\" must be number");
return {vm_num,log(val.num())};
}
nasal_ref builtin_sqrt(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
if(val.type!=vm_num)
return builtin_err("sqrt","\"x\" must be number");
return {vm_num,sqrt(val.num())};
}
nasal_ref builtin_atan2(nasal_ref* local,nasal_gc& gc)
{
nasal_ref x=local[1];
nasal_ref y=local[2];
if(x.type!=vm_num)
return builtin_err("atan2","\"x\" must be number");
if(y.type!=vm_num)
return builtin_err("atan2","\"y\" must be number");
return {vm_num,atan2(y.num(),x.num())};
}
nasal_ref builtin_isnan(nasal_ref* local,nasal_gc& gc)
{
nasal_ref x=local[1];
return (x.type==vm_num && std::isnan(x.num()))?one:zero;
}
nasal_ref builtin_time(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
if(val.type!=vm_num)
return builtin_err("time","\"begin_time\" must be number");
time_t begin_time=(time_t)val.num();
return {vm_num,(double)time(&begin_time)};
}
nasal_ref builtin_contains(nasal_ref* local,nasal_gc& gc)
{
nasal_ref hash=local[1];
nasal_ref key=local[2];
if(hash.type!=vm_hash)
return builtin_err("contains","\"hash\" must be hash");
if(key.type!=vm_str)
return builtin_err("contains","\"key\" must be string");
return hash.hash().elems.count(key.str())?one:zero;
}
nasal_ref builtin_delete(nasal_ref* local,nasal_gc& gc)
{
nasal_ref hash=local[1];
nasal_ref key=local[2];
if(hash.type!=vm_hash)
return builtin_err("delete","\"hash\" must be hash");
if(key.type!=vm_str)
return builtin_err("delete","\"key\" must be string");
if(hash.hash().elems.count(key.str()))
hash.hash().elems.erase(key.str());
return nil;
}
nasal_ref builtin_keys(nasal_ref* local,nasal_gc& gc)
{
nasal_ref hash=local[1];
if(hash.type!=vm_hash)
return builtin_err("keys","\"hash\" must be hash");
// push vector into local scope to avoid being sweeped
if(gc.top+1>=gc.stack+STACK_MAX_DEPTH-1)
builtin_err("keys","expand temporary space error:stackoverflow");
(++gc.top)[0]=gc.alloc(vm_vec);
auto& vec=gc.top[0].vec().elems;
for(auto& iter:hash.hash().elems)
{
nasal_ref str=gc.alloc(vm_str);
str.str()=iter.first;
vec.push_back(str);
}
--gc.top;
return gc.top[1];
}
nasal_ref builtin_import(nasal_ref* local,nasal_gc& gc)
{
// this function is used in preprocessing.
// this function will return nothing when running.
return builtin_err(
"import",
"\n\tthis function is used to link files together. "
"\n\tmake sure it is used in global scope. "
"\n\tmake sure it has correct argument(only one arg allowed)"
);
}
nasal_ref builtin_die(nasal_ref* local,nasal_gc& gc)
{
nasal_ref str=local[1];
if(str.type!=vm_str)
return builtin_err("die","\"str\" must be string");
std::cerr<<"[vm] error: "<<str.str()<<'\n';
return nasal_ref(vm_none);
}
nasal_ref builtin_type(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
nasal_ref ret=gc.alloc(vm_str);
switch(val.type)
{
case vm_none: ret.str()="undefined";break;
case vm_nil: ret.str()="nil"; break;
case vm_num: ret.str()="num"; break;
case vm_str: ret.str()="str"; break;
case vm_vec: ret.str()="vec"; break;
case vm_hash: ret.str()="hash"; break;
case vm_func: ret.str()="func"; break;
case vm_obj: ret.str()="obj"; break;
}
return ret;
}
nasal_ref builtin_substr(nasal_ref* local,nasal_gc& gc)
{
nasal_ref str=local[1];
nasal_ref beg=local[2];
nasal_ref len=local[3];
if(str.type!=vm_str)
return builtin_err("substr","\"str\" must be string");
if(beg.type!=vm_num)
return builtin_err("substr","\"begin\" must be number");
if(len.type!=vm_num)
return builtin_err("substr","\"length\" must be number");
int begin=(int)beg.num();
int length=(int)len.num();
if(begin>=str.str().length() || begin+length-1>=str.str().length())
return builtin_err("susbtr","index out of range");
if(length<0)
length=0;
nasal_ref ret=gc.alloc(vm_str);
ret.str()=str.str().substr(begin,length);
return ret;
}
nasal_ref builtin_streq(nasal_ref* local,nasal_gc& gc)
{
nasal_ref a=local[1];
nasal_ref b=local[2];
return {vm_num,double((a.type!=vm_str || b.type!=vm_str)?0:(a.str()==b.str()))};
}
nasal_ref builtin_left(nasal_ref* local,nasal_gc& gc)
{
nasal_ref str=local[1];
nasal_ref len=local[2];
if(str.type!=vm_str)
return builtin_err("left","\"string\" must be string");
if(len.type!=vm_num)
return builtin_err("left","\"length\" must be number");
int length=(int)len.num();
if(length<0)
length=0;
nasal_ref ret=gc.alloc(vm_str);
ret.str()=str.str().substr(0,length);
return ret;
}
nasal_ref builtin_right(nasal_ref* local,nasal_gc& gc)
{
nasal_ref str=local[1];
nasal_ref len=local[2];
if(str.type!=vm_str)
return builtin_err("right","\"string\" must be string");
if(len.type!=vm_num)
return builtin_err("right","\"length\" must be number");
int length=(int)len.num();
int srclen=str.str().length();
if(length>srclen)
length=srclen;
if(length<0)
length=0;
nasal_ref ret=gc.alloc(vm_str);
ret.str()=str.str().substr(srclen-length, srclen);
return ret;
}
nasal_ref builtin_cmp(nasal_ref* local,nasal_gc& gc)
{
nasal_ref a=local[1];
nasal_ref b=local[2];
if(a.type!=vm_str)
return builtin_err("cmp","\"a\" must be string");
if(b.type!=vm_str)
return builtin_err("cmp","\"b\" must be string");
return {vm_num,(double)strcmp(a.str().c_str(),b.str().c_str())};
}
nasal_ref builtin_chr(nasal_ref* local,nasal_gc& gc)
{
const char* extend[]={
""," ","","ƒ","","","","",
"ˆ","","Š","","Œ"," ","Ž"," ",
" ","","","","","","","",
"˜","","š","","œ"," ","ž","Ÿ",
" ","¡","¢","£","¤","¥","¦","§",
"¨","©","ª","«","¬"," ","®","¯",
"°","±","²","³","´","µ","","·",
"¸","¹","º","»","¼","½","¾","¿",
"À","Á","Â","Ã","Ä","Å","Æ","Ç",
"È","É","Ê","Ë","Ì","Í","Î","Ï",
"Ð","Ñ","Ò","Ó","Ô","Õ","Ö","×",
"Ø","Ù","Ú","Û","Ü","Ý","Þ","ß",
"à","á","â","ã","ä","å","æ","ç",
"è","é","ê","ë","ì","í","î","ï",
"ð","ñ","ò","ó","ô","õ","ö","÷",
"ø","ù","ú","û","ü","ý","þ","ÿ"
};
nasal_ref code=local[1];
if(code.type!=vm_num)
return builtin_err("chr","\"code\" must be number");
nasal_ref ret=gc.alloc(vm_str);
int num=code.num();
if(0<=num && num<128)
ret.str()=(char)num;
else if(128<=num && num<256)
ret.str()=extend[num-128];
else
ret.str()=" ";
return ret;
}
void obj_file_destructor(void* ptr)
{
fclose((FILE*)ptr);
}
nasal_ref builtin_open(nasal_ref* local,nasal_gc& gc)
{
nasal_ref name=local[1];
nasal_ref mode=local[2];
if(name.type!=vm_str)
return builtin_err("open","\"filename\" must be string");
if(mode.type!=vm_str)
return builtin_err("open","\"mode\" must be string");
FILE* res=fopen(name.str().c_str(),mode.str().c_str());
if(!res)
return builtin_err("open","failed to open file <"+name.str()+"> errno "+std::to_string(errno));
nasal_ref ret=gc.alloc(vm_obj);
ret.obj().type=nasal_obj::file;
ret.obj().ptr=(void*)res;
ret.obj().destructor=obj_file_destructor;
return ret;
}
nasal_ref builtin_close(nasal_ref* local,nasal_gc& gc)
{
nasal_ref fd=local[1];
if(!fd.objchk(nasal_obj::file))
return builtin_err("close","not a valid filehandle");
fclose((FILE*)fd.obj().ptr);
fd.obj().ptr=nullptr;
return nil;
}
nasal_ref builtin_read(nasal_ref* local,nasal_gc& gc)
{
nasal_ref fd=local[1];
nasal_ref buf=local[2];
nasal_ref len=local[3];
if(!fd.objchk(nasal_obj::file))
return builtin_err("read","not a valid filehandle");
if(buf.type!=vm_str)
return builtin_err("read","\"buf\" must be string");
if(buf.value.gcobj->unmut)
return builtin_err("read","\"buf\" is not a mutable string");
if(len.type!=vm_num)
return builtin_err("read","\"len\" must be number");
if(len.num()<=0 || len.num()>=(1<<30))
return builtin_err("read","\"len\" less than 1 or too large");
char* buff=new char[(size_t)len.num()+1];
if(!buff)
return builtin_err("read","memory allocation error");
double res=fread(buff,1,len.num(),(FILE*)fd.obj().ptr);
buf.str()=buff;
delete []buff;
return {vm_num,res};
}
nasal_ref builtin_write(nasal_ref* local,nasal_gc& gc)
{
nasal_ref fd=local[1];
nasal_ref str=local[2];
if(!fd.objchk(nasal_obj::file))
return builtin_err("write","not a valid filehandle");
if(str.type!=vm_str)
return builtin_err("write","\"str\" must be string");
double res=(double)fwrite(str.str().c_str(),1,str.str().length(),(FILE*)fd.obj().ptr);
return {vm_num,res};
}
nasal_ref builtin_seek(nasal_ref* local,nasal_gc& gc)
{
nasal_ref fd=local[1];
nasal_ref pos=local[2];
nasal_ref whence=local[3];
if(!fd.objchk(nasal_obj::file))
return builtin_err("seek","not a valid filehandle");
if(pos.type!=vm_num)
return builtin_err("seek","\"pos\" must be number");
if(whence.type!=vm_num || whence.num()<0 || whence.num()>2)
return builtin_err("seek","\"whence\" must be number between 0 and 2");
double res=fseek((FILE*)fd.obj().ptr,pos.num(),whence.num());
return {vm_num,res};
}
nasal_ref builtin_tell(nasal_ref* local,nasal_gc& gc)
{
nasal_ref fd=local[1];
if(!fd.objchk(nasal_obj::file))
return builtin_err("tell","not a valid filehandle");
double res=ftell((FILE*)fd.obj().ptr);
return {vm_num,res};
}
nasal_ref builtin_readln(nasal_ref* local,nasal_gc& gc)
{
nasal_ref fd=local[1];
if(!fd.objchk(nasal_obj::file))
return builtin_err("readln","not a valid filehandle");
nasal_ref str=gc.alloc(vm_str);
auto& s=str.str();
char c;
while((c=fgetc((FILE*)fd.obj().ptr))!=EOF)
{
if(c=='\r')
continue;
if(c=='\n')
return str;
s+=c;
}
if(s.length())
return str;
return nil;
}
nasal_ref builtin_stat(nasal_ref* local,nasal_gc& gc)
{
nasal_ref name=local[1];
if(name.type!=vm_str)
return builtin_err("stat","\"filename\" must be string");
struct stat buf;
if(stat(name.str().c_str(),&buf)<0)
return builtin_err("stat","failed to open file <"+name.str()+">");
nasal_ref ret=gc.alloc(vm_vec);
ret.vec().elems={
{vm_num,(double)buf.st_dev},
{vm_num,(double)buf.st_ino},
{vm_num,(double)buf.st_mode},
{vm_num,(double)buf.st_nlink},
{vm_num,(double)buf.st_uid},
{vm_num,(double)buf.st_gid},
{vm_num,(double)buf.st_rdev},
{vm_num,(double)buf.st_size},
{vm_num,(double)buf.st_atime},
{vm_num,(double)buf.st_mtime},
{vm_num,(double)buf.st_ctime}
};
return ret;
}
nasal_ref builtin_eof(nasal_ref* local,nasal_gc& gc)
{
nasal_ref fd=local[1];
if(!fd.objchk(nasal_obj::file))
return builtin_err("readln","not a valid filehandle");
double res=feof((FILE*)fd.obj().ptr);
return {vm_num,res};
}
nasal_ref builtin_fld(nasal_ref* local,nasal_gc& gc)
{
// bits.fld(s,0,3);
// if s stores 10100010(162)
// will get 101(5)
nasal_ref str=local[1];
nasal_ref startbit=local[2];
nasal_ref length=local[3];
if(str.type!=vm_str || str.value.gcobj->unmut)
return builtin_err("fld","\"str\" must be mutable string");
if(startbit.type!=vm_num || length.type!=vm_num)
return builtin_err("fld","\"startbit\",\"len\" must be number");
uint32_t bit=(uint32_t)startbit.num();
uint32_t len=(uint32_t)length.num();
if(bit+len>8*str.str().length())
return builtin_err("fld","bitfield out of bounds");
uint32_t res=0;
auto& s=str.str();
for(uint32_t i=bit;i<bit+len;++i)
if(s[i>>3]&(1<<(7-(i&7))))
res|=1<<(bit+len-i-1);
return {vm_num,(double)res};
}
nasal_ref builtin_sfld(nasal_ref* local,nasal_gc& gc)
{
// bits.sfld(s,0,3);
// if s stores 10100010(162)
// will get 101(5) then this will be signed extended to
// 11111101(-3)
nasal_ref str=local[1];
nasal_ref startbit=local[2];
nasal_ref length=local[3];
if(str.type!=vm_str || str.value.gcobj->unmut)
return builtin_err("sfld","\"str\" must be mutable string");
if(startbit.type!=vm_num || length.type!=vm_num)
return builtin_err("sfld","\"startbit\",\"len\" must be number");
uint32_t bit=(uint32_t)startbit.num();
uint32_t len=(uint32_t)length.num();
if(bit+len>8*str.str().length())
return builtin_err("sfld","bitfield out of bounds");
uint32_t res=0;
auto& s=str.str();
for(uint32_t 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,(double)((int32_t)res)};
}
nasal_ref builtin_setfld(nasal_ref* local,nasal_gc& gc)
{
// bits.setfld(s,0,8,69);
// set 01000101(69) to string will get this:
// 10100010(162)
// so s[0]=162
nasal_ref str=local[1];
nasal_ref startbit=local[2];
nasal_ref length=local[3];
nasal_ref value=local[4];
if(str.type!=vm_str || str.value.gcobj->unmut)
return builtin_err("setfld","\"str\" must be mutable string");
if(startbit.type!=vm_num || length.type!=vm_num || value.type!=vm_num)
return builtin_err("setfld","\"startbit\",\"len\",\"val\" must be number");
uint32_t bit=(uint32_t)startbit.num();
uint32_t len=(uint32_t)length.num();
uint64_t val=(uint64_t)value.num();
if(bit+len>8*str.str().length())
return builtin_err("setfld","bitfield out of bounds");
auto& s=str.str();
for(uint32_t 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;
}
nasal_ref builtin_buf(nasal_ref* local,nasal_gc& gc)
{
nasal_ref length=local[1];
if(length.type!=vm_num || length.num()<=0)
return builtin_err("buf","\"len\" must be a number greater than 9");
nasal_ref str=gc.alloc(vm_str);
auto& s=str.str();
s.resize(length.num(),'\0');
return str;
}
nasal_ref builtin_sleep(nasal_ref* local,nasal_gc& gc)
{
nasal_ref val=local[1];
if(val.type!=vm_num)
return builtin_err("sleep","\"duration\" must be number");
usleep((useconds_t)(val.num()*1e6));
return nil;
}
nasal_ref builtin_pipe(nasal_ref* local,nasal_gc& gc)
{
int fd[2];
#ifndef _WIN32
nasal_ref res=gc.alloc(vm_vec);
if(pipe(fd)==-1)
return builtin_err("pipe","failed to create pipe");
res.vec().elems.push_back({vm_num,(double)fd[0]});
res.vec().elems.push_back({vm_num,(double)fd[1]});
return res;
#endif
return builtin_err("pipe","not supported yet");
}
nasal_ref builtin_fork(nasal_ref* local,nasal_gc& gc)
{
#ifndef _WIN32
return {vm_num,(double)fork()};
#endif
return builtin_err("fork","not supported yet");
}
void obj_dir_destructor(void* ptr)
{
closedir((DIR*)ptr);
}
nasal_ref builtin_opendir(nasal_ref* local,nasal_gc& gc)
{
nasal_ref path=local[1];
if(path.type!=vm_str)
return builtin_err("opendir","\"path\" must be string");
DIR* p=opendir(path.str().c_str());
if(!p)
return builtin_err("opendir","cannot open dir <"+path.str()+"> errno "+std::to_string(errno));
nasal_ref ret=gc.alloc(vm_obj);
ret.obj().type=nasal_obj::dir;
ret.obj().ptr=(void*)p;
ret.obj().destructor=obj_dir_destructor;
return ret;
}
nasal_ref builtin_readdir(nasal_ref* local,nasal_gc& gc)
{
nasal_ref handle=local[1];
if(!handle.objchk(nasal_obj::dir))
return builtin_err("readdir","not a valid dir handle");
dirent* p=readdir((DIR*)handle.obj().ptr);
if(!p)
return nil;
nasal_ref ret=gc.alloc(vm_str);
ret.str()=p->d_name;
return ret;
}
nasal_ref builtin_closedir(nasal_ref* local,nasal_gc& gc)
{
nasal_ref handle=local[1];
if(!handle.objchk(nasal_obj::dir))
return builtin_err("closedir","not a valid dir handle");
closedir((DIR*)handle.obj().ptr);
handle.obj().ptr=nullptr;
return nil;
}
nasal_ref builtin_chdir(nasal_ref* local,nasal_gc& gc)
{
nasal_ref path=local[1];
if(path.type!=vm_str)
return builtin_err("chdir","\"path\" must be string");
if(chdir(path.str().c_str())<0)
return builtin_err("chdir","failed to execute chdir");
return nil;
}
nasal_ref builtin_environ(nasal_ref* local,nasal_gc& gc)
{
char** env=environ;
if(gc.top+1>=gc.stack+STACK_MAX_DEPTH-1)
builtin_err("environ","expand temporary space error:stackoverflow");
(++gc.top)[0]=gc.alloc(vm_vec);
auto& vec=gc.top[0].vec().elems;
while(*env)
{
auto s=gc.alloc(vm_str);
s.str()=*env;
vec.push_back(s);
++env;
}
--gc.top;
return gc.top[1];
}
nasal_ref builtin_getcwd(nasal_ref* local,nasal_gc& gc)
{
char buf[1024];
getcwd(buf,sizeof(buf));
nasal_ref str=gc.alloc(vm_str);
str.str()=buf;
return str;
}
nasal_ref builtin_getenv(nasal_ref* local,nasal_gc& gc)
{
nasal_ref envvar=local[1];
if(envvar.type!=vm_str)
return builtin_err("getenv","\"envvar\" must be string");
char* res=getenv(envvar.str().c_str());
if(!res)
return nil;
nasal_ref str=gc.alloc(vm_str);
str.str()=res;
return str;
}
void obj_dylib_destructor(void* ptr)
{
#ifdef _WIN32
FreeLibrary((HMODULE)ptr);
#else
dlclose(ptr);
#endif
}
nasal_ref builtin_dlopen(nasal_ref* local,nasal_gc& gc)
{
nasal_ref dlname=local[1];
if(dlname.type!=vm_str)
return builtin_err("dlopen","\"libname\" must be string");
#ifdef _WIN32
wchar_t* str=new wchar_t[dlname.str().size()+1];
memset(str,0,sizeof(wchar_t)*dlname.str().size()+1);
mbstowcs(str,dlname.str().c_str(),dlname.str().size()+1);
void* ptr=LoadLibraryW(str);
delete []str;
#else
void* ptr=dlopen(dlname.str().c_str(),RTLD_LOCAL|RTLD_LAZY);
#endif
if(!ptr)
return builtin_err("dlopen","cannot open dynamic lib <"+dlname.str()+">");
nasal_ref ret=gc.alloc(vm_obj);
ret.obj().type=nasal_obj::dylib;
ret.obj().ptr=ptr;
ret.obj().destructor=obj_dylib_destructor;
return ret;
}
nasal_ref builtin_dlsym(nasal_ref* local,nasal_gc& gc)
{
nasal_ref libptr=local[1];
nasal_ref sym=local[2];
if(!libptr.objchk(nasal_obj::dylib))
return builtin_err("dlsym","\"lib\" is not a valid dynamic lib");
if(sym.type!=vm_str)
return builtin_err("dlsym","\"sym\" must be string");
#ifdef _WIN32
void* func=(void*)GetProcAddress((HMODULE)libptr.obj().ptr,sym.str().c_str());
#else
void* func=dlsym(libptr.obj().ptr,sym.str().c_str());
#endif
if(!func)
return builtin_err("dlsym","cannot find symbol \""+sym.str()+"\"");
nasal_ref ret=gc.alloc(vm_obj);
ret.obj().type=nasal_obj::externfunc;
ret.obj().ptr=func;
return ret;
}
nasal_ref builtin_dlclose(nasal_ref* local,nasal_gc& gc)
{
nasal_ref libptr=local[1];
if(!libptr.objchk(nasal_obj::dylib))
return builtin_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;
}
nasal_ref builtin_dlcall(nasal_ref* local,nasal_gc& gc)
{
nasal_ref funcptr=local[1];
nasal_ref args=local[2];
if(!funcptr.objchk(nasal_obj::externfunc))
return builtin_err("dlcall","\"funcptr\" is not a valid function pointer");
typedef nasal_ref (*extern_func)(std::vector<nasal_ref>&,nasal_gc&);
extern_func func=(extern_func)funcptr.obj().ptr;
return func(args.vec().elems,gc);
}
nasal_ref builtin_platform(nasal_ref* local,nasal_gc& gc)
{
nasal_ref ret=gc.alloc(vm_str);
#if defined _WIN32 || defined _WIN64
ret.str()="windows";
#elif defined __linux__
ret.str()="linux";
#elif defined __APPLE__
ret.str()="macOS";
#endif
return ret;
}
nasal_ref builtin_gc(nasal_ref* local,nasal_gc& gc)
{
gc.mark();
gc.sweep();
return nil;
}
#endif