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