Moving print logic out of match.c and renaming explain -> printmatch

This commit is contained in:
Bruce Hill 2022-04-09 14:15:07 -04:00
parent c51a91c470
commit 326a0b960c
8 changed files with 116 additions and 113 deletions

View File

@ -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

View File

@ -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.

2
bp.c
View File

@ -20,11 +20,11 @@
#include <sys/wait.h>
#include <unistd.h>
#include "explain.h"
#include "files.h"
#include "json.h"
#include "match.h"
#include "pattern.h"
#include "printmatch.h"
#include "utils.h"
#ifndef BP_NAME

View File

@ -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

87
match.c
View File

@ -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

View File

@ -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

View File

@ -1,11 +1,13 @@
//
// explain.c - Debug visualization of pattern matches.
// printmatch.c - Debug visualization of pattern matches.
//
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#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

22
printmatch.h Normal file
View File

@ -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