Got past temporary variable deallocation bug. Now working on functions.

This commit is contained in:
Samuel D. Crow
2021-04-08 15:31:36 -05:00
parent 8797cb8870
commit d37eb38ad1
7 changed files with 258 additions and 100 deletions

View File

@@ -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();

View File

@@ -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();

View File

@@ -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;
list<operands *>p;
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();
}

View File

@@ -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";

View File

@@ -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<string, unique_ptr<fn> > functions;
static list<fn *> callStack;
static unsigned int nextID;
list<variableType * >params;
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<operands *>&paramList);
/* 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

View File

@@ -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, list<unsigned int>dim):
variableType(S_GLOBAL, name, t)
variableType(S_GLOBAL, name, t, 0)
{
this->dimensions=dim;
}

View File

@@ -10,35 +10,30 @@
/* static initializers */
unordered_map<string, unique_ptr<fn> > fn::functions;
list<fn *>fn::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<operands *>&paramList)
{
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" <<this->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<operands *>&paramList)
++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<fn>(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<fn>(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;
}