Version 2 moods.
[paraslash.git] / yy / mp.y
1 /*
2 * Copyright (C) 2017 Andre Noll <maan@tuebingen.mpg.de>
3 *
4 * Licensed under the GPL v2. For licencing details see COPYING.
5 */
6
7 /*
8 * Provide more verbose and specific error messages instead of just "syntax
9 * error".
10 */
11 %define parse.error verbose
12
13 /*
14 * Verbose error messages may contain incorrect information if LAC (Lookahead
15 * Correction) is not enabled.
16 */
17 %define parse.lac full
18
19 /* Avoid symbol clashes (lopsub might also expose yy* symbols). */
20 %define api.prefix {mp_yy}
21
22 /*
23 * Although locations are automatically enabled as soon as the grammar uses the
24 * special @N tokens, specifying %locations explicitly allows for more accurate
25 * syntax error messages.
26 */
27 %locations
28
29 /*
30 * Generate a pure (reentrant) parser. With this option enabled, yylval and
31 * yylloc become local variables in yyparse(), and a different calling
32 * convention is used for yylex().
33 */
34 %define api.pure full
35
36 /* Additional arguments to yylex(), yyparse() and yyerror() */
37 %param {struct mp_context *ctx}
38 %param {struct mp_ast_node **ast}
39 %param {mp_yyscan_t yyscanner} /* reentrant lexers */
40
41 %{
42 #include <fnmatch.h>
43 #include <regex.h>
44
45 #include "para.h"
46 #include "string.h"
47 #include "mp.h"
48 #include "mp.bison.h"
49 #include "error.h"
50
51 int yylex(MP_YYSTYPE *lvalp, MP_YYLTYPE *llocp, struct mp_context *ctx,
52 struct mp_ast_node **ast, mp_yyscan_t yyscanner);
53 static void yyerror(YYLTYPE *llocp, struct mp_context *ctx,
54 struct mp_ast_node **ast, mp_yyscan_t yyscanner, const char *msg);
55
56 enum semantic_types {
57 ST_STRVAL,
58 ST_INTVAL,
59 ST_BOOLVAL,
60 ST_REGEX_PATTERN,
61 ST_WC_PATTERN
62 };
63
64 static struct mp_ast_node *ast_node_raw(int id)
65 {
66 struct mp_ast_node *node = para_malloc(sizeof(struct mp_ast_node));
67 node->id = id;
68 return node;
69 }
70
71 /* This is non-static because it is also called from the lexer. */
72 struct mp_ast_node *mp_new_ast_leaf_node(int id)
73 {
74 struct mp_ast_node *node = ast_node_raw(id);
75 node->num_children = 0;
76 return node;
77 }
78
79 static struct mp_ast_node *ast_node_new_unary(int id, struct mp_ast_node *child)
80 {
81 struct mp_ast_node *node = ast_node_raw(id);
82 node->num_children = 1;
83 node->children = para_malloc(sizeof(struct mp_ast_node *));
84 node->children[0] = child;
85 return node;
86 }
87
88 static struct mp_ast_node *ast_node_new_binary(int id, struct mp_ast_node *left,
89 struct mp_ast_node *right)
90 {
91 struct mp_ast_node *node = ast_node_raw(id);
92 node->num_children = 2;
93 node->children = para_malloc(2 * sizeof(struct mp_ast_node *));
94 node->children[0] = left;
95 node->children[1] = right;
96 return node;
97 }
98
99 void mp_free_ast(struct mp_ast_node *root)
100 {
101 if (!root)
102 return;
103 if (root->num_children > 0) {
104 int i;
105 for (i = 0; i < root->num_children; i++)
106 mp_free_ast(root->children[i]);
107 free(root->children);
108 } else {
109 union mp_semantic_value *sv = &root->sv;
110 switch (root->id) {
111 case STRING_LITERAL:
112 free(sv->strval);
113 break;
114 case REGEX_PATTERN:
115 regfree(&sv->re_pattern.preg);
116 break;
117 case WILDCARD_PATTERN:
118 free(sv->wc_pattern.pat);
119 break;
120 }
121 }
122 free(root);
123 }
124
125 static int eval_node(struct mp_ast_node *node, struct mp_context *ctx,
126 union mp_semantic_value *result);
127
128 static void eval_binary_op(struct mp_ast_node *node, struct mp_context *ctx,
129 union mp_semantic_value *v1, union mp_semantic_value *v2)
130 {
131 eval_node(node->children[0], ctx, v1);
132 eval_node(node->children[1], ctx, v2);
133 }
134
135 static int eval_node(struct mp_ast_node *node, struct mp_context *ctx,
136 union mp_semantic_value *result)
137 {
138 int ret;
139 char *arg;
140 union mp_semantic_value v1, v2;
141
142 switch (node->id) {
143 /* strings */
144 case STRING_LITERAL:
145 result->strval = node->sv.strval;
146 return ST_STRVAL;
147 case PATH:
148 result->strval = mp_path(ctx);
149 return ST_STRVAL;
150 case ARTIST:
151 result->strval = mp_artist(ctx);
152 return ST_STRVAL;
153 case TITLE:
154 result->strval = mp_title(ctx);
155 return ST_STRVAL;
156 case ALBUM:
157 result->strval = mp_album(ctx);
158 return ST_STRVAL;
159 case COMMENT:
160 result->strval = mp_comment(ctx);
161 return ST_STRVAL;
162 /* integers */
163 case NUM:
164 result->intval = node->sv.intval;
165 return ST_INTVAL;
166 case '+':
167 eval_binary_op(node, ctx, &v1, &v2);
168 result->intval = v1.intval + v2.intval;
169 return ST_INTVAL;
170 case '-':
171 eval_binary_op(node, ctx, &v1, &v2);
172 result->intval = v1.intval - v2.intval;
173 return ST_INTVAL;
174 case '*':
175 eval_binary_op(node, ctx, &v1, &v2);
176 result->intval = v1.intval * v2.intval;
177 return ST_INTVAL;
178 case '/':
179 eval_binary_op(node, ctx, &v1, &v2);
180 if (v2.intval == 0) {
181 static bool warned;
182 if (!warned)
183 PARA_ERROR_LOG("division by zero\n");
184 warned = true;
185 result->intval = 0;
186 } else
187 result->intval = v1.intval / v2.intval;
188 return ST_INTVAL;
189 case NEG:
190 eval_node(node->children[0], ctx, &v1);
191 result->intval = -v1.intval;
192 return ST_INTVAL;
193 case YEAR:
194 result->intval = mp_year(ctx);
195 return ST_INTVAL;
196 case NUM_ATTRIBUTES_SET:
197 result->intval = mp_num_attributes_set(ctx);
198 return ST_INTVAL;
199 case NUM_PLAYED:
200 result->intval = mp_num_played(ctx);
201 return ST_INTVAL;
202 case IMAGE_ID:
203 result->intval = mp_image_id(ctx);
204 return ST_INTVAL;
205 case LYRICS_ID:
206 result->intval = mp_lyrics_id(ctx);
207 return ST_INTVAL;
208 case BITRATE:
209 result->intval = mp_bitrate(ctx);
210 return ST_INTVAL;
211 case FREQUENCY:
212 result->intval = mp_frequency(ctx);
213 return ST_INTVAL;
214 case CHANNELS:
215 result->intval= mp_channels(ctx);
216 return ST_INTVAL;
217 /* bools */
218 case IS_SET:
219 arg = node->children[0]->sv.strval;
220 result->boolval = mp_is_set(arg, ctx);
221 return ST_BOOLVAL;
222 case TRUE:
223 result->boolval = true;
224 return ST_BOOLVAL;
225 case FALSE:
226 result->boolval = false;
227 return ST_BOOLVAL;
228 case OR:
229 eval_binary_op(node, ctx, &v1, &v2);
230 result->boolval = v1.boolval || v2.boolval;
231 return ST_BOOLVAL;
232 case AND:
233 eval_binary_op(node, ctx, &v1, &v2);
234 result->boolval = v1.boolval && v2.boolval;
235 return ST_BOOLVAL;
236 case NOT:
237 eval_node(node->children[0], ctx, &v1);
238 result->boolval = !v1.boolval;
239 return ST_BOOLVAL;
240 case EQUAL:
241 ret = eval_node(node->children[0], ctx, &v1);
242 eval_node(node->children[1], ctx, &v2);
243 if (ret == ST_STRVAL)
244 result->boolval = !strcmp(v1.strval, v2.strval);
245 else
246 result->boolval = v1.intval == v2.intval;
247 return ST_BOOLVAL;
248 case NOT_EQUAL:
249 ret = eval_node(node->children[0], ctx, &v1);
250 eval_node(node->children[1], ctx, &v2);
251 if (ret == ST_STRVAL)
252 result->boolval = strcmp(v1.strval, v2.strval);
253 else
254 result->boolval = v1.intval != v2.intval;
255 return ST_BOOLVAL;
256 case '<':
257 eval_binary_op(node, ctx, &v1, &v2);
258 result->boolval = v1.intval < v2.intval;
259 return ST_BOOLVAL;
260 case '>':
261 eval_binary_op(node, ctx, &v1, &v2);
262 result->boolval = v1.intval > v2.intval;
263 return ST_BOOLVAL;
264 case LESS_OR_EQUAL:
265 eval_binary_op(node, ctx, &v1, &v2);
266 result->boolval = v1.intval <= v2.intval;
267 return ST_BOOLVAL;
268 case GREATER_OR_EQUAL:
269 eval_binary_op(node, ctx, &v1, &v2);
270 result->boolval = v1.intval >= v2.intval;
271 return ST_BOOLVAL;
272 case FILENAME_MATCH:
273 eval_binary_op(node, ctx, &v1, &v2);
274 result->boolval = fnmatch(v2.wc_pattern.pat, v1.strval,
275 v2.wc_pattern.flags) == 0;
276 return ST_BOOLVAL;
277 case REGEX_MATCH:
278 eval_binary_op(node, ctx, &v1, &v2);
279 result->boolval = regexec(&v2.re_pattern.preg, v1.strval,
280 0, NULL, 0) == 0;
281 return ST_BOOLVAL;
282 case REGEX_PATTERN:
283 result->re_pattern = node->sv.re_pattern;
284 return ST_REGEX_PATTERN;
285 case WILDCARD_PATTERN:
286 result->wc_pattern = node->sv.wc_pattern;
287 return ST_WC_PATTERN;
288 default:
289 PARA_EMERG_LOG("bug: invalid node id %d\n", node->id);
290 exit(EXIT_FAILURE);
291 }
292 }
293
294 bool mp_eval_ast(struct mp_ast_node *root, struct mp_context *ctx)
295 {
296 union mp_semantic_value v;
297 int ret = eval_node(root, ctx, &v);
298
299 if (ret == ST_INTVAL)
300 return v.intval != 0;
301 if (ret == ST_STRVAL)
302 return v.strval[0] != 0;
303 if (ret == ST_BOOLVAL)
304 return v.boolval;
305 assert(false);
306 }
307
308 %}
309
310 %union {
311 struct mp_ast_node *node;
312 }
313
314 /* terminals */
315 %token <node> NUM
316 %token <node> STRING_LITERAL
317 %token <node> REGEX_PATTERN
318 %token <node> WILDCARD_PATTERN
319
320 /* keywords with semantic value */
321 %token <node> PATH
322 %token <node> ARTIST
323 %token <node> TITLE
324 %token <node> ALBUM
325 %token <node> COMMENT
326 %token <node> YEAR
327 %token <node> NUM_ATTRIBUTES_SET
328 %token <node> NUM_PLAYED
329 %token <node> IMAGE_ID
330 %token <node> LYRICS_ID
331 %token <node> BITRATE
332 %token <node> FREQUENCY
333 %token <node> CHANNELS
334 %token <node> FALSE TRUE
335
336 /* keywords without semantic value */
337 %token IS_SET
338
339 /* operators, ordered by precendence */
340 %left OR
341 %left AND
342 %left EQUAL NOT_EQUAL
343 %left LESS_THAN LESS_OR_EQUAL GREATER_OR_EQUAL REGEX_MATCH FILENAME_MATCH
344 %left '-' '+'
345 %left '*' '/'
346 %right NOT NEG /* negation (unary minus) */
347
348 /* nonterminals */
349 %type <node> string
350 %type <node> exp
351 %type <node> boolexp
352
353 %%
354
355 program:
356 /* empty */ {*ast = NULL; return 0;}
357 | string {*ast = $1; return 0;}
358 | exp {*ast = $1; return 0;}
359 | boolexp {*ast = $1; return 0;}
360
361 string: STRING_LITERAL {$$ = $1;}
362 | PATH {$$ = mp_new_ast_leaf_node(PATH);}
363 | ARTIST {$$ = mp_new_ast_leaf_node(ARTIST);}
364 | TITLE {$$ = mp_new_ast_leaf_node(TITLE);}
365 | ALBUM {$$ = mp_new_ast_leaf_node(ALBUM);}
366 | COMMENT {$$ = mp_new_ast_leaf_node(COMMENT);}
367 ;
368
369 exp: NUM {$$ = $1;}
370 | exp '+' exp {$$ = ast_node_new_binary('+', $1, $3);}
371 | exp '-' exp {$$ = ast_node_new_binary('-', $1, $3);}
372 | exp '*' exp {$$ = ast_node_new_binary('*', $1, $3);}
373 | exp '/' exp {$$ = ast_node_new_binary('/', $1, $3);}
374 | '-' exp %prec NEG {$$ = ast_node_new_unary(NEG, $2);}
375 | '(' exp ')' {$$ = $2;}
376 | YEAR {$$ = mp_new_ast_leaf_node(YEAR);}
377 | NUM_ATTRIBUTES_SET {$$ = mp_new_ast_leaf_node(NUM_ATTRIBUTES_SET);}
378 | NUM_PLAYED {$$ = mp_new_ast_leaf_node(NUM_PLAYED);}
379 | IMAGE_ID {$$ = mp_new_ast_leaf_node(IMAGE_ID);}
380 | LYRICS_ID {$$ = mp_new_ast_leaf_node(LYRICS_ID);}
381 | BITRATE {$$ = mp_new_ast_leaf_node(BITRATE);}
382 | FREQUENCY {$$ = mp_new_ast_leaf_node(FREQUENCY);}
383 | CHANNELS {$$ = mp_new_ast_leaf_node(CHANNELS);}
384 ;
385
386 boolexp: IS_SET '(' STRING_LITERAL ')' {$$ = ast_node_new_unary(IS_SET, $3);}
387 | TRUE {$$ = mp_new_ast_leaf_node(TRUE);}
388 | FALSE {$$ = mp_new_ast_leaf_node(FALSE);}
389 | '(' boolexp ')' {$$ = $2;}
390 | boolexp OR boolexp {$$ = ast_node_new_binary(OR, $1, $3);}
391 | boolexp AND boolexp {$$ = ast_node_new_binary(AND, $1, $3);}
392 | NOT boolexp {$$ = ast_node_new_unary(NOT, $2);}
393 | exp EQUAL exp {$$ = ast_node_new_binary(EQUAL, $1, $3);}
394 | exp NOT_EQUAL exp {$$ = ast_node_new_binary(NOT_EQUAL, $1, $3);}
395 | exp '<' exp {$$ = ast_node_new_binary('<', $1, $3);}
396 | exp '>' exp {$$ = ast_node_new_binary('>', $1, $3);}
397 | exp LESS_OR_EQUAL exp {
398 $$ = ast_node_new_binary(LESS_OR_EQUAL, $1, $3);
399 }
400 | exp GREATER_OR_EQUAL exp {
401 $$ = ast_node_new_binary(GREATER_OR_EQUAL, $1, $3);
402 }
403 | string REGEX_MATCH REGEX_PATTERN {
404 $$ = ast_node_new_binary(REGEX_MATCH, $1, $3);
405 }
406 | string FILENAME_MATCH WILDCARD_PATTERN {
407 $$ = ast_node_new_binary(FILENAME_MATCH, $1, $3);
408 }
409 | string EQUAL string {$$ = ast_node_new_binary(EQUAL, $1, $3);}
410 | string NOT_EQUAL string {$$ = ast_node_new_binary(NOT_EQUAL, $1, $3);}
411 ;
412 %%
413
414 /* Called by yyparse() on error */
415 static void yyerror(YYLTYPE *llocp, struct mp_context *ctx,
416 struct mp_ast_node **ast, mp_yyscan_t yyscanner, const char *msg)
417 {
418 mp_parse_error(llocp->first_line, ctx, "%s", msg);
419 }