From f3ae278e530cb79b6f1389b2e8c50773104dbd86 Mon Sep 17 00:00:00 2001 From: Kyler Date: Thu, 27 Nov 2025 20:15:25 -0700 Subject: [PATCH 1/8] Added stack types --- SLS_C/include/sls/interpreter.h | 31 +++++++++++++++++++++++++++++++ SLS_C/src/interpreter.c | 25 +++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 SLS_C/include/sls/interpreter.h create mode 100644 SLS_C/src/interpreter.c diff --git a/SLS_C/include/sls/interpreter.h b/SLS_C/include/sls/interpreter.h new file mode 100644 index 0000000..8a8b3d6 --- /dev/null +++ b/SLS_C/include/sls/interpreter.h @@ -0,0 +1,31 @@ +// Kyler Olsen +// YREA SLS +// Interpreter Header +// November 2025 + +#ifndef SLS_INTERPRETER_H +#define SLS_INTERPRETER_H + +#include + +typedef enum { + STACK_IDENTIFIER, + STACK_I64, + STACK_I32, + STACK_I16, + STACK_I8, + STACK_U64, + STACK_U32, + STACK_U16, + STACK_U8, + STACK_FLOAT, + STACK_DOUBLE, + STACK_CHARACTER, + STACK_BOOLEAN, + STACK_TOKEN_STRING, +} StackTypes; + +extern const char *STACK_TYPES_NAMES[]; +extern const size_t STACK_TYPE_COUNT; + +#endif // SLS_INTERPRETER_H diff --git a/SLS_C/src/interpreter.c b/SLS_C/src/interpreter.c new file mode 100644 index 0000000..2940e8a --- /dev/null +++ b/SLS_C/src/interpreter.c @@ -0,0 +1,25 @@ +// Kyler Olsen +// YREA SLS +// Interpreter +// November 2025 + +#include "sls/interpreter.h" + +const char *STACK_TYPES_NAMES[] = { + "Identifier", + "64-bit Integer", + "32-bit Integer", + "16-bit Integer", + "8-bit Integer", + "64-bit U Integer", + "32-bit U Integer", + "16-bit U Integer", + "8-bit U Integer", + "Float", + "Double", + "Character", + "Boolean", + "Token String", +}; + +const size_t STACK_TYPE_COUNT = sizeof(STACK_TYPES_NAMES) / sizeof(*STACK_TYPES_NAMES); From d8f5ad44b584fb23e6d0cab7161d7adf09dcc606 Mon Sep 17 00:00:00 2001 From: Kyler Date: Fri, 28 Nov 2025 14:58:15 -0700 Subject: [PATCH 2/8] Added linked-list based stack --- SLS_C/include/sls/interpreter.h | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/SLS_C/include/sls/interpreter.h b/SLS_C/include/sls/interpreter.h index 8a8b3d6..a07a79a 100644 --- a/SLS_C/include/sls/interpreter.h +++ b/SLS_C/include/sls/interpreter.h @@ -8,6 +8,8 @@ #include +#include "sls/lexer.h" + typedef enum { STACK_IDENTIFIER, STACK_I64, @@ -23,9 +25,35 @@ typedef enum { STACK_CHARACTER, STACK_BOOLEAN, STACK_TOKEN_STRING, -} StackTypes; +} StackType; extern const char *STACK_TYPES_NAMES[]; extern const size_t STACK_TYPE_COUNT; +typedef struct StackItem { + StackType type; + union { + Identifier identifier; + int64_t i64; + int32_t i32; + int16_t i16; + int8_t i8; + uint64_t u64; + uint32_t u32; + uint16_t u16; + uint8_t u8; + float f32; + double f64; + char character; + Boolean boolean; + TokenString token_string; + }; + StackItem *next; +} StackItem; + +typedef struct { + StackItem *stack; + +} InterpreterState; + #endif // SLS_INTERPRETER_H From 404588d4911f9563849008bac33d61c733f10da4 Mon Sep 17 00:00:00 2001 From: Kyler Date: Fri, 28 Nov 2025 15:52:40 -0700 Subject: [PATCH 3/8] Started hash table --- SLS_C/include/sls/hash_table.h | 42 +++++++++++++++++++++++++++++++++ SLS_C/include/sls/interpreter.h | 5 ++-- SLS_C/src/hash_table.c | 6 +++++ 3 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 SLS_C/include/sls/hash_table.h create mode 100644 SLS_C/src/hash_table.c diff --git a/SLS_C/include/sls/hash_table.h b/SLS_C/include/sls/hash_table.h new file mode 100644 index 0000000..5c4d1be --- /dev/null +++ b/SLS_C/include/sls/hash_table.h @@ -0,0 +1,42 @@ +// Kyler Olsen +// YREA SLS +// Hash Table Header +// November 2025 + +#ifndef SLS_HASH_TABLE_H +#define SLS_HASH_TABLE_H + +#include + +#include "sls/string.h" + +typedef struct Bucket { + size_t key_hash_b; + void *item; + struct Bucket *next; +} Bucket; + +typedef struct { + size_t buckets_count; + Bucket *buckets; +} HashTable; + +// Initializes a HashTable with buckets_count number of buckets. Returns NULL on Memory Allocation Error. +HashTable *init_hash_table(size_t buckets_count); +// Frees memory owned by the HashTable. +void del_hash_table(HashTable *ht); + +// The hash functions used by HashTable. Returns the hash of the string. +// static size_t sls_hash_a(SlsStr key); // For bucket index (in HashTable). +// static size_t sls_hash_b(SlsStr key); // For bucket key_id (in Bucket). +// Calculates the bucket index based on the number of buckets in the HashTable and the hash (a) of the string. +// static size_t bucket_index(const HashTable *ht, SlsStr key); + +// Inserts an item into the HashTable based on its key. Returns FALSE on Memory Allocation Error. +Boolean hash_table_put(HashTable *ht, SlsStr key, void *item); +// Gets the item associated with the given key. Returns default item if not found. +void *hash_table_get(const HashTable *ht, SlsStr key, void *default_item); +// Deletes the item associated with the given key. Returns FALSE if item is not found. +Boolean hash_table_del(HashTable *ht, SlsStr key); + +#endif // SLS_HASH_TABLE_H diff --git a/SLS_C/include/sls/interpreter.h b/SLS_C/include/sls/interpreter.h index a07a79a..1d3b5a0 100644 --- a/SLS_C/include/sls/interpreter.h +++ b/SLS_C/include/sls/interpreter.h @@ -9,6 +9,7 @@ #include #include "sls/lexer.h" +#include "sls/hash_table.h" typedef enum { STACK_IDENTIFIER, @@ -48,12 +49,12 @@ typedef struct StackItem { Boolean boolean; TokenString token_string; }; - StackItem *next; + struct StackItem *next; } StackItem; typedef struct { StackItem *stack; - + HashTable functions; } InterpreterState; #endif // SLS_INTERPRETER_H diff --git a/SLS_C/src/hash_table.c b/SLS_C/src/hash_table.c new file mode 100644 index 0000000..40bdb02 --- /dev/null +++ b/SLS_C/src/hash_table.c @@ -0,0 +1,6 @@ +// Kyler Olsen +// YREA SLS +// Hash Table +// November 2025 + +#include "sls/hash_table.h" From e94d316af832cb7ff0040b5eb3d5ab3bf3125085 Mon Sep 17 00:00:00 2001 From: Kyler Date: Fri, 28 Nov 2025 16:20:11 -0700 Subject: [PATCH 4/8] Worked on hash functions --- SLS_C/include/sls/hash_table.h | 14 +++----- SLS_C/src/hash_table.c | 65 ++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 10 deletions(-) diff --git a/SLS_C/include/sls/hash_table.h b/SLS_C/include/sls/hash_table.h index 5c4d1be..61be87d 100644 --- a/SLS_C/include/sls/hash_table.h +++ b/SLS_C/include/sls/hash_table.h @@ -11,27 +11,21 @@ #include "sls/string.h" typedef struct Bucket { - size_t key_hash_b; + uint32_t key_hash_b; void *item; struct Bucket *next; } Bucket; typedef struct { size_t buckets_count; - Bucket *buckets; + Bucket *buckets[]; } HashTable; -// Initializes a HashTable with buckets_count number of buckets. Returns NULL on Memory Allocation Error. -HashTable *init_hash_table(size_t buckets_count); +// Initializes a HashTable with atleast buckets_count number of buckets. Returns NULL on Memory Allocation Error. +HashTable *init_hash_table(size_t min_buckets_count); // Frees memory owned by the HashTable. void del_hash_table(HashTable *ht); -// The hash functions used by HashTable. Returns the hash of the string. -// static size_t sls_hash_a(SlsStr key); // For bucket index (in HashTable). -// static size_t sls_hash_b(SlsStr key); // For bucket key_id (in Bucket). -// Calculates the bucket index based on the number of buckets in the HashTable and the hash (a) of the string. -// static size_t bucket_index(const HashTable *ht, SlsStr key); - // Inserts an item into the HashTable based on its key. Returns FALSE on Memory Allocation Error. Boolean hash_table_put(HashTable *ht, SlsStr key, void *item); // Gets the item associated with the given key. Returns default item if not found. diff --git a/SLS_C/src/hash_table.c b/SLS_C/src/hash_table.c index 40bdb02..2debda8 100644 --- a/SLS_C/src/hash_table.c +++ b/SLS_C/src/hash_table.c @@ -3,4 +3,69 @@ // Hash Table // November 2025 +#include + #include "sls/hash_table.h" +#include "sls/string.h" + +static size_t next_power_of_two(size_t n); + +HashTable *init_hash_table(size_t min_buckets_count); + +void del_hash_table(HashTable *ht); + +static inline uint32_t sls_hash_a(SlsStr key) { + // FNV-1a + uint32_t hash = 2166136261u; + const uint8_t *p = (const uint8_t *)key.str; + for (size_t i = 0; i < key.len; i++) { + hash ^= p[i]; + hash *= 16777619u; + } + return hash; +} + +static inline uint32_t sls_hash_b(SlsStr key) { + // Murmur2A + const uint32_t m = 0x5bd1e995; + uint32_t hash = 0; + + while (key.len >= 4) { + uint32_t k = (uint32_t)key.str[0] + | (uint32_t)key.str[1] << 8 + | (uint32_t)key.str[2] << 16 + | (uint32_t)key.str[3] << 24; + k *= m; + k ^= k >> 24; + k *= m; + + hash *= m; + hash ^= k; + + key.str += 4; + key.len -= 4; + } + + switch (key.len) { + case 3: hash ^= key.str[2] << 16; + case 2: hash ^= key.str[1] << 8; + case 1: hash ^= key.str[0]; + hash *= m; + } + + hash ^= hash >> 13; + hash *= m; + hash ^= hash >> 15; + + return hash; +} + +static inline size_t bucket_index(const HashTable *ht, SlsStr key) { + return sls_hash_a(key) & (ht->buckets_count - 1); +} + +Boolean hash_table_put(HashTable *ht, SlsStr key, void *item); + +void *hash_table_get(const HashTable *ht, SlsStr key, void *default_item); + +Boolean hash_table_del(HashTable *ht, SlsStr key); From 20434b20abefbd247df05a2a364a35baabbaa9d8 Mon Sep 17 00:00:00 2001 From: Kyler Date: Fri, 28 Nov 2025 16:20:35 -0700 Subject: [PATCH 5/8] Worked on hash table initialization --- SLS_C/src/hash_table.c | 44 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/SLS_C/src/hash_table.c b/SLS_C/src/hash_table.c index 2debda8..93961bf 100644 --- a/SLS_C/src/hash_table.c +++ b/SLS_C/src/hash_table.c @@ -8,11 +8,49 @@ #include "sls/hash_table.h" #include "sls/string.h" -static size_t next_power_of_two(size_t n); +static size_t next_power_of_two(size_t n) { + if (n < 2) return 2; + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; +#if SIZE_MAX > UINT32_MAX + n |= n >> 32; +#endif + n++; + return n; +} -HashTable *init_hash_table(size_t min_buckets_count); +HashTable *init_hash_table(size_t min_buckets_count) { + HashTable *ht = (HashTable *)malloc(sizeof(HashTable)); + if (!ht) return NULL; -void del_hash_table(HashTable *ht); + ht->buckets_count = next_power_of_two(min_buckets_count); + + ht->buckets = (Bucket **)calloc(ht->buckets_count, sizeof(Bucket *)); + if (!ht->buckets) { + free(ht); + return NULL; + } + + return ht; +} + +void del_hash_table(HashTable *ht) { + for (size_t i = 0; i < ht->buckets_count; i++) { + Bucket *next, *head; + head = (ht->buckets)+i; + while (head) { + next = head->next; + free(head); + head = next; + } + } + free(ht->buckets); + free(ht); +} static inline uint32_t sls_hash_a(SlsStr key) { // FNV-1a From 63541aac854bbdb493f73ce8d5a35e5905b189df Mon Sep 17 00:00:00 2001 From: Kyler Date: Fri, 28 Nov 2025 20:02:27 -0700 Subject: [PATCH 6/8] Finished hash table --- SLS_C/include/sls/hash_table.h | 3 +- SLS_C/src/hash_table.c | 102 ++++++++++++++++++++++++--------- 2 files changed, 76 insertions(+), 29 deletions(-) diff --git a/SLS_C/include/sls/hash_table.h b/SLS_C/include/sls/hash_table.h index 61be87d..3118055 100644 --- a/SLS_C/include/sls/hash_table.h +++ b/SLS_C/include/sls/hash_table.h @@ -11,7 +11,8 @@ #include "sls/string.h" typedef struct Bucket { - uint32_t key_hash_b; + uint32_t hash_a; + uint32_t hash_b; void *item; struct Bucket *next; } Bucket; diff --git a/SLS_C/src/hash_table.c b/SLS_C/src/hash_table.c index 93961bf..ab306c5 100644 --- a/SLS_C/src/hash_table.c +++ b/SLS_C/src/hash_table.c @@ -16,39 +16,34 @@ static size_t next_power_of_two(size_t n) { n |= n >> 4; n |= n >> 8; n |= n >> 16; -#if SIZE_MAX > UINT32_MAX - n |= n >> 32; -#endif + #if SIZE_MAX > UINT32_MAX + n |= n >> 32; + #endif n++; return n; } HashTable *init_hash_table(size_t min_buckets_count) { - HashTable *ht = (HashTable *)malloc(sizeof(HashTable)); + size_t buckets_count = next_power_of_two(min_buckets_count); + size_t size = sizeof(HashTable) + buckets_count * sizeof(Bucket); + HashTable *ht = (HashTable *)malloc(size); if (!ht) return NULL; - - ht->buckets_count = next_power_of_two(min_buckets_count); - - ht->buckets = (Bucket **)calloc(ht->buckets_count, sizeof(Bucket *)); - if (!ht->buckets) { - free(ht); - return NULL; - } - + ht->buckets_count = buckets_count; + for (size_t i = 0; i < buckets_count; i++) + ht->buckets[i] = NULL; return ht; } void del_hash_table(HashTable *ht) { for (size_t i = 0; i < ht->buckets_count; i++) { - Bucket *next, *head; - head = (ht->buckets)+i; + Bucket *head = ht->buckets[i]; while (head) { - next = head->next; + Bucket *next = head->next; + // TODO: Does item need to be freed? free(head); head = next; } } - free(ht->buckets); free(ht); } @@ -84,12 +79,13 @@ static inline uint32_t sls_hash_b(SlsStr key) { key.len -= 4; } - switch (key.len) { - case 3: hash ^= key.str[2] << 16; - case 2: hash ^= key.str[1] << 8; - case 1: hash ^= key.str[0]; - hash *= m; - } + if (key.len >= 3) + hash ^= key.str[2] << 16; + if (key.len >= 2) + hash ^= key.str[1] << 8; + if (key.len >= 1) + hash ^= key.str[0]; + hash *= m; hash ^= hash >> 13; hash *= m; @@ -98,12 +94,62 @@ static inline uint32_t sls_hash_b(SlsStr key) { return hash; } -static inline size_t bucket_index(const HashTable *ht, SlsStr key) { - return sls_hash_a(key) & (ht->buckets_count - 1); +Boolean hash_table_put(HashTable *ht, SlsStr key, void *item) { + uint32_t hash_a = sls_hash_a(key); + uint32_t hash_b = sls_hash_b(key); + size_t bucket_index = hash_a & (ht->buckets_count - 1); + + for (Bucket *node = ht->buckets[bucket_index]; node; node = node->next) { + if (node->hash_a == hash_a && node->hash_b == hash_b) { + // TODO: Does item need to be freed before being replaced? + node->item = item; + return TRUE; + } + } + + Bucket *bucket = (Bucket *)malloc(sizeof(Bucket)); + if (!bucket) + return FALSE; + bucket->hash_a = hash_a; + bucket->hash_b = hash_b; + bucket->item = item; + bucket->next = ht->buckets[bucket_index]; + ht->buckets[bucket_index] = bucket; + + return TRUE; } -Boolean hash_table_put(HashTable *ht, SlsStr key, void *item); +void *hash_table_get(const HashTable *ht, SlsStr key, void *default_item) { + uint32_t hash_a = sls_hash_a(key); + uint32_t hash_b = sls_hash_b(key); + size_t bucket_index = hash_a & (ht->buckets_count - 1); -void *hash_table_get(const HashTable *ht, SlsStr key, void *default_item); + for (Bucket *node = ht->buckets[bucket_index]; node; node = node->next) + if (node->hash_a == hash_a && node->hash_b == hash_b) + return node->item; -Boolean hash_table_del(HashTable *ht, SlsStr key); + return default_item; +} + +Boolean hash_table_del(HashTable *ht, SlsStr key) { + uint32_t hash_a = sls_hash_a(key); + uint32_t hash_b = sls_hash_b(key); + size_t bucket_index = hash_a & (ht->buckets_count - 1); + + Bucket *prev = NULL; + + for (Bucket *node = ht->buckets[bucket_index]; node; node = node->next) { + if (node->hash_a == hash_a && node->hash_b == hash_b) { + if (prev == NULL) + ht->buckets[bucket_index] = node->next; + else + prev->next = node->next; + // TODO: Does item need to be freed? + free(node); + return TRUE; + } + prev = node; + } + + return FALSE; +} From 95dba7e478008f246060b304b33482ff328ad140 Mon Sep 17 00:00:00 2001 From: Kyler Date: Fri, 28 Nov 2025 20:07:32 -0700 Subject: [PATCH 7/8] Added Hash Table Tests --- SLS_C/include/tests/tests.h | 1 + SLS_C/tests/hash_table_tests.c | 383 +++++++++++++++++++++++++++++++++ SLS_C/tests/tests.c | 2 + 3 files changed, 386 insertions(+) create mode 100644 SLS_C/tests/hash_table_tests.c diff --git a/SLS_C/include/tests/tests.h b/SLS_C/include/tests/tests.h index 33b9a7d..2d89011 100644 --- a/SLS_C/include/tests/tests.h +++ b/SLS_C/include/tests/tests.h @@ -39,6 +39,7 @@ typedef struct { TestsReport run_string_tests(); TestsReport run_lexer_tests(); +TestsReport run_hash_table_tests(); TestsReport run_extra_tests(); #endif // SLS_TESTS_H diff --git a/SLS_C/tests/hash_table_tests.c b/SLS_C/tests/hash_table_tests.c new file mode 100644 index 0000000..0ea1935 --- /dev/null +++ b/SLS_C/tests/hash_table_tests.c @@ -0,0 +1,383 @@ +// Kyler Olsen +// YREA SLS +// Hash Table Tests +// November 2025 + +#include +#include +#include +#include + +#include "sls/string.h" +#include "sls/hash_table.h" +#include "tests/tests.h" + +static const size_t NUM_HASH_TABLE_TESTS = 12; + +static TestResult pass_hash_table_test(SlsStr test_name) { + TestResult result = {.name = test_name, .status = TEST_NOT_IMPLEMENTED}; + result.status = TEST_PASS; + return result; +} + +static TestResult fail_hash_table_test(SlsStr test_name, SlsStr message) { + TestResult result = { .name = test_name, .status = TEST_NOT_IMPLEMENTED }; + result.status = TEST_ERROR; + result.message = sls_str_cpy(message); + + if (result.message.str == NULL) { + result.error = (SlsError){ SLS_STR("Out Of Memory Error."), 1 }; + result.status = TEST_ERROR; + } + return result; +} + +// Test init_hash_table and del_hash_table +static TestResult test_init_and_delete() { + HashTable *ht = init_hash_table(10); + if (!ht) return fail_hash_table_test(SLS_STR("test_init_and_delete"), SLS_STR("Initialization failed")); + + if (ht->buckets_count < 10) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_init_and_delete"), SLS_STR("Bucket count less than minimum")); + } + + del_hash_table(ht); + return pass_hash_table_test(SLS_STR("test_init_and_delete")); +} + +// Test basic put and get operations +static TestResult test_put_and_get() { + HashTable *ht = init_hash_table(10); + if (!ht) return fail_hash_table_test(SLS_STR("test_put_and_get"), SLS_STR("Initialization failed")); + + int value1 = 42; + int value2 = 84; + SlsStr key1 = SLS_STR("key1"); + SlsStr key2 = SLS_STR("key2"); + + if (!hash_table_put(ht, key1, &value1)) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_put_and_get"), SLS_STR("Put operation failed")); + } + + if (!hash_table_put(ht, key2, &value2)) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_put_and_get"), SLS_STR("Second put operation failed")); + } + + int *retrieved1 = (int*)hash_table_get(ht, key1, NULL); + int *retrieved2 = (int*)hash_table_get(ht, key2, NULL); + + if (!retrieved1 || *retrieved1 != 42) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_put_and_get"), SLS_STR("Get operation failed for key1")); + } + + if (!retrieved2 || *retrieved2 != 84) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_put_and_get"), SLS_STR("Get operation failed for key2")); + } + + del_hash_table(ht); + return pass_hash_table_test(SLS_STR("test_put_and_get")); +} + +// Test get with non-existent key returns default +static TestResult test_get_nonexistent_key() { + HashTable *ht = init_hash_table(10); + if (!ht) return fail_hash_table_test(SLS_STR("test_get_nonexistent_key"), SLS_STR("Initialization failed")); + + int default_value = -1; + SlsStr key = SLS_STR("nonexistent"); + + int *result = (int*)hash_table_get(ht, key, &default_value); + if (result != &default_value) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_get_nonexistent_key"), SLS_STR("Did not return default value")); + } + + del_hash_table(ht); + return pass_hash_table_test(SLS_STR("test_get_nonexistent_key")); +} + +// Test updating existing key +static TestResult test_update_existing_key() { + HashTable *ht = init_hash_table(10); + if (!ht) return fail_hash_table_test(SLS_STR("test_update_existing_key"), SLS_STR("Initialization failed")); + + int value1 = 100; + int value2 = 200; + SlsStr key = SLS_STR("update_key"); + + hash_table_put(ht, key, &value1); + hash_table_put(ht, key, &value2); + + int *result = (int*)hash_table_get(ht, key, NULL); + if (!result || *result != 200) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_update_existing_key"), SLS_STR("Update failed")); + } + + del_hash_table(ht); + return pass_hash_table_test(SLS_STR("test_update_existing_key")); +} + +// Test delete operation +static TestResult test_delete_key() { + HashTable *ht = init_hash_table(10); + if (!ht) return fail_hash_table_test(SLS_STR("test_delete_key"), SLS_STR("Initialization failed")); + + int value = 42; + SlsStr key = SLS_STR("delete_key"); + + hash_table_put(ht, key, &value); + + if (!hash_table_del(ht, key)) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_delete_key"), SLS_STR("Delete returned FALSE")); + } + + int default_val = -1; + int *result = (int*)hash_table_get(ht, key, &default_val); + if (result != &default_val) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_delete_key"), SLS_STR("Key still exists after delete")); + } + + del_hash_table(ht); + return pass_hash_table_test(SLS_STR("test_delete_key")); +} + +// Test delete non-existent key returns FALSE +static TestResult test_delete_nonexistent_key() { + HashTable *ht = init_hash_table(10); + if (!ht) return fail_hash_table_test(SLS_STR("test_delete_nonexistent_key"), SLS_STR("Initialization failed")); + + SlsStr key = SLS_STR("nonexistent"); + + if (hash_table_del(ht, key)) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_delete_nonexistent_key"), SLS_STR("Delete returned TRUE for nonexistent key")); + } + + del_hash_table(ht); + return pass_hash_table_test(SLS_STR("test_delete_nonexistent_key")); +} + +// Test collision handling (multiple keys in same bucket) +static TestResult test_collision_handling() { + HashTable *ht = init_hash_table(1); // Force collisions with single bucket + if (!ht) return fail_hash_table_test(SLS_STR("test_collision_handling"), SLS_STR("Initialization failed")); + + int val1 = 1, val2 = 2, val3 = 3; + SlsStr key1 = SLS_STR("key1"); + SlsStr key2 = SLS_STR("key2"); + SlsStr key3 = SLS_STR("key3"); + + hash_table_put(ht, key1, &val1); + hash_table_put(ht, key2, &val2); + hash_table_put(ht, key3, &val3); + + int *r1 = (int*)hash_table_get(ht, key1, NULL); + int *r2 = (int*)hash_table_get(ht, key2, NULL); + int *r3 = (int*)hash_table_get(ht, key3, NULL); + + if (!r1 || *r1 != 1 || !r2 || *r2 != 2 || !r3 || *r3 != 3) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_collision_handling"), SLS_STR("Collision handling failed")); + } + + del_hash_table(ht); + return pass_hash_table_test(SLS_STR("test_collision_handling")); +} + +// Test empty string key +static TestResult test_empty_key() { + HashTable *ht = init_hash_table(10); + if (!ht) return fail_hash_table_test(SLS_STR("test_empty_key"), SLS_STR("Initialization failed")); + + int value = 99; + SlsStr key = SLS_STR(""); + + if (!hash_table_put(ht, key, &value)) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_empty_key"), SLS_STR("Put with empty key failed")); + } + + int *result = (int*)hash_table_get(ht, key, NULL); + if (!result || *result != 99) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_empty_key"), SLS_STR("Get with empty key failed")); + } + + del_hash_table(ht); + return pass_hash_table_test(SLS_STR("test_empty_key")); +} + +// Test long key +static TestResult test_long_key() { + HashTable *ht = init_hash_table(10); + if (!ht) return fail_hash_table_test(SLS_STR("test_long_key"), SLS_STR("Initialization failed")); + + char long_key_str[256]; + for (size_t i = 0; i < 255; i++) long_key_str[i] = 'A'; + long_key_str[255] = '\0'; + + SlsStr key = sls_str_malloc(long_key_str, 255); + int value = 777; + + if (!hash_table_put(ht, key, &value)) { + sls_str_free(&key); + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_long_key"), SLS_STR("Put with long key failed")); + } + + int *result = (int*)hash_table_get(ht, key, NULL); + if (!result || *result != 777) { + sls_str_free(&key); + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_long_key"), SLS_STR("Get with long key failed")); + } + + sls_str_free(&key); + del_hash_table(ht); + return pass_hash_table_test(SLS_STR("test_long_key")); +} + +// Test multiple operations sequence +static TestResult test_multiple_operations() { + HashTable *ht = init_hash_table(10); + if (!ht) return fail_hash_table_test(SLS_STR("test_multiple_operations"), SLS_STR("Initialization failed")); + + int vals[5] = {10, 20, 30, 40, 50}; + SlsStr keys[5] = {SLS_STR("k1"), SLS_STR("k2"), SLS_STR("k3"), SLS_STR("k4"), SLS_STR("k5")}; + + // Insert all + for (int i = 0; i < 5; i++) { + if (!hash_table_put(ht, keys[i], &vals[i])) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_multiple_operations"), SLS_STR("Insert failed")); + } + } + + // Delete some + hash_table_del(ht, keys[1]); + hash_table_del(ht, keys[3]); + + // Verify remaining + int *r0 = (int*)hash_table_get(ht, keys[0], NULL); + int *r2 = (int*)hash_table_get(ht, keys[2], NULL); + int *r4 = (int*)hash_table_get(ht, keys[4], NULL); + + if (!r0 || *r0 != 10 || !r2 || *r2 != 30 || !r4 || *r4 != 50) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_multiple_operations"), SLS_STR("Get after delete failed")); + } + + // Verify deleted + int def = -1; + int *r1 = (int*)hash_table_get(ht, keys[1], &def); + int *r3 = (int*)hash_table_get(ht, keys[3], &def); + + if (r1 != &def || r3 != &def) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_multiple_operations"), SLS_STR("Deleted keys still present")); + } + + del_hash_table(ht); + return pass_hash_table_test(SLS_STR("test_multiple_operations")); +} + +// Test storing NULL pointers +static TestResult test_null_value() { + HashTable *ht = init_hash_table(10); + if (!ht) return fail_hash_table_test(SLS_STR("test_null_value"), SLS_STR("Initialization failed")); + + SlsStr key = SLS_STR("null_key"); + + if (!hash_table_put(ht, key, NULL)) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_null_value"), SLS_STR("Put with NULL value failed")); + } + + int default_val = -1; + void *result = hash_table_get(ht, key, &default_val); + + // Should retrieve NULL, not default + if (result == &default_val) { + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_null_value"), SLS_STR("NULL value not stored correctly")); + } + + del_hash_table(ht); + return pass_hash_table_test(SLS_STR("test_null_value")); +} + +// Test large number of entries +static TestResult test_large_capacity() { + HashTable *ht = init_hash_table(10); + if (!ht) return fail_hash_table_test(SLS_STR("test_large_capacity"), SLS_STR("Initialization failed")); + + const size_t COUNT = 100; + int *values = malloc(sizeof(int) * COUNT); + SlsStr *keys = malloc(sizeof(SlsStr) * COUNT); + + for (size_t i = 0; i < COUNT; i++) { + values[i] = (int)i; + char key_buf[32]; + snprintf(key_buf, 32, "key_%zu", i); + keys[i] = sls_str_malloc(key_buf, strlen(key_buf)); + + if (!hash_table_put(ht, keys[i], &values[i])) { + for (size_t j = 0; j <= i; j++) sls_str_free(&keys[j]); + free(keys); + free(values); + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_large_capacity"), SLS_STR("Put failed in bulk insert")); + } + } + + // Verify all entries + for (size_t i = 0; i < COUNT; i++) { + int *result = (int*)hash_table_get(ht, keys[i], NULL); + if (!result || *result != (int)i) { + for (size_t j = 0; j < COUNT; j++) sls_str_free(&keys[j]); + free(keys); + free(values); + del_hash_table(ht); + return fail_hash_table_test(SLS_STR("test_large_capacity"), SLS_STR("Get failed in bulk verify")); + } + } + + for (size_t i = 0; i < COUNT; i++) sls_str_free(&keys[i]); + free(keys); + free(values); + del_hash_table(ht); + return pass_hash_table_test(SLS_STR("test_large_capacity")); +} + +// Run all hash table tests +TestsReport run_hash_table_tests() { + TestsReport report = { + .section = SLS_STR("hash_table_tests"), + .count = NUM_HASH_TABLE_TESTS, + .tests = malloc(sizeof(TestResult) * NUM_HASH_TABLE_TESTS) + }; + + size_t i = 0; + report.tests[i++] = test_init_and_delete(); + report.tests[i++] = test_put_and_get(); + report.tests[i++] = test_get_nonexistent_key(); + report.tests[i++] = test_update_existing_key(); + report.tests[i++] = test_delete_key(); + report.tests[i++] = test_delete_nonexistent_key(); + report.tests[i++] = test_collision_handling(); + report.tests[i++] = test_empty_key(); + report.tests[i++] = test_long_key(); + report.tests[i++] = test_multiple_operations(); + report.tests[i++] = test_null_value(); + report.tests[i++] = test_large_capacity(); + + return report; +} diff --git a/SLS_C/tests/tests.c b/SLS_C/tests/tests.c index 218f349..08d5f12 100644 --- a/SLS_C/tests/tests.c +++ b/SLS_C/tests/tests.c @@ -85,6 +85,8 @@ int main(void) { test_report(run_string_tests(), &counts); printf(" ========== Lexer Tests ==========\n"); test_report(run_lexer_tests(), &counts); + printf(" ========== Hash Table Tests ==========\n"); + test_report(run_hash_table_tests(), &counts); printf(" ========== Extra Tests ==========\n"); test_report(run_extra_tests(), &counts); From 390fc1981dcb60bc8d470aad5217c017d27b4e74 Mon Sep 17 00:00:00 2001 From: Kyler Date: Fri, 28 Nov 2025 20:34:52 -0700 Subject: [PATCH 8/8] Created Interpreter Interface --- SLS_C/include/sls/interpreter.h | 4 ++++ SLS_C/src/interpreter.c | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/SLS_C/include/sls/interpreter.h b/SLS_C/include/sls/interpreter.h index 1d3b5a0..782f860 100644 --- a/SLS_C/include/sls/interpreter.h +++ b/SLS_C/include/sls/interpreter.h @@ -57,4 +57,8 @@ typedef struct { HashTable functions; } InterpreterState; +InterpreterState *interpreter_create(); +void execute(InterpreterState *interpreter, LexerTokenResult *token); +void interpreter_delete(InterpreterState *interpreter); + #endif // SLS_INTERPRETER_H diff --git a/SLS_C/src/interpreter.c b/SLS_C/src/interpreter.c index 2940e8a..5d66282 100644 --- a/SLS_C/src/interpreter.c +++ b/SLS_C/src/interpreter.c @@ -3,7 +3,9 @@ // Interpreter // November 2025 +#include "sls/string.h" #include "sls/interpreter.h" +#include "sls/lexer.h" const char *STACK_TYPES_NAMES[] = { "Identifier", @@ -23,3 +25,11 @@ const char *STACK_TYPES_NAMES[] = { }; const size_t STACK_TYPE_COUNT = sizeof(STACK_TYPES_NAMES) / sizeof(*STACK_TYPES_NAMES); + +static Boolean hash_table_put_funcs(HashTable *ht, SlsStr key, TokenString *item) { + return hash_table_put(ht, key, (void *)item); +} + +static TokenString* hash_table_get_funcs(const HashTable *ht, SlsStr key, TokenString *default_item) { + return (TokenString*)hash_table_get(ht, key, (void *)default_item); +}