#include "list.h" #include #include #include #include #include #include #include const uint8_t STARTING_ELEMENTS = 2; GroundStruct listStruct = {}; 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"); if (ptrField == NULL) { ERROR("A field called \"ptr\" was not found", "FieldNotFound"); } GroundValue* items = (GroundValue*)ptrField->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; sizeField->value.data.intVal++; if (capacity <= sizeField->value.data.intVal) { capacity *= 2; capacityField->value.data.intVal = capacity; int64_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"); } ptrField->value.data.intVal = (int64_t)items; } 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; 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"); } int64_t size = sizeField->value.data.intVal; 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"); if (ptrField == NULL) { ERROR("A field called \"ptr\" was not found", "FieldNotFound"); } GroundValue* items = (GroundValue*)ptrField->value.data.intVal; return items[index]; } GroundValue listStructToString(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; GroundVariable* ptrField = groundFindVariable(scope, "ptr"); if (ptrField == NULL) { ERROR("A field called \"ptr\" was not found", "FieldNotFound"); } GroundValue* items = (GroundValue*)ptrField->value.data.intVal; 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"); if (ptrField == NULL) { ERROR("A field called \"ptr\" was not found", "FieldNotFound"); } GroundVariable* capacityField = groundFindVariable(scope, "capacity"); if (capacityField == NULL) { 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"); } sizeField->value.data.intVal = 0; ptrField->value.data.intVal = (int64_t)newBuffer; 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; 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"); if (ptrField == NULL) { ERROR("A field called \"ptr\" was not found", "FieldNotFound"); } GroundValue* items = (GroundValue*)ptrField->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; 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"); } ptrField->value.data.intVal = (int64_t)newBuffer; memSizeField->value.data.intVal = sizeof(GroundValue) * capacity; // 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; 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"); } GroundVariable* ptrField = groundFindVariable(scope, "ptr"); if (ptrField == NULL) { ERROR("A field called \"ptr\" was not found", "FieldNotFound"); } GroundValue* items = (GroundValue*)ptrField->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; 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"); } ptrField->value.data.intVal = (int64_t)newBuffer; memSizeField->value.data.intVal = sizeof(GroundValue) * capacity; // copy elements from 0 to i-1 into new buffer 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; 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"); if (ptrField == NULL) { ERROR("A field called \"ptr\" was not found", "FieldNotFound"); } 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"); if (ptrField == NULL) { ERROR("A field called \"ptr\" was not found", "FieldNotFound"); } GroundValue* items = (GroundValue*)ptrField->value.data.intVal; for (int64_t i = 0; i < size; i++) { if (areGroundValuesEqual(items[i], targetValue)) { return groundCreateValue(BOOL, true); } } return groundCreateValue(BOOL, false); } GroundValue reverseListStruct(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; 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"); if (ptrField == NULL) { ERROR("A field called \"ptr\" was not found", "FieldNotFound"); } 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"); } 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); return groundCreateValue(INT, 0); } 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"); if (ptrField == NULL) { ERROR("A field called \"ptr\" was not found", "FieldNotFound"); } GroundValue* items = (GroundValue*)ptrField->value.data.intVal; for (int64_t i = 0; i < size; i++) { 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"); if (ptrField == NULL) { ERROR("A field called \"ptr\" was not found", "FieldNotFound"); } 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"); } 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"); } 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"); GroundValue* items = calloc(STARTING_ELEMENTS, sizeof(GroundValue)); if (items == NULL) { ERROR("Failed to allocate memory while creating list!", "AllocFail"); } sizeField->value.data.intVal = 0; capacityField->value.data.intVal = startingCapacity; memSizeField->value.data.intVal = sizeof(GroundValue) * startingCapacity; ptrField->value.data.intVal = (int64_t)items; value.type = CUSTOM; return value; } GroundValue destroyListStruct(GroundScope* scope, List args) { printf("destroying\n"); 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); } GroundValue duplicateListStruct(GroundScope* scope, List args) { GroundObject* self = args.values[0].data.customVal; GroundObjectField *sizeField = groundFindField(*self, "size"); GroundObjectField *capacityField = groundFindField(*self, "capacity"); GroundObjectField *memSizeField = groundFindField(*self, "memSize"); GroundObjectField *ptrField = groundFindField(*self, "ptr"); GroundValue newSelf = groundCreateValue(CUSTOM, &listStruct); newSelf.type = CUSTOM; GroundObjectField *newSizeField = groundFindField(*newSelf.data.customVal, "size"); GroundObjectField *newCapacityField = groundFindField(*newSelf.data.customVal, "capacity"); GroundObjectField *newMemSizeField = groundFindField(*newSelf.data.customVal, "memSize"); GroundObjectField *newPtrField = groundFindField(*newSelf.data.customVal, "ptr"); groundAddValueToScope(scope, "size", sizeField->value); groundAddValueToScope(scope, "capacity", capacityField->value); groundAddValueToScope(scope, "memSize", memSizeField->value); GroundValue* newPtr = calloc(newCapacityField->value.data.intVal, sizeof(GroundValue)); if (!newPtr) { ERROR("Failed to allocate memory while calling copy constructor of List!", "AllocFail"); } memcpy(newPtr, (GroundValue*)ptrField->value.data.intVal, newMemSizeField->value.data.intVal); newPtrField->value.data.intVal = (int64_t)newPtr; return newSelf; } void initLists(GroundScope* scope) { listStruct = groundCreateStruct(); 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 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 groundAddFunctionToStruct(&listStruct, "clear", clearListStruct, INT, 0); // clear list groundAddFunctionToStruct(&listStruct, "set", listStructSet, INT, 2, ANY, "value", INT, "index"); // replace a value at an index 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 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); groundAddFunctionToStruct(&listStruct, "duplicator", duplicateListStruct, CUSTOM, 1, CUSTOM, "self"); groundAddNativeFunction(scope, "newList", listStructConstructor, CUSTOM, 1, INT, "startingCapacity"); groundAddNativeFunction(scope, "List_SOLS_CONSTRUCTOR", listStructConstructor, CUSTOM, 1, INT, "startingCapacity"); groundAddNativeFunction(scope, "string_SOLS_AS", listStructToString, STRING, 0); }