diff --git a/Makefile b/Makefile index 8df8b4c..f2828e7 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -FILES = parser.c main.c translator.c +FILES = parser.c main.c translator.c util.c INCLUDES = -I. CFLAGS = -std=c99 -g OUTFILE = vmtranslator diff --git a/bootstrap.h b/bootstrap.h new file mode 100644 index 0000000..c8de0da --- /dev/null +++ b/bootstrap.h @@ -0,0 +1,55 @@ +#define BOOTSTRAPN 52 +const char* bootstrapcode[BOOTSTRAPN] = { + "@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)" +}; diff --git a/main.c b/main.c index c578371..db5270a 100644 --- a/main.c +++ b/main.c @@ -2,79 +2,237 @@ #include #include #include +#include +#include #include "parser.h" #include "translator.h" +#include "bootstrap.h" +#include "util.h" -char* verfname(char* fname) { - int len = strlen(fname); - if(len > 3) { - int extind = len - 3; - char* extstr = fname + (sizeof(char)*extind); - if(strcmp(extstr, ".vm") == 0) { - int startind = 0; - for(int i = extind-1; i >= 0; i--) { - if(fname[i] == '/') { - startind = i+1; - break; - } +struct TranslationList { + char** files; + char** fnames; + int filecount; + int filessz; + char* output; +}; + +char* getname(char* f, int len) { + int startind = 0; + int endind = len - 1; + short readsmt = 0; + + for(int i = endind; i >= 0; i--) { + if(f[i] == '/') { + if(!readsmt) { + endind = i-1; + f[i] = '\0'; + continue; } - int size = sizeof(char)*(extind - startind + 1); - char* startstr = fname + (sizeof(char)*startind); - char* retstr = (char*)malloc(size); - snprintf(retstr, size, "%s", startstr); - return retstr; + startind = i+1; + break; } + if(f[i] == '.') + endind = i-1; + readsmt = 1; } - fprintf(stderr, "Name format must be Xxx.vm\n"); - exit(1); + + int size = sizeof(char)*(endind - startind + 2); + char* startstr = f + (sizeof(char)*startind); + char* retstr = (char*)malloc(size); + snprintf(retstr, size, "%s", startstr); + return retstr; } -char* getoutname(char* fname) { - int len = strlen(fname); - int sz = sizeof(char)*(len+2); - char* outname = (char*)malloc(sz); - strcpy(outname, fname); - int ind = len-2; - char* ext = sizeof(char)*ind+outname; - snprintf(ext, 4, "asm"); +char* getfullname(char* f, int len) { + int endind = len - 1; + short readsmt = 0; + + for(int i = endind; i >= 0; i--) { + if(f[i] == '/') { + if(!readsmt) { + endind = i-1; + continue; + } + break; + } + if(f[i] == '.') + endind = i-1; + readsmt = 1; + } + + int size = sizeof(char)*(endind + 2); + char* retstr = (char*)malloc(size); + snprintf(retstr, size, "%s", f); + return retstr; +} + +short isdotvm(char* f, int extind) { + char* extstr = f + (sizeof(char) * extind); + return strcmp(extstr, ".vm") == 0; +} + +short isdir(char* f, int len) { + short readsmt = 0; + for(int i = len-1; i >= 0; i--) { + if(f[i] == '.') + if(readsmt) + return 0; + else + continue; + if(f[i] == '/') + return 1; + readsmt = 1; + } + return 1; +} + +char* getoutname(char* input, int len, short isdir) { + char* outname; + if(isdir) { + char* name = getname(input, len); + int sz = sizeof(char) * (strlen(name)+len+6); + outname = (char*)malloc(sz); + snprintf(outname, sz, "%s/%s.asm", input, name); + free(name); + } + else { + char* name = getfullname(input, len); + int sz = sizeof(char) * (strlen(name)+5); + outname = (char*)malloc(sz); + snprintf(outname, sz, "%s.asm", name); + free(name); + } return outname; } +void addfile(struct TranslationList* l, char* fullname, char* name) { + int count = l->filecount; + int targsize = (count + 1) * sizeof(char*); + + if(l->filessz < targsize) { + int newsz = targsize * 2; + l->files = realloc(l->files, newsz); + l->fnames = realloc(l->fnames, newsz); + l->filessz = newsz; + } + + l->files[count] = fullname; + l->fnames[count] = name; + l->filecount++; +} + +struct TranslationList* getfiles(char* input) { + int filessz = sizeof(char*) * 16; + struct TranslationList* filelist = (struct TranslationList*)malloc(sizeof(struct TranslationList)); + filelist->files = (char**)malloc(filessz); + filelist->fnames = (char**)malloc(filessz); + filelist->filessz = filessz; + filelist->filecount = 0; + + int inplen = strlen(input); + short isitdir = isdir(input, inplen); + if(isitdir) { + DIR* dir = opendir(input); + + if(dir == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + exit(1); + } + + struct dirent* thisfile; + while(thisfile = readdir(dir), thisfile != NULL) { + int len = strlen(thisfile->d_name); + if(len > 3) { + int extind = len - 3; + if(isdotvm(thisfile->d_name, extind)) { + int sz = sizeof(char)*(len+inplen+2); + char* str = (char*)malloc(sz); + snprintf(str, sz, "%s/%s", input, thisfile->d_name); + char* name = getname(thisfile->d_name, len); + addfile(filelist, str, name); + } + } + } + + closedir(dir); + + if(filelist->filecount <= 0) { + fprintf(stderr, "Directory doesn't have any .vm file\n"); + exit(1); + } + + filelist->output = getoutname(input, inplen, isitdir); + } + else { + int extind = inplen - 3; + if(isdotvm(input, extind)){ + char* name = getname(input, inplen); + char* f = heapstr(input, inplen); + addfile(filelist, f, name); + } + else { + fprintf(stderr, "Input file must be named like 'Xxx.vm'\n"); + exit(1); + } + + filelist->output = getoutname(input, inplen, isitdir); + } + + return filelist; +} + +void freetranslationlist(struct TranslationList* ls) { + for(int i = 0; i < ls->filecount; i++) { + free(ls->files[i]); + free(ls->fnames[i]); + } + free(ls->files); + free(ls->fnames); + free(ls->output); + free(ls); +} + int main(int argc, char* argv[]) { if(argc < 2) { fprintf(stderr, "Usage: %s {file}\n", argv[0]); return 1; } - // file name validating - char* fname = verfname(argv[1]); - - FILE* input = fopen(argv[1], "r"); - if(input == NULL) { - fprintf(stderr, "%s\n", strerror(errno)); - return errno; + struct TranslationList* ls = getfiles(argv[1]); + FILE* output = fopen(ls->output, "w"); + + for(int i = 0; i < BOOTSTRAPN; i++) { + fprintf(output, "%s\n", bootstrapcode[i]); } - // parsing - struct Parser* p = mkparser(input); - parse(p); + for(int i = 0; i < ls->filecount; i++) { + // file name validating + char* fname = ls->fnames[i]; + + FILE* input = fopen(ls->files[i], "r"); - // translating - struct Translator* t = mktranslator(p->lns, fname); - translate(t); - freeparser(p); - free(fname); - - // output - char* outname = getoutname(argv[1]); - - FILE* output = fopen(outname, "w"); - printasmlns(t, output); + if(input == NULL) { + fprintf(stderr, "%s\n", strerror(errno)); + return errno; + } + + // parsing + struct Parser* p = mkparser(input); + parse(p); + + // translating + struct Translator* t = mktranslator(p->lns, fname); + translate(t); + freeparser(p); + + printasmlns(t, output); + + // freeing rest + freetranslator(t); + } + + freetranslationlist(ls); fclose(output); - - // freeing rest - free(outname); - freetranslator(t); - return 0; } diff --git a/templates.h b/templates.h index 391f443..5235dbd 100644 --- a/templates.h +++ b/templates.h @@ -1,7 +1,7 @@ #ifndef templates #define templates -#define TPUSHN 10 +#define TPUSHN 11 char* tpush[TPUSHN] = { "", "", @@ -10,31 +10,34 @@ char* tpush[TPUSHN] = { "A=D+A", "D=M", "@SP", + "A=M", + "M=D", + "@SP", "M=M+1", - "A=M-1", - "M=D" }; -#define TPUSHCONSN 7 +#define TPUSHCONSN 8 char* tpushcons[TPUSHCONSN] = { "", "", "D=A", "@SP", + "A=M", + "M=D", + "@SP", "M=M+1", - "A=M-1", - "M=D" }; -#define TPUSHSTATN 7 +#define TPUSHSTATN 8 char* tpushstat[TPUSHSTATN] = { "", "", "D=M", "@SP", + "A=M", + "M=D", + "@SP", "M=M+1", - "A=M-1", - "M=D" }; #define TPUSHTEMPN TPUSHSTATN @@ -43,7 +46,7 @@ char** tpushtemp = tpushstat; #define TPUSHPOINTERN TPUSHSTATN char** tpushpointer = tpushstat; -#define TPOPN 14 +#define TPOPN 13 char* tpop[TPOPN] = { "", "", @@ -53,20 +56,18 @@ char* tpop[TPOPN] = { "@R13", "M=D", "@SP", - "M=M-1", - "A=M", + "AM=M-1", "D=M", "@R13", "A=M", "M=D" }; -#define TPOPSTATN 7 +#define TPOPSTATN 6 char* tpopstat[TPOPSTATN] = { "", "@SP", - "M=M-1", - "A=M", + "AM=M-1", "D=M", "", "" @@ -78,12 +79,11 @@ char** tpoptemp = tpopstat; #define TPOPPOINTERN TPOPSTATN char** tpoppointer = tpopstat; -#define TARITHN 7 +#define TARITHN 6 char* tarith[TARITHN] = { "", "@SP", - "M=M-1", - "A=M", + "AM=M-1", "D=M", "A=A-1", "" @@ -105,12 +105,11 @@ char* tnot[TNOTN] = { "M=!M", }; -#define TCOMPN 14 +#define TCOMPN 13 char* tcomp[TCOMPN] = { "", "@SP", - "M=M-1", - "A=M", + "AM=M-1", "D=M", "A=A-1", "D=D-M", @@ -136,15 +135,117 @@ char* tgoto[TGOTON] = { "0;JMP" }; -#define TIFGOTON 7 +#define TIFGOTON 6 char* tifgoto[TIFGOTON] = { "", "@SP", - "M=M-1", - "A=M", + "AM=M-1", "D=M", "", "D;JNE" }; +#define TCALLSTARTN 8 +char* tcallstart[TCALLSTARTN] = { + "", + "", + "D=A", + "@SP", + "A=M", + "M=D", + "@SP", + "M=M+1", +}; + +#define TCALLPUSHN 7 +char* tcallpush[TCALLPUSHN] = { + "", + "D=M", + "@SP", + "A=M", + "M=D", + "@SP", + "M=M+1", +}; + +#define TCALLSETARGN 8 +char* tcallsetarg[TCALLSETARGN] = { + "@SP", + "D=M", + "@LCL", + "M=D", + "", + "D=D-A", + "@ARG", + "M=D" +}; + +#define TCALLJMPN 3 +char* tcalljmp[TCALLJMPN] = { + "", + "0;JMP", + "" +}; + +#define TFRAMEVARSN 4 +char* tframevars[TFRAMEVARSN] = { + "@LCL", + "@ARG", + "@THIS", + "@THAT" +}; + +#define TFUNCTIONN 2 +char* tfunction[TFUNCTIONN] = { + "", + "" +}; + +#define TFUNCTIONPUSHN 5 +char* tfunctionpush[TFUNCTIONPUSHN] = { + "@SP", + "A=M", + "M=0", + "@SP", + "M=M+1" +}; + +#define TSTARTRETURNN 18 +char* tstartreturn[TSTARTRETURNN] = { + "", + "@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" +}; + +#define TRETPOPN 5 +char* tretpop[TRETPOPN] = { + "@LCL", + "AM=M-1", + "D=M", + "", + "M=D", +}; + +#define TENDRETURNN 3 +char* tendreturn[TENDRETURNN] = { + "@R13", + "A=M", + "0;JMP" +}; + #endif diff --git a/translator.c b/translator.c index f64f2a5..5bd9277 100644 --- a/translator.c +++ b/translator.c @@ -4,8 +4,7 @@ #include #include "translator.h" #include "templates.h" -#define CMPLIMIT 9999 -#define CMPLEN 4 +#include "util.h" void pushtoclean(struct Translator* t, char* topush) { int nextsz = sizeof(char*)*(t->tocleanind+1); @@ -21,32 +20,21 @@ void freetoclean(struct Translator* t) { for(int i = 0; i < t->tocleanind; i++) free(t->toclean[i]); free(t->toclean); - t->tocleansize = 0; - t->tocleanind = 0; -} - -void freeasmlns(struct Translator* t) { - for(int i = 0; i < t->asmind; i++) - free(t->asmlns[i]); - free(t->asmlns); - t->asmsize = 0; - t->asmind = 0; } void freetranslator(struct Translator* t) { - freeasmlns(t); + free(t->asmlns); freetoclean(t); free(t); } void printasmlns(struct Translator* t, FILE* stream) { for(int i = 0; i < t->asmind; i++) - fprintf(stream, "%s\n", t->asmlns[i]->instr); + fprintf(stream, "%s\n", t->asmlns[i]); } -char* heapstr(struct Translator* t, const char* input) { - char* newstr = (char*)malloc(sizeof(char)*(strlen(input)+1)); - strcpy(newstr, input); +char* heapstrtoclean(struct Translator* t, const char* input) { + char* newstr = heapstr(input, strlen(input)); pushtoclean(t, newstr); return newstr; } @@ -54,14 +42,14 @@ char* heapstr(struct Translator* t, const char* input) { char* switchseg(struct Translator* t, struct line* ln) { char* seg = ln->tokens[1]; if(!strcmp(seg, "local")) - return heapstr(t, "@LCL"); + return heapstrtoclean(t, "@LCL"); if(!strcmp(seg, "argument")) - return heapstr(t, "@ARG"); + return heapstrtoclean(t, "@ARG"); if(!strcmp(seg, "this")) - return heapstr(t, "@THIS"); + return heapstrtoclean(t, "@THIS"); if(!strcmp(seg, "that")) - return heapstr(t, "@THAT"); - fprintf(stderr, "Unrecognized segment '%s'; line %i\n", seg, ln->truen); + return heapstrtoclean(t, "@THAT"); + fprintf(stderr, "Unrecognized segment '%s'; file %s.vm, line %i\n", t->fname, seg, ln->truen); exit(1); } @@ -99,29 +87,67 @@ char* mkcom(struct Translator* t, struct line* ln) { return comment; } -void checkind(struct line* ln, int indlen) { +void checknumber(struct Translator* t, struct line* ln, int indlen, char* name, char* n) { for(int i = 0; i < indlen; i++) - if(!isdigit(ln->tokens[2][i])) { - fprintf(stderr, "Invalid index '%s'; line %i\n", ln->tokens[2], ln->truen); + if(!isdigit(n[i])) { + fprintf(stderr, "Invalid %s '%s'; file %s.vm, line %i\n", t->fname, name, n, ln->truen); exit(1); } } -char* mkcmplab(struct Translator* t, struct line* ln) { - t->compcount++; - if(t->compcount > CMPLIMIT) { - fprintf(stderr, "Reached comparison limit (%i); line %i\n", CMPLIMIT, ln->truen); +void checknargs(struct Translator* t, struct line* ln, int nargslen) { + checknumber(t, ln, nargslen, "argument number", ln->tokens[2]); +} + +void checknlocals(struct Translator* t, struct line* ln, int nlocalslen) { + checknumber(t, ln, nlocalslen, "local variable number", ln->tokens[2]); +} + +void checkind(struct Translator* t, struct line* ln, int indlen) { + checknumber(t, ln, indlen, "index", ln->tokens[2]); +} + +int countplaces(int n) { + int places = 1; + int divisor = 1; + if(n < 0) { + n = -n; + places++; + } + while(n / divisor >= 10) { + places++; + divisor *= 10; + } + return places; +} + +void checkinfun(struct Translator* t, struct line* ln) { + if(t->funcount <= 0) { + fprintf(stderr, "Instruction should be part of a function; file %s.vm, line %i\n", t->fname, ln->truen); exit(1); } - int newsz = (t->fnamelen + CMPLEN + 6) * sizeof(char); - char* label = (char*)malloc(newsz); - snprintf(label, newsz, "%s-CMP-%i", t->fname, t->compcount); - pushtoclean(t, label); - return label; +} + +char* mkspeciallab(struct Translator* t, struct line* ln, char* suffix, int* ind) { + checkinfun(t, ln); + (*ind)++; + int sz = (t->lastfunlen + countplaces(*ind) + strlen(suffix) + 3) * sizeof(char); + char* lab = (char*)malloc(sz); + snprintf(lab, sz, "%s$%s.%i", t->lastfun, suffix, (*ind)); + pushtoclean(t, lab); + return lab; +} + +char* mkcmplab(struct Translator* t, struct line* ln) { + return mkspeciallab(t, ln, "cmp", &(t->cmpind)); +} + +char* mkretlab(struct Translator* t, struct line* ln) { + return mkspeciallab(t, ln, "ret", &(t->retind)); } char* mkind(struct Translator* t, struct line* ln, int indlen) { - checkind(ln, indlen); + checkind(t, ln, indlen); int newsz = sizeof(char) * (indlen + 2); char* newind = (char*)malloc(newsz); snprintf(newind, newsz, "@%s", ln->tokens[2]); @@ -137,6 +163,14 @@ char* atlab(struct Translator* t, char* label, int labellen) { return newind; } +char* atn(struct Translator* t, int n) { + int newsz = sizeof(char) * (countplaces(n) + 2); + char* newind = (char*)malloc(newsz); + snprintf(newind, newsz, "@%i", n); + pushtoclean(t, newind); + return newind; +} + char* mklab(struct Translator* t, char* label, int labellen) { int newsz = sizeof(char) * (labellen + 3); char* newind = (char*)malloc(newsz); @@ -145,8 +179,16 @@ char* mklab(struct Translator* t, char* label, int labellen) { return newind; } +char* mkgotolab(struct Translator* t, struct line* ln) { + int sz = sizeof(char) * (t->lastfunlen + strlen(ln->tokens[1]) + 3); + char* lab = (char*)malloc(sz); + snprintf(lab, sz, "@%s$%s", t->lastfun, ln->tokens[1]); + pushtoclean(t, lab); + return lab; +} + char* mkstatind(struct Translator* t, struct line* ln, int indlen) { - checkind(ln, indlen); + checkind(t, ln, indlen); int newsz = sizeof(char) * (t->fnamelen + indlen + 3); char* newind = (char*)malloc(newsz); snprintf(newind, newsz, "@%s.%s", t->fname, ln->tokens[2]); @@ -155,7 +197,7 @@ char* mkstatind(struct Translator* t, struct line* ln, int indlen) { } char* mktempind(struct Translator* t, struct line* ln, int indlen) { - checkind(ln, indlen); + checkind(t, ln, indlen); int intind = atoi(ln->tokens[2]); int newsz = sizeof(char) * (indlen + 3); char* newind = (char*)malloc(newsz); @@ -166,7 +208,7 @@ char* mktempind(struct Translator* t, struct line* ln, int indlen) { char* mkpointerind(struct Translator* t, struct line* ln, int indlen) { if(indlen > 1) { - fprintf(stderr, "Invalid index '%s'; line %i\n", ln->tokens[2], ln->truen); + fprintf(stderr, "Invalid index '%s'; file %s.vm, line %i\n", t->fname, ln->tokens[2], ln->truen); exit(1); } char* ptr; @@ -178,7 +220,7 @@ char* mkpointerind(struct Translator* t, struct line* ln, int indlen) { ptr = "THAT"; break; default: - fprintf(stderr, "Invalid index '%s'; line %i\n", ln->tokens[2], ln->truen); + fprintf(stderr, "Invalid index '%s'; file %s.vm, line %i\n", t->fname, ln->tokens[2], ln->truen); exit(1); } int newsz = sizeof(char) * 6; @@ -189,50 +231,66 @@ char* mkpointerind(struct Translator* t, struct line* ln, int indlen) { } void checkasmsize(struct Translator* t, int toadd) { - int targ = sizeof(struct asmln*)*(t->asmind+toadd); + int targ = sizeof(char*)*(t->asmind+toadd); if(targ >= t->asmsize) { t->asmsize = targ * 2; - t->asmlns = (struct asmln**)realloc(t->asmlns, t->asmsize); + t->asmlns = (char**)realloc(t->asmlns, t->asmsize); } } -struct asmln* mkasmln(struct line* ln, char* content) { - struct asmln* newln = (struct asmln*)malloc(sizeof(struct asmln)); - newln->truen = ln->truen; - newln->instr = content; - return newln; -} - -void checkopamnt(int amnt, struct line* ln) { +void checkopamnt(struct Translator* t, int amnt, struct line* ln) { if(ln->tokenscount < 2) { - fprintf(stderr, "Missing memory segment; line %i", ln->truen); + fprintf(stderr, "Missing memory segment; file %s.vm, line %i\n", t->fname, ln->truen); exit(1); } if(amnt > 2) if(ln->tokenscount < 3) { - fprintf(stderr, "Missing operation index; line %i", ln->truen); + fprintf(stderr, "Missing operation index; file %s.vm, line %i\n", t->fname, ln->truen); exit(1); } } -void addasmlns(struct Translator* t, struct line* ln, char** insts, int instcount) { +void checklab(struct Translator* t, struct line* ln) { + if(ln->tokenscount < 2) { + fprintf(stderr, "Expected label; file %s.vm, line %i\n", t->fname, ln->truen); + exit(1); + } +} + +void checkfun(struct Translator* t, struct line* ln) { + if(ln->tokenscount < 2) { + fprintf(stderr, "Expected function; file %s.vm, line %i\n", t->fname, ln->truen); + exit(1); + } + + if(ln->tokenscount < 3) { + fprintf(stderr, "Expected argument amount; file %s.vm, line %i\n", t->fname, ln->truen); + exit(1); + } +} + +void addasm(struct Translator* t, char** insts, int instcount) { checkasmsize(t, instcount); - // instruction comment - insts[0] = mkcom(t, ln); - for(int i = 0; i < instcount; i++) { - t->asmlns[t->asmind] = mkasmln(ln, insts[i]); + t->asmlns[t->asmind] = insts[i]; t->asmind++; } } +void addasmlns(struct Translator* t, struct line* ln, char** insts, int instcount) { + // instruction comment + insts[0] = mkcom(t, ln); + + addasm(t, insts, instcount); +} + void startpoppush(struct Translator* t, struct line* ln, int indlen, char** insts) { // @segment insts[1] = switchseg(t, ln); // D=M - insts[2] = heapstr(t, "D=M"); + insts[2] = heapstrtoclean(t, "D=M"); // @i insts[3] = mkind(t, ln, indlen); @@ -277,7 +335,7 @@ void popstat(struct Translator* t, struct line* ln, int indlen) { tpopstat[TPOPSTATN-2] = mkstatind(t, ln, indlen); // M=D - tpopstat[TPOPSTATN-1] = heapstr(t, "M=D"); + tpopstat[TPOPSTATN-1] = heapstrtoclean(t, "M=D"); addasmlns(t, ln, tpopstat, TPOPSTATN); } @@ -287,7 +345,7 @@ void poptemp(struct Translator* t, struct line* ln, int indlen) { tpoptemp[TPOPTEMPN-2] = mktempind(t, ln, indlen); // M=D - tpoptemp[TPOPTEMPN-1] = heapstr(t, "M=D"); + tpoptemp[TPOPTEMPN-1] = heapstrtoclean(t, "M=D"); addasmlns(t, ln, tpoptemp, TPOPTEMPN); } @@ -297,7 +355,7 @@ void poppointer(struct Translator* t, struct line* ln, int indlen) { tpoppointer[TPOPPOINTERN-2] = mkpointerind(t, ln, indlen); // M=D - tpoppointer[TPOPPOINTERN-1] = heapstr(t, "M=D"); + tpoppointer[TPOPPOINTERN-1] = heapstrtoclean(t, "M=D"); addasmlns(t, ln, tpoppointer, TPOPPOINTERN); } @@ -309,7 +367,7 @@ void pop(struct Translator* t, struct line* ln, int indlen) { } void arith(struct Translator* t, struct line* ln, char* op) { - tarith[TARITHN-1] = heapstr(t, op); + tarith[TARITHN-1] = heapstrtoclean(t, op); addasmlns(t, ln, tarith, TARITHN); } @@ -322,7 +380,7 @@ void comp(struct Translator* t, struct line* ln, char* op) { tcomp[TCOMPN-6] = atlab(t, label, labellen); // D;J(op) - int opsz = sizeof(char)*6; + int opsz = sizeof(char) * 6; char* trueop = (char*)malloc(opsz); snprintf(trueop, opsz, "D;J%s", op); tcomp[TCOMPN-5] = trueop; @@ -335,43 +393,116 @@ void comp(struct Translator* t, struct line* ln, char* op) { }; void label(struct Translator* t, struct line* ln) { - if(ln->tokenscount < 2) { - fprintf(stderr, "Expected label; line %i", ln->truen); - exit(1); - } + checklab(t, ln); - // (label) - tlabel[TLABELN-1] = mklab(t, ln->tokens[1], strlen(ln->tokens[1])); + // (funcname$label) + checkinfun(t, ln); + int sz = (t->lastfunlen + strlen(ln->tokens[1]) + 4) * sizeof(char); + char* lab = (char*)malloc(sz); + snprintf(lab, sz, "(%s$%s)", t->lastfun, ln->tokens[1]); + pushtoclean(t, lab); + tlabel[TLABELN-1] = lab; addasmlns(t, ln, tlabel, TLABELN); } void mygoto(struct Translator* t, struct line* ln) { - if(ln->tokenscount < 2) { - fprintf(stderr, "Expected label; line %i", ln->truen); - exit(1); - } + checklab(t, ln); // @label - tgoto[TGOTON-2] = atlab(t, ln->tokens[1], strlen(ln->tokens[1])); + tgoto[TGOTON-2] = mkgotolab(t, ln); addasmlns(t, ln, tgoto, TGOTON); } void ifgoto(struct Translator* t, struct line* ln) { - if(ln->tokenscount < 2) { - fprintf(stderr, "Expected label; line %i", ln->truen); - exit(1); - } + checklab(t, ln); // @label - tifgoto[TIFGOTON-2] = atlab(t, ln->tokens[1], strlen(ln->tokens[1])); + tifgoto[TIFGOTON-2] = mkgotolab(t, ln); addasmlns(t, ln, tifgoto, TIFGOTON); } +int pushframe(struct Translator* t, struct line* ln, char* retlab, int retlablen) { + tcallstart[1] = atlab(t, retlab, retlablen); + + addasmlns(t, ln, tcallstart, TCALLSTARTN); + + for(int i = 0; i < TFRAMEVARSN; i++) { + tcallpush[0] = tframevars[i]; + addasm(t, tcallpush, TCALLPUSHN); + } + + return TFRAMEVARSN + 1; +} + +void call(struct Translator* t, struct line* ln) { + checkfun(t, ln); + + // return label + char* retlab = mkretlab(t, ln); + int retlablen = strlen(retlab); + + // push frame + int framesize = pushframe(t, ln, retlab, retlablen); + + // setting ARG + int nargslen = strlen(ln->tokens[2]); + checknargs(t, ln, nargslen); + int nargs = atoi(ln->tokens[2]); + tcallsetarg[TCALLSETARGN-4] = atn(t, nargs + framesize); + addasm(t, tcallsetarg, TCALLSETARGN); + + // jmp + int jmplen = strlen(ln->tokens[1]); + tcalljmp[TCALLJMPN-3] = atlab(t, ln->tokens[1], jmplen); + tcalljmp[TCALLJMPN-1] = mklab(t, retlab, retlablen); + addasm(t, tcalljmp, TCALLJMPN); +} + +void function(struct Translator* t, struct line* ln) { + if(!t->returned) { + fprintf(stderr, "Last function did not return; file %s.vm, line %i\n", t->fname, ln->truen); + exit(1); + } + + checkfun(t, ln); + + t->lastfun = ln->tokens[1]; + int funlen = strlen(ln->tokens[1]); + t->lastfunlen = funlen; + t->funcount++; + t->retind = 0; + t->cmpind = 0; + + // (funcname) + tfunction[1] = mklab(t, ln->tokens[1], funlen); + addasmlns(t, ln, tfunction, TFUNCTIONN); + + // repeat nVars times: + int nlocalslen = strlen(ln->tokens[2]); + checknlocals(t, ln, nlocalslen); + int nlocals = atoi(ln->tokens[2]); + + for(int i = 0; i < nlocals; i++) { + addasm(t, tfunctionpush, TFUNCTIONPUSHN); + } +} + +void myreturn(struct Translator* t, struct line* ln) { + addasmlns(t, ln, tstartreturn, TSTARTRETURNN); + + for(int i = TFRAMEVARSN-1; i >= 0; i--) { + tretpop[TRETPOPN-2] = tframevars[i]; + addasm(t, tretpop, TRETPOPN); + } + + addasm(t, tendreturn, TENDRETURNN); +} + void switchpush(struct Translator* t, struct line* ln) { - checkopamnt(3, ln); + checkopamnt(t, 3, ln); char* seg = ln->tokens[1]; int indlen = strlen(ln->tokens[2]); @@ -388,7 +519,7 @@ void switchpush(struct Translator* t, struct line* ln) { } void switchpop(struct Translator* t, struct line* ln) { - checkopamnt(3, ln); + checkopamnt(t, 3, ln); char* seg = ln->tokens[1]; int indlen = strlen(ln->tokens[2]); @@ -404,6 +535,7 @@ void switchpop(struct Translator* t, struct line* ln) { void switchop(struct Translator* t, struct line* ln) { char* op = ln->tokens[0]; + short returned = 0; if(!strcmp(op, "push")) switchpush(t, ln); @@ -433,23 +565,41 @@ void switchop(struct Translator* t, struct line* ln) { mygoto(t, ln); else if(!strcmp(op, "if-goto")) ifgoto(t, ln); + else if(!strcmp(op, "return")) { + myreturn(t, ln); + returned = 1; + } + else if(!strcmp(op, "function")) + function(t, ln); + else if(!strcmp(op, "call")) + call(t, ln); else { - fprintf(stderr, "Unrecognized operation '%s'; line %i\n", op, ln->truen); + fprintf(stderr, "Unrecognized operation '%s'; file %s.vm, line %i\n", t->fname, op, ln->truen); exit(1); } + + t->returned = returned; } void translate(struct Translator* t) { for(int i = 0; i < t->lns->count; i++) switchop(t, t->lns->lns[i]); + + if(!t->returned) { + fprintf(stderr, "Expected return before end of file; file %s.vm, line %i\n", t->fname, t->lns->count-1); + exit(1); + } } struct Translator* mktranslator(struct lnarray* lns, char* fname) { struct Translator* t = (struct Translator*)malloc(sizeof(struct Translator)); - t->asmsize = sizeof(struct asmln*)*(lns->count * 15); + t->asmsize = sizeof(char*)*(lns->count * 15); t->asmind = 0; - t->compcount = 0; - t->asmlns = (struct asmln**)malloc(t->asmsize); + t->funcount = 0; + t->retind = 0; + t->cmpind = 0; + t->returned = 1; + t->asmlns = (char**)malloc(t->asmsize); t->tocleanind = 0; t->tocleansize = sizeof(char*)*(lns->count * 5); diff --git a/translator.h b/translator.h index 7e8e587..67fb92a 100644 --- a/translator.h +++ b/translator.h @@ -2,13 +2,8 @@ #define translator #include "parser.h" -struct asmln { - char* instr; - int truen; -}; - struct Translator { - struct asmln** asmlns; + char** asmlns; int asmind; int asmsize; char** toclean; @@ -17,7 +12,12 @@ struct Translator { struct lnarray* lns; char* fname; int fnamelen; - int compcount; + char* lastfun; + int lastfunlen; + int funcount; + int retind; + int cmpind; + short returned; }; void freetranslator(struct Translator* t); diff --git a/util.c b/util.c new file mode 100644 index 0000000..9c2e957 --- /dev/null +++ b/util.c @@ -0,0 +1,10 @@ +#include +#include +#include "util.h" + +char* heapstr(const char* str, int len) { + int sz = sizeof(char) * (len + 1); + char* outstr = (char*)malloc(sz); + strcpy(outstr, str); + return outstr; +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..0dccead --- /dev/null +++ b/util.h @@ -0,0 +1 @@ +char* heapstr(const char* str, int len);