From a8e5f14082f656e8a329969c96940cc547aa54d8 Mon Sep 17 00:00:00 2001 From: tomsmeding Date: Wed, 19 Aug 2015 17:37:41 +0200 Subject: LOTS of stuff and BRAINFUCK INTERPRETER Lots of builtins added, made ready for the brainfuck interpreter, included as bf.prn. --- bf.prn | 86 +++++++++++ postrun.cpp | 465 +++++++++++++++++++++++++++++++++++++++++------------------- test.bf | 1 + test.prn | 5 +- 4 files changed, 413 insertions(+), 144 deletions(-) create mode 100644 bf.prn create mode 100644 test.bf diff --git a/bf.prn b/bf.prn new file mode 100644 index 0000000..11f0e69 --- /dev/null +++ b/bf.prn @@ -0,0 +1,86 @@ +20 "TAPELEN" store + + +##First, read in the source + +"" +1 while + getline + dup -1 = if + pop + 0 + else + "\n" swap + + + 1 + end +end +strlen 1 swap substr swap pop +#bottom of stack now contains 1 string, the source code +strlen "len" store +"code" store + + + +##Then, create the tape +TAPELEN +dup 0 > while + 0 swap +1 - dup 0 > end + + + +##Finally, start executing + +@defun "execute" { + #"start of 'execute': " print stackdump + + depth 1 + "depth" store + + #"trying to execute " print dup print lf + + "code" depth + store + + 0 "i" store + i len < while + #"i = " print i print lf + "code" depth + dup swapoutvar + i stridx "c" store + swap store #uses the dupped name before the matching swapoutvar + #"c = " c + print stackdump + + c "+" = if 1 + 256 % end + c "-" = if 255 + 256 % end + c "." = if dup chr print end + c "," = if pop getc ord 256 % end + c ">" = if 1 roll end + c "<" = if -1 roll end + c "]" = if + i "loopend" depth 1 - + store + len "i" store #essentially, break + end + c "[" = if + i "loopstart" depth + store + #"loopstart" print depth print " = " print "loopstart" depth + get print lf + dup while + "code" depth + get i 1 + len substr swap "code" depth + store execute + #stackdump "i = " print i print lf + "loopstart" depth + get "i" store + dup end + "loopend" depth + get 1 + "loopstart" depth + get + "i" store + #"after loop, i = " print i print lf + end + + i 1 + "i" store + i len < end + + depth 1 - "depth" store +} + +0 "depth" store + +"code" swapoutvar +execute #kaboom + +depth 0 = ! if + "The interpreter exited in depth " depth + "..." + print +end diff --git a/postrun.cpp b/postrun.cpp index 7d2a08d..916c65c 100644 --- a/postrun.cpp +++ b/postrun.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -13,24 +12,36 @@ using namespace std; inline bool isword(char c){return isalpha(c)||c=='_'||c=='@'||c=='$';} inline bool isextword(char c){return isword(c)||isdigit(c);} -inline bool isoperator(char c){return (bool)strchr("+-*/=> tokenise(istream &stream){ vector tokens; string token; char c,stringmode; + bool gotminus=false; while(true){ c=stream.get(); if(!stream)break; - if(isword(c)||(token.size()>0&&isextword(c))){ + if(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(isdigit(c)){ - if(token.size()==0)token="#"; + } else if(isword(c)||(token.size()>0&&isextword(c))){ token+=c; } else { if(token.size())tokens.push_back(move(token)); - if(isoperator(c))tokens.push_back(string(1,c)); - else if(c=='"'||c=='\''){ + 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(); @@ -48,6 +59,8 @@ vector tokenise(istream &stream){ 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(); @@ -70,6 +83,7 @@ vector tokenise(istream &stream){ else throw string("Invalid character found in source: '")+c+'\''; } } + if(token.size())tokens.push_back(move(token)); return tokens; } @@ -91,18 +105,31 @@ struct Stackitem{ Stackitem(void):isstr(false),intval(0){} Stackitem(int _i):isstr(false),intval(_i){} Stackitem(const string &_s):isstr(true),strval(_s),intval(0){} - operator bool(void){return (isstr&&strval.size())||(!isstr&&intval);} - bool operator==(const Stackitem &other){return isstr==other.isstr&&(isstr?strval==other.strval:intval==other.intval);} + 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);} }; -const unordered_map&,unordered_map&)>> builtins={ - {"+",[](stack &S,unordered_map &variables){ - if(S.size()<2)throw string("Builtin '+' needs 2 stack items"); - Stackitem a,b; - b=S.top(); S.pop(); - a=S.top(); S.pop(); +#define BUILTIN_GUARD_STACKSIZE(nm,sz) {if(S.size()<(sz))throw string("Builtin '" nm "' needs " #sz " stack item")+(sz==1?"":"s");} + +const unordered_map&,unordered_map&)>> builtins={ + {"+",[](vector &S,unordered_map &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(a.intval+b.intval); + S.emplace_back(a.intval+b.intval); return; } if(!a.isstr){ @@ -113,139 +140,271 @@ const unordered_map&,unordered_map &S,unordered_map &variables){ - if(S.size()<2)throw string("Builtin '-' needs 2 stack items"); - Stackitem a,b; - b=S.top(); S.pop(); - a=S.top(); S.pop(); + {"-",[](vector &S,unordered_map &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(a.intval-b.intval); + S.emplace_back(a.intval-b.intval); }}, - {"*",[](stack &S,unordered_map &variables){ - if(S.size()<2)throw string("Builtin '*' needs 2 stack items"); - Stackitem a,b; - b=S.top(); S.pop(); - a=S.top(); S.pop(); + {"*",[](vector &S,unordered_map &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(a.intval*b.intval); + S.emplace_back(a.intval*b.intval); }}, - {"/",[](stack &S,unordered_map &variables){ - if(S.size()<2)throw string("Builtin '/' needs 2 stack items"); - Stackitem a,b; - b=S.top(); S.pop(); - a=S.top(); S.pop(); + {"/",[](vector &S,unordered_map &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(a.intval/b.intval); + S.emplace_back(a.intval/b.intval); }}, - {"%",[](stack &S,unordered_map &variables){ - if(S.size()<2)throw string("Builtin '%' needs 2 stack items"); - Stackitem a,b; - b=S.top(); S.pop(); - a=S.top(); S.pop(); + {"%",[](vector &S,unordered_map &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<<'"'< &S,unordered_map &variables){ - if(S.size()<2)throw string("Builtin '^' needs 2 stack items"); - Stackitem a,b; - b=S.top(); S.pop(); - a=S.top(); S.pop(); + {"^",[](vector &S,unordered_map &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((int)pow(a.intval,b.intval)); + S.emplace_back((int)pow(a.intval,b.intval)); }}, - {"!",[](stack &S,unordered_map &variables){ - if(S.size()<1)throw string("Builtin '!' needs 1 stack item"); - Stackitem v; - v=S.top(); S.pop(); - if(v.isstr)S.emplace(!v.strval.size()); - else S.emplace(!v.intval); + {"!",[](vector &S,unordered_map &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); }}, - {"=",[](stack &S,unordered_map &variables){ - if(S.size()<2)throw string("Builtin '=' needs 2 stack items"); - Stackitem a,b; - b=S.top(); S.pop(); - a=S.top(); S.pop(); - S.emplace(a==b); + {"=",[](vector &S,unordered_map &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); }}, - {"<",[](stack &S,unordered_map &variables){ - if(S.size()<2)throw string("Builtin '<' needs 2 stack items"); - Stackitem a,b; - b=S.top(); S.pop(); - a=S.top(); S.pop(); - if(a.isstr&&b.isstr)S.emplace(a.strval &S,unordered_map &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",[](stack &S,unordered_map &variables){ - if(S.size()<2)throw string("Builtin '>' needs 2 stack items"); - Stackitem a,b; - b=S.top(); S.pop(); - a=S.top(); S.pop(); - if(a.isstr&&b.isstr)S.emplace(a.strval>b.strval); - else if(!a.isstr&&!b.isstr)S.emplace(a.intval>b.intval); - else if(a.isstr)S.emplace(a.strval>to_string(b.intval)); - else S.emplace(to_string(a.intval)>b.strval); + {">",[](vector &S,unordered_map &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",[](stack &S,unordered_map &variables){ - if(S.size()<1)throw string("Builtin 'print' needs 1 stack item"); - Stackitem v; - v=S.top(); S.pop(); + {"print",[](vector &S,unordered_map &variables){ + BUILTIN_GUARD_STACKSIZE("print",1) + Stackitem v=move(S.back()); S.pop_back(); if(v.isstr)cout< &S,unordered_map &variables){ + {"lf",[](vector &S,unordered_map &variables){ cout< &S,unordered_map &variables){ - if(S.size()<1)throw string("Builtin 'dup' needs 1 stack item"); - S.push(S.top()); + {"dup",[](vector &S,unordered_map &variables){ + BUILTIN_GUARD_STACKSIZE("dup",1) + S.push_back(S.back()); + }}, + {"pop",[](vector &S,unordered_map &variables){ + BUILTIN_GUARD_STACKSIZE("pop",1) + S.pop_back(); + }}, + {"swap",[](vector &S,unordered_map &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 &S,unordered_map &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 &S,unordered_map &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 &S,unordered_map &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 &S,unordered_map &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 tempstore; + tempstore.reserve(n); + int ssz=S.size(); + for(int i=ssz-n;i &S,unordered_map &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 &S,unordered_map &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 &S,unordered_map &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 &S,unordered_map &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 &S,unordered_map &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=""; + }}, + {"getline",[](vector &S,unordered_map &variables){ + BUILTIN_GUARD_STACKSIZE("getline",0) + string line; + getline(cin,line); + if(!cin)S.emplace_back(-1); + else S.emplace_back(line); }}, - {"pop",[](stack &S,unordered_map &variables){ - if(S.size()<1)throw string("Builtin 'pop' needs 1 stack item"); - S.pop(); + {"getc",[](vector &S,unordered_map &variables){ + BUILTIN_GUARD_STACKSIZE("getc",0) + if(!cin)S.emplace_back(-1); + else S.emplace_back(string(1,cin.get())); + }}, + {"stackdump",[](vector &S,unordered_map &variables){ + BUILTIN_GUARD_STACKSIZE("stackdump",0) + cerr<<"STACKDUMP: "; + for(const Stackitem &si : S){ + if(si.isstr)cerr<<'"'< &S,unordered_map &variables){ + BUILTIN_GUARD_STACKSIZE("exit",0) + exit(0); }}, }; +#undef BUILTIN_GUARD_STACKSIZE void runpasseval(const vector &T, - stack &S, - const unordered_map> &functions, - unordered_map &variables, - const unordered_map &jumpmap){ + vector &S, + const unordered_map,unordered_map>> &functions, + unordered_map &variables, + const unordered_map &jumpmap){ unsigned int cursor; for(cursor=0;cursor'<second); + S.push_back(it->second); } else { auto it=functions.find(word); if(it!=functions.end()){ - runpasseval(it->second,S,functions,variables,jumpmap); + 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.top(); S.pop(); + Stackitem v=S.back(); S.pop_back(); if(!v){ + auto it=jumpmap.find(cursor); + if(it==jumpmap.end()){ + cerr<<"NO JUMPMAP ENTRY FOR "<second-1; //jump to corresponding end } continue; } else if(word=="if"){ if(S.size()<1)throw string("Keyword 'if' needs 1 stack item"); - Stackitem v=S.top(); S.pop(); + Stackitem v=S.back(); S.pop_back(); if(!v){ + auto it=jumpmap.find(cursor); + if(it==jumpmap.end()){ + cerr<<"NO JUMPMAP ENTRY FOR "<second-1; //jump to corresponding else/end } continue; } else if(word=="else"||word=="end"){ + auto it=jumpmap.find(cursor); + if(it==jumpmap.end()){ + cerr<<"NO JUMPMAP ENTRY FOR "<second-1; continue; } @@ -259,53 +418,15 @@ void runpasseval(const vector &T, } } -void run(vector T){ - unordered_map> functions; - unordered_map variables; - unordered_map jumpmap; - stack S; - unsigned int cursor=0; - //first pass, handle functions and includes - while(cursor=T.size())throw string("Unterminated @defun statement at end of file"); - string name=T[cursor+1]; - if(name[0]!='\'')throw string("@defun expected a string as name, but got '")+name+"'"; - name.erase(0,1); - if(T[cursor+2]!="{")throw string("Invalid @defun statement of function ")+name; - unsigned int start=cursor+3; - unsigned int end; - for(end=start;end &functoks=functions[name]; - functoks.resize(end-start); - for(unsigned int i=start;i=T.size())throw string("Unterminated @include statement at end of file"); - string name=T[cursor+1]; - if(name[0]!='\'')throw string("@include expected a string as file, but got '")+name+"'"; - name.erase(0,1); - ifstream file(name); - if(!file)throw string("Could not open file '")+name+"' specified by @include statement"; - vector included=tokenise(file); - file.close(); - T.erase(T.begin()+cursor,T.begin()+cursor+2); - T.insert(T.begin()+cursor,included.begin(),included.end()); - } else { - cursor++; - } - } - - //second pass, index flow control keywords and translate '?' +void populatejumpmap(const vector &T,unordered_map &jumpmap){ + //first, index flow control keywords vector flowindexes; - for(cursor=0;cursor T){ else if(T[flowindexes[els]]=="while"||T[flowindexes[els]]=="if")depth++; if(depth==0)break; } - if(els==flowindexes.size())throw string("No 'else' after 'if' in source"); + 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 T){ 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 "< T){ + unordered_map,unordered_map>> functions; + //functions is a pair of tokens and jumpmap + unordered_map variables; + unordered_map jumpmap; + vector S; + unsigned int cursor=0; + //first pass, handle functions and includes + while(cursor=T.size())throw string("Unterminated @defun statement at end of file"); + string name=T[cursor+1]; + if(name[0]!='\'')throw string("@defun expected a string as name, but got '")+name+"'"; + name.erase(0,1); + if(T[cursor+2]!="{")throw string("Invalid @defun statement of function ")+name; + unsigned int start=cursor+3; + unsigned int end; + int depth=1; + for(end=start;end &functoks=functions[name].first; + functoks.resize(end-start); + for(unsigned int i=start;i=T.size())throw string("Unterminated @include statement at end of file"); + string name=T[cursor+1]; + if(name[0]!='\'')throw string("@include expected a string as file, but got '")+name+"'"; + name.erase(0,1); + ifstream file(name); + if(!file)throw string("Could not open file '")+name+"' specified by @include statement"; + vector included=tokenise(file); + file.close(); + T.erase(T.begin()+cursor,T.begin()+cursor+2); + T.insert(T.begin()+cursor,included.begin(),included.end()); + } else { + cursor++; + } + } + + //second pass, populate jumpmaps + populatejumpmap(T,jumpmap); + for(auto &p : functions){ + populatejumpmap(p.second.first,p.second.second); + } - //fourth pass, evaluate code + //third pass, evaluate code runpasseval(T,S,functions,variables,jumpmap); } @@ -365,7 +544,7 @@ int main(int argc,char **argv){ //string source=readfile(srcf); try { const vector tokens=tokenise(srcf); - for(const string &tok : tokens)cerr<<'['<++++++++++++<-]>----.<+++[>-----<-]>.<++[>+++++++<-]>.+.<++++++++++. diff --git a/test.prn b/test.prn index c04d8a9..65549f9 100644 --- a/test.prn +++ b/test.prn @@ -16,4 +16,7 @@ end 0 if "yup!" print lf end -"done." print lf + + +42 "vv" store +vv print lf -- cgit v1.2.3-70-g09d2