diff --git a/runtime/main.cpp b/runtime/main.cpp index a212d59..dbbf58d 100644 --- a/runtime/main.cpp +++ b/runtime/main.cpp @@ -5,6 +5,24 @@ */ #include "runtime.h" +struct subroutine *callStack=nullptr; + +subroutine::subroutine(enum STATES r) +{ + this->ret=r; + this->called=callStack; +} + +enum STATES subroutine::close() +{ + if (callStack==nullptr) return STACK_UNDERFLOW_ERROR; + enum STATES r=callStack->ret; + struct subroutine *l=callStack->called; + delete callStack; + callStack=l; + return r; +} + int main(int argc, char *argv[]) { unsigned int ret=run(); diff --git a/runtime/runtime.h b/runtime/runtime.h index 3a1435a..ae2c8a7 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -14,9 +14,24 @@ enum STATES:unsigned int { EXIT, UNDEFINED_STATE_ERROR, + STACK_UNDERFLOW_ERROR, START }; +class subroutine +{ + struct subroutine *called; + enum STATES ret; + +public: + static enum STATES close(); + subroutine(enum STATES r); + virtual ~subroutine() + {} +}; + +extern struct subroutine *callStack; + /* function prototype */ unsigned int run(); diff --git a/tester.cpp b/tester.cpp index 21132e7..dcd8f03 100644 --- a/tester.cpp +++ b/tester.cpp @@ -72,6 +72,7 @@ const string CODETYPES[]={ enum COMPILE_ERRORS errorLevel=E_OK; unsigned int indentLevel=0; bool scopeGlobal=true; +unsigned int currentFunc=0; bool COMPILE=false; bool DUMP=false; @@ -201,7 +202,7 @@ void setUp() { varNames.open("/dev/null"); } - if (DEBUG) + if (DEBUG || TRACE) { /* dump identifier mode */ logfile.open("parse.log"); @@ -237,7 +238,7 @@ void logger(string s) if (DEBUG) { indent(); - logfile << s << "\n"; + logfile << s << endl; } } @@ -246,12 +247,11 @@ void shutDown() { if (errorLevel != E_OK) cerr << "\nERROR: " << COMPILE_ERROR_NAMES[errorLevel] << "\n\n" << endl; - logger("Purging tempVar queues"); + logger("Shutting Down: Purging tempVar queues"); tempVar::eraseQueues(); - logger("Dumping stack."); - if (DUMP && (logfile)) fn::dumpCallStack(); if (DUMP) { + fn::dumpFunctionIDs(); varNames << "Global Variables\n"; for(auto iter=globals.begin(); iter!=globals.end(); ++iter) { @@ -276,18 +276,29 @@ void shutDown() variableType *v; printSegment *print; +fn *func; +operands *o; +expression *e; +listp; void testInt() { + logger("testInt started"); string name=string("v"); v=variableType::getOrCreateVar(name, T_INTVAR); - v->assignment(new expression(new constOp("2", T_INT))); + logger("variable v created"); + o=new constOp("2", T_INT); + logger("constOp 2 created"); + v->assignment(new expression(o)); + logger("assignment created"); print=new printSegment(new expression(v)); print->generate(); delete print; + logger("testInt cleared."); } void testString() { + logger("testString started"); string name=string("name1"); v=variableType::getOrCreateVar(name, T_STRINGVAR); v->assignment(new expression(new constOp("Hello", T_STRING))); @@ -297,10 +308,12 @@ void testString() print=new printSegment(new expression(new constOp(" world!", T_STRING))); print->generate(); delete print; + logger("testString cleared"); } void testFloat() { + logger("testFloat started"); string name=string("floater"); v=variableType::getOrCreateVar(name, T_FLOATVAR); v->assignment(new expression(new constOp("3.14159265", T_FLOAT))); @@ -310,6 +323,40 @@ void testFloat() print=new printSegment(new expression(new constOp(" is pi", T_STRING))); print->generate(); delete print; + logger("testFloat cleared"); +} + +void testFunc() +{ + logger("testFunc started"); + string name=string("square"); + logger("aloha"); + o = operands::createOp(T_FLOATVAR); + logger("mahalo"); + func=fn::declare(name, T_FLOATFUNC, o); + logger("funkBeat"); + name=string("radius"); + v=variableType::getOrCreateVar(name, T_FLOATVAR); + logger("param made"); + func->addParameter(v); + logger("param added"); + e=new expression(new expression(v), O_MULTIPLY, new expression(v)); + logger("expression made"); + func->generateReturn(e); + logger("return generated"); + func->close(); + logger("function ended"); + o=new constOp("3.0", T_FLOAT); + p.push_back(o); + name=string("square"); + o=func->generateCall(name, p); + logger("call generated"); + e=new expression(o); + print=new printSegment(e); + o->dispose(); + print->generate(); + delete print; + logger("testFunc cleared"); } /* open files and compile */ @@ -319,6 +366,9 @@ void compile() testInt(); testString(); testFloat(); + testFunc(); label::generateEnd(); + /*check for nesting error */ + if (!scopeGlobal) error(E_END_FUNCTION); shutDown(); } diff --git a/yab2cpp.cpp b/yab2cpp.cpp index 5a29d1c..b643fba 100644 --- a/yab2cpp.cpp +++ b/yab2cpp.cpp @@ -71,6 +71,7 @@ const string CODETYPES[]={ enum COMPILE_ERRORS errorLevel=E_OK; unsigned int indentLevel=0; bool scopeGlobal=true; +unsigned int currentFunc=0; bool COMPILE=false; bool DUMP=false; @@ -247,8 +248,6 @@ void shutDown() << COMPILE_ERROR_NAMES[errorLevel] << "\n\n" << endl; logger("Purging tempVar queues"); tempVar::eraseQueues(); - logger("Dumping stack."); - if (DUMP && (logfile)) fn::dumpCallStack(); if (DUMP) { varNames << "Global Variables\n"; diff --git a/yab2cpp.h b/yab2cpp.h index 7f06902..8ce4c84 100644 --- a/yab2cpp.h +++ b/yab2cpp.h @@ -60,7 +60,9 @@ enum COMPILE_ERRORS:unsigned int extern enum COMPILE_ERRORS errorLevel; extern unsigned int indentLevel; +/*TODO: Replace scopeGlobal with currentFunc==0*/ extern bool scopeGlobal; +extern unsigned int currentFunc; /* flags used internally by the compiler */ extern bool COMPILE; @@ -134,6 +136,7 @@ enum SCOPES:unsigned int enum OPERATORS:unsigned int { + O_NOP, O_PLUS, O_MINUS, O_MULTIPLY, @@ -247,8 +250,7 @@ public: /* Terminal expression node */ expression(operands *x); - virtual ~expression() - {} + virtual ~expression(); }; /* parent class of all code types */ @@ -357,12 +359,14 @@ public: class variableType:public operands { enum SCOPES myScope; + unsigned int localID; public: static variableType *getOrCreateVar(string &name, enum TYPES t); + virtual string boxName(); void assignment(expression *value); /* always call generateBox() after new variableType() */ - variableType(enum SCOPES s, string &name, enum TYPES t); + variableType(enum SCOPES s, string &name, enum TYPES t, unsigned int fnID); variableType(); ~variableType() {} @@ -400,42 +404,43 @@ public: virtual ~forLoop(); }; -class fn:codeType +class fn { static unordered_map > functions; - static list callStack; static unsigned int nextID; listparams; - string funcName; unsigned int id; + enum CODES type; enum TYPES kind; operands *rc; /* two labels common to all subroutine calls */ label *startAddr; - label *ret; - /* private constructor used by generateGosub and generateOnNSub*/ - fn(label *gosub); + /* stamdard constructor called by declare */ + fn(enum CODES t, operands *returnCode=nullptr); public: - static void dumpCallStack(); - static fn *getCurrentSub(); static fn *getSub(string &name); static void generateGosub(label * sub); /* must be called after label::generateOnNSkip */ static void generateOnNSub(expression *e, unsigned int skip); + enum CODES getType() const {return this->type;} unsigned int getID() const {return this->id;} - int getNumParams() const {return this->params.size();} + size_t getNumParams() const {return this->params.size();} void addParameter(variableType *); operands *generateCall(string &name, list¶mList); + /* standard return is for call */ void generateReturn(expression *expr); + /* parameterless return is for gosub */ void generateReturn(); virtual void generateBreak(); virtual void close(); - fn(string &name, enum CODES t, operands *returnCode=nullptr); - virtual ~fn() - {} + static fn *declare(string &name, enum CODES t, operands *returnCode=nullptr); + static void dumpFunctionIDs(); + + /* is called only by unique_ptr in static "functions" hash table */ + virtual ~fn(); }; class printSegment diff --git a/yabDataStructures.cpp b/yabDataStructures.cpp index 61da7b9..125466d 100644 --- a/yabDataStructures.cpp +++ b/yabDataStructures.cpp @@ -35,7 +35,7 @@ enum TYPES operands::getSimpleVarType(enum TYPES t) case T_STRINGVAR: return T_STRINGVAR; default: - break; + break; } error(E_UNASSIGNABLE_TYPE); } @@ -87,19 +87,18 @@ void operands::generateBox(enum SCOPES s) operands *operands::createOp(enum TYPES t) { - if (DEBUG) + if (TRACE) { - operands *x=createOp(t); - x->generateBox(scopeGlobal?S_GLOBAL:S_LOCAL); - return x; + indent(); + logfile << "Creating operand of type " + << TYPENAMES[t] << endl; } return tempVar::getOrCreateVar(t); } +/* only tempVar type needs disposing */ void operands::dispose() -{ - delete this; -} +{} string operands::boxName() { @@ -295,8 +294,8 @@ expression::expression(expression *l, enum OPERATORS o, expression *r) expression::expression(operands *x) { - op=x; - oper=O_TERM; + this->op=x; + this->oper=O_TERM; } /* binary vs. unary ops */ @@ -323,10 +322,14 @@ operands *expression::evaluate() operands *l; operands *r; enum TYPES t; + logger("evaluating left"); l=this->getLeft()->evaluate(); + logger("left evaluated"); if (this->isBinOp()) { + logger("evaluating right"); r=this->getRight()->evaluate(); + logger("evaluated right"); enum TYPES lt=l->getSimpleVarType(); enum TYPES rt=r->getSimpleVarType(); if (lt==T_INTVAR && rt==T_FLOATVAR) @@ -398,6 +401,7 @@ operands *expression::evaluate() this->op=operands::createOp(t); output_cpp << this->op->boxName() << "=" << l->boxName() << "*" << r->boxName() << ";\n"; + logger("multiply done"); break; case O_OR: if (t!=T_INTVAR) error(E_TYPE_MISMATCH); @@ -445,21 +449,32 @@ operands *expression::evaluate() error(E_INTERNAL); } /* convert expression into single operand */ - this->oper=O_TERM; - l->dispose(); + logger("deleting left"); delete left; - if (getRight()!=nullptr) + logger("left deleted"); + if (isBinOp()) { - r->dispose(); + logger("deleting right"); delete right; + logger("right deleted"); } + this->oper=O_TERM; return this->op; } +expression::~expression() +{ + if(this->getOp()==O_TERM) + { + op->dispose(); + } +} + /* variable definitions */ -variableType::variableType(enum SCOPES s, string &name, enum TYPES t):operands(t) +variableType::variableType(enum SCOPES s, string &name, enum TYPES t, unsigned int fnID):operands(t) { this->myScope=s; + this->localID=fnID; switch (s) { case S_LOCAL: @@ -481,6 +496,18 @@ variableType::variableType(enum SCOPES s, string &name, enum TYPES t):operands(t } } +string variableType::boxName() +{ + ostringstream ss; + if (myScope==S_LOCAL) + { + ss << "sub" << this->localID << "->v" << this->getID(); + return ss.str(); + } + ss << "v" << this->getID(); + return ss.str(); +} + variableType *variableType::getOrCreateVar(string &name, enum TYPES t) { if (!scopeGlobal) @@ -491,7 +518,15 @@ variableType *variableType::getOrCreateVar(string &name, enum TYPES t) if(i!=statics.end())return i->second.get(); } if (globals.find(name)!=globals.end())return globals[name].get(); - variableType *v=new variableType(scopeGlobal?S_GLOBAL:S_LOCAL, name, t); + variableType *v; + if (scopeGlobal) + { + v=new variableType(S_GLOBAL, name, t, 0); + } + else + { + v=new variableType(S_LOCAL, name, t, currentFunc); + } v->generateBox(scopeGlobal?S_GLOBAL:S_LOCAL); return v; } @@ -522,7 +557,6 @@ void variableType::assignment(expression *value) << op->boxName() << ";\n"; break; } - op->dispose(); delete value; } @@ -566,7 +600,7 @@ string arrayType::generateBox(enum SCOPES s) } arrayType::arrayType(string &name, enum TYPES t, listdim): - variableType(S_GLOBAL, name, t) + variableType(S_GLOBAL, name, t, 0) { this->dimensions=dim; } diff --git a/yabFunctions.cpp b/yabFunctions.cpp index 8269fe7..734a619 100644 --- a/yabFunctions.cpp +++ b/yabFunctions.cpp @@ -10,35 +10,30 @@ /* static initializers */ unordered_map > fn::functions; -listfn::callStack; -unsigned int fn::nextID=0; +unsigned int fn::nextID; /* function definitions */ -void fn::dumpCallStack() +void fn::dumpFunctionIDs() { - auto i=callStack.rbegin(); - if (i==callStack.rend()) + varNames << "Function IDs\n"; + auto i=functions.begin(); + if (i==functions.end()) { - logfile << "call stack was empty\n"; + varNames << "no named functions\n"; return; } do { - logfile << (*i)->funcName << "\n"; + varNames << "Function " << i->first + << " has ID f" << i->second.get()->getID() << "\n"; ++i; - } while(i!=callStack.rend()); -} - -fn *fn::getCurrentSub() -{ - return callStack.back(); + } while (i!=functions.end()); } void fn::generateOnNSub(expression *e, unsigned int skip) { label *r=new label(); - fn *self=new fn(r); - fn::callStack.push_back(self); + output_cpp << "callStack=new subroutine(" << r->getID() << ");\n"; label::generateOnNTo(e, skip); r->generate(); } @@ -46,18 +41,11 @@ void fn::generateOnNSub(expression *e, unsigned int skip) void fn::generateGosub(label *sub) { label *r=new label(); - fn *self=new fn(r); - fn::callStack.push_back(self); + output_cpp << "callStack=new subroutine(" << r->getID() << ");\n"; sub->generateJumpTo(); r->generate(); } -fn::fn(label *gosub):codeType(T_GOSUB) -{ - this->funcName="unnamed gosub"; - this->ret=gosub; -} - fn *fn::getSub(string &name) { auto iter=fn::functions.find(name); @@ -65,32 +53,42 @@ fn *fn::getSub(string &name) return iter->second.get(); } +void fn::addParameter(variableType *v) +{ + this->params.push_back(v); +} + +/* TODO needs to be broken into smaller pieces */ operands *fn::generateCall(string &name, list¶mList) { auto v=params.begin(); operands *current; + label *retAddr=new label(); fn *g=fn::getSub(name); if (g==nullptr) { error(E_SUBROUTINE_NOT_FOUND); } - if (paramList.size()>params.size()) + if (paramList.size() > this->getNumParams()) { error(E_TOO_MANY_PARAMETERS); } /* TODO CHECK THIS */ output_cpp << "struct f" << g->getID() - << "*sub" << this->getID() + << " *sub" << this->getID() << "= new struct f" << g->getID() - << "();\n"; + << "(" << retAddr->getID() << ");\n" + << "callStack = sub" <getID() << ";\n"; /* TODO Make parameter processing a separate function */ while(paramList.size()>0) { - current= paramList.front(); + current=paramList.front(); paramList.pop_front(); if(current->getSimpleVarType()!=(*v)->getType()) { + cerr << "assigning " << TYPENAMES[current->getType()] + << " to " << (*v)->getType() << endl; error(E_TYPE_MISMATCH); } (*v)->assignment(new expression(current)); @@ -115,53 +113,44 @@ operands *fn::generateCall(string &name, list¶mList) ++v; } g->startAddr->generateJumpTo(); - g->ret->generate(); + retAddr->generate(); return g->rc; } +/* typeless return for gosub family */ void fn::generateReturn() { - fn *c=getCurrentSub(); - switch(c->getType()) - { - case T_UNKNOWNFUNC: - /* set return type to NONE */ - this->kind=T_NONE; - /*fallthrough*/ - case T_GOSUB: - c->ret->generateJumpTo(); - fn::callStack.pop_back(); - break; - default: - error(E_TYPE_MISMATCH); - } + output_cpp << "state=subroutine::close();\nbreak;\n"; } void fn::generateReturn(expression *expr) { + logger("evaluating expression"); this->rc=expr->evaluate(); + logger("expression evaluated"); this->kind=rc->getSimpleVarType(); - this->ret->generateJumpTo(); - fn::callStack.pop_back(); - delete expr; + logger("generating return"); + generateReturn(); switch (this->getType()) { case T_UNKNOWNFUNC: - return; + break; case T_STRINGFUNC: if (kind!=T_STRINGVAR) error(E_TYPE_MISMATCH); - return; + break; case T_INTFUNC: if (kind!=T_INTVAR&&kind!=T_FLOATVAR) error(E_TYPE_MISMATCH); - return; + break; case T_FLOATFUNC: if(kind!=T_FLOATVAR) error(E_TYPE_MISMATCH); - return; + break; case T_GOSUB: error(E_GOSUB_CANNOT_RETURN_VALUE); default: error(E_BAD_SYNTAX); } + logger("deleting expression"); + delete expr; } /* not allowed in function definitions directly */ @@ -181,31 +170,79 @@ void fn::close() this->generateReturn(); } funcs_h << "};\n"; + if(DUMP) + { + auto i=locals.begin(); + varNames << "\nLocal variables in function f" << this->getID() << "\n"; + if(i==locals.end()) + { + varNames << "no non-static locals\n"; + } + else + { + do + { + varNames << "variable " << i->first << " has id v" + << i->second.get()->getID() << "\n"; + ++i; + }while(i!=locals.end()); + } + i=statics.begin(); + varNames << "\n Static locals in function f" << this->getID() << "\n"; + if (i==statics.end()) + { + varNames << "no static locals\n"; + } + else + { + do + { + varNames << "variable " << i->first << " has id v" + << i->second.get()->getID() << "\n"; + ++i; + } while (i!=statics.end()); + } + } locals.clear(); statics.clear(); - this->params.clear(); + currentFunc=0; scopeGlobal=true; } -fn::fn(string &s, enum CODES t, operands *returnCode):codeType(t) +fn *fn::declare(string &s, enum CODES t, operands *returnCode) { /*check for nesting error */ if (!scopeGlobal) error(E_END_FUNCTION); /*check if this function name is already used*/ if (fn::functions.find(s)!=fn::functions.end()) error(E_DUPLICATE_SYMBOL); - this->funcName=s; + logger("declaration name cleared"); + fn *self=new fn(t, returnCode); + logger("fn allocated"); + fn::functions.insert({s,unique_ptr(self)}); + logger("fn inserted"); + /* initiate local scope */ + currentFunc=self->getID(); + scopeGlobal=false; + return self; +} + +/* standard constructor for block-formatted functions */ +fn::fn(enum CODES t, operands *returnCode) +{ + this->params.clear(); + this->type=t; this->id= ++nextID; /*define storage for locals*/ - funcs_h << "struct f" << this->id <<"\n{\n"; - /*define label space for return*/ - this->ret=new label(); - /*allocate function name*/ - fn::functions[s]=unique_ptr(this); - /* initiate local scope */ - scopeGlobal=false; + if (t!=T_GOSUB) funcs_h << "struct f" << this->id <<":public subroutine\n{\n"; /*keep track of where the return code will be sent to*/ this->rc=returnCode; /*allocate and generate start address label*/ this->startAddr=new label(); startAddr->generate(); } + +fn::~fn() +{ + this->params.clear(); + delete startAddr; +}