Skip to content

Commit 17bf35a

Browse files
committed
grep: teach --debug option to dump the parse tree
Our "grep" allows complex boolean expressions to be formed to match each individual line with operators like --and, '(', ')' and --not. Introduce the "--debug" option to show the parse tree to help people who want to debug and enhance it. Also "log" learns "--grep-debug" option to do the same. The command line parser to the log family is a lot more limited than the general "git grep" parser, but it has special handling for header matching (e.g. "--author"), and a parse tree is valuable when working on it. Note that "--all-match" is *not* any individual node in the parse tree. It is an instruction to the evaluator to check all the nodes in the top-level backbone have matched and reject a document as non-matching otherwise. Signed-off-by: Junio C Hamano <[email protected]>
1 parent 785ee49 commit 17bf35a

File tree

4 files changed

+96
-2
lines changed

4 files changed

+96
-2
lines changed

builtin/grep.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
772772
"indicate hit with exit status without output"),
773773
OPT_BOOLEAN(0, "all-match", &opt.all_match,
774774
"show only matches from files that match all patterns"),
775+
{ OPTION_SET_INT, 0, "debug", &opt.debug, NULL,
776+
"show parse tree for grep expression",
777+
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1 },
775778
OPT_GROUP(""),
776779
{ OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
777780
"pager", "show matching files in the pager",

grep.c

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,87 @@ static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
332332
return compile_pattern_or(list);
333333
}
334334

335+
static void indent(int in)
336+
{
337+
while (in-- > 0)
338+
fputc(' ', stderr);
339+
}
340+
341+
static void dump_grep_pat(struct grep_pat *p)
342+
{
343+
switch (p->token) {
344+
case GREP_AND: fprintf(stderr, "*and*"); break;
345+
case GREP_OPEN_PAREN: fprintf(stderr, "*(*"); break;
346+
case GREP_CLOSE_PAREN: fprintf(stderr, "*)*"); break;
347+
case GREP_NOT: fprintf(stderr, "*not*"); break;
348+
case GREP_OR: fprintf(stderr, "*or*"); break;
349+
350+
case GREP_PATTERN: fprintf(stderr, "pattern"); break;
351+
case GREP_PATTERN_HEAD: fprintf(stderr, "pattern_head"); break;
352+
case GREP_PATTERN_BODY: fprintf(stderr, "pattern_body"); break;
353+
}
354+
355+
switch (p->token) {
356+
default: break;
357+
case GREP_PATTERN_HEAD:
358+
fprintf(stderr, "<head %d>", p->field); break;
359+
case GREP_PATTERN_BODY:
360+
fprintf(stderr, "<body>"); break;
361+
}
362+
switch (p->token) {
363+
default: break;
364+
case GREP_PATTERN_HEAD:
365+
case GREP_PATTERN_BODY:
366+
case GREP_PATTERN:
367+
fprintf(stderr, "%.*s", (int)p->patternlen, p->pattern);
368+
break;
369+
}
370+
fputc('\n', stderr);
371+
}
372+
373+
static void dump_grep_expression_1(struct grep_expr *x, int in)
374+
{
375+
indent(in);
376+
switch (x->node) {
377+
case GREP_NODE_TRUE:
378+
fprintf(stderr, "true\n");
379+
break;
380+
case GREP_NODE_ATOM:
381+
dump_grep_pat(x->u.atom);
382+
break;
383+
case GREP_NODE_NOT:
384+
fprintf(stderr, "(not\n");
385+
dump_grep_expression_1(x->u.unary, in+1);
386+
indent(in);
387+
fprintf(stderr, ")\n");
388+
break;
389+
case GREP_NODE_AND:
390+
fprintf(stderr, "(and\n");
391+
dump_grep_expression_1(x->u.binary.left, in+1);
392+
dump_grep_expression_1(x->u.binary.right, in+1);
393+
indent(in);
394+
fprintf(stderr, ")\n");
395+
break;
396+
case GREP_NODE_OR:
397+
fprintf(stderr, "(or\n");
398+
dump_grep_expression_1(x->u.binary.left, in+1);
399+
dump_grep_expression_1(x->u.binary.right, in+1);
400+
indent(in);
401+
fprintf(stderr, ")\n");
402+
break;
403+
}
404+
}
405+
406+
void dump_grep_expression(struct grep_opt *opt)
407+
{
408+
struct grep_expr *x = opt->pattern_expression;
409+
410+
if (opt->all_match)
411+
fprintf(stderr, "[all-match]\n");
412+
dump_grep_expression_1(x, 0);
413+
fflush(NULL);
414+
}
415+
335416
static struct grep_expr *grep_true_expr(void)
336417
{
337418
struct grep_expr *z = xcalloc(1, sizeof(*z));
@@ -395,7 +476,7 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
395476
return header_expr;
396477
}
397478

398-
void compile_grep_patterns(struct grep_opt *opt)
479+
static void compile_grep_patterns_real(struct grep_opt *opt)
399480
{
400481
struct grep_pat *p;
401482
struct grep_expr *header_expr = prep_header_patterns(opt);
@@ -415,7 +496,7 @@ void compile_grep_patterns(struct grep_opt *opt)
415496

416497
if (opt->all_match || header_expr)
417498
opt->extended = 1;
418-
else if (!opt->extended)
499+
else if (!opt->extended && !opt->debug)
419500
return;
420501

421502
p = opt->pattern_list;
@@ -435,6 +516,13 @@ void compile_grep_patterns(struct grep_opt *opt)
435516
opt->all_match = 1;
436517
}
437518

519+
void compile_grep_patterns(struct grep_opt *opt)
520+
{
521+
compile_grep_patterns_real(opt);
522+
if (opt->debug)
523+
dump_grep_expression(opt);
524+
}
525+
438526
static void free_pattern_expr(struct grep_expr *x)
439527
{
440528
switch (x->node) {

grep.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ struct grep_opt {
9090
int word_regexp;
9191
int fixed;
9292
int all_match;
93+
int debug;
9394
#define GREP_BINARY_DEFAULT 0
9495
#define GREP_BINARY_NOMATCH 1
9596
#define GREP_BINARY_TEXT 2

revision.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,6 +1578,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
15781578
} else if ((argcount = parse_long_opt("grep", argv, &optarg))) {
15791579
add_message_grep(revs, optarg);
15801580
return argcount;
1581+
} else if (!strcmp(arg, "--grep-debug")) {
1582+
revs->grep_filter.debug = 1;
15811583
} else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
15821584
revs->grep_filter.regflags |= REG_EXTENDED;
15831585
} else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {

0 commit comments

Comments
 (0)