422 lines
17 KiB
C
422 lines
17 KiB
C
|
#include "asmpp.h"
|
||
|
#include <stdlib.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
static void asmpp_mapdelete(asmdata_map_t* map, char* name, void* poo) {
|
||
|
}
|
||
|
|
||
|
asmpp_t* asmpp_new(asmpp_systemf_t outputf, void* udata) {
|
||
|
asmpp_t* pp = malloc(sizeof(asmpp_t));
|
||
|
if (pp == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
pp->outputf = outputf;
|
||
|
pp->udata = udata;
|
||
|
|
||
|
pp->defs = asmdata_map_new(543, &asmpp_mapdelete);
|
||
|
if (pp->defs == NULL) {
|
||
|
free(pp);
|
||
|
return NULL;
|
||
|
}
|
||
|
pp->macros = asmdata_map_new(543, &asmpp_mapdelete);
|
||
|
if (pp->macros == NULL) {
|
||
|
free(pp);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
pp->context = NULL;
|
||
|
|
||
|
return pp;
|
||
|
}
|
||
|
|
||
|
void asmpp_delete(asmpp_t* pp) {
|
||
|
if (pp == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
asmdata_map_delete(pp->defs);
|
||
|
asmdata_map_delete(pp->macros);
|
||
|
free(pp);
|
||
|
}
|
||
|
|
||
|
const char* asmpp_contexttypename(asmpp_t* pp, int type) {
|
||
|
switch (type) {
|
||
|
case ASMPP_CONTEXT_OUTER: return "OUTER";
|
||
|
case ASMPP_CONTEXT_MACRO_EXPAND: return "MACRO_EXPAND";
|
||
|
case ASMPP_CONTEXT_MACRO_COLLECT: return "MACRO_COLLECT";
|
||
|
case ASMPP_CONTEXT_IF_EXPAND: return "IF_EXPAND";
|
||
|
case ASMPP_CONTEXT_IF_PARSEONLY: return "IF_PARSEONLY";
|
||
|
default: "Invalid";
|
||
|
}
|
||
|
}
|
||
|
asmpp_context_t* asmpp_enter(asmpp_t* pp, int type, asmpp_macro_t* macro) {
|
||
|
//printf("ENTER CONTEXT %s(#%i) %s\n", asmpp_contexttypename(pp, type), type, macro == NULL ? "(not a macro)" : macro->proto->instrcopy);
|
||
|
asmpp_context_t* c = malloc(sizeof(asmpp_context_t));
|
||
|
if (c == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
c->type = type;
|
||
|
c->macro = macro;
|
||
|
c->locals = NULL;
|
||
|
c->ifvalue = 0;
|
||
|
c->next = pp->context;
|
||
|
pp->context = c;
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
void asmpp_dumpctx(asmpp_t* pp, asmpp_context_t* ctx) {
|
||
|
printf("Context %i %s @%p\n", ctx->type, ctx->macro == NULL ? "(not a macro)" : ctx->macro->proto->instrcopy, ctx);
|
||
|
}
|
||
|
|
||
|
void asmpp_dumpctxs(asmpp_t* pp) {
|
||
|
asmpp_context_t* ctx = pp->context;
|
||
|
while (ctx != NULL) {
|
||
|
asmpp_dumpctx(pp, ctx);
|
||
|
ctx = ctx->next;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void asmpp_exit(asmpp_t* pp, asmpp_context_t* context) {
|
||
|
if (context != pp->context) {
|
||
|
printf("WARNING: Trying to exit from wrong context\n");
|
||
|
asmpp_dumpctx(pp, context);
|
||
|
printf("--- STACK: ---\n");
|
||
|
asmpp_dumpctxs(pp);
|
||
|
return;
|
||
|
}
|
||
|
//printf("EXIT CONTEXT %i %s\n", context->type, context->macro == NULL ? "(not a macro)" : context->macro->proto->instrcopy);
|
||
|
pp->context = context->next;
|
||
|
if (context->locals != NULL) {
|
||
|
asmdata_map_delete(context->locals);
|
||
|
}
|
||
|
free(context);
|
||
|
}
|
||
|
|
||
|
asmpp_macro_t* asmpp_quickmacro(asmpp_t* pp, asmln_t* proto, asmpp_systemf_t systemf) {
|
||
|
asmpp_macro_t* macro = malloc(sizeof(asmpp_macro_t));
|
||
|
if (macro == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
macro->proto = proto;
|
||
|
macro->systemf = systemf;
|
||
|
macro->lines = NULL;
|
||
|
macro->next = NULL;
|
||
|
|
||
|
asmpp_macro_t* oldmacro = asmdata_map_get(pp->macros, proto->instrcopy);
|
||
|
macro->next = oldmacro;
|
||
|
asmdata_map_set(pp->macros, proto->instrcopy, macro);
|
||
|
return macro;
|
||
|
}
|
||
|
|
||
|
asmpp_def_t* asmpp_allocdef(asmpp_t* pp, int t, char* str, asmlnx_t* x) {
|
||
|
asmpp_def_t* d = malloc(sizeof(asmpp_def_t));
|
||
|
if (d == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
d->t = t;
|
||
|
d->value = str;
|
||
|
d->x = x;
|
||
|
return d;
|
||
|
}
|
||
|
|
||
|
asmpp_def_t* asmpp_finddef(asmpp_t* pp, char* name) {
|
||
|
asmpp_context_t* ctx = pp->context;
|
||
|
if (ctx != NULL && ctx->locals != NULL) {
|
||
|
asmpp_def_t* localresult = asmdata_map_get(ctx->locals, name);
|
||
|
if (localresult != NULL) {
|
||
|
return localresult;
|
||
|
}
|
||
|
}
|
||
|
return asmdata_map_get(pp->defs, name);
|
||
|
}
|
||
|
|
||
|
asmpp_macro_t* asmpp_findmacro(asmpp_t* pp, char* name, int nparams) {
|
||
|
asmpp_macro_t* firstm = asmdata_map_get(pp->macros, name);
|
||
|
asmpp_macro_t* m = firstm;
|
||
|
while (m != NULL) {
|
||
|
if (m->proto->nparams == nparams) {
|
||
|
return m;
|
||
|
}
|
||
|
m = m->next;
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
char* asmdata_strdup_(const char* str);
|
||
|
|
||
|
char* asmpp_strdupnull_(char* str) {
|
||
|
return (str == NULL) ? NULL : asmdata_strdup_(str);
|
||
|
}
|
||
|
|
||
|
/*int asmpp_copyxln_param(asmpp_t* pp, int* paramtp, char** strp, asmlnx_t** xp, bool expandparams) {
|
||
|
int t = *paramtp;
|
||
|
if (t == ASMT_TOKENTYPE_NAME) {
|
||
|
asmpp_def_t* def = asmpp_finddef(pp, *strp);
|
||
|
if (def != NULL) {
|
||
|
*paramtp = def->t;
|
||
|
*strp = asmpp_strdupnull_(def->value);
|
||
|
}
|
||
|
}
|
||
|
*strp = asmpp_strdupnull_(*strp); // NOTE: This must handle NULLs!
|
||
|
}*/
|
||
|
|
||
|
#define ASMPP_COPYXLN_EXPAND(dstt,dstc,dstx,srct,srcc,srcx,xp) \
|
||
|
do { \
|
||
|
bool _done = false; \
|
||
|
if ((xp) && (srct) == ASMT_TOKENTYPE_NAME) { \
|
||
|
asmpp_def_t* _def = asmpp_finddef(pp, srcc); \
|
||
|
if (_def != NULL) { \
|
||
|
_done = true; \
|
||
|
dstt = _def->t; \
|
||
|
dstc = asmpp_strdupnull_(_def->value); \
|
||
|
dstx = asmpp_copyxln_x(pp,_def->x,xp); \
|
||
|
} \
|
||
|
} \
|
||
|
if (!_done) { \
|
||
|
dstt = srct; \
|
||
|
dstc = asmpp_strdupnull_(srcc); \
|
||
|
dstx = asmpp_copyxln_x(pp,srcx,xp); \
|
||
|
} \
|
||
|
} while (0)
|
||
|
|
||
|
asmlnx_t* asmpp_copyxln_x(asmpp_t* pp, asmlnx_t* x, bool expandparams);
|
||
|
asmlnx_t* asmpp_copyxln_x(asmpp_t* pp, asmlnx_t* x, bool expandparams) {
|
||
|
if (x == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
asmlnx_t* result = calloc(sizeof(asmlnx_t),1);
|
||
|
if (result == NULL) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
ASMPP_COPYXLN_EXPAND(result->lhstype,result->lhscopy,result->lhsx,x->lhstype,x->lhscopy,x->lhsx,expandparams);
|
||
|
//printf("Converted '%s' to '%s'\n", x->lhscopy, result->lhscopy);
|
||
|
result->opcopy = asmpp_strdupnull_(x->opcopy);
|
||
|
ASMPP_COPYXLN_EXPAND(result->rhstype,result->rhscopy,result->rhsx,x->rhstype,x->rhscopy,x->rhsx,expandparams);
|
||
|
//printf("Converted '%s' to '%s'\n", x->lhscopy, result->lhscopy);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
asmln_t* asmpp_copyxln(asmpp_t* pp, asmln_t* ln, bool expandparams) {
|
||
|
asmln_t* xln = malloc(sizeof(asmln_t));
|
||
|
xln->labelcopy = asmpp_strdupnull_(ln->labelcopy);
|
||
|
xln->instrcopy = asmpp_strdupnull_(ln->instrcopy);
|
||
|
xln->nparams = ln->nparams;
|
||
|
xln->commentcopy = asmpp_strdupnull_(ln->commentcopy);
|
||
|
xln->errorcopy = asmpp_strdupnull_(ln->errorcopy);
|
||
|
int i;
|
||
|
for (i = 0; i < xln->nparams; i++) {
|
||
|
ASMPP_COPYXLN_EXPAND(xln->paramtype[i],xln->paramcopy[i],xln->paramx[i],ln->paramtype[i],ln->paramcopy[i],ln->paramx[i],expandparams);
|
||
|
|
||
|
}
|
||
|
return xln;
|
||
|
}
|
||
|
|
||
|
bool asmdata_streq_(const char* a, const char* b);
|
||
|
|
||
|
/* Returns true unless compilation is disabled by an #if or equivalent context. */
|
||
|
bool asmpp_ifcompiling(asmpp_t* pp) {
|
||
|
asmpp_context_t* ctx = pp->context;
|
||
|
while (ctx != NULL) {
|
||
|
if (ctx->type == ASMPP_CONTEXT_IF_EXPAND && ctx->ifvalue == 0) {
|
||
|
return false;
|
||
|
}
|
||
|
ctx = ctx->next;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
int asmpp_expand(asmpp_t* pp, asmln_t* ln) {
|
||
|
if (pp->context != NULL && pp->context->type == ASMPP_CONTEXT_MACRO_COLLECT) {
|
||
|
//printf("GOT MACRO LINE %s\n", ln->instrcopy);
|
||
|
if (asmdata_streq_(ln->instrcopy, "%endmacro")) {
|
||
|
asmpp_exit(pp, pp->context);
|
||
|
return 0;
|
||
|
}
|
||
|
asmpp_lines_t* ll = pp->context->macro->lines;
|
||
|
while (ll != NULL && ll->next != NULL) {
|
||
|
ll = ll->next;
|
||
|
}
|
||
|
//printf("Found end\n");
|
||
|
asmpp_lines_t* nl = malloc(sizeof(asmpp_lines_t));
|
||
|
if (nl == NULL) {
|
||
|
return -1;
|
||
|
}
|
||
|
nl->line = asmpp_copyxln(pp, ln, false);
|
||
|
nl->next = NULL;
|
||
|
if (ll != NULL) {
|
||
|
ll->next = nl;
|
||
|
} else {
|
||
|
pp->context->macro->lines = nl;
|
||
|
}
|
||
|
//printf("Done\n");
|
||
|
return 0;
|
||
|
}
|
||
|
if (asmdata_streq_(ln->instrcopy, "%endif")) {
|
||
|
asmpp_exit(pp, pp->context);
|
||
|
return 0;
|
||
|
} else if (!asmpp_ifcompiling(pp) && !asmdata_streq_(ln->instrcopy, "%else") && !asmdata_streq_(ln->instrcopy, "%elseif")) {
|
||
|
if (asmdata_streq_(ln->instrcopy, "%if")) {
|
||
|
asmpp_enter(pp, ASMPP_CONTEXT_IF_PARSEONLY, NULL);
|
||
|
}
|
||
|
return 0;
|
||
|
} else if (asmdata_streq_(ln->labelcopy, "%macro")) {
|
||
|
asmpp_macro_t* mac = asmpp_quickmacro(pp, asmpp_copyxln(pp, ln, false), NULL);
|
||
|
//printf("Got macro '%s'\n", ln->instrcopy);
|
||
|
asmpp_enter(pp, ASMPP_CONTEXT_MACRO_COLLECT, mac);
|
||
|
return 0;
|
||
|
} else if (asmdata_streq_(ln->labelcopy, "%def")) {
|
||
|
asmln_t* defln = asmpp_copyxln(pp, ln, true);
|
||
|
asmpp_def_t* def = asmpp_allocdef(pp, defln->paramtype[0], defln->paramcopy[0], defln->paramx[0]);
|
||
|
asmdata_map_set(pp->defs, defln->instrcopy, def);
|
||
|
return 0;
|
||
|
}
|
||
|
//dump_asmln(stdout, ln);
|
||
|
asmln_t* xln = asmpp_copyxln(pp, ln, true);
|
||
|
//dump_asmln(stdout, xln);
|
||
|
if (asmdata_streq_(xln->instrcopy, "%if")) {
|
||
|
uint64_t val;
|
||
|
char* err;
|
||
|
int sig;
|
||
|
if (asmpp_calc(pp, xln->paramtype[0], xln->paramcopy[0], xln->paramx[0], &val, &err, &sig)) {
|
||
|
//printf("Got if with value %llu\n", val);
|
||
|
} else {
|
||
|
printf("Calculation of the if factor failed: %s\n", err == NULL ? "(error string was not set)" : err);
|
||
|
}
|
||
|
asmpp_enter(pp, ASMPP_CONTEXT_IF_EXPAND, NULL);
|
||
|
pp->context->ifvalue = val;
|
||
|
if (pp->context->next != NULL) {
|
||
|
pp->context->locals = pp->context->next->locals;
|
||
|
}
|
||
|
return 0;
|
||
|
} else if (asmdata_streq_(xln->instrcopy, "%elseif")) {
|
||
|
uint64_t val;
|
||
|
char* err;
|
||
|
int sig;
|
||
|
if (pp->context->ifvalue != 0) {
|
||
|
return 0;
|
||
|
} else if (asmpp_calc(pp, xln->paramtype[0], xln->paramcopy[0], xln->paramx[0], &val, &err, &sig)) {
|
||
|
//printf("Got if with value %llu\n", val);
|
||
|
} else {
|
||
|
printf("Calculation of the if factor failed: %s\n", err == NULL ? "(error string was not set)" : err);
|
||
|
}
|
||
|
pp->context->ifvalue = val;
|
||
|
return 0;
|
||
|
} else if (asmdata_streq_(xln->instrcopy, "%else")) {
|
||
|
pp->context->ifvalue = (pp->context->ifvalue == 0) ? 1 : 0;
|
||
|
return 0;
|
||
|
}
|
||
|
asmpp_macro_t* m = (xln->instrcopy == NULL) ? NULL : asmpp_findmacro(pp, xln->instrcopy, xln->nparams);
|
||
|
int nexpanded = 0;
|
||
|
if (m != NULL) {
|
||
|
asmpp_context_t* ctx = asmpp_enter(pp, ASMPP_CONTEXT_MACRO_EXPAND, m);
|
||
|
if (m->systemf != NULL) {
|
||
|
asmpp_systemf_t sysf = m->systemf;
|
||
|
nexpanded = sysf(pp, xln);
|
||
|
} else {
|
||
|
ctx->locals = asmdata_map_new(23, &asmpp_mapdelete);
|
||
|
int argi;
|
||
|
for (argi = 0; argi < m->proto->nparams; argi++) {
|
||
|
if (m->proto->paramtype[argi] == ASMT_TOKENTYPE_OPENBR /*&& m->proto->paramx[argi]->lhstype == ASMT_TOKENTYPE_NAME && m->proto->paramx[argi]->rhstype == ASMT_TOKENTYPE_NAME && xln->paramtype[argi] == ASMT_TOKENTYPE_OPENBR*/) {
|
||
|
asmpp_def_t* deflhs = asmpp_allocdef(pp, xln->paramx[argi]->lhstype, xln->paramx[argi]->lhscopy, xln->paramx[argi]->lhsx);
|
||
|
asmpp_def_t* defop = asmpp_allocdef(pp, ASMT_TOKENTYPE_NAME, xln->paramx[argi]->opcopy, NULL);
|
||
|
asmpp_def_t* defrhs = asmpp_allocdef(pp, xln->paramx[argi]->rhstype, xln->paramx[argi]->rhscopy, xln->paramx[argi]->rhsx);
|
||
|
asmdata_map_set(ctx->locals, m->proto->paramx[argi]->lhscopy, deflhs);
|
||
|
//printf("Mapped '%s' to '%s'\n", m->proto->paramx[argi]->lhscopy, deflhs->value);
|
||
|
asmdata_map_set(ctx->locals, m->proto->paramx[argi]->opcopy, defop);
|
||
|
asmdata_map_set(ctx->locals, m->proto->paramx[argi]->rhscopy, defrhs);
|
||
|
} else {
|
||
|
asmpp_def_t* def = asmpp_allocdef(pp, xln->paramtype[argi], xln->paramcopy[argi], xln->paramx[argi]);
|
||
|
asmdata_map_set(ctx->locals, m->proto->paramcopy[argi] == NULL ? "?" : m->proto->paramcopy[argi], def);
|
||
|
}
|
||
|
}
|
||
|
asmpp_lines_t* l = m->lines;
|
||
|
|
||
|
while (l != NULL) {
|
||
|
nexpanded += asmpp_expand(pp, l->line);
|
||
|
l = l->next;
|
||
|
}
|
||
|
}
|
||
|
asmpp_exit(pp, ctx);
|
||
|
} else {
|
||
|
asmpp_systemf_t outputf = pp->outputf;
|
||
|
nexpanded = outputf(pp, xln);
|
||
|
if (nexpanded < 0) {
|
||
|
return -1;
|
||
|
//printf("TODO: exit cleanly?\n");
|
||
|
//exit(-1);
|
||
|
}
|
||
|
}
|
||
|
asmln_delete(xln);
|
||
|
return nexpanded;
|
||
|
}
|
||
|
|
||
|
bool asmpp_binop(asmpp_t* pp, uint64_t lhsresult, int lhssig, char* op, uint64_t rhsresult, int rhssig, uint64_t* resultp, char** errp, int* signp) {
|
||
|
if (asmdata_streq_(op, "&&")) {
|
||
|
if (lhsresult && rhsresult) {
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
} else if (asmdata_streq_(op, "||")) {
|
||
|
if (lhsresult || rhsresult) {
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
} else {
|
||
|
*errp = "Unknown binary operator";
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
long long asmdata_atoll_(const char* a);
|
||
|
bool asmpp_calc(asmpp_t* pp, int t, char* str, asmlnx_t* x, uint64_t* resultp, char** errp, int* sigp) {
|
||
|
char* err = NULL;
|
||
|
int sig = 0;
|
||
|
uint64_t result = 0;
|
||
|
bool calculated = false;
|
||
|
|
||
|
switch (t) {
|
||
|
case ASMT_TOKENTYPE_NUMBER:
|
||
|
result = asmdata_atoll_(str); // This handles hex/binary, TODO: foats
|
||
|
printf("Number '%s' -> %d (0x%x 0b%b)\n", str, result, result, result);
|
||
|
calculated = true;
|
||
|
break;
|
||
|
case ASMT_TOKENTYPE_OPENBR:
|
||
|
uint64_t lhsresult;
|
||
|
char* lhserror;
|
||
|
int lhssig;
|
||
|
bool lhscalc = asmpp_calc(pp, x->lhstype, x->lhscopy, x->lhsx, &lhsresult, &lhserror, &lhssig);
|
||
|
if (!lhscalc) {
|
||
|
err = lhserror;
|
||
|
goto retfromcalc;
|
||
|
}
|
||
|
uint64_t rhsresult;
|
||
|
char* rhserror;
|
||
|
int rhssig;
|
||
|
bool rhscalc = asmpp_calc(pp, x->rhstype, x->rhscopy, x->rhsx, &rhsresult, &rhserror, &rhssig);
|
||
|
if (!rhscalc) {
|
||
|
err = rhserror;
|
||
|
goto retfromcalc;
|
||
|
}
|
||
|
calculated = asmpp_binop(pp, lhsresult, lhssig, x->opcopy, rhsresult, rhssig, &result, &err, &sig);
|
||
|
break;
|
||
|
default:
|
||
|
calculated = false;
|
||
|
}
|
||
|
|
||
|
retfromcalc:
|
||
|
if (resultp != NULL) {
|
||
|
*resultp = result;
|
||
|
}
|
||
|
if (errp != NULL) {
|
||
|
*errp = err;
|
||
|
}
|
||
|
if (sigp != NULL) {
|
||
|
*sigp = sig;
|
||
|
}
|
||
|
return calculated;
|
||
|
}
|