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