discover/grub2: Add support for for-loops
[petitboot] / discover / grub2 / parser.y
1
2 %pure-parser
3 %lex-param { yyscan_t scanner }
4 %parse-param { struct grub2_parser *parser }
5 %parse-param { void *scanner }
6 %error-verbose
7
8 %{
9 #include <talloc/talloc.h>
10 #include <log/log.h>
11
12 #include "grub2.h"
13
14 void yyerror(struct grub2_parser *parser, const char *fmt, ...);
15 %}
16
17 %union {
18         struct grub2_word       *word;
19         struct grub2_argv       *argv;
20         struct grub2_statement  *statement;
21         struct grub2_statements *statements;
22 }
23
24 /* reserved words */
25 %token  TOKEN_LDSQBRACKET       "[["
26 %token  TOKEN_RDSQBRACKET       "]]"
27 %token  TOKEN_CASE              "case"
28 %token  TOKEN_DO                "do"
29 %token  TOKEN_DONE              "done"
30 %token  TOKEN_ELIF              "elif"
31 %token  TOKEN_ELSE              "else"
32 %token  TOKEN_ESAC              "esac"
33 %token  TOKEN_FI                "fi"
34 %token  TOKEN_FOR               "for"
35 %token  TOKEN_FUNCTION          "function"
36 %token  TOKEN_IF                "if"
37 %token  TOKEN_IN                "in"
38 %token  TOKEN_MENUENTRY         "menuentry"
39 %token  TOKEN_SELECT            "select"
40 %token  TOKEN_SUBMENU           "submenu"
41 %token  TOKEN_THEN              "then"
42 %token  TOKEN_TIME              "time"
43 %token  TOKEN_UTIL              "until"
44 %token  TOKEN_WHILE             "while"
45
46 %type <statement>       statement
47 %type <statements>      statements
48 %type <statement>       conditional
49 %type <statement>       elif
50 %type <statements>      elifs
51 %type <argv>            words
52 %type <word>            word
53
54 /* syntax */
55 %token  TOKEN_EOL
56 %token  TOKEN_DELIM
57 %token  <word> TOKEN_WORD
58 %token  TOKEN_EOF 0
59
60 %start  script
61 %debug
62
63 %{
64 #include "lexer.h"
65 %}
66
67 %%
68
69 script: statements {
70                 parser->script->statements = $1;
71         }
72
73 eol:    TOKEN_EOL | TOKEN_EOF;
74
75 statements: /* empty */ {
76                 $$ = create_statements(parser);
77         }
78         | statements statement eol {
79                 statement_append($1, $2);
80                 $$ = $1;
81         }
82         | statements TOKEN_EOL {
83                 $$ = $1;
84         }
85
86 sep:    TOKEN_DELIM | TOKEN_EOL;
87
88 conditional: statement TOKEN_EOL "then" sep statements {
89                 $$ = create_statement_conditional(parser, $1, $5);
90         }
91
92 elif: "elif" TOKEN_DELIM conditional {
93                 $$ = $3;
94       }
95
96 elifs: /* empty */ {
97                 $$ = create_statements(parser);
98         }
99         | elifs elif {
100                 statement_append($1, $2);
101                 $$ = $1;
102         }
103
104 statement:
105         words {
106                    $$ = create_statement_simple(parser, $1);
107         }
108         | '{' statements '}' {
109                 $$ = create_statement_block(parser, $2);
110         }
111         | "if" TOKEN_DELIM conditional elifs "fi" {
112                 $$ = create_statement_if(parser, $3, $4, NULL);
113         }
114         | "if" TOKEN_DELIM conditional
115                 elifs
116                 "else" sep
117                 statements
118                 "fi" {
119                 $$ = create_statement_if(parser, $3, $4, $7);
120         }
121         | "function" TOKEN_DELIM word TOKEN_DELIM '{' statements '}' {
122                 $$ = create_statement_function(parser, $3, $6);
123         }
124         | "menuentry" TOKEN_DELIM words TOKEN_DELIM
125                 '{' statements '}' {
126                 $$ = create_statement_menuentry(parser, $3, $6);
127         }
128         | "submenu" TOKEN_DELIM words TOKEN_DELIM
129                 '{' statements '}' {
130                 /* we just flatten everything */
131                 $$ = create_statement_block(parser, $6);
132         }
133         | "for" TOKEN_DELIM word TOKEN_DELIM "in" TOKEN_DELIM words TOKEN_EOL
134                 "do" sep
135                 statements
136                 "done" {
137                 $$ = create_statement_for(parser, $3, $7, $11);
138         }
139
140 words:  word {
141                 $$ = create_argv(parser);
142                 argv_append($$, $1);
143         }
144         | words TOKEN_DELIM word {
145                 argv_append($1, $3);
146                 $$ = $1;
147         }
148
149 word:   TOKEN_WORD
150         | word TOKEN_WORD {
151                 word_append($1, $2);
152                 $$ = $1;
153         }
154
155 %%
156 void yyerror(struct grub2_parser *parser, const char *fmt, ...)
157 {
158         const char *str;
159         va_list ap;
160
161         va_start(ap, fmt);
162         str = talloc_vasprintf(parser, fmt, ap);
163         va_end(ap);
164
165         pb_log("parse error: %d('%s'): %s\n", yyget_lineno(parser->scanner),
166                                         yyget_text(parser->scanner), str);
167 }
168
169 struct grub2_statements *create_statements(struct grub2_parser *parser)
170 {
171         struct grub2_statements *stmts = talloc(parser,
172                         struct grub2_statements);
173         list_init(&stmts->list);
174         return stmts;
175 }
176
177 struct grub2_statement *create_statement_simple(struct grub2_parser *parser,
178                 struct grub2_argv *argv)
179 {
180         struct grub2_statement_simple *stmt =
181                 talloc(parser, struct grub2_statement_simple);
182         stmt->st.type = STMT_TYPE_SIMPLE;
183         stmt->st.exec = statement_simple_execute;
184         stmt->argv = argv;
185         return &stmt->st;
186 }
187
188 struct grub2_statement *create_statement_menuentry(struct grub2_parser *parser,
189                 struct grub2_argv *argv, struct grub2_statements *stmts)
190 {
191         struct grub2_statement_menuentry *stmt =
192                 talloc(parser, struct grub2_statement_menuentry);
193         stmt->st.type = STMT_TYPE_MENUENTRY;
194         stmt->st.exec = statement_menuentry_execute;
195         stmt->argv = argv;
196         stmt->statements = stmts;
197         return &stmt->st;
198 }
199
200 struct grub2_statement *create_statement_conditional(
201                 struct grub2_parser *parser,
202                 struct grub2_statement *condition,
203                 struct grub2_statements *statements)
204 {
205         struct grub2_statement_conditional *stmt =
206                 talloc(parser, struct grub2_statement_conditional);
207         stmt->st.type = STMT_TYPE_CONDITIONAL;
208         stmt->condition = condition;
209         stmt->statements = statements;
210         return &stmt->st;
211 }
212
213 struct grub2_statement *create_statement_if(struct grub2_parser *parser,
214                 struct grub2_statement *conditional,
215                 struct grub2_statements *elifs,
216                 struct grub2_statements *else_case)
217 {
218         struct grub2_statement_if *stmt =
219                 talloc(parser, struct grub2_statement_if);
220
221         list_add(&elifs->list, &conditional->list);
222
223         stmt->st.type = STMT_TYPE_IF;
224         stmt->st.exec = statement_if_execute;
225         stmt->conditionals = elifs;
226         stmt->else_case = else_case;
227         return &stmt->st;
228 }
229
230 struct grub2_statement *create_statement_block(struct grub2_parser *parser,
231                 struct grub2_statements *stmts)
232 {
233         struct grub2_statement_block *stmt =
234                 talloc(parser, struct grub2_statement_block);
235         stmt->st.type = STMT_TYPE_BLOCK;
236         stmt->st.exec = statement_block_execute;
237         stmt->statements = stmts;
238         return &stmt->st;
239 }
240
241 struct grub2_statement *create_statement_function(struct grub2_parser *parser,
242                 struct grub2_word *name, struct grub2_statements *body)
243 {
244         struct grub2_statement_function *stmt =
245                 talloc(parser, struct grub2_statement_function);
246         stmt->st.exec = statement_function_execute;
247         stmt->name = name;
248         stmt->body = body;
249         return &stmt->st;
250 }
251
252 struct grub2_statement *create_statement_for(struct grub2_parser *parser,
253                 struct grub2_word *var, struct grub2_argv *list,
254                 struct grub2_statements *body)
255 {
256         struct grub2_statement_for *stmt =
257                 talloc(parser, struct grub2_statement_for);
258         stmt->st.exec = statement_for_execute;
259         stmt->var = var;
260         stmt->list = list;
261         stmt->body = body;
262         return &stmt->st;
263 }
264
265 void statement_append(struct grub2_statements *stmts,
266                 struct grub2_statement *stmt)
267 {
268         if (!stmt)
269                 return;
270         list_add_tail(&stmts->list, &stmt->list);
271 }
272
273 struct grub2_word *create_word_text(struct grub2_parser *parser,
274                 const char *text)
275 {
276         struct grub2_word *word = talloc(parser, struct grub2_word);
277         word->type = GRUB2_WORD_TEXT;
278         word->split = false;
279         word->text = talloc_strdup(word, text);
280         word->next = NULL;
281         word->last = word;
282         return word;
283 }
284
285 struct grub2_word *create_word_var(struct grub2_parser *parser,
286                 const char *name, bool split)
287 {
288         struct grub2_word *word = talloc(parser, struct grub2_word);
289         word->type = GRUB2_WORD_VAR;
290         word->name = talloc_strdup(word, name);
291         word->split = split;
292         word->next = NULL;
293         word->last = word;
294         return word;
295 }
296
297 struct grub2_argv *create_argv(struct grub2_parser *parser)
298 {
299         struct grub2_argv *argv = talloc(parser, struct grub2_argv);
300         list_init(&argv->words);
301         return argv;
302 }
303
304 void argv_append(struct grub2_argv *argv, struct grub2_word *word)
305 {
306         list_add_tail(&argv->words, &word->argv_list);
307 }
308
309 void word_append(struct grub2_word *w1, struct grub2_word *w2)
310 {
311         w1->last->next = w2;
312         w1->last = w2;
313 }
314
315 struct grub2_parser *grub2_parser_create(struct discover_context *ctx)
316 {
317         struct grub2_parser *parser;
318
319         parser = talloc(ctx, struct grub2_parser);
320         yylex_init_extra(parser, &parser->scanner);
321         parser->script = create_script(parser, ctx);
322
323         return parser;
324 }
325
326 void grub2_parser_parse(struct grub2_parser *parser, const char *filename,
327                 char *buf, int len)
328 {
329         YY_BUFFER_STATE bufstate;
330         int rc;
331
332         parser->script->filename = filename;
333
334         bufstate = yy_scan_bytes(buf, len - 1, parser->scanner);
335         yyset_lineno(1, parser->scanner);
336
337         rc = yyparse(parser, parser->scanner);
338
339         yy_delete_buffer(bufstate, parser->scanner);
340
341         if (!rc)
342                 script_execute(parser->script);
343 }
344