diff --git a/src/interpreter.c b/src/interpreter.c index f08d3af..9b04845 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -11,7 +11,7 @@ int currentInstruction = 0; -void runtimeError(GroundRuntimeError error, char* what, GroundInstruction* where, int whereLine) { +[[noreturn]] void runtimeError(GroundRuntimeError error, char* what, GroundInstruction* where, int whereLine) { printf("Ground runtime error:\n ErrorType: "); switch (error) { case ARG_TYPE_MISMATCH: { @@ -46,6 +46,18 @@ void runtimeError(GroundRuntimeError error, char* what, GroundInstruction* where 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)"); @@ -173,7 +185,7 @@ GroundDebugInstruction parseDebugInstruction(char* in) { } } - if (spacepos == -1) { + if (spacepos == (size_t) -1) { spacepos = insize; } @@ -234,6 +246,186 @@ void groundAddNativeFunction(GroundScope* scope, char* name, NativeGroundFunctio 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; @@ -246,52 +438,87 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) { scope.labels = &labels; scope.variables = &variables; } - // Preprocess all labels and functions - for (int i = 0; i < in->size; i++) { + // 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) { - GroundFunction* function = createGroundFunction(); - function->startLine = i; 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* functionName = in->instructions[i].args.args[0].value.refName; - 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], 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], i); - } - if (j + 1 >= length) { - runtimeError(TOO_FEW_ARGS, "Expecting a DirectRef after a TypeRef", &in->instructions[i], i); - } - if (args[j + 1].type != DIRREF) { - runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef after a TypeRef", &in->instructions[i], i); - } - addArgsToGroundFunction(function, stringToValueType(args[j].value.refName), args[j + 1].value.refName); - } - } + 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 (in->instructions[i].type != ENDFUN) { - addInstructionToProgram(&function->program, in->instructions[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++; } - addVariable(scope.variables, functionName, createFunctionGroundValue(function)); + + 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 (int i = 0; i < in->size; i++) { + for (size_t i = 0; i < in->size; i++) { if (in->instructions[i].type == FUN) { int count = 1; while (count > 0) { @@ -307,6 +534,21 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) { } } } + 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: "); @@ -428,7 +670,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop GroundInstruction* in = &copied_inst; // Insert variables prefixed with $ - for (int i = 0; i < in->args.length; i++) { + 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) { @@ -441,9 +683,11 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop } } switch (in->type) { - // We can safely ignore any CREATELABEL, FUN, and ENDFUN instructions, these have been preprocessed + // We can safely ignore these instructions, as they have been preprocessed case FUN: case ENDFUN: + case STRUCT: + case ENDSTRUCT: case CREATELABEL: { break; } @@ -516,7 +760,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop if (in->args.length < 1) { runtimeError(TOO_FEW_ARGS, "Expecting 1 or more args", in, currentInstruction); } - for (int i = 0; i < in->args.length; i++) { + for (size_t i = 0; i < in->args.length; i++) { if (i != 0) printf(" "); printGroundArg(&in->args.args[i]); } @@ -526,7 +770,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop if (in->args.length < 1) { runtimeError(TOO_FEW_ARGS, "Expecting 1 or more args", in, currentInstruction); } - for (int i = 0; i < in->args.length; i++) { + for (size_t i = 0; i < in->args.length; i++) { if (i != 0) printf(" "); printGroundArg(&in->args.args[i]); } @@ -618,9 +862,15 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop } 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; } } @@ -655,7 +905,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 1", in, currentInstruction); } List newList = createList(); - for (int i = 1; i < in->args.length; i++) { + 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); } @@ -1132,7 +1382,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", in, currentInstruction); } char* str = in->args.args[0].value.value.data.stringVal; - int64_t idx = in->args.args[1].value.value.data.intVal; + 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 { @@ -1628,6 +1878,87 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop 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); diff --git a/src/interpreter.h b/src/interpreter.h index b3a3db6..a2df833 100644 --- a/src/interpreter.h +++ b/src/interpreter.h @@ -7,7 +7,7 @@ #include "include/uthash.h" typedef enum GroundRuntimeError { - ARG_TYPE_MISMATCH, TOO_FEW_ARGS, TOO_MANY_ARGS, UNKNOWN_LABEL, UNKNOWN_VARIABLE, LIST_ERROR, STRING_ERROR, MATH_ERROR, RETURN_TYPE_MISMATCH, FIXME + ARG_TYPE_MISMATCH, TOO_FEW_ARGS, TOO_MANY_ARGS, UNKNOWN_LABEL, UNKNOWN_VARIABLE, LIST_ERROR, STRING_ERROR, MATH_ERROR, RETURN_TYPE_MISMATCH, PREMATURE_EOF, INVALID_INSTRUCTION, FIXME } GroundRuntimeError; typedef enum GroundDebugInstructionType { @@ -36,8 +36,12 @@ typedef struct GroundDebugInstruction { char* arg; } GroundDebugInstruction; +GroundStruct parseStruct(GroundProgram* in, GroundScope* scope, size_t errorOffset); +GroundFunction* parseFunction(GroundProgram* in, size_t errorOffset); GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope); GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scope); + + #endif diff --git a/src/types.c b/src/types.c index 50271aa..d5b294f 100644 --- a/src/types.c +++ b/src/types.c @@ -74,7 +74,7 @@ GroundValue copyGroundValue(const GroundValue* gv) { break; case LIST: { List newList = createList(); - for (int i = 0; i < gv->data.listVal.size; i++) { + for (size_t i = 0; i < gv->data.listVal.size; i++) { // Recursive call to handle nested lists and other types appendToList(&newList, copyGroundValue(&gv->data.listVal.values[i])); } @@ -93,6 +93,10 @@ GroundValue copyGroundValue(const GroundValue* gv) { // FIXME newGv.data.customVal = gv->data.customVal; break; + case NONE: + default: { + + } } return newGv; } @@ -125,7 +129,7 @@ void printGroundValue(GroundValue* gv) { } case LIST: { printf("["); - for (int i = 0; i < gv->data.listVal.size; i++) { + for (size_t i = 0; i < gv->data.listVal.size; i++) { printGroundValue(&gv->data.listVal.values[i]); if (i < gv->data.listVal.size - 1) { printf(", "); @@ -152,7 +156,7 @@ void freeGroundValue(GroundValue* gv) { } if (gv->type == LIST && gv->data.listVal.values != NULL) { List* list = &gv->data.listVal; - for (int i = 0; i < list->size; i++) { + for (size_t i = 0; i < list->size; i++) { freeGroundValue(&list->values[i]); } free(list->values); @@ -394,7 +398,7 @@ void printGroundInstruction(GroundInstruction* gi) { break; } if (gi->type != CREATELABEL) printf(" "); - for (int i = 0; i < gi->args.length; i++) { + for (size_t i = 0; i < gi->args.length; i++) { if (gi->args.args[i].type == VALUE && gi->args.args[i].value.value.type == STRING) { printf("\""); printGroundArg(&gi->args.args[i]); @@ -428,7 +432,7 @@ void appendToList(List* list, GroundValue value) { list->values[list->size - 1] = value; } -ListAccess getListAt(List* list, int idx) { +ListAccess getListAt(List* list, size_t idx) { if (list == NULL) { printf("Expecting a List ptr, got a null pointer instead.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/cground\n"); exit(EXIT_FAILURE); @@ -446,7 +450,7 @@ ListAccess getListAt(List* list, int idx) { } } -ListAccessStatus setListAt(List* list, int idx, GroundValue value) { +ListAccessStatus setListAt(List* list, size_t idx, GroundValue value) { if (list == NULL) { printf("Expecting a List ptr, got a null pointer instead.\nThis is likely not an error with your Ground program.\nPlease report this issue to https://chsp.au/ground/cground\n"); exit(EXIT_FAILURE); diff --git a/src/types.h b/src/types.h index f5ac32e..b1803ee 100644 --- a/src/types.h +++ b/src/types.h @@ -234,10 +234,10 @@ void appendToList(List* list, GroundValue value); // Gets item at index (idx) from list (list). If there is an error, it // will be indicated in the status field. -ListAccess getListAt(List* list, int idx); +ListAccess getListAt(List* list, size_t idx); // Sets an item in list (list) at index (idx) to GroundValue (value). -ListAccessStatus setListAt(List* list, int idx, GroundValue value); +ListAccessStatus setListAt(List* list, size_t idx, GroundValue value); // Creates a Ground struct GroundStruct createStruct();