Files
cground/src/interpreter.c

2023 lines
86 KiB
C

#include "interpreter.h"
#include "parser.h"
#include "types.h"
#include "include/uthash.h"
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <stdarg.h>
#include <unistd.h>
int currentInstruction = 0;
bool isMainScopeGlobal = true;
[[noreturn]] 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;
}
case UNKNOWN_LABEL: {
printf("UnknownLabel");
break;
}
case UNKNOWN_VARIABLE: {
printf("UnknownVariable");
break;
}
case LIST_ERROR: {
printf("ListError");
break;
}
case STRING_ERROR: {
printf("StringError");
break;
}
case MATH_ERROR: {
printf("MathError");
break;
}
case RETURN_TYPE_MISMATCH: {
printf("ReturnTypeMismatch");
break;
}
case PREMATURE_EOF: {
printf("PrematureEof");
break;
}
case INVALID_INSTRUCTION: {
printf("InvalidInstruction");
break;
}
default:
case FIXME: {
printf("FIXME (please report issue to https://chsp.au/ground/cground)");
}
}
printf("\n");
if (what != NULL) {
printf(" ErrorContext: %s\n", what);
}
if (where != NULL) {
printf(" ErrorInstruction: ");
printGroundInstruction(where);
printf("\n");
}
if (whereLine > -1) {
printf(" ErrorLine: %d\n", whereLine + 1);
}
exit(1);
}
[[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");
}
if (error->hasLine) {
printf(" ErrorLine: %zu\n", error->line + 1);
}
exit(1);
}
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);
}
void addLabel(GroundLabel **head, const char *id, int data) {
GroundLabel* label = findLabel(*head, id);
if (label) {
deleteLabel(head, label);
}
GroundLabel* item = malloc(sizeof(GroundLabel));
snprintf(item->id, MAX_ID_LEN, "%s", id);
item->lineNum = data;
HASH_ADD_STR(*head, id, item);
}
GroundVariable* findVariable(GroundVariable* head, const char *id) {
GroundVariable *item;
HASH_FIND_STR(head, id, item);
return item;
}
void deleteVariable(GroundVariable** head, GroundVariable *item) {
HASH_DEL(*head, item);
free(item);
}
void addVariable(GroundVariable **head, const char *id, GroundValue data) {
GroundVariable* variable = findVariable(*head, id);
if (variable) {
freeGroundValue(&variable->value);
deleteVariable(head, variable);
}
GroundVariable* item = malloc(sizeof(GroundVariable));
snprintf(item->id, MAX_ID_LEN, "%s", id);
item->value = copyGroundValue(&data);
HASH_ADD_STR(*head, id, item);
}
GroundFunction* createGroundFunction() {
GroundFunction* gf = malloc(sizeof(GroundFunction));
gf->argSize = 0;
gf->args = malloc(sizeof(GroundFunctionArgs));
gf->program = createGroundProgram();
gf->isNative = false;
gf->nativeFn = NULL;
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;
}
if (strcmp(in, "function") == 0) {
return FUNCTION;
}
return CUSTOM;
}
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 == (size_t) -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;
} 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;
}
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));
}
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++;
while (i < in->size) {
addInstructionToProgram(&function->program, in->instructions[i]);
i++;
}
break;
}
return function;
}
GroundStruct parseStruct(GroundProgram* in, GroundScope* scope, size_t errorOffset) {
GroundStruct gstruct = createStruct();
for (size_t i = 0; i < in->size; i++) {
switch (in->instructions[i].type) {
case SET: {
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);
break;
}
case INIT: {
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);
break;
}
case 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);
}
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);
break;
}
case ENDSTRUCT: {
break;
}
default: {
runtimeError(INVALID_INSTRUCTION, "Unsupported instruction while inside struct", &in->instructions[i], errorOffset + i);
}
}
}
return gstruct;
}
GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
GroundLabel* labels = NULL;
GroundVariable* variables = NULL;
int instructionsToPause = -1;
GroundScope scope;
if (inScope != NULL) {
scope = *inScope;
} else {
scope.labels = &labels;
scope.variables = &variables;
}
scope.isMainScope = isMainScopeGlobal;
isMainScopeGlobal = false;
// Preprocess all labels, structs and functions
for (size_t i = 0; i < in->size; i++) {
if (in->instructions[i].type == CREATELABEL) {
addLabel(scope.labels, in->instructions[i].args.args[0].value.refName, i);
}
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);
}
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);
addVariable(scope.variables, name, gv);
}
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);
}
char* name = malloc(strlen(in->instructions[i].args.args[0].value.refName) + 1);
strcpy(name, in->instructions[i].args.args[0].value.refName);
i++;
size_t counter = 1;
GroundProgram gp = createGroundProgram();
size_t errorOffset = i;
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]);
i++;
}
GroundValue gv = {
.type = STRUCTVAL,
.data.structVal = malloc(sizeof(GroundStruct))
};
*gv.data.structVal = parseStruct(&gp, &scope, errorOffset);
addVariable(scope.variables, name, gv);
}
}
for (size_t 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 == 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--;
}
}
}
if (in->instructions[i].type == PAUSE || instructionsToPause == 0) {
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;
}
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;
}
case UNKNOWN: {
printf("Unknown instruction (type \"help\" for help)");
break;
}
}
if (shouldBreak) {
break;
}
}
if (in->instructions[i].type == PAUSE) {
continue;
}
}
instructionsToPause --;
int ci = currentInstruction;
GroundValue gv = interpretGroundInstruction(in->instructions[i], &scope);
if (gv.type != NONE) {
return gv;
}
if (ci != currentInstruction) {
i = currentInstruction;
}
currentInstruction++;
}
return createNoneGroundValue();
}
GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scope) {
GroundInstruction copied_inst = copyGroundInstruction(&inst);
GroundInstruction* in = &copied_inst;
// Insert variables prefixed with $
for (size_t i = 0; i < in->args.length; i++) {
if (in->args.args[i].type == VALREF) {
GroundVariable* variable = findVariable(*scope->variables, in->args.args[i].value.refName);
if (variable) {
free(in->args.args[i].value.refName); // Free the strdup'd refName
in->args.args[i].value.value = copyGroundValue(&variable->value);
in->args.args[i].type = VALUE;
} else {
runtimeError(UNKNOWN_VARIABLE, NULL, in, currentInstruction);
}
}
}
switch (in->type) {
// We can safely ignore these instructions, as they have been preprocessed
case FUN:
case ENDFUN:
case STRUCT:
case ENDSTRUCT:
case CREATELABEL: {
break;
}
/*
* CONTROL FLOW
* These instructions are for controlling how the program is executed.
* Instructions:
* if, jump, end
*/
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);
}
if (in->args.args[0].type != VALUE || in->args.args[0].value.value.type != BOOL) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a bool for arg 0", in, currentInstruction);
}
if (in->args.args[1].type != LINEREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a LineRef for arg 1", in, currentInstruction);
}
if (in->args.args[0].value.value.data.boolVal) {
GroundLabel* label = findLabel(*scope->labels, in->args.args[1].value.refName);
if (label) {
currentInstruction = label->lineNum;
} else {
runtimeError(UNKNOWN_LABEL, NULL, in, currentInstruction);
}
}
break;
}
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);
}
GroundLabel* label = findLabel(*scope->labels, in->args.args[0].value.refName);
if (label) {
currentInstruction = label->lineNum;
} else {
runtimeError(UNKNOWN_LABEL, NULL, in, currentInstruction);
}
break;
}
case END: {
if (in->args.length < 1 || in->args.args[0].type != VALUE || in->args.args[0].value.value.type != INT) {
exit(0);
} else {
exit(in->args.args[0].value.value.data.intVal);
}
break;
}
/*
* I/O
* These instructions take information from the user, and print it out.
* Instructions:
* print, println, input
*/
case PRINT: {
if (in->args.length < 1) {
runtimeError(TOO_FEW_ARGS, "Expecting 1 or more args", in, currentInstruction);
}
for (size_t i = 0; i < in->args.length; i++) {
if (i != 0) printf(" ");
printGroundArg(&in->args.args[i]);
}
break;
}
case PRINTLN: {
if (in->args.length < 1) {
runtimeError(TOO_FEW_ARGS, "Expecting 1 or more args", in, currentInstruction);
}
for (size_t i = 0; i < in->args.length; i++) {
if (i != 0) printf(" ");
printGroundArg(&in->args.args[i]);
}
printf("\n");
break;
}
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;
}
/*
* VARIABLES AND LISTS
* These instructions are for initializing variables and lists.
* Instructions:
* set, gettype, exists, setlist, setlistat, getlistat, getlistsize, listappend
*/
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;
}
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;
}
case FUNCTION: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("function"));
break;
}
case STRUCTVAL: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("struct"));
break;
}
case NONE: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("none"));
break;
}
case ERROR: {
addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("error"));
break;
}
}
break;
}
case EXISTS: {
if (in->args.length < 2) {
runtimeError(TOO_FEW_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.length > 2) {
runtimeError(TOO_MANY_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.args[0].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 1", in, currentInstruction);
}
if (in->args.args[0].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
}
if (findVariable(*scope->variables, in->args.args[0].value.refName)) {
addVariable(scope->variables, in->args.args[1].value.refName, createBoolGroundValue(true));
} else {
addVariable(scope->variables, in->args.args[1].value.refName, createBoolGroundValue(false));
}
break;
}
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 (size_t 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:
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 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;
}
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;
}
/*
* MATHS
* These instructions allow running mathematical operations on values.
* Instructions:
* add, subtract, multiply, divide
*/
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);
}
char* newString = malloc(strlen(left->data.stringVal) + strlen(right->data.stringVal));
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;
} 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, Double, or String for arg 1", in, currentInstruction);
}
break;
}
case SUBTRACT: {
if (in->args.length < 3) {
runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.length > 3) {
runtimeError(TOO_MANY_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", in, currentInstruction);
}
GroundValue* left = &in->args.args[0].value.value;
GroundValue* right = &in->args.args[1].value.value;
if (left->type == INT || left->type == DOUBLE) {
if (right->type != INT && right->type != DOUBLE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 2", in, currentInstruction);
}
if (left->type == DOUBLE || right->type == DOUBLE) {
double result = 0;
if (left->type == INT) {
result = left->data.intVal;
} else if (left->type == DOUBLE) {
result = left->data.doubleVal;
}
if (right->type == INT) {
result += right->data.intVal;
} else if (right->type == DOUBLE) {
result += right->data.doubleVal;
}
addVariable(scope->variables, in->args.args[2].value.refName, createDoubleGroundValue(result));
} else {
int64_t result = left->data.intVal - right->data.intVal;
addVariable(scope->variables, in->args.args[2].value.refName, createIntGroundValue(result));
}
} else {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 1", in, currentInstruction);
}
break;
}
case MULTIPLY: {
if (in->args.length < 3) {
runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.length > 3) {
runtimeError(TOO_MANY_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", in, currentInstruction);
}
GroundValue* left = &in->args.args[0].value.value;
GroundValue* right = &in->args.args[1].value.value;
if (left->type == INT || left->type == DOUBLE) {
if (right->type != INT && right->type != DOUBLE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 2", in, currentInstruction);
}
if (left->type == DOUBLE || right->type == DOUBLE) {
double result = 1.0;
if (left->type == INT) {
result *= left->data.intVal;
} else if (left->type == DOUBLE) {
result += left->data.doubleVal;
}
if (right->type == INT) {
result *= right->data.intVal;
} else if (right->type == DOUBLE) {
result += right->data.doubleVal;
}
addVariable(scope->variables, in->args.args[2].value.refName, createDoubleGroundValue(result));
} else {
int64_t result = left->data.intVal * right->data.intVal;
addVariable(scope->variables, in->args.args[2].value.refName, createIntGroundValue(result));
}
} else {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 1", in, currentInstruction);
}
break;
}
case DIVIDE: {
if (in->args.length < 3) {
runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.length > 3) {
runtimeError(TOO_MANY_ARGS, "Expecting 3 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", in, currentInstruction);
}
if (in->args.args[2].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", in, currentInstruction);
}
GroundValue* left = &in->args.args[0].value.value;
GroundValue* right = &in->args.args[1].value.value;
if (left->type == INT || left->type == DOUBLE) {
if (right->type != INT && right->type != DOUBLE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 2", in, currentInstruction);
}
if (right->type == INT && right->data.intVal == 0) {
runtimeError(MATH_ERROR, "Division by zero", in, currentInstruction);
}
if (left->type == DOUBLE || right->type == DOUBLE) {
double result = 0;
if (left->type == INT) {
result = left->data.intVal;
} else if (left->type == DOUBLE) {
result = left->data.doubleVal;
}
if (right->type == INT) {
result /= right->data.intVal;
} else if (right->type == DOUBLE) {
result /= right->data.doubleVal;
}
addVariable(scope->variables, in->args.args[2].value.refName, createDoubleGroundValue(result));
} else {
int64_t result = left->data.intVal / right->data.intVal;
addVariable(scope->variables, in->args.args[2].value.refName, createIntGroundValue(result));
}
} else {
runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 1", in, currentInstruction);
}
break;
}
/*
* 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;
size_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;
}
/*
* COMPARISONS
* Allows comparing values.
* Instructions:
* equal, inequal, not, greater, lesser
*/
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) {
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(false));
break;
}
}
}
break;
}
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;
}
case NOT: {
if (in->args.length < 2) {
runtimeError(TOO_FEW_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.length > 2) {
runtimeError(TOO_MANY_ARGS, "Expecting 2 args", in, currentInstruction);
}
if (in->args.args[0].type != VALUE && in->args.args[0].value.value.type != BOOL) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Bool for arg 1", in, currentInstruction);
}
if (in->args.args[1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction);
}
bool condition = !in->args.args[0].value.value.data.boolVal;
addVariable(scope->variables, in->args.args[1].value.refName, createBoolGroundValue(condition));
break;
}
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;
}
/*
* FUNCTIONS
* Allows execution of functions in Ground.
* Instructions:
* fun, return, endfun, pusharg, call
*/
case RETURN: {
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;
}
case CALL: {
if (in->args.length < 2) {
runtimeError(TOO_FEW_ARGS, "Expecting 2 or more args", in, currentInstruction);
}
if (in->args.args[0].type != FNREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a FunctionRef for arg 1", in, currentInstruction);
}
if (in->args.args[in->args.length - 1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef as the last arg", in, currentInstruction);
}
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;
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);
}
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);
if (returnValue.type == ERROR) {
returnValue.data.errorVal.line = currentInstruction;
returnValue.data.errorVal.hasLine = true;
returnValue.data.errorVal.where = in;
if (scope->isMainScope) {
throwError(&returnValue.data.errorVal);
}
return returnValue;
}
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);
}
size_t currentCurrentInstruction = currentInstruction;
currentInstruction = function->startLine;
GroundValue returnValue = interpretGroundProgram(&function->program, &newScope);
if (returnValue.type == ERROR) {
return returnValue;
}
if (returnValue.type != function->returnType) {
runtimeError(RETURN_TYPE_MISMATCH, "Unexpected return value type from function", in, currentInstruction);
}
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
currentInstruction = currentCurrentInstruction;
}
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);
}
char* libName = in->args.args[0].value.value.data.stringVal;
char path[1024];
int found = 0;
// care about mac and windows later
char* extension = "so";
char* envPath = getenv("GROUND_LIBS");
if (envPath) {
snprintf(path, sizeof(path), "%s/%s.%s", envPath, libName, extension);
if (access(path, F_OK) == 0) found = 1;
}
if (!found) {
snprintf(path, sizeof(path), "/usr/lib/ground/%s.%s", libName, extension);
if (access(path, F_OK) == 0) found = 1;
}
if (!found) {
snprintf(path, sizeof(path), "./%s.%s", libName, extension);
if (access(path, F_OK) == 0) found = 1;
}
if (!found) {
char errorMsg[1100];
snprintf(errorMsg, sizeof(errorMsg), "Could not find external library: %s, in path: %s", libName, path);
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);
break;
}
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;
switch (stringToValueType(in->args.args[1].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: {
GroundFunction* gf = createGroundFunction();
gf->returnType = NONE;
gv = createFunctionGroundValue(gf);
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;
}
}
addVariable(scope->variables, in->args.args[0].value.refName, gv);
break;
}
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;
}
default: {
runtimeError(FIXME, "Currently unimplemented instruction", in, currentInstruction);
}
}
freeGroundInstruction(in);
return createNoneGroundValue();
}