vss: Improve error diagnostics.
[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 }