]> git.proxmox.com Git - mirror_frr.git/blame - lib/grammar_sandbox.c
lib: parser: add error location reporting
[mirror_frr.git] / lib / grammar_sandbox.c
CommitLineData
1ab84bf3
QY
1/*
2 * Testing shim and API examples for the new CLI backend.
3 *
4 * This unit defines a number of commands in the old engine that can
5 * be used to test and interact with the new engine.
6 *
7 * This shim should be removed upon integration. It is currently hooked in
8 * vtysh/vtysh.c. It has no header, vtysh.c merely includes this entire unit
9 * since it clutters up the makefiles less and this is only a temporary shim.
10 *
11 * --
12 * Copyright (C) 2016 Cumulus Networks, Inc.
13 *
14 * This file is part of GNU Zebra.
15 *
16 * GNU Zebra is free software; you can redistribute it and/or modify it
17 * under the terms of the GNU General Public License as published by the
18 * Free Software Foundation; either version 2, or (at your option) any
19 * later version.
20 *
21 * GNU Zebra is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with GNU Zebra; see the file COPYING. If not, write to the Free
28 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
29 * 02111-1307, USA.
30 */
31
51e156b3 32#include "command.h"
fa133e00 33#include "memory_vty.h"
1eb5e8dc 34#include "graph.h"
9d0662e0 35#include "command_match.h"
51e156b3
QY
36
37#define GRAMMAR_STR "CLI grammar sandbox\n"
38
fa133e00
DL
39DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc")
40
41#define MAXDEPTH 64
42
97c45dae 43/** headers **/
1ab84bf3 44void
1eb5e8dc 45grammar_sandbox_init (void);
1ab84bf3 46void
fa133e00 47pretty_print_graph (struct vty *vty, struct graph_node *, int, int, struct graph_node **, size_t);
500b1a5b 48void
fa133e00 49init_cmdgraph (struct vty *, struct graph **);
ee761cc0
QY
50vector
51completions_to_vec (struct list *);
52int
53compare_completions (const void *, const void *);
1ab84bf3 54
97c45dae 55/** shim interface commands **/
1eb5e8dc 56struct graph *nodegraph;
340a2b4a 57
9d0662e0
QY
58DEFUN (grammar_test,
59 grammar_test_cmd,
fa133e00 60 "grammar parse LINE...",
9d0662e0 61 GRAMMAR_STR
fa133e00 62 "parse a command\n"
9d0662e0
QY
63 "command to pass to new parser\n")
64{
58749582 65 int idx_command = 2;
ff35126c 66 // make a string from tokenized command line
58749582 67 char *command = argv_concat (argv, argc, idx_command);
1ab84bf3 68
ff35126c 69 // create cmd_element for parser
1eb5e8dc 70 struct cmd_element *cmd = XCALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_element));
500b1a5b 71 cmd->string = command;
fa133e00 72 cmd->doc = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n";
e648e61a 73 cmd->func = NULL;
51e156b3 74
1ab84bf3 75 // parse the command and install it into the command graph
55589d30 76 command_parse_format (nodegraph, cmd);
1ab84bf3 77
9d0662e0
QY
78 return CMD_SUCCESS;
79}
80
eceb1066
QY
81DEFUN (grammar_test_complete,
82 grammar_test_complete_cmd,
fa133e00 83 "grammar complete COMMAND...",
9d0662e0 84 GRAMMAR_STR
eceb1066 85 "attempt to complete input on DFA\n"
fa133e00 86 "command to complete\n")
9d0662e0 87{
58749582
DW
88 int idx_command = 2;
89 char *cmdstr = argv_concat (argv, argc, idx_command);
fa133e00
DL
90 if (!cmdstr)
91 return CMD_SUCCESS;
92
1ab84bf3
QY
93 vector command = cmd_make_strvec (cmdstr);
94
ff35126c 95 // generate completions of user input
795d0278 96 struct list *completions;
ff35126c 97 enum matcher_rv result = command_complete (nodegraph, command, &completions);
18be0e59 98
1ab84bf3 99 // print completions or relevant error message
ca90e051 100 if (!MATCHER_ERROR(result))
1ab84bf3 101 {
ee761cc0 102 vector comps = completions_to_vec (completions);
fa133e00 103 struct cmd_token *tkn;
ff35126c
QY
104
105 // calculate length of longest tkn->text in completions
ee761cc0
QY
106 unsigned int width = 0, i = 0;
107 for (i = 0; i < vector_active (comps); i++) {
108 tkn = vector_slot (comps, i);
109 unsigned int len = strlen (tkn->text);
110 width = len > width ? len : width;
ff35126c
QY
111 }
112
113 // print completions
ee761cc0
QY
114 for (i = 0; i < vector_active (comps); i++) {
115 tkn = vector_slot (comps, i);
fa133e00 116 vty_out (vty, " %-*s %s%s", width, tkn->text, tkn->desc, VTY_NEWLINE);
ee761cc0 117 }
795d0278
QY
118
119 for (i = 0; i < vector_active (comps); i++)
fa133e00 120 del_cmd_token ((struct cmd_token *) vector_slot (comps, i));
795d0278 121 vector_free (comps);
1ab84bf3 122 }
18be0e59 123 else
fa133e00 124 vty_out (vty, "%% No match%s", VTY_NEWLINE);
1ab84bf3
QY
125
126 // free resources
1eb5e8dc 127 list_delete (completions);
ff35126c 128 cmd_free_strvec (command);
1ab84bf3 129 free (cmdstr);
18be0e59 130
9d0662e0 131 return CMD_SUCCESS;
340a2b4a
QY
132}
133
eceb1066
QY
134DEFUN (grammar_test_match,
135 grammar_test_match_cmd,
fa133e00 136 "grammar match COMMAND...",
eceb1066
QY
137 GRAMMAR_STR
138 "attempt to match input on DFA\n"
fa133e00 139 "command to match\n")
eceb1066 140{
58749582 141 int idx_command = 2;
fa133e00 142 if (argv[2]->arg[0] == '#')
4427e9b3
QY
143 return CMD_SUCCESS;
144
58749582 145 char *cmdstr = argv_concat(argv, argc, idx_command);
1ab84bf3 146 vector command = cmd_make_strvec (cmdstr);
6ce82b63 147
de9d7e4f 148 struct list *argvv = NULL;
fa133e00 149 const struct cmd_element *element = NULL;
55589d30 150 enum matcher_rv result = command_match (nodegraph, command, &argvv, &element);
1ab84bf3
QY
151
152 // print completions or relevant error message
153 if (element)
154 {
fa133e00 155 vty_out (vty, "Matched: %s%s", element->string, VTY_NEWLINE);
1ab84bf3 156 struct listnode *ln;
fa133e00 157 struct cmd_token *token;
97f13f08 158 for (ALL_LIST_ELEMENTS_RO(argvv,ln,token))
fa133e00 159 vty_out (vty, "%s -- %s%s", token->text, token->arg, VTY_NEWLINE);
1eb5e8dc 160
fa133e00 161 vty_out (vty, "func: %p%s", element->func, VTY_NEWLINE);
1ab84bf3
QY
162
163 list_delete (argvv);
164 }
de9d7e4f 165 else {
1ab84bf3 166 assert(MATCHER_ERROR(result));
6ce82b63
QY
167 switch (result) {
168 case MATCHER_NO_MATCH:
fa133e00 169 vty_out (vty, "%% Unknown command%s", VTY_NEWLINE);
6ce82b63
QY
170 break;
171 case MATCHER_INCOMPLETE:
fa133e00 172 vty_out (vty, "%% Incomplete command%s", VTY_NEWLINE);
6ce82b63
QY
173 break;
174 case MATCHER_AMBIGUOUS:
fa133e00 175 vty_out (vty, "%% Ambiguous command%s", VTY_NEWLINE);
6ce82b63
QY
176 break;
177 default:
fa133e00 178 vty_out (vty, "%% Unknown error%s", VTY_NEWLINE);
6ce82b63
QY
179 break;
180 }
de9d7e4f
QY
181 }
182
1ab84bf3 183 // free resources
795d0278
QY
184 cmd_free_strvec (command);
185 free (cmdstr);
1ab84bf3 186
eceb1066
QY
187 return CMD_SUCCESS;
188}
189
1ab84bf3
QY
190/**
191 * Testing shim to test docstrings
192 */
193DEFUN (grammar_test_doc,
194 grammar_test_doc_cmd,
195 "grammar test docstring",
196 GRAMMAR_STR
197 "Test function for docstring\n"
198 "Command end\n")
199{
200 // create cmd_element with docstring
1eb5e8dc 201 struct cmd_element *cmd = XCALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_element));
ff35126c
QY
202 cmd->string = XSTRDUP (MTYPE_CMD_TOKENS, "test docstring <example|selector follow> (1-255) end VARIABLE [OPTION|set lol] . VARARG");
203 cmd->doc = XSTRDUP (MTYPE_CMD_TOKENS,
204 "Test stuff\n"
1ab84bf3
QY
205 "docstring thing\n"
206 "first example\n"
207 "second example\n"
208 "follow\n"
209 "random range\n"
210 "end thingy\n"
211 "variable\n"
212 "optional variable\n"
213 "optional set\n"
214 "optional lol\n"
ff35126c 215 "vararg!\n");
1ab84bf3 216 cmd->func = NULL;
1ab84bf3
QY
217
218 // parse element
55589d30 219 command_parse_format (nodegraph, cmd);
1ab84bf3
QY
220
221 return CMD_SUCCESS;
222}
223
224/**
225 * Debugging command to print command graph
226 */
227DEFUN (grammar_test_show,
228 grammar_test_show_cmd,
fa133e00 229 "grammar show [doc]",
1ab84bf3 230 GRAMMAR_STR
fa133e00
DL
231 "print current accumulated DFA\n"
232 "include docstrings\n")
1ab84bf3 233{
fa133e00
DL
234 struct graph_node *stack[MAXDEPTH];
235
1ab84bf3 236 if (!nodegraph)
fa133e00 237 vty_out(vty, "nodegraph uninitialized\r\n");
1ab84bf3 238 else
fa133e00 239 pretty_print_graph (vty, vector_slot (nodegraph->nodes, 0), 0, argc >= 3, stack, 0);
1ab84bf3
QY
240 return CMD_SUCCESS;
241}
51e156b3 242
500b1a5b
QY
243DEFUN (grammar_init_graph,
244 grammar_init_graph_cmd,
fa133e00 245 "grammar init",
500b1a5b
QY
246 GRAMMAR_STR
247 "(re)initialize graph\n")
248{
249 graph_delete_graph (nodegraph);
fa133e00 250 init_cmdgraph (vty, &nodegraph);
500b1a5b
QY
251 return CMD_SUCCESS;
252}
253
1ab84bf3 254/* this is called in vtysh.c to set up the testing shim */
fa133e00
DL
255void grammar_sandbox_init(void) {
256 init_cmdgraph (NULL, &nodegraph);
1eb5e8dc
QY
257
258 // install all enable elements
478bdaeb 259 install_element (ENABLE_NODE, &grammar_test_cmd);
340a2b4a 260 install_element (ENABLE_NODE, &grammar_test_show_cmd);
9d0662e0 261 install_element (ENABLE_NODE, &grammar_test_match_cmd);
eceb1066 262 install_element (ENABLE_NODE, &grammar_test_complete_cmd);
0aa2c2ff 263 install_element (ENABLE_NODE, &grammar_test_doc_cmd);
500b1a5b 264 install_element (ENABLE_NODE, &grammar_init_graph_cmd);
51e156b3 265}
1ab84bf3 266
fa133e00
DL
267#define item(x) { x, #x }
268struct message tokennames[] = {
269 item(WORD_TKN), // words
270 item(VARIABLE_TKN), // almost anything
271 item(RANGE_TKN), // integer range
272 item(IPV4_TKN), // IPV4 addresses
273 item(IPV4_PREFIX_TKN), // IPV4 network prefixes
274 item(IPV6_TKN), // IPV6 prefixes
275 item(IPV6_PREFIX_TKN), // IPV6 network prefixes
276
277 /* plumbing types */
0bf5b1cb
DL
278 item(FORK_TKN),
279 item(JOIN_TKN),
fa133e00
DL
280 item(START_TKN), // first token in line
281 item(END_TKN), // last token in line
282 { 0, NULL }
283};
284size_t tokennames_max = array_size(tokennames);
500b1a5b 285
97f13f08
QY
286/**
287 * Pretty-prints a graph, assuming it is a tree.
288 *
289 * @param start the node to take as the root
290 * @param level indent level for recursive calls, always pass 0
291 */
1ab84bf3 292void
fa133e00
DL
293pretty_print_graph (struct vty *vty, struct graph_node *start, int level,
294 int desc, struct graph_node **stack, size_t stackpos)
1ab84bf3
QY
295{
296 // print this node
fa133e00
DL
297 char tokennum[32];
298 struct cmd_token *tok = start->data;
299
300 snprintf(tokennum, sizeof(tokennum), "%d?", tok->type);
301 vty_out(vty, "%s", LOOKUP_DEF(tokennames, tok->type, tokennum));
302 if (tok->text)
303 vty_out(vty, ":\"%s\"", tok->text);
304 if (desc)
305 vty_out(vty, " ?'%s'", tok->desc);
306 vty_out(vty, " ");
307
308 if (stackpos == MAXDEPTH)
309 {
310 vty_out(vty, " -aborting! (depth limit)%s", VTY_NEWLINE);
311 return;
312 }
313 stack[stackpos++] = start;
1ab84bf3 314
fa133e00 315 int numto = desc ? 2 : vector_active (start->to);
97f13f08 316 if (numto)
1ab84bf3 317 {
97f13f08 318 if (numto > 1)
fa133e00 319 vty_out(vty, "%s", VTY_NEWLINE);
97f13f08 320 for (unsigned int i = 0; i < vector_active (start->to); i++)
b84f1d85 321 {
97f13f08
QY
322 struct graph_node *adj = vector_slot (start->to, i);
323 // if we're listing multiple children, indent!
324 if (numto > 1)
325 for (int j = 0; j < level+1; j++)
fa133e00 326 vty_out(vty, " ");
97f13f08
QY
327 // if this node is a vararg, just print *
328 if (adj == start)
fa133e00
DL
329 vty_out(vty, "*");
330 else if (((struct cmd_token *)adj->data)->type == END_TKN)
331 vty_out(vty, "--END%s", VTY_NEWLINE);
332 else {
333 size_t k;
334 for (k = 0; k < stackpos; k++)
335 if (stack[k] == adj) {
336 vty_out(vty, "<<loop@%zu %s", k, VTY_NEWLINE);
337 break;
338 }
339 if (k == stackpos)
340 pretty_print_graph (vty, adj, numto > 1 ? level+1 : level, desc, stack, stackpos);
341 }
342 }
1ab84bf3
QY
343 }
344 else
fa133e00 345 vty_out(vty, "%s", VTY_NEWLINE);
1eb5e8dc
QY
346}
347
348/** stuff that should go in command.c + command.h */
97c45dae 349void
fa133e00 350init_cmdgraph (struct vty *vty, struct graph **graph)
97c45dae
QY
351{
352 // initialize graph, add start noe
353 *graph = graph_new ();
fa133e00 354 struct cmd_token *token = new_cmd_token (START_TKN, 0, NULL, NULL);
97c45dae 355 graph_new_node (*graph, token, (void (*)(void *)) &del_cmd_token);
fa133e00
DL
356 if (vty)
357 vty_out (vty, "initialized graph%s", VTY_NEWLINE);
97c45dae
QY
358}
359
ee761cc0
QY
360int
361compare_completions (const void *fst, const void *snd)
362{
fa133e00
DL
363 struct cmd_token *first = *(struct cmd_token **) fst,
364 *secnd = *(struct cmd_token **) snd;
ee761cc0
QY
365 return strcmp (first->text, secnd->text);
366}
367
368vector
369completions_to_vec (struct list *completions)
370{
371 vector comps = vector_init (VECTOR_MIN_SIZE);
372
373 struct listnode *ln;
fa133e00 374 struct cmd_token *token;
ee761cc0
QY
375 unsigned int i, exists;
376 for (ALL_LIST_ELEMENTS_RO(completions,ln,token))
377 {
378 // linear search for token in completions vector
379 exists = 0;
380 for (i = 0; i < vector_active (comps) && !exists; i++)
381 {
fa133e00 382 struct cmd_token *curr = vector_slot (comps, i);
ee761cc0
QY
383 exists = !strcmp (curr->text, token->text) &&
384 !strcmp (curr->desc, token->desc);
385 }
386
387 if (!exists)
388 vector_set (comps, copy_cmd_token (token));
389 }
390
391 // sort completions
392 qsort (comps->index,
393 vector_active (comps),
394 sizeof (void *),
395 &compare_completions);
396
397 return comps;
398}
399
fa133e00 400static void vty_do_exit(void)
1eb5e8dc 401{
fa133e00
DL
402 printf ("\nend.\n");
403 exit (0);
1eb5e8dc
QY
404}
405
fa133e00
DL
406struct thread_master *master;
407
408int main(int argc, char **argv)
1eb5e8dc 409{
fa133e00 410 struct thread thread;
fe2e10e8 411
fa133e00 412 master = thread_master_create ();
fe2e10e8 413
fa133e00
DL
414 zlog_default = openzlog ("grammar_sandbox", ZLOG_NONE, 0,
415 LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
416 zlog_set_level (NULL, ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
417 zlog_set_level (NULL, ZLOG_DEST_STDOUT, LOG_DEBUG);
418 zlog_set_level (NULL, ZLOG_DEST_MONITOR, ZLOG_DISABLED);
1eb5e8dc 419
fa133e00
DL
420 /* Library inits. */
421 cmd_init (1);
422 host.name = strdup ("test");
423
424 vty_init (master);
425 memory_init ();
426 grammar_sandbox_init();
427
428 vty_stdio (vty_do_exit);
429
430 /* Fetch next active thread. */
431 while (thread_fetch (master, &thread))
432 thread_call (&thread);
433
434 /* Not reached. */
435 exit (0);
1ab84bf3 436}