save
[protos/libecoli.git] / lib / ecoli_node_subset.c
1 /*
2  * Copyright (c) 2016-2017, 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 #include <stdbool.h>
35
36 #include <ecoli_malloc.h>
37 #include <ecoli_log.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_subset.h>
43 #include <ecoli_node_str.h>
44 #include <ecoli_node_or.h>
45 #include <ecoli_test.h>
46
47 struct ec_node_subset {
48         struct ec_node gen;
49         struct ec_node **table;
50         unsigned int len;
51 };
52
53 struct parse_result {
54         size_t parsed_len;          /* number of parsed node */
55         size_t len;                 /* consumed strings */
56 };
57
58 /* recursively find the longest list of nodes that matches: the state is
59  * updated accordingly. */
60 static int
61 __ec_node_subset_parse(struct parse_result *out, struct ec_node **table,
62                 size_t table_len, struct ec_parsed *state,
63                 const struct ec_strvec *strvec)
64 {
65         struct ec_node **child_table;
66         struct ec_strvec *childvec = NULL;
67         size_t i, j, len = 0;
68         struct parse_result best_result, result;
69         struct ec_parsed *best_parsed = NULL;
70         int ret;
71
72         if (table_len == 0)
73                 return 0;
74
75         memset(&best_result, 0, sizeof(best_result));
76
77         child_table = ec_calloc(table_len - 1, sizeof(*child_table));
78         if (child_table == NULL) {
79                 ret = -ENOMEM;
80                 goto fail;
81         }
82
83         for (i = 0; i < table_len; i++) {
84                 /* try to parse elt i */
85                 ret = ec_node_parse_child(table[i], state, strvec);
86                 if (ret == EC_PARSED_NOMATCH)
87                         continue;
88                 else if (ret < 0)
89                         goto fail;
90
91                 /* build a new table without elt i */
92                 for (j = 0; j < table_len; j++) {
93                         if (j < i)
94                                 child_table[j] = table[j];
95                         else if (j > i)
96                                 child_table[j - 1] = table[j];
97                 }
98
99                 /* build a new strvec (ret is the len of matched strvec) */
100                 len = ret;
101                 childvec = ec_strvec_ndup(strvec, len,
102                                         ec_strvec_len(strvec) - len);
103                 if (childvec == NULL) {
104                         ret = -ENOMEM;
105                         goto fail;
106                 }
107
108                 memset(&result, 0, sizeof(result));
109                 ret = __ec_node_subset_parse(&result, child_table,
110                                         table_len - 1, state, childvec);
111                 ec_strvec_free(childvec);
112                 childvec = NULL;
113                 if (ret < 0)
114                         goto fail;
115
116                 /* if result is not the best, ignore */
117                 if (result.parsed_len < best_result.parsed_len) {
118                         struct ec_parsed *child_parsed;
119
120                         memset(&result, 0, sizeof(result));
121                         child_parsed = ec_parsed_get_last_child(state);
122                         ec_parsed_del_child(state, child_parsed);
123                         ec_parsed_free(child_parsed);
124                         continue;
125                 }
126
127                 /* replace the previous best result */
128                 ec_parsed_free(best_parsed);
129                 best_parsed = ec_parsed_get_last_child(state);
130                 ec_parsed_del_child(state, best_parsed);
131
132                 best_result.parsed_len = result.parsed_len + 1;
133                 best_result.len = len + result.len;
134
135                 memset(&result, 0, sizeof(result));
136         }
137
138         *out = best_result;
139         ec_free(child_table);
140         if (best_parsed != NULL)
141                 ec_parsed_add_child(state, best_parsed);
142
143         return 0;
144
145  fail:
146         ec_parsed_free(best_parsed);
147         ec_strvec_free(childvec);
148         ec_free(child_table);
149         return ret;
150 }
151
152 static int
153 ec_node_subset_parse(const struct ec_node *gen_node,
154                 struct ec_parsed *state,
155                 const struct ec_strvec *strvec)
156 {
157         struct ec_node_subset *node = (struct ec_node_subset *)gen_node;
158         struct ec_parsed *parsed = NULL;
159         struct parse_result result;
160         int ret;
161
162         memset(&result, 0, sizeof(result));
163
164         ret = __ec_node_subset_parse(&result, node->table,
165                                 node->len, state, strvec);
166         if (ret < 0)
167                 goto fail;
168
169         /* if no child node matches, return a matching empty strvec */
170         if (result.parsed_len == 0)
171                 return 0;
172
173         return result.len;
174
175  fail:
176         ec_parsed_free(parsed);
177         return ret;
178 }
179
180 static struct ec_completed *
181 __ec_node_subset_complete(struct ec_node **table, size_t table_len,
182         const struct ec_strvec *strvec)
183 {
184         struct ec_completed *completed = NULL;
185         struct ec_completed *child_completed = NULL;
186         struct ec_strvec *childvec = NULL;
187         struct ec_parsed *parsed = NULL;
188         struct ec_node *save;
189         size_t i, len;
190
191         /*
192          * example with table = [a, b, c]
193          * subset_complete([a,b,c], strvec) returns:
194          *   complete(a, strvec) + complete(b, strvec) + complete(c, strvec) +
195          *   + __subset_complete([b, c], childvec) if a matches
196          *   + __subset_complete([a, c], childvec) if b matches
197          *   + __subset_complete([a, b], childvec) if c matches
198          */
199
200         completed = ec_completed();
201         if (completed == NULL)
202                 goto fail;
203
204         /* first, try to complete with each node of the table */
205         for (i = 0; i < table_len; i++) {
206                 if (table[i] == NULL)
207                         continue;
208
209                 child_completed = ec_node_complete_strvec(table[i],
210                         strvec);
211
212                 if (child_completed == NULL)
213                         goto fail;
214
215                 ec_completed_merge(completed, child_completed);
216                 child_completed = NULL;
217         }
218
219         /* then, if a node matches, advance in strvec and try to complete with
220          * all the other nodes */
221         for (i = 0; i < table_len; i++) {
222                 if (table[i] == NULL)
223                         continue;
224
225                 parsed = ec_node_parse_strvec(table[i], strvec);
226                 if (parsed == NULL)
227                         goto fail;
228
229                 if (!ec_parsed_matches(parsed)) {
230                         ec_parsed_free(parsed);
231                         parsed = NULL;
232                         continue;
233                 }
234
235                 len = ec_parsed_len(parsed);
236                 childvec = ec_strvec_ndup(strvec, len,
237                                         ec_strvec_len(strvec) - len);
238                 if (childvec == NULL)
239                         goto fail;
240
241                 save = table[i];
242                 table[i] = NULL;
243                 child_completed = __ec_node_subset_complete(table,
244                                                         table_len, childvec);
245                 table[i] = save;
246                 ec_strvec_free(childvec);
247                 childvec = NULL;
248
249                 if (child_completed == NULL)
250                         goto fail;
251
252                 ec_completed_merge(completed, child_completed);
253                 child_completed = NULL;
254
255                 ec_parsed_free(parsed);
256                 parsed = NULL;
257         }
258
259         return completed;
260 fail:
261         ec_parsed_free(parsed);
262         ec_completed_free(child_completed);
263         ec_completed_free(completed);
264
265         return NULL;
266 }
267
268 static struct ec_completed *ec_node_subset_complete(const struct ec_node *gen_node,
269         const struct ec_strvec *strvec)
270 {
271         struct ec_node_subset *node = (struct ec_node_subset *)gen_node;
272
273         return __ec_node_subset_complete(node->table, node->len, strvec);
274 }
275
276 static void ec_node_subset_free_priv(struct ec_node *gen_node)
277 {
278         struct ec_node_subset *node = (struct ec_node_subset *)gen_node;
279         unsigned int i;
280
281         for (i = 0; i < node->len; i++)
282                 ec_node_free(node->table[i]);
283         ec_free(node->table);
284 }
285
286 int ec_node_subset_add(struct ec_node *gen_node, struct ec_node *child)
287 {
288         struct ec_node_subset *node = (struct ec_node_subset *)gen_node;
289         struct ec_node **table;
290
291         assert(node != NULL);
292
293         if (child == NULL)
294                 return -EINVAL;
295
296         gen_node->flags &= ~EC_NODE_F_BUILT;
297
298         table = ec_realloc(node->table, (node->len + 1) * sizeof(*node->table));
299         if (table == NULL) {
300                 ec_node_free(child);
301                 return -1;
302         }
303
304         node->table = table;
305         table[node->len] = child;
306         node->len++;
307
308         child->parent = gen_node;
309         TAILQ_INSERT_TAIL(&gen_node->children, child, next);
310
311         return 0;
312 }
313
314 static struct ec_node_type ec_node_subset_type = {
315         .name = "subset",
316         .parse = ec_node_subset_parse,
317         .complete = ec_node_subset_complete,
318         .size = sizeof(struct ec_node_subset),
319         .free_priv = ec_node_subset_free_priv,
320 };
321
322 EC_NODE_TYPE_REGISTER(ec_node_subset_type);
323
324 struct ec_node *__ec_node_subset(const char *id, ...)
325 {
326         struct ec_node *gen_node = NULL;
327         struct ec_node_subset *node = NULL;
328         struct ec_node *child;
329         va_list ap;
330         int fail = 0;
331
332         va_start(ap, id);
333
334         gen_node = __ec_node(&ec_node_subset_type, id);
335         node = (struct ec_node_subset *)gen_node;
336         if (node == NULL)
337                 fail = 1;;
338
339         for (child = va_arg(ap, struct ec_node *);
340              child != EC_NODE_ENDLIST;
341              child = va_arg(ap, struct ec_node *)) {
342
343                 /* on error, don't quit the loop to avoid leaks */
344                 if (fail == 1 || child == NULL ||
345                                 ec_node_subset_add(gen_node, child) < 0) {
346                         fail = 1;
347                         ec_node_free(child);
348                 }
349         }
350
351         if (fail == 1)
352                 goto fail;
353
354         va_end(ap);
355         return gen_node;
356
357 fail:
358         ec_node_free(gen_node); /* will also free children */
359         va_end(ap);
360         return NULL;
361 }
362
363 static int ec_node_subset_testcase(void)
364 {
365         struct ec_node *node;
366         int ret = 0;
367
368         node = EC_NODE_SUBSET(NULL,
369                 EC_NODE_OR(NULL,
370                         ec_node_str(NULL, "foo"),
371                         ec_node_str(NULL, "bar")),
372                 ec_node_str(NULL, "bar"),
373                 ec_node_str(NULL, "toto")
374         );
375         if (node == NULL) {
376                 ec_log(EC_LOG_ERR, "cannot create node\n");
377                 return -1;
378         }
379         ret |= EC_TEST_CHECK_PARSE(node, 0);
380         ret |= EC_TEST_CHECK_PARSE(node, 1, "foo");
381         ret |= EC_TEST_CHECK_PARSE(node, 1, "bar");
382         ret |= EC_TEST_CHECK_PARSE(node, 2, "foo", "bar", "titi");
383         ret |= EC_TEST_CHECK_PARSE(node, 3, "bar", "foo", "toto");
384         ret |= EC_TEST_CHECK_PARSE(node, 1, "foo", "foo");
385         ret |= EC_TEST_CHECK_PARSE(node, 2, "bar", "bar");
386         ret |= EC_TEST_CHECK_PARSE(node, 2, "bar", "foo");
387         ret |= EC_TEST_CHECK_PARSE(node, 0, " ");
388         ret |= EC_TEST_CHECK_PARSE(node, 0, "foox");
389         ec_node_free(node);
390
391         /* test completion */
392         node = EC_NODE_SUBSET(NULL,
393                 ec_node_str(NULL, "foo"),
394                 ec_node_str(NULL, "bar"),
395                 ec_node_str(NULL, "bar2"),
396                 ec_node_str(NULL, "toto"),
397                 ec_node_str(NULL, "titi")
398         );
399         if (node == NULL) {
400                 ec_log(EC_LOG_ERR, "cannot create node\n");
401                 return -1;
402         }
403         ret |= EC_TEST_CHECK_COMPLETE(node,
404                 "", EC_NODE_ENDLIST,
405                 "foo", "bar", "bar2", "toto", "titi", EC_NODE_ENDLIST,
406                 "");
407         ret |= EC_TEST_CHECK_COMPLETE(node,
408                 "bar", "bar2", "", EC_NODE_ENDLIST,
409                 "foo", "toto", "titi", EC_NODE_ENDLIST,
410                 "");
411         ret |= EC_TEST_CHECK_COMPLETE(node,
412                 "f", EC_NODE_ENDLIST,
413                 "oo", EC_NODE_ENDLIST,
414                 "oo");
415         ret |= EC_TEST_CHECK_COMPLETE(node,
416                 "b", EC_NODE_ENDLIST,
417                 "ar", "ar2", EC_NODE_ENDLIST,
418                 "ar");
419         ret |= EC_TEST_CHECK_COMPLETE(node,
420                 "bar", EC_NODE_ENDLIST,
421                 "", "2", EC_NODE_ENDLIST,
422                 "");
423         ret |= EC_TEST_CHECK_COMPLETE(node,
424                 "bar", "b", EC_NODE_ENDLIST,
425                 "ar2", EC_NODE_ENDLIST,
426                 "ar2");
427         ret |= EC_TEST_CHECK_COMPLETE(node,
428                 "t", EC_NODE_ENDLIST,
429                 "oto", "iti", EC_NODE_ENDLIST,
430                 "");
431         ret |= EC_TEST_CHECK_COMPLETE(node,
432                 "to", EC_NODE_ENDLIST,
433                 "to", EC_NODE_ENDLIST,
434                 "to");
435         ret |= EC_TEST_CHECK_COMPLETE(node,
436                 "x", EC_NODE_ENDLIST,
437                 EC_NODE_ENDLIST,
438                 "");
439         ec_node_free(node);
440
441         return ret;
442 }
443
444 static struct ec_test ec_node_subset_test = {
445         .name = "node_subset",
446         .test = ec_node_subset_testcase,
447 };
448
449 EC_TEST_REGISTER(ec_node_subset_test);