2 * Command format string parser for CLI backend.
5 * Copyright (C) 2015 Cumulus Networks, Inc.
7 * This file is part of GNU Zebra.
9 * GNU Zebra is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2, or (at your option) any
14 * GNU Zebra is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with GNU Zebra; see the file COPYING. If not, write to the Free
21 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
26 // compile with debugging facilities
30 /* names for generated header and parser files */
31 %defines "command_parse.h"
32 %output "command_parse.c"
34 /* required external units */
37 #include "command_graph.h"
44 set_buffer_string (const char *);
47 /* functionality this unit exports */
50 parse_command_format (struct graph_node *, struct cmd_element *);
52 /* maximum length of a number, lexer will not match anything longer */
53 #define DECIMAL_STRLEN_MAX 20
56 /* valid semantic types for tokens and rules */
60 struct graph_node *node;
63 /* union types for lexed tokens */
66 %token <string> IPV4_PREFIX
68 %token <string> IPV6_PREFIX
69 %token <string> VARIABLE
71 %token <number> NUMBER
73 /* union types for parsed rules */
75 %type <node> sentence_root
76 %type <node> literal_token
77 %type <node> placeholder_token
79 %type <node> option_token
80 %type <node> option_token_seq
82 %type <node> selector_element_root
83 %type <node> selector_token
84 %type <node> selector_token_seq
87 /* bison declarations */
89 yyerror (struct cmd_element *el, struct graph_node *sn, char const *msg);
91 /* state variables for a single parser run */
92 struct graph_node *currnode, // current position in DFA
93 *seqhead; // sequence head
95 struct graph_node *optnode_start, // start node for option set
96 *optnode_end; // end node for option set
98 struct graph_node *selnode_start, // start node for selector set
99 *selnode_end; // end node for selector set
101 char *docstr_start, *docstr; // pointers to copy of command docstring
103 /* helper functions for parser */
107 static struct graph_node *
108 node_exists (struct graph_node *, struct graph_node *);
110 static struct graph_node *
111 node_replace (struct graph_node *, struct graph_node *);
114 cmp_node (struct graph_node *, struct graph_node *);
117 terminate_graph (struct graph_node *,
119 struct cmd_element *);
125 /* yyparse parameters */
126 %parse-param { struct cmd_element *element }
127 %parse-param { struct graph_node *startnode }
129 /* called automatically before yyparse */
131 /* clear state pointers */
134 selnode_start = selnode_end = NULL;
135 optnode_start = optnode_end = NULL;
137 /* set string to parse */
138 set_buffer_string (element->string);
140 /* copy docstring and keep a pointer to the copy */
141 docstr = element->doc ? XSTRDUP(MTYPE_TMP, element->doc) : NULL;
142 docstr_start = docstr;
148 sentence_root cmd_token_seq
150 // tack on the command element
151 terminate_graph (startnode, currnode, element);
154 | sentence_root cmd_token_seq '.' placeholder_token
156 if ((currnode = node_replace (currnode, $4)) != $4)
159 // adding a node as a child of itself accepts any number
160 // of the same token, which is what we want for varags
161 node_replace (currnode, currnode);
163 // tack on the command element
164 terminate_graph (startnode, currnode, element);
170 struct graph_node *root = new_node (WORD_GN);
171 root->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
172 root->doc = doc_next();
174 if ((currnode = node_replace (startnode, root)) != root)
184 if ((currnode = node_replace (currnode, $1)) != $1)
189 if ((currnode = node_replace (currnode, $1)) != $1)
192 /* selectors and options are subgraphs with start and end nodes */
195 add_node (currnode, $1);
196 currnode = selnode_end;
197 selnode_start = selnode_end = NULL;
201 add_node (currnode, $1);
202 currnode = optnode_end;
203 optnode_start = optnode_end = NULL;
209 | cmd_token_seq cmd_token
215 $$ = new_node (IPV4_GN);
216 $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
217 $$->doc = doc_next();
222 $$ = new_node (IPV4_PREFIX_GN);
223 $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
224 $$->doc = doc_next();
229 $$ = new_node (IPV6_GN);
230 $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
231 $$->doc = doc_next();
236 $$ = new_node (IPV6_PREFIX_GN);
237 $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
238 $$->doc = doc_next();
243 $$ = new_node (VARIABLE_GN);
244 $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
245 $$->doc = doc_next();
250 $$ = new_node (RANGE_GN);
251 $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
252 $$->doc = doc_next();
254 // get the numbers out
256 $$->min = strtoll (yylval.string, &yylval.string, 10);
257 strsep (&yylval.string, "-");
258 $$->max = strtoll (yylval.string, &yylval.string, 10);
261 if ($$->min >= $$->max) yyerror (element, startnode, "Invalid range.");
270 $$ = new_node (WORD_GN);
271 $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
272 $$->doc = doc_next();
277 $$ = new_node (NUMBER_GN);
278 $$->value = yylval.number;
279 $$->text = XCALLOC(MTYPE_CMD_TOKENS, DECIMAL_STRLEN_MAX+1);
280 snprintf($$->text, DECIMAL_STRLEN_MAX, "%lld", $$->value);
281 $$->doc = doc_next();
285 /* <selector|set> productions */
286 selector: '<' selector_part '>'
288 // all the graph building is done in selector_element,
289 // so just return the selector subgraph head
294 selector_part '|' selector_element
295 | selector_element '|' selector_element
298 selector_element: selector_element_root selector_token_seq
300 // if the selector start and end do not exist, create them
301 if (!selnode_start || !selnode_end) { // if one is null
302 assert(!selnode_start && !selnode_end); // both should be null
303 selnode_start = new_node (SELECTOR_GN); // diverging node
304 selnode_end = new_node (NUL_GN); // converging node
305 selnode_start->end = selnode_end; // duh
308 // add element head as a child of the selector
309 add_node (selnode_start, $1);
311 if ($2->type != NUL_GN) {
312 add_node ($1, seqhead);
313 add_node ($2, selnode_end);
316 add_node ($1, selnode_end);
322 %empty { $$ = new_node (NUL_GN); }
323 | selector_token_seq selector_token
325 // if the sequence component is NUL_GN, this is a sequence start
326 if ($1->type == NUL_GN) {
327 assert(!seqhead); // sequence head should always be null here
330 else // chain on new node
337 selector_element_root:
343 selector_element_root
347 /* [option|set] productions */
348 option: '[' option_part ']'
351 add_node (optnode_start, optnode_end);
356 option_part '|' option_element
363 if (!optnode_start || !optnode_end) {
364 assert(!optnode_start && !optnode_end);
365 optnode_start = new_node (OPTION_GN);
366 optnode_end = new_node (NUL_GN);
369 add_node (optnode_start, seqhead);
370 add_node ($1, optnode_end);
375 { $$ = seqhead = $1; }
376 | option_token_seq option_token
377 { $$ = add_node ($1, $2); }
388 parse_command_format(struct graph_node *start, struct cmd_element *cmd)
390 // set to 1 to enable parser traces
393 // parse command into DFA
399 /* parser helper functions */
402 yyerror (struct cmd_element *el, struct graph_node *sn, char const *msg)
404 zlog_err ("%s: FATAL parse error: %s", __func__, msg);
405 zlog_err ("while parsing this command definition: \n\t%s\n", el->string);
415 /* clear state pointers */
418 docstr_start = docstr = NULL;
419 selnode_start = selnode_end = NULL;
420 optnode_start = optnode_end = NULL;
424 terminate_graph (struct graph_node *startnode,
425 struct graph_node *finalnode,
426 struct cmd_element *element)
428 struct graph_node *end = new_node (END_GN);
429 end->element = element;
430 if (node_exists (finalnode, end))
431 yyerror (element, startnode, "Duplicate command.");
433 add_node (finalnode, end);
440 if (!docstr || !(piece = strsep (&docstr, "\n")))
442 return XSTRDUP(MTYPE_CMD_TOKENS, piece);
445 static struct graph_node *
446 node_exists (struct graph_node *parent, struct graph_node *child)
448 struct graph_node *p_child;
449 for (unsigned int i = 0; i < vector_active (parent->children); i++)
451 p_child = vector_slot (parent->children, i);
452 if (cmp_node (child, p_child))
458 static struct graph_node *
459 node_replace (struct graph_node *parent, struct graph_node *child)
461 struct graph_node *existing = node_exists (parent, child);
462 return existing ? existing : add_node (parent, child);
466 cmp_node (struct graph_node *first, struct graph_node *second)
469 if (first->type != second->type) return 0;
471 switch (first->type) {
474 if (first->text && second->text && strcmp (first->text, second->text))
476 else if (first->text != second->text) return 0;
479 if (first->min != second->min || first->max != second->max)
483 if (first->value != second->value) return 0;
485 /* selectors and options should be equal if their subgraphs are equal, but
486 * the graph isomorphism problem is not known to be solvable in polynomial time
487 * so we consider selectors and options inequal in all cases; ultimately this
488 * forks the graph, but the matcher can handle this regardless
493 /* end nodes are always considered equal, since each node may only
494 * have one END_GN child at a time