2 * Copyright (c) 2016-2017, Olivier MATZ <zer0@droids-corp.org>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
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.
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.
28 #include <sys/queue.h>
37 #include <ecoli_malloc.h>
38 #include <ecoli_log.h>
39 #include <ecoli_test.h>
40 #include <ecoli_strvec.h>
41 #include <ecoli_tk_expr.h>
42 #include <ecoli_tk_str.h>
43 #include <ecoli_tk_or.h>
44 #include <ecoli_tk_int.h>
45 #include <ecoli_tk_many.h>
46 #include <ecoli_tk_seq.h>
47 #include <ecoli_tk_option.h>
48 #include <ecoli_tk_re.h>
49 #include <ecoli_tk_re_lex.h>
50 #include <ecoli_tk_cmd.h>
54 char *cmd_str; /* the command string. */
55 struct ec_tk *cmd; /* the command token. */
56 struct ec_tk *lex; /* the lexer token. */
57 struct ec_tk *expr; /* the expression parser. */
58 struct ec_tk **table; /* table of tk referenced in command. */
59 unsigned int len; /* len of the table. */
63 ec_tk_cmd_eval_var(void **result, void *userctx,
64 const struct ec_parsed_tk *var)
66 const struct ec_strvec *vec;
67 struct ec_tk_cmd *tk = userctx;
68 struct ec_tk *eval = NULL;
74 /* get parsed string vector, it should contain only one str */
75 vec = ec_parsed_tk_strvec(var);
76 if (ec_strvec_len(vec) != 1)
78 str = ec_strvec_val(vec, 0);
80 for (i = 0; i < tk->len; i++) {
81 id = ec_tk_id(tk->table[i]);
82 printf("i=%d id=%s\n", i, id);
87 /* if id matches, use a tk provided by the user... */
88 eval = ec_tk_clone(tk->table[i]);
94 /* ...or create a string token */
96 eval = ec_tk_str(NULL, str);
101 printf("eval var %s %p\n", str, eval);
108 ec_tk_cmd_eval_pre_op(void **result, void *userctx, void *operand,
109 const struct ec_parsed_tk *operator)
120 ec_tk_cmd_eval_post_op(void **result, void *userctx, void *operand,
121 const struct ec_parsed_tk *operator)
123 const struct ec_strvec *vec;
124 struct ec_tk *eval = operand;;
128 /* get parsed string vector, it should contain only one str */
129 vec = ec_parsed_tk_strvec(operator);
130 if (ec_strvec_len(vec) != 1)
133 if (!strcmp(ec_strvec_val(vec, 0), "*"))
138 printf("eval post_op %p\n", eval);
145 ec_tk_cmd_eval_bin_op(void **result, void *userctx, void *operand1,
146 const struct ec_parsed_tk *operator, void *operand2)
149 const struct ec_strvec *vec;
150 struct ec_tk *out = NULL;
151 struct ec_tk *in1 = operand1;
152 struct ec_tk *in2 = operand2;
156 printf("eval bin_op %p %p\n", in1, in2);
158 /* get parsed string vector, it should contain only one str */
159 vec = ec_parsed_tk_strvec(operator);
160 if (ec_strvec_len(vec) != 1)
163 if (!strcmp(ec_strvec_val(vec, 0), "|")) {
164 out = EC_TK_OR(NULL, ec_tk_clone(in1), ec_tk_clone(in2));
170 } else if (!strcmp(ec_strvec_val(vec, 0), ",")) {
173 *result = NULL; //XXX
182 ec_tk_cmd_eval_parenthesis(void **result, void *userctx,
183 const struct ec_parsed_tk *open_paren,
184 const struct ec_parsed_tk *close_paren,
187 const struct ec_strvec *vec;
188 struct ec_tk *in = value;;
189 struct ec_tk *out = NULL;;
194 /* get parsed string vector, it should contain only one str */
195 vec = ec_parsed_tk_strvec(open_paren);
196 if (ec_strvec_len(vec) != 1)
199 if (!strcmp(ec_strvec_val(vec, 0), "[")) {
200 out = ec_tk_option_new(NULL, ec_tk_clone(in));
208 printf("eval paren\n");
215 ec_tk_cmd_eval_free(void *result, void *userctx)
221 static const struct ec_tk_expr_eval_ops test_ops = {
222 .eval_var = ec_tk_cmd_eval_var,
223 .eval_pre_op = ec_tk_cmd_eval_pre_op,
224 .eval_post_op = ec_tk_cmd_eval_post_op,
225 .eval_bin_op = ec_tk_cmd_eval_bin_op,
226 .eval_parenthesis = ec_tk_cmd_eval_parenthesis,
227 .eval_free = ec_tk_cmd_eval_free,
230 static struct ec_parsed_tk *ec_tk_cmd_parse(const struct ec_tk *gen_tk,
231 const struct ec_strvec *strvec)
233 struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk;
235 return ec_tk_parse_tokens(tk->cmd, strvec);
238 static struct ec_completed_tk *ec_tk_cmd_complete(const struct ec_tk *gen_tk,
239 const struct ec_strvec *strvec)
241 struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk;
243 return ec_tk_complete_tokens(tk->cmd, strvec);
246 static void ec_tk_cmd_free_priv(struct ec_tk *gen_tk)
248 struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk;
251 ec_free(tk->cmd_str);
253 ec_tk_free(tk->expr);
255 for (i = 0; i < tk->len; i++)
256 ec_tk_free(tk->table[i]);
260 static int ec_tk_cmd_build(struct ec_tk *gen_tk)
262 struct ec_tk *expr = NULL, *lex = NULL, *cmd = NULL;
263 struct ec_parsed_tk *p, *child;
264 struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk;
268 /* build the expression parser */
270 expr = ec_tk_expr("expr");
273 ret = ec_tk_expr_set_val_tk(expr, ec_tk_re(NULL, "[a-zA-Z0-9]+"));
276 ret = ec_tk_expr_add_bin_op(expr, ec_tk_str(NULL, ","));
279 ret = ec_tk_expr_add_bin_op(expr, ec_tk_str(NULL, "|"));
282 ret = ec_tk_expr_add_post_op(expr, ec_tk_str(NULL, "+"));
285 ret = ec_tk_expr_add_post_op(expr, ec_tk_str(NULL, "*"));
288 ret = ec_tk_expr_add_parenthesis(expr, ec_tk_str(NULL, "["),
289 ec_tk_str(NULL, "]"));
292 ec_tk_expr_add_parenthesis(expr, ec_tk_str(NULL, "("),
293 ec_tk_str(NULL, ")"));
297 /* prepend a lexer and a "many" to the expression token */
299 lex = ec_tk_re_lex(NULL,
300 ec_tk_many(NULL, ec_tk_clone(expr), 1, 0));
304 ret = ec_tk_re_lex_add(lex, "[a-zA-Z0-9]+", 1);
307 ret = ec_tk_re_lex_add(lex, "[*|,()]", 1);
310 ret = ec_tk_re_lex_add(lex, "\\[", 1);
313 ret = ec_tk_re_lex_add(lex, "\\]", 1);
316 ret = ec_tk_re_lex_add(lex, "[ ]+", 0);
320 /* parse the command expression */
322 p = ec_tk_parse(lex, tk->cmd_str);
327 if (!ec_parsed_tk_matches(p))
329 if (TAILQ_EMPTY(&p->children))
331 if (TAILQ_EMPTY(&TAILQ_FIRST(&p->children)->children))
335 cmd = ec_tk_seq(NULL);
339 TAILQ_FOREACH(child, &TAILQ_FIRST(&p->children)->children, next) {
340 ret = ec_tk_expr_eval(&result, expr, child,
344 ret = ec_tk_seq_add(cmd, result);
348 ec_parsed_tk_free(p);
349 ec_tk_dump(stdout, cmd);
351 ec_tk_free(tk->expr);
367 static struct ec_tk_type ec_tk_cmd_type = {
369 .build = ec_tk_cmd_build,
370 .parse = ec_tk_cmd_parse,
371 .complete = ec_tk_cmd_complete,
372 .free_priv = ec_tk_cmd_free_priv,
375 EC_TK_TYPE_REGISTER(ec_tk_cmd_type);
377 int ec_tk_cmd_add_child(struct ec_tk *gen_tk, struct ec_tk *child)
379 struct ec_tk_cmd *tk = (struct ec_tk_cmd *)gen_tk;
380 struct ec_tk **table;
386 printf("add child %s\n", child->id);
390 gen_tk->flags &= ~EC_TK_F_BUILT;
392 table = ec_realloc(tk->table, (tk->len + 1) * sizeof(*tk->table));
399 table[tk->len] = child;
402 child->parent = gen_tk;
403 TAILQ_INSERT_TAIL(&gen_tk->children, child, next); // XXX really needed?
408 struct ec_tk *ec_tk_cmd(const char *id, const char *cmd_str)
410 struct ec_tk *gen_tk = NULL;
411 struct ec_tk_cmd *tk = NULL;
413 gen_tk = ec_tk_new(id, &ec_tk_cmd_type, sizeof(*tk));
417 tk = (struct ec_tk_cmd *)gen_tk;
418 tk->cmd_str = ec_strdup(cmd_str);
419 if (tk->cmd_str == NULL)
429 struct ec_tk *__ec_tk_cmd(const char *id, const char *cmd, ...)
431 struct ec_tk *gen_tk = NULL;
432 struct ec_tk_cmd *tk = NULL;
439 gen_tk = ec_tk_cmd(id, cmd);
440 tk = (struct ec_tk_cmd *)gen_tk;
444 for (child = va_arg(ap, struct ec_tk *);
445 child != EC_TK_ENDLIST;
446 child = va_arg(ap, struct ec_tk *)) {
448 /* on error, don't quit the loop to avoid leaks */
449 if (fail == 1 || child == NULL ||
450 ec_tk_cmd_add_child(&tk->gen, child) < 0) {
463 ec_tk_free(gen_tk); /* will also free children */
468 static int ec_tk_cmd_testcase(void)
475 ec_tk_int("x", 0, 10, 10),
476 ec_tk_int("y", 20, 30, 10)
479 ec_log(EC_LOG_ERR, "cannot create tk\n");
482 ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "add", "1");
483 ret |= EC_TEST_CHECK_TK_PARSE(tk, 2, "add", "23");
484 ret |= EC_TEST_CHECK_TK_PARSE(tk, 3, "add", "toto", "23");
485 ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "add", "15");
486 ret |= EC_TEST_CHECK_TK_PARSE(tk, -1, "foo");
494 static struct ec_test ec_tk_cmd_test = {
496 .test = ec_tk_cmd_testcase,
499 EC_TEST_REGISTER(ec_tk_cmd_test);