Mercurial > audlegacy
comparison src/audacious/tuple_compiler.c @ 3409:86dafe2300f7 trunk
Added Tuplez compiler (not used yet, though) and some related changes in
tuple code.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 30 Aug 2007 23:41:33 +0300 |
parents | |
children | 9580bb3e58fa |
comparison
equal
deleted
inserted
replaced
3408:aea3349e2c62 | 3409:86dafe2300f7 |
---|---|
1 /* | |
2 * Audacious - Tuplez compiler | |
3 * Copyright (c) 2007 Matti 'ccr' Hämäläinen | |
4 * | |
5 * This program is free software; you can redistribute it and/or modify | |
6 * it under the terms of the GNU General Public License as published by | |
7 * the Free Software Foundation; under version 3 of the License. | |
8 * | |
9 * This program is distributed in the hope that it will be useful, | |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 * GNU General Public License for more details. | |
13 * | |
14 * You should have received a copy of the GNU General Public License | |
15 * along with this program. If not, see <http://www.gnu.org/licenses>. | |
16 * | |
17 * The Audacious team does not consider modular code linking to | |
18 * Audacious or using our public API to be a derived work. | |
19 */ | |
20 | |
21 /* | |
22 * What's this? | |
23 * ------------ | |
24 * Nothing really. A prototype / pseudo-C for an improved Tuple formatting | |
25 * system, where the format string is "compiled" into a tree structure, | |
26 * which can then be traversed fast while "evaluating". This file does | |
27 * not represent anything but some of my (ccr) ideas for the concept. | |
28 * | |
29 * The basic ideas are: | |
30 * 1) compiled structure for faster traversing | |
31 * 2) sub-expression removal / constant elimination | |
32 * 3) indexes and/or hashes for tuple entries for faster access | |
33 * 4) avoid expensive memory re-allocation | |
34 * | |
35 * and possibly 5) caching of certain things | |
36 * | |
37 * | |
38 * TODO: | |
39 * - implement definitions (${=foo,"baz"} ${=foo,1234}) | |
40 * - implement functions | |
41 * - implement handling of external expressions | |
42 * - error handling issues? | |
43 * - evaluation context: how local variables should REALLY work? | |
44 */ | |
45 | |
46 #include "config.h" | |
47 #include <assert.h> | |
48 #include <stdarg.h> | |
49 #include "tuple_compiler.h" | |
50 | |
51 | |
52 void tuple_error(const char *fmt, ...) | |
53 { | |
54 va_list ap; | |
55 fprintf(stderr, "compiler: "); | |
56 va_start(ap, fmt); | |
57 vfprintf(stderr, fmt, ap); | |
58 va_end(ap); | |
59 exit(5); | |
60 } | |
61 | |
62 | |
63 static void tuple_evalctx_free_var(TupleEvalVar *var) | |
64 { | |
65 assert(var != NULL); | |
66 | |
67 g_free(var->defval); | |
68 g_free(var->name); | |
69 g_free(var); | |
70 } | |
71 | |
72 | |
73 static void tuple_evalctx_free_function(TupleEvalFunc *func) | |
74 { | |
75 assert(func != NULL); | |
76 | |
77 g_free(func->name); | |
78 g_free(func); | |
79 } | |
80 | |
81 | |
82 /* Initialize an evaluation context | |
83 */ | |
84 TupleEvalContext * tuple_evalctx_new(void) | |
85 { | |
86 return g_new0(TupleEvalContext, 1); | |
87 } | |
88 | |
89 | |
90 /* "Reset" the evaluation context, clean up locally set variables, | |
91 * but leave globals. | |
92 */ | |
93 void tuple_evalctx_reset(TupleEvalContext *ctx) | |
94 { | |
95 gint i; | |
96 | |
97 /* Free local variables */ | |
98 for (i = 0; i < ctx->nvariables; i++) | |
99 if (ctx->variables[i]) { | |
100 ctx->variables[i]->dictref = NULL; | |
101 | |
102 if (ctx->variables[i]->islocal) | |
103 tuple_evalctx_free_var(ctx->variables[i]); | |
104 } | |
105 | |
106 } | |
107 | |
108 | |
109 /* Free an evaluation context and associated data | |
110 */ | |
111 void tuple_evalctx_free(TupleEvalContext *ctx) | |
112 { | |
113 gint i; | |
114 | |
115 if (!ctx) return; | |
116 | |
117 /* Deallocate variables */ | |
118 for (i = 0; i < ctx->nvariables; i++) | |
119 if (ctx->variables[i]) | |
120 tuple_evalctx_free_var(ctx->variables[i]); | |
121 | |
122 g_free(ctx->variables); | |
123 | |
124 /* Deallocate functions */ | |
125 for (i = 0; i < ctx->nfunctions; i++) | |
126 if (ctx->functions[i]) | |
127 tuple_evalctx_free_function(ctx->functions[i]); | |
128 | |
129 g_free(ctx->functions); | |
130 | |
131 /* Zero context */ | |
132 memset(ctx, 0, sizeof(TupleEvalContext)); | |
133 } | |
134 | |
135 | |
136 gint tuple_evalctx_add_var(TupleEvalContext *ctx, gchar *name, gboolean islocal, gint type) | |
137 { | |
138 gint i; | |
139 TupleEvalVar * tmp = g_new0(TupleEvalVar, 1); | |
140 | |
141 tmp->name = g_strdup(name); | |
142 tmp->islocal = islocal; | |
143 tmp->type = type; | |
144 | |
145 /* Find a free slot */ | |
146 for (i = 0; i < ctx->nvariables; i++) | |
147 if (!ctx->variables[i]) { | |
148 ctx->variables[i] = tmp; | |
149 return i; | |
150 } | |
151 | |
152 i = ctx->nvariables; | |
153 ctx->variables = g_renew(TupleEvalVar *, ctx->variables, ctx->nvariables + MIN_ALLOC_NODES); | |
154 memset(&(ctx->variables[ctx->nvariables]), 0, MIN_ALLOC_NODES * sizeof(TupleEvalVar *)); | |
155 ctx->nvariables += MIN_ALLOC_NODES; | |
156 ctx->variables[i] = tmp; | |
157 | |
158 return i; | |
159 } | |
160 | |
161 | |
162 gint tuple_evalctx_add_function(TupleEvalContext *ctx, gchar *name) | |
163 { | |
164 assert(ctx != NULL); | |
165 assert(name != NULL); | |
166 | |
167 return -1; | |
168 } | |
169 | |
170 | |
171 static void tuple_evalnode_insert(TupleEvalNode **nodes, TupleEvalNode *node) | |
172 { | |
173 assert(nodes != NULL); | |
174 assert(node != NULL); | |
175 | |
176 if (*nodes) { | |
177 node->prev = (*nodes)->prev; | |
178 (*nodes)->prev->next = node; | |
179 (*nodes)->prev = node; | |
180 node->next = NULL; | |
181 } else { | |
182 *nodes = node; | |
183 node->prev = node; | |
184 node->next = NULL; | |
185 } | |
186 } | |
187 | |
188 | |
189 static TupleEvalNode *tuple_evalnode_new(void) | |
190 { | |
191 return g_new0(TupleEvalNode, 1); | |
192 } | |
193 | |
194 | |
195 void tuple_evalnode_free(TupleEvalNode *expr) | |
196 { | |
197 TupleEvalNode *curr = expr, *next; | |
198 | |
199 while (curr) { | |
200 next = curr->next; | |
201 | |
202 g_free(curr->text); | |
203 | |
204 if (curr->children) | |
205 tuple_evalnode_free(curr->children); | |
206 | |
207 g_free(curr); | |
208 | |
209 curr = next; | |
210 } | |
211 } | |
212 | |
213 | |
214 static TupleEvalNode *tuple_compiler_pass1(gint *level, TupleEvalContext *ctx, gchar **expression); | |
215 | |
216 | |
217 static gboolean tc_get_item(gchar **str, gchar *buf, size_t max, gchar endch, gboolean *literal, gchar *errstr, gchar *item) | |
218 { | |
219 size_t i = 0; | |
220 gchar *s = *str, tmpendch; | |
221 assert(str != NULL); | |
222 assert(buf != NULL); | |
223 | |
224 if (*s == '"') { | |
225 if (*literal == FALSE) { | |
226 tuple_error("Literal string value not allowed in '%s'.\n", item); | |
227 return FALSE; | |
228 } | |
229 s++; | |
230 *literal = TRUE; | |
231 tmpendch = '"'; | |
232 } else { | |
233 *literal = FALSE; | |
234 tmpendch = endch; | |
235 } | |
236 | |
237 if (*literal == FALSE) { | |
238 while (*s != '\0' && *s != tmpendch && (isalnum(*s) || *s == '-') && i < (max - 1)) { | |
239 buf[i++] = *(s++); | |
240 } | |
241 | |
242 if (*s != tmpendch && *s != '}' && !isalnum(*s) && *s != '-') { | |
243 tuple_error("Invalid field '%s' in '%s'.\n", *str, item); | |
244 return FALSE; | |
245 } else if (*s != tmpendch) { | |
246 tuple_error("Expected '%c' in '%s'.\n", tmpendch, item); | |
247 return FALSE; | |
248 } | |
249 } else { | |
250 while (*s != '\0' && *s != tmpendch && i < (max - 1)) { | |
251 if (*s == '\\') s++; | |
252 buf[i++] = *(s++); | |
253 } | |
254 } | |
255 buf[i] = '\0'; | |
256 | |
257 if (*literal) { | |
258 if (*s == tmpendch) | |
259 s++; | |
260 else { | |
261 tuple_error("Expected literal string end ('%c') in '%s'.\n", tmpendch, item); | |
262 return FALSE; | |
263 } | |
264 } | |
265 | |
266 if (*s != endch) { | |
267 tuple_error("Expected '%c' after %s in '%s'\n", endch, errstr, item); | |
268 return FALSE; | |
269 } else { | |
270 *str = s; | |
271 return TRUE; | |
272 } | |
273 } | |
274 | |
275 | |
276 static gint tc_get_variable(TupleEvalContext *ctx, gchar *name, gint type) | |
277 { | |
278 gint i; | |
279 | |
280 if (*name == '\0') return -1; | |
281 | |
282 for (i = 0; i < ctx->nvariables; i++) | |
283 if (ctx->variables[i] && !strcmp(ctx->variables[i]->name, name)) | |
284 return i; | |
285 | |
286 return tuple_evalctx_add_var(ctx, name, FALSE, type); | |
287 } | |
288 | |
289 | |
290 static gboolean tc_parse_construct1(TupleEvalContext *ctx, TupleEvalNode **res, gchar *item, gchar **c, gint *level, gint opcode) | |
291 { | |
292 gchar tmps1[MAX_STR], tmps2[MAX_STR]; | |
293 gboolean literal1 = TRUE, literal2 = TRUE; | |
294 | |
295 (*c)++; | |
296 if (tc_get_item(c, tmps1, MAX_STR, ',', &literal1, "tag1", item)) { | |
297 (*c)++; | |
298 if (tc_get_item(c, tmps2, MAX_STR, ':', &literal2, "tag2", item)) { | |
299 TupleEvalNode *tmp = tuple_evalnode_new(); | |
300 (*c)++; | |
301 | |
302 tmp->opcode = opcode; | |
303 if ((tmp->var[0] = tc_get_variable(ctx, tmps1, literal1 ? VAR_CONST : VAR_FIELD)) < 0) { | |
304 tuple_evalnode_free(tmp); | |
305 tuple_error("Invalid variable '%s' in '%s'.\n", tmps1, item); | |
306 return FALSE; | |
307 } | |
308 if ((tmp->var[1] = tc_get_variable(ctx, tmps2, literal2 ? VAR_CONST : VAR_FIELD)) < 0) { | |
309 tuple_evalnode_free(tmp); | |
310 tuple_error("Invalid variable '%s' in '%s'.\n", tmps2, item); | |
311 return FALSE; | |
312 } | |
313 tmp->children = tuple_compiler_pass1(level, ctx, c); | |
314 tuple_evalnode_insert(res, tmp); | |
315 } else | |
316 return FALSE; | |
317 } else | |
318 return FALSE; | |
319 | |
320 return TRUE; | |
321 } | |
322 | |
323 | |
324 /* Compile format expression into TupleEvalNode tree. | |
325 * A "simple" straight compilation is sufficient in first pass, later | |
326 * passes can perform subexpression removal and other optimizations. | |
327 */ | |
328 static TupleEvalNode *tuple_compiler_pass1(gint *level, TupleEvalContext *ctx, gchar **expression) | |
329 { | |
330 TupleEvalNode *res = NULL, *tmp = NULL; | |
331 gchar *c = *expression, *item, tmps1[MAX_STR]; | |
332 gboolean literal, end = FALSE; | |
333 assert(ctx != NULL); | |
334 assert(expression != NULL); | |
335 | |
336 (*level)++; | |
337 | |
338 while (*c != '\0' && !end) { | |
339 tmp = NULL; | |
340 if (*c == '}') { | |
341 c++; | |
342 (*level)--; | |
343 end = TRUE; | |
344 } else if (*c == '$') { | |
345 /* Expression? */ | |
346 item = c++; | |
347 if (*c == '{') { | |
348 gint opcode; | |
349 gchar *expr = ++c; | |
350 | |
351 switch (*c) { | |
352 case '?': c++; | |
353 /* Exists? */ | |
354 literal = FALSE; | |
355 if (tc_get_item(&c, tmps1, MAX_STR, ':', &literal, "tag", item)) { | |
356 c++; | |
357 tmp = tuple_evalnode_new(); | |
358 tmp->opcode = OP_EXISTS; | |
359 if ((tmp->var[0] = tc_get_variable(ctx, tmps1, VAR_FIELD)) < 0) { | |
360 tuple_error("Invalid variable '%s' in '%s'.\n", tmps1, expr); | |
361 goto ret_error; | |
362 } | |
363 tmp->children = tuple_compiler_pass1(level, ctx, &c); | |
364 tuple_evalnode_insert(&res, tmp); | |
365 } else | |
366 goto ret_error; | |
367 break; | |
368 | |
369 case '=': c++; | |
370 if (*c != '=') { | |
371 /* Definition */ | |
372 literal = FALSE; | |
373 if (tc_get_item(&c, tmps1, MAX_STR, ',', &literal, "variable", item)) { | |
374 c++; | |
375 if (*c == '"') { | |
376 /* String */ | |
377 c++; | |
378 } else if (isdigit(*c)) { | |
379 /* Integer */ | |
380 } | |
381 | |
382 tuple_error("definitions not yet supported!\n"); | |
383 goto ret_error; | |
384 } else | |
385 goto ret_error; | |
386 } else { | |
387 /* Equals? */ | |
388 if (!tc_parse_construct1(ctx, &res, item, &c, level, OP_EQUALS)) | |
389 goto ret_error; | |
390 } | |
391 break; | |
392 | |
393 case '!': c++; | |
394 if (*c != '=') goto ext_expression; | |
395 if (!tc_parse_construct1(ctx, &res, item, &c, level, OP_NOT_EQUALS)) | |
396 goto ret_error; | |
397 break; | |
398 | |
399 case '<': c++; | |
400 if (*c == '=') { | |
401 opcode = OP_LTEQ; | |
402 c++; | |
403 } else | |
404 opcode = OP_LT; | |
405 | |
406 if (!tc_parse_construct1(ctx, &res, item, &c, level, opcode)) | |
407 goto ret_error; | |
408 break; | |
409 | |
410 case '>': c++; | |
411 if (*c == '=') { | |
412 opcode = OP_GTEQ; | |
413 c++; | |
414 } else | |
415 opcode = OP_GT; | |
416 | |
417 if (!tc_parse_construct1(ctx, &res, item, &c, level, opcode)) | |
418 goto ret_error; | |
419 break; | |
420 | |
421 case '(': c++; | |
422 if (!strncmp(c, "empty)?", 7)) { | |
423 c += 7; | |
424 } else | |
425 goto ext_expression; | |
426 break; | |
427 | |
428 default: | |
429 ext_expression: | |
430 /* Get expression content */ | |
431 c = expr; | |
432 literal = FALSE; | |
433 if (tc_get_item(&c, tmps1, MAX_STR, '}', &literal, "field", item)) { | |
434 /* FIXME!! FIX ME! Check for external expressions */ | |
435 | |
436 /* I HAS A FIELD - A field. You has it. */ | |
437 tmp = tuple_evalnode_new(); | |
438 tmp->opcode = OP_FIELD; | |
439 if ((tmp->var[0] = tc_get_variable(ctx, tmps1, VAR_FIELD)) < 0) { | |
440 tuple_error("Invalid variable '%s' in '%s'.\n", tmps1, expr); | |
441 goto ret_error; | |
442 } | |
443 tuple_evalnode_insert(&res, tmp); | |
444 c++; | |
445 | |
446 } else | |
447 goto ret_error; | |
448 } | |
449 } else { | |
450 tuple_error("Expected '{', got '%c' in '%s'.\n", *c, c); | |
451 goto ret_error; | |
452 } | |
453 | |
454 } else if (*c == '%') { | |
455 /* Function? */ | |
456 item = c++; | |
457 if (*c == '{') { | |
458 size_t i = 0; | |
459 c++; | |
460 | |
461 while (*c != '\0' && (isalnum(*c) || *c == '-') && *c != '}' && *c != ':' && i < (MAX_STR - 1)) | |
462 tmps1[i++] = *(c++); | |
463 tmps1[i] = '\0'; | |
464 | |
465 if (*c == ':') { | |
466 c++; | |
467 } else if (*c == '}') { | |
468 c++; | |
469 } else if (*c == '\0') { | |
470 tuple_error("Expected '}' or function arguments in '%s'\n", item); | |
471 goto ret_error; | |
472 } | |
473 } else { | |
474 tuple_error("Expected '{', got '%c' in '%s'.\n", *c, c); | |
475 goto ret_error; | |
476 } | |
477 } else { | |
478 /* Parse raw/literal text */ | |
479 size_t i = 0; | |
480 while (*c != '\0' && *c != '$' && *c != '%' && *c != '}' && i < (MAX_STR - 1)) { | |
481 if (*c == '\\') c++; | |
482 tmps1[i++] = *(c++); | |
483 } | |
484 tmps1[i] = '\0'; | |
485 | |
486 tmp = tuple_evalnode_new(); | |
487 tmp->opcode = OP_RAW; | |
488 tmp->text = g_strdup(tmps1); | |
489 tuple_evalnode_insert(&res, tmp); | |
490 } | |
491 } | |
492 | |
493 if (*level <= 0) { | |
494 tuple_error("Syntax error! Uneven/unmatched nesting of elements in '%s'!\n", c); | |
495 goto ret_error; | |
496 } | |
497 | |
498 *expression = c; | |
499 return res; | |
500 | |
501 ret_error: | |
502 tuple_evalnode_free(tmp); | |
503 tuple_evalnode_free(res); | |
504 return NULL; | |
505 } | |
506 | |
507 | |
508 static TupleEvalNode *tuple_compiler_pass2(gboolean *changed, TupleEvalContext *ctx, TupleEvalNode *expr) | |
509 { | |
510 TupleEvalNode *curr = expr, *res = NULL; | |
511 *changed = FALSE; | |
512 assert(ctx != NULL); | |
513 assert(expr != NULL); | |
514 | |
515 return res; | |
516 } | |
517 | |
518 | |
519 TupleEvalNode *tuple_formatter_compile(TupleEvalContext *ctx, gchar *expr) | |
520 { | |
521 gint level = 0; | |
522 gboolean changed = FALSE; | |
523 gchar *tmpexpr = expr; | |
524 TupleEvalNode *res1, *res2; | |
525 | |
526 res1 = tuple_compiler_pass1(&level, ctx, &tmpexpr); | |
527 | |
528 if (level != 1) { | |
529 tuple_error("Syntax error! Uneven/unmatched nesting of elements! (%d)\n", level); | |
530 tuple_evalnode_free(res1); | |
531 return NULL; | |
532 } | |
533 | |
534 res2 = tuple_compiler_pass2(&changed, ctx, res1); | |
535 | |
536 if (changed) { | |
537 tuple_evalnode_free(res1); | |
538 return res2; | |
539 } else { | |
540 tuple_evalnode_free(res2); | |
541 return res1; | |
542 } | |
543 } | |
544 | |
545 | |
546 /* Evaluate tuple in given TupleEval expression in given | |
547 * context and return resulting string. | |
548 */ | |
549 static TupleValue * tf_get_dictref(TupleEvalVar *var, Tuple *tuple) | |
550 { | |
551 if (var->type == VAR_FIELD && var->dictref == NULL) | |
552 var->dictref = mowgli_dictionary_retrieve(tuple->dict, var->name); | |
553 | |
554 return var->dictref; | |
555 } | |
556 | |
557 static TupleValueType tf_get_var(gchar **tmps, gint *tmpi, TupleEvalVar *var, Tuple *tuple) | |
558 { | |
559 TupleValueType type = TUPLE_UNKNOWN; | |
560 | |
561 switch (var->type) { | |
562 case VAR_DEF: *tmps = var->defval; type = TUPLE_STRING; break; | |
563 case VAR_CONST: *tmps = var->name; type = TUPLE_STRING; break; | |
564 case VAR_FIELD: | |
565 if (tf_get_dictref(var, tuple)) { | |
566 if (var->dictref->type == TUPLE_STRING) | |
567 *tmps = var->dictref->value.string; | |
568 else | |
569 *tmpi = var->dictref->value.integer; | |
570 type = var->dictref->type; | |
571 } | |
572 break; | |
573 default: | |
574 tmps = NULL; | |
575 tmpi = 0; | |
576 } | |
577 | |
578 return type; | |
579 } | |
580 | |
581 | |
582 static gboolean tuple_formatter_eval_do(TupleEvalContext *ctx, TupleEvalNode *expr, Tuple *tuple, gchar **res, size_t *resmax, size_t *reslen) | |
583 { | |
584 TupleEvalNode *curr = expr; | |
585 gchar tmps[MAX_STR], *tmps2; | |
586 gboolean result; | |
587 gint resulti; | |
588 TupleEvalVar *var0, *var1; | |
589 gint tmpi0, tmpi1; | |
590 gchar *tmps0, *tmps1; | |
591 TupleValueType type0, type1; | |
592 | |
593 if (!expr) return FALSE; | |
594 | |
595 while (curr) { | |
596 const gchar *str = NULL; | |
597 | |
598 switch (curr->opcode) { | |
599 case OP_RAW: | |
600 str = curr->text; | |
601 break; | |
602 | |
603 case OP_FIELD: | |
604 var0 = ctx->variables[curr->var[0]]; | |
605 | |
606 switch (var0->type) { | |
607 case VAR_DEF: | |
608 str = var0->defval; | |
609 break; | |
610 | |
611 case VAR_FIELD: | |
612 if (tf_get_dictref(var0, tuple)) { | |
613 switch (var0->dictref->type) { | |
614 case TUPLE_STRING: | |
615 str = var0->dictref->value.string; | |
616 break; | |
617 | |
618 case TUPLE_INT: | |
619 snprintf(tmps, sizeof(tmps), "%d", var0->dictref->value.integer); | |
620 str = tmps; | |
621 break; | |
622 | |
623 default: | |
624 str = NULL; | |
625 } | |
626 } | |
627 break; | |
628 } | |
629 break; | |
630 | |
631 case OP_EXISTS: | |
632 if (mowgli_dictionary_retrieve(tuple->dict, ctx->variables[curr->var[0]]->name)) { | |
633 if (!tuple_formatter_eval_do(ctx, curr->children, tuple, res, resmax, reslen)) | |
634 return FALSE; | |
635 } | |
636 break; | |
637 | |
638 case OP_EQUALS: | |
639 case OP_NOT_EQUALS: | |
640 case OP_LT: case OP_LTEQ: | |
641 case OP_GT: case OP_GTEQ: | |
642 var0 = ctx->variables[curr->var[0]]; | |
643 var1 = ctx->variables[curr->var[1]]; | |
644 | |
645 type0 = tf_get_var(&tmps0, &tmpi0, var0, tuple); | |
646 type1 = tf_get_var(&tmps1, &tmpi1, var1, tuple); | |
647 | |
648 if (type0 == type1) { | |
649 if (type0 == TUPLE_STRING) | |
650 resulti = strcmp(tmps0, tmps1); | |
651 else | |
652 resulti = tmpi0 - tmpi1; | |
653 | |
654 switch (curr->opcode) { | |
655 case OP_EQUALS: result = (resulti == 0); break; | |
656 case OP_NOT_EQUALS: result = (resulti != 0); break; | |
657 case OP_LT: result = (resulti < 0); break; | |
658 case OP_LTEQ: result = (resulti <= 0); break; | |
659 case OP_GT: result = (resulti > 0); break; | |
660 case OP_GTEQ: result = (resulti >= 0); break; | |
661 default: result = FALSE; | |
662 } | |
663 | |
664 if (result && !tuple_formatter_eval_do(ctx, curr->children, tuple, res, resmax, reslen)) | |
665 return FALSE; | |
666 } | |
667 break; | |
668 | |
669 case OP_IS_EMPTY: | |
670 var0 = ctx->variables[curr->var[0]]; | |
671 | |
672 if (var0->dictref == NULL) | |
673 var0->dictref = mowgli_dictionary_retrieve(tuple->dict, var0->name); | |
674 | |
675 switch (var0->dictref->type) { | |
676 case TUPLE_INT: | |
677 result = (var0->dictref->value.integer == 0); | |
678 break; | |
679 | |
680 case TUPLE_STRING: | |
681 result = TRUE; | |
682 tmps2 = var0->dictref->value.string; | |
683 | |
684 while (result && *tmps2 != '\0') { | |
685 if (*tmps2 == ' ') | |
686 tmps2++; | |
687 else | |
688 result = FALSE; | |
689 } | |
690 break; | |
691 | |
692 default: | |
693 result = TRUE; | |
694 } | |
695 | |
696 if (result) { | |
697 if (!tuple_formatter_eval_do(ctx, curr->children, tuple, res, resmax, reslen)) | |
698 return FALSE; | |
699 } | |
700 break; | |
701 | |
702 default: | |
703 tuple_error("Unimplemented opcode %d!\n", curr->opcode); | |
704 return FALSE; | |
705 break; | |
706 } | |
707 | |
708 if (str) { | |
709 /* (Re)allocate res for more space, if needed. */ | |
710 *reslen += strlen(str); | |
711 if (*res) { | |
712 if (*reslen >= *resmax) { | |
713 *resmax += MIN_ALLOC_BUF; | |
714 *res = g_realloc(*res, *resmax); | |
715 } | |
716 | |
717 strcat(*res, str); | |
718 } else { | |
719 *resmax = *reslen + MIN_ALLOC_BUF; | |
720 *res = g_malloc(*resmax); | |
721 | |
722 strcpy(*res, str); | |
723 } | |
724 } | |
725 | |
726 curr = curr->next; | |
727 } | |
728 | |
729 return TRUE; | |
730 } | |
731 | |
732 | |
733 gchar *tuple_formatter_eval(TupleEvalContext *ctx, TupleEvalNode *expr, Tuple *tuple) | |
734 { | |
735 gchar *res = g_strdup(""); | |
736 size_t resmax = 0, reslen = 0; | |
737 assert(ctx != NULL); | |
738 assert(tuple != NULL); | |
739 | |
740 if (!expr) return NULL; | |
741 | |
742 if (!tuple_formatter_eval_do(ctx, expr, tuple, &res, &resmax, &reslen)) { | |
743 g_free(res); | |
744 return NULL; | |
745 } | |
746 | |
747 return res; | |
748 } |