liblex/src/eval.c

268 lines
8.2 KiB
C

#include "eval.h"
#include <stdlib.h>
#include <string.h>
#include "pattern.h"
#include "pairmap.h"
#define EVAL_FOREACH_MALLOC 1
static liblex_result foreach_errors[2] = {
LIBLEX_SUCCESS,
LIBLEX_MALLOC
};
void eval_config_init(eval_config* config){
ll_init(&config->states);
}
liblex_result eval_config_add(eval_config* config, const char* ptrn, int pattern_id){
liblex_result result;
pattern* new_pattern;
eval_state* new_state;
new_pattern = malloc(sizeof(*new_pattern));
new_state = malloc(sizeof(*new_pattern));
if(new_pattern && new_state){
result = pattern_compile(new_pattern, ptrn, pattern_id);
if(result == LIBLEX_SUCCESS){
new_state->source = ptrn;
new_state->pattern = new_pattern;
result = (ll_append(&config->states, new_state) == LIBDS_SUCCESS) ?
LIBLEX_SUCCESS : LIBLEX_MALLOC;
}
if(result != LIBLEX_SUCCESS){
pattern_free(new_pattern);
}
} else {
result = LIBLEX_MALLOC;
}
if(result != LIBLEX_SUCCESS) {
free(new_pattern);
free(new_state);
}
return result;
}
liblex_result _eval_config_free_state(eval_state* state) {
liblex_result result = pattern_free(state->pattern);
free(state->pattern);
free(state);
return result;
}
liblex_result eval_config_remove(eval_config* config, const char* pattern, int pattern_id) {
liblex_result result = LIBLEX_SUCCESS;
ll_node* head = config->states.head;
while(head && result == LIBLEX_SUCCESS) {
eval_state* current_state = head->data;
if(strcmp(current_state->source, pattern) == 0 && current_state->pattern->head->pattern_id == pattern_id) {
ll_node* to_delete = head;
head = head->next;
*(to_delete->prev ? &to_delete->prev->next : &(config->states.head)) = to_delete->next;
*(to_delete->next ? &to_delete->next->prev : &(config->states.tail)) = to_delete->prev;
free(to_delete);
result = _eval_config_free_state(current_state);
} else {
head = head->next;
}
}
return result;
}
int _eval_config_foreach_free(void* data, va_list args){
return _eval_config_free_state(data) == LIBLEX_SUCCESS ? 0 : EVAL_FOREACH_MALLOC;
}
liblex_result eval_config_free(eval_config* config){
liblex_result result = foreach_errors[ll_foreach(&config->states, NULL, compare_always, _eval_config_foreach_free)];
ll_free(&config->states);
return result;
}
pattern_node* _eval_pattern_node_get_next(pattern_node *node){
pattern_node* to_return = NULL;
if(node->type == PNODE_VALUE){
to_return = node->data_u.value_s.next;
} else if(node->type == PNODE_ANY){
to_return = node->data_u.any_s.next;
} else if(node->type == PNODE_RANGE){
to_return = node->data_u.range_s.next;
} else if(node->type == PNODE_CONNECT){
to_return = node->data_u.connect_s.next;
}
return to_return;
}
liblex_result _eval_pairmap_add_node(ht *table, pattern_node *node){
liblex_result result = LIBLEX_SUCCESS;
if(node->type == PNODE_ANY || node->type == PNODE_VALUE || node->type == PNODE_RANGE ||
node->type == PNODE_END){
pairmap_key tmp_key;
result = PAIRMAP_PUT(table, &tmp_key, node->pattern_id, node->id, node) == LIBDS_SUCCESS ? LIBLEX_SUCCESS : LIBLEX_MALLOC;
} else if(node->type == PNODE_CONNECT){
result = _eval_pairmap_add_node(table, _eval_pattern_node_get_next(node));
} else if(node->type == PNODE_FORK){
result = _eval_pairmap_add_node(table, node->data_u.fork_s.left);
if(result == LIBLEX_SUCCESS){
result = _eval_pairmap_add_node(table, node->data_u.fork_s.right);
}
}
return result;
}
int _eval_foreach_add_node(void *data, va_list args){
eval_state* state = data;
ht* pairmap = va_arg(args, ht*);
pattern_node* pattern_head = state->pattern->head;
return _eval_pairmap_add_node(pairmap, pattern_head) == LIBLEX_SUCCESS? 0 : EVAL_FOREACH_MALLOC;
}
int _eval_node_matches(pattern_node *node, eval *eval){
int matches = 0;
char value = eval->string[eval->index];
if(node->type == PNODE_ANY){
matches = value != '\0';
} else if(node->type == PNODE_VALUE){
matches = value == node->data_u.value_s.value;
} else if(node->type == PNODE_RANGE) {
matches = value >= node->data_u.range_s.from && value <= node->data_u.range_s.to;
}
return matches;
}
int _eval_foreach_check_node(void *data, va_list args){
int inverted_matches = 1;
int return_code = 0;
pattern_node* node = data;
eval* evl = va_arg(args, eval*);
while(node && node->invert && inverted_matches) {
inverted_matches &= !_eval_node_matches(node, evl);
node = _eval_pattern_node_get_next(node);
}
if(inverted_matches && _eval_node_matches(node, evl)){
return_code = _eval_pairmap_add_node(evl->set_next, _eval_pattern_node_get_next(node)) == LIBLEX_SUCCESS ? 0 : EVAL_FOREACH_MALLOC;
if(return_code == 0){
evl->matched++;
}
} else if(node->type == PNODE_END){
match* new_match = malloc(sizeof(*new_match));
if(new_match){
new_match->from = evl->begin;
new_match->to = evl->index;
new_match->pattern = node->pattern_id;
return_code = ll_append(&evl->matches, new_match) == LIBDS_SUCCESS ? 0 : EVAL_FOREACH_MALLOC;
if(return_code){
free(new_match);
}
} else {
return_code = EVAL_FOREACH_MALLOC;
}
}
return return_code;
}
liblex_result _eval_step(eval *eval){
ht* tmp;
liblex_result result;
eval->matched = 0;
result = foreach_errors[ht_foreach(eval->set_current, NULL, compare_always, _eval_foreach_check_node,
eval)];
tmp = eval->set_current;
eval->set_current = eval->set_next;
eval->set_next = tmp;
if(eval->matched){
eval->index++;
}
ht_free(eval->set_next);
pairmap_init_ht(eval->set_next);
return result;
}
int _eval_foreach_find_match(void *data, va_list args){
match* current_match = data;
int* max_match_id = va_arg(args, int*);
match* fill_match = va_arg(args, match*);
if(*max_match_id < current_match->pattern || current_match->to > fill_match->to){
*max_match_id = current_match->pattern;
memcpy(fill_match, current_match, sizeof(*current_match));
}
return 0;
}
liblex_result eval_word(const char* string, size_t index, eval_config* config, match* mtch){
liblex_result result = LIBLEX_SUCCESS;
eval evl;
evl.index = evl.begin = index;
evl.string = string;
evl.matched = 0;
pairmap_init_ht(&evl.set_a);
pairmap_init_ht(&evl.set_b);
ll_init(&evl.matches);
evl.set_current = &evl.set_a;
evl.set_next = &evl.set_b;
result = foreach_errors[ll_foreach(&config->states, NULL, compare_always, _eval_foreach_add_node, evl.set_current)];
if(result == LIBLEX_SUCCESS){
do {
result = _eval_step(&evl);
} while(result == LIBLEX_SUCCESS && evl.matched && *(evl.string));
}
if(result == LIBLEX_SUCCESS && evl.matches.tail){
int largest_id = -1;
ll_foreach(&evl.matches, NULL, compare_always, _eval_foreach_find_match, &largest_id, mtch);
} else {
mtch->from = 0;
mtch->to = 0;
mtch->pattern = -1;
}
ll_foreach(&evl.matches, NULL, compare_always, eval_foreach_match_free);
ll_free(&evl.matches);
ht_free(&evl.set_a);
ht_free(&evl.set_b);
return result;
}
liblex_result eval_all(const char* string, size_t index, eval_config* config, ll* matches){
liblex_result result = LIBLEX_SUCCESS;
match* new_match = NULL;
int done = 0;
do {
new_match = malloc(sizeof(*new_match));
if(new_match){
result = eval_word(string, index, config, new_match);
if(result == LIBLEX_SUCCESS){
if(new_match->pattern < 0 || new_match->from - new_match->to == 0){
done = 1;
free(new_match);
if(string[index]){
result = LIBLEX_UNRECOGNIZED;
}
} else {
result = ll_append(matches, new_match) == LIBDS_SUCCESS ? LIBLEX_SUCCESS : LIBLEX_MALLOC;
}
}
if(result == LIBLEX_SUCCESS && string[index] != '\0'){
index += new_match->to - new_match->from;
}
} else {
result = LIBLEX_MALLOC;
}
} while (result == LIBLEX_SUCCESS && done == 0);
if(result != LIBLEX_SUCCESS){
ll_foreach(matches, NULL, compare_always, eval_foreach_match_free);
ll_clear(matches);
}
return result;
}
int eval_foreach_match_free(void *data, va_list args){
free(data);
return 0;
}