0beddd6fc3e28252af96acb74cd12a34a0186ebd
[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
134 words:  word {
135                 $$ = create_argv(parser);
136                 argv_append($$, $1);
137         }
138         | words TOKEN_DELIM word {
139                 argv_append($1, $3);
140                 $$ = $1;
141         }
142
143 word:   TOKEN_WORD
144         | word TOKEN_WORD {
145                 word_append($1, $2);
146                 $$ = $1;
147         }
148
149 %%
150 void yyerror(struct grub2_parser *parser, const char *fmt, ...)
151 {
152         const char *str;
153         va_list ap;
154
155         va_start(ap, fmt);
156         str = talloc_vasprintf(parser, fmt, ap);
157         va_end(ap);
158
159         pb_log("parse error: %d('%s'): %s\n", yyget_lineno(parser->scanner),
160                                         yyget_text(parser->scanner), str);
161 }
162
163 struct grub2_statements *create_statements(struct grub2_parser *parser)
164 {
165         struct grub2_statements *stmts = talloc(parser,
166                         struct grub2_statements);
167         list_init(&stmts->list);
168         return stmts;
169 }
170
171 struct grub2_statement *create_statement_simple(struct grub2_parser *parser,
172                 struct grub2_argv *argv)
173 {
174         struct grub2_statement_simple *stmt =
175                 talloc(parser, struct grub2_statement_simple);
176         stmt->st.type = STMT_TYPE_SIMPLE;
177         stmt->st.exec = statement_simple_execute;
178         stmt->argv = argv;
179         return &stmt->st;
180 }
181
182 struct grub2_statement *create_statement_menuentry(struct grub2_parser *parser,
183                 struct grub2_argv *argv, struct grub2_statements *stmts)
184 {
185         struct grub2_statement_menuentry *stmt =
186                 talloc(parser, struct grub2_statement_menuentry);
187         stmt->st.type = STMT_TYPE_MENUENTRY;
188         stmt->st.exec = statement_menuentry_execute;
189         stmt->argv = argv;
190         stmt->statements = stmts;
191         return &stmt->st;
192 }
193
194 struct grub2_statement *create_statement_conditional(
195                 struct grub2_parser *parser,
196                 struct grub2_statement *condition,
197                 struct grub2_statements *statements)
198 {
199         struct grub2_statement_conditional *stmt =
200                 talloc(parser, struct grub2_statement_conditional);
201         stmt->st.type = STMT_TYPE_CONDITIONAL;
202         stmt->condition = condition;
203         stmt->statements = statements;
204         return &stmt->st;
205 }
206
207 struct grub2_statement *create_statement_if(struct grub2_parser *parser,
208                 struct grub2_statement *conditional,
209                 struct grub2_statements *elifs,
210                 struct grub2_statements *else_case)
211 {
212         struct grub2_statement_if *stmt =
213                 talloc(parser, struct grub2_statement_if);
214
215         list_add(&elifs->list, &conditional->list);
216
217         stmt->st.type = STMT_TYPE_IF;
218         stmt->st.exec = statement_if_execute;
219         stmt->conditionals = elifs;
220         stmt->else_case = else_case;
221         return &stmt->st;
222 }
223
224 struct grub2_statement *create_statement_block(struct grub2_parser *parser,
225                 struct grub2_statements *stmts)
226 {
227         struct grub2_statement_block *stmt =
228                 talloc(parser, struct grub2_statement_block);
229         stmt->st.type = STMT_TYPE_BLOCK;
230         stmt->st.exec = statement_block_execute;
231         stmt->statements = stmts;
232         return &stmt->st;
233 }
234
235 struct grub2_statement *create_statement_function(struct grub2_parser *parser,
236                 struct grub2_word *name, struct grub2_statements *body)
237 {
238         struct grub2_statement_function *stmt =
239                 talloc(parser, struct grub2_statement_function);
240         stmt->st.exec = statement_function_execute;
241         stmt->name = name;
242         stmt->body = body;
243         return &stmt->st;
244 }
245
246 void statement_append(struct grub2_statements *stmts,
247                 struct grub2_statement *stmt)
248 {
249         if (!stmt)
250                 return;
251         list_add_tail(&stmts->list, &stmt->list);
252 }
253
254 struct grub2_word *create_word_text(struct grub2_parser *parser,
255                 const char *text)
256 {
257         struct grub2_word *word = talloc(parser, struct grub2_word);
258         word->type = GRUB2_WORD_TEXT;
259         word->split = false;
260         word->text = talloc_strdup(word, text);
261         word->next = NULL;
262         word->last = word;
263         return word;
264 }
265
266 struct grub2_word *create_word_var(struct grub2_parser *parser,
267                 const char *name, bool split)
268 {
269         struct grub2_word *word = talloc(parser, struct grub2_word);
270         word->type = GRUB2_WORD_VAR;
271         word->name = talloc_strdup(word, name);
272         word->split = split;
273         word->next = NULL;
274         word->last = word;
275         return word;
276 }
277
278 struct grub2_argv *create_argv(struct grub2_parser *parser)
279 {
280         struct grub2_argv *argv = talloc(parser, struct grub2_argv);
281         list_init(&argv->words);
282         return argv;
283 }
284
285 void argv_append(struct grub2_argv *argv, struct grub2_word *word)
286 {
287         list_add_tail(&argv->words, &word->argv_list);
288 }
289
290 void word_append(struct grub2_word *w1, struct grub2_word *w2)
291 {
292         w1->last->next = w2;
293         w1->last = w2;
294 }
295
296 struct grub2_parser *grub2_parser_create(struct discover_context *ctx)
297 {
298         struct grub2_parser *parser;
299
300         parser = talloc(ctx, struct grub2_parser);
301         yylex_init_extra(parser, &parser->scanner);
302         parser->script = create_script(parser, ctx);
303
304         return parser;
305 }
306
307 void grub2_parser_parse(struct grub2_parser *parser, const char *filename,
308                 char *buf, int len)
309 {
310         YY_BUFFER_STATE bufstate;
311         int rc;
312
313         parser->script->filename = filename;
314
315         bufstate = yy_scan_bytes(buf, len - 1, parser->scanner);
316         yyset_lineno(1, parser->scanner);
317
318         rc = yyparse(parser, parser->scanner);
319
320         yy_delete_buffer(bufstate, parser->scanner);
321
322         if (!rc)
323                 script_execute(parser->script);
324 }
325