diff --git a/Makefile b/Makefile index f54e8af..abbdb19 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ G= O=-O3 ALL_FLAGS=$(CFLAGS) $(OSFLAGS) -DBP_NAME="\"$(NAME)\"" $(EXTRA) $(CWARN) $(G) $(O) -CFILES=pattern.c utils.c match.c files.c explain.c json.c utf8.c +CFILES=pattern.c utils.c match.c files.c printmatch.c json.c utf8.c OBJFILES=$(CFILES:.c=.o) all: $(NAME) bp.1 diff --git a/README.md b/README.md index fb8d39b..20ee2ad 100644 --- a/README.md +++ b/README.md @@ -140,11 +140,11 @@ so use at your own risk! These grammar files are only approximations of syntax. File | Description -------------------------------|----------------------------------------------------- [bp.c](bp.c) | The main program. -[explain.c](explain.c) | Printing a visual explanation of a match. [files.c](files.c) | Loading files into memory. [json.c](json.c) | JSON output of matches. [match.c](match.c) | Pattern matching code (find occurrences of a bp pattern within an input string). [pattern.c](pattern.c) | Pattern compiling code (compile a bp pattern from an input string). +[printmatch.c](printmatch.c) | Printing a visual explanation of a match. [utf8.c](utf8.c) | UTF-8 helper code. [utils.c](utils.c) | Miscellaneous helper functions. diff --git a/bp.c b/bp.c index 5146d5a..dbd72bd 100644 --- a/bp.c +++ b/bp.c @@ -20,11 +20,11 @@ #include #include -#include "explain.h" #include "files.h" #include "json.h" #include "match.h" #include "pattern.h" +#include "printmatch.h" #include "utils.h" #ifndef BP_NAME diff --git a/explain.h b/explain.h deleted file mode 100644 index fbb2abf..0000000 --- a/explain.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// Debug visualization of matches -// -#ifndef EXPLAIN__H -#define EXPLAIN__H - -#include "match.h" - -__attribute__((nonnull)) -void explain_match(match_t *m); - -#endif -// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/match.c b/match.c index 461148d..80296e9 100644 --- a/match.c +++ b/match.c @@ -857,91 +857,4 @@ match_t *get_named_capture(match_t *m, const char *name, size_t namelen) return NULL; } -static inline void fputc_safe(FILE *out, char c, print_options_t *opts) -{ - (void)fputc(c, out); - if (c == '\n' && opts && opts->on_nl) { - opts->on_nl(out); - if (opts->replace_color) fprintf(out, "%s", opts->replace_color); - } -} - -void fprint_match(FILE *out, const char *file_start, match_t *m, print_options_t *opts) -{ - if (m->pat->type == BP_REPLACE) { - const char *text = m->pat->args.replace.text; - const char *end = &text[m->pat->args.replace.len]; - if (opts && opts->replace_color) fprintf(out, "%s", opts->replace_color); - - // TODO: clean up the line numbering code - for (const char *r = text; r < end; ) { - // Capture substitution - if (*r == '@' && r+1 < end && r[1] != '@') { - const char *next = r+1; - // Retrieve the capture value: - match_t *cap = NULL; - if (isdigit(*next)) { - int n = (int)strtol(next, (char**)&next, 10); - cap = get_numbered_capture(m->children[0], n); - } else { - const char *name = next, *name_end = after_name(next, end); - if (name_end) { - cap = get_named_capture(m->children[0], name, (size_t)(name_end - name)); - next = name_end; - if (next < m->end && *next == ';') ++next; - } - } - - if (cap != NULL) { - fprint_match(out, file_start, cap, opts); - if (opts && opts->replace_color) fprintf(out, "%s", opts->replace_color); - r = next; - continue; - } - } - - if (*r == '\\') { - ++r; - if (*r == 'N') { // \N (nodent) - ++r; - // Mildly hacky: nodents here are based on the *first line* - // of the match. If the match spans multiple lines, or if - // the replacement text contains newlines, this may get weird. - const char *line_start = m->start; - while (line_start > file_start && line_start[-1] != '\n') --line_start; - fputc_safe(out, '\n', opts); - for (const char *p = line_start; p < m->start && (*p == ' ' || *p == '\t'); ++p) - fputc(*p, out); - continue; - } - fputc_safe(out, unescapechar(r, &r, end), opts); - } else { - fputc_safe(out, *r, opts); - ++r; - } - } - } else { - if (opts && opts->match_color) fprintf(out, "%s", opts->match_color); - const char *prev = m->start; - for (int i = 0; m->children && m->children[i]; i++) { - match_t *child = m->children[i]; - // Skip children from e.g. zero-width matches like >@foo - if (!(prev <= child->start && child->start <= m->end && - prev <= child->end && child->end <= m->end)) - continue; - if (child->start > prev) { - if (opts && opts->fprint_between) opts->fprint_between(out, prev, child->start, opts->match_color); - else fwrite(prev, sizeof(char), (size_t)(child->start - prev), out); - } - fprint_match(out, file_start, child, opts); - if (opts && opts->match_color) fprintf(out, "%s", opts->match_color); - prev = child->end; - } - if (m->end > prev) { - if (opts && opts->fprint_between) opts->fprint_between(out, prev, m->end, opts->match_color); - else fwrite(prev, sizeof(char), (size_t)(m->end - prev), out); - } - } -} - // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/match.h b/match.h index 93f2ade..7439b27 100644 --- a/match.h +++ b/match.h @@ -24,12 +24,6 @@ typedef struct match_s { struct match_s *_children[3]; } match_t; -typedef struct { - const char *normal_color, *match_color, *replace_color; - void (*fprint_between)(FILE *out, const char *start, const char *end, const char *normal_color); - void (*on_nl)(FILE *out); -} print_options_t; - __attribute__((nonnull)) void recycle_match(match_t **at_m); size_t free_all_matches(void); @@ -40,9 +34,6 @@ __attribute__((nonnull)) match_t *get_numbered_capture(match_t *m, int n); __attribute__((nonnull, pure)) match_t *get_named_capture(match_t *m, const char *name, size_t namelen); -__attribute__((nonnull(1,2,3))) -//void fprint_match(FILE *out, const char *file_start, match_t *m, const char *colors[3]); -void fprint_match(FILE *out, const char *file_start, match_t *m, print_options_t *opts); #endif // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/explain.c b/printmatch.c similarity index 60% rename from explain.c rename to printmatch.c index 41939c3..5b7e6e7 100644 --- a/explain.c +++ b/printmatch.c @@ -1,11 +1,13 @@ // -// explain.c - Debug visualization of pattern matches. +// printmatch.c - Debug visualization of pattern matches. // #include #include +#include #include "match.h" +#include "printmatch.h" #include "utils.h" typedef struct match_node_s { @@ -170,4 +172,92 @@ void explain_match(match_t *m) printf("\033[?7h"); // Re-enable line wrapping } +static inline void fputc_safe(FILE *out, char c, print_options_t *opts) +{ + (void)fputc(c, out); + if (c == '\n' && opts && opts->on_nl) { + opts->on_nl(out); + if (opts->replace_color) fprintf(out, "%s", opts->replace_color); + } +} + +void fprint_match(FILE *out, const char *file_start, match_t *m, print_options_t *opts) +{ + if (m->pat->type == BP_REPLACE) { + const char *text = m->pat->args.replace.text; + const char *end = &text[m->pat->args.replace.len]; + if (opts && opts->replace_color) fprintf(out, "%s", opts->replace_color); + + // TODO: clean up the line numbering code + for (const char *r = text; r < end; ) { + // Capture substitution + if (*r == '@' && r+1 < end && r[1] != '@') { + const char *next = r+1; + // Retrieve the capture value: + match_t *cap = NULL; + if (isdigit(*next)) { + int n = (int)strtol(next, (char**)&next, 10); + cap = get_numbered_capture(m->children[0], n); + } else { + const char *name = next, *name_end = after_name(next, end); + if (name_end) { + cap = get_named_capture(m->children[0], name, (size_t)(name_end - name)); + next = name_end; + if (next < m->end && *next == ';') ++next; + } + } + + if (cap != NULL) { + fprint_match(out, file_start, cap, opts); + if (opts && opts->replace_color) fprintf(out, "%s", opts->replace_color); + r = next; + continue; + } + } + + if (*r == '\\') { + ++r; + if (*r == 'N') { // \N (nodent) + ++r; + // Mildly hacky: nodents here are based on the *first line* + // of the match. If the match spans multiple lines, or if + // the replacement text contains newlines, this may get weird. + const char *line_start = m->start; + while (line_start > file_start && line_start[-1] != '\n') --line_start; + fputc_safe(out, '\n', opts); + for (const char *p = line_start; p < m->start && (*p == ' ' || *p == '\t'); ++p) + fputc(*p, out); + continue; + } + fputc_safe(out, unescapechar(r, &r, end), opts); + } else { + fputc_safe(out, *r, opts); + ++r; + } + } + } else { + if (opts && opts->match_color) fprintf(out, "%s", opts->match_color); + const char *prev = m->start; + for (int i = 0; m->children && m->children[i]; i++) { + match_t *child = m->children[i]; + // Skip children from e.g. zero-width matches like >@foo + if (!(prev <= child->start && child->start <= m->end && + prev <= child->end && child->end <= m->end)) + continue; + if (child->start > prev) { + if (opts && opts->fprint_between) opts->fprint_between(out, prev, child->start, opts->match_color); + else fwrite(prev, sizeof(char), (size_t)(child->start - prev), out); + } + fprint_match(out, file_start, child, opts); + if (opts && opts->match_color) fprintf(out, "%s", opts->match_color); + prev = child->end; + } + if (m->end > prev) { + if (opts && opts->fprint_between) opts->fprint_between(out, prev, m->end, opts->match_color); + else fwrite(prev, sizeof(char), (size_t)(m->end - prev), out); + } + } +} + + // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/printmatch.h b/printmatch.h new file mode 100644 index 0000000..f6a894d --- /dev/null +++ b/printmatch.h @@ -0,0 +1,22 @@ +// +// Debug visualization of matches +// +#ifndef EXPLAIN__H +#define EXPLAIN__H + +#include "match.h" + +typedef struct { + const char *normal_color, *match_color, *replace_color; + void (*fprint_between)(FILE *out, const char *start, const char *end, const char *normal_color); + void (*on_nl)(FILE *out); +} print_options_t; +__attribute__((nonnull(1,2,3))) +//void fprint_match(FILE *out, const char *file_start, match_t *m, const char *colors[3]); +void fprint_match(FILE *out, const char *file_start, match_t *m, print_options_t *opts); + +__attribute__((nonnull)) +void explain_match(match_t *m); + +#endif +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0