/* retoor */ #include "nfa.h" #include #include nfa_t *nfa_create(void) { nfa_t *nfa = malloc(sizeof(nfa_t)); if (!nfa) return NULL; nfa->states = NULL; nfa->state_count = 0; nfa->capacity = 0; nfa->start = NULL; nfa->group_count = 0; return nfa; } void nfa_free(nfa_t *nfa) { if (!nfa) return; for (size_t i = 0; i < nfa->state_count; i++) { free(nfa->states[i]->transitions); free(nfa->states[i]); } free(nfa->states); free(nfa); } static bool nfa_grow(nfa_t *nfa) { size_t new_cap = nfa->capacity == 0 ? 16 : nfa->capacity * 2; if (new_cap > LOREG_MAX_STATES) { if (nfa->capacity >= LOREG_MAX_STATES) return false; new_cap = LOREG_MAX_STATES; } nfa_state_t **new_states = realloc(nfa->states, new_cap * sizeof(nfa_state_t *)); if (!new_states) return false; nfa->states = new_states; nfa->capacity = new_cap; return true; } nfa_state_t *nfa_add_state(nfa_t *nfa) { if (nfa->state_count >= nfa->capacity) { if (!nfa_grow(nfa)) return NULL; } nfa_state_t *state = malloc(sizeof(nfa_state_t)); if (!state) return NULL; state->id = (int)nfa->state_count; state->accepting = false; state->transitions = NULL; state->trans_count = 0; state->trans_capacity = 0; nfa->states[nfa->state_count++] = state; return state; } static bool transition_grow(nfa_state_t *state) { size_t new_cap = state->trans_capacity == 0 ? 4 : state->trans_capacity * 2; transition_t *new_trans = realloc(state->transitions, new_cap * sizeof(transition_t)); if (!new_trans) return false; state->transitions = new_trans; state->trans_capacity = new_cap; return true; } void nfa_add_transition(nfa_state_t *from, nfa_state_t *to, transition_type_t type, char value) { if (from->trans_count >= from->trans_capacity) { if (!transition_grow(from)) return; } transition_t *t = &from->transitions[from->trans_count++]; t->type = type; t->value = value; t->target = to; t->bracket = NULL; t->group_id = -1; } void nfa_add_bracket_transition(nfa_state_t *from, nfa_state_t *to, bracket_class_t *bracket) { if (from->trans_count >= from->trans_capacity) { if (!transition_grow(from)) return; } transition_t *t = &from->transitions[from->trans_count++]; t->type = TRANS_BRACKET; t->value = '\0'; t->target = to; t->bracket = bracket; t->group_id = -1; } void nfa_add_group_transition(nfa_state_t *from, nfa_state_t *to, transition_type_t type, int group_id) { if (from->trans_count >= from->trans_capacity) { if (!transition_grow(from)) return; } transition_t *t = &from->transitions[from->trans_count++]; t->type = type; t->value = '\0'; t->target = to; t->bracket = NULL; t->group_id = group_id; } static nfa_fragment_t build_nfa(nfa_t *nfa, ast_node_t *ast, loreg_error_t *error); static nfa_fragment_t build_char(nfa_t *nfa, char c, loreg_error_t *error) { nfa_fragment_t frag = {NULL, NULL}; nfa_state_t *start = nfa_add_state(nfa); nfa_state_t *accept = nfa_add_state(nfa); if (!start || !accept) { *error = LOREG_ERR_OUT_OF_MEMORY; return frag; } nfa_add_transition(start, accept, TRANS_CHAR, c); frag.start = start; frag.accept = accept; return frag; } static nfa_fragment_t build_dot(nfa_t *nfa, loreg_error_t *error) { nfa_fragment_t frag = {NULL, NULL}; nfa_state_t *start = nfa_add_state(nfa); nfa_state_t *accept = nfa_add_state(nfa); if (!start || !accept) { *error = LOREG_ERR_OUT_OF_MEMORY; return frag; } nfa_add_transition(start, accept, TRANS_DOT, '\0'); frag.start = start; frag.accept = accept; return frag; } static nfa_fragment_t build_class(nfa_t *nfa, transition_type_t type, loreg_error_t *error) { nfa_fragment_t frag = {NULL, NULL}; nfa_state_t *start = nfa_add_state(nfa); nfa_state_t *accept = nfa_add_state(nfa); if (!start || !accept) { *error = LOREG_ERR_OUT_OF_MEMORY; return frag; } nfa_add_transition(start, accept, type, '\0'); frag.start = start; frag.accept = accept; return frag; } static nfa_fragment_t build_bracket(nfa_t *nfa, bracket_class_t *bracket, loreg_error_t *error) { nfa_fragment_t frag = {NULL, NULL}; nfa_state_t *start = nfa_add_state(nfa); nfa_state_t *accept = nfa_add_state(nfa); if (!start || !accept) { *error = LOREG_ERR_OUT_OF_MEMORY; return frag; } nfa_add_bracket_transition(start, accept, bracket); frag.start = start; frag.accept = accept; return frag; } static nfa_fragment_t build_concat(nfa_t *nfa, ast_node_t *left, ast_node_t *right, loreg_error_t *error) { nfa_fragment_t frag = {NULL, NULL}; nfa_fragment_t left_frag = build_nfa(nfa, left, error); if (*error != LOREG_OK) return frag; nfa_fragment_t right_frag = build_nfa(nfa, right, error); if (*error != LOREG_OK) return frag; nfa_add_transition(left_frag.accept, right_frag.start, TRANS_EPSILON, '\0'); frag.start = left_frag.start; frag.accept = right_frag.accept; return frag; } static nfa_fragment_t build_alter(nfa_t *nfa, ast_node_t *left, ast_node_t *right, loreg_error_t *error) { nfa_fragment_t frag = {NULL, NULL}; nfa_state_t *start = nfa_add_state(nfa); nfa_state_t *accept = nfa_add_state(nfa); if (!start || !accept) { *error = LOREG_ERR_OUT_OF_MEMORY; return frag; } nfa_fragment_t left_frag = build_nfa(nfa, left, error); if (*error != LOREG_OK) return frag; nfa_fragment_t right_frag = build_nfa(nfa, right, error); if (*error != LOREG_OK) return frag; nfa_add_transition(start, left_frag.start, TRANS_EPSILON, '\0'); nfa_add_transition(start, right_frag.start, TRANS_EPSILON, '\0'); nfa_add_transition(left_frag.accept, accept, TRANS_EPSILON, '\0'); nfa_add_transition(right_frag.accept, accept, TRANS_EPSILON, '\0'); frag.start = start; frag.accept = accept; return frag; } static nfa_fragment_t build_star(nfa_t *nfa, ast_node_t *child, bool greedy, loreg_error_t *error) { nfa_fragment_t frag = {NULL, NULL}; nfa_state_t *start = nfa_add_state(nfa); nfa_state_t *accept = nfa_add_state(nfa); if (!start || !accept) { *error = LOREG_ERR_OUT_OF_MEMORY; return frag; } nfa_fragment_t child_frag = build_nfa(nfa, child, error); if (*error != LOREG_OK) return frag; if (greedy) { nfa_add_transition(start, child_frag.start, TRANS_EPSILON, '\0'); nfa_add_transition(start, accept, TRANS_EPSILON, '\0'); } else { nfa_add_transition(start, accept, TRANS_EPSILON, '\0'); nfa_add_transition(start, child_frag.start, TRANS_EPSILON, '\0'); } nfa_add_transition(child_frag.accept, child_frag.start, TRANS_EPSILON, '\0'); nfa_add_transition(child_frag.accept, accept, TRANS_EPSILON, '\0'); frag.start = start; frag.accept = accept; return frag; } static nfa_fragment_t build_plus(nfa_t *nfa, ast_node_t *child, bool greedy, loreg_error_t *error) { nfa_fragment_t frag = {NULL, NULL}; nfa_state_t *accept = nfa_add_state(nfa); if (!accept) { *error = LOREG_ERR_OUT_OF_MEMORY; return frag; } nfa_fragment_t child_frag = build_nfa(nfa, child, error); if (*error != LOREG_OK) return frag; if (greedy) { nfa_add_transition(child_frag.accept, child_frag.start, TRANS_EPSILON, '\0'); nfa_add_transition(child_frag.accept, accept, TRANS_EPSILON, '\0'); } else { nfa_add_transition(child_frag.accept, accept, TRANS_EPSILON, '\0'); nfa_add_transition(child_frag.accept, child_frag.start, TRANS_EPSILON, '\0'); } frag.start = child_frag.start; frag.accept = accept; return frag; } static nfa_fragment_t build_question(nfa_t *nfa, ast_node_t *child, bool greedy, loreg_error_t *error) { nfa_fragment_t frag = {NULL, NULL}; nfa_state_t *start = nfa_add_state(nfa); nfa_state_t *accept = nfa_add_state(nfa); if (!start || !accept) { *error = LOREG_ERR_OUT_OF_MEMORY; return frag; } nfa_fragment_t child_frag = build_nfa(nfa, child, error); if (*error != LOREG_OK) return frag; if (greedy) { nfa_add_transition(start, child_frag.start, TRANS_EPSILON, '\0'); nfa_add_transition(start, accept, TRANS_EPSILON, '\0'); } else { nfa_add_transition(start, accept, TRANS_EPSILON, '\0'); nfa_add_transition(start, child_frag.start, TRANS_EPSILON, '\0'); } nfa_add_transition(child_frag.accept, accept, TRANS_EPSILON, '\0'); frag.start = start; frag.accept = accept; return frag; } static nfa_fragment_t build_group(nfa_t *nfa, ast_node_t *child, int group_id, loreg_error_t *error) { nfa_fragment_t frag = {NULL, NULL}; nfa_state_t *start = nfa_add_state(nfa); nfa_state_t *accept = nfa_add_state(nfa); if (!start || !accept) { *error = LOREG_ERR_OUT_OF_MEMORY; return frag; } nfa_fragment_t child_frag = build_nfa(nfa, child, error); if (*error != LOREG_OK) return frag; nfa_add_group_transition(start, child_frag.start, TRANS_GROUP_START, group_id); nfa_add_group_transition(child_frag.accept, accept, TRANS_GROUP_END, group_id); if (group_id + 1 > nfa->group_count) { nfa->group_count = group_id + 1; } frag.start = start; frag.accept = accept; return frag; } static nfa_fragment_t build_anchor(nfa_t *nfa, transition_type_t type, loreg_error_t *error) { nfa_fragment_t frag = {NULL, NULL}; nfa_state_t *start = nfa_add_state(nfa); nfa_state_t *accept = nfa_add_state(nfa); if (!start || !accept) { *error = LOREG_ERR_OUT_OF_MEMORY; return frag; } nfa_add_transition(start, accept, type, '\0'); frag.start = start; frag.accept = accept; return frag; } static nfa_fragment_t build_quantifier(nfa_t *nfa, ast_node_t *child, int min, int max, bool greedy, loreg_error_t *error) { nfa_fragment_t frag = {NULL, NULL}; if (min == 0 && max == 0) { nfa_state_t *state = nfa_add_state(nfa); if (!state) { *error = LOREG_ERR_OUT_OF_MEMORY; return frag; } frag.start = state; frag.accept = state; return frag; } nfa_state_t *start = nfa_add_state(nfa); if (!start) { *error = LOREG_ERR_OUT_OF_MEMORY; return frag; } nfa_state_t *current = start; for (int i = 0; i < min; i++) { nfa_fragment_t rep = build_nfa(nfa, child, error); if (*error != LOREG_OK) return frag; nfa_add_transition(current, rep.start, TRANS_EPSILON, '\0'); current = rep.accept; } if (max < 0) { nfa_state_t *loop_start = nfa_add_state(nfa); nfa_state_t *accept = nfa_add_state(nfa); if (!loop_start || !accept) { *error = LOREG_ERR_OUT_OF_MEMORY; return frag; } nfa_add_transition(current, loop_start, TRANS_EPSILON, '\0'); nfa_fragment_t rep = build_nfa(nfa, child, error); if (*error != LOREG_OK) return frag; if (greedy) { nfa_add_transition(loop_start, rep.start, TRANS_EPSILON, '\0'); nfa_add_transition(loop_start, accept, TRANS_EPSILON, '\0'); } else { nfa_add_transition(loop_start, accept, TRANS_EPSILON, '\0'); nfa_add_transition(loop_start, rep.start, TRANS_EPSILON, '\0'); } nfa_add_transition(rep.accept, loop_start, TRANS_EPSILON, '\0'); frag.start = start; frag.accept = accept; } else { nfa_state_t *accept = nfa_add_state(nfa); if (!accept) { *error = LOREG_ERR_OUT_OF_MEMORY; return frag; } nfa_add_transition(current, accept, TRANS_EPSILON, '\0'); for (int i = min; i < max; i++) { nfa_fragment_t rep = build_nfa(nfa, child, error); if (*error != LOREG_OK) return frag; if (greedy) { nfa_add_transition(current, rep.start, TRANS_EPSILON, '\0'); } else { nfa_add_transition(current, accept, TRANS_EPSILON, '\0'); nfa_add_transition(current, rep.start, TRANS_EPSILON, '\0'); } if (greedy) { nfa_add_transition(rep.accept, accept, TRANS_EPSILON, '\0'); } current = rep.accept; } if (!greedy) { nfa_add_transition(current, accept, TRANS_EPSILON, '\0'); } frag.start = start; frag.accept = accept; } return frag; } static nfa_fragment_t build_nfa(nfa_t *nfa, ast_node_t *ast, loreg_error_t *error) { nfa_fragment_t frag = {NULL, NULL}; if (!ast) { nfa_state_t *state = nfa_add_state(nfa); if (!state) { *error = LOREG_ERR_OUT_OF_MEMORY; return frag; } frag.start = state; frag.accept = state; return frag; } switch (ast->type) { case AST_CHAR: return build_char(nfa, ast->value, error); case AST_DOT: return build_dot(nfa, error); case AST_CONCAT: return build_concat(nfa, ast->left, ast->right, error); case AST_ALTER: return build_alter(nfa, ast->left, ast->right, error); case AST_STAR: return build_star(nfa, ast->left, ast->quant.greedy, error); case AST_PLUS: return build_plus(nfa, ast->left, ast->quant.greedy, error); case AST_QUESTION: return build_question(nfa, ast->left, ast->quant.greedy, error); case AST_GROUP: return build_group(nfa, ast->left, ast->group_id, error); case AST_ANCHOR_START: return build_anchor(nfa, TRANS_ANCHOR_START, error); case AST_ANCHOR_END: return build_anchor(nfa, TRANS_ANCHOR_END, error); case AST_BRACKET: return build_bracket(nfa, ast->bracket, error); case AST_QUANTIFIER: return build_quantifier(nfa, ast->left, ast->quant.min, ast->quant.max, ast->quant.greedy, error); case AST_CLASS_DIGIT: return build_class(nfa, TRANS_CLASS_DIGIT, error); case AST_CLASS_WORD: return build_class(nfa, TRANS_CLASS_WORD, error); case AST_CLASS_SPACE: return build_class(nfa, TRANS_CLASS_SPACE, error); case AST_CLASS_NDIGIT: return build_class(nfa, TRANS_CLASS_NDIGIT, error); case AST_CLASS_NWORD: return build_class(nfa, TRANS_CLASS_NWORD, error); case AST_CLASS_NSPACE: return build_class(nfa, TRANS_CLASS_NSPACE, error); } return frag; } nfa_t *nfa_from_ast(ast_node_t *ast, loreg_error_t *error) { *error = LOREG_OK; nfa_t *nfa = nfa_create(); if (!nfa) { *error = LOREG_ERR_OUT_OF_MEMORY; return NULL; } nfa_fragment_t frag = build_nfa(nfa, ast, error); if (*error != LOREG_OK) { nfa_free(nfa); return NULL; } nfa->start = frag.start; frag.accept->accepting = true; return nfa; }