chore: update c, h, md files
CI / build (push) Has been cancelled
CI / test (push) Has been cancelled
CI / valgrind (push) Has been cancelled
CI / coverage (push) Has been cancelled

This commit is contained in:
2026-01-04 01:58:43 +01:00
parent 3d9c4aa00b
commit 7f728a5284
26 changed files with 1967 additions and 513 deletions
+448
View File
@@ -0,0 +1,448 @@
/* retoor <retoor@molodetz.nl> */
#define _POSIX_C_SOURCE 200809L
#include "../include/lorex.h"
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#define ITERATIONS 10000
#define WARMUP 1000
typedef struct {
const char *name;
const char *pattern;
const char *text;
int expect_match;
} benchmark_t;
typedef struct {
double lorex_compile_us;
double lorex_match_us;
double lorex_total_us;
double posix_compile_us;
double posix_match_us;
double posix_total_us;
int lorex_matched;
int posix_matched;
int lorex_failed;
int posix_failed;
} result_t;
static benchmark_t benchmarks[] = {
{"literal_short", "hello", "hello world", 1},
{"literal_medium", "the quick brown", "the quick brown fox jumps over the lazy dog", 1},
{"literal_long", "Lorem ipsum dolor sit amet", "Lorem ipsum dolor sit amet, consectetur adipiscing elit", 1},
{"literal_nomatch", "xyz", "the quick brown fox jumps over the lazy dog", 0},
{"literal_end", "dog", "the quick brown fox jumps over the lazy dog", 1},
{"literal_repeated", "abcabc", "xyzabcabcdef", 1},
{"dot_single", "a.c", "abc", 1},
{"dot_multiple", "a..b", "aXYb", 1},
{"dot_many", "a.....b", "a12345b", 1},
{"dot_star", "a.*b", "aXXXXXXXXXXb", 1},
{"dot_plus", "a.+b", "aXXXXXXXXXXb", 1},
{"anchor_start", "^the", "the quick brown fox", 1},
{"anchor_end", "fox$", "the quick brown fox", 1},
{"anchor_both", "^hello$", "hello", 1},
{"anchor_start_nomatch", "^fox", "the quick brown fox", 0},
{"anchor_end_nomatch", "the$", "the quick brown fox", 0},
{"star_simple", "ab*c", "abbbbc", 1},
{"star_zero", "ab*c", "ac", 1},
{"star_greedy", "a.*b", "aXbXbXb", 1},
{"star_repeated", "a*b*c*", "aaabbbccc", 1},
{"star_empty", "a*", "", 1},
{"plus_simple", "ab+c", "abbbbc", 1},
{"plus_one", "ab+c", "abc", 1},
{"plus_nomatch", "ab+c", "ac", 0},
{"plus_greedy", "a.+b", "aXbXbXb", 1},
{"question_present", "colou?r", "colour", 1},
{"question_absent", "colou?r", "color", 1},
{"question_multiple", "a?b?c?d", "abcd", 1},
{"class_vowels", "[aeiou]", "hello", 1},
{"class_digits", "[0-9]+", "abc123def", 1},
{"class_alpha", "[a-zA-Z]+", "HelloWorld", 1},
{"class_alnum", "[a-zA-Z0-9]+", "Test123", 1},
{"class_neg_digit", "[^0-9]+", "hello", 1},
{"class_neg_alpha", "[^a-zA-Z]+", "12345", 1},
{"class_complex", "[a-zA-Z_][a-zA-Z0-9_]*", "variable_name_123", 1},
{"alt_simple", "cat|dog", "I have a cat", 1},
{"alt_simple2", "cat|dog", "I have a dog", 1},
{"alt_three", "red|green|blue", "the color is green", 1},
{"alt_nomatch", "cat|dog", "I have a bird", 0},
{"alt_words", "hello|world|test", "this is a test", 1},
{"group_simple", "(ab)+", "ababab", 1},
{"group_alt", "(cat|dog)s?", "cats", 1},
{"group_nested", "((a)(b))+", "ababab", 1},
{"group_complex", "(a(b(c)))+", "abcabc", 1},
{"quant_exact", "a{3}", "aaa", 1},
{"quant_exact_long", "a{10}", "aaaaaaaaaa", 1},
{"quant_range", "a{2,4}", "aaa", 1},
{"quant_min", "a{3,}", "aaaaa", 1},
{"quant_combined", "[0-9]{3}-[0-9]{4}", "555-1234", 1},
{"email_simple", "[a-z]+@[a-z]+\\.[a-z]+", "test@example.com", 1},
{"email_complex", "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}", "user.name+tag@sub.example.com", 1},
{"ip_address", "[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}", "192.168.1.100", 1},
{"url_http", "https?://[a-zA-Z0-9.-]+", "https://www.example.com", 1},
{"phone_us", "[0-9]{3}-[0-9]{3}-[0-9]{4}", "555-123-4567", 1},
{"date_iso", "[0-9]{4}-[0-9]{2}-[0-9]{2}", "2024-01-15", 1},
{"time_hms", "[0-9]{2}:[0-9]{2}:[0-9]{2}", "14:30:45", 1},
{"hex_color", "#[0-9a-fA-F]{6}", "#ff00ff", 1},
{"word_boundary", "[a-zA-Z]+", "hello world test", 1},
{"whitespace", "[ \\t\\n]+", "hello world", 1},
{"identifier", "[a-zA-Z_][a-zA-Z0-9_]*", "_privateVar123", 1},
{"number_int", "-?[0-9]+", "-12345", 1},
{"number_float", "-?[0-9]+\\.[0-9]+", "3.14159", 1},
{"long_text_start", "^The", "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.", 1},
{"long_text_end", "dog\\.$", "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.", 1},
{"long_text_middle", "fox", "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.", 1},
{"long_text_nomatch", "elephant", "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.", 0},
{"repeated_ab", "(ab){5}", "ababababab", 1},
{"repeated_word", "(hello ){3}", "hello hello hello ", 1},
{"alternation_long", "one|two|three|four|five|six|seven|eight|nine|ten", "the number is seven", 1},
{"escape_dot", "3\\.14", "pi is 3.14", 1},
{"escape_star", "a\\*b", "a*b", 1},
{"escape_plus", "c\\+\\+", "c++", 1},
{"escape_parens", "\\(test\\)", "(test)", 1},
{"escape_brackets", "\\[0\\]", "array[0]", 1},
{"stress_star", "a*a*a*a*a*b", "aaaaab", 1},
{"stress_plus", "a+a+a+a+a+b", "aaaaab", 1},
{"stress_nested", "((a+)+)+b", "aaaab", 1},
{"stress_alt", "(a|aa|aaa|aaaa)+b", "aaaab", 1},
{"nomatch_literal", "notfound", "the quick brown fox", 0},
{"nomatch_pattern", "^end", "start middle end", 0},
{"nomatch_class", "[0-9]+", "no digits here", 0},
{NULL, NULL, NULL, 0}
};
static double get_time_us(void) {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000000.0 + tv.tv_usec;
}
static result_t run_benchmark(benchmark_t *bench) {
result_t res = {0};
double start, end;
for (int i = 0; i < WARMUP; i++) {
lorex_error_t err;
lorex_regex_t *re = lorex_compile(bench->pattern, &err);
if (re) {
lorex_match_t m;
lorex_search(re, bench->text, &m);
lorex_free(re);
}
}
start = get_time_us();
for (int i = 0; i < ITERATIONS; i++) {
lorex_error_t err;
lorex_regex_t *re = lorex_compile(bench->pattern, &err);
if (!re) {
res.lorex_failed = 1;
break;
}
lorex_free(re);
}
end = get_time_us();
res.lorex_compile_us = (end - start) / ITERATIONS;
start = get_time_us();
{
lorex_error_t err;
lorex_regex_t *re = lorex_compile(bench->pattern, &err);
if (re) {
for (int i = 0; i < ITERATIONS; i++) {
lorex_match_t m;
res.lorex_matched = lorex_search(re, bench->text, &m) ? 1 : 0;
}
lorex_free(re);
}
}
end = get_time_us();
res.lorex_match_us = (end - start) / ITERATIONS;
res.lorex_total_us = res.lorex_compile_us + res.lorex_match_us;
for (int i = 0; i < WARMUP; i++) {
regex_t preg;
if (regcomp(&preg, bench->pattern, REG_EXTENDED) == 0) {
regmatch_t pmatch[1];
regexec(&preg, bench->text, 1, pmatch, 0);
regfree(&preg);
}
}
start = get_time_us();
for (int i = 0; i < ITERATIONS; i++) {
regex_t preg;
if (regcomp(&preg, bench->pattern, REG_EXTENDED) != 0) {
res.posix_failed = 1;
break;
}
regfree(&preg);
}
end = get_time_us();
res.posix_compile_us = (end - start) / ITERATIONS;
start = get_time_us();
{
regex_t preg;
if (regcomp(&preg, bench->pattern, REG_EXTENDED) == 0) {
for (int i = 0; i < ITERATIONS; i++) {
regmatch_t pmatch[1];
res.posix_matched = (regexec(&preg, bench->text, 1, pmatch, 0) == 0) ? 1 : 0;
}
regfree(&preg);
}
}
end = get_time_us();
res.posix_match_us = (end - start) / ITERATIONS;
res.posix_total_us = res.posix_compile_us + res.posix_match_us;
return res;
}
int main(void) {
printf("================================================================================\n");
printf(" LOREX vs POSIX REGEX PERFORMANCE BENCHMARK\n");
printf("================================================================================\n\n");
printf("Configuration:\n");
printf(" Iterations per test: %d\n", ITERATIONS);
printf(" Warmup iterations: %d\n", WARMUP);
printf("\n");
int total_tests = 0;
int lorex_wins = 0;
int posix_wins = 0;
int ties = 0;
double total_lorex_time = 0;
double total_posix_time = 0;
int lorex_compile_wins = 0;
int posix_compile_wins = 0;
int lorex_match_wins = 0;
int posix_match_wins = 0;
printf("================================================================================\n");
printf("%-25s | %-12s | %-12s | %-12s | %-8s\n", "TEST NAME", "LOREX (us)", "POSIX (us)", "SPEEDUP", "WINNER");
printf("================================================================================\n");
for (int i = 0; benchmarks[i].name != NULL; i++) {
benchmark_t *bench = &benchmarks[i];
result_t res = run_benchmark(bench);
if (res.lorex_failed || res.posix_failed) {
printf("%-25s | %-12s | %-12s | %-12s | %-8s\n",
bench->name,
res.lorex_failed ? "FAILED" : "OK",
res.posix_failed ? "FAILED" : "OK",
"-", "-");
continue;
}
total_tests++;
total_lorex_time += res.lorex_total_us;
total_posix_time += res.posix_total_us;
double speedup = res.posix_total_us / res.lorex_total_us;
const char *winner;
if (speedup > 1.05) {
winner = "LOREX";
lorex_wins++;
} else if (speedup < 0.95) {
winner = "POSIX";
posix_wins++;
} else {
winner = "TIE";
ties++;
}
if (res.lorex_compile_us < res.posix_compile_us) lorex_compile_wins++;
else posix_compile_wins++;
if (res.lorex_match_us < res.posix_match_us) lorex_match_wins++;
else posix_match_wins++;
printf("%-25s | %10.3f | %10.3f | %10.2fx | %-8s\n",
bench->name,
res.lorex_total_us,
res.posix_total_us,
speedup,
winner);
}
printf("================================================================================\n\n");
printf("================================================================================\n");
printf(" DETAILED RESULTS\n");
printf("================================================================================\n\n");
printf("%-25s | %-20s | %-20s\n", "TEST NAME", "LOREX (compile/match)", "POSIX (compile/match)");
printf("--------------------------------------------------------------------------------\n");
for (int i = 0; benchmarks[i].name != NULL; i++) {
benchmark_t *bench = &benchmarks[i];
result_t res = run_benchmark(bench);
if (res.lorex_failed || res.posix_failed) continue;
printf("%-25s | %8.3f / %8.3f | %8.3f / %8.3f\n",
bench->name,
res.lorex_compile_us, res.lorex_match_us,
res.posix_compile_us, res.posix_match_us);
}
printf("\n================================================================================\n");
printf(" SUMMARY\n");
printf("================================================================================\n\n");
printf("Total tests: %d\n", total_tests);
printf("\n");
printf("Overall wins:\n");
printf(" LOREX wins: %d (%.1f%%)\n", lorex_wins, 100.0 * lorex_wins / total_tests);
printf(" POSIX wins: %d (%.1f%%)\n", posix_wins, 100.0 * posix_wins / total_tests);
printf(" Ties: %d (%.1f%%)\n", ties, 100.0 * ties / total_tests);
printf("\n");
printf("Compilation phase wins:\n");
printf(" LOREX faster: %d\n", lorex_compile_wins);
printf(" POSIX faster: %d\n", posix_compile_wins);
printf("\n");
printf("Matching phase wins:\n");
printf(" LOREX faster: %d\n", lorex_match_wins);
printf(" POSIX faster: %d\n", posix_match_wins);
printf("\n");
printf("Total time (all tests):\n");
printf(" LOREX: %.3f us\n", total_lorex_time);
printf(" POSIX: %.3f us\n", total_posix_time);
printf(" Overall speedup: %.2fx %s\n",
total_posix_time > total_lorex_time ? total_posix_time / total_lorex_time : total_lorex_time / total_posix_time,
total_posix_time > total_lorex_time ? "(LOREX faster)" : "(POSIX faster)");
printf("\n================================================================================\n");
printf(" CATEGORY BREAKDOWN\n");
printf("================================================================================\n\n");
typedef struct {
const char *category;
const char *prefix;
double lorex_total;
double posix_total;
int count;
} category_t;
category_t categories[] = {
{"Literal matching", "literal_", 0, 0, 0},
{"Dot metacharacter", "dot_", 0, 0, 0},
{"Anchors", "anchor_", 0, 0, 0},
{"Star quantifier", "star_", 0, 0, 0},
{"Plus quantifier", "plus_", 0, 0, 0},
{"Question quantifier", "question_", 0, 0, 0},
{"Character classes", "class_", 0, 0, 0},
{"Alternation", "alt_", 0, 0, 0},
{"Groups", "group_", 0, 0, 0},
{"Brace quantifiers", "quant_", 0, 0, 0},
{"Real-world patterns", "email_", 0, 0, 0},
{"Escape sequences", "escape_", 0, 0, 0},
{"Stress tests", "stress_", 0, 0, 0},
{"No-match tests", "nomatch_", 0, 0, 0},
{NULL, NULL, 0, 0, 0}
};
for (int i = 0; benchmarks[i].name != NULL; i++) {
benchmark_t *bench = &benchmarks[i];
result_t res = run_benchmark(bench);
if (res.lorex_failed || res.posix_failed) continue;
for (int j = 0; categories[j].category != NULL; j++) {
if (strncmp(bench->name, categories[j].prefix, strlen(categories[j].prefix)) == 0) {
categories[j].lorex_total += res.lorex_total_us;
categories[j].posix_total += res.posix_total_us;
categories[j].count++;
break;
}
}
}
printf("%-25s | %-12s | %-12s | %-12s | %-8s\n", "CATEGORY", "LOREX (us)", "POSIX (us)", "SPEEDUP", "WINNER");
printf("--------------------------------------------------------------------------------\n");
for (int i = 0; categories[i].category != NULL; i++) {
if (categories[i].count == 0) continue;
double speedup = categories[i].posix_total / categories[i].lorex_total;
const char *winner = speedup > 1.0 ? "LOREX" : "POSIX";
printf("%-25s | %10.3f | %10.3f | %10.2fx | %-8s\n",
categories[i].category,
categories[i].lorex_total,
categories[i].posix_total,
speedup > 1.0 ? speedup : 1.0 / speedup,
winner);
}
printf("\n================================================================================\n");
printf(" PATTERN DETAILS\n");
printf("================================================================================\n\n");
for (int i = 0; benchmarks[i].name != NULL; i++) {
benchmark_t *bench = &benchmarks[i];
result_t res = run_benchmark(bench);
printf("Test: %s\n", bench->name);
printf(" Pattern: %s\n", bench->pattern);
printf(" Text: %.50s%s\n", bench->text, strlen(bench->text) > 50 ? "..." : "");
printf(" Expected: %s\n", bench->expect_match ? "MATCH" : "NO MATCH");
if (res.lorex_failed) {
printf(" LOREX: FAILED TO COMPILE\n");
} else {
printf(" LOREX: %s (compile: %.3f us, match: %.3f us, total: %.3f us)\n",
res.lorex_matched ? "MATCHED" : "NO MATCH",
res.lorex_compile_us, res.lorex_match_us, res.lorex_total_us);
}
if (res.posix_failed) {
printf(" POSIX: FAILED TO COMPILE\n");
} else {
printf(" POSIX: %s (compile: %.3f us, match: %.3f us, total: %.3f us)\n",
res.posix_matched ? "MATCHED" : "NO MATCH",
res.posix_compile_us, res.posix_match_us, res.posix_total_us);
}
if (!res.lorex_failed && !res.posix_failed) {
double speedup = res.posix_total_us / res.lorex_total_us;
if (speedup > 1.0) {
printf(" Result: LOREX is %.2fx faster\n", speedup);
} else {
printf(" Result: POSIX is %.2fx faster\n", 1.0 / speedup);
}
}
printf("\n");
}
printf("================================================================================\n");
printf(" BENCHMARK COMPLETE\n");
printf("================================================================================\n");
return 0;
}
+125 -125
View File
@@ -1,5 +1,5 @@
/* retoor <retoor@molodetz.nl> */
#include "../include/loreg.h"
#include "../include/lorex.h"
#include <stdio.h>
#include <string.h>
#include <time.h>
@@ -22,211 +22,211 @@ static int total_failed = 0;
} while(0)
TEST(basic_literals) {
loreg_error_t err;
loreg_regex_t *re = loreg_compile("hello", &err);
lorex_error_t err;
lorex_regex_t *re = lorex_compile("hello", &err);
ASSERT(re != NULL, "compile hello");
loreg_match_t m;
ASSERT(loreg_search(re, "hello", &m), "match hello");
ASSERT(loreg_search(re, "say hello world", &m), "search hello");
ASSERT(!loreg_search(re, "helo", &m), "no match helo");
lorex_match_t m;
ASSERT(lorex_search(re, "hello", &m), "match hello");
ASSERT(lorex_search(re, "say hello world", &m), "search hello");
ASSERT(!lorex_search(re, "helo", &m), "no match helo");
loreg_free(re);
lorex_free(re);
}
TEST(metacharacters) {
loreg_error_t err;
loreg_match_t m;
lorex_error_t err;
lorex_match_t m;
loreg_regex_t *re = loreg_compile("a.c", &err);
lorex_regex_t *re = lorex_compile("a.c", &err);
ASSERT(re != NULL, "compile a.c");
ASSERT(loreg_search(re, "abc", &m), "match abc");
ASSERT(loreg_search(re, "axc", &m), "match axc");
ASSERT(!loreg_search(re, "ac", &m), "no match ac");
loreg_free(re);
ASSERT(lorex_search(re, "abc", &m), "match abc");
ASSERT(lorex_search(re, "axc", &m), "match axc");
ASSERT(!lorex_search(re, "ac", &m), "no match ac");
lorex_free(re);
re = loreg_compile("^start", &err);
re = lorex_compile("^start", &err);
ASSERT(re != NULL, "compile ^start");
ASSERT(loreg_search(re, "start here", &m), "match start here");
ASSERT(!loreg_search(re, "not start", &m), "no match not start");
loreg_free(re);
ASSERT(lorex_search(re, "start here", &m), "match start here");
ASSERT(!lorex_search(re, "not start", &m), "no match not start");
lorex_free(re);
re = loreg_compile("end$", &err);
re = lorex_compile("end$", &err);
ASSERT(re != NULL, "compile end$");
ASSERT(loreg_search(re, "the end", &m), "match the end");
ASSERT(!loreg_search(re, "end here", &m), "no match end here");
loreg_free(re);
ASSERT(lorex_search(re, "the end", &m), "match the end");
ASSERT(!lorex_search(re, "end here", &m), "no match end here");
lorex_free(re);
}
TEST(quantifiers) {
loreg_error_t err;
loreg_match_t m;
lorex_error_t err;
lorex_match_t m;
loreg_regex_t *re = loreg_compile("ab*c", &err);
lorex_regex_t *re = lorex_compile("ab*c", &err);
ASSERT(re != NULL, "compile ab*c");
ASSERT(loreg_search(re, "ac", &m), "match ac");
ASSERT(loreg_search(re, "abc", &m), "match abc");
ASSERT(loreg_search(re, "abbbbc", &m), "match abbbbc");
loreg_free(re);
ASSERT(lorex_search(re, "ac", &m), "match ac");
ASSERT(lorex_search(re, "abc", &m), "match abc");
ASSERT(lorex_search(re, "abbbbc", &m), "match abbbbc");
lorex_free(re);
re = loreg_compile("ab+c", &err);
re = lorex_compile("ab+c", &err);
ASSERT(re != NULL, "compile ab+c");
ASSERT(!loreg_search(re, "ac", &m), "no match ac");
ASSERT(loreg_search(re, "abc", &m), "match abc");
ASSERT(loreg_search(re, "abbbbc", &m), "match abbbbc");
loreg_free(re);
ASSERT(!lorex_search(re, "ac", &m), "no match ac");
ASSERT(lorex_search(re, "abc", &m), "match abc");
ASSERT(lorex_search(re, "abbbbc", &m), "match abbbbc");
lorex_free(re);
re = loreg_compile("ab?c", &err);
re = lorex_compile("ab?c", &err);
ASSERT(re != NULL, "compile ab?c");
ASSERT(loreg_search(re, "ac", &m), "match ac");
ASSERT(loreg_search(re, "abc", &m), "match abc");
ASSERT(!loreg_search(re, "abbc", &m), "no match abbc");
loreg_free(re);
ASSERT(lorex_search(re, "ac", &m), "match ac");
ASSERT(lorex_search(re, "abc", &m), "match abc");
ASSERT(!lorex_search(re, "abbc", &m), "no match abbc");
lorex_free(re);
re = loreg_compile("a{3}", &err);
re = lorex_compile("a{3}", &err);
ASSERT(re != NULL, "compile a{3}");
ASSERT(loreg_search(re, "aaa", &m), "match aaa");
ASSERT(!loreg_search(re, "aa", &m), "no match aa");
loreg_free(re);
ASSERT(lorex_search(re, "aaa", &m), "match aaa");
ASSERT(!lorex_search(re, "aa", &m), "no match aa");
lorex_free(re);
re = loreg_compile("a{2,4}", &err);
re = lorex_compile("a{2,4}", &err);
ASSERT(re != NULL, "compile a{2,4}");
ASSERT(loreg_search(re, "aa", &m), "match aa");
ASSERT(loreg_search(re, "aaa", &m), "match aaa");
ASSERT(loreg_search(re, "aaaa", &m), "match aaaa");
ASSERT(!loreg_search(re, "a", &m), "no match a");
loreg_free(re);
ASSERT(lorex_search(re, "aa", &m), "match aa");
ASSERT(lorex_search(re, "aaa", &m), "match aaa");
ASSERT(lorex_search(re, "aaaa", &m), "match aaaa");
ASSERT(!lorex_search(re, "a", &m), "no match a");
lorex_free(re);
}
TEST(character_classes) {
loreg_error_t err;
loreg_match_t m;
lorex_error_t err;
lorex_match_t m;
loreg_regex_t *re = loreg_compile("[aeiou]", &err);
lorex_regex_t *re = lorex_compile("[aeiou]", &err);
ASSERT(re != NULL, "compile [aeiou]");
ASSERT(loreg_search(re, "a", &m), "match a");
ASSERT(loreg_search(re, "test", &m), "match test");
ASSERT(!loreg_search(re, "xyz", &m), "no match xyz");
loreg_free(re);
ASSERT(lorex_search(re, "a", &m), "match a");
ASSERT(lorex_search(re, "test", &m), "match test");
ASSERT(!lorex_search(re, "xyz", &m), "no match xyz");
lorex_free(re);
re = loreg_compile("[a-z]", &err);
re = lorex_compile("[a-z]", &err);
ASSERT(re != NULL, "compile [a-z]");
ASSERT(loreg_search(re, "m", &m), "match m");
ASSERT(!loreg_search(re, "5", &m), "no match 5");
loreg_free(re);
ASSERT(lorex_search(re, "m", &m), "match m");
ASSERT(!lorex_search(re, "5", &m), "no match 5");
lorex_free(re);
re = loreg_compile("[^0-9]", &err);
re = lorex_compile("[^0-9]", &err);
ASSERT(re != NULL, "compile [^0-9]");
ASSERT(loreg_search(re, "a", &m), "match a");
ASSERT(!loreg_search(re, "5", &m), "no match 5");
loreg_free(re);
ASSERT(lorex_search(re, "a", &m), "match a");
ASSERT(!lorex_search(re, "5", &m), "no match 5");
lorex_free(re);
re = loreg_compile("\\d", &err);
re = lorex_compile("\\d", &err);
ASSERT(re != NULL, "compile \\d");
ASSERT(loreg_search(re, "5", &m), "match 5");
ASSERT(!loreg_search(re, "a", &m), "no match a");
loreg_free(re);
ASSERT(lorex_search(re, "5", &m), "match 5");
ASSERT(!lorex_search(re, "a", &m), "no match a");
lorex_free(re);
re = loreg_compile("\\w+", &err);
re = lorex_compile("\\w+", &err);
ASSERT(re != NULL, "compile \\w+");
ASSERT(loreg_search(re, "hello_123", &m), "match hello_123");
loreg_free(re);
ASSERT(lorex_search(re, "hello_123", &m), "match hello_123");
lorex_free(re);
re = loreg_compile("\\s", &err);
re = lorex_compile("\\s", &err);
ASSERT(re != NULL, "compile \\s");
ASSERT(loreg_search(re, " ", &m), "match space");
ASSERT(loreg_search(re, "\t", &m), "match tab");
ASSERT(!loreg_search(re, "a", &m), "no match a");
loreg_free(re);
ASSERT(lorex_search(re, " ", &m), "match space");
ASSERT(lorex_search(re, "\t", &m), "match tab");
ASSERT(!lorex_search(re, "a", &m), "no match a");
lorex_free(re);
}
TEST(groups) {
loreg_error_t err;
loreg_match_t m;
lorex_error_t err;
lorex_match_t m;
loreg_regex_t *re = loreg_compile("(ab)+", &err);
lorex_regex_t *re = lorex_compile("(ab)+", &err);
ASSERT(re != NULL, "compile (ab)+");
ASSERT(loreg_search(re, "ab", &m), "match ab");
ASSERT(loreg_search(re, "abab", &m), "match abab");
ASSERT(!loreg_search(re, "a", &m), "no match a");
loreg_free(re);
ASSERT(lorex_search(re, "ab", &m), "match ab");
ASSERT(lorex_search(re, "abab", &m), "match abab");
ASSERT(!lorex_search(re, "a", &m), "no match a");
lorex_free(re);
re = loreg_compile("(\\d+)-(\\d+)", &err);
re = lorex_compile("(\\d+)-(\\d+)", &err);
ASSERT(re != NULL, "compile groups");
ASSERT(loreg_search(re, "123-456", &m), "match 123-456");
ASSERT(lorex_search(re, "123-456", &m), "match 123-456");
ASSERT(m.group_count == 2, "2 groups");
ASSERT(m.groups[0].matched, "group 0 matched");
ASSERT(m.groups[1].matched, "group 1 matched");
loreg_free(re);
lorex_free(re);
}
TEST(alternation) {
loreg_error_t err;
loreg_match_t m;
lorex_error_t err;
lorex_match_t m;
loreg_regex_t *re = loreg_compile("cat|dog", &err);
lorex_regex_t *re = lorex_compile("cat|dog", &err);
ASSERT(re != NULL, "compile cat|dog");
ASSERT(loreg_search(re, "cat", &m), "match cat");
ASSERT(loreg_search(re, "dog", &m), "match dog");
ASSERT(!loreg_search(re, "rat", &m), "no match rat");
loreg_free(re);
ASSERT(lorex_search(re, "cat", &m), "match cat");
ASSERT(lorex_search(re, "dog", &m), "match dog");
ASSERT(!lorex_search(re, "rat", &m), "no match rat");
lorex_free(re);
re = loreg_compile("(red|blue) car", &err);
re = lorex_compile("(red|blue) car", &err);
ASSERT(re != NULL, "compile (red|blue) car");
ASSERT(loreg_search(re, "red car", &m), "match red car");
ASSERT(loreg_search(re, "blue car", &m), "match blue car");
ASSERT(!loreg_search(re, "green car", &m), "no match green car");
loreg_free(re);
ASSERT(lorex_search(re, "red car", &m), "match red car");
ASSERT(lorex_search(re, "blue car", &m), "match blue car");
ASSERT(!lorex_search(re, "green car", &m), "no match green car");
lorex_free(re);
}
TEST(escapes) {
loreg_error_t err;
loreg_match_t m;
lorex_error_t err;
lorex_match_t m;
loreg_regex_t *re = loreg_compile("1\\.5", &err);
lorex_regex_t *re = lorex_compile("1\\.5", &err);
ASSERT(re != NULL, "compile 1\\.5");
ASSERT(loreg_search(re, "1.5", &m), "match 1.5");
ASSERT(!loreg_search(re, "1x5", &m), "no match 1x5");
loreg_free(re);
ASSERT(lorex_search(re, "1.5", &m), "match 1.5");
ASSERT(!lorex_search(re, "1x5", &m), "no match 1x5");
lorex_free(re);
re = loreg_compile("\\(test\\)", &err);
re = lorex_compile("\\(test\\)", &err);
ASSERT(re != NULL, "compile \\(test\\)");
ASSERT(loreg_search(re, "(test)", &m), "match (test)");
loreg_free(re);
ASSERT(lorex_search(re, "(test)", &m), "match (test)");
lorex_free(re);
}
TEST(real_patterns) {
loreg_error_t err;
loreg_match_t m;
lorex_error_t err;
lorex_match_t m;
loreg_regex_t *re = loreg_compile("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}", &err);
lorex_regex_t *re = lorex_compile("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}", &err);
ASSERT(re != NULL, "compile email");
ASSERT(loreg_search(re, "user@example.com", &m), "match email");
ASSERT(!loreg_search(re, "invalid", &m), "no match invalid");
loreg_free(re);
ASSERT(lorex_search(re, "user@example.com", &m), "match email");
ASSERT(!lorex_search(re, "invalid", &m), "no match invalid");
lorex_free(re);
re = loreg_compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}", &err);
re = lorex_compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}", &err);
ASSERT(re != NULL, "compile ip");
ASSERT(loreg_search(re, "192.168.1.1", &m), "match ip");
loreg_free(re);
ASSERT(lorex_search(re, "192.168.1.1", &m), "match ip");
lorex_free(re);
re = loreg_compile("https?://[a-zA-Z0-9.-]+(/[a-zA-Z0-9./-]*)?", &err);
re = lorex_compile("https?://[a-zA-Z0-9.-]+(/[a-zA-Z0-9./-]*)?", &err);
ASSERT(re != NULL, "compile url");
ASSERT(loreg_search(re, "http://example.com", &m), "match http");
ASSERT(loreg_search(re, "https://example.com/path", &m), "match https");
loreg_free(re);
ASSERT(lorex_search(re, "http://example.com", &m), "match http");
ASSERT(lorex_search(re, "https://example.com/path", &m), "match https");
lorex_free(re);
}
TEST(error_handling) {
loreg_error_t err;
lorex_error_t err;
loreg_regex_t *re = loreg_compile("(abc", &err);
lorex_regex_t *re = lorex_compile("(abc", &err);
ASSERT(re == NULL, "unbalanced paren");
ASSERT(err == LOREG_ERR_UNBALANCED_PAREN, "correct error");
ASSERT(err == LOREX_ERR_UNBALANCED_PAREN, "correct error");
}
int main(void) {
printf("loreg comprehensive tests\n");
printf("lorex comprehensive tests\n");
printf("========================\n\n");
clock_t start = clock();
+245 -8
View File
@@ -1,5 +1,5 @@
/* retoor <retoor@molodetz.nl> */
#include "../include/loreg.h"
#include "../include/lorex.h"
#include <stdio.h>
#include <string.h>
@@ -10,22 +10,22 @@ static int failed = 0;
#define NO_MATCH(pat, txt) test_match(pat, txt, 0, __LINE__)
static void test_match(const char *pattern, const char *text, int expect, int line) {
loreg_error_t err;
loreg_regex_t *re = loreg_compile(pattern, &err);
lorex_error_t err;
lorex_regex_t *re = lorex_compile(pattern, &err);
if (!re) {
printf("FAIL line %d: compile error for '%s': %s\n", line, pattern, loreg_error_string(err));
printf("FAIL line %d: compile error for '%s': %s\n", line, pattern, lorex_error_string(err));
failed++;
return;
}
loreg_match_t m;
int result = loreg_search(re, text, &m) ? 1 : 0;
lorex_match_t m;
int result = lorex_search(re, text, &m) ? 1 : 0;
if (result != expect) {
printf("FAIL line %d: '%s' vs '%s' expected %s\n", line, pattern, text, expect ? "match" : "no match");
failed++;
} else {
passed++;
}
loreg_free(re);
lorex_free(re);
}
static void test_literals(void) {
@@ -613,8 +613,240 @@ static void test_pathological_patterns(void) {
MATCH("(a?){5}a{5}", "aaaaa");
}
static void test_anchored_match(void) {
printf(" anchored match (lorex_match)...\n");
lorex_error_t err;
lorex_match_t m;
lorex_regex_t *re = lorex_compile("abc", &err);
if (re) {
if (lorex_match(re, "abc", &m)) {
passed++;
} else {
printf("FAIL: lorex_match should match 'abc' against 'abc'\n");
failed++;
}
if (!lorex_match(re, "xabc", &m)) {
passed++;
} else {
printf("FAIL: lorex_match should not match 'abc' against 'xabc'\n");
failed++;
}
if (lorex_match(re, "abcx", &m)) {
passed++;
} else {
printf("FAIL: lorex_match should match 'abc' at start of 'abcx'\n");
failed++;
}
lorex_free(re);
}
re = lorex_compile("^abc$", &err);
if (re) {
if (lorex_match(re, "abc", &m)) {
passed++;
} else {
printf("FAIL: lorex_match should match '^abc$' against 'abc'\n");
failed++;
}
if (!lorex_match(re, "abcx", &m)) {
passed++;
} else {
printf("FAIL: lorex_match should not match '^abc$' against 'abcx'\n");
failed++;
}
lorex_free(re);
}
re = lorex_compile("a.*z", &err);
if (re) {
if (lorex_match(re, "abcz", &m)) {
passed++;
} else {
printf("FAIL: lorex_match should match 'a.*z' against 'abcz'\n");
failed++;
}
if (!lorex_match(re, "xabcz", &m)) {
passed++;
} else {
printf("FAIL: lorex_match should not match 'a.*z' against 'xabcz'\n");
failed++;
}
lorex_free(re);
}
}
static void test_error_strings(void) {
printf(" error strings...\n");
if (strcmp(lorex_error_string(LOREX_OK), "success") == 0) {
passed++;
} else {
printf("FAIL: LOREX_OK should return 'success'\n");
failed++;
}
if (strcmp(lorex_error_string(LOREX_ERR_INVALID_PATTERN), "invalid pattern") == 0) {
passed++;
} else {
printf("FAIL: LOREX_ERR_INVALID_PATTERN error string\n");
failed++;
}
if (strcmp(lorex_error_string(LOREX_ERR_UNBALANCED_PAREN), "unbalanced parentheses") == 0) {
passed++;
} else {
printf("FAIL: LOREX_ERR_UNBALANCED_PAREN error string\n");
failed++;
}
if (strcmp(lorex_error_string(LOREX_ERR_EMPTY_GROUP), "empty group") == 0) {
passed++;
} else {
printf("FAIL: LOREX_ERR_EMPTY_GROUP error string\n");
failed++;
}
if (strcmp(lorex_error_string(LOREX_ERR_INVALID_QUANTIFIER), "invalid quantifier") == 0) {
passed++;
} else {
printf("FAIL: LOREX_ERR_INVALID_QUANTIFIER error string\n");
failed++;
}
if (strcmp(lorex_error_string(LOREX_ERR_INVALID_ESCAPE), "invalid escape sequence") == 0) {
passed++;
} else {
printf("FAIL: LOREX_ERR_INVALID_ESCAPE error string\n");
failed++;
}
if (strcmp(lorex_error_string(LOREX_ERR_OUT_OF_MEMORY), "out of memory") == 0) {
passed++;
} else {
printf("FAIL: LOREX_ERR_OUT_OF_MEMORY error string\n");
failed++;
}
if (strcmp(lorex_error_string(LOREX_ERR_STATE_OVERFLOW), "state overflow") == 0) {
passed++;
} else {
printf("FAIL: LOREX_ERR_STATE_OVERFLOW error string\n");
failed++;
}
if (strcmp(lorex_error_string((lorex_error_t)99), "unknown error") == 0) {
passed++;
} else {
printf("FAIL: unknown error code should return 'unknown error'\n");
failed++;
}
}
static void test_parser_errors(void) {
printf(" parser errors...\n");
lorex_error_t err;
lorex_regex_t *re;
re = lorex_compile("(abc", &err);
if (re == NULL && err == LOREX_ERR_UNBALANCED_PAREN) {
passed++;
} else {
printf("FAIL: '(abc' should fail with unbalanced paren\n");
failed++;
if (re) lorex_free(re);
}
re = lorex_compile("((a)", &err);
if (re == NULL && err == LOREX_ERR_UNBALANCED_PAREN) {
passed++;
} else {
printf("FAIL: '((a)' should fail with unbalanced paren\n");
failed++;
if (re) lorex_free(re);
}
re = lorex_compile("a{5,2}", &err);
if (re == NULL) {
passed++;
} else {
printf("FAIL: 'a{5,2}' should fail (min > max)\n");
failed++;
lorex_free(re);
}
re = lorex_compile("*abc", &err);
if (re == NULL) {
passed++;
} else {
printf("FAIL: '*abc' should fail\n");
failed++;
lorex_free(re);
}
re = lorex_compile("+abc", &err);
if (re == NULL) {
passed++;
} else {
printf("FAIL: '+abc' should fail\n");
failed++;
lorex_free(re);
}
re = lorex_compile("?abc", &err);
if (re == NULL) {
passed++;
} else {
printf("FAIL: '?abc' should fail\n");
failed++;
lorex_free(re);
}
}
static void test_bracket_char_classes(void) {
printf(" bracket character classes...\n");
MATCH("[\\d]", "5");
MATCH("[\\d]+", "12345");
NO_MATCH("[\\d]", "a");
MATCH("[\\w]", "a");
MATCH("[\\w]", "Z");
MATCH("[\\w]", "5");
MATCH("[\\w]", "_");
NO_MATCH("[\\w]", " ");
MATCH("[\\s]", " ");
MATCH("[\\s]", "\t");
NO_MATCH("[\\s]", "a");
MATCH("[a\\d]", "a");
MATCH("[a\\d]", "5");
NO_MATCH("[a\\d]", "b");
MATCH("[\\da-z]", "5");
MATCH("[\\da-z]", "m");
NO_MATCH("[\\da-z]", "M");
MATCH("[\\w\\s]+", "hello world");
MATCH("[0-9\\s]+", "1 2 3");
MATCH("[\\w-]+", "hello-world");
}
static void test_special_escapes(void) {
printf(" special escape sequences...\n");
MATCH("\\n", "\n");
MATCH("a\\nb", "a\nb");
MATCH("\\t", "\t");
MATCH("a\\tb", "a\tb");
MATCH("\\r", "\r");
MATCH("a\\rb", "a\rb");
MATCH("\\n\\t\\r", "\n\t\r");
MATCH("[\\n]", "\n");
MATCH("[\\t]", "\t");
MATCH("[\\r]", "\r");
MATCH("[\\n\\t]+", "\n\t\n");
NO_MATCH("\\n", "n");
NO_MATCH("\\t", "t");
NO_MATCH("\\r", "r");
}
int main(void) {
printf("loreg integration tests\n");
printf("lorex integration tests\n");
printf("=======================\n\n");
test_literals();
@@ -641,6 +873,11 @@ int main(void) {
test_nested_groups();
test_real_world_patterns();
test_pathological_patterns();
test_anchored_match();
test_error_strings();
test_parser_errors();
test_bracket_char_classes();
test_special_escapes();
printf("\n=======================\n");
printf("integration: %d passed, %d failed\n", passed, failed);
+31 -31
View File
@@ -1,5 +1,5 @@
/* retoor <retoor@molodetz.nl> */
#include "../include/loreg.h"
#include "../include/lorex.h"
#include <stdio.h>
#include <string.h>
@@ -23,21 +23,21 @@ static int tests_failed = 0;
} while(0)
#define ASSERT_MATCH(pattern, text) do { \
loreg_error_t err; \
loreg_regex_t *re = loreg_compile(pattern, &err); \
lorex_error_t err; \
lorex_regex_t *re = lorex_compile(pattern, &err); \
ASSERT(re != NULL); \
loreg_match_t result; \
ASSERT(loreg_search(re, text, &result) == true); \
loreg_free(re); \
lorex_match_t result; \
ASSERT(lorex_search(re, text, &result) == true); \
lorex_free(re); \
} while(0)
#define ASSERT_NO_MATCH(pattern, text) do { \
loreg_error_t err; \
loreg_regex_t *re = loreg_compile(pattern, &err); \
lorex_error_t err; \
lorex_regex_t *re = lorex_compile(pattern, &err); \
ASSERT(re != NULL); \
loreg_match_t result; \
ASSERT(loreg_search(re, text, &result) == false); \
loreg_free(re); \
lorex_match_t result; \
ASSERT(lorex_search(re, text, &result) == false); \
lorex_free(re); \
} while(0)
TEST(simple_char) {
@@ -209,53 +209,53 @@ TEST(complex_url) {
}
TEST(group_capture) {
loreg_error_t err;
loreg_regex_t *re = loreg_compile("(\\d+)-(\\d+)", &err);
lorex_error_t err;
lorex_regex_t *re = lorex_compile("(\\d+)-(\\d+)", &err);
ASSERT(re != NULL);
loreg_match_t result;
ASSERT(loreg_search(re, "123-456", &result));
lorex_match_t result;
ASSERT(lorex_search(re, "123-456", &result));
ASSERT(result.group_count == 2);
ASSERT(result.groups[0].matched);
ASSERT(result.groups[1].matched);
loreg_free(re);
lorex_free(re);
}
TEST(nested_groups) {
loreg_error_t err;
loreg_regex_t *re = loreg_compile("((a)(b))", &err);
lorex_error_t err;
lorex_regex_t *re = lorex_compile("((a)(b))", &err);
ASSERT(re != NULL);
loreg_match_t result;
ASSERT(loreg_search(re, "ab", &result));
lorex_match_t result;
ASSERT(lorex_search(re, "ab", &result));
ASSERT(result.group_count == 3);
loreg_free(re);
lorex_free(re);
}
TEST(empty_pattern) {
loreg_error_t err;
loreg_regex_t *re = loreg_compile("", &err);
lorex_error_t err;
lorex_regex_t *re = lorex_compile("", &err);
ASSERT(re != NULL);
loreg_match_t result;
ASSERT(loreg_match(re, "anything", &result));
lorex_match_t result;
ASSERT(lorex_match(re, "anything", &result));
loreg_free(re);
lorex_free(re);
}
TEST(match_position) {
loreg_error_t err;
loreg_regex_t *re = loreg_compile("test", &err);
lorex_error_t err;
lorex_regex_t *re = lorex_compile("test", &err);
ASSERT(re != NULL);
loreg_match_t result;
ASSERT(loreg_search(re, "xxxtestyyy", &result));
lorex_match_t result;
ASSERT(lorex_search(re, "xxxtestyyy", &result));
ASSERT(result.match_start == 3);
ASSERT(result.match_end == 7);
loreg_free(re);
lorex_free(re);
}
int main(void) {
+2 -2
View File
@@ -27,11 +27,11 @@ static nfa_t *compile_pattern(const char *pattern) {
parser_t parser;
parser_init(&parser, pattern);
ast_node_t *ast = parser_parse(&parser);
if (!ast || parser_get_error(&parser) != LOREG_OK) {
if (!ast || parser_get_error(&parser) != LOREX_OK) {
ast_free(ast);
return NULL;
}
loreg_error_t error;
lorex_error_t error;
nfa_t *nfa = nfa_from_ast(ast, &error);
ast_free(ast);
return nfa;
+2 -2
View File
@@ -245,7 +245,7 @@ TEST(complex_pattern) {
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(parser_get_error(&parser) == LOREG_OK);
ASSERT(parser_get_error(&parser) == LOREX_OK);
ast_free(ast);
}
@@ -255,7 +255,7 @@ TEST(unbalanced_paren) {
parser_init(&parser, "(abc");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast == NULL || parser_get_error(&parser) == LOREG_ERR_UNBALANCED_PAREN);
ASSERT(ast == NULL || parser_get_error(&parser) == LOREX_ERR_UNBALANCED_PAREN);
ast_free(ast);
}