diff --git a/src/interpreter.c b/src/interpreter.c index 2e8c65a..10aac7b 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -30,6 +30,10 @@ void runtimeError(GroundRuntimeError error, char* what, GroundInstruction* where printf("UnknownVariable"); break; } + case LIST_ERROR: { + printf("ListError"); + break; + } default: case FIXME: { printf("FIXME (please report issue to https://chsp.au/ground/cground)"); @@ -86,6 +90,7 @@ void deleteVariable(GroundVariable** head, GroundVariable *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)); @@ -243,9 +248,7 @@ void interpretGroundInstruction(GroundInstruction inst, GroundScope* scope) { * VARIABLES AND LISTS * These instructions are for initializing variables and lists. * Instructions: - * set, gettype, exists - * WIP Instructions: - * setlist, setlistat, getlistat, getlistsize, listappend + * set, gettype, exists, setlist, setlistat, getlistat, getlistsize, listappend */ case SET: { if (in->args.length < 2) { @@ -333,6 +336,145 @@ void interpretGroundInstruction(GroundInstruction inst, GroundScope* scope) { 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 (int i = 1; i < in->args.length; i++) { + if (in->args.args[i].type != VALUE) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for all args after arg 1", in, currentInstruction); + } + appendToList(&newList, in->args.args[i].value.value); + } + addVariable(scope->variables, in->args.args[0].value.refName, createListGroundValue(newList)); + break; + } + case SETLISTAT: { + if (in->args.length < 3) { + runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction); + } + if (in->args.args[0].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction); + } + if (in->args.args[1].type != VALUE && in->args.args[1].value.value.type != INT) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int value for arg 2", in, currentInstruction); + } + if (in->args.args[2].type != VALUE) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 3", in, currentInstruction); + } + GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName); + if (var == NULL) { + runtimeError(UNKNOWN_VARIABLE, NULL, in, currentInstruction); + } + GroundValue* value = &var->value; + if (value == NULL) { + runtimeError(FIXME, NULL, in, currentInstruction); + } + if (value->type != LIST) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction); + } + ListAccessStatus status = setListAt(&value->data.listVal, in->args.args[1].value.value.data.intVal, in->args.args[2].value.value); + switch (status) { + case LIST_OKAY: + return; + case LIST_OUT_OF_BOUNDS: + runtimeError(LIST_ERROR, "Out of bounds index", in, currentInstruction); + case LIST_FIXME: + default: + runtimeError(FIXME, "List related error", in, currentInstruction); + } + break; + } + case GETLISTAT: { + if (in->args.length < 2) { + runtimeError(TOO_FEW_ARGS, "Expecting 2 args", in, currentInstruction); + } + if (in->args.args[0].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction); + } + if (in->args.args[1].type != VALUE && in->args.args[1].value.value.type != INT) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an integer value for arg 2", in, currentInstruction); + } + if (in->args.args[2].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 3", in, currentInstruction); + } + GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName); + if (var == NULL) { + runtimeError(UNKNOWN_VARIABLE, NULL, in, currentInstruction); + } + GroundValue* value = &var->value; + if (value == NULL) { + runtimeError(FIXME, NULL, in, currentInstruction); + } + if (value->type != LIST) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction); + } + ListAccess status = getListAt(&value->data.listVal, in->args.args[1].value.value.data.intVal); + switch (status.status) { + case LIST_OKAY: { + addVariable(scope->variables, in->args.args[2].value.refName, *status.value); + break; + } + case LIST_OUT_OF_BOUNDS: + runtimeError(LIST_ERROR, "Out of bounds index", in, currentInstruction); + case LIST_FIXME: + default: + runtimeError(FIXME, "List related error", in, currentInstruction); + } + break; + } + case GETLISTSIZE: { + if (in->args.length < 2) { + runtimeError(TOO_FEW_ARGS, "Expecting 2 args", in, currentInstruction); + } + if (in->args.args[0].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction); + } + if (in->args.args[1].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction); + } + GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName); + if (var == NULL) { + runtimeError(UNKNOWN_VARIABLE, NULL, in, currentInstruction); + } + GroundValue* value = &var->value; + if (value == NULL) { + runtimeError(FIXME, NULL, in, currentInstruction); + } + if (value->type != LIST) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction); + } + addVariable(scope->variables, in->args.args[1].value.refName, createIntGroundValue(value->data.listVal.size)); + break; + } + case LISTAPPEND: { + if (in->args.length < 2) { + runtimeError(TOO_FEW_ARGS, "Expecting 2 args", in, currentInstruction); + } + if (in->args.args[0].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction); + } + if (in->args.args[1].type != VALUE) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", in, currentInstruction); + } + GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName); + if (var == NULL) { + runtimeError(UNKNOWN_VARIABLE, NULL, in, currentInstruction); + } + GroundValue* value = &var->value; + if (value == NULL) { + runtimeError(FIXME, NULL, in, currentInstruction); + } + if (value->type != LIST) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef to a List for arg 1", in, currentInstruction); + } + appendToList(&value->data.listVal, in->args.args[1].value.value); + break; + } /* * MATHS diff --git a/src/interpreter.h b/src/interpreter.h index 24e242a..c0bb0ad 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, FIXME + ARG_TYPE_MISMATCH, TOO_FEW_ARGS, TOO_MANY_ARGS, UNKNOWN_LABEL, UNKNOWN_VARIABLE, LIST_ERROR, FIXME } GroundRuntimeError; typedef struct GroundLabel { diff --git a/src/types.c b/src/types.c index d4fb35e..eff3d7a 100644 --- a/src/types.c +++ b/src/types.c @@ -37,6 +37,13 @@ GroundValue createBoolGroundValue(bool in) { return gv; } +GroundValue createListGroundValue(List in) { + GroundValue gv; + gv.data.listVal = in; + gv.type = LIST; + return gv; +} + void printGroundValue(GroundValue* gv) { switch (gv->type) { case INT: { @@ -74,6 +81,13 @@ void freeGroundValue(GroundValue* gv) { if (gv->type == STRING && gv->data.stringVal != NULL) { free(gv->data.stringVal); } + if (gv->type == LIST && gv->data.listVal.values != NULL) { + List* list = &gv->data.listVal; + for (int i = 0; i < list->size; i++) { + freeGroundValue(&list->values[i]); + } + free(list->values); + } } GroundArg createValueGroundArg(GroundValue value) { @@ -159,6 +173,19 @@ GroundInstruction copyGroundInstruction(const GroundInstruction* inst) { if (inst->args.args[i].value.value.type == STRING) { newInst.args.args[i].value.value.data.stringVal = strdup(inst->args.args[i].value.value.data.stringVal); } + if (inst->args.args[i].value.value.type == LIST) { + List* origList = &inst->args.args[i].value.value.data.listVal; + List newList = createList(); + for (int j = 0; j < origList->size; j++) { + GroundValue copiedValue = origList->values[j]; + if (copiedValue.type == STRING) { + copiedValue.data.stringVal = strdup(copiedValue.data.stringVal); + } + // Note: this doesn't handle nested lists - you'd need full recursion for that + appendToList(&newList, copiedValue); + } + newInst.args.args[i].value.value.data.listVal = newList; + } } else { newInst.args.args[i].value.refName = strdup(inst->args.args[i].value.refName); } @@ -323,7 +350,7 @@ void appendToList(List* list, GroundValue value) { 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); } - GroundValue* ptr = realloc(list, (list->size + 1) * sizeof(GroundValue)); + GroundValue* ptr = realloc(list->values, (list->size + 1) * sizeof(GroundValue)); if (ptr == NULL) { printf("There was an error allocating memory for a list.\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 1e1b9b8..9b50d2e 100644 --- a/src/types.h +++ b/src/types.h @@ -27,6 +27,16 @@ struct GroundValue; struct List; +/* + * Custom data type that stores Ground values. + * Associated functions: + * createList(), appendToList(), getListAt(), setListAt() + */ +typedef struct List { + size_t size; + struct GroundValue* values; +} List; + /* * Stores literal values created in a Ground program. * Associated functions: @@ -41,21 +51,11 @@ typedef struct GroundValue { char* stringVal; char charVal; bool boolVal; - struct List* listVal; + List listVal; void* customVal; } data; } GroundValue; -/* - * Custom data type that stores Ground values. - * Associated functions: - * createList(), appendToList(), getListAt(), setListAt() - */ -typedef struct List { - size_t size; - GroundValue* values; -} List; - /* * Indicates status when accessing a list. * Associated functions: @@ -107,6 +107,9 @@ GroundValue createCharGroundValue(char in); // Creates a GroundValue containing (in), with type BOOl. GroundValue createBoolGroundValue(bool in); +// Creates a GroundValue containing (in), with type LIST. +GroundValue createListGroundValue(List in); + // If (gv) contains any data stored on the heap, frees it. void freeGroundValue(GroundValue* gv); diff --git a/tests/list.grnd b/tests/list.grnd new file mode 100644 index 0000000..a9ea3e1 --- /dev/null +++ b/tests/list.grnd @@ -0,0 +1,14 @@ +# A cool list +setlist &favWords "hello" "there" "general" "kenobi" +println $favWords + +set &count 0 +set &passedThrough true + +@jmpbck +getlistat &favWords $count &tmp +println $tmp +add $count 1 &count +getlistsize &favWords &tmp2 +inequal $count $tmp2 &tmp3 +if $tmp3 %jmpbck