]> git.proxmox.com Git - mirror_frr.git/blame - lib/command_parse.y
Merge pull request #12708 from donaldsharp/no_notification
[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
8bb647a8 30%locations
05dbb7df 31/* define parse.error verbose */
e9484f70 32%define api.pure full
0d37f9f3 33/* define api.prefix {cmd_yy} */
e9484f70 34
51fc9379 35/* names for generated header and parser files */
4a121f99
DL
36%defines "lib/command_parse.h"
37%output "lib/command_parse.c"
5a8bbed0 38
afc3a6ce
DL
39/* note: code blocks are output in order, to both .c and .h:
40 * 1. %code requires
41 * 2. %union + bison forward decls
42 * 3. %code provides
43 * command_lex.h needs to be included at 3.; it needs the union and YYSTYPE.
44 * struct parser_ctx is needed for the bison forward decls.
45 */
eceb1066 46%code requires {
92e50261
DL
47 #include "config.h"
48
eb44e1aa 49 #include <stdbool.h>
5894e76d
DL
50 #include <stdlib.h>
51 #include <string.h>
52 #include <ctype.h>
53
54 #include "command_graph.h"
a9958674 55 #include "log.h"
51fc9379 56
bf8d3d6a 57 DECLARE_MTYPE(LEX);
55b7f20f 58
afc3a6ce 59 #define YYSTYPE CMD_YYSTYPE
8bb647a8 60 #define YYLTYPE CMD_YYLTYPE
afc3a6ce 61 struct parser_ctx;
2020b1c8
DL
62
63 /* subgraph semantic value */
64 struct subgraph {
65 struct graph_node *start, *end;
66 };
afc3a6ce
DL
67}
68
69%union {
70 long long number;
71 char *string;
72 struct graph_node *node;
2020b1c8 73 struct subgraph subgraph;
afc3a6ce
DL
74}
75
76%code provides {
77 #ifndef FLEX_SCANNER
09781197 78 #include "lib/command_lex.h"
afc3a6ce
DL
79 #endif
80
81 extern void set_lexer_string (yyscan_t *scn, const char *string);
82 extern void cleanup_lexer (yyscan_t *scn);
83
b07a15f8 84 struct parser_ctx {
e9484f70
DL
85 yyscan_t scanner;
86
154e9ca1 87 const struct cmd_element *el;
b07a15f8
DL
88
89 struct graph *graph;
fd19e7a2 90 struct graph_node *currnode;
b07a15f8
DL
91
92 /* pointers to copy of command docstring */
93 char *docstr_start, *docstr;
94 };
92055a92
QY
95}
96
51fc9379 97/* union types for lexed tokens */
5a5d576b
QY
98%token <string> WORD
99%token <string> IPV4
100%token <string> IPV4_PREFIX
101%token <string> IPV6
102%token <string> IPV6_PREFIX
103%token <string> VARIABLE
104%token <string> RANGE
9779e3f1
QY
105%token <string> MAC
106%token <string> MAC_PREFIX
782d9789 107
90c8406c
DL
108/* special syntax, value is irrelevant */
109%token <string> EXCL_BRACKET
110
51fc9379 111/* union types for parsed rules */
782d9789 112%type <node> start
782d9789
QY
113%type <node> literal_token
114%type <node> placeholder_token
16705ecc 115%type <node> placeholder_token_real
9286efab 116%type <node> simple_token
9286efab
QY
117%type <subgraph> selector
118%type <subgraph> selector_token
119%type <subgraph> selector_token_seq
120%type <subgraph> selector_seq_seq
782d9789 121
16705ecc
DL
122%type <string> varname_token
123
51fc9379 124%code {
e9484f70 125
51fc9379
QY
126 /* bison declarations */
127 void
8bb647a8 128 cmd_yyerror (CMD_YYLTYPE *locp, struct parser_ctx *ctx, char const *msg);
1eb5e8dc 129
51fc9379 130 /* helper functions for parser */
83364d20 131 static const char *
b07a15f8 132 doc_next (struct parser_ctx *ctx);
51fc9379 133
1eb5e8dc 134 static struct graph_node *
b07a15f8 135 new_token_node (struct parser_ctx *,
d0bfb22c 136 enum cmd_token_type type,
83364d20
DL
137 const char *text,
138 const char *doc);
51fc9379
QY
139
140 static void
8bb647a8 141 terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx,
b07a15f8 142 struct graph_node *);
51fc9379
QY
143
144 static void
b07a15f8 145 cleanup (struct parser_ctx *ctx);
e9484f70 146
eb44e1aa
DL
147 static void loopcheck(struct parser_ctx *ctx, struct subgraph *sg);
148
e9484f70 149 #define scanner ctx->scanner
51fc9379
QY
150}
151
152/* yyparse parameters */
e9484f70
DL
153%lex-param {yyscan_t scanner}
154%parse-param {struct parser_ctx *ctx}
51fc9379
QY
155
156/* called automatically before yyparse */
157%initial-action {
158 /* clear state pointers */
fd19e7a2 159 ctx->currnode = vector_slot (ctx->graph->nodes, 0);
51fc9379 160
51fc9379 161 /* copy docstring and keep a pointer to the copy */
b07a15f8 162 if (ctx->el->doc)
a9958674
QY
163 {
164 // allocate a new buffer, making room for a flag
b07a15f8
DL
165 size_t length = (size_t) strlen (ctx->el->doc) + 2;
166 ctx->docstr = malloc (length);
167 memcpy (ctx->docstr, ctx->el->doc, strlen (ctx->el->doc));
a9958674 168 // set the flag so doc_next knows when to print a warning
b07a15f8 169 ctx->docstr[length - 2] = 0x03;
a9958674 170 // null terminate
b07a15f8 171 ctx->docstr[length - 1] = 0x00;
a9958674 172 }
b07a15f8 173 ctx->docstr_start = ctx->docstr;
51fc9379 174}
92055a92 175
92055a92
QY
176%%
177
88255c7c 178start:
fd19e7a2 179 cmd_token_seq
880e24a1 180{
51fc9379 181 // tack on the command element
8bb647a8 182 terminate_graph (&@1, ctx, ctx->currnode);
880e24a1 183}
fd19e7a2 184| cmd_token_seq placeholder_token '.' '.' '.'
88255c7c 185{
6432969d 186 if ((ctx->currnode = graph_add_edge (ctx->currnode, $2)) != $2)
fd19e7a2 187 graph_delete_node (ctx->graph, $2);
88255c7c 188
4d94b292 189 ((struct cmd_token *)ctx->currnode->data)->allowrepeat = 1;
7d5718c1 190
1ab84bf3 191 // adding a node as a child of itself accepts any number
a9958674 192 // of the same token, which is what we want for variadics
6432969d 193 graph_add_edge (ctx->currnode, ctx->currnode);
88255c7c 194
51fc9379 195 // tack on the command element
8bb647a8 196 terminate_graph (&@1, ctx, ctx->currnode);
88255c7c 197}
9286efab 198;
92055a92 199
16705ecc
DL
200varname_token: '$' WORD
201{
4d85f868 202 $$ = $2;
16705ecc
DL
203}
204| /* empty */
205{
206 $$ = NULL;
207}
208;
209
9286efab 210cmd_token_seq:
0d4aa1b1 211 /* empty */
9286efab
QY
212| cmd_token_seq cmd_token
213;
92055a92 214
782d9789 215cmd_token:
9286efab 216 simple_token
5a5d576b 217{
6432969d 218 if ((ctx->currnode = graph_add_edge (ctx->currnode, $1)) != $1)
b07a15f8 219 graph_delete_node (ctx->graph, $1);
8005767b 220 cmd_token_varname_seqappend($1);
5a5d576b 221}
663982cd 222| selector
5a5d576b 223{
2020b1c8 224 graph_add_edge (ctx->currnode, $1.start);
8005767b 225 cmd_token_varname_seqappend($1.start);
2020b1c8 226 ctx->currnode = $1.end;
478bdaeb 227}
9286efab
QY
228;
229
230simple_token:
231 literal_token
232| placeholder_token
233;
234
16705ecc 235literal_token: WORD varname_token
9286efab 236{
83364d20 237 $$ = new_token_node (ctx, WORD_TKN, $1, doc_next(ctx));
5894e76d 238 cmd_token_varname_set ($$->data, $2);
16705ecc 239 XFREE (MTYPE_LEX, $2);
55b7f20f 240 XFREE (MTYPE_LEX, $1);
9286efab 241}
782d9789
QY
242;
243
16705ecc 244placeholder_token_real:
478bdaeb 245 IPV4
4b0abf24 246{
83364d20 247 $$ = new_token_node (ctx, IPV4_TKN, $1, doc_next(ctx));
55b7f20f 248 XFREE (MTYPE_LEX, $1);
4b0abf24 249}
478bdaeb 250| IPV4_PREFIX
340a2b4a 251{
83364d20 252 $$ = new_token_node (ctx, IPV4_PREFIX_TKN, $1, doc_next(ctx));
55b7f20f 253 XFREE (MTYPE_LEX, $1);
4b0abf24 254}
478bdaeb 255| IPV6
340a2b4a 256{
83364d20 257 $$ = new_token_node (ctx, IPV6_TKN, $1, doc_next(ctx));
55b7f20f 258 XFREE (MTYPE_LEX, $1);
4b0abf24 259}
478bdaeb 260| IPV6_PREFIX
340a2b4a 261{
83364d20 262 $$ = new_token_node (ctx, IPV6_PREFIX_TKN, $1, doc_next(ctx));
55b7f20f 263 XFREE (MTYPE_LEX, $1);
4b0abf24 264}
478bdaeb 265| VARIABLE
340a2b4a 266{
83364d20 267 $$ = new_token_node (ctx, VARIABLE_TKN, $1, doc_next(ctx));
55b7f20f 268 XFREE (MTYPE_LEX, $1);
4b0abf24 269}
478bdaeb
QY
270| RANGE
271{
83364d20 272 $$ = new_token_node (ctx, RANGE_TKN, $1, doc_next(ctx));
d0bfb22c 273 struct cmd_token *token = $$->data;
478bdaeb
QY
274
275 // get the numbers out
b3899769 276 yylval.string++;
7a6ded40 277 token->min = strtoll (yylval.string, &yylval.string, 10);
ef955a80 278 strsep (&yylval.string, "-");
7a6ded40 279 token->max = strtoll (yylval.string, &yylval.string, 10);
ef955a80
QY
280
281 // validate range
8bb647a8 282 if (token->min > token->max) cmd_yyerror (&@1, ctx, "Invalid range.");
5a5d576b 283
55b7f20f 284 XFREE (MTYPE_LEX, $1);
478bdaeb 285}
9779e3f1
QY
286| MAC
287{
288 $$ = new_token_node (ctx, MAC_TKN, $1, doc_next(ctx));
289 XFREE (MTYPE_LEX, $1);
290}
291| MAC_PREFIX
292{
293 $$ = new_token_node (ctx, MAC_PREFIX_TKN, $1, doc_next(ctx));
294 XFREE (MTYPE_LEX, $1);
295}
782d9789 296
16705ecc
DL
297placeholder_token:
298 placeholder_token_real varname_token
299{
16705ecc 300 $$ = $1;
8005767b 301 cmd_token_varname_set ($$->data, $2);
16705ecc
DL
302 XFREE (MTYPE_LEX, $2);
303};
304
305
4b0abf24 306/* <selector|set> productions */
16705ecc 307selector: '<' selector_seq_seq '>' varname_token
2a23ca6e 308{
663982cd 309 $$ = $2;
8005767b 310 cmd_token_varname_join ($2.end, $4);
16705ecc 311 XFREE (MTYPE_LEX, $4);
2a23ca6e 312};
782d9789 313
9286efab
QY
314selector_seq_seq:
315 selector_seq_seq '|' selector_token_seq
478bdaeb 316{
663982cd 317 $$ = $1;
2020b1c8
DL
318 graph_add_edge ($$.start, $3.start);
319 graph_add_edge ($3.end, $$.end);
9286efab 320}
663982cd 321| selector_token_seq
9286efab 322{
663982cd
DL
323 $$.start = new_token_node (ctx, FORK_TKN, NULL, NULL);
324 $$.end = new_token_node (ctx, JOIN_TKN, NULL, NULL);
325 ((struct cmd_token *)$$.start->data)->forkjoin = $$.end;
326 ((struct cmd_token *)$$.end->data)->forkjoin = $$.start;
327
2020b1c8
DL
328 graph_add_edge ($$.start, $1.start);
329 graph_add_edge ($1.end, $$.end);
478bdaeb 330}
9286efab 331;
782d9789 332
7d5718c1 333/* {keyword} productions */
16705ecc 334selector: '{' selector_seq_seq '}' varname_token
7d5718c1 335{
663982cd
DL
336 $$ = $2;
337 graph_add_edge ($$.end, $$.start);
338 /* there is intentionally no start->end link, for two reasons:
339 * 1) this allows "at least 1 of" semantics, which are otherwise impossible
340 * 2) this would add a start->end->start loop in the graph that the current
341 * loop-avoidal fails to handle
214d8a60 342 * just use [{a|b}] if necessary, that will work perfectly fine, and reason
663982cd 343 * #1 is good enough to keep it this way. */
16705ecc 344
eb44e1aa 345 loopcheck(ctx, &$$);
8005767b 346 cmd_token_varname_join ($2.end, $4);
16705ecc 347 XFREE (MTYPE_LEX, $4);
7d5718c1
DL
348};
349
350
535ef556 351selector_token:
9286efab 352 simple_token
4b0abf24 353{
2020b1c8 354 $$.start = $$.end = $1;
9286efab 355}
4d12266b 356| selector
9286efab 357;
782d9789 358
663982cd
DL
359selector_token_seq:
360 selector_token_seq selector_token
2a23ca6e 361{
2020b1c8 362 graph_add_edge ($1.end, $2.start);
8005767b 363 cmd_token_varname_seqappend($2.start);
2020b1c8
DL
364 $$.start = $1.start;
365 $$.end = $2.end;
2a23ca6e 366}
663982cd 367| selector_token
9286efab 368;
478bdaeb 369
663982cd 370/* [option] productions */
16705ecc 371selector: '[' selector_seq_seq ']' varname_token
9286efab 372{
663982cd
DL
373 $$ = $2;
374 graph_add_edge ($$.start, $$.end);
8005767b 375 cmd_token_varname_join ($2.end, $4);
16705ecc 376 XFREE (MTYPE_LEX, $4);
9286efab 377}
782d9789
QY
378;
379
90c8406c
DL
380/* ![option] productions */
381selector: EXCL_BRACKET selector_seq_seq ']' varname_token
382{
383 struct graph_node *neg_only = new_token_node (ctx, NEG_ONLY_TKN, NULL, NULL);
384
385 $$ = $2;
386 graph_add_edge ($$.start, neg_only);
387 graph_add_edge (neg_only, $$.end);
8005767b 388 cmd_token_varname_join ($2.end, $4);
90c8406c
DL
389 XFREE (MTYPE_LEX, $4);
390}
391;
392
92055a92 393%%
4b0abf24 394
e9484f70
DL
395#undef scanner
396
bf8d3d6a 397DEFINE_MTYPE(LIB, LEX, "Lexer token (temporary)");
55b7f20f 398
1eb5e8dc 399void
154e9ca1 400cmd_graph_parse (struct graph *graph, const struct cmd_element *cmd)
51fc9379 401{
b07a15f8
DL
402 struct parser_ctx ctx = { .graph = graph, .el = cmd };
403
1ab84bf3 404 // set to 1 to enable parser traces
51fc9379
QY
405 yydebug = 0;
406
e9484f70
DL
407 set_lexer_string (&ctx.scanner, cmd->string);
408
51fc9379 409 // parse command into DFA
e9484f70
DL
410 cmd_yyparse (&ctx);
411
412 /* cleanup lexer */
413 cleanup_lexer (&ctx.scanner);
07079d78 414
7a6ded40 415 // cleanup
b07a15f8 416 cleanup (&ctx);
51fc9379
QY
417}
418
419/* parser helper functions */
420
eb44e1aa
DL
421static bool loopcheck_inner(struct graph_node *start, struct graph_node *node,
422 struct graph_node *end, size_t depth)
423{
424 size_t i;
425 bool ret;
426
427 /* safety check */
428 if (depth++ == 64)
429 return true;
430
431 for (i = 0; i < vector_active(node->to); i++) {
432 struct graph_node *next = vector_slot(node->to, i);
433 struct cmd_token *tok = next->data;
434
435 if (next == end || next == start)
436 return true;
437 if (tok->type < SPECIAL_TKN)
438 continue;
439 ret = loopcheck_inner(start, next, end, depth);
440 if (ret)
441 return true;
442 }
443 return false;
444}
445
446static void loopcheck(struct parser_ctx *ctx, struct subgraph *sg)
447{
448 if (loopcheck_inner(sg->start, sg->start, sg->end, 0))
449 zlog_err("FATAL: '%s': {} contains an empty path! Use [{...}]",
450 ctx->el->string);
451}
452
51fc9379 453void
8bb647a8 454yyerror (CMD_YYLTYPE *loc, struct parser_ctx *ctx, char const *msg)
51fc9379 455{
8bb647a8
DL
456 char *tmpstr = strdup(ctx->el->string);
457 char *line, *eol;
458 char spacing[256];
459 int lineno = 0;
460
b87478cb
DS
461 zlog_notice ("%s: FATAL parse error: %s", __func__, msg);
462 zlog_notice ("%s: %d:%d-%d of this command definition:", __func__, loc->first_line, loc->first_column, loc->last_column);
8bb647a8
DL
463
464 line = tmpstr;
465 do {
466 lineno++;
467 eol = strchr(line, '\n');
468 if (eol)
469 *eol++ = '\0';
470
b87478cb 471 zlog_notice ("%s: | %s", __func__, line);
8bb647a8
DL
472 if (lineno == loc->first_line && lineno == loc->last_line
473 && loc->first_column < (int)sizeof(spacing) - 1
474 && loc->last_column < (int)sizeof(spacing) - 1) {
475
476 int len = loc->last_column - loc->first_column;
477 if (len == 0)
478 len = 1;
479
480 memset(spacing, ' ', loc->first_column - 1);
481 memset(spacing + loc->first_column - 1, '^', len);
482 spacing[loc->first_column - 1 + len] = '\0';
b87478cb 483 zlog_notice ("%s: | %s", __func__, spacing);
8bb647a8
DL
484 }
485 } while ((line = eol));
486 free(tmpstr);
92055a92 487}
782d9789 488
51fc9379 489static void
b07a15f8 490cleanup (struct parser_ctx *ctx)
782d9789 491{
51fc9379 492 /* free resources */
b07a15f8 493 free (ctx->docstr_start);
478bdaeb 494
4b0abf24 495 /* clear state pointers */
b07a15f8
DL
496 ctx->currnode = NULL;
497 ctx->docstr_start = ctx->docstr = NULL;
51fc9379 498}
4b0abf24 499
51fc9379 500static void
8bb647a8
DL
501terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx,
502 struct graph_node *finalnode)
51fc9379 503{
1eb5e8dc
QY
504 // end of graph should look like this
505 // * -> finalnode -> END_TKN -> cmd_element
154e9ca1 506 const struct cmd_element *element = ctx->el;
d8074cc0 507 struct graph_node *end_token_node =
83364d20 508 new_token_node (ctx, END_TKN, CMD_CR_TEXT, "");
1eb5e8dc 509 struct graph_node *end_element_node =
154e9ca1 510 graph_new_node (ctx->graph, (void *)element, NULL);
7a6ded40 511
ebb08130 512 if (ctx->docstr && strlen (ctx->docstr) > 1) {
c8a400f3
DS
513 zlog_err ("Excessive docstring while parsing '%s'", ctx->el->string);
514 zlog_err ("----------");
ebb08130 515 while (ctx->docstr && ctx->docstr[1] != '\0')
c8a400f3 516 zlog_err ("%s", strsep(&ctx->docstr, "\n"));
1d5453d6 517 zlog_err ("----------");
ebb08130
QY
518 }
519
1eb5e8dc
QY
520 graph_add_edge (finalnode, end_token_node);
521 graph_add_edge (end_token_node, end_element_node);
782d9789 522}
f4b0c72e 523
83364d20 524static const char *
b07a15f8 525doc_next (struct parser_ctx *ctx)
51fc9379 526{
b07a15f8 527 const char *piece = ctx->docstr ? strsep (&ctx->docstr, "\n") : "";
a9958674
QY
528 if (*piece == 0x03)
529 {
c8a400f3 530 zlog_err ("Ran out of docstring while parsing '%s'", ctx->el->string);
a9958674
QY
531 piece = "";
532 }
533
83364d20 534 return piece;
51fc9379
QY
535}
536
537static struct graph_node *
b07a15f8 538new_token_node (struct parser_ctx *ctx, enum cmd_token_type type,
83364d20 539 const char *text, const char *doc)
7a6ded40 540{
5894e76d
DL
541 struct cmd_token *token = cmd_token_new (type, ctx->el->attr, text, doc);
542 return graph_new_node (ctx->graph, token, (void (*)(void *)) &cmd_token_del);
7a6ded40 543}