#include #include #include #include #include #include #include #include #include #include using namespace std; inline bool isword(char c){return isalpha(c)||c=='_'||c=='@'||c=='$';} inline bool isextword(char c){return isword(c)||isdigit(c);} inline bool isoperator(char c){return (bool)strchr("+-*/=> tokenise(istream &stream){ vector tokens; string token; char c,stringmode; while(true){ c=stream.get(); if(!stream)break; if(isword(c)||(token.size()>0&&isextword(c))){ token+=c; } else if(isdigit(c)){ if(token.size()==0)token="#"; token+=c; } else { if(token.size())tokens.push_back(move(token)); if(isoperator(c))tokens.push_back(string(1,c)); else if(c=='"'||c=='\''){ stringmode=c; while(true){ c=stream.get(); if(!stream)throw string("Non-terminated string at end of file"); if(c==stringmode){ token="'"+token; tokens.push_back(move(token)); break; } if(c=='\\'){ c=stream.get(); if(!stream)throw string("Non-terminated escape sequence in string at end of file"); switch(c){ case 'n':c='\n';break; case 'r':c='\r';break; case 't':c='\t';break; case '0':c='\0';break; case 'x':{ int res; c=stream.get(); if(!stream)throw string("Non-terminated hexadecimal escape sequence in string at end of file"); res=c>='0'&&c<='9'?c-'0':(c>='a'&&c<='f')||(c>='A'&&c<='F')?(c&~32)-'A'+10:-1; if(res==-1)throw "Invalid character '"+string(1,c)+"'' in hexadecimal escape sequence in string"; res*=16; c=stream.get(); if(!stream)throw string("Non-terminated hexadecimal escape sequence in string at end of file"); res+=c>='0'&&c<='9'?c-'0':(c>='a'&&c<='f')||(c>='A'&&c<='F')?(c&~32)-'A'+10:-241; if(res<0)throw "Invalid character '"+string(1,c)+"'' in hexadecimal escape sequence in string"; c=(char)res; break; } } } token+=c; } } else if(isspace(c)); else throw string("Invalid character found in source: '")+c+'\''; } } return tokens; } string readfile(ifstream stream){ stream.seekg(0,ios::end); size_t len=stream.tellg(); stream.seekg(0,ios::beg); string contents; contents.resize(len); stream.read(&*contents.begin(),len); return contents; } struct Stackitem{ bool isstr; string strval; int intval; Stackitem(void):isstr(false),intval(0){} Stackitem(int _i):isstr(false),intval(_i){} Stackitem(const string &_s):isstr(true),strval(_s),intval(0){} operator bool(void){return (isstr&&strval.size())||(!isstr&&intval);} bool operator==(const Stackitem &other){return isstr==other.isstr&&(isstr?strval==other.strval:intval==other.intval);} }; const unordered_map&,unordered_map&)>> builtins={ {"+",[](stack &S,unordered_map &variables){ if(S.size()<2)throw string("Builtin '+' needs 2 stack items"); Stackitem a,b; b=S.top(); S.pop(); a=S.top(); S.pop(); if(!a.isstr&&!b.isstr){ S.emplace(a.intval+b.intval); return; } if(!a.isstr){ a.strval=to_string(a.intval); a.isstr=true; } if(!b.isstr){ b.strval=to_string(b.intval); b.isstr=true; } S.emplace(a.strval+b.strval); }}, {"-",[](stack &S,unordered_map &variables){ if(S.size()<2)throw string("Builtin '-' needs 2 stack items"); Stackitem a,b; b=S.top(); S.pop(); a=S.top(); S.pop(); if(a.isstr||b.isstr)throw string("Builtin '-' cannot accept a string argument"); S.emplace(a.intval-b.intval); }}, {"*",[](stack &S,unordered_map &variables){ if(S.size()<2)throw string("Builtin '*' needs 2 stack items"); Stackitem a,b; b=S.top(); S.pop(); a=S.top(); S.pop(); if(a.isstr||b.isstr)throw string("Builtin '*' cannot accept a string argument"); S.emplace(a.intval*b.intval); }}, {"/",[](stack &S,unordered_map &variables){ if(S.size()<2)throw string("Builtin '/' needs 2 stack items"); Stackitem a,b; b=S.top(); S.pop(); a=S.top(); S.pop(); if(a.isstr||b.isstr)throw string("Builtin '/' cannot accept a string argument"); S.emplace(a.intval/b.intval); }}, {"%",[](stack &S,unordered_map &variables){ if(S.size()<2)throw string("Builtin '%' needs 2 stack items"); Stackitem a,b; b=S.top(); S.pop(); a=S.top(); S.pop(); if(a.isstr||b.isstr)throw string("Builtin '%' cannot accept a string argument"); S.emplace(a.intval%b.intval); }}, {"^",[](stack &S,unordered_map &variables){ if(S.size()<2)throw string("Builtin '^' needs 2 stack items"); Stackitem a,b; b=S.top(); S.pop(); a=S.top(); S.pop(); if(a.isstr||b.isstr)throw string("Builtin '^' cannot accept a string argument"); S.emplace((int)pow(a.intval,b.intval)); }}, {"!",[](stack &S,unordered_map &variables){ if(S.size()<1)throw string("Builtin '!' needs 1 stack item"); Stackitem v; v=S.top(); S.pop(); if(v.isstr)S.emplace(!v.strval.size()); else S.emplace(!v.intval); }}, {"=",[](stack &S,unordered_map &variables){ if(S.size()<2)throw string("Builtin '=' needs 2 stack items"); Stackitem a,b; b=S.top(); S.pop(); a=S.top(); S.pop(); S.emplace(a==b); }}, {"<",[](stack &S,unordered_map &variables){ if(S.size()<2)throw string("Builtin '<' needs 2 stack items"); Stackitem a,b; b=S.top(); S.pop(); a=S.top(); S.pop(); if(a.isstr&&b.isstr)S.emplace(a.strval",[](stack &S,unordered_map &variables){ if(S.size()<2)throw string("Builtin '>' needs 2 stack items"); Stackitem a,b; b=S.top(); S.pop(); a=S.top(); S.pop(); if(a.isstr&&b.isstr)S.emplace(a.strval>b.strval); else if(!a.isstr&&!b.isstr)S.emplace(a.intval>b.intval); else if(a.isstr)S.emplace(a.strval>to_string(b.intval)); else S.emplace(to_string(a.intval)>b.strval); }}, {"print",[](stack &S,unordered_map &variables){ if(S.size()<1)throw string("Builtin 'print' needs 1 stack item"); Stackitem v; v=S.top(); S.pop(); if(v.isstr)cout< &S,unordered_map &variables){ cout< &S,unordered_map &variables){ if(S.size()<1)throw string("Builtin 'dup' needs 1 stack item"); S.push(S.top()); }}, {"pop",[](stack &S,unordered_map &variables){ if(S.size()<1)throw string("Builtin 'pop' needs 1 stack item"); S.pop(); }}, }; void runpasseval(const vector &T, stack &S, const unordered_map> &functions, unordered_map &variables, const unordered_map &jumpmap){ unsigned int cursor; for(cursor=0;cursor'<second); } else { auto it=functions.find(word); if(it!=functions.end()){ runpasseval(it->second,S,functions,variables,jumpmap); } else { if(word=="while"){ if(S.size()<1)throw string("Keyword 'while' needs 1 stack item"); Stackitem v=S.top(); S.pop(); if(!v){ cursor=jumpmap.find(cursor)->second-1; //jump to corresponding end } continue; } else if(word=="if"){ if(S.size()<1)throw string("Keyword 'if' needs 1 stack item"); Stackitem v=S.top(); S.pop(); if(!v){ cursor=jumpmap.find(cursor)->second-1; //jump to corresponding else/end } continue; } else if(word=="else"||word=="end"){ cursor=jumpmap.find(cursor)->second-1; continue; } auto it=builtins.find(word); if(it==builtins.end())throw "Unknown variable or function '"+word+"'"; it->second(S,variables); } } } } } void run(vector T){ unordered_map> functions; unordered_map variables; unordered_map jumpmap; stack S; unsigned int cursor=0; //first pass, handle functions and includes while(cursor=T.size())throw string("Unterminated @defun statement at end of file"); string name=T[cursor+1]; if(name[0]!='\'')throw string("@defun expected a string as name, but got '")+name+"'"; name.erase(0,1); if(T[cursor+2]!="{")throw string("Invalid @defun statement of function ")+name; unsigned int start=cursor+3; unsigned int end; for(end=start;end &functoks=functions[name]; functoks.resize(end-start); for(unsigned int i=start;i=T.size())throw string("Unterminated @include statement at end of file"); string name=T[cursor+1]; if(name[0]!='\'')throw string("@include expected a string as file, but got '")+name+"'"; name.erase(0,1); ifstream file(name); if(!file)throw string("Could not open file '")+name+"' specified by @include statement"; vector included=tokenise(file); file.close(); T.erase(T.begin()+cursor,T.begin()+cursor+2); T.insert(T.begin()+cursor,included.begin(),included.end()); } else { cursor++; } } //second pass, index flow control keywords and translate '?' vector flowindexes; for(cursor=0;cursor tokens=tokenise(srcf); for(const string &tok : tokens)cerr<<'['<