Files
regexx/tests/harness.c
T
retoorandClaude Sonnet 5 8f6afd6cd4 Add regexx: a single-file C regex interpreter with Python re semantics
concept.md is the full design document: the objective (Python re parity
plus binary/ASCII/UTF-8 modes, gigabyte-scale input, single C file), the
automata-theory argument for why unrestricted backreferences/lookaround
are incompatible with strict single-pass constant memory, the resulting
two-engine architecture, the exact Python-mirroring naming convention,
and a full comparison against POSIX regex.h for C-background readers.

regexx.c/regexx.h are the v1 implementation: parser, compiler to a
Pike/backtracking-style bytecode, and a single recursive backtracking
engine covering the pattern syntax and operations listed in README.md,
validated against CPython's own re module output (tests/), clean under
AddressSanitizer/UBSan, and stress-tested (50MB simple-quantifier match,
graceful failure rather than a crash on complex repeats over large
input, clean rejection of every intentionally unsupported construct).

Also included: examples/rxgrep.c (a small grep-like program exercising
all three data modes and the substitution API), the Makefile, the MIT
LICENSE, and docs/API.md, an exhaustive reference for every type, flag,
and function's exact return-value and memory-ownership convention,
checked against the current source and against a real CPython
interpreter rather than against memory.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EjuMk8kY9SDus1wWe2K9xY
2026-09-14 05:56:16 +00:00

157 lines
6.6 KiB
C

