c0e149ae7f8b7406337aadc653424efc42be6da4
[protos/libecoli.git] / lib / ecoli_node_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 #include <assert.h>
32
33 #include <ecoli_malloc.h>
34 #include <ecoli_strvec.h>
35 #include <ecoli_test.h>
36 #include <ecoli_node.h>
37 #include <ecoli_parsed.h>
38 #include <ecoli_node_int.h>
39 #include <ecoli_node_str.h>
40 #include <ecoli_node_re_lex.h>
41 #include <ecoli_node_expr.h>
42
43 struct my_eval_result {
44         int val;
45 };
46
47 static int
48 ec_node_expr_test_eval_var(void **result, void *userctx,
49         const struct ec_parsed *var)
50 {
51         const struct ec_strvec *vec;
52         struct my_eval_result *eval;
53
54         (void)userctx;
55
56         /* get parsed string vector, it should contain only one str */
57         vec = ec_parsed_strvec(var);
58         if (ec_strvec_len(vec) != 1)
59                 return -EINVAL;
60
61         eval = ec_malloc(sizeof(*eval));
62         if (eval == NULL)
63                 return -ENOMEM;
64
65         eval->val = atoi(ec_strvec_val(vec, 0)); // XXX use strtol
66         printf("eval var %d\n", eval->val);
67         *result = eval;
68
69         return 0;
70 }
71
72 static int
73 ec_node_expr_test_eval_pre_op(void **result, void *userctx, void *operand,
74         const struct ec_parsed *operator)
75 {
76         const struct ec_strvec *vec;
77         struct my_eval_result *eval = operand;;
78
79         (void)userctx;
80
81         /* get parsed string vector, it should contain only one str */
82         vec = ec_parsed_strvec(operator);
83         if (ec_strvec_len(vec) != 1)
84                 return -EINVAL;
85
86         if (!strcmp(ec_strvec_val(vec, 0), "!"))
87                 eval->val = !eval->val;
88         else
89                 return -EINVAL;
90
91         printf("eval pre_op %d\n", eval->val);
92         *result = eval;
93
94         return 0;
95 }
96
97 static int
98 ec_node_expr_test_eval_post_op(void **result, void *userctx, void *operand,
99         const struct ec_parsed *operator)
100 {
101         const struct ec_strvec *vec;
102         struct my_eval_result *eval = operand;;
103
104         (void)userctx;
105
106         /* get parsed string vector, it should contain only one str */
107         vec = ec_parsed_strvec(operator);
108         if (ec_strvec_len(vec) != 1)
109                 return -EINVAL;
110
111         if (!strcmp(ec_strvec_val(vec, 0), "^"))
112                 eval->val = eval->val * eval->val;
113         else
114                 return -EINVAL;
115
116         printf("eval post_op %d\n", eval->val);
117         *result = eval;
118
119         return 0;
120 }
121
122 static int
123 ec_node_expr_test_eval_bin_op(void **result, void *userctx, void *operand1,
124         const struct ec_parsed *operator, void *operand2)
125
126 {
127         const struct ec_strvec *vec;
128         struct my_eval_result *eval1 = operand1;;
129         struct my_eval_result *eval2 = operand2;;
130
131         (void)userctx;
132
133         /* get parsed string vector, it should contain only one str */
134         vec = ec_parsed_strvec(operator);
135         if (ec_strvec_len(vec) != 1)
136                 return -EINVAL;
137
138         if (!strcmp(ec_strvec_val(vec, 0), "+"))
139                 eval1->val = eval1->val + eval2->val;
140         else if (!strcmp(ec_strvec_val(vec, 0), "*"))
141                 eval1->val = eval1->val * eval2->val;
142         else
143                 return -EINVAL;
144
145         printf("eval bin_op %d\n", eval1->val);
146         ec_free(eval2);
147         *result = eval1;
148
149         return 0;
150 }
151
152 static int
153 ec_node_expr_test_eval_parenthesis(void **result, void *userctx,
154         const struct ec_parsed *open_paren,
155         const struct ec_parsed *close_paren,
156         void *value)
157 {
158         (void)userctx;
159         (void)open_paren;
160         (void)close_paren;
161
162         printf("eval paren\n");
163         *result = value;
164
165         return 0;
166 }
167
168 static void
169 ec_node_expr_test_eval_free(void *result, void *userctx)
170 {
171         (void)userctx;
172         ec_free(result);
173 }
174
175 static const struct ec_node_expr_eval_ops test_ops = {
176         .eval_var = ec_node_expr_test_eval_var,
177         .eval_pre_op = ec_node_expr_test_eval_pre_op,
178         .eval_post_op = ec_node_expr_test_eval_post_op,
179         .eval_bin_op = ec_node_expr_test_eval_bin_op,
180         .eval_parenthesis = ec_node_expr_test_eval_parenthesis,
181         .eval_free = ec_node_expr_test_eval_free,
182 };
183
184 static int ec_node_expr_test_eval(struct ec_node *lex_node,
185         const struct ec_node *expr_node,
186         const char *str, int val)
187 {
188         struct ec_parsed *p;
189         void *result;
190         struct my_eval_result *eval;
191         int ret;
192
193         /* XXX check node type (again and again) */
194
195         p = ec_node_parse(lex_node, str);
196         if (p == NULL)
197                 return -1;
198
199         ret = ec_node_expr_eval(&result, expr_node, p, &test_ops, NULL);
200         ec_parsed_free(p);
201         if (ret < 0)
202                 return -1;
203
204         /* the parsed value is an integer */
205         eval = result;
206         assert(eval != NULL);
207
208         printf("result: %d (expected %d)\n", eval->val, val);
209         if (eval->val == val)
210                 ret = 0;
211         else
212                 ret = -1;
213
214         ec_free(eval);
215
216         return ret;
217 }
218
219 /* LCOV_EXCL_START */
220 static int ec_node_expr_testcase(void)
221 {
222         struct ec_node *node = NULL, *lex_node = NULL;
223         int ret = 0;
224
225         node = ec_node("expr", "my_expr");
226         if (node == NULL)
227                 return -1;
228
229         ec_node_expr_set_val_node(node, ec_node_int(NULL, 0, UCHAR_MAX, 0));
230         ec_node_expr_add_bin_op(node, ec_node_str(NULL, "+"));
231         ec_node_expr_add_bin_op(node, ec_node_str(NULL, "*"));
232         ec_node_expr_add_pre_op(node, ec_node_str(NULL, "!"));  /* not */
233         ec_node_expr_add_post_op(node, ec_node_str(NULL, "^")); /* square */
234         ec_node_expr_add_parenthesis(node, ec_node_str(NULL, "("),
235                 ec_node_str(NULL, ")"));
236         ret |= EC_TEST_CHECK_PARSE(node, 1, "1");
237         ret |= EC_TEST_CHECK_PARSE(node, 1, "1", "1");
238         ret |= EC_TEST_CHECK_PARSE(node, 1, "1", "*");
239         ret |= EC_TEST_CHECK_PARSE(node, 3, "1", "*", "1");
240         ret |= EC_TEST_CHECK_PARSE(node, 3, "1", "*", "1", "*");
241         ret |= EC_TEST_CHECK_PARSE(node, 4, "1", "+", "!", "1");
242         ret |= EC_TEST_CHECK_PARSE(node, 4, "1", "^", "+", "1");
243         ret |= EC_TEST_CHECK_PARSE(node, 5, "1", "*", "1", "*", "1");
244         ret |= EC_TEST_CHECK_PARSE(node, 5, "1", "*", "1", "+", "1");
245         ret |= EC_TEST_CHECK_PARSE(node, 7, "1", "*", "1", "*", "1", "*", "1");
246         ret |= EC_TEST_CHECK_PARSE(
247                 node, 10, "!", "(", "1", "*", "(", "1", "+", "1", ")", ")");
248         ret |= EC_TEST_CHECK_PARSE(node, 5, "1", "+", "!", "1", "^");
249
250         /* prepend a lexer to the expression node */
251         lex_node = ec_node_re_lex(NULL, ec_node_clone(node));
252         if (lex_node == NULL)
253                 goto fail;
254
255         ret |= ec_node_re_lex_add(lex_node, "[0-9]+", 1); /* vars */
256         ret |= ec_node_re_lex_add(lex_node, "[+*!^()]", 1); /* operators */
257         ret |= ec_node_re_lex_add(lex_node, "[  ]+", 0); /* spaces */
258
259         /* valid expressions */
260         ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "!1");
261         ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "1^");
262         ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "1^ + 1");
263         ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "1 + 4 * (2 + 3^)^");
264         ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "(1)");
265         ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "3*!3+!3*(2+ 2)");
266         ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "!!(!1)^ + !(4 + (2*3))");
267         ret |= EC_TEST_CHECK_PARSE(lex_node, 1, "(1 + 1)^ * 1^");
268
269         /* invalid expressions */
270         ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "");
271         ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "()");
272         ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "(");
273         ret |= EC_TEST_CHECK_PARSE(lex_node, -1, ")");
274         ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "+1");
275         ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "1+");
276         ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "1+*1");
277         ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "1+(1*1");
278         ret |= EC_TEST_CHECK_PARSE(lex_node, -1, "1+!1!1)");
279
280         ret |= ec_node_expr_test_eval(lex_node, node, "1^", 1);
281         ret |= ec_node_expr_test_eval(lex_node, node, "2^", 4);
282         ret |= ec_node_expr_test_eval(lex_node, node, "!1", 0);
283         ret |= ec_node_expr_test_eval(lex_node, node, "!0", 1);
284
285         ret |= ec_node_expr_test_eval(lex_node, node, "1+1", 2);
286         ret |= ec_node_expr_test_eval(lex_node, node, "1+2+3", 6);
287         ret |= ec_node_expr_test_eval(lex_node, node, "1+1*2", 4);
288         ret |= ec_node_expr_test_eval(lex_node, node, "2 * 2^", 8);
289         ret |= ec_node_expr_test_eval(lex_node, node, "(1 + !0)^ * !0^", 4);
290         ret |= ec_node_expr_test_eval(lex_node, node, "(1 + !1) * 3", 3);
291
292         ec_node_free(node);
293         ec_node_free(lex_node);
294
295         return ret;
296
297 fail:
298         ec_node_free(lex_node);
299         ec_node_free(node);
300         return -1;
301 }
302 /* LCOV_EXCL_STOP */
303
304 static struct ec_test ec_node_expr_test = {
305         .name = "expr",
306         .test = ec_node_expr_testcase,
307 };
308
309 EC_TEST_REGISTER(ec_node_expr_test);