6025fd7f1e5faa6f127a07bfbced56c3f7127aee
[protos/libecoli.git] / lib / ecoli_node_seq.c
1 /*
2  * Copyright (c) 2016, 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 <stdio.h>
29 #include <stdlib.h>
30 #include <stdint.h>
31 #include <string.h>
32 #include <assert.h>
33 #include <stdarg.h>
34 #include <errno.h>
35
36 #include <ecoli_malloc.h>
37 #include <ecoli_log.h>
38 #include <ecoli_test.h>
39 #include <ecoli_strvec.h>
40 #include <ecoli_node.h>
41 #include <ecoli_parsed.h>
42 #include <ecoli_completed.h>
43 #include <ecoli_node_str.h>
44 #include <ecoli_node_option.h>
45 #include <ecoli_node_or.h>
46 #include <ecoli_node_many.h>
47 #include <ecoli_node_seq.h>
48
49 EC_LOG_TYPE_REGISTER(node_seq);
50
51 struct ec_node_seq {
52         struct ec_node gen;
53         struct ec_node **table;
54         size_t len;
55 };
56
57 static int
58 ec_node_seq_parse(const struct ec_node *gen_node,
59                 struct ec_parsed *state,
60                 const struct ec_strvec *strvec)
61 {
62         struct ec_node_seq *node = (struct ec_node_seq *)gen_node;
63         struct ec_strvec *childvec = NULL;
64         size_t len = 0;
65         unsigned int i;
66         int ret;
67
68         for (i = 0; i < node->len; i++) {
69                 childvec = ec_strvec_ndup(strvec, len,
70                         ec_strvec_len(strvec) - len);
71                 if (childvec == NULL) {
72                         ret = -ENOMEM;
73                         goto fail;
74                 }
75
76                 ret = ec_node_parse_child(node->table[i], state, childvec);
77                 if (ret < 0)
78                         goto fail;
79
80                 ec_strvec_free(childvec);
81                 childvec = NULL;
82
83                 if (ret == EC_PARSED_NOMATCH) {
84                         ec_parsed_free_children(state);
85                         return EC_PARSED_NOMATCH;
86                 }
87
88                 len += ret;
89         }
90
91         return len;
92
93 fail:
94         ec_strvec_free(childvec);
95         return ret;
96 }
97
98 static int
99 __ec_node_seq_complete(struct ec_node **table, size_t table_len,
100                 struct ec_completed *completed,
101                 const struct ec_strvec *strvec)
102 {
103         struct ec_parsed *parsed = ec_completed_cur_parse_state(completed);
104         struct ec_strvec *childvec = NULL;
105         unsigned int i;
106         int ret;
107
108         if (table_len == 0)
109                 return 0;
110
111         /*
112          * Example of completion for a sequence node = [n1,n2] and an
113          * input = [a,b,c,d]:
114          *
115          * result = complete(n1, [a,b,c,d]) +
116          *    complete(n2, [b,c,d]) if n1 matches [a] +
117          *    complete(n2, [c,d]) if n1 matches [a,b] +
118          *    complete(n2, [d]) if n1 matches [a,b,c] +
119          *    complete(n2, []) if n1 matches [a,b,c,d]
120          */
121
122         /* first, try to complete with the first node of the table */
123         ret = ec_node_complete_child(table[0], completed, strvec);
124         if (ret < 0)
125                 goto fail;
126
127         /* then, if the first node of the table matches the beginning of the
128          * strvec, try to complete the rest */
129         for (i = 0; i < ec_strvec_len(strvec); i++) {
130                 childvec = ec_strvec_ndup(strvec, 0, i);
131                 if (childvec == NULL)
132                         goto fail;
133
134                 ret = ec_node_parse_child(table[0], parsed, childvec);
135                 if (ret < 0)
136                         goto fail;
137
138                 ec_strvec_free(childvec);
139                 childvec = NULL;
140
141                 if ((unsigned int)ret != i) {
142                         if (ret != EC_PARSED_NOMATCH)
143                                 ec_parsed_del_last_child(parsed);
144                         continue;
145                 }
146
147                 childvec = ec_strvec_ndup(strvec, i, ec_strvec_len(strvec) - i);
148                 if (childvec == NULL) {
149                         ec_parsed_del_last_child(parsed);
150                         goto fail;
151                 }
152
153                 ret = __ec_node_seq_complete(&table[1],
154                                         table_len - 1,
155                                         completed, childvec);
156                 ec_parsed_del_last_child(parsed);
157                 ec_strvec_free(childvec);
158                 childvec = NULL;
159
160                 if (ret < 0)
161                         goto fail;
162         }
163
164         return 0;
165
166 fail:
167         ec_strvec_free(childvec);
168         return -1;
169 }
170
171 static int
172 ec_node_seq_complete(const struct ec_node *gen_node,
173                 struct ec_completed *completed,
174                 const struct ec_strvec *strvec)
175 {
176         struct ec_node_seq *node = (struct ec_node_seq *)gen_node;
177
178         return __ec_node_seq_complete(node->table, node->len, completed,
179                                 strvec);
180 }
181
182 static size_t ec_node_seq_get_max_parse_len(const struct ec_node *gen_node)
183 {
184         struct ec_node_seq *node = (struct ec_node_seq *)gen_node;
185         size_t i, len, ret = 0;
186
187         for (i = 0; i < node->len; i++) {
188                 len = ec_node_get_max_parse_len(node->table[i]);
189                 if (len <= SIZE_MAX - ret)
190                         ret += len;
191                 else
192                         ret = SIZE_MAX;
193         }
194
195         return ret;
196 }
197
198 static void ec_node_seq_free_priv(struct ec_node *gen_node)
199 {
200         struct ec_node_seq *node = (struct ec_node_seq *)gen_node;
201         unsigned int i;
202
203         for (i = 0; i < node->len; i++)
204                 ec_node_free(node->table[i]);
205         ec_free(node->table);
206 }
207
208 static struct ec_node_type ec_node_seq_type = {
209         .name = "seq",
210         .parse = ec_node_seq_parse,
211         .complete = ec_node_seq_complete,
212         .get_max_parse_len = ec_node_seq_get_max_parse_len,
213         .size = sizeof(struct ec_node_seq),
214         .free_priv = ec_node_seq_free_priv,
215 };
216
217 EC_NODE_TYPE_REGISTER(ec_node_seq_type);
218
219 int ec_node_seq_add(struct ec_node *gen_node, struct ec_node *child)
220 {
221         struct ec_node_seq *node = (struct ec_node_seq *)gen_node;
222         struct ec_node **table;
223
224         // XXX check node type
225
226         assert(node != NULL);
227
228         if (child == NULL)
229                 return -EINVAL;
230
231         gen_node->flags &= ~EC_NODE_F_BUILT;
232
233         table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table));
234         if (table == NULL) {
235                 ec_node_free(child);
236                 return -1;
237         }
238
239         node->table = table;
240         table[node->len] = child;
241         node->len++;
242
243         child->parent = gen_node;
244         TAILQ_INSERT_TAIL(&gen_node->children, child, next); // XXX really needed?
245
246         return 0;
247 }
248
249 struct ec_node *__ec_node_seq(const char *id, ...)
250 {
251         struct ec_node *gen_node = NULL;
252         struct ec_node_seq *node = NULL;
253         struct ec_node *child;
254         va_list ap;
255         int fail = 0;
256
257         va_start(ap, id);
258
259         gen_node = __ec_node(&ec_node_seq_type, id);
260         node = (struct ec_node_seq *)gen_node;
261         if (node == NULL)
262                 fail = 1;;
263
264         for (child = va_arg(ap, struct ec_node *);
265              child != EC_NODE_ENDLIST;
266              child = va_arg(ap, struct ec_node *)) {
267
268                 /* on error, don't quit the loop to avoid leaks */
269                 if (fail == 1 || child == NULL ||
270                                 ec_node_seq_add(&node->gen, child) < 0) {
271                         fail = 1;
272                         ec_node_free(child);
273                 }
274         }
275
276         if (fail == 1)
277                 goto fail;
278
279         va_end(ap);
280         return gen_node;
281
282 fail:
283         ec_node_free(gen_node); /* will also free children */
284         va_end(ap);
285         return NULL;
286 }
287
288 /* LCOV_EXCL_START */
289 static int ec_node_seq_testcase(void)
290 {
291         struct ec_node *node;
292         int ret = 0;
293
294         node = EC_NODE_SEQ(NULL,
295                 ec_node_str(NULL, "foo"),
296                 ec_node_str(NULL, "bar")
297         );
298         if (node == NULL) {
299                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
300                 return -1;
301         }
302         ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar");
303         ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar", "toto");
304         ret |= EC_TEST_CHECK_PARSE(node, -1, "foo");
305         ret |= EC_TEST_CHECK_PARSE(node, -1, "foox", "bar");
306         ret |= EC_TEST_CHECK_PARSE(node, -1, "foo", "barx");
307         ret |= EC_TEST_CHECK_PARSE(node, -1, "bar", "foo");
308         ret |= EC_TEST_CHECK_PARSE(node, -1, "", "foo");
309         ec_node_free(node);
310
311         /* test completion */
312         node = EC_NODE_SEQ(NULL,
313                 ec_node_str(NULL, "foo"),
314                 ec_node_option(NULL, ec_node_str(NULL, "toto")),
315                 ec_node_str(NULL, "bar")
316         );
317         if (node == NULL) {
318                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
319                 return -1;
320         }
321         ret |= EC_TEST_CHECK_COMPLETE(node,
322                 "", EC_NODE_ENDLIST,
323                 "foo", EC_NODE_ENDLIST);
324         ec_node_free(node);
325         return 0;
326         ret |= EC_TEST_CHECK_COMPLETE(node,
327                 "f", EC_NODE_ENDLIST,
328                 "foo", EC_NODE_ENDLIST);
329         ret |= EC_TEST_CHECK_COMPLETE(node,
330                 "foo", EC_NODE_ENDLIST,
331                 "foo", EC_NODE_ENDLIST);
332         ret |= EC_TEST_CHECK_COMPLETE(node,
333                 "foo", "", EC_NODE_ENDLIST,
334                 "bar", "toto", EC_NODE_ENDLIST);
335         ret |= EC_TEST_CHECK_COMPLETE(node,
336                 "foo", "t", EC_NODE_ENDLIST,
337                 "toto", EC_NODE_ENDLIST);
338         ret |= EC_TEST_CHECK_COMPLETE(node,
339                 "foo", "b", EC_NODE_ENDLIST,
340                 "bar", EC_NODE_ENDLIST);
341         ret |= EC_TEST_CHECK_COMPLETE(node,
342                 "foo", "bar", EC_NODE_ENDLIST,
343                 "bar", EC_NODE_ENDLIST);
344         ret |= EC_TEST_CHECK_COMPLETE(node,
345                 "x", EC_NODE_ENDLIST,
346                 EC_NODE_ENDLIST);
347         ret |= EC_TEST_CHECK_COMPLETE(node,
348                 "foobarx", EC_NODE_ENDLIST,
349                 EC_NODE_ENDLIST);
350         ec_node_free(node);
351
352         return ret;
353 }
354 /* LCOV_EXCL_STOP */
355
356 static struct ec_test ec_node_seq_test = {
357         .name = "node_seq",
358         .test = ec_node_seq_testcase,
359 };
360
361 EC_TEST_REGISTER(ec_node_seq_test);