Files
cground/src/interpreter.c

973 lines
42 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>
2025-11-23 21:23:04 +11:00
#include <string.h>
2025-11-23 18:34:30 +11:00
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;
}
2025-11-23 21:23:04 +11:00
case UNKNOWN_LABEL: {
printf("UnknownLabel");
break;
}
case UNKNOWN_VARIABLE: {
printf("UnknownVariable");
break;
}
2025-12-01 10:15:37 +11:00
case LIST_ERROR: {
printf("ListError");
break;
}
2025-11-23 18:34:30 +11:00
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
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-24 10:15:53 +11:00
void addLabel(GroundLabel **head, const char *id, int data) {
GroundLabel* label = findLabel(*head, id);
if (label) {
deleteLabel(head, label);
}
GroundLabel* item = malloc(sizeof(GroundLabel));
2025-11-23 19:59:22 +11:00
snprintf(item->id, MAX_ID_LEN, "%s", id);
2025-11-24 10:15:53 +11:00
item->lineNum = data;
2025-11-23 19:59:22 +11:00
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-24 10:15:53 +11:00
void addVariable(GroundVariable **head, const char *id, GroundValue data) {
GroundVariable* variable = findVariable(*head, id);
if (variable) {
2025-12-01 10:15:37 +11:00
freeGroundValue(&variable->value);
2025-11-24 10:15:53 +11:00
deleteVariable(head, variable);
}
GroundVariable* item = malloc(sizeof(GroundVariable));
snprintf(item->id, MAX_ID_LEN, "%s", id);
item->value = data;
HASH_ADD_STR(*head, id, item);
}
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-24 10:15:53 +11:00
interpretGroundInstruction(in->instructions[currentInstruction], &scope);
2025-11-23 18:34:30 +11:00
currentInstruction++;
}
}
2025-11-24 10:15:53 +11:00
void interpretGroundInstruction(GroundInstruction inst, GroundScope* scope) {
GroundInstruction copied_inst = copyGroundInstruction(&inst);
GroundInstruction* in = &copied_inst;
2025-11-23 21:23:04 +11:00
// Insert variables prefixed with $
for (int i = 0; i < in->args.length; i++) {
if (in->args.args[i].type == VALREF) {
2025-11-24 10:15:53 +11:00
GroundVariable* variable = findVariable(*scope->variables, in->args.args[i].value.refName);
2025-11-23 21:23:04 +11:00
if (variable) {
2025-11-24 10:15:53 +11:00
free(in->args.args[i].value.refName); // Free the strdup'd refName
2025-11-23 21:23:04 +11:00
in->args.args[i].value.value = variable->value;
in->args.args[i].type = VALUE;
} else {
runtimeError(UNKNOWN_VARIABLE, NULL, in, currentInstruction);
}
}
}
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-28 09:23:43 +11:00
/*
* CONTROL FLOW
* These instructions are for controlling how the program is executed.
* Instructions:
* if, jump, end
*/
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-28 09:23:43 +11:00
/*
* I/O
* These instructions take information from the user, and print it out.
* Instructions:
* print, println, input
*/
2025-11-23 18:34:30 +11:00
case PRINT: {
if (in->args.length < 1) {
2025-11-23 21:23:04 +11:00
runtimeError(TOO_FEW_ARGS, "Expecting 1 or more args", in, currentInstruction);
2025-11-23 18:34:30 +11:00
}
for (int i = 0; i < in->args.length; i++) {
2025-11-24 11:37:16 +11:00
if (i != 0) printf(" ");
2025-11-23 18:34:30 +11:00
printGroundArg(&in->args.args[i]);
}
break;
}
case PRINTLN: {
if (in->args.length < 1) {
2025-11-23 21:23:04 +11:00
runtimeError(TOO_FEW_ARGS, "Expecting 1 or more args", in, currentInstruction);
2025-11-23 18:34:30 +11:00
}
for (int i = 0; i < in->args.length; i++) {
2025-11-24 11:37:16 +11:00
if (i != 0) printf(" ");
2025-11-23 18:34:30 +11:00
printGroundArg(&in->args.args[i]);
}
printf("\n");
break;
}
2025-11-24 11:37:16 +11:00
case INPUT: {
if (in->args.length < 1) {
runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", in, currentInstruction);
}
if (in->args.args[0].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 1", in, currentInstruction);
}
char buffer[256];
if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
buffer[strcspn(buffer, "\n")] = '\0';
addVariable(scope->variables, in->args.args[0].value.refName, createStringGroundValue(buffer));
} else {
runtimeError(FIXME, "Failed to read input from console with fgets", in, currentInstruction);
}
break;
}
2025-11-23 21:23:04 +11:00
2025-11-28 09:23:43 +11:00
/*
* VARIABLES AND LISTS
* These instructions are for initializing variables and lists.
* Instructions:
2025-12-01 10:15:37 +11:00
* set, gettype, exists, setlist, setlistat, getlistat, getlistsize, listappend
2025-11-28 09:23:43 +11:00
*/
2025-11-23 21:23:04 +11:00
case SET: {
if (in->args.length < 2) {
runtimeError(TOO_FEW_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.length > 2) {
runtimeError(TOO_MANY_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.args[0].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", in, currentInstruction);
}
addVariable(scope->variables, in->args.args[0].value.refName, in->args.args[1].value.value);
break;
}
2025-11-28 09:23:43 +11:00
case GETTYPE: {
if (in->args.length < 2) {
runtimeError(TOO_FEW_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.length > 2) {
runtimeError(TOO_MANY_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
}
switch (in->args.args[0].value.value.type) {
case INT: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("int"));
break;
}
case DOUBLE: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("double"));
break;
}
case STRING: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("string"));
break;
}
case CHAR: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("char"));
break;
}
case BOOL: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("bool"));
break;
}
case LIST: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("list"));
break;
}
case CUSTOM: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("custom"));
break;
}
}
break;
}
case EXISTS: {
if (in->args.length < 2) {
runtimeError(TOO_FEW_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.length > 2) {
runtimeError(TOO_MANY_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.args[0].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 1", in, currentInstruction);
}
if (in->args.args[0].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
}
if (findVariable(*scope->variables, in->args.args[0].value.refName)) {
addVariable(scope->variables, in->args.args[1].value.refName, createBoolGroundValue(true));
} else {
addVariable(scope->variables, in->args.args[1].value.refName, createBoolGroundValue(false));
}
break;
}
2025-12-01 10:15:37 +11:00
case SETLIST: {
if (in->args.length < 1) {
runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", in, currentInstruction);
}
if (in->args.args[0].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 1", in, currentInstruction);
}
List newList = createList();
for (int i = 1; i < in->args.length; i++) {
if (in->args.args[i].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for all args after arg 1", in, currentInstruction);
}
appendToList(&newList, in->args.args[i].value.value);
}
addVariable(scope->variables, in->args.args[0].value.refName, createListGroundValue(newList));
break;
}
case SETLISTAT: {
if (in->args.length < 3) {
runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.args[0].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != VALUE && in->args.args[1].value.value.type != INT) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int value for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 3", in, currentInstruction);
}
GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName);
if (var == NULL) {
runtimeError(UNKNOWN_VARIABLE, NULL, in, currentInstruction);
}
GroundValue* value = &var->value;
if (value == NULL) {
runtimeError(FIXME, NULL, in, currentInstruction);
}
if (value->type != LIST) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction);
}
ListAccessStatus status = setListAt(&value->data.listVal, in->args.args[1].value.value.data.intVal, in->args.args[2].value.value);
switch (status) {
case LIST_OKAY:
return;
case LIST_OUT_OF_BOUNDS:
runtimeError(LIST_ERROR, "Out of bounds index", in, currentInstruction);
case LIST_FIXME:
default:
runtimeError(FIXME, "List related error", in, currentInstruction);
}
break;
}
case GETLISTAT: {
if (in->args.length < 2) {
runtimeError(TOO_FEW_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.args[0].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != VALUE && in->args.args[1].value.value.type != INT) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an integer value for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 3", in, currentInstruction);
}
GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName);
if (var == NULL) {
runtimeError(UNKNOWN_VARIABLE, NULL, in, currentInstruction);
}
GroundValue* value = &var->value;
if (value == NULL) {
runtimeError(FIXME, NULL, in, currentInstruction);
}
if (value->type != LIST) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction);
}
ListAccess status = getListAt(&value->data.listVal, in->args.args[1].value.value.data.intVal);
switch (status.status) {
case LIST_OKAY: {
addVariable(scope->variables, in->args.args[2].value.refName, *status.value);
break;
}
case LIST_OUT_OF_BOUNDS:
runtimeError(LIST_ERROR, "Out of bounds index", in, currentInstruction);
case LIST_FIXME:
default:
runtimeError(FIXME, "List related error", in, currentInstruction);
}
break;
}
case GETLISTSIZE: {
if (in->args.length < 2) {
runtimeError(TOO_FEW_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.args[0].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
}
GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName);
if (var == NULL) {
runtimeError(UNKNOWN_VARIABLE, NULL, in, currentInstruction);
}
GroundValue* value = &var->value;
if (value == NULL) {
runtimeError(FIXME, NULL, in, currentInstruction);
}
if (value->type != LIST) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction);
}
addVariable(scope->variables, in->args.args[1].value.refName, createIntGroundValue(value->data.listVal.size));
break;
}
case LISTAPPEND: {
if (in->args.length < 2) {
runtimeError(TOO_FEW_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.args[0].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", in, currentInstruction);
}
GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName);
if (var == NULL) {
runtimeError(UNKNOWN_VARIABLE, NULL, in, currentInstruction);
}
GroundValue* value = &var->value;
if (value == NULL) {
runtimeError(FIXME, NULL, in, currentInstruction);
}
if (value->type != LIST) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction);
}
appendToList(&value->data.listVal, in->args.args[1].value.value);
break;
}
2025-11-23 21:23:04 +11:00
2025-11-28 09:23:43 +11:00
/*
* MATHS
* These instructions allow running mathematical operations on values.
* Instructions:
* add, subtract, multiply, divide
*/
2025-11-23 21:23:04 +11:00
case ADD: {
if (in->args.length < 3) {
runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.length > 3) {
runtimeError(TOO_MANY_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", in, currentInstruction);
}
GroundValue* left = &in->args.args[0].value.value;
GroundValue* right = &in->args.args[1].value.value;
if (left->type == STRING) {
if (right->type != STRING) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a String for arg 2", in, currentInstruction);
}
2025-11-24 10:15:53 +11:00
char* newString = malloc(strlen(left->data.stringVal) + strlen(right->data.stringVal));
2025-11-23 21:23:04 +11:00
strcpy(newString, left->data.stringVal);
strcat(newString, right->data.stringVal);
addVariable(scope->variables, in->args.args[2].value.refName, createStringGroundValue(newString));
}
else if (left->type == INT || left->type == DOUBLE) {
if (right->type != INT && right->type != DOUBLE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 2", in, currentInstruction);
}
if (left->type == DOUBLE || right->type == DOUBLE) {
double result = 0;
if (left->type == INT) {
result += left->data.intVal;
} else if (left->type == DOUBLE) {
result += left->data.doubleVal;
}
if (right->type == INT) {
result += right->data.intVal;
2025-11-24 10:15:53 +11:00
} else if (right->type == DOUBLE) {
2025-11-23 21:23:04 +11:00
result += right->data.doubleVal;
}
addVariable(scope->variables, in->args.args[2].value.refName, createDoubleGroundValue(result));
} else {
int64_t result = left->data.intVal + right->data.intVal;
addVariable(scope->variables, in->args.args[2].value.refName, createIntGroundValue(result));
}
} else {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int, Double, or String for arg 1", in, currentInstruction);
2025-11-24 11:37:16 +11:00
}
break;
}
case SUBTRACT: {
if (in->args.length < 3) {
runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.length > 3) {
runtimeError(TOO_MANY_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", in, currentInstruction);
}
GroundValue* left = &in->args.args[0].value.value;
GroundValue* right = &in->args.args[1].value.value;
if (left->type == INT || left->type == DOUBLE) {
if (right->type != INT && right->type != DOUBLE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 2", in, currentInstruction);
}
if (left->type == DOUBLE || right->type == DOUBLE) {
double result = 0;
if (left->type == INT) {
result -= left->data.intVal;
} else if (left->type == DOUBLE) {
result -= left->data.doubleVal;
}
if (right->type == INT) {
result += right->data.intVal;
} else if (right->type == DOUBLE) {
result += right->data.doubleVal;
}
addVariable(scope->variables, in->args.args[2].value.refName, createDoubleGroundValue(result));
} else {
int64_t result = left->data.intVal - right->data.intVal;
addVariable(scope->variables, in->args.args[2].value.refName, createIntGroundValue(result));
}
} else {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 1", in, currentInstruction);
}
break;
}
case MULTIPLY: {
if (in->args.length < 3) {
runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.length > 3) {
runtimeError(TOO_MANY_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", in, currentInstruction);
}
GroundValue* left = &in->args.args[0].value.value;
GroundValue* right = &in->args.args[1].value.value;
if (left->type == INT || left->type == DOUBLE) {
if (right->type != INT && right->type != DOUBLE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 2", in, currentInstruction);
}
if (left->type == DOUBLE || right->type == DOUBLE) {
double result = 0;
if (left->type == INT) {
result *= left->data.intVal;
} else if (left->type == DOUBLE) {
result += left->data.doubleVal;
}
if (right->type == INT) {
result *= right->data.intVal;
} else if (right->type == DOUBLE) {
result += right->data.doubleVal;
}
addVariable(scope->variables, in->args.args[2].value.refName, createDoubleGroundValue(result));
} else {
int64_t result = left->data.intVal * right->data.intVal;
addVariable(scope->variables, in->args.args[2].value.refName, createIntGroundValue(result));
}
} else {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 1", in, currentInstruction);
}
break;
}
case DIVIDE: {
if (in->args.length < 3) {
runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.length > 3) {
runtimeError(TOO_MANY_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", in, currentInstruction);
}
GroundValue* left = &in->args.args[0].value.value;
GroundValue* right = &in->args.args[1].value.value;
if (left->type == INT || left->type == DOUBLE) {
if (right->type != INT && right->type != DOUBLE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 2", in, currentInstruction);
}
2025-11-28 09:23:43 +11:00
if (right->type == INT && right->data.intVal == 0) {
}
2025-11-24 11:37:16 +11:00
if (left->type == DOUBLE || right->type == DOUBLE) {
double result = 0;
if (left->type == INT) {
result /= left->data.intVal;
} else if (left->type == DOUBLE) {
result /= left->data.doubleVal;
}
if (right->type == INT) {
result /= right->data.intVal;
} else if (right->type == DOUBLE) {
result /= right->data.doubleVal;
}
addVariable(scope->variables, in->args.args[2].value.refName, createDoubleGroundValue(result));
} else {
int64_t result = left->data.intVal / right->data.intVal;
addVariable(scope->variables, in->args.args[2].value.refName, createIntGroundValue(result));
}
} else {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 1", in, currentInstruction);
2025-11-23 21:23:04 +11:00
}
break;
}
2025-11-28 09:23:43 +11:00
/*
* COMPARISONS
* Allows comparing values.
* Instructions:
* equal, inequal, not, greater, lesser
*/
2025-11-24 10:15:53 +11:00
case EQUAL: {
if (in->args.length < 3) {
runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.length > 3) {
runtimeError(TOO_MANY_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", in, currentInstruction);
}
GroundValue* left = &in->args.args[0].value.value;
GroundValue* right = &in->args.args[1].value.value;
if (left->type != right->type) {
2025-11-24 13:19:37 +11:00
if (left->type == INT && right->type == DOUBLE) {
bool cond = (left->data.intVal == right->data.doubleVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
} else if (left->type == DOUBLE && right->type == INT) {
bool cond = (left->data.doubleVal == right->data.intVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
} else {
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(false));
}
2025-11-24 10:15:53 +11:00
} else {
switch (left->type) {
case INT: {
bool cond = (left->data.intVal == right->data.intVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
break;
}
case DOUBLE: {
bool cond = (left->data.doubleVal == right->data.doubleVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
break;
}
case STRING: {
bool cond = (strcmp(left->data.stringVal, right->data.stringVal) == 0);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
break;
}
case CHAR: {
bool cond = (left->data.charVal == right->data.charVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
break;
}
case BOOL: {
bool cond = (left->data.boolVal == right->data.boolVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
break;
}
default: {
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(false));
break;
}
}
}
break;
}
2025-11-24 13:19:37 +11:00
case INEQUAL: {
if (in->args.length < 3) {
runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.length > 3) {
runtimeError(TOO_MANY_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", in, currentInstruction);
}
GroundValue* left = &in->args.args[0].value.value;
GroundValue* right = &in->args.args[1].value.value;
if (left->type != right->type) {
if (left->type == INT && right->type == DOUBLE) {
bool cond = (left->data.intVal != right->data.doubleVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
} else if (left->type == DOUBLE && right->type == INT) {
bool cond = (left->data.doubleVal != right->data.intVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
} else {
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(false));
}
} else {
switch (left->type) {
case INT: {
bool cond = (left->data.intVal != right->data.intVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
break;
}
case DOUBLE: {
bool cond = (left->data.doubleVal != right->data.doubleVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
break;
}
case STRING: {
bool cond = (strcmp(left->data.stringVal, right->data.stringVal) != 0);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
break;
}
case CHAR: {
bool cond = (left->data.charVal != right->data.charVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
break;
}
case BOOL: {
bool cond = (left->data.boolVal != right->data.boolVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
break;
}
default: {
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(true));
break;
}
}
}
break;
}
2025-11-28 09:23:43 +11:00
case NOT: {
if (in->args.length < 2) {
runtimeError(TOO_FEW_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.length > 2) {
runtimeError(TOO_MANY_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE && in->args.args[0].value.value.type != BOOL) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Bool for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
}
bool condition = !in->args.args[1].value.value.data.boolVal;
addVariable(scope->variables, in->args.args[1].value.refName, createBoolGroundValue(condition));
}
default: {
runtimeError(FIXME, "Currently unimplemented instruction", in, currentInstruction);
}
2025-11-24 13:19:37 +11:00
case GREATER: {
if (in->args.length < 3) {
runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.length > 3) {
runtimeError(TOO_MANY_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", in, currentInstruction);
}
GroundValue* left = &in->args.args[0].value.value;
GroundValue* right = &in->args.args[1].value.value;
if (left->type != INT && left->type != DOUBLE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 1", in, currentInstruction);
} else if (right->type != INT && right->type != DOUBLE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 2", in, currentInstruction);
}
if (left->type != right->type) {
if (left->type == INT && right->type == DOUBLE) {
bool cond = (left->data.intVal > right->data.doubleVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
} else if (left->type == DOUBLE && right->type == INT) {
bool cond = (left->data.doubleVal > right->data.intVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
} else {
runtimeError(FIXME, "Uncaught invalid type", in, currentInstruction);
}
} else {
switch (left->type) {
case INT: {
bool cond = (left->data.intVal > right->data.intVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
break;
}
case DOUBLE: {
bool cond = (left->data.doubleVal > right->data.doubleVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
break;
}
default: {
runtimeError(FIXME, "Uncaught invalid type", in, currentInstruction);
break;
}
}
}
break;
}
case LESSER: {
if (in->args.length < 3) {
runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.length > 3) {
runtimeError(TOO_MANY_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", in, currentInstruction);
}
GroundValue* left = &in->args.args[0].value.value;
GroundValue* right = &in->args.args[1].value.value;
if (left->type != INT && left->type != DOUBLE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 1", in, currentInstruction);
} else if (right->type != INT && right->type != DOUBLE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 2", in, currentInstruction);
}
if (left->type != right->type) {
if (left->type == INT && right->type == DOUBLE) {
bool cond = (left->data.intVal < right->data.doubleVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
} else if (left->type == DOUBLE && right->type == INT) {
bool cond = (left->data.doubleVal < right->data.intVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
} else {
runtimeError(FIXME, "Uncaught invalid type", in, currentInstruction);
}
} else {
switch (left->type) {
case INT: {
bool cond = (left->data.intVal < right->data.intVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
break;
}
case DOUBLE: {
bool cond = (left->data.doubleVal < right->data.doubleVal);
addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond));
break;
}
default: {
runtimeError(FIXME, "Uncaught invalid type", in, currentInstruction);
break;
}
}
}
break;
}
2025-11-23 18:34:30 +11:00
}
2025-11-24 10:15:53 +11:00
freeGroundInstruction(in);
2025-11-23 18:34:30 +11:00
}