]> git.proxmox.com Git - mirror_frr.git/blame - lib/command_parse.y
lib: parser: unify subgraphs & simplify even more
[mirror_frr.git] / lib / command_parse.y
CommitLineData
9d0662e0 1/*
1ab84bf3 2 * Command format string parser for CLI backend.
9d0662e0 3 *
1ab84bf3 4 * --
9547b5d0 5 * Copyright (C) 2016 Cumulus Networks, Inc.
9d0662e0 6 *
1ab84bf3
QY
7 * This file is part of GNU Zebra.
8 *
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
12 * later version.
13 *
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.
18 *
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
22 * 02111-1307, USA.
9d0662e0
QY
23 */
24
92055a92 25%{
1ab84bf3
QY
26// compile with debugging facilities
27#define YYDEBUG 1
51fc9379 28%}
782d9789 29
e9484f70 30%define api.pure full
0d37f9f3 31/* define api.prefix {cmd_yy} */
e9484f70 32
51fc9379
QY
33/* names for generated header and parser files */
34%defines "command_parse.h"
35%output "command_parse.c"
5a8bbed0 36
afc3a6ce
DL
37/* note: code blocks are output in order, to both .c and .h:
38 * 1. %code requires
39 * 2. %union + bison forward decls
40 * 3. %code provides
41 * command_lex.h needs to be included at 3.; it needs the union and YYSTYPE.
42 * struct parser_ctx is needed for the bison forward decls.
43 */
eceb1066 44%code requires {
460a7689
DS
45 #include "stdlib.h"
46 #include "string.h"
eceb1066 47 #include "command.h"
a9958674 48 #include "log.h"
7a6ded40 49 #include "graph.h"
51fc9379 50
afc3a6ce
DL
51 #define YYSTYPE CMD_YYSTYPE
52 struct parser_ctx;
2020b1c8
DL
53
54 /* subgraph semantic value */
55 struct subgraph {
56 struct graph_node *start, *end;
57 };
afc3a6ce
DL
58}
59
60%union {
61 long long number;
62 char *string;
63 struct graph_node *node;
2020b1c8 64 struct subgraph subgraph;
afc3a6ce
DL
65}
66
67%code provides {
68 #ifndef FLEX_SCANNER
69 #include "command_lex.h"
70 #endif
71
72 extern void set_lexer_string (yyscan_t *scn, const char *string);
73 extern void cleanup_lexer (yyscan_t *scn);
74
b07a15f8 75 struct parser_ctx {
e9484f70
DL
76 yyscan_t scanner;
77
b07a15f8
DL
78 struct cmd_element *el;
79
80 struct graph *graph;
fd19e7a2 81 struct graph_node *currnode;
b07a15f8
DL
82
83 /* pointers to copy of command docstring */
84 char *docstr_start, *docstr;
85 };
92055a92
QY
86}
87
51fc9379 88/* union types for lexed tokens */
5a5d576b
QY
89%token <string> WORD
90%token <string> IPV4
91%token <string> IPV4_PREFIX
92%token <string> IPV6
93%token <string> IPV6_PREFIX
94%token <string> VARIABLE
95%token <string> RANGE
782d9789 96
51fc9379 97/* union types for parsed rules */
782d9789 98%type <node> start
782d9789
QY
99%type <node> literal_token
100%type <node> placeholder_token
9286efab 101%type <node> simple_token
9286efab
QY
102%type <subgraph> selector
103%type <subgraph> selector_token
104%type <subgraph> selector_token_seq
105%type <subgraph> selector_seq_seq
782d9789 106
51fc9379 107%code {
e9484f70 108
51fc9379
QY
109 /* bison declarations */
110 void
e9484f70 111 cmd_yyerror (struct parser_ctx *ctx, char const *msg);
51fc9379 112
51fc9379
QY
113 /* helper functions for parser */
114 static char *
b07a15f8 115 doc_next (struct parser_ctx *ctx);
51fc9379
QY
116
117 static struct graph_node *
1eb5e8dc 118 node_adjacent (struct graph_node *, struct graph_node *);
51fc9379
QY
119
120 static struct graph_node *
1eb5e8dc 121 add_edge_dedup (struct graph_node *, struct graph_node *);
51fc9379
QY
122
123 static int
d0bfb22c 124 cmp_token (struct cmd_token *, struct cmd_token *);
1eb5e8dc
QY
125
126 static struct graph_node *
b07a15f8 127 new_token_node (struct parser_ctx *,
d0bfb22c 128 enum cmd_token_type type,
b07a15f8 129 char *text,
ce882f81 130 char *doc);
51fc9379
QY
131
132 static void
b07a15f8
DL
133 terminate_graph (struct parser_ctx *ctx,
134 struct graph_node *);
51fc9379
QY
135
136 static void
b07a15f8 137 cleanup (struct parser_ctx *ctx);
e9484f70
DL
138
139 #define scanner ctx->scanner
51fc9379
QY
140}
141
142/* yyparse parameters */
e9484f70
DL
143%lex-param {yyscan_t scanner}
144%parse-param {struct parser_ctx *ctx}
51fc9379
QY
145
146/* called automatically before yyparse */
147%initial-action {
148 /* clear state pointers */
fd19e7a2 149 ctx->currnode = vector_slot (ctx->graph->nodes, 0);
51fc9379 150
51fc9379 151 /* copy docstring and keep a pointer to the copy */
b07a15f8 152 if (ctx->el->doc)
a9958674
QY
153 {
154 // allocate a new buffer, making room for a flag
b07a15f8
DL
155 size_t length = (size_t) strlen (ctx->el->doc) + 2;
156 ctx->docstr = malloc (length);
157 memcpy (ctx->docstr, ctx->el->doc, strlen (ctx->el->doc));
a9958674 158 // set the flag so doc_next knows when to print a warning
b07a15f8 159 ctx->docstr[length - 2] = 0x03;
a9958674 160 // null terminate
b07a15f8 161 ctx->docstr[length - 1] = 0x00;
a9958674 162 }
b07a15f8 163 ctx->docstr_start = ctx->docstr;
51fc9379 164}
92055a92 165
92055a92
QY
166%%
167
88255c7c 168start:
fd19e7a2 169 cmd_token_seq
880e24a1 170{
51fc9379 171 // tack on the command element
b07a15f8 172 terminate_graph (ctx, ctx->currnode);
880e24a1 173}
fd19e7a2 174| cmd_token_seq placeholder_token '.' '.' '.'
88255c7c 175{
fd19e7a2
DL
176 if ((ctx->currnode = add_edge_dedup (ctx->currnode, $2)) != $2)
177 graph_delete_node (ctx->graph, $2);
88255c7c 178
4d94b292 179 ((struct cmd_token *)ctx->currnode->data)->allowrepeat = 1;
7d5718c1 180
1ab84bf3 181 // adding a node as a child of itself accepts any number
a9958674 182 // of the same token, which is what we want for variadics
b07a15f8 183 add_edge_dedup (ctx->currnode, ctx->currnode);
88255c7c 184
51fc9379 185 // tack on the command element
b07a15f8 186 terminate_graph (ctx, ctx->currnode);
88255c7c 187}
9286efab 188;
92055a92 189
9286efab 190cmd_token_seq:
0d4aa1b1 191 /* empty */
9286efab
QY
192| cmd_token_seq cmd_token
193;
92055a92 194
782d9789 195cmd_token:
9286efab 196 simple_token
5a5d576b 197{
b07a15f8
DL
198 if ((ctx->currnode = add_edge_dedup (ctx->currnode, $1)) != $1)
199 graph_delete_node (ctx->graph, $1);
5a5d576b 200}
663982cd 201| selector
5a5d576b 202{
2020b1c8
DL
203 graph_add_edge (ctx->currnode, $1.start);
204 ctx->currnode = $1.end;
478bdaeb 205}
9286efab
QY
206;
207
208simple_token:
209 literal_token
210| placeholder_token
211;
212
9286efab
QY
213literal_token: WORD
214{
b07a15f8 215 $$ = new_token_node (ctx, WORD_TKN, strdup($1), doc_next(ctx));
9286efab
QY
216 free ($1);
217}
782d9789
QY
218;
219
220placeholder_token:
478bdaeb 221 IPV4
4b0abf24 222{
b07a15f8 223 $$ = new_token_node (ctx, IPV4_TKN, strdup($1), doc_next(ctx));
5a5d576b 224 free ($1);
4b0abf24 225}
478bdaeb 226| IPV4_PREFIX
340a2b4a 227{
b07a15f8 228 $$ = new_token_node (ctx, IPV4_PREFIX_TKN, strdup($1), doc_next(ctx));
5a5d576b 229 free ($1);
4b0abf24 230}
478bdaeb 231| IPV6
340a2b4a 232{
b07a15f8 233 $$ = new_token_node (ctx, IPV6_TKN, strdup($1), doc_next(ctx));
5a5d576b 234 free ($1);
4b0abf24 235}
478bdaeb 236| IPV6_PREFIX
340a2b4a 237{
b07a15f8 238 $$ = new_token_node (ctx, IPV6_PREFIX_TKN, strdup($1), doc_next(ctx));
5a5d576b 239 free ($1);
4b0abf24 240}
478bdaeb 241| VARIABLE
340a2b4a 242{
b07a15f8 243 $$ = new_token_node (ctx, VARIABLE_TKN, strdup($1), doc_next(ctx));
5a5d576b 244 free ($1);
4b0abf24 245}
478bdaeb
QY
246| RANGE
247{
b07a15f8 248 $$ = new_token_node (ctx, RANGE_TKN, strdup($1), doc_next(ctx));
d0bfb22c 249 struct cmd_token *token = $$->data;
478bdaeb
QY
250
251 // get the numbers out
b3899769 252 yylval.string++;
7a6ded40 253 token->min = strtoll (yylval.string, &yylval.string, 10);
ef955a80 254 strsep (&yylval.string, "-");
7a6ded40 255 token->max = strtoll (yylval.string, &yylval.string, 10);
ef955a80
QY
256
257 // validate range
e9484f70 258 if (token->min > token->max) cmd_yyerror (ctx, "Invalid range.");
5a5d576b
QY
259
260 free ($1);
478bdaeb 261}
782d9789 262
4b0abf24 263/* <selector|set> productions */
9286efab 264selector: '<' selector_seq_seq '>'
2a23ca6e 265{
663982cd 266 $$ = $2;
2a23ca6e 267};
782d9789 268
9286efab
QY
269selector_seq_seq:
270 selector_seq_seq '|' selector_token_seq
478bdaeb 271{
663982cd 272 $$ = $1;
2020b1c8
DL
273 graph_add_edge ($$.start, $3.start);
274 graph_add_edge ($3.end, $$.end);
9286efab 275}
663982cd 276| selector_token_seq
9286efab 277{
663982cd
DL
278 $$.start = new_token_node (ctx, FORK_TKN, NULL, NULL);
279 $$.end = new_token_node (ctx, JOIN_TKN, NULL, NULL);
280 ((struct cmd_token *)$$.start->data)->forkjoin = $$.end;
281 ((struct cmd_token *)$$.end->data)->forkjoin = $$.start;
282
2020b1c8
DL
283 graph_add_edge ($$.start, $1.start);
284 graph_add_edge ($1.end, $$.end);
478bdaeb 285}
9286efab 286;
782d9789 287
7d5718c1
DL
288/* {keyword} productions */
289selector: '{' selector_seq_seq '}'
290{
663982cd
DL
291 $$ = $2;
292 graph_add_edge ($$.end, $$.start);
293 /* there is intentionally no start->end link, for two reasons:
294 * 1) this allows "at least 1 of" semantics, which are otherwise impossible
295 * 2) this would add a start->end->start loop in the graph that the current
296 * loop-avoidal fails to handle
297 * just use [{a|b}] if neccessary, that will work perfectly fine, and reason
298 * #1 is good enough to keep it this way. */
7d5718c1
DL
299};
300
301
535ef556 302selector_token:
9286efab 303 simple_token
4b0abf24 304{
2020b1c8 305 $$.start = $$.end = $1;
9286efab 306}
4d12266b 307| selector
9286efab 308;
782d9789 309
663982cd
DL
310selector_token_seq:
311 selector_token_seq selector_token
535ef556 312{
2020b1c8
DL
313 graph_add_edge ($1.end, $2.start);
314 $$.start = $1.start;
315 $$.end = $2.end;
535ef556 316}
663982cd 317| selector_token
782d9789
QY
318;
319
663982cd
DL
320/* [option] productions */
321selector: '[' selector_seq_seq ']'
9286efab 322{
663982cd
DL
323 $$ = $2;
324 graph_add_edge ($$.start, $$.end);
9286efab 325}
782d9789
QY
326;
327
92055a92 328%%
4b0abf24 329
e9484f70
DL
330#undef scanner
331
1eb5e8dc
QY
332void
333command_parse_format (struct graph *graph, struct cmd_element *cmd)
51fc9379 334{
b07a15f8
DL
335 struct parser_ctx ctx = { .graph = graph, .el = cmd };
336
1ab84bf3 337 // set to 1 to enable parser traces
51fc9379
QY
338 yydebug = 0;
339
e9484f70
DL
340 set_lexer_string (&ctx.scanner, cmd->string);
341
51fc9379 342 // parse command into DFA
e9484f70
DL
343 cmd_yyparse (&ctx);
344
345 /* cleanup lexer */
346 cleanup_lexer (&ctx.scanner);
07079d78 347
7a6ded40 348 // cleanup
b07a15f8 349 cleanup (&ctx);
51fc9379
QY
350}
351
352/* parser helper functions */
353
354void
b07a15f8 355yyerror (struct parser_ctx *ctx, char const *msg)
51fc9379 356{
1ab84bf3 357 zlog_err ("%s: FATAL parse error: %s", __func__, msg);
b07a15f8 358 zlog_err ("while parsing this command definition: \n\t%s\n", ctx->el->string);
a98d33ab 359 //exit(EXIT_FAILURE);
92055a92 360}
782d9789 361
51fc9379 362static void
b07a15f8 363cleanup (struct parser_ctx *ctx)
782d9789 364{
51fc9379 365 /* free resources */
b07a15f8 366 free (ctx->docstr_start);
478bdaeb 367
4b0abf24 368 /* clear state pointers */
b07a15f8
DL
369 ctx->currnode = NULL;
370 ctx->docstr_start = ctx->docstr = NULL;
51fc9379 371}
4b0abf24 372
51fc9379 373static void
b07a15f8 374terminate_graph (struct parser_ctx *ctx, struct graph_node *finalnode)
51fc9379 375{
1eb5e8dc
QY
376 // end of graph should look like this
377 // * -> finalnode -> END_TKN -> cmd_element
b07a15f8 378 struct cmd_element *element = ctx->el;
d8074cc0 379 struct graph_node *end_token_node =
b07a15f8 380 new_token_node (ctx,
d8074cc0 381 END_TKN,
460a7689
DS
382 strdup (CMD_CR_TEXT),
383 strdup (""));
1eb5e8dc 384 struct graph_node *end_element_node =
b5a1e9ef 385 graph_new_node (ctx->graph, element, NULL);
7a6ded40 386
1eb5e8dc 387 if (node_adjacent (finalnode, end_token_node))
e9484f70 388 cmd_yyerror (ctx, "Duplicate command.");
1eb5e8dc
QY
389
390 graph_add_edge (finalnode, end_token_node);
391 graph_add_edge (end_token_node, end_element_node);
782d9789 392}
f4b0c72e 393
51fc9379 394static char *
b07a15f8 395doc_next (struct parser_ctx *ctx)
51fc9379 396{
b07a15f8 397 const char *piece = ctx->docstr ? strsep (&ctx->docstr, "\n") : "";
a9958674
QY
398 if (*piece == 0x03)
399 {
b07a15f8 400 zlog_debug ("Ran out of docstring while parsing '%s'", ctx->el->string);
a9958674
QY
401 piece = "";
402 }
403
460a7689 404 return strdup (piece);
51fc9379
QY
405}
406
407static struct graph_node *
b07a15f8
DL
408new_token_node (struct parser_ctx *ctx, enum cmd_token_type type,
409 char *text, char *doc)
7a6ded40 410{
b07a15f8
DL
411 struct cmd_token *token = new_cmd_token (type, ctx->el->attr, text, doc);
412 return graph_new_node (ctx->graph, token, (void (*)(void *)) &del_cmd_token);
7a6ded40
QY
413}
414
415/**
1eb5e8dc 416 * Determines if there is an out edge from the first node to the second
7a6ded40
QY
417 */
418static struct graph_node *
1eb5e8dc 419node_adjacent (struct graph_node *first, struct graph_node *second)
f4b0c72e 420{
7a6ded40 421 struct graph_node *adj;
1eb5e8dc 422 for (unsigned int i = 0; i < vector_active (first->to); i++)
1ab84bf3 423 {
1eb5e8dc 424 adj = vector_slot (first->to, i);
d0bfb22c 425 struct cmd_token *ftok = adj->data,
1eb5e8dc
QY
426 *stok = second->data;
427 if (cmp_token (ftok, stok))
7a6ded40 428 return adj;
1ab84bf3 429 }
f4b0c72e
QY
430 return NULL;
431}
432
1eb5e8dc
QY
433/**
434 * Creates an edge betwen two nodes, unless there is already an edge to an
435 * equivalent node.
436 *
437 * The first node's out edges are searched to see if any of them point to a
438 * node that is equivalent to the second node. If such a node exists, it is
439 * returned. Otherwise an edge is created from the first node to the second.
440 *
441 * @param from start node for edge
442 * @param to end node for edge
443 * @return the node which the new edge points to
444 */
51fc9379 445static struct graph_node *
1eb5e8dc 446add_edge_dedup (struct graph_node *from, struct graph_node *to)
f4b0c72e 447{
1eb5e8dc 448 struct graph_node *existing = node_adjacent (from, to);
ce882f81
QY
449 if (existing)
450 {
451 struct cmd_token *ex_tok = existing->data;
452 struct cmd_token *to_tok = to->data;
453 // NORMAL takes precedence over DEPRECATED takes precedence over HIDDEN
454 ex_tok->attr = (ex_tok->attr < to_tok->attr) ? ex_tok->attr : to_tok->attr;
455 return existing;
456 }
457 else
458 return graph_add_edge (from, to);
f4b0c72e 459}
0aa2c2ff 460
1eb5e8dc
QY
461/**
462 * Compares two cmd_token's for equality,
463 *
464 * As such, this function is the working definition of token equality
465 * for parsing purposes and determines overall graph structure.
466 */
51fc9379 467static int
d0bfb22c 468cmp_token (struct cmd_token *first, struct cmd_token *second)
0aa2c2ff 469{
51fc9379
QY
470 // compare types
471 if (first->type != second->type) return 0;
472
473 switch (first->type) {
1eb5e8dc
QY
474 case WORD_TKN:
475 case VARIABLE_TKN:
e52c05cd
QY
476 if (first->text && second->text)
477 {
478 if (strcmp (first->text, second->text))
97f13f08 479 return 0;
e52c05cd 480 }
51fc9379
QY
481 else if (first->text != second->text) return 0;
482 break;
1eb5e8dc 483 case RANGE_TKN:
51fc9379
QY
484 if (first->min != second->min || first->max != second->max)
485 return 0;
486 break;
55589d30
QY
487 /* selectors and options should be equal if their subgraphs are equal,
488 * but the graph isomorphism problem is not known to be solvable in
489 * polynomial time so we consider selectors and options inequal in all
490 * cases; ultimately this forks the graph, but the matcher can handle
491 * this regardless
51fc9379 492 */
0bf5b1cb 493 case FORK_TKN:
51fc9379 494 return 0;
1eb5e8dc 495
51fc9379 496 /* end nodes are always considered equal, since each node may only
1eb5e8dc 497 * have one END_TKN child at a time
51fc9379 498 */
1eb5e8dc
QY
499 case START_TKN:
500 case END_TKN:
0bf5b1cb 501 case JOIN_TKN:
51fc9379
QY
502 default:
503 break;
504 }
505 return 1;
0aa2c2ff 506}