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