diff options
author | David Lamparter <equinox@opensourcerouting.org> | 2016-12-05 23:03:57 +0100 |
---|---|---|
committer | David Lamparter <equinox@opensourcerouting.org> | 2017-01-23 21:52:44 +0100 |
commit | 818a5168a1c141beaa63fe887d195123a23a012e (patch) | |
tree | b6a26f8aaff28f3b3b88e174fc0dec5f901113da /lib/grammar_sandbox.c | |
parent | lib: parser: add error location reporting (diff) | |
download | frr-818a5168a1c141beaa63fe887d195123a23a012e.tar.xz frr-818a5168a1c141beaa63fe887d195123a23a012e.zip |
grammar_sandbox: add .dot file writing capability
Writing a .dot graphviz file is very useful to get a graphical
representation of the command graph.
This code is intended solely for testing & development, it uses some
fixed-size array and may unexpectedly crash.
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
Diffstat (limited to 'lib/grammar_sandbox.c')
-rw-r--r-- | lib/grammar_sandbox.c | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c index b5ac36c41..98c75ca6e 100644 --- a/lib/grammar_sandbox.c +++ b/lib/grammar_sandbox.c @@ -45,6 +45,10 @@ void grammar_sandbox_init (void); void pretty_print_graph (struct vty *vty, struct graph_node *, int, int, struct graph_node **, size_t); +static void +pretty_print_dot (FILE *ofd, unsigned opts, struct graph_node *start, + struct graph_node **stack, size_t stackpos, + struct graph_node **visited, size_t *visitpos); void init_cmdgraph (struct vty *, struct graph **); vector @@ -240,6 +244,36 @@ DEFUN (grammar_test_show, return CMD_SUCCESS; } +DEFUN (grammar_test_dot, + grammar_test_dot_cmd, + "grammar dotfile OUTNAME", + GRAMMAR_STR + "print current graph for dot\n" + ".dot filename\n") +{ + struct graph_node *stack[MAXDEPTH]; + struct graph_node *visited[MAXDEPTH*MAXDEPTH]; + size_t vpos = 0; + + if (!nodegraph) { + vty_out(vty, "nodegraph uninitialized\r\n"); + return CMD_SUCCESS; + } + FILE *ofd = fopen(argv[2]->arg, "w"); + if (!ofd) { + vty_out(vty, "%s: %s\r\n", argv[2]->arg, strerror(errno)); + return CMD_SUCCESS; + } + + fprintf(ofd, "digraph {\n graph [ rankdir = LR ];\n node [ fontname = \"Fira Mono\", fontsize = 9 ];\n\n"); + pretty_print_dot (ofd, 0, + vector_slot (nodegraph->nodes, 0), + stack, 0, visited, &vpos); + fprintf(ofd, "}\n"); + fclose(ofd); + return CMD_SUCCESS; +} + DEFUN (grammar_init_graph, grammar_init_graph_cmd, "grammar init", @@ -258,6 +292,7 @@ void grammar_sandbox_init(void) { // install all enable elements install_element (ENABLE_NODE, &grammar_test_cmd); install_element (ENABLE_NODE, &grammar_test_show_cmd); + install_element (ENABLE_NODE, &grammar_test_dot_cmd); install_element (ENABLE_NODE, &grammar_test_match_cmd); install_element (ENABLE_NODE, &grammar_test_complete_cmd); install_element (ENABLE_NODE, &grammar_test_doc_cmd); @@ -345,6 +380,83 @@ pretty_print_graph (struct vty *vty, struct graph_node *start, int level, vty_out(vty, "%s", VTY_NEWLINE); } +static void +pretty_print_dot (FILE *ofd, unsigned opts, struct graph_node *start, + struct graph_node **stack, size_t stackpos, + struct graph_node **visited, size_t *visitpos) +{ + // print this node + char tokennum[32]; + struct cmd_token *tok = start->data; + const char *color; + + for (size_t i = 0; i < (*visitpos); i++) + if (visited[i] == start) + return; + visited[(*visitpos)++] = start; + if ((*visitpos) == MAXDEPTH*MAXDEPTH) + return; + + snprintf(tokennum, sizeof(tokennum), "%d?", tok->type); + fprintf(ofd, " n%016llx [ shape=box, label=<", (unsigned long long)start); + + fprintf(ofd, "<b>%s</b>", LOOKUP_DEF(tokennames, tok->type, tokennum)); + if (tok->attr == CMD_ATTR_DEPRECATED) + fprintf(ofd, " (d)"); + else if (tok->attr == CMD_ATTR_HIDDEN) + fprintf(ofd, " (h)"); + if (tok->text) { + if (tok->type == WORD_TKN) + fprintf(ofd, "<br/>\"<font color=\"#0055ff\" point-size=\"11\"><b>%s</b></font>\"", tok->text); + else + fprintf(ofd, "<br/>%s", tok->text); + } +/* if (desc) + fprintf(ofd, " ?'%s'", tok->desc); */ + switch (tok->type) { + case START_TKN: color = "#ccffcc"; break; + case FORK_TKN: color = "#aaddff"; break; + case JOIN_TKN: color = "#ddaaff"; break; + case WORD_TKN: color = "#ffffff"; break; + default: color = "#ffffff"; break; + } + fprintf(ofd, ">, style = filled, fillcolor = \"%s\" ];\n", color); + + if (stackpos == MAXDEPTH) + return; + stack[stackpos++] = start; + + for (unsigned int i = 0; i < vector_active (start->to); i++) + { + struct graph_node *adj = vector_slot (start->to, i); + // if this node is a vararg, just print * + if (adj == start) { + fprintf(ofd, " n%016llx -> n%016llx;\n", + (unsigned long long)start, + (unsigned long long)start); + } else if (((struct cmd_token *)adj->data)->type == END_TKN) { + //struct cmd_token *et = adj->data; + fprintf(ofd, " n%016llx -> end%016llx;\n", + (unsigned long long)start, + (unsigned long long)adj); + fprintf(ofd, " end%016llx [ shape=box, label=<end>, style = filled, fillcolor = \"#ffddaa\" ];\n", + (unsigned long long)adj); + } else { + fprintf(ofd, " n%016llx -> n%016llx;\n", + (unsigned long long)start, + (unsigned long long)adj); + size_t k; + for (k = 0; k < stackpos; k++) + if (stack[k] == adj) + break; + if (k == stackpos) { + pretty_print_dot (ofd, opts, adj, stack, stackpos, visited, visitpos); + } + } + } +} + + /** stuff that should go in command.c + command.h */ void init_cmdgraph (struct vty *vty, struct graph **graph) |