diff --git a/libs/collections/collections.c b/libs/collections/collections.c index 3b04f6e..639fc9d 100644 --- a/libs/collections/collections.c +++ b/libs/collections/collections.c @@ -2,7 +2,9 @@ #include #include "list.h" +#include "hashmap.h" void ground_init(GroundScope* scope) { groundAddValueToScope(scope, "List", createListStruct()); + groundAddValueToScope(scope, "Hashmap", createHashmapStruct()); } \ No newline at end of file diff --git a/libs/collections/hashmap.c b/libs/collections/hashmap.c new file mode 100644 index 0000000..7251881 --- /dev/null +++ b/libs/collections/hashmap.c @@ -0,0 +1,132 @@ +#include "hashmap.h" +#include +#include +#include +#include +#include +#include + +GroundValue hashmapStructSet(GroundScope* scope, List args) { + char* key = args.values[0].data.stringVal; + GroundValue value = args.values[1]; + + GroundVariable* ptrField = groundFindVariable(scope, "private_ptr"); + if (ptrField == NULL) { + ERROR("A field called \"private_ptr\" was not found", "FieldNotFound"); + } + HashmapItem* keys = (HashmapItem*)ptrField->value.data.intVal; + + HashmapItem* newItem = malloc(sizeof(HashmapItem)); + newItem->key = strdup(key); + newItem->value = value; + + HASH_ADD_KEYPTR(hh, keys, newItem->key, strlen(newItem->key), newItem); + + ptrField->value.data.intVal = (long long)keys; + + return groundCreateValue(INT, 0); +} + +GroundValue hashmapStructGet(GroundScope* scope, List args) { + char* query = args.values[0].data.stringVal; + + GroundVariable* ptrField = groundFindVariable(scope, "private_ptr"); + if (ptrField == NULL) { + ERROR("A field called \"private_ptr\" was not found", "FieldNotFound"); + } + HashmapItem* keys = (HashmapItem*)ptrField->value.data.intVal; + + HashmapItem* out; + + HASH_FIND_STR(keys, query, out); + + if (out == NULL) { + ERROR("Key was not found in hashmap (tip: use hashmap.getOr if you want to specify a fallback value)", "KeyNotFound"); + } + + return out->value; +} + +GroundValue hashmapStructGetOr(GroundScope* scope, List args) { + char* query = args.values[0].data.stringVal; + GroundValue fallback = args.values[1]; + + GroundVariable* ptrField = groundFindVariable(scope, "private_ptr"); + if (ptrField == NULL) { + ERROR("A field called \"private_ptr\" was not found", "FieldNotFound"); + } + HashmapItem* keys = (HashmapItem*)ptrField->value.data.intVal; + + HashmapItem* out; + + HASH_FIND_STR(keys, query, out); + + if (out == NULL) + return fallback; + + return out->value; +} + +GroundValue hashmapStructRemove(GroundScope* scope, List args) { + char* query = args.values[0].data.stringVal; + + GroundVariable* ptrField = groundFindVariable(scope, "private_ptr"); + if (ptrField == NULL) { + ERROR("A field called \"private_ptr\" was not found", "FieldNotFound"); + } + HashmapItem* keys = (HashmapItem*)ptrField->value.data.intVal; + + HashmapItem* out; + + HASH_FIND_STR(keys, query, out); + + if (out == NULL) { + ERROR("Key was not found in hashmap (tip: use hashmap.removeIfPresent if you want to avoid errors)", "KeyNotFound"); + } + + HASH_DEL(keys, out); + + free(out->key); + free(out); + + return groundCreateValue(INT, 0); +} + +GroundValue hashmapStructRemoveIfPresent(GroundScope* scope, List args) { + char* query = args.values[0].data.stringVal; + + GroundVariable* ptrField = groundFindVariable(scope, "private_ptr"); + if (ptrField == NULL) { + ERROR("A field called \"private_ptr\" was not found", "FieldNotFound"); + } + HashmapItem* keys = (HashmapItem*)ptrField->value.data.intVal; + + HashmapItem* out; + + HASH_FIND_STR(keys, query, out); + + if (out == NULL) { + return groundCreateValue(BOOL, false); + } + + HASH_DEL(keys, out); + + free(out->key); + free(out); + + return groundCreateValue(BOOL, true); +} + +GroundValue createHashmapStruct() { + GroundStruct hashmapStruct = groundCreateStruct(); + + groundAddFieldToStruct(&hashmapStruct, "private_ptr", groundCreateValue(INT, 0)); + + groundAddFunctionToStruct(&hashmapStruct, "set", hashmapStructSet, INT, 2, STRING, "key", ANY, "value"); // set a key in the hashmap + groundAddFunctionToStruct(&hashmapStruct, "get", hashmapStructGet, INT, 1, STRING, "key"); // get a key in the hashmap, throws KeyNotFound if the key does not exist + groundAddFunctionToStruct(&hashmapStruct, "getOr", hashmapStructGetOr, INT, 2, STRING, "key", ANY, "default"); // get a key in the hashmap, or fallback to a default value + groundAddFunctionToStruct(&hashmapStruct, "remove", hashmapStructRemove, INT, 1, STRING, "key"); // remove a key from the hashmap, throws KeyNotFound if the key does not exist + groundAddFunctionToStruct(&hashmapStruct, "removeIfPresent", hashmapStructRemoveIfPresent, BOOL, 1, STRING, "key"); // remove a key from the hashmap, returns true if the key was removed or false if it didn't exist + + return groundCreateValue(STRUCTVAL, hashmapStruct); +} \ No newline at end of file diff --git a/libs/collections/hashmap.h b/libs/collections/hashmap.h new file mode 100644 index 0000000..1258c6a --- /dev/null +++ b/libs/collections/hashmap.h @@ -0,0 +1,16 @@ +#ifndef HASHMAP_H +#define HASHMAP_H + +#include +#include +#include + +typedef struct { + GroundValue value; + char* key; + UT_hash_handle hh; +} HashmapItem; + +GroundValue createHashmapStruct(); + +#endif \ No newline at end of file diff --git a/libs/collections/list.c b/libs/collections/list.c index b264e0c..6eca89c 100644 --- a/libs/collections/list.c +++ b/libs/collections/list.c @@ -6,7 +6,6 @@ #include #include #include -#include const uint8_t STARTING_ELEMENTS = 2; @@ -25,9 +24,9 @@ GroundValue appendToListStruct(GroundScope* scope, List args) { ERROR("A field called \"memSize\" was not found", "FieldNotFound"); } - GroundVariable* ptrField = groundFindVariable(scope, "__ptr"); + GroundVariable* ptrField = groundFindVariable(scope, "private_ptr"); if (ptrField == NULL) { - ERROR("A field called \"__ptr\" was not found", "FieldNotFound"); + ERROR("A field called \"private_ptr\" was not found", "FieldNotFound"); } GroundValue* items = (GroundValue*)ptrField->value.data.intVal; @@ -77,9 +76,9 @@ GroundValue listStructAt(GroundScope* scope, List args) { ERROR(buffer, "OutOfBounds"); } - GroundVariable* ptrField = groundFindVariable(scope, "__ptr"); + GroundVariable* ptrField = groundFindVariable(scope, "private_ptr"); if (ptrField == NULL) { - ERROR("A field called \"__ptr\" was not found", "FieldNotFound"); + ERROR("A field called \"private_ptr\" was not found", "FieldNotFound"); } GroundValue* items = (GroundValue*)ptrField->value.data.intVal; @@ -102,9 +101,9 @@ GroundValue clearListStruct(GroundScope* scope, List args) { ERROR("A field called \"memSize\" was not found", "FieldNotFound"); } - GroundVariable* ptrField = groundFindVariable(scope, "__ptr"); + GroundVariable* ptrField = groundFindVariable(scope, "private_ptr"); if (ptrField == NULL) { - ERROR("A field called \"__ptr\" was not found", "FieldNotFound"); + ERROR("A field called \"private_ptr\" was not found", "FieldNotFound"); } GroundVariable* capacityField = groundFindVariable(scope, "capacity"); @@ -112,8 +111,13 @@ GroundValue clearListStruct(GroundScope* scope, List args) { 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!", "MemoryAllocationFailed"); + } + sizeField->value.data.intVal = 0; - ptrField->value.data.intVal = (int64_t)calloc(STARTING_ELEMENTS, sizeof(GroundValue)); + ptrField->value.data.intVal = (int64_t)newBuffer; capacityField->value.data.intVal = STARTING_ELEMENTS; memSizeField->value.data.intVal = sizeof(GroundValue) * STARTING_ELEMENTS; @@ -138,9 +142,9 @@ GroundValue insertIntoListStruct(GroundScope* scope, List args) { ERROR("A field called \"memSize\" was not found", "FieldNotFound"); } - GroundVariable* ptrField = groundFindVariable(scope, "__ptr"); + GroundVariable* ptrField = groundFindVariable(scope, "private_ptr"); if (ptrField == NULL) { - ERROR("A field called \"__ptr\" was not found", "FieldNotFound"); + ERROR("A field called \"private_ptr\" was not found", "FieldNotFound"); } GroundValue* items = (GroundValue*)ptrField->value.data.intVal; @@ -204,9 +208,9 @@ GroundValue listStructDelete(GroundScope* scope, List args) { ERROR("A field called \"memSize\" was not found", "FieldNotFound"); } - GroundVariable* ptrField = groundFindVariable(scope, "__ptr"); + GroundVariable* ptrField = groundFindVariable(scope, "private_ptr"); if (ptrField == NULL) { - ERROR("A field called \"__ptr\" was not found", "FieldNotFound"); + ERROR("A field called \"private_ptr\" was not found", "FieldNotFound"); } GroundValue* items = (GroundValue*)ptrField->value.data.intVal; @@ -270,9 +274,9 @@ GroundValue listStructSet(GroundScope* scope, List args) { } int64_t size = sizeField->value.data.intVal; - GroundVariable* ptrField = groundFindVariable(scope, "__ptr"); + GroundVariable* ptrField = groundFindVariable(scope, "private_ptr"); if (ptrField == NULL) { - ERROR("A field called \"__ptr\" was not found", "FieldNotFound"); + ERROR("A field called \"private_ptr\" was not found", "FieldNotFound"); } GroundValue* items = (GroundValue*)ptrField->value.data.intVal; @@ -333,9 +337,9 @@ GroundValue listStructContains(GroundScope* scope, List args) { } int64_t size = sizeField->value.data.intVal; - GroundVariable* ptrField = groundFindVariable(scope, "__ptr"); + GroundVariable* ptrField = groundFindVariable(scope, "private_ptr"); if (ptrField == NULL) { - ERROR("A field called \"__ptr\" was not found", "FieldNotFound"); + ERROR("A field called \"private_ptr\" was not found", "FieldNotFound"); } GroundValue* items = (GroundValue*)ptrField->value.data.intVal; @@ -361,9 +365,9 @@ GroundValue reverseListStruct(GroundScope* scope, List args) { } int64_t capacity = capacityField->value.data.intVal; - GroundVariable* ptrField = groundFindVariable(scope, "__ptr"); + GroundVariable* ptrField = groundFindVariable(scope, "private_ptr"); if (ptrField == NULL) { - ERROR("A field called \"__ptr\" was not found", "FieldNotFound"); + ERROR("A field called \"private_ptr\" was not found", "FieldNotFound"); } GroundValue* items = (GroundValue*)ptrField->value.data.intVal; @@ -389,9 +393,9 @@ GroundValue findListStruct(GroundScope* scope, List args) { } int64_t size = sizeField->value.data.intVal; - GroundVariable* ptrField = groundFindVariable(scope, "__ptr"); + GroundVariable* ptrField = groundFindVariable(scope, "private_ptr"); if (ptrField == NULL) { - ERROR("A field called \"__ptr\" was not found", "FieldNotFound"); + ERROR("A field called \"private_ptr\" was not found", "FieldNotFound"); } GroundValue* items = (GroundValue*)ptrField->value.data.intVal; @@ -413,9 +417,9 @@ GroundValue reserveListStruct(GroundScope* scope, List args) { } int64_t capacity = capacityField->value.data.intVal; - GroundVariable* ptrField = groundFindVariable(scope, "__ptr"); + GroundVariable* ptrField = groundFindVariable(scope, "private_ptr"); if (ptrField == NULL) { - ERROR("A field called \"__ptr\" was not found", "FieldNotFound"); + ERROR("A field called \"private_ptr\" was not found", "FieldNotFound"); } GroundValue* items = (GroundValue*)ptrField->value.data.intVal; @@ -447,20 +451,19 @@ GroundValue createListStruct() { 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, "private_ptr", groundCreateValue(INT, items)); // pointer to internal list struct + groundAddFieldToStruct(&listStruct, "private_ptr", groundCreateValue(INT, items)); // pointer to internal list struct - groundAddFunctionToStruct(&listStruct, "append", appendToListStruct, INT, 1, INT, "value"); // append item to end of list - groundAddFunctionToStruct(&listStruct, "string", listStructToString, STRING, 0); // convert list to string - groundAddFunctionToStruct(&listStruct, "insert", insertIntoListStruct, INT, 2, INT, "value", INT, "index"); // insert value at index - groundAddFunctionToStruct(&listStruct, "delete", listStructDelete, INT, 1, INT, "index"); // delete value at index + 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, INT, 1, INT, "index"); // get value at index groundAddFunctionToStruct(&listStruct, "clear", clearListStruct, INT, 0); // clear list - groundAddFunctionToStruct(&listStruct, "set", listStructSet, INT, 2, INT, "value", INT, "index"); // replace a value at an index + 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, INT, "value"); // returns true if value is in list + 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, INT, "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, "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 return groundCreateValue(STRUCTVAL, listStruct); } \ No newline at end of file