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