git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCHv3 01/11] grep: teach --debug option to dump the parse tree

From
Michael J Gruber <git@drmicha.warpmail.net>
Date
Sep 14, 2012, 09:46 UTC
Message-ID
<eb5e7055691692e8a60adf8edbb3b8d2aab9d02b.1347615361.git.git@drmicha.warpmail.net>
In-Reply-To
<7vfw6l9x7i.fsf@alter.siamese.dyndns.org>
From: Junio C Hamano <gitster@pobox.com>

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 "--debug-grep" 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>
Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
---
 builtin/grep.c |  3 ++
 grep.c         | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 grep.h         |  1 +
 revision.c     |  2 ++
 4 files changed, 96 insertions(+), 2 deletions(-)
diff --git a/builtin/grep.c b/builtin/grep.c
index fe1726f..8aea00c 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -772,6 +772,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
 			   "indicate hit with exit status without output"),
 		OPT_BOOLEAN(0, "all-match", &opt.all_match,
 			"show only matches from files that match all patterns"),
+		{ OPTION_SET_INT, 0, "debug", &opt.debug, NULL,
+		  "show parse tree for grep expression",
+		  PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1 },
 		OPT_GROUP(""),
 		{ OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
 			"pager", "show matching files in the pager",
diff --git a/grep.c b/grep.c
index 04e3ec6..be15c47 100644
--- a/grep.c
+++ b/grep.c
@@ -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) {
diff --git a/grep.h b/grep.h
index ed7de6b..bf5be5a 100644
--- a/grep.h
+++ b/grep.h
@@ -90,6 +90,7 @@ struct grep_opt {
 	int word_regexp;
 	int fixed;
 	int all_match;
+	int debug;
 #define GREP_BINARY_DEFAULT	0
 #define GREP_BINARY_NOMATCH	1
 #define GREP_BINARY_TEXT	2
diff --git a/revision.c b/revision.c
index 9a0d9c7..90376e8 100644
--- a/revision.c
+++ b/revision.c
@@ -1578,6 +1578,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
 	} else if ((argcount = parse_long_opt("grep", argv, &optarg))) {
 		add_message_grep(revs, optarg);
 		return argcount;
+	} else if (!strcmp(arg, "--grep-debug")) {
+		revs->grep_filter.debug = 1;
 	} else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
 		revs->grep_filter.regflags |= REG_EXTENDED;
 	} else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
-- 
1.7.12.592.g41e7905
Previous: Junio C HamanoNext: Junio C Hamano
Message 23 of 43 in “rev-list/log: document logic with several limiting options”
  1. rev-list/log: document logic with several limiting optionsMichael J Gruber, Sep 11, 2012
  2. Junio C HamanoSep 11, 2012
  3. Michael J GruberSep 12, 2012
  4. Junio C HamanoSep 12, 2012
  5. Junio C HamanoSep 12, 2012
  6. 0/6 rev-list/log: document logic with several limiting optionsMichael J Gruber, Sep 13, 2012
  7. Junio C HamanoSep 13, 2012
  8. 1/6 t7810-grep: bring log --grep tests in common formMichael J Gruber, Sep 13, 2012
  9. 2/6 t7810-grep: test multiple --grep with and without --all-matchMichael J Gruber, Sep 13, 2012
  10. 3/6 t7810-grep: test multiple --author with --all-matchMichael J Gruber, Sep 13, 2012
  11. Junio C HamanoSep 13, 2012
  12. Junio C HamanoSep 13, 2012
  13. Michael J GruberSep 14, 2012
  14. Junio C HamanoSep 14, 2012
  15. 4/6 t7810-grep: test interaction of multiple --grep and --author optionsMichael J Gruber, Sep 13, 2012
  16. 5/6 t7810-grep: test --all-match with multiple --grep and --author optionsMichael J Gruber, Sep 13, 2012
  17. 6/6 rev-list/log: document logic with several limiting optionsMichael J Gruber, Sep 13, 2012
  18. Junio C HamanoSep 13, 2012
  19. Junio C HamanoSep 14, 2012
  20. Junio C HamanoSep 14, 2012
  21. 00/11 rev-list/log: document logic with several limiting optionsMichael J Gruber, Sep 14, 2012
  22. Junio C HamanoSep 14, 2012
  23. 01/11 grep: teach --debug option to dump the parse treeMichael J Gruber, Sep 14, 2012
  24. Junio C HamanoSep 14, 2012
  25. 02/11 log: name --debug-grep option like in the commit messageMichael J Gruber, Sep 14, 2012
  26. 03/11 grep: show --debug output only onceMichael J Gruber, Sep 14, 2012
  27. Junio C HamanoSep 14, 2012
  28. 04/11 log --grep/--author: honor --all-match honored for multiple --grep patternsMichael J Gruber, Sep 14, 2012
  29. 05/11 log: document use of multiple commit limiting optionsMichael J Gruber, Sep 14, 2012
  30. 06/11 fixup! log: document use of multiple commit limiting optionsMichael J Gruber, Sep 14, 2012
  31. Junio C HamanoSep 14, 2012
  32. 07/11 t7810-grep: bring log --grep tests in common formMichael J Gruber, Sep 14, 2012
  33. 08/11 t7810-grep: test multiple --grep with and without --all-matchMichael J Gruber, Sep 14, 2012
  34. 09/11 t7810-grep: test multiple --author with --all-matchMichael J Gruber, Sep 14, 2012
  35. Junio C HamanoSep 14, 2012
  36. 10/11 t7810-grep: test interaction of multiple --grep and --author optionsMichael J Gruber, Sep 14, 2012
  37. 11/11 t7810-grep: test --all-match with multiple --grep and --author optionsMichael J Gruber, Sep 14, 2012
  38. Junio C HamanoSep 14, 2012
  39. Michael J GruberSep 13, 2012
  40. Junio C HamanoSep 13, 2012
  41. Michael J GruberSep 14, 2012
  42. Junio C HamanoSep 14, 2012
  43. Michael J GruberSep 14, 2012

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.