aboutsummaryrefslogtreecommitdiff
path: root/pattern.c
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2021-05-20 00:33:11 -0700
committerBruce Hill <bruce@bruce-hill.com>2021-05-20 00:33:11 -0700
commitd9f0a92839000018eaaf7ebb34ed0260792fd116 (patch)
tree1e406906be6dd96d2741d8922e5d0c1c10351f6a /pattern.c
parent355e06a58e918c6c1d39902b0a14029a16c323ba (diff)
Added (!) syntax for errors with proper opcode support
Diffstat (limited to 'pattern.c')
-rw-r--r--pattern.c56
1 files changed, 40 insertions, 16 deletions
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, '^'))