diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bdf8ae..b9a588c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ project(libabacus) add_compile_options(-pedantic -Wall) -add_library(abacus STATIC src/lexer.c src/util.c src/table.c src/parser.c src/libabacus.c src/tree.c src/debug.c src/parsetype.c src/reserved.c) +add_library(abacus STATIC src/lexer.c src/util.c src/table.c src/parser.c src/libabacus.c src/tree.c src/debug.c src/parsetype.c src/reserved.c src/trie.c) add_executable(libabacus src/main.c) add_subdirectory(external/liblex) diff --git a/include/trie.h b/include/trie.h new file mode 100644 index 0000000..00863c2 --- /dev/null +++ b/include/trie.h @@ -0,0 +1,63 @@ +#ifndef LIBABACUS_TRIE_H +#define LIBABACUS_TRIE_H + +#include "ll.h" +#include "result.h" + +/** + * A node in the trie. + */ +struct libab_trie_node_s { + /** + * The "child" node, which points + * to the next layer of the trie, housing + * children that have a prefix ending in this node's key value. + */ + struct libab_trie_node_s* child; + /** + * The "next" node, which points + * to the sibiling of this tree, wich has the same prefix + * as this node. + */ + struct libab_trie_node_s* next; + /** + * The values associated with the key ending with + * this node's key value. + */ + ll values; + /** + * The last letter of the key, which this node represents. + * The remainder of the key is encoded in nodes preceding this one. + */ + char key; +}; + +/** + * A struct that represents a trie. + */ +struct libab_trie_s { + /** + * The first search node in this trie. + */ + struct libab_trie_node_s* head; + /** + * The empty list returned if no value is found. + * Note that existing nodes return their own linked + * list of values, even if empty. However, for keys + * that don't exist as prefixes in the trie, + * this list is returned to maintain consistency: + * a list is always returned containing the values + * of the trie associated with the given key. + */ + ll empty_list; +}; + +typedef struct libab_trie_s libab_trie; +typedef struct libab_trie_node_s libab_trie_node; + +void libab_trie_init(libab_trie* trie); +libab_result libab_trie_put(libab_trie* trie, const char* key, void* value); +const ll* libab_trie_get(libab_trie* trie, const char* key); +void libab_trie_free(libab_trie* trie); + +#endif diff --git a/src/trie.c b/src/trie.c new file mode 100644 index 0000000..aba38e3 --- /dev/null +++ b/src/trie.c @@ -0,0 +1,92 @@ +#include "trie.h" +#include +#include "util.h" + +void libab_trie_init(libab_trie* trie) { + trie->head = NULL; + ll_init(&trie->empty_list); +} + +void _libab_trie_free(libab_trie_node* to_free) { + if(to_free == NULL) return; + _libab_trie_free(to_free->next); + _libab_trie_free(to_free->child); + ll_free(&to_free->values); + free(to_free); +} + +libab_result _libab_trie_put(libab_trie_node** node, const char* key, void* value) { + libab_result result = LIBAB_SUCCESS; + if((*node = malloc(sizeof(**node)))) { + (*node)->key = *key; + (*node)->next = NULL; + ll_init(&(*node)->values); + + if(*(key + 1)) { + result = _libab_trie_put(&(*node)->child, key + 1, value); + } else { + (*node)->child = NULL; + result = libab_convert_ds_result(ll_append(&(*node)->values, value)); + } + } else { + result = LIBAB_MALLOC; + } + + if(result != LIBAB_SUCCESS) { + free(*node); + *node = NULL; + } + + return result; +} + +libab_result _libab_trie_add(libab_trie_node* node, void* value) { + return libab_convert_ds_result(ll_append(&node->values, value)); +} + +libab_result libab_trie_put(libab_trie* trie, const char* key, void* value) { + libab_result result = LIBAB_SUCCESS; + libab_trie_node** current = &trie->head; + char search; + while(*key) { + search = *key; + while(*current && (*current)->key != search) { + current = &(*current)->next; + } + if(*current) { + if(*(key + 1)) { + current = &(*current)->child; + } else { + result = _libab_trie_add(*current, value); + } + key++; + } else { + result = _libab_trie_put(current, key, value); + break; + } + } + return result; +} + +const ll* libab_trie_get(libab_trie* trie, const char* key) { + libab_trie_node* current = trie->head; + while(current && *key) { + while(current && current->key != *key) { + current = current->next; + } + if(current == NULL) break; + + if(*(key + 1)) { + current = current->child; + key++; + } else { + return ¤t->values; + } + } + return &trie->empty_list; +} + +void libab_trie_free(libab_trie* trie) { + _libab_trie_free(trie->head); + trie->head = NULL; +}