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 <gitster@pobox.com>
This commit is contained in:
Junio C Hamano
2012-09-13 14:21:44 -07:00
parent 785ee4960c
commit 17bf35a3c7
4 changed files with 96 additions and 2 deletions

92
grep.c
View File

@ -332,6 +332,87 @@ static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
return compile_pattern_or(list);
}
static void indent(int in)
{
while (in-- > 0)
fputc(' ', stderr);
}
static void dump_grep_pat(struct grep_pat *p)
{
switch (p->token) {
case GREP_AND: fprintf(stderr, "*and*"); break;
case GREP_OPEN_PAREN: fprintf(stderr, "*(*"); break;
case GREP_CLOSE_PAREN: fprintf(stderr, "*)*"); break;
case GREP_NOT: fprintf(stderr, "*not*"); break;
case GREP_OR: fprintf(stderr, "*or*"); break;
case GREP_PATTERN: fprintf(stderr, "pattern"); break;
case GREP_PATTERN_HEAD: fprintf(stderr, "pattern_head"); break;
case GREP_PATTERN_BODY: fprintf(stderr, "pattern_body"); break;
}
switch (p->token) {
default: break;
case GREP_PATTERN_HEAD:
fprintf(stderr, "<head %d>", p->field); break;
case GREP_PATTERN_BODY:
fprintf(stderr, "<body>"); break;
}
switch (p->token) {
default: break;
case GREP_PATTERN_HEAD:
case GREP_PATTERN_BODY:
case GREP_PATTERN:
fprintf(stderr, "%.*s", (int)p->patternlen, p->pattern);
break;
}
fputc('\n', stderr);
}
static void dump_grep_expression_1(struct grep_expr *x, int in)
{
indent(in);
switch (x->node) {
case GREP_NODE_TRUE:
fprintf(stderr, "true\n");
break;
case GREP_NODE_ATOM:
dump_grep_pat(x->u.atom);
break;
case GREP_NODE_NOT:
fprintf(stderr, "(not\n");
dump_grep_expression_1(x->u.unary, in+1);
indent(in);
fprintf(stderr, ")\n");
break;
case GREP_NODE_AND:
fprintf(stderr, "(and\n");
dump_grep_expression_1(x->u.binary.left, in+1);
dump_grep_expression_1(x->u.binary.right, in+1);
indent(in);
fprintf(stderr, ")\n");
break;
case GREP_NODE_OR:
fprintf(stderr, "(or\n");
dump_grep_expression_1(x->u.binary.left, in+1);
dump_grep_expression_1(x->u.binary.right, in+1);
indent(in);
fprintf(stderr, ")\n");
break;
}
}
void dump_grep_expression(struct grep_opt *opt)
{
struct grep_expr *x = opt->pattern_expression;
if (opt->all_match)
fprintf(stderr, "[all-match]\n");
dump_grep_expression_1(x, 0);
fflush(NULL);
}
static struct grep_expr *grep_true_expr(void)
{
struct grep_expr *z = xcalloc(1, sizeof(*z));
@ -395,7 +476,7 @@ static struct grep_expr *prep_header_patterns(struct grep_opt *opt)
return header_expr;
}
void compile_grep_patterns(struct grep_opt *opt)
static void compile_grep_patterns_real(struct grep_opt *opt)
{
struct grep_pat *p;
struct grep_expr *header_expr = prep_header_patterns(opt);
@ -415,7 +496,7 @@ void compile_grep_patterns(struct grep_opt *opt)
if (opt->all_match || header_expr)
opt->extended = 1;
else if (!opt->extended)
else if (!opt->extended && !opt->debug)
return;
p = opt->pattern_list;
@ -435,6 +516,13 @@ void compile_grep_patterns(struct grep_opt *opt)
opt->all_match = 1;
}
void compile_grep_patterns(struct grep_opt *opt)
{
compile_grep_patterns_real(opt);
if (opt->debug)
dump_grep_expression(opt);
}
static void free_pattern_expr(struct grep_expr *x)
{
switch (x->node) {