discover/grub2: Allow EOF as a statement terminator
[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
10 #include "grub2.h"
11 #include "parser.h"
12 #include "lexer.h"
13
14 static void print_token(FILE *fp, int type, YYSTYPE value);
15
16 #define YYLEX_PARAM parser->scanner
17 #define YYPRINT(f, t, v) print_token(f, t, v)
18
19 static void yyerror(struct grub2_parser *, char const *s);
20 %}
21
22 %union {
23         struct grub2_word       *word;
24         struct grub2_argv       *argv;
25         struct grub2_statement  *statement;
26         struct grub2_statements *statements;
27 }
28
29 /* reserved words */
30 %token  TOKEN_LDSQBRACKET       "[["
31 %token  TOKEN_RDSQBRACKET       "]]"
32 %token  TOKEN_CASE              "case"
33 %token  TOKEN_DO                "do"
34 %token  TOKEN_DONE              "done"
35 %token  TOKEN_ELIF              "elif"
36 %token  TOKEN_ELSE              "else"
37 %token  TOKEN_ESAC              "esac"
38 %token  TOKEN_FI                "fi"
39 %token  TOKEN_FOR               "for"
40 %token  TOKEN_FUNCTION          "function"
41 %token  TOKEN_IF                "if"
42 %token  TOKEN_IN                "in"
43 %token  TOKEN_MENUENTRY         "menuentry"
44 %token  TOKEN_SELECT            "select"
45 %token  TOKEN_SUBMENU           "submenu"
46 %token  TOKEN_THEN              "then"
47 %token  TOKEN_TIME              "time"
48 %token  TOKEN_UTIL              "until"
49 %token  TOKEN_WHILE             "while"
50
51 %type <statement>       statement
52 %type <statements>      statements
53 %type <statement>       conditional
54 %type <statement>       elif
55 %type <statements>      elifs
56 %type <argv>            words
57 %type <word>            word
58
59 /* syntax */
60 %token  TOKEN_EOL
61 %token  TOKEN_DELIM
62 %token  <word> TOKEN_WORD
63 %token  TOKEN_EOF 0
64
65 %start  script
66 %debug
67
68 %%
69
70 script: statements {
71                 parser->script->statements = $1;
72         }
73
74 eol:    TOKEN_EOL | TOKEN_EOF;
75
76 statements: /* empty */ {
77                 $$ = create_statements(parser);
78         }
79         | statements statement eol {
80                 statement_append($1, $2);
81                 $$ = $1;
82         }
83         | statements TOKEN_EOL {
84                 $$ = $1;
85         }
86
87 conditional: statement TOKEN_EOL "then" TOKEN_EOL 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" TOKEN_EOL
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, char const *s)
150 {
151         fprintf(stderr, "%d: error: %s '%s'\n",
152                         yyget_lineno(parser->scanner),
153                         s, yyget_text(parser->scanner));
154 }
155
156 static void print_token(FILE *fp, int type, YYSTYPE value)
157 {
158         if (type != TOKEN_WORD)
159                 return;
160         fprintf(fp, "%s", value.word->text);
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 = NULL;
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, char *buf, int len)
308 {
309         YY_BUFFER_STATE bufstate;
310         int rc;
311
312         bufstate = yy_scan_bytes(buf, len - 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