1 /* A simple, extensible s-exp evaluation engine.
2 *
3 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4 *
5 * This library is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Michael Zucchi <notzed@ximian.com>
18 */
19
20 /*
21 * The following built-in s-exp's are supported:
22 *
23 * list = (and list*)
24 * perform an intersection of a number of lists, and return that.
25 *
26 * bool = (and bool*)
27 * perform a boolean AND of boolean values.
28 *
29 * list = (or list*)
30 * perform a union of a number of lists, returning the new list.
31 *
32 * bool = (or bool*)
33 * perform a boolean OR of boolean values.
34 *
35 * gint = (+ int*)
36 * Add integers.
37 *
38 * string = (+ string*)
39 * Concat strings.
40 *
41 * time_t = (+ time_t*)
42 * Add time_t values.
43 *
44 * gint = (- gint int*)
45 * Subtract integers from the first.
46 *
47 * time_t = (- time_t*)
48 * Subtract time_t values from the first.
49 *
50 * gint = (cast-int string|int|bool)
51 * Cast to an integer value.
52 *
53 * string = (cast-string string|int|bool)
54 * Cast to a string value.
55 *
56 * Comparison operators:
57 *
58 * bool = (< gint gint)
59 * bool = (> gint gint)
60 * bool = (= gint gint)
61 *
62 * bool = (< string string)
63 * bool = (> string string)
64 * bool = (= string string)
65 *
66 * bool = (< time_t time_t)
67 * bool = (> time_t time_t)
68 * bool = (= time_t time_t)
69 * Perform a comparision of 2 integers, 2 string values, or 2 time values.
70 *
71 * Function flow:
72 *
73 * type = (if bool function)
74 * type = (if bool function function)
75 * Choose a flow path based on a boolean value
76 *
77 * type = (begin func func func)
78 * Execute a sequence. The last function return is the return type.
79 */
80
81 #include "evolution-data-server-config.h"
82
83 #include <stdio.h>
84 #include <stdlib.h>
85 #include <time.h>
86 #include <string.h>
87
88 #include "camel-sexp.h"
89
90 #define p(x) /* parse debug */
91 #define r(x) /* run debug */
92 #define d(x) /* general debug */
93
94 struct _CamelSExpPrivate {
95 GScanner *scanner; /* for parsing text version */
96 CamelSExpTerm *tree; /* root of expression tree */
97
98 /* private stuff */
99 jmp_buf failenv;
100 gchar *error;
101 GSList *operators;
102
103 /* TODO: may also need a pool allocator for term strings,
104 * so we dont lose them in error conditions? */
105 CamelMemChunk *term_chunks;
106 CamelMemChunk *result_chunks;
107 };
108
109 G_DEFINE_TYPE_WITH_PRIVATE (CamelSExp, camel_sexp, G_TYPE_OBJECT)
110
111 static CamelSExpTerm * parse_list (CamelSExp *sexp, gint gotbrace);
112 static CamelSExpTerm * parse_value (CamelSExp *sexp);
113
114 #ifdef TESTER
115 static void parse_dump_term (CamelSExpTerm *term, gint depth);
116 #endif
117
118 typedef gboolean (CamelSGeneratorFunc) (gint argc,
119 CamelSExpResult **argv,
120 CamelSExpResult *result);
121 typedef gboolean (CamelSOperatorFunc) (gint argc,
122 CamelSExpResult **argv,
123 CamelSExpResult *result);
124
125 /* FIXME: constant _TIME_MAX used in different files, move it somewhere */
126 #define _TIME_MAX ((time_t) INT_MAX) /* Max valid time_t */
127
128 static const GScannerConfig scanner_config =
129 {
130 ( (gchar *) " \t\r\n") /* cset_skip_characters */,
131 ( (gchar *) G_CSET_a_2_z
132 "_+-<=>?"
133 G_CSET_A_2_Z) /* cset_identifier_first */,
134 ( (gchar *) G_CSET_a_2_z
135 "_0123456789-<>?"
136 G_CSET_A_2_Z
137 G_CSET_LATINS
138 G_CSET_LATINC ) /* cset_identifier_nth */,
139 ( (gchar *) ";\n" ) /* cpair_comment_single */,
140
141 FALSE /* case_sensitive */,
142
143 TRUE /* skip_comment_multi */,
144 TRUE /* skip_comment_single */,
145 TRUE /* scan_comment_multi */,
146 TRUE /* scan_identifier */,
147 TRUE /* scan_identifier_1char */,
148 FALSE /* scan_identifier_NULL */,
149 TRUE /* scan_symbols */,
150 FALSE /* scan_binary */,
151 TRUE /* scan_octal */,
152 TRUE /* scan_float */,
153 TRUE /* scan_hex */,
154 FALSE /* scan_hex_dollar */,
155 TRUE /* scan_string_sq */,
156 TRUE /* scan_string_dq */,
157 TRUE /* numbers_2_int */,
158 FALSE /* int_2_float */,
159 FALSE /* identifier_2_string */,
160 TRUE /* char_2_token */,
161 FALSE /* symbol_2_token */,
162 FALSE /* scope_0_fallback */,
163 };
164
165 /**
166 * camel_sexp_fatal_error:
167 * @sexp: a #CamelSExp
168 * @why: a string format to use
169 * @...: parameters for the format
170 *
171 * Sets an error from the given format and stops execution.
172 * Int replaces previously set error, if any.
173 *
174 * Since: 3.4
175 **/
176 void
camel_sexp_fatal_error(CamelSExp * sexp,const gchar * why,...)177 camel_sexp_fatal_error (CamelSExp *sexp,
178 const gchar *why,
179 ...)
180 {
181 va_list args;
182
183 /* jumps back to the caller of sexp->priv->failenv,
184 * only to be called from inside a callback */
185
186 g_free (sexp->priv->error);
187
188 va_start (args, why);
189 sexp->priv->error = g_strdup_vprintf (why, args);
190 va_end (args);
191
192 longjmp (sexp->priv->failenv, 1);
193 }
194
195 /**
196 * camel_sexp_error:
197 * @sexp: a #CamelSExp
198 *
199 * Returns: (nullable): Set error string on the @sexp, or %NULL, when none is set
200 *
201 * Since: 3.4
202 **/
203 const gchar *
camel_sexp_error(CamelSExp * sexp)204 camel_sexp_error (CamelSExp *sexp)
205 {
206 return sexp->priv->error;
207 }
208
209 /**
210 * camel_sexp_result_new: (skip)
211 * @sexp: a #CamelSExp
212 * @type: type of the result, one of #CamelSExpResultType
213 *
214 * Returns: (transfer full): A new #CamelSExpResult result structure, associated with @sexp.
215 * Free with camel_sexp_result_free(), when no longer needed.
216 *
217 * Since: 3.4
218 **/
219 CamelSExpResult *
camel_sexp_result_new(CamelSExp * sexp,gint type)220 camel_sexp_result_new (CamelSExp *sexp,
221 gint type)
222 {
223 CamelSExpResult *result;
224
225 result = camel_memchunk_alloc0 (sexp->priv->result_chunks);
226 result->type = type;
227 result->occuring_start = 0;
228 result->occuring_end = _TIME_MAX;
229 result->time_generator = FALSE;
230
231 return result;
232 }
233
234 /**
235 * camel_sexp_result_free:
236 * @sexp: a #CamelSExp
237 * @result: (nullable): a #CamelSExpResult to free
238 *
239 * Frees the @result and its internal data. Does nothing,
240 * when the @result is %NULL.
241 *
242 * Since: 3.4
243 **/
244 void
camel_sexp_result_free(CamelSExp * sexp,CamelSExpResult * result)245 camel_sexp_result_free (CamelSExp *sexp,
246 CamelSExpResult *result)
247 {
248 if (result == NULL)
249 return;
250
251 switch (result->type) {
252 case CAMEL_SEXP_RES_ARRAY_PTR:
253 g_ptr_array_free (result->value.ptrarray, TRUE);
254 break;
255 case CAMEL_SEXP_RES_BOOL:
256 case CAMEL_SEXP_RES_INT:
257 case CAMEL_SEXP_RES_TIME:
258 break;
259 case CAMEL_SEXP_RES_STRING:
260 g_free (result->value.string);
261 break;
262 case CAMEL_SEXP_RES_UNDEFINED:
263 break;
264 default:
265 g_return_if_reached ();
266 }
267 camel_memchunk_free (sexp->priv->result_chunks, result);
268 }
269
270 /**
271 * camel_sexp_resultv_free:
272 * @sexp: a #CamelSExp
273 * @argc: a count of the @argv
274 * @argv: (array length=argc): an array of #CamelSExpResult to free
275 *
276 * Frees an array of results.
277 *
278 * Since: 3.4
279 **/
280 void
camel_sexp_resultv_free(CamelSExp * sexp,gint argc,CamelSExpResult ** argv)281 camel_sexp_resultv_free (CamelSExp *sexp,
282 gint argc,
283 CamelSExpResult **argv)
284 {
285 gint i;
286
287 /* used in normal functions if they have to abort,
288 * and free their arguments */
289
290 for (i = 0; i < argc; i++) {
291 camel_sexp_result_free (sexp, argv[i]);
292 }
293 }
294
295 /* implementations for the builtin functions */
296
297 /* we can only itereate a hashtable from a called function */
298 struct IterData {
299 gint count;
300 GPtrArray *uids;
301 };
302
303 /* ok, store any values that are in all sets */
304 static void
htand(gchar * key,gint value,struct IterData * iter_data)305 htand (gchar *key,
306 gint value,
307 struct IterData *iter_data)
308 {
309 if (value == iter_data->count) {
310 g_ptr_array_add (iter_data->uids, key);
311 }
312 }
313
314 /* or, store all unique values */
315 static void
htor(gchar * key,gint value,struct IterData * iter_data)316 htor (gchar *key,
317 gint value,
318 struct IterData *iter_data)
319 {
320 g_ptr_array_add (iter_data->uids, key);
321 }
322
323 static CamelSExpResult *
term_eval_and(CamelSExp * sexp,gint argc,CamelSExpTerm ** argv,gpointer data)324 term_eval_and (CamelSExp *sexp,
325 gint argc,
326 CamelSExpTerm **argv,
327 gpointer data)
328 {
329 CamelSExpResult *result, *r1;
330 GHashTable *ht = g_hash_table_new (g_str_hash, g_str_equal);
331 struct IterData lambdafoo;
332 gint type=-1;
333 gint bool = TRUE;
334 gint i;
335 const gchar *oper;
336
337 r (printf ("( and\n"));
338
339 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
340
341 oper = "AND";
342 sexp->priv->operators = g_slist_prepend (sexp->priv->operators, (gpointer) oper);
343
344 for (i = 0; bool && i < argc; i++) {
345 r1 = camel_sexp_term_eval (sexp, argv[i]);
346 if (type == -1)
347 type = r1->type;
348 if (type != r1->type) {
349 camel_sexp_result_free (sexp, result);
350 camel_sexp_result_free (sexp, r1);
351 g_hash_table_destroy (ht);
352 camel_sexp_fatal_error (sexp, "Invalid types in AND");
353 } else if (r1->type == CAMEL_SEXP_RES_ARRAY_PTR) {
354 gchar **a1;
355 gint l1, j;
356
357 a1 = (gchar **) r1->value.ptrarray->pdata;
358 l1 = r1->value.ptrarray->len;
359 for (j = 0; j < l1; j++) {
360 gpointer ptr;
361 gint n;
362 ptr = g_hash_table_lookup (ht, a1[j]);
363 n = GPOINTER_TO_INT (ptr);
364 g_hash_table_insert (ht, a1[j], GINT_TO_POINTER (n + 1));
365 }
366 } else if (r1->type == CAMEL_SEXP_RES_BOOL) {
367 bool = bool && r1->value.boolean;
368 }
369 camel_sexp_result_free (sexp, r1);
370 }
371
372 if (type == CAMEL_SEXP_RES_ARRAY_PTR) {
373 lambdafoo.count = argc;
374 lambdafoo.uids = g_ptr_array_new ();
375 g_hash_table_foreach (ht, (GHFunc) htand, &lambdafoo);
376 result->type = CAMEL_SEXP_RES_ARRAY_PTR;
377 result->value.ptrarray = lambdafoo.uids;
378 } else if (type == CAMEL_SEXP_RES_BOOL) {
379 result->type = CAMEL_SEXP_RES_BOOL;
380 result->value.boolean = bool;
381 }
382
383 g_hash_table_destroy (ht);
384 sexp->priv->operators = g_slist_remove (sexp->priv->operators, oper);
385
386 return result;
387 }
388
389 static CamelSExpResult *
term_eval_or(CamelSExp * sexp,gint argc,CamelSExpTerm ** argv,gpointer data)390 term_eval_or (CamelSExp *sexp,
391 gint argc,
392 CamelSExpTerm **argv,
393 gpointer data)
394 {
395 CamelSExpResult *result, *r1;
396 GHashTable *ht = g_hash_table_new (g_str_hash, g_str_equal);
397 struct IterData lambdafoo;
398 gint type = -1;
399 gint bool = FALSE;
400 gint i;
401 const gchar *oper;
402
403 r (printf ("(or \n"));
404
405 oper = "OR";
406 sexp->priv->operators = g_slist_prepend (sexp->priv->operators, (gpointer) oper);
407
408 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
409
410 for (i = 0; !bool && i < argc; i++) {
411 r1 = camel_sexp_term_eval (sexp, argv[i]);
412 if (type == -1)
413 type = r1->type;
414 if (r1->type != type) {
415 camel_sexp_result_free (sexp, result);
416 camel_sexp_result_free (sexp, r1);
417 g_hash_table_destroy (ht);
418 camel_sexp_fatal_error (sexp, "Invalid types in OR");
419 } else if (r1->type == CAMEL_SEXP_RES_ARRAY_PTR) {
420 gchar **a1;
421 gint l1, j;
422
423 a1 = (gchar **) r1->value.ptrarray->pdata;
424 l1 = r1->value.ptrarray->len;
425 for (j = 0; j < l1; j++) {
426 g_hash_table_insert (ht, a1[j], (gpointer) 1);
427 }
428 } else if (r1->type == CAMEL_SEXP_RES_BOOL) {
429 bool |= r1->value.boolean;
430 }
431 camel_sexp_result_free (sexp, r1);
432 }
433
434 if (type == CAMEL_SEXP_RES_ARRAY_PTR) {
435 lambdafoo.count = argc;
436 lambdafoo.uids = g_ptr_array_new ();
437 g_hash_table_foreach (ht, (GHFunc) htor, &lambdafoo);
438 result->type = CAMEL_SEXP_RES_ARRAY_PTR;
439 result->value.ptrarray = lambdafoo.uids;
440 } else if (type == CAMEL_SEXP_RES_BOOL) {
441 result->type = CAMEL_SEXP_RES_BOOL;
442 result->value.boolean = bool;
443 }
444 g_hash_table_destroy (ht);
445
446 sexp->priv->operators = g_slist_remove (sexp->priv->operators, oper);
447 return result;
448 }
449
450 static CamelSExpResult *
term_eval_not(CamelSExp * sexp,gint argc,CamelSExpResult ** argv,gpointer data)451 term_eval_not (CamelSExp *sexp,
452 gint argc,
453 CamelSExpResult **argv,
454 gpointer data)
455 {
456 gint res = TRUE;
457 CamelSExpResult *result;
458
459 if (argc > 0) {
460 if (argv[0]->type == CAMEL_SEXP_RES_BOOL
461 && argv[0]->value.boolean)
462 res = FALSE;
463 }
464 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_BOOL);
465 result->value.boolean = res;
466
467 return result;
468 }
469
470 /* this should support all arguments ...? */
471 static CamelSExpResult *
term_eval_lt(CamelSExp * sexp,gint argc,CamelSExpTerm ** argv,gpointer data)472 term_eval_lt (CamelSExp *sexp,
473 gint argc,
474 CamelSExpTerm **argv,
475 gpointer data)
476 {
477 CamelSExpResult *result, *r1, *r2;
478
479 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
480
481 if (argc == 2) {
482 r1 = camel_sexp_term_eval (sexp, argv[0]);
483 r2 = camel_sexp_term_eval (sexp, argv[1]);
484 if (r1->type != r2->type) {
485 camel_sexp_result_free (sexp, r1);
486 camel_sexp_result_free (sexp, r2);
487 camel_sexp_result_free (sexp, result);
488 camel_sexp_fatal_error (sexp, "Incompatible types in compare <");
489 } else if (r1->type == CAMEL_SEXP_RES_INT) {
490 result->type = CAMEL_SEXP_RES_BOOL;
491 result->value.boolean = r1->value.number < r2->value.number;
492 } else if (r1->type == CAMEL_SEXP_RES_TIME) {
493 result->type = CAMEL_SEXP_RES_BOOL;
494 result->value.boolean = r1->value.time < r2->value.time;
495 } else if (r1->type == CAMEL_SEXP_RES_STRING) {
496 result->type = CAMEL_SEXP_RES_BOOL;
497 result->value.boolean = strcmp (r1->value.string, r2->value.string) < 0;
498 }
499 camel_sexp_result_free (sexp, r1);
500 camel_sexp_result_free (sexp, r2);
501 }
502 return result;
503 }
504
505 /* this should support all arguments ...? */
506 static CamelSExpResult *
term_eval_gt(CamelSExp * sexp,gint argc,CamelSExpTerm ** argv,gpointer data)507 term_eval_gt (CamelSExp *sexp,
508 gint argc,
509 CamelSExpTerm **argv,
510 gpointer data)
511 {
512 CamelSExpResult *result, *r1, *r2;
513
514 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
515
516 if (argc == 2) {
517 r1 = camel_sexp_term_eval (sexp, argv[0]);
518 r2 = camel_sexp_term_eval (sexp, argv[1]);
519 if (r1->type != r2->type) {
520 camel_sexp_result_free (sexp, r1);
521 camel_sexp_result_free (sexp, r2);
522 camel_sexp_result_free (sexp, result);
523 camel_sexp_fatal_error (sexp, "Incompatible types in compare >");
524 } else if (r1->type == CAMEL_SEXP_RES_INT) {
525 result->type = CAMEL_SEXP_RES_BOOL;
526 result->value.boolean = r1->value.number > r2->value.number;
527 } else if (r1->type == CAMEL_SEXP_RES_TIME) {
528 result->type = CAMEL_SEXP_RES_BOOL;
529 result->value.boolean = r1->value.time > r2->value.time;
530 } else if (r1->type == CAMEL_SEXP_RES_STRING) {
531 result->type = CAMEL_SEXP_RES_BOOL;
532 result->value.boolean = strcmp (r1->value.string, r2->value.string) > 0;
533 }
534 camel_sexp_result_free (sexp, r1);
535 camel_sexp_result_free (sexp, r2);
536 }
537 return result;
538 }
539
540 /* this should support all arguments ...? */
541 static CamelSExpResult *
term_eval_eq(CamelSExp * sexp,gint argc,CamelSExpTerm ** argv,gpointer data)542 term_eval_eq (CamelSExp *sexp,
543 gint argc,
544 CamelSExpTerm **argv,
545 gpointer data)
546 {
547 CamelSExpResult *result, *r1, *r2;
548
549 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_BOOL);
550
551 if (argc == 2) {
552 r1 = camel_sexp_term_eval (sexp, argv[0]);
553 r2 = camel_sexp_term_eval (sexp, argv[1]);
554 if (r1->type != r2->type) {
555 result->value.boolean = FALSE;
556 } else if (r1->type == CAMEL_SEXP_RES_INT) {
557 result->value.boolean = r1->value.number == r2->value.number;
558 } else if (r1->type == CAMEL_SEXP_RES_BOOL) {
559 result->value.boolean = r1->value.boolean == r2->value.boolean;
560 } else if (r1->type == CAMEL_SEXP_RES_TIME) {
561 result->value.boolean = r1->value.time == r2->value.time;
562 } else if (r1->type == CAMEL_SEXP_RES_STRING) {
563 result->value.boolean = strcmp (r1->value.string, r2->value.string) == 0;
564 }
565 camel_sexp_result_free (sexp, r1);
566 camel_sexp_result_free (sexp, r2);
567 }
568 return result;
569 }
570
571 static CamelSExpResult *
term_eval_plus(CamelSExp * sexp,gint argc,CamelSExpResult ** argv,gpointer data)572 term_eval_plus (CamelSExp *sexp,
573 gint argc,
574 CamelSExpResult **argv,
575 gpointer data)
576 {
577 CamelSExpResult *result = NULL;
578 gint type;
579 gint i;
580
581 if (argc > 0) {
582 type = argv[0]->type;
583 switch (type) {
584 case CAMEL_SEXP_RES_INT: {
585 gint total = argv[0]->value.number;
586 for (i = 1; i < argc && argv[i]->type == CAMEL_SEXP_RES_INT; i++) {
587 total += argv[i]->value.number;
588 }
589 if (i < argc) {
590 camel_sexp_resultv_free (sexp, argc, argv);
591 camel_sexp_fatal_error (sexp, "Invalid types in (+ ints)");
592 }
593 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_INT);
594 result->value.number = total;
595 break; }
596 case CAMEL_SEXP_RES_STRING: {
597 GString *string = g_string_new (argv[0]->value.string);
598 for (i = 1; i < argc && argv[i]->type == CAMEL_SEXP_RES_STRING; i++) {
599 g_string_append (string, argv[i]->value.string);
600 }
601 if (i < argc) {
602 camel_sexp_resultv_free (sexp, argc, argv);
603 camel_sexp_fatal_error (sexp, "Invalid types in (+ strings)");
604 }
605 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_STRING);
606 result->value.string = g_string_free (string, FALSE);
607 break; }
608 case CAMEL_SEXP_RES_TIME: {
609 time_t total;
610
611 total = argv[0]->value.time;
612
613 for (i = 1; i < argc && argv[i]->type == CAMEL_SEXP_RES_TIME; i++)
614 total += argv[i]->value.time;
615
616 if (i < argc) {
617 camel_sexp_resultv_free (sexp, argc, argv);
618 camel_sexp_fatal_error (sexp, "Invalid types in (+ time_t)");
619 }
620
621 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_TIME);
622 result->value.time = total;
623 break; }
624 }
625 }
626
627 if (result == NULL) {
628 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_INT);
629 result->value.number = 0;
630 }
631
632 return result;
633 }
634
635 static CamelSExpResult *
term_eval_sub(CamelSExp * sexp,gint argc,CamelSExpResult ** argv,gpointer data)636 term_eval_sub (CamelSExp *sexp,
637 gint argc,
638 CamelSExpResult **argv,
639 gpointer data)
640 {
641 CamelSExpResult *result = NULL;
642 gint type;
643 gint i;
644
645 if (argc > 0) {
646 type = argv[0]->type;
647 switch (type) {
648 case CAMEL_SEXP_RES_INT: {
649 gint total = argv[0]->value.number;
650 for (i = 1; i < argc && argv[i]->type == CAMEL_SEXP_RES_INT; i++) {
651 total -= argv[i]->value.number;
652 }
653 if (i < argc) {
654 camel_sexp_resultv_free (sexp, argc, argv);
655 camel_sexp_fatal_error (sexp, "Invalid types in -");
656 }
657 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_INT);
658 result->value.number = total;
659 break; }
660 case CAMEL_SEXP_RES_TIME: {
661 time_t total;
662
663 total = argv[0]->value.time;
664
665 for (i = 1; i < argc && argv[i]->type == CAMEL_SEXP_RES_TIME; i++)
666 total -= argv[i]->value.time;
667
668 if (i < argc) {
669 camel_sexp_resultv_free (sexp, argc, argv);
670 camel_sexp_fatal_error (sexp, "Invalid types in (- time_t)");
671 }
672
673 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_TIME);
674 result->value.time = total;
675 break; }
676 }
677 }
678
679 if (result == NULL) {
680 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_INT);
681 result->value.number = 0;
682 }
683 return result;
684 }
685
686 /* cast to gint */
687 static CamelSExpResult *
term_eval_castint(CamelSExp * sexp,gint argc,CamelSExpResult ** argv,gpointer data)688 term_eval_castint (CamelSExp *sexp,
689 gint argc,
690 CamelSExpResult **argv,
691 gpointer data)
692 {
693 CamelSExpResult *result;
694
695 if (argc != 1)
696 camel_sexp_fatal_error (sexp, "Incorrect argument count to (gint )");
697
698 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_INT);
699 switch (argv[0]->type) {
700 case CAMEL_SEXP_RES_INT:
701 result->value.number = argv[0]->value.number;
702 break;
703 case CAMEL_SEXP_RES_BOOL:
704 result->value.number = argv[0]->value.boolean != 0;
705 break;
706 case CAMEL_SEXP_RES_STRING:
707 result->value.number = strtoul (argv[0]->value.string, NULL, 10);
708 break;
709 default:
710 camel_sexp_result_free (sexp, result);
711 camel_sexp_fatal_error (sexp, "Invalid type in (cast-int )");
712 }
713
714 return result;
715 }
716
717 /* cast to string */
718 static CamelSExpResult *
term_eval_caststring(CamelSExp * sexp,gint argc,CamelSExpResult ** argv,gpointer data)719 term_eval_caststring (CamelSExp *sexp,
720 gint argc,
721 CamelSExpResult **argv,
722 gpointer data)
723 {
724 CamelSExpResult *result;
725
726 if (argc != 1)
727 camel_sexp_fatal_error (sexp, "Incorrect argument count to (cast-string )");
728
729 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_STRING);
730 switch (argv[0]->type) {
731 case CAMEL_SEXP_RES_INT:
732 result->value.string = g_strdup_printf ("%d", argv[0]->value.number);
733 break;
734 case CAMEL_SEXP_RES_BOOL:
735 result->value.string = g_strdup_printf ("%d", argv[0]->value.boolean != 0);
736 break;
737 case CAMEL_SEXP_RES_STRING:
738 result->value.string = g_strdup (argv[0]->value.string);
739 break;
740 default:
741 camel_sexp_result_free (sexp, result);
742 camel_sexp_fatal_error (sexp, "Invalid type in (gint )");
743 }
744
745 return result;
746 }
747
748 /* implements 'if' function */
749 static CamelSExpResult *
term_eval_if(CamelSExp * sexp,gint argc,CamelSExpTerm ** argv,gpointer data)750 term_eval_if (CamelSExp *sexp,
751 gint argc,
752 CamelSExpTerm **argv,
753 gpointer data)
754 {
755 CamelSExpResult *result;
756 gint doit;
757
758 if (argc >=2 && argc <= 3) {
759 result = camel_sexp_term_eval (sexp, argv[0]);
760 doit = (result->type == CAMEL_SEXP_RES_BOOL && result->value.boolean);
761 camel_sexp_result_free (sexp, result);
762 if (doit) {
763 return camel_sexp_term_eval (sexp, argv[1]);
764 } else if (argc > 2) {
765 return camel_sexp_term_eval (sexp, argv[2]);
766 }
767 }
768 return camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
769 }
770
771 /* implements 'begin' statement */
772 static CamelSExpResult *
term_eval_begin(CamelSExp * sexp,gint argc,CamelSExpTerm ** argv,gpointer data)773 term_eval_begin (CamelSExp *sexp,
774 gint argc,
775 CamelSExpTerm **argv,
776 gpointer data)
777 {
778 CamelSExpResult *result = NULL;
779 gint i;
780
781 for (i = 0; i < argc; i++) {
782 if (result != NULL)
783 camel_sexp_result_free (sexp, result);
784 result = camel_sexp_term_eval (sexp, argv[i]);
785 }
786 if (result != NULL)
787 return result;
788 else
789 return camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
790 }
791
792 /**
793 * camel_sexp_term_eval: (skip)
794 * @sexp: a #CamelSExp
795 * @term: a #CamelSExpTerm to evaluate
796 *
797 * Evaluates a part of the expression.
798 *
799 * Returns: (transfer full): a newly allocated result of the evaluation. Free
800 * the returned pointer with camel_sexp_result_free(), when no longer needed.
801 *
802 * Since: 3.4
803 **/
804 CamelSExpResult *
camel_sexp_term_eval(CamelSExp * sexp,CamelSExpTerm * term)805 camel_sexp_term_eval (CamelSExp *sexp,
806 CamelSExpTerm *term)
807 {
808 CamelSExpResult *result = NULL;
809 gint i, argc;
810 CamelSExpResult **argv;
811
812 /* this must only be called from inside term evaluation callbacks! */
813
814 g_return_val_if_fail (term != NULL, NULL);
815
816 r (printf ("eval term :\n"));
817 r (parse_dump_term (term, 0));
818
819 switch (term->type) {
820 case CAMEL_SEXP_TERM_STRING:
821 r (printf (" (string \"%s\")\n", term->value.string));
822 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_STRING);
823 /* erk, this shoul;dn't need to strdup this ... */
824 result->value.string = g_strdup (term->value.string);
825 break;
826 case CAMEL_SEXP_TERM_INT:
827 r (printf (" (gint %d)\n", term->value.number));
828 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_INT);
829 result->value.number = term->value.number;
830 break;
831 case CAMEL_SEXP_TERM_BOOL:
832 r (printf (" (gint %d)\n", term->value.number));
833 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_BOOL);
834 result->value.boolean = term->value.boolean;
835 break;
836 case CAMEL_SEXP_TERM_TIME:
837 r (printf (" (time_t %ld)\n", term->value.time));
838 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_TIME);
839 result->value.time = term->value.time;
840 break;
841 case CAMEL_SEXP_TERM_IFUNC:
842 if (term->value.func.sym && term->value.func.sym->f.ifunc)
843 result = term->value.func.sym->f.ifunc (sexp, term->value.func.termcount, term->value.func.terms, term->value.func.sym->data);
844 break;
845 case CAMEL_SEXP_TERM_FUNC:
846 /* first evaluate all arguments to result types */
847 argc = term->value.func.termcount;
848 argv = g_alloca (sizeof (argv[0]) * argc);
849 for (i = 0; i < argc; i++) {
850 argv[i] = camel_sexp_term_eval (sexp, term->value.func.terms[i]);
851 }
852 /* call the function */
853 if (term->value.func.sym->f.func)
854 result = term->value.func.sym->f.func (sexp, argc, argv, term->value.func.sym->data);
855
856 camel_sexp_resultv_free (sexp, argc, argv);
857 break;
858 default:
859 camel_sexp_fatal_error (sexp, "Unknown type in parse tree: %d", term->type);
860 }
861
862 if (result == NULL)
863 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
864
865 return result;
866 }
867
868 #ifdef TESTER
869 static void
eval_dump_result(CamelSExpResult * result,gint depth)870 eval_dump_result (CamelSExpResult *result,
871 gint depth)
872 {
873 gint i;
874
875 if (result == NULL) {
876 printf ("null result???\n");
877 return;
878 }
879
880 for (i = 0; i < depth; i++)
881 printf (" ");
882
883 switch (result->type) {
884 case CAMEL_SEXP_RES_ARRAY_PTR:
885 printf ("array pointers\n");
886 break;
887 case CAMEL_SEXP_RES_INT:
888 printf ("int: %d\n", result->value.number);
889 break;
890 case CAMEL_SEXP_RES_STRING:
891 printf ("string: '%s'\n", result->value.string);
892 break;
893 case CAMEL_SEXP_RES_BOOL:
894 printf ("bool: %c\n", result->value.boolean ? 't':'f');
895 break;
896 case CAMEL_SEXP_RES_TIME:
897 printf ("time_t: %ld\n", (glong) result->value.time);
898 break;
899 case CAMEL_SEXP_RES_UNDEFINED:
900 printf (" <undefined>\n");
901 break;
902 }
903 printf ("\n");
904 }
905 #endif
906
907 #ifdef TESTER
908 static void
parse_dump_term(CamelSExpTerm * term,gint depth)909 parse_dump_term (CamelSExpTerm *term,
910 gint depth)
911 {
912 gint i;
913
914 if (t == NULL) {
915 printf ("null term??\n");
916 return;
917 }
918
919 for (i = 0; i < depth; i++)
920 printf (" ");
921
922 switch (term->type) {
923 case CAMEL_SEXP_TERM_STRING:
924 printf (" \"%s\"", term->value.string);
925 break;
926 case CAMEL_SEXP_TERM_INT:
927 printf (" %d", term->value.number);
928 break;
929 case CAMEL_SEXP_TERM_BOOL:
930 printf (" #%c", term->value.boolean ? 't':'f');
931 break;
932 case CAMEL_SEXP_TERM_TIME:
933 printf (" %ld", (glong) term->value.time);
934 break;
935 case CAMEL_SEXP_TERM_IFUNC:
936 case CAMEL_SEXP_TERM_FUNC:
937 printf (" (function %s\n", term->value.func.sym->name);
938 /*printf(" [%d] ", term->value.func.termcount);*/
939 for (i = 0; i < term->value.func.termcount; i++) {
940 parse_dump_term (term->value.func.terms[i], depth + 1);
941 }
942 for (i = 0; i < depth; i++)
943 printf (" ");
944 printf (" )");
945 break;
946 case CAMEL_SEXP_TERM_VAR:
947 printf (" (variable %s )\n", term->value.var->name);
948 break;
949 default:
950 printf ("unknown type: %d\n", term->type);
951 }
952
953 printf ("\n");
954 }
955 #endif
956
957 static const gchar *time_functions[] = {
958 "time-now",
959 "make-time",
960 "time-add-day",
961 "time-day-begin",
962 "time-day-end"
963 };
964
965 static gboolean
occur_in_time_range_generator(gint argc,CamelSExpResult ** argv,CamelSExpResult * result)966 occur_in_time_range_generator (gint argc,
967 CamelSExpResult **argv,
968 CamelSExpResult *result)
969 {
970 g_return_val_if_fail (result != NULL, FALSE);
971 g_return_val_if_fail (argc == 2 || argc == 3, FALSE);
972
973 if ((argv[0]->type != CAMEL_SEXP_RES_TIME) || (argv[1]->type != CAMEL_SEXP_RES_TIME))
974 return FALSE;
975
976 result->occuring_start = argv[0]->value.time;
977 result->occuring_end = argv[1]->value.time;
978
979 return TRUE;
980 }
981
982 static gboolean
binary_generator(gint argc,CamelSExpResult ** argv,CamelSExpResult * result)983 binary_generator (gint argc,
984 CamelSExpResult **argv,
985 CamelSExpResult *result)
986 {
987 g_return_val_if_fail (result != NULL, FALSE);
988 g_return_val_if_fail (argc == 2, FALSE);
989
990 if ((argv[0]->type != CAMEL_SEXP_RES_TIME) || (argv[1]->type != CAMEL_SEXP_RES_TIME))
991 return FALSE;
992
993 result->occuring_start = argv[0]->value.time;
994 result->occuring_end = argv[1]->value.time;
995
996 return TRUE;
997 }
998
999 static gboolean
unary_generator(gint argc,CamelSExpResult ** argv,CamelSExpResult * result)1000 unary_generator (gint argc,
1001 CamelSExpResult **argv,
1002 CamelSExpResult *result)
1003 {
1004 /* unary generator with end time */
1005 g_return_val_if_fail (result != NULL, FALSE);
1006 g_return_val_if_fail (argc == 1, FALSE);
1007
1008 if (argv[0]->type != CAMEL_SEXP_RES_TIME)
1009 return FALSE;
1010
1011 result->occuring_start = 0;
1012 result->occuring_end = argv[0]->value.time;
1013
1014 return TRUE;
1015 }
1016
1017 static const struct {
1018 const gchar *name;
1019 CamelSGeneratorFunc *func;
1020 } generators[] = {
1021 {"occur-in-time-range?", occur_in_time_range_generator},
1022 {"due-in-time-range?", binary_generator},
1023 {"has-alarms-in-range?", binary_generator},
1024 {"completed-before?", unary_generator},
1025 };
1026
1027 static gboolean
or_operator(gint argc,CamelSExpResult ** argv,CamelSExpResult * result)1028 or_operator (gint argc,
1029 CamelSExpResult **argv,
1030 CamelSExpResult *result)
1031 {
1032 gint ii;
1033
1034 /*
1035 * A B A or B
1036 * ---- ---- ------
1037 * norm (0) norm (0) norm (0)
1038 * gen (1) norm (0) norm (0)
1039 * norm (0) gen (1) norm (0)
1040 * gen (1) gen (1) gen*(1)
1041 */
1042
1043 g_return_val_if_fail (result != NULL, FALSE);
1044 g_return_val_if_fail (argc > 0, FALSE);
1045
1046 result->time_generator = TRUE;
1047 for (ii = 0; ii < argc && result->time_generator; ii++) {
1048 result->time_generator = argv[ii]->time_generator;
1049 }
1050
1051 if (result->time_generator) {
1052 result->occuring_start = argv[0]->occuring_start;
1053 result->occuring_end = argv[0]->occuring_end;
1054
1055 for (ii = 1; ii < argc; ii++) {
1056 result->occuring_start = MIN (result->occuring_start, argv[ii]->occuring_start);
1057 result->occuring_end = MAX (result->occuring_end, argv[ii]->occuring_end);
1058 }
1059 }
1060
1061 return TRUE;
1062 }
1063
1064 static gboolean
and_operator(gint argc,CamelSExpResult ** argv,CamelSExpResult * result)1065 and_operator (gint argc,
1066 CamelSExpResult **argv,
1067 CamelSExpResult *result)
1068 {
1069 gint ii;
1070
1071 /*
1072 * A B A and B
1073 * ---- ---- ------- -
1074 * norm (0) norm (0) norm (0)
1075 * gen (1) norm (0) gen (1)
1076 * norm (0) gen (1) gen (1)
1077 * gen (1) gen (1) gen (1)
1078 * */
1079
1080 g_return_val_if_fail (result != NULL, FALSE);
1081 g_return_val_if_fail (argc > 0, FALSE);
1082
1083 result->time_generator = FALSE;
1084 for (ii = 0; ii < argc && !result->time_generator; ii++) {
1085 result->time_generator = argv[ii]->time_generator;
1086 }
1087
1088 if (result->time_generator) {
1089 result->occuring_start = argv[0]->occuring_start;
1090 result->occuring_end = argv[0]->occuring_end;
1091
1092 for (ii = 1; ii < argc; ii++) {
1093 result->occuring_start = MAX (result->occuring_start, argv[ii]->occuring_start);
1094 result->occuring_end = MIN (result->occuring_end, argv[ii]->occuring_end);
1095 }
1096 }
1097
1098 return TRUE;
1099 }
1100
1101 static const struct {
1102 const gchar *name;
1103 CamelSOperatorFunc *func;
1104 } operators[] = {
1105 {"or", or_operator},
1106 {"and", and_operator}
1107 };
1108
1109 static CamelSOperatorFunc *
get_operator_function(const gchar * fname)1110 get_operator_function (const gchar *fname)
1111 {
1112 gint i;
1113
1114 g_return_val_if_fail (fname != NULL, NULL);
1115
1116 for (i = 0; i < sizeof (operators) / sizeof (operators[0]); i++)
1117 if (strcmp (operators[i].name, fname) == 0)
1118 return operators[i].func;
1119
1120 return NULL;
1121 }
1122
1123 static inline gboolean
is_time_function(const gchar * fname)1124 is_time_function (const gchar *fname)
1125 {
1126 gint i;
1127
1128 g_return_val_if_fail (fname != NULL, FALSE);
1129
1130 for (i = 0; i < sizeof (time_functions) / sizeof (time_functions[0]); i++)
1131 if (strcmp (time_functions[i], fname) == 0)
1132 return TRUE;
1133
1134 return FALSE;
1135 }
1136
1137 static CamelSGeneratorFunc *
get_generator_function(const gchar * fname)1138 get_generator_function (const gchar *fname)
1139 {
1140 gint i;
1141
1142 g_return_val_if_fail (fname != NULL, NULL);
1143
1144 for (i = 0; i < sizeof (generators) / sizeof (generators[0]); i++)
1145 if (strcmp (generators[i].name, fname) == 0)
1146 return generators[i].func;
1147
1148 return NULL;
1149 }
1150
1151 /* this must only be called from inside term evaluation callbacks! */
1152 static CamelSExpResult *
camel_sexp_term_evaluate_occur_times(CamelSExp * sexp,CamelSExpTerm * term,time_t * start,time_t * end)1153 camel_sexp_term_evaluate_occur_times (CamelSExp *sexp,
1154 CamelSExpTerm *term,
1155 time_t *start,
1156 time_t *end)
1157 {
1158 CamelSExpResult *result = NULL;
1159 gint i, argc;
1160 CamelSExpResult **argv;
1161 gboolean ok = TRUE;
1162
1163 g_return_val_if_fail (term != NULL, NULL);
1164 g_return_val_if_fail (start != NULL, NULL);
1165 g_return_val_if_fail (end != NULL, NULL);
1166
1167 /*
1168 printf ("eval term :\n");
1169 parse_dump_term (t, 0);
1170 */
1171
1172 switch (term->type) {
1173 case CAMEL_SEXP_TERM_STRING:
1174 r (printf (" (string \"%s\")\n", term->value.string));
1175 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_STRING);
1176 result->value.string = g_strdup (term->value.string);
1177 break;
1178 case CAMEL_SEXP_TERM_IFUNC:
1179 case CAMEL_SEXP_TERM_FUNC:
1180 {
1181 CamelSGeneratorFunc *generator = NULL;
1182 CamelSOperatorFunc *operator = NULL;
1183
1184 r (printf (" (function \"%s\"\n", term->value.func.sym->name));
1185
1186 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
1187 argc = term->value.func.termcount;
1188 argv = g_alloca (sizeof (argv[0]) * argc);
1189
1190 for (i = 0; i < argc; i++) {
1191 argv[i] = camel_sexp_term_evaluate_occur_times (
1192 sexp, term->value.func.terms[i], start, end);
1193 }
1194
1195 if (is_time_function (term->value.func.sym->name)) {
1196 /* evaluate time */
1197 if (term->value.func.sym->f.func)
1198 result = term->value.func.sym->f.func (sexp, argc, argv, term->value.func.sym->data);
1199 } else if ((generator = get_generator_function (term->value.func.sym->name)) != NULL) {
1200 /* evaluate generator function */
1201 result->time_generator = TRUE;
1202 ok = generator (argc, argv, result);
1203 } else if ((operator = get_operator_function (term->value.func.sym->name)) != NULL)
1204 /* evaluate operator function */
1205 ok = operator (argc, argv, result);
1206 else {
1207 /* normal function: we need to scan all objects */
1208 result->time_generator = FALSE;
1209 }
1210
1211 camel_sexp_resultv_free (sexp, argc, argv);
1212 break;
1213 }
1214 case CAMEL_SEXP_TERM_INT:
1215 case CAMEL_SEXP_TERM_BOOL:
1216 case CAMEL_SEXP_TERM_TIME:
1217 break;
1218 default:
1219 ok = FALSE;
1220 break;
1221 }
1222
1223 if (!ok)
1224 camel_sexp_fatal_error (sexp, "Error in parse tree");
1225
1226 if (result == NULL)
1227 result = camel_sexp_result_new (sexp, CAMEL_SEXP_RES_UNDEFINED);
1228
1229 return result;
1230 }
1231
1232 /*
1233 PARSER
1234 */
1235
1236 static CamelSExpTerm *
parse_term_new(CamelSExp * sexp,gint type)1237 parse_term_new (CamelSExp *sexp,
1238 gint type)
1239 {
1240 CamelSExpTerm *term;
1241
1242 term = camel_memchunk_alloc0 (sexp->priv->term_chunks);
1243 term->type = type;
1244
1245 return term;
1246 }
1247
1248 static void
parse_term_free(CamelSExp * sexp,CamelSExpTerm * term)1249 parse_term_free (CamelSExp *sexp,
1250 CamelSExpTerm *term)
1251 {
1252 gint i;
1253
1254 if (term == NULL) {
1255 return;
1256 }
1257
1258 switch (term->type) {
1259 case CAMEL_SEXP_TERM_INT:
1260 case CAMEL_SEXP_TERM_BOOL:
1261 case CAMEL_SEXP_TERM_TIME:
1262 case CAMEL_SEXP_TERM_VAR:
1263 break;
1264
1265 case CAMEL_SEXP_TERM_STRING:
1266 g_free (term->value.string);
1267 break;
1268
1269 case CAMEL_SEXP_TERM_FUNC:
1270 case CAMEL_SEXP_TERM_IFUNC:
1271 for (i = 0; i < term->value.func.termcount; i++) {
1272 parse_term_free (sexp, term->value.func.terms[i]);
1273 }
1274 g_free (term->value.func.terms);
1275 break;
1276
1277 default:
1278 printf ("parse_term_free: unknown type: %d\n", term->type);
1279 }
1280 camel_memchunk_free (sexp->priv->term_chunks, term);
1281 }
1282
1283 static CamelSExpTerm **
parse_values(CamelSExp * sexp,gint * len)1284 parse_values (CamelSExp *sexp,
1285 gint *len)
1286 {
1287 gint token;
1288 CamelSExpTerm **terms;
1289 gint i, size = 0;
1290 GScanner *gs = sexp->priv->scanner;
1291 GSList *list = NULL, *l;
1292
1293 p (printf ("parsing values\n"));
1294
1295 while ( (token = g_scanner_peek_next_token (gs)) != G_TOKEN_EOF
1296 && token != ')') {
1297 list = g_slist_prepend (list, parse_value (sexp));
1298 size++;
1299 }
1300
1301 /* go over the list, and put them backwards into the term array */
1302 terms = g_malloc (size * sizeof (*terms));
1303 l = list;
1304 for (i = size - 1; i >= 0; i--) {
1305 if (!l || !l->data) {
1306 if (!l)
1307 g_warn_if_fail (l != NULL);
1308 if (l && !l->data)
1309 g_warn_if_fail (l->data != NULL);
1310
1311 g_slist_free (list);
1312 g_free (terms);
1313
1314 *len = 0;
1315
1316 return NULL;
1317 }
1318
1319 terms[i] = l->data;
1320 l = g_slist_next (l);
1321 }
1322 g_slist_free (list);
1323
1324 p (printf ("found %d subterms\n", size));
1325 *len = size;
1326
1327 p (printf ("done parsing values\n"));
1328 return terms;
1329 }
1330
1331 /**
1332 * camel_sexp_parse_value: (skip)
1333 * @sexp: a #CamelSExp
1334 *
1335 * Returns: (nullable) (transfer none): a #CamelSExpTerm of the next token, or %NULL when there is none.
1336 *
1337 * Since: 3.4
1338 **/
1339 CamelSExpTerm *
camel_sexp_parse_value(CamelSExp * sexp)1340 camel_sexp_parse_value (CamelSExp *sexp)
1341 {
1342 return parse_value (sexp);
1343 }
1344
1345 static CamelSExpTerm *
parse_value(CamelSExp * sexp)1346 parse_value (CamelSExp *sexp)
1347 {
1348 gint token, negative = FALSE;
1349 CamelSExpTerm *term = NULL;
1350 GScanner *gs = sexp->priv->scanner;
1351 CamelSExpSymbol *sym;
1352
1353 p (printf ("parsing value\n"));
1354
1355 token = g_scanner_get_next_token (gs);
1356 switch (token) {
1357 case G_TOKEN_EOF:
1358 break;
1359 case G_TOKEN_LEFT_PAREN:
1360 p (printf ("got brace, its a list!\n"));
1361 return parse_list (sexp, TRUE);
1362 case G_TOKEN_STRING:
1363 p (printf ("got string '%s'\n", g_scanner_cur_value (gs).v_string));
1364 term = parse_term_new (sexp, CAMEL_SEXP_TERM_STRING);
1365 term->value.string = g_strdup (g_scanner_cur_value (gs).v_string);
1366 break;
1367 case '-':
1368 p (printf ("got negative int?\n"));
1369 token = g_scanner_get_next_token (gs);
1370 if (token != G_TOKEN_INT) {
1371 camel_sexp_fatal_error (sexp, "Invalid format for a integer value");
1372 return NULL;
1373 }
1374
1375 negative = TRUE;
1376 /* fall through... */
1377 case G_TOKEN_INT:
1378 term = parse_term_new (sexp, CAMEL_SEXP_TERM_INT);
1379 term->value.number = g_scanner_cur_value (gs).v_int;
1380 if (negative)
1381 term->value.number = -term->value.number;
1382 p (printf ("got gint %d\n", term->value.number));
1383 break;
1384 case '#': {
1385 gchar *str;
1386
1387 p (printf ("got bool?\n"));
1388 token = g_scanner_get_next_token (gs);
1389 if (token != G_TOKEN_IDENTIFIER) {
1390 camel_sexp_fatal_error (sexp, "Invalid format for a boolean value");
1391 return NULL;
1392 }
1393
1394 str = g_scanner_cur_value (gs).v_identifier;
1395
1396 g_return_val_if_fail (str != NULL, NULL);
1397 if (!(strlen (str) == 1 && (str[0] == 't' || str[0] == 'f'))) {
1398 camel_sexp_fatal_error (sexp, "Invalid format for a boolean value");
1399 return NULL;
1400 }
1401
1402 term = parse_term_new (sexp, CAMEL_SEXP_TERM_BOOL);
1403 term->value.boolean = (str[0] == 't');
1404 break; }
1405 case G_TOKEN_SYMBOL:
1406 sym = g_scanner_cur_value (gs).v_symbol;
1407 p (printf ("got symbol '%s'\n", sym->name));
1408 switch (sym->type) {
1409 case CAMEL_SEXP_TERM_FUNC:
1410 case CAMEL_SEXP_TERM_IFUNC:
1411 /* this is basically invalid, since we can't use function
1412 * pointers, but let the runtime catch it ... */
1413 term = parse_term_new (sexp, sym->type);
1414 term->value.func.sym = sym;
1415 term->value.func.terms = parse_values (sexp, &term->value.func.termcount);
1416 break;
1417 case CAMEL_SEXP_TERM_VAR:
1418 term = parse_term_new (sexp, sym->type);
1419 term->value.var = sym;
1420 break;
1421 default:
1422 camel_sexp_fatal_error (sexp, "Invalid symbol type: %s: %d", sym->name, sym->type);
1423 }
1424 break;
1425 case G_TOKEN_IDENTIFIER:
1426 p (printf ("got unknown identifider '%s'\n", g_scanner_cur_value (gs).v_identifier));
1427 camel_sexp_fatal_error (sexp, "Unknown identifier: %s", g_scanner_cur_value (gs).v_identifier);
1428 break;
1429 default:
1430 camel_sexp_fatal_error (sexp, "Unexpected token encountered: %d", token);
1431 }
1432 p (printf ("done parsing value\n"));
1433
1434 return term;
1435 }
1436
1437 /* FIXME: this needs some robustification */
1438 static CamelSExpTerm *
parse_list(CamelSExp * sexp,gint gotbrace)1439 parse_list (CamelSExp *sexp,
1440 gint gotbrace)
1441 {
1442 gint token;
1443 CamelSExpTerm *term = NULL;
1444 GScanner *gs = sexp->priv->scanner;
1445
1446 p (printf ("parsing list\n"));
1447 if (gotbrace)
1448 token = '(';
1449 else
1450 token = g_scanner_get_next_token (gs);
1451 if (token =='(') {
1452 token = g_scanner_get_next_token (gs);
1453 switch (token) {
1454 case G_TOKEN_SYMBOL: {
1455 CamelSExpSymbol *sym;
1456
1457 sym = g_scanner_cur_value (gs).v_symbol;
1458 p (printf ("got funciton: %s\n", sym->name));
1459 term = parse_term_new (sexp, sym->type);
1460 p (printf ("created new list %p\n", t));
1461 /* if we have a variable, find out its base type */
1462 while (sym->type == CAMEL_SEXP_TERM_VAR) {
1463 sym = ((CamelSExpTerm *)(sym->data))->value.var;
1464 }
1465 if (sym->type == CAMEL_SEXP_TERM_FUNC
1466 || sym->type == CAMEL_SEXP_TERM_IFUNC) {
1467 term->value.func.sym = sym;
1468 term->value.func.terms = parse_values (sexp, &term->value.func.termcount);
1469 } else {
1470 parse_term_free (sexp, term);
1471 camel_sexp_fatal_error (sexp, "Trying to call variable as function: %s", sym->name);
1472 }
1473 break; }
1474 case G_TOKEN_IDENTIFIER:
1475 camel_sexp_fatal_error (sexp, "Unknown identifier: %s", g_scanner_cur_value (gs).v_identifier);
1476 break;
1477 case G_TOKEN_LEFT_PAREN:
1478 return parse_list (sexp, TRUE);
1479 default:
1480 camel_sexp_fatal_error (sexp, "Unexpected token encountered: %d", token);
1481 }
1482 token = g_scanner_get_next_token (gs);
1483 if (token != ')') {
1484 camel_sexp_fatal_error (sexp, "Missing ')'");
1485 }
1486 } else {
1487 camel_sexp_fatal_error (sexp, "Missing '('");
1488 }
1489
1490 p (printf ("returning list %p\n", term));
1491
1492 return term;
1493 }
1494
1495 static void
free_symbol(gpointer key,gpointer value,gpointer data)1496 free_symbol (gpointer key,
1497 gpointer value,
1498 gpointer data)
1499 {
1500 CamelSExpSymbol *sym = value;
1501
1502 g_free (sym->name);
1503 g_free (sym);
1504 }
1505
1506 static void
camel_sexp_finalize(GObject * object)1507 camel_sexp_finalize (GObject *object)
1508 {
1509 CamelSExp *sexp = (CamelSExp *) object;
1510
1511 if (sexp->priv->tree) {
1512 parse_term_free (sexp, sexp->priv->tree);
1513 sexp->priv->tree = NULL;
1514 }
1515
1516 camel_memchunk_destroy (sexp->priv->term_chunks);
1517 camel_memchunk_destroy (sexp->priv->result_chunks);
1518
1519 g_scanner_scope_foreach_symbol (sexp->priv->scanner, 0, free_symbol, NULL);
1520 g_scanner_destroy (sexp->priv->scanner);
1521
1522 g_free (sexp->priv->error);
1523 sexp->priv->error = NULL;
1524
1525 /* Chain up to parent's finalize() method. */
1526 G_OBJECT_CLASS (camel_sexp_parent_class)->finalize (object);
1527 }
1528
1529 static void
camel_sexp_class_init(CamelSExpClass * class)1530 camel_sexp_class_init (CamelSExpClass *class)
1531 {
1532 GObjectClass *object_class;
1533
1534 object_class = G_OBJECT_CLASS (class);
1535 object_class->finalize = camel_sexp_finalize;
1536 }
1537
1538 /* 'builtin' functions */
1539 static const struct {
1540 const gchar *name;
1541 CamelSExpFunc func;
1542 gint type; /* set to 1 if a function can perform shortcut
1543 * evaluation, or doesn't execute everything,
1544 * 0 otherwise */
1545 } symbols[] = {
1546 { "and", (CamelSExpFunc) term_eval_and, 1 },
1547 { "or", (CamelSExpFunc) term_eval_or, 1 },
1548 { "not", (CamelSExpFunc) term_eval_not, 0 },
1549 { "<", (CamelSExpFunc) term_eval_lt, 1 },
1550 { ">", (CamelSExpFunc) term_eval_gt, 1 },
1551 { "=", (CamelSExpFunc) term_eval_eq, 1 },
1552 { "+", (CamelSExpFunc) term_eval_plus, 0 },
1553 { "-", (CamelSExpFunc) term_eval_sub, 0 },
1554 { "cast-int", (CamelSExpFunc) term_eval_castint, 0 },
1555 { "cast-string", (CamelSExpFunc) term_eval_caststring, 0 },
1556 { "if", (CamelSExpFunc) term_eval_if, 1 },
1557 { "begin", (CamelSExpFunc) term_eval_begin, 1 },
1558 };
1559
1560 static void
camel_sexp_init(CamelSExp * sexp)1561 camel_sexp_init (CamelSExp *sexp)
1562 {
1563 gint i;
1564
1565 sexp->priv = camel_sexp_get_instance_private (sexp);
1566
1567 sexp->priv->scanner = g_scanner_new (&scanner_config);
1568 sexp->priv->term_chunks = camel_memchunk_new (16, sizeof (CamelSExpTerm));
1569 sexp->priv->result_chunks = camel_memchunk_new (16, sizeof (CamelSExpResult));
1570
1571 /* load in builtin symbols? */
1572 for (i = 0; i < G_N_ELEMENTS (symbols); i++) {
1573 if (symbols[i].type == 1) {
1574 camel_sexp_add_ifunction (
1575 sexp, 0, symbols[i].name,
1576 (CamelSExpIFunc) symbols[i].func,
1577 (gpointer) &symbols[i]);
1578 } else {
1579 camel_sexp_add_function (
1580 sexp, 0, symbols[i].name,
1581 symbols[i].func,
1582 (gpointer) &symbols[i]);
1583 }
1584 }
1585 }
1586
1587 /**
1588 * camel_sexp_new:
1589 *
1590 * Returns: (transfer full): a new #CamelSExp
1591 *
1592 * Since: 3.4
1593 **/
1594 CamelSExp *
camel_sexp_new(void)1595 camel_sexp_new (void)
1596 {
1597 return g_object_new (CAMEL_TYPE_SEXP, NULL);
1598 }
1599
1600 /**
1601 * camel_sexp_add_function:
1602 * @sexp: a #CamelSExp
1603 * @scope: a scope
1604 * @name: a function name
1605 * @func: (scope call) (closure user_data): a function callback
1606 * @user_data: user data for @func
1607 *
1608 * Adds a function symbol which can not perform short evaluation.
1609 * Use camel_sexp_add_ifunction() for functions which can.
1610 *
1611 * Since: 3.4
1612 **/
1613 void
camel_sexp_add_function(CamelSExp * sexp,guint scope,const gchar * name,CamelSExpFunc func,gpointer user_data)1614 camel_sexp_add_function (CamelSExp *sexp,
1615 guint scope,
1616 const gchar *name,
1617 CamelSExpFunc func,
1618 gpointer user_data)
1619 {
1620 CamelSExpSymbol *sym;
1621
1622 g_return_if_fail (CAMEL_IS_SEXP (sexp));
1623 g_return_if_fail (name != NULL);
1624
1625 camel_sexp_remove_symbol (sexp, scope, name);
1626
1627 sym = g_malloc0 (sizeof (*sym));
1628 sym->name = g_strdup (name);
1629 sym->f.func = func;
1630 sym->type = CAMEL_SEXP_TERM_FUNC;
1631 sym->data = user_data;
1632
1633 g_scanner_scope_add_symbol (sexp->priv->scanner, scope, sym->name, sym);
1634 }
1635
1636 /**
1637 * camel_sexp_add_ifunction:
1638 * @sexp: a #CamelSExp
1639 * @scope: a scope
1640 * @name: a function name
1641 * @ifunc: (scope call) (closure user_data): a function callback
1642 * @user_data: user data for @ifunc
1643 *
1644 * Adds a function symbol which can perform short evaluation,
1645 * or doesn't execute everything. Use camel_sexp_add_function()
1646 * for any other types of the function symbols.
1647 *
1648 * Since: 3.4
1649 **/
1650 void
camel_sexp_add_ifunction(CamelSExp * sexp,guint scope,const gchar * name,CamelSExpIFunc ifunc,gpointer user_data)1651 camel_sexp_add_ifunction (CamelSExp *sexp,
1652 guint scope,
1653 const gchar *name,
1654 CamelSExpIFunc ifunc,
1655 gpointer user_data)
1656 {
1657 CamelSExpSymbol *sym;
1658
1659 g_return_if_fail (CAMEL_IS_SEXP (sexp));
1660 g_return_if_fail (name != NULL);
1661
1662 camel_sexp_remove_symbol (sexp, scope, name);
1663
1664 sym = g_malloc0 (sizeof (*sym));
1665 sym->name = g_strdup (name);
1666 sym->f.ifunc = ifunc;
1667 sym->type = CAMEL_SEXP_TERM_IFUNC;
1668 sym->data = user_data;
1669
1670 g_scanner_scope_add_symbol (sexp->priv->scanner, scope, sym->name, sym);
1671 }
1672
1673 /**
1674 * camel_sexp_add_variable:
1675 * @sexp: a #CamelSExp
1676 * @scope: a scope
1677 * @name: a variable name
1678 * @value: a variable value, as a #CamelSExpTerm
1679 *
1680 * Adds a variable named @name to the given @scope, set to the given @value.
1681 *
1682 * Since: 3.4
1683 **/
1684 void
camel_sexp_add_variable(CamelSExp * sexp,guint scope,const gchar * name,CamelSExpTerm * value)1685 camel_sexp_add_variable (CamelSExp *sexp,
1686 guint scope,
1687 const gchar *name,
1688 CamelSExpTerm *value)
1689 {
1690 CamelSExpSymbol *sym;
1691
1692 g_return_if_fail (CAMEL_IS_SEXP (sexp));
1693 g_return_if_fail (name != NULL);
1694
1695 sym = g_malloc0 (sizeof (*sym));
1696 sym->name = g_strdup (name);
1697 sym->type = CAMEL_SEXP_TERM_VAR;
1698 sym->data = value;
1699
1700 g_scanner_scope_add_symbol (sexp->priv->scanner, scope, sym->name, sym);
1701 }
1702
1703 /**
1704 * camel_sexp_remove_symbol:
1705 * @sexp: a #CamelSExp
1706 * @scope: a scope
1707 * @name: a symbol name
1708 *
1709 * Revoes a symbol from a scope.
1710 *
1711 * Since: 3.4
1712 **/
1713 void
camel_sexp_remove_symbol(CamelSExp * sexp,guint scope,const gchar * name)1714 camel_sexp_remove_symbol (CamelSExp *sexp,
1715 guint scope,
1716 const gchar *name)
1717 {
1718 gint oldscope;
1719 CamelSExpSymbol *sym;
1720
1721 g_return_if_fail (CAMEL_IS_SEXP (sexp));
1722 g_return_if_fail (name != NULL);
1723
1724 oldscope = g_scanner_set_scope (sexp->priv->scanner, scope);
1725 sym = g_scanner_lookup_symbol (sexp->priv->scanner, name);
1726 g_scanner_scope_remove_symbol (sexp->priv->scanner, scope, name);
1727 g_scanner_set_scope (sexp->priv->scanner, oldscope);
1728 if (sym != NULL) {
1729 g_free (sym->name);
1730 g_free (sym);
1731 }
1732 }
1733
1734 /**
1735 * camel_sexp_set_scope:
1736 * @sexp: a #CamelSExp
1737 * @scope: a scope to set
1738 *
1739 * sets the current scope for the scanner.
1740 *
1741 * Returns: the previous scope id
1742 *
1743 * Since: 3.4
1744 **/
1745 gint
camel_sexp_set_scope(CamelSExp * sexp,guint scope)1746 camel_sexp_set_scope (CamelSExp *sexp,
1747 guint scope)
1748 {
1749 g_return_val_if_fail (CAMEL_IS_SEXP (sexp), 0);
1750
1751 return g_scanner_set_scope (sexp->priv->scanner, scope);
1752 }
1753
1754 /**
1755 * camel_sexp_input_text:
1756 * @sexp: a #CamelSExp
1757 * @text: a text buffer to scan
1758 * @len: the length of the text buffer
1759 *
1760 * Prepares to scan a text buffer.
1761 *
1762 * Since: 3.4
1763 **/
1764 void
camel_sexp_input_text(CamelSExp * sexp,const gchar * text,gint len)1765 camel_sexp_input_text (CamelSExp *sexp,
1766 const gchar *text,
1767 gint len)
1768 {
1769 g_return_if_fail (CAMEL_IS_SEXP (sexp));
1770 g_return_if_fail (text != NULL);
1771
1772 g_scanner_input_text (sexp->priv->scanner, text, len);
1773 }
1774
1775 /**
1776 * camel_sexp_input_file:
1777 * @sexp: a #CamelSExp
1778 * @fd: a file descriptor
1779 *
1780 * Prepares to scan a file.
1781 *
1782 * Since: 3.4
1783 **/
1784 void
camel_sexp_input_file(CamelSExp * sexp,gint fd)1785 camel_sexp_input_file (CamelSExp *sexp,
1786 gint fd)
1787 {
1788 g_return_if_fail (CAMEL_IS_SEXP (sexp));
1789
1790 g_scanner_input_file (sexp->priv->scanner, fd);
1791 }
1792
1793 /**
1794 * camel_sexp_parse:
1795 * @sexp: a #CamelSExp
1796 *
1797 * Since: 3.4
1798 **/
1799 gint
camel_sexp_parse(CamelSExp * sexp)1800 camel_sexp_parse (CamelSExp *sexp)
1801 {
1802 g_return_val_if_fail (CAMEL_IS_SEXP (sexp), -1);
1803
1804 if (setjmp (sexp->priv->failenv)) {
1805 g_warning ("Error in parsing: %s", sexp->priv->error);
1806 return -1;
1807 }
1808
1809 if (sexp->priv->tree)
1810 parse_term_free (sexp, sexp->priv->tree);
1811
1812 sexp->priv->tree = parse_value (sexp);
1813
1814 return 0;
1815 }
1816
1817 /**
1818 * camel_sexp_eval: (skip)
1819 * @sexp: a #CamelSExp
1820 *
1821 * Since: 3.4
1822 **/
1823 CamelSExpResult *
camel_sexp_eval(CamelSExp * sexp)1824 camel_sexp_eval (CamelSExp *sexp)
1825 {
1826 g_return_val_if_fail (CAMEL_IS_SEXP (sexp), NULL);
1827 g_return_val_if_fail (sexp->priv->tree != NULL, NULL);
1828
1829 if (setjmp (sexp->priv->failenv)) {
1830 g_warning ("Error in execution: %s", sexp->priv->error);
1831 return NULL;
1832 }
1833
1834 return camel_sexp_term_eval (sexp, sexp->priv->tree);
1835 }
1836
1837 /**
1838 * e_cal_backend_sexp_evaluate_occur_times:
1839 * @sexp: a #CamelSExp
1840 * @start: Start of the time window will be stored here.
1841 * @end: End of the time window will be stored here.
1842 *
1843 * Determines biggest time window given by expressions "occur-in-range" in sexp.
1844 *
1845 * Since: 3.4
1846 */
1847 gboolean
camel_sexp_evaluate_occur_times(CamelSExp * sexp,time_t * start,time_t * end)1848 camel_sexp_evaluate_occur_times (CamelSExp *sexp,
1849 time_t *start,
1850 time_t *end)
1851 {
1852 CamelSExpResult *result;
1853 gboolean generator;
1854 g_return_val_if_fail (CAMEL_IS_SEXP (sexp), FALSE);
1855 g_return_val_if_fail (sexp->priv->tree != NULL, FALSE);
1856 g_return_val_if_fail (start != NULL, FALSE);
1857 g_return_val_if_fail (end != NULL, FALSE);
1858
1859 *start = *end = -1;
1860
1861 if (setjmp (sexp->priv->failenv)) {
1862 g_warning ("Error in execution: %s", sexp->priv->error);
1863 return FALSE;
1864 }
1865
1866 result = camel_sexp_term_evaluate_occur_times (
1867 sexp, sexp->priv->tree, start, end);
1868 generator = result->time_generator;
1869
1870 if (generator) {
1871 *start = result->occuring_start;
1872 *end = result->occuring_end;
1873 }
1874
1875 camel_sexp_result_free (sexp, result);
1876
1877 return generator;
1878 }
1879
1880 /**
1881 * camel_sexp_encode_bool:
1882 * @string: Destination #GString
1883 * @v_bool: the value
1884 *
1885 * Encode a bool into an s-expression @string. Bools are
1886 * encoded using #t #f syntax.
1887 *
1888 * Since: 3.4
1889 **/
1890 void
camel_sexp_encode_bool(GString * string,gboolean v_bool)1891 camel_sexp_encode_bool (GString *string,
1892 gboolean v_bool)
1893 {
1894 if (v_bool)
1895 g_string_append (string, " #t");
1896 else
1897 g_string_append (string, " #f");
1898 }
1899
1900 /**
1901 * camel_sexp_encode_string:
1902 * @string: Destination #GString
1903 * @v_string: String expression.
1904 *
1905 * Add a c string @v_string to the s-expression stored in
1906 * the gstring @s. Quotes are added, and special characters
1907 * are escaped appropriately.
1908 *
1909 * Since: 3.4
1910 **/
1911 void
camel_sexp_encode_string(GString * string,const gchar * v_string)1912 camel_sexp_encode_string (GString *string,
1913 const gchar *v_string)
1914 {
1915 gchar c;
1916 const gchar *p;
1917
1918 if (v_string == NULL)
1919 p = "";
1920 else
1921 p = v_string;
1922 g_string_append (string, " \"");
1923 while ((c = *p++)) {
1924 if (c == '\\' || c == '\"' || c == '\'')
1925 g_string_append_c (string, '\\');
1926 g_string_append_c (string, c);
1927 }
1928 g_string_append_c (string, '\"');
1929 }
1930
1931 #ifdef TESTER
1932 gint
main(gint argc,gchar ** argv)1933 main (gint argc,
1934 gchar **argv)
1935 {
1936 CamelSExp *sexp;
1937 gchar *t = "(+ \"foo\" \"\\\"\" \"bar\" \"\\\\ blah \\x \")";
1938 CamelSExpResult *result;
1939
1940 sexp = camel_sexp_new ();
1941
1942 camel_sexp_add_variable (sexp, 0, "test", NULL);
1943
1944 if (argc < 2 || !argv[1])
1945 return;
1946
1947 camel_sexp_input_text (sexp, t, t);
1948 camel_sexp_parse (sexp);
1949
1950 if (sexp->priv->tree)
1951 parse_dump_term (sexp->priv->tree, 0);
1952
1953 result = camel_sexp_eval (sexp);
1954 if (result) {
1955 eval_dump_result (result, 0);
1956 } else {
1957 printf ("no result?|\n");
1958 }
1959
1960 return 0;
1961 }
1962 #endif
1963