diff --git a/Makefile b/Makefile index 683e1d906..210e6fb5e 100644 --- a/Makefile +++ b/Makefile @@ -147,7 +147,7 @@ SUBDIR += tests/sql SUBDIR += tests/queue SUBDIR += tests/aho_corasick SUBDIR += tests/retest -SUBDIR += tests/re_interpolate_groups +SUBDIR += tests/re_interpolate SUBDIR += tests .if make(theft) || make(${BUILD}/theft/theft) SUBDIR += theft diff --git a/include/re/groups.h b/include/re/groups.h index cddccf6fc..7f8d16b1b 100644 --- a/include/re/groups.h +++ b/include/re/groups.h @@ -9,6 +9,22 @@ struct re_pos; +enum re_interpolate_flags { + /* + * group numbers are single-digit only. e.g. "\0123" + * means group \0 followed by literal "123". + */ + RE_INTERPOLATE_SINGLE_DIGIT = 1 << 0, + + /* + * Enable \{...} syntax for groups. + * Multiple digits are permitted within the braces, regardless + * of whether RE_INTERPOLATE_SINGLE_DIGIT is set or not. + * At least one digit must be present. + */ + RE_INTERPOLATE_BRACES = 1 << 1 +}; + /* * esc is the character for escaping group references, * typically '\\' or '$'. @@ -31,7 +47,7 @@ struct re_pos; * You can distinguish compile-time errors (that is, * syntax errors in the format string) vs. runtime errors * (that is, nonexistent groups) by calling - * re_interpolate_groups() ahead of time with groupc = 0 + * re_interpolate() ahead of time with groupc = 0 * and passing a non-NULL nonexistent value. * * The output string will always be less than or equal in @@ -49,7 +65,7 @@ struct re_pos; * buffer is indeterminate. */ bool -re_interpolate_groups(const char *fmt, char esc, +re_interpolate(const char *fmt, char esc, enum re_interpolate_flags flags, const char *group0, unsigned groupc, const char *groupv[], const char *nonexistent, char *outs, size_t outn, struct re_pos *start, struct re_pos *end); diff --git a/src/libre/Makefile b/src/libre/Makefile index 36601004a..2d2866805 100644 --- a/src/libre/Makefile +++ b/src/libre/Makefile @@ -10,7 +10,7 @@ SRC += src/libre/ast_new_from_fsm.c SRC += src/libre/ast_rewrite.c SRC += src/libre/ac.c SRC += src/libre/print.c -SRC += src/libre/re_interpolate_groups.c +SRC += src/libre/re_interpolate.c SRC += src/libre/re_strings.c # generated diff --git a/src/libre/libre.syms b/src/libre/libre.syms index b04833777..393e58216 100644 --- a/src/libre/libre.syms +++ b/src/libre/libre.syms @@ -4,7 +4,7 @@ re_flags re_strerror re_perror re_is_anchored -re_interpolate_groups +re_interpolate ast_print ast_print_dot diff --git a/src/libre/re_interpolate_groups.c b/src/libre/re_interpolate.c similarity index 72% rename from src/libre/re_interpolate_groups.c rename to src/libre/re_interpolate.c index 52b61f116..b7525feb3 100644 --- a/src/libre/re_interpolate_groups.c +++ b/src/libre/re_interpolate.c @@ -31,7 +31,7 @@ } while (0) bool -re_interpolate_groups(const char *fmt, char esc, +re_interpolate(const char *fmt, char esc, enum re_interpolate_flags flags, const char *group0, unsigned groupc, const char *groupv[], const char *nonexistent, char *outs, size_t outn, struct re_pos *start, struct re_pos *end) @@ -43,7 +43,9 @@ re_interpolate_groups(const char *fmt, char esc, enum { STATE_LIT, STATE_ESC, - STATE_DIGIT + STATE_DIGIT, + STATE_OPEN_BRACE, + STATE_INSIDE_BRACES, } state; assert(esc != '\0'); @@ -92,6 +94,15 @@ re_interpolate_groups(const char *fmt, char esc, continue; } + if ((flags & RE_INTERPOLATE_BRACES) && *p == '{') { + if (start != NULL) { + start->byte = p - fmt; + } + + state = STATE_OPEN_BRACE; + continue; + } + if (isdigit((unsigned char) *p)) { group = *p - '0'; state = STATE_DIGIT; @@ -101,7 +112,7 @@ re_interpolate_groups(const char *fmt, char esc, goto error; case STATE_DIGIT: - if (isdigit((unsigned char) *p)) { + if (!(flags & RE_INTERPOLATE_SINGLE_DIGIT) && isdigit((unsigned char) *p)) { group *= 10; group += *p - '0'; @@ -162,6 +173,60 @@ re_interpolate_groups(const char *fmt, char esc, OUT_CHAR(*p); continue; + case STATE_OPEN_BRACE: + assert((flags & RE_INTERPOLATE_BRACES)); + + /* RE_INTERPOLATE_SINGLE_DIGIT does not apply inside braces */ + if (isdigit((unsigned char) *p)) { + group = *p - '0'; + + /* see STATE_DIGIT */ + if (group > groupc) { + group = groupc + 1; + } + + state = STATE_INSIDE_BRACES; + continue; + } + + /* at least one digit is required */ + goto error; + + case STATE_INSIDE_BRACES: + assert((flags & RE_INTERPOLATE_BRACES)); + + /* RE_INTERPOLATE_SINGLE_DIGIT does not apply inside braces */ + if (isdigit((unsigned char) *p)) { + group *= 10; + group += *p - '0'; + + /* see STATE_DIGIT */ + if (group > groupc) { + group = groupc + 1; + } + continue; + } + + if (group == 0) { + OUT_GROUP(group0); + } else if (group <= groupc) { + assert(groupv[group - 1] != NULL); + OUT_GROUP(groupv[group - 1]); + } else if (nonexistent == NULL) { + /* see STATE_DIGIT */ + goto error; + } else { + OUT_GROUP(nonexistent); + } + + if (*p == '}') { + group = 0; + state = STATE_LIT; + continue; + } + + goto error; + default: assert(!"unreached"); goto error; diff --git a/tests/re_interpolate/Makefile b/tests/re_interpolate/Makefile new file mode 100644 index 000000000..4320b7697 --- /dev/null +++ b/tests/re_interpolate/Makefile @@ -0,0 +1,23 @@ +.include "../../share/mk/top.mk" + +TEST.tests/re_interpolate != ls -1 tests/re_interpolate/re_interpolate*.c +TEST_SRCDIR.tests/re_interpolate = tests/re_interpolate +TEST_OUTDIR.tests/re_interpolate = ${BUILD}/tests/re_interpolate + +.for n in ${TEST.tests/re_interpolate:T:R:C/^re_interpolate//} +test:: ${TEST_OUTDIR.tests/re_interpolate}/res${n} +SRC += ${TEST_SRCDIR.tests/re_interpolate}/re_interpolate${n}.c +#CFLAGS.${TEST_SRCDIR.tests/re_interpolate}/re_interpolate${n}.c = -UNDEBUG +CFLAGS.${TEST_SRCDIR.tests/re_interpolate}/re_interpolate${n}.c = -std=c99 + +${TEST_OUTDIR.tests/re_interpolate}/run${n}: ${TEST_OUTDIR.tests/re_interpolate}/re_interpolate${n}.o + ${CC} ${CFLAGS} -o ${TEST_OUTDIR.tests/re_interpolate}/run${n} ${TEST_OUTDIR.tests/re_interpolate}/re_interpolate${n}.o ${BUILD}/src/libre/re_interpolate.o + +${TEST_OUTDIR.tests/re_interpolate}/res${n}: ${TEST_OUTDIR.tests/re_interpolate}/run${n} + ( ${TEST_OUTDIR.tests/re_interpolate}/run${n} 1>&2 && echo PASS || echo FAIL ) > ${TEST_OUTDIR.tests/re_interpolate}/res${n} + +#.for lib in ${LIB:Mlibfsm} ${LIB:Mlibre} +#${TEST_OUTDIR.tests/re_interpolate}/run${n}: ${BUILD}/lib/${lib:R}.a +#.endfor +.endfor + diff --git a/tests/re_interpolate_groups/re_interpolate_groups0.c b/tests/re_interpolate/re_interpolate0.c similarity index 90% rename from tests/re_interpolate_groups/re_interpolate_groups0.c rename to tests/re_interpolate/re_interpolate0.c index 4b4e0f709..1bbc75964 100644 --- a/tests/re_interpolate_groups/re_interpolate_groups0.c +++ b/tests/re_interpolate/re_interpolate0.c @@ -23,7 +23,7 @@ test(const char *fmt, size_t groupc, const char *groupv[], const char *expected) assert(fmt != NULL); assert(expected != NULL); - if (!re_interpolate_groups(fmt, '$', "", groupc, groupv, "", outs, sizeof outs, NULL, NULL)) { + if (!re_interpolate(fmt, '$', 0, "", groupc, groupv, "", outs, sizeof outs, NULL, NULL)) { printf("%s/%zu XXX\n", fmt, groupc); failed++; return; @@ -47,6 +47,10 @@ int main(void) { test("x", 0, g0, "x"); test("x", 4, gn, "x"); + test("{", 0, g0, "{"); + test("{", 4, gn, "{"); + test("}", 0, g0, "}"); + test("}", 4, gn, "}"); test("\001", 0, g0, "\001"); test("\001", 4, gn, "\001"); diff --git a/tests/re_interpolate/re_interpolate1.c b/tests/re_interpolate/re_interpolate1.c new file mode 100644 index 000000000..4f8cac5c0 --- /dev/null +++ b/tests/re_interpolate/re_interpolate1.c @@ -0,0 +1,89 @@ +/* + * Copyright 2026 Katherine Flavel + * + * See LICENCE for the full copyright terms. + */ + +#include +#include +#include +#include + +#include +#include + +static unsigned failed; + +static void +test_err(const char *fmt, enum re_interpolate_flags flags, + size_t groupc, const char *groupv[], const char *ne, + unsigned expected_start, unsigned expected_end) +{ + struct re_pos start, end; + char outs[10]; + bool rs, re; + + assert(fmt != NULL); + + outs[0] = 'x'; + + /* for these tests we're expecting to error */ + if (re_interpolate(fmt, '$', flags, "", groupc, groupv, ne, outs, sizeof outs, &start, &end)) { + printf("%s/%zu XXX\n", fmt, groupc); + failed++; + return; + } + + if (outs[0] != '\0') { + failed++; + } + + failed += rs = expected_start != start.byte; + failed += re = expected_end != end.byte; + + printf("%s/%zu => :%u-%u :%u-%u '%.*s'%s\n", fmt, groupc, + start.byte, end.byte, + expected_start, expected_end, + (int) (end.byte - start.byte), fmt + start.byte, + (rs || re) ? " XXX" : ""); +} + +int main(void) { + const char *ne = ""; + + const char *gn[] = { "one", "two", "three", "four" }; + const char **g0 = NULL; + + test_err("$", 0, 0, g0, ne, 0, 1); + test_err("$x", 0, 0, g0, ne, 0, 1); + test_err("$ ", 0, 4, gn, ne, 0, 1); + test_err("$\\01", 0, 0, g0, ne, 0, 1); + + test_err("$0$", 0, 0, g0, ne, 2, 3); + test_err("$$$x", 0, 4, gn, ne, 2, 3); + + test_err("xyz$1", 0, 0, gn, NULL, 3, 5); + test_err("xyz$2", 0, 1, gn, NULL, 3, 5); + + test_err("01234567890", 0, 1, gn, ne, 0, 10); + test_err("$$$$$$$$$$$$$$$$$$$$", 0, 1, gn, ne, 0, 20); + test_err("$1$1$1$$", 0, 1, gn, ne, 0, 8); + test_err("$1$1$1x", 0, 1, gn, ne, 0, 7); + test_err("xxxyyyzzz$$", 0, 1, gn, ne, 0, 11); + + test_err("${", 0, 4, gn, ne, 0, 1); + test_err("${}", 0, 4, gn, ne, 0, 1); + test_err("${}x", 0, 4, gn, ne, 0, 1); + test_err("_${0})", 0, 4, gn, ne, 1, 2); + test_err("${1}x", 0, 4, gn, ne, 0, 1); + test_err("${1}", 0, 4, gn, ne, 0, 1); + test_err("${10}", 0, 4, gn, ne, 0, 1); + test_err("${1}1", 0, 4, gn, ne, 0, 1); + + test_err("${", RE_INTERPOLATE_BRACES, 4, gn, ne, 1, 2); + test_err("${}", RE_INTERPOLATE_BRACES, 4, gn, ne, 1, 2); + test_err("${}x", RE_INTERPOLATE_BRACES, 4, gn, ne, 1, 2); + + return failed; +} + diff --git a/tests/re_interpolate/re_interpolate2.c b/tests/re_interpolate/re_interpolate2.c new file mode 100644 index 000000000..c5ccaa48a --- /dev/null +++ b/tests/re_interpolate/re_interpolate2.c @@ -0,0 +1,69 @@ +/* + * Copyright 2026 Katherine Flavel + * + * See LICENCE for the full copyright terms. + */ + +#include +#include +#include +#include + +#include +#include + +static unsigned failed; + +static void +test(const char *fmt, enum re_interpolate_flags flags, bool expected) +{ + bool r; + + assert(fmt != NULL); + + r = re_interpolate(fmt, '$', flags, "", 0, NULL, "", NULL, 0, NULL, NULL); + + failed += r != expected; + + printf("%s/%d => %d%s\n", fmt, 0, r, + r != expected ? " XXX" : ""); +} + +int main(void) { + test("", 0, true); + test("abc", 0, true); + test("$$", 0, true); + test("{", 0, true); + test("}", 0, true); + + test("$x", 0, false); + test("${}", 0, false); + test("${1}", 0, false); + test("_${0}_", 0, false); + test("_${1}_", 0, false); + test("${1", 0, false); + test("${", 0, false); + test("${$", 0, false); + test("${0$", 0, false); + + test("{", RE_INTERPOLATE_BRACES, true); + test("}", RE_INTERPOLATE_BRACES, true); + test("$x", RE_INTERPOLATE_BRACES, false); + test("${}", RE_INTERPOLATE_BRACES, false); + test("${1}", RE_INTERPOLATE_BRACES, true); + test("${1}", RE_INTERPOLATE_BRACES, true); + test("_${0}_", RE_INTERPOLATE_BRACES, true); + test("_${1}_", RE_INTERPOLATE_BRACES, true); + test("${1", RE_INTERPOLATE_BRACES, false); + test("${", RE_INTERPOLATE_BRACES, false); + test("${$", RE_INTERPOLATE_BRACES, false); + test("${0$", RE_INTERPOLATE_BRACES, false); + + test("${", RE_INTERPOLATE_BRACES, false); + test("${}", RE_INTERPOLATE_BRACES, false); + test("$}{", RE_INTERPOLATE_BRACES, false); + test("{$}", RE_INTERPOLATE_BRACES, false); + + return failed; +} + diff --git a/tests/re_interpolate/re_interpolate3.c b/tests/re_interpolate/re_interpolate3.c new file mode 100644 index 000000000..e5c4bc917 --- /dev/null +++ b/tests/re_interpolate/re_interpolate3.c @@ -0,0 +1,86 @@ +/* + * Copyright 2026 Katherine Flavel + * + * See LICENCE for the full copyright terms. + */ + +#include +#include +#include +#include + +#include +#include + +static unsigned failed; + +static void +test(enum re_interpolate_flags flags, const char *fmt, size_t groupc, const char *groupv[], const char *expected) +{ + char outs[50]; + bool r; + + assert(fmt != NULL); + assert(expected != NULL); + + if (!re_interpolate(fmt, '$', flags, "", groupc, groupv, "", outs, sizeof outs, NULL, NULL)) { + printf("%s/%zu XXX\n", fmt, groupc); + failed++; + return; + } + + failed += r = 0 != strcmp(outs, expected); + + printf("%s/%zu => %s%s\n", fmt, groupc, outs, + r ? " XXX" : ""); +} + +int main(void) { + const char *gn[] = { "one", "two", "three", "four" }; + const char **g0 = NULL; + const char *ga[] = { "1" }; + const char *gb[] = { "" }; + + test(RE_INTERPOLATE_SINGLE_DIGIT, "", 0, g0, ""); + test(RE_INTERPOLATE_SINGLE_DIGIT, "", 4, gn, ""); + + test(RE_INTERPOLATE_SINGLE_DIGIT, "x", 0, g0, "x"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "x", 4, gn, "x"); + + test(RE_INTERPOLATE_SINGLE_DIGIT, "\001", 0, g0, "\001"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "\001", 4, gn, "\001"); + + test(RE_INTERPOLATE_SINGLE_DIGIT, "$01", 0, g0, "1"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "$01", 4, gn, "1"); + + test(RE_INTERPOLATE_SINGLE_DIGIT, "$001", 0, g0, "01"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "$001", 4, gn, "01"); + + test(RE_INTERPOLATE_SINGLE_DIGIT, "$0", 0, gn, ""); + test(RE_INTERPOLATE_SINGLE_DIGIT, "x$000000000000000000000x", 0, gn, "x00000000000000000000x"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "x$000000000000000000001x", 1, gn, "x00000000000000000001x"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "x$900000000000000000000x", 1, gn, "x00000000000000000000x"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "x$100000000000000000000x", 1, gn, "xone00000000000000000000x"); + + test(RE_INTERPOLATE_SINGLE_DIGIT, "$$$11$11$22$11$33$44$33$22$11$$$$", 4, gn, "$one1one1two2one1three3four4three3two2one1$$"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "$$$$$$$$$$$$$$$$$$$$", 4, gn, "$$$$$$$$$$"); + + test(RE_INTERPOLATE_SINGLE_DIGIT, "xyz_$1..$0003;$3,$$.$1-$4=$123", 4, gn, "xyz_one..003;three,$.one-four=one23"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "xyz_$1..$0003;$3,$$.$1-$4=$123", 3, gn, "xyz_one..003;three,$.one-=one23"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "xyz_$1..$0003;$3,$$.$1-$4=$123", 2, gn, "xyz_one..003;,$.one-=one23"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "xyz_$1..$0003;$3,$$.$1-$4=$123", 1, gn, "xyz_one..003;,$.one-=one23"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "xyz_$1..$0003;$3,$$.$1-$4=$123", 0, g0, "xyz_..003;,$.-=23"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "xyz_$1..$0003;$3,$$.$1-$4=$123", 1, ga, "xyz_1..003;,$.1-=123"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "xyz_$1..$0003;$3,$$.$1-$4=$123", 1, gb, "xyz_..003;,$.-=23"); + + test(RE_INTERPOLATE_SINGLE_DIGIT, "xyz_$1..$2003;$3,$$.$1-$4=$123", 4, gn, "xyz_one..two003;three,$.one-four=one23"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "xyz_$1..$2003;$3,$$.$1-$4=$123", 3, gn, "xyz_one..two003;three,$.one-=one23"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "xyz_$1..$2003;$3,$$.$1-$4=$123", 2, gn, "xyz_one..two003;,$.one-=one23"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "xyz_$1..$2003;$3,$$.$1-$4=$123", 1, gn, "xyz_one..003;,$.one-=one23"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "xyz_$1..$2003;$3,$$.$1-$4=$123", 0, g0, "xyz_..003;,$.-=23"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "xyz_$1..$2003;$3,$$.$1-$4=$123", 1, ga, "xyz_1..003;,$.1-=123"); + test(RE_INTERPOLATE_SINGLE_DIGIT, "xyz_$1..$2003;$3,$$.$1-$4=$123", 1, gb, "xyz_..003;,$.-=23"); + + return failed; +} + diff --git a/tests/re_interpolate/re_interpolate4.c b/tests/re_interpolate/re_interpolate4.c new file mode 100644 index 000000000..c5e3beb0d --- /dev/null +++ b/tests/re_interpolate/re_interpolate4.c @@ -0,0 +1,101 @@ +/* + * Copyright 2026 Katherine Flavel + * + * See LICENCE for the full copyright terms. + */ + +#include +#include +#include +#include + +#include +#include + +static unsigned failed; + +static void +test(enum re_interpolate_flags flags, const char *fmt, size_t groupc, const char *groupv[], const char *expected) +{ + char outs[50]; + bool r; + + assert(fmt != NULL); + assert(expected != NULL); + + if (!re_interpolate(fmt, '$', flags, "", groupc, groupv, "", outs, sizeof outs, NULL, NULL)) { + printf("%s/%zu XXX\n", fmt, groupc); + failed++; + return; + } + + failed += r = 0 != strcmp(outs, expected); + + printf("%s/%zu => %s%s\n", fmt, groupc, outs, + r ? " XXX" : ""); +} + +int main(void) { + const char *gn[] = { "one", "two", "three", "four" }; + + test(RE_INTERPOLATE_BRACES, "${5}", 4, gn, ""); + + test(RE_INTERPOLATE_BRACES, "${0}", 4, gn, ""); + test(RE_INTERPOLATE_BRACES, "_${0}", 4, gn, "_"); + test(RE_INTERPOLATE_BRACES, "_${0}_", 4, gn, "__"); + test(RE_INTERPOLATE_BRACES, "${1}", 4, gn, "one"); + test(RE_INTERPOLATE_BRACES, "${10}", 4, gn, ""); + test(RE_INTERPOLATE_BRACES, "${10}0", 4, gn, "0"); + test(RE_INTERPOLATE_BRACES, "${10}0x", 4, gn, "0x"); + test(RE_INTERPOLATE_BRACES, "${10}1", 4, gn, "1"); + test(RE_INTERPOLATE_BRACES, "${10}1x", 4, gn, "1x"); + test(RE_INTERPOLATE_BRACES, "${1}0", 4, gn, "one0"); + test(RE_INTERPOLATE_BRACES, "0${1}0", 4, gn, "0one0"); + test(RE_INTERPOLATE_BRACES, "0${0}0", 4, gn, "00"); + test(RE_INTERPOLATE_BRACES, "${1}1", 4, gn, "one1"); + test(RE_INTERPOLATE_BRACES, "1${1}", 4, gn, "1one"); + test(RE_INTERPOLATE_BRACES, "1${1}1", 4, gn, "1one1"); + test(RE_INTERPOLATE_BRACES, "_${1}", 4, gn, "_one"); + test(RE_INTERPOLATE_BRACES, "_${1}_", 4, gn, "_one_"); + test(RE_INTERPOLATE_BRACES, "x${1}", 4, gn, "xone"); + test(RE_INTERPOLATE_BRACES, "${1}x", 4, gn, "onex"); + test(RE_INTERPOLATE_BRACES, "${01}", 4, gn, "one"); + test(RE_INTERPOLATE_BRACES, "${002}", 4, gn, "two"); + test(RE_INTERPOLATE_BRACES, "${0003}", 4, gn, "three"); + test(RE_INTERPOLATE_BRACES, "${4000}", 4, gn, ""); + + test(RE_INTERPOLATE_BRACES, "${00000000000000001}", 4, gn, "one"); + test(RE_INTERPOLATE_BRACES, "${00000000000000000000000000000000000000000000000001}", 4, gn, "one"); + + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${5}", 4, gn, ""); + + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${0}", 4, gn, ""); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "_${0}", 4, gn, "_"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "_${0}_", 4, gn, "__"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${1}", 4, gn, "one"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${10}", 4, gn, ""); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${10}0", 4, gn, "0"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${10}0x", 4, gn, "0x"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${10}1", 4, gn, "1"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${10}1x", 4, gn, "1x"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${1}0", 4, gn, "one0"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "0${1}0", 4, gn, "0one0"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "0${0}0", 4, gn, "00"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${1}1", 4, gn, "one1"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "1${1}", 4, gn, "1one"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "1${1}1", 4, gn, "1one1"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "_${1}", 4, gn, "_one"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "_${1}_", 4, gn, "_one_"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "x${1}", 4, gn, "xone"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${1}x", 4, gn, "onex"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${01}", 4, gn, "one"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${002}", 4, gn, "two"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${0003}", 4, gn, "three"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${4000}", 4, gn, ""); + + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${00000000000000001}", 4, gn, "one"); + test(RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "${00000000000000000000000000000000000000000000000001}", 4, gn, "one"); + + return failed; +} + diff --git a/tests/re_interpolate/re_interpolate5.c b/tests/re_interpolate/re_interpolate5.c new file mode 100644 index 000000000..3b3a76421 --- /dev/null +++ b/tests/re_interpolate/re_interpolate5.c @@ -0,0 +1,93 @@ +/* + * Copyright 2026 Katherine Flavel + * + * See LICENCE for the full copyright terms. + */ + +#include +#include +#include +#include + +#include +#include + +static unsigned failed; + +static void +test(const char *fmt, size_t groupc, const char *groupv[], const char *expected) +{ + char outs[40]; + bool r; + + assert(fmt != NULL); + assert(expected != NULL); + + if (!re_interpolate(fmt, '\\', RE_INTERPOLATE_BRACES | RE_INTERPOLATE_SINGLE_DIGIT, "", groupc, groupv, "", outs, sizeof outs, NULL, NULL)) { + printf("%s/%zu XXX\n", fmt, groupc); + failed++; + return; + } + + failed += r = 0 != strcmp(outs, expected); + + printf("%s/%zu => %s%s\n", fmt, groupc, outs, + r ? " XXX" : ""); +} + +int main(void) { + const char *gn[] = { "one", "two", "three", "four" }; + const char **g0 = NULL; + + test("", 0, g0, ""); + test("", 4, gn, ""); + + test("x", 0, g0, "x"); + test("x", 4, gn, "x"); + test("{", 0, g0, "{"); + test("{", 4, gn, "{"); + test("}", 0, g0, "}"); + test("}", 4, gn, "}"); + + test("\\\\", 4, gn, "\\"); + test("\\{1}", 4, gn, "one"); + test("_\\{0}", 4, gn, "_"); + test("_\\{1}", 4, gn, "_one"); + test("\\{0}_", 4, gn, "_"); + test("\\{1}_", 4, gn, "one_"); + test("_\\{0}_", 4, gn, "__"); + test("_\\{1}_", 4, gn, "_one_"); + test("0\\{0}", 4, gn, "0"); + test("0\\{1}", 4, gn, "0one"); + test("\\{0}0", 4, gn, "0"); + test("\\{1}0", 4, gn, "one0"); + test("0\\{0}0", 4, gn, "00"); + test("0\\{1}0", 4, gn, "0one0"); + test("1\\{0}", 4, gn, "1"); + test("1\\{1}", 4, gn, "1one"); + test("\\{0}1", 4, gn, "1"); + test("\\{1}1", 4, gn, "one1"); + test("1\\{0}1", 4, gn, "11"); + test("1\\{11}1", 4, gn, "11"); + test("1\\{0}", 4, gn, "1"); + test("1\\{11}", 4, gn, "1"); + test("\\{0}1", 4, gn, "1"); + test("\\{11}1", 4, gn, "1"); + test("1\\{0}1", 4, gn, "11"); + test("1\\{11}1", 4, gn, "11"); + + test("\001", 0, g0, "\001"); + test("\001", 4, gn, "\001"); + + test("\\0", 0, gn, ""); + test("_\\0_", 0, gn, "__"); + test("x\\000000000000000000000x", 0, gn, "x00000000000000000000x"); + test("x\\000000000000000000001x", 1, gn, "x00000000000000000001x"); + test("x\\100000000000000000000x", 1, gn, "xone00000000000000000000x"); + + test("\\\\\\1\\1\\2\\1\\3\\4\\3\\2\\1\\\\\\\\", 4, gn, "\\oneonetwoonethreefourthreetwoone\\\\"); + test("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\", 4, gn, "\\\\\\\\\\\\\\\\\\\\"); + + return failed; +} + diff --git a/tests/re_interpolate_groups/Makefile b/tests/re_interpolate_groups/Makefile deleted file mode 100644 index 41f9e2599..000000000 --- a/tests/re_interpolate_groups/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -.include "../../share/mk/top.mk" - -TEST.tests/re_interpolate_groups != ls -1 tests/re_interpolate_groups/re_interpolate_groups*.c -TEST_SRCDIR.tests/re_interpolate_groups = tests/re_interpolate_groups -TEST_OUTDIR.tests/re_interpolate_groups = ${BUILD}/tests/re_interpolate_groups - -.for n in ${TEST.tests/re_interpolate_groups:T:R:C/^re_interpolate_groups//} -test:: ${TEST_OUTDIR.tests/re_interpolate_groups}/res${n} -SRC += ${TEST_SRCDIR.tests/re_interpolate_groups}/re_interpolate_groups${n}.c -#CFLAGS.${TEST_SRCDIR.tests/re_interpolate_groups}/re_interpolate_groups${n}.c = -UNDEBUG -CFLAGS.${TEST_SRCDIR.tests/re_interpolate_groups}/re_interpolate_groups${n}.c = -std=c99 - -${TEST_OUTDIR.tests/re_interpolate_groups}/run${n}: ${TEST_OUTDIR.tests/re_interpolate_groups}/re_interpolate_groups${n}.o - ${CC} ${CFLAGS} -o ${TEST_OUTDIR.tests/re_interpolate_groups}/run${n} ${TEST_OUTDIR.tests/re_interpolate_groups}/re_interpolate_groups${n}.o ${BUILD}/src/libre/re_interpolate_groups.o - -${TEST_OUTDIR.tests/re_interpolate_groups}/res${n}: ${TEST_OUTDIR.tests/re_interpolate_groups}/run${n} - ( ${TEST_OUTDIR.tests/re_interpolate_groups}/run${n} 1>&2 && echo PASS || echo FAIL ) > ${TEST_OUTDIR.tests/re_interpolate_groups}/res${n} - -#.for lib in ${LIB:Mlibfsm} ${LIB:Mlibre} -#${TEST_OUTDIR.tests/re_interpolate_groups}/run${n}: ${BUILD}/lib/${lib:R}.a -#.endfor -.endfor - diff --git a/tests/re_interpolate_groups/re_interpolate_groups1.c b/tests/re_interpolate_groups/re_interpolate_groups1.c deleted file mode 100644 index 01dc3b344..000000000 --- a/tests/re_interpolate_groups/re_interpolate_groups1.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2026 Katherine Flavel - * - * See LICENCE for the full copyright terms. - */ - -#include -#include -#include -#include - -#include -#include - -static unsigned failed; - -static void -test_err(const char *fmt, size_t groupc, const char *groupv[], const char *ne, - unsigned expected_start, unsigned expected_end) -{ - struct re_pos start, end; - char outs[10]; - bool rs, re; - - assert(fmt != NULL); - - outs[0] = 'x'; - - /* for these tests we're expecting to error */ - if (re_interpolate_groups(fmt, '$', "", groupc, groupv, ne, outs, sizeof outs, &start, &end)) { - printf("%s/%zu XXX\n", fmt, groupc); - failed++; - return; - } - - if (outs[0] != '\0') { - failed++; - } - - failed += rs = expected_start != start.byte; - failed += re = expected_end != end.byte; - - printf("%s/%zu => :%u-%u :%u-%u '%.*s'%s\n", fmt, groupc, - start.byte, end.byte, - expected_start, expected_end, - (int) (end.byte - start.byte), fmt + start.byte, - (rs || re) ? " XXX" : ""); -} - -int main(void) { - const char *ne = ""; - - const char *gn[] = { "one", "two", "three", "four" }; - const char **g0 = NULL; - - test_err("$", 0, g0, ne, 0, 1); - test_err("$x", 0, g0, ne, 0, 1); - test_err("$ ", 4, gn, ne, 0, 1); - test_err("$\\01", 0, g0, ne, 0, 1); - - test_err("$0$", 0, g0, ne, 2, 3); - test_err("$$$x", 4, gn, ne, 2, 3); - - test_err("xyz$1", 0, gn, NULL, 3, 5); - test_err("xyz$2", 1, gn, NULL, 3, 5); - - test_err("01234567890", 1, gn, ne, 0, 10); - test_err("$$$$$$$$$$$$$$$$$$$$", 1, gn, ne, 0, 20); - test_err("$1$1$1$$", 1, gn, ne, 0, 8); - test_err("$1$1$1x", 1, gn, ne, 0, 7); - test_err("xxxyyyzzz$$", 1, gn, ne, 0, 11); - - return failed; -} - diff --git a/tests/re_interpolate_groups/re_interpolate_groups2.c b/tests/re_interpolate_groups/re_interpolate_groups2.c deleted file mode 100644 index d33fe656a..000000000 --- a/tests/re_interpolate_groups/re_interpolate_groups2.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2026 Katherine Flavel - * - * See LICENCE for the full copyright terms. - */ - -#include -#include -#include -#include - -#include -#include - -static unsigned failed; - -static void -test(const char *fmt, bool expected) -{ - bool r; - - assert(fmt != NULL); - - r = re_interpolate_groups(fmt, '$', "", 0, NULL, "", NULL, 0, NULL, NULL); - - failed += r != expected; - - printf("%s/%d => %d%s\n", fmt, 0, r, - r != expected ? " XXX" : ""); -} - -int main(void) { - test("", true); - test("abc", true); - test("$$", true); - test("$x", false); - - return failed; -} -