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