chore: update c, h, md files
CI / build (push) Waiting to run
CI / test (push) Blocked by required conditions
CI / valgrind (push) Blocked by required conditions
CI / coverage (push) Blocked by required conditions

This commit is contained in:
2026-01-04 00:04:48 +01:00
commit 3d9c4aa00b
26 changed files with 4459 additions and 0 deletions
+252
View File
@@ -0,0 +1,252 @@
/* retoor <retoor@molodetz.nl> */
#include "../include/loreg.h"
#include <stdio.h>
#include <string.h>
#include <time.h>
static int total_passed = 0;
static int total_failed = 0;
#define ASSERT(cond, msg) do { \
if (!(cond)) { \
printf(" FAIL: %s\n", msg); \
total_failed++; \
return; \
} \
} while(0)
#define TEST(name) static void test_##name(void)
#define RUN(name) do { \
test_##name(); \
total_passed++; \
} while(0)
TEST(basic_literals) {
loreg_error_t err;
loreg_regex_t *re = loreg_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");
loreg_free(re);
}
TEST(metacharacters) {
loreg_error_t err;
loreg_match_t m;
loreg_regex_t *re = loreg_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);
re = loreg_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);
re = loreg_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);
}
TEST(quantifiers) {
loreg_error_t err;
loreg_match_t m;
loreg_regex_t *re = loreg_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);
re = loreg_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);
re = loreg_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);
re = loreg_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);
re = loreg_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);
}
TEST(character_classes) {
loreg_error_t err;
loreg_match_t m;
loreg_regex_t *re = loreg_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);
re = loreg_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);
re = loreg_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);
re = loreg_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);
re = loreg_compile("\\w+", &err);
ASSERT(re != NULL, "compile \\w+");
ASSERT(loreg_search(re, "hello_123", &m), "match hello_123");
loreg_free(re);
re = loreg_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);
}
TEST(groups) {
loreg_error_t err;
loreg_match_t m;
loreg_regex_t *re = loreg_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);
re = loreg_compile("(\\d+)-(\\d+)", &err);
ASSERT(re != NULL, "compile groups");
ASSERT(loreg_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);
}
TEST(alternation) {
loreg_error_t err;
loreg_match_t m;
loreg_regex_t *re = loreg_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);
re = loreg_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);
}
TEST(escapes) {
loreg_error_t err;
loreg_match_t m;
loreg_regex_t *re = loreg_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);
re = loreg_compile("\\(test\\)", &err);
ASSERT(re != NULL, "compile \\(test\\)");
ASSERT(loreg_search(re, "(test)", &m), "match (test)");
loreg_free(re);
}
TEST(real_patterns) {
loreg_error_t err;
loreg_match_t m;
loreg_regex_t *re = loreg_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);
re = loreg_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);
re = loreg_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);
}
TEST(error_handling) {
loreg_error_t err;
loreg_regex_t *re = loreg_compile("(abc", &err);
ASSERT(re == NULL, "unbalanced paren");
ASSERT(err == LOREG_ERR_UNBALANCED_PAREN, "correct error");
}
int main(void) {
printf("loreg comprehensive tests\n");
printf("========================\n\n");
clock_t start = clock();
RUN(basic_literals);
RUN(metacharacters);
RUN(quantifiers);
RUN(character_classes);
RUN(groups);
RUN(alternation);
RUN(escapes);
RUN(real_patterns);
RUN(error_handling);
clock_t end = clock();
double elapsed = (double)(end - start) / CLOCKS_PER_SEC;
printf("\n========================\n");
printf("passed: %d, failed: %d\n", total_passed, total_failed);
printf("time: %.3f seconds\n", elapsed);
return total_failed > 0 ? 1 : 0;
}
+650
View File
@@ -0,0 +1,650 @@
/* retoor <retoor@molodetz.nl> */
#include "../include/loreg.h"
#include <stdio.h>
#include <string.h>
static int passed = 0;
static int failed = 0;
#define MATCH(pat, txt) test_match(pat, txt, 1, __LINE__)
#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);
if (!re) {
printf("FAIL line %d: compile error for '%s': %s\n", line, pattern, loreg_error_string(err));
failed++;
return;
}
loreg_match_t m;
int result = loreg_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);
}
static void test_literals(void) {
printf(" literals...\n");
MATCH("a", "a");
MATCH("a", "ba");
MATCH("a", "ab");
MATCH("abc", "abc");
MATCH("abc", "xabcy");
MATCH("hello", "hello world");
MATCH("world", "hello world");
MATCH("lo wo", "hello world");
NO_MATCH("abc", "ab");
NO_MATCH("abc", "abd");
NO_MATCH("xyz", "abc");
NO_MATCH("hello", "helo");
MATCH("", "anything");
MATCH("", "");
MATCH("a", "aaa");
MATCH("aa", "aaa");
MATCH("aaa", "aaa");
NO_MATCH("aaaa", "aaa");
}
static void test_dot(void) {
printf(" dot metacharacter...\n");
MATCH(".", "a");
MATCH(".", "x");
MATCH(".", "5");
MATCH(".", " ");
MATCH("..", "ab");
MATCH("...", "abc");
MATCH("a.c", "abc");
MATCH("a.c", "aXc");
MATCH("a.c", "a9c");
MATCH("a.c", "a c");
NO_MATCH("a.c", "ac");
NO_MATCH("a.c", "abbc");
MATCH("....", "test");
MATCH(".", "!");
MATCH(".", "@");
MATCH("a..b", "aXYb");
MATCH("a...b", "a123b");
NO_MATCH("a..b", "aXb");
}
static void test_anchors(void) {
printf(" anchors...\n");
MATCH("^a", "a");
MATCH("^a", "abc");
NO_MATCH("^a", "ba");
NO_MATCH("^a", " a");
MATCH("a$", "a");
MATCH("a$", "ba");
NO_MATCH("a$", "ab");
NO_MATCH("a$", "a ");
MATCH("^abc$", "abc");
NO_MATCH("^abc$", "xabc");
NO_MATCH("^abc$", "abcx");
NO_MATCH("^abc$", " abc");
NO_MATCH("^abc$", "abc ");
MATCH("^$", "");
NO_MATCH("^$", "a");
MATCH("^hello$", "hello");
MATCH("^hello world$", "hello world");
NO_MATCH("^hello world$", "hello world!");
MATCH("^a.*z$", "abcdefghijklmnopqrstuvwxyz");
MATCH("^.", "x");
MATCH(".$", "x");
}
static void test_star(void) {
printf(" star quantifier...\n");
MATCH("a*", "");
MATCH("a*", "a");
MATCH("a*", "aa");
MATCH("a*", "aaa");
MATCH("a*", "aaaaaaaaaa");
MATCH("a*", "b");
MATCH("a*b", "b");
MATCH("a*b", "ab");
MATCH("a*b", "aab");
MATCH("a*b", "aaaaaab");
NO_MATCH("a*b", "a");
MATCH("ba*", "b");
MATCH("ba*", "ba");
MATCH("ba*", "baaa");
MATCH(".*", "");
MATCH(".*", "anything at all");
MATCH("a.*b", "ab");
MATCH("a.*b", "aXb");
MATCH("a.*b", "aXXXXXb");
MATCH("a.*b", "a b");
MATCH("x*y*z*", "");
MATCH("x*y*z*", "xyz");
MATCH("x*y*z*", "xxxyyyzzz");
MATCH("ab*c", "ac");
MATCH("ab*c", "abc");
MATCH("ab*c", "abbbbc");
}
static void test_plus(void) {
printf(" plus quantifier...\n");
NO_MATCH("a+", "");
MATCH("a+", "a");
MATCH("a+", "aa");
MATCH("a+", "aaa");
MATCH("a+", "aaaaaaaaaa");
MATCH("a+", "ba");
MATCH("a+b", "ab");
MATCH("a+b", "aab");
MATCH("a+b", "aaaaaab");
NO_MATCH("a+b", "b");
NO_MATCH("a+b", "a");
MATCH("ba+", "ba");
MATCH("ba+", "baaa");
NO_MATCH("ba+", "b");
MATCH(".+", "a");
MATCH(".+", "anything");
NO_MATCH(".+", "");
MATCH("a.+b", "aXb");
MATCH("a.+b", "aXXXXXb");
NO_MATCH("a.+b", "ab");
MATCH("ab+c", "abc");
MATCH("ab+c", "abbbbc");
NO_MATCH("ab+c", "ac");
}
static void test_question(void) {
printf(" question quantifier...\n");
MATCH("a?", "");
MATCH("a?", "a");
MATCH("a?", "aa");
MATCH("a?b", "b");
MATCH("a?b", "ab");
MATCH("a?b", "aab");
MATCH("colou?r", "color");
MATCH("colou?r", "colour");
NO_MATCH("colou?r", "colouur");
MATCH("ab?c", "ac");
MATCH("ab?c", "abc");
NO_MATCH("ab?c", "abbc");
MATCH("https?://", "http://");
MATCH("https?://", "https://");
MATCH(".?", "");
MATCH(".?", "x");
}
static void test_alternation(void) {
printf(" alternation...\n");
MATCH("a|b", "a");
MATCH("a|b", "b");
NO_MATCH("a|b", "c");
MATCH("cat|dog", "cat");
MATCH("cat|dog", "dog");
NO_MATCH("cat|dog", "rat");
MATCH("cat|dog", "my cat");
MATCH("cat|dog", "my dog");
MATCH("a|b|c", "a");
MATCH("a|b|c", "b");
MATCH("a|b|c", "c");
NO_MATCH("a|b|c", "d");
MATCH("ab|cd", "ab");
MATCH("ab|cd", "cd");
NO_MATCH("ab|cd", "ac");
MATCH("abc|def|ghi", "abc");
MATCH("abc|def|ghi", "def");
MATCH("abc|def|ghi", "ghi");
MATCH("a|ab|abc", "abc");
MATCH("abc|ab|a", "abc");
MATCH("red|green|blue", "the red car");
MATCH("red|green|blue", "green light");
MATCH("red|green|blue", "blue sky");
}
static void test_groups(void) {
printf(" groups...\n");
MATCH("(a)", "a");
MATCH("(ab)", "ab");
MATCH("(abc)", "abc");
MATCH("(a)(b)", "ab");
MATCH("(a)(b)(c)", "abc");
MATCH("(ab)+", "ab");
MATCH("(ab)+", "abab");
MATCH("(ab)+", "ababab");
NO_MATCH("(ab)+", "a");
NO_MATCH("(ab)+", "ba");
MATCH("(ab)*", "");
MATCH("(ab)*", "ab");
MATCH("(ab)*", "abab");
MATCH("(ab)?", "");
MATCH("(ab)?", "ab");
MATCH("(a|b)+", "a");
MATCH("(a|b)+", "b");
MATCH("(a|b)+", "ab");
MATCH("(a|b)+", "ba");
MATCH("(a|b)+", "aabb");
MATCH("(a|b)+", "abba");
MATCH("((a))", "a");
MATCH("((ab))", "ab");
MATCH("(a(b)c)", "abc");
MATCH("(a(b(c)))", "abc");
MATCH("((a)(b))", "ab");
MATCH("(red|blue) car", "red car");
MATCH("(red|blue) car", "blue car");
NO_MATCH("(red|blue) car", "green car");
}
static void test_bracket_simple(void) {
printf(" bracket expressions (simple)...\n");
MATCH("[a]", "a");
NO_MATCH("[a]", "b");
MATCH("[ab]", "a");
MATCH("[ab]", "b");
NO_MATCH("[ab]", "c");
MATCH("[abc]", "a");
MATCH("[abc]", "b");
MATCH("[abc]", "c");
NO_MATCH("[abc]", "d");
MATCH("[aeiou]", "a");
MATCH("[aeiou]", "e");
MATCH("[aeiou]", "i");
MATCH("[aeiou]", "o");
MATCH("[aeiou]", "u");
NO_MATCH("[aeiou]", "b");
MATCH("[abc]+", "aaa");
MATCH("[abc]+", "abc");
MATCH("[abc]+", "cba");
MATCH("[abc]+", "abcabc");
MATCH("[xyz]*", "");
MATCH("[xyz]*", "xyz");
}
static void test_bracket_ranges(void) {
printf(" bracket expressions (ranges)...\n");
MATCH("[a-z]", "a");
MATCH("[a-z]", "m");
MATCH("[a-z]", "z");
NO_MATCH("[a-z]", "A");
NO_MATCH("[a-z]", "0");
MATCH("[A-Z]", "A");
MATCH("[A-Z]", "M");
MATCH("[A-Z]", "Z");
NO_MATCH("[A-Z]", "a");
MATCH("[0-9]", "0");
MATCH("[0-9]", "5");
MATCH("[0-9]", "9");
NO_MATCH("[0-9]", "a");
MATCH("[a-zA-Z]", "a");
MATCH("[a-zA-Z]", "Z");
NO_MATCH("[a-zA-Z]", "5");
MATCH("[a-zA-Z0-9]", "a");
MATCH("[a-zA-Z0-9]", "Z");
MATCH("[a-zA-Z0-9]", "5");
NO_MATCH("[a-zA-Z0-9]", "!");
MATCH("[a-z]+", "hello");
MATCH("[A-Z]+", "HELLO");
MATCH("[0-9]+", "12345");
MATCH("[a-z0-9]+", "abc123");
}
static void test_bracket_negated(void) {
printf(" bracket expressions (negated)...\n");
NO_MATCH("[^a]", "a");
MATCH("[^a]", "b");
MATCH("[^a]", "x");
NO_MATCH("[^abc]", "a");
NO_MATCH("[^abc]", "b");
NO_MATCH("[^abc]", "c");
MATCH("[^abc]", "d");
MATCH("[^abc]", "x");
NO_MATCH("[^a-z]", "a");
NO_MATCH("[^a-z]", "m");
NO_MATCH("[^a-z]", "z");
MATCH("[^a-z]", "A");
MATCH("[^a-z]", "5");
MATCH("[^a-z]", "!");
NO_MATCH("[^0-9]", "5");
MATCH("[^0-9]", "a");
MATCH("[^0-9]+", "hello");
NO_MATCH("[^aeiou]+", "aaa");
MATCH("[^aeiou]+", "xyz");
}
static void test_character_classes(void) {
printf(" character classes...\n");
MATCH("\\d", "0");
MATCH("\\d", "5");
MATCH("\\d", "9");
NO_MATCH("\\d", "a");
NO_MATCH("\\d", " ");
MATCH("\\d+", "123");
MATCH("\\d+", "0");
MATCH("\\d+", "9876543210");
NO_MATCH("\\d+", "");
NO_MATCH("\\d+", "abc");
MATCH("\\D", "a");
MATCH("\\D", " ");
MATCH("\\D", "!");
NO_MATCH("\\D", "5");
MATCH("\\w", "a");
MATCH("\\w", "Z");
MATCH("\\w", "0");
MATCH("\\w", "_");
NO_MATCH("\\w", " ");
NO_MATCH("\\w", "!");
MATCH("\\w+", "hello");
MATCH("\\w+", "Hello123");
MATCH("\\w+", "var_name");
MATCH("\\W", " ");
MATCH("\\W", "!");
MATCH("\\W", "@");
NO_MATCH("\\W", "a");
NO_MATCH("\\W", "_");
MATCH("\\s", " ");
MATCH("\\s", "\t");
MATCH("\\s", "\n");
NO_MATCH("\\s", "a");
NO_MATCH("\\s", "5");
MATCH("\\s+", " ");
MATCH("\\s+", " \t\n");
MATCH("\\S", "a");
MATCH("\\S", "5");
MATCH("\\S", "!");
NO_MATCH("\\S", " ");
NO_MATCH("\\S", "\t");
}
static void test_quantifier_braces(void) {
printf(" brace quantifiers...\n");
MATCH("a{3}", "aaa");
MATCH("a{3}", "aaaa");
NO_MATCH("a{3}", "aa");
MATCH("a{1}", "a");
MATCH("a{1}", "aa");
NO_MATCH("a{1}", "");
MATCH("a{0}", "");
MATCH("a{0}", "b");
MATCH("a{2,4}", "aa");
MATCH("a{2,4}", "aaa");
MATCH("a{2,4}", "aaaa");
MATCH("a{2,4}", "aaaaa");
NO_MATCH("a{2,4}", "a");
MATCH("a{2,}", "aa");
MATCH("a{2,}", "aaa");
MATCH("a{2,}", "aaaaaaaaaa");
NO_MATCH("a{2,}", "a");
MATCH("a{0,2}", "");
MATCH("a{0,2}", "a");
MATCH("a{0,2}", "aa");
MATCH("a{0,2}", "aaa");
MATCH("[0-9]{3}", "123");
MATCH("[0-9]{3}", "000");
NO_MATCH("[0-9]{3}", "12");
MATCH("(ab){2}", "abab");
MATCH("(ab){2}", "ababab");
NO_MATCH("(ab){2}", "ab");
}
static void test_escape_sequences(void) {
printf(" escape sequences...\n");
MATCH("\\.", ".");
NO_MATCH("\\.", "a");
MATCH("\\*", "*");
NO_MATCH("\\*", "a");
MATCH("\\+", "+");
MATCH("\\?", "?");
MATCH("\\|", "|");
MATCH("\\(", "(");
MATCH("\\)", ")");
MATCH("\\[", "[");
MATCH("\\]", "]");
MATCH("\\{", "{");
MATCH("\\}", "}");
MATCH("\\^", "^");
MATCH("\\$", "$");
MATCH("\\\\", "\\");
MATCH("a\\.b", "a.b");
NO_MATCH("a\\.b", "aXb");
MATCH("\\d\\.\\d", "1.5");
MATCH("c\\+\\+", "c++");
MATCH("\\(test\\)", "(test)");
MATCH("\\[0\\]", "[0]");
}
static void test_complex_patterns(void) {
printf(" complex patterns...\n");
MATCH("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}", "user@example.com");
MATCH("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}", "test.user@mail.example.org");
NO_MATCH("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}", "invalid");
NO_MATCH("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}", "@example.com");
MATCH("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}", "192.168.1.1");
MATCH("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}", "10.0.0.1");
MATCH("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}", "255.255.255.255");
NO_MATCH("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}", "1.2.3");
MATCH("https?://[a-zA-Z0-9.-]+(/[a-zA-Z0-9./-]*)?", "http://example.com");
MATCH("https?://[a-zA-Z0-9.-]+(/[a-zA-Z0-9./-]*)?", "https://example.com");
MATCH("https?://[a-zA-Z0-9.-]+(/[a-zA-Z0-9./-]*)?", "http://example.com/path");
MATCH("https?://[a-zA-Z0-9.-]+(/[a-zA-Z0-9./-]*)?", "https://example.com/path/to/page");
MATCH("\\d{3}-\\d{3}-\\d{4}", "123-456-7890");
MATCH("\\d{3}-\\d{3}-\\d{4}", "555-123-4567");
NO_MATCH("\\d{3}-\\d{3}-\\d{4}", "12-345-6789");
NO_MATCH("\\d{3}-\\d{3}-\\d{4}", "1234567890");
MATCH("\\(\\d{3}\\) \\d{3}-\\d{4}", "(123) 456-7890");
MATCH("[A-Z]{2}\\d{6}", "AB123456");
NO_MATCH("[A-Z]{2}\\d{6}", "A1234567");
MATCH("\\d{4}-\\d{2}-\\d{2}", "2024-01-15");
MATCH("\\d{2}/\\d{2}/\\d{4}", "01/15/2024");
MATCH("\\d{1,2}:\\d{2}(:\\d{2})?", "12:30");
MATCH("\\d{1,2}:\\d{2}(:\\d{2})?", "12:30:45");
MATCH("\\d{1,2}:\\d{2}(:\\d{2})?", "9:05");
}
static void test_word_boundaries(void) {
printf(" word patterns...\n");
MATCH("\\w+", "hello");
MATCH("\\w+", "hello123");
MATCH("\\w+", "test_var");
MATCH("[a-zA-Z_][a-zA-Z0-9_]*", "variable");
MATCH("[a-zA-Z_][a-zA-Z0-9_]*", "_private");
MATCH("[a-zA-Z_][a-zA-Z0-9_]*", "var123");
NO_MATCH("^[a-zA-Z_][a-zA-Z0-9_]*$", "123var");
MATCH("\\w+\\s+\\w+", "hello world");
MATCH("\\w+\\s+\\w+", "foo bar");
NO_MATCH("\\w+\\s+\\w+", "hello");
}
static void test_greedy_vs_nongreedy(void) {
printf(" greedy vs non-greedy...\n");
MATCH("a+", "aaa");
MATCH("a+?", "aaa");
MATCH("a*", "aaa");
MATCH("a*?", "aaa");
MATCH("a?", "a");
MATCH("a??", "a");
MATCH("a{2,4}", "aaaa");
MATCH("a{2,4}?", "aaaa");
MATCH(".*x", "abcx");
MATCH(".*?x", "abcx");
}
static void test_empty_and_edge_cases(void) {
printf(" empty and edge cases...\n");
MATCH("", "");
MATCH("", "abc");
MATCH("a*", "");
MATCH("a?", "");
MATCH("(a*)*", "");
MATCH("(a*)+", "");
MATCH("(a+)*", "");
MATCH("(a|b)*", "");
MATCH("[a-z]*", "");
NO_MATCH("a+", "");
NO_MATCH(".+", "");
NO_MATCH("[a-z]+", "");
MATCH("^", "");
MATCH("$", "");
MATCH("^$", "");
NO_MATCH("^$", "a");
MATCH("a*b*c*", "");
MATCH("a*b*c*", "abc");
MATCH("a*b*c*", "aabbcc");
MATCH("a*b*c*", "c");
MATCH("a*b*c*", "b");
}
static void test_special_characters_in_text(void) {
printf(" special characters in text...\n");
MATCH("a", "a\nb");
MATCH("b", "a\nb");
MATCH("a.b", "a\tb");
NO_MATCH("a.b", "a\nb");
MATCH("\\.", "3.14");
MATCH("\\+", "1+2");
MATCH("\\*", "2*3");
MATCH("\\?", "why?");
MATCH("\\(\\)", "func()");
MATCH("\\[\\]", "array[]");
MATCH("\\{\\}", "object{}");
MATCH("\\^", "x^2");
MATCH("\\$", "$100");
MATCH("\\|", "a|b");
}
static void test_repetition_combinations(void) {
printf(" repetition combinations...\n");
MATCH("a+b+", "ab");
MATCH("a+b+", "aabb");
MATCH("a+b+", "aaabbb");
NO_MATCH("a+b+", "a");
NO_MATCH("a+b+", "b");
MATCH("a*b+", "b");
MATCH("a*b+", "ab");
MATCH("a*b+", "aab");
MATCH("a+b*", "a");
MATCH("a+b*", "ab");
MATCH("a+b*", "abb");
MATCH("a*b*", "");
MATCH("a*b*", "a");
MATCH("a*b*", "b");
MATCH("a*b*", "ab");
MATCH("(ab)+c+", "abc");
MATCH("(ab)+c+", "ababcc");
MATCH("(a+b)+", "ab");
MATCH("(a+b)+", "aabaaab");
MATCH("((a+)+)+", "a");
MATCH("((a+)+)+", "aaa");
}
static void test_alternation_combinations(void) {
printf(" alternation combinations...\n");
MATCH("a|b|c|d|e", "a");
MATCH("a|b|c|d|e", "e");
NO_MATCH("a|b|c|d|e", "f");
MATCH("(a|b)(c|d)", "ac");
MATCH("(a|b)(c|d)", "ad");
MATCH("(a|b)(c|d)", "bc");
MATCH("(a|b)(c|d)", "bd");
NO_MATCH("(a|b)(c|d)", "ab");
MATCH("(cat|dog)s?", "cat");
MATCH("(cat|dog)s?", "cats");
MATCH("(cat|dog)s?", "dog");
MATCH("(cat|dog)s?", "dogs");
MATCH("(red|green|blue)\\s+(car|truck)", "red car");
MATCH("(red|green|blue)\\s+(car|truck)", "green truck");
MATCH("(a|aa|aaa)", "aaa");
MATCH("(aaa|aa|a)", "aaa");
}
static void test_nested_groups(void) {
printf(" nested groups...\n");
MATCH("((a))", "a");
MATCH("(((a)))", "a");
MATCH("((a)(b))", "ab");
MATCH("((a(b))c)", "abc");
MATCH("(a(b(c)))", "abc");
MATCH("((a|b)(c|d))", "ac");
MATCH("(a(b|c)d)", "abd");
MATCH("(a(b|c)d)", "acd");
MATCH("((ab)+)", "abab");
MATCH("(a(bc)*d)", "ad");
MATCH("(a(bc)*d)", "abcd");
MATCH("(a(bc)*d)", "abcbcd");
MATCH("((a+)(b+))", "aabb");
MATCH("(((a|b)+)c)", "ababc");
}
static void test_real_world_patterns(void) {
printf(" real world patterns...\n");
MATCH("[a-zA-Z]+", "Hello");
MATCH("[a-zA-Z]+", "WORLD");
MATCH("[a-zA-Z]+", "test");
MATCH("-?\\d+", "123");
MATCH("-?\\d+", "-456");
MATCH("-?\\d+", "0");
MATCH("-?\\d+\\.?\\d*", "3.14");
MATCH("-?\\d+\\.?\\d*", "-2.5");
MATCH("-?\\d+\\.?\\d*", "42");
MATCH("[a-fA-F0-9]+", "deadbeef");
MATCH("[a-fA-F0-9]+", "CAFEBABE");
MATCH("[a-fA-F0-9]+", "123abc");
MATCH("[01]+", "101010");
MATCH("[01]+", "11110000");
MATCH("[A-Z][a-z]+", "Hello");
MATCH("[A-Z][a-z]+", "World");
NO_MATCH("[A-Z][a-z]+", "hello");
MATCH("\"[^\"]*\"", "\"hello\"");
MATCH("\"[^\"]*\"", "\"hello world\"");
MATCH("\"[^\"]*\"", "\"\"");
MATCH("'[^']*'", "'test'");
MATCH("#[a-fA-F0-9]{6}", "#ff0000");
MATCH("#[a-fA-F0-9]{6}", "#00FF00");
MATCH("#[a-fA-F0-9]{3}", "#f00");
}
static void test_pathological_patterns(void) {
printf(" stress test patterns...\n");
MATCH("a?a?a?aaa", "aaa");
MATCH("(a+)+", "aaaa");
MATCH("(a*)*", "aaaa");
MATCH("(a|a)+", "aaaa");
MATCH("((a*)*)*", "aaaa");
MATCH("a*a*a*a*a*b", "aaaaab");
MATCH(".*.*.*.*.*", "test");
MATCH("(a?){5}a{5}", "aaaaa");
}
int main(void) {
printf("loreg integration tests\n");
printf("=======================\n\n");
test_literals();
test_dot();
test_anchors();
test_star();
test_plus();
test_question();
test_alternation();
test_groups();
test_bracket_simple();
test_bracket_ranges();
test_bracket_negated();
test_character_classes();
test_quantifier_braces();
test_escape_sequences();
test_complex_patterns();
test_word_boundaries();
test_greedy_vs_nongreedy();
test_empty_and_edge_cases();
test_special_characters_in_text();
test_repetition_combinations();
test_alternation_combinations();
test_nested_groups();
test_real_world_patterns();
test_pathological_patterns();
printf("\n=======================\n");
printf("integration: %d passed, %d failed\n", passed, failed);
printf("total tests: %d\n", passed + failed);
return failed > 0 ? 1 : 0;
}
+195
View File
@@ -0,0 +1,195 @@
/* retoor <retoor@molodetz.nl> */
#include "../include/lexer.h"
#include <stdio.h>
#include <assert.h>
#include <string.h>
static int tests_passed = 0;
static int tests_failed = 0;
#define TEST(name) static void test_##name(void)
#define RUN_TEST(name) do { \
printf(" %s... ", #name); \
test_##name(); \
printf("ok\n"); \
tests_passed++; \
} while(0)
#define ASSERT(cond) do { \
if (!(cond)) { \
printf("FAILED at line %d: %s\n", __LINE__, #cond); \
tests_failed++; \
return; \
} \
} while(0)
TEST(simple_chars) {
lexer_t lexer;
lexer_init(&lexer, "abc");
token_t t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == 'a');
t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == 'b');
t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == 'c');
t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_EOF);
}
TEST(meta_chars) {
lexer_t lexer;
lexer_init(&lexer, ".*+?|()^$");
ASSERT(lexer_next(&lexer).type == TOKEN_DOT);
ASSERT(lexer_next(&lexer).type == TOKEN_STAR);
ASSERT(lexer_next(&lexer).type == TOKEN_PLUS);
ASSERT(lexer_next(&lexer).type == TOKEN_QUESTION);
ASSERT(lexer_next(&lexer).type == TOKEN_PIPE);
ASSERT(lexer_next(&lexer).type == TOKEN_LPAREN);
ASSERT(lexer_next(&lexer).type == TOKEN_RPAREN);
ASSERT(lexer_next(&lexer).type == TOKEN_CARET);
ASSERT(lexer_next(&lexer).type == TOKEN_DOLLAR);
ASSERT(lexer_next(&lexer).type == TOKEN_EOF);
}
TEST(escaped_chars) {
lexer_t lexer;
lexer_init(&lexer, "\\*\\+\\.");
token_t t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == '*');
t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == '+');
t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == '.');
}
TEST(character_classes) {
lexer_t lexer;
lexer_init(&lexer, "\\d\\w\\s\\D\\W\\S");
ASSERT(lexer_next(&lexer).type == TOKEN_CLASS_DIGIT);
ASSERT(lexer_next(&lexer).type == TOKEN_CLASS_WORD);
ASSERT(lexer_next(&lexer).type == TOKEN_CLASS_SPACE);
ASSERT(lexer_next(&lexer).type == TOKEN_CLASS_NDIGIT);
ASSERT(lexer_next(&lexer).type == TOKEN_CLASS_NWORD);
ASSERT(lexer_next(&lexer).type == TOKEN_CLASS_NSPACE);
}
TEST(bracket_expression) {
lexer_t lexer;
lexer_init(&lexer, "[abc]");
ASSERT(lexer_next(&lexer).type == TOKEN_LBRACKET);
token_t t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == 'a');
t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == 'b');
t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == 'c');
ASSERT(lexer_next(&lexer).type == TOKEN_RBRACKET);
}
TEST(bracket_range) {
lexer_t lexer;
lexer_init(&lexer, "[a-z]");
ASSERT(lexer_next(&lexer).type == TOKEN_LBRACKET);
token_t t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == 'a');
ASSERT(lexer_next(&lexer).type == TOKEN_DASH);
t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == 'z');
ASSERT(lexer_next(&lexer).type == TOKEN_RBRACKET);
}
TEST(negated_bracket) {
lexer_t lexer;
lexer_init(&lexer, "[^a]");
ASSERT(lexer_next(&lexer).type == TOKEN_LBRACKET);
ASSERT(lexer_next(&lexer).type == TOKEN_CARET);
token_t t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == 'a');
ASSERT(lexer_next(&lexer).type == TOKEN_RBRACKET);
}
TEST(quantifier_braces) {
lexer_t lexer;
lexer_init(&lexer, "a{3}");
token_t t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == 'a');
ASSERT(lexer_next(&lexer).type == TOKEN_LBRACE);
t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == '3');
ASSERT(lexer_next(&lexer).type == TOKEN_RBRACE);
}
TEST(peek) {
lexer_t lexer;
lexer_init(&lexer, "ab");
token_t t = lexer_peek(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == 'a');
t = lexer_peek(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == 'a');
t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == 'a');
t = lexer_peek(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == 'b');
}
TEST(escape_sequences) {
lexer_t lexer;
lexer_init(&lexer, "\\n\\t\\r");
token_t t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == '\n');
t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == '\t');
t = lexer_next(&lexer);
ASSERT(t.type == TOKEN_CHAR && t.value == '\r');
}
int main(void) {
printf("lexer tests:\n");
RUN_TEST(simple_chars);
RUN_TEST(meta_chars);
RUN_TEST(escaped_chars);
RUN_TEST(character_classes);
RUN_TEST(bracket_expression);
RUN_TEST(bracket_range);
RUN_TEST(negated_bracket);
RUN_TEST(quantifier_braces);
RUN_TEST(peek);
RUN_TEST(escape_sequences);
printf("\nlexer: %d passed, %d failed\n", tests_passed, tests_failed);
return tests_failed > 0 ? 1 : 0;
}
+294
View File
@@ -0,0 +1,294 @@
/* retoor <retoor@molodetz.nl> */
#include "../include/loreg.h"
#include <stdio.h>
#include <string.h>
static int tests_passed = 0;
static int tests_failed = 0;
#define TEST(name) static void test_##name(void)
#define RUN_TEST(name) do { \
printf(" %s... ", #name); \
test_##name(); \
printf("ok\n"); \
tests_passed++; \
} while(0)
#define ASSERT(cond) do { \
if (!(cond)) { \
printf("FAILED at line %d: %s\n", __LINE__, #cond); \
tests_failed++; \
return; \
} \
} while(0)
#define ASSERT_MATCH(pattern, text) do { \
loreg_error_t err; \
loreg_regex_t *re = loreg_compile(pattern, &err); \
ASSERT(re != NULL); \
loreg_match_t result; \
ASSERT(loreg_search(re, text, &result) == true); \
loreg_free(re); \
} while(0)
#define ASSERT_NO_MATCH(pattern, text) do { \
loreg_error_t err; \
loreg_regex_t *re = loreg_compile(pattern, &err); \
ASSERT(re != NULL); \
loreg_match_t result; \
ASSERT(loreg_search(re, text, &result) == false); \
loreg_free(re); \
} while(0)
TEST(simple_char) {
ASSERT_MATCH("a", "a");
ASSERT_MATCH("a", "bab");
ASSERT_NO_MATCH("a", "bcd");
}
TEST(concat) {
ASSERT_MATCH("ab", "ab");
ASSERT_MATCH("ab", "xaby");
ASSERT_NO_MATCH("ab", "ba");
}
TEST(alternation) {
ASSERT_MATCH("a|b", "a");
ASSERT_MATCH("a|b", "b");
ASSERT_MATCH("cat|dog", "cat");
ASSERT_MATCH("cat|dog", "dog");
ASSERT_NO_MATCH("cat|dog", "rat");
}
TEST(star) {
ASSERT_MATCH("a*", "");
ASSERT_MATCH("a*", "a");
ASSERT_MATCH("a*", "aaa");
ASSERT_MATCH("a*b", "b");
ASSERT_MATCH("a*b", "ab");
ASSERT_MATCH("a*b", "aaab");
}
TEST(plus) {
ASSERT_NO_MATCH("a+", "");
ASSERT_MATCH("a+", "a");
ASSERT_MATCH("a+", "aaa");
ASSERT_MATCH("a+b", "ab");
ASSERT_MATCH("a+b", "aaab");
}
TEST(question) {
ASSERT_MATCH("a?", "");
ASSERT_MATCH("a?", "a");
ASSERT_MATCH("a?b", "b");
ASSERT_MATCH("a?b", "ab");
}
TEST(dot) {
ASSERT_MATCH(".", "a");
ASSERT_MATCH(".", "x");
ASSERT_MATCH("a.b", "aab");
ASSERT_MATCH("a.b", "axb");
ASSERT_NO_MATCH("a.b", "ab");
}
TEST(bracket_simple) {
ASSERT_MATCH("[abc]", "a");
ASSERT_MATCH("[abc]", "b");
ASSERT_MATCH("[abc]", "c");
ASSERT_NO_MATCH("[abc]", "d");
}
TEST(bracket_range) {
ASSERT_MATCH("[a-z]", "a");
ASSERT_MATCH("[a-z]", "m");
ASSERT_MATCH("[a-z]", "z");
ASSERT_NO_MATCH("[a-z]", "A");
ASSERT_NO_MATCH("[a-z]", "0");
}
TEST(bracket_negated) {
ASSERT_NO_MATCH("[^abc]", "a");
ASSERT_NO_MATCH("[^abc]", "b");
ASSERT_MATCH("[^abc]", "d");
ASSERT_MATCH("[^abc]", "x");
}
TEST(group) {
ASSERT_MATCH("(ab)", "ab");
ASSERT_MATCH("(ab)+", "abab");
ASSERT_MATCH("(a|b)+", "abba");
}
TEST(anchors) {
ASSERT_MATCH("^a", "a");
ASSERT_MATCH("^a", "abc");
ASSERT_NO_MATCH("^a", "ba");
ASSERT_MATCH("a$", "a");
ASSERT_MATCH("a$", "ba");
ASSERT_NO_MATCH("a$", "ab");
ASSERT_MATCH("^abc$", "abc");
ASSERT_NO_MATCH("^abc$", "xabc");
ASSERT_NO_MATCH("^abc$", "abcx");
}
TEST(quantifier_exact) {
ASSERT_MATCH("a{3}", "aaa");
ASSERT_MATCH("a{3}", "aaaa");
ASSERT_NO_MATCH("a{3}", "aa");
}
TEST(quantifier_range) {
ASSERT_MATCH("a{2,4}", "aa");
ASSERT_MATCH("a{2,4}", "aaa");
ASSERT_MATCH("a{2,4}", "aaaa");
ASSERT_NO_MATCH("a{2,4}", "a");
}
TEST(quantifier_open) {
ASSERT_MATCH("a{2,}", "aa");
ASSERT_MATCH("a{2,}", "aaaaa");
ASSERT_NO_MATCH("a{2,}", "a");
}
TEST(class_digit) {
ASSERT_MATCH("\\d", "0");
ASSERT_MATCH("\\d", "9");
ASSERT_MATCH("\\d+", "123");
ASSERT_NO_MATCH("\\d", "a");
}
TEST(class_word) {
ASSERT_MATCH("\\w", "a");
ASSERT_MATCH("\\w", "Z");
ASSERT_MATCH("\\w", "0");
ASSERT_MATCH("\\w", "_");
ASSERT_NO_MATCH("\\w", " ");
ASSERT_NO_MATCH("\\w", "-");
}
TEST(class_space) {
ASSERT_MATCH("\\s", " ");
ASSERT_MATCH("\\s", "\t");
ASSERT_MATCH("\\s", "\n");
ASSERT_NO_MATCH("\\s", "a");
}
TEST(class_negated) {
ASSERT_NO_MATCH("\\D", "0");
ASSERT_MATCH("\\D", "a");
ASSERT_NO_MATCH("\\W", "a");
ASSERT_MATCH("\\W", " ");
ASSERT_NO_MATCH("\\S", " ");
ASSERT_MATCH("\\S", "a");
}
TEST(escape_sequences) {
ASSERT_MATCH("\\.", ".");
ASSERT_NO_MATCH("\\.", "a");
ASSERT_MATCH("\\*", "*");
ASSERT_MATCH("\\+", "+");
ASSERT_MATCH("\\?", "?");
}
TEST(complex_email) {
ASSERT_MATCH("[a-z]+@[a-z]+\\.[a-z]+", "test@example.com");
ASSERT_NO_MATCH("[a-z]+@[a-z]+\\.[a-z]+", "invalid");
}
TEST(complex_phone) {
ASSERT_MATCH("\\d{3}-\\d{3}-\\d{4}", "123-456-7890");
ASSERT_NO_MATCH("\\d{3}-\\d{3}-\\d{4}", "123-456-789");
}
TEST(complex_url) {
ASSERT_MATCH("https?://[a-z]+\\.[a-z]+", "http://example.com");
ASSERT_MATCH("https?://[a-z]+\\.[a-z]+", "https://example.com");
}
TEST(group_capture) {
loreg_error_t err;
loreg_regex_t *re = loreg_compile("(\\d+)-(\\d+)", &err);
ASSERT(re != NULL);
loreg_match_t result;
ASSERT(loreg_search(re, "123-456", &result));
ASSERT(result.group_count == 2);
ASSERT(result.groups[0].matched);
ASSERT(result.groups[1].matched);
loreg_free(re);
}
TEST(nested_groups) {
loreg_error_t err;
loreg_regex_t *re = loreg_compile("((a)(b))", &err);
ASSERT(re != NULL);
loreg_match_t result;
ASSERT(loreg_search(re, "ab", &result));
ASSERT(result.group_count == 3);
loreg_free(re);
}
TEST(empty_pattern) {
loreg_error_t err;
loreg_regex_t *re = loreg_compile("", &err);
ASSERT(re != NULL);
loreg_match_t result;
ASSERT(loreg_match(re, "anything", &result));
loreg_free(re);
}
TEST(match_position) {
loreg_error_t err;
loreg_regex_t *re = loreg_compile("test", &err);
ASSERT(re != NULL);
loreg_match_t result;
ASSERT(loreg_search(re, "xxxtestyyy", &result));
ASSERT(result.match_start == 3);
ASSERT(result.match_end == 7);
loreg_free(re);
}
int main(void) {
printf("matcher tests:\n");
RUN_TEST(simple_char);
RUN_TEST(concat);
RUN_TEST(alternation);
RUN_TEST(star);
RUN_TEST(plus);
RUN_TEST(question);
RUN_TEST(dot);
RUN_TEST(bracket_simple);
RUN_TEST(bracket_range);
RUN_TEST(bracket_negated);
RUN_TEST(group);
RUN_TEST(anchors);
RUN_TEST(quantifier_exact);
RUN_TEST(quantifier_range);
RUN_TEST(quantifier_open);
RUN_TEST(class_digit);
RUN_TEST(class_word);
RUN_TEST(class_space);
RUN_TEST(class_negated);
RUN_TEST(escape_sequences);
RUN_TEST(complex_email);
RUN_TEST(complex_phone);
RUN_TEST(complex_url);
RUN_TEST(group_capture);
RUN_TEST(nested_groups);
RUN_TEST(empty_pattern);
RUN_TEST(match_position);
printf("\nmatcher: %d passed, %d failed\n", tests_passed, tests_failed);
return tests_failed > 0 ? 1 : 0;
}
+159
View File
@@ -0,0 +1,159 @@
/* retoor <retoor@molodetz.nl> */
#include "../include/nfa.h"
#include "../include/parser.h"
#include <stdio.h>
#include <assert.h>
static int tests_passed = 0;
static int tests_failed = 0;
#define TEST(name) static void test_##name(void)
#define RUN_TEST(name) do { \
printf(" %s... ", #name); \
test_##name(); \
printf("ok\n"); \
tests_passed++; \
} while(0)
#define ASSERT(cond) do { \
if (!(cond)) { \
printf("FAILED at line %d: %s\n", __LINE__, #cond); \
tests_failed++; \
return; \
} \
} while(0)
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) {
ast_free(ast);
return NULL;
}
loreg_error_t error;
nfa_t *nfa = nfa_from_ast(ast, &error);
ast_free(ast);
return nfa;
}
TEST(single_char) {
nfa_t *nfa = compile_pattern("a");
ASSERT(nfa != NULL);
ASSERT(nfa->start != NULL);
ASSERT(nfa->state_count >= 2);
nfa_free(nfa);
}
TEST(concat) {
nfa_t *nfa = compile_pattern("ab");
ASSERT(nfa != NULL);
ASSERT(nfa->start != NULL);
nfa_free(nfa);
}
TEST(alternation) {
nfa_t *nfa = compile_pattern("a|b");
ASSERT(nfa != NULL);
ASSERT(nfa->start != NULL);
nfa_free(nfa);
}
TEST(star) {
nfa_t *nfa = compile_pattern("a*");
ASSERT(nfa != NULL);
ASSERT(nfa->start != NULL);
nfa_free(nfa);
}
TEST(plus) {
nfa_t *nfa = compile_pattern("a+");
ASSERT(nfa != NULL);
ASSERT(nfa->start != NULL);
nfa_free(nfa);
}
TEST(question) {
nfa_t *nfa = compile_pattern("a?");
ASSERT(nfa != NULL);
ASSERT(nfa->start != NULL);
nfa_free(nfa);
}
TEST(group) {
nfa_t *nfa = compile_pattern("(ab)");
ASSERT(nfa != NULL);
ASSERT(nfa->group_count == 1);
nfa_free(nfa);
}
TEST(nested_groups) {
nfa_t *nfa = compile_pattern("((a)(b))");
ASSERT(nfa != NULL);
ASSERT(nfa->group_count == 3);
nfa_free(nfa);
}
TEST(bracket) {
nfa_t *nfa = compile_pattern("[abc]");
ASSERT(nfa != NULL);
ASSERT(nfa->start != NULL);
nfa_free(nfa);
}
TEST(quantifier) {
nfa_t *nfa = compile_pattern("a{2,4}");
ASSERT(nfa != NULL);
ASSERT(nfa->start != NULL);
nfa_free(nfa);
}
TEST(complex_pattern) {
nfa_t *nfa = compile_pattern("^([a-z]+)@([a-z]+)\\.([a-z]{2,})$");
ASSERT(nfa != NULL);
ASSERT(nfa->group_count == 3);
nfa_free(nfa);
}
TEST(dot) {
nfa_t *nfa = compile_pattern("a.b");
ASSERT(nfa != NULL);
ASSERT(nfa->start != NULL);
nfa_free(nfa);
}
TEST(anchors) {
nfa_t *nfa = compile_pattern("^abc$");
ASSERT(nfa != NULL);
ASSERT(nfa->start != NULL);
nfa_free(nfa);
}
TEST(character_classes) {
nfa_t *nfa = compile_pattern("\\d\\w\\s");
ASSERT(nfa != NULL);
ASSERT(nfa->start != NULL);
nfa_free(nfa);
}
int main(void) {
printf("nfa tests:\n");
RUN_TEST(single_char);
RUN_TEST(concat);
RUN_TEST(alternation);
RUN_TEST(star);
RUN_TEST(plus);
RUN_TEST(question);
RUN_TEST(group);
RUN_TEST(nested_groups);
RUN_TEST(bracket);
RUN_TEST(quantifier);
RUN_TEST(complex_pattern);
RUN_TEST(dot);
RUN_TEST(anchors);
RUN_TEST(character_classes);
printf("\nnfa: %d passed, %d failed\n", tests_passed, tests_failed);
return tests_failed > 0 ? 1 : 0;
}
+301
View File
@@ -0,0 +1,301 @@
/* retoor <retoor@molodetz.nl> */
#include "../include/parser.h"
#include <stdio.h>
#include <assert.h>
static int tests_passed = 0;
static int tests_failed = 0;
#define TEST(name) static void test_##name(void)
#define RUN_TEST(name) do { \
printf(" %s... ", #name); \
test_##name(); \
printf("ok\n"); \
tests_passed++; \
} while(0)
#define ASSERT(cond) do { \
if (!(cond)) { \
printf("FAILED at line %d: %s\n", __LINE__, #cond); \
tests_failed++; \
return; \
} \
} while(0)
TEST(single_char) {
parser_t parser;
parser_init(&parser, "a");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_CHAR);
ASSERT(ast->value == 'a');
ast_free(ast);
}
TEST(concat) {
parser_t parser;
parser_init(&parser, "ab");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_CONCAT);
ASSERT(ast->left->type == AST_CHAR);
ASSERT(ast->left->value == 'a');
ASSERT(ast->right->type == AST_CHAR);
ASSERT(ast->right->value == 'b');
ast_free(ast);
}
TEST(alternation) {
parser_t parser;
parser_init(&parser, "a|b");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_ALTER);
ASSERT(ast->left->type == AST_CHAR);
ASSERT(ast->left->value == 'a');
ASSERT(ast->right->type == AST_CHAR);
ASSERT(ast->right->value == 'b');
ast_free(ast);
}
TEST(star) {
parser_t parser;
parser_init(&parser, "a*");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_STAR);
ASSERT(ast->left->type == AST_CHAR);
ASSERT(ast->left->value == 'a');
ast_free(ast);
}
TEST(plus) {
parser_t parser;
parser_init(&parser, "a+");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_PLUS);
ASSERT(ast->left->type == AST_CHAR);
ASSERT(ast->left->value == 'a');
ast_free(ast);
}
TEST(question) {
parser_t parser;
parser_init(&parser, "a?");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_QUESTION);
ASSERT(ast->left->type == AST_CHAR);
ASSERT(ast->left->value == 'a');
ast_free(ast);
}
TEST(group) {
parser_t parser;
parser_init(&parser, "(ab)");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_GROUP);
ASSERT(ast->group_id == 0);
ASSERT(ast->left->type == AST_CONCAT);
ast_free(ast);
}
TEST(dot) {
parser_t parser;
parser_init(&parser, ".");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_DOT);
ast_free(ast);
}
TEST(anchors) {
parser_t parser;
parser_init(&parser, "^a$");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_CONCAT);
ast_free(ast);
}
TEST(bracket_simple) {
parser_t parser;
parser_init(&parser, "[abc]");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_BRACKET);
ASSERT(ast->bracket != NULL);
ASSERT(ast->bracket->count == 3);
ast_free(ast);
}
TEST(bracket_range) {
parser_t parser;
parser_init(&parser, "[a-z]");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_BRACKET);
ASSERT(ast->bracket != NULL);
ASSERT(ast->bracket->count == 1);
ASSERT(ast->bracket->ranges[0].start == 'a');
ASSERT(ast->bracket->ranges[0].end == 'z');
ast_free(ast);
}
TEST(bracket_negated) {
parser_t parser;
parser_init(&parser, "[^a]");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_BRACKET);
ASSERT(ast->bracket->negated == true);
ast_free(ast);
}
TEST(quantifier_exact) {
parser_t parser;
parser_init(&parser, "a{3}");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_QUANTIFIER);
ASSERT(ast->quant.min == 3);
ASSERT(ast->quant.max == 3);
ast_free(ast);
}
TEST(quantifier_range) {
parser_t parser;
parser_init(&parser, "a{2,5}");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_QUANTIFIER);
ASSERT(ast->quant.min == 2);
ASSERT(ast->quant.max == 5);
ast_free(ast);
}
TEST(quantifier_open) {
parser_t parser;
parser_init(&parser, "a{2,}");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_QUANTIFIER);
ASSERT(ast->quant.min == 2);
ASSERT(ast->quant.max == -1);
ast_free(ast);
}
TEST(character_class_digit) {
parser_t parser;
parser_init(&parser, "\\d");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_CLASS_DIGIT);
ast_free(ast);
}
TEST(character_class_word) {
parser_t parser;
parser_init(&parser, "\\w");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_CLASS_WORD);
ast_free(ast);
}
TEST(complex_pattern) {
parser_t parser;
parser_init(&parser, "^([a-z]+)@([a-z]+)\\.([a-z]{2,})$");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(parser_get_error(&parser) == LOREG_OK);
ast_free(ast);
}
TEST(unbalanced_paren) {
parser_t parser;
parser_init(&parser, "(abc");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast == NULL || parser_get_error(&parser) == LOREG_ERR_UNBALANCED_PAREN);
ast_free(ast);
}
TEST(non_greedy) {
parser_t parser;
parser_init(&parser, "a*?");
ast_node_t *ast = parser_parse(&parser);
ASSERT(ast != NULL);
ASSERT(ast->type == AST_STAR);
ASSERT(ast->quant.greedy == false);
ast_free(ast);
}
int main(void) {
printf("parser tests:\n");
RUN_TEST(single_char);
RUN_TEST(concat);
RUN_TEST(alternation);
RUN_TEST(star);
RUN_TEST(plus);
RUN_TEST(question);
RUN_TEST(group);
RUN_TEST(dot);
RUN_TEST(anchors);
RUN_TEST(bracket_simple);
RUN_TEST(bracket_range);
RUN_TEST(bracket_negated);
RUN_TEST(quantifier_exact);
RUN_TEST(quantifier_range);
RUN_TEST(quantifier_open);
RUN_TEST(character_class_digit);
RUN_TEST(character_class_word);
RUN_TEST(complex_pattern);
RUN_TEST(unbalanced_paren);
RUN_TEST(non_greedy);
printf("\nparser: %d passed, %d failed\n", tests_passed, tests_failed);
return tests_failed > 0 ? 1 : 0;
}