Files
cground/src/interpreter.c

1478 lines
64 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 <inttypes.h>
2025-11-23 18:34:30 +11:00
#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-12-02 09:00:21 +11:00
case STRING_ERROR: {
printf("StringError");
break;
}
case MATH_ERROR: {
printf("MathError");
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);
2025-12-01 10:36:09 +11:00
item->value = copyGroundValue(&data);
2025-11-24 10:15:53 +11:00
HASH_ADD_STR(*head, id, item);
}
2025-12-06 11:50:42 +11:00
GroundFunction* createGroundFunction() {
GroundFunction* gf = malloc(sizeof(GroundFunction));
gf->argSize = 0;
gf->args = malloc(sizeof(GroundFunctionArgs));
gf->program = createGroundProgram();
2025-12-02 09:00:21 +11:00
return gf;
}
void addArgsToGroundFunction(GroundFunction* function, GroundValueType type, char* name) {
GroundFunctionArgs arg;
arg.type = type;
arg.name = name;
function->argSize ++;
GroundFunctionArgs* ptr = realloc(function->args, function->argSize * sizeof(GroundFunctionArgs));
if (ptr == NULL) {
printf("Failed to allocate memory for arg of GroundFunction");
function->argSize --;
return;
}
function->args = ptr;
function->args[function->argSize - 1] = arg;
};
GroundValueType stringToValueType(char* in) {
if (strcmp(in, "int") == 0) {
return INT;
}
if (strcmp(in, "double") == 0) {
return DOUBLE;
}
if (strcmp(in, "bool") == 0) {
return BOOL;
}
if (strcmp(in, "string") == 0) {
return STRING;
}
if (strcmp(in, "char") == 0) {
return CHAR;
}
if (strcmp(in, "list") == 0) {
return LIST;
}
2025-12-08 11:08:08 +11:00
if (strcmp(in, "function") == 0) {
return FUNCTION;
}
2025-12-02 09:00:21 +11:00
return CUSTOM;
}
2025-12-08 11:08:08 +11:00
GroundDebugInstruction parseDebugInstruction(char* in) {
GroundDebugInstruction gdi;
size_t insize = strlen(in);
size_t spacepos = -1;
for (size_t i = 0; i < insize; i++) {
if (in[i] == ' ') {
spacepos = i;
break;
}
}
if (spacepos == -1) {
spacepos = insize;
}
char* instruction = malloc(spacepos + 1);
if (!instruction) {
gdi.type = UNKNOWN;
gdi.arg = NULL;
return gdi;
}
strncpy(instruction, in, spacepos);
instruction[spacepos] = '\0';
if (strcmp(instruction, "dump") == 0) {
gdi.type = DUMP;
} else if (strcmp(instruction, "inspect") == 0) {
gdi.type = INSPECT;
} else if (strcmp(instruction, "eval") == 0) {
gdi.type = EVAL;
} else if (strcmp(instruction, "continue") == 0) {
gdi.type = CONTINUE;
} else if (strcmp(instruction, "exit") == 0) {
gdi.type = EXIT;
} else if (strcmp(instruction, "step") == 0) {
gdi.type = STEP;
} else if (strcmp(instruction, "view") == 0) {
gdi.type = VIEW;
2025-12-08 11:08:08 +11:00
} else if (strcmp(instruction, "help") == 0) {
gdi.type = HELP;
} else {
gdi.type = UNKNOWN;
}
size_t arglen = insize - spacepos;
gdi.arg = malloc(arglen + 1);
if (gdi.arg) {
strcpy(gdi.arg, in + spacepos + 1);
}
free(instruction);
return gdi;
}
2025-12-02 09:00:21 +11:00
GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
2025-11-23 19:59:22 +11:00
GroundLabel* labels = NULL;
GroundVariable* variables = NULL;
int instructionsToPause = -1;
2025-11-23 19:59:22 +11:00
GroundScope scope;
2025-12-02 09:00:21 +11:00
if (inScope != NULL) {
scope = *inScope;
} else {
scope.labels = &labels;
scope.variables = &variables;
}
2025-12-06 14:35:13 +11:00
// Preprocess all labels and functions
2025-11-23 18:34:30 +11:00
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
}
2025-12-02 09:00:21 +11:00
if (in->instructions[i].type == FUN) {
2025-12-06 11:50:42 +11:00
GroundFunction* function = createGroundFunction();
2025-12-06 14:35:13 +11:00
function->startLine = i;
2025-12-02 09:00:21 +11:00
if (in->instructions[i].args.length < 1) {
runtimeError(TOO_FEW_ARGS, "Expecting 1 or more args", &in->instructions[i], i);
}
if (in->instructions[i].args.args[0].type != FNREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a FunctionRef for arg 1", &in->instructions[i], i);
}
2025-12-06 11:50:42 +11:00
char* functionName = in->instructions[i].args.args[0].value.refName;
2025-12-02 09:00:21 +11:00
if (in->instructions[i].args.length < 2) {
2025-12-06 11:50:42 +11:00
function->returnType = NONE;
2025-12-02 09:00:21 +11:00
} else {
if (in->instructions[i].args.args[1].type != TYPEREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a TypeRef for arg 2", &in->instructions[i], i);
}
GroundArg* args = in->instructions[i].args.args;
2025-12-11 13:07:37 +11:00
function->returnType = stringToValueType(args[1].value.refName);
2025-12-02 09:00:21 +11:00
size_t length = in->instructions[i].args.length;
for (size_t j = 2; j < length; j += 2) {
if (args[j].type != TYPEREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a TypeRef", &in->instructions[i], i);
}
if (j + 1 >= length) {
runtimeError(TOO_FEW_ARGS, "Expecting a DirectRef after a TypeRef", &in->instructions[i], i);
}
if (args[j + 1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef after a TypeRef", &in->instructions[i], i);
}
2025-12-06 11:50:42 +11:00
addArgsToGroundFunction(function, stringToValueType(args[j].value.refName), args[j + 1].value.refName);
2025-12-02 09:00:21 +11:00
}
}
i++;
while (in->instructions[i].type != ENDFUN) {
2025-12-06 11:50:42 +11:00
addInstructionToProgram(&function->program, in->instructions[i]);
2025-12-02 09:00:21 +11:00
i++;
}
2025-12-06 11:50:42 +11:00
addVariable(scope.variables, functionName, createFunctionGroundValue(function));
2025-12-02 09:00:21 +11:00
}
2025-11-23 18:34:30 +11:00
}
2025-12-06 14:35:13 +11:00
for (int i = 0; i < in->size; i++) {
if (in->instructions[i].type == FUN) {
int count = 1;
while (count > 0) {
i++;
if (i >= in->size) {
return createNoneGroundValue();
}
if (in->instructions[i].type == FUN) {
count++;
}
if (in->instructions[i].type == ENDFUN) {
count--;
}
}
}
if (in->instructions[i].type == PAUSE || instructionsToPause == 0) {
2025-12-08 11:08:08 +11:00
printf("Paused execution\n");
printf("Previous instruction: ");
if (i > 0) {
printGroundInstruction(&in->instructions[i - 1]);
}
while (true) {
printf("\nprompt> ");
char buffer[256];
if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
buffer[strcspn(buffer, "\n")] = '\0';
} else {
runtimeError(FIXME, "Failed to read input from console with fgets", NULL, -1);
}
GroundDebugInstruction gdi = parseDebugInstruction(buffer);
bool shouldBreak = false;
switch (gdi.type) {
case CONTINUE: {
shouldBreak = true;
break;
}
case EXIT: {
exit(0);
break;
}
case DUMP: {
if (scope.variables == NULL) {
printf("Can't access variables");
break;
}
if (*scope.variables == NULL) {
printf("Can't access variables");
break;
}
GroundVariable* entry;
GroundVariable* tmp;
GroundVariable* head = *scope.variables;
HASH_ITER(hh, head, entry, tmp) {
if (entry != NULL) {
printf("%s: ", entry->id);
printGroundValue(&entry->value);
printf("\n");
}
}
break;
}
case INSPECT: {
printf("%s: ", gdi.arg);
GroundVariable* var = findVariable(*scope.variables, gdi.arg);
if (var == NULL) {
printf("(nothing)");
} else {
printGroundValue(&var->value);
}
break;
}
case EVAL: {
GroundProgram program = parseFile(gdi.arg);
interpretGroundProgram(&program, &scope);
freeGroundProgram(&program);
break;
}
case STEP: {
if (gdi.arg != NULL && strlen(gdi.arg) > 0) {
instructionsToPause = atoi(gdi.arg);
} else {
instructionsToPause = 1;
}
instructionsToPause++;
shouldBreak = true;
break;
}
case VIEW: {
break;
}
2025-12-08 11:08:08 +11:00
case HELP: {
printf("Ground Debugger Help\n");
printf("Didn't mean to end up here? Type \"continue\" to escape this prompt.\n");
printf("Commands:\n");
printf(" continue: Continue execution of the program\n");
printf(" exit: Stop execution early\n");
printf(" dump: Shows all variables and their contents\n");
printf(" inspect (variable): Shows the contents of a variable\n");
printf(" eval (code): Runs Ground code in the current scope\n");
printf(" help: Shows this help message");
break;
2025-12-08 11:08:08 +11:00
}
case UNKNOWN: {
printf("Unknown instruction (type \"help\" for help)");
break;
}
}
if (shouldBreak) {
break;
}
}
if (in->instructions[i].type == PAUSE) {
continue;
}
2025-12-08 11:08:08 +11:00
}
instructionsToPause --;
2025-12-06 14:35:13 +11:00
int ci = currentInstruction;
GroundValue gv = interpretGroundInstruction(in->instructions[i], &scope);
if (gv.type != NONE) {
return gv;
}
if (ci != currentInstruction) {
i = currentInstruction;
}
2025-11-23 18:34:30 +11:00
currentInstruction++;
}
2025-12-06 14:35:13 +11:00
return createNoneGroundValue();
2025-11-23 18:34:30 +11:00
}
2025-12-02 09:00:21 +11:00
GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scope) {
2025-11-24 10:15:53 +11:00
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-12-01 10:36:09 +11:00
in->args.args[i].value.value = copyGroundValue(&variable->value);
2025-11-23 21:23:04 +11:00
in->args.args[i].type = VALUE;
} else {
runtimeError(UNKNOWN_VARIABLE, NULL, in, currentInstruction);
}
}
}
2025-11-23 18:34:30 +11:00
switch (in->type) {
2025-12-02 09:00:21 +11:00
// We can safely ignore any CREATELABEL, FUN, and ENDFUN instructions, these have been preprocessed
case FUN:
case ENDFUN:
2025-11-23 18:34:30 +11:00
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;
}
2025-12-11 13:07:37 +11:00
case FUNCTION: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("function"));
}
2025-12-02 09:00:21 +11:00
case NONE: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("none"));
}
2025-11-28 09:23:43 +11:00
}
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:
2025-12-02 09:00:21 +11:00
break;
2025-12-01 10:15:37 +11:00
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
case STOI: {
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 != STRING) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a String for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
}
addVariable(scope->variables, in->args.args[2].value.refName, createIntGroundValue(atoll(in->args.args[0].value.value.data.stringVal)));
break;
}
case STOD: {
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 != STRING) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a String for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
}
addVariable(scope->variables, in->args.args[1].value.refName, createIntGroundValue(atof(in->args.args[0].value.value.data.stringVal)));
break;
}
case TOSTRING: {
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);
}
GroundValue* value = &in->args.args[0].value.value;
switch (value->type) {
case INT: {
char* buf = malloc(sizeof(char) * 256);
snprintf(buf, sizeof(char) * 256, "%" PRId64, value->data.intVal);
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue(buf));
break;
}
case DOUBLE: {
char* buf = malloc(sizeof(char) * 256);
snprintf(buf, sizeof(char) * 256, "%f", value->data.doubleVal);
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue(buf));
break;
}
case STRING: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue(value->data.stringVal));
break;
}
case CHAR: {
char* buf = malloc(sizeof(char) * 2);
buf[0] = value->data.charVal;
buf[1] = '\0';
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue(buf));
break;
}
case BOOL: {
if (value->data.boolVal) {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("true"));
} else {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("false"));
}
break;
}
case LIST: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("<list>"));
break;
}
case FUNCTION: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("<function>"));
break;
}
case CUSTOM: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("<custom>"));
break;
}
case NONE:
default: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("<default>"));
break;
}
}
break;
}
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) {
2025-12-02 09:00:21 +11:00
result = left->data.intVal;
2025-11-24 11:37:16 +11:00
} else if (left->type == DOUBLE) {
2025-12-02 09:00:21 +11:00
result = left->data.doubleVal;
2025-11-24 11:37:16 +11:00
}
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) {
2025-12-02 09:00:21 +11:00
double result = 1.0;
2025-11-24 11:37:16 +11:00
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-12-02 09:00:21 +11:00
runtimeError(MATH_ERROR, "Division by zero", in, currentInstruction);
2025-11-28 09:23:43 +11:00
}
2025-11-24 11:37:16 +11:00
if (left->type == DOUBLE || right->type == DOUBLE) {
double result = 0;
if (left->type == INT) {
2025-12-02 09:00:21 +11:00
result = left->data.intVal;
2025-11-24 11:37:16 +11:00
} else if (left->type == DOUBLE) {
2025-12-02 09:00:21 +11:00
result = left->data.doubleVal;
2025-11-24 11:37:16 +11:00
}
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-12-01 12:28:15 +11:00
/*
* STRING OPERATIONS
* Allows easier manipulation of strings.
* Instructions:
* getstrcharat, getstrsize
*/
case GETSTRCHARAT: {
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 && in->args.args[0].value.value.type != STRING) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a String 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 for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", in, currentInstruction);
}
char* str = in->args.args[0].value.value.data.stringVal;
int64_t idx = in->args.args[1].value.value.data.intVal;
if (idx < strlen(str)) {
addVariable(scope->variables, in->args.args[2].value.refName, createCharGroundValue(str[idx]));
} else {
runtimeError(STRING_ERROR, "Out of bounds index", in, currentInstruction);
}
break;
}
case GETSTRSIZE: {
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 != STRING) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a String for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
}
addVariable(scope->variables, in->args.args[1].value.refName, createIntGroundValue(strlen(in->args.args[0].value.value.data.stringVal)));
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);
}
2025-12-02 09:00:21 +11:00
bool condition = !in->args.args[0].value.value.data.boolVal;
2025-11-28 09:23:43 +11:00
addVariable(scope->variables, in->args.args[1].value.refName, createBoolGroundValue(condition));
}
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-12-06 11:50:42 +11:00
/*
* FUNCTIONS
* Allows execution of functions in Ground.
* Instructions:
* fun, return, endfun, pusharg, call
*/
case RETURN: {
2025-12-06 14:35:13 +11:00
if (in->args.length < 1) {
runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", in, currentInstruction);
}
if (in->args.length > 1) {
runtimeError(TOO_MANY_ARGS, "Expecting 1 arg", in, currentInstruction);
}
if (in->args.args[0].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 1", in, currentInstruction);
}
return in->args.args[0].value.value;
2025-12-06 11:50:42 +11:00
}
case CALL: {
if (in->args.length < 2) {
2025-12-11 13:07:37 +11:00
runtimeError(TOO_FEW_ARGS, "Expecting 2 or more args", in, currentInstruction);
2025-12-06 11:50:42 +11:00
}
if (in->args.args[0].type != FNREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a FunctionRef for arg 1", in, currentInstruction);
}
2025-12-11 13:07:37 +11:00
if (in->args.args[in->args.length - 1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef as the last arg", in, currentInstruction);
2025-12-06 11:50:42 +11:00
}
GroundVariable* variables = NULL;
GroundLabel* labels = NULL;
GroundScope newScope;
newScope.variables = &variables;
newScope.labels = &labels;
GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName);
if (var == NULL) {
runtimeError(UNKNOWN_VARIABLE, "Function not found", in, currentInstruction);
}
GroundValue* value = &var->value;
if (value->type != FUNCTION) {
runtimeError(UNKNOWN_VARIABLE, "Provided reference does not reference a function", in, currentInstruction);
}
GroundFunction* function = value->data.fnVal;
2025-12-11 13:07:37 +11:00
if (function->argSize < in->args.length - 2) {
runtimeError(TOO_FEW_ARGS, "Incorrect amount of arguments provided for function", in, currentInstruction);
}
if (function->argSize > in->args.length - 2) {
runtimeError(TOO_MANY_ARGS, "Incorrect amount of arguments provided for function", in, currentInstruction);
}
for (size_t i = 0; i < function->argSize; i++) {
if (in->args.args[i + 1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction);
}
if (in->args.args[i + 1].value.value.type != function->args[i].type) {
runtimeError(ARG_TYPE_MISMATCH, "Mismatched function argument types", in, currentInstruction);
}
addVariable(newScope.variables, function->args[i].name, in->args.args[i + 1].value.value);
}
2025-12-06 14:35:13 +11:00
size_t currentCurrentInstruction = currentInstruction;
currentInstruction = function->startLine;
2025-12-06 11:50:42 +11:00
GroundValue returnValue = interpretGroundProgram(&function->program, &newScope);
if (returnValue.type != function->returnType) {
runtimeError(RETURN_TYPE_MISMATCH, "Unexpected return value type from function", in, currentInstruction);
}
2025-12-11 13:07:37 +11:00
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
2025-12-06 14:35:13 +11:00
currentInstruction = currentCurrentInstruction;
break;
2025-12-06 11:50:42 +11:00
}
default: {
runtimeError(FIXME, "Currently unimplemented instruction", in, currentInstruction);
}
2025-11-23 18:34:30 +11:00
}
2025-11-24 10:15:53 +11:00
freeGroundInstruction(in);
2025-12-06 14:35:13 +11:00
return createNoneGroundValue();
2025-11-23 18:34:30 +11:00
}