Some error handling

This commit is contained in:
Bruce Hill 2020-09-17 00:29:11 -07:00
parent 2477d9869c
commit 67e538e774
5 changed files with 52 additions and 18 deletions

17
bpeg.c
View File

@ -49,11 +49,28 @@ static char *getflag(const char *flag, char *argv[], int *i)
return NULL;
}
static int print_errors(file_t *f, match_t *m)
{
int ret = 0;
if (m->op->op == VM_CAPTURE && m->value.name && streq(m->value.name, "!")) {
printf("\033[31;1m");
print_match(f, m);
printf("\033[0m\n");
fprint_line(stdout, f, m->start, m->end, "");
return 1;
}
if (m->child) ret += print_errors(f, m->child);
if (m->nextsibling) ret += print_errors(f, m->nextsibling);
return ret;
}
static int run_match(grammar_t *g, const char *filename, vm_op_t *pattern, unsigned int flags)
{
file_t *f = load_file(filename);
check(f, "Could not open file: %s", filename);
match_t *m = match(g, f, f->contents, pattern, flags);
if (m && print_errors(f, m) > 0)
_exit(1);
if (m != NULL && m->end > m->start + 1) {
print_match(f, m);
destroy_file(&f);

View File

@ -101,9 +101,15 @@ void fprint_line(FILE *dest, file_t *f, const char *start, const char *end, cons
f->filename, linenum, charnum, msg);
const char *eol = linenum == f->nlines ? strchr(line, '\0') : strchr(line, '\n');
if (end == NULL || end > eol) end = eol;
fprintf(dest, "\033[2m% 5ld |\033[0m %.*s\033[31;4;1m%.*s\033[0m%.*s\n",
fprintf(dest, "\033[2m% 5ld |\033[0m %.*s\033[41;30m%.*s\033[0m%.*s\n",
linenum,
(int)charnum - 1, line,
(int)(end - &line[charnum-1]), &line[charnum-1],
(int)(eol - end), end);
fprintf(dest, " ");
const char *p = line - 1;
for (; p < start; ++p) fputc(' ', dest);
if (start == end) ++end;
for (; p < end; ++p) fputc('^', dest);
fputc('\n', dest);
}

View File

@ -1,7 +1,9 @@
# This is a file defining the BPEG grammar using BPEG syntax
Grammar: __ 0+(Def 0-1(__`;))%__ __
Def: @[name]id __ `: __ @[definition]extended-pat
Grammar: __ 0+(Def 0-1(__`;))%__ __ ($$ / @[!]{... => "Could not parse this code"})
Def: @[name]id __ `: __ (@[definition]extended-pat
/ $$ @[!]{=>"No definition for rule"}
/ @[!]{...>(`;/id__`:/$) => "Invalid definition: @0"})
# This is used for command line arguments:
String-pattern: 0+(`\ pat 0-1`; / .)
@ -9,14 +11,20 @@ String-pattern: 0+(`\ pat 0-1`; / .)
pat: suffixed-pat / simple-pat
simple-pat: Upto-and / Dot / String / Char-range / Char / Escape-range / Escape / No
/ Nodent / Repeat / After / Before / Capture / Replace / Ref / parens
suffixed-pat: Eq-pat
suffixed-pat: Eq-pat / simple-pat (
@[!]{`* => "'*' is not a BPEG operator. Use 0+<pat> instead of <pat>*"}
/ @[!]{`+ => "'+' is not a BPEG operator. Use 1+<pat> instead of <pat>+"}
/ @[!]{`? => "'?' is not a BPEG operator. Use 0-1<pat> or 1-<pat> instead of <pat>?"}
/ @[!]{`= => "'=' is not valid here. Perhaps you meant '==' or ':'"}
)
Eq-pat: @[first]simple-pat "==" @[second]pat
Dot: `. !`.
String: (
`" @[s]0+(Escape / !`"$.) `"
/ `' @[s]0+(Escape / !`'$.) `'
`" @[s]0+(Escape / !`".) (`" / @[!]{=> "Expected closing quote here"})
/ `' @[s]0+(Escape / !`'.) (`' / @[!]{=> "Expected closing quote here"})
)
Char-range: `` @[low]. `- @[high].
Char: `` @[s].
@ -37,13 +45,13 @@ Repeat: (
) _ @[repeat-pat]pat 0-1( __ `% __ @[sep]pat)
After: `< _ pat
Before: `> _ pat
Capture: `@ 0-1(_ `[ @[capture-name](...>`]) `]) _ @[capture]pat
Capture: `@ 0-1(_ `[ @[capture-name](...>(`]/$$)) (`] / @[!]{=>"Expected closing bracket here"})) _ @[capture]pat
Replace: `{ __ (
0-1(@[replace-pat]extended-pat __) "=>" 0-1(__ @[replacement]String)
) __ `}
) __ (`} / @[!]{=> "Expected closing brace here"})
Ref: @[name]id !>(__`:)
parens: `( __ extended-pat __ `)
parens: `( __ extended-pat (__ `) / @[!]{=> "Expected closing parenthesis here"})
Chain: 2+@pat % (__)
Otherwise: 2+@(Chain/pat) % (__`/__)

View File

@ -76,7 +76,10 @@ typedef struct vm_op_s {
typedef struct match_s {
// Where the match starts and ends (end is after the last character)
const char *start, *end;
const char *name_or_replacement;
union {
const char *name;
const char *replacement;
} value;
struct match_s *child, *nextsibling;
vm_op_t *op;
} match_t;

16
vm.c
View File

@ -56,9 +56,9 @@ static size_t push_backrefs(grammar_t *g, match_t *m)
if (m == NULL) return 0;
if (m->op->op == VM_REF) return 0;
size_t count = 0;
if (m->op->op == VM_CAPTURE && m->name_or_replacement) {
if (m->op->op == VM_CAPTURE && m->value.name) {
++count;
push_backref(g, m->name_or_replacement, m->child);
push_backref(g, m->value.name, m->child);
}
if (m->child) count += push_backrefs(g, m->child);
if (m->nextsibling) count += push_backrefs(g, m->nextsibling);
@ -228,7 +228,7 @@ static match_t *_match(grammar_t *g, file_t *f, const char *str, vm_op_t *op, un
m->op = op;
m->child = p;
if (op->args.capture.name)
m->name_or_replacement = op->args.capture.name;
m->value.name = op->args.capture.name;
return m;
}
case VM_OTHERWISE: {
@ -286,7 +286,7 @@ static match_t *_match(grammar_t *g, file_t *f, const char *str, vm_op_t *op, un
} else {
m->end = m->start;
}
m->name_or_replacement = op->args.replace.replacement;
m->value.replacement = op->args.replace.replacement;
return m;
}
case VM_REF: {
@ -325,7 +325,7 @@ static match_t *_match(grammar_t *g, file_t *f, const char *str, vm_op_t *op, un
m->end = best->end;
m->op = op;
m->child = best;
m->name_or_replacement = op->args.s;
m->value.name = op->args.s;
return m;
}
case VM_BACKREF: {
@ -490,7 +490,7 @@ static match_t *get_capture_n(match_t *m, int *n)
*/
static match_t *get_capture_named(match_t *m, const char *name)
{
if (m->op->op == VM_CAPTURE && m->name_or_replacement && streq(m->name_or_replacement, name))
if (m->op->op == VM_CAPTURE && m->value.name && streq(m->value.name, name))
return m;
for (match_t *c = m->child; c; c = c->nextsibling) {
match_t *cap = get_capture_named(c, name);
@ -523,7 +523,7 @@ static match_t *get_cap(match_t *m, const char **r)
void print_match(file_t *f, match_t *m)
{
if (m->op->op == VM_REPLACE) {
for (const char *r = m->name_or_replacement; *r; ) {
for (const char *r = m->value.replacement; *r; ) {
if (*r == '\\') {
++r;
fputc(unescapechar(r, &r), stdout);
@ -585,7 +585,7 @@ static match_t *match_backref(const char *str, vm_op_t *op, match_t *cap, unsign
match_t **dest = &ret->child;
if (cap->op->op == VM_REPLACE) {
for (const char *r = cap->name_or_replacement; *r; ) {
for (const char *r = cap->value.replacement; *r; ) {
if (*r == '\\') {
++r;
if (*(str++) != unescapechar(r, &r)) {