Files
compiler-the-translators/src/intermediate_code.c
2025-05-06 23:00:50 -04:00

637 lines
17 KiB
C

/* Intermediate Code */
/* The Translators - Spring 2025 */
#include "intermediate_code.h"
Stack *S_Init() {
Stack *s = calloc(1, sizeof(*s));
return s;
}
void S_Free(Stack *s) {
// since we are not responsible for the values we can just pop until
// NULL
for (void *p = S_Pop(s); p != NULL; p = S_Pop(s));
free(s);
}
void S_Push(Stack *s, void *v, int i) {
__Node *n = calloc(1, sizeof(*n));
n->v = v;
n->next = s->n;
s->n = n;
s->w = i;
s->size = s->size + 1;
}
void *S_Pop(Stack *s) {
if (s == NULL || S_IsEmpty(s)) {
return NULL;
}
__Node *node = s->n;
s->n = node->next;
s->size = s->size - 1;
void *r = node->v;
free(node);
return r;
}
void *S_Peek(Stack *s) {
if (s == NULL || S_IsEmpty(s)) {
return NULL;
}
return s->n->v;
}
bool S_IsEmpty(Stack *s) {
if (s == NULL || s->size == 0) {
return true;
}
return false;
}
int S_Size(Stack *s) {
if (s == NULL || S_IsEmpty(s)) {
return 0;
}
return s->size;
}
void S_Merge(Stack *list) {
Stack *s1 = S_Pop(list);
Stack *s2 = S_Peek(list);
if (s1 == NULL) {
return;
}
if (s2 == NULL) {
S_Push(list, s1, 0);
return;
}
for (Instruction *i = S_Pop(s1); i; i = S_Pop(s1)) {
S_Push(s2, i, 1);
}
}
void emit_backpatch(Stack *s, int l) {
for (Instruction *i = S_Pop(s); i; i = S_Pop(s)) {
i->label = l;
}
}
//_______________________________________________________________________
char *temp = NULL;
/*
TODO: this is here to bring your attention to the comment bellow.
check if start is NULL if it is assign it to the start globle variable
otherwise make it next of current and set cur to your instruction.
*/
void emit_push_all(Stack *s) {
for (Instruction *i = S_Pop(s); i; i = S_Pop(s)) {
current->next = i;
i->prev = current;
i->index = current->index + 1;
current = i;
current->next = NULL;
}
}
void emit_detach() {
current = current->prev;
current->next = NULL;
}
void backpatch(Stack *s, int l) {
while (!S_IsEmpty(s)) {
Instruction *i = S_Pop(s);
set_label(i, l);
}
}
TNodeOrConst *getOperand1(Instruction *i) {
return i->operand1;
}
TNodeOrConst *getOperand2(Instruction *i) {
return i->operand2;
}
TableNode *getResult(Instruction *i) {
return i->result;
}
Op getOp(Instruction *i) {
return i->opcode;
}
int getLabel(Instruction *i) {
return i->label;
}
int get_index(Instruction *i) {
return i->index;
}
void set_label(Instruction *i, int label) {
i->label = label;
}
bool isConst(TNodeOrConst *tnc) {
return tnc->d != NODE;
}
TNodeOrConst *tn_or_const(Discriminant d, void *tnc) {
TNodeOrConst *count = calloc(1, sizeof(*count));
count->d = d;
count->tnc_union = calloc(1, sizeof(*count->tnc_union));
switch (d) {
case NODE:
count->tnc_union->node = tnc;
break;
case ADDRESS:
count->tnc_union->address = tnc;
break;
case STRING:
count->tnc_union->string = tnc;
break;
case INTEGER:
count->tnc_union->integer = *(int *)tnc;
break;
case CHARACTER:
count->tnc_union->character = *(char *)tnc;
break;
case BOOLEAN:
count->tnc_union->Boolean = *(uint_least8_t *)tnc;
break;
}
return count;
}
static void emit_helper(void) {
Instruction *inst = calloc(1, sizeof(*inst));
if (begin == NULL) {
begin = current = inst;
current->index = 1;
} else {
current->next = inst;
inst->prev = current;
inst->index = current->index + 1;
current = inst;
}
}
void emit_function_dec(TableNode * name){
emit_helper();
current->opcode = E_FUNC_DEC;
current->result = name;
}
void emit_binary_op(
Op op,
TableNode *result,
TNodeOrConst *arg1,
TNodeOrConst *arg2
) {
emit_helper();
current->opcode = op;
// TODO: create temp and remove result from param list
current->result = result;
current->operand1 = arg1;
current->operand2 = arg2;
}
void emit_goto(int i) {
emit_helper();
current->opcode = E_GOTO;
current->label = i;
}
void emit_unary_op(Op op, TableNode *result, TNodeOrConst *arg) {
emit_helper();
current->opcode = op;
current->result = result;
current->operand1 = arg;
}
void emit_assignment(TableNode *target, TNodeOrConst *source) {
emit_helper();
current->opcode = E_ASSIGN;
current->result = target;
current->operand1 = source;
}
char *get_string(TNodeOrConst *tc) {
char *s;
switch (tc->d) {
case NODE:
return getName(tc->tnc_union->node);
case ADDRESS:
return strdup("null");
case STRING:
return tc->tnc_union->string;
case INTEGER:
s = calloc(10, sizeof(char));
sprintf(s, "%d", tc->tnc_union->integer);
return s;
case CHARACTER:
s = calloc(2, sizeof(char));
sprintf(s, "%c", tc->tnc_union->character);
return s;
case BOOLEAN:
if (tc->tnc_union->Boolean) {
return strdup("true");
}
return strdup("false");
}
}
void emit_label(int label) {
emit_helper();
current->opcode = E_LABEL;
current->label = label;
}
void emit_jump(int label) {
emit_helper();
current->opcode = E_GOTO;
current->label = label;
}
void emit_conditional_jump(Op condition, int label, ...) {
// when this instruction is a conditional jump then the imput looks like (Op, int, TNodeOrConst *).
// when the inst is a cond with a Relational operation then the input looks like (Op, int, TNodeOrConst *, TNodeOrConst *)
emit_helper();
va_list argptr;
va_start(argptr, label);
current->opcode = condition;
current->label = label;
TNodeOrConst *n1;
TNodeOrConst *n2;
switch (condition) {
case E_IF_X_TRUE:
case E_IF_X_FALSE:
n1 = va_arg(argptr, TNodeOrConst *);
current->operand1 = n1;
break;
case E_LESS_THAN:
case E_EQUAL_TO:
n1 = va_arg(argptr, TNodeOrConst *);
n2 = va_arg(argptr, TNodeOrConst *);
current->operand1 = n1;
current->operand2 = n2;
break;
}
va_end(argptr);
}
void emit_function_start(TableNode *name) {
emit_helper();
current->opcode = E_FUNC_START;
current->result = name;
}
void emit_parameter(TNodeOrConst *param) {
emit_helper();
current->opcode = E_PARAM;
current->operand1 = param;
}
void emit_function_call(
TableNode *result,
int param_count,
TNodeOrConst *name) {
emit_helper();
current->opcode = E_CALL;
current->operand1 = name;
current->operand2 = tn_or_const(INTEGER, &param_count);
current->result = result;
}
void emit_return(TNodeOrConst *value) {
emit_helper();
current->opcode = E_RETURN;
current->operand1 = value;
}
void emit_reserve(TableNode *result, TNodeOrConst *size) {
// this needs to change
// we need to take a int
/*
emit_binary_op(E_MUL, result,
*/
emit_parameter(size);
emit_function_call(result, 1, tn_or_const(NODE, look_up(cur, "reserve")));
}
void emit_release(TableNode *pointer) {
emit_parameter(tn_or_const(NODE, pointer));
emit_function_call(pointer, 1, tn_or_const(NODE, look_up(cur, "release")));
}
void emit_deref_right(TableNode *x, TNodeOrConst *y) {
emit_helper();
current->opcode = E_DEREF_RIGHT;
current->result = x;
current->operand1 = y;
}
void emit_deref_left(TableNode *x, TNodeOrConst *y) {
emit_helper();
current->opcode = E_DEREF_LEFT;
current->result = x;
current->operand1 = y;
}
void emit_address_of(TableNode *x, TNodeOrConst *y) {
emit_helper();
current->opcode = E_ADDRESS_OF;
current->result = x;
current->operand1 = y;
}
void emit_field_access(TableNode *result, TNodeOrConst *record, int offset){
emit_helper();
current->opcode = E_DEREF_RIGHT;
current->result = result;
current->operand1 = record;
current->operand2 = tn_or_const(INTEGER, &offset);
}
void emit_array_access(Op op, TableNode *result, TNodeOrConst *array, TNodeOrConst *index) {
emit_helper();
current->opcode = op;
current->result = result;
current->operand1 = array;
current->operand2 = index;
// TODO: Still don't know what to do with the dimentions
}
void emit_bounds_check(TNodeOrConst *index, TNodeOrConst *arr) {
/*
{[string: 5]
.
.
s:= reserve s(5);
s(0) := 'H';
s(1) := 'e';
.
.
s._0 num of dims Known at compile time
s._1 size Known at run time
s._1 int | 1 byte
+-------+---+---+---+---+---+
| 5 | H | e | l | l | o |
+-------+---+---+---+---+---+
size
^
|
p
s._0 ok
s._1 ok
s._2 not ok
t_0 is index
t_1 = *(int *)p = s._1
if t_0 < 0 GOTO ERROR
if t_0 < s._1 GOTO access array
GOTO ERROR
*/
/* We need a label ERROR to jump to
emit_conditional_jump(E_LESS_THAN, );
emit_conditional_jump(E_LESS_THAN, );
emit_jump();
*/
}
// * Implement temp variable generator function that produces unique names (t1, t2, etc.)
int label_gen() {
label_count++;
return label_count;
}
void emit_as_file(FILE *out_file, Instruction *i) {
if (out_file == NULL) {
fprintf(stderr, "Error: output file is NULL\n");
return;
}
if (i == NULL) {
return;
}
switch (i->opcode) {
case E_FUNC_DEC:
fprintf(out_file,
"%4.d: func_dec : %s\n",
i->index,
getName(i->result));
break;
case E_FUNC_START:
fprintf(out_file,
"%4.d: func : %s\n",
i->index,
getName(i->result));
break;
case E_LABEL:
fprintf(out_file,
"%4.d: Label : %d\n",
i->index,
i->label);
break;
case E_ADD:
fprintf(out_file,
"%4.d: %s = %s + %s\n",
i->index,
getName(i->result),
get_string(i->operand1),
get_string(i->operand2));
break;
case E_SUB:
fprintf(out_file,
"%4.d: %s = %s - %s\n",
i->index,
getName(i->result),
get_string(i->operand1),
get_string(i->operand2));
break;
case E_MUL:
fprintf(out_file,
"%4.d: %s = %s * %s\n",
i->index,
getName(i->result),
get_string(i->operand1),
get_string(i->operand2));
break;
case E_DIV:
fprintf(out_file,
"%4.d: %s = %s / %s\n",
i->index,
getName(i->result),
get_string(i->operand1),
get_string(i->operand2));
break;
case E_MOD:
fprintf(out_file,
"%4.d: %s = %s %% %s\n",
i->index,
getName(i->result),
get_string(i->operand1),
get_string(i->operand2));
break;
case E_OR:
fprintf(out_file,
"%4.d: %s = %s | %s\n",
i->index,
getName(i->result),
get_string(i->operand1),
get_string(i->operand2));
break;
case E_AND:
fprintf(out_file,
"%4.d: %s = %s & %s\n",
i->index,
getName(i->result),
get_string(i->operand1),
get_string(i->operand2));
break;
case E_NEG:
fprintf(out_file,
"%4.d: %s = -%s\n",
i->index,
getName(i->result),
get_string(i->operand1));
break;
case E_NOT:
fprintf(out_file,
"%4.d: %s = !%s\n",
i->index,
getName(i->result),
get_string(i->operand1));
break;
case E_ASSIGN:
fprintf(out_file,
"%4.d: %s = %s\n",
i->index,
getName(i->result),
get_string(i->operand1));
break;
case E_GOTO:
fprintf(out_file,
"%4.d: GOTO : %d\n",
i->index,
i->label);
break;
case E_IF_X_TRUE:
fprintf(out_file,
"%4.d: if %s True GOTO %d\n",
i->index,
get_string(i->operand1),
i->label);
break;
case E_IF_X_FALSE:
fprintf(out_file,
"%4.d: if %s False GOTO %d\n",
i->index,
get_string(i->operand1),
i->label);
break;
case E_LESS_THAN:
// this feels wrong I need to TODO: this
fprintf(out_file,
"%4.d: %s = %s < %s\n",
i->index,
getName(i->result),
get_string(i->operand1),
get_string(i->operand2));
break;
case E_EQUAL_TO:
// this feels wrong I need to TODO: this
fprintf(out_file,
"%4.d: %s = %s == %s\n",
i->index,
getName(i->result),
get_string(i->operand1),
get_string(i->operand2));
break;
case E_CALL:
fprintf(out_file,
"%4.d: call : %s %s\n",
i->index,
get_string(i->operand1),
get_string(i->operand2));
break;
case E_PARAM:
fprintf(out_file,
"%4.d: param %s \n",
i->index,
get_string(i->operand1));
break;
case E_RETURN:
fprintf(out_file,
"%4.d: return : %s\n",
i->index,
get_string(i->operand1));
break;
case E_INDEX_COPY_RIGHT:
fprintf(out_file,
"%4.d: %s = %s[ %s ]\n",
i->index,
getName(i->result),
get_string(i->operand1),
get_string(i->operand2));
break;
case E_INDEX_COPY_LEFT:
fprintf(out_file,
"%4.d: %s[ %s ] = %s\n",
i->index,
getName(i->result),
get_string(i->operand2),
get_string(i->operand1));
break;
case E_ADDRESS_OF:
fprintf(out_file,
"%4.d: %s = &%s\n",
i->index,
getName(i->result),
get_string(i->operand1));
break;
case E_DEREF_RIGHT:
fprintf(out_file,
"%4.d: %s = *((char * )%s + %s)\n",
i->index,
getName(i->result),
get_string(i->operand1),
get_string(i->operand2)
);
break;
case E_DEREF_LEFT:
fprintf(out_file,
"%4.d: *%s = %s\n",
i->index,
getName(i->result),
get_string(i->operand1));
break;
}
emit_as_file(out_file, i->next);
}
TableNode *getTN(TNodeOrConst *tnc) {
if (tnc->d == NODE) {
return tnc->tnc_union->node;
}
return NULL;
}
int getConst(TNodeOrConst *tnc) {
if (tnc->d == INTEGER) {
return tnc->tnc_union->integer;
} else if (tnc->d == CHARACTER) {
return tnc->tnc_union->character;
} else if (tnc->d == BOOLEAN) {
return tnc->tnc_union->Boolean;
} else if (tnc->d == ADDRESS) {
return 0;
}
return -1;
}