Restarted from earlier attempt
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -4,5 +4,4 @@ flex.c
|
|||||||
bison.output
|
bison.output
|
||||||
yab
|
yab
|
||||||
*.o
|
*.o
|
||||||
libyab.so
|
*.so
|
||||||
libyab_x86.so
|
|
||||||
|
|||||||
131
data/licenses/Artistic
Normal file
131
data/licenses/Artistic
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The "Artistic License"
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The intent of this document is to state the conditions under which a
|
||||||
|
Package may be copied, such that the Copyright Holder maintains some
|
||||||
|
semblance of artistic control over the development of the package,
|
||||||
|
while giving the users of the package the right to use and distribute
|
||||||
|
the Package in a more-or-less customary fashion, plus the right to make
|
||||||
|
reasonable modifications.
|
||||||
|
|
||||||
|
Definitions:
|
||||||
|
|
||||||
|
"Package" refers to the collection of files distributed by the
|
||||||
|
Copyright Holder, and derivatives of that collection of files
|
||||||
|
created through textual modification.
|
||||||
|
|
||||||
|
"Standard Version" refers to such a Package if it has not been
|
||||||
|
modified, or has been modified in accordance with the wishes
|
||||||
|
of the Copyright Holder as specified below.
|
||||||
|
|
||||||
|
"Copyright Holder" is whoever is named in the copyright or
|
||||||
|
copyrights for the package.
|
||||||
|
|
||||||
|
"You" is you, if you're thinking about copying or distributing
|
||||||
|
this Package.
|
||||||
|
|
||||||
|
"Reasonable copying fee" is whatever you can justify on the
|
||||||
|
basis of media cost, duplication charges, time of people involved,
|
||||||
|
and so on. (You will not be required to justify it to the
|
||||||
|
Copyright Holder, but only to the computing community at large
|
||||||
|
as a market that must bear the fee.)
|
||||||
|
|
||||||
|
"Freely Available" means that no fee is charged for the item
|
||||||
|
itself, though there may be fees involved in handling the item.
|
||||||
|
It also means that recipients of the item may redistribute it
|
||||||
|
under the same conditions they received it.
|
||||||
|
|
||||||
|
1. You may make and give away verbatim copies of the source form of the
|
||||||
|
Standard Version of this Package without restriction, provided that you
|
||||||
|
duplicate all of the original copyright notices and associated disclaimers.
|
||||||
|
|
||||||
|
2. You may apply bug fixes, portability fixes and other modifications
|
||||||
|
derived from the Public Domain or from the Copyright Holder. A Package
|
||||||
|
modified in such a way shall still be considered the Standard Version.
|
||||||
|
|
||||||
|
3. You may otherwise modify your copy of this Package in any way, provided
|
||||||
|
that you insert a prominent notice in each changed file stating how and
|
||||||
|
when you changed that file, and provided that you do at least ONE of the
|
||||||
|
following:
|
||||||
|
|
||||||
|
a) place your modifications in the Public Domain or otherwise make them
|
||||||
|
Freely Available, such as by posting said modifications to Usenet or
|
||||||
|
an equivalent medium, or placing the modifications on a major archive
|
||||||
|
site such as uunet.uu.net, or by allowing the Copyright Holder to include
|
||||||
|
your modifications in the Standard Version of the Package.
|
||||||
|
|
||||||
|
b) use the modified Package only within your corporation or organization.
|
||||||
|
|
||||||
|
c) rename any non-standard executables so the names do not conflict
|
||||||
|
with standard executables, which must also be provided, and provide
|
||||||
|
a separate manual page for each non-standard executable that clearly
|
||||||
|
documents how it differs from the Standard Version.
|
||||||
|
|
||||||
|
d) make other distribution arrangements with the Copyright Holder.
|
||||||
|
|
||||||
|
4. You may distribute the programs of this Package in object code or
|
||||||
|
executable form, provided that you do at least ONE of the following:
|
||||||
|
|
||||||
|
a) distribute a Standard Version of the executables and library files,
|
||||||
|
together with instructions (in the manual page or equivalent) on where
|
||||||
|
to get the Standard Version.
|
||||||
|
|
||||||
|
b) accompany the distribution with the machine-readable source of
|
||||||
|
the Package with your modifications.
|
||||||
|
|
||||||
|
c) give non-standard executables non-standard names, and clearly
|
||||||
|
document the differences in manual pages (or equivalent), together
|
||||||
|
with instructions on where to get the Standard Version.
|
||||||
|
|
||||||
|
d) make other distribution arrangements with the Copyright Holder.
|
||||||
|
|
||||||
|
5. You may charge a reasonable copying fee for any distribution of this
|
||||||
|
Package. You may charge any fee you choose for support of this
|
||||||
|
Package. You may not charge a fee for this Package itself. However,
|
||||||
|
you may distribute this Package in aggregate with other (possibly
|
||||||
|
commercial) programs as part of a larger (possibly commercial) software
|
||||||
|
distribution provided that you do not advertise this Package as a
|
||||||
|
product of your own. You may embed this Package's interpreter within
|
||||||
|
an executable of yours (by linking); this shall be construed as a mere
|
||||||
|
form of aggregation, provided that the complete Standard Version of the
|
||||||
|
interpreter is so embedded.
|
||||||
|
|
||||||
|
6. The scripts and library files supplied as input to or produced as
|
||||||
|
output from the programs of this Package do not automatically fall
|
||||||
|
under the copyright of this Package, but belong to whoever generated
|
||||||
|
them, and may be sold commercially, and may be aggregated with this
|
||||||
|
Package. If such scripts or library files are aggregated with this
|
||||||
|
Package via the so-called "undump" or "unexec" methods of producing a
|
||||||
|
binary executable image, then distribution of such an image shall
|
||||||
|
neither be construed as a distribution of this Package nor shall it
|
||||||
|
fall under the restrictions of Paragraphs 3 and 4, provided that you do
|
||||||
|
not represent such an executable image as a Standard Version of this
|
||||||
|
Package.
|
||||||
|
|
||||||
|
7. C subroutines (or comparably compiled subroutines in other
|
||||||
|
languages) supplied by you and linked into this Package in order to
|
||||||
|
emulate subroutines and variables of the language defined by this
|
||||||
|
Package shall not be considered part of this Package, but are the
|
||||||
|
equivalent of input as in Paragraph 6, provided these subroutines do
|
||||||
|
not change the language in any way that would cause it to fail the
|
||||||
|
regression tests for the language.
|
||||||
|
|
||||||
|
8. Aggregation of this Package with a commercial distribution is always
|
||||||
|
permitted provided that the use of this Package is embedded; that is,
|
||||||
|
when no overt attempt is made to make this Package's interfaces visible
|
||||||
|
to the end user of the commercial distribution. Such use shall not be
|
||||||
|
construed as a distribution of this Package.
|
||||||
|
|
||||||
|
9. The name of the Copyright Holder may not be used to endorse or promote
|
||||||
|
products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
The End
|
||||||
584
yab2cpp.cpp
Normal file
584
yab2cpp.cpp
Normal file
@@ -0,0 +1,584 @@
|
|||||||
|
#include "yab2cpp.h"
|
||||||
|
|
||||||
|
enum COMPILEERRORS errorLevel=E_OK;
|
||||||
|
unsigned int mode=0;
|
||||||
|
unsigned int indentLevel=0;
|
||||||
|
bool scopeGlobal=true;
|
||||||
|
|
||||||
|
extern ofstream output_cpp;
|
||||||
|
extern ofstream funcs_h;
|
||||||
|
extern ofstream heap_h;
|
||||||
|
extern ofstream consts_h;
|
||||||
|
extern ofstream logfile;
|
||||||
|
extern ofstream varNames;
|
||||||
|
|
||||||
|
extern ifstream src;
|
||||||
|
|
||||||
|
/* private prototypes */
|
||||||
|
void helpText(string &);
|
||||||
|
void shutDown();
|
||||||
|
void logger(string &);
|
||||||
|
|
||||||
|
/* process command line parameters */
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
atexit(shutDown);
|
||||||
|
switch (argc)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
mode=COMPILE;
|
||||||
|
cout << "\nCompile initiated." << endl;
|
||||||
|
compile();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (argv[1][0]=='-')
|
||||||
|
{
|
||||||
|
switch (argv[1][1])
|
||||||
|
{
|
||||||
|
case 'd':
|
||||||
|
cout << "\nIdentifier dump initiated." << endl;
|
||||||
|
mode=DUMP;
|
||||||
|
compile();
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
cout << "\n" << argv[0] << " version "
|
||||||
|
<< VER_MAJOR << "." << VER_MINOR << "." << VER_RELEASE << endl;
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
cout << "\nVerbose compile initiated." << endl;
|
||||||
|
mode=DUMP|COMPILE;
|
||||||
|
compile();
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
cout << "\nCompiler debug and dump mode initiated." << endl;
|
||||||
|
mode=DUMP|DEBUG;
|
||||||
|
compile();
|
||||||
|
break;
|
||||||
|
case 'G':
|
||||||
|
cout << "\nDebug, dump and compile initiated." << endl;
|
||||||
|
mode=DUMP|DEBUG|COMPILE;
|
||||||
|
compile();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
helpText(argv[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
helpText(argv[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* print the help text to stdout */
|
||||||
|
void helpText(string &commandname)
|
||||||
|
{
|
||||||
|
cout << commandname << "[-d|D|V|v|G] < filename.mb\n" <<
|
||||||
|
"Compiles filename.mb by default unless a flag is specified.\n" <<
|
||||||
|
"\n The optional flags are as follows:\n" <<
|
||||||
|
"-d is a dump of build to the parse.log file.\n" <<
|
||||||
|
"-D is a dump of identifiers and logged build.\n" <<
|
||||||
|
"-V is for a verbose build where the compiler logs and compiles.\n" <<
|
||||||
|
"-v prints the version and exits.\n\n" <<
|
||||||
|
"-G activates dump, debug and compile all at once.\n" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* open files and initialize them*/
|
||||||
|
void setUp()
|
||||||
|
{
|
||||||
|
if (mode & COMPILE)
|
||||||
|
{
|
||||||
|
/* compile mode */
|
||||||
|
output_cpp=new ofstream("build/output.cpp");
|
||||||
|
funcs_h=new ofstream ("functions.h");
|
||||||
|
consts_h=new ofstream("consts.h");
|
||||||
|
heap_h=new ofstream("heap.h");
|
||||||
|
output_cpp << "#include <runtime.h>\n#include \"consts.h\"\n"
|
||||||
|
<< "#include \"heap.h\"\n#include \"functions.h\"\n"
|
||||||
|
<< "unsigned int state=start;\nint run(){\nwhile (state>=start){\n"
|
||||||
|
<< "switch(state){\ncase start:" << endl;
|
||||||
|
if (mode & DEBUG)
|
||||||
|
{
|
||||||
|
varNames=new ofstream("varnames.txt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mode & DUMP)
|
||||||
|
{
|
||||||
|
/* dump identifier mode */
|
||||||
|
logfile=fopen("parse.log","w");
|
||||||
|
logger("Setup complete.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write a note in the logfile */
|
||||||
|
void logger(string &contents)
|
||||||
|
{
|
||||||
|
unsigned int count;
|
||||||
|
if (mode & DEBUG)
|
||||||
|
{
|
||||||
|
count=indentLevel;
|
||||||
|
while (count > 0)
|
||||||
|
{
|
||||||
|
logfile << '\t';
|
||||||
|
--count;
|
||||||
|
}
|
||||||
|
logfile << contents << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* shutdown the compiler and exit */
|
||||||
|
void shutDown()
|
||||||
|
{
|
||||||
|
if (errorLevel != E_OK) cerr << "\nERROR: " << COMPILEERRORNAMES[errorLevel] << "\n\n" << endl;
|
||||||
|
if (fn::isCallStackEmpty())
|
||||||
|
{
|
||||||
|
logger("Stack was empty");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger("Dumping stack.");
|
||||||
|
if (mode & DUMP && logfile != NULL)
|
||||||
|
{
|
||||||
|
fn::dumpCallStack(logfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operands::dumpVars();
|
||||||
|
label::dumpLabels();
|
||||||
|
output_cpp << "}\n}return state;\n}"<< endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* open files and compile */
|
||||||
|
void compile()
|
||||||
|
{
|
||||||
|
setUp();
|
||||||
|
|
||||||
|
/* parse */
|
||||||
|
ctx = mb_create(NULL);
|
||||||
|
while(mb_parse(ctx, NULL)){logger("done");}
|
||||||
|
mb_destroy(ctx);
|
||||||
|
|
||||||
|
shutDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* methods for operands */
|
||||||
|
static void operands::dumpVars(ostream &out)
|
||||||
|
{
|
||||||
|
out << "Global Variables\n";
|
||||||
|
for(auto iter=globals.begin(); iter!=globals.end(); ++iter)
|
||||||
|
{
|
||||||
|
out << "variable " << iter->first << " has ID " << iter->second << "\n";
|
||||||
|
}
|
||||||
|
out << endl;
|
||||||
|
}
|
||||||
|
unsigned int operands::getOrCreateStr(string &s, ostream &k)
|
||||||
|
{
|
||||||
|
auto iter=constStr.find(s);
|
||||||
|
if (iter!=constStr.end()) return iter->second;
|
||||||
|
++nextID;
|
||||||
|
k << "const string sk" << nextID << "=\"" << s << "\";\n";
|
||||||
|
constStr[s]=nextID;
|
||||||
|
return nextID;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TYPES operands::getSimpleVarType()
|
||||||
|
{
|
||||||
|
switch type
|
||||||
|
{
|
||||||
|
case T_FLOAT:
|
||||||
|
case T_FLOATCALL_ARRAY:
|
||||||
|
case T_FLOATVAR:
|
||||||
|
return T_FLOATVAR;
|
||||||
|
case T_INT:
|
||||||
|
case T_INTCALL_ARRAY:
|
||||||
|
case T_INTVAR:
|
||||||
|
return T_INTVAR;
|
||||||
|
case T_STRING:
|
||||||
|
case T_STRINGCALL_ARRAY:
|
||||||
|
case T_STRINGVAR:
|
||||||
|
return T_STRINGVAR;
|
||||||
|
}
|
||||||
|
return T_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TYPES operands::coerceTypes()
|
||||||
|
{
|
||||||
|
if this->isBinOp()
|
||||||
|
{
|
||||||
|
if (l->getSimpleVarType()==T_INTVAR && r->getSimpleVarType()==T_FLOATVAR)
|
||||||
|
{
|
||||||
|
/* promote l to float */
|
||||||
|
t=T_FLOATVAR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (l->getSimpleVarType()==T_FLOAT && r->getSimpleVarType()==T_INT)
|
||||||
|
{
|
||||||
|
/* promote r to float */
|
||||||
|
t=T_FLOATVAR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (l->getSimpleVarType()==r->getSimpleVarType())
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
errorLevel=E_TYPE_MISMATCH;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (t==T_NONE)
|
||||||
|
{
|
||||||
|
errorLevel=E_TYPE_MISMATCH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* operands used by expression parser and variables */
|
||||||
|
|
||||||
|
operands::operands(enum TYPES t)
|
||||||
|
{
|
||||||
|
this->id = ++nextID;
|
||||||
|
this->type=t;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operands::generateBox(ostream &out)
|
||||||
|
{
|
||||||
|
switch (this->getSimpleVarType())
|
||||||
|
{
|
||||||
|
case T_INTVAR:
|
||||||
|
out << "int v";
|
||||||
|
break;
|
||||||
|
case T_FLOATVAR:
|
||||||
|
out << "double v";
|
||||||
|
break;
|
||||||
|
case T_STRINGVAR:
|
||||||
|
out << "string v";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errorLevel=E_TYPE_MISMATCH;
|
||||||
|
exit(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out << this->getID() << ";\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void operands::boxName(ostream &out)
|
||||||
|
{
|
||||||
|
switch (this->getType())
|
||||||
|
{
|
||||||
|
case T_STRINGVAR:
|
||||||
|
case T_INTVAR:
|
||||||
|
case T_FLOATVAR:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
errorLevel=E_INTERNAL;
|
||||||
|
exit(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out << "v" << this->getID();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* expression parsing routines */
|
||||||
|
|
||||||
|
/* binary vs. unary ops */
|
||||||
|
bool expression::isBinOp()
|
||||||
|
{
|
||||||
|
switch this->getOp()
|
||||||
|
{
|
||||||
|
case O_NEGATE:
|
||||||
|
case O_NOT:
|
||||||
|
case O_INVERT:
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
operands *expression::evaluate(ostream &scope, ostream &output_cpp)
|
||||||
|
{
|
||||||
|
operands *l, *r;
|
||||||
|
if (this->getOp()==O_TERM) return op;
|
||||||
|
l=this->getLeft()->evaluate();
|
||||||
|
enum TYPES t=this->coerceTypes();
|
||||||
|
l->getSimpleVarType();
|
||||||
|
r=(this->isBinOp()?this->getRight()->evaluate():NULL);
|
||||||
|
switch (this->getOp())
|
||||||
|
{
|
||||||
|
case O_INVERT:
|
||||||
|
this->op=new operands(t);
|
||||||
|
this->op->generateBox(scope);
|
||||||
|
output_cpp << this->op->boxName() << "= ~" << l->boxName() << ";\n";
|
||||||
|
break;
|
||||||
|
case O_NEGATE:
|
||||||
|
this->op=new operands(t);
|
||||||
|
this->op->generateBox(scope);
|
||||||
|
output_cpp << this->op->boxName() << "= -" << l->boxName() << ";\n";
|
||||||
|
break;
|
||||||
|
case O_NOT:
|
||||||
|
this->op=new operands(t);
|
||||||
|
this->op->generateBox(scope);
|
||||||
|
output_cpp << this->op->boxName() << "= !" << l->boxName() << ";\n";
|
||||||
|
break;
|
||||||
|
/* TODO: Check for divide by zero error and modulo zero error */
|
||||||
|
case O_REMAINDER:
|
||||||
|
this->op=new operands(t);
|
||||||
|
this->op->generateBox(scope);
|
||||||
|
output_cpp << this->op->boxName() << "=" << l->boxName() << "%" << r->boxName() << ";\n";
|
||||||
|
break;
|
||||||
|
case O_DIVIDE:
|
||||||
|
this->op=new operands(t);
|
||||||
|
this->op->generateBox(scope);
|
||||||
|
output_cpp << this->op->boxName() << "=" << l->boxName() << "/" << r->boxName() << ";\n";
|
||||||
|
break;
|
||||||
|
case O_PLUS:
|
||||||
|
this->op=new operands(t);
|
||||||
|
this->op->generateBox(scope);
|
||||||
|
output_cpp << this->op->boxName() << "=" << l->boxName() << "+" << r->boxName() << ";\n";
|
||||||
|
break;
|
||||||
|
case O_MINUS:
|
||||||
|
this->op=new operands(t);
|
||||||
|
this->op->generateBox(scope);
|
||||||
|
output_cpp << this->op->boxName() << "=" << l->boxName() << "-" << r->boxName() << ";\n";
|
||||||
|
break;
|
||||||
|
case O_MULTIPLY:
|
||||||
|
this->op=new operands(t);
|
||||||
|
this->op->generateBox(scope);
|
||||||
|
output_cpp << this->op->boxName() << "=" << l->boxName() << "*" << r->boxName() << ";\n";
|
||||||
|
break;
|
||||||
|
case O_OR:
|
||||||
|
this->op=new operands(t);
|
||||||
|
this->op->generateBox(scope);
|
||||||
|
output_cpp << this->op->boxName() << "=" << l->boxName() << "|" << r->boxName() << ";\n";
|
||||||
|
break;
|
||||||
|
case O_AND:
|
||||||
|
this->op=new operands(t);
|
||||||
|
this->op->generateBox(scope);
|
||||||
|
output_cpp << this->op->boxName() << "=" << l->boxName() << "&" << r->boxName() << ";\n";
|
||||||
|
break;
|
||||||
|
case O_GREATER:
|
||||||
|
this->op=new operands(T_INTVAR);
|
||||||
|
this->op->generateBox(scope);
|
||||||
|
output_cpp << this->op->boxName() << "=(" << l->boxName() << ">" << r->boxName() << ")?-1:0;\n";
|
||||||
|
break;
|
||||||
|
case O_LESS:
|
||||||
|
this->op=new operands(T_INTVAR);
|
||||||
|
this->op->generateBox(scope);
|
||||||
|
output_cpp << this->op->boxName() << "=(" << l->boxName() << "<" << r->boxName() << ")?-1:0;\n";
|
||||||
|
break;
|
||||||
|
case O_GREATER_EQUAL:
|
||||||
|
this->op=new operands(T_INTVAR);
|
||||||
|
this->op->generateBox(scope);
|
||||||
|
output_cpp << this->op->boxName() << "=(" << l->boxName() << ">=" << r->boxName() << ")?-1:0;\n";
|
||||||
|
break;
|
||||||
|
case O_LESS_EQUAL:
|
||||||
|
this->op=new operands(T_INTVAR);
|
||||||
|
this->op->generateBox(scope);
|
||||||
|
output_cpp << this->op->boxName() << "=(" << l->boxName() << "<=" << r->boxName() << ")?-1:0;\n";
|
||||||
|
break;
|
||||||
|
case O_EQUAL:
|
||||||
|
this->op=new operands(T_INTVAR);
|
||||||
|
this->op->generateBox(scope);
|
||||||
|
output_cpp << this->op->boxName() << "=(" << l->boxName() << "==" << r->boxName() << ")?-1:0;\n";
|
||||||
|
break;
|
||||||
|
case O_UNEQUAL:
|
||||||
|
this->op=new operands(T_INTVAR);
|
||||||
|
this->op->generateBox(scope);
|
||||||
|
output_cpp << this->op->boxName() << "=(" << l->boxName() << "!=" << r->boxName() << ")?-1:0;\n";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
errorLevel=E_INTERNAL;
|
||||||
|
exit(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
delete this->left;
|
||||||
|
this->left=NULL;
|
||||||
|
if (this->isBinOp())
|
||||||
|
{
|
||||||
|
delete this->right;
|
||||||
|
this->right=NULL;
|
||||||
|
}
|
||||||
|
this->oper=O_TERM;
|
||||||
|
return this->op;
|
||||||
|
}
|
||||||
|
|
||||||
|
expression::~expression()
|
||||||
|
{
|
||||||
|
if(this->getOp()==O_TERM)
|
||||||
|
{
|
||||||
|
delete this->op;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete this->left;
|
||||||
|
if (this->isBinOp())
|
||||||
|
{
|
||||||
|
delete this->right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* base class of all the code structure types */
|
||||||
|
|
||||||
|
codeType::codeType(enum CODES t)
|
||||||
|
{
|
||||||
|
this->id= ++nextID;
|
||||||
|
nesting.push_back(this);
|
||||||
|
this->type=t;
|
||||||
|
}
|
||||||
|
|
||||||
|
codeType *codeType::getCurrent()
|
||||||
|
{
|
||||||
|
return nesting.back;
|
||||||
|
}
|
||||||
|
|
||||||
|
void codeType::close()
|
||||||
|
{
|
||||||
|
nesting.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* label definitions and helper routines */
|
||||||
|
|
||||||
|
label *label::find(string &s)
|
||||||
|
{
|
||||||
|
auto ret=lookup.find(s);
|
||||||
|
return(ret==lookup.end()?NULL:ret->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
void label::dumpLabels(ostream &v)
|
||||||
|
{
|
||||||
|
v << "Global Labels\n\n";
|
||||||
|
for(auto iter=lookup.begin(); iter!=lookup.end(); ++iter)
|
||||||
|
{
|
||||||
|
v << "label " << iter->first << " has ID " << iter->second->getID() << "\n" ;
|
||||||
|
}
|
||||||
|
v << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void label::generateJumpTo(ostream &out)
|
||||||
|
{
|
||||||
|
out << "state=" << this->getID() << ";\nbreak;\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void label::generateOnNSkip(ostream &k, list<label *> &dest)
|
||||||
|
{
|
||||||
|
if (dest->size()<2)
|
||||||
|
{
|
||||||
|
errorLevel=E_BAD_SYNTAX;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
auto iter=dest.start();
|
||||||
|
k << "j" << this->getID() << "[]={" << *iter;
|
||||||
|
++iter;
|
||||||
|
while(iter!=dest.end())
|
||||||
|
{
|
||||||
|
k << ", " << *iter;
|
||||||
|
++iter;
|
||||||
|
}
|
||||||
|
k << "}\njs" << this->getID()<< "=" << dest->size() << ";\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void label::generateOnNTo(ostream &out, expression *e)
|
||||||
|
{
|
||||||
|
operands *o=e->evaluate();
|
||||||
|
if (o->getType()==T_INT||o->getType()==T_INTVAR)
|
||||||
|
{
|
||||||
|
out << "if(";
|
||||||
|
o->boxName(out);
|
||||||
|
out << ">=0 && ";
|
||||||
|
o->boxName(out);
|
||||||
|
out << "<js" << this->getID() << ")state=j[";
|
||||||
|
o->boxName(out);
|
||||||
|
out << "];\nbreak;\n";
|
||||||
|
}
|
||||||
|
delete e;
|
||||||
|
}
|
||||||
|
|
||||||
|
void label::generateCondJump(ostream &out, expression *e)
|
||||||
|
{
|
||||||
|
operands *o=e->evaluate();
|
||||||
|
if (o->getType()==T_INT||o->getType()==T_INTVAR)
|
||||||
|
{
|
||||||
|
out << "if(";
|
||||||
|
o->boxName(out);
|
||||||
|
out << "!=0)state=" << this->getID() << ";\nbreak;\n";
|
||||||
|
}
|
||||||
|
delete e;
|
||||||
|
}
|
||||||
|
|
||||||
|
void label::generate(ostream &out)
|
||||||
|
{
|
||||||
|
out << "case " << this->getID() <<":\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* conditional definition */
|
||||||
|
|
||||||
|
conditional::conditional(ostream &out, expression *e):codeType(T_IF)
|
||||||
|
{
|
||||||
|
this->redo=new label();
|
||||||
|
redo->generate(out);
|
||||||
|
this->done=new label();
|
||||||
|
expression *f=new expression(e,O_NOT);
|
||||||
|
this->chain=new label();
|
||||||
|
chain->generateCondJump(out, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void conditional::generateBreak(ostream &out)
|
||||||
|
{
|
||||||
|
done->generateJumpTo(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void conditional::generateContinue(ostream &out)
|
||||||
|
{
|
||||||
|
redo->generateJumpTo(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void conditional::alternative(ostream &out, expression *e=NULL)
|
||||||
|
{
|
||||||
|
done->generateJumpTo(out);
|
||||||
|
this->chain->generate();
|
||||||
|
delete this->chain;
|
||||||
|
this->chain=NULL;
|
||||||
|
if(e!=NULL)
|
||||||
|
{
|
||||||
|
this->chain=new label();
|
||||||
|
expression *f=new expression(e,O_NOT);
|
||||||
|
chain->generateJumpCond(out, f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void conditional::close(ostream &out)
|
||||||
|
{
|
||||||
|
if(this->chain)
|
||||||
|
{
|
||||||
|
/* elsif ended without else in between */
|
||||||
|
errorLevel=E_BAD_SYNTAX;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
this->done->generate();
|
||||||
|
delete this->done;
|
||||||
|
delete this->redo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop definitions */
|
||||||
|
|
||||||
|
/* function definitions */
|
||||||
|
|
||||||
|
void fn::generateOnNSub(ostream &out, expression *e)
|
||||||
|
{
|
||||||
|
this->ret=new label();
|
||||||
|
fn::callStack.push_back(this);
|
||||||
|
label::generateOnNTo(out, e);
|
||||||
|
out << "case " << ret->getID() << ":\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void fn::generateGosub(ostream &out, shared_ptr<label> sub)
|
||||||
|
{
|
||||||
|
this->ret=new label();
|
||||||
|
fn::callStack.push_back(this);
|
||||||
|
out << "state=" << sub->getID() << ";\nbreak;\n";
|
||||||
|
out << "case " << ret->getID() << ":\n";
|
||||||
|
}
|
||||||
410
yab2cpp.h
Normal file
410
yab2cpp.h
Normal file
@@ -0,0 +1,410 @@
|
|||||||
|
#ifndef TRANSPILER_H
|
||||||
|
#define TRANSPILER_H
|
||||||
|
/*
|
||||||
|
** Yab2Cpp
|
||||||
|
**
|
||||||
|
** Transpiler by Samuel D. Crow
|
||||||
|
**
|
||||||
|
** Based on Yab
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <memory>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#define VER_MAJOR 0
|
||||||
|
#define VER_MINOR 0
|
||||||
|
#define VER_RELEASE 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
** list of all compiler errors
|
||||||
|
**
|
||||||
|
** Note: There must be a corresponding error message
|
||||||
|
** to each entry in the COMPILE_ERROR_NAMES constant array.
|
||||||
|
*/
|
||||||
|
enum COMPILE_ERRORS {
|
||||||
|
E_OK=0,
|
||||||
|
E_BAD_SYNTAX,
|
||||||
|
E_TYPE_MISMATCH,
|
||||||
|
E_BAD_ALLOC,
|
||||||
|
E_STACK_UNDERFLOW,
|
||||||
|
E_INTERNAL,
|
||||||
|
E_DUPLICATE_SYMBOL
|
||||||
|
};
|
||||||
|
|
||||||
|
/* These correspond to the enum COMPILE_ERRORS. */
|
||||||
|
const char *COMPILE_ERROR_NAMES[]={
|
||||||
|
"no error",
|
||||||
|
"incorrect syntax",
|
||||||
|
"wrong type",
|
||||||
|
"failed allocation",
|
||||||
|
"stack underflow",
|
||||||
|
"internal compiler error",
|
||||||
|
"duplicated label"
|
||||||
|
};
|
||||||
|
|
||||||
|
extern enum COMPILEERRORS errorLevel;
|
||||||
|
|
||||||
|
/* flags used internally by the compiler
|
||||||
|
(must be powers of 2) */
|
||||||
|
#define COMPILE 1
|
||||||
|
#define DUMP 2
|
||||||
|
#define DEBUG 4
|
||||||
|
|
||||||
|
/* list of all variable and constant types */
|
||||||
|
enum TYPES
|
||||||
|
{
|
||||||
|
T_NONE=0,
|
||||||
|
T_STRING,
|
||||||
|
T_INT,
|
||||||
|
T_FLOAT,
|
||||||
|
T_STRINGVAR,
|
||||||
|
T_INTVAR,
|
||||||
|
T_FLOATVAR,
|
||||||
|
T_INTCALL_ARRAY,
|
||||||
|
T_FLOATCALL_ARRAY,
|
||||||
|
T_STRINGCALL_ARRAY,
|
||||||
|
}
|
||||||
|
/* list of all kinds of other code structures */
|
||||||
|
enum CODES
|
||||||
|
{
|
||||||
|
T_PRINT=0,
|
||||||
|
T_PRINTSEGMENT,
|
||||||
|
T_WHILELOOP,
|
||||||
|
T_FORLOOP,
|
||||||
|
T_REPEATLOOP,
|
||||||
|
T_DOLOOP,
|
||||||
|
T_IF,
|
||||||
|
T_PROCEDURE,
|
||||||
|
T_FUNCTION,
|
||||||
|
T_GOSUB,
|
||||||
|
T_ASSIGNMENT,
|
||||||
|
T_LABEL,
|
||||||
|
T_PARAMLIST,
|
||||||
|
T_DATAITEM
|
||||||
|
};
|
||||||
|
|
||||||
|
/* These correspond to the types of enum TYPES. */
|
||||||
|
const string TYPENAMES[]={
|
||||||
|
"none",
|
||||||
|
"string constant",
|
||||||
|
"integer constant",
|
||||||
|
"floating point constant",
|
||||||
|
"string variable",
|
||||||
|
"integer variable",
|
||||||
|
"floating point variable",
|
||||||
|
"string array or function",
|
||||||
|
"integer array or function",
|
||||||
|
"floating point array or function"
|
||||||
|
};
|
||||||
|
|
||||||
|
const string CODETYPES[]={
|
||||||
|
"print sequence",
|
||||||
|
"print segment",
|
||||||
|
"while loop",
|
||||||
|
"for loop",
|
||||||
|
"repeat loop",
|
||||||
|
"do loop",
|
||||||
|
"if statement",
|
||||||
|
"procedure statement",
|
||||||
|
"function statement",
|
||||||
|
"assignment",
|
||||||
|
"label",
|
||||||
|
"parameter list or array index",
|
||||||
|
"data item"
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
double d;
|
||||||
|
int i;
|
||||||
|
string *s;
|
||||||
|
}boxTypes;
|
||||||
|
|
||||||
|
/* subtypes of the T_PRINTSEPARATOR type */
|
||||||
|
enum SEPARATORS
|
||||||
|
{
|
||||||
|
S_COMMA,
|
||||||
|
S_SEMICOLON,
|
||||||
|
S_LINEFEED
|
||||||
|
};
|
||||||
|
|
||||||
|
enum OPERATORS
|
||||||
|
{
|
||||||
|
O_PLUS,
|
||||||
|
O_MINUS,
|
||||||
|
O_MULTIPLY,
|
||||||
|
O_DIVIDE,
|
||||||
|
O_REMAINDER,
|
||||||
|
O_NEGATE,
|
||||||
|
O_EXPONENT,
|
||||||
|
O_GREATER,
|
||||||
|
O_LESS,
|
||||||
|
O_EQUAL,
|
||||||
|
O_GREATER_EQUAL,
|
||||||
|
O_LESS_EQUAL,
|
||||||
|
O_UNEQUAL,
|
||||||
|
O_NOT,
|
||||||
|
O_INVERT,
|
||||||
|
O_OR,
|
||||||
|
O_AND,
|
||||||
|
O_TERM
|
||||||
|
};
|
||||||
|
|
||||||
|
/* internal states used by the parser */
|
||||||
|
class operands
|
||||||
|
{
|
||||||
|
enum TYPES type;
|
||||||
|
unsigned int id;
|
||||||
|
static unsigned int nextID;
|
||||||
|
static unordered_map<string &, operands *> globals;
|
||||||
|
static unordered_map<string &, unsigned int> strConst;
|
||||||
|
public:
|
||||||
|
enum TYPES getType() const {return type;}
|
||||||
|
unsigned int getID() const {return id;}
|
||||||
|
|
||||||
|
static void dumpVars(ostream &out);
|
||||||
|
static unsigned int getOrCreateStr(string &s);
|
||||||
|
|
||||||
|
enum TYPES getSimpleVarType();
|
||||||
|
void generateBox(ostream &out);
|
||||||
|
virtual void boxName(ostream &out);
|
||||||
|
enum TYPES coerceTypes();
|
||||||
|
|
||||||
|
explicit operands(enum TYPES t);
|
||||||
|
virtual ~operands()
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* expression can be terminal or non-terminal */
|
||||||
|
class expression
|
||||||
|
{
|
||||||
|
operands *op;
|
||||||
|
expression *left;
|
||||||
|
expression *right;
|
||||||
|
enum OPERATORS oper;
|
||||||
|
public:
|
||||||
|
enum OPERATORS getOp() const {return oper;}
|
||||||
|
expression *getLeft() const {return left;}
|
||||||
|
expression *getRight() const {return right;}
|
||||||
|
|
||||||
|
bool isBinOp();
|
||||||
|
operands *evaluate();
|
||||||
|
|
||||||
|
/* r is NULL for unary operators */
|
||||||
|
expression(expression *l, enum OPERATORS o, expression *r=NULL)
|
||||||
|
{
|
||||||
|
this->left=l;
|
||||||
|
this->right=r;
|
||||||
|
this->oper=o;
|
||||||
|
}
|
||||||
|
virtual ~expression();
|
||||||
|
};
|
||||||
|
|
||||||
|
/* parent class of all code types */
|
||||||
|
class codeType
|
||||||
|
{
|
||||||
|
unsigned int id;
|
||||||
|
enum CODES type;
|
||||||
|
static unsigned int nextID;
|
||||||
|
static list<codeType *> nesting;
|
||||||
|
public:
|
||||||
|
enum CODES getType() const {return this->type;}
|
||||||
|
unsigned int getID() const {return this->id;}
|
||||||
|
|
||||||
|
codeType *getCurrent();
|
||||||
|
virtual void close();
|
||||||
|
|
||||||
|
explicit codeType(enum CODES t);
|
||||||
|
virtual ~codeType()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class label
|
||||||
|
{
|
||||||
|
unsigned int id;
|
||||||
|
static unsigned int nextID;
|
||||||
|
static unordered_map<string, label *> lookup;
|
||||||
|
public:
|
||||||
|
static dumpLabels(ostream &v);
|
||||||
|
|
||||||
|
unsigned int getID() const {return id;}
|
||||||
|
|
||||||
|
void generateJumpTo(ostream &out);
|
||||||
|
void generateOnNSkip(ostream &k, list<label *> &dest);
|
||||||
|
void generateOnNTo(ostream &out, expression *e);
|
||||||
|
void generateCondJump(ostream &out, expression *e);
|
||||||
|
void generate(ostream &out);
|
||||||
|
|
||||||
|
static label *find(string &s);
|
||||||
|
|
||||||
|
label(){this->id = ++nextID;}
|
||||||
|
label(string &s)
|
||||||
|
{
|
||||||
|
label();
|
||||||
|
lookup[s]=this;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~label()
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* if statement */
|
||||||
|
class conditional:public codeType
|
||||||
|
{
|
||||||
|
shared_ptr<label>redo; /* for continue command */
|
||||||
|
shared_ptr<label>done; /* for break or after "then" condition */
|
||||||
|
shared_ptr<label>chain; /* For elsif command */
|
||||||
|
public:
|
||||||
|
void generateContinue(ostream &out);
|
||||||
|
void generateBreak(ostream &out);
|
||||||
|
void alternative(ostream &out, expression *e=NULL); /* enable else or elsif condition */
|
||||||
|
virtual void close(ostream &out) override; /* end if */
|
||||||
|
|
||||||
|
explicit conditional(expression *e):codeType(T_IF);
|
||||||
|
virtual ~conditional()
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* looping constructs */
|
||||||
|
class repeatLoop:public codeType
|
||||||
|
{
|
||||||
|
shared_ptr<label>loopStart;
|
||||||
|
shared_ptr<label>loopEnd;
|
||||||
|
public:
|
||||||
|
virtual void close(ostream &out, expression *) override;
|
||||||
|
|
||||||
|
explicit repeatLoop():codeType(T_REPEATLOOP);
|
||||||
|
virtual ~repeatLoop()
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
class doLoop:public codeType
|
||||||
|
{
|
||||||
|
shared_ptr<label>loopStart;
|
||||||
|
shared_ptr<label>loopEnd;
|
||||||
|
public:
|
||||||
|
virtual void close();
|
||||||
|
|
||||||
|
explicit doLoop():codeType(T_DOLOOP);
|
||||||
|
virtual ~doLoop()
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
class whileLoop:public codeType
|
||||||
|
{
|
||||||
|
shared_ptr<label> loopContinue;
|
||||||
|
shared_ptr<label> loopExit;
|
||||||
|
expression cond;
|
||||||
|
public:
|
||||||
|
virtual void close();
|
||||||
|
|
||||||
|
explicit whileLoop(expression e):codeType(T_WHILELOOP);
|
||||||
|
virtual ~whileLoop()
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
class variable;
|
||||||
|
|
||||||
|
/* TODO: Define expression constant ONE */
|
||||||
|
class forLoop:public codeType
|
||||||
|
{
|
||||||
|
shared_ptr<variable *>var;
|
||||||
|
shared_ptr<label>loopContinue;
|
||||||
|
shared_ptr<label>loopExit;
|
||||||
|
expression *step;
|
||||||
|
expression *cond;
|
||||||
|
public:
|
||||||
|
virtual void close();
|
||||||
|
|
||||||
|
explicit forLoop(string &name, enum TYPES t, expression *start, expression *stop, expression *stepVal):assignment(name, t, start)
|
||||||
|
{
|
||||||
|
this->cond=stop;
|
||||||
|
this->step=stepVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit forLoop(string &name, enum TYPES t, expression *start, expression *stop)
|
||||||
|
{
|
||||||
|
extern operands *ONE;
|
||||||
|
forLoop(name, t, start, stop, ONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~forLoop();
|
||||||
|
};
|
||||||
|
|
||||||
|
class fn:codeType
|
||||||
|
{
|
||||||
|
static unordered_map<string, operands *>locals;
|
||||||
|
static unordered_map<string, operands *>statics;
|
||||||
|
static unordered_map<string, fn>functions;
|
||||||
|
static list<fn *> callStack;
|
||||||
|
enum TYPES kind;
|
||||||
|
shared_ptr<label>ret;
|
||||||
|
unsigned int parameters;
|
||||||
|
public:
|
||||||
|
static bool isCallStackEmpty() {return callStack.size()>0;}
|
||||||
|
static void dumpCallStack(ostream &out);
|
||||||
|
void setParameters(unsigned int num) const {this->parameters=num;}
|
||||||
|
|
||||||
|
void addLocal(operands *);
|
||||||
|
void addStatic(operands *);
|
||||||
|
void generateCall(ostream &out, string &name, unsigned int params);
|
||||||
|
void generateReturn(ostream &out, expression *expr=NULL);
|
||||||
|
void generateGosub(ostream &out, shared_ptr<label> sub);
|
||||||
|
/* must be called after label::generateOnNSkip */
|
||||||
|
void generateOnNSub(ostream &out, expression *e);
|
||||||
|
virtual void close();
|
||||||
|
|
||||||
|
fn(string &name);
|
||||||
|
fn(label *gosub);
|
||||||
|
virtual ~fn();
|
||||||
|
};
|
||||||
|
|
||||||
|
class variable:public operands
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static assignment(expression *value);
|
||||||
|
explicit variable(ostream &scope, string &name, enum TYPES t):operands(t);
|
||||||
|
virtual variable()
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
|
||||||
|
class arrayType:public variable
|
||||||
|
{
|
||||||
|
list<unsigned int> dimensions;
|
||||||
|
public:
|
||||||
|
virtual void boxName(ostream &out, list<unsigned int>indexes) override;
|
||||||
|
|
||||||
|
explicit arrayType(ostream &scope, string &name, enum TYPES t, list<unsigned int>dim):variable(scope, name, t);
|
||||||
|
virtual ~arrayType()
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The next two structures are used to implement the PRINT statement. */
|
||||||
|
class printSegments
|
||||||
|
{
|
||||||
|
expression *cargo;
|
||||||
|
enum SEPARATORS kind;
|
||||||
|
public:
|
||||||
|
printSegments(expression *e, enum SEPARATORS k)
|
||||||
|
{
|
||||||
|
this->cargo=e;
|
||||||
|
this->kind=k;
|
||||||
|
}
|
||||||
|
printSegments(expression *e) {printSegments(e, S_LINEFEED);}
|
||||||
|
printSegments() {printSegments(NULL);}
|
||||||
|
virtual ~printSegments()
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct printStatement
|
||||||
|
{
|
||||||
|
list<printSegments *> segments;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user