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