Files
cground/src/interpreter.c

184 lines
5.8 KiB
C
Raw Normal View History

2025-11-23 18:34:30 +11:00
#include "interpreter.h"
#include "parser.h"
#include "types.h"
#include "include/uthash.h"
#include <stdlib.h>
int currentInstruction = 0;
void runtimeError(GroundRuntimeError error, char* what, GroundInstruction* where, int whereLine) {
printf("Ground runtime error:\n ErrorType: ");
switch (error) {
case ARG_TYPE_MISMATCH: {
printf("ArgTypeMismatch");
break;
}
case TOO_FEW_ARGS: {
printf("TooFewArgs");
break;
}
case TOO_MANY_ARGS: {
printf("TooManyArgs");
break;
}
default:
case FIXME: {
printf("FIXME (please report issue to https://chsp.au/ground/cground)");
}
}
printf("\n");
if (what != NULL) {
printf(" ErrorContext: %s\n", what);
}
if (where != NULL) {
printf(" ErrorInstruction: ");
printGroundInstruction(where);
printf("\n");
}
if (whereLine > -1) {
printf(" ErrorLine: %d\n", whereLine + 1);
}
exit(1);
}
2025-11-23 19:18:10 +11:00
void addLabel(GroundLabel **head, const char *id, int data) {
GroundLabel* item = malloc(sizeof(GroundLabel));
snprintf(item->id, MAX_ID_LEN, "%s", id);
item->lineNum = data;
HASH_ADD_STR(*head, id, item);
}
GroundLabel* findLabel(GroundLabel* head, const char *id) {
GroundLabel *item;
HASH_FIND_STR(head, id, item);
return item;
}
void deleteLabel(GroundLabel** head, GroundLabel *item) {
HASH_DEL(*head, item);
free(item);
}
2025-11-23 19:59:22 +11:00
void addVariable(GroundVariable **head, const char *id, GroundValue data) {
GroundVariable* item = malloc(sizeof(GroundVariable));
snprintf(item->id, MAX_ID_LEN, "%s", id);
item->value = data;
HASH_ADD_STR(*head, id, item);
}
GroundVariable* findVariable(GroundVariable* head, const char *id) {
GroundVariable *item;
HASH_FIND_STR(head, id, item);
return item;
}
void deleteVariable(GroundVariable** head, GroundVariable *item) {
HASH_DEL(*head, item);
free(item);
2025-11-23 19:18:10 +11:00
}
2025-11-23 18:34:30 +11:00
void interpretGroundProgram(GroundProgram* in) {
2025-11-23 19:59:22 +11:00
GroundLabel* labels = NULL;
GroundVariable* variables = NULL;
GroundScope scope;
scope.labels = &labels;
scope.variables = &variables;
2025-11-23 18:34:30 +11:00
// Preprocess all labels
for (int i = 0; i < in->size; i++) {
if (in->instructions[i].type == CREATELABEL) {
2025-11-23 19:59:22 +11:00
addLabel(scope.labels, in->instructions[i].args.args[0].value.refName, i);
2025-11-23 18:34:30 +11:00
}
}
while (currentInstruction < in->size) {
2025-11-23 19:59:22 +11:00
interpretGroundInstruction(&in->instructions[currentInstruction], &scope);
2025-11-23 18:34:30 +11:00
currentInstruction++;
}
}
2025-11-23 19:59:22 +11:00
void interpretGroundInstruction(GroundInstruction* in, GroundScope* scope) {
2025-11-23 18:34:30 +11:00
switch (in->type) {
// We can safely ignore any CREATELABEL instructions, these have been preprocessed
case CREATELABEL: {
break;
}
2025-11-23 19:18:10 +11:00
case IF: {
if (in->args.length < 2) {
runtimeError(TOO_FEW_ARGS, "Expecting 2 arguments", in, currentInstruction);
}
if (in->args.length > 2) {
runtimeError(TOO_MANY_ARGS, "Expecting 2 arguments", in, currentInstruction);
}
2025-11-23 19:59:22 +11:00
if (in->args.args[0].type != VALUE || in->args.args[0].value.value.type != BOOL) {
2025-11-23 19:18:10 +11:00
runtimeError(ARG_TYPE_MISMATCH, "Expecting a bool for arg 0", in, currentInstruction);
}
2025-11-23 19:59:22 +11:00
if (in->args.args[1].type != LINEREF) {
2025-11-23 19:18:10 +11:00
runtimeError(ARG_TYPE_MISMATCH, "Expecting a LineRef for arg 1", in, currentInstruction);
}
if (in->args.args[0].value.value.data.boolVal) {
2025-11-23 19:59:22 +11:00
GroundLabel* label = findLabel(*scope->labels, in->args.args[1].value.refName);
2025-11-23 19:18:10 +11:00
if (label) {
currentInstruction = label->lineNum;
} else {
runtimeError(UNKNOWN_LABEL, NULL, in, currentInstruction);
}
}
2025-11-23 19:59:22 +11:00
break;
2025-11-23 19:18:10 +11:00
}
case JUMP: {
if (in->args.length < 1) {
runtimeError(TOO_FEW_ARGS, "Expecting 1 argument", in, currentInstruction);
}
if (in->args.length > 1) {
runtimeError(TOO_MANY_ARGS, "Expecting 1 argument", in, currentInstruction);
}
if (in->args.args[0].type != LINEREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a LineRef for arg 1", in, currentInstruction);
}
2025-11-23 19:59:22 +11:00
GroundLabel* label = findLabel(*scope->labels, in->args.args[0].value.refName);
2025-11-23 19:18:10 +11:00
if (label) {
currentInstruction = label->lineNum;
} else {
runtimeError(UNKNOWN_LABEL, NULL, in, currentInstruction);
}
2025-11-23 19:59:22 +11:00
break;
2025-11-23 19:18:10 +11:00
}
case END: {
2025-11-23 19:59:22 +11:00
if (in->args.length < 1 || in->args.args[0].type != VALUE || in->args.args[0].value.value.type != INT) {
2025-11-23 19:18:10 +11:00
exit(0);
} else {
2025-11-23 19:59:22 +11:00
exit(in->args.args[0].value.value.data.intVal);
2025-11-23 19:18:10 +11:00
}
break;
}
2025-11-23 18:34:30 +11:00
case PRINT: {
if (in->args.length < 1) {
runtimeError(TOO_FEW_ARGS, NULL, in, currentInstruction);
}
for (int i = 0; i < in->args.length; i++) {
printGroundArg(&in->args.args[i]);
printf(" ");
}
break;
}
case PRINTLN: {
if (in->args.length < 1) {
runtimeError(TOO_FEW_ARGS, NULL, in, currentInstruction);
}
for (int i = 0; i < in->args.length; i++) {
printGroundArg(&in->args.args[i]);
printf(" ");
}
printf("\n");
break;
}
default: {
runtimeError(FIXME, "Currently unimplemented instruction", in, currentInstruction);
}
}
}