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