summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile15
-rw-r--r--functions.cpp310
-rw-r--r--postrun.cpp572
-rw-r--r--runtime.cpp285
-rw-r--r--runtime.h26
6 files changed, 639 insertions, 570 deletions
diff --git a/.gitignore b/.gitignore
index babfeab..58b0e1b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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);