2025-11-23 18:34:30 +11:00
|
|
|
#include "interpreter.h"
|
|
|
|
|
#include "parser.h"
|
|
|
|
|
#include "types.h"
|
|
|
|
|
#include "include/uthash.h"
|
2025-12-13 09:50:49 +11:00
|
|
|
#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>
|
2026-01-02 20:29:27 +11:00
|
|
|
#include <dlfcn.h>
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
#include <unistd.h>
|
2025-11-23 18:34:30 +11:00
|
|
|
|
|
|
|
|
int currentInstruction = 0;
|
|
|
|
|
|
2026-01-18 20:49:50 +11:00
|
|
|
bool isMainScopeGlobal = true;
|
|
|
|
|
|
2026-01-17 12:21:43 +11:00
|
|
|
[[noreturn]] void runtimeError(GroundRuntimeError error, char* what, GroundInstruction* where, int whereLine) {
|
2025-11-23 18:34:30 +11:00
|
|
|
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;
|
|
|
|
|
}
|
2026-01-17 12:21:43 +11:00
|
|
|
case RETURN_TYPE_MISMATCH: {
|
|
|
|
|
printf("ReturnTypeMismatch");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case PREMATURE_EOF: {
|
|
|
|
|
printf("PrematureEof");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case INVALID_INSTRUCTION: {
|
|
|
|
|
printf("InvalidInstruction");
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-18 20:49:50 +11:00
|
|
|
[[noreturn]] void throwError(GroundError* error) {
|
|
|
|
|
printf("Uncaught Ground runtime error:\n ErrorType: %s\n", error->type);
|
|
|
|
|
if (error->what != NULL) {
|
|
|
|
|
printf(" ErrorContext: %s\n", error->what);
|
|
|
|
|
}
|
|
|
|
|
if (error->where != NULL) {
|
|
|
|
|
printf(" ErrorInstruction: ");
|
|
|
|
|
printGroundInstruction(error->where);
|
|
|
|
|
printf("\n");
|
|
|
|
|
}
|
2026-01-18 21:37:46 +11:00
|
|
|
if (error->hasLine) {
|
2026-01-18 20:49:50 +11:00
|
|
|
printf(" ErrorLine: %zu\n", error->line + 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();
|
2026-01-02 20:29:27 +11:00
|
|
|
gf->isNative = false;
|
|
|
|
|
gf->nativeFn = NULL;
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 12:21:43 +11:00
|
|
|
if (spacepos == (size_t) -1) {
|
2025-12-08 11:08:08 +11:00
|
|
|
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;
|
2025-12-08 15:06:29 +11:00
|
|
|
} 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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-02 20:29:27 +11:00
|
|
|
void groundAddNativeFunction(GroundScope* scope, char* name, NativeGroundFunction fn, GroundValueType returnType, int argCount, ...) {
|
|
|
|
|
GroundFunction* gf = createGroundFunction();
|
|
|
|
|
gf->isNative = true;
|
|
|
|
|
gf->nativeFn = fn;
|
|
|
|
|
gf->returnType = returnType;
|
|
|
|
|
|
|
|
|
|
va_list args;
|
|
|
|
|
va_start(args, argCount);
|
|
|
|
|
for(int i = 0; i < argCount; i++) {
|
|
|
|
|
GroundValueType type = va_arg(args, GroundValueType);
|
|
|
|
|
char* argName = va_arg(args, char*);
|
|
|
|
|
addArgsToGroundFunction(gf, type, argName);
|
|
|
|
|
}
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
|
|
addVariable(scope->variables, name, createFunctionGroundValue(gf));
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 20:30:14 +11:00
|
|
|
GroundFunction* parseFunction(GroundProgram* in, size_t errorOffset) {
|
|
|
|
|
GroundFunction* function = createGroundFunction();
|
|
|
|
|
for (size_t i = 0; i < in->size; i++) {
|
|
|
|
|
if (in->instructions[i].args.length < 2) {
|
|
|
|
|
function->returnType = NONE;
|
|
|
|
|
} else {
|
|
|
|
|
if (in->instructions[i].args.args[1].type != TYPEREF) {
|
|
|
|
|
runtimeError(ARG_TYPE_MISMATCH, "Expecting a TypeRef for arg 2", &in->instructions[i], errorOffset + i);
|
|
|
|
|
}
|
|
|
|
|
GroundArg* args = in->instructions[i].args.args;
|
|
|
|
|
function->returnType = stringToValueType(args[1].value.refName);
|
|
|
|
|
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], errorOffset + i);
|
|
|
|
|
}
|
|
|
|
|
if (j + 1 >= length) {
|
|
|
|
|
runtimeError(TOO_FEW_ARGS, "Expecting a DirectRef after a TypeRef", &in->instructions[i], errorOffset + i);
|
|
|
|
|
}
|
|
|
|
|
if (args[j + 1].type != DIRREF) {
|
|
|
|
|
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef after a TypeRef", &in->instructions[i], errorOffset + i);
|
|
|
|
|
}
|
|
|
|
|
addArgsToGroundFunction(function, stringToValueType(args[j].value.refName), args[j + 1].value.refName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
i++;
|
2026-01-17 20:37:16 +11:00
|
|
|
while (i < in->size) {
|
2026-01-17 20:30:14 +11:00
|
|
|
addInstructionToProgram(&function->program, in->instructions[i]);
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return function;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 20:04:18 +11:00
|
|
|
GroundStruct parseStruct(GroundProgram* in, GroundScope* scope, size_t errorOffset) {
|
2026-01-16 17:45:48 +11:00
|
|
|
GroundStruct gstruct = createStruct();
|
2026-01-17 12:21:43 +11:00
|
|
|
for (size_t i = 0; i < in->size; i++) {
|
|
|
|
|
switch (in->instructions[i].type) {
|
|
|
|
|
case SET: {
|
2026-01-17 19:58:21 +11:00
|
|
|
if (in->instructions[i].args.length < 2) {
|
|
|
|
|
runtimeError(TOO_FEW_ARGS, "Expecting 2 args", &in->instructions[i], i + errorOffset);
|
|
|
|
|
}
|
|
|
|
|
if (in->instructions[i].args.length > 2) {
|
|
|
|
|
runtimeError(TOO_MANY_ARGS, "Expecting 2 args", &in->instructions[i], i + errorOffset);
|
|
|
|
|
}
|
|
|
|
|
if (in->instructions[i].args.args[0].type != DIRREF) {
|
|
|
|
|
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 1", &in->instructions[i], i + errorOffset);
|
|
|
|
|
}
|
|
|
|
|
if (in->instructions[i].args.args[1].type != VALUE) {
|
|
|
|
|
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", &in->instructions[i], i + errorOffset);
|
|
|
|
|
}
|
|
|
|
|
addFieldToStruct(&gstruct, in->instructions[i].args.args[0].value.refName, in->instructions[i].args.args[1].value.value);
|
2026-01-17 12:21:43 +11:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case INIT: {
|
2026-01-17 20:04:18 +11:00
|
|
|
if (in->instructions[i].args.length < 2) {
|
|
|
|
|
runtimeError(TOO_FEW_ARGS, "Expecting 2 args", &in->instructions[i], currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
if (in->instructions[i].args.length > 2) {
|
|
|
|
|
runtimeError(TOO_MANY_ARGS, "Expecting 2 args", &in->instructions[i], currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
if (in->instructions[i].args.args[0].type != DIRREF) {
|
|
|
|
|
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 1", &in->instructions[i], currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
if (in->instructions[i].args.args[1].type != TYPEREF) {
|
|
|
|
|
runtimeError(ARG_TYPE_MISMATCH, "Expecting a TypeRef for arg 2", &in->instructions[i], currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroundValue gv;
|
|
|
|
|
|
|
|
|
|
switch (stringToValueType(in->instructions[i].args.args[0].value.refName)) {
|
|
|
|
|
case INT: {
|
|
|
|
|
gv = createIntGroundValue(0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case DOUBLE: {
|
|
|
|
|
gv = createDoubleGroundValue(0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case STRING: {
|
|
|
|
|
gv = createStringGroundValue("");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case CHAR: {
|
|
|
|
|
gv = createCharGroundValue('\0');
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case BOOL: {
|
|
|
|
|
gv = createBoolGroundValue(false);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case LIST: {
|
|
|
|
|
gv = createListGroundValue(createList());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case FUNCTION: {
|
|
|
|
|
gv = createFunctionGroundValue(createGroundFunction());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case STRUCTVAL: {
|
|
|
|
|
gv.type = STRUCTVAL;
|
|
|
|
|
gv.data.structVal = malloc(sizeof(GroundStruct));
|
|
|
|
|
*gv.data.structVal = createStruct();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case CUSTOM: {
|
|
|
|
|
GroundVariable* var = findVariable(*scope->variables, in->instructions[i].args.args[1].value.refName);
|
|
|
|
|
if (var == NULL) {
|
|
|
|
|
runtimeError(UNKNOWN_VARIABLE, "Couldn't find the specified type", &in->instructions[i], currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
if (var->value.type != STRUCTVAL) {
|
|
|
|
|
runtimeError(ARG_TYPE_MISMATCH, "TypeRef does not reference a struct", &in->instructions[i], currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
GroundStruct* gstruct = var->value.data.structVal;
|
|
|
|
|
gv.type = CUSTOM;
|
|
|
|
|
gv.data.customVal = malloc(sizeof(GroundObject));
|
|
|
|
|
*gv.data.customVal = createObject(*gstruct);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case NONE: {
|
|
|
|
|
gv.type = NONE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
runtimeError(FIXME, "Reached should-be-impossible state", &in->instructions[i], currentInstruction);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
addFieldToStruct(&gstruct, in->instructions[i].args.args[0].value.refName, gv);
|
|
|
|
|
|
2026-01-17 12:21:43 +11:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case FUN: {
|
2026-01-18 13:44:26 +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);
|
|
|
|
|
}
|
|
|
|
|
char* name = malloc(strlen(in->instructions[i].args.args[0].value.refName) + 1);
|
|
|
|
|
strcpy(name, in->instructions[i].args.args[0].value.refName);
|
|
|
|
|
|
|
|
|
|
size_t counter = 1;
|
|
|
|
|
GroundProgram gp = createGroundProgram();
|
|
|
|
|
addInstructionToProgram(&gp, in->instructions[i]);
|
|
|
|
|
size_t errorOffset = i;
|
|
|
|
|
i++;
|
|
|
|
|
while (counter > 0) {
|
|
|
|
|
if (i >= in->size) {
|
|
|
|
|
runtimeError(PREMATURE_EOF, "Reached end of scope before function definition ended", &in->instructions[i - 1], i - 1);
|
|
|
|
|
}
|
|
|
|
|
if (in->instructions[i].type == FUN) {
|
|
|
|
|
counter++;
|
|
|
|
|
}
|
|
|
|
|
if (in->instructions[i].type == ENDFUN) {
|
|
|
|
|
counter--;
|
|
|
|
|
}
|
|
|
|
|
addInstructionToProgram(&gp, in->instructions[i]);
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroundFunction* function = parseFunction(&gp, errorOffset);
|
|
|
|
|
function->startLine = i;
|
|
|
|
|
GroundValue gv = createFunctionGroundValue(function);
|
|
|
|
|
addFieldToStruct(&gstruct, name, gv);
|
2026-01-17 12:21:43 +11:00
|
|
|
break;
|
|
|
|
|
}
|
2026-01-17 19:58:21 +11:00
|
|
|
case ENDSTRUCT: {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-01-17 12:21:43 +11:00
|
|
|
default: {
|
|
|
|
|
runtimeError(INVALID_INSTRUCTION, "Unsupported instruction while inside struct", &in->instructions[i], errorOffset + i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-16 17:45:48 +11:00
|
|
|
return gstruct;
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
2025-12-08 15:06:29 +11:00
|
|
|
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;
|
|
|
|
|
}
|
2026-01-18 20:49:50 +11:00
|
|
|
scope.isMainScope = isMainScopeGlobal;
|
|
|
|
|
isMainScopeGlobal = false;
|
2026-01-16 17:45:48 +11:00
|
|
|
// Preprocess all labels, structs and functions
|
2026-01-17 12:21:43 +11:00
|
|
|
for (size_t i = 0; i < in->size; i++) {
|
2025-11-23 18:34:30 +11:00
|
|
|
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) {
|
|
|
|
|
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);
|
|
|
|
|
}
|
2026-01-17 20:30:14 +11:00
|
|
|
char* name = malloc(strlen(in->instructions[i].args.args[0].value.refName) + 1);
|
|
|
|
|
strcpy(name, in->instructions[i].args.args[0].value.refName);
|
|
|
|
|
|
|
|
|
|
size_t counter = 1;
|
|
|
|
|
GroundProgram gp = createGroundProgram();
|
|
|
|
|
addInstructionToProgram(&gp, in->instructions[i]);
|
|
|
|
|
size_t errorOffset = i;
|
|
|
|
|
i++;
|
|
|
|
|
while (counter > 0) {
|
|
|
|
|
if (i >= in->size) {
|
|
|
|
|
runtimeError(PREMATURE_EOF, "Reached end of scope before function definition ended", &in->instructions[i - 1], i - 1);
|
|
|
|
|
}
|
|
|
|
|
if (in->instructions[i].type == FUN) {
|
|
|
|
|
counter++;
|
|
|
|
|
}
|
|
|
|
|
if (in->instructions[i].type == ENDFUN) {
|
|
|
|
|
counter--;
|
|
|
|
|
}
|
|
|
|
|
addInstructionToProgram(&gp, in->instructions[i]);
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-18 13:44:26 +11:00
|
|
|
GroundFunction* function = parseFunction(&gp, errorOffset);
|
|
|
|
|
function->startLine = i;
|
|
|
|
|
GroundValue gv = createFunctionGroundValue(function);
|
2026-01-17 20:30:14 +11:00
|
|
|
|
|
|
|
|
addVariable(scope.variables, name, gv);
|
2025-12-02 09:00:21 +11:00
|
|
|
}
|
2026-01-16 17:45:48 +11:00
|
|
|
if (in->instructions[i].type == STRUCT) {
|
|
|
|
|
if (in->instructions[i].args.length < 1) {
|
|
|
|
|
runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", &in->instructions[i], i);
|
|
|
|
|
}
|
|
|
|
|
if (in->instructions[i].args.length > 1) {
|
|
|
|
|
runtimeError(TOO_MANY_ARGS, "Expecting 1 arg", &in->instructions[i], i);
|
|
|
|
|
}
|
|
|
|
|
if (in->instructions[i].args.args[0].type != TYPEREF) {
|
|
|
|
|
runtimeError(ARG_TYPE_MISMATCH, "Expected arg 1 to be a typeref", &in->instructions[i], i);
|
|
|
|
|
}
|
2026-01-17 19:58:21 +11:00
|
|
|
char* name = malloc(strlen(in->instructions[i].args.args[0].value.refName) + 1);
|
|
|
|
|
strcpy(name, in->instructions[i].args.args[0].value.refName);
|
2026-01-16 17:45:48 +11:00
|
|
|
i++;
|
|
|
|
|
|
|
|
|
|
size_t counter = 1;
|
2026-01-17 12:21:43 +11:00
|
|
|
GroundProgram gp = createGroundProgram();
|
|
|
|
|
size_t errorOffset = i;
|
2026-01-16 17:45:48 +11:00
|
|
|
while (counter > 0) {
|
|
|
|
|
if (i >= in->size) {
|
|
|
|
|
runtimeError(PREMATURE_EOF, "Reached end of scope before struct definition ended", &in->instructions[i - 1], i - 1);
|
|
|
|
|
}
|
|
|
|
|
if (in->instructions[i].type == STRUCT) {
|
|
|
|
|
counter++;
|
|
|
|
|
}
|
|
|
|
|
if (in->instructions[i].type == ENDSTRUCT) {
|
|
|
|
|
counter--;
|
|
|
|
|
}
|
|
|
|
|
addInstructionToProgram(&gp, in->instructions[i]);
|
2026-01-17 12:21:43 +11:00
|
|
|
i++;
|
2026-01-16 17:45:48 +11:00
|
|
|
}
|
|
|
|
|
|
2026-01-17 19:58:21 +11:00
|
|
|
GroundValue gv = {
|
|
|
|
|
.type = STRUCTVAL,
|
|
|
|
|
.data.structVal = malloc(sizeof(GroundStruct))
|
|
|
|
|
};
|
2026-01-17 20:07:24 +11:00
|
|
|
*gv.data.structVal = parseStruct(&gp, &scope, errorOffset);
|
2026-01-17 19:58:21 +11:00
|
|
|
|
|
|
|
|
addVariable(scope.variables, name, gv);
|
2026-01-16 17:45:48 +11:00
|
|
|
}
|
2025-11-23 18:34:30 +11:00
|
|
|
}
|
2026-01-17 12:21:43 +11:00
|
|
|
for (size_t i = 0; i < in->size; i++) {
|
2025-12-06 14:35:13 +11:00
|
|
|
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--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-18 13:44:26 +11:00
|
|
|
if (in->instructions[i].type == STRUCT) {
|
|
|
|
|
int count = 1;
|
|
|
|
|
while (count > 0) {
|
|
|
|
|
i++;
|
|
|
|
|
if (i >= in->size) {
|
|
|
|
|
return createNoneGroundValue();
|
|
|
|
|
}
|
|
|
|
|
if (in->instructions[i].type == STRUCT) {
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
if (in->instructions[i].type == ENDSTRUCT) {
|
|
|
|
|
count--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-08 15:06:29 +11:00
|
|
|
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;
|
|
|
|
|
}
|
2025-12-08 15:06:29 +11:00
|
|
|
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");
|
2025-12-08 15:06:29 +11:00
|
|
|
break;
|
2025-12-08 11:08:08 +11:00
|
|
|
}
|
|
|
|
|
case UNKNOWN: {
|
|
|
|
|
printf("Unknown instruction (type \"help\" for help)");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (shouldBreak) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-08 15:06:29 +11:00
|
|
|
if (in->instructions[i].type == PAUSE) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2025-12-08 11:08:08 +11:00
|
|
|
}
|
2025-12-08 15:06:29 +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 $
|
2026-01-17 12:21:43 +11:00
|
|
|
for (size_t i = 0; i < in->args.length; i++) {
|
2025-11-23 21:23:04 +11:00
|
|
|
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) {
|
2026-01-17 12:21:43 +11:00
|
|
|
// We can safely ignore these instructions, as they have been preprocessed
|
2025-12-02 09:00:21 +11:00
|
|
|
case FUN:
|
|
|
|
|
case ENDFUN:
|
2026-01-17 12:21:43 +11:00
|
|
|
case STRUCT:
|
|
|
|
|
case ENDSTRUCT:
|
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
|
|
|
}
|
2026-01-17 12:21:43 +11:00
|
|
|
for (size_t 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
|
|
|
}
|
2026-01-17 12:21:43 +11:00
|
|
|
for (size_t 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"));
|
2026-01-17 12:21:43 +11:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case STRUCTVAL: {
|
|
|
|
|
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("struct"));
|
|
|
|
|
break;
|
2025-12-11 13:07:37 +11:00
|
|
|
}
|
2025-12-02 09:00:21 +11:00
|
|
|
case NONE: {
|
|
|
|
|
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("none"));
|
2026-01-17 12:21:43 +11:00
|
|
|
break;
|
2025-12-02 09:00:21 +11:00
|
|
|
}
|
2026-01-18 20:49:50 +11:00
|
|
|
case ERROR: {
|
|
|
|
|
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("error"));
|
|
|
|
|
break;
|
|
|
|
|
}
|
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();
|
2026-01-17 12:21:43 +11:00
|
|
|
for (size_t i = 1; i < in->args.length; i++) {
|
2025-12-01 10:15:37 +11:00
|
|
|
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
|
|
|
|
2025-12-13 09:50:49 +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;
|
2026-01-17 12:21:43 +11:00
|
|
|
size_t idx = in->args.args[1].value.value.data.intVal;
|
2025-12-01 12:28:15 +11:00
|
|
|
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-12-15 11:34:56 +11:00
|
|
|
break;
|
2025-11-28 09:23:43 +11:00
|
|
|
}
|
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);
|
|
|
|
|
}
|
2026-01-02 20:29:27 +11:00
|
|
|
|
|
|
|
|
if (function->isNative) {
|
|
|
|
|
List argsList = createList();
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
appendToList(&argsList, in->args.args[i + 1].value.value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (function->nativeFn) {
|
|
|
|
|
GroundValue returnValue = function->nativeFn(scope, argsList);
|
2026-01-18 20:49:50 +11:00
|
|
|
if (returnValue.type == ERROR) {
|
2026-01-18 21:37:46 +11:00
|
|
|
returnValue.data.errorVal.line = currentInstruction;
|
|
|
|
|
returnValue.data.errorVal.hasLine = true;
|
|
|
|
|
returnValue.data.errorVal.where = in;
|
2026-01-18 20:49:50 +11:00
|
|
|
if (scope->isMainScope) {
|
|
|
|
|
throwError(&returnValue.data.errorVal);
|
|
|
|
|
}
|
|
|
|
|
return returnValue;
|
|
|
|
|
}
|
2026-01-02 20:29:27 +11:00
|
|
|
if (returnValue.type != function->returnType) {
|
|
|
|
|
runtimeError(RETURN_TYPE_MISMATCH, "Unexpected return value type from native function", in, currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
|
|
|
|
|
}
|
|
|
|
|
free(argsList.values);
|
|
|
|
|
} else {
|
|
|
|
|
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-11 13:07:37 +11:00
|
|
|
}
|
2026-01-02 20:29:27 +11:00
|
|
|
size_t currentCurrentInstruction = currentInstruction;
|
|
|
|
|
currentInstruction = function->startLine;
|
|
|
|
|
GroundValue returnValue = interpretGroundProgram(&function->program, &newScope);
|
2026-01-18 20:49:50 +11:00
|
|
|
if (returnValue.type == ERROR) {
|
|
|
|
|
return returnValue;
|
|
|
|
|
}
|
2026-01-02 20:29:27 +11:00
|
|
|
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
|
|
|
}
|
2026-01-02 20:29:27 +11:00
|
|
|
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
|
|
|
|
|
currentInstruction = currentCurrentInstruction;
|
2025-12-11 13:07:37 +11:00
|
|
|
}
|
2026-01-02 20:29:27 +11:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case USE: {
|
|
|
|
|
if (in->args.length < 1) {
|
|
|
|
|
runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char* libName = in->args.args[0].value.value.data.stringVal;
|
|
|
|
|
char path[1024];
|
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
|
|
char* envPath = getenv("GROUND_LIBS");
|
|
|
|
|
|
|
|
|
|
if (envPath) {
|
|
|
|
|
snprintf(path, sizeof(path), "%s/%s", envPath, libName);
|
|
|
|
|
if (access(path, F_OK) == 0) found = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
|
snprintf(path, sizeof(path), "/usr/lib/ground/%s", libName);
|
|
|
|
|
if (access(path, F_OK) == 0) found = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
|
snprintf(path, sizeof(path), "%s", libName);
|
|
|
|
|
if (access(path, F_OK) == 0) found = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
|
char errorMsg[1100];
|
|
|
|
|
snprintf(errorMsg, sizeof(errorMsg), "Could not find library: %s", libName);
|
|
|
|
|
runtimeError(FIXME, errorMsg, in, currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FILE* f = fopen(path, "r");
|
|
|
|
|
if (!f) {
|
|
|
|
|
runtimeError(FIXME, "Failed to open file", in, currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
|
|
|
long fsize = ftell(f);
|
|
|
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
|
|
|
|
|
|
char* content = malloc(fsize + 1);
|
|
|
|
|
if (!content) {
|
|
|
|
|
fclose(f);
|
|
|
|
|
runtimeError(FIXME, "Failed to allocate memory for file content", in, currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
fread(content, 1, fsize, f);
|
|
|
|
|
fclose(f);
|
|
|
|
|
content[fsize] = 0;
|
|
|
|
|
|
|
|
|
|
GroundProgram program = parseFile(content);
|
|
|
|
|
free(content);
|
|
|
|
|
|
|
|
|
|
interpretGroundProgram(&program, scope);
|
|
|
|
|
freeGroundProgram(&program);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case EXTERN: {
|
|
|
|
|
if (in->args.length < 1) {
|
|
|
|
|
runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", 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);
|
2025-12-06 11:50:42 +11:00
|
|
|
}
|
2026-01-02 20:29:27 +11:00
|
|
|
|
|
|
|
|
char* libName = in->args.args[0].value.value.data.stringVal;
|
|
|
|
|
char path[1024];
|
|
|
|
|
int found = 0;
|
2026-01-02 21:04:42 +00:00
|
|
|
|
|
|
|
|
// care about mac and windows later
|
|
|
|
|
char* extension = "so";
|
2026-01-02 20:29:27 +11:00
|
|
|
|
|
|
|
|
char* envPath = getenv("GROUND_LIBS");
|
|
|
|
|
|
|
|
|
|
if (envPath) {
|
2026-01-02 21:04:42 +00:00
|
|
|
snprintf(path, sizeof(path), "%s/%s.%s", envPath, libName, extension);
|
2026-01-02 20:29:27 +11:00
|
|
|
if (access(path, F_OK) == 0) found = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!found) {
|
2026-01-02 21:04:42 +00:00
|
|
|
snprintf(path, sizeof(path), "/usr/lib/ground/%s.%s", libName, extension);
|
2026-01-02 20:29:27 +11:00
|
|
|
if (access(path, F_OK) == 0) found = 1;
|
|
|
|
|
}
|
|
|
|
|
if (!found) {
|
2026-01-02 21:04:42 +00:00
|
|
|
snprintf(path, sizeof(path), "./%s.%s", libName, extension);
|
2026-01-02 20:29:27 +11:00
|
|
|
if (access(path, F_OK) == 0) found = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
|
char errorMsg[1100];
|
2026-01-02 21:04:42 +00:00
|
|
|
snprintf(errorMsg, sizeof(errorMsg), "Could not find external library: %s, in path: %s", libName, path);
|
2026-01-02 20:29:27 +11:00
|
|
|
runtimeError(FIXME, errorMsg, in, currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void* handle = dlopen(path, RTLD_LAZY);
|
|
|
|
|
if (!handle) {
|
|
|
|
|
runtimeError(FIXME, dlerror(), in, currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef void (*GroundInitFn)(GroundScope*);
|
|
|
|
|
GroundInitFn initFn = (GroundInitFn)dlsym(handle, "ground_init");
|
|
|
|
|
|
|
|
|
|
if (!initFn) {
|
|
|
|
|
runtimeError(FIXME, "Could not find 'ground_init' symbol in library", in, currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
initFn(scope);
|
2025-12-06 14:35:13 +11:00
|
|
|
break;
|
2025-12-06 11:50:42 +11:00
|
|
|
}
|
2025-12-22 14:05:40 +11:00
|
|
|
|
2026-01-17 19:58:21 +11:00
|
|
|
case INIT: {
|
|
|
|
|
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 != TYPEREF) {
|
|
|
|
|
runtimeError(ARG_TYPE_MISMATCH, "Expecting a TypeRef for arg 2", in, currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroundValue gv;
|
|
|
|
|
|
2026-01-17 20:30:14 +11:00
|
|
|
switch (stringToValueType(in->args.args[1].value.refName)) {
|
2026-01-17 19:58:21 +11:00
|
|
|
case INT: {
|
|
|
|
|
gv = createIntGroundValue(0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case DOUBLE: {
|
|
|
|
|
gv = createDoubleGroundValue(0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case STRING: {
|
|
|
|
|
gv = createStringGroundValue("");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case CHAR: {
|
|
|
|
|
gv = createCharGroundValue('\0');
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case BOOL: {
|
|
|
|
|
gv = createBoolGroundValue(false);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case LIST: {
|
|
|
|
|
gv = createListGroundValue(createList());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case FUNCTION: {
|
2026-01-18 13:44:26 +11:00
|
|
|
GroundFunction* gf = createGroundFunction();
|
|
|
|
|
gf->returnType = NONE;
|
|
|
|
|
gv = createFunctionGroundValue(gf);
|
2026-01-17 19:58:21 +11:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case STRUCTVAL: {
|
|
|
|
|
gv.type = STRUCTVAL;
|
|
|
|
|
gv.data.structVal = malloc(sizeof(GroundStruct));
|
|
|
|
|
*gv.data.structVal = createStruct();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case CUSTOM: {
|
|
|
|
|
GroundVariable* var = findVariable(*scope->variables, in->args.args[1].value.refName);
|
|
|
|
|
if (var == NULL) {
|
|
|
|
|
runtimeError(UNKNOWN_VARIABLE, "Couldn't find the specified type", in, currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
if (var->value.type != STRUCTVAL) {
|
|
|
|
|
runtimeError(ARG_TYPE_MISMATCH, "TypeRef does not reference a struct", in, currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
GroundStruct* gstruct = var->value.data.structVal;
|
|
|
|
|
gv.type = CUSTOM;
|
|
|
|
|
gv.data.customVal = malloc(sizeof(GroundObject));
|
|
|
|
|
*gv.data.customVal = createObject(*gstruct);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case NONE: {
|
|
|
|
|
gv.type = NONE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
runtimeError(FIXME, "Reached should-be-impossible state", in, currentInstruction);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-17 20:04:18 +11:00
|
|
|
addVariable(scope->variables, in->args.args[0].value.refName, gv);
|
2026-01-17 19:58:21 +11:00
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-22 14:05:40 +11:00
|
|
|
case DROP: {
|
|
|
|
|
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 != DIRREF) {
|
|
|
|
|
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 1", in, currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName);
|
|
|
|
|
if (!var) {
|
|
|
|
|
runtimeError(UNKNOWN_VARIABLE, NULL, in, currentInstruction);
|
|
|
|
|
}
|
|
|
|
|
deleteVariable(scope->variables, var);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-12-13 09:50:49 +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
|
|
|
}
|