2 * Command format string parser for CLI backend.
5 * Copyright (C) 2016 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
27 typedef union CMD_YYSTYPE CMD_YYSTYPE;
28 #define YYSTYPE CMD_YYSTYPE
29 #include "command_lex.h"
31 // compile with debugging facilities
36 %define api.prefix {cmd_yy}
38 /* names for generated header and parser files */
39 %defines "command_parse.h"
40 %output "command_parse.c"
42 /* required external units */
53 struct cmd_element *el;
56 struct graph_node *currnode, *startnode;
58 /* pointers to copy of command docstring */
59 char *docstr_start, *docstr;
62 extern void set_lexer_string (yyscan_t *scn, const char *string);
63 extern void cleanup_lexer (yyscan_t *scn);
66 /* functionality this unit exports */
68 /* maximum length of a number, lexer will not match anything longer */
69 #define DECIMAL_STRLEN_MAX 20
72 /* valid semantic types for tokens and rules */
76 struct graph_node *node;
77 struct subgraph *subgraph;
80 /* union types for lexed tokens */
83 %token <string> IPV4_PREFIX
85 %token <string> IPV6_PREFIX
86 %token <string> VARIABLE
89 /* union types for parsed rules */
91 %type <node> sentence_root
92 %type <node> literal_token
93 %type <node> placeholder_token
94 %type <node> simple_token
95 %type <subgraph> option
96 %type <subgraph> option_token
97 %type <subgraph> option_token_seq
98 %type <subgraph> selector
99 %type <subgraph> selector_token
100 %type <subgraph> selector_token_seq
101 %type <subgraph> selector_seq_seq
102 %type <subgraph> compound_token
106 /* bison declarations */
108 cmd_yyerror (struct parser_ctx *ctx, char const *msg);
110 /* subgraph semantic value */
112 struct graph_node *start, *end;
115 /* helper functions for parser */
117 doc_next (struct parser_ctx *ctx);
119 static struct graph_node *
120 node_adjacent (struct graph_node *, struct graph_node *);
122 static struct graph_node *
123 add_edge_dedup (struct graph_node *, struct graph_node *);
126 cmp_token (struct cmd_token *, struct cmd_token *);
128 static struct graph_node *
129 new_token_node (struct parser_ctx *,
130 enum cmd_token_type type,
135 terminate_graph (struct parser_ctx *ctx,
136 struct graph_node *);
139 cleanup (struct parser_ctx *ctx);
141 #define scanner ctx->scanner
144 /* yyparse parameters */
145 %lex-param {yyscan_t scanner}
146 %parse-param {struct parser_ctx *ctx}
148 /* called automatically before yyparse */
150 /* clear state pointers */
151 ctx->currnode = ctx->startnode = NULL;
153 ctx->startnode = vector_slot (ctx->graph->nodes, 0);
155 /* copy docstring and keep a pointer to the copy */
158 // allocate a new buffer, making room for a flag
159 size_t length = (size_t) strlen (ctx->el->doc) + 2;
160 ctx->docstr = malloc (length);
161 memcpy (ctx->docstr, ctx->el->doc, strlen (ctx->el->doc));
162 // set the flag so doc_next knows when to print a warning
163 ctx->docstr[length - 2] = 0x03;
165 ctx->docstr[length - 1] = 0x00;
167 ctx->docstr_start = ctx->docstr;
173 sentence_root cmd_token_seq
175 // tack on the command element
176 terminate_graph (ctx, ctx->currnode);
178 | sentence_root cmd_token_seq placeholder_token '.' '.' '.'
180 if ((ctx->currnode = add_edge_dedup (ctx->currnode, $3)) != $3)
181 graph_delete_node (ctx->graph, $3);
183 // adding a node as a child of itself accepts any number
184 // of the same token, which is what we want for variadics
185 add_edge_dedup (ctx->currnode, ctx->currnode);
187 // tack on the command element
188 terminate_graph (ctx, ctx->currnode);
194 struct graph_node *root =
195 new_token_node (ctx, WORD_TKN, strdup ($1), doc_next(ctx));
197 if ((ctx->currnode = add_edge_dedup (ctx->startnode, root)) != root)
198 graph_delete_node (ctx->graph, root);
207 | cmd_token_seq cmd_token
213 if ((ctx->currnode = add_edge_dedup (ctx->currnode, $1)) != $1)
214 graph_delete_node (ctx->graph, $1);
218 graph_add_edge (ctx->currnode, $1->start);
219 ctx->currnode = $1->end;
236 $$ = new_token_node (ctx, WORD_TKN, strdup($1), doc_next(ctx));
244 $$ = new_token_node (ctx, IPV4_TKN, strdup($1), doc_next(ctx));
249 $$ = new_token_node (ctx, IPV4_PREFIX_TKN, strdup($1), doc_next(ctx));
254 $$ = new_token_node (ctx, IPV6_TKN, strdup($1), doc_next(ctx));
259 $$ = new_token_node (ctx, IPV6_PREFIX_TKN, strdup($1), doc_next(ctx));
264 $$ = new_token_node (ctx, VARIABLE_TKN, strdup($1), doc_next(ctx));
269 $$ = new_token_node (ctx, RANGE_TKN, strdup($1), doc_next(ctx));
270 struct cmd_token *token = $$->data;
272 // get the numbers out
274 token->min = strtoll (yylval.string, &yylval.string, 10);
275 strsep (&yylval.string, "-");
276 token->max = strtoll (yylval.string, &yylval.string, 10);
279 if (token->min > token->max) cmd_yyerror (ctx, "Invalid range.");
284 /* <selector|set> productions */
285 selector: '<' selector_seq_seq '>'
287 $$ = malloc (sizeof (struct subgraph));
288 $$->start = new_token_node (ctx, SELECTOR_TKN, NULL, NULL);
289 $$->end = new_token_node (ctx, NUL_TKN, NULL, NULL);
290 for (unsigned int i = 0; i < vector_active ($2->start->to); i++)
292 struct graph_node *sn = vector_slot ($2->start->to, i),
293 *en = vector_slot ($2->end->from, i);
294 graph_add_edge ($$->start, sn);
295 graph_add_edge (en, $$->end);
297 graph_delete_node (ctx->graph, $2->start);
298 graph_delete_node (ctx->graph, $2->end);
303 selector_seq_seq '|' selector_token_seq
305 $$ = malloc (sizeof (struct subgraph));
306 $$->start = graph_new_node (ctx->graph, NULL, NULL);
307 $$->end = graph_new_node (ctx->graph, NULL, NULL);
309 // link in last sequence
310 graph_add_edge ($$->start, $3->start);
311 graph_add_edge ($3->end, $$->end);
313 for (unsigned int i = 0; i < vector_active ($1->start->to); i++)
315 struct graph_node *sn = vector_slot ($1->start->to, i),
316 *en = vector_slot ($1->end->from, i);
317 graph_add_edge ($$->start, sn);
318 graph_add_edge (en, $$->end);
320 graph_delete_node (ctx->graph, $1->start);
321 graph_delete_node (ctx->graph, $1->end);
325 | selector_token_seq '|' selector_token_seq
327 $$ = malloc (sizeof (struct subgraph));
328 $$->start = graph_new_node (ctx->graph, NULL, NULL);
329 $$->end = graph_new_node (ctx->graph, NULL, NULL);
330 graph_add_edge ($$->start, $1->start);
331 graph_add_edge ($1->end, $$->end);
332 graph_add_edge ($$->start, $3->start);
333 graph_add_edge ($3->end, $$->end);
342 $$ = malloc (sizeof (struct subgraph));
343 $$->start = $$->end = $1;
345 | selector_token_seq selector_token
347 $$ = malloc (sizeof (struct subgraph));
348 graph_add_edge ($1->end, $2->start);
349 $$->start = $1->start;
359 $$ = malloc (sizeof (struct subgraph));
360 $$->start = $$->end = $1;
366 /* [option] productions */
367 option: '[' option_token_seq ']'
370 $$ = malloc (sizeof (struct subgraph));
371 $$->start = new_token_node (ctx, OPTION_TKN, NULL, NULL);
372 $$->end = new_token_node (ctx, NUL_TKN, NULL, NULL);
373 // add a path through the sequence to the end
374 graph_add_edge ($$->start, $2->start);
375 graph_add_edge ($2->end, $$->end);
376 // add a path directly from the start to the end
377 graph_add_edge ($$->start, $$->end);
384 | option_token_seq option_token
386 $$ = malloc (sizeof (struct subgraph));
387 graph_add_edge ($1->end, $2->start);
388 $$->start = $1->start;
398 $$ = malloc (sizeof (struct subgraph));
399 $$->start = $$->end = $1;
409 command_parse_format (struct graph *graph, struct cmd_element *cmd)
411 struct parser_ctx ctx = { .graph = graph, .el = cmd };
413 // set to 1 to enable parser traces
416 set_lexer_string (&ctx.scanner, cmd->string);
418 // parse command into DFA
422 cleanup_lexer (&ctx.scanner);
428 /* parser helper functions */
431 yyerror (struct parser_ctx *ctx, char const *msg)
433 zlog_err ("%s: FATAL parse error: %s", __func__, msg);
434 zlog_err ("while parsing this command definition: \n\t%s\n", ctx->el->string);
435 //exit(EXIT_FAILURE);
439 cleanup (struct parser_ctx *ctx)
442 free (ctx->docstr_start);
444 /* clear state pointers */
445 ctx->currnode = NULL;
446 ctx->docstr_start = ctx->docstr = NULL;
450 terminate_graph (struct parser_ctx *ctx, struct graph_node *finalnode)
452 // end of graph should look like this
453 // * -> finalnode -> END_TKN -> cmd_element
454 struct cmd_element *element = ctx->el;
455 struct graph_node *end_token_node =
458 strdup (CMD_CR_TEXT),
460 struct graph_node *end_element_node =
461 graph_new_node (ctx->graph, element, NULL);
463 if (node_adjacent (finalnode, end_token_node))
464 cmd_yyerror (ctx, "Duplicate command.");
466 graph_add_edge (finalnode, end_token_node);
467 graph_add_edge (end_token_node, end_element_node);
471 doc_next (struct parser_ctx *ctx)
473 const char *piece = ctx->docstr ? strsep (&ctx->docstr, "\n") : "";
476 zlog_debug ("Ran out of docstring while parsing '%s'", ctx->el->string);
480 return strdup (piece);
483 static struct graph_node *
484 new_token_node (struct parser_ctx *ctx, enum cmd_token_type type,
485 char *text, char *doc)
487 struct cmd_token *token = new_cmd_token (type, ctx->el->attr, text, doc);
488 return graph_new_node (ctx->graph, token, (void (*)(void *)) &del_cmd_token);
492 * Determines if there is an out edge from the first node to the second
494 static struct graph_node *
495 node_adjacent (struct graph_node *first, struct graph_node *second)
497 struct graph_node *adj;
498 for (unsigned int i = 0; i < vector_active (first->to); i++)
500 adj = vector_slot (first->to, i);
501 struct cmd_token *ftok = adj->data,
502 *stok = second->data;
503 if (cmp_token (ftok, stok))
510 * Creates an edge betwen two nodes, unless there is already an edge to an
513 * The first node's out edges are searched to see if any of them point to a
514 * node that is equivalent to the second node. If such a node exists, it is
515 * returned. Otherwise an edge is created from the first node to the second.
517 * @param from start node for edge
518 * @param to end node for edge
519 * @return the node which the new edge points to
521 static struct graph_node *
522 add_edge_dedup (struct graph_node *from, struct graph_node *to)
524 struct graph_node *existing = node_adjacent (from, to);
527 struct cmd_token *ex_tok = existing->data;
528 struct cmd_token *to_tok = to->data;
529 // NORMAL takes precedence over DEPRECATED takes precedence over HIDDEN
530 ex_tok->attr = (ex_tok->attr < to_tok->attr) ? ex_tok->attr : to_tok->attr;
534 return graph_add_edge (from, to);
538 * Compares two cmd_token's for equality,
540 * As such, this function is the working definition of token equality
541 * for parsing purposes and determines overall graph structure.
544 cmp_token (struct cmd_token *first, struct cmd_token *second)
547 if (first->type != second->type) return 0;
549 switch (first->type) {
552 if (first->text && second->text)
554 if (strcmp (first->text, second->text))
557 else if (first->text != second->text) return 0;
560 if (first->min != second->min || first->max != second->max)
563 /* selectors and options should be equal if their subgraphs are equal,
564 * but the graph isomorphism problem is not known to be solvable in
565 * polynomial time so we consider selectors and options inequal in all
566 * cases; ultimately this forks the graph, but the matcher can handle
573 /* end nodes are always considered equal, since each node may only
574 * have one END_TKN child at a time