string.c: Fix typos in comment.
[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         /* bools */
214         case IS_SET:
215                 arg = node->children[0]->sv.strval;
216                 result->boolval = mp_is_set(arg, ctx);
217                 return ST_BOOLVAL;
218         case TRUE:
219                 result->boolval = true;
220                 return ST_BOOLVAL;
221         case FALSE:
222                 result->boolval = false;
223                 return ST_BOOLVAL;
224         case OR:
225                 eval_binary_op(node, ctx, &v1, &v2);
226                 result->boolval = v1.boolval || v2.boolval;
227                 return ST_BOOLVAL;
228         case AND:
229                 eval_binary_op(node, ctx, &v1, &v2);
230                 result->boolval = v1.boolval && v2.boolval;
231                 return ST_BOOLVAL;
232         case NOT:
233                 eval_node(node->children[0], ctx, &v1);
234                 result->boolval = !v1.boolval;
235                 return ST_BOOLVAL;
236         case EQUAL:
237                 ret = eval_node(node->children[0], ctx, &v1);
238                 eval_node(node->children[1], ctx, &v2);
239                 if (ret == ST_STRVAL)
240                         result->boolval = !strcmp(v1.strval, v2.strval);
241                 else
242                         result->boolval = v1.intval == v2.intval;
243                 return ST_BOOLVAL;
244         case NOT_EQUAL:
245                 ret = eval_node(node->children[0], ctx, &v1);
246                 eval_node(node->children[1], ctx, &v2);
247                 if (ret == ST_STRVAL)
248                         result->boolval = strcmp(v1.strval, v2.strval);
249                 else
250                         result->boolval = v1.intval != v2.intval;
251                 return ST_BOOLVAL;
252         case '<':
253                 eval_binary_op(node, ctx, &v1, &v2);
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 LESS_OR_EQUAL:
261                 eval_binary_op(node, ctx, &v1, &v2);
262                 result->boolval = v1.intval <= v2.intval;
263                 return ST_BOOLVAL;
264         case GREATER_OR_EQUAL:
265                 eval_binary_op(node, ctx, &v1, &v2);
266                 result->boolval = v1.intval >= v2.intval;
267                 return ST_BOOLVAL;
268         case FILENAME_MATCH:
269                 eval_binary_op(node, ctx, &v1, &v2);
270                 result->boolval = fnmatch(v2.wc_pattern.pat, v1.strval,
271                         v2.wc_pattern.flags) == 0;
272                 return ST_BOOLVAL;
273         case REGEX_MATCH:
274                 eval_binary_op(node, ctx, &v1, &v2);
275                 result->boolval = regexec(&v2.re_pattern.preg, v1.strval,
276                          0, NULL, 0) == 0;
277                 return ST_BOOLVAL;
278         case REGEX_PATTERN:
279                 result->re_pattern = node->sv.re_pattern;
280                 return ST_REGEX_PATTERN;
281         case WILDCARD_PATTERN:
282                 result->wc_pattern = node->sv.wc_pattern;
283                 return ST_WC_PATTERN;
284         default:
285                 PARA_EMERG_LOG("bug: invalid node id %d\n", node->id);
286                 exit(EXIT_FAILURE);
287         }
288 }
289
290 bool mp_eval_ast(struct mp_ast_node *root, struct mp_context *ctx)
291 {
292         union mp_semantic_value v;
293         int ret = eval_node(root, ctx, &v);
294
295         if (ret == ST_INTVAL)
296                 return v.intval != 0;
297         if (ret == ST_STRVAL)
298                 return v.strval[0] != 0;
299         if (ret == ST_BOOLVAL)
300                 return v.boolval;
301         assert(false);
302 }
303
304 %}
305
306 %union {
307         struct mp_ast_node *node;
308 }
309
310 /* terminals */
311 %token <node> NUM
312 %token <node> STRING_LITERAL
313 %token <node> REGEX_PATTERN
314 %token <node> WILDCARD_PATTERN
315
316 /* keywords with semantic value */
317 %token <node> PATH
318 %token <node> ARTIST
319 %token <node> TITLE
320 %token <node> ALBUM
321 %token <node> COMMENT
322 %token <node> YEAR
323 %token <node> NUM_ATTRIBUTES_SET
324 %token <node> NUM_PLAYED
325 %token <node> IMAGE_ID
326 %token <node> LYRICS_ID
327 %token <node> BITRATE
328 %token <node> FREQUENCY
329 %token <node> CHANNELS
330 %token <node> FALSE TRUE
331
332 /* keywords without semantic value */
333 %token IS_SET
334
335 /* operators, ordered by precendence */
336 %left OR
337 %left AND
338 %left EQUAL NOT_EQUAL
339 %left LESS_THAN LESS_OR_EQUAL GREATER_OR_EQUAL REGEX_MATCH FILENAME_MATCH
340 %left '-' '+'
341 %left '*' '/'
342 %right NOT NEG /* negation (unary minus) */
343
344 /* nonterminals */
345 %type <node> string
346 %type <node> exp
347 %type <node> boolexp
348
349 %%
350
351 program:
352         /* empty */ {*ast = NULL; return 0;}
353         | string {*ast = $1; return 0;}
354         | exp {*ast = $1; return 0;}
355         | boolexp {*ast = $1; return 0;}
356
357 string: STRING_LITERAL {$$ = $1;}
358         | PATH {$$ = mp_new_ast_leaf_node(PATH);}
359         | ARTIST {$$ = mp_new_ast_leaf_node(ARTIST);}
360         | TITLE {$$ = mp_new_ast_leaf_node(TITLE);}
361         | ALBUM {$$ = mp_new_ast_leaf_node(ALBUM);}
362         | COMMENT {$$ = mp_new_ast_leaf_node(COMMENT);}
363 ;
364
365 exp: NUM {$$ = $1;}
366         | exp '+' exp {$$ = ast_node_new_binary('+', $1, $3);}
367         | exp '-' exp {$$ = ast_node_new_binary('-', $1, $3);}
368         | exp '*' exp {$$ = ast_node_new_binary('*', $1, $3);}
369         | exp '/' exp {$$ = ast_node_new_binary('/', $1, $3);}
370         | '-' exp %prec NEG {$$ = ast_node_new_unary(NEG, $2);}
371         | '(' exp ')' {$$ = $2;}
372         | YEAR {$$ = mp_new_ast_leaf_node(YEAR);}
373         | NUM_ATTRIBUTES_SET {$$ = mp_new_ast_leaf_node(NUM_ATTRIBUTES_SET);}
374         | NUM_PLAYED {$$ = mp_new_ast_leaf_node(NUM_PLAYED);}
375         | IMAGE_ID {$$ = mp_new_ast_leaf_node(IMAGE_ID);}
376         | LYRICS_ID {$$ = mp_new_ast_leaf_node(LYRICS_ID);}
377         | BITRATE {$$ = mp_new_ast_leaf_node(BITRATE);}
378         | FREQUENCY {$$ = mp_new_ast_leaf_node(FREQUENCY);}
379         | CHANNELS {$$ = mp_new_ast_leaf_node(CHANNELS);}
380 ;
381
382 boolexp: IS_SET '(' STRING_LITERAL ')' {$$ = ast_node_new_unary(IS_SET, $3);}
383         | TRUE {$$ = mp_new_ast_leaf_node(TRUE);}
384         | FALSE {$$ = mp_new_ast_leaf_node(FALSE);}
385         | '(' boolexp ')' {$$ = $2;}
386         | boolexp OR boolexp {$$ = ast_node_new_binary(OR, $1, $3);}
387         | boolexp AND boolexp {$$ = ast_node_new_binary(AND, $1, $3);}
388         | NOT boolexp {$$ = ast_node_new_unary(NOT, $2);}
389         | exp EQUAL exp {$$ = ast_node_new_binary(EQUAL, $1, $3);}
390         | exp NOT_EQUAL exp {$$ = ast_node_new_binary(NOT_EQUAL, $1, $3);}
391         | exp '<' exp {$$ = ast_node_new_binary('<', $1, $3);}
392         | exp '>' exp {$$ = ast_node_new_binary('>', $1, $3);}
393         | exp LESS_OR_EQUAL exp {
394                 $$ = ast_node_new_binary(LESS_OR_EQUAL, $1, $3);
395         }
396         | exp GREATER_OR_EQUAL exp {
397                 $$ = ast_node_new_binary(GREATER_OR_EQUAL, $1, $3);
398         }
399         | string REGEX_MATCH REGEX_PATTERN {
400                 $$ = ast_node_new_binary(REGEX_MATCH, $1, $3);
401         }
402         | string FILENAME_MATCH WILDCARD_PATTERN {
403                 $$ = ast_node_new_binary(FILENAME_MATCH, $1, $3);
404         }
405         | string EQUAL string {$$ = ast_node_new_binary(EQUAL, $1, $3);}
406         | string NOT_EQUAL string {$$ = ast_node_new_binary(NOT_EQUAL, $1, $3);}
407 ;
408 %%
409
410 /* Called by yyparse() on error */
411 static void yyerror(YYLTYPE *llocp, struct mp_context *ctx,
412                 struct mp_ast_node **ast, mp_yyscan_t yyscanner, const char *msg)
413 {
414         mp_parse_error(llocp->first_line, ctx, "%s", msg);
415 }