save
[protos/libecoli.git] / lib / ecoli_tk_expr_test.c
1 /*
2  * Copyright (c) 2016-2017, Olivier MATZ <zer0@droids-corp.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the University of California, Berkeley nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <errno.h>
29 #include <limits.h>
30 #include <stdint.h>
31
32 #include <ecoli_malloc.h>
33 #include <ecoli_strvec.h>
34 #include <ecoli_test.h>
35 #include <ecoli_tk_int.h>
36 #include <ecoli_tk_str.h>
37 #include <ecoli_tk_re_lex.h>
38 #include <ecoli_tk_expr.h>
39
40 struct my_eval_result {
41         int val;
42 };
43
44 static struct ec_tk_expr_eval_result
45 ec_tk_expr_test_eval_var(void *userctx, const struct ec_parsed_tk *var)
46 {
47         const struct ec_strvec *vec;
48         struct ec_tk_expr_eval_result res = { .code = -EINVAL, .parsed = NULL };
49         struct my_eval_result *eval;
50
51         (void)userctx;
52
53         /* get parsed string vector, it should contain only one str */
54         vec = ec_parsed_tk_strvec(var);
55         if (ec_strvec_len(vec) != 1)
56                 return res;
57
58         res.code = -ENOMEM;
59         eval = ec_malloc(sizeof(*eval));
60         if (eval == NULL)
61                 return res;
62
63         eval->val = atoi(ec_strvec_val(vec, 0)); // XXX use strtol
64         printf("eval var %d\n", eval->val);
65         res.code = 0;
66         res.parsed = eval;
67
68         return res;
69 }
70
71 static struct ec_tk_expr_eval_result
72 ec_tk_expr_test_eval_pre_op(void *userctx,
73         struct ec_tk_expr_eval_result operand,
74         const struct ec_parsed_tk *operator)
75 {
76         const struct ec_strvec *vec;
77         struct ec_tk_expr_eval_result res = { .code = -EINVAL, .parsed = NULL };
78         struct my_eval_result *eval = operand.parsed;;
79
80         (void)userctx;
81
82         /* get parsed string vector, it should contain only one str */
83         vec = ec_parsed_tk_strvec(operator);
84         if (ec_strvec_len(vec) != 1) {
85                 ec_free(eval);
86                 return res;
87         }
88
89         if (!strcmp(ec_strvec_val(vec, 0), "~")) {
90                 eval->val = !eval->val;
91         } else {
92                 ec_free(eval);
93                 return res;
94         }
95
96         printf("eval pre_op %d\n", eval->val);
97         res.code = 0;
98         res.parsed = eval;
99
100         return res;
101 }
102
103 static struct ec_tk_expr_eval_result
104 ec_tk_expr_test_eval_post_op(void *userctx,
105         struct ec_tk_expr_eval_result operand,
106         const struct ec_parsed_tk *operator)
107 {
108         const struct ec_strvec *vec;
109         struct ec_tk_expr_eval_result res = { .code = -EINVAL, .parsed = NULL };
110         struct my_eval_result *eval = operand.parsed;;
111
112         (void)userctx;
113
114         /* get parsed string vector, it should contain only one str */
115         vec = ec_parsed_tk_strvec(operator);
116         if (ec_strvec_len(vec) != 1) {
117                 ec_free(eval);
118                 return res;
119         }
120
121         if (!strcmp(ec_strvec_val(vec, 0), "!")) {
122                 eval->val = eval->val * eval->val;
123         } else {
124                 ec_free(eval);
125                 return res;
126         }
127
128         printf("eval post_op %d\n", eval->val);
129         res.code = 0;
130         res.parsed = eval;
131
132         return res;
133 }
134
135 static struct ec_tk_expr_eval_result
136 ec_tk_expr_test_eval_bin_op(void *userctx,
137         struct ec_tk_expr_eval_result operand1,
138         const struct ec_parsed_tk *operator,
139         struct ec_tk_expr_eval_result operand2)
140
141 {
142         const struct ec_strvec *vec;
143         struct ec_tk_expr_eval_result res = { .code = -EINVAL, .parsed = NULL };
144         struct my_eval_result *eval1 = operand1.parsed;;
145         struct my_eval_result *eval2 = operand2.parsed;;
146
147         (void)userctx;
148
149         /* get parsed string vector, it should contain only one str */
150         vec = ec_parsed_tk_strvec(operator);
151         if (ec_strvec_len(vec) != 1) {
152                 ec_free(eval1);
153                 ec_free(eval2);
154                 return res;
155         }
156
157         if (!strcmp(ec_strvec_val(vec, 0), "+")) {
158                 eval1->val = eval1->val + eval2->val;
159         } else if (!strcmp(ec_strvec_val(vec, 0), "*")) {
160                 eval1->val = eval1->val * eval2->val;
161         } else {
162                 ec_free(eval1);
163                 ec_free(eval2);
164                 return res;
165         }
166
167         printf("eval bin_op %d\n", eval1->val);
168         res.code = 0;
169         res.parsed = eval1;
170
171         return res;
172 }
173
174 static const struct ec_tk_expr_eval_ops test_ops = {
175         .eval_var = ec_tk_expr_test_eval_var,
176         .eval_pre_op = ec_tk_expr_test_eval_pre_op,
177         .eval_post_op = ec_tk_expr_test_eval_post_op,
178         .eval_bin_op = ec_tk_expr_test_eval_bin_op,
179 };
180
181 static int ec_tk_expr_test_eval(struct ec_tk *lex_tk,
182         const struct ec_tk *expr_tk,
183         const char *str, int val)
184 {
185         struct ec_tk_expr_eval_result res;
186         struct ec_parsed_tk *p;
187         struct my_eval_result *eval;
188         int ret;
189
190         /* XXX check tk type (again and again) */
191
192
193         p = ec_tk_parse(lex_tk, str);
194         if (p == NULL)
195                 return -1;
196
197         res = ec_tk_expr_eval(expr_tk, p, &test_ops, NULL);
198         ec_parsed_tk_free(p);
199         if (res.code < 0)
200                 return res.code;
201
202         /* the parsed value is an integer */
203         eval = res.parsed;
204         if (eval == NULL) {
205                 ret = -1;
206         } else {
207                 printf("result: %d (expected %d)\n", eval->val, val);
208                 if (eval->val == val)
209                         ret = 0;
210                 else
211                         ret = -1;
212         }
213         ec_free(eval);
214
215         return ret;
216 }
217
218 static int ec_tk_expr_testcase(void)
219 {
220         struct ec_tk *tk = NULL, *lex_tk = NULL;
221         int ret = 0;
222
223         tk = ec_tk_expr("expr");
224         if (tk == NULL)
225                 return -1;
226
227         ec_tk_expr_set_val_tk(tk, ec_tk_int(NULL, 0, UCHAR_MAX, 0));
228         ec_tk_expr_add_bin_op(tk, ec_tk_str(NULL, "+"));
229         ec_tk_expr_add_bin_op(tk, ec_tk_str(NULL, "*"));
230         ec_tk_expr_add_pre_op(tk, ec_tk_str(NULL, "~"));
231         ec_tk_expr_add_post_op(tk, ec_tk_str(NULL, "!"));
232         ec_tk_expr_add_parenthesis(tk, ec_tk_str(NULL, "("),
233                 ec_tk_str(NULL, ")"));
234         ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "1");
235         ret |= EC_TEST_CHECK_TK_PARSE(tk, 1, "1", "*");
236         ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "1", "*", "1");
237         ret |= EC_TEST_CHECK_TK_PARSE(tk, 4, "1", "+", "~", "1");
238         ret |= EC_TEST_CHECK_TK_PARSE(tk, 4, "1", "!", "+", "1");
239         ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "*", "1", "*", "1");
240         ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "*", "1", "*", "1", "*", "1");
241         ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "*", "1", "+", "1");
242         ret |= EC_TEST_CHECK_TK_PARSE(
243                 tk, 10, "~", "(", "1", "*", "(", "1", "+", "1", ")", ")");
244         ret |= EC_TEST_CHECK_TK_PARSE(tk, 5, "1", "+", "~", "1", "!");
245
246         /* prepend a lexer to the expression token */
247         lex_tk = ec_tk_re_lex(NULL, ec_tk_clone(tk));
248         if (lex_tk == NULL)
249                 goto fail;
250
251         ret |= ec_tk_re_lex_add(lex_tk, "^[0-9]+", 1); /* vars */
252         ret |= ec_tk_re_lex_add(lex_tk, "^[+*~!()]", 1); /* operators */
253         ret |= ec_tk_re_lex_add(lex_tk, "^[     ]+", 0); /* spaces */
254         if (ret != 0)
255                 goto fail;
256
257         /* valid expressions */
258         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "~1");
259         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1!");
260         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1! + 1");
261         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "1 + 4 * (2 + 3!)!");
262         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "(1)");
263         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "3*~3+~3*(2+ 2)");
264         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "~~(~1)! + ~(4 + (2*3))");
265         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, 1, "(1 + 1)! * 1!");
266
267         /* invalid expressions */
268         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "");
269         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "()");
270         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "(");
271         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, ")");
272         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "+1");
273         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+");
274         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+*1");
275         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+(1*1");
276         ret |= EC_TEST_CHECK_TK_PARSE(lex_tk, -1, "1+~1~1)");
277         if (ret)
278                 exit(ret);
279
280         ret |= ec_tk_expr_test_eval(lex_tk, tk, "2!", 4);
281         ret |= ec_tk_expr_test_eval(lex_tk, tk, "1+1", 2);
282         ret |= ec_tk_expr_test_eval(lex_tk, tk, "1+1*2", 4);
283
284         // XXX marche pas:
285         // le "*" s'applique avant le "!" final
286         ret |= ec_tk_expr_test_eval(lex_tk, tk, "2 * 2!", 8);
287         /* ret |= ec_tk_expr_test_eval(lex_tk, tk, "(1 + ~0)! * ~0!", 4); */
288         /* ret |= ec_tk_expr_test_eval(lex_tk, tk, "(1 + ~1) * 3", 3); */
289         /* ret |= ec_tk_expr_test_eval(lex_tk, tk, "~1", 0); */
290         printf("exit\n");
291         exit(ret);
292
293         /*
294           idee:
295           eval_expression() retourne soit:
296           - NONE
297           - *_OP, parsed_tk
298           - VAL, eval(parsed_tk)
299           */
300
301
302         ec_tk_free(tk);
303         ec_tk_free(lex_tk);
304
305         return ret;
306
307 fail:
308         ec_tk_free(lex_tk);
309         ec_tk_free(tk);
310         return -1;
311 }
312
313 static struct ec_test ec_tk_expr_test = {
314         .name = "tk_expr",
315         .test = ec_tk_expr_testcase,
316 };
317
318 EC_REGISTER_TEST(ec_tk_expr_test);