]> git.proxmox.com Git - mirror_frr.git/blob - lib/command_parse.y
Merge remote-tracking branch 'origin/stable/2.0'
[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 "command_parse.h"
37 %output "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 <stdlib.h>
50 #include <string.h>
51 #include <ctype.h>
52
53 #include "command_graph.h"
54 #include "log.h"
55
56 DECLARE_MTYPE(LEX)
57
58 #define YYSTYPE CMD_YYSTYPE
59 #define YYLTYPE CMD_YYLTYPE
60 struct parser_ctx;
61
62 /* subgraph semantic value */
63 struct subgraph {
64 struct graph_node *start, *end;
65 };
66 }
67
68 %union {
69 long long number;
70 char *string;
71 struct graph_node *node;
72 struct subgraph subgraph;
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
83 struct parser_ctx {
84 yyscan_t scanner;
85
86 struct cmd_element *el;
87
88 struct graph *graph;
89 struct graph_node *currnode;
90
91 /* pointers to copy of command docstring */
92 char *docstr_start, *docstr;
93 };
94 }
95
96 /* union types for lexed tokens */
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
104
105 /* union types for parsed rules */
106 %type <node> start
107 %type <node> literal_token
108 %type <node> placeholder_token
109 %type <node> placeholder_token_real
110 %type <node> simple_token
111 %type <subgraph> selector
112 %type <subgraph> selector_token
113 %type <subgraph> selector_token_seq
114 %type <subgraph> selector_seq_seq
115
116 %type <string> varname_token
117
118 %code {
119
120 /* bison declarations */
121 void
122 cmd_yyerror (CMD_YYLTYPE *locp, struct parser_ctx *ctx, char const *msg);
123
124 /* helper functions for parser */
125 static const char *
126 doc_next (struct parser_ctx *ctx);
127
128 static struct graph_node *
129 new_token_node (struct parser_ctx *,
130 enum cmd_token_type type,
131 const char *text,
132 const char *doc);
133
134 static void
135 terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx,
136 struct graph_node *);
137
138 static void
139 cleanup (struct parser_ctx *ctx);
140
141 #define scanner ctx->scanner
142 }
143
144 /* yyparse parameters */
145 %lex-param {yyscan_t scanner}
146 %parse-param {struct parser_ctx *ctx}
147
148 /* called automatically before yyparse */
149 %initial-action {
150 /* clear state pointers */
151 ctx->currnode = vector_slot (ctx->graph->nodes, 0);
152
153 /* copy docstring and keep a pointer to the copy */
154 if (ctx->el->doc)
155 {
156 // allocate a new buffer, making room for a flag
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));
160 // set the flag so doc_next knows when to print a warning
161 ctx->docstr[length - 2] = 0x03;
162 // null terminate
163 ctx->docstr[length - 1] = 0x00;
164 }
165 ctx->docstr_start = ctx->docstr;
166 }
167
168 %%
169
170 start:
171 cmd_token_seq
172 {
173 // tack on the command element
174 terminate_graph (&@1, ctx, ctx->currnode);
175 }
176 | cmd_token_seq placeholder_token '.' '.' '.'
177 {
178 if ((ctx->currnode = graph_add_edge (ctx->currnode, $2)) != $2)
179 graph_delete_node (ctx->graph, $2);
180
181 ((struct cmd_token *)ctx->currnode->data)->allowrepeat = 1;
182
183 // adding a node as a child of itself accepts any number
184 // of the same token, which is what we want for variadics
185 graph_add_edge (ctx->currnode, ctx->currnode);
186
187 // tack on the command element
188 terminate_graph (&@1, ctx, ctx->currnode);
189 }
190 ;
191
192 varname_token: '$' WORD
193 {
194 $$ = $2;
195 }
196 | /* empty */
197 {
198 $$ = NULL;
199 }
200 ;
201
202 cmd_token_seq:
203 /* empty */
204 | cmd_token_seq cmd_token
205 ;
206
207 cmd_token:
208 simple_token
209 {
210 if ((ctx->currnode = graph_add_edge (ctx->currnode, $1)) != $1)
211 graph_delete_node (ctx->graph, $1);
212 }
213 | selector
214 {
215 graph_add_edge (ctx->currnode, $1.start);
216 ctx->currnode = $1.end;
217 }
218 ;
219
220 simple_token:
221 literal_token
222 | placeholder_token
223 ;
224
225 literal_token: WORD varname_token
226 {
227 $$ = new_token_node (ctx, WORD_TKN, $1, doc_next(ctx));
228 cmd_token_varname_set ($$->data, $2);
229 XFREE (MTYPE_LEX, $2);
230 XFREE (MTYPE_LEX, $1);
231 }
232 ;
233
234 placeholder_token_real:
235 IPV4
236 {
237 $$ = new_token_node (ctx, IPV4_TKN, $1, doc_next(ctx));
238 XFREE (MTYPE_LEX, $1);
239 }
240 | IPV4_PREFIX
241 {
242 $$ = new_token_node (ctx, IPV4_PREFIX_TKN, $1, doc_next(ctx));
243 XFREE (MTYPE_LEX, $1);
244 }
245 | IPV6
246 {
247 $$ = new_token_node (ctx, IPV6_TKN, $1, doc_next(ctx));
248 XFREE (MTYPE_LEX, $1);
249 }
250 | IPV6_PREFIX
251 {
252 $$ = new_token_node (ctx, IPV6_PREFIX_TKN, $1, doc_next(ctx));
253 XFREE (MTYPE_LEX, $1);
254 }
255 | VARIABLE
256 {
257 $$ = new_token_node (ctx, VARIABLE_TKN, $1, doc_next(ctx));
258 XFREE (MTYPE_LEX, $1);
259 }
260 | RANGE
261 {
262 $$ = new_token_node (ctx, RANGE_TKN, $1, doc_next(ctx));
263 struct cmd_token *token = $$->data;
264
265 // get the numbers out
266 yylval.string++;
267 token->min = strtoll (yylval.string, &yylval.string, 10);
268 strsep (&yylval.string, "-");
269 token->max = strtoll (yylval.string, &yylval.string, 10);
270
271 // validate range
272 if (token->min > token->max) cmd_yyerror (&@1, ctx, "Invalid range.");
273
274 XFREE (MTYPE_LEX, $1);
275 }
276
277 placeholder_token:
278 placeholder_token_real varname_token
279 {
280 struct cmd_token *token = $$->data;
281 $$ = $1;
282 cmd_token_varname_set (token, $2);
283 XFREE (MTYPE_LEX, $2);
284 };
285
286
287 /* <selector|set> productions */
288 selector: '<' selector_seq_seq '>' varname_token
289 {
290 $$ = $2;
291 cmd_token_varname_set ($2.end->data, $4);
292 XFREE (MTYPE_LEX, $4);
293 };
294
295 selector_seq_seq:
296 selector_seq_seq '|' selector_token_seq
297 {
298 $$ = $1;
299 graph_add_edge ($$.start, $3.start);
300 graph_add_edge ($3.end, $$.end);
301 }
302 | selector_token_seq
303 {
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
309 graph_add_edge ($$.start, $1.start);
310 graph_add_edge ($1.end, $$.end);
311 }
312 ;
313
314 /* {keyword} productions */
315 selector: '{' selector_seq_seq '}' varname_token
316 {
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. */
325
326 cmd_token_varname_set ($2.end->data, $4);
327 XFREE (MTYPE_LEX, $4);
328 };
329
330
331 selector_token:
332 simple_token
333 {
334 $$.start = $$.end = $1;
335 }
336 | selector
337 ;
338
339 selector_token_seq:
340 selector_token_seq selector_token
341 {
342 graph_add_edge ($1.end, $2.start);
343 $$.start = $1.start;
344 $$.end = $2.end;
345 }
346 | selector_token
347 ;
348
349 /* [option] productions */
350 selector: '[' selector_seq_seq ']' varname_token
351 {
352 $$ = $2;
353 graph_add_edge ($$.start, $$.end);
354 cmd_token_varname_set ($2.end->data, $4);
355 XFREE (MTYPE_LEX, $4);
356 }
357 ;
358
359 %%
360
361 #undef scanner
362
363 DEFINE_MTYPE(LIB, LEX, "Lexer token (temporary)")
364
365 void
366 cmd_graph_parse (struct graph *graph, struct cmd_element *cmd)
367 {
368 struct parser_ctx ctx = { .graph = graph, .el = cmd };
369
370 // set to 1 to enable parser traces
371 yydebug = 0;
372
373 set_lexer_string (&ctx.scanner, cmd->string);
374
375 // parse command into DFA
376 cmd_yyparse (&ctx);
377
378 /* cleanup lexer */
379 cleanup_lexer (&ctx.scanner);
380
381 // cleanup
382 cleanup (&ctx);
383 }
384
385 /* parser helper functions */
386
387 void
388 yyerror (CMD_YYLTYPE *loc, struct parser_ctx *ctx, char const *msg)
389 {
390 char *tmpstr = strdup(ctx->el->string);
391 char *line, *eol;
392 char spacing[256];
393 int lineno = 0;
394
395 zlog_err ("%s: FATAL parse error: %s", __func__, msg);
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);
421 }
422
423 static void
424 cleanup (struct parser_ctx *ctx)
425 {
426 /* free resources */
427 free (ctx->docstr_start);
428
429 /* clear state pointers */
430 ctx->currnode = NULL;
431 ctx->docstr_start = ctx->docstr = NULL;
432 }
433
434 static void
435 terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx,
436 struct graph_node *finalnode)
437 {
438 // end of graph should look like this
439 // * -> finalnode -> END_TKN -> cmd_element
440 struct cmd_element *element = ctx->el;
441 struct graph_node *end_token_node =
442 new_token_node (ctx, END_TKN, CMD_CR_TEXT, "");
443 struct graph_node *end_element_node =
444 graph_new_node (ctx->graph, element, NULL);
445
446 graph_add_edge (finalnode, end_token_node);
447 graph_add_edge (end_token_node, end_element_node);
448 }
449
450 static const char *
451 doc_next (struct parser_ctx *ctx)
452 {
453 const char *piece = ctx->docstr ? strsep (&ctx->docstr, "\n") : "";
454 if (*piece == 0x03)
455 {
456 zlog_debug ("Ran out of docstring while parsing '%s'", ctx->el->string);
457 piece = "";
458 }
459
460 return piece;
461 }
462
463 static struct graph_node *
464 new_token_node (struct parser_ctx *ctx, enum cmd_token_type type,
465 const char *text, const char *doc)
466 {
467 struct cmd_token *token = cmd_token_new (type, ctx->el->attr, text, doc);
468 return graph_new_node (ctx->graph, token, (void (*)(void *)) &cmd_token_del);
469 }