528 lines
12 KiB
C
528 lines
12 KiB
C
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include "compiler.h"
|
|
|
|
typedef enum {
|
|
subdec, classvardec, vardec, cl
|
|
} OBJTYPE;
|
|
|
|
void addtoken(LINE* l, char* token) {
|
|
l->tokens[l->tokenscount] = token;
|
|
l->tokenscount++;
|
|
}
|
|
|
|
bool existclass(CLASS* c, char* name) {
|
|
CLASS* current = c;
|
|
while(current != NULL) {
|
|
if(!strcmp(current->name, name))
|
|
return true;
|
|
current = current->next;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void doubledeclarationmsg(char* name, char* f1, int l1, char* f2, int l2) {
|
|
fprintf(stderr, "Double declaration of '%s' at '%s', line %i; previously defined at '%s', line %i\n", name, f1, l1, f2, l2);
|
|
exit(1);
|
|
}
|
|
|
|
void xtractinfo(void* obj, OBJTYPE type, char** file, int* line) {
|
|
if(type == classvardec) {
|
|
*file = ((CLASSVARDEC*)obj)->base->file;
|
|
*line = ((CLASSVARDEC*)obj)->base->definedat;
|
|
}
|
|
else if(type == vardec) {
|
|
*file = ((VARDEC*)obj)->file;
|
|
*line = ((VARDEC*)obj)->definedat;
|
|
}
|
|
else if(type == subdec) {
|
|
*file = ((SUBDEC*)obj)->file;
|
|
*line = ((SUBDEC*)obj)->definedat;
|
|
}
|
|
else if(type == cl) {
|
|
*file = ((CLASS*)obj)->file;
|
|
*line = ((CLASS*)obj)->definedat;
|
|
}
|
|
}
|
|
|
|
void doubledeclaration(char* name, void* o1, OBJTYPE t1, void* o2, OBJTYPE t2) {
|
|
char* f1;
|
|
char* f2;
|
|
int l1, l2;
|
|
xtractinfo(o1, t1, &f1, &l1);
|
|
xtractinfo(o2, t2, &f2, &l2);
|
|
doubledeclarationmsg(name, f1, l1, f2, l2);
|
|
}
|
|
|
|
SCOPE* mkscope(SCOPE* prev) {
|
|
SCOPE* s = (SCOPE*)malloc(sizeof(SCOPE));
|
|
s->subroutines = NULL;
|
|
s->classvardecs = NULL;
|
|
s->vardecs = NULL;
|
|
s->classes = NULL;
|
|
s->previous = prev;
|
|
return s;
|
|
}
|
|
|
|
void popscope(SCOPE** s) {
|
|
SCOPE* prev = (*s)->previous;
|
|
free(*s);
|
|
(*s) = prev;
|
|
}
|
|
|
|
bool existstr(STRINGLIST* strs, char* str) {
|
|
STRINGLIST* current = strs;
|
|
while(current != NULL) {
|
|
if(!strcmp(current->content, str))
|
|
return true;
|
|
current = current->next;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
VARDEC* getvardec(SCOPE* s, char* name) {
|
|
VARDEC* current = s->vardecs;
|
|
while(current != NULL) {
|
|
if(existstr(current->names, name))
|
|
return current;
|
|
current = current->next;
|
|
}
|
|
if(s->previous != NULL)
|
|
return getvardec(s->previous, name);
|
|
return NULL;
|
|
}
|
|
|
|
CLASSVARDEC* getclassvardec(SCOPE* s, char* name) {
|
|
CLASSVARDEC* current = s->classvardecs;
|
|
while(current != NULL) {
|
|
if(existstr(current->base->names, name))
|
|
return current;
|
|
current = current->next;
|
|
}
|
|
if(s->previous != NULL)
|
|
return getclassvardec(s->previous, name);
|
|
return NULL;
|
|
}
|
|
|
|
SUBDEC* getsubdec(SCOPE* s, char* name) {
|
|
SUBDEC* current = s->subroutines;
|
|
while(current != NULL) {
|
|
if(!strcmp(current->name, name))
|
|
return current;
|
|
current = current->next;
|
|
}
|
|
if(s->previous != NULL)
|
|
return getsubdec(s->previous, name);
|
|
return NULL;
|
|
}
|
|
|
|
SUBDEC* getsubdecfromclass(CLASS* c, char* name) {
|
|
SUBDEC* current = c->subdecs;
|
|
while(current != NULL) {
|
|
if(!strcmp(current->name, name))
|
|
return current;
|
|
current = current->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
CLASS* getclass(SCOPE* s, char* name) {
|
|
CLASS* current = s->classes;
|
|
while(current != NULL) {
|
|
if(!strcmp(current->name, name))
|
|
return current;
|
|
current = current->next;
|
|
}
|
|
if(s->previous != NULL)
|
|
return getclass(s->previous, name);
|
|
return NULL;
|
|
}
|
|
|
|
void* getbyname(SCOPE* s, OBJTYPE* t, char* name) {
|
|
SUBDEC* sd = getsubdec(s, name);
|
|
if(sd != NULL) {
|
|
*t = subdec;
|
|
return sd;
|
|
}
|
|
CLASSVARDEC* cvd = getclassvardec(s, name);
|
|
if(cvd != NULL) {
|
|
*t = classvardec;
|
|
return cvd;
|
|
}
|
|
VARDEC* vd = getvardec(s, name);
|
|
if(vd != NULL) {
|
|
*t = vardec;
|
|
return vd;
|
|
}
|
|
CLASS* c = getclass(s, name);
|
|
if(c != NULL) {
|
|
*t = cl;
|
|
return c;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void* getbynamelist(SCOPE* s, STRINGLIST* names, OBJTYPE* t, char** name) {
|
|
STRINGLIST* current = names;
|
|
while(current != NULL) {
|
|
void* obj = getbyname(s, t, current->content);
|
|
if(obj != NULL) {
|
|
*name = current->content;
|
|
return obj;
|
|
}
|
|
current = current->next;
|
|
}
|
|
if(s->previous != NULL)
|
|
return getbynamelist(s->previous, names, t, name);
|
|
return NULL;
|
|
}
|
|
|
|
void addclassvardec(SCOPE* s, CLASSVARDEC* v) {
|
|
OBJTYPE type;
|
|
char* name;
|
|
void* tmp = getbynamelist(s, v->base->names, &type, &name);
|
|
if(tmp != NULL)
|
|
doubledeclaration(name, v, classvardec, tmp, type);
|
|
v->next = s->classvardecs;
|
|
s->classvardecs = v;
|
|
}
|
|
|
|
void addvardec(SCOPE* s, VARDEC* v) {
|
|
OBJTYPE type;
|
|
char* name;
|
|
void* tmp = getbynamelist(s, v->names, &type, &name);
|
|
if(tmp != NULL)
|
|
doubledeclaration(name, v, vardec, tmp, type);
|
|
v->next = s->vardecs;
|
|
s->vardecs = v;
|
|
}
|
|
|
|
void addsubdec(SCOPE* s, SUBDEC* sd) {
|
|
OBJTYPE type;
|
|
void* tmp = getbyname(s, &type, sd->name);
|
|
if(tmp != NULL)
|
|
doubledeclaration(sd->name, sd, subdec, tmp, type);
|
|
sd->next = s->subroutines;
|
|
s->subroutines = sd;
|
|
}
|
|
|
|
void addclass(SCOPE* s, CLASS* c) {
|
|
OBJTYPE type;
|
|
void* tmp = getbyname(s, &type, c->name);
|
|
if(tmp != NULL)
|
|
doubledeclaration(c->name, c, cl, tmp, type);
|
|
c->next = s->classes;
|
|
s->classes = c;
|
|
}
|
|
|
|
void addclassvardecs(SCOPE* s, CLASSVARDEC* vs) {
|
|
CLASSVARDEC* current = vs;
|
|
while(current != NULL) {
|
|
addclassvardec(s, current);
|
|
current = current->next;
|
|
}
|
|
}
|
|
|
|
void addvardecs(SCOPE* s, VARDEC* vs) {
|
|
VARDEC* current = vs;
|
|
while(current != NULL) {
|
|
addvardec(s, current);
|
|
current = current->next;
|
|
}
|
|
}
|
|
|
|
void addsubdecs(SCOPE* s, SUBDEC* ss) {
|
|
SUBDEC* current = ss;
|
|
while(current != NULL) {
|
|
addsubdec(s, current);
|
|
current = current->next;
|
|
}
|
|
}
|
|
|
|
void addclasses(SCOPE* s, CLASS* c) {
|
|
CLASS* current = c;
|
|
while(current != NULL) {
|
|
addclass(s, current);
|
|
current = current->next;
|
|
}
|
|
}
|
|
|
|
int countparameters(EXPRESSIONLIST* params) {
|
|
int i = 0;
|
|
while(params != NULL) {
|
|
i++;
|
|
params = params->next;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
int countlocalvars(VARDEC* decs) {
|
|
int i = 0;
|
|
while(decs != NULL) {
|
|
i++;
|
|
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);
|
|
snprintf(result, sz, "%s.%s", n1, n2);
|
|
return result;
|
|
}
|
|
|
|
char* subdecname(CLASS* c, SUBDEC* sd) {
|
|
return dotlabel(c->name, sd->name);
|
|
}
|
|
|
|
SUBDEC* getsubdecfromparent(SCOPE* s, SUBROUTCALL* call) {
|
|
SUBDEC* sd;
|
|
OBJTYPE type;
|
|
void* parent = getbyname(s, &type, call->parentname);
|
|
if(type == cl)
|
|
sd = getsubdecfromclass((CLASS*)parent, call->name);
|
|
else {
|
|
VARDEC* vd;
|
|
if (type == classvardec)
|
|
vd = ((CLASSVARDEC*)parent)->base;
|
|
else if (type == vardec)
|
|
vd = (VARDEC*)parent;
|
|
else {
|
|
fprintf(stderr, "Unexpected subroutine identifier; file '%s', line %i\n", call->file, call->definedat);
|
|
exit(1);
|
|
}
|
|
if(vd->primitive) {
|
|
fprintf(stderr, "Primitive type doesn't have methods; file '%s', line %i\n", call->file, call->definedat);
|
|
exit(1);
|
|
}
|
|
sd = getsubdecfromparent(s, call);
|
|
}
|
|
return sd;
|
|
}
|
|
|
|
LINE* onetoken(char* str) {
|
|
LINE* ln = mkline(1);
|
|
addtoken(ln, ezheapstr(str));
|
|
return ln;
|
|
}
|
|
|
|
LINE* mksimpleln(char** tokens) {
|
|
int count = sizeof(tokens) / sizeof(char*);
|
|
|
|
LINE* ln = mkline(count);
|
|
for(int i = 0; i < count; i++)
|
|
addtoken(ln, ezheapstr(tokens[i]));
|
|
|
|
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" };
|
|
return mksimpleln(tokens);
|
|
}
|
|
if(op == '*') {
|
|
char* tokens[] = { "call", "Math.multiply", "2" };
|
|
return mksimpleln(tokens);
|
|
}
|
|
}
|
|
|
|
LINE* compileexpression(SCOPE* s, TERM* e, LINE** tail) {
|
|
LINE* nexts = NULL;
|
|
LINE* nextstail;
|
|
LINE* r;
|
|
|
|
if(e->next != NULL) {
|
|
nexts = compileexpression(s, e->next, &nextstail);
|
|
LINE* op = mathopln(e->op);
|
|
nextstail->next = op;
|
|
nextstail = op;
|
|
op->next = NULL;
|
|
}
|
|
|
|
if(e->type == intconstant) {
|
|
r = mkline(3);
|
|
addtoken(r, ezheapstr("push"));
|
|
addtoken(r, ezheapstr("constant"));
|
|
addtoken(r, itoa(e->integer));
|
|
}
|
|
else if(e->type == unaryopterm) {
|
|
r = mkline(1);
|
|
addtoken(r, ezheapstr("neg"));
|
|
}
|
|
else if(e->type == innerexpression) {
|
|
r = compileexpression(s, e->expression, tail); // might be wrong tail
|
|
}
|
|
else {
|
|
fprintf(stderr, "Unsuported SHIT %i\n", e->type);
|
|
exit(1);
|
|
}
|
|
|
|
if(nexts != NULL) {
|
|
r->next = nexts;
|
|
(*tail) = nextstail;
|
|
}
|
|
else {
|
|
(*tail) = r;
|
|
r->next = NULL;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
LINE* compileparameters(SCOPE* s, EXPRESSIONLIST* ps, LINE** tail) {
|
|
LINE* head;
|
|
LINE* mytail;
|
|
if(ps != NULL)
|
|
head = compileexpression(s, ps->expression, &mytail);
|
|
LINE* currln = head;
|
|
EXPRESSIONLIST* current = ps->next;
|
|
while(current != NULL) {
|
|
LINE* newln = compileexpression(s, current->expression, &mytail);
|
|
current = current->next;
|
|
currln->next = newln;
|
|
currln = newln;
|
|
}
|
|
(*tail) = mytail;
|
|
return head;
|
|
}
|
|
|
|
LINE* compilesubroutcall(SCOPE* s, CLASS* c, SUBROUTCALL* call) {
|
|
/* FOR NOW THERE IS NO OS SO THIS WILL CAUSE PROBLEMS
|
|
SUBDEC* sd;
|
|
if(call->parentname != NULL)
|
|
sd = getsubdecfromparent(s, call);
|
|
else
|
|
sd = getsubdec(s, call->name);
|
|
if(sd == NULL) {
|
|
fprintf(stderr, "Method '%s' does not exist; file '%', line %i\n", call->name, call->file, call->definedat);
|
|
exit(1);
|
|
}
|
|
*/
|
|
|
|
// At the moment can only call functions
|
|
LINE* tail;
|
|
LINE* head = compileparameters(s, call->parameters, &tail);
|
|
|
|
LINE* callvm = mkline(3);
|
|
addtoken(callvm, ezheapstr("call"));
|
|
if(call->parentname != NULL)
|
|
addtoken(callvm, dotlabel(call->parentname, call->name));
|
|
else
|
|
addtoken(callvm, dotlabel(c->name, call->name));
|
|
|
|
addtoken(callvm, itoa(countparameters(call->parameters)));
|
|
|
|
tail->next = callvm;
|
|
tail = callvm;
|
|
|
|
return head;
|
|
}
|
|
|
|
LINE* compileret(SCOPE* s, TERM* e) {
|
|
// missing expression handling
|
|
if(e == NULL) {
|
|
LINE* r = mkline(1);
|
|
addtoken(r, ezheapstr("return"));
|
|
return r;
|
|
}
|
|
}
|
|
|
|
LINE* compilestatement(SCOPE* s, CLASS* c, STATEMENT* st) {
|
|
if(st->type == dostatement)
|
|
return compilesubroutcall(s, c, st->dost);
|
|
else if(st->type == returnstatement)
|
|
return compileret(s, st->retst);
|
|
else {
|
|
fprintf(stderr, "UNSUPPORTED\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
LINE* compilestatements(SCOPE* s, CLASS* c, STATEMENT* sts) {
|
|
LINE* head;
|
|
LINE* curr;
|
|
if(sts != NULL) {
|
|
head = compilestatement(s, c, sts);
|
|
curr = head;
|
|
sts = sts->next;
|
|
while(sts != NULL) {
|
|
LINE* ln = compilestatement(s, c, sts);
|
|
curr->next = ln;
|
|
curr = ln;
|
|
sts = sts->next;
|
|
}
|
|
}
|
|
return head;
|
|
}
|
|
|
|
LINE* compilefunbody(SCOPE* s, CLASS* c, SUBROUTBODY* b) {
|
|
// missing scope and vardecs handling
|
|
LINE* head = compilestatements(s, c, b->statements);
|
|
return head;
|
|
}
|
|
|
|
LINE* compilefundec(SCOPE* s, CLASS* c, SUBDEC* f) {
|
|
LINE* head = mkline(3);
|
|
addtoken(head, ezheapstr("function"));
|
|
addtoken(head, subdecname(c, f));
|
|
addtoken(head, itoa(countlocalvars(f->body->vardecs)));
|
|
|
|
head->next = compilefunbody(s, c, f->body);
|
|
return head;
|
|
}
|
|
|
|
LINE* compilesubdec(SCOPE* s, CLASS* c, SUBDEC* sd) {
|
|
// '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
|
|
if(sd->subroutclass == function)
|
|
return compilefundec(s, c, sd);
|
|
}
|
|
|
|
void compileclass(COMPILER* c, CLASS* class) {
|
|
SCOPE* topscope = mkscope(c->globalscope);
|
|
addclassvardecs(topscope, class->vardecs);
|
|
addsubdecs(topscope, class->subdecs);
|
|
|
|
SUBDEC* current = class->subdecs;
|
|
while(current != NULL) {
|
|
compilesubdec(topscope, class, current);
|
|
current = current->next;
|
|
}
|
|
}
|
|
|
|
void compile(COMPILER* c) {
|
|
CLASS* current = c->globalscope->classes;
|
|
while(current != NULL) {
|
|
compileclass(c, current);
|
|
current = current->next;
|
|
}
|
|
}
|
|
|
|
COMPILER* mkcompiler(CLASS* classes) {
|
|
COMPILER* c = (COMPILER*)malloc(sizeof(COMPILER));
|
|
c->globalscope = mkscope(NULL);
|
|
addclasses(c->globalscope, classes);
|
|
return c;
|
|
}
|