Files
cground/libs/collections/list.c

514 lines
19 KiB
C
Raw Normal View History

2026-04-11 16:06:34 +10:00
#include "list.h"
#include <groundext.h>
#include <groundvm.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const uint8_t STARTING_ELEMENTS = 2;
GroundStruct listStruct = {};
2026-04-11 16:06:34 +10:00
GroundValue appendToListStruct(GroundScope* scope, List args) {
GroundValue newValue = args.values[0];
// grab fields we need from struct
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
GroundVariable* memSizeField = groundFindVariable(scope, "memSize");
if (memSizeField == NULL) {
ERROR("A field called \"memSize\" was not found", "FieldNotFound");
}
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
2026-04-11 16:06:34 +10:00
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
2026-04-11 16:06:34 +10:00
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
GroundVariable* capacityField = groundFindVariable(scope, "capacity");
2026-04-11 17:04:54 +10:00
if (capacityField == NULL) {
2026-04-11 16:06:34 +10:00
ERROR("A field called \"capacity\" was not found", "FieldNotFound");
}
int64_t capacity = capacityField->value.data.intVal;
sizeField->value.data.intVal++;
if (capacity <= sizeField->value.data.intVal) {
capacity *= 2;
capacityField->value.data.intVal = capacity;
uint64_t newSize = sizeof(GroundValue) * capacity;
memSizeField->value.data.intVal = newSize;
items = realloc(items, newSize);
if (items == NULL) {
ERROR("Failed to allocate memory when increasing list size!", "AllocFail");
2026-04-11 16:06:34 +10:00
}
2026-04-11 17:04:54 +10:00
ptrField->value.data.intVal = (int64_t)items;
2026-04-11 16:06:34 +10:00
}
items[sizeField->value.data.intVal - 1] = newValue;
return groundCreateValue(INT, 0);
}
GroundValue listStructAt(GroundScope* scope, List args) {
int64_t index = args.values[0].data.intVal;
2026-04-11 16:06:34 +10:00
if (index < 0) {
ERROR("Attempt to access list at negative index", "OutOfBounds");
}
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
2026-04-11 17:04:54 +10:00
int64_t size = sizeField->value.data.intVal;
2026-04-11 16:06:34 +10:00
if (index >= size) {
char buffer[512];
sprintf(buffer, "Attempt to access list at index %ld when list is of length %ld", index, size);
ERROR(buffer, "OutOfBounds");
}
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
2026-04-11 16:06:34 +10:00
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
2026-04-11 16:06:34 +10:00
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
return items[index];
}
GroundValue listStructToString(GroundScope* scope, List args) {
return groundCreateValue(STRING, "TODO");
}
GroundValue clearListStruct(GroundScope* scope, List args) {
// grab fields we need from struct
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
GroundVariable* memSizeField = groundFindVariable(scope, "memSize");
if (memSizeField == NULL) {
ERROR("A field called \"memSize\" was not found", "FieldNotFound");
}
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
2026-04-11 16:06:34 +10:00
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
2026-04-11 16:06:34 +10:00
}
GroundVariable* capacityField = groundFindVariable(scope, "capacity");
2026-04-11 17:04:54 +10:00
if (capacityField == NULL) {
2026-04-11 16:06:34 +10:00
ERROR("A field called \"capacity\" was not found", "FieldNotFound");
}
GroundValue* newBuffer = calloc(STARTING_ELEMENTS, sizeof(GroundValue));
if (newBuffer == NULL) {
ERROR("Failed to allocate memory when clearing list!", "AllocFail");
}
2026-04-11 16:06:34 +10:00
sizeField->value.data.intVal = 0;
ptrField->value.data.intVal = (int64_t)newBuffer;
2026-04-11 16:06:34 +10:00
capacityField->value.data.intVal = STARTING_ELEMENTS;
memSizeField->value.data.intVal = sizeof(GroundValue) * STARTING_ELEMENTS;
return groundCreateValue(INT, 0);
}
GroundValue insertIntoListStruct(GroundScope* scope, List args) {
GroundValue value = args.values[0];
int64_t index = args.values[1].data.intVal;
2026-04-11 16:06:34 +10:00
if (index < 0) {
ERROR("Attempt to insert element into list at negative index", "OutOfBounds");
}
// grab fields we need from struct
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
GroundVariable* memSizeField = groundFindVariable(scope, "memSize");
if (memSizeField == NULL) {
ERROR("A field called \"memSize\" was not found", "FieldNotFound");
}
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
2026-04-11 16:06:34 +10:00
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
2026-04-11 16:06:34 +10:00
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
GroundVariable* capacityField = groundFindVariable(scope, "capacity");
2026-04-11 17:04:54 +10:00
if (capacityField == NULL) {
2026-04-11 16:06:34 +10:00
ERROR("A field called \"capacity\" was not found", "FieldNotFound");
}
int64_t capacity = capacityField->value.data.intVal;
if (sizeField->value.data.intVal <= index) {
sizeField->value.data.intVal = index+1;
} else {
sizeField->value.data.intVal++;
}
// check if we have enough space to insert the element
if (capacity <= sizeField->value.data.intVal+1) {
// not enough space, double size
capacity = sizeField->value.data.intVal + 1;
capacityField->value.data.intVal = capacity;
}
// allocate new buffer
GroundValue* newBuffer = calloc(capacity, sizeof(GroundValue));
if (newBuffer == NULL) {
ERROR("Failed to allocate memory when increasing list size!", "AllocFail");
2026-04-11 16:06:34 +10:00
}
2026-04-11 17:04:54 +10:00
ptrField->value.data.intVal = (int64_t)newBuffer;
memSizeField->value.data.intVal = sizeof(GroundValue) * capacity;
2026-04-11 16:06:34 +10:00
// copy elements from 0 to i-1 into new buffer
memcpy(newBuffer, items, sizeof(GroundValue) * index);
// insert element at i
newBuffer[index] = value;
// copy elements 0 to i+1 into new buffer
memcpy(newBuffer + (index+1), items + index, sizeof(GroundValue) * (sizeField->value.data.intVal - index));
// free older buffer
free(items);
// retur null
return groundCreateValue(INT, 0);
}
GroundValue listStructDelete(GroundScope* scope, List args) {
int64_t index = args.values[0].data.intVal;
2026-04-11 16:06:34 +10:00
if (index < 0) {
ERROR("Attempt to remove element into list at negative index", "OutOfBounds");
}
// grab fields we need from struct
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
GroundVariable* memSizeField = groundFindVariable(scope, "memSize");
if (memSizeField == NULL) {
ERROR("A field called \"memSizvoid initLists(GroundScope* scope);e\" was not found", "FieldNotFound");
2026-04-11 16:06:34 +10:00
}
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
2026-04-11 16:06:34 +10:00
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
2026-04-11 16:06:34 +10:00
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
GroundVariable* capacityField = groundFindVariable(scope, "capacity");
2026-04-11 17:04:54 +10:00
if (capacityField == NULL) {
2026-04-11 16:06:34 +10:00
ERROR("A field called \"capacity\" was not found", "FieldNotFound");
}
int64_t capacity = capacityField->value.data.intVal;
printf("size = %ld, index = %ld\n", sizeField->value.data.intVal, index);
if (sizeField->value.data.intVal < index) {
char buffer[512];
sprintf(buffer, "Attempt to delete element at index %ld when list is of size %ld", index, sizeField->value.data.intVal);
ERROR(buffer, "OutOfBounds");
} else {
sizeField->value.data.intVal--;
}
int64_t size = sizeField->value.data.intVal;
// check if we have enough space to insert the element
if (capacity > size+1) {
// too much space, make list smaller
capacity = size + 1;
capacityField->value.data.intVal = capacity;
}
// allocate new buffer
GroundValue* newBuffer = calloc(capacity, sizeof(GroundValue));
if (newBuffer == NULL) {
ERROR("Failed to allocate memory when decreasing list size!", "AllocFail");
2026-04-11 16:06:34 +10:00
}
2026-04-11 17:04:54 +10:00
ptrField->value.data.intVal = (int64_t)newBuffer;
memSizeField->value.data.intVal = sizeof(GroundValue) * capacity;
2026-04-11 16:06:34 +10:00
2026-04-11 17:04:54 +10:00
// copy elements from 0 to i-1 into new buffer
2026-04-11 16:06:34 +10:00
if (index > 0)
memcpy(newBuffer, items, sizeof(GroundValue) * index);
// copy elements 0 to i+1 into new buffer
if (index < size)
memcpy(newBuffer + index, items + index + 1, sizeof(GroundValue) * (size - index));
// free older buffer
free(items);
// retur null
return groundCreateValue(INT, 0);
}
GroundValue listStructSet(GroundScope* scope, List args) {
GroundValue value = args.values[0];
int64_t index = args.values[1].data.intVal;
2026-04-11 16:06:34 +10:00
if (index < 0) {
ERROR("Attempt to set element in list at negative index", "OutOfBounds");
}
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
int64_t size = sizeField->value.data.intVal;
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
2026-04-11 16:06:34 +10:00
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
2026-04-11 16:06:34 +10:00
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
if (index >= size) {
insertIntoListStruct(scope, args);
} else {
items[index] = value;
}
return groundCreateValue(INT, 0);
}
GroundValue listStructIsEmpty(GroundScope* scope, List args) {
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
int64_t size = sizeField->value.data.intVal;
return groundCreateValue(BOOL, size <= 0);
}
bool areGroundValuesEqual(GroundValue a, GroundValue b) {
if (a.type != b.type) {
if (a.type == INT && b.type == DOUBLE) {
return a.data.intVal == b.data.doubleVal;
} else if (a.type == DOUBLE && b.type == INT) {
return a.data.doubleVal == b.data.intVal;
}
} else {
switch (a.type) {
case INT:
return a.data.intVal == b.data.intVal;
case DOUBLE:
return a.data.doubleVal == b.data.doubleVal;
case BOOL:
return a.data.boolVal == b.data.boolVal;
case CHAR:
return a.data.charVal == b.data.charVal;
case STRING:
return strcmp(a.data.stringVal, b.data.stringVal) == 0;
default:
return false;
}
}
return false;
}
GroundValue listStructContains(GroundScope* scope, List args) {
GroundValue targetValue = args.values[0];
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
int64_t size = sizeField->value.data.intVal;
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
2026-04-11 16:06:34 +10:00
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
2026-04-11 16:06:34 +10:00
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
for (int64_t i = 0; i < size; i++) {
2026-04-11 16:06:34 +10:00
if (areGroundValuesEqual(items[i], targetValue)) {
return groundCreateValue(BOOL, true);
}
}
return groundCreateValue(BOOL, false);
}
GroundValue reverseListStruct(GroundScope* scope, List args) {
2026-04-11 17:04:54 +10:00
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
int64_t size = sizeField->value.data.intVal;
GroundVariable* capacityField = groundFindVariable(scope, "capacity");
if (capacityField == NULL) {
ERROR("A field called \"capacity\" was not found", "FieldNotFound");
}
int64_t capacity = capacityField->value.data.intVal;
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
2026-04-11 17:04:54 +10:00
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
2026-04-11 17:04:54 +10:00
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
GroundValue* newBuffer = calloc(capacity, sizeof(GroundValue));
if (newBuffer == NULL) {
ERROR("Failed to allocate memory when reversing list!", "AllocFail");
}
2026-04-11 17:04:54 +10:00
int z = 0;
for (int i = size - 1; i >= 0; i--, z++) {
newBuffer[z] = items[i];
}
ptrField->value.data.intVal = (int64_t)newBuffer;
free(items);
2026-04-11 16:06:34 +10:00
return groundCreateValue(INT, 0);
}
2026-04-11 17:04:54 +10:00
GroundValue findListStruct(GroundScope* scope, List args) {
GroundValue targetValue = args.values[0];
GroundVariable* sizeField = groundFindVariable(scope, "size");
if (sizeField == NULL) {
ERROR("A field called \"size\" was not found", "FieldNotFound");
}
int64_t size = sizeField->value.data.intVal;
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
2026-04-11 17:04:54 +10:00
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
2026-04-11 17:04:54 +10:00
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
for (int64_t i = 0; i < size; i++) {
2026-04-11 17:04:54 +10:00
if (areGroundValuesEqual(items[i], targetValue)) {
return groundCreateValue(INT, i);
}
}
return groundCreateValue(INT, (int64_t)-1);
}
GroundValue reserveListStruct(GroundScope* scope, List args) {
int64_t amount = args.values[0].data.intVal;
GroundVariable* capacityField = groundFindVariable(scope, "capacity");
if (capacityField == NULL) {
ERROR("A field called \"capacity\" was not found", "FieldNotFound");
}
int64_t capacity = capacityField->value.data.intVal;
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
2026-04-11 17:04:54 +10:00
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
2026-04-11 17:04:54 +10:00
}
GroundValue* items = (GroundValue*)ptrField->value.data.intVal;
GroundVariable* memSizeField = groundFindVariable(scope, "memSize");
if (memSizeField == NULL) {
ERROR("A field called \"memSize\" was not found", "FieldNotFound");
}
if (capacity > amount) {
return groundCreateValue(BOOL, false);
}
capacityField->value.data.intVal = amount;
items = realloc(items, sizeof(GroundValue) * amount);
if (items == NULL) {
ERROR("Failed to allocate memory when reserving list space!", "AllocFail");
2026-04-11 17:04:54 +10:00
}
memSizeField->value.data.intVal = sizeof(GroundValue) * amount;
return groundCreateValue(BOOL, true);
}
GroundValue listStructConstructor(GroundScope* scope, List args) {
int64_t startingCapacity = args.values[0].data.intVal;
if (startingCapacity < 1) {
ERROR("List can't be less than 1 element in capacity on initialization!", "OutOfBounds");
}
2026-04-13 10:44:23 +10:00
GroundValue value = groundCreateValue(CUSTOM, &listStruct);
GroundObjectField *sizeField = groundFindField(*value.data.customVal, "size");
GroundObjectField *capacityField = groundFindField(*value.data.customVal, "capacity");
GroundObjectField *memSizeField = groundFindField(*value.data.customVal, "memSize");
GroundObjectField *ptrField = groundFindField(*value.data.customVal, "ptr");
2026-04-11 16:06:34 +10:00
GroundValue* items = calloc(STARTING_ELEMENTS, sizeof(GroundValue));
if (items == NULL) {
ERROR("Failed to allocate memory while creating list!", "AllocFail");
}
sizeField->value.data.intVal = 0;
2026-04-13 10:44:23 +10:00
capacityField->value.data.intVal = startingCapacity;
memSizeField->value.data.intVal = sizeof(GroundValue) * startingCapacity;
ptrField->value.data.intVal = (int64_t)items;
value.type = CUSTOM;
return value;
}
2026-04-11 16:06:34 +10:00
GroundValue destroyListStruct(GroundScope* scope, List args) {
GroundVariable* ptrField = groundFindVariable(scope, "ptr");
if (ptrField == NULL) {
ERROR("A field called \"ptr\" was not found", "FieldNotFound");
}
free((GroundValue*)ptrField->value.data.intVal);
return groundCreateValue(NONE);
}
void initLists(GroundScope* scope) {
listStruct = groundCreateStruct();
2026-04-11 16:06:34 +10:00
groundAddFieldToStruct(&listStruct, "size", groundCreateValue(INT, 0)); // number of elements
groundAddFieldToStruct(&listStruct, "memSize", groundCreateValue(INT, sizeof(GroundValue) * STARTING_ELEMENTS)); // number of bytes allocated
groundAddFieldToStruct(&listStruct, "capacity", groundCreateValue(INT, STARTING_ELEMENTS)); // number of elements that can fit in the currently allocated space
groundAddFieldToStruct(&listStruct, "ptr", groundCreateValue(INT, 0)); // pointer to internal list struct
2026-04-11 16:06:34 +10:00
groundAddFunctionToStruct(&listStruct, "init", listStructConstructor, INT, 1, INT, "startingCapacity"); // init struct (ground constructor)
groundAddFunctionToStruct(&listStruct, "append", appendToListStruct, INT, 1, ANY, "value"); // append item to end of list
groundAddFunctionToStruct(&listStruct, "insert", insertIntoListStruct, INT, 2, ANY, "value", INT, "index"); // insert value at index
groundAddFunctionToStruct(&listStruct, "remove", listStructDelete, INT, 1, INT, "index"); // delete value at index
groundAddFunctionToStruct(&listStruct, "at", listStructAt, ANY, 1, INT, "index"); // get value at index
2026-04-11 16:06:34 +10:00
groundAddFunctionToStruct(&listStruct, "clear", clearListStruct, INT, 0); // clear list
groundAddFunctionToStruct(&listStruct, "set", listStructSet, INT, 2, ANY, "value", INT, "index"); // replace a value at an index
2026-04-11 16:06:34 +10:00
groundAddFunctionToStruct(&listStruct, "isEmpty", listStructIsEmpty, BOOL, 0); // returns true if list is empty, otherwise false
groundAddFunctionToStruct(&listStruct, "contains", listStructContains, BOOL, 1, ANY, "value"); // returns true if value is in list
2026-04-11 17:04:54 +10:00
groundAddFunctionToStruct(&listStruct, "reverse", reverseListStruct, INT, 0); // return list struct in reverse order
groundAddFunctionToStruct(&listStruct, "find", findListStruct, INT, 1, ANY, "value"); // return index of value in list, if not found, returns -1
groundAddFunctionToStruct(&listStruct, "reserve", reserveListStruct, BOOL, 1, INT, "amount"); // ensure list capacity >= amount. returns true if the list's capacity was expanded
groundAddFunctionToStruct(&listStruct, "destructor", destroyListStruct, ANY,0);
groundAddNativeFunction(scope, "newList", listStructConstructor, CUSTOM, 1, INT, "startingCapacity");
groundAddNativeFunction(scope, "List_SOLS_CONSTRUCTOR", listStructConstructor, CUSTOM, 1, INT, "startingCapacity");
2026-04-11 16:06:34 +10:00
}