diff --git a/Makefile b/Makefile index 9ccf36f..8ce2e57 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ FILES = *.c */*.c -INCLUDES = -I. -I./parser/ -I./compiler -I./vm -I./tokenizer +LIBRARIES = -lpthread +INCLUDES = -I. -I./parser/ -I./compiler/ -I./vm/ -I./tokenizer/ CFLAGS = -std=c99 -g OUTFILE = jack-compiler main: ${FILES} - ${CC} ${CFLAGS} ${INCLUDES} -o ${OUTFILE} ${FILES} + ${CC} ${CFLAGS} ${LIBRARIES} ${INCLUDES} -o ${OUTFILE} ${FILES} diff --git a/compiler/compiler.c b/compiler/compiler.c index f4c50ae..dd21c0c 100644 --- a/compiler/compiler.c +++ b/compiler/compiler.c @@ -391,16 +391,6 @@ LINEBLOCK* compileclass(COMPILER* c, CLASS* class) { return output; } -void compile(COMPILER* c) { - LINEBLOCK* output = NULL; - CLASS* curr = c->classes; - while(curr != NULL) { - output = mergelnblks(output, compileclass(c, curr)); - curr = curr->next; - } - c->output = output; -} - COMPILER* mkcompiler(CLASS* classes) { COMPILER* c = (COMPILER*)malloc(sizeof(COMPILER)); c->globalscope = mkscope(NULL); diff --git a/compiler/compiler.h b/compiler/compiler.h index f9819e2..cbff6c5 100644 --- a/compiler/compiler.h +++ b/compiler/compiler.h @@ -7,10 +7,8 @@ typedef struct { CLASS* classes; SCOPE* globalscope; - LINEBLOCK* output; } COMPILER; COMPILER* mkcompiler(CLASS* classes); -void compile(COMPILER* c); - +LINEBLOCK* compileclass(COMPILER* c, CLASS* class); #endif diff --git a/io.c b/io.c new file mode 100644 index 0000000..ee22f28 --- /dev/null +++ b/io.c @@ -0,0 +1,177 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" +#include "io.h" + +#include +#ifndef PATH_MAX +#ifdef __linux__ +#include +#else +#define PATH_MAX 512 +#endif +#endif + +char* strtail(char* str, int len, int count) { + int index = len - count; + if (index <= 0) return str; + return str + (sizeof(char) * (index)); +} + +char* strhead(char* str, int count) { + return str + (sizeof(char) * count); +} + +char* trimstr(char* str, int len, int end) { + int count = len - end; + char oldchar = str[count]; + str[count] = '\0'; + char* newstr = (char*)malloc(sizeof(char) * (1 + count)); + strcpy(newstr, str); + str[count] = oldchar; + return newstr; +} + +char* getname(char* f, int len) { + int startind = 0; + int endind = len - 1; + bool readsmt = false; + + for(int i = endind; i >= 0; i--) { + if(f[i] == '/') { + if(!readsmt) { + endind = i-1; + f[i] = '\0'; + continue; + } + startind = i+1; + break; + } + if(f[i] == '.') + endind = i-1; + readsmt = true; + } + + int sz = sizeof(char)*(endind - startind + 2); + char* startstr = strhead(f, startind); + char* retstr = (char*)malloc(sz); + snprintf(retstr, sz, "%s", startstr); + return retstr; +} + +char* getfullname(char* fname, char* dirname, int dirlen) { + int fnamelen = strlen(fname); + int sz = sizeof(char)*(fnamelen+dirlen+2); + char* fullname = (char*)malloc(sz); + sprintf(fullname, "%s/%s", dirname, fname); + return fullname; +} + +bool isdotjack(char* f, int len) { + const char* ext = ".jack"; + return strcmp(strtail(f, len, strlen(ext)), ext) == 0; +} + +bool isdir(char* f, int len) { + bool readsmt = false; + for(int i = len-1; i >= 0; i--) { + if(f[i] == '.') + if(readsmt) + return false; + else + continue; + if(f[i] == '/') + return 1; + readsmt = true; + } + return true; +} + +char* getoutname(char* fullname, int len) { + char* trimmed = trimstr(fullname, len, 4); + int sz = sizeof(char) * (len-1); + char* outname = (char*)malloc(sz); + sprintf(outname, "%svm", trimmed); + return outname; +} + +FILELIST* addfile(FILELIST* l, char* fullname, char* name) { + FILELIST* new = (FILELIST*)malloc(sizeof(FILELIST)); + new->name = name; + new->fullname = fullname; + new->next = l; + new->outname = getoutname(fullname, strlen(fullname)); + return new; +} + +FILELIST* getfilesfromdir(char* dir) { + FILELIST* filelist = NULL; + DIR* d = opendir(dir); + + if(d == NULL) { + eprintf("Error while opening directory '%s': %s\n", dir, strerror(errno)); + exit(errno); + } + + int len = strlen(dir); + struct dirent* thisfile; + while(thisfile = readdir(d), thisfile != NULL) { + if(isdotjack(thisfile->d_name, strlen(thisfile->d_name))) { + char* fullname = getfullname(thisfile->d_name, dir, len); + char* name = getname(thisfile->d_name, len); + filelist = addfile(filelist, fullname, name); + } + } + + closedir(d); + + if(filelist == NULL) { + eprintf("Directory '%s' doesn't have any .jack file\n", dir); + exit(1); + } + return filelist; +} + +FILELIST* getsinglefile(char* file) { + int len = strlen(file); + if(isdotjack(file, len)){ + char* name = getname(file, len); + char* fullname = heapstr(file, len); + + FILE* input = fopen(fullname, "r"); + if(input == NULL) { + eprintf("Error while reading file '%s': %s\n", file, strerror(errno)); + exit(errno); + } + fclose(input); + + return addfile(NULL, fullname, name); + } + else { + eprintf("Input file must be named like 'Xxx.vm'\n"); + exit(1); + } +} + +FILELIST* getfiles(char* input) { + int inplen = strlen(input); + bool isitdir = isdir(input, inplen); + + if(isitdir) + return getfilesfromdir(input); + else + return getsinglefile(input); +} + +void freefilelist(FILELIST* fs) { + free(fs->name); + free(fs->fullname); + if(fs->next != NULL) + freefilelist(fs->next); +} diff --git a/io.h b/io.h new file mode 100644 index 0000000..1e493b3 --- /dev/null +++ b/io.h @@ -0,0 +1,14 @@ +#ifndef IO_H +#define IO_H + +typedef struct flist { + char* name; + char* fullname; + char* outname; + struct flist* next; +} FILELIST; + + +FILELIST* getfiles(char* input); +void freefilelist(FILELIST* fs); +#endif diff --git a/main.c b/main.c index 78dd57c..e892bd7 100644 --- a/main.c +++ b/main.c @@ -2,31 +2,126 @@ #include #include #include +#include +#include #include "parser.h" #include "compiler.h" +#include "io.h" -void printcompiler(COMPILER* c) { - printlns(c->output->head, stdout); +typedef struct unit { + FILELIST* file; + TOKEN* tokens; + CLASS* parsed; + COMPILER* compiler; + LINEBLOCK* compiled; + struct unit* next; +} COMPILEUNIT; + +void* parseunit(void* input) { + COMPILEUNIT* unit = (COMPILEUNIT*)input; + + unit->parsed = parse(unit->tokens, unit->file->name); + + pthread_exit(NULL); +} + +void* compileunit(void* input) { + COMPILEUNIT* unit = (COMPILEUNIT*)input; + + unit->compiled = compileclass(unit->compiler, unit->parsed); + + pthread_exit(NULL); +} + +void waitthreads(pthread_t* threads, int amount) { + void* status; + int code; + for(int i = 0; i < amount; i++) { + code = pthread_join(threads[i], &status); + if(code) { + eprintf("Error while joining thread %i: %s\n", i, strerror(code)); + exit(code); + } + } +} + +void actonunits(COMPILEUNIT* units, void*(*fun)(void*)) { + pthread_t mythreads[_SC_THREAD_THREADS_MAX]; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + + COMPILEUNIT* curr = units; + + int i; + int code; + do { + i = 0; + while(curr != NULL && i < _SC_THREAD_THREADS_MAX) { + code = pthread_create(&mythreads[i], NULL, fun, curr); + + if(code) { + eprintf("Error while creating thread %i: %s\n", i, strerror(code)); + exit(code); + } + + curr = curr->next; + i++; + } + waitthreads(mythreads, i); + } while(i == _SC_THREAD_THREADS_MAX); + + pthread_attr_destroy(&attr); } int main(int argc, char* argv[]) { + if(argc < 2) { - fprintf(stderr, "Usage: %s {input file}\n", argv[0]); + eprintf("Usage: %s {input file(s)}\n", argv[0]); return 1; } - FILE* input = fopen(argv[1], "r"); + FILELIST* files = getfiles(argv[1]); + FILELIST* curr = files->next; - if(input == NULL) { - fprintf(stderr, "%s\n", strerror(errno)); - return errno; + COMPILEUNIT* head = (COMPILEUNIT*)malloc(sizeof(COMPILEUNIT)); + + head->file = files; + head->tokens = tokenize(files->fullname); + + COMPILEUNIT* currunit = head; + while(curr != NULL) { + COMPILEUNIT* newunit = (COMPILEUNIT*)malloc(sizeof(COMPILEUNIT)); + newunit->file = curr; + newunit->tokens = tokenize(curr->fullname); + currunit->next = newunit; + currunit = newunit; + curr = curr->next; + } + currunit->next = NULL; + + actonunits(head, parseunit); + + CLASS* headclass = head->parsed; + CLASS* currclass = headclass; + currunit = head->next; + while(currunit != NULL) { + currclass->next = currunit->parsed; + currclass = currunit->parsed; + currunit = currunit->next; + } + currclass->next = NULL; + COMPILER* compiler = mkcompiler(headclass); + + currunit = head; + while(currunit != NULL) { + currunit->compiler = compiler; + currunit = currunit->next; } - PARSER* p = mkparser(tokenize(input), argv[1]); - parse(p); - COMPILER* c = mkcompiler(p->output); - compile(c); - printcompiler(c); - + actonunits(head, compileunit); + + printlns(head->compiled->head, stdout); + return 0; } diff --git a/parser/parser-structure.c b/parser/parser-structure.c index 6dff980..08ba8bf 100644 --- a/parser/parser-structure.c +++ b/parser/parser-structure.c @@ -43,19 +43,11 @@ CLASS* parseclass(PARSER* p) { class->subroutdecs = parsesubroutdecs(p); checkcontent(p, "}"); - return class; -} -CLASS* parseclasses(PARSER* p) { - CLASS* head = parseclass(p); - CLASS* curr = head; - while(p->current != NULL && equals(p, "class")) { - curr->next = parseclass(p); - curr = curr->next; - } - if(curr != NULL) - curr->next = NULL; - return head; + if(p->current != NULL) + unexpected(p); + + return class; } int parsepossibilities(PARSER* p, STRINGARRAY* poss) { diff --git a/parser/parser-structure.h b/parser/parser-structure.h index 8c1cf34..0b66485 100644 --- a/parser/parser-structure.h +++ b/parser/parser-structure.h @@ -1,5 +1,5 @@ #ifndef PARSER_STRUCTURE_H #define PARSER_STRUCTURE_H #include "parser.h" -CLASS* parseclasses(PARSER* p); +CLASS* parseclass(PARSER* p); #endif diff --git a/parser/parser.c b/parser/parser.c index dfd6187..732ccfe 100644 --- a/parser/parser.c +++ b/parser/parser.c @@ -5,15 +5,10 @@ #include "parser.h" #include "parser-structure.h" -// Statements -PARSER* mkparser(TOKEN* tokens, char* file) { +CLASS* parse(TOKEN* tokens, char* file) { PARSER* parser = (PARSER*)malloc(sizeof(PARSER)); parser->tokens = tokens; parser->current = tokens; parser->file = file; - return parser; -} - -void parse(PARSER* p) { - p->output = parseclasses(p); + return parseclass(parser); } diff --git a/parser/parser.h b/parser/parser.h index d4f27b4..bbd341a 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -8,9 +8,7 @@ typedef struct { TOKEN* current; TOKEN* checkpoint; char* file; - CLASS* output; } PARSER; -PARSER* mkparser(TOKEN* tokens, char* file); -void parse(PARSER* p); +CLASS* parse(TOKEN* tokens, char* file); #endif diff --git a/tokenizer/tokenizer.c b/tokenizer/tokenizer.c index 7c8ce89..cf14b4e 100644 --- a/tokenizer/tokenizer.c +++ b/tokenizer/tokenizer.c @@ -23,7 +23,7 @@ void freestr(STRING* str); // Token manipulation; TOKEN* appendtokenraw(TOKEN* curitem, STRING* token, int definedat, TOKENTYPE type); -TOKEN* appendtoken(TOKEN* curitem, STRING* token, int definedat); +TOKEN* appendtoken(TOKEN* curitem, STRING* token, char* file, int definedat); #define mktoken() (TOKEN*)malloc(sizeof(TOKEN)) // Char types @@ -33,7 +33,7 @@ bool issymbol(STRING* tk); bool isint(char* str); bool isintcons(STRING* tk); bool isidentifier(STRING* tk); -TOKENTYPE gettokentype(STRING* tk, int definedat); +TOKENTYPE gettokentype(STRING* tk, char* file, int definedat); // Stream handling void skipln(FILE* input); @@ -78,9 +78,9 @@ TOKEN* appendtokenraw(TOKEN* curitem, STRING* token, int definedat, TOKENTYPE ty return nextitem; } -TOKEN* appendtoken(TOKEN* curitem, STRING* token, int definedat) { +TOKEN* appendtoken(TOKEN* curitem, STRING* token, char* file, int definedat) { append(token, '\0'); - return appendtokenraw(curitem, token, definedat, gettokentype(token, definedat)); + return appendtokenraw(curitem, token, definedat, gettokentype(token, file, definedat)); } // Char types @@ -128,12 +128,12 @@ bool isidentifier(STRING* tk) { return true; } -TOKENTYPE gettokentype(STRING* tk, int definedat) { +TOKENTYPE gettokentype(STRING* tk, char* file, int definedat) { if(iskeyword(tk)) return keyword; if(issymbol(tk)) return symbol; if(isintcons(tk)) return integer; if(isidentifier(tk)) return identifier; - eprintf("Unexpected token '%s'; line %i\n", tk->str, definedat); + eprintf("Unexpected token '%s'; file '%s', line %i\n", tk->str, file, definedat); exit(1); } @@ -188,7 +188,7 @@ void readstr(FILE* input, STRING* tmp, int definedat) { append(tmp, '\0'); } -TOKEN* tokenize(FILE* input) { +TOKEN* tokenize(char* file) { TOKEN* head = mktoken(); TOKEN* lastitem = head; TOKEN* curitem = head; @@ -198,6 +198,7 @@ TOKEN* tokenize(FILE* input) { CHARTYPE curtype; int lnscount = 1; + FILE* input = fopen(file, "r"); unsigned char c; while(c = fgetc(input), !feof(input)) { @@ -208,7 +209,7 @@ TOKEN* tokenize(FILE* input) { continue; else if(c == '"') { if(lasttype != space) - curitem = appendtoken(curitem, tmptoken, lnscount); + curitem = appendtoken(curitem, tmptoken, file, lnscount); readstr(input, tmptoken, lnscount); lastitem = curitem; curitem = appendtokenraw(curitem, tmptoken, lnscount, string); @@ -221,13 +222,13 @@ TOKEN* tokenize(FILE* input) { if(curtype == common) { if(lasttype == charsymbol) { lastitem = curitem; - curitem = appendtoken(curitem, tmptoken, lnscount); + curitem = appendtoken(curitem, tmptoken, file, lnscount); } append(tmptoken, c); } else { if(lasttype != space){ lastitem = curitem; - curitem = appendtoken(curitem, tmptoken, lnscount); + curitem = appendtoken(curitem, tmptoken, file, lnscount); } if(curtype == charsymbol) append(tmptoken, c); diff --git a/tokenizer/tokenizer.h b/tokenizer/tokenizer.h index 5c4cda6..d3b47c6 100644 --- a/tokenizer/tokenizer.h +++ b/tokenizer/tokenizer.h @@ -16,6 +16,6 @@ typedef struct token { struct token* next; } TOKEN; -TOKEN* tokenize(FILE* input); +TOKEN* tokenize(char* filename); void freetokenlist(TOKEN* list); #endif