aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--grammars/bp.bp44
-rw-r--r--match.c6
-rw-r--r--pattern.c56
-rw-r--r--print.c3
-rw-r--r--types.h1
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 <pat> => <pat>
- 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