chore: rename project from loreg to lorex and add NFA optimization infrastructure
- Rename all source files, headers, and build targets from loreg to lorex - Add match context struct with pre-allocated state sets for reuse across calls - Introduce optimization fields in nfa_t: literal prefix/suffix, first character bitmap, alternation dispatch table, anchor flags - Add benchmark target with -O3 -march=native in Makefile - Implement bracket_class_t bitmap cache for O(1) character class lookups - Update README with performance section detailing optimization techniques - Add coverage/ directory to .gitignore - Update CHANGELOG to reflect reduced line counts after rename
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#include "ast.h"
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
static ast_node_t *ast_create_node(ast_type_t type) {
|
||||
ast_node_t *node = malloc(sizeof(ast_node_t));
|
||||
@@ -126,6 +127,8 @@ bracket_class_t *bracket_create(void) {
|
||||
bracket->count = 0;
|
||||
bracket->capacity = 0;
|
||||
bracket->negated = false;
|
||||
memset(bracket->bitmap, 0, 32);
|
||||
bracket->bitmap_valid = true;
|
||||
return bracket;
|
||||
}
|
||||
|
||||
@@ -149,6 +152,14 @@ void bracket_add_range(bracket_class_t *bracket, char start, char end) {
|
||||
bracket->ranges[bracket->count].start = start;
|
||||
bracket->ranges[bracket->count].end = end;
|
||||
bracket->count++;
|
||||
|
||||
if (bracket->bitmap_valid) {
|
||||
unsigned char s = (unsigned char)start;
|
||||
unsigned char e = (unsigned char)end;
|
||||
for (unsigned int c = s; c <= e; c++) {
|
||||
bracket->bitmap[c >> 3] |= (1u << (c & 7));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bracket_free(bracket_class_t *bracket) {
|
||||
@@ -158,11 +169,18 @@ void bracket_free(bracket_class_t *bracket) {
|
||||
}
|
||||
|
||||
bool bracket_matches(bracket_class_t *bracket, char c) {
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < bracket->count; i++) {
|
||||
if (c >= bracket->ranges[i].start && c <= bracket->ranges[i].end) {
|
||||
found = true;
|
||||
break;
|
||||
unsigned char uc = (unsigned char)c;
|
||||
bool found;
|
||||
|
||||
if (bracket->bitmap_valid) {
|
||||
found = (bracket->bitmap[uc >> 3] & (1u << (uc & 7))) != 0;
|
||||
} else {
|
||||
found = false;
|
||||
for (size_t i = 0; i < bracket->count; i++) {
|
||||
if (c >= bracket->ranges[i].start && c <= bracket->ranges[i].end) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return bracket->negated ? !found : found;
|
||||
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
/* retoor <retoor@molodetz.nl> */
|
||||
#include "loreg.h"
|
||||
#include "parser.h"
|
||||
#include "nfa.h"
|
||||
#include "matcher.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
struct loreg_regex {
|
||||
nfa_t *nfa;
|
||||
ast_node_t *ast;
|
||||
};
|
||||
|
||||
loreg_regex_t *loreg_compile(const char *pattern, loreg_error_t *error) {
|
||||
*error = LOREG_OK;
|
||||
|
||||
loreg_regex_t *regex = malloc(sizeof(loreg_regex_t));
|
||||
if (!regex) {
|
||||
*error = LOREG_ERR_OUT_OF_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
parser_t parser;
|
||||
parser_init(&parser, pattern);
|
||||
|
||||
regex->ast = parser_parse(&parser);
|
||||
*error = parser_get_error(&parser);
|
||||
|
||||
if (*error != LOREG_OK) {
|
||||
ast_free(regex->ast);
|
||||
free(regex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
regex->nfa = nfa_from_ast(regex->ast, error);
|
||||
if (*error != LOREG_OK) {
|
||||
ast_free(regex->ast);
|
||||
free(regex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return regex;
|
||||
}
|
||||
|
||||
void loreg_free(loreg_regex_t *regex) {
|
||||
if (!regex) return;
|
||||
nfa_free(regex->nfa);
|
||||
ast_free(regex->ast);
|
||||
free(regex);
|
||||
}
|
||||
|
||||
bool loreg_match(loreg_regex_t *regex, const char *text, loreg_match_t *result) {
|
||||
return nfa_match(regex->nfa, text, 0, result);
|
||||
}
|
||||
|
||||
bool loreg_search(loreg_regex_t *regex, const char *text, loreg_match_t *result) {
|
||||
return nfa_search(regex->nfa, text, result);
|
||||
}
|
||||
|
||||
const char *loreg_error_string(loreg_error_t error) {
|
||||
switch (error) {
|
||||
case LOREG_OK: return "success";
|
||||
case LOREG_ERR_INVALID_PATTERN: return "invalid pattern";
|
||||
case LOREG_ERR_UNBALANCED_PAREN: return "unbalanced parentheses";
|
||||
case LOREG_ERR_EMPTY_GROUP: return "empty group";
|
||||
case LOREG_ERR_INVALID_QUANTIFIER: return "invalid quantifier";
|
||||
case LOREG_ERR_INVALID_ESCAPE: return "invalid escape sequence";
|
||||
case LOREG_ERR_OUT_OF_MEMORY: return "out of memory";
|
||||
case LOREG_ERR_STATE_OVERFLOW: return "state overflow";
|
||||
default: return "unknown error";
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/* retoor <retoor@molodetz.nl> */
|
||||
#include "lorex.h"
|
||||
#include "parser.h"
|
||||
#include "nfa.h"
|
||||
#include "matcher.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
struct lorex_regex {
|
||||
nfa_t *nfa;
|
||||
ast_node_t *ast;
|
||||
};
|
||||
|
||||
lorex_regex_t *lorex_compile(const char *pattern, lorex_error_t *error) {
|
||||
*error = LOREX_OK;
|
||||
|
||||
lorex_regex_t *regex = malloc(sizeof(lorex_regex_t));
|
||||
if (!regex) {
|
||||
*error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
parser_t parser;
|
||||
parser_init(&parser, pattern);
|
||||
|
||||
regex->ast = parser_parse(&parser);
|
||||
*error = parser_get_error(&parser);
|
||||
|
||||
if (*error != LOREX_OK) {
|
||||
ast_free(regex->ast);
|
||||
free(regex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
regex->nfa = nfa_from_ast(regex->ast, error);
|
||||
if (*error != LOREX_OK) {
|
||||
ast_free(regex->ast);
|
||||
free(regex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return regex;
|
||||
}
|
||||
|
||||
void lorex_free(lorex_regex_t *regex) {
|
||||
if (!regex) return;
|
||||
nfa_free(regex->nfa);
|
||||
ast_free(regex->ast);
|
||||
free(regex);
|
||||
}
|
||||
|
||||
bool lorex_match(lorex_regex_t *regex, const char *text, lorex_match_t *result) {
|
||||
return nfa_match(regex->nfa, text, 0, result);
|
||||
}
|
||||
|
||||
bool lorex_search(lorex_regex_t *regex, const char *text, lorex_match_t *result) {
|
||||
return nfa_search(regex->nfa, text, result);
|
||||
}
|
||||
|
||||
const char *lorex_error_string(lorex_error_t error) {
|
||||
switch (error) {
|
||||
case LOREX_OK: return "success";
|
||||
case LOREX_ERR_INVALID_PATTERN: return "invalid pattern";
|
||||
case LOREX_ERR_UNBALANCED_PAREN: return "unbalanced parentheses";
|
||||
case LOREX_ERR_EMPTY_GROUP: return "empty group";
|
||||
case LOREX_ERR_INVALID_QUANTIFIER: return "invalid quantifier";
|
||||
case LOREX_ERR_INVALID_ESCAPE: return "invalid escape sequence";
|
||||
case LOREX_ERR_OUT_OF_MEMORY: return "out of memory";
|
||||
case LOREX_ERR_STATE_OVERFLOW: return "state overflow";
|
||||
default: return "unknown error";
|
||||
}
|
||||
}
|
||||
+10
-10
@@ -1,5 +1,5 @@
|
||||
/* retoor <retoor@molodetz.nl> */
|
||||
#include "loreg.h"
|
||||
#include "lorex.h"
|
||||
#include "repl.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -20,10 +20,10 @@ static void print_usage(const char *program) {
|
||||
}
|
||||
|
||||
static void print_version(void) {
|
||||
printf("loreg %s\n", LOREG_VERSION);
|
||||
printf("lorex %s\n", LOREX_VERSION);
|
||||
}
|
||||
|
||||
static void print_match(const char *text, loreg_match_t *result) {
|
||||
static void print_match(const char *text, lorex_match_t *result) {
|
||||
if (!result->matched) {
|
||||
printf("no match\n");
|
||||
return;
|
||||
@@ -86,22 +86,22 @@ int main(int argc, char *argv[]) {
|
||||
const char *pattern = argv[arg_idx];
|
||||
const char *text = argv[arg_idx + 1];
|
||||
|
||||
loreg_error_t error;
|
||||
loreg_regex_t *regex = loreg_compile(pattern, &error);
|
||||
lorex_error_t error;
|
||||
lorex_regex_t *regex = lorex_compile(pattern, &error);
|
||||
if (!regex) {
|
||||
fprintf(stderr, "error: %s\n", loreg_error_string(error));
|
||||
fprintf(stderr, "error: %s\n", lorex_error_string(error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
loreg_match_t result;
|
||||
lorex_match_t result;
|
||||
if (match_mode) {
|
||||
loreg_match(regex, text, &result);
|
||||
lorex_match(regex, text, &result);
|
||||
} else {
|
||||
loreg_search(regex, text, &result);
|
||||
lorex_search(regex, text, &result);
|
||||
}
|
||||
|
||||
print_match(text, &result);
|
||||
|
||||
loreg_free(regex);
|
||||
lorex_free(regex);
|
||||
return result.matched ? 0 : 1;
|
||||
}
|
||||
|
||||
+424
-68
@@ -3,6 +3,33 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define BITMAP_SET(bm, id) ((bm)[(id) >> 3] |= (1u << ((id) & 7)))
|
||||
#define BITMAP_GET(bm, id) ((bm)[(id) >> 3] & (1u << ((id) & 7)))
|
||||
#define BITMAP_CLR(bm, id) ((bm)[(id) >> 3] &= ~(1u << ((id) & 7)))
|
||||
#define BITMAP_SIZE(n) (((n) + 7) >> 3)
|
||||
|
||||
static const uint8_t char_class_digit[32] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static const uint8_t char_class_word[32] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03,
|
||||
0xFE, 0xFF, 0xFF, 0x87, 0xFE, 0xFF, 0xFF, 0x07,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static const uint8_t char_class_space[32] = {
|
||||
0x00, 0x26, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
state_set_t *state_set_create(size_t initial_capacity, int group_count) {
|
||||
state_set_t *set = malloc(sizeof(state_set_t));
|
||||
@@ -78,27 +105,26 @@ bool state_set_contains(state_set_t *set, nfa_state_t *state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_digit(char c) {
|
||||
return c >= '0' && c <= '9';
|
||||
static inline bool is_digit(unsigned char c) {
|
||||
return (char_class_digit[c >> 3] & (1u << (c & 7))) != 0;
|
||||
}
|
||||
|
||||
static bool is_word(char c) {
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
||||
(c >= '0' && c <= '9') || c == '_';
|
||||
static inline bool is_word(unsigned char c) {
|
||||
return (char_class_word[c >> 3] & (1u << (c & 7))) != 0;
|
||||
}
|
||||
|
||||
static bool is_space(char c) {
|
||||
return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
|
||||
static inline bool is_space(unsigned char c) {
|
||||
return (char_class_space[c >> 3] & (1u << (c & 7))) != 0;
|
||||
}
|
||||
|
||||
static bool transition_matches(transition_t *t, char c, size_t pos, size_t len) {
|
||||
static inline bool transition_matches(const transition_t * restrict t, unsigned char c, size_t pos, size_t len) {
|
||||
switch (t->type) {
|
||||
case TRANS_CHAR:
|
||||
return t->value == c;
|
||||
return (unsigned char)t->value == c;
|
||||
case TRANS_DOT:
|
||||
return c != '\n' && c != '\0';
|
||||
case TRANS_BRACKET:
|
||||
return bracket_matches(t->bracket, c);
|
||||
return bracket_matches(t->bracket, (char)c);
|
||||
case TRANS_CLASS_DIGIT:
|
||||
return is_digit(c);
|
||||
case TRANS_CLASS_WORD:
|
||||
@@ -131,9 +157,25 @@ typedef struct {
|
||||
size_t count;
|
||||
size_t capacity;
|
||||
int group_count;
|
||||
uint8_t *state_bitmap;
|
||||
size_t bitmap_size;
|
||||
size_t *scratch_starts;
|
||||
size_t *scratch_ends;
|
||||
} thread_list_t;
|
||||
|
||||
static thread_list_t *thread_list_create(size_t capacity, int group_count) {
|
||||
struct match_ctx {
|
||||
thread_list_t *current;
|
||||
thread_list_t *next;
|
||||
uint32_t *visited;
|
||||
size_t *init_starts;
|
||||
size_t *init_ends;
|
||||
size_t *best_starts;
|
||||
size_t *best_ends;
|
||||
int group_count;
|
||||
size_t num_states;
|
||||
};
|
||||
|
||||
static thread_list_t *thread_list_create(size_t capacity, int group_count, size_t num_states) {
|
||||
thread_list_t *list = malloc(sizeof(thread_list_t));
|
||||
if (!list) return NULL;
|
||||
|
||||
@@ -143,6 +185,30 @@ static thread_list_t *thread_list_create(size_t capacity, int group_count) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list->bitmap_size = BITMAP_SIZE(num_states);
|
||||
list->state_bitmap = calloc(list->bitmap_size, 1);
|
||||
if (!list->state_bitmap) {
|
||||
free(list->threads);
|
||||
free(list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (group_count > 0) {
|
||||
list->scratch_starts = malloc(group_count * sizeof(size_t));
|
||||
list->scratch_ends = malloc(group_count * sizeof(size_t));
|
||||
if (!list->scratch_starts || !list->scratch_ends) {
|
||||
free(list->scratch_starts);
|
||||
free(list->scratch_ends);
|
||||
free(list->state_bitmap);
|
||||
free(list->threads);
|
||||
free(list);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
list->scratch_starts = NULL;
|
||||
list->scratch_ends = NULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < capacity; i++) {
|
||||
if (group_count > 0) {
|
||||
list->threads[i].group_starts = malloc(group_count * sizeof(size_t));
|
||||
@@ -152,6 +218,9 @@ static thread_list_t *thread_list_create(size_t capacity, int group_count) {
|
||||
free(list->threads[j].group_starts);
|
||||
free(list->threads[j].group_ends);
|
||||
}
|
||||
free(list->scratch_starts);
|
||||
free(list->scratch_ends);
|
||||
free(list->state_bitmap);
|
||||
free(list->threads);
|
||||
free(list);
|
||||
return NULL;
|
||||
@@ -174,19 +243,76 @@ static void thread_list_free(thread_list_t *list) {
|
||||
free(list->threads[i].group_starts);
|
||||
free(list->threads[i].group_ends);
|
||||
}
|
||||
free(list->scratch_starts);
|
||||
free(list->scratch_ends);
|
||||
free(list->state_bitmap);
|
||||
free(list->threads);
|
||||
free(list);
|
||||
}
|
||||
|
||||
static void thread_list_clear(thread_list_t *list);
|
||||
|
||||
match_ctx_t *match_ctx_create(nfa_t *nfa) {
|
||||
match_ctx_t *ctx = malloc(sizeof(match_ctx_t));
|
||||
if (!ctx) return NULL;
|
||||
|
||||
size_t num_states = nfa->state_count;
|
||||
int group_count = nfa->group_count > 0 ? nfa->group_count : 1;
|
||||
|
||||
ctx->num_states = num_states;
|
||||
ctx->group_count = group_count;
|
||||
|
||||
ctx->current = thread_list_create(num_states, group_count, num_states);
|
||||
ctx->next = thread_list_create(num_states, group_count, num_states);
|
||||
ctx->visited = calloc(num_states, sizeof(uint32_t));
|
||||
ctx->init_starts = calloc(group_count, sizeof(size_t));
|
||||
ctx->init_ends = calloc(group_count, sizeof(size_t));
|
||||
ctx->best_starts = calloc(group_count, sizeof(size_t));
|
||||
ctx->best_ends = calloc(group_count, sizeof(size_t));
|
||||
|
||||
if (!ctx->current || !ctx->next || !ctx->visited ||
|
||||
!ctx->init_starts || !ctx->init_ends ||
|
||||
!ctx->best_starts || !ctx->best_ends) {
|
||||
match_ctx_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void match_ctx_free(match_ctx_t *ctx) {
|
||||
if (!ctx) return;
|
||||
thread_list_free(ctx->current);
|
||||
thread_list_free(ctx->next);
|
||||
free(ctx->visited);
|
||||
free(ctx->init_starts);
|
||||
free(ctx->init_ends);
|
||||
free(ctx->best_starts);
|
||||
free(ctx->best_ends);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
static void match_ctx_reset(match_ctx_t *ctx) {
|
||||
thread_list_clear(ctx->current);
|
||||
thread_list_clear(ctx->next);
|
||||
memset(ctx->visited, 0, ctx->num_states * sizeof(uint32_t));
|
||||
for (int i = 0; i < ctx->group_count; i++) {
|
||||
ctx->init_starts[i] = (size_t)-1;
|
||||
ctx->init_ends[i] = (size_t)-1;
|
||||
ctx->best_starts[i] = (size_t)-1;
|
||||
ctx->best_ends[i] = (size_t)-1;
|
||||
}
|
||||
}
|
||||
|
||||
static void thread_list_clear(thread_list_t *list) {
|
||||
for (size_t i = 0; i < list->count; i++) {
|
||||
BITMAP_CLR(list->state_bitmap, list->threads[i].state->id);
|
||||
}
|
||||
list->count = 0;
|
||||
}
|
||||
|
||||
static bool thread_list_contains_state(thread_list_t *list, nfa_state_t *state) {
|
||||
for (size_t i = 0; i < list->count; i++) {
|
||||
if (list->threads[i].state == state) return true;
|
||||
}
|
||||
return false;
|
||||
static inline bool thread_list_contains_state(const thread_list_t * restrict list, const nfa_state_t * restrict state) {
|
||||
return BITMAP_GET(list->state_bitmap, state->id) != 0;
|
||||
}
|
||||
|
||||
static void add_thread(thread_list_t *list, nfa_state_t *state,
|
||||
@@ -194,44 +320,36 @@ static void add_thread(thread_list_t *list, nfa_state_t *state,
|
||||
|
||||
static void follow_epsilons(thread_list_t *list, nfa_state_t *state,
|
||||
size_t *group_starts, size_t *group_ends,
|
||||
size_t pos, size_t len, bool *visited) {
|
||||
if (!state || visited[state->id]) return;
|
||||
visited[state->id] = true;
|
||||
size_t pos, size_t len, uint32_t *visited, uint32_t gen) {
|
||||
if (!state || visited[state->id] == gen) return;
|
||||
visited[state->id] = gen;
|
||||
|
||||
for (size_t i = 0; i < state->trans_count; i++) {
|
||||
transition_t *t = &state->transitions[i];
|
||||
|
||||
if (t->type == TRANS_EPSILON) {
|
||||
follow_epsilons(list, t->target, group_starts, group_ends,
|
||||
pos, len, visited);
|
||||
pos, len, visited, gen);
|
||||
} else if (t->type == TRANS_GROUP_START) {
|
||||
size_t *new_starts = malloc(list->group_count * sizeof(size_t));
|
||||
size_t *new_ends = malloc(list->group_count * sizeof(size_t));
|
||||
if (new_starts && new_ends) {
|
||||
memcpy(new_starts, group_starts, list->group_count * sizeof(size_t));
|
||||
memcpy(new_ends, group_ends, list->group_count * sizeof(size_t));
|
||||
new_starts[t->group_id] = pos;
|
||||
follow_epsilons(list, t->target, new_starts, new_ends,
|
||||
pos, len, visited);
|
||||
}
|
||||
free(new_starts);
|
||||
free(new_ends);
|
||||
size_t *scratch_s = list->scratch_starts;
|
||||
size_t *scratch_e = list->scratch_ends;
|
||||
memcpy(scratch_s, group_starts, list->group_count * sizeof(size_t));
|
||||
memcpy(scratch_e, group_ends, list->group_count * sizeof(size_t));
|
||||
scratch_s[t->group_id] = pos;
|
||||
follow_epsilons(list, t->target, scratch_s, scratch_e,
|
||||
pos, len, visited, gen);
|
||||
} else if (t->type == TRANS_GROUP_END) {
|
||||
size_t *new_starts = malloc(list->group_count * sizeof(size_t));
|
||||
size_t *new_ends = malloc(list->group_count * sizeof(size_t));
|
||||
if (new_starts && new_ends) {
|
||||
memcpy(new_starts, group_starts, list->group_count * sizeof(size_t));
|
||||
memcpy(new_ends, group_ends, list->group_count * sizeof(size_t));
|
||||
new_ends[t->group_id] = pos;
|
||||
follow_epsilons(list, t->target, new_starts, new_ends,
|
||||
pos, len, visited);
|
||||
}
|
||||
free(new_starts);
|
||||
free(new_ends);
|
||||
size_t *scratch_s = list->scratch_starts;
|
||||
size_t *scratch_e = list->scratch_ends;
|
||||
memcpy(scratch_s, group_starts, list->group_count * sizeof(size_t));
|
||||
memcpy(scratch_e, group_ends, list->group_count * sizeof(size_t));
|
||||
scratch_e[t->group_id] = pos;
|
||||
follow_epsilons(list, t->target, scratch_s, scratch_e,
|
||||
pos, len, visited, gen);
|
||||
} else if (t->type == TRANS_ANCHOR_START || t->type == TRANS_ANCHOR_END) {
|
||||
if (transition_matches(t, '\0', pos, len)) {
|
||||
follow_epsilons(list, t->target, group_starts, group_ends,
|
||||
pos, len, visited);
|
||||
pos, len, visited, gen);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,9 +361,9 @@ static void add_thread(thread_list_t *list, nfa_state_t *state,
|
||||
size_t *group_starts, size_t *group_ends) {
|
||||
if (!state) return;
|
||||
if (thread_list_contains_state(list, state)) return;
|
||||
|
||||
if (list->count >= list->capacity) return;
|
||||
|
||||
BITMAP_SET(list->state_bitmap, state->id);
|
||||
thread_t *thread = &list->threads[list->count++];
|
||||
thread->state = state;
|
||||
if (list->group_count > 0) {
|
||||
@@ -254,14 +372,15 @@ static void add_thread(thread_list_t *list, nfa_state_t *state,
|
||||
}
|
||||
}
|
||||
|
||||
bool nfa_match(nfa_t *nfa, const char *text, size_t start_pos, loreg_match_t *result) {
|
||||
bool nfa_match(nfa_t *nfa, const char *text, size_t start_pos, lorex_match_t *result) {
|
||||
size_t len = strlen(text);
|
||||
size_t num_states = nfa->state_count;
|
||||
int group_count = nfa->group_count > 0 ? nfa->group_count : 1;
|
||||
|
||||
thread_list_t *current = thread_list_create(num_states, group_count);
|
||||
thread_list_t *next = thread_list_create(num_states, group_count);
|
||||
bool *visited = calloc(num_states, sizeof(bool));
|
||||
thread_list_t *current = thread_list_create(num_states, group_count, num_states);
|
||||
thread_list_t *next = thread_list_create(num_states, group_count, num_states);
|
||||
uint32_t *visited = calloc(num_states, sizeof(uint32_t));
|
||||
uint32_t generation = 1;
|
||||
|
||||
if (!current || !next || !visited) {
|
||||
thread_list_free(current);
|
||||
@@ -286,9 +405,8 @@ bool nfa_match(nfa_t *nfa, const char *text, size_t start_pos, loreg_match_t *re
|
||||
init_ends[i] = (size_t)-1;
|
||||
}
|
||||
|
||||
memset(visited, 0, num_states * sizeof(bool));
|
||||
follow_epsilons(current, nfa->start, init_starts, init_ends,
|
||||
start_pos, len, visited);
|
||||
start_pos, len, visited, generation++);
|
||||
|
||||
bool matched = false;
|
||||
size_t match_end = start_pos;
|
||||
@@ -322,27 +440,29 @@ bool nfa_match(nfa_t *nfa, const char *text, size_t start_pos, loreg_match_t *re
|
||||
}
|
||||
|
||||
for (size_t pos = start_pos; pos < len; pos++) {
|
||||
char c = text[pos];
|
||||
unsigned char c = (unsigned char)text[pos];
|
||||
thread_list_clear(next);
|
||||
|
||||
for (size_t i = 0; i < current->count; i++) {
|
||||
thread_t *thread = ¤t->threads[i];
|
||||
nfa_state_t *state = thread->state;
|
||||
size_t trans_count = state->trans_count;
|
||||
transition_t *transitions = state->transitions;
|
||||
|
||||
for (size_t j = 0; j < state->trans_count; j++) {
|
||||
transition_t *t = &state->transitions[j];
|
||||
for (size_t j = 0; j < trans_count; j++) {
|
||||
transition_t *t = &transitions[j];
|
||||
transition_type_t type = t->type;
|
||||
|
||||
if (t->type != TRANS_EPSILON &&
|
||||
t->type != TRANS_GROUP_START &&
|
||||
t->type != TRANS_GROUP_END &&
|
||||
t->type != TRANS_ANCHOR_START &&
|
||||
t->type != TRANS_ANCHOR_END) {
|
||||
if (type != TRANS_EPSILON &&
|
||||
type != TRANS_GROUP_START &&
|
||||
type != TRANS_GROUP_END &&
|
||||
type != TRANS_ANCHOR_START &&
|
||||
type != TRANS_ANCHOR_END) {
|
||||
|
||||
if (transition_matches(t, c, pos, len)) {
|
||||
memset(visited, 0, num_states * sizeof(bool));
|
||||
follow_epsilons(next, t->target,
|
||||
thread->group_starts, thread->group_ends,
|
||||
pos + 1, len, visited);
|
||||
pos + 1, len, visited, generation++);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -371,7 +491,7 @@ bool nfa_match(nfa_t *nfa, const char *text, size_t start_pos, loreg_match_t *re
|
||||
result->match_end = matched ? match_end : start_pos;
|
||||
result->group_count = nfa->group_count;
|
||||
|
||||
for (int i = 0; i < LOREG_MAX_GROUPS && i < nfa->group_count; i++) {
|
||||
for (int i = 0; i < LOREX_MAX_GROUPS && i < nfa->group_count; i++) {
|
||||
result->groups[i].start = best_starts[i];
|
||||
result->groups[i].end = best_ends[i];
|
||||
result->groups[i].matched = (best_starts[i] != (size_t)-1 && best_ends[i] != (size_t)-1);
|
||||
@@ -389,23 +509,259 @@ bool nfa_match(nfa_t *nfa, const char *text, size_t start_pos, loreg_match_t *re
|
||||
return matched;
|
||||
}
|
||||
|
||||
bool nfa_search(nfa_t *nfa, const char *text, loreg_match_t *result) {
|
||||
bool nfa_match_with_ctx(nfa_t *nfa, const char *text, size_t start_pos, lorex_match_t *result, match_ctx_t *ctx) {
|
||||
size_t len = strlen(text);
|
||||
int group_count = ctx->group_count;
|
||||
uint32_t generation = 1;
|
||||
|
||||
for (size_t i = 0; i <= len; i++) {
|
||||
if (nfa_match(nfa, text, i, result)) {
|
||||
if (result) {
|
||||
result->match_start = i;
|
||||
}
|
||||
return true;
|
||||
match_ctx_reset(ctx);
|
||||
|
||||
follow_epsilons(ctx->current, nfa->start, ctx->init_starts, ctx->init_ends,
|
||||
start_pos, len, ctx->visited, generation++);
|
||||
|
||||
bool matched = false;
|
||||
size_t match_end = start_pos;
|
||||
|
||||
for (size_t i = 0; i < ctx->current->count; i++) {
|
||||
if (ctx->current->threads[i].state->accepting) {
|
||||
matched = true;
|
||||
match_end = start_pos;
|
||||
memcpy(ctx->best_starts, ctx->current->threads[i].group_starts, group_count * sizeof(size_t));
|
||||
memcpy(ctx->best_ends, ctx->current->threads[i].group_ends, group_count * sizeof(size_t));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
thread_list_t *current = ctx->current;
|
||||
thread_list_t *next = ctx->next;
|
||||
|
||||
for (size_t pos = start_pos; pos < len; pos++) {
|
||||
unsigned char c = (unsigned char)text[pos];
|
||||
thread_list_clear(next);
|
||||
|
||||
for (size_t i = 0; i < current->count; i++) {
|
||||
thread_t *thread = ¤t->threads[i];
|
||||
nfa_state_t *state = thread->state;
|
||||
size_t trans_count = state->trans_count;
|
||||
transition_t *transitions = state->transitions;
|
||||
|
||||
for (size_t j = 0; j < trans_count; j++) {
|
||||
transition_t *t = &transitions[j];
|
||||
transition_type_t type = t->type;
|
||||
|
||||
if (type != TRANS_EPSILON &&
|
||||
type != TRANS_GROUP_START &&
|
||||
type != TRANS_GROUP_END &&
|
||||
type != TRANS_ANCHOR_START &&
|
||||
type != TRANS_ANCHOR_END) {
|
||||
|
||||
if (transition_matches(t, c, pos, len)) {
|
||||
follow_epsilons(next, t->target,
|
||||
thread->group_starts, thread->group_ends,
|
||||
pos + 1, len, ctx->visited, generation++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (next->count == 0) break;
|
||||
|
||||
thread_list_t *tmp = current;
|
||||
current = next;
|
||||
next = tmp;
|
||||
|
||||
for (size_t i = 0; i < current->count; i++) {
|
||||
if (current->threads[i].state->accepting) {
|
||||
matched = true;
|
||||
match_end = pos + 1;
|
||||
memcpy(ctx->best_starts, current->threads[i].group_starts, group_count * sizeof(size_t));
|
||||
memcpy(ctx->best_ends, current->threads[i].group_ends, group_count * sizeof(size_t));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result) {
|
||||
result->matched = matched;
|
||||
result->match_start = start_pos;
|
||||
result->match_end = matched ? match_end : start_pos;
|
||||
result->group_count = nfa->group_count;
|
||||
|
||||
for (int i = 0; i < LOREX_MAX_GROUPS && i < nfa->group_count; i++) {
|
||||
result->groups[i].start = ctx->best_starts[i];
|
||||
result->groups[i].end = ctx->best_ends[i];
|
||||
result->groups[i].matched = (ctx->best_starts[i] != (size_t)-1 && ctx->best_ends[i] != (size_t)-1);
|
||||
}
|
||||
}
|
||||
|
||||
return matched;
|
||||
}
|
||||
|
||||
static void set_no_match(lorex_match_t *result) {
|
||||
if (result) {
|
||||
result->matched = false;
|
||||
result->match_start = 0;
|
||||
result->match_end = 0;
|
||||
result->group_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool nfa_search(nfa_t *nfa, const char *text, lorex_match_t *result) {
|
||||
size_t len = strlen(text);
|
||||
|
||||
if (nfa->anchored_start) {
|
||||
bool matched = nfa_match(nfa, text, 0, result);
|
||||
if (matched && result) {
|
||||
result->match_start = 0;
|
||||
}
|
||||
if (!matched) {
|
||||
set_no_match(result);
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
|
||||
if (nfa->is_pure_literal && nfa->literal_prefix && nfa->prefix_len > 0) {
|
||||
const char *found = strstr(text, nfa->literal_prefix);
|
||||
if (found) {
|
||||
if (result) {
|
||||
result->matched = true;
|
||||
result->match_start = (size_t)(found - text);
|
||||
result->match_end = result->match_start + nfa->prefix_len;
|
||||
result->group_count = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
set_no_match(result);
|
||||
return false;
|
||||
}
|
||||
|
||||
match_ctx_t *ctx = match_ctx_create(nfa);
|
||||
if (!ctx) {
|
||||
set_no_match(result);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nfa->anchored_end && nfa->suffix_len > 0) {
|
||||
if (len < nfa->suffix_len) {
|
||||
match_ctx_free(ctx);
|
||||
set_no_match(result);
|
||||
return false;
|
||||
}
|
||||
if (memcmp(text + len - nfa->suffix_len, nfa->literal_suffix, nfa->suffix_len) != 0) {
|
||||
match_ctx_free(ctx);
|
||||
set_no_match(result);
|
||||
return false;
|
||||
}
|
||||
size_t suffix_start = len - nfa->suffix_len;
|
||||
for (size_t i = suffix_start + 1; i > 0; i--) {
|
||||
size_t pos = i - 1;
|
||||
if (nfa_match_with_ctx(nfa, text, pos, result, ctx)) {
|
||||
if (result) result->match_start = pos;
|
||||
match_ctx_free(ctx);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
match_ctx_free(ctx);
|
||||
set_no_match(result);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nfa->has_alt_dispatch) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
unsigned char c = (unsigned char)text[i];
|
||||
if (nfa->alt_dispatch[c] == 255) continue;
|
||||
if (nfa_match_with_ctx(nfa, text, i, result, ctx)) {
|
||||
if (result) result->match_start = i;
|
||||
match_ctx_free(ctx);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
match_ctx_free(ctx);
|
||||
set_no_match(result);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nfa->prefix_len >= 2) {
|
||||
const char *pos = text;
|
||||
const char *end = text + len;
|
||||
while (pos <= end - nfa->prefix_len) {
|
||||
pos = strstr(pos, nfa->literal_prefix);
|
||||
if (!pos) break;
|
||||
size_t offset = (size_t)(pos - text);
|
||||
if (nfa_match_with_ctx(nfa, text, offset, result, ctx)) {
|
||||
if (result) {
|
||||
result->match_start = offset;
|
||||
}
|
||||
match_ctx_free(ctx);
|
||||
return true;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
match_ctx_free(ctx);
|
||||
set_no_match(result);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nfa->single_first_char != 0) {
|
||||
const char *pos = text;
|
||||
const char *end = text + len;
|
||||
while (pos < end) {
|
||||
pos = memchr(pos, nfa->single_first_char, (size_t)(end - pos));
|
||||
if (!pos) break;
|
||||
size_t offset = (size_t)(pos - text);
|
||||
if (nfa_match_with_ctx(nfa, text, offset, result, ctx)) {
|
||||
if (result) {
|
||||
result->match_start = offset;
|
||||
}
|
||||
match_ctx_free(ctx);
|
||||
return true;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
match_ctx_free(ctx);
|
||||
set_no_match(result);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nfa->first_chars_valid) {
|
||||
bool has_any_first_char = false;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (nfa->first_chars[i]) {
|
||||
has_any_first_char = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (has_any_first_char) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
unsigned char c = (unsigned char)text[i];
|
||||
if (!(nfa->first_chars[c >> 3] & (1u << (c & 7)))) {
|
||||
continue;
|
||||
}
|
||||
if (nfa_match_with_ctx(nfa, text, i, result, ctx)) {
|
||||
if (result) {
|
||||
result->match_start = i;
|
||||
}
|
||||
match_ctx_free(ctx);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
match_ctx_free(ctx);
|
||||
set_no_match(result);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i <= len; i++) {
|
||||
if (nfa_match_with_ctx(nfa, text, i, result, ctx)) {
|
||||
if (result) {
|
||||
result->match_start = i;
|
||||
}
|
||||
match_ctx_free(ctx);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
match_ctx_free(ctx);
|
||||
set_no_match(result);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -11,9 +11,282 @@ nfa_t *nfa_create(void) {
|
||||
nfa->capacity = 0;
|
||||
nfa->start = NULL;
|
||||
nfa->group_count = 0;
|
||||
nfa->anchored_start = false;
|
||||
nfa->anchored_end = false;
|
||||
memset(nfa->first_chars, 0, 32);
|
||||
nfa->first_chars_valid = false;
|
||||
nfa->literal_prefix = NULL;
|
||||
nfa->prefix_len = 0;
|
||||
nfa->is_pure_literal = false;
|
||||
nfa->single_first_char = 0;
|
||||
nfa->literal_suffix = NULL;
|
||||
nfa->suffix_len = 0;
|
||||
memset(nfa->alt_dispatch, 255, 256);
|
||||
nfa->has_alt_dispatch = false;
|
||||
return nfa;
|
||||
}
|
||||
|
||||
static bool ast_starts_with_anchor(ast_node_t *ast) {
|
||||
if (!ast) return false;
|
||||
switch (ast->type) {
|
||||
case AST_ANCHOR_START:
|
||||
return true;
|
||||
case AST_CONCAT:
|
||||
return ast_starts_with_anchor(ast->left);
|
||||
case AST_GROUP:
|
||||
return ast_starts_with_anchor(ast->left);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ast_ends_with_anchor(ast_node_t *ast) {
|
||||
if (!ast) return false;
|
||||
switch (ast->type) {
|
||||
case AST_ANCHOR_END:
|
||||
return true;
|
||||
case AST_CONCAT:
|
||||
return ast_ends_with_anchor(ast->right);
|
||||
case AST_GROUP:
|
||||
return ast_ends_with_anchor(ast->left);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void extract_first_chars(ast_node_t *ast, unsigned char *bitmap, bool *valid) {
|
||||
if (!ast) {
|
||||
*valid = false;
|
||||
return;
|
||||
}
|
||||
switch (ast->type) {
|
||||
case AST_CHAR:
|
||||
bitmap[(unsigned char)ast->value >> 3] |= (1u << (ast->value & 7));
|
||||
break;
|
||||
case AST_DOT:
|
||||
*valid = false;
|
||||
break;
|
||||
case AST_CONCAT:
|
||||
extract_first_chars(ast->left, bitmap, valid);
|
||||
break;
|
||||
case AST_ALTER:
|
||||
extract_first_chars(ast->left, bitmap, valid);
|
||||
extract_first_chars(ast->right, bitmap, valid);
|
||||
break;
|
||||
case AST_STAR:
|
||||
case AST_QUESTION:
|
||||
*valid = false;
|
||||
break;
|
||||
case AST_PLUS:
|
||||
extract_first_chars(ast->left, bitmap, valid);
|
||||
break;
|
||||
case AST_GROUP:
|
||||
extract_first_chars(ast->left, bitmap, valid);
|
||||
break;
|
||||
case AST_ANCHOR_START:
|
||||
case AST_ANCHOR_END:
|
||||
break;
|
||||
case AST_BRACKET:
|
||||
if (ast->bracket && !ast->bracket->negated) {
|
||||
for (size_t i = 0; i < ast->bracket->count; i++) {
|
||||
unsigned char s = (unsigned char)ast->bracket->ranges[i].start;
|
||||
unsigned char e = (unsigned char)ast->bracket->ranges[i].end;
|
||||
for (unsigned int c = s; c <= e; c++) {
|
||||
bitmap[c >> 3] |= (1u << (c & 7));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
*valid = false;
|
||||
}
|
||||
break;
|
||||
case AST_QUANTIFIER:
|
||||
if (ast->quant.min > 0) {
|
||||
extract_first_chars(ast->left, bitmap, valid);
|
||||
} else {
|
||||
*valid = false;
|
||||
}
|
||||
break;
|
||||
case AST_CLASS_DIGIT:
|
||||
for (char c = '0'; c <= '9'; c++) {
|
||||
bitmap[(unsigned char)c >> 3] |= (1u << (c & 7));
|
||||
}
|
||||
break;
|
||||
case AST_CLASS_WORD:
|
||||
for (char c = 'a'; c <= 'z'; c++)
|
||||
bitmap[(unsigned char)c >> 3] |= (1u << (c & 7));
|
||||
for (char c = 'A'; c <= 'Z'; c++)
|
||||
bitmap[(unsigned char)c >> 3] |= (1u << (c & 7));
|
||||
for (char c = '0'; c <= '9'; c++)
|
||||
bitmap[(unsigned char)c >> 3] |= (1u << (c & 7));
|
||||
bitmap['_' >> 3] |= (1u << ('_' & 7));
|
||||
break;
|
||||
case AST_CLASS_SPACE:
|
||||
bitmap[' ' >> 3] |= (1u << (' ' & 7));
|
||||
bitmap['\t' >> 3] |= (1u << ('\t' & 7));
|
||||
bitmap['\n' >> 3] |= (1u << ('\n' & 7));
|
||||
bitmap['\r' >> 3] |= (1u << ('\r' & 7));
|
||||
break;
|
||||
case AST_CLASS_NDIGIT:
|
||||
case AST_CLASS_NWORD:
|
||||
case AST_CLASS_NSPACE:
|
||||
*valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ast_is_pure_literal(ast_node_t *ast) {
|
||||
if (!ast) return true;
|
||||
switch (ast->type) {
|
||||
case AST_CHAR:
|
||||
return true;
|
||||
case AST_CONCAT:
|
||||
return ast_is_pure_literal(ast->left) && ast_is_pure_literal(ast->right);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t extract_literal_prefix(ast_node_t *ast, char *buf, size_t max_len) {
|
||||
if (!ast || max_len == 0) return 0;
|
||||
switch (ast->type) {
|
||||
case AST_CHAR:
|
||||
buf[0] = ast->value;
|
||||
return 1;
|
||||
case AST_CONCAT: {
|
||||
size_t left_len = extract_literal_prefix(ast->left, buf, max_len);
|
||||
if (left_len > 0 && ast_is_pure_literal(ast->left)) {
|
||||
size_t right_len = extract_literal_prefix(ast->right, buf + left_len, max_len - left_len);
|
||||
return left_len + right_len;
|
||||
}
|
||||
return left_len;
|
||||
}
|
||||
case AST_GROUP:
|
||||
return extract_literal_prefix(ast->left, buf, max_len);
|
||||
case AST_ANCHOR_START:
|
||||
case AST_ANCHOR_END:
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static ast_node_t *strip_end_anchor(ast_node_t *ast) {
|
||||
if (!ast) return NULL;
|
||||
if (ast->type == AST_ANCHOR_END) return NULL;
|
||||
if (ast->type == AST_CONCAT && ast->right && ast->right->type == AST_ANCHOR_END) {
|
||||
return ast->left;
|
||||
}
|
||||
return ast;
|
||||
}
|
||||
|
||||
static size_t extract_literal_suffix_rev(ast_node_t *ast, char *buf, size_t max_len) {
|
||||
if (!ast || max_len == 0) return 0;
|
||||
switch (ast->type) {
|
||||
case AST_CHAR:
|
||||
buf[0] = ast->value;
|
||||
return 1;
|
||||
case AST_CONCAT: {
|
||||
size_t right_len = extract_literal_suffix_rev(ast->right, buf, max_len);
|
||||
if (right_len == 0 || !ast_is_pure_literal(ast->right)) return right_len;
|
||||
size_t left_len = extract_literal_suffix_rev(ast->left, buf + right_len, max_len - right_len);
|
||||
return right_len + left_len;
|
||||
}
|
||||
case AST_GROUP:
|
||||
return extract_literal_suffix_rev(ast->left, buf, max_len);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t extract_literal_suffix(ast_node_t *ast, char *buf, size_t max_len) {
|
||||
ast_node_t *stripped = strip_end_anchor(ast);
|
||||
if (!stripped) return 0;
|
||||
size_t len = extract_literal_suffix_rev(stripped, buf, max_len);
|
||||
for (size_t i = 0; i < len / 2; i++) {
|
||||
char tmp = buf[i];
|
||||
buf[i] = buf[len - 1 - i];
|
||||
buf[len - 1 - i] = tmp;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static void build_alt_dispatch_node(ast_node_t *ast, unsigned char *dispatch) {
|
||||
if (!ast) return;
|
||||
switch (ast->type) {
|
||||
case AST_CHAR:
|
||||
dispatch[(unsigned char)ast->value] = 1;
|
||||
break;
|
||||
case AST_CONCAT:
|
||||
build_alt_dispatch_node(ast->left, dispatch);
|
||||
break;
|
||||
case AST_ALTER:
|
||||
build_alt_dispatch_node(ast->left, dispatch);
|
||||
build_alt_dispatch_node(ast->right, dispatch);
|
||||
break;
|
||||
case AST_GROUP:
|
||||
build_alt_dispatch_node(ast->left, dispatch);
|
||||
break;
|
||||
case AST_BRACKET:
|
||||
if (ast->bracket && !ast->bracket->negated) {
|
||||
for (size_t i = 0; i < ast->bracket->count; i++) {
|
||||
unsigned char s = (unsigned char)ast->bracket->ranges[i].start;
|
||||
unsigned char e = (unsigned char)ast->bracket->ranges[i].end;
|
||||
for (unsigned int c = s; c <= e; c++) {
|
||||
dispatch[c] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AST_CLASS_DIGIT:
|
||||
for (char c = '0'; c <= '9'; c++) {
|
||||
dispatch[(unsigned char)c] = 1;
|
||||
}
|
||||
break;
|
||||
case AST_CLASS_WORD:
|
||||
for (char c = 'a'; c <= 'z'; c++) dispatch[(unsigned char)c] = 1;
|
||||
for (char c = 'A'; c <= 'Z'; c++) dispatch[(unsigned char)c] = 1;
|
||||
for (char c = '0'; c <= '9'; c++) dispatch[(unsigned char)c] = 1;
|
||||
dispatch['_'] = 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_top_level_alternation(ast_node_t *ast) {
|
||||
if (!ast) return false;
|
||||
if (ast->type == AST_ALTER) return true;
|
||||
if (ast->type == AST_GROUP) return is_top_level_alternation(ast->left);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool build_alt_dispatch(ast_node_t *ast, unsigned char *dispatch) {
|
||||
if (!is_top_level_alternation(ast)) return false;
|
||||
memset(dispatch, 255, 256);
|
||||
build_alt_dispatch_node(ast, dispatch);
|
||||
bool has_any = false;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (dispatch[i] == 1) {
|
||||
dispatch[i] = 0;
|
||||
has_any = true;
|
||||
}
|
||||
}
|
||||
return has_any;
|
||||
}
|
||||
|
||||
static char compute_single_first_char(unsigned char *bitmap) {
|
||||
int count = 0;
|
||||
char first_char = 0;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
if (bitmap[i >> 3] & (1u << (i & 7))) {
|
||||
count++;
|
||||
if (count == 1) first_char = (char)i;
|
||||
if (count > 1) return 0;
|
||||
}
|
||||
}
|
||||
return first_char;
|
||||
}
|
||||
|
||||
void nfa_free(nfa_t *nfa) {
|
||||
if (!nfa) return;
|
||||
for (size_t i = 0; i < nfa->state_count; i++) {
|
||||
@@ -21,14 +294,16 @@ void nfa_free(nfa_t *nfa) {
|
||||
free(nfa->states[i]);
|
||||
}
|
||||
free(nfa->states);
|
||||
free(nfa->literal_prefix);
|
||||
free(nfa->literal_suffix);
|
||||
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;
|
||||
if (new_cap > LOREX_MAX_STATES) {
|
||||
if (nfa->capacity >= LOREX_MAX_STATES) return false;
|
||||
new_cap = LOREX_MAX_STATES;
|
||||
}
|
||||
nfa_state_t **new_states = realloc(nfa->states, new_cap * sizeof(nfa_state_t *));
|
||||
if (!new_states) return false;
|
||||
@@ -100,14 +375,14 @@ void nfa_add_group_transition(nfa_state_t *from, nfa_state_t *to, transition_typ
|
||||
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_nfa(nfa_t *nfa, ast_node_t *ast, lorex_error_t *error);
|
||||
|
||||
static nfa_fragment_t build_char(nfa_t *nfa, char c, loreg_error_t *error) {
|
||||
static nfa_fragment_t build_char(nfa_t *nfa, char c, lorex_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;
|
||||
*error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return frag;
|
||||
}
|
||||
nfa_add_transition(start, accept, TRANS_CHAR, c);
|
||||
@@ -116,12 +391,12 @@ static nfa_fragment_t build_char(nfa_t *nfa, char c, loreg_error_t *error) {
|
||||
return frag;
|
||||
}
|
||||
|
||||
static nfa_fragment_t build_dot(nfa_t *nfa, loreg_error_t *error) {
|
||||
static nfa_fragment_t build_dot(nfa_t *nfa, lorex_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;
|
||||
*error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return frag;
|
||||
}
|
||||
nfa_add_transition(start, accept, TRANS_DOT, '\0');
|
||||
@@ -130,12 +405,12 @@ static nfa_fragment_t build_dot(nfa_t *nfa, loreg_error_t *error) {
|
||||
return frag;
|
||||
}
|
||||
|
||||
static nfa_fragment_t build_class(nfa_t *nfa, transition_type_t type, loreg_error_t *error) {
|
||||
static nfa_fragment_t build_class(nfa_t *nfa, transition_type_t type, lorex_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;
|
||||
*error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return frag;
|
||||
}
|
||||
nfa_add_transition(start, accept, type, '\0');
|
||||
@@ -144,12 +419,12 @@ static nfa_fragment_t build_class(nfa_t *nfa, transition_type_t type, loreg_erro
|
||||
return frag;
|
||||
}
|
||||
|
||||
static nfa_fragment_t build_bracket(nfa_t *nfa, bracket_class_t *bracket, loreg_error_t *error) {
|
||||
static nfa_fragment_t build_bracket(nfa_t *nfa, bracket_class_t *bracket, lorex_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;
|
||||
*error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return frag;
|
||||
}
|
||||
nfa_add_bracket_transition(start, accept, bracket);
|
||||
@@ -158,12 +433,12 @@ static nfa_fragment_t build_bracket(nfa_t *nfa, bracket_class_t *bracket, loreg_
|
||||
return frag;
|
||||
}
|
||||
|
||||
static nfa_fragment_t build_concat(nfa_t *nfa, ast_node_t *left, ast_node_t *right, loreg_error_t *error) {
|
||||
static nfa_fragment_t build_concat(nfa_t *nfa, ast_node_t *left, ast_node_t *right, lorex_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;
|
||||
if (*error != LOREX_OK) return frag;
|
||||
nfa_fragment_t right_frag = build_nfa(nfa, right, error);
|
||||
if (*error != LOREG_OK) return frag;
|
||||
if (*error != LOREX_OK) return frag;
|
||||
|
||||
nfa_add_transition(left_frag.accept, right_frag.start, TRANS_EPSILON, '\0');
|
||||
frag.start = left_frag.start;
|
||||
@@ -171,19 +446,19 @@ static nfa_fragment_t build_concat(nfa_t *nfa, ast_node_t *left, ast_node_t *rig
|
||||
return frag;
|
||||
}
|
||||
|
||||
static nfa_fragment_t build_alter(nfa_t *nfa, ast_node_t *left, ast_node_t *right, loreg_error_t *error) {
|
||||
static nfa_fragment_t build_alter(nfa_t *nfa, ast_node_t *left, ast_node_t *right, lorex_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;
|
||||
*error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return frag;
|
||||
}
|
||||
|
||||
nfa_fragment_t left_frag = build_nfa(nfa, left, error);
|
||||
if (*error != LOREG_OK) return frag;
|
||||
if (*error != LOREX_OK) return frag;
|
||||
nfa_fragment_t right_frag = build_nfa(nfa, right, error);
|
||||
if (*error != LOREG_OK) return frag;
|
||||
if (*error != LOREX_OK) return frag;
|
||||
|
||||
nfa_add_transition(start, left_frag.start, TRANS_EPSILON, '\0');
|
||||
nfa_add_transition(start, right_frag.start, TRANS_EPSILON, '\0');
|
||||
@@ -195,17 +470,17 @@ static nfa_fragment_t build_alter(nfa_t *nfa, ast_node_t *left, ast_node_t *righ
|
||||
return frag;
|
||||
}
|
||||
|
||||
static nfa_fragment_t build_star(nfa_t *nfa, ast_node_t *child, bool greedy, loreg_error_t *error) {
|
||||
static nfa_fragment_t build_star(nfa_t *nfa, ast_node_t *child, bool greedy, lorex_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;
|
||||
*error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return frag;
|
||||
}
|
||||
|
||||
nfa_fragment_t child_frag = build_nfa(nfa, child, error);
|
||||
if (*error != LOREG_OK) return frag;
|
||||
if (*error != LOREX_OK) return frag;
|
||||
|
||||
if (greedy) {
|
||||
nfa_add_transition(start, child_frag.start, TRANS_EPSILON, '\0');
|
||||
@@ -222,16 +497,16 @@ static nfa_fragment_t build_star(nfa_t *nfa, ast_node_t *child, bool greedy, lor
|
||||
return frag;
|
||||
}
|
||||
|
||||
static nfa_fragment_t build_plus(nfa_t *nfa, ast_node_t *child, bool greedy, loreg_error_t *error) {
|
||||
static nfa_fragment_t build_plus(nfa_t *nfa, ast_node_t *child, bool greedy, lorex_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;
|
||||
*error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return frag;
|
||||
}
|
||||
|
||||
nfa_fragment_t child_frag = build_nfa(nfa, child, error);
|
||||
if (*error != LOREG_OK) return frag;
|
||||
if (*error != LOREX_OK) return frag;
|
||||
|
||||
if (greedy) {
|
||||
nfa_add_transition(child_frag.accept, child_frag.start, TRANS_EPSILON, '\0');
|
||||
@@ -246,17 +521,17 @@ static nfa_fragment_t build_plus(nfa_t *nfa, ast_node_t *child, bool greedy, lor
|
||||
return frag;
|
||||
}
|
||||
|
||||
static nfa_fragment_t build_question(nfa_t *nfa, ast_node_t *child, bool greedy, loreg_error_t *error) {
|
||||
static nfa_fragment_t build_question(nfa_t *nfa, ast_node_t *child, bool greedy, lorex_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;
|
||||
*error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return frag;
|
||||
}
|
||||
|
||||
nfa_fragment_t child_frag = build_nfa(nfa, child, error);
|
||||
if (*error != LOREG_OK) return frag;
|
||||
if (*error != LOREX_OK) return frag;
|
||||
|
||||
if (greedy) {
|
||||
nfa_add_transition(start, child_frag.start, TRANS_EPSILON, '\0');
|
||||
@@ -272,17 +547,17 @@ static nfa_fragment_t build_question(nfa_t *nfa, ast_node_t *child, bool greedy,
|
||||
return frag;
|
||||
}
|
||||
|
||||
static nfa_fragment_t build_group(nfa_t *nfa, ast_node_t *child, int group_id, loreg_error_t *error) {
|
||||
static nfa_fragment_t build_group(nfa_t *nfa, ast_node_t *child, int group_id, lorex_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;
|
||||
*error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return frag;
|
||||
}
|
||||
|
||||
nfa_fragment_t child_frag = build_nfa(nfa, child, error);
|
||||
if (*error != LOREG_OK) return frag;
|
||||
if (*error != LOREX_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);
|
||||
@@ -296,12 +571,12 @@ static nfa_fragment_t build_group(nfa_t *nfa, ast_node_t *child, int group_id, l
|
||||
return frag;
|
||||
}
|
||||
|
||||
static nfa_fragment_t build_anchor(nfa_t *nfa, transition_type_t type, loreg_error_t *error) {
|
||||
static nfa_fragment_t build_anchor(nfa_t *nfa, transition_type_t type, lorex_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;
|
||||
*error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return frag;
|
||||
}
|
||||
nfa_add_transition(start, accept, type, '\0');
|
||||
@@ -310,13 +585,13 @@ static nfa_fragment_t build_anchor(nfa_t *nfa, transition_type_t type, loreg_err
|
||||
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) {
|
||||
static nfa_fragment_t build_quantifier(nfa_t *nfa, ast_node_t *child, int min, int max, bool greedy, lorex_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;
|
||||
*error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return frag;
|
||||
}
|
||||
frag.start = state;
|
||||
@@ -326,7 +601,7 @@ static nfa_fragment_t build_quantifier(nfa_t *nfa, ast_node_t *child, int min, i
|
||||
|
||||
nfa_state_t *start = nfa_add_state(nfa);
|
||||
if (!start) {
|
||||
*error = LOREG_ERR_OUT_OF_MEMORY;
|
||||
*error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return frag;
|
||||
}
|
||||
|
||||
@@ -334,7 +609,7 @@ static nfa_fragment_t build_quantifier(nfa_t *nfa, ast_node_t *child, int min, i
|
||||
|
||||
for (int i = 0; i < min; i++) {
|
||||
nfa_fragment_t rep = build_nfa(nfa, child, error);
|
||||
if (*error != LOREG_OK) return frag;
|
||||
if (*error != LOREX_OK) return frag;
|
||||
nfa_add_transition(current, rep.start, TRANS_EPSILON, '\0');
|
||||
current = rep.accept;
|
||||
}
|
||||
@@ -343,14 +618,14 @@ static nfa_fragment_t build_quantifier(nfa_t *nfa, ast_node_t *child, int min, i
|
||||
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;
|
||||
*error = LOREX_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 (*error != LOREX_OK) return frag;
|
||||
|
||||
if (greedy) {
|
||||
nfa_add_transition(loop_start, rep.start, TRANS_EPSILON, '\0');
|
||||
@@ -366,7 +641,7 @@ static nfa_fragment_t build_quantifier(nfa_t *nfa, ast_node_t *child, int min, i
|
||||
} else {
|
||||
nfa_state_t *accept = nfa_add_state(nfa);
|
||||
if (!accept) {
|
||||
*error = LOREG_ERR_OUT_OF_MEMORY;
|
||||
*error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return frag;
|
||||
}
|
||||
|
||||
@@ -374,7 +649,7 @@ static nfa_fragment_t build_quantifier(nfa_t *nfa, ast_node_t *child, int min, i
|
||||
|
||||
for (int i = min; i < max; i++) {
|
||||
nfa_fragment_t rep = build_nfa(nfa, child, error);
|
||||
if (*error != LOREG_OK) return frag;
|
||||
if (*error != LOREX_OK) return frag;
|
||||
|
||||
if (greedy) {
|
||||
nfa_add_transition(current, rep.start, TRANS_EPSILON, '\0');
|
||||
@@ -400,13 +675,13 @@ static nfa_fragment_t build_quantifier(nfa_t *nfa, ast_node_t *child, int min, i
|
||||
return frag;
|
||||
}
|
||||
|
||||
static nfa_fragment_t build_nfa(nfa_t *nfa, ast_node_t *ast, loreg_error_t *error) {
|
||||
static nfa_fragment_t build_nfa(nfa_t *nfa, ast_node_t *ast, lorex_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;
|
||||
*error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return frag;
|
||||
}
|
||||
frag.start = state;
|
||||
@@ -456,16 +731,53 @@ static nfa_fragment_t build_nfa(nfa_t *nfa, ast_node_t *ast, loreg_error_t *erro
|
||||
return frag;
|
||||
}
|
||||
|
||||
nfa_t *nfa_from_ast(ast_node_t *ast, loreg_error_t *error) {
|
||||
*error = LOREG_OK;
|
||||
nfa_t *nfa_from_ast(ast_node_t *ast, lorex_error_t *error) {
|
||||
*error = LOREX_OK;
|
||||
nfa_t *nfa = nfa_create();
|
||||
if (!nfa) {
|
||||
*error = LOREG_ERR_OUT_OF_MEMORY;
|
||||
*error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nfa->anchored_start = ast_starts_with_anchor(ast);
|
||||
nfa->anchored_end = ast_ends_with_anchor(ast);
|
||||
|
||||
nfa->first_chars_valid = true;
|
||||
extract_first_chars(ast, nfa->first_chars, &nfa->first_chars_valid);
|
||||
if (nfa->first_chars_valid) {
|
||||
nfa->single_first_char = compute_single_first_char(nfa->first_chars);
|
||||
}
|
||||
|
||||
nfa->is_pure_literal = ast_is_pure_literal(ast);
|
||||
|
||||
char prefix_buf[256];
|
||||
size_t prefix_len = extract_literal_prefix(ast, prefix_buf, sizeof(prefix_buf));
|
||||
if (prefix_len > 0) {
|
||||
nfa->literal_prefix = malloc(prefix_len + 1);
|
||||
if (nfa->literal_prefix) {
|
||||
memcpy(nfa->literal_prefix, prefix_buf, prefix_len);
|
||||
nfa->literal_prefix[prefix_len] = '\0';
|
||||
nfa->prefix_len = prefix_len;
|
||||
}
|
||||
}
|
||||
|
||||
if (nfa->anchored_end) {
|
||||
char suffix_buf[256];
|
||||
size_t suffix_len = extract_literal_suffix(ast, suffix_buf, sizeof(suffix_buf));
|
||||
if (suffix_len > 0) {
|
||||
nfa->literal_suffix = malloc(suffix_len + 1);
|
||||
if (nfa->literal_suffix) {
|
||||
memcpy(nfa->literal_suffix, suffix_buf, suffix_len);
|
||||
nfa->literal_suffix[suffix_len] = '\0';
|
||||
nfa->suffix_len = suffix_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nfa->has_alt_dispatch = build_alt_dispatch(ast, nfa->alt_dispatch);
|
||||
|
||||
nfa_fragment_t frag = build_nfa(nfa, ast, error);
|
||||
if (*error != LOREG_OK) {
|
||||
if (*error != LOREX_OK) {
|
||||
nfa_free(nfa);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+20
-14
@@ -10,11 +10,11 @@ static void parser_advance(parser_t *parser) {
|
||||
void parser_init(parser_t *parser, const char *pattern) {
|
||||
lexer_init(&parser->lexer, pattern);
|
||||
parser->current = lexer_next(&parser->lexer);
|
||||
parser->error = LOREG_OK;
|
||||
parser->error = LOREX_OK;
|
||||
parser->group_count = 0;
|
||||
}
|
||||
|
||||
loreg_error_t parser_get_error(parser_t *parser) {
|
||||
lorex_error_t parser_get_error(parser_t *parser) {
|
||||
return parser->error;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ static int parse_number(parser_t *parser);
|
||||
|
||||
static ast_node_t *parse_expr(parser_t *parser) {
|
||||
ast_node_t *left = parse_term(parser);
|
||||
if (!left || parser->error != LOREG_OK) return left;
|
||||
if (!left || parser->error != LOREX_OK) return left;
|
||||
|
||||
while (parser->current.type == TOKEN_PIPE) {
|
||||
parser_advance(parser);
|
||||
@@ -38,7 +38,7 @@ static ast_node_t *parse_expr(parser_t *parser) {
|
||||
}
|
||||
left = ast_create_alter(left, right);
|
||||
if (!left) {
|
||||
parser->error = LOREG_ERR_OUT_OF_MEMORY;
|
||||
parser->error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ static ast_node_t *parse_term(parser_t *parser) {
|
||||
} else {
|
||||
left = ast_create_concat(left, factor);
|
||||
if (!left) {
|
||||
parser->error = LOREG_ERR_OUT_OF_MEMORY;
|
||||
parser->error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,7 @@ static ast_node_t *parse_term(parser_t *parser) {
|
||||
|
||||
static ast_node_t *parse_factor(parser_t *parser) {
|
||||
ast_node_t *atom = parse_atom(parser);
|
||||
if (!atom || parser->error != LOREG_OK) return atom;
|
||||
if (!atom || parser->error != LOREX_OK) return atom;
|
||||
|
||||
while (parser->current.type == TOKEN_STAR ||
|
||||
parser->current.type == TOKEN_PLUS ||
|
||||
@@ -107,7 +107,13 @@ static ast_node_t *parse_factor(parser_t *parser) {
|
||||
}
|
||||
|
||||
if (parser->current.type != TOKEN_RBRACE) {
|
||||
parser->error = LOREG_ERR_INVALID_QUANTIFIER;
|
||||
parser->error = LOREX_ERR_INVALID_QUANTIFIER;
|
||||
ast_free(atom);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (max != -1 && min > max) {
|
||||
parser->error = LOREX_ERR_INVALID_QUANTIFIER;
|
||||
ast_free(atom);
|
||||
return NULL;
|
||||
}
|
||||
@@ -122,7 +128,7 @@ static ast_node_t *parse_factor(parser_t *parser) {
|
||||
}
|
||||
|
||||
if (!atom) {
|
||||
parser->error = LOREG_ERR_OUT_OF_MEMORY;
|
||||
parser->error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -167,7 +173,7 @@ static ast_node_t *parse_atom(parser_t *parser) {
|
||||
int group_id = parser->group_count++;
|
||||
ast_node_t *inner = parse_expr(parser);
|
||||
if (parser->current.type != TOKEN_RPAREN) {
|
||||
parser->error = LOREG_ERR_UNBALANCED_PAREN;
|
||||
parser->error = LOREX_ERR_UNBALANCED_PAREN;
|
||||
ast_free(inner);
|
||||
return NULL;
|
||||
}
|
||||
@@ -216,12 +222,12 @@ static ast_node_t *parse_atom(parser_t *parser) {
|
||||
return NULL;
|
||||
|
||||
default:
|
||||
parser->error = LOREG_ERR_INVALID_PATTERN;
|
||||
parser->error = LOREX_ERR_INVALID_PATTERN;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!node && parser->error == LOREG_OK) {
|
||||
parser->error = LOREG_ERR_OUT_OF_MEMORY;
|
||||
if (!node && parser->error == LOREX_OK) {
|
||||
parser->error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
@@ -231,7 +237,7 @@ static ast_node_t *parse_bracket(parser_t *parser) {
|
||||
|
||||
bracket_class_t *bracket = bracket_create();
|
||||
if (!bracket) {
|
||||
parser->error = LOREG_ERR_OUT_OF_MEMORY;
|
||||
parser->error = LOREX_ERR_OUT_OF_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -293,7 +299,7 @@ static ast_node_t *parse_bracket(parser_t *parser) {
|
||||
|
||||
if (parser->current.type != TOKEN_RBRACKET) {
|
||||
bracket_free(bracket);
|
||||
parser->error = LOREG_ERR_INVALID_PATTERN;
|
||||
parser->error = LOREX_ERR_INVALID_PATTERN;
|
||||
return NULL;
|
||||
}
|
||||
parser_advance(parser);
|
||||
|
||||
+16
-16
@@ -1,6 +1,6 @@
|
||||
/* retoor <retoor@molodetz.nl> */
|
||||
#include "repl.h"
|
||||
#include "loreg.h"
|
||||
#include "lorex.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -8,12 +8,12 @@
|
||||
#define MAX_INPUT 4096
|
||||
|
||||
static void print_banner(void) {
|
||||
printf("loreg v%s - regex interpreter\n", LOREG_VERSION);
|
||||
printf("lorex v%s - regex interpreter\n", LOREX_VERSION);
|
||||
printf("commands: :q quit, :h help, :p <pattern> set pattern, :m <text> match, :s <text> search\n\n");
|
||||
}
|
||||
|
||||
static void print_help(void) {
|
||||
printf("loreg REPL commands:\n");
|
||||
printf("lorex REPL commands:\n");
|
||||
printf(" :q quit\n");
|
||||
printf(" :h show this help\n");
|
||||
printf(" :p <regex> compile and set pattern\n");
|
||||
@@ -40,7 +40,7 @@ static void print_help(void) {
|
||||
printf(" \\D \\W \\S negated classes\n\n");
|
||||
}
|
||||
|
||||
static void print_match(const char *text, loreg_match_t *result) {
|
||||
static void print_match(const char *text, lorex_match_t *result) {
|
||||
if (!result->matched) {
|
||||
printf("no match\n");
|
||||
return;
|
||||
@@ -83,7 +83,7 @@ static char *read_line(void) {
|
||||
void repl_run(void) {
|
||||
print_banner();
|
||||
|
||||
loreg_regex_t *regex = NULL;
|
||||
lorex_regex_t *regex = NULL;
|
||||
char *line;
|
||||
|
||||
while ((line = read_line()) != NULL) {
|
||||
@@ -103,14 +103,14 @@ void repl_run(void) {
|
||||
while (*pattern == ' ') pattern++;
|
||||
|
||||
if (regex) {
|
||||
loreg_free(regex);
|
||||
lorex_free(regex);
|
||||
regex = NULL;
|
||||
}
|
||||
|
||||
loreg_error_t error;
|
||||
regex = loreg_compile(pattern, &error);
|
||||
lorex_error_t error;
|
||||
regex = lorex_compile(pattern, &error);
|
||||
if (!regex) {
|
||||
printf("error: %s\n", loreg_error_string(error));
|
||||
printf("error: %s\n", lorex_error_string(error));
|
||||
} else {
|
||||
printf("pattern compiled: %s\n", pattern);
|
||||
}
|
||||
@@ -126,8 +126,8 @@ void repl_run(void) {
|
||||
const char *text = line + 3;
|
||||
while (*text == ' ') text++;
|
||||
|
||||
loreg_match_t result;
|
||||
loreg_match(regex, text, &result);
|
||||
lorex_match_t result;
|
||||
lorex_match(regex, text, &result);
|
||||
print_match(text, &result);
|
||||
continue;
|
||||
}
|
||||
@@ -141,8 +141,8 @@ void repl_run(void) {
|
||||
const char *text = line + 3;
|
||||
while (*text == ' ') text++;
|
||||
|
||||
loreg_match_t result;
|
||||
loreg_search(regex, text, &result);
|
||||
lorex_match_t result;
|
||||
lorex_search(regex, text, &result);
|
||||
print_match(text, &result);
|
||||
continue;
|
||||
}
|
||||
@@ -157,13 +157,13 @@ void repl_run(void) {
|
||||
continue;
|
||||
}
|
||||
|
||||
loreg_match_t result;
|
||||
loreg_search(regex, line, &result);
|
||||
lorex_match_t result;
|
||||
lorex_search(regex, line, &result);
|
||||
print_match(line, &result);
|
||||
}
|
||||
|
||||
if (regex) {
|
||||
loreg_free(regex);
|
||||
lorex_free(regex);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
|
||||
Reference in New Issue
Block a user