jack-compiler/compiler/compiler.c

401 lines
10 KiB
C
Raw Normal View History

2020-12-20 13:58:10 -05:00
#include <stdlib.h>
#include <string.h>
#include "compiler.h"
2020-12-27 16:52:28 -05:00
LINEBLOCK* compilestatements(SCOPE* s, STATEMENT* sts);
LINEBLOCK* compilesubroutcall(SCOPE* s, SUBROUTCALL* call);
LINEBLOCK* compileexpression(SCOPE* s, TERM* e);
2020-12-22 13:45:12 -05:00
2020-12-27 16:52:28 -05:00
int countparameters(PARAMETER* params) {
2020-12-20 13:58:10 -05:00
int i = 0;
while(params != NULL) {
i++;
params = params->next;
}
return i;
}
2020-12-27 16:52:28 -05:00
int countexpressions(EXPRESSIONLIST* explist) {
int i = 0;
while(explist != NULL) {
i++;
explist = explist->next;
}
return i;
}
2020-12-20 13:58:10 -05:00
int countlocalvars(VARDEC* decs) {
int i = 0;
while(decs != NULL) {
2020-12-27 16:52:28 -05:00
STRINGLIST* curr = decs->names;
while(curr != NULL) {
i++;
curr = curr->next;
}
2020-12-20 13:58:10 -05:00
decs = decs->next;
}
return i;
}
char* dotlabel(char* n1, char* n2) {
int sz = (strlen(n1) + strlen(n2) + 2) * sizeof(char);
char* result = (char*)malloc(sz);
2020-12-21 13:05:49 -05:00
sprintf(result, "%s.%s", n1, n2);
2020-12-20 13:58:10 -05:00
return result;
}
2020-12-27 16:52:28 -05:00
char* mkcondlabel(char* name, int count) {
int sz = (strlen(name) + countplaces(count) + 1) * sizeof(char);
char* result = (char*)malloc(sz);
sprintf(result, "%s%i", name, count);
return result;
2020-12-20 13:58:10 -05:00
}
LINE* onetoken(char* str) {
LINE* ln = mkline(1);
addtoken(ln, ezheapstr(str));
2020-12-21 13:05:49 -05:00
ln->next = NULL;
2020-12-20 13:58:10 -05:00
return ln;
}
2020-12-21 13:05:49 -05:00
LINE* mksimpleln(char** tokens, int count) {
2020-12-20 13:58:10 -05:00
LINE* ln = mkline(count);
for(int i = 0; i < count; i++)
addtoken(ln, ezheapstr(tokens[i]));
2020-12-21 13:21:37 -05:00
ln->next = NULL;
2020-12-20 13:58:10 -05:00
return ln;
}
LINE* mathopln(char op) {
if(op == '+')
return onetoken("add");
if(op == '-')
return onetoken("sub");
if(op == '=')
return onetoken("eq");
if(op == '>')
return onetoken("gt");
if(op == '<')
return onetoken("lt");
if(op == '|')
return onetoken("or");
if(op == '&')
return onetoken("and");
if(op == '/') {
char* tokens[] = { "call", "Math.divide", "2" };
2020-12-21 16:11:23 -05:00
return mksimpleln(tokens, strcount(tokens));
2020-12-20 13:58:10 -05:00
}
if(op == '*') {
char* tokens[] = { "call", "Math.multiply", "2" };
2020-12-21 16:11:23 -05:00
return mksimpleln(tokens, strcount(tokens));
2020-12-20 13:58:10 -05:00
}
}
2020-12-27 16:52:28 -05:00
LINEBLOCK* pushconstant(int n) {
char* tokens[] = { "push", "constant", itoa(n) };
return mklnblk(mksimpleln(tokens, strcount(tokens)));
}
2020-12-20 13:58:10 -05:00
2020-12-27 16:52:28 -05:00
LINEBLOCK* pushunaryopterm(SCOPE* s, TERM* t) {
LINEBLOCK* blk = compileexpression(s, t->expression);
LINE* neg;
if(t->unaryop == '-')
neg = onetoken("neg");
else
neg = onetoken("not");
appendln(blk, neg);
return blk;
}
LINEBLOCK* opvar(SCOPE* s, char* op, const char* name) {
VAR* v = getvar(s, name);
char* tokens[] = { op, v->memsegment, itoa(v->index) };
return mklnblk(mksimpleln(tokens, strcount(tokens)));
}
LINEBLOCK* pushvar(SCOPE* s, const char* name) {
return opvar(s, "push", name);
}
LINEBLOCK* popvar(SCOPE* s, const char* name) {
return opvar(s, "pop", name);
}
LINEBLOCK* pushfalse() {
return pushconstant(0);
}
LINEBLOCK* pushtrue() {
LINEBLOCK* blk = pushfalse();
appendln(blk, onetoken("not"));
return blk;
}
LINEBLOCK* compilekeywordconst(SCOPE* s, TERM* t) {
if(!strcmp(t->string, "true")) return pushtrue();
if(!strcmp(t->string, "false")) return pushfalse();
eprintf("Unsupported keyword '%s'\n", t->string);
exit(1);
}
LINEBLOCK* compileterm(SCOPE* s, TERM* t) {
if(t->type == intconstant) return pushconstant(t->integer);
if(t->type == unaryopterm) return pushunaryopterm(s, t);
if(t->type == innerexpression) return compileexpression(s, t->expression);
if(t->type == varname) return pushvar(s, t->string);
if(t->type == subroutcall) return compilesubroutcall(s, t->call);
if(t->type == keywordconstant) return compilekeywordconst(s, t);
2020-12-20 13:58:10 -05:00
else {
2020-12-27 16:52:28 -05:00
eprintf("Unsupported term yet %i\n", t->type);
2020-12-20 13:58:10 -05:00
exit(1);
}
2020-12-27 16:52:28 -05:00
}
2020-12-20 13:58:10 -05:00
2020-12-27 16:52:28 -05:00
LINEBLOCK* compileexpression(SCOPE* s, TERM* e) {
LINEBLOCK* blk = compileterm(s, e);
TERM* curr = e->next;
if(curr != NULL) {
while(true) {
blk = mergelnblks(blk, compileterm(s, curr));
if(curr->next != NULL) {
appendln(blk, mathopln(curr->op));
curr = curr->next;
}
else break;
}
appendln(blk, mathopln(e->op));
2020-12-20 13:58:10 -05:00
}
2020-12-27 16:52:28 -05:00
return blk;
2020-12-20 13:58:10 -05:00
}
2020-12-27 16:52:28 -05:00
LINEBLOCK* compileexplist(SCOPE* s, EXPRESSIONLIST* explist) {
2020-12-21 13:05:49 -05:00
LINEBLOCK* head = NULL;
2020-12-27 16:52:28 -05:00
while(explist != NULL) {
head = mergelnblks(head, compileexpression(s, explist->expression));
explist = explist->next;
2020-12-20 13:58:10 -05:00
}
return head;
}
2020-12-27 16:52:28 -05:00
LINEBLOCK* compilecallln(SCOPE* s, SUBROUTCALL* call) {
2020-12-21 13:05:49 -05:00
LINE* ln = mkline(3);
2020-12-20 13:58:10 -05:00
2020-12-21 13:05:49 -05:00
addtoken(ln, ezheapstr("call"));
2020-12-20 13:58:10 -05:00
if(call->parentname != NULL)
2020-12-21 13:05:49 -05:00
addtoken(ln, dotlabel(call->parentname, call->name));
2020-12-20 13:58:10 -05:00
else
2020-12-27 16:52:28 -05:00
addtoken(ln, dotlabel(s->currclass->name, call->name));
2020-12-20 13:58:10 -05:00
2020-12-27 16:52:28 -05:00
addtoken(ln, itoa(countexpressions(call->parameters)));
2020-12-20 13:58:10 -05:00
2020-12-21 13:05:49 -05:00
return mklnblk(ln);
}
2020-12-20 13:58:10 -05:00
2020-12-21 13:05:49 -05:00
// temporary ignore list for OS functions
2020-12-21 18:35:41 -05:00
char* ignoresubroutdecs[] = {
2020-12-27 16:52:28 -05:00
"printInt", "void", "peek", "int", "poke", "void"
2020-12-21 13:05:49 -05:00
};
2020-12-21 18:35:41 -05:00
int ignorecount = sizeof(ignoresubroutdecs) / sizeof(char*);
2020-12-21 13:05:49 -05:00
2020-12-27 16:52:28 -05:00
LINEBLOCK* compilesubroutcall(SCOPE* s, SUBROUTCALL* call) {
LINEBLOCK* blk = compilecallln(s, call);
2020-12-21 13:05:49 -05:00
if(call->parameters != NULL)
2020-12-27 16:52:28 -05:00
blk = mergelnblks(compileexplist(s, call->parameters), blk);
2020-12-21 13:05:49 -05:00
// void functions always return 0
// therefore must be thrown away
// gambiarra
char* type = NULL;
for(int i = 0; i < ignorecount; i += 2) {
2020-12-21 18:35:41 -05:00
if(!strcmp(call->name, ignoresubroutdecs[i])) {
type = ignoresubroutdecs[i+1];
2020-12-21 13:05:49 -05:00
break;
}
}
if(type == NULL)
2020-12-21 18:35:41 -05:00
type = getsubroutdecfromcall(s, call)->type;
2020-12-21 13:05:49 -05:00
if(!strcmp(type, "void")) {
char* tokens[] = { "pop", "temp", "0" };
2020-12-27 16:52:28 -05:00
appendln(blk, mksimpleln(tokens, sizeof(tokens) / sizeof(char*)));
2020-12-21 13:05:49 -05:00
}
2020-12-27 16:52:28 -05:00
return blk;
2020-12-20 13:58:10 -05:00
}
2020-12-21 13:05:49 -05:00
LINEBLOCK* compileret(SCOPE* s, TERM* e) {
LINE* ret = onetoken("return");
2020-12-27 16:52:28 -05:00
LINEBLOCK* blk = mklnblk(ret);
2020-12-21 13:05:49 -05:00
2020-12-21 18:35:41 -05:00
// void subroutdecs return 0
2020-12-20 13:58:10 -05:00
if(e == NULL) {
2020-12-21 13:05:49 -05:00
char* tokens[] = { "push", "constant", "0" };
2020-12-27 16:52:28 -05:00
appendlnbefore(blk, mksimpleln(tokens, strcount(tokens)));
2020-12-21 13:05:49 -05:00
} else
2020-12-27 16:52:28 -05:00
blk = mergelnblks(compileexpression(s, e), blk);
2020-12-21 13:05:49 -05:00
2020-12-27 16:52:28 -05:00
return blk;
2020-12-20 13:58:10 -05:00
}
2020-12-27 16:52:28 -05:00
LINEBLOCK* compileif(SCOPE* s, IFSTATEMENT* st) {
LINEBLOCK* blk = compileexpression(s, st->base->expression);
static int ifcount = 0;
int mycount = ifcount;
ifcount++;
2020-12-22 13:45:12 -05:00
2020-12-27 16:52:28 -05:00
char* truelabel = mkcondlabel("IF_TRUE", mycount);
char* ifgoto[] = { "if-goto", truelabel };
appendln(blk, mksimpleln(ifgoto, strcount(ifgoto)));
char* falselabel = mkcondlabel("IF_FALSE", mycount);
char* gotofalse[] = { "goto", falselabel };
appendln(blk, mksimpleln(gotofalse, strcount(gotofalse)));
char* truelabelln[] = { "label", truelabel };
appendln(blk, mksimpleln(truelabelln, strcount(truelabelln)));
2020-12-22 13:45:12 -05:00
2020-12-27 16:52:28 -05:00
blk = mergelnblks(blk, compilestatements(s, st->base->statements));
2020-12-22 13:45:12 -05:00
2020-12-27 16:52:28 -05:00
char* endlabel;
2020-12-22 13:45:12 -05:00
bool haselse = st->elsestatements != NULL;
if(haselse) {
2020-12-27 16:52:28 -05:00
endlabel = mkcondlabel("IF_END", mycount);
char* endgoto[] = { "goto", endlabel };
appendln(blk, mksimpleln(endgoto, strcount(endgoto)));
2020-12-20 13:58:10 -05:00
}
2020-12-22 13:45:12 -05:00
2020-12-27 16:52:28 -05:00
char* falselabelln[] = { "label", falselabel};
appendln(blk, mksimpleln(falselabelln, strcount(falselabelln)));
2020-12-22 13:45:12 -05:00
if(haselse) {
2020-12-27 16:52:28 -05:00
blk = mergelnblks(blk, compilestatements(s, st->elsestatements));
char* endlabelln[] = { "label", endlabel };
appendln(blk, mksimpleln(endlabelln, strcount(endlabelln)));
2020-12-22 13:45:12 -05:00
}
2020-12-27 16:52:28 -05:00
return blk;
2020-12-22 13:45:12 -05:00
}
2020-12-27 16:52:28 -05:00
LINEBLOCK* compilewhile(SCOPE* s, CONDSTATEMENT* w) {
LINEBLOCK* blk = compileexpression(s, w->expression);
2020-12-22 13:45:12 -05:00
2020-12-27 16:52:28 -05:00
static int whilecount = 0;
int mycount = whilecount;
whilecount++;
2020-12-22 13:45:12 -05:00
2020-12-27 16:52:28 -05:00
char* explabel = mkcondlabel("WHILE_EXP", mycount);
char* explabelln[] = { "label", explabel };
appendlnbefore(blk, mksimpleln(explabelln, strcount(explabelln)));
2020-12-22 13:45:12 -05:00
2020-12-27 16:52:28 -05:00
appendln(blk, onetoken("not"));
2020-12-22 13:45:12 -05:00
2020-12-27 16:52:28 -05:00
char* endlabel = mkcondlabel("WHILE_END", mycount);
char* ifgoto[] = { "if-goto", endlabel };
appendln(blk, mksimpleln(ifgoto, strcount(ifgoto)));
2020-12-22 13:45:12 -05:00
2020-12-27 16:52:28 -05:00
blk = mergelnblks(blk, compilestatements(s, w->statements));
2020-12-22 13:45:12 -05:00
2020-12-27 16:52:28 -05:00
char* gotoln[] = { "goto", explabel };
appendln(blk, mksimpleln(gotoln, strcount(gotoln)));
2020-12-22 13:45:12 -05:00
2020-12-27 16:52:28 -05:00
char* endlabelln[] = { "label", endlabel };
appendln(blk, mksimpleln(endlabelln, strcount(endlabelln)));
return blk;
2020-12-22 13:45:12 -05:00
}
2020-12-27 16:52:28 -05:00
LINEBLOCK* compilelet(SCOPE* s, LETSTATEMENT* l) {
// missing array ind
LINEBLOCK* blk = compileexpression(s, l->expression);
blk = mergelnblks(blk, popvar(s, l->varname));
return blk;
2020-12-22 13:45:12 -05:00
}
2020-12-27 16:52:28 -05:00
LINEBLOCK* compilestatement(SCOPE* s, STATEMENT* st) {
s->currdebug = st->debug;
if(st->type == dostatement) return compilesubroutcall(s, st->dostatement);
2020-12-22 13:45:12 -05:00
if(st->type == returnstatement) return compileret(s, st->retstatement);
2020-12-27 16:52:28 -05:00
if(st->type == ifstatement) return compileif(s, st->ifstatement);
if(st->type == whilestatement) return compilewhile(s, st->whilestatement);
if(st->type == letstatement) return compilelet(s, st->letstatement);
2020-12-22 13:45:12 -05:00
eprintf("UNSUPPORTED type %i\n", st->type);
exit(1);
2020-12-20 13:58:10 -05:00
}
2020-12-27 16:52:28 -05:00
LINEBLOCK* compilestatements(SCOPE* s, STATEMENT* sts) {
2020-12-21 13:05:49 -05:00
LINEBLOCK* head = NULL;
while(sts != NULL) {
2020-12-27 16:52:28 -05:00
head = mergelnblks(head, compilestatement(s, sts));
2020-12-20 13:58:10 -05:00
sts = sts->next;
}
return head;
}
2020-12-21 13:05:49 -05:00
LINEBLOCK* compilefunbody(SCOPE* s, CLASS* c, SUBROUTBODY* b) {
2020-12-24 14:22:22 -05:00
SCOPE* myscope = mkscope(s);
2020-12-27 16:52:28 -05:00
myscope->currclass = c;
2020-12-24 14:22:22 -05:00
if(b->vardecs != NULL)
2020-12-27 16:52:28 -05:00
addlocalvars(s, b->vardecs);
LINEBLOCK* head = compilestatements(myscope, b->statements);
2020-12-20 13:58:10 -05:00
return head;
}
2020-12-21 18:35:41 -05:00
LINEBLOCK* compilefundec(SCOPE* s, CLASS* c, SUBROUTDEC* f) {
2020-12-21 13:05:49 -05:00
LINE* label = mkline(3);
addtoken(label, ezheapstr("function"));
2020-12-22 13:45:12 -05:00
addtoken(label, dotlabel(c->name, f->name));
2020-12-21 13:05:49 -05:00
addtoken(label, itoa(countlocalvars(f->body->vardecs)));
label->next = NULL;
2020-12-20 13:58:10 -05:00
2020-12-21 13:05:49 -05:00
if(f->body->statements != NULL) {
LINEBLOCK* body = compilefunbody(s, c, f->body);
appendlnbefore(body, label);
return body;
}
else
return mklnblk(label);
2020-12-20 13:58:10 -05:00
}
2020-12-21 18:35:41 -05:00
LINEBLOCK* compilesubroutdec(SCOPE* s, CLASS* c, SUBROUTDEC* sd) {
2020-12-20 13:58:10 -05:00
// 'this' and arguments are pushed by caller
// Must have a 'return' at the end
// Label names must have class name too (see mapping)
// types: method, function, constructor
// must switch all of these
2020-12-27 16:52:28 -05:00
SCOPE* myscope = mkscope(s);
if(sd->parameters != NULL)
addparameters(myscope, sd->parameters);
2020-12-20 13:58:10 -05:00
if(sd->subroutclass == function)
2020-12-27 16:52:28 -05:00
return compilefundec(myscope, c, sd);
2020-12-20 13:58:10 -05:00
}
2020-12-21 13:05:49 -05:00
LINEBLOCK* compileclass(COMPILER* c, CLASS* class) {
2020-12-20 13:58:10 -05:00
SCOPE* topscope = mkscope(c->globalscope);
2020-12-24 14:22:22 -05:00
if(class->vardecs != NULL)
addclassvardecs(topscope, class->vardecs);
if(class->subroutdecs != NULL)
2020-12-27 16:52:28 -05:00
topscope->subroutines = class->subroutdecs;
2020-12-20 13:58:10 -05:00
2020-12-21 13:05:49 -05:00
LINEBLOCK* output = NULL;
2020-12-21 18:35:41 -05:00
SUBROUTDEC* curr = class->subroutdecs;
2020-12-21 13:05:49 -05:00
while(curr != NULL) {
2020-12-21 18:35:41 -05:00
output = mergelnblks(output, compilesubroutdec(topscope, class, curr));
2020-12-21 13:05:49 -05:00
curr = curr->next;
2020-12-20 13:58:10 -05:00
}
2020-12-21 13:05:49 -05:00
return output;
2020-12-20 13:58:10 -05:00
}
COMPILER* mkcompiler(CLASS* classes) {
COMPILER* c = (COMPILER*)malloc(sizeof(COMPILER));
c->globalscope = mkscope(NULL);
2020-12-27 16:52:28 -05:00
c->globalscope->classes = classes;
2020-12-24 14:22:22 -05:00
c->classes = classes;
2020-12-20 13:58:10 -05:00
return c;
}