#include "harness.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int tc_pass = 0, tc_fail = 0;
void tc_search_family(const char *desc, const char *pat, int flags,
const char *subj, size_t subjlen, int kind,
int exp_matched, int n_spans, const long long *exp_spans) {
PatternError err; memset(&err, 0, sizeof err);
Pattern *p = re_compile(pat, strlen(pat), flags, &err);
if (!p) {
printf("FAIL [%s]: compile error: %s\n", desc, err.msg ? err.msg : "?");
tc_fail++;
return;
}
Input *in = Input_from_buffer((const uint8_t *)subj, subjlen);
Match m; memset(&m, 0, sizeof m);
int r;
if (kind == 0) r = Pattern_search(p, in, 0, -1, &m);
else if (kind == 1) r = Pattern_match(p, in, 0, -1, &m);
else r = Pattern_fullmatch(p, in, 0, -1, &m);
if (r < 0) {
printf("FAIL [%s]: internal error (depth exceeded or bad UTF-8)\n", desc);
tc_fail++;
} else if ((r == 1) != (exp_matched != 0)) {
printf("FAIL [%s]: matched=%d expected=%d\n", desc, r, exp_matched);
tc_fail++;
} else if (r == 1) {
int ok = 1;
for (int g = 0; g < n_spans; g++) {
long long es = exp_spans[2 * g], ee = exp_spans[2 * g + 1];
long long as = Match_start(&m, g), ae = Match_end(&m, g);
if (as != es || ae != ee) {
printf("FAIL [%s]: group %d span (%lld,%lld) expected (%lld,%lld)\n", desc, g, as, ae, es, ee);
ok = 0;
}
}
if (ok) tc_pass++; else tc_fail++;
Match_free(&m);
} else {
tc_pass++;
}
Input_free(in);
Pattern_free(p);
}
typedef struct { long long *spans; int n, cap; } SpanList;
static void span_cb(void *ctx, const Match *m_const) {
SpanList *sl = ctx;
Match *m = (Match *)m_const;
if (sl->n == sl->cap) { sl->cap = sl->cap ? sl->cap * 2 : 8; sl->spans = realloc(sl->spans, (size_t)sl->cap * 2 * sizeof(long long)); }
sl->spans[2 * sl->n] = Match_start(m, 0);
sl->spans[2 * sl->n + 1] = Match_end(m, 0);
sl->n++;
}
void tc_finditer(const char *desc, const char *pat, int flags,
const char *subj, size_t subjlen,
int exp_n_matches, const long long *exp_spans) {
PatternError err; memset(&err, 0, sizeof err);
Pattern *p = re_compile(pat, strlen(pat), flags, &err);
if (!p) { printf("FAIL [%s]: compile error: %s\n", desc, err.msg ? err.msg : "?"); tc_fail++; return; }
Input *in = Input_from_buffer((const uint8_t *)subj, subjlen);
SpanList sl; memset(&sl, 0, sizeof sl);
int total = Pattern_finditer(p, in, 0, -1, span_cb, &sl);
if (total < 0) { printf("FAIL [%s]: internal error\n", desc); tc_fail++; }
else if (sl.n != exp_n_matches) { printf("FAIL [%s]: %d matches, expected %d\n", desc, sl.n, exp_n_matches); tc_fail++; }
else {
int ok = 1;
for (int i = 0; i < sl.n; i++)
if (sl.spans[2 * i] != exp_spans[2 * i] || sl.spans[2 * i + 1] != exp_spans[2 * i + 1]) {
printf("FAIL [%s]: match %d span (%lld,%lld) expected (%lld,%lld)\n", desc, i,
sl.spans[2 * i], sl.spans[2 * i + 1], exp_spans[2 * i], exp_spans[2 * i + 1]);
ok = 0;
}
if (ok) tc_pass++; else tc_fail++;
}
free(sl.spans);
Input_free(in);
Pattern_free(p);
}
void tc_sub(const char *desc, const char *pat, int flags,
const char *subj, size_t subjlen, const char *repl, int count,
const char *exp_result, int exp_n) {
PatternError err; memset(&err, 0, sizeof err);
Pattern *p = re_compile(pat, strlen(pat), flags, &err);
if (!p) { printf("FAIL [%s]: compile error: %s\n", desc, err.msg ? err.msg : "?"); tc_fail++; return; }
Input *in = Input_from_buffer((const uint8_t *)subj, subjlen);
char *out = NULL; size_t outlen = 0; int n = 0;
int r = Pattern_subn(p, in, repl, NULL, NULL, count, &out, &outlen, &n);
if (r < 0) { printf("FAIL [%s]: internal error\n", desc); tc_fail++; }
else {
size_t explen = strlen(exp_result);
if (outlen != explen || memcmp(out, exp_result, explen) != 0 || n != exp_n) {
printf("FAIL [%s]: sub result '%.*s' (n=%d) expected '%s' (n=%d)\n", desc, (int)outlen, out, n, exp_result, exp_n);
tc_fail++;
} else tc_pass++;
}
free(out);
Input_free(in);
Pattern_free(p);
}
typedef struct { int present; const char *ptr; size_t len; } SplitItem;
typedef struct { SplitItem *items; int n, cap; } SplitList;
static void split_cb(void *ctx, const Match *m_const) {
SplitList *sl = ctx;
Match *m = (Match *)m_const;
if (sl->n == sl->cap) { sl->cap = sl->cap ? sl->cap * 2 : 8; sl->items = realloc(sl->items, (size_t)sl->cap * sizeof(SplitItem)); }
const char *out; size_t outlen;
int r = Match_group(m, NULL, 0, &out, &outlen);
sl->items[sl->n].present = (r == 1);
sl->items[sl->n].ptr = out; sl->items[sl->n].len = outlen;
sl->n++;
}
void tc_split(const char *desc, const char *pat, int flags,
const char *subj, size_t subjlen, int maxsplit,
int n_items, const char *const *texts) {
PatternError err; memset(&err, 0, sizeof err);
Pattern *p = re_compile(pat, strlen(pat), flags, &err);
if (!p) { printf("FAIL [%s]: compile error: %s\n", desc, err.msg ? err.msg : "?"); tc_fail++; return; }
Input *in = Input_from_buffer((const uint8_t *)subj, subjlen);
SplitList sl; memset(&sl, 0, sizeof sl);
int r = Pattern_split(p, in, maxsplit, split_cb, &sl);
if (r < 0) { printf("FAIL [%s]: internal error\n", desc); tc_fail++; }
else if (sl.n != n_items) { printf("FAIL [%s]: %d items, expected %d\n", desc, sl.n, n_items); tc_fail++; }
else {
int ok = 1;
for (int i = 0; i < sl.n; i++) {
int exp_none = (texts[i] == NULL);
if (exp_none && sl.items[i].present) { printf("FAIL [%s]: item %d expected None\n", desc, i); ok = 0; }
else if (!exp_none) {
size_t explen = strlen(texts[i]);
if (!sl.items[i].present || sl.items[i].len != explen || memcmp(sl.items[i].ptr, texts[i], explen) != 0) {
printf("FAIL [%s]: item %d = '%.*s' expected '%s'\n", desc, i,
sl.items[i].present ? (int)sl.items[i].len : 4,
sl.items[i].present ? sl.items[i].ptr : "NONE", texts[i]);
ok = 0;
}
}
}
if (ok) tc_pass++; else tc_fail++;
}
free(sl.items);
Input_free(in);
Pattern_free(p);
}
void harness_report(void) {
printf("\n%d passed, %d failed\n", tc_pass, tc_fail);
}