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