#include #include #include #include #include #include #include #include #include #include #include "runtime.h" extern bool input_is_stdin; 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("+-*/%&|~^!=><{}",c);} Stackitem::Stackitem(void){} Stackitem::Stackitem(int _i):type(SIT_INT),intval(_i){} Stackitem::Stackitem(const string &_s):type(SIT_STR),strval(_s){} Stackitem::Stackitem(const vector &_a):type(SIT_ARR),arrval(_a){} Stackitem::Stackitem(const Stackitem &other)=default; Stackitem::Stackitem(Stackitem &&other):type(other.type),strval(move(other.strval)),intval(other.intval),arrval(move(other.arrval)){ other.type=SIT_INT; } Stackitem& Stackitem::operator=(const Stackitem &other){ type=other.type; strval=other.strval; intval=other.intval; arrval=other.arrval; return *this; } Stackitem& Stackitem::operator=(Stackitem &&other){ type=other.type; strval=move(other.strval); intval=other.intval; arrval=move(other.arrval); other.type=SIT_INT; return *this; } Stackitem::operator bool(void) const{ return (type==SIT_INT&&intval)|| (type==SIT_STR&&strval.size())|| (type==SIT_ARR&&arrval.size()); } bool Stackitem::operator==(const Stackitem &other) const{ if(type!=other.type)return false; if(type==SIT_INT)return intval==other.intval; if(type==SIT_STR)return strval==other.strval; assert(type==SIT_ARR); auto sz=arrval.size(); if(sz!=other.arrval.size())return false; for(int i=0;i&,unordered_map&)>> builtins; vector tokenise(istream &stream){ vector tokens; string token; char c,stringmode; bool gotminus=false; while(true){ c=stream.get(); if(!stream)break; while(c=='#'){ while((c=stream.get())!='\n'&&stream); c=stream.get(); } if(!stream)break; if(isdigit(c)){ if(token.size()==0){ token="#"; if(gotminus)token+="-"; gotminus=false; } token+=c; } else if(isword(c)||(token.size()>0&&isextword(c))){ token+=c; } else { if(token.size())tokens.push_back(move(token)); if(isoperator(c)){ if(c=='-'&&isdigit(stream.peek()))gotminus=true; else 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 '"':c='"';break; case '\'':c='\'';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+"' ("+to_string((int)(unsigned char)c)+')'; } } if(token.size())tokens.push_back(move(token)); return tokens; } void runpasseval(const vector &T, vector &S, const unordered_map,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()){ builtins.find("enterscope")->second(S,variables); runpasseval(it->second.first,S,functions,variables,it->second.second); builtins.find("leavescope")->second(S,variables); } else { if(word=="while"){ if(S.size()<1)throw string("Keyword 'while' needs 1 stack item"); Stackitem v=S.back(); S.pop_back(); if(!v){ auto it=jumpmap.find(cursor); if(it==jumpmap.end()){ cerr<<"NO JUMPMAP ENTRY FOR "<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.back(); S.pop_back(); if(!v){ auto it=jumpmap.find(cursor); if(it==jumpmap.end()){ cerr<<"NO JUMPMAP ENTRY FOR "<second-1; //jump to corresponding else/end } continue; } else if(word=="else"||word=="end"){ auto it=jumpmap.find(cursor); if(it==jumpmap.end()){ cerr<<"NO JUMPMAP ENTRY FOR "<second-1; continue; } auto it=builtins.find(word); if(it==builtins.end())throw "Unknown variable or function '"+word+"'"; it->second(S,variables); } } } } } void populatejumpmap(const vector &T,unordered_map &jumpmap){ //first, index flow control keywords vector flowindexes; for(unsigned int cursor=0;cursor T){ unordered_map,unordered_map>> functions; //functions is a pair of tokens and jumpmap unordered_map variables; unordered_map jumpmap; vector S; unordered_set includeonces; unsigned int cursor=0; bool skipincluding; //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; int depth=1; for(end=start;end &functoks=functions[name].first; 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); skipincluding=false; if(word=="@includeonce"){ if(includeonces.find(name)==includeonces.end()) includeonces.insert(name); else skipincluding=true; } T.erase(T.begin()+cursor,T.begin()+cursor+2); if(!skipincluding){ ifstream file(name); if(!file)throw "Could not open file '"+name+"' specified by "+word+" statement"; vector included=tokenise(file); file.close(); T.insert(T.begin()+cursor,included.begin(),included.end()); } } else { cursor++; } } //second pass, populate jumpmaps populatejumpmap(T,jumpmap); for(auto &p : functions){ populatejumpmap(p.second.first,p.second.second); } //third pass, evaluate code runpasseval(T,S,functions,variables,jumpmap); if(!input_is_stdin){ //restore canonical mode, since the code might have changed it to raw mode struct termios old={0}; if(tcgetattr(0,&old)<0)perror("tcgetattr"); old.c_lflag|=ICANON; old.c_lflag|=ECHO; if(tcsetattr(0,TCSANOW,&old)<0)perror("tcsetattr"); } }