chore: update c, h, md files
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user