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