From ee40622ceb28a1f3a9ea22b719f7cb56907ad2d2 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Sat, 21 Jan 2017 21:19:12 -0800 Subject: [PATCH] Implement pattern compilation and freeing. --- src/pattern.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) diff --git a/src/pattern.c b/src/pattern.c index 147d6e0..1ec089f 100644 --- a/src/pattern.c +++ b/src/pattern.c @@ -1,6 +1,9 @@ #include "pattern.h" #include #include +#include +#include "sprs.h" +#include "ll.h" void _pattern_node_clear(pattern_node* to_clear){ to_clear->type = PNODE_CLEAR; @@ -135,3 +138,288 @@ void _pattern_chain_append_chain(pattern_chain* append_to, pattern_chain* to_app } } } + +int _pattern_node_foreach_free(void* node, va_list args){ + free(node); + return 0; +} + +void _pattern_node_find_all(pattern_node* node, sprs* set){ + if(node && !sprs_contains(set, node->id)){ + sprs_put(set, node->id, node); + if(node->type == PNODE_ANY) { + _pattern_node_find_all(node->data_u.any_s.next, set); + } else if(node->type == PNODE_CONNECT){ + _pattern_node_find_all(node->data_u.connect_s.next, set); + } else if(node->type == PNODE_FORK){ + _pattern_node_find_all(node->data_u.fork_s.left, set); + _pattern_node_find_all(node->data_u.fork_s.right, set); + } else if(node->type == PNODE_RANGE){ + _pattern_node_find_all(node->data_u.range_s.next, set); + } else if(node->type == PNODE_VALUE){ + _pattern_node_find_all(node->data_u.value_s.next, set); + } + } +} + +liblex_result _pattern_free(pattern_node* root, int size){ + sprs set; + liblex_result result; + sprs_init(&set); + result = (sprs_setup(&set, size) == LIBDS_SUCCESS) ? LIBLEX_SUCCESS : LIBLEX_MALLOC; + if(result == LIBLEX_SUCCESS){ + _pattern_node_find_all(root, &set); + sprs_foreach(&set, NULL, compare_always, _pattern_node_foreach_free); + } + sprs_free(&set); + return result; +} + +liblex_result _pattern_read_value(char* read_into, char* string, int* index){ + liblex_result result = LIBLEX_SUCCESS; + if(string[*index] == '\\'){ + (*index)++; + } + if(string[*index]) { + *read_into = string[*index]; + (*index)++; + } else { + result = LIBLEX_INVALID; + } + return result; +} + +liblex_result _pattern_build_or(pattern_chain** into, int* ids, char* string, int* index) { + pattern_node *tail_node; + liblex_result result = LIBLEX_SUCCESS; + result = _pattern_node_create_connect(&tail_node, (*ids)++, NULL); + if (result == LIBLEX_SUCCESS) { + result = _pattern_chain_create(into, NULL, tail_node); + } + + while (string[*index] && string[*index] != ']' && result == LIBLEX_SUCCESS) { + char from = '\0'; + char to = '\0'; + pattern_node *new_node = NULL; + pattern_node *fork_node = NULL; + pattern_node *add_node = NULL; + result = _pattern_read_value(&from, string, index); + if (result == LIBLEX_SUCCESS) { + if (string[*index] == '-') { + (*index)++; + result = _pattern_read_value(&to, string, index); + } + } + + if (result == LIBLEX_SUCCESS) { + if (to) { + result = _pattern_node_create_range(&new_node, (*ids)++, from, to, tail_node); + } else { + result = _pattern_node_create_value(&new_node, (*ids)++, from, tail_node); + } + add_node = new_node; + } + + if (result == LIBLEX_SUCCESS && (*into)->head) { + result = _pattern_node_create_fork(&fork_node, (*ids)++, (*into)->head, new_node); + if (result == LIBLEX_SUCCESS) { + add_node = fork_node; + } + } + + if (result == LIBLEX_SUCCESS) { + (*into)->head = add_node; + } else { + free(new_node); + free(fork_node); + } + } + + if (result != LIBLEX_SUCCESS) { + if (*into && (*into)->head) { + _pattern_free((*into)->head, *ids); + } else { + free(tail_node); + } + free(*into); + } + + return result; +} + +void _pattern_chain_append_chain_discard(pattern_chain* append_to, pattern_chain** to_append){ + _pattern_chain_append_chain(append_to, *to_append); + free(*to_append); + *to_append = NULL; +} + +liblex_result _pattern_build_chain(pattern_chain** into, int* ids, char* string, int* index){ + liblex_result result = LIBLEX_SUCCESS; + ll or_stack; + pattern_chain* current_chain = NULL; + pattern_chain* sub_chain = NULL; + int is_subchain = (*index > -1) && (string[*index] == '('); + + ll_init(&or_stack); + result = _pattern_chain_create(¤t_chain, NULL, NULL); + if(result == LIBLEX_SUCCESS){ + (*index)++; + } + + while(string[*index] && string[*index] != ')' && result == LIBLEX_SUCCESS){ + if(string[*index] == '('){ + _pattern_chain_append_chain_discard(current_chain, &sub_chain); + result = _pattern_build_chain(&sub_chain, ids, string, index); + } else if(string[*index] == '['){ + _pattern_chain_append_chain_discard(current_chain, &sub_chain); + result = _pattern_build_or(&sub_chain, ids, string, index); + } else if(string[*index] == '?' || string[*index] == '*' || string[*index] == '+'){ + if(sub_chain != NULL && sub_chain->head){ + pattern_node* connection_node = NULL; + pattern_node* fork_node = NULL; + + result = _pattern_node_create_connect(&connection_node, (*ids)++, NULL); + if(result == LIBLEX_SUCCESS){ + result = _pattern_node_create_fork(&fork_node, (*ids)++, sub_chain->head, connection_node); + } + + if(result == LIBLEX_SUCCESS){ + pattern_node** next_node = _pattern_node_get_next(sub_chain->tail); + if(next_node){ + *next_node = (string[*index] == '?') ? connection_node : fork_node; + sub_chain->head = (string[*index] == '+') ? sub_chain->head : fork_node; + sub_chain->tail = connection_node; + } else { + result = LIBLEX_INVALID; + } + } + + if(result == LIBLEX_SUCCESS){ + _pattern_chain_append_chain_discard(current_chain, &sub_chain); + (*index)++; + } else { + free(connection_node); + free(fork_node); + } + } else { + result = LIBLEX_INVALID; + } + } else if(string[*index] == '|'){ + _pattern_chain_append_chain_discard(current_chain, &sub_chain); + + if(current_chain->head){ + result = (ll_append(&or_stack, current_chain) == LIBDS_SUCCESS) ? LIBLEX_SUCCESS : LIBLEX_MALLOC; + } else { + result = LIBLEX_INVALID; + } + if(result == LIBLEX_SUCCESS){ + result = _pattern_chain_create(¤t_chain, NULL, NULL); + (*index)++; + } + } else { + char new_char = '\0'; + pattern_node* new_node = NULL; + + _pattern_chain_append_chain_discard(current_chain, &sub_chain); + result = _pattern_read_value(&new_char, string, index); + if(result == LIBLEX_SUCCESS){ + result = _pattern_chain_create(&sub_chain, NULL, NULL); + } + if(result == LIBLEX_SUCCESS){ + result = _pattern_node_create_value(&new_node, (*ids)++, new_char, NULL); + } + if(result == LIBLEX_SUCCESS){ + sub_chain->head = sub_chain->tail = new_node; + } else { + free(new_node); + } + } + } + + if(is_subchain && result == LIBLEX_SUCCESS && string[*index] != ')'){ + result = LIBLEX_INVALID; + } + + if(result == LIBLEX_SUCCESS){ + _pattern_chain_append_chain_discard(current_chain, &sub_chain); + } + + if(result == LIBLEX_SUCCESS && or_stack.head){ + pattern_node* connect_node = NULL; + + result = _pattern_node_create_connect(&connect_node, (*ids)++, NULL); + if(result == LIBLEX_SUCCESS){ + _pattern_chain_append_node(current_chain, connect_node); + } + while(or_stack.head && result == LIBLEX_SUCCESS){ + pattern_node* fork = NULL; + pattern_chain* new_chain = ll_pophead(&or_stack); + + if(new_chain && new_chain->head){ + result = _pattern_node_create_fork(&fork, (*ids)++, current_chain->head, new_chain->head); + if(result == LIBLEX_SUCCESS){ + _pattern_chain_append_node(new_chain, connect_node); + current_chain->head = fork; + } else { + _pattern_free(new_chain->head, *ids); + } + free(new_chain); + } + + } + } + + if(result != LIBLEX_SUCCESS){ + if(current_chain){ + _pattern_free(current_chain->head, *ids); + } + free(current_chain); + if(sub_chain){ + _pattern_free(sub_chain->head, *ids); + } + free(sub_chain); + while(or_stack.head){ + pattern_chain* new_chain = ll_pophead(&or_stack); + if(new_chain){ + _pattern_free(new_chain->head, *ids); + } + } + } + + if(result == LIBLEX_SUCCESS){ + *into = current_chain; + if(is_subchain) { + (*index)++; + } + } + + return result; +} + +liblex_result pattern_compile(pattern* ptrn, char* expression, int id){ + liblex_result result; + pattern_node* end_node = NULL; + pattern_chain* full_chain = NULL; + int index = -1; + int ids = 0; + + result = _pattern_build_chain(&full_chain, &ids, expression, &index); + if(result == LIBLEX_SUCCESS){ + result = _pattern_node_create_end(&end_node, ids, id); + } + if(result == LIBLEX_SUCCESS){ + _pattern_chain_append_node(full_chain, end_node); + ptrn->head = full_chain->head; + ptrn->size = ids + 1; + free(full_chain); + } else { + if(full_chain){ + _pattern_free(full_chain->head, ids); + } + } + + return result; +} +liblex_result pattern_free(pattern* ptrn){ + return _pattern_free(ptrn->head, ptrn->size); +}