summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortomsmeding <hallo@tomsmeding.nl>2015-08-19 09:54:11 +0200
committertomsmeding <hallo@tomsmeding.nl>2015-08-19 09:54:11 +0200
commitd16640ae2a2bc1e8264a1b71008b65f02f1e252b (patch)
tree1ec6cb9978610d2e68535eb56524149fc746a52a
Initial, first working version
-rw-r--r--.gitignore2
-rw-r--r--postrun.cpp222
-rw-r--r--test.prn2
3 files changed, 226 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..babfeab
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+postrun
+*.dSYM
diff --git a/postrun.cpp b/postrun.cpp
new file mode 100644
index 0000000..4de856b
--- /dev/null
+++ b/postrun.cpp
@@ -0,0 +1,222 @@
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <vector>
+#include <unordered_map>
+#include <stack>
+#include <functional>
+#include <cstdlib>
+#include <cstring>
+
+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);}
+
+vector<string> tokenise(istream &stream){
+ vector<string> tokens;
+ string token;
+ char c,stringmode;
+ while(true){
+ c=stream.get();
+ if(!stream)break;
+ if(isword(c)||(token.size()>0&&isextword(c))){
+ token+=c;
+ } else if(isdigit(c)){
+ if(token.size()==0)token="#";
+ token+=c;
+ } else {
+ if(token.size())tokens.push_back(move(token));
+ else if(isoperator(c))tokens.push_back(string(1,c));
+ else if(c=='"'||c=='\''){
+ stringmode=c;
+ while(true){
+ c=stream.get();
+ if(!stream)throw string("Non-terminated string at end of file");
+ if(c==stringmode){
+ token="'"+token;
+ tokens.push_back(move(token));
+ break;
+ }
+ if(c=='\\'){
+ c=stream.get();
+ if(!stream)throw string("Non-terminated escape sequence in string at end of file");
+ switch(c){
+ case 'n':c='\n';break;
+ case 'r':c='\r';break;
+ case 't':c='\t';break;
+ case '0':c='\0';break;
+ case 'x':{
+ int res;
+ c=stream.get();
+ cerr<<"Parsed "<<c;
+ 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();
+ cerr<<c<<" into ";
+ 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;
+ cerr<<(int)c<<endl;
+ break;
+ }
+ }
+ }
+ token+=c;
+ }
+ } else if(isspace(c));
+ else throw string("Invalid character found in source: '")+c+'\'';
+ }
+ }
+ return tokens;
+}
+
+string readfile(ifstream stream){
+ stream.seekg(0,ios::end);
+ size_t len=stream.tellg();
+ stream.seekg(0,ios::beg);
+ string contents;
+ contents.resize(len);
+ stream.read(&*contents.begin(),len);
+ return contents;
+}
+
+struct Stackitem{
+ bool isstr;
+ string strval;
+ int intval;
+};
+
+const unordered_map<string,function<void(stack<Stackitem>&,unordered_map<string,Stackitem>&)>> builtins={
+ {"+",[](stack<Stackitem> &S,unordered_map<string,Stackitem> &variables){
+ if(S.size()<2)throw string("Builtin '+' needs 2 stack items");
+ Stackitem a,b,c;
+ b=S.top(); S.pop();
+ a=S.top(); S.pop();
+ if(!a.isstr&&!b.isstr){
+ S.push({false,"",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.push({true,a.strval+b.strval,0});
+ }},
+ {"-",[](stack<Stackitem> &S,unordered_map<string,Stackitem> &variables){
+ if(S.size()<2)throw string("Builtin '-' needs 2 stack items");
+ Stackitem a,b,c;
+ b=S.top(); S.pop();
+ a=S.top(); S.pop();
+ if(a.isstr||b.isstr)throw string("Builtin '-' cannot accept a string argument");
+ S.push({false,"",a.intval-b.intval});
+ }},
+ {"print",[](stack<Stackitem> &S,unordered_map<string,Stackitem> &variables){
+ if(S.size()<1)throw string("Builtin 'print' needs 1 stack item");
+ Stackitem v;
+ v=S.top(); S.pop();
+ if(v.isstr)cout<<v.strval<<flush;
+ else cout<<v.intval<<flush;
+ }}
+};
+
+void runpass2(const vector<string> &T,
+ stack<Stackitem> &S,
+ const unordered_map<string,vector<string>> &functions,
+ unordered_map<string,Stackitem> &variables){
+ unsigned int cursor;
+ for(cursor=0;cursor<T.size();cursor++){
+ const string &word=T[cursor];
+ //cerr<<"Executing word <"<<word<<'>'<<endl;
+ if(word[0]=='#'){
+ S.push({false,"",(int)strtol(word.data()+1,NULL,10)});
+ } else if(word[0]=='\''){
+ S.push({true,word.substr(1),0});
+ } else {
+ auto it=variables.find(word);
+ if(it!=variables.end()){
+ S.push(it->second);
+ } else {
+ auto it=functions.find(word);
+ if(it!=functions.end()){
+ runpass2(it->second,S,functions,variables);
+ } else {
+ auto it=builtins.find(word);
+ if(it==builtins.end())throw "Unknown variable or function '"+word+"'";
+ it->second(S,variables);
+ }
+ }
+ }
+ }
+}
+
+void run(vector<string> T){
+ unordered_map<string,vector<string>> functions;
+ unordered_map<string,Stackitem> variables;
+ stack<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;
+ for(end=start;end<T.size()&&T[end]!="}";end++);
+ vector<string> &functoks=functions[name];
+ functoks.resize(end-start);
+ for(unsigned int i=start;i<end;i++)functoks[i]=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, evaluate file
+ runpass2(T,S,functions,variables);
+}
+
+int main(int argc,char **argv){
+ if(argc!=2){
+ cerr<<"Call this interpreter with the Postrun source file"<<endl;
+ return 1;
+ }
+ ifstream srcf(argv[1]);
+ if(!srcf){
+ cerr<<"Could not open file '"<<argv[1]<<"'"<<endl;
+ return 1;
+ }
+ //string source=readfile(srcf);
+ try {
+ const vector<string> tokens=tokenise(srcf);
+ for(const string &tok : tokens)cerr<<'['<<tok<<"] "; cerr<<endl;
+ run(tokens);
+ srcf.close();
+ } catch(string e){
+ cerr<<"ERROR: "<<e<<endl;
+ return 1;
+ }
+}
diff --git a/test.prn b/test.prn
new file mode 100644
index 0000000..13fe1d7
--- /dev/null
+++ b/test.prn
@@ -0,0 +1,2 @@
+"a" "b\x0a" + print
+1 12 + 3 - print