diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 15 | ||||
-rw-r--r-- | functions.cpp | 310 | ||||
-rw-r--r-- | postrun.cpp | 572 | ||||
-rw-r--r-- | runtime.cpp | 285 | ||||
-rw-r--r-- | runtime.h | 26 |
6 files changed, 639 insertions, 570 deletions
@@ -1,2 +1,3 @@ postrun *.dSYM +*.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ad763d5 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +CXX = g++ +CXXFLAGS = -Wall -O2 -std=c++11 +OBJECTS = postrun.o runtime.o functions.o + +.PHONY: all clean remake + +all: postrun + +clean: + rm -f $(OBJECTS) postrun + +remake: clean all + +postrun: $(OBJECTS) + g++ -Wall -O2 -std=c++11 -o postrun $(OBJECTS) diff --git a/functions.cpp b/functions.cpp new file mode 100644 index 0000000..4795c93 --- /dev/null +++ b/functions.cpp @@ -0,0 +1,310 @@ +#include <iostream> +#include <fstream> +#include <unordered_map> +#include <vector> +#include <string> +#include <functional> +#include <unistd.h> +#include <fcntl.h> + +#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");} + +//please extern this shit with const! +unordered_map<string,function<void(vector<Stackitem>&,unordered_map<string,Stackitem>&)>> builtins={ + {"+",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("+",2) + Stackitem b=move(S.back()); S.pop_back(); + Stackitem a=move(S.back()); S.pop_back(); + if(!a.isstr&&!b.isstr){ + S.emplace_back(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_back(a.strval+b.strval); + }}, + {"-",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("-",2) + Stackitem b=move(S.back()); S.pop_back(); + Stackitem a=move(S.back()); S.pop_back(); + if(a.isstr||b.isstr)throw string("Builtin '-' cannot accept a string argument"); + S.emplace_back(a.intval-b.intval); + }}, + {"*",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("*",2) + Stackitem b=move(S.back()); S.pop_back(); + Stackitem a=move(S.back()); S.pop_back(); + if(a.isstr||b.isstr)throw string("Builtin '*' cannot accept a string argument"); + S.emplace_back(a.intval*b.intval); + }}, + {"/",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("/",2) + Stackitem b=move(S.back()); S.pop_back(); + Stackitem a=move(S.back()); S.pop_back(); + if(a.isstr||b.isstr)throw string("Builtin '/' cannot accept a string argument"); + S.emplace_back(a.intval/b.intval); + }}, + {"%",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("%",2) + Stackitem b=move(S.back()); S.pop_back(); + Stackitem a=move(S.back()); S.pop_back(); + if(a.isstr||b.isstr){ + cerr<<"modding "; + if(a.isstr)cerr<<'"'<<a.strval<<'"'; else cerr<<a.intval; + cerr<<" and "; + if(b.isstr)cerr<<'"'<<b.strval<<'"'; else cerr<<b.intval; + cerr<<endl; + } + if(a.isstr||b.isstr)throw string("Builtin '%' cannot accept a string argument"); + S.emplace_back(a.intval%b.intval); + }}, + {"^",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("^",2) + Stackitem b=move(S.back()); S.pop_back(); + Stackitem a=move(S.back()); S.pop_back(); + if(a.isstr||b.isstr)throw string("Builtin '^' cannot accept a string argument"); + S.emplace_back((int)pow(a.intval,b.intval)); + }}, + {"!",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("!",1) + Stackitem v=move(S.back()); S.pop_back(); + if(v.isstr)S.emplace_back(!v.strval.size()); + else S.emplace_back(!v.intval); + }}, + {"=",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &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<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("<",2) + Stackitem b=move(S.back()); S.pop_back(); + Stackitem a=move(S.back()); S.pop_back(); + if(a.isstr&&b.isstr)S.emplace_back(a.strval<b.strval); + else if(!a.isstr&&!b.isstr)S.emplace_back(a.intval<b.intval); + else if(a.isstr)S.emplace_back(a.strval<to_string(b.intval)); + else S.emplace_back(to_string(a.intval)<b.strval); + }}, + {">",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE(">",2) + Stackitem b=move(S.back()); S.pop_back(); + Stackitem a=move(S.back()); S.pop_back(); + if(a.isstr&&b.isstr)S.emplace_back(a.strval>b.strval); + else if(!a.isstr&&!b.isstr)S.emplace_back(a.intval>b.intval); + else if(a.isstr)S.emplace_back(a.strval>to_string(b.intval)); + else S.emplace_back(to_string(a.intval)>b.strval); + }}, + {"print",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("print",1) + Stackitem v=move(S.back()); S.pop_back(); + if(v.isstr)cout<<v.strval<<flush; + else cout<<v.intval<<flush; + }}, + {"lf",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("lf",0) + cout<<endl; + }}, + {"getline",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("getline",0) + string line; + getline(cin,line); + if(!cin)S.emplace_back(-1); + else S.emplace_back(line); + }}, + {"getc",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &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<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("fprint",2) + Stackitem file=move(S.back()); S.pop_back(); + Stackitem v=move(S.back()); S.pop_back(); + if(file.isstr)throw string("Second argument to 'fprint' not a number"); + if(v.isstr)write(file.intval,v.strval.data(),v.strval.size()); + else { + string s=to_string(v.intval); + write(file.intval,s.data(),s.size()); + } + }}, + {"flf",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("flf",1) + Stackitem file=move(S.back()); S.pop_back(); + if(file.isstr)throw string("Argument to 'flf' not a number"); + write(file.intval,"\n",1); + }}, + {"fgetc",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("fgetc",1) + Stackitem file=move(S.back()); S.pop_back(); + if(file.isstr)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<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("fopen",2) + Stackitem mode=move(S.back()); S.pop_back(); + Stackitem fname=move(S.back()); S.pop_back(); + if(!fname.isstr)throw string("First argument to 'fopen' not a string"); + if(!mode.isstr)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<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("argc",0) + S.emplace_back(g_argc); + }}, + {"argvget",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("argvget",1) + Stackitem idx=move(S.back()); S.pop_back(); + if(idx.isstr)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<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("dup",1) + S.push_back(S.back()); + }}, + {"pop",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("pop",1) + S.pop_back(); + }}, + {"swap",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &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(b); + S.push_back(a); + }}, + {"store",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("store",2) + Stackitem name=move(S.back()); S.pop_back(); + if(!name.isstr)throw string("Second argument to 'store' not a string"); + variables[name.strval]=move(S.back()); + S.pop_back(); + }}, + {"get",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("get",1) + Stackitem name=move(S.back()); S.pop_back(); + if(!name.isstr)throw string("Argument to 'get' not a string"); + S.push_back(variables[name.strval]); + }}, + //leaves variable with value 0 + {"swapoutvar",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("swapoutvar",1) + Stackitem name=move(S.back()); S.pop_back(); + if(!name.isstr)throw string("Second argument to 'swapoutvar' not a string"); + S.push_back(move(variables[name.strval])); + variables[name.strval].intval=0; + }}, + //positive n: roll OFF top to bottom; negative n: roll off bottom ONTO top + {"roll",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("roll",1) + Stackitem ns=move(S.back()); S.pop_back(); + if(ns.isstr)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<Stackitem> tempstore; + tempstore.reserve(n); + int ssz=S.size(); + for(int i=ssz-n;i<ssz;i++)tempstore.push_back(move(S[i])); + S.erase(S.end()-n,S.end()); + S.insert(S.begin(),n,Stackitem()); + for(int i=0;i<n;i++)S[i]=move(tempstore[i]); + } else { + //cerr<<"rolling back n="<<n<<endl; + vector<Stackitem> tempstore; + tempstore.reserve(n); + for(int i=0;i<n;i++)tempstore.push_back(move(S[i])); + S.erase(S.begin(),S.begin()+n); + for(int i=0;i<n;i++)S.push_back(move(tempstore[i])); + } + }}, + //leaves the string on the stack + {"stridx",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("stridx",2) + Stackitem idx=move(S.back()); S.pop_back(); + const Stackitem &str=S.back(); + if(!str.isstr)throw string("First argument to 'stridx' not a string"); + if(idx.isstr)throw string("Second argument to 'stridx' not a number"); + if(idx.intval<0||idx.intval>=(int)str.strval.size())throw string("Index argument to 'stridx' out of range"); + S.emplace_back(string(1,str.strval[idx.intval])); + }}, + //leaves the string on the stack + {"strlen",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("strlen",1) + const Stackitem &str=S.back(); + if(!str.isstr)throw string("Argument to 'strlen' not a string"); + S.emplace_back(str.strval.size()); + }}, + //leaves the string on the stack + {"substr",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &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.isstr)throw string("First argument to 'substr' not a string"); + if(from.isstr)throw string("Second argument to 'substr' not a number"); + if(to.isstr)throw string("Third argument to 'substr' not a number"); + S.emplace_back(str.strval.substr(from.intval,to.intval-from.intval)); + }}, + {"chr",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("chr",1) + Stackitem ascii=move(S.back()); S.pop_back(); + if(ascii.isstr)throw string("Argument to 'chr' not a number"); + S.emplace_back(string(1,ascii.intval)); + }}, + {"ord",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("ord",1) + if(!S.back().isstr)throw string("Argument to 'ord' not a string"); + S.back().isstr=false; + S.back().intval=(int)S.back().strval[0]; + S.back().strval=""; + }}, + {"stackdump",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("stackdump",0) + cerr<<"STACKDUMP: "; + for(const Stackitem &si : S){ + if(si.isstr)cerr<<'"'<<si.strval<<"\" "; + else cerr<<si.intval<<' '; + } + cerr<<endl; + }}, + {"exit",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ + BUILTIN_GUARD_STACKSIZE("exit",0) + exit(0); + }}, +}; +#undef BUILTIN_GUARD_STACKSIZE diff --git a/postrun.cpp b/postrun.cpp index 78998cf..4933d8b 100644 --- a/postrun.cpp +++ b/postrun.cpp @@ -10,88 +10,13 @@ #include <unistd.h> #include <fcntl.h> -using namespace std; +#include "runtime.h" -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);} +using namespace std; int g_argc; char **g_argv; -vector<string> tokenise(istream &stream){ - vector<string> 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+'\''; - } - } - if(token.size())tokens.push_back(move(token)); - return tokens; -} - string readfile(ifstream stream){ stream.seekg(0,ios::end); size_t len=stream.tellg(); @@ -102,499 +27,6 @@ string readfile(ifstream stream){ 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){} - Stackitem(const Stackitem&)=default; - Stackitem(Stackitem &&other):isstr(other.isstr),strval(move(other.strval)),intval(other.intval){ - other.isstr=false; - } - Stackitem& operator=(const Stackitem&)=default; - Stackitem& operator=(Stackitem &&other){ - isstr=other.isstr; - strval=move(other.strval); - intval=other.intval; - other.isstr=false; - return *this; - } - explicit operator bool(void) const{return (isstr&&strval.size())||(!isstr&&intval);} - bool operator==(const Stackitem &other) const{return isstr==other.isstr&&(isstr?strval==other.strval:intval==other.intval);} -}; - -#define BUILTIN_GUARD_STACKSIZE(nm,sz) {if(S.size()<(sz))throw string("Builtin '" nm "' needs " #sz " stack item")+(sz==1?"":"s");} - -const unordered_map<string,function<void(vector<Stackitem>&,unordered_map<string,Stackitem>&)>> builtins={ - {"+",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("+",2) - Stackitem b=move(S.back()); S.pop_back(); - Stackitem a=move(S.back()); S.pop_back(); - if(!a.isstr&&!b.isstr){ - S.emplace_back(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_back(a.strval+b.strval); - }}, - {"-",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("-",2) - Stackitem b=move(S.back()); S.pop_back(); - Stackitem a=move(S.back()); S.pop_back(); - if(a.isstr||b.isstr)throw string("Builtin '-' cannot accept a string argument"); - S.emplace_back(a.intval-b.intval); - }}, - {"*",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("*",2) - Stackitem b=move(S.back()); S.pop_back(); - Stackitem a=move(S.back()); S.pop_back(); - if(a.isstr||b.isstr)throw string("Builtin '*' cannot accept a string argument"); - S.emplace_back(a.intval*b.intval); - }}, - {"/",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("/",2) - Stackitem b=move(S.back()); S.pop_back(); - Stackitem a=move(S.back()); S.pop_back(); - if(a.isstr||b.isstr)throw string("Builtin '/' cannot accept a string argument"); - S.emplace_back(a.intval/b.intval); - }}, - {"%",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("%",2) - Stackitem b=move(S.back()); S.pop_back(); - Stackitem a=move(S.back()); S.pop_back(); - if(a.isstr||b.isstr){ - cerr<<"modding "; - if(a.isstr)cerr<<'"'<<a.strval<<'"'; else cerr<<a.intval; - cerr<<" and "; - if(b.isstr)cerr<<'"'<<b.strval<<'"'; else cerr<<b.intval; - cerr<<endl; - } - if(a.isstr||b.isstr)throw string("Builtin '%' cannot accept a string argument"); - S.emplace_back(a.intval%b.intval); - }}, - {"^",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("^",2) - Stackitem b=move(S.back()); S.pop_back(); - Stackitem a=move(S.back()); S.pop_back(); - if(a.isstr||b.isstr)throw string("Builtin '^' cannot accept a string argument"); - S.emplace_back((int)pow(a.intval,b.intval)); - }}, - {"!",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("!",1) - Stackitem v=move(S.back()); S.pop_back(); - if(v.isstr)S.emplace_back(!v.strval.size()); - else S.emplace_back(!v.intval); - }}, - {"=",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &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<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("<",2) - Stackitem b=move(S.back()); S.pop_back(); - Stackitem a=move(S.back()); S.pop_back(); - if(a.isstr&&b.isstr)S.emplace_back(a.strval<b.strval); - else if(!a.isstr&&!b.isstr)S.emplace_back(a.intval<b.intval); - else if(a.isstr)S.emplace_back(a.strval<to_string(b.intval)); - else S.emplace_back(to_string(a.intval)<b.strval); - }}, - {">",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE(">",2) - Stackitem b=move(S.back()); S.pop_back(); - Stackitem a=move(S.back()); S.pop_back(); - if(a.isstr&&b.isstr)S.emplace_back(a.strval>b.strval); - else if(!a.isstr&&!b.isstr)S.emplace_back(a.intval>b.intval); - else if(a.isstr)S.emplace_back(a.strval>to_string(b.intval)); - else S.emplace_back(to_string(a.intval)>b.strval); - }}, - {"print",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("print",1) - Stackitem v=move(S.back()); S.pop_back(); - if(v.isstr)cout<<v.strval<<flush; - else cout<<v.intval<<flush; - }}, - {"lf",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("lf",0) - cout<<endl; - }}, - {"getline",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("getline",0) - string line; - getline(cin,line); - if(!cin)S.emplace_back(-1); - else S.emplace_back(line); - }}, - {"getc",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &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<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("fprint",2) - Stackitem file=move(S.back()); S.pop_back(); - Stackitem v=move(S.back()); S.pop_back(); - if(file.isstr)throw string("Second argument to 'fprint' not a number"); - if(v.isstr)write(file.intval,v.strval.data(),v.strval.size()); - else { - string s=to_string(v.intval); - write(file.intval,s.data(),s.size()); - } - }}, - {"flf",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("flf",1) - Stackitem file=move(S.back()); S.pop_back(); - if(file.isstr)throw string("Argument to 'flf' not a number"); - write(file.intval,"\n",1); - }}, - {"fgetc",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("fgetc",1) - Stackitem file=move(S.back()); S.pop_back(); - if(file.isstr)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<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("fopen",2) - Stackitem mode=move(S.back()); S.pop_back(); - Stackitem fname=move(S.back()); S.pop_back(); - if(!fname.isstr)throw string("First argument to 'fopen' not a string"); - if(!mode.isstr)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<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("argc",0) - S.emplace_back(g_argc); - }}, - {"argvget",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("argvget",1) - Stackitem idx=move(S.back()); S.pop_back(); - if(idx.isstr)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<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("dup",1) - S.push_back(S.back()); - }}, - {"pop",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("pop",1) - S.pop_back(); - }}, - {"swap",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &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(b); - S.push_back(a); - }}, - {"store",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("store",2) - Stackitem name=move(S.back()); S.pop_back(); - if(!name.isstr)throw string("Second argument to 'store' not a string"); - variables[name.strval]=move(S.back()); - S.pop_back(); - }}, - {"get",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("get",1) - Stackitem name=move(S.back()); S.pop_back(); - if(!name.isstr)throw string("Argument to 'get' not a string"); - S.push_back(variables[name.strval]); - }}, - //leaves variable with value 0 - {"swapoutvar",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("swapoutvar",1) - Stackitem name=move(S.back()); S.pop_back(); - if(!name.isstr)throw string("Second argument to 'swapoutvar' not a string"); - S.push_back(move(variables[name.strval])); - variables[name.strval].intval=0; - }}, - //positive n: roll OFF top to bottom; negative n: roll off bottom ONTO top - {"roll",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("roll",1) - Stackitem ns=move(S.back()); S.pop_back(); - if(ns.isstr)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<Stackitem> tempstore; - tempstore.reserve(n); - int ssz=S.size(); - for(int i=ssz-n;i<ssz;i++)tempstore.push_back(move(S[i])); - S.erase(S.end()-n,S.end()); - S.insert(S.begin(),n,Stackitem()); - for(int i=0;i<n;i++)S[i]=move(tempstore[i]); - } else { - //cerr<<"rolling back n="<<n<<endl; - vector<Stackitem> tempstore; - tempstore.reserve(n); - for(int i=0;i<n;i++)tempstore.push_back(move(S[i])); - S.erase(S.begin(),S.begin()+n); - for(int i=0;i<n;i++)S.push_back(move(tempstore[i])); - } - }}, - //leaves the string on the stack - {"stridx",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("stridx",2) - Stackitem idx=move(S.back()); S.pop_back(); - const Stackitem &str=S.back(); - if(!str.isstr)throw string("First argument to 'stridx' not a string"); - if(idx.isstr)throw string("Second argument to 'stridx' not a number"); - if(idx.intval<0||idx.intval>=(int)str.strval.size())throw string("Index argument to 'stridx' out of range"); - S.emplace_back(string(1,str.strval[idx.intval])); - }}, - //leaves the string on the stack - {"strlen",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("strlen",1) - const Stackitem &str=S.back(); - if(!str.isstr)throw string("Argument to 'strlen' not a string"); - S.emplace_back(str.strval.size()); - }}, - //leaves the string on the stack - {"substr",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &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.isstr)throw string("First argument to 'substr' not a string"); - if(from.isstr)throw string("Second argument to 'substr' not a number"); - if(to.isstr)throw string("Third argument to 'substr' not a number"); - S.emplace_back(str.strval.substr(from.intval,to.intval-from.intval)); - }}, - {"chr",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("chr",1) - Stackitem ascii=move(S.back()); S.pop_back(); - if(ascii.isstr)throw string("Argument to 'chr' not a number"); - S.emplace_back(string(1,ascii.intval)); - }}, - {"ord",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("ord",1) - if(!S.back().isstr)throw string("Argument to 'ord' not a string"); - S.back().isstr=false; - S.back().intval=(int)S.back().strval[0]; - S.back().strval=""; - }}, - {"stackdump",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("stackdump",0) - cerr<<"STACKDUMP: "; - for(const Stackitem &si : S){ - if(si.isstr)cerr<<'"'<<si.strval<<"\" "; - else cerr<<si.intval<<' '; - } - cerr<<endl; - }}, - {"exit",[](vector<Stackitem> &S,unordered_map<string,Stackitem> &variables){ - BUILTIN_GUARD_STACKSIZE("exit",0) - exit(0); - }}, -}; -#undef BUILTIN_GUARD_STACKSIZE - -void runpasseval(const vector<string> &T, - vector<Stackitem> &S, - const unordered_map<string,pair<vector<string>,unordered_map<unsigned int,unsigned int>>> &functions, - unordered_map<string,Stackitem> &variables, - const unordered_map<unsigned int,unsigned int> &jumpmap){ - unsigned int cursor; - for(cursor=0;cursor<T.size();cursor++){ - const string &word=T[cursor]; - //cerr<<"Executing word <"<<word<<'>'<<endl; - if(word[0]=='#'){ - S.emplace_back((int)strtol(word.data()+1,NULL,10)); - } else if(word[0]=='\''){ - S.push_back(word.substr(1)); - } else { - auto it=variables.find(word); - if(it!=variables.end()){ - S.push_back(it->second); - } else { - auto it=functions.find(word); - if(it!=functions.end()){ - runpasseval(it->second.first,S,functions,variables,it->second.second); - } 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 "<<word<<" AT INDEX "<<cursor<<endl; - } - 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.back(); S.pop_back(); - if(!v){ - auto it=jumpmap.find(cursor); - if(it==jumpmap.end()){ - cerr<<"NO JUMPMAP ENTRY FOR "<<word<<" AT INDEX "<<cursor<<endl; - } - cursor=jumpmap.find(cursor)->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 "<<word<<" AT INDEX "<<cursor<<endl; - } - 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 populatejumpmap(const vector<string> &T,unordered_map<unsigned int,unsigned int> &jumpmap){ - //first, index flow control keywords - vector<unsigned int> flowindexes; - for(unsigned int cursor=0;cursor<T.size();cursor++){ - const string &word=T[cursor]; - if(word=="end"||word=="while"||word=="if"||word=="else")flowindexes.push_back(cursor); - } - - //then, index jump targets in jumpmap - for(unsigned int i=0;i<flowindexes.size();i++){ - const string &word=T[flowindexes[i]]; - if(word=="while"){ - if(i==flowindexes.size()-1)throw string("No 'end' after 'while' in source"); - int depth=1; - unsigned int end; - for(end=i+1;end<flowindexes.size();end++){ - if(T[flowindexes[end]]=="end")depth--; - else if(T[flowindexes[end]]=="while"||T[flowindexes[end]]=="if")depth++; - if(depth==0)break; - } - if(end==flowindexes.size())throw string("No 'end' after 'while' in source"); - jumpmap[flowindexes[i]]=flowindexes[end]+1; - jumpmap[flowindexes[end]]=flowindexes[i]; - } else if(word=="if"){ - if(i==flowindexes.size()-1)throw string("No 'end' after 'if' in source"); - int depth=1; - unsigned int els,end; - for(els=i+1;els<flowindexes.size();els++){ - if(depth==1&&T[flowindexes[els]]=="else")break; - else if(T[flowindexes[els]]=="end")depth--; - else if(T[flowindexes[els]]=="while"||T[flowindexes[els]]=="if")depth++; - if(depth==0)break; - } - if(els==flowindexes.size())throw string("No 'else' or 'end' after 'if' in source"); - if(depth!=0){ //we found an else - for(end=els+1;end<flowindexes.size();end++){ - if(T[flowindexes[end]]=="end")depth--; - else if(T[flowindexes[end]]=="while"||T[flowindexes[end]]=="if")depth++; - if(depth==0)break; - } - if(end==flowindexes.size())throw string("No 'end' after 'if' in source"); - jumpmap[flowindexes[i]]=flowindexes[els]+1; - jumpmap[flowindexes[els]]=flowindexes[end]+1; - jumpmap[flowindexes[end]]=flowindexes[end]+1; - } else { //we found an end in the first place - //cerr<<"coupling if-end indexes "<<flowindexes[i]<<" and "<<flowindexes[els]<<endl; - jumpmap[flowindexes[i]]=flowindexes[els]+1; - jumpmap[flowindexes[els]]=flowindexes[els]+1; - } - } - } -} - -void run(vector<string> T){ - unordered_map<string,pair<vector<string>,unordered_map<unsigned int,unsigned int>>> functions; - //functions is a pair of tokens and jumpmap - unordered_map<string,Stackitem> variables; - unordered_map<unsigned int,unsigned int> jumpmap; - vector<Stackitem> S; - unsigned int cursor=0; - //first pass, handle functions and includes - while(cursor<T.size()){ - const string &word=T[cursor]; - if(word=="@defun"){ - if(cursor+3>=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<T.size();end++){ - if(T[end]=="{")depth++; - else if(T[end]=="}")depth--; - else if(T[end]=="\""){ - while(++end<T.size()&&T[end]!="\"")if(T[end]=="\\")end++; - } else if(T[end]=="'"){ - while(++end<T.size()&&T[end]!="'")if(T[end]=="\\")end++; - } - if(depth==0)break; - } - if(depth!=0)throw string("Non-terminated @defun statement at end of file"); - vector<string> &functoks=functions[name].first; - functoks.resize(end-start); - for(unsigned int i=start;i<end;i++)functoks[i-start]=move(T[i]); - T.erase(T.begin()+cursor,T.begin()+end+1); - } else if(word=="@include"){ - if(cursor+1>=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<string> 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, 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); -} - int main(int argc,char **argv){ if(argc<2){ cerr<<"Call this interpreter with the Postrun source file"<<endl; diff --git a/runtime.cpp b/runtime.cpp new file mode 100644 index 0000000..63e0b52 --- /dev/null +++ b/runtime.cpp @@ -0,0 +1,285 @@ +#include <iostream> +#include <vector> +#include <string> +#include <unordered_map> +#include <functional> +#include <cstring> + +#include "runtime.h" + +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):isstr(false),intval(0){} +Stackitem::Stackitem(int _i):isstr(false),intval(_i){} +Stackitem::Stackitem(const string &_s):isstr(true),strval(_s),intval(0){} +Stackitem::Stackitem(const Stackitem&)=default; +Stackitem::Stackitem(Stackitem &&other):isstr(other.isstr),strval(move(other.strval)),intval(other.intval){ + other.isstr=false; +} +Stackitem& Stackitem::operator=(const Stackitem&)=default; +Stackitem& Stackitem::operator=(Stackitem &&other){ + isstr=other.isstr; + strval=move(other.strval); + intval=other.intval; + other.isstr=false; + return *this; +} +Stackitem::operator bool(void) const{return (isstr&&strval.size())||(!isstr&&intval);} +bool Stackitem::operator==(const Stackitem &other) const{return isstr==other.isstr&&(isstr?strval==other.strval:intval==other.intval);} + + +extern const unordered_map<string,function<void(vector<Stackitem>&,unordered_map<string,Stackitem>&)>> builtins; + + +vector<string> tokenise(istream &stream){ + vector<string> 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+'\''; + } + } + if(token.size())tokens.push_back(move(token)); + return tokens; +} + +void runpasseval(const vector<string> &T, + vector<Stackitem> &S, + const unordered_map<string,pair<vector<string>,unordered_map<unsigned int,unsigned int>>> &functions, + unordered_map<string,Stackitem> &variables, + const unordered_map<unsigned int,unsigned int> &jumpmap){ + unsigned int cursor; + for(cursor=0;cursor<T.size();cursor++){ + const string &word=T[cursor]; + //cerr<<"Executing word <"<<word<<'>'<<endl; + if(word[0]=='#'){ + S.emplace_back((int)strtol(word.data()+1,NULL,10)); + } else if(word[0]=='\''){ + S.push_back(word.substr(1)); + } else { + auto it=variables.find(word); + if(it!=variables.end()){ + S.push_back(it->second); + } else { + auto it=functions.find(word); + if(it!=functions.end()){ + runpasseval(it->second.first,S,functions,variables,it->second.second); + } 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 "<<word<<" AT INDEX "<<cursor<<endl; + } + 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.back(); S.pop_back(); + if(!v){ + auto it=jumpmap.find(cursor); + if(it==jumpmap.end()){ + cerr<<"NO JUMPMAP ENTRY FOR "<<word<<" AT INDEX "<<cursor<<endl; + } + cursor=jumpmap.find(cursor)->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 "<<word<<" AT INDEX "<<cursor<<endl; + } + 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 populatejumpmap(const vector<string> &T,unordered_map<unsigned int,unsigned int> &jumpmap){ + //first, index flow control keywords + vector<unsigned int> flowindexes; + for(unsigned int cursor=0;cursor<T.size();cursor++){ + const string &word=T[cursor]; + if(word=="end"||word=="while"||word=="if"||word=="else")flowindexes.push_back(cursor); + } + + //then, index jump targets in jumpmap + for(unsigned int i=0;i<flowindexes.size();i++){ + const string &word=T[flowindexes[i]]; + if(word=="while"){ + if(i==flowindexes.size()-1)throw string("No 'end' after 'while' in source"); + int depth=1; + unsigned int end; + for(end=i+1;end<flowindexes.size();end++){ + if(T[flowindexes[end]]=="end")depth--; + else if(T[flowindexes[end]]=="while"||T[flowindexes[end]]=="if")depth++; + if(depth==0)break; + } + if(end==flowindexes.size())throw string("No 'end' after 'while' in source"); + jumpmap[flowindexes[i]]=flowindexes[end]+1; + jumpmap[flowindexes[end]]=flowindexes[i]; + } else if(word=="if"){ + if(i==flowindexes.size()-1)throw string("No 'end' after 'if' in source"); + int depth=1; + unsigned int els,end; + for(els=i+1;els<flowindexes.size();els++){ + if(depth==1&&T[flowindexes[els]]=="else")break; + else if(T[flowindexes[els]]=="end")depth--; + else if(T[flowindexes[els]]=="while"||T[flowindexes[els]]=="if")depth++; + if(depth==0)break; + } + if(els==flowindexes.size())throw string("No 'else' or 'end' after 'if' in source"); + if(depth!=0){ //we found an else + for(end=els+1;end<flowindexes.size();end++){ + if(T[flowindexes[end]]=="end")depth--; + else if(T[flowindexes[end]]=="while"||T[flowindexes[end]]=="if")depth++; + if(depth==0)break; + } + if(end==flowindexes.size())throw string("No 'end' after 'if' in source"); + jumpmap[flowindexes[i]]=flowindexes[els]+1; + jumpmap[flowindexes[els]]=flowindexes[end]+1; + jumpmap[flowindexes[end]]=flowindexes[end]+1; + } else { //we found an end in the first place + //cerr<<"coupling if-end indexes "<<flowindexes[i]<<" and "<<flowindexes[els]<<endl; + jumpmap[flowindexes[i]]=flowindexes[els]+1; + jumpmap[flowindexes[els]]=flowindexes[els]+1; + } + } + } +} + +void run(vector<string> T){ + unordered_map<string,pair<vector<string>,unordered_map<unsigned int,unsigned int>>> functions; + //functions is a pair of tokens and jumpmap + unordered_map<string,Stackitem> variables; + unordered_map<unsigned int,unsigned int> jumpmap; + vector<Stackitem> S; + unsigned int cursor=0; + //first pass, handle functions and includes + while(cursor<T.size()){ + const string &word=T[cursor]; + if(word=="@defun"){ + if(cursor+3>=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<T.size();end++){ + if(T[end]=="{")depth++; + else if(T[end]=="}")depth--; + else if(T[end]=="\""){ + while(++end<T.size()&&T[end]!="\"")if(T[end]=="\\")end++; + } else if(T[end]=="'"){ + while(++end<T.size()&&T[end]!="'")if(T[end]=="\\")end++; + } + if(depth==0)break; + } + if(depth!=0)throw string("Non-terminated @defun statement at end of file"); + vector<string> &functoks=functions[name].first; + functoks.resize(end-start); + for(unsigned int i=start;i<end;i++)functoks[i-start]=move(T[i]); + T.erase(T.begin()+cursor,T.begin()+end+1); + } else if(word=="@include"){ + if(cursor+1>=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<string> 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, 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); +} diff --git a/runtime.h b/runtime.h new file mode 100644 index 0000000..1a49300 --- /dev/null +++ b/runtime.h @@ -0,0 +1,26 @@ +#pragma once + +#include <fstream> +#include <vector> +#include <string> + +using namespace std; + +struct Stackitem{ + bool isstr; + string strval; + int intval; + + Stackitem(void); + Stackitem(int _i); + Stackitem(const string &_s); + Stackitem(const Stackitem&); + Stackitem(Stackitem &&other); + Stackitem& operator=(const Stackitem&); + Stackitem& operator=(Stackitem &&other); + explicit operator bool(void) const; + bool operator==(const Stackitem &other) const; +}; + +vector<string> tokenise(istream &stream); +void run(vector<string> T); |