Add lists (MEMORY ERRORS)

This commit is contained in:
2025-12-01 10:15:37 +11:00
parent 0b917a6702
commit 6eeccf58fe
5 changed files with 202 additions and 16 deletions

View File

@@ -30,6 +30,10 @@ void runtimeError(GroundRuntimeError error, char* what, GroundInstruction* where
printf("UnknownVariable"); printf("UnknownVariable");
break; break;
} }
case LIST_ERROR: {
printf("ListError");
break;
}
default: default:
case FIXME: { case FIXME: {
printf("FIXME (please report issue to https://chsp.au/ground/cground)"); 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) { void addVariable(GroundVariable **head, const char *id, GroundValue data) {
GroundVariable* variable = findVariable(*head, id); GroundVariable* variable = findVariable(*head, id);
if (variable) { if (variable) {
freeGroundValue(&variable->value);
deleteVariable(head, variable); deleteVariable(head, variable);
} }
GroundVariable* item = malloc(sizeof(GroundVariable)); GroundVariable* item = malloc(sizeof(GroundVariable));
@@ -243,9 +248,7 @@ void interpretGroundInstruction(GroundInstruction inst, GroundScope* scope) {
* VARIABLES AND LISTS * VARIABLES AND LISTS
* These instructions are for initializing variables and lists. * These instructions are for initializing variables and lists.
* Instructions: * Instructions:
* set, gettype, exists * set, gettype, exists, setlist, setlistat, getlistat, getlistsize, listappend
* WIP Instructions:
* setlist, setlistat, getlistat, getlistsize, listappend
*/ */
case SET: { case SET: {
if (in->args.length < 2) { if (in->args.length < 2) {
@@ -333,6 +336,145 @@ void interpretGroundInstruction(GroundInstruction inst, GroundScope* scope) {
break; 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 * MATHS

View File

@@ -7,7 +7,7 @@
#include "include/uthash.h" #include "include/uthash.h"
typedef enum GroundRuntimeError { 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; } GroundRuntimeError;
typedef struct GroundLabel { typedef struct GroundLabel {

View File

@@ -37,6 +37,13 @@ GroundValue createBoolGroundValue(bool in) {
return gv; return gv;
} }
GroundValue createListGroundValue(List in) {
GroundValue gv;
gv.data.listVal = in;
gv.type = LIST;
return gv;
}
void printGroundValue(GroundValue* gv) { void printGroundValue(GroundValue* gv) {
switch (gv->type) { switch (gv->type) {
case INT: { case INT: {
@@ -74,6 +81,13 @@ void freeGroundValue(GroundValue* gv) {
if (gv->type == STRING && gv->data.stringVal != NULL) { if (gv->type == STRING && gv->data.stringVal != NULL) {
free(gv->data.stringVal); 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) { GroundArg createValueGroundArg(GroundValue value) {
@@ -159,6 +173,19 @@ GroundInstruction copyGroundInstruction(const GroundInstruction* inst) {
if (inst->args.args[i].value.value.type == STRING) { 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); 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 { } else {
newInst.args.args[i].value.refName = strdup(inst->args.args[i].value.refName); 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"); 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); 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) { 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"); 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); exit(EXIT_FAILURE);

View File

@@ -27,6 +27,16 @@ struct GroundValue;
struct List; 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. * Stores literal values created in a Ground program.
* Associated functions: * Associated functions:
@@ -41,21 +51,11 @@ typedef struct GroundValue {
char* stringVal; char* stringVal;
char charVal; char charVal;
bool boolVal; bool boolVal;
struct List* listVal; List listVal;
void* customVal; void* customVal;
} data; } data;
} GroundValue; } 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. * Indicates status when accessing a list.
* Associated functions: * Associated functions:
@@ -107,6 +107,9 @@ GroundValue createCharGroundValue(char in);
// Creates a GroundValue containing (in), with type BOOl. // Creates a GroundValue containing (in), with type BOOl.
GroundValue createBoolGroundValue(bool in); 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. // If (gv) contains any data stored on the heap, frees it.
void freeGroundValue(GroundValue* gv); void freeGroundValue(GroundValue* gv);

14
tests/list.grnd Normal file
View File

@@ -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