summaryrefslogtreecommitdiff
path: root/lib/grammar_sandbox.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/grammar_sandbox.c')
-rw-r--r--lib/grammar_sandbox.c383
1 files changed, 383 insertions, 0 deletions
diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c
new file mode 100644
index 0000000000..41fee1c1cb
--- /dev/null
+++ b/lib/grammar_sandbox.c
@@ -0,0 +1,383 @@
+/*
+ * Testing shim and API examples for the new CLI backend.
+ *
+ * This unit defines a number of commands in the old engine that can
+ * be used to test and interact with the new engine.
+ *
+ * This shim should be removed upon integration. It is currently hooked in
+ * vtysh/vtysh.c. It has no header, vtysh.c merely includes this entire unit
+ * since it clutters up the makefiles less and this is only a temporary shim.
+ *
+ * --
+ * Copyright (C) 2016 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "command.h"
+#include "graph.h"
+#include "command_parse.h"
+#include "command_match.h"
+
+#define GRAMMAR_STR "CLI grammar sandbox\n"
+
+/** headers **/
+void
+grammar_sandbox_init (void);
+void
+pretty_print_graph (struct graph_node *, int);
+void
+init_cmdgraph (struct graph **);
+vector
+completions_to_vec (struct list *);
+int
+compare_completions (const void *, const void *);
+
+/** shim interface commands **/
+struct graph *nodegraph;
+
+DEFUN (grammar_test,
+ grammar_test_cmd,
+ "grammar parse .COMMAND",
+ GRAMMAR_STR
+ "command to pass to new parser\n")
+{
+ // make a string from tokenized command line
+ char *command = argv_concat (argv, argc, 0);
+
+ // create cmd_element for parser
+ struct cmd_element *cmd = XCALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_element));
+ cmd->string = command;
+ cmd->doc = NULL;
+ cmd->func = NULL;
+ cmd->tokens = vector_init (VECTOR_MIN_SIZE);
+
+ // parse the command and install it into the command graph
+ command_parse_format (nodegraph, cmd);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (grammar_test_complete,
+ grammar_test_complete_cmd,
+ "grammar complete .COMMAND",
+ GRAMMAR_STR
+ "attempt to complete input on DFA\n"
+ "command to complete")
+{
+ char *cmdstr = argv_concat (argv, argc, 0);
+ vector command = cmd_make_strvec (cmdstr);
+
+ // generate completions of user input
+ struct list *completions;
+ enum matcher_rv result = command_complete (nodegraph, command, &completions);
+
+ // print completions or relevant error message
+ if (!MATCHER_ERROR(result))
+ {
+ vector comps = completions_to_vec (completions);
+ struct cmd_token_t *tkn;
+
+ // calculate length of longest tkn->text in completions
+ unsigned int width = 0, i = 0;
+ for (i = 0; i < vector_active (comps); i++) {
+ tkn = vector_slot (comps, i);
+ unsigned int len = strlen (tkn->text);
+ width = len > width ? len : width;
+ }
+
+ // print completions
+ for (i = 0; i < vector_active (comps); i++) {
+ tkn = vector_slot (comps, i);
+ fprintf (stdout, " %-*s %s%s", width, tkn->text, tkn->desc, "\n");
+ }
+
+ for (i = 0; i < vector_active (comps); i++)
+ del_cmd_token ((struct cmd_token_t *) vector_slot (comps, i));
+ vector_free (comps);
+ }
+ else
+ fprintf (stdout, "%% No match%s", "\n");
+
+ // free resources
+ list_delete (completions);
+ cmd_free_strvec (command);
+ free (cmdstr);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (grammar_test_match,
+ grammar_test_match_cmd,
+ "grammar match .COMMAND",
+ GRAMMAR_STR
+ "attempt to match input on DFA\n"
+ "command to match")
+{
+ if (argv[0][0] == '#')
+ return CMD_SUCCESS;
+
+ char *cmdstr = argv_concat(argv, argc, 0);
+ vector command = cmd_make_strvec (cmdstr);
+
+ struct list *argvv = NULL;
+ struct cmd_element *element = NULL;
+ enum matcher_rv result = command_match (nodegraph, command, &argvv, &element);
+
+ // print completions or relevant error message
+ if (element)
+ {
+ fprintf (stdout, "Matched: %s%s", element->string, "\n");
+ struct listnode *ln;
+ struct cmd_token_t *token;
+ for (ALL_LIST_ELEMENTS_RO(argvv,ln,token))
+ fprintf (stdout, "%s -- %s%s", token->text, token->arg, "\n");
+
+ fprintf (stdout, "func: %p%s", element->func, "\n");
+
+ list_delete (argvv);
+ del_cmd_element (element);
+ }
+ else {
+ assert(MATCHER_ERROR(result));
+ switch (result) {
+ case MATCHER_NO_MATCH:
+ fprintf (stdout, "%% Unknown command%s", "\n");
+ break;
+ case MATCHER_INCOMPLETE:
+ fprintf (stdout, "%% Incomplete command%s", "\n");
+ break;
+ case MATCHER_AMBIGUOUS:
+ fprintf (stdout, "%% Ambiguous command%s", "\n");
+ break;
+ default:
+ fprintf (stdout, "%% Unknown error%s", "\n");
+ break;
+ }
+ }
+
+ // free resources
+ cmd_free_strvec (command);
+ free (cmdstr);
+
+ return CMD_SUCCESS;
+}
+
+/**
+ * Testing shim to test docstrings
+ */
+DEFUN (grammar_test_doc,
+ grammar_test_doc_cmd,
+ "grammar test docstring",
+ GRAMMAR_STR
+ "Test function for docstring\n"
+ "Command end\n")
+{
+ // create cmd_element with docstring
+ struct cmd_element *cmd = XCALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_element));
+ cmd->string = XSTRDUP (MTYPE_CMD_TOKENS, "test docstring <example|selector follow> (1-255) end VARIABLE [OPTION|set lol] . VARARG");
+ cmd->doc = XSTRDUP (MTYPE_CMD_TOKENS,
+ "Test stuff\n"
+ "docstring thing\n"
+ "first example\n"
+ "second example\n"
+ "follow\n"
+ "random range\n"
+ "end thingy\n"
+ "variable\n"
+ "optional variable\n"
+ "optional set\n"
+ "optional lol\n"
+ "vararg!\n");
+ cmd->func = NULL;
+ cmd->tokens = vector_init (VECTOR_MIN_SIZE);
+
+ // parse element
+ command_parse_format (nodegraph, cmd);
+
+ return CMD_SUCCESS;
+}
+
+/**
+ * Debugging command to print command graph
+ */
+DEFUN (grammar_test_show,
+ grammar_test_show_cmd,
+ "grammar show graph",
+ GRAMMAR_STR
+ "print current accumulated DFA\n")
+{
+ if (!nodegraph)
+ zlog_info("nodegraph uninitialized");
+ else
+ pretty_print_graph (vector_slot (nodegraph->nodes, 0), 0);
+ return CMD_SUCCESS;
+}
+
+DEFUN (grammar_init_graph,
+ grammar_init_graph_cmd,
+ "grammar init graph",
+ GRAMMAR_STR
+ "(re)initialize graph\n")
+{
+ graph_delete_graph (nodegraph);
+ init_cmdgraph (&nodegraph);
+ return CMD_SUCCESS;
+}
+
+/* this is called in vtysh.c to set up the testing shim */
+void grammar_sandbox_init() {
+ init_cmdgraph (&nodegraph);
+
+ // 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_match_cmd);
+ install_element (ENABLE_NODE, &grammar_test_complete_cmd);
+ install_element (ENABLE_NODE, &grammar_test_doc_cmd);
+ install_element (ENABLE_NODE, &grammar_init_graph_cmd);
+}
+
+
+/**
+ * Pretty-prints a graph, assuming it is a tree.
+ *
+ * @param start the node to take as the root
+ * @param level indent level for recursive calls, always pass 0
+ */
+void
+pretty_print_graph (struct graph_node *start, int level)
+{
+ // print this node
+ struct cmd_token_t *tok = start->data;
+ fprintf (stdout, "%s[%d] ", tok->text, tok->type);
+
+ int numto = vector_active (start->to);
+ if (numto)
+ {
+ if (numto > 1)
+ fprintf (stdout, "\n");
+ for (unsigned int i = 0; i < vector_active (start->to); i++)
+ {
+ struct graph_node *adj = vector_slot (start->to, i);
+ // if we're listing multiple children, indent!
+ if (numto > 1)
+ for (int j = 0; j < level+1; j++)
+ fprintf (stdout, " ");
+ // if this node is a vararg, just print *
+ if (adj == start)
+ fprintf (stdout, "*");
+ else
+ pretty_print_graph (adj, numto > 1 ? level+1 : level);
+ }
+ }
+ else
+ fprintf(stdout, "\n");
+}
+
+/** stuff that should go in command.c + command.h */
+void
+init_cmdgraph (struct graph **graph)
+{
+ // initialize graph, add start noe
+ *graph = graph_new ();
+ struct cmd_token_t *token = new_cmd_token (START_TKN, NULL, NULL);
+ graph_new_node (*graph, token, (void (*)(void *)) &del_cmd_token);
+ fprintf (stdout, "initialized graph\n");
+}
+
+int
+compare_completions (const void *fst, const void *snd)
+{
+ struct cmd_token_t *first = *(struct cmd_token_t **) fst,
+ *secnd = *(struct cmd_token_t **) snd;
+ return strcmp (first->text, secnd->text);
+}
+
+vector
+completions_to_vec (struct list *completions)
+{
+ vector comps = vector_init (VECTOR_MIN_SIZE);
+
+ struct listnode *ln;
+ struct cmd_token_t *token;
+ unsigned int i, exists;
+ for (ALL_LIST_ELEMENTS_RO(completions,ln,token))
+ {
+ // linear search for token in completions vector
+ exists = 0;
+ for (i = 0; i < vector_active (comps) && !exists; i++)
+ {
+ struct cmd_token_t *curr = vector_slot (comps, i);
+ exists = !strcmp (curr->text, token->text) &&
+ !strcmp (curr->desc, token->desc);
+ }
+
+ if (!exists)
+ vector_set (comps, copy_cmd_token (token));
+ }
+
+ // sort completions
+ qsort (comps->index,
+ vector_active (comps),
+ sizeof (void *),
+ &compare_completions);
+
+ return comps;
+}
+
+struct cmd_token_t *
+new_cmd_token (enum cmd_token_type_t type, char *text, char *desc)
+{
+ struct cmd_token_t *token = XMALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_token_t));
+ token->type = type;
+ token->text = text;
+ token->desc = desc;
+ token->arg = NULL;
+
+ return token;
+}
+
+void
+del_cmd_token (struct cmd_token_t *token)
+{
+ if (!token) return;
+
+ if (token->text)
+ XFREE (MTYPE_CMD_TOKENS, token->text);
+ if (token->desc)
+ XFREE (MTYPE_CMD_TOKENS, token->desc);
+ if (token->arg)
+ XFREE (MTYPE_CMD_TOKENS, token->arg);
+
+ XFREE (MTYPE_CMD_TOKENS, token);
+}
+
+struct cmd_token_t *
+copy_cmd_token (struct cmd_token_t *token)
+{
+ struct cmd_token_t *copy = new_cmd_token (token->type, NULL, NULL);
+ copy->value = token->value;
+ copy->max = token->max;
+ copy->min = token->min;
+ copy->text = token->text ? XSTRDUP (MTYPE_CMD_TOKENS, token->text) : NULL;
+ copy->desc = token->desc ? XSTRDUP (MTYPE_CMD_TOKENS, token->desc) : NULL;
+ copy->arg = token->arg ? XSTRDUP (MTYPE_CMD_TOKENS, token->arg) : NULL;
+
+ return copy;
+}