Better error handling behavior

This commit is contained in:
Bruce Hill 2022-10-27 13:17:23 -04:00
parent 54e87ff91e
commit e258645a52
5 changed files with 49 additions and 36 deletions

View File

@ -26,9 +26,9 @@ O=-O3
ALL_FLAGS=$(CFLAGS) $(OSFLAGS) $(INCS) $(EXTRA) $(CWARN) $(G) $(O)
ifeq ($(shell uname -s),Darwin)
MAKESO= $(CC) -bundle -undefined dynamic_lookup
MAKESO= $(CC) -bundle -undefined dynamic_lookup $(CFLAGS) $(OSFLAGS) $(EXTRA) $(CWARN) $(G) $(O)
else
MAKESO= $(CC) -shared
MAKESO= $(CC) -shared $(CFLAGS) $(OSFLAGS) $(EXTRA) $(CWARN) $(G) $(O)
endif
all: bp.so

View File

@ -33,10 +33,8 @@ static void push_match(lua_State *L, match_t *m, const char *start);
lua_State *cur_state = NULL;
static void match_error(pat_t *pat, const char *msg)
static void match_error(const char *msg)
{
(void)pat;
recycle_all_matches();
lua_pushstring(cur_state, msg);
lua_error(cur_state);
}
@ -170,11 +168,13 @@ static int Lmatch(lua_State *L)
match_t *m = NULL;
int ret = 0;
cur_state = L;
if (next_match_safe(&m, text+index-1, &text[textlen], pat, builtins, NULL, false, match_error)) {
bp_errhand_t old = bp_set_error_handler(match_error);
if (next_match(&m, text+index-1, &text[textlen], pat, builtins, NULL, false)) {
push_match(L, m, text);
stop_matching(&m);
ret = 1;
}
bp_set_error_handler(old);
return ret;
}
@ -209,12 +209,14 @@ static int Lreplace(lua_State *L)
const char *prev = text;
pat_t *rep_pat = maybe_replacement.value.pat;
cur_state = L;
for (match_t *m = NULL; next_match_safe(&m, text, &text[textlen], rep_pat, builtins, NULL, false, match_error); ) {
bp_errhand_t old = bp_set_error_handler(match_error);
for (match_t *m = NULL; next_match(&m, text, &text[textlen], rep_pat, builtins, NULL, false); ) {
fwrite(prev, sizeof(char), (size_t)(m->start - prev), out);
fprint_match(out, text, m, NULL);
prev = m->end;
++replacements;
}
bp_set_error_handler(old);
fwrite(prev, sizeof(char), (size_t)(&text[textlen] - prev), out);
fflush(out);
lua_pushlstring(L, buf, size);

View File

@ -38,7 +38,7 @@ print("Testing parse errors:")
local ok, msg = pcall(function()
bp.match(".;//;;; wtf", "xxx")
end)
if not ok then print(("\x1B[41;30mParse error:\x1B[0;1;31m %s\x1B[m\n"):format(msg)) end
if not ok then print(("Successfully got parse error: \"%s\"\n"):format(msg)) end
print("Testing builtins:")
print(bp.match("parens", "...(foo())..."))

63
match.c
View File

@ -5,6 +5,7 @@
#include <ctype.h>
#include <err.h>
#include <limits.h>
#include <setjmp.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@ -37,6 +38,7 @@ typedef struct match_ctx_s {
pat_t *defs;
cache_t *cache;
const char *start, *end;
jmp_buf error_jump;
bool ignorecase;
} match_ctx_t;
@ -49,13 +51,19 @@ typedef struct match_ctx_s {
static match_t *unused_matches = NULL;
static match_t *in_use_matches = NULL;
static void default_error_handler(pat_t *pat, const char *msg) {
(void)pat;
static void default_error_handler(const char *msg) {
errx(EXIT_FAILURE, "%s", msg);
}
static bp_errhand_t error_handler = default_error_handler;
bp_errhand_t bp_set_error_handler(bp_errhand_t new_handler)
{
bp_errhand_t old_handler = error_handler;
error_handler = new_handler;
return old_handler;
}
#define MATCHES(...) (match_t*[]){__VA_ARGS__, NULL}
__attribute__((hot, nonnull(1,2,3)))
@ -63,16 +71,17 @@ static match_t *match(match_ctx_t *ctx, const char *str, pat_t *pat);
__attribute__((returns_nonnull))
static match_t *new_match(pat_t *pat, const char *start, const char *end, match_t *children[]);
char *error_message = NULL;
__attribute__((format(printf,2,3)))
static inline void match_error(pat_t *pat, const char *fmt, ...)
static inline void match_error(match_ctx_t *ctx, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
char buf[256];
vsnprintf(buf, sizeof(buf)-1, fmt, args);
if (error_message) free(error_message);
vasprintf(&error_message, fmt, args);
va_end(args);
if (error_handler)
error_handler(pat, buf);
longjmp(ctx->error_jump, 1);
}
static match_t *clone_match(match_t *m)
@ -214,11 +223,11 @@ void cache_destroy(match_ctx_t *ctx)
// Look up a pattern definition by name from a definition pattern.
//
__attribute__((nonnull(2)))
static pat_t *_lookup_def(pat_t *defs, const char *name, size_t namelen)
static pat_t *_lookup_def(match_ctx_t *ctx, pat_t *defs, const char *name, size_t namelen)
{
while (defs) {
if (defs->type == BP_CHAIN) {
pat_t *second = _lookup_def(defs->args.multiple.second, name, namelen);
pat_t *second = _lookup_def(ctx, defs->args.multiple.second, name, namelen);
if (second) return second;
defs = defs->args.multiple.first;
} else if (defs->type == BP_DEFINITIONS) {
@ -226,7 +235,7 @@ static pat_t *_lookup_def(pat_t *defs, const char *name, size_t namelen)
return defs->args.def.meaning;
defs = defs->args.def.next_def;
} else {
match_error(defs, "Invalid pattern type in definitions");
match_error(ctx, "Invalid pattern type in definitions");
return NULL;
}
}
@ -240,7 +249,7 @@ __attribute__((nonnull))
pat_t *lookup_ctx(match_ctx_t *ctx, const char *name, size_t namelen)
{
for (; ctx; ctx = ctx->parent_ctx) {
pat_t *def = _lookup_def(ctx->defs, name, namelen);
pat_t *def = _lookup_def(ctx, ctx->defs, name, namelen);
if (def) return def;
}
return NULL;
@ -667,7 +676,7 @@ static match_t *match(match_ctx_t *ctx, const char *str, pat_t *pat)
pat_t *ref = lookup_ctx(ctx, pat->args.ref.name, pat->args.ref.len);
if (ref == NULL) {
match_error(pat, "Unknown pattern: '%.*s'", (int)pat->args.ref.len, pat->args.ref.name);
match_error(ctx, "Unknown pattern: '%.*s'", (int)pat->args.ref.len, pat->args.ref.name);
return NULL;
}
@ -754,7 +763,7 @@ static match_t *match(match_ctx_t *ctx, const char *str, pat_t *pat)
return new_match(pat, str, str, NULL);
}
default: {
match_error(pat, "Unknown pattern type: %u", pat->type);
match_error(ctx, "Unknown pattern type: %u", pat->type);
return NULL;
}
}
@ -864,20 +873,22 @@ bool next_match(match_t **m, const char *start, const char *end, pat_t *pat, pat
.ignorecase = ignorecase,
.defs = defs,
};
*m = (pos <= end) ? _next_match(&ctx, pos, pat, skip) : NULL;
cache_destroy(&ctx);
return *m != NULL;
}
if (setjmp(ctx.error_jump) == 0) {
*m = (pos <= end) ? _next_match(&ctx, pos, pat, skip) : NULL;
cache_destroy(&ctx);
} else {
recycle_all_matches();
cache_destroy(&ctx);
*m = NULL;
if (error_handler)
error_handler(error_message);
//
// Wrapper for next_match() that sets an error handler
//
bool next_match_safe(match_t **m, const char *start, const char *end, pat_t *pat, pat_t *defs, pat_t *skip, bool ignorecase, bp_errhand_t errhand)
{
error_handler = errhand;
bool ret = next_match(m, start, end, pat, defs, skip, ignorecase);
error_handler = default_error_handler;
return ret;
if (error_message) {
free(error_message);
error_message = NULL;
}
}
return *m != NULL;
}
//

View File

@ -25,7 +25,7 @@ typedef struct match_s {
struct match_s *_children[3];
} match_t;
typedef void (*bp_errhand_t)(pat_t *pat, const char *err_msg);
typedef void (*bp_errhand_t)(const char *err_msg);
__attribute__((nonnull))
void recycle_match(match_t **at_m);
@ -33,7 +33,7 @@ size_t free_all_matches(void);
size_t recycle_all_matches(void);
bool next_match(match_t **m, const char *start, const char *end, pat_t *pat, pat_t *defs, pat_t *skip, bool ignorecase);
#define stop_matching(m) next_match(m, NULL, NULL, NULL, NULL, NULL, 0)
bool next_match_safe(match_t **m, const char *start, const char *end, pat_t *pat, pat_t *defs, pat_t *skip, bool ignorecase, bp_errhand_t errhand);
bp_errhand_t bp_set_error_handler(bp_errhand_t handler);
__attribute__((nonnull))
match_t *get_numbered_capture(match_t *m, int n);
__attribute__((nonnull, pure))