schema compile CHANGE support for uses's augments
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 17921d7..9f8d3e7 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -1478,7 +1478,6 @@
parts = NULL;
ret = LY_SUCCESS;
cleanup:
- /* TODO clean up */
LY_ARRAY_FREE(parts);
return ret;
@@ -3769,6 +3768,195 @@
}
/**
+ * @brief Internal sorting process for the lys_compile_augment_sort().
+ * @param[in] aug_p The parsed augment structure to insert into the sorter sized array @p result.
+ * @param[in,out] result Sized array to store the sorted list of augments. The array is expected
+ * to be allocated to hold the complete list, its size is just incremented by adding another item.
+ */
+static void
+lys_compile_augment_sort_(struct lysp_augment *aug_p, struct lysp_augment **result)
+{
+ unsigned int v;
+ size_t len;
+
+ len = strlen(aug_p->nodeid);
+ LY_ARRAY_FOR(result, v) {
+ if (strlen(result[v]->nodeid) <= len) {
+ continue;
+ }
+ if (v < LY_ARRAY_SIZE(result)) {
+ /* move the rest of array */
+ memmove(&result[v + 1], &result[v], (LY_ARRAY_SIZE(result) - v) * sizeof *result);
+ break;
+ }
+ }
+ result[v] = aug_p;
+ LY_ARRAY_INCREMENT(result);
+}
+
+/**
+ * @brief Sort augments to apply /a/b before /a/b/c (where the /a/b/c was added by the first augment).
+ *
+ * The sorting is based only on the length of the augment's path since it guarantee the correct order
+ * (it doesn't matter the /a/x is done before /a/b/c from the example above).
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] mod_p Parsed module with the global augments (also augments from the submodules are taken).
+ * @param[in] aug_p Parsed sized array of augments to sort (no matter if global or uses's)
+ * @param[in] inc_p In case of global augments, sized array of module includes (submodules) to get global augments from submodules.
+ * @param[out] augments Resulting sorted sized array of pointers to the augments.
+ * @return LY_ERR value.
+ */
+LY_ERR
+lys_compile_augment_sort(struct lysc_ctx *ctx, struct lysp_augment *aug_p, struct lysp_include *inc_p, struct lysp_augment ***augments)
+{
+ struct lysp_augment **result = NULL;
+ unsigned int u, v;
+ size_t count = 0;
+
+ assert(augments);
+
+ /* get count of the augments in module and all its submodules */
+ if (aug_p) {
+ count += LY_ARRAY_SIZE(aug_p);
+ }
+ LY_ARRAY_FOR(inc_p, u) {
+ if (inc_p[u].submodule->augments) {
+ count += LY_ARRAY_SIZE(inc_p[u].submodule->augments);
+ }
+ }
+
+ if (!count) {
+ *augments = NULL;
+ return LY_SUCCESS;
+ }
+ LY_ARRAY_CREATE_RET(ctx->ctx, result, count, LY_EMEM);
+
+ /* sort by the length of schema-nodeid - we need to solve /x before /x/xy. It is not necessary to group them
+ * together, so there can be even /z/y betwwen them. */
+ LY_ARRAY_FOR(aug_p, u) {
+ lys_compile_augment_sort_(&aug_p[u], result);
+ }
+ LY_ARRAY_FOR(inc_p, u) {
+ LY_ARRAY_FOR(inc_p[u].submodule->augments, v) {
+ lys_compile_augment_sort_(&inc_p[u].submodule->augments[v], result);
+ }
+ }
+
+ *augments = result;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Compile the parsed augment connecting it into its target.
+ *
+ * It is expected that all the data referenced in path are present - augments are ordered so that augment B
+ * targeting data from augment A is being compiled after augment A. Also the modules referenced in the path
+ * are already implemented and compiled.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] aug_p Parsed augment to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] parent Parent node to provide the augment's context. It is NULL for the top level augments and a node holding uses's
+ * children in case of the augmenting uses data.
+ * @return LY_SUCCESS on success.
+ * @return LY_EVALID on failure.
+ */
+LY_ERR
+lys_compile_augment(struct lysc_ctx *ctx, struct lysp_augment *aug_p, int options, const struct lysc_node *parent)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysp_node *node_p, *case_node_p;
+ struct lysc_node *target; /* target target of the augment */
+ struct lysc_node *node;
+ struct lysc_node_case *next_case;
+ struct lysc_when **when, *when_shared;
+ int allow_mandatory = 0;
+
+ ret = lys_resolve_schema_nodeid(ctx, aug_p->nodeid, 0, parent,
+ LYS_CONTAINER | LYS_LIST | LYS_CHOICE | LYS_CASE | LYS_INOUT | LYS_NOTIF,
+ 1, (const struct lysc_node**)&target);
+ if (ret != LY_SUCCESS) {
+ if (ret == LY_EDENIED) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Augment's %s-schema-nodeid \"%s\" refers to a %s node which is not an allowed augment's target.",
+ parent ? "descendant" : "absolute", aug_p->nodeid, lys_nodetype2str(target->nodetype));
+ }
+ return LY_EVALID;
+ }
+
+ /* check for mandatory nodes
+ * - new cases augmenting some choice can have mandatory nodes
+ * - mandatory nodes are allowed only in case the augmentation is made conditional with a when statement
+ */
+ if (aug_p->when || target->nodetype == LYS_CHOICE) {
+ allow_mandatory = 1;
+ }
+
+ when_shared = NULL;
+ LY_LIST_FOR(aug_p->child, node_p) {
+ /* check if the subnode can be connected to the found target (e.g. case cannot be inserted into container) */
+ if (!(target->nodetype == LYS_CHOICE && node_p->nodetype == LYS_CASE)
+ && !((target->nodetype & (LYS_CONTAINER | LYS_LIST)) && (node_p->nodetype & (LYS_ACTION | LYS_NOTIF)))
+ && !(node_p->nodetype & (LYS_ANYDATA | LYS_CONTAINER | LYS_CHOICE | LYS_LEAF | LYS_LIST | LYS_LEAFLIST | LYS_USES))) {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid augment (%s) of %s node which is not allowed to contain %s node \"%s\".",
+ aug_p->nodeid, lys_nodetype2str(target->nodetype), lys_nodetype2str(node_p->nodetype), node_p->name);
+ return LY_EVALID;
+ }
+
+ /* compile the children */
+ if (node_p->nodetype != LYS_CASE) {
+ LY_CHECK_RET(lys_compile_node(ctx, node_p, options, target, 0));
+ } else {
+ LY_LIST_FOR(((struct lysp_node_case *)node_p)->child, case_node_p) {
+ LY_CHECK_RET(lys_compile_node(ctx, case_node_p, options, target, 0));
+ }
+ }
+
+ /* since the augment node is not present in the compiled tree, we need to pass some of its statements to all its children */
+ if (target->nodetype == LYS_CASE) {
+ /* the compiled node is the last child of the target (but it is a case, so we have to be careful) */
+ next_case = target->next ? (struct lysc_node_case*)target->next : ((struct lysc_node_choice*)target->parent)->cases;
+ for (node = (struct lysc_node*)lysc_node_children(target); node->next && node->next != next_case->child; node = node->next);
+ } else if (target->nodetype == LYS_CHOICE) {
+ /* to pass when statement, we need the last case no matter if it is explicit or implicit case */
+ node = ((struct lysc_node_choice*)target)->cases->prev;
+ } else {
+ /* the compiled node is the last child of the target */
+ node = lysc_node_children(target)->prev;
+ }
+
+ if (!allow_mandatory && (node->flags & LYS_MAND_TRUE)) {
+ node->flags &= ~LYS_MAND_TRUE;
+ lys_compile_mandatory_parents(target, 0);
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+ "Invalid augment (%s) adding mandatory node \"%s\" without making it conditional via when statement.",
+ aug_p->nodeid, node->name);
+ return LY_EVALID;
+ }
+
+ /* pass augment's when to all the children */
+ if (aug_p->when) {
+ LY_ARRAY_NEW_GOTO(ctx->ctx, node->when, when, ret, error);
+ if (!when_shared) {
+ ret = lys_compile_when(ctx, aug_p->when, options, when);
+ LY_CHECK_GOTO(ret, error);
+ (*when)->context = lysc_xpath_context(target);
+ when_shared = *when;
+ } else {
+ ++when_shared->refcount;
+ (*when) = when_shared;
+ }
+ }
+ }
+ /* TODO actions, notifications */
+
+error:
+ return ret;
+}
+
+/**
* @brief Compile parsed uses statement - resolve target grouping and connect its content into parent.
* If present, also apply uses's modificators.
*
@@ -3803,6 +3991,7 @@
uint32_t min, max;
struct ly_set refined = {0};
struct lysc_when **when, *when_shared;
+ struct lysp_augment **augments = NULL;
/* search for the grouping definition */
found = 0;
@@ -3898,8 +4087,7 @@
if (uses_p->when) {
LY_ARRAY_NEW_GOTO(ctx->ctx, child->when, when, ret, error);
if (!when_shared) {
- ret = lys_compile_when(ctx, uses_p->when, options, when);
- LY_CHECK_GOTO(ret, error);
+ LY_CHECK_GOTO(lys_compile_when(ctx, uses_p->when, options, when), error);
(*when)->context = lysc_xpath_context(parent);
when_shared = *when;
} else {
@@ -3913,7 +4101,11 @@
context_node_fake.child->prev = parent ? lysc_node_children(parent)->prev : ctx->mod->compiled->data->prev;
}
- /* TODO: apply augment */
+ /* sort and apply augments */
+ LY_CHECK_GOTO(lys_compile_augment_sort(ctx, uses_p->augments, NULL, &augments), error);
+ LY_ARRAY_FOR(augments, u) {
+ LY_CHECK_GOTO(lys_compile_augment(ctx, augments[u], options, (struct lysc_node*)&context_node_fake), error);
+ }
/* reload previous context's mod_def */
ctx->mod_def = mod_old;
@@ -4164,6 +4356,7 @@
ly_set_rm_index(&ctx->groupings, ctx->groupings.count - 1, NULL);
assert(ctx->groupings.count == grp_stack_count);
ly_set_erase(&refined, NULL);
+ LY_ARRAY_FREE(augments);
return ret;
}
@@ -4330,194 +4523,6 @@
}
/**
- * @brief Internal sorting process for the lys_compile_augment_sort().
- * @param[in] aug_p The parsed augment structure to insert into the sorter sized array @p result.
- * @param[in,out] result Sized array to store the sorted list of augments. The array is expected
- * to be allocated to hold the complete list, its size is just incremented by adding another item.
- */
-static void
-lys_compile_augment_sort_(struct lysp_augment *aug_p, struct lysp_augment **result)
-{
- unsigned int v;
- size_t len;
-
- len = strlen(aug_p->nodeid);
- LY_ARRAY_FOR(result, v) {
- if (strlen(result[v]->nodeid) <= len) {
- continue;
- }
- if (v < LY_ARRAY_SIZE(result)) {
- /* move the rest of array */
- memmove(&result[v + 1], &result[v], (LY_ARRAY_SIZE(result) - v) * sizeof *result);
- break;
- }
- }
- result[v] = aug_p;
- LY_ARRAY_INCREMENT(result);
-}
-
-/**
- * @brief Sort augments to apply /a/b before /a/b/c (where the /a/b/c was added by the first augment).
- *
- * The sorting is based only on the length of the augment's path since it guarantee the correct order
- * (it doesn't matter the /a/x is done before /a/b/c from the example above).
- *
- * @param[in] ctx Compile context.
- * @param[in] mod_p Parsed module with the global augments (also augments from the submodules are taken).
- * @param[out] augments Resulting sorted sized array of pointers to the augments.
- * @return LY_ERR value.
- */
-LY_ERR
-lys_compile_augment_sort(struct lysc_ctx *ctx, struct lysp_module *mod_p, struct lysp_augment ***augments)
-{
- struct lysp_augment **result = NULL;
- unsigned int u, v;
- size_t count = 0;
-
- assert(mod_p);
- assert(augments);
-
- /* get count of the augments in module and all its submodules */
- if (mod_p->augments) {
- count += LY_ARRAY_SIZE(mod_p->augments);
- }
- LY_ARRAY_FOR(mod_p->includes, u) {
- if (mod_p->includes[u].submodule->augments) {
- count += LY_ARRAY_SIZE(mod_p->includes[u].submodule->augments);
- }
- }
-
- if (!count) {
- *augments = NULL;
- return LY_SUCCESS;
- }
- LY_ARRAY_CREATE_RET(ctx->ctx, result, count, LY_EMEM);
-
- /* sort by the length of schema-nodeid - we need to solve /x before /x/xy. It is not necessary to group them
- * together, so there can be even /z/y betwwen them. */
- LY_ARRAY_FOR(mod_p->augments, u) {
- lys_compile_augment_sort_(&mod_p->augments[u], result);
- }
- LY_ARRAY_FOR(mod_p->includes, u) {
- LY_ARRAY_FOR(mod_p->includes[u].submodule->augments, v) {
- lys_compile_augment_sort_(&mod_p->includes[u].submodule->augments[v], result);
- }
- }
-
- *augments = result;
- return LY_SUCCESS;
-}
-
-/**
- * @brief Compile the parsed augment connecting it into its target.
- *
- * It is expected that all the data referenced in path are present - augments are ordered so that augment B
- * targeting data from augment A is being compiled after augment A. Also the modules referenced in the path
- * are already implemented and compiled.
- *
- * @param[in] ctx Compile context.
- * @param[in] aug_p Parsed augment to compile.
- * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
- * @param[in] parent Parent node to provide the augment's context. It is NULL for the top level augments and a node holding uses's
- * children in case of the augmenting uses data.
- * @return LY_SUCCESS on success.
- * @return LY_EVALID on failure.
- */
-LY_ERR
-lys_compile_augment(struct lysc_ctx *ctx, struct lysp_augment *aug_p, int options, const struct lysc_node *parent)
-{
- LY_ERR ret = LY_SUCCESS;
- struct lysp_node *node_p, *case_node_p;
- struct lysc_node *target; /* target target of the augment */
- struct lysc_node *node;
- struct lysc_node_case *next_case;
- struct lysc_when **when, *when_shared;
- int allow_mandatory = 0;
-
- ret = lys_resolve_schema_nodeid(ctx, aug_p->nodeid, 0, parent,
- LYS_CONTAINER | LYS_LIST | LYS_CHOICE | LYS_CASE | LYS_INOUT | LYS_NOTIF,
- 1, (const struct lysc_node**)&target);
- if (ret != LY_SUCCESS) {
- if (ret == LY_EDENIED) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
- "Augment's %s-schema-nodeid \"%s\" refers to a %s node which is not an allowed augment's target.",
- parent ? "descendant" : "absolute", aug_p->nodeid, lys_nodetype2str(target->nodetype));
- }
- return LY_EVALID;
- }
-
- /* check for mandatory nodes
- * - new cases augmenting some choice can have mandatory nodes
- * - mandatory nodes are allowed only in case the augmentation is made conditional with a when statement
- */
- if (aug_p->when || target->nodetype == LYS_CHOICE) {
- allow_mandatory = 1;
- }
-
- when_shared = NULL;
- LY_LIST_FOR(aug_p->child, node_p) {
- /* check if the subnode can be connected to the found target (e.g. case cannot be inserted into container) */
- if (!(target->nodetype == LYS_CHOICE && node_p->nodetype == LYS_CASE)
- && !((target->nodetype & (LYS_CONTAINER | LYS_LIST)) && (node_p->nodetype & (LYS_ACTION | LYS_NOTIF)))
- && !(node_p->nodetype & (LYS_ANYDATA | LYS_CONTAINER | LYS_CHOICE | LYS_LEAF | LYS_LIST | LYS_LEAFLIST | LYS_USES))) {
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
- "Invalid augment (%s) of %s node which is not allowed to contain %s node \"%s\".",
- aug_p->nodeid, lys_nodetype2str(target->nodetype), lys_nodetype2str(node_p->nodetype), node_p->name);
- return LY_EVALID;
- }
-
- /* compile the children */
- if (node_p->nodetype != LYS_CASE) {
- LY_CHECK_RET(lys_compile_node(ctx, node_p, options, target, 0));
- } else {
- LY_LIST_FOR(((struct lysp_node_case *)node_p)->child, case_node_p) {
- LY_CHECK_RET(lys_compile_node(ctx, case_node_p, options, target, 0));
- }
- }
-
- /* since the augment node is not present in the compiled tree, we need to pass some of its statements to all its children */
- if (target->nodetype == LYS_CASE) {
- /* the compiled node is the last child of the target (but it is a case, so we have to be careful) */
- next_case = target->next ? (struct lysc_node_case*)target->next : ((struct lysc_node_choice*)target->parent)->cases;
- for (node = (struct lysc_node*)lysc_node_children(target); node->next && node->next != next_case->child; node = node->next);
- } else if (target->nodetype == LYS_CHOICE) {
- /* to pass when statement, we need the last case no matter if it is explicit or implicit case */
- node = ((struct lysc_node_choice*)target)->cases->prev;
- } else {
- /* the compiled node is the last child of the target */
- node = lysc_node_children(target)->prev;
- }
-
- if (!allow_mandatory && (node->flags & LYS_MAND_TRUE)) {
- node->flags &= ~LYS_MAND_TRUE;
- lys_compile_mandatory_parents(target, 0);
- LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
- "Invalid augment (%s) adding mandatory node \"%s\" without making it conditional via when statement.",
- aug_p->nodeid, node->name);
- return LY_EVALID;
- }
-
- /* pass augment's when to all the children */
- if (aug_p->when) {
- LY_ARRAY_NEW_GOTO(ctx->ctx, node->when, when, ret, error);
- if (!when_shared) {
- ret = lys_compile_when(ctx, aug_p->when, options, when);
- LY_CHECK_GOTO(ret, error);
- (*when)->context = lysc_xpath_context(target);
- when_shared = *when;
- } else {
- ++when_shared->refcount;
- (*when) = when_shared;
- }
- }
- }
- /* TODO actions, notifications */
-
-error:
- return ret;
-}
-
-/**
* @brief Compile the given YANG submodule into the main module.
* @param[in] ctx Compile context
* @param[in] inc Include structure from the main module defining the submodule.
@@ -4619,7 +4624,7 @@
}
/* augments - sort first to cover augments augmenting other augments */
- ret = lys_compile_augment_sort(&ctx, sp, &augments);
+ ret = lys_compile_augment_sort(&ctx, sp->augments, sp->includes, &augments);
LY_CHECK_GOTO(ret, error);
LY_ARRAY_FOR(augments, u) {
ret = lys_compile_augment(&ctx, augments[u], options, NULL);
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 7067701..742b882 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -106,6 +106,13 @@
/* descendant-schema-nodeid */
nodeid_type = "descendant";
context_module = context_node->module;
+
+ if (*id == '/') {
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Invalid descendant-schema-nodeid value \"%.*s\" - absolute-schema-nodeid used.",
+ nodeid_len ? nodeid_len : strlen(nodeid), nodeid);
+ return LY_EVALID;
+ }
} else {
/* absolute-schema-nodeid */
nodeid_type = "absolute";
@@ -114,7 +121,7 @@
if (*id != '/') {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
"Invalid absolute-schema-nodeid value \"%.*s\" - missing starting \"/\".",
- nodeid_len, nodeid);
+ nodeid_len ? nodeid_len : strlen(nodeid), nodeid);
return LY_EVALID;
}
++id;
@@ -162,7 +169,7 @@
} else {
LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
"Invalid %s-schema-nodeid value \"%.*s\" - unexpected end of expression.",
- nodeid_type, nodeid_len, nodeid);
+ nodeid_type, nodeid_len ? nodeid_len : strlen(nodeid), nodeid);
}
return ret;
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index 0591479..cfdc33a 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -2121,7 +2121,7 @@
struct ly_ctx *ctx;
struct lys_module *mod;
- struct lysc_node *parent, *child;
+ const struct lysc_node *parent, *child;
assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
@@ -2177,6 +2177,14 @@
assert_true(LYS_STATUS_OBSLT & mod->compiled->data->next->flags);
logbuf_assert(""); /* no warning about inheriting deprecated flag from uses */
+ assert_non_null(mod = lys_parse_mem(ctx, "module d {namespace urn:d;prefix d; grouping grp {container g;}"
+ "container top {uses grp {augment g {leaf x {type int8;}}}}}", LYS_IN_YANG));
+ assert_non_null(mod->compiled->data);
+ assert_non_null(child = lysc_node_children(mod->compiled->data));
+ assert_string_equal("g", child->name);
+ assert_non_null(child = lysc_node_children(child));
+ assert_string_equal("x", child->name);
+
/* invalid */
assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;uses missinggrp;}", LYS_IN_YANG));
logbuf_assert("Grouping \"missinggrp\" referenced by a uses statement not found.");
@@ -2205,6 +2213,13 @@
"uses grp;leaf m {type int8;}}", LYS_IN_YANG));
logbuf_assert("Duplicate identifier \"m\" of data definition statement.");
+
+ assert_null(lys_parse_mem(ctx, "module gg {namespace urn:gg;prefix gg; grouping grp {container g;}"
+ "leaf g {type string;}"
+ "container top {uses grp {augment /g {leaf x {type int8;}}}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid descendant-schema-nodeid value \"/g\" - absolute-schema-nodeid used.");
+
+
*state = NULL;
ly_ctx_destroy(ctx, NULL);
}
@@ -2458,12 +2473,13 @@
"augment /c {case b {leaf d {type int8;}}}}", LYS_IN_YANG));
logbuf_assert("Invalid augment (/c) of container node which is not allowed to contain case node \"b\".");
-
assert_null(lys_parse_mem(ctx, "module ee {namespace urn:ee;prefix ee; container top;"
"augment /top {container c {leaf d {mandatory true; type int8;}}}}", LYS_IN_YANG));
logbuf_assert("Invalid augment (/top) adding mandatory node \"c\" without making it conditional via when statement.");
-
+ assert_null(lys_parse_mem(ctx, "module ff {namespace urn:ff;prefix ff; container top;"
+ "augment ../top {leaf x {type int8;}}}", LYS_IN_YANG));
+ logbuf_assert("Invalid absolute-schema-nodeid value \"../top\" - missing starting \"/\".");
*state = NULL;
ly_ctx_destroy(ctx, NULL);