Add compiler and vm-translator

This commit is contained in:
Augusto Gunsch
2021-01-04 17:00:48 -03:00
commit 553c87029f
46 changed files with 4767 additions and 0 deletions

75
vm/vm-lines.c Normal file
View File

@@ -0,0 +1,75 @@
#include <stdlib.h>
#include "vm-lines.h"
LINE* mkline(int size) {
LINE* ln = (LINE*)malloc(sizeof(LINE));
ln->tokens = (char**)malloc(sizeof(char*)*size);
ln->count = 0;
return ln;
}
void addtoken(LINE* ln, char* token) {
ln->tokens[ln->count] = token;
ln->count++;
}
void println(LINE* ln, FILE* stream) {
for(int i = 0; i < ln->count; i++) {
fprintf(stream, "%s", ln->tokens[i]);
if(i + 1 < ln->count)
fprintf(stream, " ");
}
fprintf(stream, "\n");
}
void printlns(LINE* lns, FILE* stream) {
while(lns != NULL) {
println(lns, stream);
lns = lns->next;
}
}
void freeln(LINE* ln) {
for(int i = 0; i < ln->count; i++)
free(ln->tokens[i]);
free(ln->tokens);
free(ln);
}
void freelns(LINE* lns) {
LINE* next = lns->next;
freeln(lns);
if(next != NULL)
freelns(next);
}
void freelnblk(LINEBLOCK* blk) {
freelns(blk->head);
free(blk);
}
LINEBLOCK* mklnblk(LINE* start) {
LINEBLOCK* blk = (LINEBLOCK*)malloc(sizeof(LINEBLOCK));
blk->head = start;
blk->tail = start;
return blk;
}
LINEBLOCK* mergelnblks(LINEBLOCK* head, LINEBLOCK* tail) {
if(head == NULL)
return tail;
head->tail->next = tail->head;
head->tail = tail->tail;
free(tail);
return head;
}
void appendln(LINEBLOCK* lnblk, LINE* ln) {
lnblk->tail->next = ln;
lnblk->tail = ln;
}
void appendlnbefore(LINEBLOCK* lnblk, LINE* ln) {
ln->next = lnblk->head;
lnblk->head = ln;
}

39
vm/vm-lines.h Normal file
View File

@@ -0,0 +1,39 @@
#ifndef VM_LINES_H
#define VM_LINES_H
#include <stdio.h>
/* vm-lines
* Unified standard for the compiler's output and vm-translator's input.
* It is also used by vm-parser when reading .vm files. */
// Data types
typedef struct line {
char** tokens;
int count;
struct line* next;
} LINE;
typedef struct {
LINE* head;
LINE* tail;
} LINEBLOCK;
// Line manipulation
LINE* mkline(int size);
void addtoken(LINE* ln, char* token);
// Line printing
void println(LINE* ln, FILE* stream);
void printlns(LINE* lns, FILE* stream);
// Line freeing
void freeln(LINE* ln);
void freelns(LINE* lns);
void freelnblk(LINEBLOCK* blk);
// Line block manipulation
LINEBLOCK* mklnblk(LINE* start);
LINEBLOCK* mergelnblks(LINEBLOCK* head, LINEBLOCK* tail);
void appendln(LINEBLOCK* lnblk, LINE* ln);
void appendlnbefore(LINEBLOCK* lnblk, LINE* ln);
#endif

313
vm/vm-templates.h Normal file
View File

