From f1bc406edb6faef3420de7f77a6f1246065861d9 Mon Sep 17 00:00:00 2001 From: Tyge Løvset Date: Thu, 21 Jul 2022 16:33:39 +0200 Subject: cregex API change: Added cregex_match_ex() and cregex_match() with string pattern input instead of a cregex*, similar to cregex_replace*(). --- examples/regex1.c | 3 +- examples/regex2.c | 2 +- examples/regex_match.c | 6 +-- include/stc/cregex.h | 58 +++++++++++--------- src/cregex.c | 144 ++++++++++++++++++++++++++----------------------- 5 files changed, 116 insertions(+), 97 deletions(-) diff --git a/examples/regex1.c b/examples/regex1.c index 09e4299d..d5c14509 100644 --- a/examples/regex1.c +++ b/examples/regex1.c @@ -1,3 +1,4 @@ +#define i_implement #include #include @@ -21,7 +22,7 @@ int main(int argc, char* argv[]) if (cstr_equals(input, "q")) break; - if (cregex_match(&float_expr, cstr_str(&input), 0, NULL, 0) > 0) + if (cregex_match_re(cstr_str(&input), &float_expr, 0, NULL, 0) > 0) printf("Input is a float\n"); else printf("Invalid input : Not a float\n"); diff --git a/examples/regex2.c b/examples/regex2.c index 1f3163f7..0d10205a 100644 --- a/examples/regex2.c +++ b/examples/regex2.c @@ -20,7 +20,7 @@ int main() } cregmatch m[20]; printf("input: %s\n", inputs[i]); - if (cregex_match(&re, inputs[i], 20, m, 0) > 0) + if (cregex_match_re(inputs[i], &re, 20, m, 0) > 0) { c_forrange (j, cregex_captures(&re)) { diff --git a/examples/regex_match.c b/examples/regex_match.c index 5680b55e..5f1075ff 100644 --- a/examples/regex_match.c +++ b/examples/regex_match.c @@ -14,20 +14,20 @@ int main() int res = cregex_compile(&re, "[+-]?([0-9]*\\.)?\\d+([Ee][+-]?\\d+)?", 0); printf("%d\n", res); csview m[10]; - if (cregex_match(&re, s, 10, m, 0) > 0) { + if (cregex_match_re(s, &re, 10, m, 0) > 0) { printf("Found digits at position %" PRIuMAX "-%" PRIuMAX "\n", m[0].str - s, m[0].str - s + m[0].size); } else { printf("Could not find any digits\n"); } - while (cregex_match(&re, s, 10, m, cregex_NEXT) > 0) { + while (cregex_match_re(s, &re, 10, m, cre_NEXT) > 0) { printf("%" c_PRIsv " ; ", c_ARGsv(m[0])); } puts(""); res = cregex_compile(&re, "(.+)\\b(.+)", 0); printf("groups: %d\n", res); - if ((res = cregex_match(&re, "hello@wørld", 10, m, 0)) > 0) { + if ((res = cregex_match_re("hello@wørld", &re, 10, m, 0)) > 0) { c_forrange (i, res) printf("match: [%" c_PRIsv "]\n", c_ARGsv(m[i])); } else diff --git a/include/stc/cregex.h b/include/stc/cregex.h index 2b972fc8..4e82c60a 100644 --- a/include/stc/cregex.h +++ b/include/stc/cregex.h @@ -34,33 +34,33 @@ THE SOFTWARE. #include "forward.h" // csview typedef enum { - creg_success = 1, - creg_nomatch = 0, - creg_matcherror = -1, - creg_outofmemory = -2, - creg_unmatchedleftparenthesis = -3, - creg_unmatchedrightparenthesis = -4, - creg_toomanysubexpressions = -5, - creg_toomanycharacterclasses = -6, - creg_malformedcharacterclass = -7, - creg_missingoperand = -8, - creg_unknownoperator = -9, - creg_operandstackoverflow = -10, - creg_operatorstackoverflow = -11, - creg_operatorstackunderflow = -12, + cre_success = 1, + cre_nomatch = 0, + cre_matcherror = -1, + cre_outofmemory = -2, + cre_unmatchedleftparenthesis = -3, + cre_unmatchedrightparenthesis = -4, + cre_toomanysubexpressions = -5, + cre_toomanycharacterclasses = -6, + cre_malformedcharacterclass = -7, + cre_missingoperand = -8, + cre_unknownoperator = -9, + cre_operandstackoverflow = -10, + cre_operatorstackoverflow = -11, + cre_operatorstackunderflow = -12, } cregex_error_t; enum { - /* compile flags */ - cregex_DOTALL = 1<<0, - cregex_CASELESS = 1<<1, - /* execution flags */ - cregex_FULLMATCH = 1<<2, - cregex_NEXT = 1<<3, - cregex_STARTEND = 1<<4, + /* compile-flags */ + cre_DOTALL = 1<<0, + cre_CASELESS = 1<<1, + /* match-flags */ + cre_FULLMATCH = 1<<2, + cre_NEXT = 1<<3, + cre_STARTEND = 1<<4, /* limits */ - cregex_MAXCLASSES = 16, - cregex_MAXCAPTURES = 32, + cre_MAXCLASSES = 16, + cre_MAXCAPTURES = 32, }; typedef struct { @@ -80,11 +80,17 @@ int cregex_compile(cregex *self, const char* pattern, int cflags); int cregex_captures(const cregex* self); /* return 1 on match, 0 on nomatch, and -1 on failure. */ -int cregex_match(const cregex *self, const char* string, - unsigned nmatch, csview match[], int mflags); +int cregex_match_re(const char* input, const cregex* re, + unsigned nmatch, csview match[], int mflags); + +int cregex_match_ex(const char* input, const char* pattern, int cflags, + unsigned nmatch, csview match[], int mflags); +static inline +int cregex_match(const char* input, const char* pattern, unsigned nmatch, csview match[]) + { return cregex_match_ex(input, pattern, 0, nmatch, match, 0); } /* replace regular expression */ -cstr cregex_replace_re(const char* input, const cregex* re, const char* repl, +cstr cregex_replace_re(const char* input, const cregex* re, const char* replace, cstr (*mfun)(int i, csview match), int cflags, unsigned count); cstr cregex_replace_ex(const char* input, const char* pattern, const char* replace, diff --git a/src/cregex.c b/src/cregex.c index be5fb069..7ae2b9ce 100644 --- a/src/cregex.c +++ b/src/cregex.c @@ -40,9 +40,9 @@ THE SOFTWARE. typedef uint32_t Rune; /* Utf8 code point */ typedef int32_t Token; /* max character classes per program */ -#define NCLASS cregex_MAXCLASSES +#define NCLASS cre_MAXCLASSES /* max subexpressions */ -#define NSUBEXP cregex_MAXCAPTURES +#define NSUBEXP cre_MAXCAPTURES /* max rune ranges per character class */ #define NCCRUNE (NSUBEXP * 2) @@ -387,10 +387,10 @@ static void _operator(Parser *par, Token t) { if (t==RBRA && --par->nbra<0) - rcerror(par, creg_unmatchedrightparenthesis); + rcerror(par, cre_unmatchedrightparenthesis); if (t==LBRA) { if (++par->cursubid >= NSUBEXP) - rcerror(par, creg_toomanysubexpressions); + rcerror(par, cre_toomanysubexpressions); par->nbra++; if (par->lastwasand) _operator(par, CAT); @@ -407,7 +407,7 @@ static void pushand(Parser *par, Reinst *f, Reinst *l) { if (par->andp >= &par->andstack[NSTACK]) - rcerror(par, creg_operandstackoverflow); + rcerror(par, cre_operandstackoverflow); par->andp->first = f; par->andp->last = l; par->andp++; @@ -417,7 +417,7 @@ static void pushator(Parser *par, Token t) { if (par->atorp >= &par->atorstack[NSTACK]) - rcerror(par, creg_operatorstackoverflow); + rcerror(par, cre_operatorstackoverflow); *par->atorp++ = t; *par->subidp++ = par->cursubid; } @@ -428,7 +428,7 @@ popand(Parser *par, Token op) Reinst *inst; if (par->andp <= &par->andstack[0]) { - rcerror(par, creg_missingoperand); + rcerror(par, cre_missingoperand); inst = newinst(par, NOP); pushand(par, inst, inst); } @@ -439,7 +439,7 @@ static Token popator(Parser *par) { if (par->atorp <= &par->atorstack[0]) - rcerror(par, creg_operatorstackunderflow); + rcerror(par, cre_operatorstackunderflow); --par->subidp; return *--par->atorp; } @@ -453,7 +453,7 @@ evaluntil(Parser *par, Token pri) while (pri==RBRA || par->atorp[-1]>=pri) { switch (popator(par)) { default: - rcerror(par, creg_unknownoperator); + rcerror(par, cre_unknownoperator); break; case LBRA: /* must have been RBRA */ op1 = popand(par, '('); @@ -564,7 +564,7 @@ static Reclass* newclass(Parser *par) { if (par->nclass >= NCLASS) - rcerror(par, creg_toomanycharacterclasses); + rcerror(par, cre_toomanycharacterclasses); return &(par->classp[par->nclass++]); } @@ -597,7 +597,7 @@ nextc(Parser *par, Rune *rp) *rp = 0; sscanf(++par->exprp, "%x", rp); while (*par->exprp) if (*(par->exprp++) == '}') break; if (par->exprp[-1] != '}') - rcerror(par, creg_unmatchedrightparenthesis); + rcerror(par, cre_unmatchedrightparenthesis); return 2; case 'p': case 'P': { /* https://www.regular-expressions.info/unicode.html */ static struct { const char* c; int n, r; } cls[] = { @@ -624,7 +624,7 @@ nextc(Parser *par, Rune *rp) break; } if (*rp < OPERATOR) { - rcerror(par, creg_unknownoperator); + rcerror(par, cre_unknownoperator); *rp = 0; } break; @@ -679,7 +679,7 @@ lex(Parser *par) case '-': enable = 0; break; case 's': if (!par->flags.dotall) par->dot_type = ANY + enable; break; case 'i': if (!par->flags.caseless) par->rune_type = RUNE + enable; break; - default: rcerror(par, creg_unknownoperator); return 0; + default: rcerror(par, cre_unknownoperator); return 0; } } return LBRA; @@ -718,7 +718,7 @@ bldcclass(Parser *par) /* parse class into a set of spans */ for (; ep < &r[NCCRUNE]; quoted = nextc(par, &rune)) { if (rune == 0) { - rcerror(par, creg_malformedcharacterclass); + rcerror(par, cre_malformedcharacterclass); return 0; } if (!quoted) { @@ -728,7 +728,7 @@ bldcclass(Parser *par) if (ep != r && *par->exprp != ']') { quoted = nextc(par, &rune); if (rune == 0) { - rcerror(par, creg_malformedcharacterclass); + rcerror(par, cre_malformedcharacterclass); return 0; } ep[-1] = rune; @@ -804,12 +804,12 @@ regcomp1(Reprog *progp, Parser *par, const char *s, int cflags) const int instcap = 5 + 6*strlen(s); Reprog* pp = (Reprog *)realloc(progp, sizeof(Reprog) + instcap*sizeof(Reinst)); if (pp == NULL) { - par->errors = creg_outofmemory; + par->errors = cre_outofmemory; free(progp); return NULL; } - pp->flags.caseless = (cflags & cregex_CASELESS) != 0; - pp->flags.dotall = (cflags & cregex_DOTALL) != 0; + pp->flags.caseless = (cflags & cre_CASELESS) != 0; + pp->flags.dotall = (cflags & cre_DOTALL) != 0; par->freep = pp->firstinst; par->classp = pp->cclass; par->errors = 0; @@ -851,7 +851,7 @@ regcomp1(Reprog *progp, Parser *par, const char *s, int cflags) dumpstack(par); #endif if (par->nbra) - rcerror(par, creg_unmatchedleftparenthesis); + rcerror(par, cre_unmatchedleftparenthesis); --par->andp; /* points to first and only operand */ pp->startinst = par->andp->first; #ifdef DEBUG @@ -1059,7 +1059,7 @@ regexec1(const Reprog *progp, /* program to run */ /* efficiency: advance and re-evaluate */ continue; case END: /* Match! */ - match = !(mflags & cregex_FULLMATCH) || + match = !(mflags & cre_FULLMATCH) || ((s == j->eol || r == 0 || r == '\n') && (tlp->se.m[0].str == bol || tlp->se.m[0].str[-1] == '\n')); tlp->se.m[0].size = s - tlp->se.m[0].str; @@ -1126,9 +1126,9 @@ regexec(const Reprog *progp, /* program to run */ j.eol = NULL; if (ms && mp->size) { - if (mflags & cregex_STARTEND) + if (mflags & cre_STARTEND) j.starts = mp->str, j.eol = mp->str + mp->size; - else if (mflags & cregex_NEXT) + else if (mflags & cre_NEXT) j.starts = mp->str + mp->size; } @@ -1155,16 +1155,16 @@ regexec(const Reprog *progp, /* program to run */ return rv; } - -void cregex_build_replace(const char* repl, unsigned nmatch, const csview match[], - cstr (*mfun)(int i, csview match), cstr* sub) { - cstr_clear(sub); - unsigned len = 0, cap = cstr_capacity(*sub); - char* dst = cstr_data(sub); - - while (*repl != '\0') { - if (*repl == '\\') { - const char num = *++repl; +static void +cregex_build_subst(const char* replace, unsigned nmatch, const csview match[], + cstr (*mfun)(int i, csview match), cstr* subst) { + cstr_clear(subst); + unsigned len = 0, cap = cstr_capacity(*subst); + char* dst = cstr_data(subst); + + while (*replace != '\0') { + if (*replace == '\\') { + const char num = *++replace; int i; switch (num) { case '0': case '1': case '2': case '3': case '4': @@ -1172,25 +1172,25 @@ void cregex_build_replace(const char* repl, unsigned nmatch, const csview match[ i = num - '0'; if (i < nmatch) { csview m; - cstr s = cstr_null; - if (mfun) { s = mfun(i, match[i]); m = cstr_sv(&s); } + cstr mstr = cstr_null; + if (mfun) { mstr = mfun(i, match[i]); m = cstr_sv(&mstr); } else m = match[i]; if (len + m.size >= cap) - dst = cstr_reserve(sub, cap = cap*3/2 + m.size); + dst = cstr_reserve(subst, cap = cap*3/2 + m.size); for (const char* rp = m.str; rp != (m.str + m.size); ++rp) dst[len++] = *rp; - cstr_drop(&s); + cstr_drop(&mstr); } - ++repl; + ++replace; case '\0': continue; } } if (len == cap) - dst = cstr_reserve(sub, cap = cap*3/2 + 4); - dst[len++] = *repl++; + dst = cstr_reserve(subst, cap = cap*3/2 + 4); + dst[len++] = *replace++; } - _cstr_set_size(sub, len); + _cstr_set_size(subst, len); } @@ -1198,62 +1198,74 @@ void cregex_build_replace(const char* repl, unsigned nmatch, const csview match[ * API functions */ -int cregex_compile(cregex *rx, const char* pattern, int cflags) { +int +cregex_compile(cregex *self, const char* pattern, int cflags) { Parser par; - rx->prog = regcomp1(rx->prog, &par, pattern, cflags); - if (rx->prog) - return 1 + rx->prog->nsubids; - return par.errors; + self->prog = regcomp1(self->prog, &par, pattern, cflags); + return self->prog ? 1 + self->prog->nsubids : par.errors; } -int cregex_captures(const cregex* self) { +int +cregex_captures(const cregex* self) { return self->prog ? 1 + self->prog->nsubids : 0; } -int cregex_match(const cregex *rx, const char* string, - unsigned nmatch, csview match[], int mflags) { - int res = regexec(rx->prog, string, nmatch, match, mflags); +int +cregex_match_re(const char* input, const cregex* re, + unsigned nmatch, csview match[], int mflags) { + int res = regexec(re->prog, input, nmatch, match, mflags); switch (res) { - case 1: return creg_success; - case 0: return creg_nomatch; - default: return creg_matcherror; + case 1: return cre_success; + case 0: return cre_nomatch; + default: return cre_matcherror; } } +int cregex_match_ex(const char* input, const char* pattern, int cflags, + unsigned nmatch, csview match[], int mflags) { + cregex re = cregex_init(); + int res = cregex_compile(&re, pattern, cflags); + if (res < 0) return res; + res = cregex_match_re(input, &re, nmatch, match, mflags); + cregex_drop(&re); + return res; +} -cstr cregex_replace_re(const char* input, const cregex* re, const char* repl, - cstr (*mfun)(int i, csview match), int cflags, unsigned count) { +cstr +cregex_replace_re(const char* input, const cregex* re, const char* replace, + cstr (*mfun)(int i, csview match), int cflags, unsigned count) { cstr out = cstr_null; - cstr sub = cstr_null; + cstr subst = cstr_null; size_t from = 0; - csview match[cregex_MAXCAPTURES]; + csview match[cre_MAXCAPTURES]; unsigned nmatch = cregex_captures(re); if (!count) count = ~0; - while (count-- && cregex_match(re, input + from, nmatch, match, 0) > 0) { - cregex_build_replace(repl, nmatch, match, mfun, &sub); + while (count-- && cregex_match_re(input + from, re, nmatch, match, 0) > 0) { + cregex_build_subst(replace, nmatch, match, mfun, &subst); const size_t pos = match[0].str - input; cstr_append_n(&out, input + from, pos - from); - cstr_append_s(&out, sub); + cstr_append_s(&out, subst); from = pos + match[0].size; } cstr_append(&out, input + from); - cstr_drop(&sub); + cstr_drop(&subst); return out; } -cstr cregex_replace_ex(const char* input, const char* pattern, const char* repl, - cstr (*mfun)(int i, csview match), int cflags, unsigned count) { +cstr +cregex_replace_ex(const char* input, const char* pattern, const char* replace, + cstr (*mfun)(int i, csview match), int cflags, unsigned count) { cregex re = cregex_init(); int res = cregex_compile(&re, pattern, cflags); if (res < 0) - return cstr_new("[[cregex_replace_ex]]: invalid pattern"); - cstr out = cregex_replace_re(input, &re, repl, mfun, cflags, count); + return cstr_new("[[error: invalid regex pattern]]"); + cstr out = cregex_replace_re(input, &re, replace, mfun, cflags, count); cregex_drop(&re); return out; } - -void cregex_drop(cregex* self) { +void +cregex_drop(cregex* self) { free(self->prog); } -- cgit v1.2.3