]> git.tuebingen.mpg.de Git - paraslash.git/blob - yy/mp.y
Merge branch 'refs/heads/t/mp'
[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 }