2 * Command format string parser.
4 * Turns a command definition into a DFA that together with the functions
5 * provided in command_match.c may be used to map command line input to a
8 * @author Quentin Young <qlyoung@cumulusnetworks.com>
12 extern int yylex(void);
13 extern void yyerror(const char *);
15 // compile with debugging facilities
20 #include "command_graph.h"
25 set_buffer_string(const char *);
27 parse_command_format(struct graph_node *, struct cmd_element *);
31 /* valid types for tokens */
33 signed long long integer;
35 struct graph_node *node;
38 /* some helpful state variables */
40 struct graph_node *startnode, // command root node
41 *currnode, // current node
42 *seqhead; // sequence head
45 struct graph_node *optnode_start, // start node for option set
46 *optnode_end; // end node for option set
48 struct graph_node *selnode_start, // start node for selector set
49 *selnode_end; // end node for selector set
51 struct cmd_element *command; // command we're parsing
56 %token <string> IPV4_PREFIX
58 %token <string> IPV6_PREFIX
59 %token <string> VARIABLE
61 %token <integer> NUMBER
64 %type <node> sentence_root
65 %type <node> literal_token
66 %type <node> placeholder_token
68 %type <node> option_token
69 %type <node> option_token_seq
71 %type <node> selector_element_root
72 %type <node> selector_token
73 %type <node> selector_token_seq
75 %defines "command_parse.h"
76 %output "command_parse.c"
85 struct graph_node *end = new_node(END_GN);
86 end->element = command;
89 if (add_node(currnode, end) != end)
91 yyerror("Duplicate command.");
94 fprintf(stderr, "Parsed full command successfully.\n");
99 struct graph_node *root = new_node(WORD_GN);
100 root->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
102 currnode = add_node(startnode, root);
103 if (currnode != root)
110 /* valid top level tokens */
114 currnode = add_node(currnode, $1);
120 currnode = add_node(currnode, $1);
124 /* selectors and options are subgraphs with start and end nodes */
127 add_node(currnode, $1);
128 currnode = selnode_end;
129 selnode_start = selnode_end = NULL;
133 add_node(currnode, $1);
134 currnode = optnode_end;
135 optnode_start = optnode_end = NULL;
141 | cmd_token_seq cmd_token
147 $$ = new_node(IPV4_GN);
148 $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
153 $$ = new_node(IPV4_PREFIX_GN);
154 $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
159 $$ = new_node(IPV6_GN);
160 $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
165 $$ = new_node(IPV6_PREFIX_GN);
166 $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
171 $$ = new_node(VARIABLE_GN);
172 $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
177 $$ = new_node(RANGE_GN);
178 $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
180 // get the numbers out
181 strsep(&yylval.string, "(-)");
183 $$->min = strtoll( strsep(&yylval.string, "(-)"), &endptr, 10 );
184 $$->max = strtoll( strsep(&yylval.string, "(-)"), &endptr, 10 );
193 $$ = new_node(WORD_GN);
194 $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1);
199 $$ = new_node(NUMBER_GN);
200 $$->value = yylval.integer;
204 /* <selector|set> productions */
206 '<' selector_part '|' selector_element '>'
208 // all the graph building is done in selector_element,
209 // so just return the selector subgraph head
214 selector_part '|' selector_element
219 selector_element_root selector_token_seq
221 // if the selector start and end do not exist, create them
222 if (!selnode_start || !selnode_end) { // if one is null
223 assert(!selnode_start && !selnode_end); // both should be null
224 selnode_start = new_node(SELECTOR_GN); // diverging node
225 selnode_end = new_node(NUL_GN); // converging node
226 selnode_start->end = selnode_end; // duh
229 // add element head as a child of the selector
230 add_node(selnode_start, $1);
232 if ($2->type != NUL_GN) {
233 add_node($1, seqhead);
234 add_node($2, selnode_end);
237 add_node($1, selnode_end);
243 %empty { $$ = new_node(NUL_GN); }
244 | selector_token_seq selector_token
246 // if the sequence component is NUL_GN, this is a sequence start
247 if ($1->type == NUL_GN) {
248 assert(!seqhead); // sequence head should always be null here
251 else // chain on new node
258 selector_element_root:
264 selector_element_root
268 /* [option|set] productions */
269 option: '[' option_part ']'
272 struct graph_node *nullpath = new_node(NUL_GN);
273 add_node(optnode_start, nullpath);
274 add_node(nullpath, optnode_end);
280 option_part '|' option_element
287 if (!optnode_start || !optnode_end) {
288 assert(!optnode_start && !optnode_end);
289 optnode_start = new_node(OPTION_GN);
290 optnode_end = new_node(NUL_GN);
293 add_node(optnode_start, seqhead);
294 add_node($1, optnode_end);
299 { $$ = seqhead = $1; }
300 | option_token_seq option_token
301 { $$ = add_node($1, $2); }
311 void yyerror(char const *message) {
313 fprintf(stderr, "Grammar error: %s\n", message);
314 fprintf(stderr, "Token on error: ");
315 if (yylval.string) fprintf(stderr, "%s\n", yylval.string);
316 else if (yylval.node) fprintf(stderr, "%s\n", yylval.node->text);
317 else fprintf(stderr, "%lld\n", yylval.integer);
322 parse_command_format(struct graph_node *start, struct cmd_element *cmd)
324 fprintf(stderr, "parsing: %s\n", cmd->string);
326 /* clear state pointers */
328 currnode = seqhead = NULL;
329 selnode_start = selnode_end = NULL;
330 optnode_start = optnode_end = NULL;
336 // make flex read from a string
337 set_buffer_string(command->string);
338 // parse command into DFA
340 // startnode points to command DFA