From 6e8f52f49af3927da5851d4d497bb61c94e18ad0 Mon Sep 17 00:00:00 2001 From: Olivier Matz Date: Sat, 22 Feb 2020 22:41:59 +0100 Subject: [PATCH] enhance documentation of ec_parse --- examples/parse-yaml/parse-yaml.c | 4 +- include/ecoli_parse.h | 82 +++++++++++-- src/ecoli_node_cmd.c | 12 +- src/ecoli_node_cond.c | 10 +- src/ecoli_node_expr_test.c | 8 +- src/ecoli_node_int.c | 6 +- src/ecoli_parse.c | 201 +++++++++++++++---------------- 7 files changed, 185 insertions(+), 138 deletions(-) diff --git a/examples/parse-yaml/parse-yaml.c b/examples/parse-yaml/parse-yaml.c index ff4ec74..16a98e4 100644 --- a/examples/parse-yaml/parse-yaml.c +++ b/examples/parse-yaml/parse-yaml.c @@ -125,10 +125,10 @@ __dump_as_shell(FILE *f, const struct ec_pnode *parse, size_t *seq) fprintf(f, "ec_node%zu_type='%s'\n", cur_seq, ec_node_type_name(ec_node_type(node))); - len = ec_strvec_len(ec_pnode_strvec(parse)); + len = ec_strvec_len(ec_pnode_get_strvec(parse)); fprintf(f, "ec_node%zu_strvec_len=%zu\n", cur_seq, len); for (i = 0; i < len; i++) { - s = ec_strvec_val(ec_pnode_strvec(parse), i); + s = ec_strvec_val(ec_pnode_get_strvec(parse), i); fprintf(f, "ec_node%zu_str%zu='%s'\n", cur_seq, i, s); } diff --git a/include/ecoli_parse.h b/include/ecoli_parse.h index b475522..ec41895 100644 --- a/include/ecoli_parse.h +++ b/include/ecoli_parse.h @@ -14,6 +14,8 @@ * strings) matches the node tree. On success, the result is stored in a * tree that describes which part of the input matches which node. * + * The parsing tree is sometimes referenced by another node than the + * root node. Use ec_pnode_get_root() to get the root node in that case. * @} */ @@ -30,56 +32,110 @@ struct ec_node; struct ec_pnode; /** - * Create an empty parse tree. + * Create an empty parsing tree. * + * This function is used internally when parsing an input using + * a grammar tree. + * + * @param node + * The grammar node. * @return * The empty parse tree. */ struct ec_pnode *ec_pnode(const struct ec_node *node); /** + * Free a parsing tree. * - * - * + * @param pnode + * The root of the parsing tree to be freed. It must not have + * any parent. */ void ec_pnode_free(struct ec_pnode *pnode); /** + * Remove and free all the children of a parsing tree node. * - * - * + * @param pnode + * Node whose children will be freed. */ void ec_pnode_free_children(struct ec_pnode *pnode); /** + * Duplicate a parsing tree. * - * - * + * @param pnode + * A node inside a parsing tree. + * @return + * A pointer to the copy of the input node, at the same place in the + * copy of the parsing tree. Return NULL on error (errno is set). */ struct ec_pnode *ec_pnode_dup(const struct ec_pnode *pnode); /** + * Get the string vector associated to a parsing node. * + * When an input is parsed successfully (i.e. the input string vector + * matches the grammar tree), the matching string vector is copied + * inside the associated parsing node. * + * For instance, parsing the input ["foo", "bar"] on a grammar which is + * a sequence of strings, the attached string vector will be ["foo", + * "bar"] on the root pnode, ["foo"] on the first leaf, and ["bar"] on + * the second leaf. * + * If the parsing tree does not match (see ec_pnode_matches()), it + * the associated string vector is NULL. + * + * @param pnode + * The parsing node. If NULL, the function returns NULL. + * @return + * The string vector associated to the parsing node, or NULL + * if the node is not yet parsed (this happens when building the + * parsing tree), or if the parsing tree does not match the + * input. */ -// _get_ XXX -const struct ec_strvec *ec_pnode_strvec(const struct ec_pnode *pnode); +const struct ec_strvec *ec_pnode_get_strvec(const struct ec_pnode *pnode); -/* a NULL return value is an error, with errno set - ENOTSUP: no ->parse() operation -*/ /** + * Parse a string using a grammar tree. * + * This is equivalent to calling ec_parse_strvec() on the same + * node, with a string vector containing only the argument string str. * - * + * @param node + * The grammar node. + * @param str + * The input string. + * @return + * A parsing tree, or NULL on error (errno is set). */ struct ec_pnode *ec_parse(const struct ec_node *node, const char *str); /** + * Parse a string vector using a grammar tree. * + * Generate a parsing tree by parsing the input string vector using the + * given grammar tree. * + * The parsing tree is composed of struct ec_pnode, and each of them is + * associated to a struct ec_node (the grammar node), to the string vector + * that matched the subtree, and to optional attributes. * + * When the input matches the grammar tree, the string vector associated + * to the root node of the returned parsing tree is the same than the + * strvec argument. Calling ec_pnode_matches() on this tree returns true. + * + * If the input does not match the grammar tree, the returned parsing + * tree only contains one root node, with no associated string vector. + * Calling ec_pnode_matches() on this tree returns false. + * + * @param node + * The grammar node. + * @param str + * The input string vector. + * @return + * A parsing tree, or NULL on error (errno is set). */ struct ec_pnode *ec_parse_strvec(const struct ec_node *node, const struct ec_strvec *strvec); diff --git a/src/ecoli_node_cmd.c b/src/ecoli_node_cmd.c index 1fb006a..5aac4e9 100644 --- a/src/ecoli_node_cmd.c +++ b/src/ecoli_node_cmd.c @@ -62,7 +62,7 @@ ec_node_cmd_eval_var(void **result, void *userctx, unsigned int i; /* get parsed string vector, it should contain only one str */ - vec = ec_pnode_strvec(var); + vec = ec_pnode_get_strvec(var); if (ec_strvec_len(vec) != 1) { errno = EINVAL; return -1; @@ -118,7 +118,7 @@ ec_node_cmd_eval_post_op(void **result, void *userctx, void *operand, (void)userctx; /* get parsed string vector, it should contain only one str */ - vec = ec_pnode_strvec(operator); + vec = ec_pnode_get_strvec(operator); if (ec_strvec_len(vec) != 1) { errno = EINVAL; return -1; @@ -152,7 +152,7 @@ ec_node_cmd_eval_bin_op(void **result, void *userctx, void *operand1, (void)userctx; /* get parsed string vector, it should contain only one str */ - vec = ec_pnode_strvec(operator); + vec = ec_pnode_get_strvec(operator); if (ec_strvec_len(vec) > 1) { errno = EINVAL; return -1; @@ -235,7 +235,7 @@ ec_node_cmd_eval_parenthesis(void **result, void *userctx, (void)close_paren; /* get parsed string vector, it should contain only one str */ - vec = ec_pnode_strvec(open_paren); + vec = ec_pnode_get_strvec(open_paren); if (ec_strvec_len(vec) != 1) { errno = EINVAL; return -1; @@ -371,10 +371,6 @@ ec_node_cmd_build(const char *cmd_str, struct ec_node **table, size_t len) errno = EINVAL; goto fail; } - if (!ec_pnode_has_child(p)) { - errno = EINVAL; - goto fail; - } ret = ec_node_expr_eval(&result, ec_node_cmd_expr, ec_pnode_get_first_child(p), diff --git a/src/ecoli_node_cond.c b/src/ecoli_node_cond.c index def5b39..8938830 100644 --- a/src/ecoli_node_cond.c +++ b/src/ecoli_node_cond.c @@ -193,10 +193,6 @@ ec_node_cond_build(const char *cond_str) errno = EINVAL; goto fail; } - if (!ec_pnode_has_child(p)) { - errno = EINVAL; - goto fail; - } return p; @@ -650,7 +646,7 @@ eval_condition(const struct ec_pnode *cond, const struct ec_pnode *pstate) (void *)iter, "id_arg", 0); } - res = eval_func(ec_strvec_val(ec_pnode_strvec(func_name), 0), + res = eval_func(ec_strvec_val(ec_pnode_get_strvec(func_name), 0), pstate, args, n_arg); args = NULL; return res; @@ -662,7 +658,7 @@ eval_condition(const struct ec_pnode *cond, const struct ec_pnode *pstate) if (res == NULL) goto fail; res->type = STR; - res->str = ec_strdup(ec_strvec_val(ec_pnode_strvec(value), 0)); + res->str = ec_strdup(ec_strvec_val(ec_pnode_get_strvec(value), 0)); if (res->str == NULL) goto fail; return res; @@ -674,7 +670,7 @@ eval_condition(const struct ec_pnode *cond, const struct ec_pnode *pstate) if (res == NULL) goto fail; res->type = INT; - if (ec_str_parse_llint(ec_strvec_val(ec_pnode_strvec(value), 0), + if (ec_str_parse_llint(ec_strvec_val(ec_pnode_get_strvec(value), 0), 0, LLONG_MIN, LLONG_MAX, &res->int64) < 0) goto fail; diff --git a/src/ecoli_node_expr_test.c b/src/ecoli_node_expr_test.c index c2096ed..6507d33 100644 --- a/src/ecoli_node_expr_test.c +++ b/src/ecoli_node_expr_test.c @@ -35,7 +35,7 @@ ec_node_expr_test_eval_var(void **result, void *userctx, (void)userctx; /* get parsed string vector, it should contain only one str */ - vec = ec_pnode_strvec(var); + vec = ec_pnode_get_strvec(var); if (ec_strvec_len(vec) != 1) { errno = EINVAL; return -1; @@ -66,7 +66,7 @@ ec_node_expr_test_eval_pre_op(void **result, void *userctx, void *operand, (void)userctx; /* get parsed string vector, it should contain only one str */ - vec = ec_pnode_strvec(operator); + vec = ec_pnode_get_strvec(operator); if (ec_strvec_len(vec) != 1) { errno = EINVAL; return -1; @@ -96,7 +96,7 @@ ec_node_expr_test_eval_post_op(void **result, void *userctx, void *operand, (void)userctx; /* get parsed string vector, it should contain only one str */ - vec = ec_pnode_strvec(operator); + vec = ec_pnode_get_strvec(operator); if (ec_strvec_len(vec) != 1) { errno = EINVAL; return -1; @@ -127,7 +127,7 @@ ec_node_expr_test_eval_bin_op(void **result, void *userctx, void *operand1, (void)userctx; /* get parsed string vector, it should contain only one str */ - vec = ec_pnode_strvec(operator); + vec = ec_pnode_get_strvec(operator); if (ec_strvec_len(vec) != 1) { errno = EINVAL; return -1; diff --git a/src/ecoli_node_int.c b/src/ecoli_node_int.c index efebae9..ae7fbf5 100644 --- a/src/ecoli_node_int.c +++ b/src/ecoli_node_int.c @@ -392,14 +392,14 @@ static int ec_node_int_testcase(void) testres |= EC_TEST_CHECK_PARSE(node, -1, "4r"); p = ec_parse(node, "1"); - s = ec_strvec_val(ec_pnode_strvec(p), 0); + s = ec_strvec_val(ec_pnode_get_strvec(p), 0); testres |= EC_TEST_CHECK(s != NULL && ec_node_uint_getval(node, s, &u64) == 0 && u64 == 1, "bad integer value"); ec_pnode_free(p); p = ec_parse(node, "10"); - s = ec_strvec_val(ec_pnode_strvec(p), 0); + s = ec_strvec_val(ec_pnode_get_strvec(p), 0); testres |= EC_TEST_CHECK(s != NULL && ec_node_uint_getval(node, s, &u64) == 0 && u64 == 10, "bad integer value"); @@ -421,7 +421,7 @@ static int ec_node_int_testcase(void) testres |= EC_TEST_CHECK_PARSE(node, -1, "4r"); p = ec_parse(node, "10"); - s = ec_strvec_val(ec_pnode_strvec(p), 0); + s = ec_strvec_val(ec_pnode_get_strvec(p), 0); testres |= EC_TEST_CHECK(s != NULL && ec_node_int_getval(node, s, &i64) == 0 && i64 == 16, "bad integer value"); diff --git a/src/ecoli_parse.c b/src/ecoli_parse.c index 9a6eabc..c47c7b4 100644 --- a/src/ecoli_parse.c +++ b/src/ecoli_parse.c @@ -93,29 +93,28 @@ int ec_parse_child(const struct ec_node *node, struct ec_pnode *pstate, return __ec_parse_child(node, pstate, false, strvec); } -// XXX what is returned if no match ?? struct ec_pnode *ec_parse_strvec(const struct ec_node *node, const struct ec_strvec *strvec) { - struct ec_pnode *parse = ec_pnode(node); + struct ec_pnode *pnode = ec_pnode(node); int ret; - if (parse == NULL) + if (pnode == NULL) return NULL; - ret = __ec_parse_child(node, parse, true, strvec); + ret = __ec_parse_child(node, pnode, true, strvec); if (ret < 0) { - ec_pnode_free(parse); + ec_pnode_free(pnode); return NULL; } - return parse; + return pnode; } struct ec_pnode *ec_parse(const struct ec_node *node, const char *str) { struct ec_strvec *strvec = NULL; - struct ec_pnode *parse = NULL; + struct ec_pnode *pnode = NULL; errno = ENOMEM; strvec = ec_strvec(); @@ -125,40 +124,40 @@ struct ec_pnode *ec_parse(const struct ec_node *node, const char *str) if (ec_strvec_add(strvec, str) < 0) goto fail; - parse = ec_parse_strvec(node, strvec); - if (parse == NULL) + pnode = ec_parse_strvec(node, strvec); + if (pnode == NULL) goto fail; ec_strvec_free(strvec); - return parse; + return pnode; fail: ec_strvec_free(strvec); - ec_pnode_free(parse); + ec_pnode_free(pnode); return NULL; } struct ec_pnode *ec_pnode(const struct ec_node *node) { - struct ec_pnode *parse = NULL; + struct ec_pnode *pnode = NULL; - parse = ec_calloc(1, sizeof(*parse)); - if (parse == NULL) + pnode = ec_calloc(1, sizeof(*pnode)); + if (pnode == NULL) goto fail; - TAILQ_INIT(&parse->children); + TAILQ_INIT(&pnode->children); - parse->node = node; - parse->attrs = ec_dict(); - if (parse->attrs == NULL) + pnode->node = node; + pnode->attrs = ec_dict(); + if (pnode->attrs == NULL) goto fail; - return parse; + return pnode; fail: - if (parse != NULL) - ec_dict_free(parse->attrs); - ec_free(parse); + if (pnode != NULL) + ec_dict_free(pnode->attrs); + ec_free(pnode); return NULL; } @@ -207,13 +206,13 @@ fail: return NULL; } -struct ec_pnode *ec_pnode_dup(const struct ec_pnode *parse) +struct ec_pnode *ec_pnode_dup(const struct ec_pnode *pnode) { const struct ec_pnode *root; struct ec_pnode *dup_root, *dup = NULL; - root = ec_pnode_get_root(parse); - dup_root = __ec_pnode_dup(root, parse, &dup); + root = ec_pnode_get_root(pnode); + dup_root = __ec_pnode_dup(root, pnode, &dup); if (dup_root == NULL) return NULL; assert(dup != NULL); @@ -221,167 +220,167 @@ struct ec_pnode *ec_pnode_dup(const struct ec_pnode *parse) return dup; } -void ec_pnode_free_children(struct ec_pnode *parse) +void ec_pnode_free_children(struct ec_pnode *pnode) { struct ec_pnode *child; - if (parse == NULL) + if (pnode == NULL) return; - while (!TAILQ_EMPTY(&parse->children)) { - child = TAILQ_FIRST(&parse->children); - TAILQ_REMOVE(&parse->children, child, next); + while (!TAILQ_EMPTY(&pnode->children)) { + child = TAILQ_FIRST(&pnode->children); + TAILQ_REMOVE(&pnode->children, child, next); child->parent = NULL; ec_pnode_free(child); } } -void ec_pnode_free(struct ec_pnode *parse) +void ec_pnode_free(struct ec_pnode *pnode) { - if (parse == NULL) + if (pnode == NULL) return; - ec_assert_print(parse->parent == NULL, + ec_assert_print(pnode->parent == NULL, "parent not NULL in ec_pnode_free()"); - ec_pnode_free_children(parse); - ec_strvec_free(parse->strvec); - ec_dict_free(parse->attrs); - ec_free(parse); + ec_pnode_free_children(pnode); + ec_strvec_free(pnode->strvec); + ec_dict_free(pnode->attrs); + ec_free(pnode); } static void __ec_pnode_dump(FILE *out, - const struct ec_pnode *parse, size_t indent) + const struct ec_pnode *pnode, size_t indent) { struct ec_pnode *child; const struct ec_strvec *vec; const char *id = "none", *typename = "none"; /* node can be null when parsing is incomplete */ - if (parse->node != NULL) { - id = ec_node_id(parse->node); - typename = ec_node_type(parse->node)->name; + if (pnode->node != NULL) { + id = ec_node_id(pnode->node); + typename = ec_node_type(pnode->node)->name; } fprintf(out, "%*s" "type=%s id=%s vec=", (int)indent * 4, "", typename, id); - vec = ec_pnode_strvec(parse); + vec = ec_pnode_get_strvec(pnode); ec_strvec_dump(out, vec); - TAILQ_FOREACH(child, &parse->children, next) + TAILQ_FOREACH(child, &pnode->children, next) __ec_pnode_dump(out, child, indent + 1); } -void ec_pnode_dump(FILE *out, const struct ec_pnode *parse) +void ec_pnode_dump(FILE *out, const struct ec_pnode *pnode) { fprintf(out, "------------------- parse dump:\n"); - if (parse == NULL) { - fprintf(out, "parse is NULL\n"); + if (pnode == NULL) { + fprintf(out, "pnode is NULL\n"); return; } - /* only exist if it does not match (strvec == NULL) and if it - * does not have children: an incomplete parse, like those - * generated by complete() don't match but have children that - * may match. */ - if (!ec_pnode_matches(parse) && TAILQ_EMPTY(&parse->children)) { + /* Do not dump if it does not match (strvec == NULL) and if it + * does not have children. Note that an incomplete parsing tree, + * like those generated by complete(), don't match but have + * children that may match, and we want to dump them. */ + if (!ec_pnode_matches(pnode) && TAILQ_EMPTY(&pnode->children)) { fprintf(out, "no match\n"); return; } - __ec_pnode_dump(out, parse, 0); + __ec_pnode_dump(out, pnode, 0); } -void ec_pnode_link_child(struct ec_pnode *parse, +void ec_pnode_link_child(struct ec_pnode *pnode, struct ec_pnode *child) { - TAILQ_INSERT_TAIL(&parse->children, child, next); - child->parent = parse; + TAILQ_INSERT_TAIL(&pnode->children, child, next); + child->parent = pnode; } -void ec_pnode_unlink_child(struct ec_pnode *parse, +void ec_pnode_unlink_child(struct ec_pnode *pnode, struct ec_pnode *child) { - TAILQ_REMOVE(&parse->children, child, next); + TAILQ_REMOVE(&pnode->children, child, next); child->parent = NULL; } struct ec_pnode * -ec_pnode_get_first_child(const struct ec_pnode *parse) +ec_pnode_get_first_child(const struct ec_pnode *pnode) { - return TAILQ_FIRST(&parse->children); + return TAILQ_FIRST(&pnode->children); } struct ec_pnode * -ec_pnode_get_last_child(const struct ec_pnode *parse) +ec_pnode_get_last_child(const struct ec_pnode *pnode) { - return TAILQ_LAST(&parse->children, ec_pnode_list); + return TAILQ_LAST(&pnode->children, ec_pnode_list); } -struct ec_pnode *ec_pnode_next(const struct ec_pnode *parse) +struct ec_pnode *ec_pnode_next(const struct ec_pnode *pnode) { - return TAILQ_NEXT(parse, next); + return TAILQ_NEXT(pnode, next); } -bool ec_pnode_has_child(const struct ec_pnode *parse) +bool ec_pnode_has_child(const struct ec_pnode *pnode) { - return !TAILQ_EMPTY(&parse->children); + return !TAILQ_EMPTY(&pnode->children); } -const struct ec_node *ec_pnode_get_node(const struct ec_pnode *parse) +const struct ec_node *ec_pnode_get_node(const struct ec_pnode *pnode) { - if (parse == NULL) + if (pnode == NULL) return NULL; - return parse->node; + return pnode->node; } -void ec_pnode_del_last_child(struct ec_pnode *parse) +void ec_pnode_del_last_child(struct ec_pnode *pnode) { struct ec_pnode *child; - child = ec_pnode_get_last_child(parse); - ec_pnode_unlink_child(parse, child); + child = ec_pnode_get_last_child(pnode); + ec_pnode_unlink_child(pnode, child); ec_pnode_free(child); } -struct ec_pnode *__ec_pnode_get_root(struct ec_pnode *parse) +struct ec_pnode *__ec_pnode_get_root(struct ec_pnode *pnode) { - if (parse == NULL) + if (pnode == NULL) return NULL; - while (parse->parent != NULL) - parse = parse->parent; + while (pnode->parent != NULL) + pnode = pnode->parent; - return parse; + return pnode; } -struct ec_pnode *ec_pnode_get_parent(const struct ec_pnode *parse) +struct ec_pnode *ec_pnode_get_parent(const struct ec_pnode *pnode) { - if (parse == NULL) + if (pnode == NULL) return NULL; - return parse->parent; + return pnode->parent; } struct ec_pnode *__ec_pnode_iter_next(const struct ec_pnode *root, - struct ec_pnode *parse, bool iter_children) + struct ec_pnode *pnode, bool iter_children) { struct ec_pnode *child, *parent, *next; if (iter_children) { - child = TAILQ_FIRST(&parse->children); + child = TAILQ_FIRST(&pnode->children); if (child != NULL) return child; } - parent = parse->parent; - while (parent != NULL && parse != root) { - next = TAILQ_NEXT(parse, next); + parent = pnode->parent; + while (parent != NULL && pnode != root) { + next = TAILQ_NEXT(pnode, next); if (next != NULL) return next; - parse = parent; - parent = parse->parent; + pnode = parent; + parent = pnode->parent; } return NULL; } @@ -409,44 +408,44 @@ ec_pnode_find_next(struct ec_pnode *root, struct ec_pnode *start, return NULL; } -struct ec_pnode *ec_pnode_find(struct ec_pnode *parse, +struct ec_pnode *ec_pnode_find(struct ec_pnode *pnode, const char *id) { - return ec_pnode_find_next(parse, NULL, id, 1); + return ec_pnode_find_next(pnode, NULL, id, 1); } struct ec_dict * -ec_pnode_get_attrs(struct ec_pnode *parse) +ec_pnode_get_attrs(struct ec_pnode *pnode) { - if (parse == NULL) + if (pnode == NULL) return NULL; - return parse->attrs; + return pnode->attrs; } -const struct ec_strvec *ec_pnode_strvec(const struct ec_pnode *parse) +const struct ec_strvec *ec_pnode_get_strvec(const struct ec_pnode *pnode) { - if (parse == NULL || parse->strvec == NULL) + if (pnode == NULL) return NULL; - return parse->strvec; + return pnode->strvec; } -/* number of strings in the parse vector */ -size_t ec_pnode_len(const struct ec_pnode *parse) +/* number of strings in the parsed string vector */ +size_t ec_pnode_len(const struct ec_pnode *pnode) { - if (parse == NULL || parse->strvec == NULL) + if (pnode == NULL || pnode->strvec == NULL) return 0; - return ec_strvec_len(parse->strvec); + return ec_strvec_len(pnode->strvec); } -size_t ec_pnode_matches(const struct ec_pnode *parse) +size_t ec_pnode_matches(const struct ec_pnode *pnode) { - if (parse == NULL) + if (pnode == NULL) return 0; - if (parse->strvec == NULL) + if (pnode->strvec == NULL) return 0; return 1; -- 2.20.1