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