]> git.proxmox.com Git - mirror_frr.git/blame - lib/command_parse.y
lib: parser: add named variables in CLI
[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
QY
35/* names for generated header and parser files */
36%defines "command_parse.h"
37%output "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 {
460a7689
DS
47 #include "stdlib.h"
48 #include "string.h"
55b7f20f 49 #include "memory.h"
eceb1066 50 #include "command.h"
a9958674 51 #include "log.h"
7a6ded40 52 #include "graph.h"
51fc9379 53
55b7f20f
DL
54 DECLARE_MTYPE(LEX)
55
afc3a6ce 56 #define YYSTYPE CMD_YYSTYPE
8bb647a8 57 #define YYLTYPE CMD_YYLTYPE
afc3a6ce 58 struct parser_ctx;
2020b1c8
DL
59
60 /* subgraph semantic value */
61 struct subgraph {
62 struct graph_node *start, *end;
63 };
afc3a6ce
DL
64}
65
66%union {
67 long long number;
68 char *string;
69 struct graph_node *node;
2020b1c8 70 struct subgraph subgraph;
afc3a6ce
DL
71}
72
73%code provides {
74 #ifndef FLEX_SCANNER
75 #include "command_lex.h"
76 #endif
77
78 extern void set_lexer_string (yyscan_t *scn, const char *string);
79 extern void cleanup_lexer (yyscan_t *scn);
80
b07a15f8 81 struct parser_ctx {
e9484f70
DL
82 yyscan_t scanner;
83
b07a15f8
DL
84 struct cmd_element *el;
85
86 struct graph *graph;
fd19e7a2 87 struct graph_node *currnode;
b07a15f8
DL
88
89 /* pointers to copy of command docstring */
90 char *docstr_start, *docstr;
91 };
92055a92
QY
92}
93
51fc9379 94/* union types for lexed tokens */
5a5d576b
QY
95%token <string> WORD
96%token <string> IPV4
97%token <string> IPV4_PREFIX
98%token <string> IPV6
99%token <string> IPV6_PREFIX
100%token <string> VARIABLE
101%token <string> RANGE
782d9789 102
51fc9379 103/* union types for parsed rules */
782d9789 104%type <node> start
782d9789
QY
105%type <node> literal_token
106%type <node> placeholder_token
16705ecc 107%type <node> placeholder_token_real
9286efab 108%type <node> simple_token
9286efab
QY
109%type <subgraph> selector
110%type <subgraph> selector_token
111%type <subgraph> selector_token_seq
112%type <subgraph> selector_seq_seq
782d9789 113
16705ecc
DL
114%type <string> varname_token
115
51fc9379 116%code {
e9484f70 117
51fc9379
QY
118 /* bison declarations */
119 void
8bb647a8 120 cmd_yyerror (CMD_YYLTYPE *locp, struct parser_ctx *ctx, char const *msg);
1eb5e8dc 121
51fc9379 122 /* helper functions for parser */
83364d20 123 static const char *
b07a15f8 124 doc_next (struct parser_ctx *ctx);
51fc9379 125
1eb5e8dc 126 static struct graph_node *
b07a15f8 127 new_token_node (struct parser_ctx *,
d0bfb22c 128 enum cmd_token_type type,
83364d20
DL
129 const char *text,
130 const char *doc);
51fc9379
QY
131
132 static void
8bb647a8 133 terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx,
b07a15f8 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
8bb647a8 172 terminate_graph (&@1, ctx, ctx->currnode);
880e24a1 173}
fd19e7a2 174| cmd_token_seq placeholder_token '.' '.' '.'
88255c7c 175{
6432969d 176 if ((ctx->currnode = graph_add_edge (ctx->currnode, $2)) != $2)
fd19e7a2 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
6432969d 183 graph_add_edge (ctx->currnode, ctx->currnode);
88255c7c 184
51fc9379 185 // tack on the command element
8bb647a8 186 terminate_graph (&@1, ctx, ctx->currnode);
88255c7c 187}
9286efab 188;
92055a92 189
16705ecc
DL
190varname_token: '$' WORD
191{
192 $$ = XSTRDUP (MTYPE_LEX, $2);
193}
194| /* empty */
195{
196 $$ = NULL;
197}
198;
199
9286efab 200cmd_token_seq:
0d4aa1b1 201 /* empty */
9286efab
QY
202| cmd_token_seq cmd_token
203;
92055a92 204
782d9789 205cmd_token:
9286efab 206 simple_token
5a5d576b 207{
6432969d 208 if ((ctx->currnode = graph_add_edge (ctx->currnode, $1)) != $1)
b07a15f8 209 graph_delete_node (ctx->graph, $1);
5a5d576b 210}
663982cd 211| selector
5a5d576b 212{
2020b1c8
DL
213 graph_add_edge (ctx->currnode, $1.start);
214 ctx->currnode = $1.end;
478bdaeb 215}
9286efab
QY
216;
217
218simple_token:
219 literal_token
220| placeholder_token
221;
222
16705ecc 223literal_token: WORD varname_token
9286efab 224{
83364d20 225 $$ = new_token_node (ctx, WORD_TKN, $1, doc_next(ctx));
16705ecc
DL
226 cmd_set_varname ($$->data, $2);
227 XFREE (MTYPE_LEX, $2);
55b7f20f 228 XFREE (MTYPE_LEX, $1);
9286efab 229}
782d9789
QY
230;
231
16705ecc 232placeholder_token_real:
478bdaeb 233 IPV4
4b0abf24 234{
83364d20 235 $$ = new_token_node (ctx, IPV4_TKN, $1, doc_next(ctx));
55b7f20f 236 XFREE (MTYPE_LEX, $1);
4b0abf24 237}
478bdaeb 238| IPV4_PREFIX
340a2b4a 239{
83364d20 240 $$ = new_token_node (ctx, IPV4_PREFIX_TKN, $1, doc_next(ctx));
55b7f20f 241 XFREE (MTYPE_LEX, $1);
4b0abf24 242}
478bdaeb 243| IPV6
340a2b4a 244{
83364d20 245 $$ = new_token_node (ctx, IPV6_TKN, $1, doc_next(ctx));
55b7f20f 246 XFREE (MTYPE_LEX, $1);
4b0abf24 247}
478bdaeb 248| IPV6_PREFIX
340a2b4a 249{
83364d20 250 $$ = new_token_node (ctx, IPV6_PREFIX_TKN, $1, doc_next(ctx));
55b7f20f 251 XFREE (MTYPE_LEX, $1);
4b0abf24 252}
478bdaeb 253| VARIABLE
340a2b4a 254{
83364d20 255 $$ = new_token_node (ctx, VARIABLE_TKN, $1, doc_next(ctx));
55b7f20f 256 XFREE (MTYPE_LEX, $1);
4b0abf24 257}
478bdaeb
QY
258| RANGE
259{
83364d20 260 $$ = new_token_node (ctx, RANGE_TKN, $1, doc_next(ctx));
d0bfb22c 261 struct cmd_token *token = $$->data;
478bdaeb
QY
262
263 // get the numbers out
b3899769 264 yylval.string++;
7a6ded40 265 token->min = strtoll (yylval.string, &yylval.string, 10);
ef955a80 266 strsep (&yylval.string, "-");
7a6ded40 267 token->max = strtoll (yylval.string, &yylval.string, 10);
ef955a80
QY
268
269 // validate range
8bb647a8 270 if (token->min > token->max) cmd_yyerror (&@1, ctx, "Invalid range.");
5a5d576b 271
55b7f20f 272 XFREE (MTYPE_LEX, $1);
478bdaeb 273}
782d9789 274
16705ecc
DL
275placeholder_token:
276 placeholder_token_real varname_token
277{
278 struct cmd_token *token = $$->data;
279 $$ = $1;
280 cmd_set_varname (token, $2);
281 XFREE (MTYPE_LEX, $2);
282};
283
284
4b0abf24 285/* <selector|set> productions */
16705ecc 286selector: '<' selector_seq_seq '>' varname_token
2a23ca6e 287{
663982cd 288 $$ = $2;
16705ecc
DL
289 cmd_set_varname ($2.end->data, $4);
290 XFREE (MTYPE_LEX, $4);
2a23ca6e 291};
782d9789 292
9286efab
QY
293selector_seq_seq:
294 selector_seq_seq '|' selector_token_seq
478bdaeb 295{
663982cd 296 $$ = $1;
2020b1c8
DL
297 graph_add_edge ($$.start, $3.start);
298 graph_add_edge ($3.end, $$.end);
9286efab 299}
663982cd 300| selector_token_seq
9286efab 301{
663982cd
DL
302 $$.start = new_token_node (ctx, FORK_TKN, NULL, NULL);
303 $$.end = new_token_node (ctx, JOIN_TKN, NULL, NULL);
304 ((struct cmd_token *)$$.start->data)->forkjoin = $$.end;
305 ((struct cmd_token *)$$.end->data)->forkjoin = $$.start;
306
2020b1c8
DL
307 graph_add_edge ($$.start, $1.start);
308 graph_add_edge ($1.end, $$.end);
478bdaeb 309}
9286efab 310;
782d9789 311
7d5718c1 312/* {keyword} productions */
16705ecc 313selector: '{' selector_seq_seq '}' varname_token
7d5718c1 314{
663982cd
DL
315 $$ = $2;
316 graph_add_edge ($$.end, $$.start);
317 /* there is intentionally no start->end link, for two reasons:
318 * 1) this allows "at least 1 of" semantics, which are otherwise impossible
319 * 2) this would add a start->end->start loop in the graph that the current
320 * loop-avoidal fails to handle
321 * just use [{a|b}] if neccessary, that will work perfectly fine, and reason
322 * #1 is good enough to keep it this way. */
16705ecc
DL
323
324 cmd_set_varname ($2.end->data, $4);
325 XFREE (MTYPE_LEX, $4);
7d5718c1
DL
326};
327
328
535ef556 329selector_token:
9286efab 330 simple_token
4b0abf24 331{
2020b1c8 332 $$.start = $$.end = $1;
9286efab 333}
4d12266b 334| selector
9286efab 335;
782d9789 336
663982cd
DL
337selector_token_seq:
338 selector_token_seq selector_token
2a23ca6e 339{
2020b1c8
DL
340 graph_add_edge ($1.end, $2.start);
341 $$.start = $1.start;
342 $$.end = $2.end;
2a23ca6e 343}
663982cd 344| selector_token
9286efab 345;
478bdaeb 346
663982cd 347/* [option] productions */
16705ecc 348selector: '[' selector_seq_seq ']' varname_token
9286efab 349{
663982cd
DL
350 $$ = $2;
351 graph_add_edge ($$.start, $$.end);
16705ecc
DL
352 cmd_set_varname ($2.end->data, $4);
353 XFREE (MTYPE_LEX, $4);
9286efab 354}
782d9789
QY
355;
356
92055a92 357%%
4b0abf24 358
e9484f70
DL
359#undef scanner
360
55b7f20f
DL
361DEFINE_MTYPE(LIB, LEX, "Lexer token (temporary)")
362
1eb5e8dc
QY
363void
364command_parse_format (struct graph *graph, struct cmd_element *cmd)
51fc9379 365{
b07a15f8
DL
366 struct parser_ctx ctx = { .graph = graph, .el = cmd };
367
1ab84bf3 368 // set to 1 to enable parser traces
51fc9379
QY
369 yydebug = 0;
370
e9484f70
DL
371 set_lexer_string (&ctx.scanner, cmd->string);
372
51fc9379 373 // parse command into DFA
e9484f70
DL
374 cmd_yyparse (&ctx);
375
376 /* cleanup lexer */
377 cleanup_lexer (&ctx.scanner);
07079d78 378
7a6ded40 379 // cleanup
b07a15f8 380 cleanup (&ctx);
51fc9379
QY
381}
382
383/* parser helper functions */
384
385void
8bb647a8 386yyerror (CMD_YYLTYPE *loc, struct parser_ctx *ctx, char const *msg)
51fc9379 387{
8bb647a8
DL
388 char *tmpstr = strdup(ctx->el->string);
389 char *line, *eol;
390 char spacing[256];
391 int lineno = 0;
392
1ab84bf3 393 zlog_err ("%s: FATAL parse error: %s", __func__, msg);
8bb647a8
DL
394 zlog_err ("%s: %d:%d-%d of this command definition:", __func__, loc->first_line, loc->first_column, loc->last_column);
395
396 line = tmpstr;
397 do {
398 lineno++;
399 eol = strchr(line, '\n');
400 if (eol)
401 *eol++ = '\0';
402
403 zlog_err ("%s: | %s", __func__, line);
404 if (lineno == loc->first_line && lineno == loc->last_line
405 && loc->first_column < (int)sizeof(spacing) - 1
406 && loc->last_column < (int)sizeof(spacing) - 1) {
407
408 int len = loc->last_column - loc->first_column;
409 if (len == 0)
410 len = 1;
411
412 memset(spacing, ' ', loc->first_column - 1);
413 memset(spacing + loc->first_column - 1, '^', len);
414 spacing[loc->first_column - 1 + len] = '\0';
415 zlog_err ("%s: | %s", __func__, spacing);
416 }
417 } while ((line = eol));
418 free(tmpstr);
92055a92 419}
782d9789 420
51fc9379 421static void
b07a15f8 422cleanup (struct parser_ctx *ctx)
782d9789 423{
51fc9379 424 /* free resources */
b07a15f8 425 free (ctx->docstr_start);
478bdaeb 426
4b0abf24 427 /* clear state pointers */
b07a15f8
DL
428 ctx->currnode = NULL;
429 ctx->docstr_start = ctx->docstr = NULL;
51fc9379 430}
4b0abf24 431
51fc9379 432static void
8bb647a8
DL
433terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx,
434 struct graph_node *finalnode)
51fc9379 435{
1eb5e8dc
QY
436 // end of graph should look like this
437 // * -> finalnode -> END_TKN -> cmd_element
b07a15f8 438 struct cmd_element *element = ctx->el;
d8074cc0 439 struct graph_node *end_token_node =
83364d20 440 new_token_node (ctx, END_TKN, CMD_CR_TEXT, "");
1eb5e8dc 441 struct graph_node *end_element_node =
b5a1e9ef 442 graph_new_node (ctx->graph, element, NULL);
7a6ded40 443
1eb5e8dc
QY
444 graph_add_edge (finalnode, end_token_node);
445 graph_add_edge (end_token_node, end_element_node);
782d9789 446}
f4b0c72e 447
83364d20 448static const char *
b07a15f8 449doc_next (struct parser_ctx *ctx)
51fc9379 450{
b07a15f8 451 const char *piece = ctx->docstr ? strsep (&ctx->docstr, "\n") : "";
a9958674
QY
452 if (*piece == 0x03)
453 {
b07a15f8 454 zlog_debug ("Ran out of docstring while parsing '%s'", ctx->el->string);
a9958674
QY
455 piece = "";
456 }
457
83364d20 458 return piece;
51fc9379
QY
459}
460
461static struct graph_node *
b07a15f8 462new_token_node (struct parser_ctx *ctx, enum cmd_token_type type,
83364d20 463 const char *text, const char *doc)
7a6ded40 464{
b07a15f8
DL
465 struct cmd_token *token = new_cmd_token (type, ctx->el->attr, text, doc);
466 return graph_new_node (ctx->graph, token, (void (*)(void *)) &del_cmd_token);
7a6ded40 467}