list children in a table, not in a list
[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_get_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         assert(node != NULL);
225
226         if (child == NULL) {
227                 errno = EINVAL;
228                 goto fail;
229         }
230
231         if (ec_node_check_type(gen_node, &ec_node_seq_type) < 0)
232                 goto fail;
233
234         table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table));
235         if (table == NULL)
236                 goto fail;
237
238         node->table = table;
239
240         if (ec_node_add_child(gen_node, child) < 0)
241                 goto fail;
242
243         table[node->len] = child;
244         node->len++;
245
246         return 0;
247
248 fail:
249         ec_node_free(child);
250         return -1;
251 }
252
253 struct ec_node *__ec_node_seq(const char *id, ...)
254 {
255         struct ec_node *gen_node = NULL;
256         struct ec_node_seq *node = NULL;
257         struct ec_node *child;
258         va_list ap;
259         int fail = 0;
260
261         va_start(ap, id);
262
263         gen_node = __ec_node(&ec_node_seq_type, id);
264         node = (struct ec_node_seq *)gen_node;
265         if (node == NULL)
266                 fail = 1;;
267
268         for (child = va_arg(ap, struct ec_node *);
269              child != EC_NODE_ENDLIST;
270              child = va_arg(ap, struct ec_node *)) {
271
272                 /* on error, don't quit the loop to avoid leaks */
273                 if (fail == 1 || child == NULL ||
274                                 ec_node_seq_add(&node->gen, child) < 0) {
275                         fail = 1;
276                         ec_node_free(child);
277                 }
278         }
279
280         if (fail == 1)
281                 goto fail;
282
283         va_end(ap);
284         return gen_node;
285
286 fail:
287         ec_node_free(gen_node); /* will also free children */
288         va_end(ap);
289         return NULL;
290 }
291
292 /* LCOV_EXCL_START */
293 static int ec_node_seq_testcase(void)
294 {
295         struct ec_node *node;
296         int ret = 0;
297
298         node = EC_NODE_SEQ(EC_NO_ID,
299                 ec_node_str(EC_NO_ID, "foo"),
300                 ec_node_str(EC_NO_ID, "bar")
301         );
302         if (node == NULL) {
303                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
304                 return -1;
305         }
306         ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar");
307         ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar", "toto");
308         ret |= EC_TEST_CHECK_PARSE(node, -1, "foo");
309         ret |= EC_TEST_CHECK_PARSE(node, -1, "foox", "bar");
310         ret |= EC_TEST_CHECK_PARSE(node, -1, "foo", "barx");
311         ret |= EC_TEST_CHECK_PARSE(node, -1, "bar", "foo");
312         ret |= EC_TEST_CHECK_PARSE(node, -1, "", "foo");
313         ec_node_free(node);
314
315         /* test completion */
316         node = EC_NODE_SEQ(EC_NO_ID,
317                 ec_node_str(EC_NO_ID, "foo"),
318                 ec_node_option(EC_NO_ID, ec_node_str(EC_NO_ID, "toto")),
319                 ec_node_str(EC_NO_ID, "bar")
320         );
321         if (node == NULL) {
322                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
323                 return -1;
324         }
325         ret |= EC_TEST_CHECK_COMPLETE(node,
326                 "", EC_NODE_ENDLIST,
327                 "foo", EC_NODE_ENDLIST);
328         ec_node_free(node);
329         return 0;
330         ret |= EC_TEST_CHECK_COMPLETE(node,
331                 "f", EC_NODE_ENDLIST,
332                 "foo", EC_NODE_ENDLIST);
333         ret |= EC_TEST_CHECK_COMPLETE(node,
334                 "foo", EC_NODE_ENDLIST,
335                 "foo", EC_NODE_ENDLIST);
336         ret |= EC_TEST_CHECK_COMPLETE(node,
337                 "foo", "", EC_NODE_ENDLIST,
338                 "bar", "toto", EC_NODE_ENDLIST);
339         ret |= EC_TEST_CHECK_COMPLETE(node,
340                 "foo", "t", EC_NODE_ENDLIST,
341                 "toto", EC_NODE_ENDLIST);
342         ret |= EC_TEST_CHECK_COMPLETE(node,
343                 "foo", "b", EC_NODE_ENDLIST,
344                 "bar", EC_NODE_ENDLIST);
345         ret |= EC_TEST_CHECK_COMPLETE(node,
346                 "foo", "bar", EC_NODE_ENDLIST,
347                 "bar", EC_NODE_ENDLIST);
348         ret |= EC_TEST_CHECK_COMPLETE(node,
349                 "x", EC_NODE_ENDLIST,
350                 EC_NODE_ENDLIST);
351         ret |= EC_TEST_CHECK_COMPLETE(node,
352                 "foobarx", EC_NODE_ENDLIST,
353                 EC_NODE_ENDLIST);
354         ec_node_free(node);
355
356         return ret;
357 }
358 /* LCOV_EXCL_STOP */
359
360 static struct ec_test ec_node_seq_test = {
361         .name = "node_seq",
362         .test = ec_node_seq_testcase,
363 };
364
365 EC_TEST_REGISTER(ec_node_seq_test);