303 lines
6.9 KiB
C++
303 lines
6.9 KiB
C++
/*
|
|
** Yab2Cpp
|
|
**
|
|
** Transpiler by Samuel D. Crow
|
|
**
|
|
** Based on Yab
|
|
**
|
|
*/
|
|
#include "yab2cpp.h"
|
|
|
|
/* static initializers */
|
|
unordered_map<string, unique_ptr<fn> > fn::functions;
|
|
unsigned int fn::nextID;
|
|
unsigned int callEnumerator=0;
|
|
|
|
/* function definitions */
|
|
void fn::dumpFunctionIDs()
|
|
{
|
|
varNames << "Function IDs\n";
|
|
auto i=functions.begin();
|
|
if (i==functions.end())
|
|
{
|
|
varNames << "no named functions\n";
|
|
return;
|
|
}
|
|
do
|
|
{
|
|
varNames << "Function " << i->first
|
|
<< " has ID f" << i->second.get()->getID() << "\n";
|
|
++i;
|
|
} while (i!=functions.end());
|
|
}
|
|
|
|
void fn::generateOnNSub(expression *e, unsigned int skip)
|
|
{
|
|
label *retLabel=new label();
|
|
output_cpp << "callStack=new subroutine(" << retLabel->getID() << ");\n";
|
|
label::generateOnNTo(e, skip);
|
|
retLabel->generate();
|
|
}
|
|
|
|
void fn::generateGosub(label *sub)
|
|
{
|
|
label *retLabel=new label();
|
|
output_cpp << "callStack=new subroutine(" << retLabel->getID() << ");\n";
|
|
sub->generateJumpTo();
|
|
retLabel->generate();
|
|
}
|
|
|
|
fn *fn::getSub(string &name)
|
|
{
|
|
auto iter=fn::functions.find(name);
|
|
if(iter==fn::functions.end()) return nullptr;
|
|
return iter->second.get();
|
|
}
|
|
|
|
variableType *fn::getLocalVar(string &name)
|
|
{
|
|
auto iter=this->parameters.find(name);
|
|
if (iter!=parameters.end()) return iter->second;
|
|
auto i=locals.find(name);
|
|
if (i!=locals.end()) return i->second.get();
|
|
i=statics.find(name);
|
|
if (i!=statics.end()) return i->second.get();
|
|
return nullptr;
|
|
}
|
|
|
|
void fn::addParameter(string &name, enum TYPES t)
|
|
{
|
|
variableType *v=new variableType(S_PARAMETER, name, t, this);
|
|
v->generateBox(S_PARAMETER);
|
|
this->parameters[name]=v;
|
|
this->params.push_back(v);
|
|
}
|
|
|
|
/* TODO needs to be broken into smaller pieces */
|
|
operands *fn::generateCall(string &name, list<operands *>¶mList)
|
|
{
|
|
++callEnumerator;
|
|
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() > this->getNumParams())
|
|
{
|
|
error(E_TOO_MANY_PARAMETERS);
|
|
}
|
|
heap_h << "struct f" << g->getID()
|
|
<< " *sub" << callEnumerator << ";\n";
|
|
output_cpp << "sub" << callEnumerator
|
|
<< "= new f" << g->getID()
|
|
<< "(" << retAddr->getID() << ");\n"
|
|
<< "callStack = sub" << callEnumerator << ";\n";
|
|
|
|
/* TODO Make parameter processing a separate function */
|
|
while(paramList.size()>0)
|
|
{
|
|
current=paramList.front();
|
|
paramList.pop_front();
|
|
if(current->getSimpleVarType()!=(*v)->getType())
|
|
{
|
|
cerr << "assigning " << TYPENAMES[current->getType()]
|
|
<< " to " << TYPENAMES[(*v)->getType()] << endl;
|
|
error(E_TYPE_MISMATCH);
|
|
}
|
|
cerr << "assigning to parameter in sub" << callEnumerator <<"\n";
|
|
(*v)->assignment(new expression(current), true);
|
|
++v;
|
|
}
|
|
/* pad remaining unassigned variables with empty values */
|
|
while (v!=params.end())
|
|
{
|
|
switch ((*v)->getType())
|
|
{
|
|
case T_FLOATVAR:
|
|
(*v)->assignment(new expression(new constOp("0.0", T_FLOAT)));
|
|
break;
|
|
case T_INTVAR:
|
|
(*v)->assignment(new expression(new constOp("0", T_INT)));
|
|
break;
|
|
case T_STRINGVAR:
|
|
(*v)->assignment(new expression(new constOp("", T_STRING)));
|
|
default:
|
|
error(E_INTERNAL);
|
|
}
|
|
++v;
|
|
}
|
|
g->startAddr->generateJumpTo();
|
|
retAddr->generate();
|
|
return g->rc;
|
|
}
|
|
|
|
/* typeless return for gosub family */
|
|
void fn::generateReturn()
|
|
{
|
|
switch (this->getType())
|
|
{
|
|
case T_UNKNOWNFUNC:
|
|
output_cpp << "state=f" << this->getID()
|
|
<< "::close();\nbreak;\n";
|
|
break;
|
|
case T_GOSUB:
|
|
output_cpp << "state=subroutine::close();\nbreak;\n";
|
|
break;
|
|
default:
|
|
error(E_RETURN_CODE_OMITTED);
|
|
}
|
|
}
|
|
|
|
void fn::generateReturn(expression *expr)
|
|
{
|
|
logger("evaluating expression");
|
|
this->rc=expr->evaluate();
|
|
logger("expression evaluated");
|
|
this->kind=rc->getSimpleVarType();
|
|
logger("generating return");
|
|
output_cpp << "state=f" << this->getID()
|
|
<< "::close();\nbreak;\n";
|
|
switch (this->getType())
|
|
{
|
|
case T_UNKNOWNFUNC:
|
|
break;
|
|
case T_STRINGFUNC:
|
|
if (kind!=T_STRINGVAR) error(E_TYPE_MISMATCH);
|
|
break;
|
|
case T_INTFUNC:
|
|
if (kind!=T_INTVAR&&kind!=T_FLOATVAR) error(E_TYPE_MISMATCH);
|
|
break;
|
|
case T_FLOATFUNC:
|
|
if(kind!=T_FLOATVAR) error(E_TYPE_MISMATCH);
|
|
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 */
|
|
void fn::generateBreak()
|
|
{
|
|
error(E_BAD_SYNTAX);
|
|
}
|
|
|
|
void fn::close()
|
|
{
|
|
/* check if no returns and no return type */
|
|
enum CODES t=this->getType();
|
|
if (this->kind==T_UNKNOWN && t==T_UNKNOWNFUNC)
|
|
{
|
|
/* generate a typeless return */
|
|
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" << endl;
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
varNames << "variable " << i->first << " has id v"
|
|
<< i->second.get()->getID() << endl;
|
|
++i;
|
|
}while(i!=locals.end());
|
|
}
|
|
i=statics.begin();
|
|
varNames << "\nStatic locals in function f" << this->getID() << "\n";
|
|
if (i==statics.end())
|
|
{
|
|
varNames << "no static locals" << endl;
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
varNames << "variable " << i->first << " has id v"
|
|
<< i->second.get()->getID() << endl;
|
|
++i;
|
|
} while (i!=statics.end());
|
|
}
|
|
auto iter=this->parameters.begin();
|
|
if (iter==this->parameters.end())
|
|
{
|
|
varNames << "no parameters passed to function f" << this->getID() << endl;
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
varNames << "paramater " << iter->first << " has id v"
|
|
<< iter->second->getID() << endl;
|
|
++iter;
|
|
} while (iter!=this->parameters.end());
|
|
|
|
}
|
|
}
|
|
locals.clear();
|
|
statics.clear();
|
|
skipDef->generate();
|
|
currentFunc=nullptr;
|
|
scopeGlobal=true;
|
|
}
|
|
|
|
fn *fn::declare(string &s, enum CODES t, operands *returnCode)
|
|
{
|
|
/* sd is the skipDef of this function */
|
|
label *sd=new label();
|
|
/* 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);
|
|
logger("declaration name cleared");
|
|
sd->generateJumpTo();
|
|
fn *self=new fn(t, returnCode);
|
|
logger("fn allocated");
|
|
fn::functions.insert({s,unique_ptr<fn>(self)});
|
|
logger("fn inserted");
|
|
/* initiate local scope */
|
|
self->skipDef=sd;
|
|
currentFunc=self;
|
|
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*/
|
|
if (t!=T_GOSUB)
|
|
{
|
|
funcs_h << "struct f" << this->id <<":public subroutine\n{\nf"
|
|
<< this->id << "(unsigned int x):subroutine(x)\n{}\n"
|
|
<< "virtual ~f" << this->id <<"()\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();
|
|
}
|
|
|
|
/* Destructor is called only at the end of the unique pointer's existence */
|
|
fn::~fn()
|
|
{
|
|
delete startAddr;
|
|
delete skipDef;
|
|
}
|