2 * Command format string parser for CLI backend.
5 * Copyright (C) 2016 Cumulus Networks, Inc.
7 * This file is part of GNU Zebra.
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
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.
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
26 // compile with debugging facilities
31 /* define parse.error verbose */
33 /* define api.prefix {cmd_yy} */
35 /* names for generated header and parser files */
36 %defines "command_parse.h"
37 %output "command_parse.c"
39 /* note: code blocks are output in order, to both .c and .h:
41 * 2. %union + bison forward decls
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.
56 #define YYSTYPE CMD_YYSTYPE
57 #define YYLTYPE CMD_YYLTYPE
60 /* subgraph semantic value */
62 struct graph_node *start, *end;
69 struct graph_node *node;
70 struct subgraph subgraph;
75 #include "command_lex.h"
78 extern void set_lexer_string (yyscan_t *scn, const char *string);
79 extern void cleanup_lexer (yyscan_t *scn);
84 struct cmd_element *el;
87 struct graph_node *currnode;
89 /* pointers to copy of command docstring */
90 char *docstr_start, *docstr;
94 /* union types for lexed tokens */
97 %token <string> IPV4_PREFIX
99 %token <string> IPV6_PREFIX
100 %token <string> VARIABLE
101 %token <string> RANGE
103 /* union types for parsed rules */
105 %type <node> literal_token
106 %type <node> placeholder_token
107 %type <node> simple_token
108 %type <subgraph> selector
109 %type <subgraph> selector_token
110 %type <subgraph> selector_token_seq
111 %type <subgraph> selector_seq_seq
115 /* bison declarations */
117 cmd_yyerror (CMD_YYLTYPE *locp, struct parser_ctx *ctx, char const *msg);
119 /* helper functions for parser */
121 doc_next (struct parser_ctx *ctx);
123 static struct graph_node *
124 new_token_node (struct parser_ctx *,
125 enum cmd_token_type type,
130 terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx,
131 struct graph_node *);
134 cleanup (struct parser_ctx *ctx);
136 #define scanner ctx->scanner
139 /* yyparse parameters */
140 %lex-param {yyscan_t scanner}
141 %parse-param {struct parser_ctx *ctx}
143 /* called automatically before yyparse */
145 /* clear state pointers */
146 ctx->currnode = vector_slot (ctx->graph->nodes, 0);
148 /* copy docstring and keep a pointer to the copy */
151 // allocate a new buffer, making room for a flag
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));
155 // set the flag so doc_next knows when to print a warning
156 ctx->docstr[length - 2] = 0x03;
158 ctx->docstr[length - 1] = 0x00;
160 ctx->docstr_start = ctx->docstr;
168 // tack on the command element
169 terminate_graph (&@1, ctx, ctx->currnode);
171 | cmd_token_seq placeholder_token '.' '.' '.'
173 if ((ctx->currnode = graph_add_edge (ctx->currnode, $2)) != $2)
174 graph_delete_node (ctx->graph, $2);
176 ((struct cmd_token *)ctx->currnode->data)->allowrepeat = 1;
178 // adding a node as a child of itself accepts any number
179 // of the same token, which is what we want for variadics
180 graph_add_edge (ctx->currnode, ctx->currnode);
182 // tack on the command element
183 terminate_graph (&@1, ctx, ctx->currnode);
189 | cmd_token_seq cmd_token
195 if ((ctx->currnode = graph_add_edge (ctx->currnode, $1)) != $1)
196 graph_delete_node (ctx->graph, $1);
200 graph_add_edge (ctx->currnode, $1.start);
201 ctx->currnode = $1.end;
212 $$ = new_token_node (ctx, WORD_TKN, $1, doc_next(ctx));
213 XFREE (MTYPE_LEX, $1);
220 $$ = new_token_node (ctx, IPV4_TKN, $1, doc_next(ctx));
221 XFREE (MTYPE_LEX, $1);
225 $$ = new_token_node (ctx, IPV4_PREFIX_TKN, $1, doc_next(ctx));
226 XFREE (MTYPE_LEX, $1);
230 $$ = new_token_node (ctx, IPV6_TKN, $1, doc_next(ctx));
231 XFREE (MTYPE_LEX, $1);
235 $$ = new_token_node (ctx, IPV6_PREFIX_TKN, $1, doc_next(ctx));
236 XFREE (MTYPE_LEX, $1);
240 $$ = new_token_node (ctx, VARIABLE_TKN, $1, doc_next(ctx));
241 XFREE (MTYPE_LEX, $1);
245 $$ = new_token_node (ctx, RANGE_TKN, $1, doc_next(ctx));
246 struct cmd_token *token = $$->data;
248 // get the numbers out
250 token->min = strtoll (yylval.string, &yylval.string, 10);
251 strsep (&yylval.string, "-");
252 token->max = strtoll (yylval.string, &yylval.string, 10);
255 if (token->min > token->max) cmd_yyerror (&@1, ctx, "Invalid range.");
257 XFREE (MTYPE_LEX, $1);
260 /* <selector|set> productions */
261 selector: '<' selector_seq_seq '>'
267 selector_seq_seq '|' selector_token_seq
270 graph_add_edge ($$.start, $3.start);
271 graph_add_edge ($3.end, $$.end);
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;
280 graph_add_edge ($$.start, $1.start);
281 graph_add_edge ($1.end, $$.end);
285 /* {keyword} productions */
286 selector: '{' selector_seq_seq '}'
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. */
302 $$.start = $$.end = $1;
308 selector_token_seq selector_token
310 graph_add_edge ($1.end, $2.start);
317 /* [option] productions */
318 selector: '[' selector_seq_seq ']'
321 graph_add_edge ($$.start, $$.end);
329 DEFINE_MTYPE(LIB, LEX, "Lexer token (temporary)")
332 command_parse_format (struct graph *graph, struct cmd_element *cmd)
334 struct parser_ctx ctx = { .graph = graph, .el = cmd };
336 // set to 1 to enable parser traces
339 set_lexer_string (&ctx.scanner, cmd->string);
341 // parse command into DFA
345 cleanup_lexer (&ctx.scanner);
351 /* parser helper functions */
354 yyerror (CMD_YYLTYPE *loc, struct parser_ctx *ctx, char const *msg)
356 char *tmpstr = strdup(ctx->el->string);
361 zlog_err ("%s: FATAL parse error: %s", __func__, msg);
362 zlog_err ("%s: %d:%d-%d of this command definition:", __func__, loc->first_line, loc->first_column, loc->last_column);
367 eol = strchr(line, '\n');
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) {
376 int len = loc->last_column - loc->first_column;
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);
385 } while ((line = eol));
390 cleanup (struct parser_ctx *ctx)
393 free (ctx->docstr_start);
395 /* clear state pointers */
396 ctx->currnode = NULL;
397 ctx->docstr_start = ctx->docstr = NULL;
401 terminate_graph (CMD_YYLTYPE *locp, struct parser_ctx *ctx,
402 struct graph_node *finalnode)
404 // end of graph should look like this
405 // * -> finalnode -> END_TKN -> cmd_element
406 struct cmd_element *element = ctx->el;
407 struct graph_node *end_token_node =
408 new_token_node (ctx, END_TKN, CMD_CR_TEXT, "");
409 struct graph_node *end_element_node =
410 graph_new_node (ctx->graph, element, NULL);
412 graph_add_edge (finalnode, end_token_node);
413 graph_add_edge (end_token_node, end_element_node);
417 doc_next (struct parser_ctx *ctx)
419 const char *piece = ctx->docstr ? strsep (&ctx->docstr, "\n") : "";
422 zlog_debug ("Ran out of docstring while parsing '%s'", ctx->el->string);
429 static struct graph_node *
430 new_token_node (struct parser_ctx *ctx, enum cmd_token_type type,
431 const char *text, const char *doc)
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);