#include #include #include // #include #include #include #include #include #include #include #include #include #include "runtime.h" using namespace std; extern int g_argc; extern char **g_argv; #define BUILTIN_GUARD_STACKSIZE(nm,sz) {if(S.size()<(sz))throw string("Builtin '" nm "' needs " #sz " stack item")+(sz==1?"":"s");} stack>> scopestack; //Each stack frame contains the variables local to the stack frame. The bool is //whether it was already present in the previous frame, and the Stackitem //is the value held by the previous frame, if any. //please extern this shit with const! unordered_map&,unordered_map&)>> builtins={ {"+",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("+",2) Stackitem b=move(S.back()); S.pop_back(); Stackitem a=move(S.back()); S.pop_back(); if(a.type==SIT_INT&&b.type==SIT_INT){ S.emplace_back(a.intval+b.intval); return; } if(a.type==SIT_ARR||b.type==SIT_ARR){ if(a.type!=SIT_ARR){ b.arrval.insert(b.arrval.begin(),move(a)); S.push_back(move(b)); } else if(b.type!=SIT_ARR){ a.arrval.push_back(move(b)); S.push_back(move(a)); } else { const auto bsz=b.arrval.size(); a.arrval.reserve(a.arrval.size()+b.arrval.size()); for(unsigned int i=0;i at least one is string and other is int if(a.type!=SIT_STR){ S.emplace_back(to_string(a.intval)+b.strval); } else if(b.type!=SIT_STR){ S.emplace_back(a.strval+to_string(b.intval)); } else { S.emplace_back(a.strval+b.strval); } }}, {"-",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("-",2) Stackitem b=move(S.back()); S.pop_back(); Stackitem a=move(S.back()); S.pop_back(); if(a.type!=SIT_INT||b.type!=SIT_INT) throw string("Builtin '-' expects two number arguments"); S.emplace_back(a.intval-b.intval); }}, {"*",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("*",2) Stackitem b=move(S.back()); S.pop_back(); Stackitem a=move(S.back()); S.pop_back(); if(a.type!=SIT_INT||b.type!=SIT_INT) throw string("Builtin '*' expects two number arguments"); S.emplace_back(a.intval*b.intval); }}, {"/",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("/",2) Stackitem b=move(S.back()); S.pop_back(); Stackitem a=move(S.back()); S.pop_back(); if(a.type!=SIT_INT||b.type!=SIT_INT) throw string("Builtin '/' expects two number arguments"); S.emplace_back(a.intval/b.intval); }}, {"%",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("%",2) Stackitem b=move(S.back()); S.pop_back(); Stackitem a=move(S.back()); S.pop_back(); if(a.type!=SIT_INT||b.type!=SIT_INT) throw string("Builtin '%' expects two number arguments"); S.emplace_back(a.intval%b.intval); }}, {"&",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("&",2) Stackitem b=move(S.back()); S.pop_back(); Stackitem a=move(S.back()); S.pop_back(); if(a.type!=SIT_INT||b.type!=SIT_INT) throw string("Builtin '&' expects two number arguments"); S.emplace_back(a.intval&b.intval); }}, {"|",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("|",2) Stackitem b=move(S.back()); S.pop_back(); Stackitem a=move(S.back()); S.pop_back(); if(a.type!=SIT_INT||b.type!=SIT_INT) throw string("Builtin '|' expects two number arguments"); S.emplace_back(a.intval|b.intval); }}, {"~",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("~",1) Stackitem &back=S.back(); if(back.type!=SIT_INT)throw string("Builtin '~' expects a number argument"); else back.intval=~back.intval; }}, {"^",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("^",2) Stackitem b=move(S.back()); S.pop_back(); Stackitem a=move(S.back()); S.pop_back(); if(a.type!=SIT_INT||b.type!=SIT_INT) throw string("Builtin '^' expects two number arguments"); S.emplace_back((int)pow(a.intval,b.intval)); }}, {"!",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("!",1) Stackitem &back=S.back(); if(back.type==SIT_ARR){ back.type=SIT_INT; back.intval=!back.arrval.size(); back.arrval.clear(); } else if(back.type==SIT_STR){ back.type=SIT_INT; back.intval=!back.strval.size(); back.strval.clear(); } else back.intval=!back.intval; }}, {"=",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("=",2) Stackitem b=move(S.back()); S.pop_back(); Stackitem a=move(S.back()); S.pop_back(); S.emplace_back(a==b); }}, {"<",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("<",2) Stackitem b=move(S.back()); S.pop_back(); Stackitem a=move(S.back()); S.pop_back(); if(a.type==SIT_ARR&&b.type==SIT_ARR)S.emplace_back(a.arrval.size()",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE(">",2) Stackitem b=move(S.back()); S.pop_back(); Stackitem a=move(S.back()); S.pop_back(); if(a.type==SIT_ARR&&b.type==SIT_ARR)S.emplace_back(a.arrval.size()>b.arrval.size()); else if(a.type==SIT_ARR||b.type==SIT_ARR){ throw string("Builtin '>' cannot accept an array and a non-array argument"); } else if(a.type==SIT_STR&&b.type==SIT_STR)S.emplace_back(a.strval>b.strval); else if(a.type==SIT_INT&&b.type==SIT_INT)S.emplace_back(a.intval>b.intval); else if(a.type==SIT_STR)S.emplace_back(a.strval>to_string(b.intval)); else S.emplace_back(to_string(a.intval)>b.strval); }}, {"print",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("print",1) cout< &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("lf",0) cout< &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("getline",0) string line; getline(cin,line); if(!cin)S.emplace_back(-1); else S.emplace_back(move(line)); }}, {"getc",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("getc",0) char c=cin.get(); if(!cin)S.emplace_back(-1); else S.emplace_back(string(1,c)); }}, {"fprint",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("fprint",2) Stackitem file=move(S.back()); S.pop_back(); Stackitem v=move(S.back()); S.pop_back(); if(file.type!=SIT_INT)throw string("Second argument to 'fprint' not a number"); if(v.type==SIT_STR)write(file.intval,v.strval.data(),v.strval.size()); else { string s=to_string(v); write(file.intval,s.data(),s.size()); } }}, {"flf",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("flf",1) Stackitem file=move(S.back()); S.pop_back(); if(file.type!=SIT_INT)throw string("Argument to 'flf' not a number"); write(file.intval,"\n",1); }}, {"fgetc",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("fgetc",1) Stackitem file=move(S.back()); S.pop_back(); if(file.type!=SIT_INT)throw string("Argument to 'fgetc' not a number"); char c; int ret=read(file.intval,&c,1); if(ret==-1)perror("read"); if(ret<=0)S.emplace_back(-1); else S.emplace_back(string(1,c)); }}, {"fopen",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("fopen",2) Stackitem mode=move(S.back()); S.pop_back(); Stackitem fname=move(S.back()); S.pop_back(); if(fname.type!=SIT_STR)throw string("First argument to 'fopen' not a string"); if(mode.type!=SIT_STR)throw string("Second argument to 'fopen' not a string"); int oflag=0; if(mode.strval.find('r')!=string::npos)oflag|=O_RDONLY; if(mode.strval.find('w')!=string::npos){ if(oflag&O_RDONLY)oflag=(oflag&~O_RDONLY)|O_RDWR; else oflag|=O_WRONLY; } if(mode.strval.find('a')!=string::npos)oflag|=O_APPEND; if(mode.strval.find('x')!=string::npos)oflag|=O_EXCL|O_CREAT; if(oflag==0)oflag=O_RDONLY; int ret=open(fname.strval.c_str(),oflag); if(ret<0)perror("open"); S.emplace_back(ret); }}, {"argc",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("argc",0) S.emplace_back(g_argc); }}, {"argvget",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("argvget",1) Stackitem idx=move(S.back()); S.pop_back(); if(idx.type!=SIT_INT)throw string("Argument to 'argvget' not a number"); if(idx.intval<0||idx.intval>=g_argc)throw string("Argument to 'argvget' out of bounds"); S.emplace_back(g_argv[idx.intval]); }}, {"dup",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("dup",1) S.push_back(S.back()); }}, {"pop",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("pop",1) S.pop_back(); }}, {"swap",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("swap",2) Stackitem b=move(S.back()); S.pop_back(); Stackitem a=move(S.back()); S.pop_back(); S.push_back(move(b)); S.push_back(move(a)); }}, {"store",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("store",2) Stackitem name=move(S.back()); S.pop_back(); if(name.type!=SIT_STR)throw string("Second argument to 'store' not a string"); auto it=variables.find(name.strval); bool exists=it!=variables.end(); if(scopestack.size()&&scopestack.top().find(name.strval)==scopestack.top().end()){ if(exists) scopestack.top()[name.strval]={move(variables[name.strval]),true}; else scopestack.top()[name.strval]={Stackitem(),false}; } //insert_or_assign is C++17 sadly /*if(exists)variables.insert_or_assign(it,make_pair(name.strval,move(S.back()))); else variables[name.strval]=move(S.back());*/ variables[name.strval]=move(S.back()); S.pop_back(); }}, {"gstore",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("gstore",2) Stackitem name=move(S.back()); S.pop_back(); if(name.type!=SIT_STR)throw string("Second argument to 'store' not a string"); variables[name.strval]=move(S.back()); S.pop_back(); }}, {"get",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("get",1) Stackitem name=move(S.back()); S.pop_back(); if(name.type!=SIT_STR)throw string("Argument to 'get' not a string"); S.push_back(variables[name.strval]); }}, //leaves variable with value 0 {"swapoutvar",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("swapoutvar",1) Stackitem name=move(S.back()); S.pop_back(); if(name.type!=SIT_STR)throw string("Second argument to 'swapoutvar' not a string"); auto it=variables.find(name.strval); if(it==variables.end()){ variables.emplace(piecewise_construct, forward_as_tuple(name.strval), forward_as_tuple()); it=variables.find(name.strval); } S.push_back(move(it->second)); it->second.intval=0; it->second.type=SIT_INT; }}, {"enterscope",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("enterscope",0) scopestack.emplace(); }}, {"leavescope",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("leavescope",0) if(scopestack.size()<1)throw string("Scope stack empty while requesting scope leave"); for(const pair> &var : scopestack.top()){ if(var.second.second){ variables[var.first]=move(var.second.first); } else { variables.erase(var.first); } } scopestack.pop(); }}, //positive n: roll OFF top to bottom; negative n: roll off bottom ONTO top {"roll",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("roll",1) Stackitem ns=move(S.back()); S.pop_back(); if(ns.type!=SIT_INT)throw string("Argument to 'roll' not a number"); int n=ns.intval; if(S.size()<2)return; bool negative=n<0; if(negative)n=-n; n%=S.size(); if(n==0)return; if(negative){ vector tempstore; tempstore.reserve(n); int ssz=S.size(); for(int i=ssz-n;i &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("stridx",2) Stackitem idx=move(S.back()); S.pop_back(); const Stackitem &str=S.back(); if(str.type!=SIT_STR)throw string("Top of stack is not a string in builtin 'stridx'"); if(idx.type!=SIT_INT)throw string("Argument to 'stridx' not a number"); if(idx.intval<0||idx.intval>=(int)str.strval.size())throw string("Argument to 'stridx' out of range"); S.emplace_back(string(1,str.strval[idx.intval])); }}, //leaves the string on the stack {"strlen",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("strlen",1) const Stackitem &str=S.back(); if(str.type!=SIT_STR)throw string("Argument to 'strlen' not a string"); S.emplace_back(str.strval.size()); }}, //leaves the string on the stack; `to` is exclusive {"substr",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("substr",3) const Stackitem to=move(S.back()); S.pop_back(); const Stackitem from=move(S.back()); S.pop_back(); const Stackitem &str=S.back(); if(str.type!=SIT_STR)throw string("Top of stack is not a string in builtin 'substr'"); if(from.type!=SIT_INT)throw string("First argument to 'substr' not a number"); if(to.type!=SIT_INT)throw string("Second argument to 'substr' not a number"); S.emplace_back(str.strval.substr(from.intval,to.intval-from.intval)); }}, {"chr",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("chr",1) if(S.back().type!=SIT_INT)throw string("Argument to 'chr' not a number"); S.back().type=SIT_STR; S.back().strval=string(1,S.back().intval); }}, {"ord",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("ord",1) if(S.back().type!=SIT_STR)throw string("Argument to 'ord' not a string"); S.back().type=SIT_INT; S.back().intval=(int)S.back().strval[0]; S.back().strval.clear(); }}, {"mkarray",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("mkarray",0) S.emplace_back(Stackitem(vector())); }}, //mkarray prefill {"mkarrayp",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("mkarrayp",1) const Stackitem nitems=move(S.back()); S.pop_back(); if(nitems.type!=SIT_INT)throw string("Argument to 'mkarrayp' not a number"); if(nitems.intval<0)throw string("Negative argument to 'mkarrayp'"); BUILTIN_GUARD_STACKSIZE("mkarrayp: prefill",nitems.intval); Stackitem res((vector())); res.arrval.reserve(nitems.intval); const int sz=S.size(); for(int i=sz-nitems.intval;i &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("arridx",2) const Stackitem idx=move(S.back()); S.pop_back(); if(idx.type!=SIT_INT) throw string("Argument to 'arridx' not a number"); if(S.back().type!=SIT_ARR) throw string("Top of stack is not an array in builtin 'arridx'"); if(idx.intval<0||idx.intval>=S.back().arrval.size()) throw string("Argument to 'arridx' out of range"); S.push_back(S.back().arrval[idx.intval]); }}, //leaves the array on the stack {"arrlen",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("arrlen",1) if(S.back().type!=SIT_ARR) throw string("Top of stack is not an array in builtin 'arrlen'"); S.emplace_back(S.back().arrval.size()); }}, //leaves the array on the stack, obviously {"arrpush",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("arrpush",2) if(S[S.size()-2].type!=SIT_ARR) throw string("Top of stack is not an array in builtin 'arrpush'"); S[S.size()-2].arrval.push_back(move(S.back())); S.pop_back(); }}, //leaves the array on the stack, obviously {"arrpushf",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("arrpushf",2) Stackitem &arrref=S[S.size()-2]; if(arrref.type!=SIT_ARR) throw string("Top of stack is not an array in builtin 'arrpushf'"); arrref.arrval.insert(arrref.arrval.begin(),move(S.back())); S.pop_back(); }}, //leaves the array on the stack, obviously {"arrpop",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("arrpop",1) if(S.back().type!=SIT_ARR) throw string("Top of stack is not an array in builtin 'arrpop'"); S.back().arrval.pop_back(); }}, //leaves the array on the stack, obviously {"arrpopf",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("arrpopf",1) if(S.back().type!=SIT_ARR) throw string("Top of stack is not an array in builtin 'arrpopf'"); S.back().arrval.erase(S.back().arrval.begin()); }}, {"rand",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("rand",0) assert(RAND_MAX==0x7fffffff); S.emplace_back(rand()); }}, //`to` exclusive {"randr",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("rand",2) assert(RAND_MAX==0x7fffffff); const Stackitem from=move(S.back()); S.pop_back(); const Stackitem to=move(S.back()); S.pop_back(); if(from.type!=SIT_INT)throw string("First argument to 'randr' not a number"); if(to.type!=SIT_INT)throw string("Second argument to 'randr' not a number"); if(to.intval<=from.intval)throw string("Arguments to 'randr' form an empty range"); const int rval=rand(); S.emplace_back(rval%(to.intval-from.intval)+from.intval); }}, {"sleep",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("sleep",1) if(S.back().type!=SIT_INT)throw string("Argument to 'sleep' not a number"); usleep(S.back().intval); S.pop_back(); }}, {"stackdump",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("stackdump",0) cerr<<"STACKDUMP: "; for(const Stackitem &si : S){ if(si.type==SIT_STR)cerr<<'"'< &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("error",0) exit(1); //STUB IMPLEMENTATION }}, {"exit",[](vector &S,unordered_map &variables){ BUILTIN_GUARD_STACKSIZE("exit",0) exit(0); }}, }; #undef BUILTIN_GUARD_STACKSIZE