schema compile CHANGE partial support for choice/case nodes

- still work in progress - missing choice's default statement support
and most of the unit tests.
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 9cb5860..beb0e95 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -42,12 +42,19 @@
         /* get know where to start */
         if (parent) {
             /* schema subtree */
-            snode = lysc_node_children(parent);
-            /* do not return anything if the augment does not have any children */
-            if (!snode || !(*snode)) {
-                return NULL;
+            if (parent->nodetype == LYS_CHOICE && (options & LYS_GETNEXT_WITHCASE)) {
+                if (!((struct lysc_node_choice*)parent)->cases) {
+                    return NULL;
+                }
+                next = last = (const struct lysc_node*)&((struct lysc_node_choice*)parent)->cases[0];
+            } else {
+                snode = lysc_node_children_p(parent);
+                /* do not return anything if the augment does not have any children */
+                if (!snode || !(*snode)) {
+                    return NULL;
+                }
+                next = last = *snode;
             }
-            next = last = *snode;
         } else {
             /* top level data */
             next = last = module->data;
@@ -403,7 +410,6 @@
 lys_is_disabled(const struct lysc_node *node, int recursive)
 {
     unsigned int u;
-    struct lysc_iffeature **iff;
 
     LY_CHECK_ARG_RET(NULL, node, NULL);
 
@@ -412,12 +418,11 @@
             return NULL;
         }
 
-        iff = lysc_node_iff(node);
-        if (iff && *iff) {
+        if (node->iffeatures) {
             /* check local if-features */
-            LY_ARRAY_FOR(*iff, u) {
-                if (!lysc_iffeature_value(&(*iff)[u])) {
-                    return &(*iff)[u];
+            LY_ARRAY_FOR(node->iffeatures, u) {
+                if (!lysc_iffeature_value(&node->iffeatures[u])) {
+                    return &node->iffeatures[u];
                 }
             }
         }
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 7341188..156007b 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1133,6 +1133,20 @@
     struct lysc_range *length;       /**< Optional length limitation */
 };
 
+struct lysc_action {
+    uint16_t nodetype;               /**< LYS_ACTION */
+    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
+    const char *name;                /**< action/RPC name (mandatory) */
+    /* TODO */
+};
+
+struct lysc_notif {
+    uint16_t nodetype;               /**< LYS_NOTIF */
+    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
+    const char *name;                /**< Notification name (mandatory) */
+    /* TODO */
+};
+
 /**
  * @brief Compiled YANG data node
  */
@@ -1149,6 +1163,8 @@
                                           node in the list. */
     const char *name;                /**< node name (mandatory) */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    struct lysc_when *when;          /**< when statement */
+    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
 };
 
 struct lysc_node_container {
@@ -1164,7 +1180,6 @@
                                           node in the list. */
     const char *name;                /**< node name (mandatory) */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-
     struct lysc_when *when;          /**< when statement */
     struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
 
@@ -1175,7 +1190,21 @@
 };
 
 struct lysc_node_case {
+    uint16_t nodetype;               /**< LYS_CASE */
+    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
+    struct lys_module *module;       /**< module structure */
+    struct lysp_node *sp;            /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
+    struct lysc_node *parent;        /**< parent node (NULL in case of top level node) */
+    struct lysc_node *next;          /**< next sibling node (NULL if there is no one) */
+    struct lysc_node *prev;          /**< pointer to the previous sibling node \note Note that this pointer is
+                                          never NULL. If there is no sibling node, pointer points to the node
+                                          itself. In case of the first node, this pointer points to the last
+                                          node in the list. */
     const char *name;                /**< name of the case, including the implicit case */
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    struct lysc_when *when;          /**< when statement */
+    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+
     struct lysc_node *child;         /**< first child node of the case (linked list). Note that all the children of all the sibling cases are linked
                                           each other as siblings with the parent pointer pointing to the choice node holding the case. To distinguish
                                           which children node belongs to which case, it is needed to match the first children of the cases while going
@@ -1195,11 +1224,14 @@
                                           node in the list. */
     const char *name;                /**< node name (mandatory) */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    struct lysc_when *when;          /**< when statement */
+    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
 
     struct lysc_node_case *cases;    /**< list of the cases with their name and pointer to the first children of each case ([sized array](@ref sizedarrays))
                                           Note that all the children of all the cases are linked each other as siblings with the parent pointer pointing
                                           to this choice node. To distinguish which children node belongs to which case, it is needed to match the first
                                           children of the cases while going through the children linked list. */
+    struct lysc_node_case *dflt;     /**< default case of the choice, only a pointer into the cases array. */
 };
 
 struct lysc_node_leaf {
@@ -1215,7 +1247,6 @@
                                           node in the list. */
     const char *name;                /**< node name (mandatory) */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-
     struct lysc_when *when;          /**< when statement */
     struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
 
@@ -1239,7 +1270,6 @@
                                           node in the list. */
     const char *name;                /**< node name (mandatory) */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-
     struct lysc_when *when;          /**< when statement */
     struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
 
@@ -1266,7 +1296,6 @@
                                           node in the list. */
     const char *name;                /**< node name (mandatory) */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-
     struct lysc_when *when;          /**< when statement */
     struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
 
@@ -1294,7 +1323,8 @@
                                           node in the list. */
     const char *name;                /**< node name (mandatory) */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-
+    struct lysc_when *when;          /**< when statement */
+    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
 };
 
 /**
@@ -1328,6 +1358,62 @@
 };
 
 /**
+ * @brief Get the typedefs sized array of the given (parsed) schema node.
+ * Decides the node's type and in case it has a typedefs array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's typedefs sized array if any, NULL otherwise.
+ */
+const struct lysp_tpdf *lysp_node_typedefs(const struct lysp_node *node);
+
+/**
+ * @brief Get the actions/RPCs sized array of the given (parsed) schema node.
+ * Decides the node's type and in case it has a actions/RPCs array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's actions/RPCs sized array if any, NULL otherwise.
+ */
+const struct lysp_action *lysp_node_actions(const struct lysp_node *node);
+
+/**
+ * @brief Get the Notifications sized array of the given (parsed) schema node.
+ * Decides the node's type and in case it has a Notifications array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's Notifications sized array if any, NULL otherwise.
+ */
+const struct lysp_notif *lysp_node_notifs(const struct lysp_node *node);
+
+/**
+ * @brief Get the children linked list of the given (parsed) schema node.
+ * Decides the node's type and in case it has a children list, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's children linked list if any, NULL otherwise.
+ */
+const struct lysp_node *lysp_node_children(const struct lysp_node *node);
+
+/**
+ * @brief Get the actions/RPCs sized array of the given (compiled) schema node.
+ * Decides the node's type and in case it has a actions/RPCs array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's actions/RPCs sized array if any, NULL otherwise.
+ */
+const struct lysc_action *lysc_node_actions(const struct lysc_node *node);
+
+/**
+ * @brief Get the Notifications sized array of the given (compiled) schema node.
+ * Decides the node's type and in case it has a Notifications array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's Notifications sized array if any, NULL otherwise.
+ */
+const struct lysc_notif *lysc_node_notifs(const struct lysc_node *node);
+
+/**
+ * @brief Get the children linked list of the given (compiled) schema node.
+ * Decides the node's type and in case it has a children list, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's children linked list if any, NULL otherwise.
+ */
+const struct lysc_node *lysc_node_children(const struct lysc_node *node);
+
+/**
  * @brief Get how the if-feature statement currently evaluates.
  *
  * @param[in] iff Compiled if-feature statement to evaluate.
@@ -1497,6 +1583,7 @@
  */
 #define LYS_GETNEXT_WITHCHOICE   0x01 /**< lys_getnext() option to allow returning #LYS_CHOICE nodes instead of looking into them */
 #define LYS_GETNEXT_NOCHOICE     0x02 /**< lys_getnext() option to ignore (kind of conditional) nodes within choice node */
+#define LYS_GETNEXT_WITHCASE     0x04 /**< lys_getnext() option to allow returning #LYS_CASE nodes instead of looking into them */
 #define LYS_GETNEXT_INTONPCONT   0x40 /**< lys_getnext() option to look into non-presence container, instead of returning container itself */
 #define LYS_GETNEXT_NOSTATECHECK 0x100 /**< lys_getnext() option to skip checking module validity (import-only, disabled) and
                                             relevant if-feature conditions state */
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 21aac07..8394022 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -1891,16 +1891,14 @@
 {
     LY_ERR ret = LY_EVALID;
     const struct lysc_node *iter;
-    struct lysc_iffeature **iff;
     unsigned int u, v, count;
     struct ly_set features = {0};
 
     for (iter = refnode; iter; iter = iter->parent) {
-        iff = lysc_node_iff(iter);
-        if (iff && *iff) {
-            LY_ARRAY_FOR(*iff, u) {
-                LY_ARRAY_FOR((*iff)[u].features, v) {
-                    LY_CHECK_GOTO(ly_set_add(&features, (*iff)[u].features[v], 0) == -1, cleanup);
+        if (iter->iffeatures) {
+            LY_ARRAY_FOR(iter->iffeatures, u) {
+                LY_ARRAY_FOR(iter->iffeatures[u].features, v) {
+                    LY_CHECK_GOTO(ly_set_add(&features, iter->iffeatures[u].features[v], 0) == -1, cleanup);
                 }
             }
         }
@@ -1912,11 +1910,10 @@
      * to the leafref itself. */
     count = features.count;
     for (iter = target; iter; iter = iter->parent) {
-        iff = lysc_node_iff(iter);
-        if (iff && *iff) {
-            LY_ARRAY_FOR(*iff, u) {
-                LY_ARRAY_FOR((*iff)[u].features, v) {
-                    if ((unsigned int)ly_set_add(&features, (*iff)[u].features[v], 0) >= count) {
+        if (iter->iffeatures) {
+            LY_ARRAY_FOR(iter->iffeatures, u) {
+                LY_ARRAY_FOR(iter->iffeatures[u].features, v) {
+                    if ((unsigned int)ly_set_add(&features, iter->iffeatures[u].features[v], 0) >= count) {
                         /* new feature was added (or LY_EMEM) */
                         goto cleanup;
                     }
@@ -2988,6 +2985,202 @@
 }
 
 /**
+ * @brief Compile parsed choice node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed choice node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the container-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_choice(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+{
+    struct lysp_node_choice *ch_p = (struct lysp_node_choice*)node_p;
+    struct lysc_node_choice *ch = (struct lysc_node_choice*)node;
+    struct lysp_node *child_p, *case_child_p;
+    unsigned int u;
+    LY_ERR ret = LY_SUCCESS;
+
+    COMPILE_MEMBER_GOTO(ctx, ch_p->when, ch->when, options, lys_compile_when, ret, done);
+    COMPILE_ARRAY_GOTO(ctx, ch_p->iffeatures, ch->iffeatures, options, u, lys_compile_iffeature, ret, done);
+
+    LY_LIST_FOR(ch_p->child, child_p) {
+        if (child_p->nodetype == LYS_CASE) {
+            LY_LIST_FOR(((struct lysp_node_case*)child_p)->child, case_child_p) {
+                LY_CHECK_RET(lys_compile_node(ctx, case_child_p, options, node));
+            }
+        } else {
+            LY_CHECK_RET(lys_compile_node(ctx, child_p, options, node));
+        }
+    }
+
+    /* default branch */
+
+done:
+    return ret;
+}
+
+static LY_ERR
+lys_compile_status(struct lysc_ctx *ctx, struct lysc_node *node,  struct lysc_node *parent)
+{
+
+    /* status - it is not inherited by specification, but it does not make sense to have
+     * current in deprecated or deprecated in obsolete, so we do print warning and inherit status */
+    if (!(node->flags & LYS_STATUS_MASK)) {
+        if (parent && (parent->flags & (LYS_STATUS_DEPRC | LYS_STATUS_OBSLT))) {
+            LOGWRN(ctx->ctx, "Missing explicit \"%s\" status that was already specified in parent, inheriting.",
+                   (parent->flags & LYS_STATUS_DEPRC) ? "deprecated" : "obsolete");
+            node->flags |= parent->flags & LYS_STATUS_MASK;
+        } else {
+            node->flags |= LYS_STATUS_CURR;
+        }
+    } else if (parent) {
+        /* check status compatibility with the parent */
+        if ((parent->flags & LYS_STATUS_MASK) > (node->flags & LYS_STATUS_MASK)) {
+            if (node->flags & LYS_STATUS_CURR) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                       "A \"current\" status is in conflict with the parent's \"%s\" status.",
+                       (parent->flags & LYS_STATUS_DEPRC) ? "deprecated" : "obsolete");
+            } else { /* LYS_STATUS_DEPRC */
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                       "A \"deprecated\" status is in conflict with the parent's \"obsolete\" status.");
+            }
+            return LY_EVALID;
+        }
+    }
+    return LY_SUCCESS;
+}
+
+static LY_ERR
+lys_compile_node_uniqness(struct lysc_ctx *ctx, const struct lysc_node *children,
+                          const struct lysc_action *actions, const struct lysc_notif *notifs,
+                          const char *name, void *exclude)
+{
+    const struct lysc_node *iter;
+    unsigned int u;
+
+    LY_LIST_FOR(children, iter) {
+        if (iter != exclude && !strcmp(name, iter->name)) {
+            goto error;
+        }
+    }
+    LY_ARRAY_FOR(actions, u) {
+        if (&actions[u] != exclude && !strcmp(name, actions[u].name)) {
+            goto error;
+        }
+    }
+    LY_ARRAY_FOR(notifs, u) {
+        if (&notifs[u] != exclude && !strcmp(name, notifs[u].name)) {
+            goto error;
+        }
+    }
+    return LY_SUCCESS;
+error:
+    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DUPIDENT, name, "data definition");
+    return LY_EEXIST;
+}
+
+/**
+ * @brief Connect the node into the siblings list and check its name uniqueness.
+ *
+ * @param[in] ctx Compile context
+ * @param[in] parent Parent node holding the children list, in case of node from a choice's case,
+ * the choice itself is expected instead of a specific case node.
+ * @param[in] node Schema node to connect into the list.
+ * @return LY_ERR value - LY_SUCCESS or LY_EEXIST.
+ */
+static LY_ERR
+lys_compile_node_connect(struct lysc_ctx *ctx, struct lysc_node *parent, struct lysc_node *node)
+{
+    struct lysc_node **children;
+
+    if (node->nodetype == LYS_CASE) {
+        children = (struct lysc_node**)&((struct lysc_node_choice*)parent)->cases;
+    } else {
+        children = lysc_node_children_p(parent);
+    }
+    if (children) {
+        if (!(*children)) {
+            /* first child */
+            *children = node;
+        } else if (*children != node) {
+            /* by the condition in previous branch we cover the choice/case children
+             * - the children list is shared by the choice and the the first case, in addition
+             * the first child of each case must be referenced from the case node. So the node is
+             * actually always already inserted in case it is the first children - so here such
+             * a situation actually corresponds to the first branch */
+            /* insert at the end of the parent's children list */
+            (*children)->prev->next = node;
+            node->prev = (*children)->prev;
+            (*children)->prev = node;
+
+            /* check the name uniqueness */
+            if (lys_compile_node_uniqness(ctx, *children, lysc_node_actions(parent),
+                                          lysc_node_notifs(parent), node->name, node)) {
+                return LY_EEXIST;
+            }
+        }
+    }
+    return LY_SUCCESS;
+}
+
+static struct lysc_node_case*
+lys_compile_node_case(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node_choice *ch, struct lysc_node *child)
+{
+    struct lysc_node *iter;
+    struct lysc_node_case *cs;
+    unsigned int u;
+    LY_ERR ret;
+
+#define UNIQUE_CHECK(NAME) \
+    LY_LIST_FOR((struct lysc_node*)ch->cases, iter) { \
+        if (!strcmp(iter->name, NAME)) { \
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DUPIDENT, NAME, "case"); \
+            return NULL; \
+        } \
+    }
+
+    if (node_p->nodetype == LYS_CHOICE) {
+        UNIQUE_CHECK(child->name);
+
+        /* we have to add an implicit case node into the parent choice */
+        cs = calloc(1, sizeof(struct lysc_node_case));
+        DUP_STRING(ctx->ctx, child->name, cs->name);
+        cs->flags = ch->flags & LYS_STATUS_MASK;
+    } else { /* node_p->nodetype == LYS_CASE */
+        if (ch->cases && (node_p == ch->cases->prev->sp)) {
+            /* the case is already present since the child is not its first children */
+            return (struct lysc_node_case*)ch->cases->prev;
+        }
+        UNIQUE_CHECK(node_p->name);
+
+        /* explicit parent case is not present (this is its first child) */
+        cs = calloc(1, sizeof(struct lysc_node_case));
+        DUP_STRING(ctx->ctx, node_p->name, cs->name);
+        cs->flags = LYS_STATUS_MASK & node_p->flags;
+        cs->sp = node_p;
+
+        /* check the case's status */
+        LY_CHECK_RET(lys_compile_status(ctx, (struct lysc_node*)cs, (struct lysc_node* )ch), NULL);
+        COMPILE_MEMBER_GOTO(ctx, node_p->when, cs->when, options, lys_compile_when, ret, error);
+        COMPILE_ARRAY_GOTO(ctx, node_p->iffeatures, cs->iffeatures, options, u, lys_compile_iffeature, ret, error);
+    }
+    cs->module = ctx->mod;
+    cs->prev = (struct lysc_node*)cs;
+    cs->nodetype = LYS_CASE;
+    lys_compile_node_connect(ctx, (struct lysc_node*)ch, (struct lysc_node*)cs);
+    cs->parent = (struct lysc_node*)ch;
+    cs->child = child;
+
+    return cs;
+error:
+    return NULL;
+
+#undef UNIQUE_CHECK
+}
+
+/**
  * @brief Compile parsed schema node information.
  * @param[in] ctx Compile context
  * @param[in] node_p Parsed schema node.
@@ -3001,7 +3194,8 @@
 lys_compile_node(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *parent)
 {
     LY_ERR ret = LY_EVALID;
-    struct lysc_node *node, **children;
+    struct lysc_node *node;
+    struct lysc_node_case *cs;
     unsigned int u;
     LY_ERR (*node_compile_spec)(struct lysc_ctx*, struct lysp_node*, int, struct lysc_node*);
 
@@ -3024,6 +3218,7 @@
         break;
     case LYS_CHOICE:
         node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_choice));
+        node_compile_spec = lys_compile_node_choice;
         break;
     case LYS_ANYXML:
     case LYS_ANYDATA:
@@ -3070,27 +3265,10 @@
 
     /* status - it is not inherited by specification, but it does not make sense to have
      * current in deprecated or deprecated in obsolete, so we do print warning and inherit status */
-    if (!(node->flags & LYS_STATUS_MASK)) {
-        if (parent && (parent->flags & (LYS_STATUS_DEPRC | LYS_STATUS_OBSLT))) {
-            LOGWRN(ctx->ctx, "Missing explicit \"%s\" status that was already specified in parent, inheriting.",
-                   (parent->flags & LYS_STATUS_DEPRC) ? "deprecated" : "obsolete");
-            node->flags |= parent->flags & LYS_STATUS_MASK;
-        } else {
-            node->flags |= LYS_STATUS_CURR;
-        }
-    } else if (parent) {
-        /* check status compatibility with the parent */
-        if ((parent->flags & LYS_STATUS_MASK) > (node->flags & LYS_STATUS_MASK)) {
-            if (node->flags & LYS_STATUS_CURR) {
-                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                       "A \"current\" status is in conflict with the parent's \"%s\" status.",
-                       (parent->flags & LYS_STATUS_DEPRC) ? "deprecated" : "obsolete");
-            } else { /* LYS_STATUS_DEPRC */
-                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                       "A \"deprecated\" status is in conflict with the parent's \"obsolete\" status.");
-            }
-            goto error;
-        }
+    if (!parent || parent->nodetype != LYS_CHOICE) {
+        /* in case of choice/case's children, postpone the check to the moment we know if
+         * the parent is choice (parent here) or some case (so we have to get its flags to check) */
+        LY_CHECK_GOTO(lys_compile_status(ctx, node, parent), error);
     }
 
     if (!(options & LYSC_OPT_FREE_SP)) {
@@ -3105,19 +3283,16 @@
     /* insert into parent's children */
     if (parent) {
         if (parent->nodetype == LYS_CHOICE) {
-            /* TODO exception for cases */
-        } else if ((children = lysc_node_children(parent))) {
-            if (!(*children)) {
-                /* first child */
-                *children = node;
-            } else {
-                /* insert at the end of the parent's children list */
-                (*children)->prev->next = node;
-                node->prev = (*children)->prev;
-                (*children)->prev = node;
-            }
+            cs = lys_compile_node_case(ctx, node_p->parent, options, (struct lysc_node_choice*)parent, node);
+            LY_CHECK_ERR_GOTO(!cs, ret = LY_EVALID, error);
+            /* the postponed status check of the node and its real parent - in case of implicit case,
+             * it directly gets the same status flags as the choice */
+            LY_CHECK_GOTO(lys_compile_status(ctx, node, (struct lysc_node*)cs), error);
+            node->parent = (struct lysc_node*)cs;
+        } else { /* other than choice */
+            node->parent = parent;
         }
-        node->parent = parent;
+        LY_CHECK_RET(lys_compile_node_connect(ctx, parent, node), LY_EVALID);
     } else {
         /* top-level element */
         if (!ctx->mod->compiled->data) {
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index 9b7a3b5..28be01d 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -585,8 +585,6 @@
 {
     struct lysc_node *child, *child_next;
 
-    FREE_MEMBER(ctx, node->when, lysc_when_free);
-    FREE_ARRAY(ctx, node->iffeatures, lysc_iffeature_free);
     LY_LIST_FOR_SAFE(node->child, child_next, child) {
         lysc_node_free(ctx, child);
     }
@@ -598,8 +596,6 @@
 static void
 lysc_node_leaf_free(struct ly_ctx *ctx, struct lysc_node_leaf *node)
 {
-    FREE_MEMBER(ctx, node->when, lysc_when_free);
-    FREE_ARRAY(ctx, node->iffeatures, lysc_iffeature_free);
     FREE_ARRAY(ctx, node->musts, lysc_must_free);
     if (node->type) {
         lysc_type_free(ctx, node->type);
@@ -613,8 +609,6 @@
 {
     unsigned int u;
 
-    FREE_MEMBER(ctx, node->when, lysc_when_free);
-    FREE_ARRAY(ctx, node->iffeatures, lysc_iffeature_free);
     FREE_ARRAY(ctx, node->musts, lysc_must_free);
     if (node->type) {
         lysc_type_free(ctx, node->type);
@@ -632,8 +626,6 @@
     unsigned int u;
     struct lysc_node *child, *child_next;
 
-    FREE_MEMBER(ctx, node->when, lysc_when_free);
-    FREE_ARRAY(ctx, node->iffeatures, lysc_iffeature_free);
     LY_LIST_FOR_SAFE(node->child, child_next, child) {
         lysc_node_free(ctx, child);
     }
@@ -648,6 +640,24 @@
     /* TODO actions, notifs */
 }
 
+static void
+lysc_node_choice_free(struct ly_ctx *ctx, struct lysc_node_choice *node)
+{
+    struct lysc_node *child, *child_next;
+
+    FREE_MEMBER(ctx, node->when, lysc_when_free);
+    FREE_ARRAY(ctx, node->iffeatures, lysc_iffeature_free);
+    if (node->cases) {
+        LY_LIST_FOR_SAFE(node->cases->child, child_next, child) {
+            lysc_node_free(ctx, child);
+        }
+        LY_LIST_FOR_SAFE((struct lysc_node*)node->cases, child_next, child) {
+            lysc_node_free(ctx, child);
+        }
+    }
+
+}
+
 void
 lysc_node_free(struct ly_ctx *ctx, struct lysc_node *node)
 {
@@ -668,10 +678,19 @@
     case LYS_LIST:
         lysc_node_list_free(ctx, (struct lysc_node_list*)node);
         break;
+    case LYS_CHOICE:
+        lysc_node_choice_free(ctx, (struct lysc_node_choice*)node);
+        break;
+    case LYS_CASE:
+        /* nothing specific */
+        break;
     default:
         LOGINT(ctx);
     }
 
+    FREE_MEMBER(ctx, node->when, lysc_when_free);
+    FREE_ARRAY(ctx, node->iffeatures, lysc_iffeature_free);
+    FREE_ARRAY(ctx, node->exts, lysc_ext_instance_free);
     free(node);
 }
 
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 55c11bb..e6ea168 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -260,7 +260,7 @@
     struct lysp_tpdf **typedefs;
     unsigned int u;
 
-    typedefs = lysp_node_typedefs(node);
+    typedefs = lysp_node_typedefs_p(node);
     if (typedefs && *typedefs) {
         LY_ARRAY_FOR(*typedefs, u) {
             if (!strcmp(name, (*typedefs)[u].name)) {
@@ -458,7 +458,7 @@
 
     /* check locally scoped typedefs (avoid name shadowing) */
     if (node) {
-        typedefs = lysp_node_typedefs(node);
+        typedefs = lysp_node_typedefs_p(node);
         if (typedefs && *typedefs) {
             LY_ARRAY_FOR(*typedefs, u) {
                 if (typedefs[u] == tpdf) {
@@ -535,7 +535,7 @@
         }
     }
     for (u = 0; u < ctx->tpdfs_nodes.count; ++u) {
-        typedefs = lysp_node_typedefs((struct lysp_node *)ctx->tpdfs_nodes.objs[u]);
+        typedefs = lysp_node_typedefs_p((struct lysp_node *)ctx->tpdfs_nodes.objs[u]);
         LY_ARRAY_FOR(*typedefs, i) {
             if (lysp_check_typedef(ctx, (struct lysp_node *)ctx->tpdfs_nodes.objs[u], &(*typedefs)[i], ids_global, ids_scoped)) {
                 goto cleanup;
@@ -905,7 +905,7 @@
 }
 
 struct lysp_tpdf **
-lysp_node_typedefs(struct lysp_node *node)
+lysp_node_typedefs_p(const struct lysp_node *node)
 {
     switch (node->nodetype) {
     case LYS_CONTAINER:
@@ -925,8 +925,20 @@
     }
 }
 
+API const struct lysp_tpdf *
+lysp_node_typedefs(const struct lysp_node *node)
+{
+    struct lysp_tpdf **tpdfs;
+    tpdfs = lysp_node_typedefs_p(node);
+    if (tpdfs) {
+        return *tpdfs;
+    } else {
+        return NULL;
+    }
+}
+
 struct lysp_action **
-lysp_node_actions(struct lysp_node *node)
+lysp_node_actions_p(struct lysp_node *node)
 {
     assert(node);
     switch (node->nodetype) {
@@ -943,8 +955,20 @@
     }
 }
 
+API const struct lysp_action *
+lysp_node_actions(const struct lysp_node *node)
+{
+    struct lysp_action **actions;
+    actions = lysp_node_actions_p((struct lysp_node*)node);
+    if (actions) {
+        return *actions;
+    } else {
+        return NULL;
+    }
+}
+
 struct lysp_notif **
-lysp_node_notifs(struct lysp_node *node)
+lysp_node_notifs_p(struct lysp_node *node)
 {
     assert(node);
     switch (node->nodetype) {
@@ -961,8 +985,20 @@
     }
 }
 
+API const struct lysp_notif *
+lysp_node_notifs(const struct lysp_node *node)
+{
+    struct lysp_notif **notifs;
+    notifs = lysp_node_notifs_p((struct lysp_node*)node);
+    if (notifs) {
+        return *notifs;
+    } else {
+        return NULL;
+    }
+}
+
 struct lysp_node **
-lysp_node_children(struct lysp_node *node)
+lysp_node_children_p(struct lysp_node *node)
 {
     assert(node);
     switch (node->nodetype) {
@@ -987,8 +1023,72 @@
     }
 }
 
+API const struct lysp_node *
+lysp_node_children(const struct lysp_node *node)
+{
+    struct lysp_node **children;
+    children = lysp_node_children_p((struct lysp_node*)node);
+    if (children) {
+        return *children;
+    } else {
+        return NULL;
+    }
+}
+
+struct lysc_action **
+lysc_node_actions_p(struct lysc_node *node)
+{
+    assert(node);
+    switch (node->nodetype) {
+    case LYS_CONTAINER:
+        return &((struct lysc_node_container*)node)->actions;
+    case LYS_LIST:
+        return &((struct lysc_node_list*)node)->actions;
+    default:
+        return NULL;
+    }
+}
+
+API const struct lysc_action *
+lysc_node_actions(const struct lysc_node *node)
+{
+    struct lysc_action **actions;
+    actions = lysc_node_actions_p((struct lysc_node*)node);
+    if (actions) {
+        return *actions;
+    } else {
+        return NULL;
+    }
+}
+
+struct lysc_notif **
+lysc_node_notifs_p(struct lysc_node *node)
+{
+    assert(node);
+    switch (node->nodetype) {
+    case LYS_CONTAINER:
+        return &((struct lysc_node_container*)node)->notifs;
+    case LYS_LIST:
+        return &((struct lysc_node_list*)node)->notifs;
+    default:
+        return NULL;
+    }
+}
+
+API const struct lysc_notif *
+lysc_node_notifs(const struct lysc_node *node)
+{
+    struct lysc_notif **notifs;
+    notifs = lysc_node_notifs_p((struct lysc_node*)node);
+    if (notifs) {
+        return *notifs;
+    } else {
+        return NULL;
+    }
+}
+
 struct lysc_node **
-lysc_node_children(const struct lysc_node *node)
+lysc_node_children_p(const struct lysc_node *node)
 {
     assert(node);
     switch (node->nodetype) {
@@ -1013,22 +1113,14 @@
     }
 }
 
-struct lysc_iffeature **
-lysc_node_iff(const struct lysc_node *node)
+API const struct lysc_node *
+lysc_node_children(const struct lysc_node *node)
 {
-    assert(node);
-    switch (node->nodetype) {
-    case LYS_CONTAINER:
-        return &((struct lysc_node_container*)node)->iffeatures;
-    case LYS_LEAF:
-        return &((struct lysc_node_leaf*)node)->iffeatures;
-/* TODO
-    case LYS_LIST:
-        return &((struct lysc_node_list*)node)->iffeatures;
-    case LYS_NOTIF:
-        return &((struct lysc_notif*)node)->child;
-*/
-    default:
+    struct lysc_node **children;
+    children = lysc_node_children_p((struct lysc_node*)node);
+    if (children) {
+        return *children;
+    } else {
         return NULL;
     }
 }
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 7b6d8de..66fa5af 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -143,7 +143,7 @@
  * @param[in] node Node to check.
  * @return Address of the node's tpdf member if any, NULL otherwise.
  */
-struct lysp_tpdf **lysp_node_typedefs(struct lysp_node *node);
+struct lysp_tpdf **lysp_node_typedefs_p(const struct lysp_node *node);
 
 /**
  * @brief Get address of a node's actions list if any.
@@ -152,7 +152,7 @@
  * @param[in] node Node to check.
  * @return Address of the node's actions member if any, NULL otherwise.
  */
-struct lysp_action **lysp_node_actions(struct lysp_node *node);
+struct lysp_action **lysp_node_actions_p(struct lysp_node *node);
 
 /**
  * @brief Get address of a node's notifications list if any.
@@ -161,7 +161,7 @@
  * @param[in] node Node to check.
  * @return Address of the node's notifs member if any, NULL otherwise.
  */
-struct lysp_notif **lysp_node_notifs(struct lysp_node *node);
+struct lysp_notif **lysp_node_notifs_p(struct lysp_node *node);
 
 /**
  * @brief Get address of a node's child pointer if any.
@@ -170,7 +170,7 @@
  * @param[in] node Node to check.
  * @return Address of the node's child member if any, NULL otherwise.
  */
-struct lysp_node **lysp_node_children(struct lysp_node *node);
+struct lysp_node **lysp_node_children_p(struct lysp_node *node);
 
 /**
  * @brief Get address of a node's child pointer if any.
@@ -179,14 +179,7 @@
  * @param[in] node Node to check.
  * @return Address of the node's child member if any, NULL otherwise.
  */
-struct lysc_node **lysc_node_children(const struct lysc_node *node);
-
-/**
- * @brief Get address of a node's iffeatures pointer if any.
- * @param[in] node Node to check.
- * @return Address of the node's iffeature member if any, NULL otherwise.
- */
-struct lysc_iffeature **lysc_node_iff(const struct lysc_node *node);
+struct lysc_node **lysc_node_children_p(const struct lysc_node *node);
 
 /**
  * @brief Get the covering schema module structure for the given parsed module structure.
diff --git a/tests/src/test_parser_yang.c b/tests/src/test_parser_yang.c
index d4bc352..bc31e12 100644
--- a/tests/src/test_parser_yang.c
+++ b/tests/src/test_parser_yang.c
@@ -1562,6 +1562,59 @@
     ly_ctx_destroy(ctx.ctx, NULL);
 }
 
+static void
+test_choice(void **state)
+{
+    *state = test_choice;
+
+    struct lysp_module mod = {0};
+    struct ly_parser_ctx ctx = {0};
+    struct lysp_node_choice *ch = NULL;
+    const char *str;
+
+    assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx.ctx));
+    assert_non_null(ctx.ctx);
+    ctx.line = 1;
+    ctx.mod = &mod;
+    ctx.mod->version = 2; /* simulate YANG 1.1 */
+
+    /* invalid cardinality */
+#define TEST_DUP(MEMBER, VALUE1, VALUE2) \
+    str = "ch {" MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+    assert_int_equal(LY_EVALID, parse_choice(&ctx, &str, NULL, (struct lysp_node**)&ch)); \
+    logbuf_assert("Duplicate keyword \""MEMBER"\". Line number 1."); \
+    lysp_node_free(ctx.ctx, (struct lysp_node*)ch); ch = NULL;
+
+    TEST_DUP("config", "true", "false");
+    TEST_DUP("default", "a", "b");
+    TEST_DUP("description", "text1", "text2");
+    TEST_DUP("mandatory", "true", "false");
+    TEST_DUP("reference", "1", "2");
+    TEST_DUP("status", "current", "obsolete");
+    TEST_DUP("when", "true", "false");
+#undef TEST_DUP
+
+    /* full content */
+    str = "ch {anydata any;anyxml anyxml; case c;choice ch;config false;container c;default c;description test;if-feature f;leaf l {type string;}"
+          "leaf-list ll {type string;} list li;mandatory true;reference test;status current;when true;m:ext;} ...";
+    assert_int_equal(LY_SUCCESS, parse_choice(&ctx, &str, NULL, (struct lysp_node**)&ch));
+    assert_non_null(ch);
+    assert_int_equal(LYS_CHOICE, ch->nodetype);
+    assert_string_equal("ch", ch->name);
+    assert_string_equal("test", ch->dsc);
+    assert_non_null(ch->exts);
+    assert_non_null(ch->iffeatures);
+    assert_string_equal("test", ch->ref);
+    assert_non_null(ch->when);
+    assert_null(ch->parent);
+    assert_null(ch->next);
+    assert_int_equal(LYS_CONFIG_R | LYS_STATUS_CURR | LYS_MAND_TRUE, ch->flags);
+    lysp_node_free(ctx.ctx, (struct lysp_node*)ch); ch = NULL;
+
+    *state = NULL;
+    ly_ctx_destroy(ctx.ctx, NULL);
+}
+
 int main(void)
 {
     const struct CMUnitTest tests[] = {
@@ -1579,6 +1632,7 @@
         cmocka_unit_test_setup_teardown(test_leaf, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_leaflist, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_list, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_choice, logger_setup, logger_teardown),
     };
 
     return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index fb2e6f8..fe711f6 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -640,6 +640,63 @@
     ly_ctx_destroy(ctx, NULL);
 }
 
+static void
+test_node_choice(void **state)
+{
+    *state = test_node_choice;
+
+    struct ly_ctx *ctx;
+    struct lys_module *mod;
+    struct lysc_node_choice *ch;
+    struct lysc_node_case *cs;
+
+    assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;feature f;"
+                                        "choice ch {case a {leaf a {type string;}}leaf b {type string;}}}", LYS_IN_YANG));
+    assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+    ch = (struct lysc_node_choice*)mod->compiled->data;
+    assert_non_null(ch);
+    assert_int_equal(LYS_CONFIG_W | LYS_STATUS_CURR, ch->flags);
+    cs = ch->cases;
+    assert_non_null(cs);
+    assert_string_equal("a", cs->name);
+    assert_ptr_equal(ch, cs->parent);
+    assert_non_null(cs->child);
+    assert_string_equal("a", cs->child->name);
+    assert_ptr_equal(cs, cs->child->parent);
+    cs = (struct lysc_node_case*)cs->next;
+    assert_non_null(cs);
+    assert_string_equal("b", cs->name);
+    assert_ptr_equal(ch, cs->parent);
+    assert_non_null(cs->child);
+    assert_string_equal("b", cs->child->name);
+    assert_ptr_equal(cs, cs->child->parent);
+    assert_ptr_equal(ch->cases->child, cs->child->prev);
+    assert_ptr_equal(ch->cases->child->next, cs->child);
+    assert_ptr_equal(ch->cases->child->prev, cs->child);
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;"
+                                        "choice ch {case a {leaf x {type string;}}leaf x {type string;}}}", LYS_IN_YANG));
+    assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+    logbuf_assert("Duplicate identifier \"x\" of data definition statement.");
+    assert_non_null(mod = lys_parse_mem(ctx, "module aa2 {namespace urn:aa2;prefix aa;"
+                                        "choice ch {case a {leaf y {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG));
+    assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+    logbuf_assert("Duplicate identifier \"y\" of data definition statement.");
+    assert_non_null(mod = lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb;"
+                                        "choice ch {case a {leaf x {type string;}}leaf a {type string;}}}", LYS_IN_YANG));
+    assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+    logbuf_assert("Duplicate identifier \"a\" of case statement.");
+    assert_non_null(mod = lys_parse_mem(ctx, "module bb2 {namespace urn:bb2;prefix bb;"
+                                        "choice ch {case b {leaf x {type string;}}case b {leaf y {type string;}}}}", LYS_IN_YANG));
+    assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+    logbuf_assert("Duplicate identifier \"b\" of case statement.");
+
+    *state = NULL;
+    ly_ctx_destroy(ctx, NULL);
+}
+
 /**
  * actually the same as length restriction (tested in test_type_length()), so just check the correct handling in appropriate types,
  * do not test the expression itself
@@ -1666,7 +1723,7 @@
                                           "leaf address {type leafref{ path \"../../interface[  name = current()/../ifname ]/address/ip\";}}}}",
                                         LYS_IN_YANG));
     assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
-    type = ((struct lysc_node_leaf*)(*lysc_node_children(mod->compiled->data->prev))->prev)->type;
+    type = ((struct lysc_node_leaf*)(*lysc_node_children_p(mod->compiled->data->prev))->prev)->type;
     assert_non_null(type);
     assert_int_equal(1, type->refcount);
     assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
@@ -2108,6 +2165,7 @@
         cmocka_unit_test_setup_teardown(test_node_container, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_node_leaflist, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_node_list, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_node_choice, logger_setup, logger_teardown),
     };
 
     return cmocka_run_group_tests(tests, NULL, NULL);