@@ -0,0 +1,313 @@
#ifndef VM_TEMPLATES
#define VM_TEMPLATES
#else
#error vm-templates may only be included once
#endif
#include "util.h"
#define mktemplate(name, array) TEMPLATE name = { .items = array, .count = strcount(array) };
typedef struct {
char** items;
int count;
} TEMPLATE;
char* tpushlns[] = {
"",
"",
"",
"",
"A=D+A",
"D=M",
"@SP",
"A=M",
"M=D",
"@SP",
"M=M+1",
};
mktemplate(tpush, tpushlns);
char* tpushconslns[] = {
"",
"",
"D=A",
"@SP",
"A=M",
"M=D",
"@SP",
"M=M+1",
};
mktemplate(tpushcons, tpushconslns);
char* tpushstatlns[] = {
"",
"",
"D=M",
"@SP",
"A=M",
"M=D",
"@SP",
"M=M+1",
};
mktemplate(tpushstat, tpushstatlns);
mktemplate(tpushtemp, tpushstatlns);
mktemplate(tpushpointer, tpushstatlns);
char* tpoplns[] = {
"",
"",
"",
"",
"D=D+A",
"@R13",
"M=D",
"@SP",
"AM=M-1",
"D=M",
"@R13",
"A=M",
"M=D"
};
mktemplate(tpop, tpoplns);
char* tpopstatlns[] = {
"",
"@SP",
"AM=M-1",
"D=M",
"",
""
};
mktemplate(tpopstat, tpopstatlns);
mktemplate(tpoptemp, tpopstatlns);
mktemplate(tpoppointer, tpopstatlns);
char* tarithlns[] = {
"",
"@SP",
"AM=M-1",
"D=M",
"A=A-1",
""
};
mktemplate(tarith, tarithlns);
char* tneglns[] = {
"",
"@SP",
"A=M-1",
"M=-M",
};
mktemplate(tneg, tneglns);
char* tnotlns[] = {
"",
"@SP",
"A=M-1",
"M=!M",
};
mktemplate(tnot, tnotlns);
char* tcomplns[] = {
"",
"@SP",
"AM=M-1",
"D=M",
"A=A-1",
"D=D-M",
"M=-1",
"",
"",
"@SP",
"A=M-1",
"M=0",
""
};
mktemplate(tcomp, tcomplns);
char* tlabellns[] = {
"",
""
};
mktemplate(tlabel, tlabellns);
char* tgotolns[] = {
"",
"",
"0;JMP"
};
mktemplate(tgoto, tgotolns);
char* tifgotolns[] = {
"",
"@SP",
"AM=M-1",
"D=M",
"",
"D;JNE"
};
mktemplate(tifgoto, tifgotolns);
char* tcallstartlns[] = {
"",
"",
"D=A",
"@SP",
"A=M",
"M=D",
"@SP",
"M=M+1",
};
mktemplate(tcallstart, tcallstartlns);
char* tcallpushlns[] = {
"",
"D=M",
"@SP",
"A=M",
"M=D",
"@SP",
"M=M+1",
};
mktemplate(tcallpush, tcallpushlns);
char* tcallsetarglns[] = {
"@SP",
"D=M",
"@LCL",
"M=D",
"",
"D=D-A",
"@ARG",
"M=D"
};
mktemplate(tcallsetarg, tcallsetarglns);
char* tcalljmplns[] = {
"",
"0;JMP",
""
};
mktemplate(tcalljmp, tcalljmplns);
char* tframevarslns[] = {
"@LCL",
"@ARG",
"@THIS",
"@THAT"
};
mktemplate(tframevars, tframevarslns);
char* tfunctionlns[] = {
"",
""
};
mktemplate(tfunction, tfunctionlns);
char* tfunctionpushlns[] = {
"@SP",
"A=M",
"M=0",
"@SP",
"M=M+1"
};
mktemplate(tfunctionpush, tfunctionpushlns);
char* tstartreturnlns[] = {
"",
"@LCL",
"D=M",
"@5",
"A=D-A",
"D=M",
"@R13",
"M=D",
"@SP",
"A=M-1",
"D=M",
"@ARG",
"A=M",
"M=D",
"@ARG",
"D=M+1",
"@SP",
"M=D"
};
mktemplate(tstartreturn, tstartreturnlns);
char* tretpoplns[] = {
"@LCL",
"AM=M-1",
"D=M",
"",
"M=D",
};
mktemplate(tretpop, tretpoplns);
char* tendreturnlns[] = {
"@R13",
"A=M",
"0;JMP"
};
mktemplate(tendreturn, tendreturnlns);
char* tbootstraplns[] = {
"@256",
"D=A",
"@SP",
"M=D",
"@BOOTSTRAP$ret",
"D=A",
"@SP",
"A=M",
"M=D",
"@SP",
"M=M+1",
"@LCL",
"D=M",
"@SP",
"A=M",
"M=D",
"@SP",
"M=M+1",
"@ARG",
"D=M",
"@SP",
"A=M",
"M=D",
"@SP",
"M=M+1",
"@THIS",
"D=M",
"@SP",
"A=M",
"M=D",
"@SP",
"M=M+1",
"@THAT",
"D=M",
"@SP",
"A=M",
"M=D",
"@SP",
"M=M+1",
"@5",
"D=A",
"@SP",
"D=M-D",
"@ARG",
"M=D",
"@SP",
"D=M",
"@LCL",
"M=D",
"@Sys.init",
"0;JMP",
"(BOOTSTRAP$ret)"
};
mktemplate(tbootstrap, tbootstraplns);

