Add compiler and vm-translator
This commit is contained in:
226
compiler/compiler-expressions.c
Normal file
226
compiler/compiler-expressions.c
Normal file
@@ -0,0 +1,226 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "compiler-expressions.h"
|
||||
#include "compiler-util.h"
|
||||
|
||||
/* BEGIN FORWARD DECLARATIONS */
|
||||
|
||||
// Miscelaneous
|
||||
LINE* pushconstant(int n);
|
||||
LINE* mathopln(char op);
|
||||
LINE* pushthat();
|
||||
int countexpressions(EXPRESSIONLIST* explist);
|
||||
char* toascii(char c);
|
||||
|
||||
// Dealing with singular terms
|
||||
LINEBLOCK* compilestrconst(char* str);
|
||||
LINEBLOCK* compilekeywordconst(SCOPE* s, TERM* t);
|
||||
LINEBLOCK* compilearrayitem(SCOPE* s, TERM* t);
|
||||
LINEBLOCK* compilecallln(SCOPE* s, SUBROUTDEC* d, SUBROUTCALL* call);
|
||||
LINEBLOCK* pushunaryopterm(SCOPE* s, TERM* t);
|
||||
LINEBLOCK* compileterm(SCOPE* s, TERM* t);
|
||||
|
||||
/* END FORWARD DECLARATIONS */
|
||||
|
||||
|
||||
|
||||
// Miscelaneous
|
||||
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 mkln(tokens);
|
||||
}
|
||||
char* tokens[] = { "call", "Math.multiply", "2" };
|
||||
return mkln(tokens);
|
||||
}
|
||||
|
||||
LINE* pushconstant(int n) {
|
||||
char* tokens[] = { "push", "constant", itoa(n) };
|
||||
LINE* ln = mkln(tokens);
|
||||
free(tokens[2]);
|
||||
return ln;
|
||||
}
|
||||
|
||||
LINE* pushthat() {
|
||||
char* pushthat[] = { "push", "that", "0" };
|
||||
return mkln(pushthat);
|
||||
}
|
||||
|
||||
int countexpressions(EXPRESSIONLIST* explist) {
|
||||
int i = 0;
|
||||
while(explist != NULL) {
|
||||
i++;
|
||||
explist = explist->next;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
char* toascii(char c) {
|
||||
char* result = (char*)malloc(sizeof(char) * (countplaces(c) + 1));
|
||||
sprintf(result, "%i", c);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Dealing with singular terms
|
||||
LINEBLOCK* compilestrconst(char* str) {
|
||||
if(str[0] == '\0')
|
||||
return NULL;
|
||||
|
||||
char* pushchar[] = { "push", "constant", toascii(str[0]) };
|
||||
LINEBLOCK* blk = mklnblk(mksimpleln(pushchar, strcount(pushchar)));
|
||||
free(pushchar[2]);
|
||||
|
||||
char* appendchar[] = { "call", "String.appendChar", "2" };
|
||||
appendln(blk, mkln(appendchar));
|
||||
|
||||
int i = 1;
|
||||
char c;
|
||||
while(c = str[i], c != '\0') {
|
||||
pushchar[2] = toascii(c);
|
||||
appendln(blk, mksimpleln(pushchar, strcount(pushchar)));
|
||||
free(pushchar[2]);
|
||||
appendln(blk, mksimpleln(appendchar, strcount(appendchar)));
|
||||
i++;
|
||||
}
|
||||
|
||||
char* strsize[] = { "push", "constant", itoa(i) };
|
||||
char* mknew[] = { "call", "String.new", "1" };
|
||||
appendlnbefore(blk, mkln(mknew));
|
||||
appendlnbefore(blk, mkln(strsize));
|
||||
free(strsize[2]);
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINE* pushthisadd() {
|
||||
char* pushthisadd[] = { "push", "pointer", "0" };
|
||||
return mkln(pushthisadd);
|
||||
}
|
||||
|
||||
LINE* pushfalse() {
|
||||
return pushconstant(0);
|
||||
}
|
||||
|
||||
LINEBLOCK* pushtrue() {
|
||||
LINEBLOCK* blk = mklnblk(pushfalse());
|
||||
appendln(blk, onetoken("not"));
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compilekeywordconst(SCOPE* s, TERM* t) {
|
||||
if(!strcmp(t->string, "this")) return mklnblk(pushthisadd());
|
||||
if(!strcmp(t->string, "false")) return mklnblk(pushfalse());
|
||||
if(!strcmp(t->string, "true")) return pushtrue();
|
||||
return mklnblk(pushconstant(0));
|
||||
}
|
||||
|
||||
LINEBLOCK* compilearrayitem(SCOPE* s, TERM* t) {
|
||||
LINEBLOCK* blk = compileexpression(s, t->array->exp);
|
||||
appendln(blk, pushvar(s, t->array->name));
|
||||
|
||||
appendln(blk, onetoken("add"));
|
||||
|
||||
appendln(blk, popthatadd());
|
||||
appendln(blk, pushthat());
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compilecallln(SCOPE* s, SUBROUTDEC* d, SUBROUTCALL* call) {
|
||||
LINE* ln = mkline(3);
|
||||
|
||||
addtoken(ln, ezheapstr("call"));
|
||||
|
||||
addtoken(ln, dotlabel(d->class->name, call->name));
|
||||
|
||||
int count = countexpressions(call->parameters);
|
||||
if(d->subroutclass == method)
|
||||
count++;
|
||||
addtoken(ln, itoa(count));
|
||||
|
||||
return mklnblk(ln);
|
||||
}
|
||||
|
||||
LINEBLOCK* compilesubroutcall(SCOPE* s, SUBROUTCALL* call) {
|
||||
VAR* v;
|
||||
SUBROUTDEC* d = getsubroutdecfromcall(s, call, &v);
|
||||
LINEBLOCK* blk = compilecallln(s, d, call);
|
||||
|
||||
if(call->parameters != NULL)
|
||||
blk = mergelnblks(compileexplist(s, call->parameters), blk);
|
||||
|
||||
if(d->subroutclass == method) {
|
||||
if(call->parentname == NULL)
|
||||
appendlnbefore(blk, pushthisadd());
|
||||
else
|
||||
appendlnbefore(blk, pushvarraw(s, v));
|
||||
}
|
||||
|
||||
// void functions always return 0
|
||||
// therefore must be thrown away
|
||||
if(!strcmp(d->type, "void")) {
|
||||
appendln(blk, poptemp());
|
||||
}
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
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* compileterm(SCOPE* s, TERM* t) {
|
||||
if(t->type == varname) return mklnblk(pushvar(s, t->string));
|
||||
if(t->type == intconstant) return mklnblk(pushconstant(t->integer));
|
||||
if(t->type == stringconstant) return compilestrconst(t->string);
|
||||
if(t->type == keywordconstant) return compilekeywordconst(s, t);
|
||||
if(t->type == arrayitem) return compilearrayitem(s, t);
|
||||
if(t->type == subroutcall) return compilesubroutcall(s, t->call);
|
||||
if(t->type == innerexpression) return compileexpression(s, t->expression);
|
||||
return pushunaryopterm(s, t);
|
||||
}
|
||||
|
||||
// Dealing with whole expressions
|
||||
LINEBLOCK* compileexpression(SCOPE* s, TERM* e) {
|
||||
LINEBLOCK* blk = NULL;
|
||||
if(e != NULL) {
|
||||
while(true) {
|
||||
blk = mergelnblks(blk, compileterm(s, e));
|
||||
if(e->next == NULL)
|
||||
break;
|
||||
appendln(blk, mathopln(e->op));
|
||||
e = e->next;
|
||||
}
|
||||
}
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compileexplist(SCOPE* s, EXPRESSIONLIST* explist) {
|
||||
LINEBLOCK* head = NULL;
|
||||
while(explist != NULL) {
|
||||
head = mergelnblks(head, compileexpression(s, explist->expression));
|
||||
explist = explist->next;
|
||||
}
|
||||
return head;
|
||||
}
|
15
compiler/compiler-expressions.h
Normal file
15
compiler/compiler-expressions.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef COMPILER_EXPRESSIONS_H
|
||||
#define COMPILER_EXPRESSIONS_H
|
||||
#include "vm-lines.h"
|
||||
#include "compiler.h"
|
||||
|
||||
/* compiler-expressions
|
||||
* Functions for dealing and compiling expressions and singular terms. */
|
||||
|
||||
// Dealing with singular terms
|
||||
LINEBLOCK* compilesubroutcall(SCOPE* s, SUBROUTCALL* call);
|
||||
|
||||
// Dealing with whole expressions
|
||||
LINEBLOCK* compileexpression(SCOPE* s, TERM* e);
|
||||
LINEBLOCK* compileexplist(SCOPE* s, EXPRESSIONLIST* explist);
|
||||
#endif
|
310
compiler/compiler-scopes.c
Normal file
310
compiler/compiler-scopes.c
Normal file
@@ -0,0 +1,310 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include "compiler.h"
|
||||
#include "compiler-scopes.h"
|
||||
#include "os.h"
|
||||
|
||||
typedef enum { local, staticseg, arg, fieldseg } MEMSEGMENT;
|
||||
char* memsegnames[] = { "local", "static", "argument", "this" };
|
||||
|
||||
// Error messages
|
||||
void doubledeclaration(const char* name, DEBUGINFO* d1, DEBUGINFO* d2);
|
||||
void ensurenoduplicate(SCOPE* s, char* name);
|
||||
|
||||
// Getters
|
||||
VAR* getvarinvars(VAR* vars, const char* name);
|
||||
CLASS* getclass(SCOPE* s, const char* name);
|
||||
SUBROUTDEC* getsubroutdecfromlist(SUBROUTDEC* start, char* name);
|
||||
SUBROUTDEC* getmethod(SCOPE* s, VAR* parent, SUBROUTCALL* call);
|
||||
SUBROUTDEC* getfunction(SCOPE* s, SUBROUTCALL* call);
|
||||
SUBROUTDEC* getsubroutdecwithparent(SCOPE* s, SUBROUTCALL* call, VAR** varret);
|
||||
SUBROUTDEC* getsubroutdecwithoutparent(SCOPE* s, SUBROUTCALL* call);
|
||||
SUBROUTDEC* getsubroutdec(SCOPE* s, const char* name);
|
||||
|
||||
// Scope adding
|
||||
VAR* mkvar(char* type, char* name, bool primitive, DEBUGINFO* debug, MEMSEGMENT seg, int i);
|
||||
void addvar(SCOPE* s, VAR** dest, VAR* v);
|
||||
void addlocalvar(SCOPE* s, VARDEC* v, int* i);
|
||||
void addstaticvar(SCOPE* s, CLASSVARDEC* v);
|
||||
void addfield(SCOPE* s, CLASSVARDEC* v, int* i);
|
||||
void addclassvardec(SCOPE* s, CLASSVARDEC* v, int* i);
|
||||
void addparameter(SCOPE* s, PARAMETER* p, int* i);
|
||||
|
||||
// Error messages
|
||||
void doubledeclaration(const char* name, DEBUGINFO* d1, DEBUGINFO* d2) {
|
||||
eprintf("Double declaration of '%s' at '%s', line %i; previously defined at '%s', line %i\n",
|
||||
name, d1->file, d1->definedat, d2->file, d2->definedat);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void notdeclared(const char* name, DEBUGINFO* debug) {
|
||||
eprintf("'%s' not declared; file '%s', line %i\n", name, debug->file, debug->definedat);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void invalidparent(SUBROUTCALL* call) {
|
||||
eprintf("Invalid subroutine parent '%s'; file '%s', line %i\n", call->parentname, call->debug->file, call->debug->definedat);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void ensurenoduplicate(SCOPE* s, char* name) {
|
||||
VAR* v = getvar(s, name);
|
||||
if(v != NULL)
|
||||
doubledeclaration(name, s->currdebug, v->debug);
|
||||
|
||||
CLASS* c = getclass(s, name);
|
||||
if(c != NULL)
|
||||
doubledeclaration(name, s->currdebug, c->debug);
|
||||
|
||||
SUBROUTDEC* sr = getsubroutdec(s, name);
|
||||
if(sr != NULL)
|
||||
doubledeclaration(name, s->currdebug, sr->debug);
|
||||
}
|
||||
|
||||
// Scope handling
|
||||
SCOPE* mkscope(SCOPE* prev) {
|
||||
SCOPE* s = (SCOPE*)malloc(sizeof(SCOPE));
|
||||
s->previous = prev;
|
||||
if(prev != NULL)
|
||||
s->compiler = prev->compiler;
|
||||
s->localvars = NULL;
|
||||
s->fields = NULL;
|
||||
s->staticvars = NULL;
|
||||
s->parameters = NULL;
|
||||
s->classes = NULL;
|
||||
s->subroutines = NULL;
|
||||
return s;
|
||||
}
|
||||
|
||||
// Getters
|
||||
VAR* getvarinvars(VAR* vars, const char* name) {
|
||||
while(vars != NULL) {
|
||||
if(!strcmp(vars->name, name))
|
||||
return vars;
|
||||
vars = vars->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
VAR* getvar(SCOPE* s, const char* name) {
|
||||
VAR* var = getvarinvars(s->localvars, name);
|
||||
if(var != NULL)
|
||||
return var;
|
||||
var = getvarinvars(s->parameters, name);
|
||||
if(var != NULL)
|
||||
return var;
|
||||
var = getvarinvars(s->fields, name);
|
||||
if(var != NULL)
|
||||
return var;
|
||||
var = getvarinvars(s->staticvars, name);
|
||||
if(var != NULL)
|
||||
return var;
|
||||
if(s->previous != NULL)
|
||||
return getvar(s->previous, name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CLASS* getclass(SCOPE* s, const char* name) {
|
||||
CLASS* curr = s->classes;
|
||||
while(curr != NULL) {
|
||||
if(!strcmp(curr->name, name))
|
||||
return curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
if(s->previous != NULL)
|
||||
return getclass(s->previous, name);
|
||||
return getosclass(s->compiler->os, name);
|
||||
}
|
||||
|
||||
SUBROUTDEC* getsubroutdecfromlist(SUBROUTDEC* start, char* name) {
|
||||
while(start != NULL) {
|
||||
if(!strcmp(start->name, name))
|
||||
return start;
|
||||
start = start->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SUBROUTDEC* getmethod(SCOPE* s, VAR* parent, SUBROUTCALL* call) {
|
||||
CLASS* c = getclass(s, parent->type);
|
||||
SUBROUTDEC* d = getsubroutdecfromlist(c->subroutdecs, call->name);
|
||||
if(d == NULL)
|
||||
return NULL;
|
||||
if(d->subroutclass != method) {
|
||||
eprintf("Calling a function/constructor as if it were a method; file '%s', line %i\n", call->debug->file, call->debug->definedat);
|
||||
exit(1);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
SUBROUTDEC* getfunction(SCOPE* s, SUBROUTCALL* call) {
|
||||
CLASS* c = getclass(s, call->parentname);
|
||||
if(c == NULL)
|
||||
notdeclared(call->parentname, call->debug);
|
||||
SUBROUTDEC* d = getsubroutdecfromlist(c->subroutdecs, call->name);
|
||||
if(d == NULL)
|
||||
return NULL;
|
||||
if(d->subroutclass == method) {
|
||||
eprintf("Calling a method as if it were a function; file '%s', line %i\n", call->debug->file, call->debug->definedat);
|
||||
exit(1);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
SUBROUTDEC* getsubroutdecwithparent(SCOPE* s, SUBROUTCALL* call, VAR** varret) {
|
||||
VAR* parent = getvar(s, call->parentname);
|
||||
if(parent != NULL) {
|
||||
if(parent->primitive) {
|
||||
eprintf("Primitive type does not have subroutines; file '%s', line %i\n", call->debug->file, call->debug->definedat);
|
||||
exit(1);
|
||||
}
|
||||
*varret = parent;
|
||||
return getmethod(s, parent, call);
|
||||
}
|
||||
else
|
||||
return getfunction(s, call);
|
||||
}
|
||||
|
||||
SUBROUTDEC* getsubroutdecwithoutparent(SCOPE* s, SUBROUTCALL* call) {
|
||||
SUBROUTDEC* d = getsubroutdecfromlist(s->currclass->subroutdecs, call->name);
|
||||
return d;
|
||||
}
|
||||
|
||||
SUBROUTDEC* getsubroutdecfromcall(SCOPE* s, SUBROUTCALL* call, VAR** varret) {
|
||||
SUBROUTDEC* d;
|
||||
*varret = NULL;
|
||||
if(call->parentname != NULL) {
|
||||
d = getossubroutdec(s->compiler->os, call);
|
||||
if(d == NULL)
|
||||
d = getsubroutdecwithparent(s, call, varret);
|
||||
}
|
||||
else {
|
||||
d = getsubroutdecwithoutparent(s, call);
|
||||
}
|
||||
if(d == NULL)
|
||||
notdeclared(call->name, call->debug);
|
||||
return d;
|
||||
}
|
||||
|
||||
SUBROUTDEC* getsubroutdec(SCOPE* s, const char* name) {
|
||||
SUBROUTDEC* curr = s->subroutines;
|
||||
while(curr != NULL) {
|
||||
if(!strcmp(curr->name, name))
|
||||
return curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
if(s->previous != NULL)
|
||||
return getsubroutdec(s->previous, name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Scope adding
|
||||
VAR* mkvar(char* type, char* name, bool primitive, DEBUGINFO* debug, MEMSEGMENT seg, int i) {
|
||||
VAR* v = (VAR*)malloc(sizeof(VAR));
|
||||
v->name = name;
|
||||
v->type = type;
|
||||
v->debug = debug;
|
||||
v->memsegment = memsegnames[seg];
|
||||
v->primitive = primitive;
|
||||
v->index = i;
|
||||
return v;
|
||||
}
|
||||
|
||||
void addvar(SCOPE* s, VAR** dest, VAR* v) {
|
||||
ensurenoduplicate(s, v->name);
|
||||
|
||||
if(!v->primitive) {
|
||||
CLASS* type = getclass(s, v->type);
|
||||
if(type == NULL)
|
||||
notdeclared(v->type, v->debug);
|
||||
}
|
||||
|
||||
v->next = *dest;
|
||||
*dest = v;
|
||||
}
|
||||
|
||||
void addlocalvar(SCOPE* s, VARDEC* v, int* i) {
|
||||
STRINGLIST* currname = v->names;
|
||||
while(currname != NULL) {
|
||||
addvar(s, &(s->localvars), mkvar(v->type, currname->content, v->primitive, v->debug, local, *i));
|
||||
currname = currname->next;
|
||||
(*i)++;
|
||||
}
|
||||
}
|
||||
|
||||
void addstaticvar(SCOPE* s, CLASSVARDEC* v) {
|
||||
STRINGLIST* currname = v->base->names;
|
||||
pthread_mutex_lock(&(s->compiler->staticmutex));
|
||||
static int i = 0;
|
||||
while(currname != NULL) {
|
||||
addvar(s, &(s->staticvars), mkvar(v->base->type, currname->content, v->base->primitive, v->base->debug, staticseg, i));
|
||||
currname = currname->next;
|
||||
i++;
|
||||
}
|
||||
pthread_mutex_unlock(&(s->compiler->staticmutex));
|
||||
}
|
||||
|
||||
void addfield(SCOPE* s, CLASSVARDEC* v, int* i) {
|
||||
STRINGLIST* currname = v->base->names;
|
||||
while(currname != NULL) {
|
||||
addvar(s, &(s->fields), mkvar(v->base->type, currname->content, v->base->primitive, v->base->debug, fieldseg, *i));
|
||||
currname = currname->next;
|
||||
(*i)++;
|
||||
}
|
||||
}
|
||||
|
||||
void addclassvardec(SCOPE* s, CLASSVARDEC* v, int* i) {
|
||||
if(v->type == stat)
|
||||
addstaticvar(s, v);
|
||||
else {
|
||||
addfield(s, v, i);
|
||||
}
|
||||
}
|
||||
|
||||
void addparameter(SCOPE* s, PARAMETER* p, int* i) {
|
||||
addvar(s, &(s->parameters), mkvar(p->type, p->name, p->primitive, p->debug, arg, *i));
|
||||
(*i)++;
|
||||
}
|
||||
|
||||
// Group adding
|
||||
void addclassvardecs(SCOPE* s, CLASSVARDEC* classvardecs) {
|
||||
int i = 0;
|
||||
while(classvardecs != NULL) {
|
||||
addclassvardec(s, classvardecs, &i);
|
||||
classvardecs = classvardecs->next;
|
||||
}
|
||||
}
|
||||
|
||||
void addlocalvars(SCOPE* s, VARDEC* localvars) {
|
||||
int i = 0;
|
||||
while(localvars != NULL) {
|
||||
addlocalvar(s, localvars, &i);
|
||||
localvars = localvars->next;
|
||||
}
|
||||
}
|
||||
|
||||
void addparameters(SCOPE* s, bool isformethod, PARAMETER* params) {
|
||||
int i = isformethod ? 1 : 0;
|
||||
while(params != NULL) {
|
||||
addparameter(s, params, &i);
|
||||
params = params->next;
|
||||
}
|
||||
}
|
||||
|
||||
void freevars(VAR* v) {
|
||||
if(v != NULL) {
|
||||
VAR* next = v->next;
|
||||
free(v);
|
||||
freevars(next);
|
||||
}
|
||||
}
|
||||
|
||||
void freescope(SCOPE* s) {
|
||||
freevars(s->fields);
|
||||
freevars(s->staticvars);
|
||||
freevars(s->localvars);
|
||||
freevars(s->parameters);
|
||||
free(s);
|
||||
};
|
58
compiler/compiler-scopes.h
Normal file
58
compiler/compiler-scopes.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#ifndef COMPILER_SCOPES_H
|
||||
#define COMPILER_SCOPES_H
|
||||
#include "parser-tree.h"
|
||||
#include "compiler.h"
|
||||
|
||||
/* compiler-scopes
|
||||
* Tools for dealing with scopes.
|
||||
*
|
||||
* They can be used to create, expand and stack scopes, as well as to enforce
|
||||
* certain semantic rules. */
|
||||
|
||||
// Data types
|
||||
typedef struct var {
|
||||
DEBUGINFO* debug;
|
||||
char* memsegment;
|
||||
char* type;
|
||||
char* name;
|
||||
int index;
|
||||
bool primitive;
|
||||
struct var* next;
|
||||
} VAR;
|
||||
|
||||
typedef struct scope {
|
||||
struct compiler* compiler;
|
||||
DEBUGINFO* currdebug;
|
||||
CLASS* currclass;
|
||||
|
||||
CLASS* classes;
|
||||
SUBROUTDEC* subroutines;
|
||||
|
||||
VAR* fields;
|
||||
VAR* staticvars;
|
||||
VAR* localvars;
|
||||
VAR* parameters;
|
||||
|
||||
struct scope* previous;
|
||||
} SCOPE;
|
||||
|
||||
struct compiler;
|
||||
|
||||
// Group adding
|
||||
void addclassvardecs(SCOPE* s, CLASSVARDEC* classvardecs);
|
||||
void addlocalvars(SCOPE* s, VARDEC* localvars);
|
||||
void addparameters(SCOPE* s, bool isformethod, PARAMETER* params);
|
||||
|
||||
// Scope handling
|
||||
SCOPE* mkscope(SCOPE* prev);
|
||||
|
||||
// Single type getters
|
||||
SUBROUTDEC* getsubroutdecfromcall(SCOPE* s, SUBROUTCALL* call, VAR** varret);
|
||||
CLASS* getclass(SCOPE* s, const char* name);
|
||||
|
||||
// Generic getters
|
||||
VAR* getvar(SCOPE* s, const char* name);
|
||||
|
||||
// Freeing
|
||||
void freescope(SCOPE* s);
|
||||
#endif
|
171
compiler/compiler-statements.c
Normal file
171
compiler/compiler-statements.c
Normal file
@@ -0,0 +1,171 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "compiler-expressions.h"
|
||||
#include "compiler-statements.h"
|
||||
#include "compiler-util.h"
|
||||
|
||||
/* BEGIN FORWARD DECLARATIONS */
|
||||
|
||||
// Miscelaneous
|
||||
LINE* popthat();
|
||||
LINE* pushtemp();
|
||||
char* mkcondlabel(char* name, int count);
|
||||
|
||||
// Handling individual statements
|
||||
LINEBLOCK* compileret(SCOPE* s, TERM* e);
|
||||
LINEBLOCK* compileif(SCOPE* s, IFSTATEMENT* st);
|
||||
LINEBLOCK* compilewhile(SCOPE* s, CONDSTATEMENT* w);
|
||||
LINEBLOCK* compilelet(SCOPE* s, LETSTATEMENT* l);
|
||||
LINEBLOCK* compilestatement(SCOPE* s, STATEMENT* st);
|
||||
|
||||
/* END FORWARD DECLARATIONS */
|
||||
|
||||
|
||||
|
||||
// Miscelaneous
|
||||
LINE* popthat() {
|
||||
char* popthat[] = { "pop", "that", "0" };
|
||||
return mkln(popthat);
|
||||
}
|
||||
|
||||
LINE* pushtemp() {
|
||||
char* pushtemp[] = { "push", "temp", "0" };
|
||||
return mkln(pushtemp);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Handling individual statements
|
||||
LINEBLOCK* compileret(SCOPE* s, TERM* e) {
|
||||
LINE* ret = onetoken("return");
|
||||
LINEBLOCK* blk = mklnblk(ret);
|
||||
|
||||
// void subroutdecs return 0
|
||||
if(e == NULL) {
|
||||
char* tokens[] = { "push", "constant", "0" };
|
||||
appendlnbefore(blk, mkln(tokens));
|
||||
} else
|
||||
blk = mergelnblks(compileexpression(s, e), blk);
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compileif(SCOPE* s, IFSTATEMENT* st) {
|
||||
LINEBLOCK* blk = compileexpression(s, st->base->expression);
|
||||
|
||||
pthread_mutex_lock(&(s->compiler->ifmutex));
|
||||
static int ifcount = 0;
|
||||
int mycount = ifcount;
|
||||
ifcount++;
|
||||
pthread_mutex_unlock(&(s->compiler->ifmutex));
|
||||
|
||||
char* truelabel = mkcondlabel("IF_TRUE", mycount);
|
||||
char* ifgoto[] = { "if-goto", truelabel };
|
||||
appendln(blk, mkln(ifgoto));
|
||||
|
||||
char* falselabel = mkcondlabel("IF_FALSE", mycount);
|
||||
char* gotofalse[] = { "goto", falselabel };
|
||||
appendln(blk, mkln(gotofalse));
|
||||
|
||||
char* truelabelln[] = { "label", truelabel };
|
||||
appendln(blk, mkln(truelabelln));
|
||||
|
||||
blk = mergelnblks(blk, compilestatements(s, st->base->statements));
|
||||
|
||||
char* endlabel;
|
||||
bool haselse = st->elsestatements != NULL;
|
||||
if(haselse) {
|
||||
endlabel = mkcondlabel("IF_END", mycount);
|
||||
char* endgoto[] = { "goto", endlabel };
|
||||
appendln(blk, mkln(endgoto));
|
||||
}
|
||||
|
||||
char* falselabelln[] = { "label", falselabel};
|
||||
appendln(blk, mkln(falselabelln));
|
||||
|
||||
if(haselse) {
|
||||
blk = mergelnblks(blk, compilestatements(s, st->elsestatements));
|
||||
char* endlabelln[] = { "label", endlabel };
|
||||
appendln(blk, mkln(endlabelln));
|
||||
free(endlabel);
|
||||
}
|
||||
|
||||
free(falselabel);
|
||||
free(truelabel);
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compilewhile(SCOPE* s, CONDSTATEMENT* w) {
|
||||
LINEBLOCK* blk = compileexpression(s, w->expression);
|
||||
|
||||
pthread_mutex_lock(&(s->compiler->whilemutex));
|
||||
static int whilecount = 0;
|
||||
int mycount = whilecount;
|
||||
whilecount++;
|
||||
pthread_mutex_unlock(&(s->compiler->whilemutex));
|
||||
|
||||
char* explabel = mkcondlabel("WHILE_EXP", mycount);
|
||||
char* explabelln[] = { "label", explabel };
|
||||
appendlnbefore(blk, mkln(explabelln));
|
||||
|
||||
appendln(blk, onetoken("not"));
|
||||
|
||||
char* endlabel = mkcondlabel("WHILE_END", mycount);
|
||||
char* ifgoto[] = { "if-goto", endlabel };
|
||||
appendln(blk, mkln(ifgoto));
|
||||
|
||||
blk = mergelnblks(blk, compilestatements(s, w->statements));
|
||||
|
||||
char* gotoln[] = { "goto", explabel };
|
||||
appendln(blk, mkln(gotoln));
|
||||
|
||||
char* endlabelln[] = { "label", endlabel };
|
||||
appendln(blk, mkln(endlabelln));
|
||||
|
||||
free(explabel);
|
||||
free(endlabel);
|
||||
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compilelet(SCOPE* s, LETSTATEMENT* l) {
|
||||
LINEBLOCK* blk = compileexpression(s, l->expression);
|
||||
|
||||
if(l->arrayind != NULL) {
|
||||
appendlnbefore(blk, onetoken("add"));
|
||||
appendlnbefore(blk, pushvar(s, l->varname));
|
||||
blk = mergelnblks(compileexpression(s, l->arrayind), blk);
|
||||
|
||||
appendln(blk, poptemp());
|
||||
appendln(blk, popthatadd());
|
||||
appendln(blk, pushtemp());
|
||||
appendln(blk, popthat());
|
||||
}
|
||||
else
|
||||
appendln(blk, popvar(s, l->varname));
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compilestatement(SCOPE* s, STATEMENT* st) {
|
||||
s->currdebug = st->debug;
|
||||
if(st->type == dostatement) return compilesubroutcall(s, st->dostatement);
|
||||
if(st->type == returnstatement) return compileret(s, st->retstatement);
|
||||
if(st->type == ifstatement) return compileif(s, st->ifstatement);
|
||||
if(st->type == whilestatement) return compilewhile(s, st->whilestatement);
|
||||
return compilelet(s, st->letstatement);
|
||||
}
|
||||
|
||||
LINEBLOCK* compilestatements(SCOPE* s, STATEMENT* sts) {
|
||||
LINEBLOCK* head = NULL;
|
||||
while(sts != NULL) {
|
||||
head = mergelnblks(head, compilestatement(s, sts));
|
||||
sts = sts->next;
|
||||
}
|
||||
return head;
|
||||
}
|
9
compiler/compiler-statements.h
Normal file
9
compiler/compiler-statements.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef COMPILER_STATEMENTS_H
|
||||
#define COMPILER_STATEMENTS_H
|
||||
#include "compiler.h"
|
||||
|
||||
/* compiler-statements
|
||||
* Single function for compiling statements */
|
||||
|
||||
LINEBLOCK* compilestatements(SCOPE* s, STATEMENT* sts);
|
||||
#endif
|
135
compiler/compiler-structure.c
Normal file
135
compiler/compiler-structure.c
Normal file
@@ -0,0 +1,135 @@
|
||||
#include <stdlib.h>
|
||||
#include "compiler-statements.h"
|
||||
#include "compiler-structure.h"
|
||||
#include "compiler-util.h"
|
||||
|
||||
/* BEGIN FORWARD DECLARATIONS */
|
||||
|
||||
// Miscelaneous
|
||||
int countlocalvars(VARDEC* decs);
|
||||
int countstrs(STRINGLIST* ls);
|
||||
int getobjsize(CLASS* c);
|
||||
LINE* mksubdeclabel(CLASS* c, SUBROUTDEC* sd);
|
||||
|
||||
// Compiling methods
|
||||
LINEBLOCK* compilefunbody(SCOPE* s, CLASS* cl, SUBROUTBODY* b);
|
||||
LINEBLOCK* compilefundec(SCOPE* s, CLASS* cl, SUBROUTDEC* f);
|
||||
LINEBLOCK* compileconstructor(SCOPE* s, CLASS* cl, SUBROUTDEC* con);
|
||||
LINEBLOCK* compilemethod(SCOPE* s, CLASS* cl, SUBROUTDEC* m);
|
||||
|
||||
/* END FORWARD DECLARATIONS */
|
||||
|
||||
|
||||
// Miscelaneous
|
||||
int countlocalvars(VARDEC* decs) {
|
||||
int i = 0;
|
||||
while(decs != NULL) {
|
||||
STRINGLIST* curr = decs->names;
|
||||
while(curr != NULL) {
|
||||
i++;
|
||||
curr = curr->next;
|
||||
}
|
||||
decs = decs->next;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int countstrs(STRINGLIST* ls) {
|
||||
int count = 0;
|
||||
while(ls != NULL) {
|
||||
count++;
|
||||
ls = ls->next;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int getobjsize(CLASS* c) {
|
||||
CLASSVARDEC* curr = c->vardecs;
|
||||
int count = 0;
|
||||
while(curr != NULL) {
|
||||
if(curr->type == field)
|
||||
count += countstrs(curr->base->names);
|
||||
curr = curr->next;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
LINE* mksubdeclabel(CLASS* c, SUBROUTDEC* sd) {
|
||||
char* labelstrs[] = { "function", dotlabel(c->name, sd->name), itoa(countlocalvars(sd->body->vardecs)) };
|
||||
LINE* label = mkln(labelstrs);
|
||||
free(labelstrs[1]);
|
||||
free(labelstrs[2]);
|
||||
label->next = NULL;
|
||||
return label;
|
||||
}
|
||||
|
||||
// Compiling methods
|
||||
LINEBLOCK* compilefunbody(SCOPE* s, CLASS* cl, SUBROUTBODY* b) {
|
||||
SCOPE* myscope = mkscope(s);
|
||||
myscope->currclass = cl;
|
||||
if(b->vardecs != NULL)
|
||||
addlocalvars(myscope, b->vardecs);
|
||||
LINEBLOCK* head = compilestatements(myscope, b->statements);
|
||||
freescope(myscope);
|
||||
return head;
|
||||
}
|
||||
|
||||
LINEBLOCK* compilefundec(SCOPE* s, CLASS* cl, SUBROUTDEC* f) {
|
||||
LINE* label = mksubdeclabel(cl, f);
|
||||
|
||||
if(f->body->statements != NULL) {
|
||||
LINEBLOCK* body = compilefunbody(s, cl, f->body);
|
||||
appendlnbefore(body, label);
|
||||
return body;
|
||||
}
|
||||
else
|
||||
return mklnblk(label);
|
||||
}
|
||||
|
||||
LINEBLOCK* compileconstructor(SCOPE* s, CLASS* cl, SUBROUTDEC* con) {
|
||||
LINE* label = mksubdeclabel(cl, con);
|
||||
LINEBLOCK* blk = mklnblk(label);
|
||||
|
||||
char* size[] = { "push", "constant", itoa(getobjsize(cl)) };
|
||||
char* memalloc[] = { "call", "Memory.alloc", "1" };
|
||||
char* poppointer[] = { "pop", "pointer", "0" };
|
||||
appendln(blk, mkln(size));
|
||||
appendln(blk, mkln(memalloc));
|
||||
appendln(blk, mkln(poppointer));
|
||||
free(size[2]);
|
||||
|
||||
if(con->body != NULL)
|
||||
return mergelnblks(blk, compilefunbody(s, cl, con->body));
|
||||
else
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compilemethod(SCOPE* s, CLASS* cl, SUBROUTDEC* m) {
|
||||
LINE* label = mksubdeclabel(cl, m);
|
||||
LINEBLOCK* blk = mklnblk(label);
|
||||
|
||||
char* pusharg0[] = { "push", "argument", "0" };
|
||||
char* poppointer[] = { "pop", "pointer", "0" };
|
||||
appendln(blk, mkln(pusharg0));
|
||||
appendln(blk, mkln(poppointer));
|
||||
|
||||
if(m->body != NULL)
|
||||
return mergelnblks(blk, compilefunbody(s, cl, m->body));
|
||||
else
|
||||
return blk;
|
||||
}
|
||||
|
||||
LINEBLOCK* compilesubroutdec(SCOPE* s, CLASS* cl, SUBROUTDEC* sd) {
|
||||
SCOPE* myscope = mkscope(s);
|
||||
LINEBLOCK* blk;
|
||||
if(sd->parameters != NULL)
|
||||
addparameters(myscope, sd->subroutclass == method, sd->parameters);
|
||||
if(sd->subroutclass == function)
|
||||
blk = compilefundec(myscope, cl, sd);
|
||||
else if(sd->subroutclass == constructor)
|
||||
blk = compileconstructor(myscope, cl, sd);
|
||||
else
|
||||
blk = compilemethod(myscope, cl, sd);
|
||||
freescope(myscope);
|
||||
return blk;
|
||||
}
|
9
compiler/compiler-structure.h
Normal file
9
compiler/compiler-structure.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef COMPILER_STRUCTURE_H
|
||||
#define COMPILER_STRUCTURE_H
|
||||
#include "compiler.h"
|
||||
|
||||
/* compiler-structure
|
||||
* Module for dealing with and compiling general program structure. */
|
||||
|
||||
LINEBLOCK* compilesubroutdec(SCOPE* s, CLASS* cl, SUBROUTDEC* sd);
|
||||
#endif
|
51
compiler/compiler-util.c
Normal file
51
compiler/compiler-util.c
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <stdlib.h>
|
||||
#include "compiler-util.h"
|
||||
|
||||
LINE* opvarraw(SCOPE* s, char* op, VAR* v) {
|
||||
char* tokens[] = { op, v->memsegment, itoa(v->index) };
|
||||
LINE* ln = mksimpleln(tokens, strcount(tokens));
|
||||
free(tokens[2]);
|
||||
return ln;
|
||||
}
|
||||
|
||||
LINE* opvar(SCOPE* s, char* op, const char* name) {
|
||||
VAR* v = getvar(s, name);
|
||||
return opvarraw(s, op, v);
|
||||
}
|
||||
|
||||
LINE* pushvarraw(SCOPE*s, VAR* v) {
|
||||
return opvarraw(s, "push", v);
|
||||
}
|
||||
|
||||
LINE* pushvar(SCOPE* s, const char* name) {
|
||||
return opvar(s, "push", name);
|
||||
}
|
||||
|
||||
LINE* popvar(SCOPE* s, const char* name) {
|
||||
return opvar(s, "pop", name);
|
||||
}
|
||||
|
||||
LINE* poptemp() {
|
||||
char* poptemp[] = { "pop", "temp", "0" };
|
||||
return mksimpleln(poptemp, strcount(poptemp));
|
||||
}
|
||||
|
||||
LINE* popthatadd() {
|
||||
char* popthatadd[] = { "pop", "pointer", "1" };
|
||||
return mksimpleln(popthatadd, strcount(popthatadd));
|
||||
}
|
||||
|
||||
LINE* onetoken(char* str) {
|
||||
LINE* ln = mkline(1);
|
||||
addtoken(ln, ezheapstr(str));
|
||||
ln->next = NULL;
|
||||
return ln;
|
||||
}
|
||||
|
||||
LINE* mksimpleln(char** tokens, int count) {
|
||||
LINE* ln = mkline(count);
|
||||
for(int i = 0; i < count; i++)
|
||||
addtoken(ln, ezheapstr(tokens[i]));
|
||||
ln->next = NULL;
|
||||
return ln;
|
||||
}
|
21
compiler/compiler-util.h
Normal file
21
compiler/compiler-util.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef COMPILER_UTIL_H
|
||||
#define COMPILER_UTIL_H
|
||||
#include "vm-lines.h"
|
||||
#include "compiler-scopes.h"
|
||||
#include "compiler.h"
|
||||
|
||||
/* compiler-util
|
||||
* Random utilities used in the compiler module. */
|
||||
|
||||
#define mkln(id) mksimpleln(id, strcount(id))
|
||||
|
||||
LINE* onetoken(char* str);
|
||||
LINE* mksimpleln(char** tokens, int count);
|
||||
|
||||
LINE* pushvarraw(SCOPE*s, VAR* v);
|
||||
LINE* pushvar(SCOPE* s, const char* name);
|
||||
LINE* popvar(SCOPE* s, const char* name);
|
||||
LINE* poptemp();
|
||||
LINE* popthatadd();
|
||||
|
||||
#endif
|
46
compiler/compiler.c
Normal file
46
compiler/compiler.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <stdlib.h>
|
||||
#include "os.h"
|
||||
#include "compiler-structure.h"
|
||||
#include "compiler.h"
|
||||
|
||||
/* This should be part of compiler-structure, but since it is used by other modules,
|
||||
* it will stay here for convenience */
|
||||
LINEBLOCK* compileclass(COMPILER* c, CLASS* class) {
|
||||
SCOPE* topscope = mkscope(c->globalscope);
|
||||
if(class->vardecs != NULL)
|
||||
addclassvardecs(topscope, class->vardecs);
|
||||
if(class->subroutdecs != NULL)
|
||||
topscope->subroutines = class->subroutdecs;
|
||||
|
||||
LINEBLOCK* output = NULL;
|
||||
SUBROUTDEC* curr = class->subroutdecs;
|
||||
while(curr != NULL) {
|
||||
output = mergelnblks(output, compilesubroutdec(topscope, class, curr));
|
||||
curr = curr->next;
|
||||
}
|
||||
freescope(topscope);
|
||||
return output;
|
||||
}
|
||||
|
||||
COMPILER* mkcompiler(CLASS* classes) {
|
||||
COMPILER* c = (COMPILER*)malloc(sizeof(COMPILER));
|
||||
c->globalscope = mkscope(NULL);
|
||||
c->globalscope->compiler = c;
|
||||
c->globalscope->classes = classes;
|
||||
c->classes = classes;
|
||||
c->os = mkos();
|
||||
pthread_mutex_init(&(c->ifmutex), NULL);
|
||||
pthread_mutex_init(&(c->whilemutex), NULL);
|
||||
pthread_mutex_init(&(c->staticmutex), NULL);
|
||||
return c;
|
||||
}
|
||||
|
||||
void freecompiler(COMPILER* c) {
|
||||
pthread_mutex_destroy(&(c->ifmutex));
|
||||
pthread_mutex_destroy(&(c->whilemutex));
|
||||
pthread_mutex_destroy(&(c->staticmutex));
|
||||
// to be continued
|
||||
freeos(c->os);
|
||||
freescope(c->globalscope);
|
||||
free(c);
|
||||
}
|
25
compiler/compiler.h
Normal file
25
compiler/compiler.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef COMPILER_H
|
||||
#define COMPILER_H
|
||||
#include <pthread.h>
|
||||
#include "parser-tree.h"
|
||||
#include "vm-lines.h"
|
||||
#include "compiler-scopes.h"
|
||||
|
||||
/* compiler
|
||||
* This is the file that should be included in other modules
|
||||
* that want to compile a class/program. */
|
||||
|
||||
struct scope;
|
||||
typedef struct compiler {
|
||||
pthread_mutex_t ifmutex;
|
||||
pthread_mutex_t whilemutex;
|
||||
pthread_mutex_t staticmutex;
|
||||
CLASS* classes;
|
||||
CLASS* os;
|
||||
struct scope* globalscope;
|
||||
} COMPILER;
|
||||
|
||||
COMPILER* mkcompiler(CLASS* classes);
|
||||
LINEBLOCK* compileclass(COMPILER* c, CLASS* class);
|
||||
void freecompiler(COMPILER* c);
|
||||
#endif
|
Reference in New Issue
Block a user