From d9f0a92839000018eaaf7ebb34ed0260792fd116 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Thu, 20 May 2021 00:33:11 -0700 Subject: Added (!) syntax for errors with proper opcode support --- grammars/bp.bp | 44 ++++++++++++++++++++++++-------------------- match.c | 6 +++++- pattern.c | 56 ++++++++++++++++++++++++++++++++++++++++---------------- print.c | 3 +-- types.h | 1 + 5 files changed, 71 insertions(+), 39 deletions(-) diff --git a/grammars/bp.bp b/grammars/bp.bp index 7240729..fc1e794 100644 --- a/grammars/bp.bp +++ b/grammars/bp.bp @@ -5,18 +5,18 @@ # The grammar files provided with BP are not otherwise intended to be full # language grammars. -Grammar: __ *(Def [__`;])%__ __ ($$ / @!=(..%\n$$ => "Could not parse this code")) +Grammar: __ *(Def [__`;])%__ __ ($$ / (!)(..%\n$$ => "Could not parse this code")) Def: @name=id __ `: __ ( @definition=extended-pat - / $$ @!=(''=>"No definition for rule") - / @!=(..%\n>(`;/id_`:/$) => "Invalid definition: @0")) + / $$ (!)=>"No definition for rule" + / (!)(..%\n>(`;/id_`:/$) => "Invalid definition: @0")) # This is used for command line arguments: String-pattern: ..%(\n / Nodent / Escape / `\ pat [`;])$$ pat: simple-pat !(__("!~"/"~")) / suffixed-pat simple-pat: Upto-and / Dot / String / Chars / Nodent / Escape-range - / Escape / Repeat / Optional / No / After / Before / Capture + / Escape / Repeat / Optional / No / After / Before / Capture / Error / Empty-replacement / Start-of-File / Start-of-Line / End-of-File / End-of-Line / Ref / parens suffixed-pat: ( @@ -24,43 +24,47 @@ suffixed-pat: ( / Not-match-pat ) -Match-pat: @first=(suffixed-pat / simple-pat)__"~"__@second=(pat / @!=(''=> "Expected pattern after '~'")) -Not-match-pat: @first=(suffixed-pat / simple-pat)__"!~"__@second=(pat / @!=(''=> "Expected pattern after '!~'")) +Match-pat: @first=(suffixed-pat / simple-pat)__"~"__@second=(pat / (!)=>"Expected pattern after '~'") +Not-match-pat: @first=(suffixed-pat / simple-pat)__"!~"__@second=(pat / (!)=>"Expected pattern after '!~'") Dot: `. !`. String: ( - `" @s=.. (`" / $ @!=(''=> "Expected closing quote here")) - / `' @s=.. (`' / $ @!=(''=> "Expected closing quote here")) - / `{ @s=.. (`} / $ @!=(''=> "Expected closing brace here")) + `" @s=.. (`" / $ (!)=>"Expected closing quote here") + / `' @s=.. (`' / $ (!)=>"Expected closing quote here") + / `{ @s=.. (`} / $ (!)=>"Expected closing brace here") ) Chars: `` @+(Char-range/Char) % `, -Char-range: @low=. `- (@high=. / @!=(''=> "Expected a second character to form a character range")) -Char: (@s=. / @!=(''=> "Expected a character following the '`'")) +Char-range: @low=. `- (@high=. / (!)=>"Expected a second character to form a character range") +Char: (@s=. / (!)=>"Expected a character following the '`'") Escape-range: `\ @low=escape-sequence `- @high=escape-sequence Escape: `\ (@s=escape-sequence - / $ @!=(''=>"Backslashes are used for escape sequences, not splitting lines") - / @!=(. *(Abc/`0-9) => "Invalid escape sequence: '@0'") + / $ (!)=>"Backslashes are used for escape sequences, not splitting lines" + / (!)(. *(Abc/`0-9) => "Invalid escape sequence: '@0'") ) escape-sequence: ( `n,t,r,e,b,a,v / 1-3 `0-7 / `x 2 `0-9,a-f,A-F ) -No: `! (__@pat / @!=(''=>"Expected a pattern after the exclamation mark")) +No: `! (__@pat / (!)=>"Expected a pattern after the exclamation mark") Nodent: `\ `N Upto-and: ".." [__`%__@second=simple-pat] [__@first=simple-pat] Repeat: ( - @min=(''=>'0') (`*=>"-") @max=(''=>'∞') + @min=(=>'0') (`*=>"-") @max=(=>'∞') / @min=int __ `- __ @max=int - / @min=(int / ''=>'1') __ (`+=>"-") @max=(''=>'∞') + / @min=(int / =>'1') __ (`+=>"-") @max=(=>'∞') / @min=@max=int ) __ @repeat-pat=pat [__`%__@sep=pat] -Optional: `[ __ extended-pat (__`] / @!=(''=> "Expected closing square bracket here")) +Optional: `[ __ extended-pat (__`] / (!)=>"Expected closing square bracket here") After: `< __ pat Before: `> __ pat -Capture: `@ [__ @capture-name=(id/`!) __ !"=>" `=] __ (@capture=pat / @!=(''=> "Expected pattern to capture")) +Capture: `@ [__ @capture-name=(id/`!) __ !"=>" `=] __ (@capture=pat / (!)=>"Expected pattern to capture") +Error: "(!)" @pat=[__ (Replace / Chain / pat)] Replace: ( - @replace-pat=(Replace / Chain / pat) __ "=>" (__ @replacement=String / @!=(''=> "Expected replacement string")) + @replace-pat=(Replace / Chain / pat) __ "=>" (__ @replacement=String / (!)=>"Expected replacement string") + ) +Empty-replacement: ( + @replace-pat=(=>"''") "=>" (__ @replacement=String / (!)=>"Expected replacement string") ) Ref: @name=id !(__`:) Start-of-File: "^^" @@ -68,7 +72,7 @@ Start-of-Line: "^" End-of-File: "$$" End-of-Line: "$" -parens: `( __ extended-pat (__ `) / @!=(''=> "Expected closing parenthesis here")) +parens: `( __ extended-pat (__ `) / (!)=>"Expected closing parenthesis here") Chain: 2+@(pat !(__"=>") / Replace)%__ Otherwise: 2+@(Replace / Chain / pat)%(__`/__) diff --git a/match.c b/match.c index f8f0ab4..9f31975 100644 --- a/match.c +++ b/match.c @@ -201,7 +201,7 @@ static match_t *match(def_t *defs, file_t *f, const char *str, pat_t *pat, bool } case BP_STRING: { if (&str[pat->len] > f->end) return NULL; - if ((ignorecase ? memicmp : memcmp)(str, pat->args.string, (size_t)pat->len) != 0) + if (pat->len > 0 && (ignorecase ? memicmp : memcmp)(str, pat->args.string, (size_t)pat->len) != 0) return NULL; return new_match(pat, str, str + pat->len, NULL); } @@ -476,6 +476,10 @@ static match_t *match(def_t *defs, file_t *f, const char *str, pat_t *pat, bool return new_match(pat, start, &str[dents], NULL); } + case BP_ERROR: { + match_t *p = match(defs, f, str, pat->args.pat, ignorecase); + return p ? new_match(pat, str, p->end, p) : NULL; + } default: { errx(EXIT_FAILURE, "Unknown pattern type: %d", pat->type); return NULL; diff --git a/pattern.c b/pattern.c index bc34e4b..d0a8e99 100644 --- a/pattern.c +++ b/pattern.c @@ -14,6 +14,8 @@ #include "pattern.h" #include "utils.h" +__attribute__((nonnull(1,2))) +static pat_t *expand_replacements(file_t *f, const char *str, pat_t *replace_pat); __attribute__((nonnull)) static pat_t *expand_chain(file_t *f, pat_t *first); __attribute__((nonnull)) @@ -76,20 +78,15 @@ static pat_t *expand_chain(file_t *f, pat_t *first) } // -// Take a pattern and parse any "=>" replacements and then expand it into a -// chain of choices if it's followed by any "/"-separated patterns (e.g. -// "`x/`y"), otherwise return the original input. +// Match trailing => replacements (with optional pattern beforehand) // -static pat_t *expand_choices(file_t *f, pat_t *first) +static pat_t *expand_replacements(file_t *f, const char *str, pat_t *replace_pat) { - first = expand_chain(f, first); - const char *str = first->end; - - while (str+2 < f->end && matchstr(&str, "=>")) { // Replacement => - str = after_spaces(str); - char quote = *str; + const char *start = str; + while (matchstr(&str, "=>")) { if (!(matchchar(&str, '"') || matchchar(&str, '\''))) file_err(f, str, str, "There should be a string literal as a replacement here."); + char quote = str[-1]; const char *repstr = str; for (; *str && *str != quote; str++) { if (*str == '\\') { @@ -101,13 +98,26 @@ static pat_t *expand_choices(file_t *f, pat_t *first) } (void)matchchar(&str, quote); - pat_t *replacepat = first; - first = new_pat(f, replacepat->start, str, replacepat->len, BP_REPLACE); - first->args.replace.pat = replacepat; - first->args.replace.text = repstr; - first->args.replace.len = (size_t)(str-repstr-1); + if (replace_pat == NULL) replace_pat = new_pat(f, start, start, 0, BP_STRING); + pat_t *pat = new_pat(f, replace_pat->start, str, replace_pat->len, BP_REPLACE); + pat->args.replace.pat = replace_pat; + pat->args.replace.text = repstr; + pat->args.replace.len = (size_t)(str-repstr-1); + replace_pat = pat; } + return replace_pat; +} +// +// Take a pattern and parse any "=>" replacements and then expand it into a +// chain of choices if it's followed by any "/"-separated patterns (e.g. +// "`x/`y"), otherwise return the original input. +// +static pat_t *expand_choices(file_t *f, pat_t *first) +{ + first = expand_chain(f, first); + first = expand_replacements(f, first->end, first); + const char *str = first->end; if (!matchchar(&str, '/')) return first; pat_t *second = bp_simplepattern(f, str); if (!second) @@ -383,6 +393,16 @@ static pat_t *_bp_simplepattern(file_t *f, const char *str) } // Parentheses case '(': { + if (matchstr(&str, "!)")) { + pat_t *pat = bp_simplepattern(f, str); + if (!pat) pat = new_pat(f, str, str, 0, BP_STRING); + pat = expand_replacements(f, pat->end, pat); + + pat_t *error = new_pat(f, start, pat->end, pat->len, BP_ERROR); + error->args.pat = pat; + return error; + } + pat_t *pat = bp_simplepattern(f, str); if (!pat) file_err(f, str, str, "There should be a valid pattern after this parenthesis."); @@ -423,7 +443,7 @@ static pat_t *_bp_simplepattern(file_t *f, const char *str) case '@': { const char *name = NULL; size_t namelen = 0; - const char *a = *str == '!' ? &str[1] : after_name(str); + const char *a = after_name(str); if (a > str && after_spaces(a)[0] == '=' && after_spaces(a)[1] != '>') { name = str; namelen = (size_t)(a-str); @@ -439,6 +459,10 @@ static pat_t *_bp_simplepattern(file_t *f, const char *str) capture->args.capture.namelen = namelen; return capture; } + // Replacement with empty pattern: (=> 'blah') + case '=': { + return expand_replacements(f, start, NULL); + } // Start of file/line: case '^': { if (matchchar(&str, '^')) diff --git a/print.c b/print.c index 9e0897c..8c2fe5f 100644 --- a/print.c +++ b/print.c @@ -413,8 +413,7 @@ void print_match(FILE *out, printer_t *pr, match_t *m) int print_errors(printer_t *pr, match_t *m) { int ret = 0; - if (m->pat->type == BP_CAPTURE && m->pat->args.capture.name - && strncmp(m->pat->args.capture.name, "!", m->pat->args.capture.namelen) == 0) { + if (m->pat->type == BP_ERROR) { printf("\033[31;1m"); printer_t tmp = {.file = pr->file}; // No bells and whistles print_match(stdout, &tmp, m); // Error message diff --git a/types.h b/types.h index ae41d4a..dda89de 100644 --- a/types.h +++ b/types.h @@ -33,6 +33,7 @@ enum pattype_e { BP_END_OF_FILE, BP_END_OF_LINE, BP_LEFTRECURSION, + BP_ERROR, }; struct match_s; // forward declared to resolve circular struct defs -- cgit v1.2.3