471
vm/vm-translator.c Normal file
View File

@@ -0,0 +1,471 @@
#include <stdlib.h>
#include <string.h>
#include "vm-templates.h"
#include "vm-translator.h"
#include "util.h"
#define eq(translator, index, str) !strcmp(translator->currln->tokens[index], str)
typedef struct {
STRINGLIST* head;
STRINGLIST* tail;
} ASMBLK;
STRINGLIST* asmln(char* content) {
STRINGLIST* ln = (STRINGLIST*)malloc(sizeof(STRINGLIST));
ln->content = content;
return ln;
}
void togarbage(VMTRANSLATOR* t, char* str) {
STRINGLIST* garb = asmln(str);
garb->next = t->garbage;
t->garbage = garb;
}
char* atraw(VMTRANSLATOR* t, char* n, int len) {
int sz = sizeof(char) * (len + 2);
char* atstr = (char*)malloc(sz);
sprintf(atstr, "@%s", n);
togarbage(t, atstr);
return atstr;
}
char* at(VMTRANSLATOR* t, char* n) {
return atraw(t, n, strlen(n));
}
char* atn(VMTRANSLATOR* t, int n) {
char* str = itoa(n);
togarbage(t, str);
return at(t, str);
}
char* mkstr(VMTRANSLATOR* t, char* str) {
char* heapstr = ezheapstr(str);
togarbage(t, heapstr);
return heapstr;
}
char* mkpointerind(VMTRANSLATOR* t) {
if(t->currln->tokens[2][0] == 0)
return mkstr(t, "@THIS");
else
return mkstr(t, "@THAT");
}
char* mktempind(VMTRANSLATOR* t) {
int index = atoi(t->currln->tokens[2]);
char* actualind = itoa(index+5);
togarbage(t, actualind);
return at(t, actualind);
}
char* dotat(VMTRANSLATOR* t, char* name, char* n) {
int sz = sizeof(char) * (strlen(name) + strlen(n) + 3);
char* atstr = (char*)malloc(sz);
sprintf(atstr, "@%s.%s", name, n);
togarbage(t, atstr);
return atstr;
}
char* comment(VMTRANSLATOR* t) {
int sz = (4 + strlen(t->currln->tokens[0])) * sizeof(char);
for(int i = 1; i < t->currln->count; i++)
sz += (1 + strlen(t->currln->tokens[i])) * sizeof(char);
char* com = (char*)malloc(sz);
if(t->currln->count == 1)
sprintf(com, "// %s", t->currln->tokens[0]);
else if(t->currln->count == 2)
sprintf(com, "// %s %s", t->currln->tokens[0],
t->currln->tokens[1]);
else if(t->currln->count == 3)
sprintf(com, "// %s %s %s", t->currln->tokens[0],
t->currln->tokens[1],
t->currln->tokens[2]);
togarbage(t, com);
return com;
}
char* switchsegment(VMTRANSLATOR* t) {
if(eq(t, 1, "local"))
return mkstr(t, "@LCL");
if(eq(t, 1, "argument"))
return mkstr(t, "@ARG");
if(eq(t, 1, "this"))
return mkstr(t, "@THIS");
return mkstr(t, "@THAT");
}
char* mkspeciallab(VMTRANSLATOR* t, char* suffix, int* ind, int* len) {
(*ind)++;
*len = t->classnamelen + countplaces(*ind) + strlen(suffix) + 2;
int sz = ((*len)+1) * sizeof(char);
char* lab = (char*)malloc(sz);
sprintf(lab, "%s$%s.%i", t->classname, suffix, (*ind));
togarbage(t, lab);
return lab;
}
char* mkcmplab(VMTRANSLATOR* t, int* len) {
return mkspeciallab(t, "cmp", &(t->cmpind), len);
}
char* mkretlab(VMTRANSLATOR* t, int* len) {
return mkspeciallab(t, "ret", &(t->retind), len);
}
char* enclosingparenthesis(VMTRANSLATOR* t, char* content, int len) {
int sz = sizeof(char) * (len + 3);
char* str = (char*)malloc(sz);
sprintf(str, "(%s)", content);
togarbage(t, str);
return str;
}
char* mklab(VMTRANSLATOR* t) {
int sz = (t->classnamelen + strlen(t->currln->tokens[1]) + 4) * sizeof(char);
char* lab = (char*)malloc(sz);
sprintf(lab, "(%s$%s)", t->classname, t->currln->tokens[1]);
togarbage(t, lab);
return lab;
}
char* mkgotolab(VMTRANSLATOR* t) {
int sz = sizeof(char) * (t->classnamelen + strlen(t->currln->tokens[1]) + 3);
char* lab = (char*)malloc(sz);
sprintf(lab, "@%s$%s", t->classname, t->currln->tokens[1]);
togarbage(t, lab);
return lab;
}
ASMBLK* copytemplate(TEMPLATE* t) {
ASMBLK* blk = (ASMBLK*)malloc(sizeof(ASMBLK));
blk->head = asmln(t->items[0]);
STRINGLIST* curr = blk->head;
for(int i = 1; i < t->count; i++) {
STRINGLIST* newln = asmln(t->items[i]);
curr->next = newln;
curr = newln;
}
curr->next = NULL;
blk->tail = curr;
return blk;
}
ASMBLK* mkasmlns(VMTRANSLATOR* t, TEMPLATE* tp) {
// instruction comment
tp->items[0] = comment(t);
return copytemplate(tp);
}
void mergeasmblks(ASMBLK* a, ASMBLK* b) {
a->tail->next = b->head;
a->tail = b->tail;
free(b);
}
/* START STACK MANIPULATION */
ASMBLK* translatepushconst(VMTRANSLATOR* t) {
// @i
tpushcons.items[1] = at(t, t->currln->tokens[2]);
return mkasmlns(t, &tpushcons);
}
ASMBLK* translatepushstatic(VMTRANSLATOR* t) {
// @classname.i
tpushstat.items[1] = dotat(t, t->classname, t->currln->tokens[2]);
return mkasmlns(t, &tpushstat);
}
ASMBLK* translatepushpointer(VMTRANSLATOR* t) {
// @THIS/@THAT
tpushpointer.items[1] = mkpointerind(t);
return mkasmlns(t, &tpushpointer);
}
ASMBLK* translatepushtemp(VMTRANSLATOR* t) {
// @5+i
tpushtemp.items[1] = mktempind(t);
return mkasmlns(t, &tpushtemp);
}
void pushpopcommon(VMTRANSLATOR* t, TEMPLATE* tp) {
// @segment
tp->items[1] = switchsegment(t);
// D=M
tp->items[2] = mkstr(t, "D=M");
// @i
tp->items[3] = at(t, t->currln->tokens[2]);
}
ASMBLK* translatepushgeneric(VMTRANSLATOR* t) {
pushpopcommon(t, &tpush);
return mkasmlns(t, &tpush);
}
ASMBLK* translatepush(VMTRANSLATOR* t) {
if(eq(t, 1, "constant"))
return translatepushconst(t);
if(eq(t, 1, "static"))
return translatepushstatic(t);
if(eq(t, 1, "pointer"))
return translatepushpointer(t);
if(eq(t, 1, "temp"))
return translatepushtemp(t);
return translatepushgeneric(t);
}
ASMBLK* translatepopstatic(VMTRANSLATOR* t) {
// @classname.i
tpopstat.items[tpopstat.count-2] = dotat(t, t->classname, t->currln->tokens[2]);
// M=D
tpopstat.items[tpopstat.count-1] = mkstr(t, "M=D");
return mkasmlns(t, &tpopstat);
}
ASMBLK* translatepoppointer(VMTRANSLATOR* t) {
// @THIS/@THAT
tpoppointer.items[tpoppointer.count-2] = mkpointerind(t);
// M=D
tpoppointer.items[tpoppointer.count-1] = mkstr(t, "M=D");
return mkasmlns(t, &tpoppointer);
}
ASMBLK* translatepoptemp(VMTRANSLATOR* t) {
// @5+i
tpoptemp.items[tpoptemp.count-2] = mktempind(t);
tpoptemp.items[tpoptemp.count-1] = mkstr(t, "M=D");
return mkasmlns(t, &tpoptemp);
}
ASMBLK* translatepopgeneric(VMTRANSLATOR* t) {
pushpopcommon(t, &tpop);
return mkasmlns(t, &tpop);
}
ASMBLK* translatepop(VMTRANSLATOR* t) {
if(eq(t, 1, "static"))
return translatepopstatic(t);
if(eq(t, 1, "pointer"))
return translatepoppointer(t);
if(eq(t, 1, "temp"))
return translatepoptemp(t);
return translatepopgeneric(t);
}
/* END STACK MANIPULATION */
/* BEGIN OPERATIONS */
ASMBLK* translatearith(VMTRANSLATOR* t, char* op) {
tarith.items[tarith.count-1] = mkstr(t, op);
return mkasmlns(t, &tarith);
}
ASMBLK* translatecomp(VMTRANSLATOR* t, char* op) {
int labellen;
char* label = mkcmplab(t, &labellen);
// @label
tcomp.items[tcomp.count-6] = atraw(t, label, labellen);
// D;J(op)
int sz = sizeof(char) * 6;
char* trueop = (char*)malloc(sz);
sprintf(trueop, "D;J%s", op);
tcomp.items[tcomp.count-5] = trueop;
togarbage(t, trueop);
// (label)
tcomp.items[tcomp.count-1] = enclosingparenthesis(t, label, labellen);
return mkasmlns(t, &tcomp);
}
/* END OPERATIONS */
ASMBLK* translatelabel(VMTRANSLATOR* t) {
// (classname$label)
tlabel.items[tlabel.count-1] = mklab(t);
return mkasmlns(t, &tlabel);
}
ASMBLK* translategoto(VMTRANSLATOR* t) {
// @label
tgoto.items[tgoto.count-2] = mkgotolab(t);
return mkasmlns(t, &tgoto);
}
ASMBLK* translateifgoto(VMTRANSLATOR* t) {
// @label
tifgoto.items[tifgoto.count-2] = mkgotolab(t);
return mkasmlns(t, &tifgoto);
}
ASMBLK* translatereturn(VMTRANSLATOR* t) {
ASMBLK* blk = mkasmlns(t, &tstartreturn);
for(int i = tframevars.count-1; i >= 0; i--) {
tretpop.items[tretpop.count-2] = tframevars.items[i];
mergeasmblks(blk, copytemplate(&tretpop));
}
mergeasmblks(blk, copytemplate(&tendreturn));
return blk;
}
ASMBLK* translatefunction(VMTRANSLATOR* t) {
t->retind = 0;
t->cmpind = 0;
// (funcname)
tfunction.items[1] = mklab(t);
ASMBLK* blk = mkasmlns(t, &tfunction);
// repeat nVars times:
int nlocals = atoi(t->currln->tokens[2]);
for(int i = 0; i < nlocals; i++)
mergeasmblks(blk, copytemplate(&tfunctionpush));
return blk;
}
ASMBLK* pushframe(VMTRANSLATOR* t, char* retlab, int retlablen, int* framesize) {
tcallstart.items[1] = atraw(t, retlab, retlablen);
ASMBLK* blk = mkasmlns(t, &tcallstart);
for(int i = 0; i < tframevars.count; i++) {
tcallpush.items[0] = tframevars.items[i];
mergeasmblks(blk, copytemplate((&tcallpush)));
}
*framesize = tframevars.count + 1;
return blk;
}
ASMBLK* translatecall(VMTRANSLATOR* t) {
// return label
int retlablen;
char* retlab = mkretlab(t, &retlablen);
// push frame
int framesize;
ASMBLK* blk = pushframe(t, retlab, retlablen, &framesize);
// setting ARG
int nargs = atoi(t->currln->tokens[2]);
tcallsetarg.items[tcallsetarg.count-4] = atn(t, nargs + framesize);
mergeasmblks(blk, copytemplate(&tcallsetarg));
// jmp
tcalljmp.items[tcalljmp.count-3] = at(t, t->currln->tokens[1]);
tcalljmp.items[tcalljmp.count-1] = enclosingparenthesis(t, retlab, retlablen);
mergeasmblks(blk, copytemplate(&tcalljmp));
return blk;
}
ASMBLK* translateln(VMTRANSLATOR* t) {
if(eq(t, 0, "push"))
return translatepush(t);
if(eq(t, 0, "pop"))
return translatepop(t);
if(eq(t, 0, "add"))
return translatearith(t, "M=D+M");
if(eq(t, 0, "sub"))
return translatearith(t, "M=M-D");
if(eq(t, 0, "and"))
return translatearith(t, "M=D&M");
if(eq(t, 0, "or"))
return translatearith(t, "M=D|M");
if(eq(t, 0, "neg"))
return mkasmlns(t, &tneg);
if(eq(t, 0, "not"))
return mkasmlns(t, &tnot);
if(eq(t, 0, "eq"))
return translatecomp(t, "EQ");
if(eq(t, 0, "gt"))
return translatecomp(t, "LT");
if(eq(t, 0, "lt"))
return translatecomp(t, "GT");
if(eq(t, 0, "label"))
return translatelabel(t);
if(eq(t, 0, "goto"))
return translategoto(t);
if(eq(t, 0, "if-goto"))
return translateifgoto(t);
if(eq(t, 0, "return"))
return translatereturn(t);
if(eq(t, 0, "function"))
return translatefunction(t);
return translatecall(t);
}
STRINGLIST* translatevm(VMTRANSLATOR* t) {
ASMBLK* blk = copytemplate(&tbootstrap);
while(t->currln != NULL) {
mergeasmblks(blk, translateln(t));
t->currln = t->currln->next;
}
STRINGLIST* output = blk->head;
free(blk);
return output;
}
VMTRANSLATOR* mkvmtranslator(char* classname, LINEBLOCK* vmlines) {
VMTRANSLATOR* transl = (VMTRANSLATOR*)malloc(sizeof(VMTRANSLATOR));
transl->currln = vmlines->head;
transl->start = vmlines->head;
transl->garbage = NULL;
transl->retind = 0;
transl->cmpind = 0;
transl->classname = classname;
transl->classnamelen = strlen(classname);
return transl;
}
void freegarbage(STRINGLIST* garbage) {
if(garbage != NULL) {
free(garbage->content);
STRINGLIST* next = garbage->next;
free(garbage);
freegarbage(next);
}
}
void freevmtranslator(VMTRANSLATOR* t) {
freegarbage(t->garbage);
free(t);
}

20
vm/vm-translator.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef VM_TRANSLATOR
#define VM_TRANSLATOR
#include "vm-lines.h"
typedef struct {
char* classname;
int classnamelen;
LINE* start;
LINE* currln;
STRINGLIST* garbage;
int cmpind;
int retind;
} VMTRANSLATOR;
STRINGLIST* translatevm(VMTRANSLATOR* t);
VMTRANSLATOR* mkvmtranslator(char* classname, LINEBLOCK* vmlines);
void freevmtranslator(VMTRANSLATOR* t);
#endif