summaryrefslogtreecommitdiffstats
path: root/lib/grammar_sandbox.c
diff options
context:
space:
mode:
authorDavid Lamparter <equinox@opensourcerouting.org>2016-12-05 23:03:57 +0100
committerDavid Lamparter <equinox@opensourcerouting.org>2017-01-23 21:52:44 +0100
commit818a5168a1c141beaa63fe887d195123a23a012e (patch)
treeb6a26f8aaff28f3b3b88e174fc0dec5f901113da /lib/grammar_sandbox.c
parentlib: parser: add error location reporting (diff)
downloadfrr-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.c112
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)