9240e4f6cce85eff0ca7e675363242148a5e3e13
[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                 ec_strvec_free(childvec);
78                 childvec = NULL;
79                 if (ret == EC_PARSED_NOMATCH) {
80                         ec_parsed_free_children(state);
81                         return EC_PARSED_NOMATCH;
82                 } else if (ret < 0) {
83                         goto fail;
84                 }
85
86                 len += ret;
87         }
88
89         return len;
90
91 fail:
92         ec_strvec_free(childvec);
93         return ret;
94 }
95
96 static int
97 __ec_node_seq_complete(struct ec_node **table, size_t table_len,
98                 struct ec_completed *completed,
99                 struct ec_parsed *parsed,
100                 const struct ec_strvec *strvec)
101 {
102         struct ec_strvec *childvec = NULL;
103         unsigned int i;
104         int ret;
105
106         if (table_len == 0)
107                 return 0;
108
109         /*
110          * Example of completion for a sequence node = [n1,n2] and an
111          * input = [a,b,c,d]:
112          *
113          * result = complete(n1, [a,b,c,d]) +
114          *    complete(n2, [b,c,d]) if n1 matches [a] +
115          *    complete(n2, [c,d]) if n1 matches [a,b] +
116          *    complete(n2, [d]) if n1 matches [a,b,c] +
117          *    complete(n2, []) if n1 matches [a,b,c,d]
118          */
119
120         /* first, try to complete with the first node of the table */
121         ret = ec_node_complete_child(table[0], completed, parsed, strvec);
122         if (ret < 0)
123                 goto fail;
124
125         /* then, if the first node of the table matches the beginning of the
126          * strvec, try to complete the rest */
127         for (i = 0; i < ec_strvec_len(strvec); i++) {
128                 childvec = ec_strvec_ndup(strvec, 0, i);
129                 if (childvec == NULL)
130                         goto fail;
131
132                 ret = ec_node_parse_child(table[0], parsed, childvec);
133                 if (ret < 0 && ret != EC_PARSED_NOMATCH)
134                         goto fail;
135
136                 ec_strvec_free(childvec);
137                 childvec = NULL;
138
139                 if ((unsigned int)ret != i) {
140                         if (ret != EC_PARSED_NOMATCH)
141                                 ec_parsed_del_last_child(parsed);
142                         continue;
143                 }
144
145                 childvec = ec_strvec_ndup(strvec, i, ec_strvec_len(strvec) - i);
146                 if (childvec == NULL) {
147                         ec_parsed_del_last_child(parsed);
148                         goto fail;
149                 }
150
151                 ret = __ec_node_seq_complete(&table[1],
152                                         table_len - 1,
153                                         completed, parsed, childvec);
154                 ec_parsed_del_last_child(parsed);
155                 ec_strvec_free(childvec);
156                 childvec = NULL;
157
158                 if (ret < 0)
159                         goto fail;
160         }
161
162         return 0;
163
164 fail:
165         ec_strvec_free(childvec);
166         return -1;
167 }
168
169 static int
170 ec_node_seq_complete(const struct ec_node *gen_node,
171                 struct ec_completed *completed,
172                 struct ec_parsed *parsed,
173                 const struct ec_strvec *strvec)
174 {
175         struct ec_node_seq *node = (struct ec_node_seq *)gen_node;
176
177         return __ec_node_seq_complete(node->table, node->len, completed,
178                                 parsed, strvec);
179 }
180
181 static size_t ec_node_seq_get_max_parse_len(const struct ec_node *gen_node)
182 {
183         struct ec_node_seq *node = (struct ec_node_seq *)gen_node;
184         size_t i, len, ret = 0;
185
186         for (i = 0; i < node->len; i++) {
187                 len = ec_node_get_max_parse_len(node->table[i]);
188                 if (len <= SIZE_MAX - ret)
189                         ret += len;
190                 else
191                         ret = SIZE_MAX;
192         }
193
194         return ret;
195 }
196
197 static void ec_node_seq_free_priv(struct ec_node *gen_node)
198 {
199         struct ec_node_seq *node = (struct ec_node_seq *)gen_node;
200         unsigned int i;
201
202         for (i = 0; i < node->len; i++)
203                 ec_node_free(node->table[i]);
204         ec_free(node->table);
205 }
206
207 static struct ec_node_type ec_node_seq_type = {
208         .name = "seq",
209         .parse = ec_node_seq_parse,
210         .complete = ec_node_seq_complete,
211         .get_max_parse_len = ec_node_seq_get_max_parse_len,
212         .size = sizeof(struct ec_node_seq),
213         .free_priv = ec_node_seq_free_priv,
214 };
215
216 EC_NODE_TYPE_REGISTER(ec_node_seq_type);
217
218 int ec_node_seq_add(struct ec_node *gen_node, struct ec_node *child)
219 {
220         struct ec_node_seq *node = (struct ec_node_seq *)gen_node;
221         struct ec_node **table;
222
223         // XXX check node type
224
225         assert(node != NULL);
226
227         if (child == NULL)
228                 return -EINVAL;
229
230         gen_node->flags &= ~EC_NODE_F_BUILT;
231
232         table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table));
233         if (table == NULL) {
234                 ec_node_free(child);
235                 return -1;
236         }
237
238         node->table = table;
239         table[node->len] = child;
240         node->len++;
241
242         child->parent = gen_node;
243         TAILQ_INSERT_TAIL(&gen_node->children, child, next); // XXX really needed?
244
245         return 0;
246 }
247
248 struct ec_node *__ec_node_seq(const char *id, ...)
249 {
250         struct ec_node *gen_node = NULL;
251         struct ec_node_seq *node = NULL;
252         struct ec_node *child;
253         va_list ap;
254         int fail = 0;
255
256         va_start(ap, id);
257
258         gen_node = __ec_node(&ec_node_seq_type, id);
259         node = (struct ec_node_seq *)gen_node;
260         if (node == NULL)
261                 fail = 1;;
262
263         for (child = va_arg(ap, struct ec_node *);
264              child != EC_NODE_ENDLIST;
265              child = va_arg(ap, struct ec_node *)) {
266
267                 /* on error, don't quit the loop to avoid leaks */
268                 if (fail == 1 || child == NULL ||
269                                 ec_node_seq_add(&node->gen, child) < 0) {
270                         fail = 1;
271                         ec_node_free(child);
272                 }
273         }
274
275         if (fail == 1)
276                 goto fail;
277
278         va_end(ap);
279         return gen_node;
280
281 fail:
282         ec_node_free(gen_node); /* will also free children */
283         va_end(ap);
284         return NULL;
285 }
286
287 /* LCOV_EXCL_START */
288 static int ec_node_seq_testcase(void)
289 {
290         struct ec_node *node;
291         int ret = 0;
292
293         node = EC_NODE_SEQ(NULL,
294                 ec_node_str(NULL, "foo"),
295                 ec_node_str(NULL, "bar")
296         );
297         if (node == NULL) {
298                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
299                 return -1;
300         }
301         ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar");
302         ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar", "toto");
303         ret |= EC_TEST_CHECK_PARSE(node, -1, "foo");
304         ret |= EC_TEST_CHECK_PARSE(node, -1, "foox", "bar");
305         ret |= EC_TEST_CHECK_PARSE(node, -1, "foo", "barx");
306         ret |= EC_TEST_CHECK_PARSE(node, -1, "bar", "foo");
307         ret |= EC_TEST_CHECK_PARSE(node, -1, "", "foo");
308         ec_node_free(node);
309
310         /* test completion */
311         node = EC_NODE_SEQ(NULL,
312                 ec_node_str(NULL, "foo"),
313                 ec_node_option(NULL, ec_node_str(NULL, "toto")),
314                 ec_node_str(NULL, "bar")
315         );
316         if (node == NULL) {
317                 EC_LOG(EC_LOG_ERR, "cannot create node\n");
318                 return -1;
319         }
320         ret |= EC_TEST_CHECK_COMPLETE(node,
321                 "", EC_NODE_ENDLIST,
322                 "foo", EC_NODE_ENDLIST);
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);