]>
Commit | Line | Data |
---|---|---|
9d0662e0 QY |
1 | /* |
2 | * Command format string parser. | |
3 | * | |
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 | |
6 | * function. | |
7 | * | |
8 | * @author Quentin Young <qlyoung@cumulusnetworks.com> | |
9 | */ | |
10 | ||
92055a92 | 11 | %{ |
92055a92 | 12 | extern int yylex(void); |
2a23ca6e | 13 | extern void yyerror(const char *); |
782d9789 | 14 | |
2a23ca6e | 15 | // compile with debugging facilities |
782d9789 | 16 | #define YYDEBUG 1 |
92055a92 | 17 | %} |
eceb1066 QY |
18 | %code requires { |
19 | #include "command.h" | |
20 | #include "command_graph.h" | |
5a5d576b | 21 | #include "memory.h" |
eceb1066 | 22 | } |
478bdaeb | 23 | %code provides { |
eceb1066 QY |
24 | extern void |
25 | set_buffer_string(const char *); | |
26 | struct graph_node * | |
27 | parse_command_format(struct graph_node *, struct cmd_element *); | |
478bdaeb | 28 | } |
92055a92 | 29 | |
eceb1066 | 30 | |
9d0662e0 | 31 | /* valid types for tokens */ |
92055a92 | 32 | %union{ |
e648e61a | 33 | signed long long integer; |
92055a92 | 34 | char *string; |
782d9789 | 35 | struct graph_node *node; |
92055a92 QY |
36 | } |
37 | ||
9d0662e0 | 38 | /* some helpful state variables */ |
782d9789 | 39 | %{ |
478bdaeb QY |
40 | struct graph_node *startnode, // command root node |
41 | *currnode, // current node | |
478bdaeb QY |
42 | *seqhead; // sequence head |
43 | ||
2a23ca6e | 44 | |
4b0abf24 QY |
45 | struct graph_node *optnode_start, // start node for option set |
46 | *optnode_end; // end node for option set | |
2a23ca6e | 47 | |
4b0abf24 QY |
48 | struct graph_node *selnode_start, // start node for selector set |
49 | *selnode_end; // end node for selector set | |
880e24a1 | 50 | |
eceb1066 | 51 | struct cmd_element *command; // command we're parsing |
782d9789 QY |
52 | %} |
53 | ||
5a5d576b QY |
54 | %token <string> WORD |
55 | %token <string> IPV4 | |
56 | %token <string> IPV4_PREFIX | |
57 | %token <string> IPV6 | |
58 | %token <string> IPV6_PREFIX | |
59 | %token <string> VARIABLE | |
60 | %token <string> RANGE | |
61 | %token <integer> NUMBER | |
782d9789 QY |
62 | |
63 | %type <node> start | |
64 | %type <node> sentence_root | |
782d9789 QY |
65 | %type <node> literal_token |
66 | %type <node> placeholder_token | |
782d9789 | 67 | %type <node> option |
478bdaeb QY |
68 | %type <node> option_token |
69 | %type <node> option_token_seq | |
782d9789 | 70 | %type <node> selector |
340a2b4a | 71 | %type <node> selector_element_root |
478bdaeb | 72 | %type <node> selector_token |
782d9789 | 73 | %type <node> selector_token_seq |
782d9789 | 74 | |
2a23ca6e QY |
75 | %defines "command_parse.h" |
76 | %output "command_parse.c" | |
92055a92 QY |
77 | |
78 | /* grammar proper */ | |
79 | %% | |
80 | ||
782d9789 | 81 | start: sentence_root |
880e24a1 QY |
82 | cmd_token_seq |
83 | { | |
5a5d576b | 84 | // create leaf node |
880e24a1 | 85 | struct graph_node *end = new_node(END_GN); |
a53fbbf5 | 86 | end->element = command; |
880e24a1 | 87 | |
5a5d576b QY |
88 | // add node |
89 | if (add_node(currnode, end) != end) | |
eceb1066 | 90 | { |
5a5d576b QY |
91 | yyerror("Duplicate command."); |
92 | YYABORT; | |
eceb1066 | 93 | } |
e648e61a | 94 | fprintf(stderr, "Parsed full command successfully.\n"); |
880e24a1 | 95 | } |
92055a92 | 96 | |
2a23ca6e | 97 | sentence_root: WORD |
478bdaeb | 98 | { |
5a5d576b QY |
99 | struct graph_node *root = new_node(WORD_GN); |
100 | root->text = XSTRDUP(MTYPE_CMD_TOKENS, $1); | |
101 | ||
102 | currnode = add_node(startnode, root); | |
103 | if (currnode != root) | |
104 | free (root); | |
4b0abf24 | 105 | |
5a5d576b QY |
106 | free ($1); |
107 | $$ = currnode; | |
478bdaeb | 108 | }; |
92055a92 QY |
109 | |
110 | /* valid top level tokens */ | |
782d9789 QY |
111 | cmd_token: |
112 | placeholder_token | |
5a5d576b QY |
113 | { |
114 | currnode = add_node(currnode, $1); | |
115 | if (currnode != $1) | |
116 | free_node ($1); | |
117 | } | |
782d9789 | 118 | | literal_token |
5a5d576b QY |
119 | { |
120 | currnode = add_node(currnode, $1); | |
121 | if (currnode != $1) | |
122 | free_node ($1); | |
123 | } | |
478bdaeb | 124 | /* selectors and options are subgraphs with start and end nodes */ |
782d9789 | 125 | | selector |
478bdaeb QY |
126 | { |
127 | add_node(currnode, $1); | |
128 | currnode = selnode_end; | |
129 | selnode_start = selnode_end = NULL; | |
130 | } | |
782d9789 | 131 | | option |
478bdaeb QY |
132 | { |
133 | add_node(currnode, $1); | |
134 | currnode = optnode_end; | |
135 | optnode_start = optnode_end = NULL; | |
136 | } | |
782d9789 | 137 | ; |
92055a92 | 138 | |
782d9789 QY |
139 | cmd_token_seq: |
140 | %empty | |
141 | | cmd_token_seq cmd_token | |
142 | ; | |
143 | ||
144 | placeholder_token: | |
478bdaeb | 145 | IPV4 |
4b0abf24 QY |
146 | { |
147 | $$ = new_node(IPV4_GN); | |
5a5d576b QY |
148 | $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1); |
149 | free ($1); | |
4b0abf24 | 150 | } |
478bdaeb | 151 | | IPV4_PREFIX |
340a2b4a | 152 | { |
4b0abf24 | 153 | $$ = new_node(IPV4_PREFIX_GN); |
5a5d576b QY |
154 | $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1); |
155 | free ($1); | |
4b0abf24 | 156 | } |
478bdaeb | 157 | | IPV6 |
340a2b4a | 158 | { |
4b0abf24 | 159 | $$ = new_node(IPV6_GN); |
5a5d576b QY |
160 | $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1); |
161 | free ($1); | |
4b0abf24 | 162 | } |
478bdaeb | 163 | | IPV6_PREFIX |
340a2b4a | 164 | { |
4b0abf24 | 165 | $$ = new_node(IPV6_PREFIX_GN); |
5a5d576b QY |
166 | $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1); |
167 | free ($1); | |
4b0abf24 | 168 | } |
478bdaeb | 169 | | VARIABLE |
340a2b4a | 170 | { |
4b0abf24 | 171 | $$ = new_node(VARIABLE_GN); |
5a5d576b QY |
172 | $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1); |
173 | free ($1); | |
4b0abf24 | 174 | } |
478bdaeb QY |
175 | | RANGE |
176 | { | |
177 | $$ = new_node(RANGE_GN); | |
5a5d576b | 178 | $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1); |
478bdaeb QY |
179 | |
180 | // get the numbers out | |
181 | strsep(&yylval.string, "(-)"); | |
e648e61a QY |
182 | char *endptr; |
183 | $$->min = strtoll( strsep(&yylval.string, "(-)"), &endptr, 10 ); | |
184 | $$->max = strtoll( strsep(&yylval.string, "(-)"), &endptr, 10 ); | |
5a5d576b QY |
185 | |
186 | free ($1); | |
478bdaeb | 187 | } |
782d9789 QY |
188 | ; |
189 | ||
190 | literal_token: | |
478bdaeb QY |
191 | WORD |
192 | { | |
193 | $$ = new_node(WORD_GN); | |
5a5d576b QY |
194 | $$->text = XSTRDUP(MTYPE_CMD_TOKENS, $1); |
195 | free ($1); | |
478bdaeb QY |
196 | } |
197 | | NUMBER | |
198 | { | |
199 | $$ = new_node(NUMBER_GN); | |
200 | $$->value = yylval.integer; | |
201 | } | |
782d9789 | 202 | ; |
92055a92 | 203 | |
4b0abf24 | 204 | /* <selector|set> productions */ |
782d9789 | 205 | selector: |
478bdaeb | 206 | '<' selector_part '|' selector_element '>' |
2a23ca6e | 207 | { |
478bdaeb QY |
208 | // all the graph building is done in selector_element, |
209 | // so just return the selector subgraph head | |
210 | $$ = selnode_start; | |
2a23ca6e | 211 | }; |
782d9789 QY |
212 | |
213 | selector_part: | |
2a23ca6e | 214 | selector_part '|' selector_element |
782d9789 QY |
215 | | selector_element |
216 | ; | |
217 | ||
218 | selector_element: | |
340a2b4a | 219 | selector_element_root selector_token_seq |
478bdaeb QY |
220 | { |
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 | |
340a2b4a | 226 | selnode_start->end = selnode_end; // duh |
478bdaeb QY |
227 | } |
228 | ||
229 | // add element head as a child of the selector | |
230 | add_node(selnode_start, $1); | |
231 | ||
232 | if ($2->type != NUL_GN) { | |
233 | add_node($1, seqhead); | |
234 | add_node($2, selnode_end); | |
235 | } | |
236 | else | |
237 | add_node($1, selnode_end); | |
238 | ||
239 | seqhead = NULL; | |
240 | } | |
782d9789 QY |
241 | |
242 | selector_token_seq: | |
478bdaeb | 243 | %empty { $$ = new_node(NUL_GN); } |
2a23ca6e QY |
244 | | selector_token_seq selector_token |
245 | { | |
478bdaeb QY |
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 | |
249 | seqhead = $2; | |
250 | } | |
251 | else // chain on new node | |
252 | add_node($1, $2); | |
253 | ||
254 | $$ = $2; | |
2a23ca6e | 255 | } |
782d9789 QY |
256 | ; |
257 | ||
340a2b4a | 258 | selector_element_root: |
782d9789 QY |
259 | literal_token |
260 | | placeholder_token | |
478bdaeb QY |
261 | ; |
262 | ||
263 | selector_token: | |
340a2b4a | 264 | selector_element_root |
782d9789 QY |
265 | | option |
266 | ; | |
92055a92 QY |
267 | |
268 | /* [option|set] productions */ | |
2a23ca6e | 269 | option: '[' option_part ']' |
4b0abf24 QY |
270 | { |
271 | // add null path | |
272 | struct graph_node *nullpath = new_node(NUL_GN); | |
273 | add_node(optnode_start, nullpath); | |
274 | add_node(nullpath, optnode_end); | |
275 | ||
276 | $$ = optnode_start; | |
277 | }; | |
782d9789 QY |
278 | |
279 | option_part: | |
478bdaeb QY |
280 | option_part '|' option_element |
281 | | option_element | |
782d9789 QY |
282 | ; |
283 | ||
478bdaeb QY |
284 | option_element: |
285 | option_token_seq | |
2a23ca6e | 286 | { |
478bdaeb QY |
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); | |
291 | } | |
292 | ||
293 | add_node(optnode_start, seqhead); | |
294 | add_node($1, optnode_end); | |
2a23ca6e | 295 | } |
478bdaeb QY |
296 | |
297 | option_token_seq: | |
298 | option_token | |
299 | { $$ = seqhead = $1; } | |
300 | | option_token_seq option_token | |
301 | { $$ = add_node($1, $2); } | |
782d9789 QY |
302 | ; |
303 | ||
304 | option_token: | |
305 | literal_token | |
306 | | placeholder_token | |
307 | ; | |
308 | ||
92055a92 | 309 | %% |
4b0abf24 | 310 | |
92055a92 | 311 | void yyerror(char const *message) { |
880e24a1 | 312 | // fail on bad parse |
5a5d576b | 313 | fprintf(stderr, "Grammar error: %s\n", message); |
1a8c390d QY |
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); | |
e648e61a | 317 | else fprintf(stderr, "%lld\n", yylval.integer); |
1a8c390d | 318 | |
92055a92 | 319 | } |
782d9789 | 320 | |
478bdaeb | 321 | struct graph_node * |
eceb1066 | 322 | parse_command_format(struct graph_node *start, struct cmd_element *cmd) |
782d9789 | 323 | { |
eceb1066 | 324 | fprintf(stderr, "parsing: %s\n", cmd->string); |
478bdaeb | 325 | |
4b0abf24 | 326 | /* clear state pointers */ |
5a5d576b QY |
327 | startnode = start; |
328 | currnode = seqhead = NULL; | |
4b0abf24 QY |
329 | selnode_start = selnode_end = NULL; |
330 | optnode_start = optnode_end = NULL; | |
331 | ||
332 | // trace parser | |
de9d7e4f | 333 | yydebug = 0; |
a53fbbf5 QY |
334 | // command string |
335 | command = cmd; | |
2a23ca6e | 336 | // make flex read from a string |
eceb1066 | 337 | set_buffer_string(command->string); |
2a23ca6e | 338 | // parse command into DFA |
782d9789 | 339 | yyparse(); |
478bdaeb QY |
340 | // startnode points to command DFA |
341 | return startnode; | |
782d9789 | 342 | } |