Merge remote-tracking branch 'upstream/libyang2' into libyang2
diff --git a/src/common.c b/src/common.c
index ab3b71f..dc96310 100644
--- a/src/common.c
+++ b/src/common.c
@@ -11,11 +11,12 @@
  *
  *     https://opensource.org/licenses/BSD-3-Clause
  */
-#define _XOPEN_SOURCE
-#define _DEFAULT_SOURCE
+
+#include "common.h"
 
 #include <assert.h>
 #include <errno.h>
+#include <limits.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <ctype.h>
@@ -25,7 +26,6 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include "common.h"
 #include "tree_schema.h"
 
 const char *const ly_stmt_list[] = {
@@ -145,6 +145,22 @@
     [LYS_DEV_REPLACE] = "replace",
 };
 
+#ifndef  __USE_GNU
+char *
+get_current_dir_name(void)
+{
+    char tmp[PATH_MAX];
+    char *retval;
+
+    if (getcwd(tmp, sizeof(tmp))) {
+        retval = strdup(tmp);
+        LY_CHECK_ERR_RET(!retval, LOGMEM(NULL), NULL);
+        return retval;
+    }
+    return NULL;
+}
+#endif
+
 void *
 ly_realloc(void *ptr, size_t size)
 {
@@ -304,3 +320,59 @@
     }
     return LY_SUCCESS;
 }
+
+LY_ERR
+ly_parse_int(const char *val_str, int64_t min, int64_t max, int base, int64_t *ret)
+{
+    char *strptr;
+
+    LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
+
+    /* convert to 64-bit integer, all the redundant characters are handled */
+    errno = 0;
+    strptr = NULL;
+
+    /* parse the value */
+    *ret = strtoll(val_str, &strptr, base);
+    if (errno) {
+        return LY_EVALID;
+    } else if ((*ret < min) || (*ret > max)) {
+        return LY_EDENIED;
+    } else if (strptr && *strptr) {
+        while (isspace(*strptr)) {
+            ++strptr;
+        }
+        if (*strptr) {
+            return LY_EVALID;
+        }
+    }
+    return LY_SUCCESS;
+}
+
+LY_ERR
+ly_parse_uint(const char *val_str, uint64_t max, int base, uint64_t *ret)
+{
+    char *strptr;
+    uint64_t u;
+
+    LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
+
+    errno = 0;
+    strptr = NULL;
+    u = strtoull(val_str, &strptr, base);
+    if (errno) {
+        return LY_EVALID;
+    } else if ((u > max) || (u && val_str[0] == '-')) {
+        return LY_EDENIED;
+    } else if (strptr && *strptr) {
+        while (isspace(*strptr)) {
+            ++strptr;
+        }
+        if (*strptr) {
+            return LY_EVALID;
+        }
+    }
+
+    *ret = u;
+    return LY_SUCCESS;
+}
diff --git a/src/common.h b/src/common.h
index c704ca8..3a550b8 100644
--- a/src/common.h
+++ b/src/common.h
@@ -15,13 +15,21 @@
 #ifndef LY_COMMON_H_
 #define LY_COMMON_H_
 
+#define _DEFAULT_SOURCE
+#define _GNU_SOURCE
+
 #include <assert.h>
+#include <pthread.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <unistd.h>
 
 #include "config.h"
 #include "log.h"
+#include "context.h"
 #include "tree_schema.h"
+#include "set.h"
+#include "hash_table.h"
 
 #if __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
 # define THREAD_LOCAL _Thread_local
@@ -45,6 +53,13 @@
  */
 #define API __attribute__((visibility("default")))
 
+#ifndef  __USE_GNU
+/*
+ * If we don't have GNU extension, implement these function on your own
+ */
+char *get_current_dir_name(void);
+#endif
+
 /******************************************************************************
  * Logger
  *****************************************************************************/
@@ -75,7 +90,7 @@
 void ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...);
 
 #define LOGERR(ctx, errno, str, args...) ly_log(ctx, LY_LLERR, errno, str, ##args)
-#define LOGWRN(ctx, str, args...) ly_log(ctx, LY_LLWRN, 0, str, ##args)
+#define LOGWRN(ctx, str, ...) ly_log(ctx, LY_LLWRN, 0, str, ##__VA_ARGS__)
 #define LOGVRB(str, args...) ly_log(NULL, LY_LLVRB, 0, str, ##args)
 
 #ifdef NDEBUG
@@ -99,17 +114,17 @@
  */
 #define LY_CHECK_GOTO(COND, GOTO) if (COND) {goto GOTO;}
 #define LY_CHECK_ERR_GOTO(COND, ERR, GOTO) if (COND) {ERR; goto GOTO;}
-#define LY_CHECK_RET1(RETVAL) if (RETVAL != LY_SUCCESS) {return RETVAL;}
+#define LY_CHECK_RET1(RETVAL) {LY_ERR ret__ = RETVAL;if (ret__ != LY_SUCCESS) {return ret__;}}
 #define LY_CHECK_RET2(COND, RETVAL) if (COND) {return RETVAL;}
 #define LY_CHECK_RET(...) GETMACRO2(__VA_ARGS__, LY_CHECK_RET2, LY_CHECK_RET1)(__VA_ARGS__)
 #define LY_CHECK_ERR_RET(COND, ERR, RETVAL) if (COND) {ERR; return RETVAL;}
 
-#define LY_CHECK_ARG_GOTO1(CTX, ARG, GOTO) if (!ARG) {LOGARG(CTX, ARG);goto GOTO;}
+#define LY_CHECK_ARG_GOTO1(CTX, ARG, GOTO) if (!(ARG)) {LOGARG(CTX, ARG);goto GOTO;}
 #define LY_CHECK_ARG_GOTO2(CTX, ARG1, ARG2, GOTO) LY_CHECK_ARG_GOTO1(CTX, ARG1, GOTO);LY_CHECK_ARG_GOTO1(CTX, ARG2, GOTO)
 #define LY_CHECK_ARG_GOTO3(CTX, ARG1, ARG2, ARG3, GOTO) LY_CHECK_ARG_GOTO2(CTX, ARG1, ARG2, GOTO);LY_CHECK_ARG_GOTO1(CTX, ARG3, GOTO)
 #define LY_CHECK_ARG_GOTO(CTX, ...) GETMACRO4(__VA_ARGS__, LY_CHECK_ARG_GOTO3, LY_CHECK_ARG_GOTO2, LY_CHECK_ARG_GOTO1)(CTX, __VA_ARGS__)
 
-#define LY_CHECK_ARG_RET1(CTX, ARG, RETVAL) if (!ARG) {LOGARG(CTX, ARG);return RETVAL;}
+#define LY_CHECK_ARG_RET1(CTX, ARG, RETVAL) if (!(ARG)) {LOGARG(CTX, ARG);return RETVAL;}
 #define LY_CHECK_ARG_RET2(CTX, ARG1, ARG2, RETVAL) LY_CHECK_ARG_RET1(CTX, ARG1, RETVAL);LY_CHECK_ARG_RET1(CTX, ARG2, RETVAL)
 #define LY_CHECK_ARG_RET3(CTX, ARG1, ARG2, ARG3, RETVAL) LY_CHECK_ARG_RET2(CTX, ARG1, ARG2, RETVAL);LY_CHECK_ARG_RET1(CTX, ARG3, RETVAL)
 #define LY_CHECK_ARG_RET(CTX, ...) GETMACRO4(__VA_ARGS__, LY_CHECK_ARG_RET3, LY_CHECK_ARG_RET2, LY_CHECK_ARG_RET1)(CTX, __VA_ARGS__)
@@ -126,12 +141,50 @@
 #define LY_VCODE_NSUPP       LYVE_SYNTAX, "%s not supported."
 #define LY_VCODE_INSTMT      LYVE_SYNTAX_YANG, "Invalid keyword \"%s\"."
 #define LY_VCODE_INCHILDSTMT LYVE_SYNTAX_YANG, "Invalid keyword \"%s\" as a child of \"%s\"."
+#define LY_VCODE_INCHILDSTMT2 LYVE_SYNTAX_YANG, "Invalid keyword \"%s\" as a child of \"%s\" - the statement is allowed only in YANG 1.1 modules."
+#define LY_VCODE_INCHILDSTMSCOMB LYVE_SYNTAX_YANG, "Invalid combination of keywords \"%s\" and \"%s\" as substatements of \"%s\"."
 #define LY_VCODE_DUPSTMT     LYVE_SYNTAX_YANG, "Duplicate keyword \"%s\"."
+#define LY_VCODE_DUPIDENT    LYVE_SYNTAX_YANG, "Duplicate identifier \"%s\" of %s statement."
 #define LY_VCODE_INVAL       LYVE_SYNTAX_YANG, "Invalid value \"%.*s\" of \"%s\"."
 #define LY_VCODE_MISSTMT     LYVE_SYNTAX_YANG, "Missing mandatory keyword \"%s\" as a child of \"%s\"."
+#define LY_VCODE_MISSCHILDSTMT LYVE_SYNTAX_YANG, "Missing %s substatement for %s%s."
 #define LY_VCODE_INORD       LYVE_SYNTAX_YANG, "Invalid keyword \"%s\", it cannot appear after \"%s\"."
 #define LY_VCODE_OOB         LYVE_SYNTAX_YANG, "Value \"%.*s\" is out of \"%s\" bounds."
 #define LY_VCODE_INDEV       LYVE_SYNTAX_YANG, "Deviate \"%s\" does not support keyword \"%s\"."
+#define LY_VCODE_INREGEXP    LYVE_SYNTAX_YANG, "Regular expression \"%s\" is not valid (\"%s\": %s)."
+#define LY_VCODE_XP_EOE      LYVE_XPATH, "Unterminated string delimited with %c (%.15s)."
+#define LY_VCODE_XP_INEXPR   LYVE_XPATH, "Invalid expression 0x%x."
+
+/******************************************************************************
+ * Context
+ *****************************************************************************/
+
+/**
+ * @brief Context of the YANG schemas
+ */
+struct ly_ctx {
+    struct dict_table dict;           /**< dictionary to effectively store strings used in the context related structures */
+    struct ly_set search_paths;       /**< set of directories where to search for schema's imports/includes */
+    struct ly_set list;               /**< set of YANG schemas */
+    ly_module_imp_clb imp_clb;        /**< Optional callback for retrieving missing included or imported models in a custom way. */
+    void *imp_clb_data;               /**< Optional private data for imp_clb() */
+    uint16_t module_set_id;           /**< ID of the current set of schemas */
+    uint16_t flags;                   /**< context settings, see @ref contextoptions. */
+    pthread_key_t errlist_key;        /**< key for the thread-specific list of errors related to the context */
+};
+
+/**
+ * @brief Try to find submodule in the context. Submodules are present only in the parsed (lysp_) schema trees, if only
+ * the compiled versions of the schemas are present, the submodule cannot be returned even if it was used to compile
+ * some of the currently present schemas.
+ *
+ * @param[in] ctx Context where to search
+ * @param[in] module Name of the module where the submodule is supposed to belongs-to. If NULL, the module name is not checked.
+ * @param[in] submodule Name of the submodule to find.
+ * @param[in] revision Optional revision of the submodule to find. If not specified, the latest revision is returned.
+ * @return Pointer to the specified submodule if it is present in the context.
+ */
+struct lysp_module *ly_ctx_get_submodule(const struct ly_ctx *ctx, const char *module, const char *submodule, const char *revision);
 
 /******************************************************************************
  * Parsers
@@ -230,6 +283,8 @@
  * Generic useful functions.
  *****************************************************************************/
 
+#define FREE_STRING(CTX, STRING) if (STRING) {lydict_remove(CTX, STRING);}
+
 /**
  * @brief Wrapper for realloc() call. The only difference is that if it fails to
  * allocate the requested memory, the original memory is freed as well.
@@ -252,6 +307,39 @@
 LY_ERR ly_getutf8(const char **input, unsigned int *utf8_char, size_t *bytes_read);
 
 /**
+ * @brief Parse signed integer with possible limitation.
+ * @param[in] val_str String value containing signed integer, note that
+ * nothing else than whitespaces are expected after the value itself.
+ * @param[in] min Limitation for the value which must not be lower than min.
+ * @param[in] max Limitation for the value which must not be higher than max.
+ * @param[in] base Numeric base for parsing:
+ *        0 - to accept decimal, octal, hexadecimal (e.g. in default value)
+ *       10 - to accept only decimal (e.g. data instance value)
+ * @param[out] ret Resulting value.
+ * @return LY_ERR value:
+ * LY_EDENIED - the value breaks the limits,
+ * LY_EVALID - string contains invalid value,
+ * LY_SUCCESS - successful parsing.
+ */
+LY_ERR ly_parse_int(const char *val_str, int64_t min, int64_t max, int base, int64_t *ret);
+
+/**
+ * @brief Parse unsigned integer with possible limitation.
+ * @param[in] val_str String value containing unsigned integer, note that
+ * nothing else than whitespaces are expected after the value itself.
+ * @param[in] max Limitation for the value which must not be higher than max.
+ * @param[in] base Numeric base for parsing:
+ *        0 - to accept decimal, octal, hexadecimal (e.g. in default value)
+ *       10 - to accept only decimal (e.g. data instance value)
+ * @param[out] ret Resulting value.
+ * @return LY_ERR value:
+ * LY_EDENIED - the value breaks the limits,
+ * LY_EVALID - string contains invalid value,
+ * LY_SUCCESS - successful parsing.
+ */
+LY_ERR ly_parse_uint(const char *val_str, uint64_t max, int base, uint64_t *ret);
+
+/**
  * @brief mmap(2) wrapper to map input files into memory to unify parsing.
  *
  * The address space is allocate only for reading.
@@ -298,7 +386,33 @@
         memset(NEW_ITEM, 0, sizeof *(NEW_ITEM))
 
 /**
+ * @brief (Re-)Allocation of a ([sized array](@ref sizedarrays)).
+ *
+ * Increases the size information.
+ *
+ * @param[in] CTX libyang context for logging.
+ * @param[in,out] ARRAY Pointer to the array to allocate/resize. The size of the allocated
+ * space is counted from the type of the ARRAY, so do not provide placeholder void pointers.
+ * @param[out] NEW_ITEM Returning pointer to the newly allocated record in the ARRAY.
+ * @param[out] RET Variable to store error code.
+ * @param[in] GOTO Label to go in case of error (memory allocation failure).
+ */
+#define LY_ARRAY_NEW_GOTO(CTX, ARRAY, NEW_ITEM, RET, GOTO) \
+        if (!(ARRAY)) { \
+            ARRAY = malloc(sizeof(uint32_t) + sizeof *(ARRAY)); \
+            *((uint32_t*)(ARRAY)) = 1; \
+        } else { \
+            ++(*((uint32_t*)(ARRAY) - 1)); \
+            ARRAY = ly_realloc(((uint32_t*)(ARRAY) - 1), sizeof(uint32_t) + (*((uint32_t*)(ARRAY) - 1) * sizeof *(ARRAY))); \
+            LY_CHECK_ERR_GOTO(!(ARRAY), LOGMEM(CTX); RET = LY_EMEM, GOTO); \
+        } \
+        ARRAY = (void*)((uint32_t*)(ARRAY) + 1); \
+        (NEW_ITEM) = &(ARRAY)[*((uint32_t*)(ARRAY) - 1) - 1]; \
+        memset(NEW_ITEM, 0, sizeof *(NEW_ITEM))
+
+/**
  * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
+ * If the ARRAY already exists, it is resized (space for SIZE items is added).
  *
  * Does not set the size information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
  * when the items are filled.
@@ -310,9 +424,42 @@
  * @param[in] RETVAL Return value for the case of error (memory allocation failure).
  */
 #define LY_ARRAY_CREATE_RET(CTX, ARRAY, SIZE, RETVAL) \
-        ARRAY = calloc(1, sizeof(uint32_t) + SIZE * sizeof *(ARRAY)); \
-        LY_CHECK_ERR_RET(!(ARRAY), LOGMEM(CTX), RETVAL); \
-        ARRAY = (void*)((uint32_t*)(ARRAY) + 1)
+        if (ARRAY) { \
+            ARRAY = ly_realloc(((uint32_t*)(ARRAY) - 1), sizeof(uint32_t) + ((*((uint32_t*)(ARRAY) - 1) + SIZE) * sizeof *(ARRAY))); \
+            LY_CHECK_ERR_RET(!(ARRAY), LOGMEM(CTX), RETVAL); \
+            ARRAY = (void*)((uint32_t*)(ARRAY) + 1); \
+            memset(&(ARRAY)[*((uint32_t*)(ARRAY) - 1)], 0, SIZE * sizeof *(ARRAY)); \
+        } else { \
+            ARRAY = calloc(1, sizeof(uint32_t) + SIZE * sizeof *(ARRAY)); \
+            LY_CHECK_ERR_RET(!(ARRAY), LOGMEM(CTX), RETVAL); \
+            ARRAY = (void*)((uint32_t*)(ARRAY) + 1); \
+        }
+
+/**
+ * @brief Allocate a ([sized array](@ref sizedarrays)) for the specified number of items.
+ * If the ARRAY already exists, it is resized (space for SIZE items is added).
+ *
+ * Does not set the size information, it is supposed to be incremented via ::LY_ARRAY_INCREMENT
+ * when the items are filled.
+ *
+ * @param[in] CTX libyang context for logging.
+ * @param[in,out] ARRAY Pointer to the array to create.
+ * @param[in] SIZE Number of items the array is supposed to hold. The size of the allocated
+ * space is then counted from the type of the ARRAY, so do not provide placeholder void pointers.
+ * @param[out] RET Variable to store error code.
+ * @param[in] GOTO Label to go in case of error (memory allocation failure).
+ */
+#define LY_ARRAY_CREATE_GOTO(CTX, ARRAY, SIZE, RET, GOTO) \
+        if (ARRAY) { \
+            ARRAY = ly_realloc(((uint32_t*)(ARRAY) - 1), sizeof(uint32_t) + ((*((uint32_t*)(ARRAY) - 1) + SIZE) * sizeof *(ARRAY))); \
+            LY_CHECK_ERR_GOTO(!(ARRAY), LOGMEM(CTX); RET = LY_EMEM, GOTO); \
+            ARRAY = (void*)((uint32_t*)(ARRAY) + 1); \
+            memset(&(ARRAY)[*((uint32_t*)(ARRAY) - 1)], 0, SIZE * sizeof *(ARRAY)); \
+        } else { \
+            ARRAY = calloc(1, sizeof(uint32_t) + SIZE * sizeof *(ARRAY)); \
+            LY_CHECK_ERR_GOTO(!(ARRAY), LOGMEM(CTX); RET = LY_EMEM, GOTO); \
+            ARRAY = (void*)((uint32_t*)(ARRAY) + 1); \
+        }
 
 #define LY_ARRAY_INCREMENT(ARRAY) \
         ++(*((uint32_t*)(ARRAY) - 1))
diff --git a/src/config.h.in b/src/config.h.in
index 2a2af23..0d9284b 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -15,6 +15,8 @@
 #ifndef LY_CONFIG_H_
 #define LY_CONFIG_H_
 
+#cmakedefine APPLE
+
 /**
  * @brief Compiler flag for unused function attributes
  */
diff --git a/src/context.c b/src/context.c
index 1216704..66b62e5 100644
--- a/src/context.c
+++ b/src/context.c
@@ -12,8 +12,8 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _DEFAULT_SOURCE
-#define _BSD_SOURCE
+#include "common.h"
+
 #include <errno.h>
 #include <limits.h>
 #include <stdlib.h>
@@ -23,27 +23,19 @@
 #include <unistd.h>
 
 #include "context.h"
-#include "common.h"
 #include "tree_schema_internal.h"
 #include "libyang.h"
 
 #define LY_INTERNAL_MODS_COUNT 6
 
-#define IETF_YANG_METADATA_PATH "../models/ietf-yang-metadata@2016-08-05.h"
-#define YANG_PATH "../models/yang@2017-02-20.h"
-#define IETF_INET_TYPES_PATH "../models/ietf-inet-types@2013-07-15.h"
-#define IETF_YANG_TYPES_PATH "../models/ietf-yang-types@2013-07-15.h"
-#define IETF_DATASTORES "../models/ietf-datastores@2017-08-17.h"
-#define IETF_YANG_LIB_PATH "../models/ietf-yang-library@2018-01-17.h"
+#include "../models/ietf-yang-metadata@2016-08-05.h"
+#include "../models/yang@2017-02-20.h"
+#include "../models/ietf-inet-types@2013-07-15.h"
+#include "../models/ietf-yang-types@2013-07-15.h"
+#include "../models/ietf-datastores@2017-08-17.h"
+#include "../models/ietf-yang-library@2018-01-17.h"
 #define IETF_YANG_LIB_REV "2018-01-17"
 
-#include IETF_YANG_METADATA_PATH
-#include YANG_PATH
-#include IETF_INET_TYPES_PATH
-#include IETF_YANG_TYPES_PATH
-#include IETF_DATASTORES
-#include IETF_YANG_LIB_PATH
-
 static struct internal_modules_s {
     const char *name;
     const char *revision;
@@ -95,6 +87,9 @@
             return LY_EMEM;
         }
 
+        /* new searchdir - possibly more latest revision available */
+        ly_ctx_reset_latests(ctx);
+
         return LY_SUCCESS;
     } else {
         /* consider that no change is not actually an error */
@@ -212,7 +207,7 @@
     /* load internal modules */
     for (i = 0; i < ((options & LY_CTX_NOYANGLIBRARY) ? (LY_INTERNAL_MODS_COUNT - 2) : LY_INTERNAL_MODS_COUNT); i++) {
         module = (struct lys_module *)lys_parse_mem(ctx, internal_modules[i].data, internal_modules[i].format);
-        LY_CHECK_GOTO(!module, error);
+        LY_CHECK_ERR_GOTO(!module, rc = ly_errcode(ctx), error);
         module->parsed->implemented = internal_modules[i].implemented;
     }
 
@@ -262,6 +257,242 @@
 }
 
 API void
+ly_ctx_set_module_imp_clb(struct ly_ctx *ctx, ly_module_imp_clb clb, void *user_data)
+{
+    LY_CHECK_ARG_RET(ctx, ctx,);
+
+    ctx->imp_clb = clb;
+    ctx->imp_clb_data = user_data;
+}
+
+API ly_module_imp_clb
+ly_ctx_get_module_imp_clb(const struct ly_ctx *ctx, void **user_data)
+{
+    LY_CHECK_ARG_RET(ctx, ctx, NULL);
+
+    if (user_data) {
+        *user_data = ctx->imp_clb_data;
+    }
+    return ctx->imp_clb;
+}
+
+/**
+ * @brief Iterate over the modules in the given context. Returned modules must match the given key at the offset of
+ * lysp_module and lysc_module structures (they are supposed to be placed at the same offset in both structures).
+ *
+ * @param[in] ctx Context where to iterate.
+ * @param[in] key Key value to search for.
+ * @param[in] key_offset Key's offset in struct lysp_module and struct lysc_module to get value from the context's
+ * modules to match with the key.
+ * @param[in,out] Iterator to pass between the function calls. On the first call, the variable is supposed to be
+ * initiated to 0. After each call returning a module, the value is greater by 1 than the index of the returned
+ * module in the context.
+ * @return Module matching the given key, NULL if no such module found.
+ */
+static const struct lys_module *
+ly_ctx_get_module_by_iter(const struct ly_ctx *ctx, const char *key, size_t key_offset, unsigned int *index)
+{
+    const struct lys_module *mod;
+    const char *value;
+
+    for (; *index < ctx->list.count; ++(*index)) {
+        mod = ctx->list.objs[*index];
+        if (mod->compiled) {
+            value = *(const char**)(((int8_t*)(mod->compiled)) + key_offset);
+        } else {
+            value = *(const char**)(((int8_t*)(mod->parsed)) + key_offset);
+        }
+        if (!strcmp(key, value)) {
+            /* increment index for the next run */
+            ++(*index);
+            return mod;
+        }
+    }
+    /* done */
+    return NULL;
+}
+
+/**
+ * @brief Unifying function for ly_ctx_get_module() and ly_ctx_get_module_ns()
+ * @param[in] ctx Context where to search.
+ * @param[in] key Name or Namespace as a search key.
+ * @param[in] key_offset Key's offset in struct lysp_module to get value from the context's modules to match with the key.
+ * @param[in] revision Revision date to match. If NULL, the matching module must have no revision. To search for the latest
+ * revision module, use ly_ctx_get_module_latest_by().
+ * @return Matching module if any.
+ */
+static const struct lys_module *
+ly_ctx_get_module_by(const struct ly_ctx *ctx, const char *key, size_t key_offset, const char *revision)
+{
+    const struct lys_module *mod;
+    unsigned int index = 0;
+
+    while ((mod = ly_ctx_get_module_by_iter(ctx, key, key_offset, &index))) {
+        if (!revision) {
+            if ((mod->compiled && !mod->compiled->revision) || (!mod->compiled && !mod->parsed->revs)) {
+                /* found requested module without revision */
+                return mod;
+            }
+        } else {
+            if ((mod->compiled && mod->compiled->revision && !strcmp(mod->compiled->revision, revision)) ||
+                    (!mod->compiled && mod->parsed->revs && !strcmp(mod->parsed->revs[0].date, revision))) {
+                /* found requested module of the specific revision */
+                return mod;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+API const struct lys_module *
+ly_ctx_get_module_ns(const struct ly_ctx *ctx, const char *ns, const char *revision)
+{
+    LY_CHECK_ARG_RET(ctx, ctx, ns, NULL);
+    return ly_ctx_get_module_by(ctx, ns, offsetof(struct lysp_module, ns), revision);
+}
+
+API const struct lys_module *
+ly_ctx_get_module(const struct ly_ctx *ctx, const char *name, const char *revision)
+{
+    LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
+    return ly_ctx_get_module_by(ctx, name, offsetof(struct lysp_module, name), revision);
+}
+
+/**
+ * @brief Unifying function for ly_ctx_get_module_latest() and ly_ctx_get_module_latest_ns()
+ * @param[in] ctx Context where to search.
+ * @param[in] key Name or Namespace as a search key.
+ * @param[in] key_offset Key's offset in struct lysp_module to get value from the context's modules to match with the key.
+ * @return Matching module if any.
+ */
+static const struct lys_module *
+ly_ctx_get_module_latest_by(const struct ly_ctx *ctx, const char *key, size_t key_offset)
+{
+    const struct lys_module *mod;
+    unsigned int index = 0;
+
+    while ((mod = ly_ctx_get_module_by_iter(ctx, key, key_offset, &index))) {
+        if ((mod->compiled && mod->compiled->latest_revision) || (!mod->compiled && mod->parsed->latest_revision)) {
+            return mod;
+        }
+    }
+
+    return NULL;
+}
+
+API const struct lys_module *
+ly_ctx_get_module_latest(const struct ly_ctx *ctx, const char *name)
+{
+    LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
+    return ly_ctx_get_module_latest_by(ctx, name, offsetof(struct lysp_module, name));
+}
+
+const struct lys_module *
+ly_ctx_get_module_latest_ns(const struct ly_ctx *ctx, const char *ns)
+{
+    LY_CHECK_ARG_RET(ctx, ctx, ns, NULL);
+    return ly_ctx_get_module_latest_by(ctx, ns, offsetof(struct lysp_module, ns));
+}
+
+/**
+ * @brief Unifying function for ly_ctx_get_module_implemented() and ly_ctx_get_module_implemented_ns()
+ * @param[in] ctx Context where to search.
+ * @param[in] key Name or Namespace as a search key.
+ * @param[in] key_offset Key's offset in struct lysp_module to get value from the context's modules to match with the key.
+ * @return Matching module if any.
+ */
+static const struct lys_module *
+ly_ctx_get_module_implemented_by(const struct ly_ctx *ctx, const char *key, size_t key_offset)
+{
+    const struct lys_module *mod;
+    unsigned int index = 0;
+
+    while ((mod = ly_ctx_get_module_by_iter(ctx, key, key_offset, &index))) {
+        if ((mod->compiled && mod->compiled->implemented) || (!mod->compiled && mod->parsed->implemented)) {
+            return mod;
+        }
+    }
+
+    return NULL;
+}
+
+API const struct lys_module *
+ly_ctx_get_module_implemented(const struct ly_ctx *ctx, const char *name)
+{
+    LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
+    return ly_ctx_get_module_implemented_by(ctx, name, offsetof(struct lysp_module, name));
+}
+
+API const struct lys_module *
+ly_ctx_get_module_implemented_ns(const struct ly_ctx *ctx, const char *ns)
+{
+    LY_CHECK_ARG_RET(ctx, ctx, ns, NULL);
+    return ly_ctx_get_module_implemented_by(ctx, ns, offsetof(struct lysp_module, ns));
+}
+
+struct lysp_module *
+ly_ctx_get_submodule(const struct ly_ctx *ctx, const char *module, const char *submodule, const char *revision)
+{
+    const struct lys_module *mod;
+    struct lysp_include *inc;
+    unsigned int v, u;
+
+    assert(submodule);
+
+    for (v = 0; v < ctx->list.count; ++v) {
+        mod = ctx->list.objs[v];
+        if (!mod->parsed) {
+            continue;
+        }
+        if (module && strcmp(module, mod->parsed->name)) {
+            continue;
+        }
+
+        LY_ARRAY_FOR(mod->parsed->includes, u) {
+            if (mod->parsed->includes[u].submodule && !strcmp(submodule, mod->parsed->includes[u].submodule->name)) {
+                inc = &mod->parsed->includes[u];
+                if (!revision) {
+                    if (inc->submodule->latest_revision) {
+                        return inc->submodule;
+                    }
+                } else if (inc->submodule->revs && !strcmp(revision, inc->submodule->revs[0].date)) {
+                    return inc->submodule;
+                }
+            }
+        }
+    }
+
+    return NULL;
+}
+
+API void
+ly_ctx_reset_latests(struct ly_ctx *ctx)
+{
+    unsigned int u,v ;
+    struct lys_module *mod;
+
+    for (u = 0; u < ctx->list.count; ++u) {
+        mod = ctx->list.objs[u];
+        if (mod->compiled && mod->compiled->latest_revision == 2) {
+            mod->compiled->latest_revision = 1;
+        }
+        if (mod->parsed) {
+            if (mod->parsed->latest_revision == 2) {
+                mod->parsed->latest_revision = 1;
+            }
+            if (mod->parsed->includes) {
+                for (v = 0; v < LY_ARRAY_SIZE(mod->parsed->includes); ++v) {
+                    if (mod->parsed->includes[v].submodule->latest_revision == 2) {
+                        mod->parsed->includes[v].submodule->latest_revision = 1;
+                    }
+                }
+            }
+        }
+    }
+}
+
+API void
 ly_ctx_destroy(struct ly_ctx *ctx, void (*private_destructor)(const struct lysc_node *node, void *priv))
 {
     if (!ctx) {
diff --git a/src/context.h b/src/context.h
index e907042..8bed61b 100644
--- a/src/context.h
+++ b/src/context.h
@@ -15,23 +15,293 @@
 #ifndef LY_CONTEXT_H_
 #define LY_CONTEXT_H_
 
-#include <pthread.h>
-
-#include "common.h"
-#include "set.h"
-#include "hash_table.h"
+#include "log.h"
 #include "tree_schema.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /**
- * @brief Context of the YANG schemas
+ * @defgroup context Context
+ * @{
+ *
+ * Structures and functions to manipulate with the libyang "containers". The \em context concept allows callers
+ * to work in environments with different sets of YANG schemas. More detailed information can be found at
+ * @ref howtocontext page.
  */
-struct ly_ctx {
-    struct dict_table dict;           /**< dictionary to effectively store strings used in the context related structures */
-    struct ly_set search_paths;       /**< set of directories where to search for schema's imports/includes */
-    struct ly_set list;               /**< set of YANG schemas */
-    uint16_t module_set_id;           /**< ID of the current set of schemas */
-    uint16_t flags;                   /**< context settings, see @ref contextoptions. */
-    pthread_key_t errlist_key;        /**< key for the thread-specific list of errors related to the context */
-};
+
+/**
+ * @struct ly_ctx
+ * @brief libyang context handler.
+ */
+struct ly_ctx;
+
+/**
+ * @defgroup contextoptions Context options
+ * @ingroup context
+ *
+ * Options to change context behavior.
+ * @{
+ */
+
+#define LY_CTX_ALLIMPLEMENTED 0x01 /**< All the imports of the schema being parsed are treated implemented. */
+#define LY_CTX_TRUSTED        0x02 /**< Handle the schema being parsed as trusted and skip its validation
+                                        tests. Note that while this option improves performance, it can
+                                        lead to an undefined behavior if the schema is not correct. */
+#define LY_CTX_NOYANGLIBRARY  0x04 /**< Do not internally implement ietf-yang-library module. The option
+                                        causes that function ly_ctx_info() does not work (returns NULL) until
+                                        the ietf-yang-library module is loaded manually. While any revision
+                                        of this schema can be loaded with this option, note that the only
+                                        revisions implemented by ly_ctx_info() are 2016-04-09 and 2018-01-17.
+                                        This option cannot be changed on existing context. */
+#define LY_CTX_DISABLE_SEARCHDIRS 0x08  /**< Do not search for schemas in context's searchdirs neither in current
+                                        working directory. It is entirely skipped and the only way to get
+                                        schema data for imports or for ly_ctx_load_module() is to use the
+                                        callbacks provided by caller via ly_ctx_set_module_imp_clb() */
+#define LY_CTX_DISABLE_SEARCHDIR_CWD 0x10 /**< Do not automatically search for schemas in current working
+                                        directory, which is by default searched automatically (despite not
+                                        recursively). */
+#define LY_CTX_PREFER_SEARCHDIRS 0x20 /**< When searching for schema, prefer searchdirs instead of user callback. */
+/**@} contextoptions */
+
+/**
+ * @brief Create libyang context.
+ *
+ * Context is used to hold all information about schemas. Usually, the application is supposed
+ * to work with a single context in which libyang is holding all schemas (and other internal
+ * information) according to which the data trees will be processed and validated. So, the schema
+ * trees are tightly connected with the specific context and they are held by the context internally
+ * - caller does not need to keep pointers to the schemas returned by lys_parse(), context knows
+ * about them. The data trees created with lyd_parse() are still connected with the specific context,
+ * but they are not internally held by the context. The data tree just points and lean on some data
+ * held by the context (schema tree, string dictionary, etc.). Therefore, in case of data trees, caller
+ * is supposed to keep pointers returned by the lyd_parse() and manage the data tree on its own. This
+ * also affects the number of instances of both tree types. While you can have only one instance of
+ * specific schema connected with a single context, number of data tree instances is not connected.
+ *
+ * @param[in] search_dir Directory where libyang will search for the imported or included modules
+ * and submodules. If no such directory is available, NULL is accepted.
+ * @param[in] options Context options, see @ref contextoptions.
+ * @param[out] new_ctx Pointer to the created libyang context if LY_SUCCESS returned.
+ * @return LY_ERR return value.
+ */
+LY_ERR ly_ctx_new(const char *search_dir, int options, struct ly_ctx **new_ctx);
+
+/**
+ * @brief Add the search path into libyang context
+ *
+ * To reset search paths set in the context, use ly_ctx_unset_searchdirs() and then
+ * set search paths again.
+ *
+ * @param[in] ctx Context to be modified.
+ * @param[in] search_dir New search path to add to the current paths previously set in ctx.
+ * @return LY_ERR return value.
+ */
+LY_ERR ly_ctx_set_searchdir(struct ly_ctx *ctx, const char *search_dir);
+
+/**
+ * @brief Clean the search path(s) from the libyang context
+ *
+ * @param[in] ctx Context to be modified.
+ * @param[in] value Searchdir to be removed, use NULL to remove them all.
+ * @return LY_ERR return value
+ */
+LY_ERR ly_ctx_unset_searchdirs(struct ly_ctx *ctx, const char *value);
+
+/**
+ * @brief Get the NULL-terminated list of the search paths in libyang context. Do not modify the result!
+ *
+ * @param[in] ctx Context to query.
+ * @return NULL-terminated list (array) of the search paths, NULL if no searchpath was set.
+ * Do not modify the provided data in any way!
+ */
+const char * const *ly_ctx_get_searchdirs(const struct ly_ctx *ctx);
+
+/**
+ * @brief Get the currently set context's options.
+ *
+ * @param[in] ctx Context to query.
+ * @return Combination of all the currently set context's options, see @ref contextoptions.
+ */
+int ly_ctx_get_options(const struct ly_ctx *ctx);
+
+/**
+ * @brief Set some of the context's options, see @ref contextoptions.
+ * @param[in] ctx Context to be modified.
+ * @param[in] option Combination of the context's options to be set, see @ref contextoptions.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_ctx_set_option(struct ly_ctx *ctx, int option);
+
+/**
+ * @brief Unset some of the context's options, see @ref contextoptions.
+ * @param[in] ctx Context to be modified.
+ * @param[in] option Combination of the context's options to be unset, see @ref contextoptions.
+ * @return LY_ERR value.
+ */
+LY_ERR ly_ctx_unset_option(struct ly_ctx *ctx, int option);
+
+/**
+ * @brief Get current ID of the modules set. The value is available also
+ * as module-set-id in ly_ctx_info() result.
+ *
+ * @param[in] ctx Context to be examined.
+ * @return Numeric identifier of the current context's modules set.
+ */
+uint16_t ly_ctx_get_module_set_id(const struct ly_ctx *ctx);
+
+/**
+ * @brief Callback for retrieving missing included or imported models in a custom way.
+ *
+ * When submod_name is provided, the submodule is requested instead of the module (in this case only
+ * the module name without its revision is provided).
+ *
+ * If an @arg free_module_data callback is provided, it will be used later to free the allegedly const data
+ * which were returned by this callback.
+ *
+ * @param[in] mod_name Missing module name.
+ * @param[in] mod_rev Optional missing module revision. If NULL and submod_name is not provided, the latest revision is
+ * requested, the parsed module is then marked by the latest_revision flag.
+ * @param[in] submod_name Optional missing submodule name.
+ * @param[in] submod_rev Optional missing submodule revision. If NULL and submod_name is provided, the latest revision is
+ * requested, the parsed submodule is then marked by the latest_revision flag.
+ * @param[in] user_data User-supplied callback data.
+ * @param[out] format Format of the returned module data.
+ * @param[out] module_data Requested module data.
+ * @param[out] free_module_data Callback for freeing the returned module data. If not set, the data will be left untouched.
+ * @return LY_ERR value. If the returned value differs from LY_SUCCESS, libyang continue in trying to get the module data
+ * according to the settings of its mechanism to search for the imported/included schemas.
+ */
+typedef LY_ERR (*ly_module_imp_clb)(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev,
+                                    void *user_data, LYS_INFORMAT *format, const char **module_data,
+                                    void (**free_module_data)(void *model_data, void *user_data));
+
+/**
+ * @brief Get the custom callback for missing import/include module retrieval.
+ *
+ * @param[in] ctx Context to read from.
+ * @param[in] user_data Optional pointer for getting the user-supplied callback data.
+ * @return Callback or NULL if not set.
+ */
+ly_module_imp_clb ly_ctx_get_module_imp_clb(const struct ly_ctx *ctx, void **user_data);
+
+/**
+ * @brief Set missing include or import module callback. It is meant to be used when the models
+ * are not locally available (such as when downloading modules from a NETCONF server), it should
+ * not be required in other cases.
+ *
+ * @param[in] ctx Context that will use this callback.
+ * @param[in] clb Callback responsible for returning the missing model.
+ * @param[in] user_data Arbitrary data that will always be passed to the callback \p clb.
+ */
+void ly_ctx_set_module_imp_clb(struct ly_ctx *ctx, ly_module_imp_clb clb, void *user_data);
+
+/**
+ * @brief Get YANG module of the given name and revision.
+ *
+ * @param[in] ctx Context to work in.
+ * @param[in] name Name of the YANG module to get.
+ * @param[in] revision Requested revision date of the YANG module to get. If not specified,
+ * the schema with no revision is returned, if it is present in the context.
+ * @return Pointer to the YANG module, NULL if no schema in the context follows the name and revision requirements.
+ */
+const struct lys_module *ly_ctx_get_module(const struct ly_ctx *ctx, const char *name, const char *revision);
+
+/**
+ * @brief Get the latest revision of the YANG module specified by its name.
+ *
+ * YANG modules with no revision are supposed to be the oldest one.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] name Name of the YANG module to get.
+ * @return The latest revision of the specified YANG module in the given context, NULL if no YANG module of the
+ * given name is present in the context.
+ */
+const struct lys_module *ly_ctx_get_module_latest(const struct ly_ctx *ctx, const char *name);
+
+/**
+ * @brief Get the (only) implemented YANG module specified by its name.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] name Name of the YANG module to get.
+ * @return The only implemented YANG module revision of the given name in the given context. NULL if there is no
+ * implemented module of the given name.
+ */
+const struct lys_module *ly_ctx_get_module_implemented(const struct ly_ctx *ctx, const char *name);
+
+/**
+ * @brief Get YANG module of the given namespace and revision.
+ *
+ * @param[in] ctx Context to work in.
+ * @param[in] ns Namespace of the YANG module to get.
+ * @param[in] revision Requested revision date of the YANG module to get. If not specified,
+ * the schema with no revision is returned, if it is present in the context.
+ * @return Pointer to the YANG module, NULL if no schema in the context follows the namespace and revision requirements.
+ */
+const struct lys_module *ly_ctx_get_module_ns(const struct ly_ctx *ctx, const char *ns, const char *revision);
+
+/**
+ * @brief Get the latest revision of the YANG module specified by its namespace.
+ *
+ * YANG modules with no revision are supposed to be the oldest one.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] ns Namespace of the YANG module to get.
+ * @return The latest revision of the specified YANG module in the given context, NULL if no YANG module of the
+ * given namespace is present in the context.
+ */
+const struct lys_module *ly_ctx_get_module_latest_ns(const struct ly_ctx *ctx, const char *ns);
+
+/**
+ * @brief Get the (only) implemented YANG module specified by its namespace.
+ *
+ * @param[in] ctx Context where to search.
+ * @param[in] ns Namespace of the YANG module to get.
+ * @return The only implemented YANG module revision of the given namespace in the given context. NULL if there is no
+ * implemented module of the given namespace.
+ */
+const struct lys_module *ly_ctx_get_module_implemented_ns(const struct ly_ctx *ctx, const char *ns);
+
+/**
+ * @brief Reset cached latest revision information of the schemas in the context.
+ *
+ * When a (sub)module is imported/included without revision, the latest revision is
+ * searched. libyang searches for the latest revision in searchdirs and/or via provided
+ * import callback ly_module_imp_clb() just once. Then it is expected that the content
+ * of searchdirs or data returned by the callback does not change. So when it changes,
+ * it is necessary to force searching for the latest revision in case of loading another
+ * module, which what this function does.
+ *
+ * The latest revision information is also reset when the searchdirs set changes via
+ * ly_ctx_set_searchdir().
+ *
+ * @param[in] ctx libyang context where the latest revision information is going to be reset.
+ */
+void ly_ctx_reset_latests(struct ly_ctx *ctx);
+
+/**
+ * @brief Free all internal structures of the specified context.
+ *
+ * The function should be used before terminating the application to destroy
+ * and free all structures internally used by libyang. If the caller uses
+ * multiple contexts, the function should be called for each used context.
+ *
+ * All instance data are supposed to be freed before destroying the context.
+ * Data models are destroyed automatically as part of ly_ctx_destroy() call.
+ *
+ * @param[in] ctx libyang context to destroy
+ * @param[in] private_destructor Optional destructor function for private objects assigned
+ * to the nodes via lys_set_private(). If NULL, the private objects are not freed by libyang.
+ * Remember the differences between the structures derived from ::lysc_node and always check
+ * ::lysc_node#nodetype.
+ */
+void ly_ctx_destroy(struct ly_ctx *ctx, void (*private_destructor)(const struct lysc_node *node, void *priv));
+
+/** @} context */
+
+#ifdef __cplusplus
+}
+#endif
 
 #endif /* LY_CONTEXT_H_ */
diff --git a/src/extensions.h b/src/extensions.h
new file mode 100644
index 0000000..588f63a
--- /dev/null
+++ b/src/extensions.h
@@ -0,0 +1,108 @@
+/**
+ * @file extesnions.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang support for YANG extensions implementation.
+ *
+ * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_EXTENSIONS_H_
+#define LY_EXTENSIONS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup extensions YANG Extensions
+ *
+ * @{
+ */
+
+/**
+ * @brief Extension instance structure parent enumeration
+ */
+typedef enum {
+    LYEXT_PAR_MODULE, /**< ::lys_module or ::lys_submodule */
+    LYEXT_PAR_NODE, /**< ::lys_node (and the derived structures) */
+    LYEXT_PAR_TPDF, /**< ::lys_tpdf */
+    LYEXT_PAR_TYPE, /**< ::lys_type */
+    LYEXT_PAR_TYPE_BIT, /**< ::lys_type_bit */
+    LYEXT_PAR_TYPE_ENUM, /**< ::lys_type_enum */
+    LYEXT_PAR_FEATURE, /**< ::lys_feature */
+    LYEXT_PAR_RESTR, /**< ::lys_restr - YANG's must, range, length and pattern statements */
+    LYEXT_PAR_WHEN, /**< ::lys_when */
+    LYEXT_PAR_IDENT, /**< ::lys_ident */
+    LYEXT_PAR_EXT, /**< ::lys_ext */
+    LYEXT_PAR_EXTINST, /**< ::lys_ext_instance */
+    LYEXT_PAR_REFINE, /**< ::lys_refine */
+    LYEXT_PAR_DEVIATION, /**< ::lys_deviation */
+    LYEXT_PAR_DEVIATE, /**< ::lys_deviate */
+    LYEXT_PAR_IMPORT, /**< ::lys_import */
+    LYEXT_PAR_INCLUDE,           /**< ::lysp_include */
+    LYEXT_PAR_REVISION,          /**< ::lysc_revision */
+} LYEXT_PARENT;
+
+/**
+ * @brief Enum of substatements in which extension instances can appear.
+ */
+typedef enum {
+    LYEXT_SUBSTMT_SELF = 0,      /**< extension of the structure itself, not substatement's */
+    LYEXT_SUBSTMT_ARGUMENT,      /**< extension of the argument statement, can appear in lys_ext */
+    LYEXT_SUBSTMT_BASE,          /**< extension of the base statement, can appear (repeatedly) in lys_type and lys_ident */
+    LYEXT_SUBSTMT_BELONGSTO,     /**< extension of the belongs-to statement, can appear in lys_submodule */
+    LYEXT_SUBSTMT_CONTACT,       /**< extension of the contact statement, can appear in lys_module */
+    LYEXT_SUBSTMT_DEFAULT,       /**< extension of the default statement, can appear in lys_node_leaf, lys_node_leaflist,
+                                      lys_node_choice and lys_deviate */
+    LYEXT_SUBSTMT_DESCRIPTION,   /**< extension of the description statement, can appear in lys_module, lys_submodule,
+                                      lys_node, lys_import, lys_include, lys_ext, lys_feature, lys_tpdf, lys_restr,
+                                      lys_ident, lys_deviation, lys_type_enum, lys_type_bit, lys_when and lys_revision */
+    LYEXT_SUBSTMT_ERRTAG,        /**< extension of the error-app-tag statement, can appear in lys_restr */
+    LYEXT_SUBSTMT_ERRMSG,        /**< extension of the error-message statement, can appear in lys_restr */
+    LYEXT_SUBSTMT_KEY,           /**< extension of the key statement, can appear in lys_node_list */
+    LYEXT_SUBSTMT_NAMESPACE,     /**< extension of the namespace statement, can appear in lys_module */
+    LYEXT_SUBSTMT_ORGANIZATION,  /**< extension of the organization statement, can appear in lys_module and lys_submodule */
+    LYEXT_SUBSTMT_PATH,          /**< extension of the path statement, can appear in lys_type */
+    LYEXT_SUBSTMT_PREFIX,        /**< extension of the prefix statement, can appear in lys_module, lys_submodule (for
+                                      belongs-to's prefix) and lys_import */
+    LYEXT_SUBSTMT_PRESENCE,      /**< extension of the presence statement, can appear in lys_node_container */
+    LYEXT_SUBSTMT_REFERENCE,     /**< extension of the reference statement, can appear in lys_module, lys_submodule,
+                                      lys_node, lys_import, lys_include, lys_revision, lys_tpdf, lys_restr, lys_ident,
+                                      lys_ext, lys_feature, lys_deviation, lys_type_enum, lys_type_bit and lys_when */
+    LYEXT_SUBSTMT_REVISIONDATE,  /**< extension of the revision-date statement, can appear in lys_import and lys_include */
+    LYEXT_SUBSTMT_UNITS,         /**< extension of the units statement, can appear in lys_tpdf, lys_node_leaf,
+                                      lys_node_leaflist and lys_deviate */
+    LYEXT_SUBSTMT_VALUE,         /**< extension of the value statement, can appear in lys_type_enum */
+    LYEXT_SUBSTMT_VERSION,       /**< extension of the yang-version statement, can appear in lys_module and lys_submodule */
+    LYEXT_SUBSTMT_MODIFIER,      /**< extension of the modifier statement, can appear in lys_restr */
+    LYEXT_SUBSTMT_REQINSTANCE,   /**< extension of the require-instance statement, can appear in lys_type */
+    LYEXT_SUBSTMT_YINELEM,       /**< extension of the yin-element statement, can appear in lys_ext */
+    LYEXT_SUBSTMT_CONFIG,        /**< extension of the config statement, can appear in lys_node and lys_deviate */
+    LYEXT_SUBSTMT_MANDATORY,     /**< extension of the mandatory statement, can appear in lys_node_leaf, lys_node_choice,
+                                      lys_node_anydata and lys_deviate */
+    LYEXT_SUBSTMT_ORDEREDBY,     /**< extension of the ordered-by statement, can appear in lys_node_list and lys_node_leaflist */
+    LYEXT_SUBSTMT_STATUS,        /**< extension of the status statement, can appear in lys_tpdf, lys_node, lys_ident,
+                                      lys_ext, lys_feature, lys_type_enum and lys_type_bit */
+    LYEXT_SUBSTMT_FRACDIGITS,    /**< extension of the fraction-digits statement, can appear in lys_type */
+    LYEXT_SUBSTMT_MAX,           /**< extension of the max-elements statement, can appear in lys_node_list,
+                                      lys_node_leaflist and lys_deviate */
+    LYEXT_SUBSTMT_MIN,           /**< extension of the min-elements statement, can appear in lys_node_list,
+                                      lys_node_leaflist and lys_deviate */
+    LYEXT_SUBSTMT_POSITION,      /**< extension of the position statement, can appear in lys_type_bit */
+    LYEXT_SUBSTMT_UNIQUE,        /**< extension of the unique statement, can appear in lys_node_list and lys_deviate */
+    LYEXT_SUBSTMT_IFFEATURE,     /**< extension of the if-feature statement */
+} LYEXT_SUBSTMT;
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_TREE_SCHEMA_H_ */
diff --git a/src/hash_table.c b/src/hash_table.c
index a60c3da..11ca8f3 100644
--- a/src/hash_table.c
+++ b/src/hash_table.c
@@ -83,28 +83,6 @@
 }
 
 /*
- * Bob Jenkin's one-at-a-time hash
- * http://www.burtleburtle.net/bob/hash/doobs.html
- *
- * Spooky hash is faster, but it works only for little endian architectures.
- */
-static uint32_t
-dict_hash(const char *key, size_t len)
-{
-    uint32_t hash, i;
-
-    for (hash = i = 0; i < len; ++i) {
-        hash += key[i];
-        hash += (hash << 10);
-        hash ^= (hash >> 6);
-    }
-    hash += (hash << 3);
-    hash ^= (hash >> 11);
-    hash += (hash << 15);
-    return hash;
-}
-
-/*
  * Usage:
  * - init hash to 0
  * - repeatedly call dict_hash_multi(), provide hash from the last call
@@ -130,6 +108,21 @@
     return hash;
 }
 
+/*
+ * Bob Jenkin's one-at-a-time hash
+ * http://www.burtleburtle.net/bob/hash/doobs.html
+ *
+ * Spooky hash is faster, but it works only for little endian architectures.
+ */
+uint32_t
+dict_hash(const char *key, size_t len)
+{
+    uint32_t hash;
+
+    hash = dict_hash_multi(0, key, len);
+    return dict_hash_multi(hash, NULL, len);
+}
+
 API void
 lydict_remove(struct ly_ctx *ctx, const char *value)
 {
@@ -139,8 +132,6 @@
     struct dict_rec rec, *match = NULL;
     char *val_p;
 
-    LY_CHECK_ARG_RET(ctx, ctx, value,);
-
     if (!value) {
         return;
     }
diff --git a/src/hash_table.h b/src/hash_table.h
index 55a0158..70ec695 100644
--- a/src/hash_table.h
+++ b/src/hash_table.h
@@ -32,6 +32,11 @@
  */
 uint32_t dict_hash_multi(uint32_t hash, const char *key_part, size_t len);
 
+/*
+ * @brief Compute hash from a string.
+ */
+uint32_t dict_hash(const char *key, size_t len);
+
 /**
  * @brief Callback for checking hash table values equivalence.
  *
diff --git a/src/libyang.h b/src/libyang.h
index 060d49a..3c001fc 100644
--- a/src/libyang.h
+++ b/src/libyang.h
@@ -21,6 +21,12 @@
 extern "C" {
 #endif
 
+#include "log.h"
+#include "set.h"
+#include "dict.h"
+#include "context.h"
+#include "tree_schema.h"
+
 /**
  * @mainpage About
  *
@@ -97,167 +103,6 @@
  * The lists are structures connected via a `next` pointer. Iterating over the siblings can be simply done by ::LY_LIST_FOR macro.
  */
 
-/**
- * @defgroup context Context
- * @{
- *
- * Structures and functions to manipulate with the libyang "containers". The \em context concept allows callers
- * to work in environments with different sets of YANG schemas. More detailed information can be found at
- * @ref howtocontext page.
- */
-
-/**
- * @struct ly_ctx
- * @brief libyang context handler.
- */
-struct ly_ctx;
-
-/**@} context */
-
-#include "log.h"
-#include "set.h"
-#include "dict.h"
-#include "tree_schema.h"
-
-/**
- * @ingroup context
- * @{
- */
-
-/**
- * @defgroup contextoptions Context options
- * @ingroup context
- *
- * Options to change context behavior.
- * @{
- */
-
-#define LY_CTX_ALLIMPLEMENTED 0x01 /**< All the imports of the schema being parsed are treated implemented. */
-#define LY_CTX_TRUSTED        0x02 /**< Handle the schema being parsed as trusted and skip its validation
-                                        tests. Note that while this option improves performance, it can
-                                        lead to an undefined behavior if the schema is not correct. */
-#define LY_CTX_NOYANGLIBRARY  0x04 /**< Do not internally implement ietf-yang-library module. The option
-                                        causes that function ly_ctx_info() does not work (returns NULL) until
-                                        the ietf-yang-library module is loaded manually. While any revision
-                                        of this schema can be loaded with this option, note that the only
-                                        revisions implemented by ly_ctx_info() are 2016-04-09 and 2018-01-17.
-                                        This option cannot be changed on existing context. */
-#define LY_CTX_DISABLE_SEARCHDIRS 0x08  /**< Do not search for schemas in context's searchdirs neither in current
-                                        working directory. It is entirely skipped and the only way to get
-                                        schema data for imports or for ly_ctx_load_module() is to use the
-                                        callbacks provided by caller via ly_ctx_set_module_imp_clb() */
-#define LY_CTX_DISABLE_SEARCHDIR_CWD 0x10 /**< Do not automatically search for schemas in current working
-                                        directory, which is by default searched automatically (despite not
-                                        recursively). */
-#define LY_CTX_PREFER_SEARCHDIRS 0x20 /**< When searching for schema, prefer searchdirs instead of user callback. */
-/**@} contextoptions */
-
-/**
- * @brief Create libyang context.
- *
- * Context is used to hold all information about schemas. Usually, the application is supposed
- * to work with a single context in which libyang is holding all schemas (and other internal
- * information) according to which the data trees will be processed and validated. So, the schema
- * trees are tightly connected with the specific context and they are held by the context internally
- * - caller does not need to keep pointers to the schemas returned by lys_parse(), context knows
- * about them. The data trees created with lyd_parse() are still connected with the specific context,
- * but they are not internally held by the context. The data tree just points and lean on some data
- * held by the context (schema tree, string dictionary, etc.). Therefore, in case of data trees, caller
- * is supposed to keep pointers returned by the lyd_parse() and manage the data tree on its own. This
- * also affects the number of instances of both tree types. While you can have only one instance of
- * specific schema connected with a single context, number of data tree instances is not connected.
- *
- * @param[in] search_dir Directory where libyang will search for the imported or included modules
- * and submodules. If no such directory is available, NULL is accepted.
- * @param[in] options Context options, see @ref contextoptions.
- * @param[out] new_ctx Pointer to the created libyang context if LY_SUCCESS returned.
- * @return LY_ERR return value.
- */
-LY_ERR ly_ctx_new(const char *search_dir, int options, struct ly_ctx **new_ctx);
-
-/**
- * @brief Add the search path into libyang context
- *
- * To reset search paths set in the context, use ly_ctx_unset_searchdirs() and then
- * set search paths again.
- *
- * @param[in] ctx Context to be modified.
- * @param[in] search_dir New search path to add to the current paths previously set in ctx.
- * @return LY_ERR return value.
- */
-LY_ERR ly_ctx_set_searchdir(struct ly_ctx *ctx, const char *search_dir);
-
-/**
- * @brief Clean the search path(s) from the libyang context
- *
- * @param[in] ctx Context to be modified.
- * @param[in] value Searchdir to be removed, use NULL to remove them all.
- * @return LY_ERR return value
- */
-LY_ERR ly_ctx_unset_searchdirs(struct ly_ctx *ctx, const char *value);
-
-/**
- * @brief Get the NULL-terminated list of the search paths in libyang context. Do not modify the result!
- *
- * @param[in] ctx Context to query.
- * @return NULL-terminated list (array) of the search paths, NULL if no searchpath was set.
- * Do not modify the provided data in any way!
- */
-const char * const *ly_ctx_get_searchdirs(const struct ly_ctx *ctx);
-
-/**
- * @brief Get the currently set context's options.
- *
- * @param[in] ctx Context to query.
- * @return Combination of all the currently set context's options, see @ref contextoptions.
- */
-int ly_ctx_get_options(const struct ly_ctx *ctx);
-
-/**
- * @brief Set some of the context's options, see @ref contextoptions.
- * @param[in] ctx Context to be modified.
- * @param[in] option Combination of the context's options to be set, see @ref contextoptions.
- * @return LY_ERR value.
- */
-LY_ERR ly_ctx_set_option(struct ly_ctx *ctx, int option);
-
-/**
- * @brief Unset some of the context's options, see @ref contextoptions.
- * @param[in] ctx Context to be modified.
- * @param[in] option Combination of the context's options to be unset, see @ref contextoptions.
- * @return LY_ERR value.
- */
-LY_ERR ly_ctx_unset_option(struct ly_ctx *ctx, int option);
-
-/**
- * @brief Get current ID of the modules set. The value is available also
- * as module-set-id in ly_ctx_info() result.
- *
- * @param[in] ctx Context to be examined.
- * @return Numeric identifier of the current context's modules set.
- */
-uint16_t ly_ctx_get_module_set_id(const struct ly_ctx *ctx);
-
-/**
- * @brief Free all internal structures of the specified context.
- *
- * The function should be used before terminating the application to destroy
- * and free all structures internally used by libyang. If the caller uses
- * multiple contexts, the function should be called for each used context.
- *
- * All instance data are supposed to be freed before destroying the context.
- * Data models are destroyed automatically as part of ly_ctx_destroy() call.
- *
- * @param[in] ctx libyang context to destroy
- * @param[in] private_destructor Optional destructor function for private objects assigned
- * to the nodes via lys_set_private(). If NULL, the private objects are not freed by libyang.
- * Remember the differences between the structures derived from ::lysc_node and always check
- * ::lysc_node#nodetype.
- */
-void ly_ctx_destroy(struct ly_ctx *ctx, void (*private_destructor)(const struct lysc_node *node, void *priv));
-
-/** @} context */
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/log.c b/src/log.c
index 18d0b38..b576437 100644
--- a/src/log.c
+++ b/src/log.c
@@ -12,14 +12,14 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _GNU_SOURCE
+#include "common.h"
+
 #include <assert.h>
 #include <inttypes.h>
 #include <stdarg.h>
 #include <stdio.h>
 
 #include "libyang.h"
-#include "common.h"
 #include "context.h"
 
 THREAD_LOCAL enum int_log_opts log_opt;
@@ -34,6 +34,19 @@
 /* how many bytes add when enlarging buffers */
 #define LY_BUF_STEP 128
 
+API LY_ERR
+ly_errcode(const struct ly_ctx *ctx)
+{
+    struct ly_err_item *i;
+
+    i = ly_err_first(ctx);
+    if (i) {
+        return i->prev->no;
+    }
+
+    return LY_SUCCESS;
+}
+
 API LY_VECODE
 ly_vecode(const struct ly_ctx *ctx)
 {
diff --git a/src/log.h b/src/log.h
index 95232bc..d8b0a04 100644
--- a/src/log.h
+++ b/src/log.h
@@ -146,6 +146,7 @@
     LY_ESYS,        /**< System call failure */
     LY_EINVAL,      /**< Invalid value */
     LY_EEXIST,      /**< Item already exists */
+    LY_ENOTFOUND,   /**< Item does not exists */
     LY_EINT,        /**< Internal error */
     LY_EVALID,      /**< Validation failure */
     LY_EPLUGIN,     /**< Error reported by a plugin */
@@ -163,6 +164,7 @@
     LYVE_SYNTAX,       /**< generic syntax error */
     LYVE_SYNTAX_YANG,  /**< YANG-related syntax error */
     LYVE_REFERENCE,    /**< invalid referencing or using an item */
+    LYVE_XPATH,        /**< invalid XPath expression */
     LYVE_SEMANTICS     /**< generic semantic error */
 } LY_VECODE;
 
@@ -191,6 +193,14 @@
 LY_VECODE ly_vecode(const struct ly_ctx *ctx);
 
 /**
+ * @brief Get the last (thread, context-specific) error code.
+ *
+ * @param[in] ctx Relative context.
+ * @return LY_ERR value of the last error code.
+ */
+LY_ERR ly_errcode(const struct ly_ctx *ctx);
+
+/**
  * @brief Get the last (thread, context-specific) error message. If the coresponding module defined
  * a specific error message, it will be used instead the default one.
  *
diff --git a/src/parser_yang.c b/src/parser_yang.c
index 434073d..9eadc37 100644
--- a/src/parser_yang.c
+++ b/src/parser_yang.c
@@ -41,10 +41,15 @@
         (c >= 0xd0000 && c <= 0xdfffd) || (c >= 0xe0000 && c <= 0xefffd) || \
         (c >= 0xf0000 && c <= 0xffffd) || (c >= 0x100000 && c <= 0x10fffd))
 
-/* These 2 macros checks YANG's identifier grammar rule */
-#define is_yangidentstartchar(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')
-#define is_yangidentchar(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || \
-        c == '_' || c == '-' || c == '.')
+#define CHECK_UNIQUENESS(CTX, ARRAY, MEMBER, STMT, IDENT) \
+    if (ARRAY) { \
+        for (unsigned int u = 0; u < LY_ARRAY_SIZE(ARRAY) - 1; ++u) { \
+            if (!strcmp((ARRAY)[u].MEMBER, IDENT)) { \
+                LOGVAL_YANG(CTX, LY_VCODE_DUPIDENT, IDENT, STMT); \
+                return LY_EVALID; \
+            } \
+        } \
+    }
 
 #define INSERT_WORD(CTX, BUF, TARGET, WORD, LEN) \
     if (BUF) {(TARGET) = lydict_insert_zc((CTX)->ctx, WORD);}\
@@ -52,8 +57,6 @@
 
 #define MOVE_INPUT(CTX, DATA, COUNT) (*(data))+=COUNT;(CTX)->indent+=COUNT
 
-#define LOGVAL_YANG(CTX, ...) LOGVAL((CTX)->ctx, LY_VLOG_LINE, &(CTX)->line, __VA_ARGS__)
-
 /**
  * @brief Loop through all substatements providing, return if there are none.
  *
@@ -66,12 +69,11 @@
  *
  * @return In case there are no substatements or a fatal error encountered.
  */
-#define YANG_READ_SUBSTMT_FOR(CTX, DATA, KW, WORD, WORD_LEN, ERR) \
-    ERR = get_keyword(CTX, DATA, &KW, &WORD, &WORD_LEN); \
-    LY_CHECK_RET(ERR); \
-    \
+#define YANG_READ_SUBSTMT_FOR(CTX, DATA, KW, WORD, WORD_LEN, ERR, CHECKGOTO) \
+    LY_CHECK_RET(get_keyword(CTX, DATA, &KW, &WORD, &WORD_LEN)); \
     if (KW == YANG_SEMICOLON) { \
-        return ERR; \
+        CHECKGOTO; \
+        return LY_SUCCESS; \
     } \
     if (KW != YANG_LEFT_BRACE) { \
         LOGVAL_YANG(CTX, LYVE_SYNTAX_YANG, "Invalid keyword \"%s\", expected \";\" or \"{\".", ly_stmt2str(KW)); \
@@ -81,12 +83,15 @@
             !ERR && (KW != YANG_RIGHT_BRACE); \
             ERR = get_keyword(CTX, DATA, &KW, &WORD, &WORD_LEN))
 
-static LY_ERR parse_container(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings);
-static LY_ERR parse_uses(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings);
-static LY_ERR parse_choice(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings);
-static LY_ERR parse_case(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings);
-static LY_ERR parse_list(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings);
-static LY_ERR parse_grouping(struct ly_parser_ctx *ctx, const char **data, struct lysp_grp **groupings);
+#define YANG_CHECK_STMTVER_RET(CTX, KW, PARENT) \
+    if ((CTX)->mod->version < 2) {LOGVAL_YANG((CTX), LY_VCODE_INCHILDSTMT2, KW, PARENT); return LY_EVALID;}
+
+static LY_ERR parse_container(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+static LY_ERR parse_uses(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+static LY_ERR parse_choice(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+static LY_ERR parse_case(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+static LY_ERR parse_list(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
+static LY_ERR parse_grouping(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_grp **groupings);
 
 /**
  * @brief Add another character to dynamic buffer, a low-level function.
@@ -539,7 +544,6 @@
 get_argument(struct ly_parser_ctx *ctx, const char **data, enum yang_arg arg, char **word_p, char **word_b, size_t *word_len)
 {
     size_t buf_len = 0;
-    LY_ERR ret;
 
     /* word buffer - dynamically allocated */
     *word_b = NULL;
@@ -558,23 +562,21 @@
                             "unquoted string character, optsep, semicolon or opening brace");
                 return LY_EVALID;
             }
-            ret = read_qstring(ctx, data, arg, word_p, word_b, word_len, &buf_len);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(read_qstring(ctx, data, arg, word_p, word_b, word_len, &buf_len));
             goto str_end;
         case '/':
             if ((*data)[1] == '/') {
                 /* one-line comment */
                 MOVE_INPUT(ctx, data, 2);
-                ret = skip_comment(ctx, data, 1);
+                LY_CHECK_RET(skip_comment(ctx, data, 1));
             } else if ((*data)[1] == '*') {
                 /* block comment */
                 MOVE_INPUT(ctx, data, 2);
-                ret = skip_comment(ctx, data, 2);
+                LY_CHECK_RET(skip_comment(ctx, data, 2));
             } else {
                 /* not a comment after all */
-                ret = buf_store_char(ctx, data, arg, word_p, word_len, word_b, &buf_len, 0);
+                LY_CHECK_RET(buf_store_char(ctx, data, arg, word_p, word_len, word_b, &buf_len, 0));
             }
-            LY_CHECK_RET(ret);
             break;
         case ' ':
             if (*word_len) {
@@ -652,7 +654,6 @@
 static LY_ERR
 get_keyword(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword *kw, char **word_p, size_t *word_len)
 {
-    LY_ERR ret;
     int prefix;
     const char *word_start;
     unsigned int c;
@@ -670,17 +671,11 @@
             if ((*data)[1] == '/') {
                 /* one-line comment */
                 MOVE_INPUT(ctx, data, 2);
-                ret = skip_comment(ctx, data, 1);
-                if (ret) {
-                    return ret;
-                }
+                LY_CHECK_RET(skip_comment(ctx, data, 1));
             } else if ((*data)[1] == '*') {
                 /* block comment */
                 MOVE_INPUT(ctx, data, 2);
-                ret = skip_comment(ctx, data, 2);
-                if (ret) {
-                    return ret;
-                }
+                LY_CHECK_RET(skip_comment(ctx, data, 2));
             } else {
                 /* error - not a comment after all, keyword cannot start with slash */
                 LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Invalid identifier first character '/'.");
@@ -973,7 +968,7 @@
                   struct lysp_stmt **child)
 {
     char *buf;
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     enum yang_keyword kw;
     struct lysp_stmt *stmt, *par_child;
 
@@ -983,8 +978,7 @@
     stmt->stmt = lydict_insert(ctx->ctx, word, word_len);
 
     /* get optional argument */
-    ret = get_argument(ctx, data, Y_MAYBE_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_MAYBE_STR_ARG, &word, &buf, &word_len));
 
     if (word) {
         if (buf) {
@@ -1002,14 +996,9 @@
         par_child->next = stmt;
     }
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
-        ret = parse_ext_substmt(ctx, data, word, word_len, &stmt->child);
-        LY_CHECK_RET(ret);
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, ) {
+        LY_CHECK_RET(parse_ext_substmt(ctx, data, word, word_len, &stmt->child));
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -1030,7 +1019,7 @@
 parse_ext(struct ly_parser_ctx *ctx, const char **data, const char *ext_name, int ext_name_len, LYEXT_SUBSTMT insubstmt,
           uint32_t insubstmt_index, struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     struct lysp_ext_instance *e;
@@ -1044,21 +1033,15 @@
     e->insubstmt_index = insubstmt_index;
 
     /* get optional argument */
-    ret = get_argument(ctx, data, Y_MAYBE_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_MAYBE_STR_ARG, &word, &buf, &word_len));
 
     if (word) {
         INSERT_WORD(ctx, buf, e->argument, word, word_len);
     }
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
-        ret = parse_ext_substmt(ctx, data, word, word_len, &e->child);
-        LY_CHECK_RET(ret);
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
+        LY_CHECK_RET(parse_ext_substmt(ctx, data, word, word_len, &e->child));
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -1080,7 +1063,7 @@
 parse_text_field(struct ly_parser_ctx *ctx, const char **data, LYEXT_SUBSTMT substmt, uint32_t substmt_index,
                  const char **value, enum yang_arg arg, struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -1091,27 +1074,21 @@
     }
 
     /* get value */
-    ret = get_argument(ctx, data, arg, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, arg, &word, &buf, &word_len));
 
     /* store value and spend buf if allocated */
     INSERT_WORD(ctx, buf, *value, word, word_len);
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, substmt, substmt_index, exts);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, substmt, substmt_index, exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), lyext_substmt2str(substmt));
             return LY_EVALID;
         }
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -1127,7 +1104,7 @@
 static LY_ERR
 parse_yangversion(struct ly_parser_ctx *ctx, const char **data, struct lysp_module *mod)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -1138,8 +1115,7 @@
     }
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     if ((word_len == 3) && !strncmp(word, "1.0", word_len)) {
         mod->version = LYS_VERSION_1_0;
@@ -1152,21 +1128,16 @@
     }
     free(buf);
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_VERSION, 0, &mod->exts);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_VERSION, 0, &mod->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "yang-version");
             return LY_EVALID;
         }
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -1184,7 +1155,7 @@
 static LY_ERR
 parse_belongsto(struct ly_parser_ctx *ctx, const char **data, const char **belongsto, const char **prefix, struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -1195,34 +1166,29 @@
     }
 
     /* get value */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
 
     INSERT_WORD(ctx, buf, *belongsto, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
         switch (kw) {
         case YANG_PREFIX:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PREFIX, 0, prefix, Y_IDENTIF_ARG, exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_PREFIX, 0, prefix, Y_IDENTIF_ARG, exts));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_BELONGSTO, 0, exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_BELONGSTO, 0, exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "belongs-to");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
     LY_CHECK_RET(ret);
-
+checks:
     /* mandatory substatements */
     if (!*prefix) {
         LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "prefix", "belongs-to");
         return LY_EVALID;
     }
-
     return ret;
 }
 
@@ -1239,7 +1205,7 @@
 static LY_ERR
 parse_revisiondate(struct ly_parser_ctx *ctx, const char **data, char *rev, struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -1250,11 +1216,10 @@
     }
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     /* check value */
-    if (lysp_check_date(ctx->ctx, word, word_len, "revision-date")) {
+    if (lysp_check_date(ctx, word, word_len, "revision-date")) {
         free(buf);
         return LY_EVALID;
     }
@@ -1263,21 +1228,16 @@
     strncpy(rev, word, word_len);
     free(buf);
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_REVISIONDATE, 0, exts);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_REVISIONDATE, 0, exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "revision-date");
             return LY_EVALID;
         }
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -1291,45 +1251,49 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_include(struct ly_parser_ctx *ctx, const char **data, struct lysp_include **includes)
+parse_include(struct ly_parser_ctx *ctx, const char **data, struct lysp_module *mod)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
     struct lysp_include *inc;
 
-    LY_ARRAY_NEW_RET(ctx->ctx, *includes, inc, LY_EMEM);
+    LY_ARRAY_NEW_RET(ctx->ctx, mod->includes, inc, LY_EMEM);
 
     /* get value */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
 
     INSERT_WORD(ctx, buf, inc->name, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
 
+    /* submodules share the namespace with the module names, so there must not be
+     * a module of the same name in the context, no need for revision matching */
+    if (!strcmp(ctx->mod->name, inc->name) || ly_ctx_get_module_latest(ctx->ctx, inc->name)) {
+        LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Name collision between module and submodule of name \"%s\".", inc->name);
+        return LY_EVALID;
+    }
+
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &inc->dsc, Y_STR_ARG, &inc->exts);
+            YANG_CHECK_STMTVER_RET(ctx, "description", "include");
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &inc->dsc, Y_STR_ARG, &inc->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &inc->ref, Y_STR_ARG, &inc->exts);
+            YANG_CHECK_STMTVER_RET(ctx, "reference", "include");
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &inc->ref, Y_STR_ARG, &inc->exts));
             break;
         case YANG_REVISION_DATE:
-            ret = parse_revisiondate(ctx, data, inc->rev, &inc->exts);
+            LY_CHECK_RET(parse_revisiondate(ctx, data, inc->rev, &inc->exts));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &inc->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &inc->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "include");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -1345,7 +1309,7 @@
 static LY_ERR
 parse_import(struct ly_parser_ctx *ctx, const char **data, struct lysp_module *module)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -1354,43 +1318,38 @@
     LY_ARRAY_NEW_RET(ctx->ctx, module->imports, imp, LY_EVALID);
 
     /* get value */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, imp->name, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
 
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
         switch (kw) {
         case YANG_PREFIX:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PREFIX, 0, &imp->prefix, Y_IDENTIF_ARG, &imp->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_PREFIX, 0, &imp->prefix, Y_IDENTIF_ARG, &imp->exts));
             LY_CHECK_RET(lysp_check_prefix(ctx, module, &imp->prefix), LY_EVALID);
             break;
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &imp->dsc, Y_STR_ARG, &imp->exts);
+            YANG_CHECK_STMTVER_RET(ctx, "description", "import");
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &imp->dsc, Y_STR_ARG, &imp->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &imp->ref, Y_STR_ARG, &imp->exts);
+            YANG_CHECK_STMTVER_RET(ctx, "reference", "import");
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &imp->ref, Y_STR_ARG, &imp->exts));
             break;
         case YANG_REVISION_DATE:
-            ret = parse_revisiondate(ctx, data, imp->rev, &imp->exts);
+            LY_CHECK_RET(parse_revisiondate(ctx, data, imp->rev, &imp->exts));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &imp->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &imp->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "import");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
     LY_CHECK_RET(ret);
-
+checks:
     /* mandatory substatements */
-    if (!imp->prefix) {
-        LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "prefix", "import");
-        return LY_EVALID;
-    }
+    LY_CHECK_ERR_RET(!imp->prefix, LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "prefix", "import"), LY_EVALID);
 
     return ret;
 }
@@ -1407,7 +1366,7 @@
 static LY_ERR
 parse_revision(struct ly_parser_ctx *ctx, const char **data, struct lysp_revision **revs)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -1416,38 +1375,32 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *revs, rev, LY_EMEM);
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     /* check value */
-    if (lysp_check_date(ctx->ctx, word, word_len, "revision")) {
+    if (lysp_check_date(ctx, word, word_len, "revision")) {
         return LY_EVALID;
     }
 
-    strncpy(rev->rev, word, word_len);
+    strncpy(rev->date, word, word_len);
     free(buf);
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &rev->dsc, Y_STR_ARG, &rev->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &rev->dsc, Y_STR_ARG, &rev->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &rev->ref, Y_STR_ARG, &rev->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &rev->ref, Y_STR_ARG, &rev->exts));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &rev->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &rev->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "revision");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -1467,7 +1420,7 @@
 parse_text_fields(struct ly_parser_ctx *ctx, const char **data, LYEXT_SUBSTMT substmt, const char ***texts, enum yang_arg arg,
                   struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     const char **item;
     size_t word_len;
@@ -1477,25 +1430,19 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *texts, item, LY_EMEM);
 
     /* get value */
-    ret = get_argument(ctx, data, arg, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, arg, &word, &buf, &word_len));
 
     INSERT_WORD(ctx, buf, *item, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, substmt, LY_ARRAY_SIZE(*texts) - 1, exts);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, substmt, LY_ARRAY_SIZE(*texts) - 1, exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), lyext_substmt2str(substmt));
             return LY_EVALID;
         }
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -1512,7 +1459,7 @@
 static LY_ERR
 parse_config(struct ly_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -1523,8 +1470,7 @@
     }
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     if ((word_len == 4) && !strncmp(word, "true", word_len)) {
         *flags |= LYS_CONFIG_W;
@@ -1537,21 +1483,16 @@
     }
     free(buf);
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_CONFIG, 0, exts);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_CONFIG, 0, exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "config");
             return LY_EVALID;
         }
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -1568,7 +1509,7 @@
 static LY_ERR
 parse_mandatory(struct ly_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -1579,8 +1520,7 @@
     }
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     if ((word_len == 4) && !strncmp(word, "true", word_len)) {
         *flags |= LYS_MAND_TRUE;
@@ -1593,21 +1533,16 @@
     }
     free(buf);
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MANDATORY, 0, exts);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MANDATORY, 0, exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "mandatory");
             return LY_EVALID;
         }
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -1624,43 +1559,37 @@
 static LY_ERR
 parse_restr(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword restr_kw, struct lysp_restr *restr)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     INSERT_WORD(ctx, buf, restr->arg, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &restr->dsc, Y_STR_ARG, &restr->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &restr->dsc, Y_STR_ARG, &restr->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &restr->ref, Y_STR_ARG, &restr->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &restr->ref, Y_STR_ARG, &restr->exts));
             break;
         case YANG_ERROR_APP_TAG:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRTAG, 0, &restr->eapptag, Y_STR_ARG, &restr->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRTAG, 0, &restr->eapptag, Y_STR_ARG, &restr->exts));
             break;
         case YANG_ERROR_MESSAGE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRMSG, 0, &restr->emsg, Y_STR_ARG, &restr->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRMSG, 0, &restr->emsg, Y_STR_ARG, &restr->exts));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &restr->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &restr->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), ly_stmt2str(restr_kw));
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -1680,7 +1609,6 @@
     struct lysp_restr *restr;
 
     LY_ARRAY_NEW_RET(ctx->ctx, *restrs, restr, LY_EMEM);
-
     return parse_restr(ctx, data, restr_kw, restr);
 }
 
@@ -1697,7 +1625,7 @@
 static LY_ERR
 parse_status(struct ly_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -1708,8 +1636,7 @@
     }
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     if ((word_len == 7) && !strncmp(word, "current", word_len)) {
         *flags |= LYS_STATUS_CURR;
@@ -1724,21 +1651,16 @@
     }
     free(buf);
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_STATUS, 0, exts);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_STATUS, 0, exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "status");
             return LY_EVALID;
         }
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -1754,7 +1676,7 @@
 static LY_ERR
 parse_when(struct ly_parser_ctx *ctx, const char **data, struct lysp_when **when_p)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -1770,31 +1692,25 @@
     *when_p = when;
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, when->cond, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
 
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &when->dsc, Y_STR_ARG, &when->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &when->dsc, Y_STR_ARG, &when->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &when->ref, Y_STR_ARG, &when->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &when->ref, Y_STR_ARG, &when->exts));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &when->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &when->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "when");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -1809,9 +1725,9 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_any(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword kw, struct lysp_node **siblings)
+parse_any(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword kw, struct lysp_node *parent, struct lysp_node **siblings)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     struct lysp_node *iter;
@@ -1821,6 +1737,7 @@
     any = calloc(1, sizeof *any);
     LY_CHECK_ERR_RET(!any, LOGMEM(ctx->ctx), LY_EMEM);
     any->nodetype = kw == YANG_ANYDATA ? LYS_ANYDATA : LYS_ANYXML;
+    any->parent = parent;
 
     /* insert into siblings */
     if (!*siblings) {
@@ -1831,52 +1748,45 @@
     }
 
     /* get name */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, any->name, word, word_len);
 
     /* parse substatements */
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CONFIG:
-            ret = parse_config(ctx, data, &any->flags, &any->exts);
+            LY_CHECK_RET(parse_config(ctx, data, &any->flags, &any->exts));
             break;
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &any->dsc, Y_STR_ARG, &any->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &any->dsc, Y_STR_ARG, &any->exts));
             break;
         case YANG_IF_FEATURE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &any->iffeatures, Y_STR_ARG, &any->exts);
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &any->iffeatures, Y_STR_ARG, &any->exts));
             break;
         case YANG_MANDATORY:
-            ret = parse_mandatory(ctx, data, &any->flags, &any->exts);
+            LY_CHECK_RET(parse_mandatory(ctx, data, &any->flags, &any->exts));
             break;
         case YANG_MUST:
-            ret = parse_restrs(ctx, data, kw, &any->musts);
+            LY_CHECK_RET(parse_restrs(ctx, data, kw, &any->musts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &any->ref, Y_STR_ARG, &any->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &any->ref, Y_STR_ARG, &any->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &any->flags, &any->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &any->flags, &any->exts));
             break;
         case YANG_WHEN:
-            ret = parse_when(ctx, data, &any->when);
+            LY_CHECK_RET(parse_when(ctx, data, &any->when));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &any->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &any->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw),
                    (any->nodetype & LYS_ANYDATA) == LYS_ANYDATA ? ly_stmt2str(YANG_ANYDATA) : ly_stmt2str(YANG_ANYXML));
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -1896,7 +1806,7 @@
 parse_type_enum_value_pos(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword val_kw, int64_t *value, uint16_t *flags,
                           struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word, *ptr;
     size_t word_len;
     long int num;
@@ -1910,31 +1820,35 @@
     *flags |= LYS_SET_VALUE;
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     if (!word_len || (word[0] == '+') || ((word[0] == '0') && (word_len > 1)) || ((val_kw == YANG_VALUE) && !strncmp(word, "-0", 2))) {
         LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, ly_stmt2str(val_kw));
-        free(buf);
-        return LY_EVALID;
+        goto error;
     }
 
     errno = 0;
     if (val_kw == YANG_VALUE) {
         num = strtol(word, &ptr, 10);
+        if (num < INT64_C(-2147483648) || num > INT64_C(2147483647)) {
+            LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, ly_stmt2str(val_kw));
+            goto error;
+        }
     } else {
         unum = strtoul(word, &ptr, 10);
+        if (unum > UINT64_C(4294967295)) {
+            LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, ly_stmt2str(val_kw));
+            goto error;
+        }
     }
     /* we have not parsed the whole argument */
     if ((size_t)(ptr - word) != word_len) {
         LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, ly_stmt2str(val_kw));
-        free(buf);
-        return LY_EVALID;
+        goto error;
     }
     if (errno == ERANGE) {
         LOGVAL_YANG(ctx, LY_VCODE_OOB, word_len, word, ly_stmt2str(val_kw));
-        free(buf);
-        return LY_EVALID;
+        goto error;
     }
     if (val_kw == YANG_VALUE) {
         *value = num;
@@ -1943,22 +1857,21 @@
     }
     free(buf);
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, val_kw == YANG_VALUE ? LYEXT_SUBSTMT_VALUE : LYEXT_SUBSTMT_POSITION, 0, exts);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, val_kw == YANG_VALUE ? LYEXT_SUBSTMT_VALUE : LYEXT_SUBSTMT_POSITION, 0, exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), ly_stmt2str(val_kw));
             return LY_EVALID;
         }
     }
-    LY_CHECK_RET(ret);
-
     return ret;
+
+error:
+    free(buf);
+    return LY_EVALID;
 }
 
 /**
@@ -1974,50 +1887,68 @@
 static LY_ERR
 parse_type_enum(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword enum_kw, struct lysp_type_enum **enums)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
-    size_t word_len;
+    size_t word_len, u;
     enum yang_keyword kw;
     struct lysp_type_enum *enm;
 
     LY_ARRAY_NEW_RET(ctx->ctx, *enums, enm, LY_EMEM);
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, enum_kw == YANG_ENUM ? Y_STR_ARG : Y_IDENTIF_ARG, &word, &buf, &word_len));
+    if (enum_kw == YANG_ENUM) {
+        if (!word_len) {
+            LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Enum name must not be zero-length.");
+            free(buf);
+            return LY_EVALID;
+        } else if (isspace(word[0]) || isspace(word[word_len - 1])) {
+            LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Enum name must not have any leading or trailing whitespaces (\"%.*s\").",
+                        word_len, word);
+            free(buf);
+            return LY_EVALID;
+        } else {
+            for (u = 0; u < word_len; ++u) {
+                if (iscntrl(word[u])) {
+                    LOGWRN(ctx->ctx, "Control characters in enum name should be avoided (\"%.*s\", character number %d).",
+                           word_len, word, u + 1);
+                    break;
+                }
+            }
+        }
+    } else { /* YANG_BIT */
 
+    }
     INSERT_WORD(ctx, buf, enm->name, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
+    CHECK_UNIQUENESS(ctx, *enums, name, ly_stmt2str(enum_kw), enm->name);
 
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &enm->dsc, Y_STR_ARG, &enm->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &enm->dsc, Y_STR_ARG, &enm->exts));
             break;
         case YANG_IF_FEATURE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &enm->iffeatures, Y_STR_ARG, &enm->exts);
+            YANG_CHECK_STMTVER_RET(ctx, "if-feature", ly_stmt2str(enum_kw));
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &enm->iffeatures, Y_STR_ARG, &enm->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &enm->ref, Y_STR_ARG, &enm->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &enm->ref, Y_STR_ARG, &enm->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &enm->flags, &enm->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &enm->flags, &enm->exts));
             break;
         case YANG_VALUE:
         case YANG_POSITION:
-            ret = parse_type_enum_value_pos(ctx, data, kw, &enm->value, &enm->flags, &enm->exts);
+            LY_CHECK_RET(parse_type_enum_value_pos(ctx, data, kw, &enm->value, &enm->flags, &enm->exts));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &enm->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &enm->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), ly_stmt2str(enum_kw));
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -2034,7 +1965,7 @@
 static LY_ERR
 parse_type_fracdigits(struct ly_parser_ctx *ctx, const char **data, uint8_t *fracdig, struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word, *ptr;
     size_t word_len;
     unsigned long int num;
@@ -2046,8 +1977,7 @@
     }
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     if (!word_len || (word[0] == '0') || !isdigit(word[0])) {
         LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "fraction-digits");
@@ -2071,21 +2001,16 @@
     *fracdig = num;
     free(buf);
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_FRACDIGITS, 0, exts);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_FRACDIGITS, 0, exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "fraction-digits");
             return LY_EVALID;
         }
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -2104,7 +2029,7 @@
 parse_type_reqinstance(struct ly_parser_ctx *ctx, const char **data, uint8_t *reqinst, uint16_t *flags,
                        struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -2116,8 +2041,7 @@
     *flags |= LYS_SET_REQINST;
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     if ((word_len == 4) && !strncmp(word, "true", word_len)) {
         *reqinst = 1;
@@ -2128,21 +2052,16 @@
     }
     free(buf);
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_REQINSTANCE, 0, exts);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_REQINSTANCE, 0, exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "require-instance");
             return LY_EVALID;
         }
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -2159,7 +2078,7 @@
 static LY_ERR
 parse_type_pattern_modifier(struct ly_parser_ctx *ctx, const char **data, const char **pat, struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -2170,8 +2089,7 @@
     }
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     if ((word_len != 12) || strncmp(word, "invert-match", word_len)) {
         LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "modifier");
@@ -2190,21 +2108,16 @@
     buf[0] = 0x15;
     *pat = lydict_insert_zc(ctx->ctx, buf);
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MODIFIER, 0, exts);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MODIFIER, 0, exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "modifier");
             return LY_EVALID;
         }
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -2220,7 +2133,7 @@
 static LY_ERR
 parse_type_pattern(struct ly_parser_ctx *ctx, const char **data, struct lysp_restr **patterns)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -2229,8 +2142,7 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *patterns, restr, LY_EMEM);
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     /* add special meaning first byte */
     if (buf) {
@@ -2245,36 +2157,32 @@
     buf[word_len + 1] = '\0'; /* terminating NULL byte */
     restr->arg = lydict_insert_zc(ctx->ctx, buf);
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &restr->dsc, Y_STR_ARG, &restr->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &restr->dsc, Y_STR_ARG, &restr->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &restr->ref, Y_STR_ARG, &restr->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &restr->ref, Y_STR_ARG, &restr->exts));
             break;
         case YANG_ERROR_APP_TAG:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRTAG, 0, &restr->eapptag, Y_STR_ARG, &restr->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRTAG, 0, &restr->eapptag, Y_STR_ARG, &restr->exts));
             break;
         case YANG_ERROR_MESSAGE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRMSG, 0, &restr->emsg, Y_STR_ARG, &restr->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_ERRMSG, 0, &restr->emsg, Y_STR_ARG, &restr->exts));
             break;
         case YANG_MODIFIER:
-            ret = parse_type_pattern_modifier(ctx, data, &restr->arg, &restr->exts);
+            YANG_CHECK_STMTVER_RET(ctx, "modifier", "pattern");
+            LY_CHECK_RET(parse_type_pattern_modifier(ctx, data, &restr->arg, &restr->exts));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &restr->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &restr->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "pattern");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -2290,7 +2198,7 @@
 static LY_ERR
 parse_type(struct ly_parser_ctx *ctx, const char **data, struct lysp_type *type)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -2302,25 +2210,26 @@
     }
 
     /* get value */
-    ret = get_argument(ctx, data, Y_PREF_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_PREF_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, type->name, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
 
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_BASE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_BASE, &type->bases, Y_PREF_IDENTIF_ARG, &type->exts);
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_BASE, &type->bases, Y_PREF_IDENTIF_ARG, &type->exts));
+            type->flags |= LYS_SET_BASE;
             break;
         case YANG_BIT:
-            ret = parse_type_enum(ctx, data, kw, &type->bits);
+            LY_CHECK_RET(parse_type_enum(ctx, data, kw, &type->bits));
+            type->flags |= LYS_SET_BIT;
             break;
         case YANG_ENUM:
-            ret = parse_type_enum(ctx, data, kw, &type->enums);
+            LY_CHECK_RET(parse_type_enum(ctx, data, kw, &type->enums));
+            type->flags |= LYS_SET_ENUM;
             break;
         case YANG_FRACTION_DIGITS:
-            ret = parse_type_fracdigits(ctx, data, &type->fraction_digits, &type->exts);
+            LY_CHECK_RET(parse_type_fracdigits(ctx, data, &type->fraction_digits, &type->exts));
+            type->flags |= LYS_SET_FRDIGITS;
             break;
         case YANG_LENGTH:
             if (type->length) {
@@ -2330,13 +2239,16 @@
             type->length = calloc(1, sizeof *type->length);
             LY_CHECK_ERR_RET(!type->length, LOGMEM(ctx->ctx), LY_EMEM);
 
-            ret = parse_restr(ctx, data, kw, type->length);
+            LY_CHECK_RET(parse_restr(ctx, data, kw, type->length));
+            type->flags |= LYS_SET_LENGTH;
             break;
         case YANG_PATH:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PATH, 0, &type->path, Y_STR_ARG, &type->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_PATH, 0, &type->path, Y_STR_ARG, &type->exts));
+            type->flags |= LYS_SET_PATH;
             break;
         case YANG_PATTERN:
-            ret = parse_type_pattern(ctx, data, &type->patterns);
+            LY_CHECK_RET(parse_type_pattern(ctx, data, &type->patterns));
+            type->flags |= LYS_SET_PATTERN;
             break;
         case YANG_RANGE:
             if (type->range) {
@@ -2346,28 +2258,26 @@
             type->range = calloc(1, sizeof *type->range);
             LY_CHECK_ERR_RET(!type->range, LOGMEM(ctx->ctx), LY_EVALID);
 
-            ret = parse_restr(ctx, data, kw, type->range);
+            LY_CHECK_RET(parse_restr(ctx, data, kw, type->range));
+            type->flags |= LYS_SET_RANGE;
             break;
         case YANG_REQUIRE_INSTANCE:
-            ret = parse_type_reqinstance(ctx, data, &type->require_instance, &type->flags, &type->exts);
+            LY_CHECK_RET(parse_type_reqinstance(ctx, data, &type->require_instance, &type->flags, &type->exts));
+            /* LYS_SET_REQINST checked and set inside parse_type_reqinstance() */
             break;
         case YANG_TYPE:
-            {
-                LY_ARRAY_NEW_RET(ctx->ctx, type->types, nest_type, LY_EMEM);
-            }
-            ret = parse_type(ctx, data, nest_type);
+            LY_ARRAY_NEW_RET(ctx->ctx, type->types, nest_type, LY_EMEM);
+            LY_CHECK_RET(parse_type(ctx, data, nest_type));
+            type->flags |= LYS_SET_TYPE;
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &type->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &type->exts));
             break;
         default:
-            LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "when");
+            LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "type");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -2381,9 +2291,9 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_leaf(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings)
+parse_leaf(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -2394,6 +2304,7 @@
     leaf = calloc(1, sizeof *leaf);
     LY_CHECK_ERR_RET(!leaf, LOGMEM(ctx->ctx), LY_EMEM);
     leaf->nodetype = LYS_LEAF;
+    leaf->parent = parent;
 
     /* insert into siblings */
     if (!*siblings) {
@@ -2404,65 +2315,64 @@
     }
 
     /* get name */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, leaf->name, word, word_len);
 
     /* parse substatements */
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
         switch (kw) {
         case YANG_CONFIG:
-            ret = parse_config(ctx, data, &leaf->flags, &leaf->exts);
+            LY_CHECK_RET(parse_config(ctx, data, &leaf->flags, &leaf->exts));
             break;
         case YANG_DEFAULT:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &leaf->dflt, Y_STR_ARG, &leaf->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &leaf->dflt, Y_STR_ARG, &leaf->exts));
             break;
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &leaf->dsc, Y_STR_ARG, &leaf->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &leaf->dsc, Y_STR_ARG, &leaf->exts));
             break;
         case YANG_IF_FEATURE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &leaf->iffeatures, Y_STR_ARG, &leaf->exts);
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &leaf->iffeatures, Y_STR_ARG, &leaf->exts));
             break;
         case YANG_MANDATORY:
-            ret = parse_mandatory(ctx, data, &leaf->flags, &leaf->exts);
+            LY_CHECK_RET(parse_mandatory(ctx, data, &leaf->flags, &leaf->exts));
             break;
         case YANG_MUST:
-            ret = parse_restrs(ctx, data, kw, &leaf->musts);
+            LY_CHECK_RET(parse_restrs(ctx, data, kw, &leaf->musts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &leaf->ref, Y_STR_ARG, &leaf->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &leaf->ref, Y_STR_ARG, &leaf->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &leaf->flags, &leaf->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &leaf->flags, &leaf->exts));
             break;
         case YANG_TYPE:
-            ret = parse_type(ctx, data, &leaf->type);
+            LY_CHECK_RET(parse_type(ctx, data, &leaf->type));
             break;
         case YANG_UNITS:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, &leaf->units, Y_STR_ARG, &leaf->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, &leaf->units, Y_STR_ARG, &leaf->exts));
             break;
         case YANG_WHEN:
-            ret = parse_when(ctx, data, &leaf->when);
+            LY_CHECK_RET(parse_when(ctx, data, &leaf->when));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &leaf->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &leaf->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "leaf");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
     LY_CHECK_RET(ret);
-
+checks:
     /* mandatory substatements */
     if (!leaf->type.name) {
         LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "type", "leaf");
         return LY_EVALID;
     }
+    if ((leaf->flags & LYS_MAND_TRUE) && (leaf->dflt)) {
+        LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMSCOMB, "mandatory", "default", "leaf");
+        return LY_EVALID;
+    }
 
     return ret;
 }
@@ -2481,7 +2391,7 @@
 static LY_ERR
 parse_maxelements(struct ly_parser_ctx *ctx, const char **data, uint32_t *max, uint16_t *flags, struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word, *ptr;
     size_t word_len;
     unsigned long int num;
@@ -2494,8 +2404,7 @@
     *flags |= LYS_SET_MAX;
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     if (!word_len || (word[0] == '0') || ((word[0] != 'u') && !isdigit(word[0]))) {
         LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "max-elements");
@@ -2522,21 +2431,16 @@
     }
     free(buf);
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MAX, 0, exts);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MAX, 0, exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "max-elements");
             return LY_EVALID;
         }
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -2554,7 +2458,7 @@
 static LY_ERR
 parse_minelements(struct ly_parser_ctx *ctx, const char **data, uint32_t *min, uint16_t *flags, struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word, *ptr;
     size_t word_len;
     unsigned long int num;
@@ -2567,8 +2471,7 @@
     *flags |= LYS_SET_MIN;
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     if (!word_len || !isdigit(word[0]) || ((word[0] == '0') && (word_len > 1))) {
         LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "min-elements");
@@ -2592,21 +2495,16 @@
     *min = num;
     free(buf);
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MIN, 0, exts);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_MIN, 0, exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "min-elements");
             return LY_EVALID;
         }
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -2623,7 +2521,7 @@
 static LY_ERR
 parse_orderedby(struct ly_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -2634,8 +2532,7 @@
     }
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     if ((word_len == 6) && !strncmp(word, "system", word_len)) {
         *flags |= LYS_ORDBY_SYSTEM;
@@ -2648,21 +2545,16 @@
     }
     free(buf);
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_ORDEREDBY, 0, exts);
-            LY_CHECK_RET(ret);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_ORDEREDBY, 0, exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "ordered-by");
             return LY_EVALID;
         }
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -2676,9 +2568,9 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_leaflist(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings)
+parse_leaflist(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -2689,6 +2581,7 @@
     llist = calloc(1, sizeof *llist);
     LY_CHECK_ERR_RET(!llist, LOGMEM(ctx->ctx), LY_EMEM);
     llist->nodetype = LYS_LEAFLIST;
+    llist->parent = parent;
 
     /* insert into siblings */
     if (!*siblings) {
@@ -2699,71 +2592,77 @@
     }
 
     /* get name */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, llist->name, word, word_len);
 
     /* parse substatements */
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
         switch (kw) {
         case YANG_CONFIG:
-            ret = parse_config(ctx, data, &llist->flags, &llist->exts);
+            LY_CHECK_RET(parse_config(ctx, data, &llist->flags, &llist->exts));
             break;
         case YANG_DEFAULT:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_DEFAULT, &llist->dflts, Y_STR_ARG, &llist->exts);
+            YANG_CHECK_STMTVER_RET(ctx, "default", "leaf-list");
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_DEFAULT, &llist->dflts, Y_STR_ARG, &llist->exts));
             break;
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &llist->dsc, Y_STR_ARG, &llist->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &llist->dsc, Y_STR_ARG, &llist->exts));
             break;
         case YANG_IF_FEATURE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &llist->iffeatures, Y_STR_ARG, &llist->exts);
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &llist->iffeatures, Y_STR_ARG, &llist->exts));
             break;
         case YANG_MAX_ELEMENTS:
-            ret = parse_maxelements(ctx, data, &llist->max, &llist->flags, &llist->exts);
+            LY_CHECK_RET(parse_maxelements(ctx, data, &llist->max, &llist->flags, &llist->exts));
             break;
         case YANG_MIN_ELEMENTS:
-            ret = parse_minelements(ctx, data, &llist->min, &llist->flags, &llist->exts);
+            LY_CHECK_RET(parse_minelements(ctx, data, &llist->min, &llist->flags, &llist->exts));
             break;
         case YANG_MUST:
-            ret = parse_restrs(ctx, data, kw, &llist->musts);
+            LY_CHECK_RET(parse_restrs(ctx, data, kw, &llist->musts));
             break;
         case YANG_ORDERED_BY:
-            ret = parse_orderedby(ctx, data, &llist->flags, &llist->exts);
+            LY_CHECK_RET(parse_orderedby(ctx, data, &llist->flags, &llist->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &llist->ref, Y_STR_ARG, &llist->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &llist->ref, Y_STR_ARG, &llist->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &llist->flags, &llist->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &llist->flags, &llist->exts));
             break;
         case YANG_TYPE:
-            ret = parse_type(ctx, data, &llist->type);
+            LY_CHECK_RET(parse_type(ctx, data, &llist->type));
             break;
         case YANG_UNITS:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, &llist->units, Y_STR_ARG, &llist->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, &llist->units, Y_STR_ARG, &llist->exts));
             break;
         case YANG_WHEN:
-            ret = parse_when(ctx, data, &llist->when);
+            LY_CHECK_RET(parse_when(ctx, data, &llist->when));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &llist->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &llist->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "llist");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
     LY_CHECK_RET(ret);
-
+checks:
     /* mandatory substatements */
     if (!llist->type.name) {
         LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "type", "leaf-list");
         return LY_EVALID;
     }
+    if ((llist->min) && (llist->dflts)) {
+        LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMSCOMB, "min-elements", "default", "leaf-list");
+        return LY_EVALID;
+    }
+    if (llist->max && llist->min > llist->max) {
+        LOGVAL_YANG(ctx, LYVE_SEMANTICS,
+                    "Invalid combination of min-elements and max-elements: min value %u is bigger than the max value %u.",
+                    llist->min, llist->max);
+        return LY_EVALID;
+    }
 
     return ret;
 }
@@ -2780,7 +2679,7 @@
 static LY_ERR
 parse_refine(struct ly_parser_ctx *ctx, const char **data, struct lysp_refine **refines)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -2789,55 +2688,50 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *refines, rf, LY_EMEM);
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, rf->nodeid, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
 
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CONFIG:
-            ret = parse_config(ctx, data, &rf->flags, &rf->exts);
+            LY_CHECK_RET(parse_config(ctx, data, &rf->flags, &rf->exts));
             break;
         case YANG_DEFAULT:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_DEFAULT, &rf->dflts, Y_STR_ARG, &rf->exts);
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_DEFAULT, &rf->dflts, Y_STR_ARG, &rf->exts));
             break;
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &rf->dsc, Y_STR_ARG, &rf->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &rf->dsc, Y_STR_ARG, &rf->exts));
             break;
         case YANG_IF_FEATURE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &rf->iffeatures, Y_STR_ARG, &rf->exts);
+            YANG_CHECK_STMTVER_RET(ctx, "if-feature", "refine");
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &rf->iffeatures, Y_STR_ARG, &rf->exts));
             break;
         case YANG_MAX_ELEMENTS:
-            ret = parse_maxelements(ctx, data, &rf->max, &rf->flags, &rf->exts);
+            LY_CHECK_RET(parse_maxelements(ctx, data, &rf->max, &rf->flags, &rf->exts));
             break;
         case YANG_MIN_ELEMENTS:
-            ret = parse_minelements(ctx, data, &rf->min, &rf->flags, &rf->exts);
+            LY_CHECK_RET(parse_minelements(ctx, data, &rf->min, &rf->flags, &rf->exts));
             break;
         case YANG_MUST:
-            ret = parse_restrs(ctx, data, kw, &rf->musts);
+            LY_CHECK_RET(parse_restrs(ctx, data, kw, &rf->musts));
             break;
         case YANG_MANDATORY:
-            ret = parse_mandatory(ctx, data, &rf->flags, &rf->exts);
+            LY_CHECK_RET(parse_mandatory(ctx, data, &rf->flags, &rf->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &rf->ref, Y_STR_ARG, &rf->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &rf->ref, Y_STR_ARG, &rf->exts));
             break;
         case YANG_PRESENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PRESENCE, 0, &rf->presence, Y_STR_ARG, &rf->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_PRESENCE, 0, &rf->presence, Y_STR_ARG, &rf->exts));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &rf->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &rf->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "refine");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -2851,9 +2745,9 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_typedef(struct ly_parser_ctx *ctx, const char **data, struct lysp_tpdf **typedefs)
+parse_typedef(struct ly_parser_ctx *ctx, struct lysp_node *parent, const char **data, struct lysp_tpdf **typedefs)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -2862,51 +2756,51 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *typedefs, tpdf, LY_EMEM);
 
     /* get value */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, tpdf->name, word, word_len);
 
     /* parse substatements */
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
         switch (kw) {
         case YANG_DEFAULT:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &tpdf->dflt, Y_STR_ARG, &tpdf->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &tpdf->dflt, Y_STR_ARG, &tpdf->exts));
             break;
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &tpdf->dsc, Y_STR_ARG, &tpdf->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &tpdf->dsc, Y_STR_ARG, &tpdf->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &tpdf->ref, Y_STR_ARG, &tpdf->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &tpdf->ref, Y_STR_ARG, &tpdf->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &tpdf->flags, &tpdf->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &tpdf->flags, &tpdf->exts));
             break;
         case YANG_TYPE:
-            ret = parse_type(ctx, data, &tpdf->type);
+            LY_CHECK_RET(parse_type(ctx, data, &tpdf->type));
             break;
         case YANG_UNITS:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, &tpdf->units, Y_STR_ARG, &tpdf->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, &tpdf->units, Y_STR_ARG, &tpdf->exts));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &tpdf->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &tpdf->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "typedef");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
     LY_CHECK_RET(ret);
-
+checks:
     /* mandatory substatements */
     if (!tpdf->type.name) {
         LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "type", "typedef");
         return LY_EVALID;
     }
 
+    /* store data for collision check */
+    if (parent) {
+        ly_set_add(&ctx->tpdfs_nodes, parent, 0);
+    }
+
     return ret;
 }
 
@@ -2921,15 +2815,18 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_inout(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword kw, struct lysp_action_inout **inout_p)
+parse_inout(struct ly_parser_ctx *ctx, const char **data, enum yang_keyword inout_kw, struct lysp_node *parent, struct lysp_action_inout **inout_p)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *word;
     size_t word_len;
     struct lysp_action_inout *inout;
+    enum yang_keyword kw;
+    unsigned int u;
+    struct lysp_node *child;
 
     if (*inout_p) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(kw));
+        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(inout_kw));
         return LY_EVALID;
     }
 
@@ -2937,54 +2834,60 @@
     inout = calloc(1, sizeof *inout);
     LY_CHECK_ERR_RET(!inout, LOGMEM(ctx->ctx), LY_EMEM);
     *inout_p = inout;
+    inout->nodetype = LYS_INOUT;
+    inout->parent = parent;
 
     /* parse substatements */
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_ANYDATA:
+            YANG_CHECK_STMTVER_RET(ctx, "anydata", ly_stmt2str(inout_kw));
+            /* fall through */
         case YANG_ANYXML:
-            ret = parse_any(ctx, data, kw, &inout->data);
+            LY_CHECK_RET(parse_any(ctx, data, kw, (struct lysp_node*)inout, &inout->data));
             break;
         case YANG_CHOICE:
-            ret = parse_choice(ctx, data, &inout->data);
+            LY_CHECK_RET(parse_choice(ctx, data, (struct lysp_node*)inout, &inout->data));
             break;
         case YANG_CONTAINER:
-            ret = parse_container(ctx, data, &inout->data);
+            LY_CHECK_RET(parse_container(ctx, data, (struct lysp_node*)inout, &inout->data));
             break;
         case YANG_LEAF:
-            ret = parse_leaf(ctx, data, &inout->data);
+            LY_CHECK_RET(parse_leaf(ctx, data, (struct lysp_node*)inout, &inout->data));
             break;
         case YANG_LEAF_LIST:
-            ret = parse_leaflist(ctx, data, &inout->data);
+            LY_CHECK_RET(parse_leaflist(ctx, data, (struct lysp_node*)inout, &inout->data));
             break;
         case YANG_LIST:
-            ret = parse_list(ctx, data, &inout->data);
+            LY_CHECK_RET(parse_list(ctx, data, (struct lysp_node*)inout, &inout->data));
             break;
         case YANG_USES:
-            ret = parse_uses(ctx, data, &inout->data);
+            LY_CHECK_RET(parse_uses(ctx, data, (struct lysp_node*)inout, &inout->data));
             break;
         case YANG_TYPEDEF:
-            ret = parse_typedef(ctx, data, &inout->typedefs);
+            LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)inout, data, &inout->typedefs));
             break;
         case YANG_MUST:
-            ret = parse_restrs(ctx, data, kw, &inout->musts);
+            YANG_CHECK_STMTVER_RET(ctx, "must", ly_stmt2str(inout_kw));
+            LY_CHECK_RET(parse_restrs(ctx, data, kw, &inout->musts));
             break;
         case YANG_GROUPING:
-            ret = parse_grouping(ctx, data, &inout->groupings);
+            LY_CHECK_RET(parse_grouping(ctx, data, (struct lysp_node*)inout, &inout->groupings));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &inout->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &inout->exts));
             break;
         default:
-            LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "input/output");
+            LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), ly_stmt2str(inout_kw));
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
+    /* finalize parent pointers to the reallocated items */
+    LY_ARRAY_FOR(inout->groupings, u) {
+        LY_LIST_FOR(inout->groupings[u].data, child) {
+            child->parent = (struct lysp_node*)&inout->groupings[u];
+        }
+    }
     return ret;
 }
 
@@ -2998,62 +2901,66 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_action(struct ly_parser_ctx *ctx, const char **data, struct lysp_action **actions)
+parse_action(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_action **actions)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
     struct lysp_action *act;
+    struct lysp_node *child;
+    unsigned int u;
 
     LY_ARRAY_NEW_RET(ctx->ctx, *actions, act, LY_EMEM);
 
     /* get value */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, act->name, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
+    act->nodetype = LYS_ACTION;
+    act->parent = parent;
 
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &act->dsc, Y_STR_ARG, &act->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &act->dsc, Y_STR_ARG, &act->exts));
             break;
         case YANG_IF_FEATURE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &act->iffeatures, Y_STR_ARG, &act->exts);
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &act->iffeatures, Y_STR_ARG, &act->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &act->ref, Y_STR_ARG, &act->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &act->ref, Y_STR_ARG, &act->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &act->flags, &act->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &act->flags, &act->exts));
             break;
 
         case YANG_INPUT:
-            ret = parse_inout(ctx, data, kw, &act->input);
+            LY_CHECK_RET(parse_inout(ctx, data, kw, (struct lysp_node*)act, &act->input));
             break;
         case YANG_OUTPUT:
-            ret = parse_inout(ctx, data, kw, &act->output);
+            LY_CHECK_RET(parse_inout(ctx, data, kw, (struct lysp_node*)act, &act->output));
             break;
 
         case YANG_TYPEDEF:
-            ret = parse_typedef(ctx, data, &act->typedefs);
+            LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)act, data, &act->typedefs));
             break;
         case YANG_GROUPING:
-            ret = parse_grouping(ctx, data, &act->groupings);
+            LY_CHECK_RET(parse_grouping(ctx, data, (struct lysp_node*)act, &act->groupings));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &act->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &act->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "action");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
+    /* finalize parent pointers to the reallocated items */
+    LY_ARRAY_FOR(act->groupings, u) {
+        LY_LIST_FOR(act->groupings[u].data, child) {
+            child->parent = (struct lysp_node*)&act->groupings[u];
+        }
+    }
     return ret;
 }
 
@@ -3067,81 +2974,88 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_notif(struct ly_parser_ctx *ctx, const char **data, struct lysp_notif **notifs)
+parse_notif(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_notif **notifs)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
     struct lysp_notif *notif;
+    struct lysp_node *child;
+    unsigned int u;
 
     LY_ARRAY_NEW_RET(ctx->ctx, *notifs, notif, LY_EMEM);
 
     /* get value */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, notif->name, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
+    notif->nodetype = LYS_NOTIF;
+    notif->parent = parent;
 
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &notif->dsc, Y_STR_ARG, &notif->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &notif->dsc, Y_STR_ARG, &notif->exts));
             break;
         case YANG_IF_FEATURE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &notif->iffeatures, Y_STR_ARG, &notif->exts);
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &notif->iffeatures, Y_STR_ARG, &notif->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &notif->ref, Y_STR_ARG, &notif->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &notif->ref, Y_STR_ARG, &notif->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &notif->flags, &notif->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &notif->flags, &notif->exts));
             break;
 
         case YANG_ANYDATA:
+            YANG_CHECK_STMTVER_RET(ctx, "anydata", "notification");
+            /* fall through */
         case YANG_ANYXML:
-            ret = parse_any(ctx, data, kw, &notif->data);
+            LY_CHECK_RET(parse_any(ctx, data, kw, (struct lysp_node*)notif, &notif->data));
             break;
         case YANG_CHOICE:
-            ret = parse_case(ctx, data, &notif->data);
+            LY_CHECK_RET(parse_case(ctx, data, (struct lysp_node*)notif, &notif->data));
             break;
         case YANG_CONTAINER:
-            ret = parse_container(ctx, data, &notif->data);
+            LY_CHECK_RET(parse_container(ctx, data, (struct lysp_node*)notif, &notif->data));
             break;
         case YANG_LEAF:
-            ret = parse_leaf(ctx, data, &notif->data);
+            LY_CHECK_RET(parse_leaf(ctx, data, (struct lysp_node*)notif, &notif->data));
             break;
         case YANG_LEAF_LIST:
-            ret = parse_leaflist(ctx, data, &notif->data);
+            LY_CHECK_RET(parse_leaflist(ctx, data, (struct lysp_node*)notif, &notif->data));
             break;
         case YANG_LIST:
-            ret = parse_list(ctx, data, &notif->data);
+            LY_CHECK_RET(parse_list(ctx, data, (struct lysp_node*)notif, &notif->data));
             break;
         case YANG_USES:
-            ret = parse_uses(ctx, data, &notif->data);
+            LY_CHECK_RET(parse_uses(ctx, data, (struct lysp_node*)notif, &notif->data));
             break;
 
         case YANG_MUST:
-            ret = parse_restrs(ctx, data, kw, &notif->musts);
+            YANG_CHECK_STMTVER_RET(ctx, "must", "notification");
+            LY_CHECK_RET(parse_restrs(ctx, data, kw, &notif->musts));
             break;
         case YANG_TYPEDEF:
-            ret = parse_typedef(ctx, data, &notif->typedefs);
+            LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)notif, data, &notif->typedefs));
             break;
         case YANG_GROUPING:
-            ret = parse_grouping(ctx, data, &notif->groupings);
+            LY_CHECK_RET(parse_grouping(ctx, data, (struct lysp_node*)notif, &notif->groupings));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &notif->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &notif->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "notification");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
+    /* finalize parent pointers to the reallocated items */
+    LY_ARRAY_FOR(notif->groupings, u) {
+        LY_LIST_FOR(notif->groupings[u].data, child) {
+            child->parent = (struct lysp_node*)&notif->groupings[u];
+        }
+    }
     return ret;
 }
 
@@ -3155,82 +3069,89 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_grouping(struct ly_parser_ctx *ctx, const char **data, struct lysp_grp **groupings)
+parse_grouping(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_grp **groupings)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
     struct lysp_grp *grp;
+    struct lysp_node *child;
+    unsigned int u;
 
     LY_ARRAY_NEW_RET(ctx->ctx, *groupings, grp, LY_EMEM);
 
     /* get value */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, grp->name, word, word_len);
+    grp->nodetype = LYS_GROUPING;
+    grp->parent = parent;
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &grp->dsc, Y_STR_ARG, &grp->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &grp->dsc, Y_STR_ARG, &grp->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &grp->ref, Y_STR_ARG, &grp->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &grp->ref, Y_STR_ARG, &grp->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &grp->flags, &grp->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &grp->flags, &grp->exts));
             break;
 
         case YANG_ANYDATA:
+            YANG_CHECK_STMTVER_RET(ctx, "anydata", "grouping");
+            /* fall through */
         case YANG_ANYXML:
-            ret = parse_any(ctx, data, kw, &grp->data);
+            LY_CHECK_RET(parse_any(ctx, data, kw, (struct lysp_node*)grp, &grp->data));
             break;
         case YANG_CHOICE:
-            ret = parse_choice(ctx, data, &grp->data);
+            LY_CHECK_RET(parse_choice(ctx, data, (struct lysp_node*)grp, &grp->data));
             break;
         case YANG_CONTAINER:
-            ret = parse_container(ctx, data, &grp->data);
+            LY_CHECK_RET(parse_container(ctx, data, (struct lysp_node*)grp, &grp->data));
             break;
         case YANG_LEAF:
-            ret = parse_leaf(ctx, data, &grp->data);
+            LY_CHECK_RET(parse_leaf(ctx, data, (struct lysp_node*)grp, &grp->data));
             break;
         case YANG_LEAF_LIST:
-            ret = parse_leaflist(ctx, data, &grp->data);
+            LY_CHECK_RET(parse_leaflist(ctx, data, (struct lysp_node*)grp, &grp->data));
             break;
         case YANG_LIST:
-            ret = parse_list(ctx, data, &grp->data);
+            LY_CHECK_RET(parse_list(ctx, data, (struct lysp_node*)grp, &grp->data));
             break;
         case YANG_USES:
-            ret = parse_uses(ctx, data, &grp->data);
+            LY_CHECK_RET(parse_uses(ctx, data, (struct lysp_node*)grp, &grp->data));
             break;
 
         case YANG_TYPEDEF:
-            ret = parse_typedef(ctx, data, &grp->typedefs);
+            LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)grp, data, &grp->typedefs));
             break;
         case YANG_ACTION:
-            ret = parse_action(ctx, data, &grp->actions);
+            YANG_CHECK_STMTVER_RET(ctx, "action", "grouping");
+            LY_CHECK_RET(parse_action(ctx, data, (struct lysp_node*)grp, &grp->actions));
             break;
         case YANG_GROUPING:
-            ret = parse_grouping(ctx, data, &grp->groupings);
+            LY_CHECK_RET(parse_grouping(ctx, data, (struct lysp_node*)grp, &grp->groupings));
             break;
         case YANG_NOTIFICATION:
-            ret = parse_notif(ctx, data, &grp->notifs);
+            YANG_CHECK_STMTVER_RET(ctx, "notification", "grouping");
+            LY_CHECK_RET(parse_notif(ctx, data, (struct lysp_node*)grp, &grp->notifs));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &grp->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &grp->exts));
             break;
         default:
-            LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "augment");
+            LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "grouping");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
+    /* finalize parent pointers to the reallocated items */
+    LY_ARRAY_FOR(grp->groupings, u) {
+        LY_LIST_FOR(grp->groupings[u].data, child) {
+            child->parent = (struct lysp_node*)&grp->groupings[u];
+        }
+    }
     return ret;
 }
 
@@ -3244,9 +3165,9 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_augment(struct ly_parser_ctx *ctx, const char **data, struct lysp_augment **augments)
+parse_augment(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_augment **augments)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -3255,73 +3176,73 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *augments, aug, LY_EMEM);
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, aug->nodeid, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
+    aug->nodetype = LYS_AUGMENT;
+    aug->parent = parent;
 
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &aug->dsc, Y_STR_ARG, &aug->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &aug->dsc, Y_STR_ARG, &aug->exts));
             break;
         case YANG_IF_FEATURE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &aug->iffeatures, Y_STR_ARG, &aug->exts);
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &aug->iffeatures, Y_STR_ARG, &aug->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &aug->ref, Y_STR_ARG, &aug->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &aug->ref, Y_STR_ARG, &aug->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &aug->flags, &aug->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &aug->flags, &aug->exts));
             break;
         case YANG_WHEN:
-            ret = parse_when(ctx, data, &aug->when);
+            LY_CHECK_RET(parse_when(ctx, data, &aug->when));
             break;
 
         case YANG_ANYDATA:
+            YANG_CHECK_STMTVER_RET(ctx, "anydata", "augment");
+            /* fall through */
         case YANG_ANYXML:
-            ret = parse_any(ctx, data, kw, &aug->child);
+            LY_CHECK_RET(parse_any(ctx, data, kw, (struct lysp_node*)aug, &aug->child));
             break;
         case YANG_CASE:
-            ret = parse_case(ctx, data, &aug->child);
+            LY_CHECK_RET(parse_case(ctx, data, (struct lysp_node*)aug, &aug->child));
             break;
         case YANG_CHOICE:
-            ret = parse_choice(ctx, data, &aug->child);
+            LY_CHECK_RET(parse_choice(ctx, data, (struct lysp_node*)aug, &aug->child));
             break;
         case YANG_CONTAINER:
-            ret = parse_container(ctx, data, &aug->child);
+            LY_CHECK_RET(parse_container(ctx, data, (struct lysp_node*)aug, &aug->child));
             break;
         case YANG_LEAF:
-            ret = parse_leaf(ctx, data, &aug->child);
+            LY_CHECK_RET(parse_leaf(ctx, data, (struct lysp_node*)aug, &aug->child));
             break;
         case YANG_LEAF_LIST:
-            ret = parse_leaflist(ctx, data, &aug->child);
+            LY_CHECK_RET(parse_leaflist(ctx, data, (struct lysp_node*)aug, &aug->child));
             break;
         case YANG_LIST:
-            ret = parse_list(ctx, data, &aug->child);
+            LY_CHECK_RET(parse_list(ctx, data, (struct lysp_node*)aug, &aug->child));
             break;
         case YANG_USES:
-            ret = parse_uses(ctx, data, &aug->child);
+            LY_CHECK_RET(parse_uses(ctx, data, (struct lysp_node*)aug, &aug->child));
             break;
 
         case YANG_ACTION:
-            ret = parse_action(ctx, data, &aug->actions);
+            YANG_CHECK_STMTVER_RET(ctx, "action", "augment");
+            LY_CHECK_RET(parse_action(ctx, data, (struct lysp_node*)aug, &aug->actions));
             break;
         case YANG_NOTIFICATION:
-            ret = parse_notif(ctx, data, &aug->notifs);
+            YANG_CHECK_STMTVER_RET(ctx, "notification", "augment");
+            LY_CHECK_RET(parse_notif(ctx, data, (struct lysp_node*)aug, &aug->notifs));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &aug->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &aug->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "augment");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -3335,9 +3256,9 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_uses(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings)
+parse_uses(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -3348,6 +3269,7 @@
     uses = calloc(1, sizeof *uses);
     LY_CHECK_ERR_RET(!uses, LOGMEM(ctx->ctx), LY_EMEM);
     uses->nodetype = LYS_USES;
+    uses->parent = parent;
 
     /* insert into siblings */
     if (!*siblings) {
@@ -3358,49 +3280,42 @@
     }
 
     /* get name */
-    ret = get_argument(ctx, data, Y_PREF_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_PREF_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, uses->name, word, word_len);
 
     /* parse substatements */
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &uses->dsc, Y_STR_ARG, &uses->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &uses->dsc, Y_STR_ARG, &uses->exts));
             break;
         case YANG_IF_FEATURE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &uses->iffeatures, Y_STR_ARG, &uses->exts);
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &uses->iffeatures, Y_STR_ARG, &uses->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &uses->ref, Y_STR_ARG, &uses->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &uses->ref, Y_STR_ARG, &uses->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &uses->flags, &uses->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &uses->flags, &uses->exts));
             break;
         case YANG_WHEN:
-            ret = parse_when(ctx, data, &uses->when);
+            LY_CHECK_RET(parse_when(ctx, data, &uses->when));
             break;
 
         case YANG_REFINE:
-            ret = parse_refine(ctx, data, &uses->refines);
+            LY_CHECK_RET(parse_refine(ctx, data, &uses->refines));
             break;
         case YANG_AUGMENT:
-            ret = parse_augment(ctx, data, &uses->augments);
+            LY_CHECK_RET(parse_augment(ctx, data, (struct lysp_node*)uses, &uses->augments));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &uses->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &uses->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "uses");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -3414,9 +3329,9 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_case(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings)
+parse_case(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -3427,6 +3342,7 @@
     cas = calloc(1, sizeof *cas);
     LY_CHECK_ERR_RET(!cas, LOGMEM(ctx->ctx), LY_EMEM);
     cas->nodetype = LYS_CASE;
+    cas->parent = parent;
 
     /* insert into siblings */
     if (!*siblings) {
@@ -3437,65 +3353,60 @@
     }
 
     /* get name */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, cas->name, word, word_len);
 
     /* parse substatements */
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &cas->dsc, Y_STR_ARG, &cas->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &cas->dsc, Y_STR_ARG, &cas->exts));
             break;
         case YANG_IF_FEATURE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &cas->iffeatures, Y_STR_ARG, &cas->exts);
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &cas->iffeatures, Y_STR_ARG, &cas->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &cas->ref, Y_STR_ARG, &cas->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &cas->ref, Y_STR_ARG, &cas->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &cas->flags, &cas->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &cas->flags, &cas->exts));
             break;
         case YANG_WHEN:
-            ret = parse_when(ctx, data, &cas->when);
+            LY_CHECK_RET(parse_when(ctx, data, &cas->when));
             break;
 
         case YANG_ANYDATA:
+            YANG_CHECK_STMTVER_RET(ctx, "anydata", "case");
+            /* fall through */
         case YANG_ANYXML:
-            ret = parse_any(ctx, data, kw, &cas->child);
+            LY_CHECK_RET(parse_any(ctx, data, kw, (struct lysp_node*)cas, &cas->child));
             break;
         case YANG_CHOICE:
-            ret = parse_case(ctx, data, &cas->child);
+            LY_CHECK_RET(parse_choice(ctx, data, (struct lysp_node*)cas, &cas->child));
             break;
         case YANG_CONTAINER:
-            ret = parse_container(ctx, data, &cas->child);
+            LY_CHECK_RET(parse_container(ctx, data, (struct lysp_node*)cas, &cas->child));
             break;
         case YANG_LEAF:
-            ret = parse_leaf(ctx, data, &cas->child);
+            LY_CHECK_RET(parse_leaf(ctx, data, (struct lysp_node*)cas, &cas->child));
             break;
         case YANG_LEAF_LIST:
-            ret = parse_leaflist(ctx, data, &cas->child);
+            LY_CHECK_RET(parse_leaflist(ctx, data, (struct lysp_node*)cas, &cas->child));
             break;
         case YANG_LIST:
-            ret = parse_list(ctx, data, &cas->child);
+            LY_CHECK_RET(parse_list(ctx, data, (struct lysp_node*)cas, &cas->child));
             break;
         case YANG_USES:
-            ret = parse_uses(ctx, data, &cas->child);
+            LY_CHECK_RET(parse_uses(ctx, data, (struct lysp_node*)cas, &cas->child));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &cas->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &cas->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "case");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -3509,9 +3420,9 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_choice(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings)
+parse_choice(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -3522,6 +3433,7 @@
     choice = calloc(1, sizeof *choice);
     LY_CHECK_ERR_RET(!choice, LOGMEM(ctx->ctx), LY_EMEM);
     choice->nodetype = LYS_CHOICE;
+    choice->parent = parent;
 
     /* insert into siblings */
     if (!*siblings) {
@@ -3532,74 +3444,76 @@
     }
 
     /* get name */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, choice->name, word, word_len);
 
     /* parse substatements */
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
         switch (kw) {
         case YANG_CONFIG:
-            ret = parse_config(ctx, data, &choice->flags, &choice->exts);
+            LY_CHECK_RET(parse_config(ctx, data, &choice->flags, &choice->exts));
             break;
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &choice->dsc, Y_STR_ARG, &choice->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &choice->dsc, Y_STR_ARG, &choice->exts));
             break;
         case YANG_IF_FEATURE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &choice->iffeatures, Y_STR_ARG, &choice->exts);
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &choice->iffeatures, Y_STR_ARG, &choice->exts));
             break;
         case YANG_MANDATORY:
-            ret = parse_mandatory(ctx, data, &choice->flags, &choice->exts);
+            LY_CHECK_RET(parse_mandatory(ctx, data, &choice->flags, &choice->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &choice->ref, Y_STR_ARG, &choice->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &choice->ref, Y_STR_ARG, &choice->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &choice->flags, &choice->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &choice->flags, &choice->exts));
             break;
         case YANG_WHEN:
-            ret = parse_when(ctx, data, &choice->when);
+            LY_CHECK_RET(parse_when(ctx, data, &choice->when));
             break;
         case YANG_DEFAULT:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &choice->dflt, Y_IDENTIF_ARG, &choice->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &choice->dflt, Y_PREF_IDENTIF_ARG, &choice->exts));
             break;
 
         case YANG_ANYDATA:
+            YANG_CHECK_STMTVER_RET(ctx, "anydata", "choice");
+            /* fall through */
         case YANG_ANYXML:
-            ret = parse_any(ctx, data, kw, &choice->child);
+            LY_CHECK_RET(parse_any(ctx, data, kw, (struct lysp_node*)choice, &choice->child));
             break;
         case YANG_CASE:
-            ret = parse_case(ctx, data, &choice->child);
+            LY_CHECK_RET(parse_case(ctx, data, (struct lysp_node*)choice, &choice->child));
             break;
         case YANG_CHOICE:
-            ret = parse_choice(ctx, data, &choice->child);
+            YANG_CHECK_STMTVER_RET(ctx, "choice", "choice");
+            LY_CHECK_RET(parse_choice(ctx, data, (struct lysp_node*)choice, &choice->child));
             break;
         case YANG_CONTAINER:
-            ret = parse_container(ctx, data, &choice->child);
+            LY_CHECK_RET(parse_container(ctx, data, (struct lysp_node*)choice, &choice->child));
             break;
         case YANG_LEAF:
-            ret = parse_leaf(ctx, data, &choice->child);
+            LY_CHECK_RET(parse_leaf(ctx, data, (struct lysp_node*)choice, &choice->child));
             break;
         case YANG_LEAF_LIST:
-            ret = parse_leaflist(ctx, data, &choice->child);
+            LY_CHECK_RET(parse_leaflist(ctx, data, (struct lysp_node*)choice, &choice->child));
             break;
         case YANG_LIST:
-            ret = parse_list(ctx, data, &choice->child);
+            LY_CHECK_RET(parse_list(ctx, data, (struct lysp_node*)choice, &choice->child));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &choice->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &choice->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "choice");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
     LY_CHECK_RET(ret);
-
+checks:
+    if ((choice->flags & LYS_MAND_TRUE) && choice->dflt) {
+        LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMSCOMB, "mandatory", "default", "choice");
+        return LY_EVALID;
+    }
     return ret;
 }
 
@@ -3613,7 +3527,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_container(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings)
+parse_container(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
 {
     LY_ERR ret = 0;
     char *buf, *word;
@@ -3621,11 +3535,13 @@
     enum yang_keyword kw;
     struct lysp_node *iter;
     struct lysp_node_container *cont;
+    unsigned int u;
 
     /* create structure */
     cont = calloc(1, sizeof *cont);
     LY_CHECK_ERR_RET(!cont, LOGMEM(ctx->ctx), LY_EMEM);
     cont->nodetype = LYS_CONTAINER;
+    cont->parent = parent;
 
     /* insert into siblings */
     if (!*siblings) {
@@ -3636,87 +3552,90 @@
     }
 
     /* get name */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, cont->name, word, word_len);
 
     /* parse substatements */
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CONFIG:
-            ret = parse_config(ctx, data, &cont->flags, &cont->exts);
+            LY_CHECK_RET(parse_config(ctx, data, &cont->flags, &cont->exts));
             break;
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &cont->dsc, Y_STR_ARG, &cont->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &cont->dsc, Y_STR_ARG, &cont->exts));
             break;
         case YANG_IF_FEATURE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &cont->iffeatures, Y_STR_ARG, &cont->exts);
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &cont->iffeatures, Y_STR_ARG, &cont->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &cont->ref, Y_STR_ARG, &cont->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &cont->ref, Y_STR_ARG, &cont->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &cont->flags, &cont->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &cont->flags, &cont->exts));
             break;
         case YANG_WHEN:
-            ret = parse_when(ctx, data, &cont->when);
+            LY_CHECK_RET(parse_when(ctx, data, &cont->when));
             break;
         case YANG_PRESENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PRESENCE, 0, &cont->presence, Y_STR_ARG, &cont->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_PRESENCE, 0, &cont->presence, Y_STR_ARG, &cont->exts));
             break;
 
         case YANG_ANYDATA:
+            YANG_CHECK_STMTVER_RET(ctx, "anydata", "container");
+            /* fall through */
         case YANG_ANYXML:
-            ret = parse_any(ctx, data, kw, &cont->child);
+            LY_CHECK_RET(parse_any(ctx, data, kw, (struct lysp_node*)cont, &cont->child));
             break;
         case YANG_CHOICE:
-            ret = parse_choice(ctx, data, &cont->child);
+            LY_CHECK_RET(parse_choice(ctx, data, (struct lysp_node*)cont, &cont->child));
             break;
         case YANG_CONTAINER:
-            ret = parse_container(ctx, data, &cont->child);
+            LY_CHECK_RET(parse_container(ctx, data, (struct lysp_node*)cont, &cont->child));
             break;
         case YANG_LEAF:
-            ret = parse_leaf(ctx, data, &cont->child);
+            LY_CHECK_RET(parse_leaf(ctx, data, (struct lysp_node*)cont, &cont->child));
             break;
         case YANG_LEAF_LIST:
-            ret = parse_leaflist(ctx, data, &cont->child);
+            LY_CHECK_RET(parse_leaflist(ctx, data, (struct lysp_node*)cont, &cont->child));
             break;
         case YANG_LIST:
-            ret = parse_list(ctx, data, &cont->child);
+            LY_CHECK_RET(parse_list(ctx, data, (struct lysp_node*)cont, &cont->child));
             break;
         case YANG_USES:
-            ret = parse_uses(ctx, data, &cont->child);
+            LY_CHECK_RET(parse_uses(ctx, data, (struct lysp_node*)cont, &cont->child));
             break;
 
         case YANG_TYPEDEF:
-            ret = parse_typedef(ctx, data, &cont->typedefs);
+            LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)cont, data, &cont->typedefs));
             break;
         case YANG_MUST:
-            ret = parse_restrs(ctx, data, kw, &cont->musts);
+            LY_CHECK_RET(parse_restrs(ctx, data, kw, &cont->musts));
             break;
         case YANG_ACTION:
-            ret = parse_action(ctx, data, &cont->actions);
+            YANG_CHECK_STMTVER_RET(ctx, "action", "container");
+            LY_CHECK_RET(parse_action(ctx, data, (struct lysp_node*)cont, &cont->actions));
             break;
         case YANG_GROUPING:
-            ret = parse_grouping(ctx, data, &cont->groupings);
+            LY_CHECK_RET(parse_grouping(ctx, data, (struct lysp_node*)cont, &cont->groupings));
             break;
         case YANG_NOTIFICATION:
-            ret = parse_notif(ctx, data, &cont->notifs);
+            YANG_CHECK_STMTVER_RET(ctx, "notification", "container");
+            LY_CHECK_RET(parse_notif(ctx, data, (struct lysp_node*)cont, &cont->notifs));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &cont->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &cont->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "container");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
+    /* finalize parent pointers to the reallocated items */
+    LY_ARRAY_FOR(cont->groupings, u) {
+        LY_LIST_FOR(cont->groupings[u].data, iter) {
+            iter->parent = (struct lysp_node*)&cont->groupings[u];
+        }
+    }
     return ret;
 }
 
@@ -3730,19 +3649,21 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_list(struct ly_parser_ctx *ctx, const char **data, struct lysp_node **siblings)
+parse_list(struct ly_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
     struct lysp_node *iter;
     struct lysp_node_list *list;
+    unsigned int u;
 
     /* create structure */
     list = calloc(1, sizeof *list);
     LY_CHECK_ERR_RET(!list, LOGMEM(ctx->ctx), LY_EMEM);
     list->nodetype = LYS_LIST;
+    list->parent = parent;
 
     /* insert into siblings */
     if (!*siblings) {
@@ -3753,98 +3674,110 @@
     }
 
     /* get name */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, list->name, word, word_len);
 
     /* parse substatements */
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
         switch (kw) {
         case YANG_CONFIG:
-            ret = parse_config(ctx, data, &list->flags, &list->exts);
+            LY_CHECK_RET(parse_config(ctx, data, &list->flags, &list->exts));
             break;
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &list->dsc, Y_STR_ARG, &list->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &list->dsc, Y_STR_ARG, &list->exts));
             break;
         case YANG_IF_FEATURE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &list->iffeatures, Y_STR_ARG, &list->exts);
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &list->iffeatures, Y_STR_ARG, &list->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &list->ref, Y_STR_ARG, &list->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &list->ref, Y_STR_ARG, &list->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &list->flags, &list->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &list->flags, &list->exts));
             break;
         case YANG_WHEN:
-            ret = parse_when(ctx, data, &list->when);
+            LY_CHECK_RET(parse_when(ctx, data, &list->when));
             break;
         case YANG_KEY:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_KEY, 0, &list->key, Y_STR_ARG, &list->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_KEY, 0, &list->key, Y_STR_ARG, &list->exts));
             break;
         case YANG_MAX_ELEMENTS:
-            ret = parse_maxelements(ctx, data, &list->max, &list->flags, &list->exts);
+            LY_CHECK_RET(parse_maxelements(ctx, data, &list->max, &list->flags, &list->exts));
             break;
         case YANG_MIN_ELEMENTS:
-            ret = parse_minelements(ctx, data, &list->min, &list->flags, &list->exts);
+            LY_CHECK_RET(parse_minelements(ctx, data, &list->min, &list->flags, &list->exts));
             break;
         case YANG_ORDERED_BY:
-            ret = parse_orderedby(ctx, data, &list->flags, &list->exts);
+            LY_CHECK_RET(parse_orderedby(ctx, data, &list->flags, &list->exts));
             break;
         case YANG_UNIQUE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_UNIQUE, &list->uniques, Y_STR_ARG, &list->exts);
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_UNIQUE, &list->uniques, Y_STR_ARG, &list->exts));
             break;
 
         case YANG_ANYDATA:
+            YANG_CHECK_STMTVER_RET(ctx, "anydata", "list");
+            /* fall through */
         case YANG_ANYXML:
-            ret = parse_any(ctx, data, kw, &list->child);
+            LY_CHECK_RET(parse_any(ctx, data, kw, (struct lysp_node*)list, &list->child));
             break;
         case YANG_CHOICE:
-            ret = parse_choice(ctx, data, &list->child);
+            LY_CHECK_RET(parse_choice(ctx, data, (struct lysp_node*)list, &list->child));
             break;
         case YANG_CONTAINER:
-            ret = parse_container(ctx, data, &list->child);
+            LY_CHECK_RET(parse_container(ctx, data, (struct lysp_node*)list, &list->child));
             break;
         case YANG_LEAF:
-            ret = parse_leaf(ctx, data, &list->child);
+            LY_CHECK_RET(parse_leaf(ctx, data, (struct lysp_node*)list, &list->child));
             break;
         case YANG_LEAF_LIST:
-            ret = parse_leaflist(ctx, data, &list->child);
+            LY_CHECK_RET(parse_leaflist(ctx, data, (struct lysp_node*)list, &list->child));
             break;
         case YANG_LIST:
-            ret = parse_list(ctx, data, &list->child);
+            LY_CHECK_RET(parse_list(ctx, data, (struct lysp_node*)list, &list->child));
             break;
         case YANG_USES:
-            ret = parse_uses(ctx, data, &list->child);
+            LY_CHECK_RET(parse_uses(ctx, data, (struct lysp_node*)list, &list->child));
             break;
 
         case YANG_TYPEDEF:
-            ret = parse_typedef(ctx, data, &list->typedefs);
+            LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)list, data, &list->typedefs));
             break;
         case YANG_MUST:
-            ret = parse_restrs(ctx, data, kw, &list->musts);
+            LY_CHECK_RET(parse_restrs(ctx, data, kw, &list->musts));
             break;
         case YANG_ACTION:
-            ret = parse_action(ctx, data, &list->actions);
+            YANG_CHECK_STMTVER_RET(ctx, "action", "list");
+            LY_CHECK_RET(parse_action(ctx, data, (struct lysp_node*)list, &list->actions));
             break;
         case YANG_GROUPING:
-            ret = parse_grouping(ctx, data, &list->groupings);
+            LY_CHECK_RET(parse_grouping(ctx, data, (struct lysp_node*)list, &list->groupings));
             break;
         case YANG_NOTIFICATION:
-            ret = parse_notif(ctx, data, &list->notifs);
+            YANG_CHECK_STMTVER_RET(ctx, "notification", "list");
+            LY_CHECK_RET(parse_notif(ctx, data, (struct lysp_node*)list, &list->notifs));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &list->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &list->exts));
             break;
         default:
-            LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "container");
+            LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "list");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
     LY_CHECK_RET(ret);
+    /* finalize parent pointers to the reallocated items */
+    LY_ARRAY_FOR(list->groupings, u) {
+        LY_LIST_FOR(list->groupings[u].data, iter) {
+            iter->parent = (struct lysp_node*)&list->groupings[u];
+        }
+    }
+checks:
+    if (list->max && list->min > list->max) {
+        LOGVAL_YANG(ctx, LYVE_SEMANTICS,
+                    "Invalid combination of min-elements and max-elements: min value %u is bigger than the max value %u.",
+                    list->min, list->max);
+        return LY_EVALID;
+    }
 
     return ret;
 }
@@ -3862,7 +3795,7 @@
 static LY_ERR
 parse_yinelement(struct ly_parser_ctx *ctx, const char **data, uint16_t *flags, struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -3873,8 +3806,7 @@
     }
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     if ((word_len == 4) && !strncmp(word, "true", word_len)) {
         *flags |= LYS_YINELEM_TRUE;
@@ -3887,21 +3819,16 @@
     }
     free(buf);
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_YINELEM, 0, exts);
-            LY_CHECK_RET(ret);
-            break;
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_YINELEM, 0, exts));
+            LY_CHECK_RET(ret);            break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "yin-element");
             return LY_EVALID;
         }
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -3919,7 +3846,7 @@
 static LY_ERR
 parse_argument(struct ly_parser_ctx *ctx, const char **data, const char **argument, uint16_t *flags, struct lysp_ext_instance **exts)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -3930,28 +3857,22 @@
     }
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, *argument, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
 
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_YIN_ELEMENT:
-            ret = parse_yinelement(ctx, data, flags, exts);
+            LY_CHECK_RET(parse_yinelement(ctx, data, flags, exts));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_ARGUMENT, 0, exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_ARGUMENT, 0, exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "argument");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -3967,7 +3888,7 @@
 static LY_ERR
 parse_extension(struct ly_parser_ctx *ctx, const char **data, struct lysp_ext **extensions)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -3976,37 +3897,31 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *extensions, ex, LY_EMEM);
 
     /* get value */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, ex->name, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
 
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &ex->dsc, Y_STR_ARG, &ex->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &ex->dsc, Y_STR_ARG, &ex->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &ex->ref, Y_STR_ARG, &ex->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &ex->ref, Y_STR_ARG, &ex->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &ex->flags, &ex->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &ex->flags, &ex->exts));
             break;
         case YANG_ARGUMENT:
-            ret = parse_argument(ctx, data, &ex->argument, &ex->flags, &ex->exts);
+            LY_CHECK_RET(parse_argument(ctx, data, &ex->argument, &ex->flags, &ex->exts));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &ex->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &ex->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "extension");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -4022,7 +3937,7 @@
 static LY_ERR
 parse_deviate(struct ly_parser_ctx *ctx, const char **data, struct lysp_deviate **deviates)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len, dev_mod;
     enum yang_keyword kw;
@@ -4036,8 +3951,7 @@
     uint32_t *d_min, *d_max;
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     if ((word_len == 13) && !strncmp(word, "not-supported", word_len)) {
         dev_mod = LYS_DEV_NOT_SUPPORTED;
@@ -4105,9 +4019,7 @@
         iter->next = d;
     }
 
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
-
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_CONFIG:
             switch (dev_mod) {
@@ -4116,7 +4028,7 @@
                 LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             default:
-                ret = parse_config(ctx, data, d_flags, &d->exts);
+                LY_CHECK_RET(parse_config(ctx, data, d_flags, &d->exts));
                 break;
             }
             break;
@@ -4126,10 +4038,10 @@
                 LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             case LYS_DEV_REPLACE:
-                ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &d_rpl->dflt, Y_STR_ARG, &d->exts);
+                LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &d_rpl->dflt, Y_STR_ARG, &d->exts));
                 break;
             default:
-                ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_DEFAULT, d_dflts, Y_STR_ARG, &d->exts);
+                LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_DEFAULT, d_dflts, Y_STR_ARG, &d->exts));
                 break;
             }
             break;
@@ -4140,7 +4052,7 @@
                 LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             default:
-                ret = parse_mandatory(ctx, data, d_flags, &d->exts);
+                LY_CHECK_RET(parse_mandatory(ctx, data, d_flags, &d->exts));
                 break;
             }
             break;
@@ -4151,7 +4063,7 @@
                 LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             default:
-                ret = parse_maxelements(ctx, data, d_max, d_flags, &d->exts);
+                LY_CHECK_RET(parse_maxelements(ctx, data, d_max, d_flags, &d->exts));
                 break;
             }
             break;
@@ -4162,7 +4074,7 @@
                 LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             default:
-                ret = parse_minelements(ctx, data, d_min, d_flags, &d->exts);
+                LY_CHECK_RET(parse_minelements(ctx, data, d_min, d_flags, &d->exts));
                 break;
             }
             break;
@@ -4173,7 +4085,7 @@
                 LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             default:
-                ret = parse_restrs(ctx, data, kw, d_musts);
+                LY_CHECK_RET(parse_restrs(ctx, data, kw, d_musts));
                 break;
             }
             break;
@@ -4191,7 +4103,7 @@
                 }
                 d_rpl->type = calloc(1, sizeof *d_rpl->type);
                 LY_CHECK_ERR_RET(!d_rpl->type, LOGMEM(ctx->ctx), LY_EMEM);
-                ret = parse_type(ctx, data, d_rpl->type);
+                LY_CHECK_RET(parse_type(ctx, data, d_rpl->type));
                 break;
             }
             break;
@@ -4202,7 +4114,7 @@
                 LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             default:
-                ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_UNIQUE, d_uniques, Y_STR_ARG, &d->exts);
+                LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_UNIQUE, d_uniques, Y_STR_ARG, &d->exts));
                 break;
             }
             break;
@@ -4212,21 +4124,18 @@
                 LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             default:
-                ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, d_units, Y_STR_ARG, &d->exts);
+                LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, d_units, Y_STR_ARG, &d->exts));
                 break;
             }
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &d->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &d->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "deviate");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -4242,7 +4151,7 @@
 static LY_ERR
 parse_deviation(struct ly_parser_ctx *ctx, const char **data, struct lysp_deviation **deviations)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -4251,34 +4160,30 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *deviations, dev, LY_EMEM);
 
     /* get value */
-    ret = get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, dev->nodeid, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
 
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
         switch (kw) {
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &dev->dsc, Y_STR_ARG, &dev->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &dev->dsc, Y_STR_ARG, &dev->exts));
             break;
         case YANG_DEVIATE:
-            ret = parse_deviate(ctx, data, &dev->deviates);
+            LY_CHECK_RET(parse_deviate(ctx, data, &dev->deviates));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &dev->ref, Y_STR_ARG, &dev->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &dev->ref, Y_STR_ARG, &dev->exts));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &dev->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &dev->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "deviation");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
     LY_CHECK_RET(ret);
-
+checks:
     /* mandatory substatements */
     if (!dev->deviates) {
         LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "deviate", "deviation");
@@ -4300,7 +4205,7 @@
 static LY_ERR
 parse_feature(struct ly_parser_ctx *ctx, const char **data, struct lysp_feature **features)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -4309,37 +4214,31 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *features, feat, LY_EMEM);
 
     /* get value */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, feat->name, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
 
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &feat->dsc, Y_STR_ARG, &feat->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &feat->dsc, Y_STR_ARG, &feat->exts));
             break;
         case YANG_IF_FEATURE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &feat->iffeatures, Y_STR_ARG, &feat->exts);
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &feat->iffeatures, Y_STR_ARG, &feat->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &feat->ref, Y_STR_ARG, &feat->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &feat->ref, Y_STR_ARG, &feat->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &feat->flags, &feat->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &feat->flags, &feat->exts));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &feat->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &feat->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "feature");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-    LY_CHECK_RET(ret);
-
     return ret;
 }
 
@@ -4355,7 +4254,7 @@
 static LY_ERR
 parse_identity(struct ly_parser_ctx *ctx, const char **data, struct lysp_ident **identities)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
     size_t word_len;
     enum yang_keyword kw;
@@ -4364,39 +4263,39 @@
     LY_ARRAY_NEW_RET(ctx->ctx, *identities, ident, LY_EMEM);
 
     /* get value */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, ident->name, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
 
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &ident->dsc, Y_STR_ARG, &ident->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &ident->dsc, Y_STR_ARG, &ident->exts));
             break;
         case YANG_IF_FEATURE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &ident->iffeatures, Y_STR_ARG, &ident->exts);
+            YANG_CHECK_STMTVER_RET(ctx, "if-feature", "identity");
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_IFFEATURE, &ident->iffeatures, Y_STR_ARG, &ident->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &ident->ref, Y_STR_ARG, &ident->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &ident->ref, Y_STR_ARG, &ident->exts));
             break;
         case YANG_STATUS:
-            ret = parse_status(ctx, data, &ident->flags, &ident->exts);
+            LY_CHECK_RET(parse_status(ctx, data, &ident->flags, &ident->exts));
             break;
         case YANG_BASE:
-            ret = parse_text_fields(ctx, data, LYEXT_SUBSTMT_BASE, &ident->bases, Y_PREF_IDENTIF_ARG, &ident->exts);
+            if (ident->bases && ctx->mod->version < 2) {
+                LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Identity can be derived from multiple base identities only in YANG 1.1 modules");
+                return LY_EVALID;
+            }
+            LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_BASE, &ident->bases, Y_PREF_IDENTIF_ARG, &ident->exts));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &ident->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &ident->exts));
             break;
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "identity");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
-
     return ret;
 }
 
@@ -4417,14 +4316,15 @@
     size_t word_len;
     enum yang_keyword kw, prev_kw = 0;
     enum yang_module_stmt mod_stmt = Y_MOD_MODULE_HEADER;
+    struct lysp_module *dup;
+    struct lysp_node *child;
+    unsigned int u;
 
     /* (sub)module name */
-    ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
-    LY_CHECK_RET(ret);
-
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, mod->name, word, word_len);
-    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
-        LY_CHECK_RET(ret);
+
+    YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
 
 #define CHECK_ORDER(SECTION) \
         if (mod_stmt > SECTION) {LOGVAL_YANG(ctx, LY_VCODE_INORD, ly_stmt2str(kw), ly_stmt2str(prev_kw)); return LY_EVALID;}mod_stmt = SECTION
@@ -4497,109 +4397,119 @@
         switch (kw) {
         /* module header */
         case YANG_YANG_VERSION:
-            ret = parse_yangversion(ctx, data, mod);
+            LY_CHECK_RET(parse_yangversion(ctx, data, mod));
             break;
         case YANG_NAMESPACE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_NAMESPACE, 0, &mod->ns, Y_STR_ARG, &mod->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_NAMESPACE, 0, &mod->ns, Y_STR_ARG, &mod->exts));
             break;
         case YANG_PREFIX:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_PREFIX, 0, &mod->prefix, Y_IDENTIF_ARG, &mod->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_PREFIX, 0, &mod->prefix, Y_IDENTIF_ARG, &mod->exts));
             LY_CHECK_RET(lysp_check_prefix(ctx, mod, &mod->prefix), LY_EVALID);
             break;
         case YANG_BELONGS_TO:
-            ret = parse_belongsto(ctx, data, &mod->belongsto, &mod->prefix, &mod->exts);
+            LY_CHECK_RET(parse_belongsto(ctx, data, &mod->belongsto, &mod->prefix, &mod->exts));
             break;
 
         /* linkage */
         case YANG_INCLUDE:
-            ret = parse_include(ctx, data, &mod->includes);
+            LY_CHECK_RET(parse_include(ctx, data, mod));
             break;
         case YANG_IMPORT:
-            ret = parse_import(ctx, data, mod);
+            LY_CHECK_RET(parse_import(ctx, data, mod));
             break;
 
         /* meta */
         case YANG_ORGANIZATION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_ORGANIZATION, 0, &mod->org, Y_STR_ARG, &mod->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_ORGANIZATION, 0, &mod->org, Y_STR_ARG, &mod->exts));
             break;
         case YANG_CONTACT:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_CONTACT, 0, &mod->contact, Y_STR_ARG, &mod->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_CONTACT, 0, &mod->contact, Y_STR_ARG, &mod->exts));
             break;
         case YANG_DESCRIPTION:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &mod->dsc, Y_STR_ARG, &mod->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DESCRIPTION, 0, &mod->dsc, Y_STR_ARG, &mod->exts));
             break;
         case YANG_REFERENCE:
-            ret = parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &mod->ref, Y_STR_ARG, &mod->exts);
+            LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_REFERENCE, 0, &mod->ref, Y_STR_ARG, &mod->exts));
             break;
 
         /* revision */
         case YANG_REVISION:
-            ret = parse_revision(ctx, data, &mod->revs);
+            LY_CHECK_RET(parse_revision(ctx, data, &mod->revs));
             break;
 
         /* body */
         case YANG_ANYDATA:
+            YANG_CHECK_STMTVER_RET(ctx, "anydata", mod->submodule ? "submodule" : "module");
+            /* fall through */
         case YANG_ANYXML:
-            ret = parse_any(ctx, data, kw, &mod->data);
+            LY_CHECK_RET(parse_any(ctx, data, kw, NULL, &mod->data));
             break;
         case YANG_CHOICE:
-            ret = parse_choice(ctx, data, &mod->data);
+            LY_CHECK_RET(parse_choice(ctx, data, NULL, &mod->data));
             break;
         case YANG_CONTAINER:
-            ret = parse_container(ctx, data, &mod->data);
+            LY_CHECK_RET(parse_container(ctx, data, NULL, &mod->data));
             break;
         case YANG_LEAF:
-            ret = parse_leaf(ctx, data, &mod->data);
+            LY_CHECK_RET(parse_leaf(ctx, data, NULL, &mod->data));
             break;
         case YANG_LEAF_LIST:
-            ret = parse_leaflist(ctx, data, &mod->data);
+            LY_CHECK_RET(parse_leaflist(ctx, data, NULL, &mod->data));
             break;
         case YANG_LIST:
-            ret = parse_list(ctx, data, &mod->data);
+            LY_CHECK_RET(parse_list(ctx, data, NULL, &mod->data));
             break;
         case YANG_USES:
-            ret = parse_uses(ctx, data, &mod->data);
+            LY_CHECK_RET(parse_uses(ctx, data, NULL, &mod->data));
             break;
 
         case YANG_AUGMENT:
-            ret = parse_augment(ctx, data, &mod->augments);
+            LY_CHECK_RET(parse_augment(ctx, data, NULL, &mod->augments));
             break;
         case YANG_DEVIATION:
-            ret = parse_deviation(ctx, data, &mod->deviations);
+            LY_CHECK_RET(parse_deviation(ctx, data, &mod->deviations));
             break;
         case YANG_EXTENSION:
-            ret = parse_extension(ctx, data, &mod->extensions);
+            LY_CHECK_RET(parse_extension(ctx, data, &mod->extensions));
             break;
         case YANG_FEATURE:
-            ret = parse_feature(ctx, data, &mod->features);
+            LY_CHECK_RET(parse_feature(ctx, data, &mod->features));
             break;
         case YANG_GROUPING:
-            ret = parse_grouping(ctx, data, &mod->groupings);
+            LY_CHECK_RET(parse_grouping(ctx, data, NULL, &mod->groupings));
             break;
         case YANG_IDENTITY:
-            ret = parse_identity(ctx, data, &mod->identities);
+            LY_CHECK_RET(parse_identity(ctx, data, &mod->identities));
             break;
         case YANG_NOTIFICATION:
-            ret = parse_notif(ctx, data, &mod->notifs);
+            LY_CHECK_RET(parse_notif(ctx, data, NULL, &mod->notifs));
             break;
         case YANG_RPC:
-            ret = parse_action(ctx, data, &mod->rpcs);
+            LY_CHECK_RET(parse_action(ctx, data, NULL, &mod->rpcs));
             break;
         case YANG_TYPEDEF:
-            ret = parse_typedef(ctx, data, &mod->typedefs);
+            LY_CHECK_RET(parse_typedef(ctx, NULL, data, &mod->typedefs));
             break;
         case YANG_CUSTOM:
-            ret = parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &mod->exts);
+            LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &mod->exts));
             break;
 
         default:
             LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), mod->submodule ? "submodule" : "module");
             return LY_EVALID;
         }
-        LY_CHECK_RET(ret);
     }
     LY_CHECK_RET(ret);
 
+    /* finalize parent pointers to the reallocated items */
+    LY_ARRAY_FOR(mod->groupings, u) {
+        LY_LIST_FOR(mod->groupings[u].data, child) {
+            child->parent = (struct lysp_node*)&mod->groupings[u];
+        }
+    }
+    /* TODO the same finalization for rpcs and notifications, do also in the relevant nodes */
+
+checks:
     /* mandatory substatements */
     if (mod->submodule) {
         if (!mod->belongsto) {
@@ -4616,62 +4526,69 @@
         }
     }
 
+    /* submodules share the namespace with the module names, so there must not be
+     * a submodule of the same name in the context, no need for revision matching */
+    dup = ly_ctx_get_submodule(ctx->ctx, NULL, mod->name, NULL);
+    if (dup && (!mod->submodule || strcmp(dup->belongsto, mod->belongsto))) {
+        LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Name collision between %s of name \"%s\".",
+                    mod->submodule ? "submodules" : "module and submodule", mod->name);
+        return LY_EVALID;
+    }
+
     return ret;
 }
 
 LY_ERR
-yang_parse(struct ly_ctx *ctx, const char *data, struct lysp_module **mod_p)
+yang_parse(struct ly_parser_ctx *context, const char *data, struct lysp_module **mod_p)
 {
-    LY_ERR ret = 0;
+    LY_ERR ret = LY_SUCCESS;
     char *word, *buf;
     size_t word_len;
     enum yang_keyword kw;
     struct lysp_module *mod = NULL;
-    struct ly_parser_ctx context = {0};
-
-    context.ctx = ctx;
-    context.line = 1;
 
     /* "module"/"submodule" */
-    ret = get_keyword(&context, &data, &kw, &word, &word_len);
-    LY_CHECK_GOTO(ret, error);
+    ret = get_keyword(context, &data, &kw, &word, &word_len);
+    LY_CHECK_GOTO(ret, cleanup);
 
     if ((kw != YANG_MODULE) && (kw != YANG_SUBMODULE)) {
-        LOGVAL_YANG(&context, LYVE_SYNTAX, "Invalid keyword \"%s\", expected \"module\" or \"submodule\".",
+        LOGVAL_YANG(context, LYVE_SYNTAX, "Invalid keyword \"%s\", expected \"module\" or \"submodule\".",
                ly_stmt2str(kw));
-        goto error;
+        goto cleanup;
     }
 
     mod = calloc(1, sizeof *mod);
-    LY_CHECK_ERR_GOTO(!mod, LOGMEM(ctx), error);
+    LY_CHECK_ERR_GOTO(!mod, LOGMEM(context->ctx), cleanup);
     if (kw == YANG_SUBMODULE) {
         mod->submodule = 1;
     }
-    mod->ctx = ctx;
+    mod->parsing = 1;
+    mod->ctx = context->ctx;
+    context->mod = mod;
 
     /* substatements */
-    ret = parse_sub_module(&context, &data, mod);
-    LY_CHECK_GOTO(ret, error);
+    ret = parse_sub_module(context, &data, mod);
+    LY_CHECK_GOTO(ret, cleanup);
 
     /* read some trailing spaces or new lines */
-    ret = get_argument(&context, &data, Y_MAYBE_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_GOTO(ret, error);
+    ret = get_argument(context, &data, Y_MAYBE_STR_ARG, &word, &buf, &word_len);
+    LY_CHECK_GOTO(ret, cleanup);
 
     if (word) {
-        LOGVAL_YANG(&context, LYVE_SYNTAX, "Invalid character sequence \"%.*s\", expected end-of-file.",
+        LOGVAL_YANG(context, LYVE_SYNTAX, "Invalid character sequence \"%.*s\", expected end-of-file.",
                word_len, word);
         free(buf);
-        goto error;
+        goto cleanup;
     }
     assert(!buf);
 
-    /* make sure that the newest revision is at position 0 */
-    lysp_sort_revisions(mod->revs);
-
+    mod->parsing = 0;
     *mod_p = mod;
-    return ret;
 
-error:
-    lysp_module_free(mod);
+cleanup:
+    if (ret) {
+        lysp_module_free(mod);
+    }
+
     return ret;
 }
diff --git a/src/parser_yin.c b/src/parser_yin.c
index 3501585..e8b0333 100644
--- a/src/parser_yin.c
+++ b/src/parser_yin.c
@@ -257,7 +257,6 @@
 
     while (xml_ctx->status == LYXML_ELEMENT || xml_ctx->status == LYXML_ELEM_CONTENT) {
         ret = lyxml_get_element(xml_ctx, data, &prefix, &prefix_len, &name, &name_len);
-        printf("%s\n\n\n", name);
         LY_CHECK_ERR_RET(ret != LY_SUCCESS, LOGMEM(xml_ctx->ctx), LY_EMEM);
         kw = match_keyword(name);
 
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 3960bb3..dd085de 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -11,542 +11,165 @@
  *
  *     https://opensource.org/licenses/BSD-3-Clause
  */
-#define _DEFAULT_SOURCE
 
-#include <ctype.h>
+#include "common.h"
+
+#include <dirent.h>
 #include <errno.h>
+#include <limits.h>
 #include <fcntl.h>
-#include <linux/limits.h>
 #include <stdio.h>
-#include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include "libyang.h"
-#include "common.h"
 #include "context.h"
 #include "tree_schema_internal.h"
+#include "xpath.h"
 
-#define FREE_ARRAY(CTX, ARRAY, FUNC) {uint64_t c__; LY_ARRAY_FOR(ARRAY, c__){FUNC(CTX, &ARRAY[c__], dict);}LY_ARRAY_FREE(ARRAY);}
-#define FREE_MEMBER(CTX, MEMBER, FUNC) if (MEMBER) {FUNC(CTX, MEMBER, dict);free(MEMBER);}
-#define FREE_STRING(CTX, STRING, DICT) if (DICT && STRING) {lydict_remove(CTX, STRING);}
-#define FREE_STRINGS(CTX, ARRAY, DICT) {uint64_t c__; LY_ARRAY_FOR(ARRAY, c__){FREE_STRING(CTX, ARRAY[c__], DICT);}LY_ARRAY_FREE(ARRAY);}
-
-static void lysp_grp_free(struct ly_ctx *ctx, struct lysp_grp *grp, int dict);
-static void lysp_node_free(struct ly_ctx *ctx, struct lysp_node *node, int dict);
-
-#define LYSC_CTX_BUFSIZE 4086
-struct lysc_ctx {
-    struct lysc_module *mod;
-    uint16_t path_len;
-    char path[LYSC_CTX_BUFSIZE];
-};
-
-static void
-lysp_stmt_free(struct ly_ctx *ctx, struct lysp_stmt *stmt, int dict)
+API const struct lysc_node *
+lys_getnext(const struct lysc_node *last, const struct lysc_node *parent, const struct lysc_module *module, int options)
 {
-    struct lysp_stmt *child, *next;
+    const struct lysc_node *next;
+    struct lysc_node **snode;
 
-    FREE_STRING(ctx, stmt->stmt, dict);
-    FREE_STRING(ctx, stmt->arg, dict);
+    LY_CHECK_ARG_RET(NULL, parent || module, NULL);
 
-    LY_LIST_FOR_SAFE(stmt->child, next, child) {
-        lysp_stmt_free(ctx, child, dict);
-    }
+    if (!last) {
+        /* first call */
 
-    free(stmt);
-}
-
-static void
-lysp_ext_instance_free(struct ly_ctx *ctx, struct lysp_ext_instance *ext, int dict)
-{
-    struct lysp_stmt *stmt, *next;
-
-    FREE_STRING(ctx, ext->name, dict);
-    FREE_STRING(ctx, ext->argument, dict);
-
-    LY_LIST_FOR_SAFE(ext->child, next, stmt) {
-        lysp_stmt_free(ctx, stmt, dict);
-    }
-}
-
-static void
-lysp_import_free(struct ly_ctx *ctx, struct lysp_import *import, int dict)
-{
-    FREE_STRING(ctx, import->name, dict);
-    FREE_STRING(ctx, import->prefix, dict);
-    FREE_STRING(ctx, import->dsc, dict);
-    FREE_STRING(ctx, import->ref, dict);
-    FREE_ARRAY(ctx, import->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_include_free(struct ly_ctx *ctx, struct lysp_include *include, int dict)
-{
-    FREE_STRING(ctx, include->name, dict);
-    FREE_STRING(ctx, include->dsc, dict);
-    FREE_STRING(ctx, include->ref, dict);
-    FREE_ARRAY(ctx, include->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_revision_free(struct ly_ctx *ctx, struct lysp_revision *rev, int dict)
-{
-    FREE_STRING(ctx, rev->dsc, dict);
-    FREE_STRING(ctx, rev->ref, dict);
-    FREE_ARRAY(ctx, rev->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_ext_free(struct ly_ctx *ctx, struct lysp_ext *ext, int dict)
-{
-    FREE_STRING(ctx, ext->name, dict);
-    FREE_STRING(ctx, ext->argument, dict);
-    FREE_STRING(ctx, ext->dsc, dict);
-    FREE_STRING(ctx, ext->ref, dict);
-    FREE_ARRAY(ctx, ext->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_feature_free(struct ly_ctx *ctx, struct lysp_feature *feat, int dict)
-{
-    FREE_STRING(ctx, feat->name, dict);
-    FREE_STRINGS(ctx, feat->iffeatures, 1);
-    FREE_STRING(ctx, feat->dsc, dict);
-    FREE_STRING(ctx, feat->ref, dict);
-    FREE_ARRAY(ctx, feat->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_ident_free(struct ly_ctx *ctx, struct lysp_ident *ident, int dict)
-{
-    FREE_STRING(ctx, ident->name, dict);
-    FREE_STRINGS(ctx, ident->iffeatures, 1);
-    FREE_STRINGS(ctx, ident->bases, dict);
-    FREE_STRING(ctx, ident->dsc, dict);
-    FREE_STRING(ctx, ident->ref, dict);
-    FREE_ARRAY(ctx, ident->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_restr_free(struct ly_ctx *ctx, struct lysp_restr *restr, int dict)
-{
-    FREE_STRING(ctx, restr->arg, dict);
-    FREE_STRING(ctx, restr->emsg, dict);
-    FREE_STRING(ctx, restr->eapptag, dict);
-    FREE_STRING(ctx, restr->dsc, dict);
-    FREE_STRING(ctx, restr->ref, dict);
-    FREE_ARRAY(ctx, restr->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_type_enum_free(struct ly_ctx *ctx, struct lysp_type_enum *item, int dict)
-{
-    FREE_STRING(ctx, item->name, dict);
-    FREE_STRING(ctx, item->dsc, dict);
-    FREE_STRING(ctx, item->ref, dict);
-    FREE_STRINGS(ctx, item->iffeatures, 1);
-    FREE_ARRAY(ctx, item->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_type_free(struct ly_ctx *ctx, struct lysp_type *type, int dict)
-{
-    FREE_STRING(ctx, type->name, dict);
-    FREE_MEMBER(ctx, type->range, lysp_restr_free);
-    FREE_MEMBER(ctx, type->length, lysp_restr_free);
-    FREE_ARRAY(ctx, type->patterns, lysp_restr_free);
-    FREE_ARRAY(ctx, type->enums, lysp_type_enum_free);
-    FREE_ARRAY(ctx, type->bits, lysp_type_enum_free);
-    FREE_STRING(ctx, type->path, dict);
-    FREE_STRINGS(ctx, type->bases, dict);
-    FREE_ARRAY(ctx, type->types, lysp_type_free);
-    FREE_ARRAY(ctx, type->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_tpdf_free(struct ly_ctx *ctx, struct lysp_tpdf *tpdf, int dict)
-{
-    FREE_STRING(ctx, tpdf->name, dict);
-    FREE_STRING(ctx, tpdf->units, dict);
-    FREE_STRING(ctx, tpdf->dflt, dict);
-    FREE_STRING(ctx, tpdf->dsc, dict);
-    FREE_STRING(ctx, tpdf->ref, dict);
-    FREE_ARRAY(ctx, tpdf->exts, lysp_ext_instance_free);
-    lysp_type_free(ctx, &tpdf->type, dict);
-}
-
-static void
-lysp_action_inout_free(struct ly_ctx *ctx, struct lysp_action_inout *inout, int dict)
-{
-    struct lysp_node *node, *next;
-
-    FREE_ARRAY(ctx, inout->musts, lysp_restr_free);
-    FREE_ARRAY(ctx, inout->typedefs, lysp_tpdf_free);
-    FREE_ARRAY(ctx, inout->groupings, lysp_grp_free);
-    LY_LIST_FOR_SAFE(inout->data, next, node) {
-        lysp_node_free(ctx, node, dict);
-    }
-    FREE_ARRAY(ctx, inout->exts, lysp_ext_instance_free);
-
-}
-
-static void
-lysp_action_free(struct ly_ctx *ctx, struct lysp_action *action, int dict)
-{
-    FREE_STRING(ctx, action->name, dict);
-    FREE_STRING(ctx, action->dsc, dict);
-    FREE_STRING(ctx, action->ref, dict);
-    FREE_STRINGS(ctx, action->iffeatures, 1);
-    FREE_ARRAY(ctx, action->typedefs, lysp_tpdf_free);
-    FREE_ARRAY(ctx, action->groupings, lysp_grp_free);
-    FREE_MEMBER(ctx, action->input, lysp_action_inout_free);
-    FREE_MEMBER(ctx, action->output, lysp_action_inout_free);
-    FREE_ARRAY(ctx, action->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_notif_free(struct ly_ctx *ctx, struct lysp_notif *notif, int dict)
-{
-    struct lysp_node *node, *next;
-
-    FREE_STRING(ctx, notif->name, dict);
-    FREE_STRING(ctx, notif->dsc, dict);
-    FREE_STRING(ctx, notif->ref, dict);
-    FREE_STRINGS(ctx, notif->iffeatures, 1);
-    FREE_ARRAY(ctx, notif->musts, lysp_restr_free);
-    FREE_ARRAY(ctx, notif->typedefs, lysp_tpdf_free);
-    FREE_ARRAY(ctx, notif->groupings, lysp_grp_free);
-    LY_LIST_FOR_SAFE(notif->data, next, node) {
-        lysp_node_free(ctx, node, dict);
-    }
-    FREE_ARRAY(ctx, notif->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_grp_free(struct ly_ctx *ctx, struct lysp_grp *grp, int dict)
-{
-    struct lysp_node *node, *next;
-
-    FREE_STRING(ctx, grp->name, dict);
-    FREE_STRING(ctx, grp->dsc, dict);
-    FREE_STRING(ctx, grp->ref, dict);
-    FREE_ARRAY(ctx, grp->typedefs, lysp_tpdf_free);
-    FREE_ARRAY(ctx, grp->groupings, lysp_grp_free);
-    LY_LIST_FOR_SAFE(grp->data, next, node) {
-        lysp_node_free(ctx, node, dict);
-    }
-    FREE_ARRAY(ctx, grp->actions, lysp_action_free);
-    FREE_ARRAY(ctx, grp->notifs, lysp_notif_free);
-    FREE_ARRAY(ctx, grp->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_when_free(struct ly_ctx *ctx, struct lysp_when *when, int dict)
-{
-    FREE_STRING(ctx, when->cond, dict);
-    FREE_STRING(ctx, when->dsc, dict);
-    FREE_STRING(ctx, when->ref, dict);
-    FREE_ARRAY(ctx, when->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_augment_free(struct ly_ctx *ctx, struct lysp_augment *augment, int dict)
-{
-    struct lysp_node *node, *next;
-
-    FREE_STRING(ctx, augment->nodeid, dict);
-    FREE_STRING(ctx, augment->dsc, dict);
-    FREE_STRING(ctx, augment->ref, dict);
-    FREE_MEMBER(ctx, augment->when, lysp_when_free);
-    FREE_STRINGS(ctx, augment->iffeatures, 1);
-    LY_LIST_FOR_SAFE(augment->child, next, node) {
-        lysp_node_free(ctx, node, dict);
-    }
-    FREE_ARRAY(ctx, augment->actions, lysp_action_free);
-    FREE_ARRAY(ctx, augment->notifs, lysp_notif_free);
-    FREE_ARRAY(ctx, augment->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_deviate_free(struct ly_ctx *ctx, struct lysp_deviate *d, int dict)
-{
-    struct lysp_deviate_add *add = (struct lysp_deviate_add*)d;
-    struct lysp_deviate_rpl *rpl = (struct lysp_deviate_rpl*)d;
-
-    FREE_ARRAY(ctx, d->exts, lysp_ext_instance_free);
-    switch(d->mod) {
-    case LYS_DEV_NOT_SUPPORTED:
-        /* nothing to do */
-        break;
-    case LYS_DEV_ADD:
-    case LYS_DEV_DELETE: /* compatible for dynamically allocated data */
-        FREE_STRING(ctx, add->units, dict);
-        FREE_ARRAY(ctx, add->musts, lysp_restr_free);
-        FREE_STRINGS(ctx, add->uniques, dict);
-        FREE_STRINGS(ctx, add->dflts, dict);
-        break;
-    case LYS_DEV_REPLACE:
-        FREE_MEMBER(ctx, rpl->type, lysp_type_free);
-        FREE_STRING(ctx, rpl->units, dict);
-        FREE_STRING(ctx, rpl->dflt, dict);
-        break;
-    default:
-        LOGINT(ctx);
-        break;
-    }
-}
-
-static void
-lysp_deviation_free(struct ly_ctx *ctx, struct lysp_deviation *dev, int dict)
-{
-    struct lysp_deviate *next, *iter;
-
-    FREE_STRING(ctx, dev->nodeid, dict);
-    FREE_STRING(ctx, dev->dsc, dict);
-    FREE_STRING(ctx, dev->ref, dict);
-    LY_LIST_FOR_SAFE(dev->deviates, next, iter) {
-        lysp_deviate_free(ctx, iter, dict);
-        free(iter);
-    }
-    FREE_ARRAY(ctx, dev->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_refine_free(struct ly_ctx *ctx, struct lysp_refine *ref, int dict)
-{
-    FREE_STRING(ctx, ref->nodeid, dict);
-    FREE_STRING(ctx, ref->dsc, dict);
-    FREE_STRING(ctx, ref->ref, dict);
-    FREE_STRINGS(ctx, ref->iffeatures, 1);
-    FREE_ARRAY(ctx, ref->musts, lysp_restr_free);
-    FREE_STRING(ctx, ref->presence, dict);
-    FREE_STRINGS(ctx, ref->dflts, dict);
-    FREE_ARRAY(ctx, ref->exts, lysp_ext_instance_free);
-}
-
-static void
-lysp_node_free(struct ly_ctx *ctx, struct lysp_node *node, int dict)
-{
-    struct lysp_node *child, *next;
-
-    FREE_STRING(ctx, node->name, dict);
-    FREE_STRING(ctx, node->dsc, dict);
-    FREE_STRING(ctx, node->ref, dict);
-    FREE_MEMBER(ctx, node->when, lysp_when_free);
-    FREE_STRINGS(ctx, node->iffeatures, dict);
-    FREE_ARRAY(ctx, node->exts, lysp_ext_instance_free);
-
-    switch(node->nodetype) {
-    case LYS_CONTAINER:
-        FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->musts, lysp_restr_free);
-        FREE_STRING(ctx, ((struct lysp_node_container*)node)->presence, dict);
-        FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->typedefs, lysp_tpdf_free);
-        FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->groupings, lysp_grp_free);
-        LY_LIST_FOR_SAFE(((struct lysp_node_container*)node)->child, next, child) {
-            lysp_node_free(ctx, child, dict);
+        /* get know where to start */
+        if (parent) {
+            /* schema subtree */
+            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;
+            }
+        } else {
+            /* top level data */
+            next = last = module->data;
         }
-        FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->actions, lysp_action_free);
-        FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->notifs, lysp_notif_free);
-        break;
+        if (!next) {
+            return next;
+        } else if (!(options & LYS_GETNEXT_NOSTATECHECK)) {
+            if (!lys_is_disabled(next, 0)) {
+                return next;
+            }
+            /* continue to find next available node */
+        } else {
+            return next;
+        }
+    }
+
+    next = last->next;
+repeat:
+    if (!next) {
+        /* possibly go back to parent */
+        if (last->parent != parent) {
+            last = last->parent;
+            next = last->next;
+            goto repeat;
+        }
+        return next;
+    }
+    switch (next->nodetype) {
+    case LYS_ACTION:
+    case LYS_NOTIF:
     case LYS_LEAF:
-        FREE_ARRAY(ctx, ((struct lysp_node_leaf*)node)->musts, lysp_restr_free);
-        lysp_type_free(ctx, &((struct lysp_node_leaf*)node)->type, dict);
-        FREE_STRING(ctx, ((struct lysp_node_leaf*)node)->units, dict);
-        FREE_STRING(ctx, ((struct lysp_node_leaf*)node)->dflt, dict);
-        break;
-    case LYS_LEAFLIST:
-        FREE_ARRAY(ctx, ((struct lysp_node_leaflist*)node)->musts, lysp_restr_free);
-        lysp_type_free(ctx, &((struct lysp_node_leaflist*)node)->type, dict);
-        FREE_STRING(ctx, ((struct lysp_node_leaflist*)node)->units, dict);
-        FREE_STRINGS(ctx, ((struct lysp_node_leaflist*)node)->dflts, dict);
-        break;
+    case LYS_ANYXML:
+    case LYS_ANYDATA:
     case LYS_LIST:
-        FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->musts, lysp_restr_free);
-        FREE_STRING(ctx, ((struct lysp_node_list*)node)->key, dict);
-        FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->typedefs, lysp_tpdf_free);
-        FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->groupings,  lysp_grp_free);
-        LY_LIST_FOR_SAFE(((struct lysp_node_list*)node)->child, next, child) {
-            lysp_node_free(ctx, child, dict);
+    case LYS_LEAFLIST:
+    case LYS_CASE:
+        break;
+    case LYS_CONTAINER:
+        if (!(((struct lysc_node_container *)next)->flags & LYS_PRESENCE) && (options & LYS_GETNEXT_INTONPCONT)) {
+            if (((struct lysc_node_container *)next)->child) {
+                /* go into */
+                next = ((struct lysc_node_container *)next)->child;
+            } else {
+                next = next->next;
+            }
+            goto repeat;
         }
-        FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->actions, lysp_action_free);
-        FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->notifs, lysp_notif_free);
-        FREE_STRINGS(ctx, ((struct lysp_node_list*)node)->uniques, dict);
         break;
     case LYS_CHOICE:
-        LY_LIST_FOR_SAFE(((struct lysp_node_choice*)node)->child, next, child) {
-            lysp_node_free(ctx, child, dict);
+        if (options & LYS_GETNEXT_WITHCHOICE) {
+            return next;
+        } else if ((options & LYS_GETNEXT_NOCHOICE) || !((struct lysc_node_choice *)next)->cases) {
+            next = next->next;
+        } else {
+            /* go into */
+            if (options & LYS_GETNEXT_WITHCASE) {
+                next = (struct lysc_node*)&((struct lysc_node_choice *)next)->cases;
+            } else {
+                next = ((struct lysc_node_choice *)next)->cases->child;
+            }
         }
-        FREE_STRING(ctx, ((struct lysp_node_choice*)node)->dflt, dict);
-        break;
-    case LYS_CASE:
-        LY_LIST_FOR_SAFE(((struct lysp_node_case*)node)->child, next, child) {
-            lysp_node_free(ctx, child, dict);
-        }
-        break;
-    case LYS_ANYDATA:
-    case LYS_ANYXML:
-        FREE_ARRAY(ctx, ((struct lysp_node_anydata*)node)->musts, lysp_restr_free);
-        break;
-    case LYS_USES:
-        FREE_ARRAY(ctx, ((struct lysp_node_uses*)node)->refines, lysp_refine_free);
-        FREE_ARRAY(ctx, ((struct lysp_node_uses*)node)->augments, lysp_augment_free);
-        break;
+        goto repeat;
     default:
-        LOGINT(ctx);
+        /* we should not be here */
+        LOGINT(last->module->compiled->ctx);
+        return NULL;
     }
 
-    free(node);
-}
-
-static void
-lysp_module_free_(struct lysp_module *module, int dict)
-{
-    struct ly_ctx *ctx;
-    struct lysp_node *node, *next;
-
-    LY_CHECK_ARG_RET(NULL, module,);
-    ctx = module->ctx;
-
-    FREE_STRING(ctx, module->name, dict);
-    FREE_STRING(ctx, module->filepath, dict);
-    FREE_STRING(ctx, module->ns, dict);  /* or belongs-to */
-    FREE_STRING(ctx, module->prefix, dict);
-
-    FREE_ARRAY(ctx, module->imports, lysp_import_free);
-    FREE_ARRAY(ctx, module->includes, lysp_include_free);
-
-    FREE_STRING(ctx, module->org, dict);
-    FREE_STRING(ctx, module->contact, dict);
-    FREE_STRING(ctx, module->dsc, dict);
-    FREE_STRING(ctx, module->ref, dict);
-
-    FREE_ARRAY(ctx, module->revs, lysp_revision_free);
-    FREE_ARRAY(ctx, module->extensions, lysp_ext_free);
-    FREE_ARRAY(ctx, module->features, lysp_feature_free);
-    FREE_ARRAY(ctx, module->identities, lysp_ident_free);
-    FREE_ARRAY(ctx, module->typedefs, lysp_tpdf_free);
-    FREE_ARRAY(ctx, module->groupings, lysp_grp_free);
-    LY_LIST_FOR_SAFE(module->data, next, node) {
-        lysp_node_free(ctx, node, dict);
+    if (!(options & LYS_GETNEXT_NOSTATECHECK)) {
+        /* check if the node is disabled by if-feature */
+        if (lys_is_disabled(next, 0)) {
+            next = next->next;
+            goto repeat;
+        }
     }
-    FREE_ARRAY(ctx, module->augments, lysp_augment_free);
-    FREE_ARRAY(ctx, module->rpcs, lysp_action_free);
-    FREE_ARRAY(ctx, module->notifs, lysp_notif_free);
-    FREE_ARRAY(ctx, module->deviations, lysp_deviation_free);
-    FREE_ARRAY(ctx, module->exts, lysp_ext_instance_free);
-
-    free(module);
+    return next;
 }
 
-API void
-lysp_module_free(struct lysp_module *module)
+API const struct lysc_node *
+lys_child(const struct lysc_node *parent, const struct lys_module *module,
+          const char *name, size_t name_len, uint16_t nodetype, int options)
 {
-    if (module) {
-        lysp_module_free_(module, 1);
-    }
-}
+    const struct lysc_node *node = NULL;
 
-static void
-lysc_iffeature_free(struct ly_ctx *UNUSED(ctx), struct lysc_iffeature *iff, int UNUSED(dict))
-{
-    LY_ARRAY_FREE(iff->features);
-    free(iff->expr);
-}
-
-static void
-lysc_feature_free(struct ly_ctx *ctx, struct lysc_feature *feat, int dict)
-{
-    FREE_STRING(ctx, feat->name, dict);
-    FREE_ARRAY(ctx, feat->iffeatures, lysc_iffeature_free);
-    LY_ARRAY_FREE(feat->depfeatures);
-}
-
-static void
-lysc_module_free_(struct lysc_module *module, int dict)
-{
-    struct ly_ctx *ctx;
-
-    LY_CHECK_ARG_RET(NULL, module,);
-    ctx = module->ctx;
-
-    FREE_STRING(ctx, module->name, dict);
-    FREE_STRING(ctx, module->ns, dict);
-    FREE_STRING(ctx, module->prefix, dict);
-
-
-    FREE_ARRAY(ctx, module->features, lysc_feature_free);
-
-
-    free(module);
-}
-
-API void
-lysc_module_free(struct lysc_module *module, void (*private_destructor)(const struct lysc_node *node, void *priv))
-{
-    if (module) {
-        lysc_module_free_(module, 1);
-    }
-}
-
-void
-lys_module_free(struct lys_module *module, void (*private_destructor)(const struct lysc_node *node, void *priv))
-{
-    if (!module) {
-        return;
+    LY_CHECK_ARG_RET(NULL, module, name, NULL);
+    if (!nodetype) {
+        nodetype = 0xffff;
     }
 
-    lysc_module_free(module->compiled, private_destructor);
-    lysp_module_free(module->parsed);
-    free(module);
-}
+    while ((node = lys_getnext(node, parent, module->compiled, options))) {
+        if (!(node->nodetype & nodetype)) {
+            continue;
+        }
+        if (node->module != module) {
+            continue;
+        }
 
-struct iff_stack {
-    int size;
-    int index;     /* first empty item */
-    uint8_t *stack;
-};
-
-static LY_ERR
-iff_stack_push(struct iff_stack *stack, uint8_t value)
-{
-    if (stack->index == stack->size) {
-        stack->size += 4;
-        stack->stack = ly_realloc(stack->stack, stack->size * sizeof *stack->stack);
-        LY_CHECK_ERR_RET(!stack->stack, LOGMEM(NULL); stack->size = 0, LY_EMEM);
+        if (name_len) {
+            if (!strncmp(node->name, name, name_len) && !node->name[name_len]) {
+                return node;
+            }
+        } else {
+            if (!strcmp(node->name, name)) {
+                return node;
+            }
+        }
     }
-    stack->stack[stack->index++] = value;
-    return LY_SUCCESS;
+    return NULL;
 }
 
-static uint8_t
-iff_stack_pop(struct iff_stack *stack)
+API int
+lysc_feature_value(const struct lysc_feature *feature)
 {
-    stack->index--;
-    return stack->stack[stack->index];
-}
-
-static void
-iff_stack_clean(struct iff_stack *stack)
-{
-    stack->size = 0;
-    free(stack->stack);
-}
-
-static void
-iff_setop(uint8_t *list, uint8_t op, int pos)
-{
-    uint8_t *item;
-    uint8_t mask = 3;
-
-    assert(pos >= 0);
-    assert(op <= 3); /* max 2 bits */
-
-    item = &list[pos / 4];
-    mask = mask << 2 * (pos % 4);
-    *item = (*item) & ~mask;
-    *item = (*item) | (op << 2 * (pos % 4));
+    LY_CHECK_ARG_RET(NULL, feature, -1);
+    return feature->flags & LYS_FENABLED ? 1 : 0;
 }
 
 static uint8_t
@@ -562,44 +185,6 @@
     return result >> 2 * (pos % 4);
 }
 
-#define LYS_IFF_LP 0x04 /* ( */
-#define LYS_IFF_RP 0x08 /* ) */
-
-API int
-lysc_feature_value(const struct lysc_feature *feature)
-{
-    LY_CHECK_ARG_RET(NULL, feature, -1);
-    return feature->flags & LYS_FENABLED ? 1 : 0;
-}
-
-static struct lysc_feature *
-lysc_feature_find(struct lysc_module *mod, const char *name, size_t len)
-{
-    size_t i;
-    struct lysc_feature *f;
-
-    for (i = 0; i < len; ++i) {
-        if (name[i] == ':') {
-            /* we have a prefixed feature */
-            mod = lysc_module_find_prefix(mod, name, i);
-            LY_CHECK_RET(!mod, NULL);
-
-            name = &name[i + 1];
-            len = len - i - 1;
-        }
-    }
-
-    /* we have the correct module, get the feature */
-    LY_ARRAY_FOR(mod->features, i) {
-        f = &mod->features[i];
-        if (!strncmp(f->name, name, len) && f->name[len] == '\0') {
-            return f;
-        }
-    }
-
-    return NULL;
-}
-
 static int
 lysc_iffeature_value_(const struct lysc_iffeature *iff, int *index_e, int *index_f)
 {
@@ -643,9 +228,6 @@
     return 0;
 }
 
-/*
- * op: 1 - enable, 0 - disable
- */
 /**
  * @brief Enable/Disable the specified feature in the module.
  *
@@ -664,7 +246,7 @@
 lys_feature_change(const struct lysc_module *mod, const char *name, int value)
 {
     int all = 0;
-    unsigned int u;
+    unsigned int u, changed_count, disabled_count;
     struct lysc_feature *f, **df;
     struct lysc_iffeature *iff;
     struct ly_set *changed;
@@ -679,8 +261,10 @@
         all = 1;
     }
     changed = ly_set_new();
+    changed_count = 0;
 
-    for (u = 0; u < LY_ARRAY_SIZE(mod->features); ++u) {
+run:
+    for (disabled_count = u = 0; u < LY_ARRAY_SIZE(mod->features); ++u) {
         f = &mod->features[u];
         if (all || !strcmp(f->name, name)) {
             if ((value && (f->flags & LYS_FENABLED)) || (!value && !(f->flags & LYS_FENABLED))) {
@@ -699,9 +283,7 @@
                 LY_ARRAY_FOR(f->iffeatures, struct lysc_iffeature, iff) {
                     if (!lysc_iffeature_value(iff)) {
                         if (all) {
-                            LOGWRN(mod->ctx,
-                                   "Feature \"%s\" cannot be enabled since it is disabled by its if-feature condition(s).",
-                                   f->name);
+                            ++disabled_count;
                             goto next;
                         } else {
                             LOGERR(mod->ctx, LY_EDENIED,
@@ -737,6 +319,34 @@
         return LY_EINVAL;
     }
 
+    if (value && all && disabled_count) {
+        if (changed_count == changed->count) {
+            /* no change in last run -> not able to enable all ... */
+            /* ... print errors */
+            for (u = 0; disabled_count && u < LY_ARRAY_SIZE(mod->features); ++u) {
+                if (!(mod->features[u].flags & LYS_FENABLED)) {
+                    LOGERR(mod->ctx, LY_EDENIED,
+                           "Feature \"%s\" cannot be enabled since it is disabled by its if-feature condition(s).",
+                           mod->features[u].name);
+                    --disabled_count;
+                }
+            }
+            /* ... restore the original state */
+            for (u = 0; u < changed->count; ++u) {
+                f = changed->objs[u];
+                /* re-disable the feature */
+                f->flags &= ~LYS_FENABLED;
+            }
+
+            ly_set_free(changed, NULL);
+            return LY_EDENIED;
+        } else {
+            /* we did some change in last run, try it again */
+            changed_count = changed->count;
+            goto run;
+        }
+    }
+
     /* reflect change(s) in the dependent features */
     for (u = 0; u < changed->count; ++u) {
         /* If a dependent feature is enabled, it can be now changed by the change (to false) of the value of
@@ -807,283 +417,73 @@
     return -1;
 }
 
-static LY_ERR
-lys_compile_iffeature(struct lysc_ctx *ctx, const char *value, int UNUSED(options), struct lysc_iffeature *iff, struct lysc_feature *parent)
+API const struct lysc_iffeature *
+lys_is_disabled(const struct lysc_node *node, int recursive)
 {
-    const char *c = value;
-    int r, rc = EXIT_FAILURE;
-    int i, j, last_not, checkversion = 0;
-    unsigned int f_size = 0, expr_size = 0, f_exp = 1;
-    uint8_t op;
-    struct iff_stack stack = {0, 0, NULL};
-    struct lysc_feature *f, **df;
+    unsigned int u;
 
-    assert(c);
+    LY_CHECK_ARG_RET(NULL, node, NULL);
 
-    /* pre-parse the expression to get sizes for arrays, also do some syntax checks of the expression */
-    for (i = j = last_not = 0; c[i]; i++) {
-        if (c[i] == '(') {
-            j++;
-            checkversion = 1;
-            continue;
-        } else if (c[i] == ')') {
-            j--;
-            continue;
-        } else if (isspace(c[i])) {
-            checkversion = 1;
-            continue;
+    while(node) {
+        if (node->nodetype & LYS_CHOICE) {
+            return NULL;
         }
 
-        if (!strncmp(&c[i], "not", r = 3) || !strncmp(&c[i], "and", r = 3) || !strncmp(&c[i], "or", r = 2)) {
-            if (c[i + r] == '\0') {
-                LOGVAL(ctx->mod->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
-                       "Invalid value \"%s\" of if-feature - unexpected end of expression.", value);
-                return LY_EVALID;
-            } else if (!isspace(c[i + r])) {
-                /* feature name starting with the not/and/or */
-                last_not = 0;
-                f_size++;
-            } else if (c[i] == 'n') { /* not operation */
-                if (last_not) {
-                    /* double not */
-                    expr_size = expr_size - 2;
-                    last_not = 0;
-                } else {
-                    last_not = 1;
+        if (node->iffeatures) {
+            /* check local if-features */
+            LY_ARRAY_FOR(node->iffeatures, u) {
+                if (!lysc_iffeature_value(&node->iffeatures[u])) {
+                    return &node->iffeatures[u];
                 }
-            } else { /* and, or */
-                f_exp++;
-                /* not a not operation */
-                last_not = 0;
             }
-            i += r;
-        } else {
-            f_size++;
-            last_not = 0;
-        }
-        expr_size++;
-
-        while (!isspace(c[i])) {
-            if (!c[i] || c[i] == ')') {
-                i--;
-                break;
-            }
-            i++;
-        }
-    }
-    if (j || f_exp != f_size) {
-        /* not matching count of ( and ) */
-        LOGVAL(ctx->mod->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
-               "Invalid value \"%s\" of if-feature - non-matching opening and closing parentheses.", value);
-        return LY_EVALID;
-    }
-
-    if (checkversion || expr_size > 1) {
-        /* check that we have 1.1 module */
-        if (ctx->mod->version != LYS_VERSION_1_1) {
-            LOGVAL(ctx->mod->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
-                   "Invalid value \"%s\" of if-feature - YANG 1.1 expression in YANG 1.0 module.", value);
-            return LY_EVALID;
-        }
-    }
-
-    /* allocate the memory */
-    LY_ARRAY_CREATE_RET(ctx->mod->ctx, iff->features, f_size, LY_EMEM);
-    iff->expr = calloc((j = (expr_size / 4) + ((expr_size % 4) ? 1 : 0)), sizeof *iff->expr);
-    stack.stack = malloc(expr_size * sizeof *stack.stack);
-    LY_CHECK_ERR_GOTO(!stack.stack || !iff->expr, LOGMEM(ctx->mod->ctx), error);
-
-    stack.size = expr_size;
-    f_size--; expr_size--; /* used as indexes from now */
-
-    for (i--; i >= 0; i--) {
-        if (c[i] == ')') {
-            /* push it on stack */
-            iff_stack_push(&stack, LYS_IFF_RP);
-            continue;
-        } else if (c[i] == '(') {
-            /* pop from the stack into result all operators until ) */
-            while((op = iff_stack_pop(&stack)) != LYS_IFF_RP) {
-                iff_setop(iff->expr, op, expr_size--);
-            }
-            continue;
-        } else if (isspace(c[i])) {
-            continue;
         }
 
-        /* end of operator or operand -> find beginning and get what is it */
-        j = i + 1;
-        while (i >= 0 && !isspace(c[i]) && c[i] != '(') {
-            i--;
+        if (!recursive) {
+            return NULL;
         }
-        i++; /* go back by one step */
 
-        if (!strncmp(&c[i], "not", 3) && isspace(c[i + 3])) {
-            if (stack.index && stack.stack[stack.index - 1] == LYS_IFF_NOT) {
-                /* double not */
-                iff_stack_pop(&stack);
-            } else {
-                /* not has the highest priority, so do not pop from the stack
-                 * as in case of AND and OR */
-                iff_stack_push(&stack, LYS_IFF_NOT);
-            }
-        } else if (!strncmp(&c[i], "and", 3) && isspace(c[i + 3])) {
-            /* as for OR - pop from the stack all operators with the same or higher
-             * priority and store them to the result, then push the AND to the stack */
-            while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_AND) {
-                op = iff_stack_pop(&stack);
-                iff_setop(iff->expr, op, expr_size--);
-            }
-            iff_stack_push(&stack, LYS_IFF_AND);
-        } else if (!strncmp(&c[i], "or", 2) && isspace(c[i + 2])) {
-            while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_OR) {
-                op = iff_stack_pop(&stack);
-                iff_setop(iff->expr, op, expr_size--);
-            }
-            iff_stack_push(&stack, LYS_IFF_OR);
-        } else {
-            /* feature name, length is j - i */
-
-            /* add it to the expression */
-            iff_setop(iff->expr, LYS_IFF_F, expr_size--);
-
-            /* now get the link to the feature definition */
-            f = lysc_feature_find(ctx->mod, &c[i], j - i);
-            LY_CHECK_ERR_GOTO(!f,
-                              LOGVAL(ctx->mod->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
-                                     "Invalid value \"%s\" of if-feature - unable to find feature \"%.*s\".", value, j - i, &c[i]);
-                              rc = LY_EINVAL,
-                              error)
-            iff->features[f_size] = f;
-            LY_ARRAY_INCREMENT(iff->features);
-            if (parent) {
-                /* and add itself into the dependants list */
-                LY_ARRAY_NEW_RET(ctx->mod->ctx, f->depfeatures, df, LY_EMEM);
-                *df = parent;
-
-                /* TODO check for circular dependency */
-            }
-            f_size--;
-        }
+        /* go through parents */
+        node = node->parent;
     }
-    while (stack.index) {
-        op = iff_stack_pop(&stack);
-        iff_setop(iff->expr, op, expr_size--);
-    }
-
-    if (++expr_size || ++f_size) {
-        /* not all expected operators and operands found */
-        LOGVAL(ctx->mod->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
-               "Invalid value \"%s\" of if-feature - processing error.", value);
-        rc = LY_EINT;
-    } else {
-        rc = LY_SUCCESS;
-    }
-
-error:
-    /* cleanup */
-    iff_stack_clean(&stack);
-
-    return rc;
+    return NULL;
 }
 
-static LY_ERR
-lys_compile_feature(struct lysc_ctx *ctx, struct lysp_feature *feature_p, int options, struct lysc_feature *feature)
+static void
+lys_latest_switch(struct lys_module *old, struct lysp_module *new)
 {
-    unsigned int u;
-    LY_ERR ret;
-
-    if (options & LYSC_OPT_FREE_SP) {
-        /* just switch the pointers */
-        feature->name = feature_p->name;
-    } else {
-        /* keep refcounts correct for lysp_module_free() */
-        feature->name = lydict_insert(ctx->mod->ctx, feature_p->name, 0);
+    if (old->parsed) {
+        new->latest_revision = old->parsed->latest_revision;
+        old->parsed->latest_revision = 0;
     }
-    feature->flags = feature_p->flags;
-
-    if (feature_p->iffeatures) {
-        /* allocate everything now */
-        LY_ARRAY_CREATE_RET(ctx->mod->ctx, feature->iffeatures, LY_ARRAY_SIZE(feature_p->iffeatures), LY_EMEM);
-
-        for (u = 0; u < LY_ARRAY_SIZE(feature_p->iffeatures); ++u) {
-            ret = lys_compile_iffeature(ctx, feature_p->iffeatures[u], options, &feature->iffeatures[u], feature);
-            LY_CHECK_RET(ret);
-            LY_ARRAY_INCREMENT(feature->iffeatures);
-        }
+    if (old->compiled) {
+        new->latest_revision = old->parsed->latest_revision;
+        old->compiled->latest_revision = 0;
     }
-
-    return LY_SUCCESS;
 }
 
-LY_ERR
-lys_compile(struct lysp_module *sp, int options, struct lysc_module **sc)
+struct lys_module *
+lys_parse_mem_(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, int implement, struct ly_parser_ctx *main_ctx,
+               LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, void *data), void *check_data)
 {
-    struct lysc_ctx ctx = {0};
-    struct lysc_module *mod_c;
-    unsigned int u;
-    LY_ERR ret;
-
-    LY_CHECK_ARG_RET(NULL, sc, sp, sp->ctx, LY_EINVAL);
-
-    if (sp->submodule) {
-        LOGERR(sp->ctx, LY_EINVAL, "Submodules (%s) are not supposed to be compiled, compile only the main modules.", sp->name);
-        return LY_EINVAL;
-    }
-
-    ctx.mod = mod_c = calloc(1, sizeof *mod_c);
-    LY_CHECK_ERR_RET(!mod_c, LOGMEM(sp->ctx), LY_EMEM);
-    mod_c->ctx = sp->ctx;
-    mod_c->version = sp->version;
-
-    if (options & LYSC_OPT_FREE_SP) {
-        /* just switch the pointers */
-        mod_c->name = sp->name;
-        mod_c->ns = sp->ns;
-        mod_c->prefix = sp->prefix;
-    } else {
-        /* keep refcounts correct for lysp_module_free() */
-        mod_c->name = lydict_insert(sp->ctx, sp->name, 0);
-        mod_c->ns = lydict_insert(sp->ctx, sp->ns, 0);
-        mod_c->prefix = lydict_insert(sp->ctx, sp->prefix, 0);
-    }
-
-    if (sp->features) {
-        /* allocate everything now */
-        LY_ARRAY_CREATE_RET(ctx.mod->ctx, mod_c->features, LY_ARRAY_SIZE(sp->features), LY_EMEM);
-
-        for (u = 0; u < LY_ARRAY_SIZE(sp->features); ++u) {
-            ret = lys_compile_feature(&ctx, &sp->features[u], options, &mod_c->features[u]);
-            LY_CHECK_GOTO(ret != LY_SUCCESS, error);
-            LY_ARRAY_INCREMENT(mod_c->features);
-        }
-    }
-
-    if (options & LYSC_OPT_FREE_SP) {
-        lysp_module_free_(sp, 0);
-    }
-
-    (*sc) = mod_c;
-    return LY_SUCCESS;
-
-error:
-
-    if (options & LYSC_OPT_FREE_SP) {
-        lysc_module_free_(mod_c, 0);
-    } else {
-        lysc_module_free_(mod_c, 1);
-    }
-    return ret;
-}
-
-const struct lys_module *
-lys_parse_mem_(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, const char *revision, int implement)
-{
-    struct lys_module *mod = NULL;
-    LY_ERR ret;
+    struct lys_module *mod = NULL, *latest, *mod_dup;
+    struct lysp_module *latest_p;
+    struct lysp_import *imp;
+    struct lysp_include *inc;
+    LY_ERR ret = LY_EINVAL;
+    unsigned int u, i;
+    struct ly_parser_ctx context = {0};
 
     LY_CHECK_ARG_RET(ctx, ctx, data, NULL);
 
+    context.ctx = ctx;
+    context.line = 1;
+
+    if (main_ctx) {
+        /* map the typedefs and groupings list from main context to the submodule's context */
+        memcpy(&context.tpdfs_nodes, &main_ctx->tpdfs_nodes, sizeof main_ctx->tpdfs_nodes);
+        memcpy(&context.grps_nodes, &main_ctx->grps_nodes, sizeof main_ctx->grps_nodes);
+    }
+
     mod = calloc(1, sizeof *mod);
     LY_CHECK_ERR_RET(!mod, LOGMEM(ctx), NULL);
 
@@ -1094,33 +494,79 @@
         */
         break;
     case LYS_IN_YANG:
-        ret = yang_parse(ctx, data, &mod->parsed);
-        LY_CHECK_RET(ret, NULL);
+        ret = yang_parse(&context, data, &mod->parsed);
         break;
     default:
         LOGERR(ctx, LY_EINVAL, "Invalid schema input format.");
         break;
     }
+    LY_CHECK_ERR_RET(ret, free(mod), NULL);
+
+    /* make sure that the newest revision is at position 0 */
+    lysp_sort_revisions(mod->parsed->revs);
 
     if (implement) {
+        /* mark the loaded module implemented */
+        if (ly_ctx_get_module_implemented(ctx, mod->parsed->name)) {
+            LOGERR(ctx, LY_EDENIED, "Module \"%s\" is already implemented in the context.", mod->parsed->name);
+            goto error;
+        }
         mod->parsed->implemented = 1;
     }
 
-    if (revision) {
-        /* check revision of the parsed model */
-        if (!mod->parsed->revs || strcmp(revision, mod->parsed->revs[0].rev)) {
-            LOGERR(ctx, LY_EINVAL, "Module \"%s\" parsed with the wrong revision (\"%s\" instead \"%s\").",
-                   mod->parsed->name, mod->parsed->revs[0].rev, revision);
-            lysp_module_free(mod->parsed);
-            free(mod);
-            return NULL;
-        }
+    if (custom_check) {
+        LY_CHECK_GOTO(custom_check(ctx, mod->parsed, check_data), error);
     }
 
-    /* check for duplicity in the context */
-
-    /* add into context */
-    ly_set_add(&ctx->list, mod, LY_SET_OPT_USEASLIST);
+    if (mod->parsed->submodule) { /* submodule */
+        if (!main_ctx) {
+            LOGERR(ctx, LY_EDENIED, "Input data contains submodule \"%s\" which cannot be parsed directly without its main module.",
+                   mod->parsed->name);
+            goto error;
+        }
+        /* decide the latest revision */
+        latest_p = ly_ctx_get_submodule(ctx, mod->parsed->belongsto, mod->parsed->name, NULL);
+        if (latest_p) {
+            if (mod->parsed->revs) {
+                if (!latest_p->revs) {
+                    /* latest has no revision, so mod is anyway newer */
+                    mod->parsed->latest_revision = latest_p->latest_revision;
+                    latest_p->latest_revision = 0;
+                } else {
+                    if (strcmp(mod->parsed->revs[0].date, latest_p->revs[0].date) > 0) {
+                        mod->parsed->latest_revision = latest_p->latest_revision;
+                        latest_p->latest_revision = 0;
+                    }
+                }
+            }
+        } else {
+            mod->parsed->latest_revision = 1;
+        }
+        /* remap possibly changed and reallocated typedefs and groupings list back to the main context */
+        memcpy(&main_ctx->tpdfs_nodes, &context.tpdfs_nodes, sizeof main_ctx->tpdfs_nodes);
+        memcpy(&main_ctx->grps_nodes, &context.grps_nodes, sizeof main_ctx->grps_nodes);
+    } else { /* module */
+        /* check for duplicity in the context */
+        mod_dup = (struct lys_module*)ly_ctx_get_module(ctx, mod->parsed->name, mod->parsed->revs ? mod->parsed->revs[0].date : NULL);
+        if (mod_dup) {
+            if (mod_dup->parsed) {
+                /* error */
+                if (mod->parsed->revs) {
+                    LOGERR(ctx, LY_EEXIST, "Module \"%s\" of revision \"%s\" is already present in the context.",
+                           mod->parsed->name, mod->parsed->revs[0].date);
+                } else {
+                    LOGERR(ctx, LY_EEXIST, "Module \"%s\" with no revision is already present in the context.",
+                           mod->parsed->name);
+                }
+                goto error;
+            } else {
+                /* add the parsed data to the currently compiled-only module in the context */
+                mod_dup->parsed = mod->parsed;
+                free(mod);
+                mod = mod_dup;
+                goto finish_parsing;
+            }
+        }
 
 #if 0
     /* hack for NETCONF's edit-config's operation attribute. It is not defined in the schema, but since libyang
@@ -1136,30 +582,83 @@
     }
 #endif
 
+        /* decide the latest revision */
+        latest = (struct lys_module*)ly_ctx_get_module_latest(ctx, mod->parsed->name);
+        if (latest) {
+            if (mod->parsed->revs) {
+                if ((latest->parsed && !latest->parsed->revs) || (!latest->parsed && !latest->compiled->revision)) {
+                    /* latest has no revision, so mod is anyway newer */
+                    lys_latest_switch(latest, mod->parsed);
+                } else {
+                    if (strcmp(mod->parsed->revs[0].date, latest->parsed ? latest->parsed->revs[0].date : latest->compiled->revision) > 0) {
+                        lys_latest_switch(latest, mod->parsed);
+                    }
+                }
+            }
+        } else {
+            mod->parsed->latest_revision = 1;
+        }
+
+        /* add into context */
+        ly_set_add(&ctx->list, mod, LY_SET_OPT_USEASLIST);
+
+finish_parsing:
+        /* resolve imports */
+        mod->parsed->parsing = 1;
+        LY_ARRAY_FOR(mod->parsed->imports, u) {
+            imp = &mod->parsed->imports[u];
+            if (!imp->module && lysp_load_module(ctx, imp->name, imp->rev[0] ? imp->rev : NULL, 0, 0, &imp->module)) {
+                goto error_ctx;
+            }
+            /* check for importing the same module twice */
+            for (i = 0; i < u; ++i) {
+                if (imp->module == mod->parsed->imports[i].module) {
+                    LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Single revision of the module \"%s\" referred twice.", imp->name);
+                    goto error_ctx;
+                }
+            }
+        }
+        LY_ARRAY_FOR(mod->parsed->includes, u) {
+            inc = &mod->parsed->includes[u];
+            if (!inc->submodule && lysp_load_submodule(&context, mod->parsed, inc)) {
+                goto error_ctx;
+            }
+        }
+        mod->parsed->parsing = 0;
+
+        /* check name collisions - typedefs and groupings */
+        LY_CHECK_GOTO(lysp_check_typedefs(&context), error_ctx);
+    }
+
     return mod;
+
+error_ctx:
+    ly_set_rm(&ctx->list, mod, NULL);
+error:
+    lys_module_free(mod, NULL);
+    ly_set_erase(&context.tpdfs_nodes, NULL);
+    return NULL;
 }
 
-API const struct lys_module *
+API struct lys_module *
 lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format)
 {
-    return lys_parse_mem_(ctx, data, format, NULL, 1);
+    return lys_parse_mem_(ctx, data, format, 1, NULL, NULL, NULL);
 }
 
 static void
 lys_parse_set_filename(struct ly_ctx *ctx, const char **filename, int fd)
 {
-#ifdef __APPLE__
-    char path[MAXPATHLEN];
-#else
-    int len;
-    char path[PATH_MAX], proc_path[32];
-#endif
+    char path[PATH_MAX];
 
 #ifdef __APPLE__
     if (fcntl(fd, F_GETPATH, path) != -1) {
         *filename = lydict_insert(ctx, path, 0);
     }
 #else
+    int len;
+    char proc_path[32];
+
     /* get URI if there is /proc */
     sprintf(proc_path, "/proc/self/fd/%d", fd);
     if ((len = readlink(proc_path, path, PATH_MAX - 1)) > 0) {
@@ -1168,10 +667,11 @@
 #endif
 }
 
-const struct lys_module *
-lys_parse_fd_(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, const char *revision, int implement)
+struct lys_module *
+lys_parse_fd_(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement, struct ly_parser_ctx *main_ctx,
+              LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, void *data), void *check_data)
 {
-    const struct lys_module *mod;
+    struct lys_module *mod;
     size_t length;
     char *addr;
 
@@ -1187,7 +687,7 @@
         return NULL;
     }
 
-    mod = lys_parse_mem_(ctx, addr, format, revision, implement);
+    mod = lys_parse_mem_(ctx, addr, format, implement, main_ctx, custom_check, check_data);
     ly_munmap(addr, length);
 
     if (mod && !mod->parsed->filepath) {
@@ -1197,17 +697,18 @@
     return mod;
 }
 
-API const struct lys_module *
+API struct lys_module *
 lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format)
 {
-    return lys_parse_fd_(ctx, fd, format, NULL, 1);
+    return lys_parse_fd_(ctx, fd, format, 1, NULL, NULL, NULL);
 }
 
-API const struct lys_module *
-lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format)
+struct lys_module *
+lys_parse_path_(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format, int implement, struct ly_parser_ctx *main_ctx,
+                LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, void *data), void *check_data)
 {
     int fd;
-    const struct lys_module *mod;
+    struct lys_module *mod;
     const char *rev, *dot, *filename;
     size_t len;
 
@@ -1216,7 +717,7 @@
     fd = open(path, O_RDONLY);
     LY_CHECK_ERR_RET(fd == -1, LOGERR(ctx, LY_ESYS, "Opening file \"%s\" failed (%s).", path, strerror(errno)), NULL);
 
-    mod = lys_parse_fd(ctx, fd, format);
+    mod = lys_parse_fd_(ctx, fd, format, implement, main_ctx, custom_check, check_data);
     close(fd);
     LY_CHECK_RET(!mod, NULL);
 
@@ -1238,9 +739,9 @@
     }
     if (rev) {
         len = dot - ++rev;
-        if (!mod->parsed->revs || len != 10 || strncmp(mod->parsed->revs[0].rev, rev, len)) {
+        if (!mod->parsed->revs || len != 10 || strncmp(mod->parsed->revs[0].date, rev, len)) {
             LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
-                   mod->parsed->revs ? mod->parsed->revs[0].rev : "none");
+                   mod->parsed->revs ? mod->parsed->revs[0].date : "none");
         }
     }
 
@@ -1257,3 +758,199 @@
     return mod;
 }
 
+API struct lys_module *
+lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format)
+{
+    return lys_parse_path_(ctx, path, format, 1, NULL, NULL, NULL);
+}
+
+API LY_ERR
+lys_search_localfile(const char * const *searchpaths, int cwd, const char *name, const char *revision,
+                     char **localfile, LYS_INFORMAT *format)
+{
+    size_t len, flen, match_len = 0, dir_len;
+    int i, implicit_cwd = 0, ret = EXIT_FAILURE;
+    char *wd, *wn = NULL;
+    DIR *dir = NULL;
+    struct dirent *file;
+    char *match_name = NULL;
+    LYS_INFORMAT format_aux, match_format = 0;
+    struct ly_set *dirs;
+    struct stat st;
+
+    LY_CHECK_ARG_RET(NULL, localfile, LY_EINVAL);
+
+    /* start to fill the dir fifo with the context's search path (if set)
+     * and the current working directory */
+    dirs = ly_set_new();
+    if (!dirs) {
+        LOGMEM(NULL);
+        return EXIT_FAILURE;
+    }
+
+    len = strlen(name);
+    if (cwd) {
+        wd = get_current_dir_name();
+        if (!wd) {
+            LOGMEM(NULL);
+            goto cleanup;
+        } else {
+            /* add implicit current working directory (./) to be searched,
+             * this directory is not searched recursively */
+            if (ly_set_add(dirs, wd, 0) == -1) {
+                goto cleanup;
+            }
+            implicit_cwd = 1;
+        }
+    }
+    if (searchpaths) {
+        for (i = 0; searchpaths[i]; i++) {
+            /* check for duplicities with the implicit current working directory */
+            if (implicit_cwd && !strcmp(dirs->objs[0], searchpaths[i])) {
+                implicit_cwd = 0;
+                continue;
+            }
+            wd = strdup(searchpaths[i]);
+            if (!wd) {
+                LOGMEM(NULL);
+                goto cleanup;
+            } else if (ly_set_add(dirs, wd, 0) == -1) {
+                goto cleanup;
+            }
+        }
+    }
+    wd = NULL;
+
+    /* start searching */
+    while (dirs->count) {
+        free(wd);
+        free(wn); wn = NULL;
+
+        dirs->count--;
+        wd = (char *)dirs->objs[dirs->count];
+        dirs->objs[dirs->count] = NULL;
+        LOGVRB("Searching for \"%s\" in %s.", name, wd);
+
+        if (dir) {
+            closedir(dir);
+        }
+        dir = opendir(wd);
+        dir_len = strlen(wd);
+        if (!dir) {
+            LOGWRN(NULL, "Unable to open directory \"%s\" for searching (sub)modules (%s).", wd, strerror(errno));
+        } else {
+            while ((file = readdir(dir))) {
+                if (!strcmp(".", file->d_name) || !strcmp("..", file->d_name)) {
+                    /* skip . and .. */
+                    continue;
+                }
+                free(wn);
+                if (asprintf(&wn, "%s/%s", wd, file->d_name) == -1) {
+                    LOGMEM(NULL);
+                    goto cleanup;
+                }
+                if (stat(wn, &st) == -1) {
+                    LOGWRN(NULL, "Unable to get information about \"%s\" file in \"%s\" when searching for (sub)modules (%s)",
+                           file->d_name, wd, strerror(errno));
+                    continue;
+                }
+                if (S_ISDIR(st.st_mode) && (dirs->count || !implicit_cwd)) {
+                    /* we have another subdirectory in searchpath to explore,
+                     * subdirectories are not taken into account in current working dir (dirs->set.g[0]) */
+                    if (ly_set_add(dirs, wn, 0) == -1) {
+                        goto cleanup;
+                    }
+                    /* continue with the next item in current directory */
+                    wn = NULL;
+                    continue;
+                } else if (!S_ISREG(st.st_mode)) {
+                    /* not a regular file (note that we see the target of symlinks instead of symlinks */
+                    continue;
+                }
+
+                /* here we know that the item is a file which can contain a module */
+                if (strncmp(name, file->d_name, len) ||
+                        (file->d_name[len] != '.' && file->d_name[len] != '@')) {
+                    /* different filename than the module we search for */
+                    continue;
+                }
+
+                /* get type according to filename suffix */
+                flen = strlen(file->d_name);
+                if (!strcmp(&file->d_name[flen - 4], ".yin")) {
+                    format_aux = LYS_IN_YIN;
+                } else if (!strcmp(&file->d_name[flen - 5], ".yang")) {
+                    format_aux = LYS_IN_YANG;
+                } else {
+                    /* not supportde suffix/file format */
+                    continue;
+                }
+
+                if (revision) {
+                    /* we look for the specific revision, try to get it from the filename */
+                    if (file->d_name[len] == '@') {
+                        /* check revision from the filename */
+                        if (strncmp(revision, &file->d_name[len + 1], strlen(revision))) {
+                            /* another revision */
+                            continue;
+                        } else {
+                            /* exact revision */
+                            free(match_name);
+                            match_name = wn;
+                            wn = NULL;
+                            match_len = dir_len + 1 + len;
+                            match_format = format_aux;
+                            goto success;
+                        }
+                    } else {
+                        /* continue trying to find exact revision match, use this only if not found */
+                        free(match_name);
+                        match_name = wn;
+                        wn = NULL;
+                        match_len = dir_len + 1 +len;
+                        match_format = format_aux;
+                        continue;
+                    }
+                } else {
+                    /* remember the revision and try to find the newest one */
+                    if (match_name) {
+                        if (file->d_name[len] != '@' ||
+                                lysp_check_date(NULL, &file->d_name[len + 1], flen - (format_aux == LYS_IN_YANG ? 5 : 4) - len - 1, NULL)) {
+                            continue;
+                        } else if (match_name[match_len] == '@' &&
+                                (strncmp(&match_name[match_len + 1], &file->d_name[len + 1], LY_REV_SIZE - 1) >= 0)) {
+                            continue;
+                        }
+                        free(match_name);
+                    }
+
+                    match_name = wn;
+                    wn = NULL;
+                    match_len = dir_len + 1 + len;
+                    match_format = format_aux;
+                    continue;
+                }
+            }
+        }
+    }
+
+success:
+    (*localfile) = match_name;
+    match_name = NULL;
+    if (format) {
+        (*format) = match_format;
+    }
+    ret = EXIT_SUCCESS;
+
+cleanup:
+    free(wn);
+    free(wd);
+    if (dir) {
+        closedir(dir);
+    }
+    free(match_name);
+    ly_set_free(dirs, free);
+
+    return ret;
+}
+
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 12394eb..a6ed85e 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -15,8 +15,11 @@
 #ifndef LY_TREE_SCHEMA_H_
 #define LY_TREE_SCHEMA_H_
 
+#include <pcre.h>
 #include <stdint.h>
 
+#include "extensions.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -110,7 +113,6 @@
          (ELEM) = (ELEM)->next)
 
 /**
- * @ingroup datatree
  * @brief Macro to iterate via all sibling elements allowing to modify the list itself (e.g. removing elements)
  *
  * Use with opening curly bracket (`{`). All parameters must be of the same type.
@@ -154,15 +156,55 @@
 #define LYS_LEAFLIST 0x0008       /**< leaf-list statement node */
 #define LYS_LIST 0x0010           /**< list statement node */
 #define LYS_ANYXML 0x0020         /**< anyxml statement node */
+#define LYS_ANYDATA 0x0120        /**< anydata statement node, in tests it can be used for both #LYS_ANYXML and #LYS_ANYDATA */
+
 #define LYS_CASE 0x0040           /**< case statement node */
 #define LYS_USES 0x0080           /**< uses statement node */
-#define LYS_ANYDATA 0x0120        /**< anydata statement node, in tests it can be used for both #LYS_ANYXML and #LYS_ANYDATA */
+#define LYS_INOUT 0x200
+#define LYS_ACTION 0x400
+#define LYS_NOTIF 0x800
+#define LYS_GROUPING 0x1000
+#define LYS_AUGMENT 0x2000
+
+/**
+ * @brief YANG built-in types
+ */
+typedef enum {
+    LY_TYPE_UNKNOWN = 0,  /**< Unknown type */
+    LY_TYPE_BINARY,       /**< Any binary data ([RFC 6020 sec 9.8](http://tools.ietf.org/html/rfc6020#section-9.8)) */
+    LY_TYPE_UINT8,        /**< 8-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+    LY_TYPE_UINT16,       /**< 16-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+    LY_TYPE_UINT32,       /**< 32-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+    LY_TYPE_UINT64,       /**< 64-bit unsigned integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+    LY_TYPE_STRING,       /**< Human-readable string ([RFC 6020 sec 9.4](http://tools.ietf.org/html/rfc6020#section-9.4)) */
+    LY_TYPE_BITS,         /**< A set of bits or flags ([RFC 6020 sec 9.7](http://tools.ietf.org/html/rfc6020#section-9.7)) */
+    LY_TYPE_BOOL,         /**< "true" or "false" ([RFC 6020 sec 9.5](http://tools.ietf.org/html/rfc6020#section-9.5)) */
+    LY_TYPE_DEC64,        /**< 64-bit signed decimal number ([RFC 6020 sec 9.3](http://tools.ietf.org/html/rfc6020#section-9.3))*/
+    LY_TYPE_EMPTY,        /**< A leaf that does not have any value ([RFC 6020 sec 9.11](http://tools.ietf.org/html/rfc6020#section-9.11)) */
+    LY_TYPE_ENUM,         /**< Enumerated strings ([RFC 6020 sec 9.6](http://tools.ietf.org/html/rfc6020#section-9.6)) */
+    LY_TYPE_IDENT,        /**< A reference to an abstract identity ([RFC 6020 sec 9.10](http://tools.ietf.org/html/rfc6020#section-9.10)) */
+    LY_TYPE_INST,         /**< References a data tree node ([RFC 6020 sec 9.13](http://tools.ietf.org/html/rfc6020#section-9.13)) */
+    LY_TYPE_LEAFREF,      /**< A reference to a leaf instance ([RFC 6020 sec 9.9](http://tools.ietf.org/html/rfc6020#section-9.9))*/
+    LY_TYPE_UNION,        /**< Choice of member types ([RFC 6020 sec 9.12](http://tools.ietf.org/html/rfc6020#section-9.12)) */
+    LY_TYPE_INT8,         /**< 8-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+    LY_TYPE_INT16,        /**< 16-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+    LY_TYPE_INT32,        /**< 32-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+    LY_TYPE_INT64,        /**< 64-bit signed integer ([RFC 6020 sec 9.2](http://tools.ietf.org/html/rfc6020#section-9.2)) */
+} LY_DATA_TYPE;
+#define LY_DATA_TYPE_COUNT 20 /**< Number of different types */
+
+/**
+ * @brief Stringified YANG built-in data types
+ */
+extern const char* ly_data_type2str[LY_DATA_TYPE_COUNT];
 
 /**
  * @brief YANG import-stmt
  */
 struct lysp_import {
-    const char *name;                /**< name of the module to import (mandatory) */
+    struct lys_module *module;       /**< pointer to the imported module
+                                          (mandatory, but resolved when the referring module is completely parsed) */
+    const char *name;                /**< name of the imported module (mandatory) */
     const char *prefix;              /**< prefix for the data from the imported schema (mandatory) */
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
@@ -174,7 +216,9 @@
  * @brief YANG include-stmt
  */
 struct lysp_include {
-    const char *name;                /**< name of the submodule to include (mandatory) */
+    struct lysp_module *submodule;   /**< pointer to the parsed submodule structure
+                                         (mandatory, but resolved when the referring module is completely parsed) */
+    const char *name;                /**< name of the included submodule (mandatory) */
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysp_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
@@ -204,56 +248,6 @@
 };
 
 /**
- * @brief Enum of substatements in which extension instances can appear.
- */
-typedef enum {
-    LYEXT_SUBSTMT_SELF = 0,      /**< extension of the structure itself, not substatement's */
-    LYEXT_SUBSTMT_ARGUMENT,      /**< extension of the argument statement, can appear in lys_ext */
-    LYEXT_SUBSTMT_BASE,          /**< extension of the base statement, can appear (repeatedly) in lys_type and lys_ident */
-    LYEXT_SUBSTMT_BELONGSTO,     /**< extension of the belongs-to statement, can appear in lys_submodule */
-    LYEXT_SUBSTMT_CONTACT,       /**< extension of the contact statement, can appear in lys_module */
-    LYEXT_SUBSTMT_DEFAULT,       /**< extension of the default statement, can appear in lys_node_leaf, lys_node_leaflist,
-                                      lys_node_choice and lys_deviate */
-    LYEXT_SUBSTMT_DESCRIPTION,   /**< extension of the description statement, can appear in lys_module, lys_submodule,
-                                      lys_node, lys_import, lys_include, lys_ext, lys_feature, lys_tpdf, lys_restr,
-                                      lys_ident, lys_deviation, lys_type_enum, lys_type_bit, lys_when and lys_revision */
-    LYEXT_SUBSTMT_ERRTAG,        /**< extension of the error-app-tag statement, can appear in lys_restr */
-    LYEXT_SUBSTMT_ERRMSG,        /**< extension of the error-message statement, can appear in lys_restr */
-    LYEXT_SUBSTMT_KEY,           /**< extension of the key statement, can appear in lys_node_list */
-    LYEXT_SUBSTMT_NAMESPACE,     /**< extension of the namespace statement, can appear in lys_module */
-    LYEXT_SUBSTMT_ORGANIZATION,  /**< extension of the organization statement, can appear in lys_module and lys_submodule */
-    LYEXT_SUBSTMT_PATH,          /**< extension of the path statement, can appear in lys_type */
-    LYEXT_SUBSTMT_PREFIX,        /**< extension of the prefix statement, can appear in lys_module, lys_submodule (for
-                                      belongs-to's prefix) and lys_import */
-    LYEXT_SUBSTMT_PRESENCE,      /**< extension of the presence statement, can appear in lys_node_container */
-    LYEXT_SUBSTMT_REFERENCE,     /**< extension of the reference statement, can appear in lys_module, lys_submodule,
-                                      lys_node, lys_import, lys_include, lys_revision, lys_tpdf, lys_restr, lys_ident,
-                                      lys_ext, lys_feature, lys_deviation, lys_type_enum, lys_type_bit and lys_when */
-    LYEXT_SUBSTMT_REVISIONDATE,  /**< extension of the revision-date statement, can appear in lys_import and lys_include */
-    LYEXT_SUBSTMT_UNITS,         /**< extension of the units statement, can appear in lys_tpdf, lys_node_leaf,
-                                      lys_node_leaflist and lys_deviate */
-    LYEXT_SUBSTMT_VALUE,         /**< extension of the value statement, can appear in lys_type_enum */
-    LYEXT_SUBSTMT_VERSION,       /**< extension of the yang-version statement, can appear in lys_module and lys_submodule */
-    LYEXT_SUBSTMT_MODIFIER,      /**< extension of the modifier statement, can appear in lys_restr */
-    LYEXT_SUBSTMT_REQINSTANCE,   /**< extension of the require-instance statement, can appear in lys_type */
-    LYEXT_SUBSTMT_YINELEM,       /**< extension of the yin-element statement, can appear in lys_ext */
-    LYEXT_SUBSTMT_CONFIG,        /**< extension of the config statement, can appear in lys_node and lys_deviate */
-    LYEXT_SUBSTMT_MANDATORY,     /**< extension of the mandatory statement, can appear in lys_node_leaf, lys_node_choice,
-                                      lys_node_anydata and lys_deviate */
-    LYEXT_SUBSTMT_ORDEREDBY,     /**< extension of the ordered-by statement, can appear in lys_node_list and lys_node_leaflist */
-    LYEXT_SUBSTMT_STATUS,        /**< extension of the status statement, can appear in lys_tpdf, lys_node, lys_ident,
-                                      lys_ext, lys_feature, lys_type_enum and lys_type_bit */
-    LYEXT_SUBSTMT_FRACDIGITS,    /**< extension of the fraction-digits statement, can appear in lys_type */
-    LYEXT_SUBSTMT_MAX,           /**< extension of the max-elements statement, can appear in lys_node_list,
-                                      lys_node_leaflist and lys_deviate */
-    LYEXT_SUBSTMT_MIN,           /**< extension of the min-elements statement, can appear in lys_node_list,
-                                      lys_node_leaflist and lys_deviate */
-    LYEXT_SUBSTMT_POSITION,      /**< extension of the position statement, can appear in lys_type_bit */
-    LYEXT_SUBSTMT_UNIQUE,        /**< extension of the unique statement, can appear in lys_node_list and lys_deviate */
-    LYEXT_SUBSTMT_IFFEATURE,     /**< extension of the if-feature statement */
-} LYEXT_SUBSTMT;
-
-/**
  * @brief YANG extension instance
  */
 struct lysp_ext_instance {
@@ -308,7 +302,7 @@
  * @brief YANG revision-stmt
  */
 struct lysp_revision {
-    char rev[LY_REV_SIZE];           /**< revision date (madatory) */
+    char date[LY_REV_SIZE];           /**< revision date (madatory) */
     const char *dsc;                 /**< description statement */
     const char *ref;                 /**< reference statement */
     struct lysp_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
@@ -345,9 +339,11 @@
     struct lysp_type *types;         /**< list of sub-types ([sized array](@ref sizedarrays)) - union */
     struct lysp_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
 
+    struct lysc_type *compiled;      /**< pointer to the compiled type */
+
     uint8_t fraction_digits;         /**< number of fraction digits - decimal64 */
     uint8_t require_instance;        /**< require-instance flag - leafref, instance */
-    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) - only LYS_SET_REQINST allowed */
+    uint16_t flags;                  /**< [schema node flags](@ref spnodeflags) */
 };
 
 /**
@@ -361,13 +357,16 @@
     const char *ref;                 /**< reference statement */
     struct lysp_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     struct lysp_type type;           /**< base type from which the typedef is derived (mandatory) */
-    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values allowed */
+    uint16_t flags;                  /**< [schema node flags](@ref spnodeflags) */
 };
 
 /**
  * @brief YANG grouping-stmt
  */
 struct lysp_grp {
+    struct lysp_node *parent;        /**< parent node (NULL if this is a top-level grouping) */
+    uint16_t nodetype;               /**< LYS_GROUPING */
+    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
     const char *name;                /**< grouping name (mandatory) */
     const char *dsc;                 /**< description statement */
     const char *ref;                 /**< reference statement */
@@ -377,7 +376,6 @@
     struct lysp_action *actions;     /**< list of actions ([sized array](@ref sizedarrays)) */
     struct lysp_notif *notifs;       /**< list of notifications ([sized array](@ref sizedarrays)) */
     struct lysp_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
 };
 
 /**
@@ -411,6 +409,9 @@
  * @brief YANG uses-augment-stmt and augment-stmt
  */
 struct lysp_augment {
+    struct lysp_node *parent;        /**< parent node (NULL if this is a top-level augment) */
+    uint16_t nodetype;               /**< LYS_AUGMENT */
+    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
     const char *nodeid;              /**< target schema nodeid (mandatory) - absolute for global augments, descendant for uses's augments */
     const char *dsc;                 /**< description statement */
     const char *ref;                 /**< reference statement */
@@ -420,7 +421,6 @@
     struct lysp_action *actions;     /**< list of actions ([sized array](@ref sizedarrays)) */
     struct lysp_notif *notifs;       /**< list of notifications ([sized array](@ref sizedarrays)) */
     struct lysp_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
 };
 
 /**
@@ -486,43 +486,160 @@
     struct lysp_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
 };
 
+/**
+ * @defgroup spnodeflags Parsed schema nodes flags
+ * @ingroup snodeflags
+ *
+ * Various flags for parsed schema nodes.
+ *
+ *     1 - container    6 - anydata/anyxml    11 - output       16 - grouping   21 - enum
+ *     2 - choice       7 - case              12 - feature      17 - uses       22 - type
+ *     3 - leaf         8 - notification      13 - identity     18 - refine
+ *     4 - leaflist     9 - rpc               14 - extension    19 - augment
+ *     5 - list        10 - input             15 - typedef      20 - deviate
+ *
+ *                                             1 1 1 1 1 1 1 1 1 1 2 2 2
+ *     bit name              1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
+ *     ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       1 LYS_CONFIG_W     |x|x|x|x|x|x|x| | | | | | | | | | |x| |x| | |
+ *         LYS_SET_BASE     | | | | | | | | | | | | | | | | | | | | | |x|
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       2 LYS_CONFIG_R     |x|x|x|x|x|x|x| | | | | | | | | | |x| |x| | |
+ *         LYS_SET_BIT      | | | | | | | | | | | | | | | | | | | | | |x|
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       3 LYS_STATUS_CURR  |x|x|x|x|x|x|x|x|x| | |x|x|x|x|x|x| |x|x|x| |
+ *         LYS_SET_ENUM     | | | | | | | | | | | | | | | | | | | | | |x|
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       4 LYS_STATUS_DEPRC |x|x|x|x|x|x|x|x|x| | |x|x|x|x|x|x| |x|x|x| |
+ *         LYS_SET_FRDIGITS | | | | | | | | | | | | | | | | | | | | | |x|
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       5 LYS_STATUS_OBSLT |x|x|x|x|x|x|x|x|x| | |x|x|x|x|x|x| |x|x|x| |
+ *         LYS_SET_LENGTH   | | | | | | | | | | | | | | | | | | | | | |x|
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       6 LYS_MAND_TRUE    | |x|x| | |x| | | | | | | | | | | |x| |x| | |
+ *         LYS_ORDBY_SYSTEM | | | |x|x| | | | | | | | | | | | | | | | | |
+ *         LYS_SET_PATH     | | | | | | | | | | | | | | | | | | | | | |x|
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       7 LYS_MAND_FALSE   | |x|x| | |x| | | | | | | | | | | |x| |x| | |
+ *         LYS_ORDBY_USER   | | | |x|x| | | | | | | | | | | | | | | | | |
+ *         LYS_SET_PATTERN  | | | | | | | | | | | | | | | | | | | | | |x|
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       8 LYS_YINELEM_TRUE | | | | | | | | | | | | | |x| | | | | | | | |
+ *         LYS_SET_RANGE    | | | | | | | | | | | | | | | | | | | | | |x|
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       9 LYS_YINELEM_FALSE| | | | | | | | | | | | | |x| | | | | | | | |
+ *         LYS_SET_TYPE     | | | | | | | | | | | | | | | | | | | | | |x|
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *      10 LYS_SET_VALUE    | | | | | | | | | | | | | | | | | | | | |x| |
+ *         LYS_SET_REQINST  | | | | | | | | | | | | | | | | | | | | | |x|
+ *         LYS_SET_MIN      | | | |x|x| | | | | | | | | | | | |x| |x| | |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *      11 LYS_SET_MAX      | | | |x|x| | | | | | | | | | | | |x| |x| | |
+ *     ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+/**
+ * @defgroup scnodeflags Compiled schema nodes flags
+ * @ingroup snodeflags
+ *
+ * Various flags for compiled schema nodes.
+ *
+ *     1 - container    6 - anydata/anyxml    11 - output
+ *     2 - choice       7 - case              12 - feature
+ *     3 - leaf         8 - notification      13 - identity
+ *     4 - leaflist     9 - rpc               14 - extension
+ *     5 - list        10 - input
+ *
+ *                                             1 1 1 1 1
+ *     bit name              1 2 3 4 5 6 7 8 9 0 1 2 3 4
+ *     ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       1 LYS_CONFIG_W     |x|x|x|x|x|x|x| | |x| | | | |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       2 LYS_CONFIG_R     |x|x|x|x|x|x|x| | | |x| | | |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       3 LYS_STATUS_CURR  |x|x|x|x|x|x|x|x|x| | |x|x|x|
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       4 LYS_STATUS_DEPRC |x|x|x|x|x|x|x|x|x| | |x|x|x|
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       5 LYS_STATUS_OBSLT |x|x|x|x|x|x|x|x|x| | |x|x|x|
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       6 LYS_MAND_TRUE    | |x|x| | |x| | | | | | | | |
+ *         LYS_ORDBY_SYSTEM | | | |x|x| | | | | | | | | |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       7 LYS_ORDBY_USER   | | | |x|x| | | | | | | | | |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       8 LYS_PRESENCE     |x| | | | | | | | | | | | | |
+ *         LYS_UNIQUE       | | |x| | | | | | | | | | | |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *       9 LYS_KEY          | | |x| | | | | | | | | | | |
+ *         LYS_FENABLED     | | | | | | | | | | | |x| | |
+ *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *      10 LYS_SET_DFLT     | | |x| | | |x| | | | | | | |
+ *     ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+/**
+ * @defgroup snodeflags Schema nodes flags
+ * @ingroup schematree
+ * @{
+ */
 #define LYS_CONFIG_W     0x01        /**< config true; */
 #define LYS_CONFIG_R     0x02        /**< config false; */
 #define LYS_CONFIG_MASK  0x03        /**< mask for config value */
-#define LYS_STATUS_CURR  0x08        /**< status current; */
-#define LYS_STATUS_DEPRC 0x10        /**< status deprecated; */
-#define LYS_STATUS_OBSLT 0x20        /**< status obsolete; */
-#define LYS_STATUS_MASK  0x38        /**< mask for status value */
-#define LYS_MAND_TRUE    0x40        /**< mandatory true; applicable only to
+#define LYS_STATUS_CURR  0x04        /**< status current; */
+#define LYS_STATUS_DEPRC 0x08        /**< status deprecated; */
+#define LYS_STATUS_OBSLT 0x10        /**< status obsolete; */
+#define LYS_STATUS_MASK  0x1C        /**< mask for status value */
+#define LYS_MAND_TRUE    0x20        /**< mandatory true; applicable only to ::lysp_node_choice/::lysc_node_choice,
+                                          ::lysp_node_leaf/::lysc_node_leaf and ::lysp_node_anydata/::lysc_node_anydata */
+#define LYS_MAND_FALSE   0x40        /**< mandatory false; applicable only to
                                           ::lysp_node_choice/::lysc_node_choice, ::lysp_node_leaf/::lysc_node_leaf
                                           and ::lysp_node_anydata/::lysc_node_anydata */
-#define LYS_MAND_FALSE   0x80        /**< mandatory false; applicable only to
-                                          ::lysp_node_choice/::lysc_node_choice, ::lysp_node_leaf/::lysc_node_leaf
-                                          and ::lysp_node_anydata/::lysc_node_anydata */
-#define LYS_MAND_MASK    0xc0        /**< mask for mandatory values */
-#define LYS_ORDBY_SYSTEM 0x100       /**< ordered-by system lists, applicable only to
-                                          ::lysp_node_list/lysc_node_list and ::lysp_node_leaflist/::lysc_node_list */
-#define LYS_ORDBY_USER   0x200       /**< ordered-by user lists, applicable only to
-                                          ::lysp_node_list/lysc_node_list and ::lysp_node_leaflist/::lysc_node_list */
-#define LYS_ORDBY_MASK   0x300       /**< mask for ordered-by flags */
-#define LYS_FENABLED     0x100       /**< feature enabled flag, applicable only to ::lysp_feature/::lysc_feature */
-#define LYS_AUTOASSIGNED 0x01        /**< value was auto-assigned, applicable only to
-                                          ::lysp_type/::lysc_type enum and bits flags */
-#define LYS_YINELEM_TRUE 0x01        /**< yin-element true for extension's argument */
-#define LYS_YINELEM_FALSE 0x02       /**< yin-element false for extension's argument */
-#define LYS_YINELEM_MASK 0x03        /**< mask for yin-element value */
-#define LYS_SET_VALUE    0x01        /**< value attribute is set */
+#define LYS_MAND_MASK    0x60        /**< mask for mandatory values */
+#define LYS_PRESENCE     0x80        /**< flag for presence property of a container, applicable only to ::lysc_node_container */
+#define LYS_UNIQUE       0x80        /**< flag for leafs being part of a unique set, applicable only to ::lysc_node_leaf */
+#define LYS_KEY          0x100       /**< flag for leafs being a key of a list, applicable only to ::lysc_node_leaf */
+#define LYS_FENABLED     0x100       /**< feature enabled flag, applicable only to ::lysc_feature */
+#define LYS_ORDBY_SYSTEM 0x20        /**< ordered-by user lists, applicable only to ::lysc_node_leaflist/::lysp_node_leaflist and
+                                          ::lysc_node_list/::lysp_node_list */
+#define LYS_ORDBY_USER   0x40        /**< ordered-by user lists, applicable only to ::lysc_node_leaflist/::lysp_node_leaflist and
+                                          ::lysc_node_list/::lysp_node_list */
+#define LYS_ORDBY_MASK   0x60        /**< mask for ordered-by values */
+#define LYS_YINELEM_TRUE 0x80        /**< yin-element true for extension's argument */
+#define LYS_YINELEM_FALSE 0x100      /**< yin-element false for extension's argument */
+#define LYS_YINELEM_MASK 0x180       /**< mask for yin-element value */
+#define LYS_SET_VALUE    0x200       /**< value attribute is set */
+#define LYS_SET_MIN      0x200       /**< min attribute is set */
 #define LYS_SET_MAX      0x400       /**< max attribute is set */
-#define LYS_SET_MIN      0x800       /**< min attribute is set */
-#define LYS_SET_REQINST  0x01        /**< require_instance attribute is set */
+
+#define LYS_SET_BASE     0x0001      /**< type's flag for present base substatement */
+#define LYS_SET_BIT      0x0002      /**< type's flag for present bit substatement */
+#define LYS_SET_ENUM     0x0004      /**< type's flag for present enum substatement */
+#define LYS_SET_FRDIGITS 0x0008      /**< type's flag for present fraction-digits substatement */
+#define LYS_SET_LENGTH   0x0010      /**< type's flag for present length substatement */
+#define LYS_SET_PATH     0x0020      /**< type's flag for present path substatement */
+#define LYS_SET_PATTERN  0x0040      /**< type's flag for present pattern substatement */
+#define LYS_SET_RANGE    0x0080      /**< type's flag for present range substatement */
+#define LYS_SET_TYPE     0x0100      /**< type's flag for present type substatement */
+#define LYS_SET_REQINST  0x0200      /**< type's flag for present require-instance substatement */
+#define LYS_SET_DFLT     0x0200      /**< flag to mark leaf with own default value, not a default value taken from its type, and default
+                                          cases of choice. This information is important for refines, since it is prohibited to make leafs
+                                          with default statement mandatory. In case the default leaf value is taken from type, it is thrown
+                                          away when it is refined to be mandatory node. */
+
+#define LYS_FLAGS_COMPILED_MASK 0x7f /**< mask for flags that maps to the compiled structures */
+/** @} */
 
 /**
  * @brief Generic YANG data node
  */
 struct lysp_node {
+    struct lysp_node *parent;        /**< parent node (NULL if this is a top-level node) */
     uint16_t nodetype;               /**< type of the node (mandatory) */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
-    struct lysp_node *next;          /**< pointer to the next sibling node (NULL if there is no one) */
+    struct lysp_node *next;          /**< next sibling node (NULL if there is no one) */
     const char *name;                /**< node name (mandatory) */
     const char *dsc;                 /**< description statement */
     const char *ref;                 /**< reference statement */
@@ -535,6 +652,7 @@
  * @brief Extension structure of the lysp_node for YANG container
  */
 struct lysp_node_container {
+    struct lysp_node *parent;        /**< parent node (NULL if this is a top-level node) */
     uint16_t nodetype;               /**< LYS_CONTAINER */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
     struct lysp_node *next;          /**< pointer to the next sibling node (NULL if there is no one) */
@@ -556,6 +674,7 @@
 };
 
 struct lysp_node_leaf {
+    struct lysp_node *parent;        /**< parent node (NULL if this is a top-level node) */
     uint16_t nodetype;               /**< LYS_LEAF */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
     struct lysp_node *next;          /**< pointer to the next sibling node (NULL if there is no one) */
@@ -574,6 +693,7 @@
 };
 
 struct lysp_node_leaflist {
+    struct lysp_node *parent;        /**< parent node (NULL if this is a top-level node) */
     uint16_t nodetype;               /**< LYS_LEAFLIST */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
     struct lysp_node *next;          /**< pointer to the next sibling node (NULL if there is no one) */
@@ -594,6 +714,7 @@
 };
 
 struct lysp_node_list {
+    struct lysp_node *parent;        /**< parent node (NULL if this is a top-level node) */
     uint16_t nodetype;               /**< LYS_LIST */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
     struct lysp_node *next;          /**< pointer to the next sibling node (NULL if there is no one) */
@@ -618,6 +739,7 @@
 };
 
 struct lysp_node_choice {
+    struct lysp_node *parent;        /**< parent node (NULL if this is a top-level node) */
     uint16_t nodetype;               /**< LYS_CHOICE */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
     struct lysp_node *next;          /**< pointer to the next sibling node (NULL if there is no one) */
@@ -634,6 +756,7 @@
 };
 
 struct lysp_node_case {
+    struct lysp_node *parent;        /**< parent node (NULL if this is a top-level node) */
     uint16_t nodetype;               /**< LYS_CASE */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
     struct lysp_node *next;          /**< pointer to the next sibling node (NULL if there is no one) */
@@ -649,6 +772,7 @@
 };
 
 struct lysp_node_anydata {
+    struct lysp_node *parent;        /**< parent node (NULL if this is a top-level node) */
     uint16_t nodetype;               /**< LYS_ANYXML || LYS_ANYDATA */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
     struct lysp_node *next;          /**< pointer to the next sibling node (NULL if there is no one) */
@@ -664,6 +788,7 @@
 };
 
 struct lysp_node_uses {
+    struct lysp_node *parent;        /**< parent node (NULL if this is a top-level node) */
     uint16_t nodetype;               /**< LYS_USES */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
     struct lysp_node *next;          /**< pointer to the next sibling node (NULL if there is no one) */
@@ -683,6 +808,8 @@
  * @brief YANG input-stmt and output-stmt
  */
 struct lysp_action_inout {
+    struct lysp_node *parent;        /**< parent node (NULL if this is a top-level node) */
+    uint16_t nodetype;               /**< LYS_INOUT */
     struct lysp_restr *musts;        /**< list of must restrictions ([sized array](@ref sizedarrays)) */
     struct lysp_tpdf *typedefs;      /**< list of typedefs ([sized array](@ref sizedarrays)) */
     struct lysp_grp *groupings;      /**< list of groupings ([sized array](@ref sizedarrays)) */
@@ -694,6 +821,9 @@
  * @brief YANG rpc-stmt and action-stmt
  */
 struct lysp_action {
+    struct lysp_node *parent;        /**< parent node (NULL if this is a top-level node) */
+    uint16_t nodetype;               /**< LYS_ACTION */
+    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
     const char *name;                /**< grouping name reference (mandatory) */
     const char *dsc;                 /**< description statement */
     const char *ref;                 /**< reference statement */
@@ -703,13 +833,15 @@
     struct lysp_action_inout *input; /**< RPC's/Action's input */
     struct lysp_action_inout *output;/**< RPC's/Action's output */
     struct lysp_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
 };
 
 /**
  * @brief YANG notification-stmt
  */
 struct lysp_notif {
+    struct lysp_node *parent;        /**< parent node (NULL if this is a top-level node) */
+    uint16_t nodetype;               /**< LYS_NOTIF */
+    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
     const char *name;                /**< grouping name reference (mandatory) */
     const char *dsc;                 /**< description statement */
     const char *ref;                 /**< reference statement */
@@ -719,7 +851,6 @@
     struct lysp_grp *groupings;      /**< list of groupings ([sized array](@ref sizedarrays)) */
     struct lysp_node *data;          /**< list of data nodes (linked list) */
     struct lysp_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
 };
 
 /**
@@ -747,14 +878,14 @@
         const char *belongsto;       /**< belongs to parent module (submodule - mandatory) */
     };
     const char *prefix;              /**< module prefix or submodule belongsto prefix of main module (mandatory) */
+    struct lysp_revision *revs;      /**< list of the module revisions ([sized array](@ref sizedarrays)), the first revision
+                                          in the list is always the last (newest) revision of the module */
     struct lysp_import *imports;     /**< list of imported modules ([sized array](@ref sizedarrays)) */
     struct lysp_include *includes;   /**< list of included submodules ([sized array](@ref sizedarrays)) */
     const char *org;                 /**< party/company responsible for the module */
     const char *contact;             /**< contact information for the module */
     const char *dsc;                 /**< description of the module */
     const char *ref;                 /**< cross-reference for the module */
-    struct lysp_revision *revs;      /**< list of the module revisions ([sized array](@ref sizedarrays)), the first revision
-                                          in the list is always the last (newest) revision of the module */
     struct lysp_ext *extensions;     /**< list of extension statements ([sized array](@ref sizedarrays)) */
     struct lysp_feature *features;   /**< list of feature definitions ([sized array](@ref sizedarrays)) */
     struct lysp_ident *identities;   /**< list of identities ([sized array](@ref sizedarrays)) */
@@ -769,9 +900,12 @@
 
     uint8_t submodule:1;             /**< flag to distinguish main modules and submodules */
     uint8_t implemented:1;           /**< flag if the module is implemented, not just imported */
-    uint8_t latest_revision:1;       /**< flag if the module was loaded without specific revision and is
-                                          the latest revision found */
-    uint8_t version:4;               /**< yang-version (LYS_VERSION values) */
+    uint8_t latest_revision:2;       /**< flag to mark the latest available revision:
+                                          1 - the latest revision in searchdirs was not searched yet and this is the
+                                          latest revision in the current context
+                                          2 - searchdirs were searched and this is the latest available revision */
+    uint8_t parsing:1;               /**< flag for circular check */
+    uint8_t version;                 /**< yang-version (LYS_VERSION values) */
 };
 
 /**
@@ -782,12 +916,42 @@
 void lysp_module_free(struct lysp_module *module);
 
 /**
+ * @brief YANG extension instance
+ */
+struct lysc_ext_instance {
+    struct lyext_plugin *plugin;     /**< pointer to the plugin implementing the extension (if present) */
+    void *parent;                    /**< pointer to the parent element holding the extension instance(s), use
+                                          ::lysc_ext_instance#parent_type to access the schema element */
+    const char *argument;            /**< optional value of the extension's argument */
+    LYEXT_SUBSTMT insubstmt;         /**< value identifying placement of the extension instance */
+    uint32_t insubstmt_index;        /**< in case the instance is in a substatement that can appear multiple times,
+                                          this identifies the index of the substatement for this extension instance */
+    LYEXT_PARENT parent_type;        /**< type of the parent structure */
+#if 0
+    uint8_t ext_type;                /**< extension type (#LYEXT_TYPE) */
+    uint8_t padding;                 /**< 32b padding */
+    struct lys_module *module;       /**< pointer to the extension instance's module (mandatory) */
+    LYS_NODE nodetype;               /**< LYS_EXT */
+#endif
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    void *priv;                      /**< private caller's data, not used by libyang */
+};
+
+/**
+ * @brief Compiled YANG if-feature-stmt
+ */
+struct lysc_iffeature {
+    uint8_t *expr;                   /**< 2bits array describing the if-feature expression in prefix format, see @ref ifftokens */
+    struct lysc_feature **features;  /**< array of pointers to the features used in expression ([sized array](@ref sizedarrays)) */
+};
+
+/**
  * @brief YANG import-stmt
  */
 struct lysc_import {
-    struct lysc_module *module;      /**< link to the imported module */
+    struct lys_module *module;       /**< link to the imported module */
     const char *prefix;              /**< prefix for the data from the imported schema (mandatory) */
-    struct lysp_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
 };
 
 /**
@@ -795,17 +959,29 @@
  */
 struct lysc_when {
     struct lyxp_expr *cond;          /**< XPath when condition */
+    struct lysc_node *context;       /**< context node of the expression */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
 };
 
 /**
+ * @brief YANG identity-stmt
+ */
+struct lysc_ident {
+    const char *name;                /**< identity name (mandatory), including possible prefix */
+    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+    struct lysc_ident **derived;     /**< list of (pointers to the) derived identities ([sized array](@ref sizedarrays)) */
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_ values are allowed */
+};
+
+/**
  * @brief YANG feature-stmt
  */
 struct lysc_feature {
     const char *name;                /**< feature name (mandatory) */
     struct lysc_feature **depfeatures;/**< list of pointers to other features depending on this one ([sized array](@ref sizedarrays)) */
     struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
-    struct lysp_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* and
                                           #LYS_FENABLED values allowed */
 };
@@ -825,11 +1001,156 @@
  */
 
 /**
- * @brief Compiled YANG if-feature-stmt
+ * @brief Compiled YANG revision statement
  */
-struct lysc_iffeature {
-    uint8_t *expr;                   /**< 2bits array describing the if-feature expression in prefix format, see @ref ifftokens */
-    struct lysc_feature **features;  /**< array of pointers to the features used in expression ([sized array](@ref sizedarrays)) */
+struct lysc_revision {
+    char date[LY_REV_SIZE];          /**< revision-date (mandatory) */
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+struct lysc_default {
+    struct lys_module *module;       /**< module where the default was defined */
+    const char *value;               /**< default value (with possible prefixes from the module where defined) */
+};
+
+struct lysc_range {
+    struct lysc_range_part {
+        union {                      /**< min boundary TODO decimal */
+            int64_t min_64;          /**< for int8, int16, int32 and int64 */
+            uint64_t min_u64;        /**< for uint8, uint16, uint32 and uint64 */
+        };
+        union {                      /**< max boundary TODO decimal */
+            int64_t max_64;          /**< for int8, int16, int32 and int64 */
+            uint64_t max_u64;        /**< for uint8, uint16, uint32 and uint64 */
+        };
+    } *parts;                        /**< compiled range expression ([sized array](@ref sizedarrays)) */
+    const char *emsg;                /**< error-message */
+    const char *eapptag;             /**< error-app-tag value */
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+struct lysc_pattern {
+    pcre *expr;                      /**< compiled regular expression */
+    pcre_extra *expr_extra;          /**< additional information to speed up matching */
+    const char *emsg;                /**< error-message */
+    const char *eapptag;             /**< error-app-tag value */
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    uint32_t inverted:1;             /**< invert-match flag */
+    uint32_t refcount:31;            /**< reference counter */
+};
+
+struct lysc_must {
+    struct lys_module *module;       /**< module where the must was defined */
+    struct lyxp_expr *cond;          /**< XPath when condition */
+    const char *emsg;                /**< error-message */
+    const char *eapptag;             /**< error-app-tag value */
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+};
+
+struct lysc_type {
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    LY_DATA_TYPE basetype;           /**< Base type of the type */
+    uint32_t refcount;               /**< reference counter for type sharing */
+};
+
+struct lysc_type_num {
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    LY_DATA_TYPE basetype;           /**< Base type of the type */
+    uint32_t refcount;               /**< reference counter for type sharing */
+    struct lysc_range *range;        /**< Optional range limitation */
+};
+
+struct lysc_type_dec {
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    LY_DATA_TYPE basetype;           /**< Base type of the type */
+    uint32_t refcount;               /**< reference counter for type sharing */
+    uint8_t fraction_digits;         /**< fraction digits specification */
+    struct lysc_range *range;        /**< Optional range limitation */
+};
+
+struct lysc_type_str {
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    LY_DATA_TYPE basetype;           /**< Base type of the type */
+    uint32_t refcount;               /**< reference counter for type sharing */
+    struct lysc_range *length;       /**< Optional length limitation */
+    struct lysc_pattern **patterns;  /**< Optional list of pointers to pattern limitations ([sized array](@ref sizedarrays)) */
+};
+
+struct lysc_type_enum {
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    LY_DATA_TYPE basetype;           /**< Base type of the type */
+    uint32_t refcount;               /**< reference counter for type sharing */
+    struct lysc_type_enum_item {
+        const char *name;            /**< enumeration identifier */
+        struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+        struct lysc_ext_instance *exts;    /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+        int32_t value;               /**< integer value associated with the enumeration */
+    } *enums;                        /**< enumerations list ([sized array](@ref sizedarrays)), mandatory (at least 1 item) */
+};
+
+struct lysc_type_leafref {
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    LY_DATA_TYPE basetype;           /**< Base type of the type */
+    uint32_t refcount;               /**< reference counter for type sharing */
+    const char* path;                /**< target path */
+    struct lys_module *path_context; /**< module where the path is defined, so it provides context to resolve prefixes */
+    struct lysc_type *realtype;      /**< pointer to the real (first non-leafref in possible leafrefs chain) type. */
+    uint8_t require_instance;        /**< require-instance flag */
+};
+
+struct lysc_type_identityref {
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    LY_DATA_TYPE basetype;           /**< Base type of the type */
+    uint32_t refcount;               /**< reference counter for type sharing */
+    struct lysc_ident **bases;       /**< list of pointers to the base identities ([sized array](@ref sizedarrays)),
+                                          mandatory (at least 1 item) */
+};
+
+struct lysc_type_instanceid {
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    LY_DATA_TYPE basetype;           /**< Base type of the type */
+    uint32_t refcount;               /**< reference counter for type sharing */
+    uint8_t require_instance;        /**< require-instance flag */
+};
+
+struct lysc_type_bits {
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    LY_DATA_TYPE basetype;           /**< Base type of the type */
+    uint32_t refcount;               /**< reference counter for type sharing */
+    struct lysc_type_bits_item {
+        const char *name;            /**< bit identifier */
+        struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+        struct lysc_ext_instance *exts;    /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+        uint32_t position;           /**< non-negative integer value associated with the bit */
+    } *bits;                         /**< bits list ([sized array](@ref sizedarrays)), mandatory (at least 1 item) */
+};
+
+struct lysc_type_union {
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    LY_DATA_TYPE basetype;           /**< Base type of the type */
+    uint32_t refcount;               /**< reference counter for type sharing */
+    struct lysc_type **types;        /**< list of types in the union ([sized array](@ref sizedarrays)), mandatory (at least 1 item) */
+};
+
+struct lysc_type_bin {
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+    LY_DATA_TYPE basetype;           /**< Base type of the type */
+    uint32_t refcount;               /**< reference counter for type sharing */
+    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 */
 };
 
 /**
@@ -838,12 +1159,179 @@
 struct lysc_node {
     uint16_t nodetype;               /**< type of the node (mandatory) */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
-    struct lysp_node *sp;            /**< link to the simply parsed (SP) original of the node, NULL if the SP schema was removed. */
-    struct lysc_node *next;          /**< pointer to the next sibling node (NULL if there is no one) */
+    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;                /**< 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 {
+    uint16_t nodetype;               /**< LYS_CONTAINER */
+    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;                /**< 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 *child;         /**< first child node (linked list) */
+    struct lysc_must *musts;         /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+    struct lysc_action *actions;     /**< list of actions ([sized array](@ref sizedarrays)) */
+    struct lysc_notif *notifs;       /**< list of notifications ([sized array](@ref sizedarrays)) */
+};
+
+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
+                                          through the children linked list. */
+};
+
+struct lysc_node_choice {
+    uint16_t nodetype;               /**< LYS_CHOICE */
+    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;                /**< 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 (linked list). Note that all the children of all the cases are linked each other
+                                          as siblings. Their parent pointers points to the specific case they belongs to, so distinguish the
+                                          case is simple. */
+    struct lysc_node_case *dflt;     /**< default case of the choice, only a pointer into the cases array. */
+};
+
+struct lysc_node_leaf {
+    uint16_t nodetype;               /**< LYS_LEAF */
+    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;                /**< 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_must *musts;         /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+    struct lysc_type *type;          /**< type of the leaf node (mandatory) */
+
+    const char *units;               /**< units of the leaf's type */
+    const char *dflt;                /**< default value */
+};
+
+struct lysc_node_leaflist {
+    uint16_t nodetype;               /**< LYS_LEAFLIST */
+    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;                /**< 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_must *musts;         /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+    struct lysc_type *type;          /**< type of the leaf node (mandatory) */
+
+    const char *units;               /**< units of the leaf's type */
+    const char **dflts;              /**< list of default values ([sized array](@ref sizedarrays)) */
+    uint32_t min;                    /**< min-elements constraint */
+    uint32_t max;                    /**< max-elements constraint */
+
+};
+
+struct lysc_node_list {
+    uint16_t nodetype;               /**< LYS_LIST */
+    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;                /**< 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 *child;         /**< first child node (linked list) */
+    struct lysc_must *musts;         /**< list of must restrictions ([sized array](@ref sizedarrays)) */
+    struct lysc_action *actions;     /**< list of actions ([sized array](@ref sizedarrays)) */
+    struct lysc_notif *notifs;       /**< list of notifications ([sized array](@ref sizedarrays)) */
+
+    struct lysc_node_leaf **keys;    /**< list of pointers to the keys ([sized array](@ref sizedarrays)) */
+    struct lysc_node_leaf ***uniques; /**< list of sized arrays of pointers to the unique nodes ([sized array](@ref sizedarrays)) */
+    uint32_t min;                    /**< min-elements constraint */
+    uint32_t max;                    /**< max-elements constraint */
+};
+
+struct lysc_node_anydata {
+    uint16_t nodetype;               /**< LYS_ANYXML or LYS_ANYDATA */
+    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;                /**< 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_must *musts;         /**< list of must restrictions ([sized array](@ref sizedarrays)) */
 };
 
 /**
@@ -855,21 +1343,92 @@
 struct lysc_module {
     struct ly_ctx *ctx;              /**< libyang context of the module (mandatory) */
     const char *name;                /**< name of the module (mandatory) */
+    const char *filepath;            /**< path, if the schema was read from a file, NULL in case of reading from memory */
     const char *ns;                  /**< namespace of the module (mandatory) */
     const char *prefix;              /**< module prefix (mandatory) */
+    const char *revision;            /**< the revision of the module */
     struct lysc_import *imports;     /**< list of imported modules ([sized array](@ref sizedarrays)) */
 
-
     struct lysc_feature *features;   /**< list of feature definitions ([sized array](@ref sizedarrays)) */
-
+    struct lysc_ident *identities;   /**< list of identities ([sized array](@ref sizedarrays)) */
+    struct lysc_node *data;          /**< list of module's top-level data nodes (linked list) */
+    struct lysc_action *rpcs;        /**< list of RPCs ([sized array](@ref sizedarrays)) */
+    struct lysc_notif *notifs;       /**< list of notifications ([sized array](@ref sizedarrays)) */
+    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
 
     uint8_t implemented:1;           /**< flag if the module is implemented, not just imported */
-    uint8_t latest_revision:1;       /**< flag if the module was loaded without specific revision and is
-                                          the latest revision found */
-    uint8_t version:4;               /**< yang-version (LYS_VERSION values) */
+    uint8_t latest_revision:2;       /**< flag to mark the latest available revision:
+                                          1 - the latest revision in searchdirs was not searched yet and this is the
+                                          latest revision in the current context
+                                          2 - searchdirs were searched and this is the latest available revision */
+    uint8_t version;                 /**< yang-version (LYS_VERSION values) */
 };
 
 /**
+ * @brief Get the groupings sized array of the given (parsed) schema node.
+ * Decides the node's type and in case it has a groupings array, returns it.
+ * @param[in] node Node to examine.
+ * @return The node's groupings sized array if any, NULL otherwise.
+ */
+const struct lysp_grp *lysp_node_groupings(const struct lysp_node *node);
+
+/**
+ * @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.
@@ -901,6 +1460,14 @@
  *
  * By default, when the module is loaded by libyang parser, all features are disabled.
  *
+ * If all features are being enabled, it must be possible respecting their if-feature conditions. For example,
+ * enabling all features on the following feature set will fail since it is not possible to enable both features
+ * (and it is unclear which of them should be enabled then). In this case the LY_EDENIED is returned and the feature
+ * is untouched.
+ *
+ *     feature f1;
+ *     feature f2 { if-feature 'not f1';}
+ *
  * @param[in] module Module where the feature will be enabled.
  * @param[in] feature Name of the feature to enable. To enable all features at once, use asterisk (`*`) character.
  * @return LY_ERR value.
@@ -939,7 +1506,7 @@
  * @param[in] format Format of the input data (YANG or YIN).
  * @return Pointer to the data model structure or NULL on error.
  */
-const struct lys_module *lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format);
+struct lys_module *lys_parse_mem(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format);
 
 /**
  * @brief Read a schema from file descriptor into the specified context.
@@ -952,17 +1519,34 @@
  * @param[in] format Format of the input data (YANG or YIN).
  * @return Pointer to the data model structure or NULL on error.
  */
-const struct lys_module *lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format);
+struct lys_module *lys_parse_fd(struct ly_ctx *ctx, int fd, LYS_INFORMAT format);
 
 /**
- * @brief Load a schema into the specified context from a file.
+ * @brief Read a schema into the specified context from a file.
  *
  * @param[in] ctx libyang context where to process the data model.
  * @param[in] path Path to the file with the model in the specified format.
  * @param[in] format Format of the input data (YANG or YIN).
  * @return Pointer to the data model structure or NULL on error.
  */
-const struct lys_module *lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format);
+struct lys_module *lys_parse_path(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format);
+
+/**
+ * @brief Search for the schema file in the specified searchpaths.
+ *
+ * @param[in] searchpaths NULL-terminated array of paths to be searched (recursively). Current working
+ * directory is searched automatically (but non-recursively if not in the provided list). Caller can use
+ * result of the ly_ctx_get_searchdirs().
+ * @param[in] cwd Flag to implicitly search also in the current working directory (non-recursively).
+ * @param[in] name Name of the schema to find.
+ * @param[in] revision Revision of the schema to find. If NULL, the newest found schema filepath is returned.
+ * @param[out] localfile Mandatory output variable containing absolute path of the found schema. If no schema
+ * complying the provided restriction is found, NULL is set.
+ * @param[out] format Optional output variable containing expected format of the schema document according to the
+ * file suffix.
+ * @return LY_ERR value (LY_SUCCESS is returned even if the file is not found, then the *localfile is NULL).
+ */
+LY_ERR lys_search_localfile(const char * const *searchpaths, int cwd, const char *name, const char *revision, char **localfile, LYS_INFORMAT *format);
 
 /**
  * @defgroup scflags Schema compile flags
@@ -971,20 +1555,81 @@
  * @{
  */
 #define LYSC_OPT_FREE_SP 1           /**< Free the input printable schema */
-
-/**
- * @}
- */
+/** @} scflags */
 
 /**
  * @brief Compile printable schema into a validated schema linking all the references.
  *
- * @param[in] sp Simple parsed printable schema to compile. Can be changed according to the provided options.
+ * @param[in, out] mod Schema structure holding pointers to both schema structure types. The ::lys_module#parsed
+ * member is used as input and ::lys_module#compiled is used to hold the result of the compilation.
  * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
- * @param[out] sc Resulting compiled schema structure.
  * @return LY_ERR value.
  */
-LY_ERR lys_compile(struct lysp_module *sp, int options, struct lysc_module **sc);
+LY_ERR lys_compile(struct lys_module *mod, int options);
+
+/**
+ * @brief Get next schema tree (sibling) node element that can be instantiated in a data tree. Returned node can
+ * be from an augment.
+ *
+ * lys_getnext() is supposed to be called sequentially. In the first call, the \p last parameter is usually NULL
+ * and function starts returning i) the first \p parent's child or ii) the first top level element of the \p module.
+ * Consequent calls suppose to provide the previously returned node as the \p last parameter and still the same
+ * \p parent and \p module parameters.
+ *
+ * Without options, the function is used to traverse only the schema nodes that can be paired with corresponding
+ * data nodes in a data tree. By setting some \p options the behavior can be modified to the extent that
+ * all the schema nodes are iteratively returned.
+ *
+ * @param[in] last Previously returned schema tree node, or NULL in case of the first call.
+ * @param[in] parent Parent of the subtree where the function starts processing.
+ * @param[in] module In case of iterating on top level elements, the \p parent is NULL and
+ * module must be specified.
+ * @param[in] options [ORed options](@ref sgetnextflags).
+ * @return Next schema tree node that can be instantiated in a data tree, NULL in case there is no such element.
+ */
+const struct lysc_node *lys_getnext(const struct lysc_node *last, const struct lysc_node *parent,
+                                    const struct lysc_module *module, int options);
+
+/**
+ * @defgroup sgetnextflags lys_getnext() flags
+ * @ingroup schematree
+ *
+ * @{
+ */
+#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 */
+/** @} sgetnextflags */
+
+/**
+ * @brief Get child node according to the specified criteria.
+ *
+ * @param[in] parent Optional parent of the node to find. If not specified, the module's top-level nodes are searched.
+ * @param[in] module module of the node to find. It is also limitation for the children node of the given parent.
+ * @param[in] name Name of the node to find.
+ * @param[in] name_len Optional length of the name in case it is not NULL-terminated string.
+ * @param[in] nodetype Optional criteria (to speedup) specifying nodetype(s) of the node to find.
+ * Used as a bitmask, so multiple nodetypes can be specified.
+ * @param[in] options [ORed options](@ref sgetnextflags).
+ * @return Found node if any.
+ */
+const struct lysc_node *lys_child(const struct lysc_node *parent, const struct lys_module *module,
+                                  const char *name, size_t name_len, uint16_t nodetype, int options);
+
+/**
+ * @brief Check if the schema node is disabled in the schema tree, i.e. there is any disabled if-feature statement
+ * affecting the node.
+ *
+ * @param[in] node Schema node to check.
+ * @param[in] recursive - 0 to check if-feature only in the \p node schema node,
+ * - 1 to check if-feature in all ascendant schema nodes until there is a node possibly having an instance in a data tree
+ * @return - NULL if enabled,
+ * - pointer to the node with the unsatisfied (disabling) if-feature expression.
+ */
+const struct lysc_iffeature *lys_is_disabled(const struct lysc_node *node, int recursive);
 
 /** @} */
 
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
new file mode 100644
index 0000000..4761520
--- /dev/null
+++ b/src/tree_schema_compile.c
@@ -0,0 +1,3787 @@
+/**
+ * @file tree_schema.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Schema tree implementation
+ *
+ * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "common.h"
+
+#include <ctype.h>
+#include <stdio.h>
+
+#include "libyang.h"
+#include "context.h"
+#include "tree_schema_internal.h"
+#include "xpath.h"
+
+/**
+ * @brief Duplicate string into dictionary
+ * @param[in] CTX libyang context of the dictionary.
+ * @param[in] ORIG String to duplicate.
+ * @param[out] DUP Where to store the result.
+ */
+#define DUP_STRING(CTX, ORIG, DUP) if (ORIG) {DUP = lydict_insert(CTX, ORIG, 0);}
+
+#define COMPILE_ARRAY_GOTO(CTX, ARRAY_P, ARRAY_C, OPTIONS, ITER, FUNC, RET, GOTO) \
+    if (ARRAY_P) { \
+        LY_ARRAY_CREATE_GOTO((CTX)->ctx, ARRAY_C, LY_ARRAY_SIZE(ARRAY_P), RET, GOTO); \
+        size_t __array_offset = LY_ARRAY_SIZE(ARRAY_C); \
+        for (ITER = 0; ITER < LY_ARRAY_SIZE(ARRAY_P); ++ITER) { \
+            LY_ARRAY_INCREMENT(ARRAY_C); \
+            RET = FUNC(CTX, &(ARRAY_P)[ITER], OPTIONS, &(ARRAY_C)[ITER + __array_offset]); \
+            LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
+        } \
+    }
+
+#define COMPILE_ARRAY_UNIQUE_GOTO(CTX, ARRAY_P, ARRAY_C, OPTIONS, ITER, FUNC, RET, GOTO) \
+    if (ARRAY_P) { \
+        LY_ARRAY_CREATE_GOTO((CTX)->ctx, ARRAY_C, LY_ARRAY_SIZE(ARRAY_P), RET, GOTO); \
+        size_t __array_offset = LY_ARRAY_SIZE(ARRAY_C); \
+        for (ITER = 0; ITER < LY_ARRAY_SIZE(ARRAY_P); ++ITER) { \
+            LY_ARRAY_INCREMENT(ARRAY_C); \
+            RET = FUNC(CTX, &(ARRAY_P)[ITER], OPTIONS, ARRAY_C, &(ARRAY_C)[ITER + __array_offset]); \
+            LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
+        } \
+    }
+
+#define COMPILE_MEMBER_GOTO(CTX, MEMBER_P, MEMBER_C, OPTIONS, FUNC, RET, GOTO) \
+    if (MEMBER_P) { \
+        MEMBER_C = calloc(1, sizeof *(MEMBER_C)); \
+        LY_CHECK_ERR_GOTO(!(MEMBER_C), LOGMEM((CTX)->ctx); RET = LY_EMEM, GOTO); \
+        RET = FUNC(CTX, MEMBER_P, OPTIONS, MEMBER_C); \
+        LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
+    }
+
+#define COMPILE_CHECK_UNIQUENESS(CTX, ARRAY, MEMBER, EXCL, STMT, IDENT) \
+    if (ARRAY) { \
+        for (unsigned int u = 0; u < LY_ARRAY_SIZE(ARRAY); ++u) { \
+            if (&(ARRAY)[u] != EXCL && (void*)((ARRAY)[u].MEMBER) == (void*)(IDENT)) { \
+                LOGVAL((CTX)->ctx, LY_VLOG_STR, (CTX)->path, LY_VCODE_DUPIDENT, IDENT, STMT); \
+                return LY_EVALID; \
+            } \
+        } \
+    }
+
+static struct lysc_ext_instance *
+lysc_ext_instance_dup(struct ly_ctx *ctx, struct lysc_ext_instance *orig)
+{
+    /* TODO */
+    (void) ctx;
+    (void) orig;
+    return NULL;
+}
+
+static struct lysc_pattern*
+lysc_pattern_dup(struct lysc_pattern *orig)
+{
+    ++orig->refcount;
+    return orig;
+}
+
+static struct lysc_pattern**
+lysc_patterns_dup(struct ly_ctx *ctx, struct lysc_pattern **orig)
+{
+    struct lysc_pattern **dup = NULL;
+    unsigned int u;
+
+    LY_ARRAY_CREATE_RET(ctx, dup, LY_ARRAY_SIZE(orig), NULL);
+    LY_ARRAY_FOR(orig, u) {
+        dup[u] = lysc_pattern_dup(orig[u]);
+        LY_ARRAY_INCREMENT(dup);
+    }
+    return dup;
+}
+
+struct lysc_range*
+lysc_range_dup(struct ly_ctx *ctx, const struct lysc_range *orig)
+{
+    struct lysc_range *dup;
+    LY_ERR ret;
+
+    dup = calloc(1, sizeof *dup);
+    LY_CHECK_ERR_RET(!dup, LOGMEM(ctx), NULL);
+    if (orig->parts) {
+        LY_ARRAY_CREATE_GOTO(ctx, dup->parts, LY_ARRAY_SIZE(orig->parts), ret, cleanup);
+        LY_ARRAY_SIZE(dup->parts) = LY_ARRAY_SIZE(orig->parts);
+        memcpy(dup->parts, orig->parts, LY_ARRAY_SIZE(dup->parts) * sizeof *dup->parts);
+    }
+    DUP_STRING(ctx, orig->eapptag, dup->eapptag);
+    DUP_STRING(ctx, orig->emsg, dup->emsg);
+    dup->exts = lysc_ext_instance_dup(ctx, orig->exts);
+
+    return dup;
+cleanup:
+    free(dup);
+    (void) ret; /* set but not used due to the return type */
+    return NULL;
+}
+
+struct iff_stack {
+    int size;
+    int index;     /* first empty item */
+    uint8_t *stack;
+};
+
+static LY_ERR
+iff_stack_push(struct iff_stack *stack, uint8_t value)
+{
+    if (stack->index == stack->size) {
+        stack->size += 4;
+        stack->stack = ly_realloc(stack->stack, stack->size * sizeof *stack->stack);
+        LY_CHECK_ERR_RET(!stack->stack, LOGMEM(NULL); stack->size = 0, LY_EMEM);
+    }
+    stack->stack[stack->index++] = value;
+    return LY_SUCCESS;
+}
+
+static uint8_t
+iff_stack_pop(struct iff_stack *stack)
+{
+    stack->index--;
+    return stack->stack[stack->index];
+}
+
+static void
+iff_stack_clean(struct iff_stack *stack)
+{
+    stack->size = 0;
+    free(stack->stack);
+}
+
+static void
+iff_setop(uint8_t *list, uint8_t op, int pos)
+{
+    uint8_t *item;
+    uint8_t mask = 3;
+
+    assert(pos >= 0);
+    assert(op <= 3); /* max 2 bits */
+
+    item = &list[pos / 4];
+    mask = mask << 2 * (pos % 4);
+    *item = (*item) & ~mask;
+    *item = (*item) | (op << 2 * (pos % 4));
+}
+
+#define LYS_IFF_LP 0x04 /* ( */
+#define LYS_IFF_RP 0x08 /* ) */
+
+static struct lysc_feature *
+lysc_feature_find(struct lysc_module *mod, const char *name, size_t len)
+{
+    size_t i;
+    struct lysc_feature *f;
+
+    for (i = 0; i < len; ++i) {
+        if (name[i] == ':') {
+            /* we have a prefixed feature */
+            mod = lysc_module_find_prefix(mod, name, i);
+            LY_CHECK_RET(!mod, NULL);
+
+            name = &name[i + 1];
+            len = len - i - 1;
+        }
+    }
+
+    /* we have the correct module, get the feature */
+    LY_ARRAY_FOR(mod->features, i) {
+        f = &mod->features[i];
+        if (!strncmp(f->name, name, len) && f->name[len] == '\0') {
+            return f;
+        }
+    }
+
+    return NULL;
+}
+
+static LY_ERR
+lys_compile_ext(struct lysc_ctx *ctx, struct lysp_ext_instance *ext_p, int UNUSED(options), struct lysc_ext_instance *ext)
+{
+    const char *name;
+    unsigned int u;
+    const struct lys_module *mod;
+    struct lysp_ext *edef = NULL;
+
+    DUP_STRING(ctx->ctx, ext_p->argument, ext->argument);
+    ext->insubstmt = ext_p->insubstmt;
+    ext->insubstmt_index = ext_p->insubstmt_index;
+
+    /* get module where the extension definition should be placed */
+    for (u = 0; ext_p->name[u] != ':'; ++u);
+    mod = lys_module_find_prefix(ctx->mod_def, ext_p->name, u);
+    LY_CHECK_ERR_RET(!mod, LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                                  "Invalid prefix \"%.*s\" used for extension instance identifier.", u, ext_p->name),
+                     LY_EVALID);
+    LY_CHECK_ERR_RET(!mod->parsed->extensions,
+                     LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                            "Extension instance \"%s\" refers \"%s\" module that does not contain extension definitions.",
+                            ext_p->name, mod->parsed->name),
+                     LY_EVALID);
+    name = &ext_p->name[u + 1];
+    /* find the extension definition there */
+    for (ext = NULL, u = 0; u < LY_ARRAY_SIZE(mod->parsed->extensions); ++u) {
+        if (!strcmp(name, mod->parsed->extensions[u].name)) {
+            edef = &mod->parsed->extensions[u];
+            break;
+        }
+    }
+    LY_CHECK_ERR_RET(!edef, LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                                   "Extension definition of extension instance \"%s\" not found.", ext_p->name),
+                     LY_EVALID);
+    /* TODO plugins */
+
+    return LY_SUCCESS;
+}
+
+static LY_ERR
+lys_compile_iffeature(struct lysc_ctx *ctx, const char **value, int UNUSED(options), struct lysc_iffeature *iff)
+{
+    const char *c = *value;
+    int r, rc = EXIT_FAILURE;
+    int i, j, last_not, checkversion = 0;
+    unsigned int f_size = 0, expr_size = 0, f_exp = 1;
+    uint8_t op;
+    struct iff_stack stack = {0, 0, NULL};
+    struct lysc_feature *f;
+
+    assert(c);
+
+    /* pre-parse the expression to get sizes for arrays, also do some syntax checks of the expression */
+    for (i = j = last_not = 0; c[i]; i++) {
+        if (c[i] == '(') {
+            j++;
+            checkversion = 1;
+            continue;
+        } else if (c[i] == ')') {
+            j--;
+            continue;
+        } else if (isspace(c[i])) {
+            checkversion = 1;
+            continue;
+        }
+
+        if (!strncmp(&c[i], "not", r = 3) || !strncmp(&c[i], "and", r = 3) || !strncmp(&c[i], "or", r = 2)) {
+            if (c[i + r] == '\0') {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid value \"%s\" of if-feature - unexpected end of expression.", *value);
+                return LY_EVALID;
+            } else if (!isspace(c[i + r])) {
+                /* feature name starting with the not/and/or */
+                last_not = 0;
+                f_size++;
+            } else if (c[i] == 'n') { /* not operation */
+                if (last_not) {
+                    /* double not */
+                    expr_size = expr_size - 2;
+                    last_not = 0;
+                } else {
+                    last_not = 1;
+                }
+            } else { /* and, or */
+                f_exp++;
+                /* not a not operation */
+                last_not = 0;
+            }
+            i += r;
+        } else {
+            f_size++;
+            last_not = 0;
+        }
+        expr_size++;
+
+        while (!isspace(c[i])) {
+            if (!c[i] || c[i] == ')') {
+                i--;
+                break;
+            }
+            i++;
+        }
+    }
+    if (j || f_exp != f_size) {
+        /* not matching count of ( and ) */
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+               "Invalid value \"%s\" of if-feature - non-matching opening and closing parentheses.", *value);
+        return LY_EVALID;
+    }
+
+    if (checkversion || expr_size > 1) {
+        /* check that we have 1.1 module */
+        if (ctx->mod_def->parsed->version != LYS_VERSION_1_1) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                   "Invalid value \"%s\" of if-feature - YANG 1.1 expression in YANG 1.0 module.", *value);
+            return LY_EVALID;
+        }
+    }
+
+    /* allocate the memory */
+    LY_ARRAY_CREATE_RET(ctx->ctx, iff->features, f_size, LY_EMEM);
+    iff->expr = calloc((j = (expr_size / 4) + ((expr_size % 4) ? 1 : 0)), sizeof *iff->expr);
+    stack.stack = malloc(expr_size * sizeof *stack.stack);
+    LY_CHECK_ERR_GOTO(!stack.stack || !iff->expr, LOGMEM(ctx->ctx), error);
+
+    stack.size = expr_size;
+    f_size--; expr_size--; /* used as indexes from now */
+
+    for (i--; i >= 0; i--) {
+        if (c[i] == ')') {
+            /* push it on stack */
+            iff_stack_push(&stack, LYS_IFF_RP);
+            continue;
+        } else if (c[i] == '(') {
+            /* pop from the stack into result all operators until ) */
+            while((op = iff_stack_pop(&stack)) != LYS_IFF_RP) {
+                iff_setop(iff->expr, op, expr_size--);
+            }
+            continue;
+        } else if (isspace(c[i])) {
+            continue;
+        }
+
+        /* end of operator or operand -> find beginning and get what is it */
+        j = i + 1;
+        while (i >= 0 && !isspace(c[i]) && c[i] != '(') {
+            i--;
+        }
+        i++; /* go back by one step */
+
+        if (!strncmp(&c[i], "not", 3) && isspace(c[i + 3])) {
+            if (stack.index && stack.stack[stack.index - 1] == LYS_IFF_NOT) {
+                /* double not */
+                iff_stack_pop(&stack);
+            } else {
+                /* not has the highest priority, so do not pop from the stack
+                 * as in case of AND and OR */
+                iff_stack_push(&stack, LYS_IFF_NOT);
+            }
+        } else if (!strncmp(&c[i], "and", 3) && isspace(c[i + 3])) {
+            /* as for OR - pop from the stack all operators with the same or higher
+             * priority and store them to the result, then push the AND to the stack */
+            while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_AND) {
+                op = iff_stack_pop(&stack);
+                iff_setop(iff->expr, op, expr_size--);
+            }
+            iff_stack_push(&stack, LYS_IFF_AND);
+        } else if (!strncmp(&c[i], "or", 2) && isspace(c[i + 2])) {
+            while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_OR) {
+                op = iff_stack_pop(&stack);
+                iff_setop(iff->expr, op, expr_size--);
+            }
+            iff_stack_push(&stack, LYS_IFF_OR);
+        } else {
+            /* feature name, length is j - i */
+
+            /* add it to the expression */
+            iff_setop(iff->expr, LYS_IFF_F, expr_size--);
+
+            /* now get the link to the feature definition */
+            f = lysc_feature_find(ctx->mod->compiled, &c[i], j - i);
+            LY_CHECK_ERR_GOTO(!f,
+                              LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                                     "Invalid value \"%s\" of if-feature - unable to find feature \"%.*s\".", *value, j - i, &c[i]);
+                              rc = LY_EVALID,
+                              error)
+            iff->features[f_size] = f;
+            LY_ARRAY_INCREMENT(iff->features);
+            f_size--;
+        }
+    }
+    while (stack.index) {
+        op = iff_stack_pop(&stack);
+        iff_setop(iff->expr, op, expr_size--);
+    }
+
+    if (++expr_size || ++f_size) {
+        /* not all expected operators and operands found */
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+               "Invalid value \"%s\" of if-feature - processing error.", *value);
+        rc = LY_EINT;
+    } else {
+        rc = LY_SUCCESS;
+    }
+
+error:
+    /* cleanup */
+    iff_stack_clean(&stack);
+
+    return rc;
+}
+
+static LY_ERR
+lys_compile_when(struct lysc_ctx *ctx, struct lysp_when *when_p, int options, struct lysc_when *when)
+{
+    unsigned int u;
+    LY_ERR ret = LY_SUCCESS;
+
+    when->cond = lyxp_expr_parse(ctx->ctx, when_p->cond);
+    LY_CHECK_ERR_GOTO(!when->cond, ret = ly_errcode(ctx->ctx), done);
+    COMPILE_ARRAY_GOTO(ctx, when_p->exts, when->exts, options, u, lys_compile_ext, ret, done);
+
+done:
+    return ret;
+}
+
+static LY_ERR
+lys_compile_must(struct lysc_ctx *ctx, struct lysp_restr *must_p, int options, struct lysc_must *must)
+{
+    unsigned int u;
+    LY_ERR ret = LY_SUCCESS;
+
+    must->cond = lyxp_expr_parse(ctx->ctx, must_p->arg);
+    LY_CHECK_ERR_GOTO(!must->cond, ret = ly_errcode(ctx->ctx), done);
+
+    DUP_STRING(ctx->ctx, must_p->eapptag, must->eapptag);
+    DUP_STRING(ctx->ctx, must_p->emsg, must->emsg);
+    COMPILE_ARRAY_GOTO(ctx, must_p->exts, must->exts, options, u, lys_compile_ext, ret, done);
+
+done:
+    return ret;
+}
+
+static LY_ERR
+lys_compile_import(struct lysc_ctx *ctx, struct lysp_import *imp_p, int options, struct lysc_import *imp)
+{
+    unsigned int u;
+    struct lys_module *mod = NULL;
+    struct lysc_module *comp;
+    LY_ERR ret = LY_SUCCESS;
+
+    DUP_STRING(ctx->ctx, imp_p->prefix, imp->prefix);
+    COMPILE_ARRAY_GOTO(ctx, imp_p->exts, imp->exts, options, u, lys_compile_ext, ret, done);
+    imp->module = imp_p->module;
+
+    /* make sure that we have the parsed version (lysp_) of the imported module to import groupings or typedefs.
+     * The compiled version is needed only for augments, deviates and leafrefs, so they are checked (and added,
+     * if needed) when these nodes are finally being instantiated and validated at the end of schema compilation. */
+    if (!imp->module->parsed) {
+        comp = imp->module->compiled;
+        /* try to get filepath from the compiled version */
+        if (comp->filepath) {
+            mod = (struct lys_module*)lys_parse_path(ctx->ctx, comp->filepath,
+                                 !strcmp(&comp->filepath[strlen(comp->filepath - 4)], ".yin") ? LYS_IN_YIN : LYS_IN_YANG);
+            if (mod != imp->module) {
+                LOGERR(ctx->ctx, LY_EINT, "Filepath \"%s\" of the module \"%s\" does not match.",
+                       comp->filepath, comp->name);
+                mod = NULL;
+            }
+        }
+        if (!mod) {
+            if (lysp_load_module(ctx->ctx, comp->name, comp->revision, 0, 1, &mod)) {
+                LOGERR(ctx->ctx, LY_ENOTFOUND, "Unable to reload \"%s\" module to import it into \"%s\", source data not found.",
+                       comp->name, ctx->mod->compiled->name);
+                return LY_ENOTFOUND;
+            }
+        }
+    }
+
+done:
+    return ret;
+}
+
+static LY_ERR
+lys_compile_identity(struct lysc_ctx *ctx, struct lysp_ident *ident_p, int options, struct lysc_ident *idents, struct lysc_ident *ident)
+{
+    unsigned int u;
+    LY_ERR ret = LY_SUCCESS;
+
+    COMPILE_CHECK_UNIQUENESS(ctx, idents, name, ident, "identity", ident_p->name);
+    DUP_STRING(ctx->ctx, ident_p->name, ident->name);
+    COMPILE_ARRAY_GOTO(ctx, ident_p->iffeatures, ident->iffeatures, options, u, lys_compile_iffeature, ret, done);
+    /* backlings (derived) can be added no sooner than when all the identities in the current module are present */
+    COMPILE_ARRAY_GOTO(ctx, ident_p->exts, ident->exts, options, u, lys_compile_ext, ret, done);
+    ident->flags = ident_p->flags;
+
+done:
+    return ret;
+}
+
+/**
+ * @brief Find and process the referenced base identities from another identity or identityref
+ *
+ * For bases in identity se backlinks to them from the base identities. For identityref, store
+ * the array of pointers to the base identities. So one of the ident or bases parameter must be set
+ * to distinguish these two use cases.
+ *
+ * @param[in] ctx Compile context, not only for logging but also to get the current module to resolve prefixes.
+ * @param[in] bases_p Array of names (including prefix if necessary) of base identities.
+ * @param[in] ident Referencing identity to work with.
+ * @param[in] bases Array of bases of identityref to fill in.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_identity_bases(struct lysc_ctx *ctx, const char **bases_p,  struct lysc_ident *ident, struct lysc_ident ***bases)
+{
+    unsigned int u, v;
+    const char *s, *name;
+    struct lys_module *mod;
+    struct lysc_ident **idref;
+
+    assert(ident || bases);
+
+    if (LY_ARRAY_SIZE(bases_p) > 1 && ctx->mod_def->parsed->version < 2) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+               "Multiple bases in %s are allowed only in YANG 1.1 modules.", ident ? "identity" : "identityref type");
+        return LY_EVALID;
+    }
+
+    for (u = 0; u < LY_ARRAY_SIZE(bases_p); ++u) {
+        s = strchr(bases_p[u], ':');
+        if (s) {
+            /* prefixed identity */
+            name = &s[1];
+            mod = lys_module_find_prefix(ctx->mod_def, bases_p[u], s - bases_p[u]);
+        } else {
+            name = bases_p[u];
+            mod = ctx->mod_def;
+        }
+        if (!mod) {
+            if (ident) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid prefix used for base (%s) of identity \"%s\".", bases_p[u], ident->name);
+            } else {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid prefix used for base (%s) of identityref.", bases_p[u]);
+            }
+            return LY_EVALID;
+        }
+        idref = NULL;
+        if (mod->compiled && mod->compiled->identities) {
+            for (v = 0; v < LY_ARRAY_SIZE(mod->compiled->identities); ++v) {
+                if (!strcmp(name, mod->compiled->identities[v].name)) {
+                    if (ident) {
+                        /* we have match! store the backlink */
+                        LY_ARRAY_NEW_RET(ctx->ctx, mod->compiled->identities[v].derived, idref, LY_EMEM);
+                        *idref = ident;
+                    } else {
+                        /* we have match! store the found identity */
+                        LY_ARRAY_NEW_RET(ctx->ctx, *bases, idref, LY_EMEM);
+                        *idref = &mod->compiled->identities[v];
+                    }
+                    break;
+                }
+            }
+        }
+        if (!idref || !(*idref)) {
+            if (ident) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Unable to find base (%s) of identity \"%s\".", bases_p[u], ident->name);
+            } else {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Unable to find base (%s) of identityref.", bases_p[u]);
+            }
+            return LY_EVALID;
+        }
+    }
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief For the given array of identities, set the backlinks from all their base identities.
+ * @param[in] ctx Compile context, not only for logging but also to get the current module to resolve prefixes.
+ * @param[in] idents_p Array of identities definitions from the parsed schema structure.
+ * @param[in] idents Array of referencing identities to which the backlinks are supposed to be set.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_identities_derived(struct lysc_ctx *ctx, struct lysp_ident *idents_p, struct lysc_ident *idents)
+{
+    unsigned int i;
+
+    for (i = 0; i < LY_ARRAY_SIZE(idents_p); ++i) {
+        if (!idents_p[i].bases) {
+            continue;
+        }
+        LY_CHECK_RET(lys_compile_identity_bases(ctx, idents_p[i].bases, &idents[i], NULL));
+    }
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Create compiled feature structure.
+ * @param[in] ctx Compile context.
+ * @param[in] feature_p Parsed feature definition to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] features List of already compiled features to check name duplicity.
+ * @param[in,out] feature Compiled feature structure to fill.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_feature(struct lysc_ctx *ctx, struct lysp_feature *feature_p, int options, struct lysc_feature *features, struct lysc_feature *feature)
+{
+    unsigned int u, v;
+    LY_ERR ret = LY_SUCCESS;
+    struct lysc_feature **df;
+
+    COMPILE_CHECK_UNIQUENESS(ctx, features, name, feature, "feature", feature_p->name);
+    DUP_STRING(ctx->ctx, feature_p->name, feature->name);
+    feature->flags = feature_p->flags;
+
+    COMPILE_ARRAY_GOTO(ctx, feature_p->exts, feature->exts, options, u, lys_compile_ext, ret, done);
+    COMPILE_ARRAY_GOTO(ctx, feature_p->iffeatures, feature->iffeatures, options, u, lys_compile_iffeature, ret, done);
+    if (feature->iffeatures) {
+        for (u = 0; u < LY_ARRAY_SIZE(feature->iffeatures); ++u) {
+            if (feature->iffeatures[u].features) {
+                for (v = 0; v < LY_ARRAY_SIZE(feature->iffeatures[u].features); ++v) {
+                    /* add itself into the dependants list */
+                    LY_ARRAY_NEW_RET(ctx->ctx, feature->iffeatures[u].features[v]->depfeatures, df, LY_EMEM);
+                    *df = feature;
+                }
+                /* TODO check for circular dependency */
+            }
+        }
+    }
+done:
+    return ret;
+}
+
+/**
+ * @brief Validate and normalize numeric value from a range definition.
+ * @param[in] ctx Compile context.
+ * @param[in] basetype Base YANG built-in type of the node connected with the range restriction. Actually only LY_TYPE_DEC64 is important to
+ * allow processing of the fractions. The fraction point is extracted from the value which is then normalize according to given frdigits into
+ * valcopy to allow easy parsing and storing of the value. libyang stores decimal number without the decimal point which is always recovered from
+ * the known fraction-digits value. So, with fraction-digits 2, number 3.14 is stored as 314 and number 1 is stored as 100.
+ * @param[in] frdigits The fraction-digits of the type in case of LY_TYPE_DEC64.
+ * @param[in] value String value of the range boundary.
+ * @param[out] len Number of the processed bytes from the value. Processing stops on the first character which is not part of the number boundary.
+ * @param[out] valcopy NULL-terminated string with the numeric value to parse and store.
+ * @return LY_ERR value - LY_SUCCESS, LY_EMEM, LY_EVALID (no number) or LY_EINVAL (decimal64 not matching fraction-digits value).
+ */
+static LY_ERR
+range_part_check_value_syntax(struct lysc_ctx *ctx, LY_DATA_TYPE basetype, uint8_t frdigits, const char *value, size_t *len, char **valcopy)
+{
+    size_t fraction = 0, size;
+
+    *len = 0;
+
+    assert(value);
+    /* parse value */
+    if (!isdigit(value[*len]) && (value[*len] != '-') && (value[*len] != '+')) {
+        return LY_EVALID;
+    }
+
+    if ((value[*len] == '-') || (value[*len] == '+')) {
+        ++(*len);
+    }
+
+    while (isdigit(value[*len])) {
+        ++(*len);
+    }
+
+    if ((basetype != LY_TYPE_DEC64) || (value[*len] != '.') || !isdigit(value[*len + 1])) {
+        if (basetype == LY_TYPE_DEC64) {
+            goto decimal;
+        } else {
+            *valcopy = strndup(value, *len);
+            return LY_SUCCESS;
+        }
+    }
+    fraction = *len;
+
+    ++(*len);
+    while (isdigit(value[*len])) {
+        ++(*len);
+    }
+
+    if (basetype == LY_TYPE_DEC64) {
+decimal:
+        assert(frdigits);
+        if (*len - 1 - fraction > frdigits) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                   "Range boundary \"%.*s\" of decimal64 type exceeds defined number (%u) of fraction digits.",
+                   *len, value, frdigits);
+            return LY_EINVAL;
+        }
+        if (fraction) {
+            size = (*len) + (frdigits - ((*len) - 1 - fraction));
+        } else {
+            size = (*len) + frdigits + 1;
+        }
+        *valcopy = malloc(size * sizeof **valcopy);
+        LY_CHECK_ERR_RET(!(*valcopy), LOGMEM(ctx->ctx), LY_EMEM);
+
+        (*valcopy)[size - 1] = '\0';
+        if (fraction) {
+            memcpy(&(*valcopy)[0], &value[0], fraction);
+            memcpy(&(*valcopy)[fraction], &value[fraction + 1], (*len) - 1 - (fraction));
+            memset(&(*valcopy)[(*len) - 1], '0', frdigits - ((*len) - 1 - fraction));
+        } else {
+            memcpy(&(*valcopy)[0], &value[0], *len);
+            memset(&(*valcopy)[*len], '0', frdigits);
+        }
+    }
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Check that values in range are in ascendant order.
+ * @param[in] unsigned_value Flag to note that we are working with unsigned values.
+ * @param[in] max Flag to distinguish if checking min or max value. min value must be strictly higher than previous,
+ * max can be also equal.
+ * @param[in] value Current value to check.
+ * @param[in] prev_value The last seen value.
+ * @return LY_SUCCESS or LY_EEXIST for invalid order.
+ */
+static LY_ERR
+range_part_check_ascendancy(int unsigned_value, int max, int64_t value, int64_t prev_value)
+{
+    if (unsigned_value) {
+        if ((max && (uint64_t)prev_value > (uint64_t)value) || (!max && (uint64_t)prev_value >= (uint64_t)value)) {
+            return LY_EEXIST;
+        }
+    } else {
+        if ((max && prev_value > value) || (!max && prev_value >= value)) {
+            return LY_EEXIST;
+        }
+    }
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Set min/max value of the range part.
+ * @param[in] ctx Compile context.
+ * @param[in] part Range part structure to fill.
+ * @param[in] max Flag to distinguish if storing min or max value.
+ * @param[in] prev The last seen value to check that all values in range are specified in ascendant order.
+ * @param[in] basetype Type of the value to get know implicit min/max values and other checking rules.
+ * @param[in] first Flag for the first value of the range to avoid ascendancy order.
+ * @param[in] length_restr Flag to distinguish between range and length restrictions. Only for logging.
+ * @param[in] frdigits The fraction-digits value in case of LY_TYPE_DEC64 basetype.
+ * @param[in] base_range Range from the type from which the current type is derived (if not built-in) to get type's min and max values.
+ * @param[in,out] value Numeric range value to be stored, if not provided the type's min/max value is set.
+ * @return LY_ERR value - LY_SUCCESS, LY_EDENIED (value brokes type's boundaries), LY_EVALID (not a number),
+ * LY_EEXIST (value is smaller than the previous one), LY_EINVAL (decimal64 value does not corresponds with the
+ * frdigits value), LY_EMEM.
+ */
+static LY_ERR
+range_part_minmax(struct lysc_ctx *ctx, struct lysc_range_part *part, int max, int64_t prev, LY_DATA_TYPE basetype, int first, int length_restr,
+                  uint8_t frdigits, struct lysc_range *base_range, const char **value)
+{
+    LY_ERR ret = LY_SUCCESS;
+    char *valcopy = NULL;
+    size_t len;
+
+    if (value) {
+        ret = range_part_check_value_syntax(ctx, basetype, frdigits, *value, &len, &valcopy);
+        LY_CHECK_GOTO(ret, finalize);
+    }
+    if (!valcopy && base_range) {
+        if (max) {
+            part->max_64 = base_range->parts[LY_ARRAY_SIZE(base_range->parts) - 1].max_64;
+        } else {
+            part->min_64 = base_range->parts[0].min_64;
+        }
+        if (!first) {
+            ret = range_part_check_ascendancy(basetype <= LY_TYPE_STRING ? 1 : 0, max, max ? part->max_64 : part->min_64, prev);
+        }
+        goto finalize;
+    }
+
+    switch (basetype) {
+    case LY_TYPE_INT8: /* range */
+        if (valcopy) {
+            ret = ly_parse_int(valcopy, INT64_C(-128), INT64_C(127), 10, max ? &part->max_64 : &part->min_64);
+        } else if (max) {
+            part->max_64 = INT64_C(127);
+        } else {
+            part->min_64 = INT64_C(-128);
+        }
+        if (!ret && !first) {
+            ret = range_part_check_ascendancy(0, max, max ? part->max_64 : part->min_64, prev);
+        }
+        break;
+    case LY_TYPE_INT16: /* range */
+        if (valcopy) {
+            ret = ly_parse_int(valcopy, INT64_C(-32768), INT64_C(32767), 10, max ? &part->max_64 : &part->min_64);
+        } else if (max) {
+            part->max_64 = INT64_C(32767);
+        } else {
+            part->min_64 = INT64_C(-32768);
+        }
+        if (!ret && !first) {
+            ret = range_part_check_ascendancy(0, max, max ? part->max_64 : part->min_64, prev);
+        }
+        break;
+    case LY_TYPE_INT32: /* range */
+        if (valcopy) {
+            ret = ly_parse_int(valcopy, INT64_C(-2147483648), INT64_C(2147483647), 10, max ? &part->max_64 : &part->min_64);
+        } else if (max) {
+            part->max_64 = INT64_C(2147483647);
+        } else {
+            part->min_64 = INT64_C(-2147483648);
+        }
+        if (!ret && !first) {
+            ret = range_part_check_ascendancy(0, max, max ? part->max_64 : part->min_64, prev);
+        }
+        break;
+    case LY_TYPE_INT64: /* range */
+    case LY_TYPE_DEC64: /* range */
+        if (valcopy) {
+            ret = ly_parse_int(valcopy, INT64_C(-9223372036854775807) - INT64_C(1), INT64_C(9223372036854775807), 10,
+                               max ? &part->max_64 : &part->min_64);
+        } else if (max) {
+            part->max_64 = INT64_C(9223372036854775807);
+        } else {
+            part->min_64 = INT64_C(-9223372036854775807) - INT64_C(1);
+        }
+        if (!ret && !first) {
+            ret = range_part_check_ascendancy(0, max, max ? part->max_64 : part->min_64, prev);
+        }
+        break;
+    case LY_TYPE_UINT8: /* range */
+        if (valcopy) {
+            ret = ly_parse_uint(valcopy, UINT64_C(255), 10, max ? &part->max_u64 : &part->min_u64);
+        } else if (max) {
+            part->max_u64 = UINT64_C(255);
+        } else {
+            part->min_u64 = UINT64_C(0);
+        }
+        if (!ret && !first) {
+            ret = range_part_check_ascendancy(1, max, max ? part->max_64 : part->min_64, prev);
+        }
+        break;
+    case LY_TYPE_UINT16: /* range */
+        if (valcopy) {
+            ret = ly_parse_uint(valcopy, UINT64_C(65535), 10, max ? &part->max_u64 : &part->min_u64);
+        } else if (max) {
+            part->max_u64 = UINT64_C(65535);
+        } else {
+            part->min_u64 = UINT64_C(0);
+        }
+        if (!ret && !first) {
+            ret = range_part_check_ascendancy(1, max, max ? part->max_64 : part->min_64, prev);
+        }
+        break;
+    case LY_TYPE_UINT32: /* range */
+        if (valcopy) {
+            ret = ly_parse_uint(valcopy, UINT64_C(4294967295), 10, max ? &part->max_u64 : &part->min_u64);
+        } else if (max) {
+            part->max_u64 = UINT64_C(4294967295);
+        } else {
+            part->min_u64 = UINT64_C(0);
+        }
+        if (!ret && !first) {
+            ret = range_part_check_ascendancy(1, max, max ? part->max_64 : part->min_64, prev);
+        }
+        break;
+    case LY_TYPE_UINT64: /* range */
+    case LY_TYPE_STRING: /* length */
+    case LY_TYPE_BINARY: /* length */
+        if (valcopy) {
+            ret = ly_parse_uint(valcopy, UINT64_C(18446744073709551615), 10, max ? &part->max_u64 : &part->min_u64);
+        } else if (max) {
+            part->max_u64 = UINT64_C(18446744073709551615);
+        } else {
+            part->min_u64 = UINT64_C(0);
+        }
+        if (!ret && !first) {
+            ret = range_part_check_ascendancy(1, max, max ? part->max_64 : part->min_64, prev);
+        }
+        break;
+    default:
+        LOGINT(ctx->ctx);
+        ret = LY_EINT;
+    }
+
+finalize:
+    if (ret == LY_EDENIED) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+               "Invalid %s restriction - value \"%s\" does not fit the type limitations.",
+               length_restr ? "length" : "range", valcopy ? valcopy : *value);
+    } else if (ret == LY_EVALID) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+               "Invalid %s restriction - invalid value \"%s\".",
+               length_restr ? "length" : "range", valcopy ? valcopy : *value);
+    } else if (ret == LY_EEXIST) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid %s restriction - values are not in ascending order (%s).",
+                       length_restr ? "length" : "range",
+                       (valcopy && basetype != LY_TYPE_DEC64) ? valcopy : value ? *value : max ? "max" : "min");
+    } else if (!ret && value) {
+        *value = *value + len;
+    }
+    free(valcopy);
+    return ret;
+}
+
+/**
+ * @brief Compile the parsed range restriction.
+ * @param[in] ctx Compile context.
+ * @param[in] range_p Parsed range structure to compile.
+ * @param[in] basetype Base YANG built-in type of the node with the range restriction.
+ * @param[in] length_restr Flag to distinguish between range and length restrictions. Only for logging.
+ * @param[in] frdigits The fraction-digits value in case of LY_TYPE_DEC64 basetype.
+ * @param[in] base_range Range restriction of the type from which the current type is derived. The current
+ * range restriction must be more restrictive than the base_range.
+ * @param[in,out] range Pointer to the created current range structure.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_type_range(struct lysc_ctx *ctx, struct lysp_restr *range_p, LY_DATA_TYPE basetype, int length_restr, uint8_t frdigits,
+                       struct lysc_range *base_range, struct lysc_range **range)
+{
+    LY_ERR ret = LY_EVALID;
+    const char *expr;
+    struct lysc_range_part *parts = NULL, *part;
+    int range_expected = 0, uns;
+    unsigned int parts_done = 0, u, v;
+
+    assert(range);
+    assert(range_p);
+
+    expr = range_p->arg;
+    while(1) {
+        if (isspace(*expr)) {
+            ++expr;
+        } else if (*expr == '\0') {
+            if (range_expected) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid %s restriction - unexpected end of the expression after \"..\" (%s).",
+                       length_restr ? "length" : "range", range_p->arg);
+                goto cleanup;
+            } else if (!parts || parts_done == LY_ARRAY_SIZE(parts)) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid %s restriction - unexpected end of the expression (%s).",
+                       length_restr ? "length" : "range", range_p->arg);
+                goto cleanup;
+            }
+            parts_done++;
+            break;
+        } else if (!strncmp(expr, "min", 3)) {
+            if (parts) {
+                /* min cannot be used elsewhere than in the first part */
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid %s restriction - unexpected data before min keyword (%.*s).", length_restr ? "length" : "range",
+                       expr - range_p->arg, range_p->arg);
+                goto cleanup;
+            }
+            expr += 3;
+
+            LY_ARRAY_NEW_GOTO(ctx->ctx, parts, part, ret, cleanup);
+            LY_CHECK_GOTO(range_part_minmax(ctx, part, 0, 0, basetype, 1, length_restr, frdigits, base_range, NULL), cleanup);
+            part->max_64 = part->min_64;
+        } else if (*expr == '|') {
+            if (!parts || range_expected) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid %s restriction - unexpected beginning of the expression (%s).", length_restr ? "length" : "range", expr);
+                goto cleanup;
+            }
+            expr++;
+            parts_done++;
+            /* process next part of the expression */
+        } else if (!strncmp(expr, "..", 2)) {
+            expr += 2;
+            while (isspace(*expr)) {
+                expr++;
+            }
+            if (!parts || LY_ARRAY_SIZE(parts) == parts_done) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid %s restriction - unexpected \"..\" without a lower bound.", length_restr ? "length" : "range");
+                goto cleanup;
+            }
+            /* continue expecting the upper boundary */
+            range_expected = 1;
+        } else if (isdigit(*expr) || (*expr == '-') || (*expr == '+')) {
+            /* number */
+            if (range_expected) {
+                part = &parts[LY_ARRAY_SIZE(parts) - 1];
+                LY_CHECK_GOTO(range_part_minmax(ctx, part, 1, part->min_64, basetype, 0, length_restr, frdigits, NULL, &expr), cleanup);
+                range_expected = 0;
+            } else {
+                LY_ARRAY_NEW_GOTO(ctx->ctx, parts, part, ret, cleanup);
+                LY_CHECK_GOTO(range_part_minmax(ctx, part, 0, parts_done ? parts[LY_ARRAY_SIZE(parts) - 2].max_64 : 0,
+                                                basetype, parts_done ? 0 : 1, length_restr, frdigits, NULL, &expr), cleanup);
+                part->max_64 = part->min_64;
+            }
+
+            /* continue with possible another expression part */
+        } else if (!strncmp(expr, "max", 3)) {
+            expr += 3;
+            while (isspace(*expr)) {
+                expr++;
+            }
+            if (*expr != '\0') {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Invalid %s restriction - unexpected data after max keyword (%s).",
+                       length_restr ? "length" : "range", expr);
+                goto cleanup;
+            }
+            if (range_expected) {
+                part = &parts[LY_ARRAY_SIZE(parts) - 1];
+                LY_CHECK_GOTO(range_part_minmax(ctx, part, 1, part->min_64, basetype, 0, length_restr, frdigits, base_range, NULL), cleanup);
+                range_expected = 0;
+            } else {
+                LY_ARRAY_NEW_GOTO(ctx->ctx, parts, part, ret, cleanup);
+                LY_CHECK_GOTO(range_part_minmax(ctx, part, 1, parts_done ? parts[LY_ARRAY_SIZE(parts) - 2].max_64 : 0,
+                                                basetype, parts_done ? 0 : 1, length_restr, frdigits, base_range, NULL), cleanup);
+                part->min_64 = part->max_64;
+            }
+        } else {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Invalid %s restriction - unexpected data (%s).",
+                   length_restr ? "length" : "range", expr);
+            goto cleanup;
+        }
+    }
+
+    /* check with the previous range/length restriction */
+    if (base_range) {
+        switch (basetype) {
+        case LY_TYPE_BINARY:
+        case LY_TYPE_UINT8:
+        case LY_TYPE_UINT16:
+        case LY_TYPE_UINT32:
+        case LY_TYPE_UINT64:
+        case LY_TYPE_STRING:
+            uns = 1;
+            break;
+        case LY_TYPE_DEC64:
+        case LY_TYPE_INT8:
+        case LY_TYPE_INT16:
+        case LY_TYPE_INT32:
+        case LY_TYPE_INT64:
+            uns = 0;
+            break;
+        default:
+            LOGINT(ctx->ctx);
+            ret = LY_EINT;
+            goto cleanup;
+        }
+        for (u = v = 0; u < parts_done && v < LY_ARRAY_SIZE(base_range->parts); ++u) {
+            if ((uns && parts[u].min_u64 < base_range->parts[v].min_u64) || (!uns && parts[u].min_64 < base_range->parts[v].min_64)) {
+                goto baseerror;
+            }
+            /* current lower bound is not lower than the base */
+            if (base_range->parts[v].min_64 == base_range->parts[v].max_64) {
+                /* base has single value */
+                if (base_range->parts[v].min_64 == parts[u].min_64) {
+                    /* both lower bounds are the same */
+                    if (parts[u].min_64 != parts[u].max_64) {
+                        /* current continues with a range */
+                        goto baseerror;
+                    } else {
+                        /* equal single values, move both forward */
+                        ++v;
+                        continue;
+                    }
+                } else {
+                    /* base is single value lower than current range, so the
+                     * value from base range is removed in the current,
+                     * move only base and repeat checking */
+                    ++v;
+                    --u;
+                    continue;
+                }
+            } else {
+                /* base is the range */
+                if (parts[u].min_64 == parts[u].max_64) {
+                    /* current is a single value */
+                    if ((uns && parts[u].max_u64 > base_range->parts[v].max_u64) || (!uns && parts[u].max_64 > base_range->parts[v].max_64)) {
+                        /* current is behind the base range, so base range is omitted,
+                         * move the base and keep the current for further check */
+                        ++v;
+                        --u;
+                    } /* else it is within the base range, so move the current, but keep the base */
+                    continue;
+                } else {
+                    /* both are ranges - check the higher bound, the lower was already checked */
+                    if ((uns && parts[u].max_u64 > base_range->parts[v].max_u64) || (!uns && parts[u].max_64 > base_range->parts[v].max_64)) {
+                        /* higher bound is higher than the current higher bound */
+                        if ((uns && parts[u].min_u64 > base_range->parts[v].max_u64) || (!uns && parts[u].min_64 > base_range->parts[v].max_64)) {
+                            /* but the current lower bound is also higher, so the base range is omitted,
+                             * continue with the same current, but move the base */
+                            --u;
+                            ++v;
+                            continue;
+                        }
+                        /* current range starts within the base range but end behind it */
+                        goto baseerror;
+                    } else {
+                        /* current range is smaller than the base,
+                         * move current, but stay with the base */
+                        continue;
+                    }
+                }
+            }
+        }
+        if (u != parts_done) {
+baseerror:
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                   "Invalid %s restriction - the derived restriction (%s) is not equally or more limiting.",
+                   length_restr ? "length" : "range", range_p->arg);
+            goto cleanup;
+        }
+    }
+
+    if (!(*range)) {
+        *range = calloc(1, sizeof **range);
+        LY_CHECK_ERR_RET(!(*range), LOGMEM(ctx->ctx), LY_EMEM);
+    }
+
+    if (range_p->eapptag) {
+        lydict_remove(ctx->ctx, (*range)->eapptag);
+        (*range)->eapptag = lydict_insert(ctx->ctx, range_p->eapptag, 0);
+    }
+    if (range_p->emsg) {
+        lydict_remove(ctx->ctx, (*range)->emsg);
+        (*range)->emsg = lydict_insert(ctx->ctx, range_p->emsg, 0);
+    }
+    /* extensions are taken only from the last range by the caller */
+
+    (*range)->parts = parts;
+    parts = NULL;
+    ret = LY_SUCCESS;
+cleanup:
+    /* TODO clean up */
+    LY_ARRAY_FREE(parts);
+
+    return ret;
+}
+
+/**
+ * @brief Checks pattern syntax.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] pattern Pattern to check.
+ * @param[in,out] pcre_precomp Precompiled PCRE pattern. If NULL, the compiled information used to validate pattern are freed.
+ * @return LY_ERR value - LY_SUCCESS, LY_EMEM, LY_EVALID.
+ */
+static LY_ERR
+lys_compile_type_pattern_check(struct lysc_ctx *ctx, const char *pattern, pcre **pcre_precomp)
+{
+    int idx, idx2, start, end, err_offset, count;
+    char *perl_regex, *ptr;
+    const char *err_msg, *orig_ptr;
+    pcre *precomp;
+#define URANGE_LEN 19
+    char *ublock2urange[][2] = {
+        {"BasicLatin", "[\\x{0000}-\\x{007F}]"},
+        {"Latin-1Supplement", "[\\x{0080}-\\x{00FF}]"},
+        {"LatinExtended-A", "[\\x{0100}-\\x{017F}]"},
+        {"LatinExtended-B", "[\\x{0180}-\\x{024F}]"},
+        {"IPAExtensions", "[\\x{0250}-\\x{02AF}]"},
+        {"SpacingModifierLetters", "[\\x{02B0}-\\x{02FF}]"},
+        {"CombiningDiacriticalMarks", "[\\x{0300}-\\x{036F}]"},
+        {"Greek", "[\\x{0370}-\\x{03FF}]"},
+        {"Cyrillic", "[\\x{0400}-\\x{04FF}]"},
+        {"Armenian", "[\\x{0530}-\\x{058F}]"},
+        {"Hebrew", "[\\x{0590}-\\x{05FF}]"},
+        {"Arabic", "[\\x{0600}-\\x{06FF}]"},
+        {"Syriac", "[\\x{0700}-\\x{074F}]"},
+        {"Thaana", "[\\x{0780}-\\x{07BF}]"},
+        {"Devanagari", "[\\x{0900}-\\x{097F}]"},
+        {"Bengali", "[\\x{0980}-\\x{09FF}]"},
+        {"Gurmukhi", "[\\x{0A00}-\\x{0A7F}]"},
+        {"Gujarati", "[\\x{0A80}-\\x{0AFF}]"},
+        {"Oriya", "[\\x{0B00}-\\x{0B7F}]"},
+        {"Tamil", "[\\x{0B80}-\\x{0BFF}]"},
+        {"Telugu", "[\\x{0C00}-\\x{0C7F}]"},
+        {"Kannada", "[\\x{0C80}-\\x{0CFF}]"},
+        {"Malayalam", "[\\x{0D00}-\\x{0D7F}]"},
+        {"Sinhala", "[\\x{0D80}-\\x{0DFF}]"},
+        {"Thai", "[\\x{0E00}-\\x{0E7F}]"},
+        {"Lao", "[\\x{0E80}-\\x{0EFF}]"},
+        {"Tibetan", "[\\x{0F00}-\\x{0FFF}]"},
+        {"Myanmar", "[\\x{1000}-\\x{109F}]"},
+        {"Georgian", "[\\x{10A0}-\\x{10FF}]"},
+        {"HangulJamo", "[\\x{1100}-\\x{11FF}]"},
+        {"Ethiopic", "[\\x{1200}-\\x{137F}]"},
+        {"Cherokee", "[\\x{13A0}-\\x{13FF}]"},
+        {"UnifiedCanadianAboriginalSyllabics", "[\\x{1400}-\\x{167F}]"},
+        {"Ogham", "[\\x{1680}-\\x{169F}]"},
+        {"Runic", "[\\x{16A0}-\\x{16FF}]"},
+        {"Khmer", "[\\x{1780}-\\x{17FF}]"},
+        {"Mongolian", "[\\x{1800}-\\x{18AF}]"},
+        {"LatinExtendedAdditional", "[\\x{1E00}-\\x{1EFF}]"},
+        {"GreekExtended", "[\\x{1F00}-\\x{1FFF}]"},
+        {"GeneralPunctuation", "[\\x{2000}-\\x{206F}]"},
+        {"SuperscriptsandSubscripts", "[\\x{2070}-\\x{209F}]"},
+        {"CurrencySymbols", "[\\x{20A0}-\\x{20CF}]"},
+        {"CombiningMarksforSymbols", "[\\x{20D0}-\\x{20FF}]"},
+        {"LetterlikeSymbols", "[\\x{2100}-\\x{214F}]"},
+        {"NumberForms", "[\\x{2150}-\\x{218F}]"},
+        {"Arrows", "[\\x{2190}-\\x{21FF}]"},
+        {"MathematicalOperators", "[\\x{2200}-\\x{22FF}]"},
+        {"MiscellaneousTechnical", "[\\x{2300}-\\x{23FF}]"},
+        {"ControlPictures", "[\\x{2400}-\\x{243F}]"},
+        {"OpticalCharacterRecognition", "[\\x{2440}-\\x{245F}]"},
+        {"EnclosedAlphanumerics", "[\\x{2460}-\\x{24FF}]"},
+        {"BoxDrawing", "[\\x{2500}-\\x{257F}]"},
+        {"BlockElements", "[\\x{2580}-\\x{259F}]"},
+        {"GeometricShapes", "[\\x{25A0}-\\x{25FF}]"},
+        {"MiscellaneousSymbols", "[\\x{2600}-\\x{26FF}]"},
+        {"Dingbats", "[\\x{2700}-\\x{27BF}]"},
+        {"BraillePatterns", "[\\x{2800}-\\x{28FF}]"},
+        {"CJKRadicalsSupplement", "[\\x{2E80}-\\x{2EFF}]"},
+        {"KangxiRadicals", "[\\x{2F00}-\\x{2FDF}]"},
+        {"IdeographicDescriptionCharacters", "[\\x{2FF0}-\\x{2FFF}]"},
+        {"CJKSymbolsandPunctuation", "[\\x{3000}-\\x{303F}]"},
+        {"Hiragana", "[\\x{3040}-\\x{309F}]"},
+        {"Katakana", "[\\x{30A0}-\\x{30FF}]"},
+        {"Bopomofo", "[\\x{3100}-\\x{312F}]"},
+        {"HangulCompatibilityJamo", "[\\x{3130}-\\x{318F}]"},
+        {"Kanbun", "[\\x{3190}-\\x{319F}]"},
+        {"BopomofoExtended", "[\\x{31A0}-\\x{31BF}]"},
+        {"EnclosedCJKLettersandMonths", "[\\x{3200}-\\x{32FF}]"},
+        {"CJKCompatibility", "[\\x{3300}-\\x{33FF}]"},
+        {"CJKUnifiedIdeographsExtensionA", "[\\x{3400}-\\x{4DB5}]"},
+        {"CJKUnifiedIdeographs", "[\\x{4E00}-\\x{9FFF}]"},
+        {"YiSyllables", "[\\x{A000}-\\x{A48F}]"},
+        {"YiRadicals", "[\\x{A490}-\\x{A4CF}]"},
+        {"HangulSyllables", "[\\x{AC00}-\\x{D7A3}]"},
+        {"PrivateUse", "[\\x{E000}-\\x{F8FF}]"},
+        {"CJKCompatibilityIdeographs", "[\\x{F900}-\\x{FAFF}]"},
+        {"AlphabeticPresentationForms", "[\\x{FB00}-\\x{FB4F}]"},
+        {"ArabicPresentationForms-A", "[\\x{FB50}-\\x{FDFF}]"},
+        {"CombiningHalfMarks", "[\\x{FE20}-\\x{FE2F}]"},
+        {"CJKCompatibilityForms", "[\\x{FE30}-\\x{FE4F}]"},
+        {"SmallFormVariants", "[\\x{FE50}-\\x{FE6F}]"},
+        {"ArabicPresentationForms-B", "[\\x{FE70}-\\x{FEFE}]"},
+        {"HalfwidthandFullwidthForms", "[\\x{FF00}-\\x{FFEF}]"},
+        {NULL, NULL}
+    };
+
+    /* adjust the expression to a Perl equivalent
+     * http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#regexs */
+
+    /* we need to replace all "$" with "\$", count them now */
+    for (count = 0, ptr = strpbrk(pattern, "^$"); ptr; ++count, ptr = strpbrk(ptr + 1, "^$"));
+
+    perl_regex = malloc((strlen(pattern) + 4 + count) * sizeof(char));
+    LY_CHECK_ERR_RET(!perl_regex, LOGMEM(ctx->ctx), LY_EMEM);
+    perl_regex[0] = '\0';
+
+    ptr = perl_regex;
+
+    if (strncmp(pattern + strlen(pattern) - 2, ".*", 2)) {
+        /* we will add line-end anchoring */
+        ptr[0] = '(';
+        ++ptr;
+    }
+
+    for (orig_ptr = pattern; orig_ptr[0]; ++orig_ptr) {
+        if (orig_ptr[0] == '$') {
+            ptr += sprintf(ptr, "\\$");
+        } else if (orig_ptr[0] == '^') {
+            ptr += sprintf(ptr, "\\^");
+        } else {
+            ptr[0] = orig_ptr[0];
+            ++ptr;
+        }
+    }
+
+    if (strncmp(pattern + strlen(pattern) - 2, ".*", 2)) {
+        ptr += sprintf(ptr, ")$");
+    } else {
+        ptr[0] = '\0';
+        ++ptr;
+    }
+
+    /* substitute Unicode Character Blocks with exact Character Ranges */
+    while ((ptr = strstr(perl_regex, "\\p{Is"))) {
+        start = ptr - perl_regex;
+
+        ptr = strchr(ptr, '}');
+        if (!ptr) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_INREGEXP,
+                   pattern, perl_regex + start + 2, "unterminated character property");
+            free(perl_regex);
+            return LY_EVALID;
+        }
+        end = (ptr - perl_regex) + 1;
+
+        /* need more space */
+        if (end - start < URANGE_LEN) {
+            perl_regex = ly_realloc(perl_regex, strlen(perl_regex) + (URANGE_LEN - (end - start)) + 1);
+            LY_CHECK_ERR_RET(!perl_regex, LOGMEM(ctx->ctx); free(perl_regex), LY_EMEM);
+        }
+
+        /* find our range */
+        for (idx = 0; ublock2urange[idx][0]; ++idx) {
+            if (!strncmp(perl_regex + start + 5, ublock2urange[idx][0], strlen(ublock2urange[idx][0]))) {
+                break;
+            }
+        }
+        if (!ublock2urange[idx][0]) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_INREGEXP,
+                   pattern, perl_regex + start + 5, "unknown block name");
+            free(perl_regex);
+            return LY_EVALID;
+        }
+
+        /* make the space in the string and replace the block (but we cannot include brackets if it was already enclosed in them) */
+        for (idx2 = 0, count = 0; idx2 < start; ++idx2) {
+            if ((perl_regex[idx2] == '[') && (!idx2 || (perl_regex[idx2 - 1] != '\\'))) {
+                ++count;
+            }
+            if ((perl_regex[idx2] == ']') && (!idx2 || (perl_regex[idx2 - 1] != '\\'))) {
+                --count;
+            }
+        }
+        if (count) {
+            /* skip brackets */
+            memmove(perl_regex + start + (URANGE_LEN - 2), perl_regex + end, strlen(perl_regex + end) + 1);
+            memcpy(perl_regex + start, ublock2urange[idx][1] + 1, URANGE_LEN - 2);
+        } else {
+            memmove(perl_regex + start + URANGE_LEN, perl_regex + end, strlen(perl_regex + end) + 1);
+            memcpy(perl_regex + start, ublock2urange[idx][1], URANGE_LEN);
+        }
+    }
+
+    /* must return 0, already checked during parsing */
+    precomp = pcre_compile(perl_regex, PCRE_UTF8 | PCRE_ANCHORED | PCRE_DOLLAR_ENDONLY | PCRE_NO_AUTO_CAPTURE,
+                           &err_msg, &err_offset, NULL);
+    if (!precomp) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_INREGEXP, pattern, perl_regex + err_offset, err_msg);
+        free(perl_regex);
+        return LY_EVALID;
+    }
+    free(perl_regex);
+
+    if (pcre_precomp) {
+        *pcre_precomp = precomp;
+    } else {
+        free(precomp);
+    }
+
+    return LY_SUCCESS;
+
+#undef URANGE_LEN
+}
+
+/**
+ * @brief Compile parsed pattern restriction in conjunction with the patterns from base type.
+ * @param[in] ctx Compile context.
+ * @param[in] patterns_p Array of parsed patterns from the current type to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] base_patterns Compiled patterns from the type from which the current type is derived.
+ * Patterns from the base type are inherited to have all the patterns that have to match at one place.
+ * @param[out] patterns Pointer to the storage for the patterns of the current type.
+ * @return LY_ERR LY_SUCCESS, LY_EMEM, LY_EVALID.
+ */
+static LY_ERR
+lys_compile_type_patterns(struct lysc_ctx *ctx, struct lysp_restr *patterns_p, int options,
+                          struct lysc_pattern **base_patterns, struct lysc_pattern ***patterns)
+{
+    struct lysc_pattern **pattern;
+    unsigned int u, v;
+    const char *err_msg;
+    LY_ERR ret = LY_SUCCESS;
+
+    /* first, copy the patterns from the base type */
+    if (base_patterns) {
+        *patterns = lysc_patterns_dup(ctx->ctx, base_patterns);
+        LY_CHECK_ERR_RET(!(*patterns), LOGMEM(ctx->ctx), LY_EMEM);
+    }
+
+    LY_ARRAY_FOR(patterns_p, u) {
+        LY_ARRAY_NEW_RET(ctx->ctx, (*patterns), pattern, LY_EMEM);
+        *pattern = calloc(1, sizeof **pattern);
+        ++(*pattern)->refcount;
+
+        ret = lys_compile_type_pattern_check(ctx, &patterns_p[u].arg[1], &(*pattern)->expr);
+        LY_CHECK_RET(ret);
+        (*pattern)->expr_extra = pcre_study((*pattern)->expr, 0, &err_msg);
+        if (err_msg) {
+            LOGWRN(ctx->ctx, "Studying pattern \"%s\" failed (%s).", pattern, err_msg);
+        }
+
+        if (patterns_p[u].arg[0] == 0x15) {
+            (*pattern)->inverted = 1;
+        }
+        DUP_STRING(ctx->ctx, patterns_p[u].eapptag, (*pattern)->eapptag);
+        DUP_STRING(ctx->ctx, patterns_p[u].emsg, (*pattern)->emsg);
+        COMPILE_ARRAY_GOTO(ctx, patterns_p[u].exts, (*pattern)->exts,
+                           options, v, lys_compile_ext, ret, done);
+    }
+done:
+    return ret;
+}
+
+/**
+ * @brief map of the possible restrictions combination for the specific built-in type.
+ */
+static uint16_t type_substmt_map[LY_DATA_TYPE_COUNT] = {
+    0 /* LY_TYPE_UNKNOWN */,
+    LYS_SET_LENGTH /* LY_TYPE_BINARY */,
+    LYS_SET_RANGE /* LY_TYPE_UINT8 */,
+    LYS_SET_RANGE /* LY_TYPE_UINT16 */,
+    LYS_SET_RANGE /* LY_TYPE_UINT32 */,
+    LYS_SET_RANGE /* LY_TYPE_UINT64 */,
+    LYS_SET_LENGTH | LYS_SET_PATTERN /* LY_TYPE_STRING */,
+    LYS_SET_BIT /* LY_TYPE_BITS */,
+    0 /* LY_TYPE_BOOL */,
+    LYS_SET_FRDIGITS | LYS_SET_RANGE /* LY_TYPE_DEC64 */,
+    0 /* LY_TYPE_EMPTY */,
+    LYS_SET_ENUM /* LY_TYPE_ENUM */,
+    LYS_SET_BASE /* LY_TYPE_IDENT */,
+    LYS_SET_REQINST /* LY_TYPE_INST */,
+    LYS_SET_REQINST | LYS_SET_PATH /* LY_TYPE_LEAFREF */,
+    LYS_SET_TYPE /* LY_TYPE_UNION */,
+    LYS_SET_RANGE /* LY_TYPE_INT8 */,
+    LYS_SET_RANGE /* LY_TYPE_INT16 */,
+    LYS_SET_RANGE /* LY_TYPE_INT32 */,
+    LYS_SET_RANGE /* LY_TYPE_INT64 */
+};
+
+/**
+ * @brief stringification of the YANG built-in data types
+ */
+const char* ly_data_type2str[LY_DATA_TYPE_COUNT] = {"unknown", "binary", "8bit unsigned integer", "16bit unsigned integer",
+    "32bit unsigned integer", "64bit unsigned integer", "string", "bits", "boolean", "decimal64", "empty", "enumeration",
+    "identityref", "instance-identifier", "leafref", "union", "8bit integer", "16bit integer", "32bit integer", "64bit integer"
+};
+
+/**
+ * @brief Compile parsed type's enum structures (for enumeration and bits types).
+ * @param[in] ctx Compile context.
+ * @param[in] enums_p Array of the parsed enum structures to compile.
+ * @param[in] basetype Base YANG built-in type from which the current type is derived. Only LY_TYPE_ENUM and LY_TYPE_BITS are expected.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] base_enums Array of the compiled enums information from the (latest) base type to check if the current enums are compatible.
+ * @param[out] enums Newly created array of the compiled enums information for the current type.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_type_enums(struct lysc_ctx *ctx, struct lysp_type_enum *enums_p, LY_DATA_TYPE basetype, int options,
+                       struct lysc_type_enum_item *base_enums, struct lysc_type_enum_item **enums)
+{
+    LY_ERR ret = LY_SUCCESS;
+    unsigned int u, v, match;
+    int32_t value = 0;
+    uint32_t position = 0;
+    struct lysc_type_enum_item *e, storage;
+
+    if (base_enums && ctx->mod_def->parsed->version < 2) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "%s type can be subtyped only in YANG 1.1 modules.",
+               basetype == LY_TYPE_ENUM ? "Enumeration" : "Bits");
+        return LY_EVALID;
+    }
+
+    LY_ARRAY_FOR(enums_p, u) {
+        LY_ARRAY_NEW_RET(ctx->ctx, *enums, e, LY_EMEM);
+        DUP_STRING(ctx->ctx, enums_p[u].name, e->name);
+        if (base_enums) {
+            /* check the enum/bit presence in the base type - the set of enums/bits in the derived type must be a subset */
+            LY_ARRAY_FOR(base_enums, v) {
+                if (!strcmp(e->name, base_enums[v].name)) {
+                    break;
+                }
+            }
+            if (v == LY_ARRAY_SIZE(base_enums)) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid %s - derived type adds new item \"%s\".",
+                       basetype == LY_TYPE_ENUM ? "enumeration" : "bits", e->name);
+                return LY_EVALID;
+            }
+            match = v;
+        }
+
+        if (basetype == LY_TYPE_ENUM) {
+            if (enums_p[u].flags & LYS_SET_VALUE) {
+                e->value = (int32_t)enums_p[u].value;
+                if (!u || e->value >= value) {
+                    value = e->value + 1;
+                }
+                /* check collision with other values */
+                for (v = 0; v < LY_ARRAY_SIZE(*enums) - 1; ++v) {
+                    if (e->value == (*enums)[v].value) {
+                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                               "Invalid enumeration - value %d collide in items \"%s\" and \"%s\".",
+                               e->value, e->name, (*enums)[v].name);
+                        return LY_EVALID;
+                    }
+                }
+            } else if (base_enums) {
+                /* inherit the assigned value */
+                e->value = base_enums[match].value;
+                if (!u || e->value >= value) {
+                    value = e->value + 1;
+                }
+            } else {
+                /* assign value automatically */
+                if (u && value == INT32_MIN) {
+                    /* counter overflow */
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                           "Invalid enumeration - it is not possible to auto-assign enum value for "
+                           "\"%s\" since the highest value is already 2147483647.", e->name);
+                    return LY_EVALID;
+                }
+                e->value = value++;
+            }
+        } else { /* LY_TYPE_BITS */
+            if (enums_p[u].flags & LYS_SET_VALUE) {
+                e->value = (int32_t)enums_p[u].value;
+                if (!u || (uint32_t)e->value >= position) {
+                    position = (uint32_t)e->value + 1;
+                }
+                /* check collision with other values */
+                for (v = 0; v < LY_ARRAY_SIZE(*enums) - 1; ++v) {
+                    if (e->value == (*enums)[v].value) {
+                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                               "Invalid bits - position %u collide in items \"%s\" and \"%s\".",
+                               (uint32_t)e->value, e->name, (*enums)[v].name);
+                        return LY_EVALID;
+                    }
+                }
+            } else if (base_enums) {
+                /* inherit the assigned value */
+                e->value = base_enums[match].value;
+                if (!u || (uint32_t)e->value >= position) {
+                    position = (uint32_t)e->value + 1;
+                }
+            } else {
+                /* assign value automatically */
+                if (u && position == 0) {
+                    /* counter overflow */
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                           "Invalid bits - it is not possible to auto-assign bit position for "
+                           "\"%s\" since the highest value is already 4294967295.", e->name);
+                    return LY_EVALID;
+                }
+                e->value = position++;
+            }
+        }
+
+        if (base_enums) {
+            /* the assigned values must not change from the derived type */
+            if (e->value != base_enums[match].value) {
+                if (basetype == LY_TYPE_ENUM) {
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid enumeration - value of the item \"%s\" has changed from %d to %d in the derived type.",
+                       e->name, base_enums[match].value, e->value);
+                } else {
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid bits - position of the item \"%s\" has changed from %u to %u in the derived type.",
+                       e->name, (uint32_t)base_enums[match].value, (uint32_t)e->value);
+                }
+                return LY_EVALID;
+            }
+        }
+
+        COMPILE_ARRAY_GOTO(ctx, enums_p[u].iffeatures, e->iffeatures, options, v, lys_compile_iffeature, ret, done);
+        COMPILE_ARRAY_GOTO(ctx, enums_p[u].exts, e->exts, options, v, lys_compile_ext, ret, done);
+
+        if (basetype == LY_TYPE_BITS) {
+            /* keep bits ordered by position */
+            for (v = u; v && (*enums)[v - 1].value > e->value; --v);
+            if (v != u) {
+                memcpy(&storage, e, sizeof *e);
+                memmove(&(*enums)[v + 1], &(*enums)[v], (u - v) * sizeof **enums);
+                memcpy(&(*enums)[v], &storage, sizeof storage);
+            }
+        }
+    }
+
+done:
+    return ret;
+}
+
+#define MOVE_PATH_PARENT(NODE, LIMIT_COND, TERM, ERR_MSG, ...) \
+    for ((NODE) = (NODE)->parent; \
+         (NODE) && !((NODE)->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_ACTION | LYS_NOTIF | LYS_ACTION)); \
+         (NODE) = (NODE)->parent); \
+    if (!(NODE) && (LIMIT_COND)) { /* we are going higher than top-level */ \
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE, ERR_MSG, ##__VA_ARGS__); \
+        TERM; \
+    }
+
+/**
+ * @brief Validate the predicate(s) from the leafref path.
+ * @param[in] ctx Compile context
+ * @param[in, out] predicate Pointer to the predicate in the leafref path. The pointer is moved after the validated predicate(s).
+ * Since there can be multiple adjacent predicates for lists with multiple keys, all such predicates are validated.
+ * @param[in] start_node Path context node (where the path is instantiated).
+ * @param[in] context_node Predicate context node (where the predicate is placed).
+ * @param[in] path_context Schema where the path was defined to correct resolve of the prefixes.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_leafref_predicate_validate(struct lysc_ctx *ctx, const char **predicate, const struct lysc_node *start_node,
+                                       const struct lysc_node_list *context_node, const struct lys_module *path_context)
+{
+    LY_ERR ret = LY_EVALID;
+    const struct lys_module *mod;
+    const struct lysc_node *src_node, *dst_node;
+    const char *path_key_expr, *pke_start, *src, *src_prefix, *dst, *dst_prefix;
+    size_t src_len, src_prefix_len, dst_len, dst_prefix_len;
+    unsigned int dest_parent_times, c, u;
+    const char *start, *end, *pke_end;
+    struct ly_set keys = {0};
+    int i;
+
+    assert(path_context);
+
+    while (**predicate == '[') {
+        start = (*predicate)++;
+
+        while (isspace(**predicate)) {
+            ++(*predicate);
+        }
+        LY_CHECK_GOTO(lys_parse_nodeid(predicate, &src_prefix, &src_prefix_len, &src, &src_len), cleanup);
+        while (isspace(**predicate)) {
+            ++(*predicate);
+        }
+        if (**predicate != '=') {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%.*s\" - missing \"=\" after node-identifier.",
+                   *predicate - start + 1, start);
+            goto cleanup;
+        }
+        ++(*predicate);
+        while (isspace(**predicate)) {
+            ++(*predicate);
+        }
+
+        if ((end = pke_end = strchr(*predicate, ']')) == NULL) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%s\" - missing predicate termination.", start);
+            goto cleanup;
+        }
+        --pke_end;
+        while (isspace(*pke_end)) {
+            --pke_end;
+        }
+        ++pke_end;
+        /* localize path-key-expr */
+        pke_start = path_key_expr = *predicate;
+        /* move after the current predicate */
+        *predicate = end + 1;
+
+        /* source (must be leaf or leaf-list) */
+        if (src_prefix) {
+            mod = lys_module_find_prefix(path_context, src_prefix, src_prefix_len);
+            if (!mod) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                       "Invalid leafref path predicate \"%.*s\" - prefix \"%.*s\" not defined in module \"%s\".",
+                       *predicate - start, start, src_prefix_len, src_prefix, path_context->compiled->name);
+                goto cleanup;
+            }
+        } else {
+            mod = start_node->module;
+        }
+        src_node = NULL;
+        if (context_node->keys) {
+            for (u = 0; u < LY_ARRAY_SIZE(context_node->keys); ++u) {
+                if (!strncmp(src, context_node->keys[u]->name, src_len) && context_node->keys[u]->name[src_len] == '\0') {
+                    src_node = (const struct lysc_node*)context_node->keys[u];
+                    break;
+                }
+            }
+        }
+        if (!src_node) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%.*s\" - predicate's key node \"%.*s\" not found.",
+                   *predicate - start, start, src_len, src, mod->compiled->name);
+            goto cleanup;
+        }
+
+        /* check that there is only one predicate for the */
+        c = keys.count;
+        i = ly_set_add(&keys, (void*)src_node, 0);
+        LY_CHECK_GOTO(i == -1, cleanup);
+        if (keys.count == c) { /* node was already present in the set */
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%.*s\" - multiple equality tests for the key \"%s\".",
+                   *predicate - start, start, src_node->name);
+            goto cleanup;
+        }
+
+        /* destination */
+        dest_parent_times = 0;
+        dst_node = start_node;
+
+        /* current-function-invocation *WSP "/" *WSP rel-path-keyexpr */
+        if (strncmp(path_key_expr, "current()", 9)) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%.*s\" - missing current-function-invocation.",
+                   *predicate - start, start);
+            goto cleanup;
+        }
+        path_key_expr += 9;
+        while (isspace(*path_key_expr)) {
+            ++path_key_expr;
+        }
+
+        if (*path_key_expr != '/') {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%.*s\" - missing \"/\" after current-function-invocation.",
+                   *predicate - start, start);
+            goto cleanup;
+        }
+        ++path_key_expr;
+        while (isspace(*path_key_expr)) {
+            ++path_key_expr;
+        }
+
+        /* rel-path-keyexpr:
+         * 1*(".." *WSP "/" *WSP) *(node-identifier *WSP "/" *WSP) node-identifier */
+        while (!strncmp(path_key_expr, "..", 2)) {
+            ++dest_parent_times;
+            path_key_expr += 2;
+            while (isspace(*path_key_expr)) {
+                ++path_key_expr;
+            }
+            if (*path_key_expr != '/') {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                       "Invalid leafref path predicate \"%.*s\" - missing \"/\" in \"../\" rel-path-keyexpr pattern.",
+                       *predicate - start, start);
+                goto cleanup;
+            }
+            ++path_key_expr;
+            while (isspace(*path_key_expr)) {
+                ++path_key_expr;
+            }
+
+            /* path is supposed to be evaluated in data tree, so we have to skip
+             * all schema nodes that cannot be instantiated in data tree */
+            MOVE_PATH_PARENT(dst_node, !strncmp(path_key_expr, "..", 2), goto cleanup,
+                             "Invalid leafref path predicate \"%.*s\" - too many \"..\" in rel-path-keyexpr.",
+                             *predicate - start, start);
+        }
+        if (!dest_parent_times) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%.*s\" - at least one \"..\" is expected in rel-path-keyexpr.",
+                   *predicate - start, start);
+            goto cleanup;
+        }
+        if (path_key_expr == pke_end) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%.*s\" - at least one node-identifier is expected in rel-path-keyexpr.",
+                   *predicate - start, start);
+            goto cleanup;
+        }
+
+        while(path_key_expr != pke_end) {
+            if (lys_parse_nodeid(&path_key_expr, &dst_prefix, &dst_prefix_len, &dst, &dst_len)) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid node identifier in leafref path predicate - character %d (of %.*s).",
+                       path_key_expr - start + 1, *predicate - start, start);
+                goto cleanup;
+            }
+
+            if (dst_prefix) {
+                mod = lys_module_find_prefix(path_context, dst_prefix, dst_prefix_len);
+            } else {
+                mod = start_node->module;
+            }
+            if (!mod) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                       "Invalid leafref path predicate \"%.*s\" - unable to find module of the node \"%.*s\" in rel-path_keyexpr.",
+                       *predicate - start, start, dst_len, dst);
+                goto cleanup;
+            }
+
+            dst_node = lys_child(dst_node, mod, dst, dst_len, 0, LYS_GETNEXT_NOSTATECHECK);
+            if (!dst_node) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                       "Invalid leafref path predicate \"%.*s\" - unable to find node \"%.*s\" in the rel-path_keyexpr.",
+                       *predicate - start, start, path_key_expr - pke_start, pke_start);
+                goto cleanup;
+            }
+        }
+        if (!(dst_node->nodetype & (dst_node->module->compiled->version < LYS_VERSION_1_1 ? LYS_LEAF : LYS_LEAF | LYS_LEAFLIST))) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%.*s\" - rel-path_keyexpr \"%.*s\" refers %s instead of leaf.",
+                   *predicate - start, start, path_key_expr - pke_start, pke_start, lys_nodetype2str(dst_node->nodetype));
+            goto cleanup;
+        }
+    }
+
+    ret = LY_SUCCESS;
+cleanup:
+    ly_set_erase(&keys, NULL);
+    return ret;
+}
+
+/**
+ * @brief Parse path-arg (leafref). Get tokens of the path by repetitive calls of the function.
+ *
+ * path-arg            = absolute-path / relative-path
+ * absolute-path       = 1*("/" (node-identifier *path-predicate))
+ * relative-path       = 1*(".." "/") descendant-path
+ *
+ * @param[in,out] path Path to parse.
+ * @param[out] prefix Prefix of the token, NULL if there is not any.
+ * @param[out] pref_len Length of the prefix, 0 if there is not any.
+ * @param[out] name Name of the token.
+ * @param[out] nam_len Length of the name.
+ * @param[out] parent_times Number of leading ".." in the path. Must be 0 on the first call,
+ *                          must not be changed between consecutive calls. -1 if the
+ *                          path is absolute.
+ * @param[out] has_predicate Flag to mark whether there is a predicate specified.
+ * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid character in the path.
+ */
+static LY_ERR
+lys_path_token(const char **path, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len,
+               int *parent_times, int *has_predicate)
+{
+    int par_times = 0;
+
+    assert(path && *path);
+    assert(parent_times);
+    assert(prefix);
+    assert(prefix_len);
+    assert(name);
+    assert(name_len);
+    assert(has_predicate);
+
+    *prefix = NULL;
+    *prefix_len = 0;
+    *name = NULL;
+    *name_len = 0;
+    *has_predicate = 0;
+
+    if (!*parent_times) {
+        if (!strncmp(*path, "..", 2)) {
+            *path += 2;
+            ++par_times;
+            while (!strncmp(*path, "/..", 3)) {
+                *path += 3;
+                ++par_times;
+            }
+        }
+        if (par_times) {
+            *parent_times = par_times;
+        } else {
+            *parent_times = -1;
+        }
+    }
+
+    if (**path != '/') {
+        return LY_EINVAL;
+    }
+    /* skip '/' */
+    ++(*path);
+
+    /* node-identifier ([prefix:]name) */
+    LY_CHECK_RET(lys_parse_nodeid(path, prefix, prefix_len, name, name_len));
+
+    if ((**path == '/' && (*path)[1]) || !**path) {
+        /* path continues by another token or this is the last token */
+        return LY_SUCCESS;
+    } else if ((*path)[0] != '[') {
+        /* unexpected character */
+        return LY_EINVAL;
+    } else {
+        /* predicate starting with [ */
+        *has_predicate = 1;
+        return LY_SUCCESS;
+    }
+}
+
+/**
+ * @brief Check the features used in if-feature statements applicable to the leafref and its target.
+ *
+ * The set of features used for target must be a subset of features used for the leafref.
+ * This is not a perfect, we should compare the truth tables but it could require too much resources
+ * and RFC 7950 does not require it explicitely, so we simplify that.
+ *
+ * @param[in] refnode The leafref node.
+ * @param[in] target Tha target node of the leafref.
+ * @return LY_SUCCESS or LY_EVALID;
+ */
+static LY_ERR
+lys_compile_leafref_features_validate(const struct lysc_node *refnode, const struct lysc_node *target)
+{
+    LY_ERR ret = LY_EVALID;
+    const struct lysc_node *iter;
+    unsigned int u, v, count;
+    struct ly_set features = {0};
+
+    for (iter = refnode; iter; iter = iter->parent) {
+        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);
+                }
+            }
+        }
+    }
+
+    /* we should have, in features set, a superset of features applicable to the target node.
+     * So when adding features applicable to the target into the features set, we should not be
+     * able to actually add any new feature, otherwise it is not a subset of features applicable
+     * to the leafref itself. */
+    count = features.count;
+    for (iter = target; iter; iter = iter->parent) {
+        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;
+                    }
+                }
+            }
+        }
+    }
+    ret = LY_SUCCESS;
+
+cleanup:
+    ly_set_erase(&features, NULL);
+    return ret;
+}
+
+/**
+ * @brief Validate the leafref path.
+ * @param[in] ctx Compile context
+ * @param[in] startnode Path context node (where the leafref path begins/is placed).
+ * @param[in] leafref Leafref to validate.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_leafref_validate(struct lysc_ctx *ctx, struct lysc_node *startnode, struct lysc_type_leafref *leafref)
+{
+    const struct lysc_node *node = NULL, *parent = NULL;
+    const struct lys_module *mod;
+    struct lysc_type *type;
+    const char *id, *prefix, *name;
+    size_t prefix_len, name_len;
+    int parent_times = 0, has_predicate;
+    unsigned int iter, u;
+    LY_ERR ret = LY_SUCCESS;
+
+    assert(ctx);
+    assert(startnode);
+    assert(leafref);
+
+    /* TODO leafref targets may be not implemented, in such a case we actually could make (we did it in libyang1) such a models implemented */
+
+    iter = 0;
+    id = leafref->path;
+    while(*id && (ret = lys_path_token(&id, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate)) == LY_SUCCESS) {
+        if (!iter) { /* first iteration */
+            /* precess ".." in relative paths */
+            if (parent_times > 0) {
+                /* move from the context node */
+                for (u = 0, parent = startnode; u < (unsigned int)parent_times; u++) {
+                    /* path is supposed to be evaluated in data tree, so we have to skip
+                    * all schema nodes that cannot be instantiated in data tree */
+                    MOVE_PATH_PARENT(parent, u < (unsigned int)parent_times - 1, return LY_EVALID,
+                                     "Invalid leafref path \"%s\" - too many \"..\" in the path.", leafref->path);
+                }
+            }
+        }
+
+        if (prefix) {
+            mod = lys_module_find_prefix(leafref->path_context, prefix, prefix_len);
+        } else {
+            mod = startnode->module;
+        }
+        if (!mod) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path - unable to find module connected with the prefix of the node \"%.*s\".",
+                   id - leafref->path, leafref->path);
+            return LY_EVALID;
+        }
+
+        node = lys_child(parent, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
+        if (!node) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path - unable to find \"%.*s\".", id - leafref->path, leafref->path);
+            return LY_EVALID;
+        }
+        parent = node;
+
+        if (has_predicate) {
+            /* we have predicate, so the current result must be list */
+            if (node->nodetype != LYS_LIST) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                       "Invalid leafref path - node \"%.*s\" is expected to be a list, but it is %s.",
+                       id - leafref->path, leafref->path, lys_nodetype2str(node->nodetype));
+                return LY_EVALID;
+            }
+
+            LY_CHECK_RET(lys_compile_leafref_predicate_validate(ctx, &id, startnode, (struct lysc_node_list*)node, leafref->path_context),
+                         LY_EVALID);
+        }
+
+        ++iter;
+    }
+    if (ret) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+               "Invalid leafref path at character %d (%s).", id - leafref->path + 1, leafref->path);
+        return LY_EVALID;
+    }
+
+    if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+               "Invalid leafref path \"%s\" - target node is %s instead of leaf or leaf-list.",
+               leafref->path, lys_nodetype2str(node->nodetype));
+        return LY_EVALID;
+    }
+
+    /* check status */
+    if (lysc_check_status(ctx, startnode->flags, startnode->module, startnode->name, node->flags, node->module, node->name)) {
+        return LY_EVALID;
+    }
+
+    /* check config */
+    if (leafref->require_instance && (startnode->flags & LYS_CONFIG_W)) {
+        if (node->flags & LYS_CONFIG_R) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path \"%s\" - target is supposed to represent configuration data (as the leafref does), but it does not.",
+                   leafref->path);
+            return LY_EVALID;
+        }
+    }
+
+    /* store the target's type and check for circular chain of leafrefs */
+    leafref->realtype = ((struct lysc_node_leaf*)node)->type;
+    for (type = leafref->realtype; type && type->basetype == LY_TYPE_LEAFREF; type = ((struct lysc_type_leafref*)type)->realtype) {
+        if (type == (struct lysc_type*)leafref) {
+            /* circular chain detected */
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path \"%s\" - circular chain of leafrefs detected.", leafref->path);
+            return LY_EVALID;
+        }
+    }
+
+    /* check if leafref and its target are under common if-features */
+    if (lys_compile_leafref_features_validate(startnode, node)) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+               "Invalid leafref path \"%s\" - set of features applicable to the leafref target is not a subset of features applicable to the leafref itself.",
+               leafref->path);
+        return LY_EVALID;
+    }
+
+    return LY_SUCCESS;
+}
+
+static LY_ERR lys_compile_type(struct lysc_ctx *ctx, struct lysp_node *context_node_p, uint16_t context_flags, struct lysp_module *context_mod, const char *context_name,
+                               struct lysp_type *type_p, int options, struct lysc_type **type, const char **units, const char **dflt);
+/**
+ * @brief The core of the lys_compile_type() - compile information about the given type (from typedef or leaf/leaf-list).
+ * @param[in] ctx Compile context.
+ * @param[in] context_node_p Schema node where the type/typedef is placed to correctly find the base types.
+ * @param[in] context_flags Flags of the context node or the referencing typedef to correctly check status of referencing and referenced objects.
+ * @param[in] context_mod Module of the context node or the referencing typedef to correctly check status of referencing and referenced objects.
+ * @param[in] context_name Name of the context node or referencing typedef for logging.
+ * @param[in] type_p Parsed type to compile.
+ * @param[in] module Context module for the leafref path (to correctly resolve prefixes in path)
+ * @param[in] basetype Base YANG built-in type of the type to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] tpdfname Name of the type's typedef, serves as a flag - if it is leaf/leaf-list's type, it is NULL.
+ * @param[in] base The latest base (compiled) type from which the current type is being derived.
+ * @param[out] type Newly created type structure with the filled information about the type.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_type_(struct lysc_ctx *ctx, struct lysp_node *context_node_p, uint16_t context_flags, struct lysp_module *context_mod, const char *context_name,
+                  struct lysp_type *type_p, struct lys_module *module, LY_DATA_TYPE basetype, int options, const char *tpdfname,
+                  struct lysc_type *base,  struct lysc_type **type)
+{
+    LY_ERR ret = LY_SUCCESS;
+    unsigned int u, v, additional;
+    struct lysc_type_bin *bin;
+    struct lysc_type_num *num;
+    struct lysc_type_str *str;
+    struct lysc_type_bits *bits;
+    struct lysc_type_enum *enumeration;
+    struct lysc_type_dec *dec;
+    struct lysc_type_identityref *idref;
+    struct lysc_type_union *un, *un_aux;
+    void *p;
+
+    switch (basetype) {
+    case LY_TYPE_BINARY:
+        bin = (struct lysc_type_bin*)(*type);
+
+        /* RFC 7950 9.8.1, 9.4.4 - length, number of octets it contains */
+        if (type_p->length) {
+            ret = lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
+                                         base ? ((struct lysc_type_bin*)base)->length : NULL, &bin->length);
+            LY_CHECK_RET(ret);
+            if (!tpdfname) {
+                COMPILE_ARRAY_GOTO(ctx, type_p->length->exts, bin->length->exts,
+                                   options, u, lys_compile_ext, ret, done);
+            }
+        }
+
+        if (tpdfname) {
+            type_p->compiled = *type;
+            *type = calloc(1, sizeof(struct lysc_type_bin));
+        }
+        break;
+    case LY_TYPE_BITS:
+        /* RFC 7950 9.7 - bits */
+        bits = (struct lysc_type_bits*)(*type);
+        if (type_p->bits) {
+            ret = lys_compile_type_enums(ctx, type_p->bits, basetype, options,
+                                         base ? (struct lysc_type_enum_item*)((struct lysc_type_bits*)base)->bits : NULL,
+                                         (struct lysc_type_enum_item**)&bits->bits);
+            LY_CHECK_RET(ret);
+        }
+
+        if (!base && !type_p->flags) {
+            /* type derived from bits built-in type must contain at least one bit */
+            if (tpdfname) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "bit", "bits type ", tpdfname);
+            } else {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "bit", "bits type", "");
+                free(*type);
+                *type = NULL;
+            }
+            return LY_EVALID;
+        }
+
+        if (tpdfname) {
+            type_p->compiled = *type;
+            *type = calloc(1, sizeof(struct lysc_type_bits));
+        }
+        break;
+    case LY_TYPE_DEC64:
+        dec = (struct lysc_type_dec*)(*type);
+
+        /* RFC 7950 9.3.4 - fraction-digits */
+        if (!base) {
+            if (!type_p->fraction_digits) {
+                if (tpdfname) {
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "fraction-digits", "decimal64 type ", tpdfname);
+                } else {
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "fraction-digits", "decimal64 type", "");
+                    free(*type);
+                    *type = NULL;
+                }
+                return LY_EVALID;
+            }
+        } else if (type_p->fraction_digits) {
+            /* fraction digits is prohibited in types not directly derived from built-in decimal64 */
+            if (tpdfname) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid fraction-digits substatement for type \"%s\" not directly derived from decimal64 built-in type.",
+                       tpdfname);
+            } else {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid fraction-digits substatement for type not directly derived from decimal64 built-in type.");
+                free(*type);
+                *type = NULL;
+            }
+            return LY_EVALID;
+        }
+        dec->fraction_digits = type_p->fraction_digits;
+
+        /* RFC 7950 9.2.4 - range */
+        if (type_p->range) {
+            ret = lys_compile_type_range(ctx, type_p->range, basetype, 0, dec->fraction_digits,
+                                         base ? ((struct lysc_type_dec*)base)->range : NULL, &dec->range);
+            LY_CHECK_RET(ret);
+            if (!tpdfname) {
+                COMPILE_ARRAY_GOTO(ctx, type_p->range->exts, dec->range->exts,
+                                   options, u, lys_compile_ext, ret, done);
+            }
+        }
+
+        if (tpdfname) {
+            type_p->compiled = *type;
+            *type = calloc(1, sizeof(struct lysc_type_dec));
+        }
+        break;
+    case LY_TYPE_STRING:
+        str = (struct lysc_type_str*)(*type);
+
+        /* RFC 7950 9.4.4 - length */
+        if (type_p->length) {
+            ret = lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
+                                         base ? ((struct lysc_type_str*)base)->length : NULL, &str->length);
+            LY_CHECK_RET(ret);
+            if (!tpdfname) {
+                COMPILE_ARRAY_GOTO(ctx, type_p->length->exts, str->length->exts,
+                                   options, u, lys_compile_ext, ret, done);
+            }
+        } else if (base && ((struct lysc_type_str*)base)->length) {
+            str->length = lysc_range_dup(ctx->ctx, ((struct lysc_type_str*)base)->length);
+        }
+
+        /* RFC 7950 9.4.5 - pattern */
+        if (type_p->patterns) {
+            ret = lys_compile_type_patterns(ctx, type_p->patterns, options,
+                                            base ? ((struct lysc_type_str*)base)->patterns : NULL, &str->patterns);
+            LY_CHECK_RET(ret);
+        } else if (base && ((struct lysc_type_str*)base)->patterns) {
+            str->patterns = lysc_patterns_dup(ctx->ctx, ((struct lysc_type_str*)base)->patterns);
+        }
+
+        if (tpdfname) {
+            type_p->compiled = *type;
+            *type = calloc(1, sizeof(struct lysc_type_str));
+        }
+        break;
+    case LY_TYPE_ENUM:
+        enumeration = (struct lysc_type_enum*)(*type);
+
+        /* RFC 7950 9.6 - enum */
+        if (type_p->enums) {
+            ret = lys_compile_type_enums(ctx, type_p->enums, basetype, options,
+                                         base ? ((struct lysc_type_enum*)base)->enums : NULL, &enumeration->enums);
+            LY_CHECK_RET(ret);
+        }
+
+        if (!base && !type_p->flags) {
+            /* type derived from enumerations built-in type must contain at least one enum */
+            if (tpdfname) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "enum", "enumeration type ", tpdfname);
+            } else {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "enum", "enumeration type", "");
+                free(*type);
+                *type = NULL;
+            }
+            return LY_EVALID;
+        }
+
+        if (tpdfname) {
+            type_p->compiled = *type;
+            *type = calloc(1, sizeof(struct lysc_type_enum));
+        }
+        break;
+    case LY_TYPE_INT8:
+    case LY_TYPE_UINT8:
+    case LY_TYPE_INT16:
+    case LY_TYPE_UINT16:
+    case LY_TYPE_INT32:
+    case LY_TYPE_UINT32:
+    case LY_TYPE_INT64:
+    case LY_TYPE_UINT64:
+        num = (struct lysc_type_num*)(*type);
+
+        /* RFC 6020 9.2.4 - range */
+        if (type_p->range) {
+            ret = lys_compile_type_range(ctx, type_p->range, basetype, 0, 0,
+                                         base ? ((struct lysc_type_num*)base)->range : NULL, &num->range);
+            LY_CHECK_RET(ret);
+            if (!tpdfname) {
+                COMPILE_ARRAY_GOTO(ctx, type_p->range->exts, num->range->exts,
+                                   options, u, lys_compile_ext, ret, done);
+            }
+        }
+
+        if (tpdfname) {
+            type_p->compiled = *type;
+            *type = calloc(1, sizeof(struct lysc_type_num));
+        }
+        break;
+    case LY_TYPE_IDENT:
+        idref = (struct lysc_type_identityref*)(*type);
+
+        /* RFC 7950 9.10.2 - base */
+        if (type_p->bases) {
+            if (base) {
+                /* only the directly derived identityrefs can contain base specification */
+                if (tpdfname) {
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                           "Invalid base substatement for the type \"%s\" not directly derived from identityref built-in type.",
+                           tpdfname);
+                } else {
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                           "Invalid base substatement for the type not directly derived from identityref built-in type.");
+                    free(*type);
+                    *type = NULL;
+                }
+                return LY_EVALID;
+            }
+            ret = lys_compile_identity_bases(ctx, type_p->bases, NULL, &idref->bases);
+            LY_CHECK_RET(ret);
+        }
+
+        if (!base && !type_p->flags) {
+            /* type derived from identityref built-in type must contain at least one base */
+            if (tpdfname) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "base", "identityref type ", tpdfname);
+            } else {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "base", "identityref type", "");
+                free(*type);
+                *type = NULL;
+            }
+            return LY_EVALID;
+        }
+
+        if (tpdfname) {
+            type_p->compiled = *type;
+            *type = calloc(1, sizeof(struct lysc_type_identityref));
+        }
+        break;
+    case LY_TYPE_LEAFREF:
+        /* RFC 7950 9.9.3 - require-instance */
+        if (type_p->flags & LYS_SET_REQINST) {
+            if (context_mod->version < LYS_VERSION_1_1) {
+                if (tpdfname) {
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                           "Leafref type \"%s\" can be restricted by require-instance statement only in YANG 1.1 modules.", tpdfname);
+                } else {
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                           "Leafref type can be restricted by require-instance statement only in YANG 1.1 modules.");
+                    free(*type);
+                    *type = NULL;
+                }
+                return LY_EVALID;
+            }
+            ((struct lysc_type_leafref*)(*type))->require_instance = type_p->require_instance;
+        } else if (base) {
+            /* inherit */
+            ((struct lysc_type_leafref*)(*type))->require_instance = ((struct lysc_type_leafref*)base)->require_instance;
+        } else {
+            /* default is true */
+            ((struct lysc_type_leafref*)(*type))->require_instance = 1;
+        }
+        if (type_p->path) {
+            DUP_STRING(ctx->ctx, (void*)type_p->path, ((struct lysc_type_leafref*)(*type))->path);
+            ((struct lysc_type_leafref*)(*type))->path_context = module;
+        } else if (base) {
+            DUP_STRING(ctx->ctx, ((struct lysc_type_leafref*)base)->path, ((struct lysc_type_leafref*)(*type))->path);
+            ((struct lysc_type_leafref*)(*type))->path_context = ((struct lysc_type_leafref*)base)->path_context;
+        } else if (tpdfname) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "path", "leafref type ", tpdfname);
+            return LY_EVALID;
+        } else {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "path", "leafref type", "");
+            free(*type);
+            *type = NULL;
+            return LY_EVALID;
+        }
+        if (tpdfname) {
+            type_p->compiled = *type;
+            *type = calloc(1, sizeof(struct lysc_type_leafref));
+        }
+        break;
+    case LY_TYPE_INST:
+        /* RFC 7950 9.9.3 - require-instance */
+        if (type_p->flags & LYS_SET_REQINST) {
+            ((struct lysc_type_instanceid*)(*type))->require_instance = type_p->require_instance;
+        } else {
+            /* default is true */
+            ((struct lysc_type_instanceid*)(*type))->require_instance = 1;
+        }
+
+        if (tpdfname) {
+            type_p->compiled = *type;
+            *type = calloc(1, sizeof(struct lysc_type_instanceid));
+        }
+        break;
+    case LY_TYPE_UNION:
+        un = (struct lysc_type_union*)(*type);
+
+        /* RFC 7950 7.4 - type */
+        if (type_p->types) {
+            if (base) {
+                /* only the directly derived union can contain types specification */
+                if (tpdfname) {
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                           "Invalid type substatement for the type \"%s\" not directly derived from union built-in type.",
+                           tpdfname);
+                } else {
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                           "Invalid type substatement for the type not directly derived from union built-in type.");
+                    free(*type);
+                    *type = NULL;
+                }
+                return LY_EVALID;
+            }
+            /* compile the type */
+            additional = 0;
+            LY_ARRAY_CREATE_RET(ctx->ctx, un->types, LY_ARRAY_SIZE(type_p->types), LY_EVALID);
+            for (u = 0; u < LY_ARRAY_SIZE(type_p->types); ++u) {
+                ret = lys_compile_type(ctx, context_node_p, context_flags, context_mod, context_name, &type_p->types[u], options, &un->types[u + additional], NULL, NULL);
+                if (un->types[u + additional]->basetype == LY_TYPE_UNION) {
+                    /* add space for additional types from the union subtype */
+                    un_aux = (struct lysc_type_union *)un->types[u + additional];
+                    p = ly_realloc(((uint32_t*)(un->types) - 1), sizeof(uint32_t) + ((LY_ARRAY_SIZE(type_p->types) + additional + LY_ARRAY_SIZE(un_aux->types) - 1) * sizeof *(un->types)));
+                    LY_CHECK_ERR_RET(!p, LOGMEM(ctx->ctx);lysc_type_free(ctx->ctx, (struct lysc_type*)un_aux), LY_EMEM);
+                    un->types = (void*)((uint32_t*)(p) + 1);
+
+                    /* copy subtypes of the subtype union */
+                    for (v = 0; v < LY_ARRAY_SIZE(un_aux->types); ++v) {
+                        if (un_aux->types[v]->basetype == LY_TYPE_LEAFREF) {
+                            /* duplicate the whole structure because of the instance-specific path resolving for realtype */
+                            un->types[u + additional] = calloc(1, sizeof(struct lysc_type_leafref));
+                            LY_CHECK_ERR_RET(!un->types[u + additional], LOGMEM(ctx->ctx);lysc_type_free(ctx->ctx, (struct lysc_type*)un_aux), LY_EMEM);
+                            ((struct lysc_type_leafref*)un->types[u + additional])->basetype = LY_TYPE_LEAFREF;
+                            DUP_STRING(ctx->ctx, ((struct lysc_type_leafref*)un_aux->types[v])->path, ((struct lysc_type_leafref*)un->types[u + additional])->path);
+                            ((struct lysc_type_leafref*)un->types[u + additional])->refcount = 1;
+                            ((struct lysc_type_leafref*)un->types[u + additional])->require_instance = ((struct lysc_type_leafref*)un_aux->types[v])->require_instance;
+                            ((struct lysc_type_leafref*)un->types[u + additional])->path_context = ((struct lysc_type_leafref*)un_aux->types[v])->path_context;
+                            /* TODO extensions */
+
+                        } else {
+                            un->types[u + additional] = un_aux->types[v];
+                            ++un_aux->types[v]->refcount;
+                        }
+                        ++additional;
+                        LY_ARRAY_INCREMENT(un->types);
+                    }
+                    /* compensate u increment in main loop */
+                    --additional;
+
+                    /* free the replaced union subtype */
+                    lysc_type_free(ctx->ctx, (struct lysc_type*)un_aux);
+                } else {
+                    LY_ARRAY_INCREMENT(un->types);
+                }
+                LY_CHECK_RET(ret);
+            }
+        }
+
+        if (!base && !type_p->flags) {
+            /* type derived from union built-in type must contain at least one type */
+            if (tpdfname) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "type", "union type ", tpdfname);
+            } else {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "type", "union type", "");
+                free(*type);
+                *type = NULL;
+            }
+            return LY_EVALID;
+        }
+
+        if (tpdfname) {
+            type_p->compiled = *type;
+            *type = calloc(1, sizeof(struct lysc_type_union));
+        }
+        break;
+    case LY_TYPE_BOOL:
+    case LY_TYPE_EMPTY:
+    case LY_TYPE_UNKNOWN: /* just to complete switch */
+        break;
+    }
+    LY_CHECK_ERR_RET(!(*type), LOGMEM(ctx->ctx), LY_EMEM);
+done:
+    return ret;
+}
+
+/**
+ * @brief Compile information about the leaf/leaf-list's type.
+ * @param[in] ctx Compile context.
+ * @param[in] context_node_p Schema node where the type/typedef is placed to correctly find the base types.
+ * @param[in] context_flags Flags of the context node or the referencing typedef to correctly check status of referencing and referenced objects.
+ * @param[in] context_mod Module of the context node or the referencing typedef to correctly check status of referencing and referenced objects.
+ * @param[in] context_name Name of the context node or referencing typedef for logging.
+ * @param[in] type_p Parsed type to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[out] type Newly created (or reused with increased refcount) type structure with the filled information about the type.
+ * @param[out] units Storage for inheriting units value from the typedefs the current type derives from.
+ * @param[out] dflt Storage for inheriting default value from the typedefs the current type derives from.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lys_compile_type(struct lysc_ctx *ctx, struct lysp_node *context_node_p, uint16_t context_flags, struct lysp_module *context_mod, const char *context_name,
+                 struct lysp_type *type_p, int options, struct lysc_type **type, const char **units, const char **dflt)
+{
+    LY_ERR ret = LY_SUCCESS;
+    unsigned int u;
+    int dummyloops = 0;
+    struct type_context {
+        const struct lysp_tpdf *tpdf;
+        struct lysp_node *node;
+        struct lysp_module *mod;
+    } *tctx, *tctx_prev = NULL;
+    LY_DATA_TYPE basetype = LY_TYPE_UNKNOWN;
+    struct lysc_type *base = NULL, *prev_type;
+    struct ly_set tpdf_chain = {0};
+
+    (*type) = NULL;
+
+    tctx = calloc(1, sizeof *tctx);
+    LY_CHECK_ERR_RET(!tctx, LOGMEM(ctx->ctx), LY_EMEM);
+    for (ret = lysp_type_find(type_p->name, context_node_p, ctx->mod_def->parsed,
+                             &basetype, &tctx->tpdf, &tctx->node, &tctx->mod);
+            ret == LY_SUCCESS;
+            ret = lysp_type_find(tctx_prev->tpdf->type.name, tctx_prev->node, tctx_prev->mod,
+                                         &basetype, &tctx->tpdf, &tctx->node, &tctx->mod)) {
+        if (basetype) {
+            break;
+        }
+
+        /* check status */
+        ret = lysc_check_status(ctx, context_flags, context_mod, context_name,
+                                tctx->tpdf->flags, tctx->mod, tctx->node ? tctx->node->name : tctx->tpdf->name);
+        LY_CHECK_ERR_GOTO(ret,  free(tctx), cleanup);
+
+        if (units && !*units) {
+            /* inherit units */
+            DUP_STRING(ctx->ctx, tctx->tpdf->units, *units);
+        }
+        if (dflt && !*dflt) {
+            /* inherit default */
+            DUP_STRING(ctx->ctx, tctx->tpdf->dflt, *dflt);
+        }
+        if (dummyloops && (!units || *units) && (!dflt || *dflt)) {
+            basetype = ((struct type_context*)tpdf_chain.objs[tpdf_chain.count - 1])->tpdf->type.compiled->basetype;
+            break;
+        }
+
+        if (tctx->tpdf->type.compiled) {
+            /* it is not necessary to continue, the rest of the chain was already compiled,
+             * but we still may need to inherit default and units values, so start dummy loops */
+            basetype = tctx->tpdf->type.compiled->basetype;
+            ly_set_add(&tpdf_chain, tctx, LY_SET_OPT_USEASLIST);
+            if ((units && !*units) || (dflt && !*dflt)) {
+                dummyloops = 1;
+                goto preparenext;
+            } else {
+                tctx = NULL;
+                break;
+            }
+        }
+
+        /* store information for the following processing */
+        ly_set_add(&tpdf_chain, tctx, LY_SET_OPT_USEASLIST);
+
+preparenext:
+        /* prepare next loop */
+        tctx_prev = tctx;
+        tctx = calloc(1, sizeof *tctx);
+        LY_CHECK_ERR_RET(!tctx, LOGMEM(ctx->ctx), LY_EMEM);
+    }
+    free(tctx);
+
+    /* allocate type according to the basetype */
+    switch (basetype) {
+    case LY_TYPE_BINARY:
+        *type = calloc(1, sizeof(struct lysc_type_bin));
+        break;
+    case LY_TYPE_BITS:
+        *type = calloc(1, sizeof(struct lysc_type_bits));
+        break;
+    case LY_TYPE_BOOL:
+    case LY_TYPE_EMPTY:
+        *type = calloc(1, sizeof(struct lysc_type));
+        break;
+    case LY_TYPE_DEC64:
+        *type = calloc(1, sizeof(struct lysc_type_dec));
+        break;
+    case LY_TYPE_ENUM:
+        *type = calloc(1, sizeof(struct lysc_type_enum));
+        break;
+    case LY_TYPE_IDENT:
+        *type = calloc(1, sizeof(struct lysc_type_identityref));
+        break;
+    case LY_TYPE_INST:
+        *type = calloc(1, sizeof(struct lysc_type_instanceid));
+        break;
+    case LY_TYPE_LEAFREF:
+        *type = calloc(1, sizeof(struct lysc_type_leafref));
+        break;
+    case LY_TYPE_STRING:
+        *type = calloc(1, sizeof(struct lysc_type_str));
+        break;
+    case LY_TYPE_UNION:
+        *type = calloc(1, sizeof(struct lysc_type_union));
+        break;
+    case LY_TYPE_INT8:
+    case LY_TYPE_UINT8:
+    case LY_TYPE_INT16:
+    case LY_TYPE_UINT16:
+    case LY_TYPE_INT32:
+    case LY_TYPE_UINT32:
+    case LY_TYPE_INT64:
+    case LY_TYPE_UINT64:
+        *type = calloc(1, sizeof(struct lysc_type_num));
+        break;
+    case LY_TYPE_UNKNOWN:
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+               "Referenced type \"%s\" not found.", tctx_prev ? tctx_prev->tpdf->type.name : type_p->name);
+        ret = LY_EVALID;
+        goto cleanup;
+    }
+    LY_CHECK_ERR_GOTO(!(*type), LOGMEM(ctx->ctx), cleanup);
+    if (~type_substmt_map[basetype] & type_p->flags) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Invalid type restrictions for %s type.",
+               ly_data_type2str[basetype]);
+        free(*type);
+        (*type) = NULL;
+        ret = LY_EVALID;
+        goto cleanup;
+    }
+
+    /* get restrictions from the referred typedefs */
+    for (u = tpdf_chain.count - 1; u + 1 > 0; --u) {
+        tctx = (struct type_context*)tpdf_chain.objs[u];
+        if (tctx->tpdf->type.compiled) {
+            base = tctx->tpdf->type.compiled;
+            continue;
+        } else if (basetype != LY_TYPE_LEAFREF && (u != tpdf_chain.count - 1) && !(tctx->tpdf->type.flags)) {
+            /* no change, just use the type information from the base */
+            base = ((struct lysp_tpdf*)tctx->tpdf)->type.compiled = ((struct type_context*)tpdf_chain.objs[u + 1])->tpdf->type.compiled;
+            ++base->refcount;
+            continue;
+        }
+
+        ++(*type)->refcount;
+        if (~type_substmt_map[basetype] & tctx->tpdf->type.flags) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Invalid type \"%s\" restriction(s) for %s type.",
+                   tctx->tpdf->name, ly_data_type2str[basetype]);
+            ret = LY_EVALID;
+            goto cleanup;
+        } else if (basetype == LY_TYPE_EMPTY && tctx->tpdf->dflt) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                   "Invalid type \"%s\" - \"empty\" type must not have a default value (%s).",
+                   tctx->tpdf->name, tctx->tpdf->dflt);
+            ret = LY_EVALID;
+            goto cleanup;
+        }
+
+        (*type)->basetype = basetype;
+        prev_type = *type;
+        ret = lys_compile_type_(ctx, tctx->node, tctx->tpdf->flags, tctx->mod, tctx->tpdf->name, &((struct lysp_tpdf*)tctx->tpdf)->type,
+                                basetype & (LY_TYPE_LEAFREF | LY_TYPE_UNION) ? lysp_find_module(ctx->ctx, tctx->mod) : NULL,
+                                basetype, options, tctx->tpdf->name, base, type);
+        LY_CHECK_GOTO(ret, cleanup);
+        base = prev_type;
+    }
+
+    /* process the type definition in leaf */
+    if (type_p->flags || !base || basetype == LY_TYPE_LEAFREF) {
+        /* get restrictions from the node itself */
+        (*type)->basetype = basetype;
+        ++(*type)->refcount;
+        ret = lys_compile_type_(ctx, context_node_p, context_flags, context_mod, context_name, type_p, ctx->mod_def, basetype, options, NULL, base, type);
+        LY_CHECK_GOTO(ret, cleanup);
+    } else {
+        /* no specific restriction in leaf's type definition, copy from the base */
+        free(*type);
+        (*type) = base;
+        ++(*type)->refcount;
+    }
+
+    COMPILE_ARRAY_GOTO(ctx, type_p->exts, (*type)->exts, options, u, lys_compile_ext, ret, cleanup);
+
+cleanup:
+    ly_set_erase(&tpdf_chain, free);
+    return ret;
+}
+
+static LY_ERR lys_compile_node(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *parent);
+
+/**
+ * @brief Compile parsed container node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed container 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_container(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+{
+    struct lysp_node_container *cont_p = (struct lysp_node_container*)node_p;
+    struct lysc_node_container *cont = (struct lysc_node_container*)node;
+    struct lysp_node *child_p;
+    unsigned int u;
+    LY_ERR ret = LY_SUCCESS;
+
+    LY_LIST_FOR(cont_p->child, child_p) {
+        LY_CHECK_RET(lys_compile_node(ctx, child_p, options, node));
+    }
+
+    COMPILE_ARRAY_GOTO(ctx, cont_p->musts, cont->musts, options, u, lys_compile_must, ret, done);
+    //COMPILE_ARRAY_GOTO(ctx, cont_p->actions, cont->actions, options, u, lys_compile_action, ret, done);
+    //COMPILE_ARRAY_GOTO(ctx, cont_p->notifs, cont->notifs, options, u, lys_compile_notif, ret, done);
+
+done:
+    return ret;
+}
+
+/**
+ * @brief Compile parsed leaf node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed leaf 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 leaf-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_leaf(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+{
+    struct lysp_node_leaf *leaf_p = (struct lysp_node_leaf*)node_p;
+    struct lysc_node_leaf *leaf = (struct lysc_node_leaf*)node;
+    unsigned int u;
+    LY_ERR ret = LY_SUCCESS;
+
+    COMPILE_ARRAY_GOTO(ctx, leaf_p->musts, leaf->musts, options, u, lys_compile_must, ret, done);
+    DUP_STRING(ctx->ctx, leaf_p->units, leaf->units);
+    DUP_STRING(ctx->ctx, leaf_p->dflt, leaf->dflt);
+    if (leaf->dflt) {
+        leaf->flags |= LYS_SET_DFLT;
+    }
+
+    ret = lys_compile_type(ctx, node_p, node_p->flags, ctx->mod_def->parsed, node_p->name, &leaf_p->type, options, &leaf->type,
+                           leaf->units ? NULL : &leaf->units, leaf->dflt || (leaf->flags & LYS_MAND_TRUE) ? NULL : &leaf->dflt);
+    LY_CHECK_GOTO(ret, done);
+    if (leaf->type->basetype == LY_TYPE_LEAFREF) {
+        /* store to validate the path in the current context at the end of schema compiling when all the nodes are present */
+        ly_set_add(&ctx->unres, leaf, 0);
+    } else if (leaf->type->basetype == LY_TYPE_UNION) {
+        LY_ARRAY_FOR(((struct lysc_type_union*)leaf->type)->types, u) {
+            if (((struct lysc_type_union*)leaf->type)->types[u]->basetype == LY_TYPE_LEAFREF) {
+                /* store to validate the path in the current context at the end of schema compiling when all the nodes are present */
+                ly_set_add(&ctx->unres, leaf, 0);
+            }
+        }
+    } else if (leaf->type->basetype == LY_TYPE_EMPTY && leaf_p->dflt) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+               "Leaf of type \"empty\" must not have a default value (%s).",leaf_p->dflt);
+        return LY_EVALID;
+    }
+
+    /* TODO validate default value according to the type, possibly postpone the check when the leafref target is known */
+
+done:
+    return ret;
+}
+
+/**
+ * @brief Compile parsed leaf-list node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed leaf-list 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 leaf-list-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_leaflist(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+{
+    struct lysp_node_leaflist *llist_p = (struct lysp_node_leaflist*)node_p;
+    struct lysc_node_leaflist *llist = (struct lysc_node_leaflist*)node;
+    unsigned int u, v;
+    const char *dflt = NULL;
+    LY_ERR ret = LY_SUCCESS;
+
+    COMPILE_ARRAY_GOTO(ctx, llist_p->musts, llist->musts, options, u, lys_compile_must, ret, done);
+    DUP_STRING(ctx->ctx, llist_p->units, llist->units);
+
+    if (llist_p->dflts) {
+        LY_ARRAY_CREATE_GOTO(ctx->ctx, llist->dflts, LY_ARRAY_SIZE(llist_p->dflts), ret, done);
+        LY_ARRAY_FOR(llist_p->dflts, u) {
+            DUP_STRING(ctx->ctx, llist_p->dflts[u], llist->dflts[u]);
+            LY_ARRAY_INCREMENT(llist->dflts);
+        }
+    }
+
+    llist->min = llist_p->min;
+    llist->max = llist_p->max ? llist_p->max : (uint32_t)-1;
+
+    ret = lys_compile_type(ctx, node_p, node_p->flags, ctx->mod_def->parsed, node_p->name, &llist_p->type, options, &llist->type,
+                           llist->units ? NULL : &llist->units, (llist->dflts || llist->min) ? NULL : &dflt);
+    LY_CHECK_GOTO(ret, done);
+    if (dflt) {
+        LY_ARRAY_CREATE_GOTO(ctx->ctx, llist->dflts, 1, ret, done);
+        llist->dflts[0] = dflt;
+        LY_ARRAY_INCREMENT(llist->dflts);
+    }
+
+    if (llist->type->basetype == LY_TYPE_LEAFREF) {
+        /* store to validate the path in the current context at the end of schema compiling when all the nodes are present */
+        ly_set_add(&ctx->unres, llist, 0);
+    } else if (llist->type->basetype == LY_TYPE_UNION) {
+        LY_ARRAY_FOR(((struct lysc_type_union*)llist->type)->types, u) {
+            if (((struct lysc_type_union*)llist->type)->types[u]->basetype == LY_TYPE_LEAFREF) {
+                /* store to validate the path in the current context at the end of schema compiling when all the nodes are present */
+                ly_set_add(&ctx->unres, llist, 0);
+            }
+        }
+    } else if (llist->type->basetype == LY_TYPE_EMPTY) {
+        if (ctx->mod_def->parsed->version < LYS_VERSION_1_1) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                   "Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.");
+            return LY_EVALID;
+        } else if (llist_p->dflts) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                   "Leaf-list of type \"empty\" must not have a default value (%s).", llist_p->dflts[0]);
+            return LY_EVALID;
+        }
+    }
+
+    if ((llist->flags & LYS_CONFIG_W) && llist->dflts && LY_ARRAY_SIZE(llist->dflts)) {
+        /* configuration data values must be unique - so check the default values */
+        LY_ARRAY_FOR(llist->dflts, u) {
+            for (v = u + 1; v < LY_ARRAY_SIZE(llist->dflts); ++v) {
+                if (!strcmp(llist->dflts[u], llist->dflts[v])) {
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                           "Configuration leaf-list has multiple defaults of the same value \"%s\".", llist->dflts[v]);
+                    return LY_EVALID;
+                }
+            }
+        }
+    }
+
+    /* TODO validate default value according to the type, possibly postpone the check when the leafref target is known */
+
+done:
+    return ret;
+}
+
+/**
+ * @brief Compile parsed list node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed list 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 list-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_list(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+{
+    struct lysp_node_list *list_p = (struct lysp_node_list*)node_p;
+    struct lysc_node_list *list = (struct lysc_node_list*)node;
+    struct lysp_node *child_p;
+    struct lysc_node_leaf **key, ***unique;
+    size_t len;
+    unsigned int u, v;
+    const char *keystr, *delim;
+    int config;
+    LY_ERR ret = LY_SUCCESS;
+
+    list->min = list_p->min;
+    list->max = list_p->max ? list_p->max : (uint32_t)-1;
+
+    LY_LIST_FOR(list_p->child, child_p) {
+        LY_CHECK_RET(lys_compile_node(ctx, child_p, options, node));
+    }
+
+    COMPILE_ARRAY_GOTO(ctx, list_p->musts, list->musts, options, u, lys_compile_must, ret, done);
+
+    /* keys */
+    if ((list->flags & LYS_CONFIG_W) && (!list_p->key || !list_p->key[0])) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS, "Missing key in list representing configuration data.");
+        return LY_EVALID;
+    }
+
+    /* find all the keys (must be direct children) */
+    keystr = list_p->key;
+    while (keystr) {
+        delim = strpbrk(keystr, " \t\n");
+        if (delim) {
+            len = delim - keystr;
+            while (isspace(*delim)) {
+                ++delim;
+            }
+        } else {
+            len = strlen(keystr);
+        }
+
+        /* key node must be present */
+        LY_ARRAY_NEW_RET(ctx->ctx, list->keys, key, LY_EMEM);
+        *key = (struct lysc_node_leaf*)lys_child(node, node->module, keystr, len, LYS_LEAF, LYS_GETNEXT_NOCHOICE | LYS_GETNEXT_NOSTATECHECK);
+        if (!(*key)) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "The list's key \"%.*s\" not found.", len, keystr);
+            return LY_EVALID;
+        }
+        /* keys must be unique */
+        for(u = 0; u < LY_ARRAY_SIZE(list->keys) - 1; ++u) {
+            if (*key == list->keys[u]) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                       "Duplicated key identifier \"%.*s\".", len, keystr);
+                return LY_EVALID;
+            }
+        }
+        /* key must have the same config flag as the list itself */
+        if ((list->flags & LYS_CONFIG_MASK) != ((*key)->flags & LYS_CONFIG_MASK)) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS, "Key of the configuration list must not be status leaf.");
+            return LY_EVALID;
+        }
+        if (ctx->mod_def->parsed->version < LYS_VERSION_1_1) {
+            /* YANG 1.0 denies key to be of empty type */
+            if ((*key)->type->basetype == LY_TYPE_EMPTY) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                       "Key of a list can be of type \"empty\" only in YANG 1.1 modules.");
+                return LY_EVALID;
+            }
+        } else {
+            /* when and if-feature are illegal on list keys */
+            if ((*key)->when) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                       "List's key \"%s\" must not have any \"when\" statement.", (*key)->name);
+                return LY_EVALID;
+            }
+            if ((*key)->iffeatures) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                       "List's key \"%s\" must not have any \"if-feature\" statement.", (*key)->name);
+                return LY_EVALID;
+            }
+        }
+
+        /* check status */
+        LY_CHECK_RET(lysc_check_status(ctx, list->flags, list->module, list->name,
+                                       (*key)->flags, (*key)->module, (*key)->name));
+
+        /* ignore default values of the key */
+        if ((*key)->dflt) {
+            lydict_remove(ctx->ctx, (*key)->dflt);
+            (*key)->dflt = NULL;
+        }
+        /* mark leaf as key */
+        (*key)->flags |= LYS_KEY;
+
+        /* next key value */
+        keystr = delim;
+    }
+
+    /* uniques */
+    if (list_p->uniques) {
+        for (v = 0; v < LY_ARRAY_SIZE(list_p->uniques); ++v) {
+            config = -1;
+            LY_ARRAY_NEW_RET(ctx->ctx, list->uniques, unique, LY_EMEM);
+            keystr = list_p->uniques[v];
+            while (keystr) {
+                delim = strpbrk(keystr, " \t\n");
+                if (delim) {
+                    len = delim - keystr;
+                    while (isspace(*delim)) {
+                        ++delim;
+                    }
+                } else {
+                    len = strlen(keystr);
+                }
+
+                /* unique node must be present */
+                LY_ARRAY_NEW_RET(ctx->ctx, *unique, key, LY_EMEM);
+                ret = lys_resolve_descendant_schema_nodeid(ctx, keystr, len, node, LYS_LEAF, (const struct lysc_node**)key);
+                if (ret != LY_SUCCESS) {
+                    if (ret == LY_EDENIED) {
+                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                               "Unique's descendant-schema-nodeid \"%.*s\" refers to a %s node instead of a leaf.",
+                               len, keystr, lys_nodetype2str((*key)->nodetype));
+                    }
+                    return LY_EVALID;
+                }
+
+                /* all referenced leafs must be of the same config type */
+                if (config != -1 && ((((*key)->flags & LYS_CONFIG_W) && config == 0) || (((*key)->flags & LYS_CONFIG_R) && config == 1))) {
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                           "Unique statement \"%s\" refers to leafs with different config type.", list_p->uniques[v]);
+                    return LY_EVALID;
+                } else if ((*key)->flags & LYS_CONFIG_W) {
+                    config = 1;
+                } else { /* LYS_CONFIG_R */
+                    config = 0;
+                }
+
+                /* check status */
+                LY_CHECK_RET(lysc_check_status(ctx, list->flags, list->module, list->name,
+                                               (*key)->flags, (*key)->module, (*key)->name));
+
+                /* mark leaf as unique */
+                (*key)->flags |= LYS_UNIQUE;
+
+                /* next unique value in line */
+                keystr = delim;
+            }
+            /* next unique definition */
+        }
+    }
+
+    //COMPILE_ARRAY_GOTO(ctx, list_p->actions, list->actions, options, u, lys_compile_action, ret, done);
+    //COMPILE_ARRAY_GOTO(ctx, list_p->notifs, list->notifs, options, u, lys_compile_notif, ret, done);
+
+done:
+    return ret;
+}
+
+static LY_ERR
+lys_compile_node_choice_dflt(struct lysc_ctx *ctx, const char *dflt, struct lysc_node_choice *ch)
+{
+    struct lysc_node *iter, *node = (struct lysc_node*)ch;
+    const char *prefix = NULL, *name;
+    size_t prefix_len = 0;
+
+    /* could use lys_parse_nodeid(), but it checks syntax which is already done in this case by the parsers */
+    name = strchr(dflt, ':');
+    if (name) {
+        prefix = dflt;
+        prefix_len = name - prefix;
+        ++name;
+    } else {
+        name = dflt;
+    }
+    if (prefix && (strncmp(prefix, node->module->compiled->prefix, prefix_len) || node->module->compiled->prefix[prefix_len] != '\0')) {
+        /* prefixed default case make sense only for the prefix of the schema itself */
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+               "Invalid default case referencing a case from different YANG module (by prefix \"%.*s\").",
+               prefix_len, prefix);
+        return LY_EVALID;
+    }
+    ch->dflt = (struct lysc_node_case*)lys_child(node, node->module, name, 0, LYS_CASE, LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_WITHCASE);
+    if (!ch->dflt) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+               "Default case \"%s\" not found.", dflt);
+        return LY_EVALID;
+    }
+    /* no mandatory nodes directly under the default case */
+    LY_LIST_FOR(ch->dflt->child, iter) {
+        if (iter->flags & LYS_MAND_TRUE) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                   "Mandatory node \"%s\" under the default case \"%s\".", iter->name, dflt);
+            return LY_EVALID;
+        }
+    }
+    ch->flags |= LYS_SET_DFLT;
+    return LY_SUCCESS;
+}
+
+/**
+ * @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 choice-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;
+    struct lys_module;
+    LY_ERR ret = LY_SUCCESS;
+
+    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 */
+    if (ch_p->dflt) {
+        LY_CHECK_RET(lys_compile_node_choice_dflt(ctx, ch_p->dflt, ch));
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Compile parsed anydata or anyxml node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed anydata or anyxml 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 any-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_node_any(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
+{
+    struct lysp_node_anydata *any_p = (struct lysp_node_anydata*)node_p;
+    struct lysc_node_anydata *any = (struct lysc_node_anydata*)node;
+    unsigned int u;
+    LY_ERR ret = LY_SUCCESS;
+
+    COMPILE_ARRAY_GOTO(ctx, any_p->musts, any->musts, options, u, lys_compile_must, ret, done);
+
+    if (any->flags & LYS_CONFIG_W) {
+        LOGWRN(ctx->ctx, "Use of %s to define configuration data is not recommended.",
+               ly_stmt2str(any->nodetype == LYS_ANYDATA ? YANG_ANYDATA : YANG_ANYXML));
+    }
+done:
+    return ret;
+}
+
+static LY_ERR
+lys_compile_status_check(struct lysc_ctx *ctx, uint16_t node_flags, uint16_t parent_flags)
+{
+    /* 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_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) {
+        return lys_compile_status_check(ctx, node->flags, parent->flags);
+    }
+    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
+}
+
+static LY_ERR
+lys_compile_refine_config(struct lysc_ctx *ctx, struct lysc_node *node, struct lysp_refine *rfn, int inheriting)
+{
+    struct lysc_node *child;
+    uint16_t config = rfn->flags & LYS_CONFIG_MASK;
+
+    if (config == (node->flags & LYS_CONFIG_MASK)) {
+        /* nothing to do */
+        return LY_SUCCESS;
+    }
+
+    if (!inheriting) {
+        /* explicit refine */
+        if (config == LYS_CONFIG_W && node->parent && (node->parent->flags & LYS_CONFIG_R)) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                   "Invalid refine of config in \"%s\" - configuration node cannot be child of any state data node.");
+            return LY_EVALID;
+        }
+    }
+    node->flags &= ~LYS_CONFIG_MASK;
+    node->flags |= config;
+
+    /* inherit the change into the children */
+    LY_LIST_FOR((struct lysc_node*)lysc_node_children(node), child) {
+        LY_CHECK_RET(lys_compile_refine_config(ctx, child, rfn, 1));
+    }
+
+    /* TODO actions and notifications */
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Compile parsed uses statement - resolve target grouping and connect its content into parent.
+ * If present, also apply uses's modificators.
+ *
+ * @param[in] ctx Compile context
+ * @param[in] uses_p Parsed uses schema node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] parent Compiled parent node where the content of the referenced grouping is supposed to be connected. It is
+ * NULL for top-level nodes, in such a case the module where the node will be connected is taken from
+ * the compile context.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_uses(struct lysc_ctx *ctx, struct lysp_node_uses *uses_p, int options, struct lysc_node *parent)
+{
+    struct lysp_node *node_p;
+    struct lysc_node *node;
+    const struct lysp_grp *grp = NULL;
+    unsigned int u, v, grp_stack_count;
+    int found;
+    const char *id, *name, *prefix;
+    size_t prefix_len, name_len;
+    struct lys_module *mod, *mod_old;
+    struct lysp_refine *rfn;
+    LY_ERR ret = LY_EVALID;
+
+    /* search for the grouping definition */
+    found = 0;
+    id = uses_p->name;
+    lys_parse_nodeid(&id, &prefix, &prefix_len, &name, &name_len);
+    if (prefix) {
+        mod = lys_module_find_prefix(ctx->mod_def, prefix, prefix_len);
+        if (!mod) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid prefix used for grouping reference (%s).", uses_p->name);
+            return LY_EVALID;
+        }
+    } else {
+        mod = ctx->mod_def;
+    }
+    if (mod == ctx->mod_def) {
+        for (node_p = uses_p->parent; !found && node_p; node_p = node_p->parent) {
+            grp = lysp_node_groupings(node_p);
+            LY_ARRAY_FOR(grp, u) {
+                if (!strcmp(grp[u].name, name)) {
+                    grp = &grp[u];
+                    found = 1;
+                    break;
+                }
+            }
+        }
+    }
+    if (!found) {
+        /* search in top-level groupings of the main module ... */
+        grp = mod->parsed->groupings;
+        if (grp) {
+            for (u = 0; !found && u < LY_ARRAY_SIZE(grp); ++u) {
+                if (!strcmp(grp[u].name, name)) {
+                    grp = &grp[u];
+                    found = 1;
+                }
+            }
+        }
+        if (!found && mod->parsed->includes) {
+            /* ... and all the submodules */
+            for (u = 0; !found && u < LY_ARRAY_SIZE(mod->parsed->includes); ++u) {
+                grp = mod->parsed->includes[u].submodule->groupings;
+                if (grp) {
+                    for (v = 0; !found && v < LY_ARRAY_SIZE(grp); ++v) {
+                        if (!strcmp(grp[v].name, name)) {
+                            grp = &grp[v];
+                            found = 1;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    if (!found) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+               "Grouping \"%s\" referenced by a uses statement not found.", uses_p->name);
+        return LY_EVALID;
+    }
+
+    /* grouping must not reference themselves - stack in ctx maintains list of groupings currently being applied */
+    grp_stack_count = ctx->groupings.count;
+    ly_set_add(&ctx->groupings, (void*)grp, 0);
+    if (grp_stack_count == ctx->groupings.count) {
+        /* the target grouping is already in the stack, so we are already inside it -> circular dependency */
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+               "Grouping \"%s\" references itself through a uses statement.", grp->name);
+        return LY_EVALID;
+    }
+
+    /* switch context's mod_def */
+    mod_old = ctx->mod_def;
+    ctx->mod_def = mod;
+
+    /* check status */
+    LY_CHECK_GOTO(lysc_check_status(ctx, uses_p->flags, mod_old, uses_p->name, grp->flags, mod, grp->name), error);
+
+    /* connect the grouping's content */
+    LY_LIST_FOR(grp->data, node_p) {
+        LY_CHECK_GOTO(lys_compile_node(ctx, node_p, options, parent), error);
+        /* check status between parent (uses in this case) and child - lys_compile_node() compares parent and the new node */
+        if (lys_compile_status_check(ctx, parent ? lysc_node_children(parent)->prev->flags : ctx->mod->compiled->data->prev->flags, uses_p->flags)) {
+            goto error;
+        }
+    }
+
+    /* apply refine */
+    LY_ARRAY_FOR(uses_p->refines, struct lysp_refine, rfn) {
+        LY_CHECK_GOTO(lys_resolve_descendant_schema_nodeid(ctx, rfn->nodeid, 0, parent, 0, (const struct lysc_node**)&node), error);
+
+        /* default value */
+        if (rfn->dflts) {
+            if ((node->nodetype & (LYS_LEAF | LYS_CHOICE)) && LY_ARRAY_SIZE(rfn->dflts) > 1) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                       "Invalid refine of default in \"%s\" - %s cannot hold %d default values.",
+                       rfn->nodeid, lys_nodetype2str(node->nodetype), LY_ARRAY_SIZE(rfn->dflts));
+                goto error;
+            }
+            if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_CHOICE))) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                       "Invalid refine of default in \"%s\" - %s cannot hold default value(s).",
+                       rfn->nodeid, lys_nodetype2str(node->nodetype));
+                goto error;
+            }
+            if (node->nodetype == LYS_LEAF) {
+                FREE_STRING(ctx->ctx, ((struct lysc_node_leaf*)node)->dflt);
+                DUP_STRING(ctx->ctx, rfn->dflts[0], ((struct lysc_node_leaf*)node)->dflt);
+                /* TODO check the default value according to type */
+            } else if (node->nodetype == LYS_LEAFLIST) {
+                LY_ARRAY_FOR(((struct lysc_node_leaflist*)node)->dflts, u) {
+                    lydict_remove(ctx->ctx, ((struct lysc_node_leaflist*)node)->dflts[u]);
+                }
+                LY_ARRAY_FREE(((struct lysc_node_leaflist*)node)->dflts);
+                LY_ARRAY_CREATE_GOTO(ctx->ctx, ((struct lysc_node_leaflist*)node)->dflts, LY_ARRAY_SIZE(rfn->dflts), ret, error);
+                LY_ARRAY_FOR(rfn->dflts, u) {
+                    LY_ARRAY_INCREMENT(((struct lysc_node_leaflist*)node)->dflts);
+                    DUP_STRING(ctx->ctx, rfn->dflts[u], ((struct lysc_node_leaflist*)node)->dflts[u]);
+                }
+                /* TODO check the default values according to type */
+            } else if (node->nodetype == LYS_CHOICE) {
+                LY_CHECK_GOTO(lys_compile_node_choice_dflt(ctx, rfn->dflts[0], (struct lysc_node_choice*)node), error);
+            }
+        }
+
+        /* description refine not applicable */
+        /* reference refine not applicable */
+
+        /* config */
+        if (rfn->flags & LYS_CONFIG_MASK) {
+            LY_CHECK_GOTO(lys_compile_refine_config(ctx, node, rfn, 0), error);
+        }
+
+        /* mandatory */
+        if (rfn->flags & LYS_MAND_MASK) {
+            if (!(node->nodetype & (LYS_LEAF | LYS_ANYDATA | LYS_ANYXML | LYS_CHOICE))) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                       "Invalid refine of mandatory in \"%s\" - %s cannot hold mandatory statement.",
+                       rfn->nodeid, lys_nodetype2str(node->nodetype));
+                goto error;
+            }
+            /* in compiled flags, only the LYS_MAND_TRUE is present */
+            if (rfn->flags & LYS_MAND_TRUE) {
+                /* check if node has default value */
+                if (node->nodetype & LYS_LEAF) {
+                    if (node->flags & LYS_SET_DFLT) {
+                        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                               "Invalid refine of mandatory in \"%s\" - leafswith \"default\" statement.", rfn->nodeid);
+                        goto error;
+                    } else {
+                        /* remove the default value taken from the leaf's type */
+                        FREE_STRING(ctx->ctx, ((struct lysc_node_leaf*)node)->dflt);
+                        ((struct lysc_node_leaf*)node)->dflt = NULL;
+                    }
+                } else if ((node->nodetype & LYS_CHOICE) && ((struct lysc_node_choice*)node)->dflt) {
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                           "Invalid refine of mandatory in \"%s\" - choice with \"default\" statement.", rfn->nodeid);
+                    goto error;
+                }
+                if (node->parent && (node->parent->flags & LYS_SET_DFLT)) {
+                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+                           "Invalid refine of mandatory in \"%s\" - %s under the default case.",
+                           rfn->nodeid, lys_nodetype2str(node->nodetype));
+                    goto error;
+                }
+
+                node->flags |= LYS_MAND_TRUE;
+            } else {
+                node->flags &= ~LYS_MAND_TRUE;
+            }
+        }
+    }
+
+    ret = LY_SUCCESS;
+error:
+    /* reload previous context's mod_def */
+    ctx->mod_def = mod_old;
+    /* remove the grouping from the stack for circular groupings dependency check */
+    ly_set_rm_index(&ctx->groupings, ctx->groupings.count - 1, NULL);
+    assert(ctx->groupings.count == grp_stack_count);
+
+    return ret;
+}
+
+/**
+ * @brief Compile parsed schema node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed schema node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] parent Compiled parent node where the current node is supposed to be connected. It is
+ * NULL for top-level nodes, in such a case the module where the node will be connected is taken from
+ * the compile context.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+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;
+    struct lysc_node_case *cs;
+    unsigned int u;
+    LY_ERR (*node_compile_spec)(struct lysc_ctx*, struct lysp_node*, int, struct lysc_node*);
+
+    switch (node_p->nodetype) {
+    case LYS_CONTAINER:
+        node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_container));
+        node_compile_spec = lys_compile_node_container;
+        break;
+    case LYS_LEAF:
+        node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_leaf));
+        node_compile_spec = lys_compile_node_leaf;
+        break;
+    case LYS_LIST:
+        node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_list));
+        node_compile_spec = lys_compile_node_list;
+        break;
+    case LYS_LEAFLIST:
+        node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_leaflist));
+        node_compile_spec = lys_compile_node_leaflist;
+        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:
+        node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_anydata));
+        node_compile_spec = lys_compile_node_any;
+        break;
+    case LYS_USES:
+        return lys_compile_uses(ctx, (struct lysp_node_uses*)node_p, options, parent);
+    default:
+        LOGINT(ctx->ctx);
+        return LY_EINT;
+    }
+    LY_CHECK_ERR_RET(!node, LOGMEM(ctx->ctx), LY_EMEM);
+    node->nodetype = node_p->nodetype;
+    node->module = ctx->mod;
+    node->prev = node;
+    node->flags = node_p->flags & LYS_FLAGS_COMPILED_MASK;
+
+    /* config */
+    if (!(node->flags & LYS_CONFIG_MASK)) {
+        /* config not explicitely set, inherit it from parent */
+        if (parent) {
+            node->flags |= parent->flags & LYS_CONFIG_MASK;
+        } else {
+            /* default is config true */
+            node->flags |= LYS_CONFIG_W;
+        }
+    }
+    if (parent && (parent->flags & LYS_CONFIG_R) && (node->flags & LYS_CONFIG_W)) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
+               "Configuration node cannot be child of any state data node.");
+        goto error;
+    }
+
+    /* *list ordering */
+    if (node->nodetype & (LYS_LIST | LYS_LEAFLIST)) {
+        if ((node->flags & LYS_CONFIG_R) && (node->flags & LYS_ORDBY_MASK)) {
+            LOGWRN(ctx->ctx, "The ordered-by statement is ignored in lists representing state data, "
+                   "RPC/action output parameters or notification content (%s).", ctx->path);
+            node->flags &= ~LYS_ORDBY_MASK;
+            node->flags |= LYS_ORDBY_SYSTEM;
+        } else if (!(node->flags & LYS_ORDBY_MASK)) {
+            /* default ordering is system */
+            node->flags |= LYS_ORDBY_SYSTEM;
+        }
+    }
+
+    /* 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 (!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)) {
+        node->sp = node_p;
+    }
+    DUP_STRING(ctx->ctx, node_p->name, node->name);
+    COMPILE_MEMBER_GOTO(ctx, node_p->when, node->when, options, lys_compile_when, ret, error);
+    COMPILE_ARRAY_GOTO(ctx, node_p->iffeatures, node->iffeatures, options, u, lys_compile_iffeature, ret, error);
+    COMPILE_ARRAY_GOTO(ctx, node_p->exts, node->exts, options, u, lys_compile_ext, ret, error);
+
+    /* nodetype-specific part */
+    LY_CHECK_GOTO(node_compile_spec(ctx, node_p, options, node), error);
+
+    /* insert into parent's children */
+    if (parent) {
+        if (parent->nodetype == LYS_CHOICE) {
+            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;
+        }
+        LY_CHECK_RET(lys_compile_node_connect(ctx, parent, node), LY_EVALID);
+    } else {
+        /* top-level element */
+        if (!ctx->mod->compiled->data) {
+            ctx->mod->compiled->data = node;
+        } else {
+            /* insert at the end of the module's top-level nodes list */
+            ctx->mod->compiled->data->prev->next = node;
+            node->prev = ctx->mod->compiled->data->prev;
+            ctx->mod->compiled->data->prev = node;
+        }
+        if (lys_compile_node_uniqness(ctx, ctx->mod->compiled->data, ctx->mod->compiled->rpcs,
+                                      ctx->mod->compiled->notifs, node->name, node)) {
+            return LY_EVALID;
+        }
+    }
+
+    return LY_SUCCESS;
+
+error:
+    lysc_node_free(ctx->ctx, node);
+    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.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+LY_ERR
+lys_compile_submodule(struct lysc_ctx *ctx, struct lysp_include *inc, int options)
+{
+    unsigned int u;
+    LY_ERR ret = LY_SUCCESS;
+    /* shortcuts */
+    struct lysp_module *submod = inc->submodule;
+    struct lysc_module *mainmod = ctx->mod->compiled;
+
+    COMPILE_ARRAY_UNIQUE_GOTO(ctx, submod->features, mainmod->features, options, u, lys_compile_feature, ret, error);
+    COMPILE_ARRAY_UNIQUE_GOTO(ctx, submod->identities, mainmod->identities, options, u, lys_compile_identity, ret, error);
+
+error:
+    return ret;
+}
+
+/**
+ * @brief Compile the given YANG module.
+ * @param[in] mod Module structure where the parsed schema is expected and the compiled schema will be placed.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+LY_ERR
+lys_compile(struct lys_module *mod, int options)
+{
+    struct lysc_ctx ctx = {0};
+    struct lysc_module *mod_c;
+    struct lysc_type *type, *typeiter;
+    struct lysp_module *sp;
+    struct lysp_node *node_p;
+    unsigned int u, v;
+    LY_ERR ret = LY_SUCCESS;
+
+    LY_CHECK_ARG_RET(NULL, mod, mod->parsed, mod->parsed->ctx, LY_EINVAL);
+    sp = mod->parsed;
+
+    if (sp->submodule) {
+        LOGERR(sp->ctx, LY_EINVAL, "Submodules (%s) are not supposed to be compiled, compile only the main modules.", sp->name);
+        return LY_EINVAL;
+    }
+
+    ctx.ctx = sp->ctx;
+    ctx.mod = mod;
+    ctx.mod_def = mod;
+
+    mod->compiled = mod_c = calloc(1, sizeof *mod_c);
+    LY_CHECK_ERR_RET(!mod_c, LOGMEM(sp->ctx), LY_EMEM);
+    mod_c->ctx = sp->ctx;
+    mod_c->implemented = sp->implemented;
+    mod_c->latest_revision = sp->latest_revision;
+    mod_c->version = sp->version;
+
+    DUP_STRING(sp->ctx, sp->name, mod_c->name);
+    DUP_STRING(sp->ctx, sp->ns, mod_c->ns);
+    DUP_STRING(sp->ctx, sp->prefix, mod_c->prefix);
+    if (sp->revs) {
+        DUP_STRING(sp->ctx, sp->revs[0].date, mod_c->revision);
+    }
+    COMPILE_ARRAY_GOTO(&ctx, sp->imports, mod_c->imports, options, u, lys_compile_import, ret, error);
+    LY_ARRAY_FOR(sp->includes, u) {
+        ret = lys_compile_submodule(&ctx, &sp->includes[u], options);
+        LY_CHECK_GOTO(ret != LY_SUCCESS, error);
+    }
+    COMPILE_ARRAY_UNIQUE_GOTO(&ctx, sp->features, mod_c->features, options, u, lys_compile_feature, ret, error);
+    COMPILE_ARRAY_UNIQUE_GOTO(&ctx, sp->identities, mod_c->identities, options, u, lys_compile_identity, ret, error);
+    if (sp->identities) {
+        LY_CHECK_RET(lys_compile_identities_derived(&ctx, sp->identities, mod_c->identities));
+    }
+
+    LY_LIST_FOR(sp->data, node_p) {
+        ret = lys_compile_node(&ctx, node_p, options, NULL);
+        LY_CHECK_GOTO(ret, error);
+    }
+    //COMPILE_ARRAY_GOTO(ctx, sp->rpcs, mod_c->rpcs, options, u, lys_compile_action, ret, error);
+    //COMPILE_ARRAY_GOTO(ctx, sp->notifs, mod_c->notifs, options, u, lys_compile_notif, ret, error);
+
+    COMPILE_ARRAY_GOTO(&ctx, sp->exts, mod_c->exts, options, u, lys_compile_ext, ret, error);
+
+    /* validate leafref's paths and when/must xpaths */
+    /* for leafref, we need 2 rounds - first detects circular chain by storing the first referred type (which
+     * can be also leafref, in case it is already resolved, go through the chain and check that it does not
+     * point to the starting leafref type). The second round stores the first non-leafref type for later data validation. */
+    for (u = 0; u < ctx.unres.count; ++u) {
+        if (((struct lysc_node*)ctx.unres.objs[u])->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+            type = ((struct lysc_node_leaf*)ctx.unres.objs[u])->type;
+            if (type->basetype == LY_TYPE_LEAFREF) {
+                /* validate the path */
+                ret = lys_compile_leafref_validate(&ctx, ((struct lysc_node*)ctx.unres.objs[u]), (struct lysc_type_leafref*)type);
+                LY_CHECK_GOTO(ret, error);
+            } else if (type->basetype == LY_TYPE_UNION) {
+                LY_ARRAY_FOR(((struct lysc_type_union*)type)->types, v) {
+                    if (((struct lysc_type_union*)type)->types[v]->basetype == LY_TYPE_LEAFREF) {
+                        /* validate the path */
+                        ret = lys_compile_leafref_validate(&ctx, ((struct lysc_node*)ctx.unres.objs[u]),
+                                                           (struct lysc_type_leafref*)((struct lysc_type_union*)type)->types[v]);
+                        LY_CHECK_GOTO(ret, error);
+                    }
+                }
+            }
+        }
+    }
+    for (u = 0; u < ctx.unres.count; ++u) {
+        if (((struct lysc_node*)ctx.unres.objs[u])->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
+            type = ((struct lysc_node_leaf*)ctx.unres.objs[u])->type;
+            if (type->basetype == LY_TYPE_LEAFREF) {
+                /* store pointer to the real type */
+                for (typeiter = ((struct lysc_type_leafref*)type)->realtype;
+                        typeiter->basetype == LY_TYPE_LEAFREF;
+                        typeiter = ((struct lysc_type_leafref*)typeiter)->realtype);
+                ((struct lysc_type_leafref*)type)->realtype = typeiter;
+            } else if (type->basetype == LY_TYPE_UNION) {
+                LY_ARRAY_FOR(((struct lysc_type_union*)type)->types, v) {
+                    if (((struct lysc_type_union*)type)->types[v]->basetype == LY_TYPE_LEAFREF) {
+                        /* store pointer to the real type */
+                        for (typeiter = ((struct lysc_type_leafref*)((struct lysc_type_union*)type)->types[v])->realtype;
+                                typeiter->basetype == LY_TYPE_LEAFREF;
+                                typeiter = ((struct lysc_type_leafref*)typeiter)->realtype);
+                        ((struct lysc_type_leafref*)((struct lysc_type_union*)type)->types[v])->realtype = typeiter;
+                    }
+                }
+            }
+        }
+    }
+    ly_set_erase(&ctx.unres, NULL);
+    ly_set_erase(&ctx.groupings, NULL);
+
+    if (options & LYSC_OPT_FREE_SP) {
+        lysp_module_free(mod->parsed);
+        ((struct lys_module*)mod)->parsed = NULL;
+    }
+
+    ((struct lys_module*)mod)->compiled = mod_c;
+    return LY_SUCCESS;
+
+error:
+    ly_set_erase(&ctx.unres, NULL);
+    ly_set_erase(&ctx.groupings, NULL);
+    lysc_module_free(mod_c, NULL);
+    ((struct lys_module*)mod)->compiled = NULL;
+    return ret;
+}
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
new file mode 100644
index 0000000..7dbe83c
--- /dev/null
+++ b/src/tree_schema_free.c
@@ -0,0 +1,750 @@
+/**
+ * @file tree_schema.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Schema tree implementation
+ *
+ * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "common.h"
+
+#include "libyang.h"
+#include "context.h"
+#include "tree_schema_internal.h"
+#include "xpath.h"
+
+#define FREE_ARRAY(CTX, ARRAY, FUNC) {uint64_t c__; LY_ARRAY_FOR(ARRAY, c__){FUNC(CTX, &(ARRAY)[c__]);}LY_ARRAY_FREE(ARRAY);}
+#define FREE_MEMBER(CTX, MEMBER, FUNC) if (MEMBER) {FUNC(CTX, MEMBER);free(MEMBER);}
+#define FREE_STRINGS(CTX, ARRAY) {uint64_t c__; LY_ARRAY_FOR(ARRAY, c__){FREE_STRING(CTX, ARRAY[c__]);}LY_ARRAY_FREE(ARRAY);}
+
+static void lysp_grp_free(struct ly_ctx *ctx, struct lysp_grp *grp);
+static void lysp_node_free(struct ly_ctx *ctx, struct lysp_node *node);
+
+static void
+lysp_stmt_free(struct ly_ctx *ctx, struct lysp_stmt *stmt)
+{
+    struct lysp_stmt *child, *next;
+
+    FREE_STRING(ctx, stmt->stmt);
+    FREE_STRING(ctx, stmt->arg);
+
+    LY_LIST_FOR_SAFE(stmt->child, next, child) {
+        lysp_stmt_free(ctx, child);
+    }
+
+    free(stmt);
+}
+
+static void
+lysp_ext_instance_free(struct ly_ctx *ctx, struct lysp_ext_instance *ext)
+{
+    struct lysp_stmt *stmt, *next;
+
+    FREE_STRING(ctx, ext->name);
+    FREE_STRING(ctx, ext->argument);
+
+    LY_LIST_FOR_SAFE(ext->child, next, stmt) {
+        lysp_stmt_free(ctx, stmt);
+    }
+}
+
+static void
+lysp_import_free(struct ly_ctx *ctx, struct lysp_import *import)
+{
+    /* imported module is freed directly from the context's list */
+    FREE_STRING(ctx, import->name);
+    FREE_STRING(ctx, import->prefix);
+    FREE_STRING(ctx, import->dsc);
+    FREE_STRING(ctx, import->ref);
+    FREE_ARRAY(ctx, import->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_include_free(struct ly_ctx *ctx, struct lysp_include *include)
+{
+    if (include->submodule) {
+        lysp_module_free(include->submodule);
+    }
+    FREE_STRING(ctx, include->name);
+    FREE_STRING(ctx, include->dsc);
+    FREE_STRING(ctx, include->ref);
+    FREE_ARRAY(ctx, include->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_revision_free(struct ly_ctx *ctx, struct lysp_revision *rev)
+{
+    FREE_STRING(ctx, rev->dsc);
+    FREE_STRING(ctx, rev->ref);
+    FREE_ARRAY(ctx, rev->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_ext_free(struct ly_ctx *ctx, struct lysp_ext *ext)
+{
+    FREE_STRING(ctx, ext->name);
+    FREE_STRING(ctx, ext->argument);
+    FREE_STRING(ctx, ext->dsc);
+    FREE_STRING(ctx, ext->ref);
+    FREE_ARRAY(ctx, ext->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_feature_free(struct ly_ctx *ctx, struct lysp_feature *feat)
+{
+    FREE_STRING(ctx, feat->name);
+    FREE_STRINGS(ctx, feat->iffeatures);
+    FREE_STRING(ctx, feat->dsc);
+    FREE_STRING(ctx, feat->ref);
+    FREE_ARRAY(ctx, feat->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_ident_free(struct ly_ctx *ctx, struct lysp_ident *ident)
+{
+    FREE_STRING(ctx, ident->name);
+    FREE_STRINGS(ctx, ident->iffeatures);
+    FREE_STRINGS(ctx, ident->bases);
+    FREE_STRING(ctx, ident->dsc);
+    FREE_STRING(ctx, ident->ref);
+    FREE_ARRAY(ctx, ident->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_restr_free(struct ly_ctx *ctx, struct lysp_restr *restr)
+{
+    FREE_STRING(ctx, restr->arg);
+    FREE_STRING(ctx, restr->emsg);
+    FREE_STRING(ctx, restr->eapptag);
+    FREE_STRING(ctx, restr->dsc);
+    FREE_STRING(ctx, restr->ref);
+    FREE_ARRAY(ctx, restr->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_type_enum_free(struct ly_ctx *ctx, struct lysp_type_enum *item)
+{
+    FREE_STRING(ctx, item->name);
+    FREE_STRING(ctx, item->dsc);
+    FREE_STRING(ctx, item->ref);
+    FREE_STRINGS(ctx, item->iffeatures);
+    FREE_ARRAY(ctx, item->exts, lysp_ext_instance_free);
+}
+
+void lysc_type_free(struct ly_ctx *ctx, struct lysc_type *type);
+static void
+lysp_type_free(struct ly_ctx *ctx, struct lysp_type *type)
+{
+    FREE_STRING(ctx, type->name);
+    FREE_MEMBER(ctx, type->range, lysp_restr_free);
+    FREE_MEMBER(ctx, type->length, lysp_restr_free);
+    FREE_ARRAY(ctx, type->patterns, lysp_restr_free);
+    FREE_ARRAY(ctx, type->enums, lysp_type_enum_free);
+    FREE_ARRAY(ctx, type->bits, lysp_type_enum_free);
+    FREE_STRING(ctx, type->path);
+    FREE_STRINGS(ctx, type->bases);
+    FREE_ARRAY(ctx, type->types, lysp_type_free);
+    FREE_ARRAY(ctx, type->exts, lysp_ext_instance_free);
+    if (type->compiled) {
+        lysc_type_free(ctx, type->compiled);
+    }
+}
+
+static void
+lysp_tpdf_free(struct ly_ctx *ctx, struct lysp_tpdf *tpdf)
+{
+    FREE_STRING(ctx, tpdf->name);
+    FREE_STRING(ctx, tpdf->units);
+    FREE_STRING(ctx, tpdf->dflt);
+    FREE_STRING(ctx, tpdf->dsc);
+    FREE_STRING(ctx, tpdf->ref);
+    FREE_ARRAY(ctx, tpdf->exts, lysp_ext_instance_free);
+
+    lysp_type_free(ctx, &tpdf->type);
+
+}
+
+static void
+lysp_action_inout_free(struct ly_ctx *ctx, struct lysp_action_inout *inout)
+{
+    struct lysp_node *node, *next;
+
+    FREE_ARRAY(ctx, inout->musts, lysp_restr_free);
+    FREE_ARRAY(ctx, inout->typedefs, lysp_tpdf_free);
+    FREE_ARRAY(ctx, inout->groupings, lysp_grp_free);
+    LY_LIST_FOR_SAFE(inout->data, next, node) {
+        lysp_node_free(ctx, node);
+    }
+    FREE_ARRAY(ctx, inout->exts, lysp_ext_instance_free);
+
+}
+
+static void
+lysp_action_free(struct ly_ctx *ctx, struct lysp_action *action)
+{
+    FREE_STRING(ctx, action->name);
+    FREE_STRING(ctx, action->dsc);
+    FREE_STRING(ctx, action->ref);
+    FREE_STRINGS(ctx, action->iffeatures);
+    FREE_ARRAY(ctx, action->typedefs, lysp_tpdf_free);
+    FREE_ARRAY(ctx, action->groupings, lysp_grp_free);
+    FREE_MEMBER(ctx, action->input, lysp_action_inout_free);
+    FREE_MEMBER(ctx, action->output, lysp_action_inout_free);
+    FREE_ARRAY(ctx, action->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_notif_free(struct ly_ctx *ctx, struct lysp_notif *notif)
+{
+    struct lysp_node *node, *next;
+
+    FREE_STRING(ctx, notif->name);
+    FREE_STRING(ctx, notif->dsc);
+    FREE_STRING(ctx, notif->ref);
+    FREE_STRINGS(ctx, notif->iffeatures);
+    FREE_ARRAY(ctx, notif->musts, lysp_restr_free);
+    FREE_ARRAY(ctx, notif->typedefs, lysp_tpdf_free);
+    FREE_ARRAY(ctx, notif->groupings, lysp_grp_free);
+    LY_LIST_FOR_SAFE(notif->data, next, node) {
+        lysp_node_free(ctx, node);
+    }
+    FREE_ARRAY(ctx, notif->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_grp_free(struct ly_ctx *ctx, struct lysp_grp *grp)
+{
+    struct lysp_node *node, *next;
+
+    FREE_STRING(ctx, grp->name);
+    FREE_STRING(ctx, grp->dsc);
+    FREE_STRING(ctx, grp->ref);
+    FREE_ARRAY(ctx, grp->typedefs, lysp_tpdf_free);
+    FREE_ARRAY(ctx, grp->groupings, lysp_grp_free);
+    LY_LIST_FOR_SAFE(grp->data, next, node) {
+        lysp_node_free(ctx, node);
+    }
+    FREE_ARRAY(ctx, grp->actions, lysp_action_free);
+    FREE_ARRAY(ctx, grp->notifs, lysp_notif_free);
+    FREE_ARRAY(ctx, grp->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_when_free(struct ly_ctx *ctx, struct lysp_when *when)
+{
+    FREE_STRING(ctx, when->cond);
+    FREE_STRING(ctx, when->dsc);
+    FREE_STRING(ctx, when->ref);
+    FREE_ARRAY(ctx, when->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_augment_free(struct ly_ctx *ctx, struct lysp_augment *augment)
+{
+    struct lysp_node *node, *next;
+
+    FREE_STRING(ctx, augment->nodeid);
+    FREE_STRING(ctx, augment->dsc);
+    FREE_STRING(ctx, augment->ref);
+    FREE_MEMBER(ctx, augment->when, lysp_when_free);
+    FREE_STRINGS(ctx, augment->iffeatures);
+    LY_LIST_FOR_SAFE(augment->child, next, node) {
+        lysp_node_free(ctx, node);
+    }
+    FREE_ARRAY(ctx, augment->actions, lysp_action_free);
+    FREE_ARRAY(ctx, augment->notifs, lysp_notif_free);
+    FREE_ARRAY(ctx, augment->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_deviate_free(struct ly_ctx *ctx, struct lysp_deviate *d)
+{
+    struct lysp_deviate_add *add = (struct lysp_deviate_add*)d;
+    struct lysp_deviate_rpl *rpl = (struct lysp_deviate_rpl*)d;
+
+    FREE_ARRAY(ctx, d->exts, lysp_ext_instance_free);
+    switch(d->mod) {
+    case LYS_DEV_NOT_SUPPORTED:
+        /* nothing to do */
+        break;
+    case LYS_DEV_ADD:
+    case LYS_DEV_DELETE: /* compatible for dynamically allocated data */
+        FREE_STRING(ctx, add->units);
+        FREE_ARRAY(ctx, add->musts, lysp_restr_free);
+        FREE_STRINGS(ctx, add->uniques);
+        FREE_STRINGS(ctx, add->dflts);
+        break;
+    case LYS_DEV_REPLACE:
+        FREE_MEMBER(ctx, rpl->type, lysp_type_free);
+        FREE_STRING(ctx, rpl->units);
+        FREE_STRING(ctx, rpl->dflt);
+        break;
+    default:
+        LOGINT(ctx);
+        break;
+    }
+}
+
+static void
+lysp_deviation_free(struct ly_ctx *ctx, struct lysp_deviation *dev)
+{
+    struct lysp_deviate *next, *iter;
+
+    FREE_STRING(ctx, dev->nodeid);
+    FREE_STRING(ctx, dev->dsc);
+    FREE_STRING(ctx, dev->ref);
+    LY_LIST_FOR_SAFE(dev->deviates, next, iter) {
+        lysp_deviate_free(ctx, iter);
+        free(iter);
+    }
+    FREE_ARRAY(ctx, dev->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_refine_free(struct ly_ctx *ctx, struct lysp_refine *ref)
+{
+    FREE_STRING(ctx, ref->nodeid);
+    FREE_STRING(ctx, ref->dsc);
+    FREE_STRING(ctx, ref->ref);
+    FREE_STRINGS(ctx, ref->iffeatures);
+    FREE_ARRAY(ctx, ref->musts, lysp_restr_free);
+    FREE_STRING(ctx, ref->presence);
+    FREE_STRINGS(ctx, ref->dflts);
+    FREE_ARRAY(ctx, ref->exts, lysp_ext_instance_free);
+}
+
+static void
+lysp_node_free(struct ly_ctx *ctx, struct lysp_node *node)
+{
+    struct lysp_node *child, *next;
+
+    FREE_STRING(ctx, node->name);
+    FREE_STRING(ctx, node->dsc);
+    FREE_STRING(ctx, node->ref);
+    FREE_MEMBER(ctx, node->when, lysp_when_free);
+    FREE_STRINGS(ctx, node->iffeatures);
+    FREE_ARRAY(ctx, node->exts, lysp_ext_instance_free);
+
+    switch(node->nodetype) {
+    case LYS_CONTAINER:
+        FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->musts, lysp_restr_free);
+        FREE_STRING(ctx, ((struct lysp_node_container*)node)->presence);
+        FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->typedefs, lysp_tpdf_free);
+        FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->groupings, lysp_grp_free);
+        LY_LIST_FOR_SAFE(((struct lysp_node_container*)node)->child, next, child) {
+            lysp_node_free(ctx, child);
+        }
+        FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->actions, lysp_action_free);
+        FREE_ARRAY(ctx, ((struct lysp_node_container*)node)->notifs, lysp_notif_free);
+        break;
+    case LYS_LEAF:
+        FREE_ARRAY(ctx, ((struct lysp_node_leaf*)node)->musts, lysp_restr_free);
+        lysp_type_free(ctx, &((struct lysp_node_leaf*)node)->type);
+        FREE_STRING(ctx, ((struct lysp_node_leaf*)node)->units);
+        FREE_STRING(ctx, ((struct lysp_node_leaf*)node)->dflt);
+        break;
+    case LYS_LEAFLIST:
+        FREE_ARRAY(ctx, ((struct lysp_node_leaflist*)node)->musts, lysp_restr_free);
+        lysp_type_free(ctx, &((struct lysp_node_leaflist*)node)->type);
+        FREE_STRING(ctx, ((struct lysp_node_leaflist*)node)->units);
+        FREE_STRINGS(ctx, ((struct lysp_node_leaflist*)node)->dflts);
+        break;
+    case LYS_LIST:
+        FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->musts, lysp_restr_free);
+        FREE_STRING(ctx, ((struct lysp_node_list*)node)->key);
+        FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->typedefs, lysp_tpdf_free);
+        FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->groupings,  lysp_grp_free);
+        LY_LIST_FOR_SAFE(((struct lysp_node_list*)node)->child, next, child) {
+            lysp_node_free(ctx, child);
+        }
+        FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->actions, lysp_action_free);
+        FREE_ARRAY(ctx, ((struct lysp_node_list*)node)->notifs, lysp_notif_free);
+        FREE_STRINGS(ctx, ((struct lysp_node_list*)node)->uniques);
+        break;
+    case LYS_CHOICE:
+        LY_LIST_FOR_SAFE(((struct lysp_node_choice*)node)->child, next, child) {
+            lysp_node_free(ctx, child);
+        }
+        FREE_STRING(ctx, ((struct lysp_node_choice*)node)->dflt);
+        break;
+    case LYS_CASE:
+        LY_LIST_FOR_SAFE(((struct lysp_node_case*)node)->child, next, child) {
+            lysp_node_free(ctx, child);
+        }
+        break;
+    case LYS_ANYDATA:
+    case LYS_ANYXML:
+        FREE_ARRAY(ctx, ((struct lysp_node_anydata*)node)->musts, lysp_restr_free);
+        break;
+    case LYS_USES:
+        FREE_ARRAY(ctx, ((struct lysp_node_uses*)node)->refines, lysp_refine_free);
+        FREE_ARRAY(ctx, ((struct lysp_node_uses*)node)->augments, lysp_augment_free);
+        break;
+    default:
+        LOGINT(ctx);
+    }
+
+    free(node);
+}
+
+API void
+lysp_module_free(struct lysp_module *module)
+{
+    struct ly_ctx *ctx;
+    struct lysp_node *node, *next;
+
+    LY_CHECK_ARG_RET(NULL, module,);
+    ctx = module->ctx;
+
+    FREE_STRING(ctx, module->name);
+    FREE_STRING(ctx, module->filepath);
+    FREE_STRING(ctx, module->ns);  /* or belongs-to */
+    FREE_STRING(ctx, module->prefix);
+
+    FREE_ARRAY(ctx, module->imports, lysp_import_free);
+    FREE_ARRAY(ctx, module->includes, lysp_include_free);
+
+    FREE_STRING(ctx, module->org);
+    FREE_STRING(ctx, module->contact);
+    FREE_STRING(ctx, module->dsc);
+    FREE_STRING(ctx, module->ref);
+
+    FREE_ARRAY(ctx, module->revs, lysp_revision_free);
+    FREE_ARRAY(ctx, module->extensions, lysp_ext_free);
+    FREE_ARRAY(ctx, module->features, lysp_feature_free);
+    FREE_ARRAY(ctx, module->identities, lysp_ident_free);
+    FREE_ARRAY(ctx, module->typedefs, lysp_tpdf_free);
+    FREE_ARRAY(ctx, module->groupings, lysp_grp_free);
+    LY_LIST_FOR_SAFE(module->data, next, node) {
+        lysp_node_free(ctx, node);
+    }
+    FREE_ARRAY(ctx, module->augments, lysp_augment_free);
+    FREE_ARRAY(ctx, module->rpcs, lysp_action_free);
+    FREE_ARRAY(ctx, module->notifs, lysp_notif_free);
+    FREE_ARRAY(ctx, module->deviations, lysp_deviation_free);
+    FREE_ARRAY(ctx, module->exts, lysp_ext_instance_free);
+
+    free(module);
+}
+
+static void
+lysc_ext_instance_free(struct ly_ctx *ctx, struct lysc_ext_instance *ext)
+{
+    FREE_STRING(ctx, ext->argument);
+    FREE_ARRAY(ctx, ext->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_iffeature_free(struct ly_ctx *UNUSED(ctx), struct lysc_iffeature *iff)
+{
+    LY_ARRAY_FREE(iff->features);
+    free(iff->expr);
+}
+
+static void
+lysc_when_free(struct ly_ctx *ctx, struct lysc_when *w)
+{
+    lyxp_expr_free(ctx, w->cond);
+    FREE_ARRAY(ctx, w->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_must_free(struct ly_ctx *ctx, struct lysc_must *must)
+{
+    lyxp_expr_free(ctx, must->cond);
+    FREE_STRING(ctx, must->emsg);
+    FREE_STRING(ctx, must->eapptag);
+    FREE_ARRAY(ctx, must->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_import_free(struct ly_ctx *ctx, struct lysc_import *import)
+{
+    /* imported module is freed directly from the context's list */
+    FREE_STRING(ctx, import->prefix);
+    FREE_ARRAY(ctx, import->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_ident_free(struct ly_ctx *ctx, struct lysc_ident *ident)
+{
+    FREE_STRING(ctx, ident->name);
+    FREE_ARRAY(ctx, ident->iffeatures, lysc_iffeature_free);
+    LY_ARRAY_FREE(ident->derived);
+    FREE_ARRAY(ctx, ident->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_feature_free(struct ly_ctx *ctx, struct lysc_feature *feat)
+{
+    FREE_STRING(ctx, feat->name);
+    FREE_ARRAY(ctx, feat->iffeatures, lysc_iffeature_free);
+    LY_ARRAY_FREE(feat->depfeatures);
+    FREE_ARRAY(ctx, feat->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_range_free(struct ly_ctx *ctx, struct lysc_range *range)
+{
+    LY_ARRAY_FREE(range->parts);
+    FREE_STRING(ctx, range->eapptag);
+    FREE_STRING(ctx, range->emsg);
+    FREE_ARRAY(ctx, range->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_pattern_free(struct ly_ctx *ctx, struct lysc_pattern **pattern)
+{
+    if (--(*pattern)->refcount) {
+        return;
+    }
+    pcre_free((*pattern)->expr);
+    pcre_free_study((*pattern)->expr_extra);
+    FREE_STRING(ctx, (*pattern)->eapptag);
+    FREE_STRING(ctx, (*pattern)->emsg);
+    FREE_ARRAY(ctx, (*pattern)->exts, lysc_ext_instance_free);
+    free(*pattern);
+}
+
+static void
+lysc_enum_item_free(struct ly_ctx *ctx, struct lysc_type_enum_item *item)
+{
+    FREE_STRING(ctx, item->name);
+    FREE_ARRAY(ctx, item->iffeatures, lysc_iffeature_free);
+    FREE_ARRAY(ctx, item->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_type2_free(struct ly_ctx *ctx, struct lysc_type **type)
+{
+    lysc_type_free(ctx, *type);
+}
+void
+lysc_type_free(struct ly_ctx *ctx, struct lysc_type *type)
+{
+    if (--type->refcount) {
+        return;
+    }
+    switch(type->basetype) {
+    case LY_TYPE_BINARY:
+        FREE_MEMBER(ctx, ((struct lysc_type_bin*)type)->length, lysc_range_free);
+        break;
+    case LY_TYPE_BITS:
+        FREE_ARRAY(ctx, (struct lysc_type_enum_item*)((struct lysc_type_bits*)type)->bits, lysc_enum_item_free);
+        break;
+    case LY_TYPE_DEC64:
+        FREE_MEMBER(ctx, ((struct lysc_type_dec*)type)->range, lysc_range_free);
+        break;
+    case LY_TYPE_STRING:
+        FREE_MEMBER(ctx, ((struct lysc_type_str*)type)->length, lysc_range_free);
+        FREE_ARRAY(ctx, ((struct lysc_type_str*)type)->patterns, lysc_pattern_free);
+        break;
+    case LY_TYPE_ENUM:
+        FREE_ARRAY(ctx, ((struct lysc_type_enum*)type)->enums, lysc_enum_item_free);
+        break;
+    case LY_TYPE_INT8:
+    case LY_TYPE_UINT8:
+    case LY_TYPE_INT16:
+    case LY_TYPE_UINT16:
+    case LY_TYPE_INT32:
+    case LY_TYPE_UINT32:
+    case LY_TYPE_INT64:
+    case LY_TYPE_UINT64:
+        FREE_MEMBER(ctx, ((struct lysc_type_num*)type)->range, lysc_range_free);
+        break;
+    case LY_TYPE_IDENT:
+        LY_ARRAY_FREE(((struct lysc_type_identityref*)type)->bases);
+        break;
+    case LY_TYPE_UNION:
+        FREE_ARRAY(ctx, ((struct lysc_type_union*)type)->types, lysc_type2_free);
+        break;
+    case LY_TYPE_LEAFREF:
+        FREE_STRING(ctx, ((struct lysc_type_leafref*)type)->path);
+        break;
+    case LY_TYPE_INST:
+    case LY_TYPE_BOOL:
+    case LY_TYPE_EMPTY:
+    case LY_TYPE_UNKNOWN:
+        /* nothing to do */
+        break;
+    }
+    FREE_ARRAY(ctx, type->exts, lysc_ext_instance_free);
+
+    free(type);
+}
+
+static void
+lysc_node_container_free(struct ly_ctx *ctx, struct lysc_node_container *node)
+{
+    struct lysc_node *child, *child_next;
+
+    LY_LIST_FOR_SAFE(node->child, child_next, child) {
+        lysc_node_free(ctx, child);
+    }
+    FREE_ARRAY(ctx, node->musts, lysc_must_free);
+
+    /* TODO actions, notifs */
+}
+
+static void
+lysc_node_leaf_free(struct ly_ctx *ctx, struct lysc_node_leaf *node)
+{
+    FREE_ARRAY(ctx, node->musts, lysc_must_free);
+    if (node->type) {
+        lysc_type_free(ctx, node->type);
+    }
+    FREE_STRING(ctx, node->units);
+    FREE_STRING(ctx, node->dflt);
+}
+
+static void
+lysc_node_leaflist_free(struct ly_ctx *ctx, struct lysc_node_leaflist *node)
+{
+    unsigned int u;
+
+    FREE_ARRAY(ctx, node->musts, lysc_must_free);
+    if (node->type) {
+        lysc_type_free(ctx, node->type);
+    }
+    FREE_STRING(ctx, node->units);
+    LY_ARRAY_FOR(node->dflts, u) {
+        lydict_remove(ctx, node->dflts[u]);
+    }
+    LY_ARRAY_FREE(node->dflts);
+}
+
+static void
+lysc_node_list_free(struct ly_ctx *ctx, struct lysc_node_list *node)
+{
+    unsigned int u;
+    struct lysc_node *child, *child_next;
+
+    LY_LIST_FOR_SAFE(node->child, child_next, child) {
+        lysc_node_free(ctx, child);
+    }
+    FREE_ARRAY(ctx, node->musts, lysc_must_free);
+
+    LY_ARRAY_FREE(node->keys);
+    LY_ARRAY_FOR(node->uniques, u) {
+        LY_ARRAY_FREE(node->uniques[u]);
+    }
+    LY_ARRAY_FREE(node->uniques);
+
+    /* 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);
+        }
+    }
+}
+
+static void
+lysc_node_anydata_free(struct ly_ctx *ctx, struct lysc_node_anydata *node)
+{
+    FREE_ARRAY(ctx, node->musts, lysc_must_free);
+}
+
+void
+lysc_node_free(struct ly_ctx *ctx, struct lysc_node *node)
+{
+    /* common part */
+    FREE_STRING(ctx, node->name);
+
+    /* nodetype-specific part */
+    switch(node->nodetype) {
+    case LYS_CONTAINER:
+        lysc_node_container_free(ctx, (struct lysc_node_container*)node);
+        break;
+    case LYS_LEAF:
+        lysc_node_leaf_free(ctx, (struct lysc_node_leaf*)node);
+        break;
+    case LYS_LEAFLIST:
+        lysc_node_leaflist_free(ctx, (struct lysc_node_leaflist*)node);
+        break;
+    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;
+    case LYS_ANYDATA:
+    case LYS_ANYXML:
+        lysc_node_anydata_free(ctx, (struct lysc_node_anydata*)node);
+        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);
+}
+
+static void
+lysc_module_free_(struct lysc_module *module)
+{
+    struct ly_ctx *ctx;
+    struct lysc_node *node, *node_next;
+
+    LY_CHECK_ARG_RET(NULL, module,);
+    ctx = module->ctx;
+
+    FREE_STRING(ctx, module->name);
+    FREE_STRING(ctx, module->ns);
+    FREE_STRING(ctx, module->prefix);
+    FREE_STRING(ctx, module->revision);
+
+    FREE_ARRAY(ctx, module->imports, lysc_import_free);
+    FREE_ARRAY(ctx, module->features, lysc_feature_free);
+    FREE_ARRAY(ctx, module->identities, lysc_ident_free);
+
+    LY_LIST_FOR_SAFE(module->data, node_next, node) {
+        lysc_node_free(ctx, node);
+    }
+
+    FREE_ARRAY(ctx, module->exts, lysc_ext_instance_free);
+
+    free(module);
+}
+
+void
+lysc_module_free(struct lysc_module *module, void (*private_destructor)(const struct lysc_node *node, void *priv))
+{
+    if (module) {
+        lysc_module_free_(module);
+    }
+}
+
+void
+lys_module_free(struct lys_module *module, void (*private_destructor)(const struct lysc_node *node, void *priv))
+{
+    if (!module) {
+        return;
+    }
+
+    lysc_module_free(module->compiled, private_destructor);
+    lysp_module_free(module->parsed);
+    free(module);
+}
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 41a1adb..66fcf37 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -11,16 +11,136 @@
  *
  *     https://opensource.org/licenses/BSD-3-Clause
  */
-#define _XOPEN_SOURCE
+#include "common.h"
 
 #include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <limits.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 #include <time.h>
 
 #include "libyang.h"
-#include "common.h"
 #include "tree_schema_internal.h"
 
+/**
+ * @brief Parse an identifier.
+ *
+ * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
+ * identifier          = (ALPHA / "_")
+ *                       *(ALPHA / DIGIT / "_" / "-" / ".")
+ *
+ * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
+ * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
+ */
+static LY_ERR
+lys_parse_id(const char **id)
+{
+    assert(id && *id);
+
+    if (!is_yangidentstartchar(**id)) {
+        return LY_EINVAL;
+    }
+    ++(*id);
+
+    while (is_yangidentchar(**id)) {
+        ++(*id);
+    }
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lys_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
+{
+    assert(id && *id);
+    assert(prefix && prefix_len);
+    assert(name && name_len);
+
+    *prefix = *id;
+    *prefix_len = 0;
+    *name = NULL;
+    *name_len = 0;
+
+    LY_CHECK_RET(lys_parse_id(id));
+    if (**id == ':') {
+        /* there is prefix */
+        *prefix_len = *id - *prefix;
+        ++(*id);
+        *name = *id;
+
+        LY_CHECK_RET(lys_parse_id(id));
+        *name_len = *id - *name;
+    } else {
+        /* there is no prefix, so what we have as prefix now is actually the name */
+        *name = *prefix;
+        *name_len = *id - *name;
+        *prefix = NULL;
+    }
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lys_resolve_descendant_schema_nodeid(struct lysc_ctx *ctx, const char *nodeid, size_t nodeid_len, const struct lysc_node *context_node,
+                                     int nodetype, const struct lysc_node **target)
+{
+    LY_ERR ret = LY_EVALID;
+    const char *name, *prefix, *id;
+    const struct lysc_node *context;
+    size_t name_len, prefix_len;
+    const struct lys_module *mod;
+
+    assert(nodeid);
+    assert(context_node);
+    assert(target);
+    *target = NULL;
+
+    id = nodeid;
+    context = context_node;
+    while (*id && (ret = lys_parse_nodeid(&id, &prefix, &prefix_len, &name, &name_len)) == LY_SUCCESS) {
+        if (prefix) {
+            mod = lys_module_find_prefix(context_node->module, prefix, prefix_len);
+            if (!mod) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                       "Invalid descendant-schema-nodeid value \"%.*s\" - prefix \"%.*s\" not defined in module \"%s\".",
+                       id - nodeid, nodeid, prefix_len, prefix, context_node->module->compiled->name);
+                return LY_ENOTFOUND;
+            }
+        } else {
+            mod = context_node->module;
+        }
+        context = lys_child(context, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHCASE);
+        if (!context) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid descendant-schema-nodeid value \"%.*s\" - target node not found.", id - nodeid, nodeid);
+            return LY_ENOTFOUND;
+        }
+        if (nodeid_len && ((size_t)(id - nodeid) >= nodeid_len)) {
+            break;
+        }
+        if (id && *id != '/') {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid descendant-schema-nodeid value \"%.*s\" - missing \"/\" as node-identifier separator.",
+                   id - nodeid + 1, nodeid);
+            return LY_EVALID;
+        }
+        ++id;
+    }
+
+    if (ret == LY_SUCCESS) {
+        *target = context;
+        if (nodetype && !(context->nodetype & nodetype)) {
+            return LY_EDENIED;
+        }
+    }
+
+    return ret;
+}
+
 LY_ERR
 lysp_check_prefix(struct ly_parser_ctx *ctx, struct lysp_module *module, const char **value)
 {
@@ -44,14 +164,37 @@
 }
 
 LY_ERR
-lysp_check_date(struct ly_ctx *ctx, const char *date, int date_len, const char *stmt)
+lysc_check_status(struct lysc_ctx *ctx,
+                  uint16_t flags1, void *mod1, const char *name1,
+                  uint16_t flags2, void *mod2, const char *name2)
+{
+    uint16_t flg1, flg2;
+
+    flg1 = (flags1 & LYS_STATUS_MASK) ? (flags1 & LYS_STATUS_MASK) : LYS_STATUS_CURR;
+    flg2 = (flags2 & LYS_STATUS_MASK) ? (flags2 & LYS_STATUS_MASK) : LYS_STATUS_CURR;
+
+    if ((flg1 < flg2) && (mod1 == mod2)) {
+        if (ctx) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "A %s definition \"%s\" is not allowed to reference %s definition \"%s\".",
+                   flg1 == LYS_STATUS_CURR ? "current" : "deprecated", name1,
+                   flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", name2);
+        }
+        return LY_EVALID;
+    }
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lysp_check_date(struct ly_parser_ctx *ctx, const char *date, int date_len, const char *stmt)
 {
     int i;
     struct tm tm, tm_;
     char *r;
 
-    LY_CHECK_ARG_RET(ctx, date, LY_EINVAL);
-    LY_CHECK_ERR_RET(date_len != LY_REV_SIZE - 1, LOGARG(ctx, date_len), LY_EINVAL);
+    LY_CHECK_ARG_RET(ctx ? ctx->ctx : NULL, date, LY_EINVAL);
+    LY_CHECK_ERR_RET(date_len != LY_REV_SIZE - 1, LOGARG(ctx ? ctx->ctx : NULL, date_len), LY_EINVAL);
 
     /* check format */
     for (i = 0; i < date_len; i++) {
@@ -81,7 +224,13 @@
     return LY_SUCCESS;
 
 error:
-    LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, date_len, date, stmt);
+    if (stmt) {
+        if (ctx) {
+            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LY_VCODE_INVAL, date_len, date, stmt);
+        } else {
+            LOGVAL(NULL, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, date_len, date, stmt);
+        }
+    }
     return LY_EINVAL;
 }
 
@@ -92,7 +241,7 @@
     struct lysp_revision rev;
 
     for (i = 1, r = 0; revs && i < LY_ARRAY_SIZE(revs); i++) {
-        if (strcmp(revs[i].rev, revs[r].rev) > 0) {
+        if (strcmp(revs[i].date, revs[r].date) > 0) {
             r = i;
         }
     }
@@ -105,213 +254,1078 @@
     }
 }
 
-struct lysc_module *
-lysc_module_find_prefix(struct lysc_module *mod, const char *prefix, size_t len)
+static const struct lysp_tpdf *
+lysp_type_match(const char *name, struct lysp_node *node)
 {
-    struct lysc_import *imp;
+    const struct lysp_tpdf *typedefs;
+    unsigned int u;
 
-    assert(mod);
-
-    if (!strncmp(mod->prefix, prefix, len) && mod->prefix[len] == '\0') {
-        /* it is the prefix of the module itself */
-        return mod;
-    }
-
-    /* search in imports */
-    LY_ARRAY_FOR(mod->imports, struct lysc_import, imp) {
-        if (!strncmp(imp->prefix, prefix, len) && mod->prefix[len] == '\0') {
-            return imp->module;
+    typedefs = lysp_node_typedefs(node);
+    LY_ARRAY_FOR(typedefs, u) {
+        if (!strcmp(name, typedefs[u].name)) {
+            /* match */
+            return &typedefs[u];
         }
     }
 
     return NULL;
 }
 
+static LY_DATA_TYPE
+lysp_type_str2builtin(const char *name, size_t len)
+{
+    if (len >= 4) { /* otherwise it does not match any built-in type */
+        if (name[0] == 'b') {
+            if (name[1] == 'i') {
+                if (len == 6 && !strncmp(&name[2], "nary", 4)) {
+                    return LY_TYPE_BINARY;
+                } else if (len == 4 && !strncmp(&name[2], "ts", 2)) {
+                    return LY_TYPE_BITS;
+                }
+            } else if (len == 7 && !strncmp(&name[1], "oolean", 6)) {
+                return LY_TYPE_BOOL;
+            }
+        } else if (name[0] == 'd') {
+            if (len == 9 && !strncmp(&name[1], "ecimal64", 8)) {
+                return LY_TYPE_DEC64;
+            }
+        } else if (name[0] == 'e') {
+            if (len == 5 && !strncmp(&name[1], "mpty", 4)) {
+                return LY_TYPE_EMPTY;
+            } else if (len == 11 && !strncmp(&name[1], "numeration", 10)) {
+                return LY_TYPE_ENUM;
+            }
+        } else if (name[0] == 'i') {
+            if (name[1] == 'n') {
+                if (len == 4 && !strncmp(&name[2], "t8", 2)) {
+                    return LY_TYPE_INT8;
+                } else if (len == 5) {
+                    if (!strncmp(&name[2], "t16", 3)) {
+                        return LY_TYPE_INT16;
+                    } else if (!strncmp(&name[2], "t32", 3)) {
+                        return LY_TYPE_INT32;
+                    } else if (!strncmp(&name[2], "t64", 3)) {
+                        return LY_TYPE_INT64;
+                    }
+                } else if (len == 19 && !strncmp(&name[2], "stance-identifier", 17)) {
+                    return LY_TYPE_INST;
+                }
+            } else if (len == 11 && !strncmp(&name[1], "dentityref", 10)) {
+                return LY_TYPE_IDENT;
+            }
+        } else if (name[0] == 'l') {
+            if (len == 7 && !strncmp(&name[1], "eafref", 6)) {
+                return LY_TYPE_LEAFREF;
+            }
+        } else if (name[0] == 's') {
+            if (len == 6 && !strncmp(&name[1], "tring", 5)) {
+                return LY_TYPE_STRING;
+            }
+        } else if (name[0] == 'u') {
+            if (name[1] == 'n') {
+                if (len == 5 && !strncmp(&name[2], "ion", 3)) {
+                    return LY_TYPE_UNION;
+                }
+            } else if (name[1] == 'i' && name[2] == 'n' && name[3] == 't') {
+                if (len == 5 && name[4] == '8') {
+                    return LY_TYPE_UINT8;
+                } else if (len == 6) {
+                    if (!strncmp(&name[4], "16", 2)) {
+                        return LY_TYPE_UINT16;
+                    } else if (!strncmp(&name[4], "32", 2)) {
+                        return LY_TYPE_UINT32;
+                    } else if (!strncmp(&name[4], "64", 2)) {
+                        return LY_TYPE_UINT64;
+                    }
+                }
+            }
+        }
+    }
+
+    return LY_TYPE_UNKNOWN;
+}
+
+LY_ERR
+lysp_type_find(const char *id, struct lysp_node *start_node, struct lysp_module *start_module,
+               LY_DATA_TYPE *type, const struct lysp_tpdf **tpdf, struct lysp_node **node, struct lysp_module **module)
+{
+    const char *str, *name;
+    struct lysp_tpdf *typedefs;
+    unsigned int u, v;
+
+    assert(id);
+    assert(start_module);
+    assert(tpdf);
+    assert(node);
+    assert(module);
+
+    *node = NULL;
+    str = strchr(id, ':');
+    if (str) {
+        *module = lysp_module_find_prefix(start_module, id, str - id);
+        name = str + 1;
+        *type = LY_TYPE_UNKNOWN;
+    } else {
+        *module = start_module;
+        name = id;
+
+        /* check for built-in types */
+        *type = lysp_type_str2builtin(name, strlen(name));
+        if (*type) {
+            *tpdf = NULL;
+            return LY_SUCCESS;
+        }
+    }
+    LY_CHECK_RET(!(*module), LY_ENOTFOUND);
+
+    if (start_node && *module == start_module) {
+        /* search typedefs in parent's nodes */
+        *node = start_node;
+        while (*node) {
+            *tpdf = lysp_type_match(name, *node);
+            if (*tpdf) {
+                /* match */
+                return LY_SUCCESS;
+            }
+            *node = (*node)->parent;
+        }
+    }
+
+    /* search in top-level typedefs */
+    if ((*module)->typedefs) {
+        LY_ARRAY_FOR((*module)->typedefs, u) {
+            if (!strcmp(name, (*module)->typedefs[u].name)) {
+                /* match */
+                *tpdf = &(*module)->typedefs[u];
+                return LY_SUCCESS;
+            }
+        }
+    }
+
+    /* search in submodules' typedefs */
+    LY_ARRAY_FOR((*module)->includes, u) {
+        typedefs = (*module)->includes[u].submodule->typedefs;
+        LY_ARRAY_FOR(typedefs, v) {
+            if (!strcmp(name, typedefs[v].name)) {
+                /* match */
+                *tpdf = &typedefs[v];
+                return LY_SUCCESS;
+            }
+        }
+    }
+
+    return LY_ENOTFOUND;
+}
+
+/*
+ * @brief Check name of a new type to avoid name collisions.
+ *
+ * @param[in] ctx Parser context, module where the type is being defined is taken from here.
+ * @param[in] node Schema node where the type is being defined, NULL in case of a top-level typedef.
+ * @param[in] tpdf Typedef definition to check.
+ * @param[in,out] tpdfs_global Initialized hash table to store temporary data between calls. When the module's
+ *            typedefs are checked, caller is supposed to free the table.
+ * @param[in,out] tpdfs_global Initialized hash table to store temporary data between calls. When the module's
+ *            typedefs are checked, caller is supposed to free the table.
+ * @return LY_EEXIST in case of collision, LY_SUCCESS otherwise.
+ */
+static LY_ERR
+lysp_check_typedef(struct ly_parser_ctx *ctx, struct lysp_node *node, const struct lysp_tpdf *tpdf,
+                   struct hash_table *tpdfs_global, struct hash_table *tpdfs_scoped)
+{
+    struct lysp_node *parent;
+    uint32_t hash;
+    size_t name_len;
+    const char *name;
+    unsigned int u;
+    const struct lysp_tpdf *typedefs;
+
+    assert(ctx);
+    assert(tpdf);
+
+    name = tpdf->name;
+    name_len = strlen(name);
+
+    if (lysp_type_str2builtin(name, name_len)) {
+        LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
+               "Invalid name \"%s\" of typedef - name collision with a built-in type.", name);
+        return LY_EEXIST;
+    }
+
+    /* check locally scoped typedefs (avoid name shadowing) */
+    if (node) {
+        typedefs = lysp_node_typedefs(node);
+        LY_ARRAY_FOR(typedefs, u) {
+            if (&typedefs[u] == tpdf) {
+                break;
+            }
+            if (!strcmp(name, typedefs[u].name)) {
+                LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
+                       "Invalid name \"%s\" of typedef - name collision with sibling type.", name);
+                return LY_EEXIST;
+            }
+        }
+        /* search typedefs in parent's nodes */
+        for (parent = node->parent; parent; parent = node->parent) {
+            if (lysp_type_match(name, parent)) {
+                LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
+                       "Invalid name \"%s\" of typedef - name collision with another scoped type.", name);
+                return LY_EEXIST;
+            }
+        }
+    }
+
+    /* check collision with the top-level typedefs */
+    hash = dict_hash(name, name_len);
+    if (node) {
+        lyht_insert(tpdfs_scoped, &name, hash, NULL);
+        if (!lyht_find(tpdfs_global, &name, hash, NULL)) {
+            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
+                   "Invalid name \"%s\" of typedef - scoped type collide with a top-level type.", name);
+            return LY_EEXIST;
+        }
+    } else {
+        if (lyht_insert(tpdfs_global, &name, hash, NULL)) {
+            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
+                   "Invalid name \"%s\" of typedef - name collision with another top-level type.", name);
+            return LY_EEXIST;
+        }
+        /* it is not necessary to test collision with the scoped types - in lysp_check_typedefs, all the
+         * top-level typedefs are inserted into the tables before the scoped typedefs, so the collision
+         * is detected in the first branch few lines above */
+    }
+
+    return LY_SUCCESS;
+}
+
+static int
+lysp_id_cmp(void *val1, void *val2, int UNUSED(mod), void *UNUSED(cb_data))
+{
+    return !strcmp(val1, val2);
+}
+
+LY_ERR
+lysp_check_typedefs(struct ly_parser_ctx *ctx)
+{
+    struct hash_table *ids_global;
+    struct hash_table *ids_scoped;
+    const struct lysp_tpdf *typedefs;
+    unsigned int i, u;
+    LY_ERR ret = LY_EVALID;
+
+    /* check name collisions - typedefs and groupings */
+    ids_global = lyht_new(8, sizeof(char*), lysp_id_cmp, NULL, 1);
+    ids_scoped = lyht_new(8, sizeof(char*), lysp_id_cmp, NULL, 1);
+    LY_ARRAY_FOR(ctx->mod->typedefs, i) {
+        if (lysp_check_typedef(ctx, NULL, &ctx->mod->typedefs[i], ids_global, ids_scoped)) {
+            goto cleanup;
+        }
+    }
+    LY_ARRAY_FOR(ctx->mod->includes, i) {
+        LY_ARRAY_FOR(ctx->mod->includes[i].submodule->typedefs, u) {
+            if (lysp_check_typedef(ctx, NULL, &ctx->mod->includes[i].submodule->typedefs[u], ids_global, ids_scoped)) {
+                goto cleanup;
+            }
+        }
+    }
+    for (u = 0; u < ctx->tpdfs_nodes.count; ++u) {
+        typedefs = lysp_node_typedefs((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;
+            }
+        }
+    }
+    ret = LY_SUCCESS;
+cleanup:
+    lyht_free(ids_global);
+    lyht_free(ids_scoped);
+    ly_set_erase(&ctx->tpdfs_nodes, NULL);
+
+    return ret;
+}
+
+void
+lys_module_implement(struct lys_module *mod)
+{
+    assert(mod);
+    if (mod->parsed) {
+        mod->parsed->implemented = 1;
+    }
+    if (mod->compiled) {
+        mod->compiled->implemented = 1;
+    }
+}
+
+struct lysp_load_module_check_data {
+    const char *name;
+    const char *revision;
+    const char *path;
+    const char* submoduleof;
+};
+
+static LY_ERR
+lysp_load_module_check(struct ly_ctx *ctx, struct lysp_module *mod, void *data)
+{
+    struct lysp_load_module_check_data *info = data;
+    const char *filename, *dot, *rev;
+    size_t len;
+
+    if (info->name) {
+        /* check name of the parsed model */
+        if (strcmp(info->name, mod->name)) {
+            LOGERR(ctx, LY_EINVAL, "Unexpected module \"%s\" parsed instead of \"%s\").", mod->name, info->name);
+            return LY_EINVAL;
+        }
+    }
+    if (info->revision) {
+        /* check revision of the parsed model */
+        if (!mod->revs || strcmp(info->revision, mod->revs[0].date)) {
+            LOGERR(ctx, LY_EINVAL, "Module \"%s\" parsed with the wrong revision (\"%s\" instead \"%s\").", mod->name,
+                   mod->revs[0].date, info->revision);
+            return LY_EINVAL;
+        }
+    }
+    if (info->submoduleof) {
+        /* check that we have really a submodule */
+        if (!mod->submodule) {
+            /* submodule is not a submodule */
+            LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Included \"%s\" schema from \"%s\" is actually not a submodule.",
+                   mod->name, info->submoduleof);
+            return LY_EVALID;
+        }
+        /* check that the submodule belongs-to our module */
+        if (strcmp(info->submoduleof, mod->belongsto)) {
+            LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Included \"%s\" submodule from \"%s\" belongs-to a different module \"%s\".",
+                   mod->name, info->submoduleof, mod->belongsto);
+            return LY_EVALID;
+        }
+        /* check circular dependency */
+        if (mod->parsing) {
+            LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "A circular dependency (include) for module \"%s\".", mod->name);
+            return LY_EVALID;
+        }
+    }
+    if (info->path) {
+        /* check that name and revision match filename */
+        filename = strrchr(info->path, '/');
+        if (!filename) {
+            filename = info->path;
+        } else {
+            filename++;
+        }
+        /* name */
+        len = strlen(mod->name);
+        rev = strchr(filename, '@');
+        dot = strrchr(info->path, '.');
+        if (strncmp(filename, mod->name, len) ||
+                ((rev && rev != &filename[len]) || (!rev && dot != &filename[len]))) {
+            LOGWRN(ctx, "File name \"%s\" does not match module name \"%s\".", filename, mod->name);
+        }
+        /* revision */
+        if (rev) {
+            len = dot - ++rev;
+            if (!mod->revs || len != 10 || strncmp(mod->revs[0].date, rev, len)) {
+                LOGWRN(ctx, "File name \"%s\" does not match module revision \"%s\".", filename,
+                       mod->revs ? mod->revs[0].date : "none");
+            }
+        }
+    }
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, int implement, struct ly_parser_ctx *main_ctx,
+                     struct lys_module **result)
+{
+    int fd;
+    char *filepath = NULL;
+    LYS_INFORMAT format;
+    struct lys_module *mod = NULL;
+    LY_ERR ret = LY_SUCCESS;
+    struct lysp_load_module_check_data check_data = {0};
+
+    LY_CHECK_RET(lys_search_localfile(ly_ctx_get_searchdirs(ctx), !(ctx->flags & LY_CTX_DISABLE_SEARCHDIR_CWD), name, revision,
+                                      &filepath, &format));
+    LY_CHECK_ERR_RET(!filepath, LOGERR(ctx, LY_ENOTFOUND, "Data model \"%s%s%s\" not found in local searchdirs.",
+                                       name, revision ? "@" : "", revision ? revision : ""), LY_ENOTFOUND);
+
+
+    LOGVRB("Loading schema from \"%s\" file.", filepath);
+
+    /* open the file */
+    fd = open(filepath, O_RDONLY);
+    LY_CHECK_ERR_GOTO(fd < 0, LOGERR(ctx, LY_ESYS, "Unable to open data model file \"%s\" (%s).",
+                                     filepath, strerror(errno)); ret = LY_ESYS, cleanup);
+
+    check_data.name = name;
+    check_data.revision = revision;
+    check_data.path = filepath;
+    mod = lys_parse_fd_(ctx, fd, format, implement, main_ctx,
+                        lysp_load_module_check, &check_data);
+    close(fd);
+    LY_CHECK_ERR_GOTO(!mod, ly_errcode(ctx), cleanup);
+
+    if (!mod->parsed->filepath) {
+        char rpath[PATH_MAX];
+        if (realpath(filepath, rpath) != NULL) {
+            mod->parsed->filepath = lydict_insert(ctx, rpath, 0);
+        } else {
+            mod->parsed->filepath = lydict_insert(ctx, filepath, 0);
+        }
+    }
+
+    *result = mod;
+
+    /* success */
+cleanup:
+    free(filepath);
+    return ret;
+}
+
+LY_ERR
+lysp_load_module(struct ly_ctx *ctx, const char *name, const char *revision, int implement, int require_parsed, struct lys_module **mod)
+{
+    const char *module_data = NULL;
+    LYS_INFORMAT format = LYS_IN_UNKNOWN;
+    void (*module_data_free)(void *module_data, void *user_data) = NULL;
+    struct lysp_load_module_check_data check_data = {0};
+
+    /* try to get the module from the context */
+    if (revision) {
+        *mod = (struct lys_module*)ly_ctx_get_module(ctx, name, revision);
+    } else {
+        *mod = (struct lys_module*)ly_ctx_get_module_latest(ctx, name);
+    }
+
+    if (!(*mod) || (require_parsed && !(*mod)->parsed)) {
+        (*mod) = NULL;
+
+        /* check collision with other implemented revision */
+        if (implement && ly_ctx_get_module_implemented(ctx, name)) {
+            LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE,
+                   "Module \"%s\" is already present in other implemented revision.", name);
+            return LY_EDENIED;
+        }
+
+        /* module not present in the context, get the input data and parse it */
+        if (!(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+search_clb:
+            if (ctx->imp_clb) {
+                if (ctx->imp_clb(name, revision, NULL, NULL, ctx->imp_clb_data,
+                                      &format, &module_data, &module_data_free) == LY_SUCCESS) {
+                    check_data.name = name;
+                    check_data.revision = revision;
+                    *mod = lys_parse_mem_(ctx, module_data, format, implement, NULL,
+                                          lysp_load_module_check, &check_data);
+                    if (module_data_free) {
+                        module_data_free((void*)module_data, ctx->imp_clb_data);
+                    }
+                }
+            }
+            if (!(*mod) && !(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+                goto search_file;
+            }
+        } else {
+search_file:
+            if (!(ctx->flags & LY_CTX_DISABLE_SEARCHDIRS)) {
+                /* module was not received from the callback or there is no callback set */
+                lys_module_localfile(ctx, name, revision, implement, NULL, mod);
+            }
+            if (!(*mod) && (ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+                goto search_clb;
+            }
+        }
+
+        if ((*mod) && !revision && ((*mod)->parsed->latest_revision == 1)) {
+            /* update the latest_revision flag - here we have selected the latest available schema,
+             * consider that even the callback provides correct latest revision */
+            (*mod)->parsed->latest_revision = 2;
+        }
+    } else {
+        /* we have module from the current context */
+        if (implement && (ly_ctx_get_module_implemented(ctx, name) != *mod)) {
+            /* check collision with other implemented revision */
+            LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE,
+                   "Module \"%s\" is already present in other implemented revision.", name);
+            *mod = NULL;
+            return LY_EDENIED;
+        }
+
+        /* circular check */
+        if ((*mod)->parsed && (*mod)->parsed->parsing) {
+            LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "A circular dependency (import) for module \"%s\".", name);
+            *mod = NULL;
+            return LY_EVALID;
+        }
+    }
+    if (!(*mod)) {
+        LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "%s \"%s\" module failed.", implement ? "Loading" : "Importing", name);
+        return LY_EVALID;
+    }
+
+    if (implement) {
+        /* mark the module implemented, check for collision was already done */
+        lys_module_implement(*mod);
+    }
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lysp_load_submodule(struct ly_parser_ctx *ctx, struct lysp_module *mod, struct lysp_include *inc)
+{
+    struct lys_module *submod;
+    const char *submodule_data = NULL;
+    LYS_INFORMAT format = LYS_IN_UNKNOWN;
+    void (*submodule_data_free)(void *module_data, void *user_data) = NULL;
+    struct lysp_load_module_check_data check_data = {0};
+
+    /* submodule not present in the context, get the input data and parse it */
+    if (!(ctx->ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+search_clb:
+        if (ctx->ctx->imp_clb) {
+            if (ctx->ctx->imp_clb(mod->name, NULL, inc->name, inc->rev[0] ? inc->rev : NULL, ctx->ctx->imp_clb_data,
+                                  &format, &submodule_data, &submodule_data_free) == LY_SUCCESS) {
+                check_data.name = inc->name;
+                check_data.revision = inc->rev[0] ? inc->rev : NULL;
+                check_data.submoduleof = mod->name;
+                submod = lys_parse_mem_(ctx->ctx, submodule_data, format, mod->implemented, ctx,
+                                        lysp_load_module_check, &check_data);
+                if (submodule_data_free) {
+                    submodule_data_free((void*)submodule_data, ctx->ctx->imp_clb_data);
+                }
+            }
+        }
+        if (!submod && !(ctx->ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+            goto search_file;
+        }
+    } else {
+search_file:
+        if (!(ctx->ctx->flags & LY_CTX_DISABLE_SEARCHDIRS)) {
+            /* module was not received from the callback or there is no callback set */
+            lys_module_localfile(ctx->ctx, inc->name, inc->rev[0] ? inc->rev : NULL, mod->implemented, ctx, &submod);
+        }
+        if (!submod && (ctx->ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+            goto search_clb;
+        }
+    }
+    if (submod) {
+        if (!inc->rev[0] && (submod->parsed->latest_revision == 1)) {
+            /* update the latest_revision flag - here we have selected the latest available schema,
+             * consider that even the callback provides correct latest revision */
+            submod->parsed->latest_revision = 2;
+        }
+
+        inc->submodule = submod->parsed;
+        free(submod);
+    }
+    if (!inc->submodule) {
+        LOGVAL(ctx->ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Including \"%s\" submodule into \"%s\" failed.", inc->name, mod->name);
+        return LY_EVALID;
+    }
+
+    return LY_SUCCESS;
+}
+
+#define FIND_MODULE(TYPE, MOD, ID) \
+    TYPE *imp; \
+    if (!strncmp((MOD)->prefix, prefix, len) && (MOD)->prefix[len] == '\0') { \
+        /* it is the prefix of the module itself */ \
+        m = ly_ctx_get_module((MOD)->ctx, (MOD)->name, ((struct lysc_module*)(MOD))->revision); \
+    } \
+    /* search in imports */ \
+    if (!m) { \
+        LY_ARRAY_FOR((MOD)->imports, TYPE, imp) { \
+            if (!strncmp(imp->prefix, prefix, len) && (MOD)->prefix[len] == '\0') { \
+                m = imp->module; \
+                break; \
+            } \
+        } \
+    }
+
+struct lysc_module *
+lysc_module_find_prefix(const struct lysc_module *mod, const char *prefix, size_t len)
+{
+    const struct lys_module *m = NULL;
+
+    FIND_MODULE(struct lysc_import, mod, 1);
+    return m ? m->compiled : NULL;
+}
+
+struct lysp_module *
+lysp_module_find_prefix(const struct lysp_module *mod, const char *prefix, size_t len)
+{
+    const struct lys_module *m = NULL;
+
+    FIND_MODULE(struct lysp_import, mod, 1);
+    return m ? m->parsed : NULL;
+}
+
+struct lys_module *
+lys_module_find_prefix(const struct lys_module *mod, const char *prefix, size_t len)
+{
+    const struct lys_module *m = NULL;
+
+    if (mod->compiled) {
+        FIND_MODULE(struct lysc_import, mod->compiled, 1);
+    } else {
+        FIND_MODULE(struct lysp_import, mod->parsed, 2);
+    }
+    return (struct lys_module*)m;
+}
+
+const char *
+lys_nodetype2str(uint16_t nodetype)
+{
+    switch(nodetype) {
+    case LYS_CONTAINER:
+        return "container";
+    case LYS_CHOICE:
+        return "choice";
+    case LYS_LEAF:
+        return "leaf";
+    case LYS_LEAFLIST:
+        return "leaf-list";
+    case LYS_LIST:
+        return "list";
+    case LYS_ANYXML:
+        return "anyxml";
+    case LYS_ANYDATA:
+        return "anydata";
+    default:
+        return "unknown";
+    }
+}
+
+API const struct lysp_tpdf *
+lysp_node_typedefs(const struct lysp_node *node)
+{
+    switch (node->nodetype) {
+    case LYS_CONTAINER:
+        return ((struct lysp_node_container*)node)->typedefs;
+    case LYS_LIST:
+        return ((struct lysp_node_list*)node)->typedefs;
+    case LYS_GROUPING:
+        return ((struct lysp_grp*)node)->typedefs;
+    case LYS_ACTION:
+        return ((struct lysp_action*)node)->typedefs;
+    case LYS_INOUT:
+        return ((struct lysp_action_inout*)node)->typedefs;
+    case LYS_NOTIF:
+        return ((struct lysp_notif*)node)->typedefs;
+    default:
+        return NULL;
+    }
+}
+
+API const struct lysp_grp *
+lysp_node_groupings(const struct lysp_node *node)
+{
+    switch (node->nodetype) {
+    case LYS_CONTAINER:
+        return ((struct lysp_node_container*)node)->groupings;
+    case LYS_LIST:
+        return ((struct lysp_node_list*)node)->groupings;
+    case LYS_GROUPING:
+        return ((struct lysp_grp*)node)->groupings;
+    case LYS_ACTION:
+        return ((struct lysp_action*)node)->groupings;
+    case LYS_INOUT:
+        return ((struct lysp_action_inout*)node)->groupings;
+    case LYS_NOTIF:
+        return ((struct lysp_notif*)node)->groupings;
+    default:
+        return NULL;
+    }
+}
+
+struct lysp_action **
+lysp_node_actions_p(struct lysp_node *node)
+{
+    assert(node);
+    switch (node->nodetype) {
+    case LYS_CONTAINER:
+        return &((struct lysp_node_container*)node)->actions;
+    case LYS_LIST:
+        return &((struct lysp_node_list*)node)->actions;
+    case LYS_GROUPING:
+        return &((struct lysp_grp*)node)->actions;
+    case LYS_AUGMENT:
+        return &((struct lysp_augment*)node)->actions;
+    default:
+        return NULL;
+    }
+}
+
+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_p(struct lysp_node *node)
+{
+    assert(node);
+    switch (node->nodetype) {
+    case LYS_CONTAINER:
+        return &((struct lysp_node_container*)node)->notifs;
+    case LYS_LIST:
+        return &((struct lysp_node_list*)node)->notifs;
+    case LYS_GROUPING:
+        return &((struct lysp_grp*)node)->notifs;
+    case LYS_AUGMENT:
+        return &((struct lysp_augment*)node)->notifs;
+    default:
+        return NULL;
+    }
+}
+
+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_p(struct lysp_node *node)
+{
+    assert(node);
+    switch (node->nodetype) {
+    case LYS_CONTAINER:
+        return &((struct lysp_node_container*)node)->child;
+    case LYS_CHOICE:
+        return &((struct lysp_node_choice*)node)->child;
+    case LYS_LIST:
+        return &((struct lysp_node_list*)node)->child;
+    case LYS_CASE:
+        return &((struct lysp_node_case*)node)->child;
+    case LYS_GROUPING:
+        return &((struct lysp_grp*)node)->data;
+    case LYS_AUGMENT:
+        return &((struct lysp_augment*)node)->child;
+    case LYS_INOUT:
+        return &((struct lysp_action_inout*)node)->data;
+    case LYS_NOTIF:
+        return &((struct lysp_notif*)node)->data;
+    default:
+        return NULL;
+    }
+}
+
+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_p(const struct lysc_node *node)
+{
+    assert(node);
+    switch (node->nodetype) {
+    case LYS_CONTAINER:
+        return &((struct lysc_node_container*)node)->child;
+    case LYS_CHOICE:
+        if (((struct lysc_node_choice*)node)->cases) {
+            return &((struct lysc_node_choice*)node)->cases[0].child;
+        } else {
+            return NULL;
+        }
+    case LYS_LIST:
+        return &((struct lysc_node_list*)node)->child;
+/* TODO
+    case LYS_INOUT:
+        return &((struct lysc_action_inout*)node)->child;
+    case LYS_NOTIF:
+        return &((struct lysc_notif*)node)->child;
+*/
+    default:
+        return NULL;
+    }
+}
+
+API const struct lysc_node *
+lysc_node_children(const struct lysc_node *node)
+{
+    struct lysc_node **children;
+    children = lysc_node_children_p((struct lysc_node*)node);
+    if (children) {
+        return *children;
+    } else {
+        return NULL;
+    }
+}
+
+struct lys_module *
+lysp_find_module(struct ly_ctx *ctx, const struct lysp_module *mod)
+{
+    unsigned int u;
+
+    for (u = 0; u < ctx->list.count; ++u) {
+        if (((struct lys_module*)ctx->list.objs[u])->parsed == mod) {
+            return ((struct lys_module*)ctx->list.objs[u]);
+        }
+    }
+    return NULL;
+}
+
 enum yang_keyword
-match_keyword(char *data)
+match_keyword(const char *data)
 {
 /* TODO make this function usable in get_keyword function */
-#define MOVE_INPUT(DATA, COUNT) (data)+=COUNT;
-#define IF_KW(STR, LEN, STMT) if (!strncmp((data), STR, LEN)) {MOVE_INPUT(data, LEN);kw=STMT;}
-#define IF_KW_PREFIX(STR, LEN) if (!strncmp((data), STR, LEN)) {MOVE_INPUT(data, LEN);
-#define IF_KW_PREFIX_END }
+#define MOVE_IN(DATA, COUNT) (data)+=COUNT;
+#define IF_KEYWORD(STR, LEN, STMT) if (!strncmp((data), STR, LEN)) {MOVE_IN(data, LEN);kw=STMT;}
+#define IF_KEYWORD_PREFIX(STR, LEN) if (!strncmp((data), STR, LEN)) {MOVE_IN(data, LEN);
+#define IF_KEYWORD_PREFIX_END }
 
     enum yang_keyword kw = YANG_NONE;
     /* read the keyword itself */
     switch (*data) {
     case 'a':
-        MOVE_INPUT(data, 1);
-        IF_KW("rgument", 7, YANG_ARGUMENT)
-        else IF_KW("ugment", 6, YANG_AUGMENT)
-        else IF_KW("ction", 5, YANG_ACTION)
-        else IF_KW_PREFIX("ny", 2)
-            IF_KW("data", 4, YANG_ANYDATA)
-            else IF_KW("xml", 3, YANG_ANYXML)
-        IF_KW_PREFIX_END
+        MOVE_IN(data, 1);
+        IF_KEYWORD("rgument", 7, YANG_ARGUMENT)
+        else IF_KEYWORD("ugment", 6, YANG_AUGMENT)
+        else IF_KEYWORD("ction", 5, YANG_ACTION)
+        else IF_KEYWORD_PREFIX("ny", 2)
+            IF_KEYWORD("data", 4, YANG_ANYDATA)
+            else IF_KEYWORD("xml", 3, YANG_ANYXML)
+        IF_KEYWORD_PREFIX_END
         break;
     case 'b':
-        MOVE_INPUT(data, 1);
-        IF_KW("ase", 3, YANG_BASE)
-        else IF_KW("elongs-to", 9, YANG_BELONGS_TO)
-        else IF_KW("it", 2, YANG_BIT)
+        MOVE_IN(data, 1);
+        IF_KEYWORD("ase", 3, YANG_BASE)
+        else IF_KEYWORD("elongs-to", 9, YANG_BELONGS_TO)
+        else IF_KEYWORD("it", 2, YANG_BIT)
         break;
     case 'c':
-        MOVE_INPUT(data, 1);
-        IF_KW("ase", 3, YANG_CASE)
-        else IF_KW("hoice", 5, YANG_CHOICE)
-        else IF_KW_PREFIX("on", 2)
-            IF_KW("fig", 3, YANG_CONFIG)
-            else IF_KW_PREFIX("ta", 2)
-                IF_KW("ct", 2, YANG_CONTACT)
-                else IF_KW("iner", 4, YANG_CONTAINER)
-            IF_KW_PREFIX_END
-        IF_KW_PREFIX_END
+        MOVE_IN(data, 1);
+        IF_KEYWORD("ase", 3, YANG_CASE)
+        else IF_KEYWORD("hoice", 5, YANG_CHOICE)
+        else IF_KEYWORD_PREFIX("on", 2)
+            IF_KEYWORD("fig", 3, YANG_CONFIG)
+            else IF_KEYWORD_PREFIX("ta", 2)
+                IF_KEYWORD("ct", 2, YANG_CONTACT)
+                else IF_KEYWORD("iner", 4, YANG_CONTAINER)
+            IF_KEYWORD_PREFIX_END
+        IF_KEYWORD_PREFIX_END
         break;
     case 'd':
-        MOVE_INPUT(data, 1);
-        IF_KW_PREFIX("e", 1)
-            IF_KW("fault", 5, YANG_DEFAULT)
-            else IF_KW("scription", 9, YANG_DESCRIPTION)
-            else IF_KW_PREFIX("viat", 4)
-                IF_KW("e", 1, YANG_DEVIATE)
-                else IF_KW("ion", 3, YANG_DEVIATION)
-            IF_KW_PREFIX_END
-        IF_KW_PREFIX_END
+        MOVE_IN(data, 1);
+        IF_KEYWORD_PREFIX("e", 1)
+            IF_KEYWORD("fault", 5, YANG_DEFAULT)
+            else IF_KEYWORD("scription", 9, YANG_DESCRIPTION)
+            else IF_KEYWORD_PREFIX("viat", 4)
+                IF_KEYWORD("e", 1, YANG_DEVIATE)
+                else IF_KEYWORD("ion", 3, YANG_DEVIATION)
+            IF_KEYWORD_PREFIX_END
+        IF_KEYWORD_PREFIX_END
         break;
     case 'e':
-        MOVE_INPUT(data, 1);
-        IF_KW("num", 3, YANG_ENUM)
-        else IF_KW_PREFIX("rror-", 5)
-            IF_KW("app-tag", 7, YANG_ERROR_APP_TAG)
-            else IF_KW("message", 7, YANG_ERROR_MESSAGE)
-        IF_KW_PREFIX_END
-        else IF_KW("xtension", 8, YANG_EXTENSION)
+        MOVE_IN(data, 1);
+        IF_KEYWORD("num", 3, YANG_ENUM)
+        else IF_KEYWORD_PREFIX("rror-", 5)
+            IF_KEYWORD("app-tag", 7, YANG_ERROR_APP_TAG)
+            else IF_KEYWORD("message", 7, YANG_ERROR_MESSAGE)
+        IF_KEYWORD_PREFIX_END
+        else IF_KEYWORD("xtension", 8, YANG_EXTENSION)
         break;
     case 'f':
-        MOVE_INPUT(data, 1);
-        IF_KW("eature", 6, YANG_FEATURE)
-        else IF_KW("raction-digits", 14, YANG_FRACTION_DIGITS)
+        MOVE_IN(data, 1);
+        IF_KEYWORD("eature", 6, YANG_FEATURE)
+        else IF_KEYWORD("raction-digits", 14, YANG_FRACTION_DIGITS)
         break;
     case 'g':
-        MOVE_INPUT(data, 1);
-        IF_KW("rouping", 7, YANG_GROUPING)
+        MOVE_IN(data, 1);
+        IF_KEYWORD("rouping", 7, YANG_GROUPING)
         break;
     case 'i':
-        MOVE_INPUT(data, 1);
-        IF_KW("dentity", 7, YANG_IDENTITY)
-        else IF_KW("f-feature", 9, YANG_IF_FEATURE)
-        else IF_KW("mport", 5, YANG_IMPORT)
-        else IF_KW_PREFIX("n", 1)
-            IF_KW("clude", 5, YANG_INCLUDE)
-            else IF_KW("put", 3, YANG_INPUT)
-        IF_KW_PREFIX_END
+        MOVE_IN(data, 1);
+        IF_KEYWORD("dentity", 7, YANG_IDENTITY)
+        else IF_KEYWORD("f-feature", 9, YANG_IF_FEATURE)
+        else IF_KEYWORD("mport", 5, YANG_IMPORT)
+        else IF_KEYWORD_PREFIX("n", 1)
+            IF_KEYWORD("clude", 5, YANG_INCLUDE)
+            else IF_KEYWORD("put", 3, YANG_INPUT)
+        IF_KEYWORD_PREFIX_END
         break;
     case 'k':
-        MOVE_INPUT(data, 1);
-        IF_KW("ey", 2, YANG_KEY)
+        MOVE_IN(data, 1);
+        IF_KEYWORD("ey", 2, YANG_KEY)
         break;
     case 'l':
-        MOVE_INPUT(data, 1);
-        IF_KW_PREFIX("e", 1)
-            IF_KW("af-list", 7, YANG_LEAF_LIST)
-            else IF_KW("af", 2, YANG_LEAF)
-            else IF_KW("ngth", 4, YANG_LENGTH)
-        IF_KW_PREFIX_END
-        else IF_KW("ist", 3, YANG_LIST)
+        MOVE_IN(data, 1);
+        IF_KEYWORD_PREFIX("e", 1)
+            IF_KEYWORD("af-list", 7, YANG_LEAF_LIST)
+            else IF_KEYWORD("af", 2, YANG_LEAF)
+            else IF_KEYWORD("ngth", 4, YANG_LENGTH)
+        IF_KEYWORD_PREFIX_END
+        else IF_KEYWORD("ist", 3, YANG_LIST)
         break;
     case 'm':
-        MOVE_INPUT(data, 1);
-        IF_KW_PREFIX("a", 1)
-            IF_KW("ndatory", 7, YANG_MANDATORY)
-            else IF_KW("x-elements", 10, YANG_MAX_ELEMENTS)
-        IF_KW_PREFIX_END
-        else IF_KW("in-elements", 11, YANG_MIN_ELEMENTS)
-        else IF_KW("ust", 3, YANG_MUST)
-        else IF_KW_PREFIX("od", 2)
-            IF_KW("ule", 3, YANG_MODULE)
-            else IF_KW("ifier", 5, YANG_MODIFIER)
-        IF_KW_PREFIX_END
+        MOVE_IN(data, 1);
+        IF_KEYWORD_PREFIX("a", 1)
+            IF_KEYWORD("ndatory", 7, YANG_MANDATORY)
+            else IF_KEYWORD("x-elements", 10, YANG_MAX_ELEMENTS)
+        IF_KEYWORD_PREFIX_END
+        else IF_KEYWORD("in-elements", 11, YANG_MIN_ELEMENTS)
+        else IF_KEYWORD("ust", 3, YANG_MUST)
+        else IF_KEYWORD_PREFIX("od", 2)
+            IF_KEYWORD("ule", 3, YANG_MODULE)
+            else IF_KEYWORD("ifier", 5, YANG_MODIFIER)
+        IF_KEYWORD_PREFIX_END
         break;
     case 'n':
-        MOVE_INPUT(data, 1);
-        IF_KW("amespace", 8, YANG_NAMESPACE)
-        else IF_KW("otification", 11, YANG_NOTIFICATION)
+        MOVE_IN(data, 1);
+        IF_KEYWORD("amespace", 8, YANG_NAMESPACE)
+        else IF_KEYWORD("otification", 11, YANG_NOTIFICATION)
         break;
     case 'o':
-        MOVE_INPUT(data, 1);
-        IF_KW_PREFIX("r", 1)
-            IF_KW("dered-by", 8, YANG_ORDERED_BY)
-            else IF_KW("ganization", 10, YANG_ORGANIZATION)
-        IF_KW_PREFIX_END
-        else IF_KW("utput", 5, YANG_OUTPUT)
+        MOVE_IN(data, 1);
+        IF_KEYWORD_PREFIX("r", 1)
+            IF_KEYWORD("dered-by", 8, YANG_ORDERED_BY)
+            else IF_KEYWORD("ganization", 10, YANG_ORGANIZATION)
+        IF_KEYWORD_PREFIX_END
+        else IF_KEYWORD("utput", 5, YANG_OUTPUT)
         break;
     case 'p':
-        MOVE_INPUT(data, 1);
-        IF_KW("ath", 3, YANG_PATH)
-        else IF_KW("attern", 6, YANG_PATTERN)
-        else IF_KW("osition", 7, YANG_POSITION)
-        else IF_KW_PREFIX("re", 2)
-            IF_KW("fix", 3, YANG_PREFIX)
-            else IF_KW("sence", 5, YANG_PRESENCE)
-        IF_KW_PREFIX_END
+        MOVE_IN(data, 1);
+        IF_KEYWORD("ath", 3, YANG_PATH)
+        else IF_KEYWORD("attern", 6, YANG_PATTERN)
+        else IF_KEYWORD("osition", 7, YANG_POSITION)
+        else IF_KEYWORD_PREFIX("re", 2)
+            IF_KEYWORD("fix", 3, YANG_PREFIX)
+            else IF_KEYWORD("sence", 5, YANG_PRESENCE)
+        IF_KEYWORD_PREFIX_END
         break;
     case 'r':
-        MOVE_INPUT(data, 1);
-        IF_KW("ange", 4, YANG_RANGE)
-        else IF_KW_PREFIX("e", 1)
-            IF_KW_PREFIX("f", 1)
-                IF_KW("erence", 6, YANG_REFERENCE)
-                else IF_KW("ine", 3, YANG_REFINE)
-            IF_KW_PREFIX_END
-            else IF_KW("quire-instance", 14, YANG_REQUIRE_INSTANCE)
-            else IF_KW("vision-date", 11, YANG_REVISION_DATE)
-            else IF_KW("vision", 6, YANG_REVISION)
-        IF_KW_PREFIX_END
-        else IF_KW("pc", 2, YANG_RPC)
+        MOVE_IN(data, 1);
+        IF_KEYWORD("ange", 4, YANG_RANGE)
+        else IF_KEYWORD_PREFIX("e", 1)
+            IF_KEYWORD_PREFIX("f", 1)
+                IF_KEYWORD("erence", 6, YANG_REFERENCE)
+                else IF_KEYWORD("ine", 3, YANG_REFINE)
+            IF_KEYWORD_PREFIX_END
+            else IF_KEYWORD("quire-instance", 14, YANG_REQUIRE_INSTANCE)
+            else IF_KEYWORD("vision-date", 11, YANG_REVISION_DATE)
+            else IF_KEYWORD("vision", 6, YANG_REVISION)
+        IF_KEYWORD_PREFIX_END
+        else IF_KEYWORD("pc", 2, YANG_RPC)
         break;
     case 's':
-        MOVE_INPUT(data, 1);
-        IF_KW("tatus", 5, YANG_STATUS)
-        else IF_KW("ubmodule", 8, YANG_SUBMODULE)
+        MOVE_IN(data, 1);
+        IF_KEYWORD("tatus", 5, YANG_STATUS)
+        else IF_KEYWORD("ubmodule", 8, YANG_SUBMODULE)
         break;
     case 't':
-        MOVE_INPUT(data, 1);
-        IF_KW("ypedef", 6, YANG_TYPEDEF)
-        else IF_KW("ype", 3, YANG_TYPE)
+        MOVE_IN(data, 1);
+        IF_KEYWORD("ypedef", 6, YANG_TYPEDEF)
+        else IF_KEYWORD("ype", 3, YANG_TYPE)
         break;
     case 'u':
-        MOVE_INPUT(data, 1);
-        IF_KW_PREFIX("ni", 2)
-            IF_KW("que", 3, YANG_UNIQUE)
-            else IF_KW("ts", 2, YANG_UNITS)
-        IF_KW_PREFIX_END
-        else IF_KW("ses", 3, YANG_USES)
+        MOVE_IN(data, 1);
+        IF_KEYWORD_PREFIX("ni", 2)
+            IF_KEYWORD("que", 3, YANG_UNIQUE)
+            else IF_KEYWORD("ts", 2, YANG_UNITS)
+        IF_KEYWORD_PREFIX_END
+        else IF_KEYWORD("ses", 3, YANG_USES)
         break;
     case 'v':
-        MOVE_INPUT(data, 1);
-        IF_KW("alue", 4, YANG_VALUE)
+        MOVE_IN(data, 1);
+        IF_KEYWORD("alue", 4, YANG_VALUE)
         break;
     case 'w':
-        MOVE_INPUT(data, 1);
-        IF_KW("hen", 3, YANG_WHEN)
+        MOVE_IN(data, 1);
+        IF_KEYWORD("hen", 3, YANG_WHEN)
         break;
     case 'y':
-        MOVE_INPUT(data, 1);
-        IF_KW("ang-version", 11, YANG_YANG_VERSION)
-        else IF_KW("in-element", 10, YANG_YIN_ELEMENT)
+        MOVE_IN(data, 1);
+        IF_KEYWORD("ang-version", 11, YANG_YANG_VERSION)
+        else IF_KEYWORD("in-element", 10, YANG_YIN_ELEMENT)
         break;
     case ';':
-        MOVE_INPUT(data, 1);
+        MOVE_IN(data, 1);
         kw = YANG_SEMICOLON;
         //goto success;
         break;
     case '{':
-        MOVE_INPUT(data, 1);
+        MOVE_IN(data, 1);
         kw = YANG_LEFT_BRACE;
         //goto success;
         break;
     case '}':
-        MOVE_INPUT(data, 1);
+        MOVE_IN(data, 1);
         kw = YANG_RIGHT_BRACE;
         //goto success;
         break;
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index da8789c..26159b1 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -15,6 +15,13 @@
 #ifndef LY_TREE_SCHEMA_INTERNAL_H_
 #define LY_TREE_SCHEMA_INTERNAL_H_
 
+#define LOGVAL_YANG(CTX, ...) LOGVAL((CTX)->ctx, LY_VLOG_LINE, &(CTX)->line, __VA_ARGS__)
+
+/* These 2 macros checks YANG's identifier grammar rule */
+#define is_yangidentstartchar(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')
+#define is_yangidentchar(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || \
+        c == '_' || c == '-' || c == '.')
+
 /**
  * @brief List of YANG statement groups - the (sub)module's substatements
  */
@@ -31,7 +38,7 @@
  */
 enum yang_arg {
     Y_IDENTIF_ARG,        /**< YANG "identifier-arg-str" rule */
-    Y_PREF_IDENTIF_ARG,   /**< YANG "identifier-ref-arg-str" rule */
+    Y_PREF_IDENTIF_ARG,   /**< YANG "identifier-ref-arg-str" or node-identifier rule */
     Y_STR_ARG,            /**< YANG "string" rule */
     Y_MAYBE_STR_ARG       /**< optional YANG "string" rule */
 };
@@ -41,14 +48,34 @@
  */
 struct ly_parser_ctx {
     struct ly_ctx *ctx;
-    uint64_t line;      /* line number */
-    uint64_t indent;    /* current position on the line for YANG indentation */
+    struct lysp_module *mod;
+    struct ly_set tpdfs_nodes;
+    struct ly_set grps_nodes;
+    uint64_t line;      /**< line number */
+    uint64_t indent;    /**< current position on the line for YANG indentation */
+};
+
+/**
+ * @brief internal context for compilation
+ */
+struct lysc_ctx {
+    struct ly_ctx *ctx;
+    struct lys_module *mod;
+    struct lys_module *mod_def; /* context module for the definitions of the nodes being currently
+                                   processed - groupings are supposed to be evaluated in place where
+                                   defined, but its content instances are supposed to be placed into
+                                   the target module (mod) */
+    struct ly_set groupings;    /* stack for groupings circular check */
+    struct ly_set unres;        /* to validate leafref's target and xpath of when/must */
+    uint16_t path_len;
+#define LYSC_CTX_BUFSIZE 4078
+    char path[LYSC_CTX_BUFSIZE];
 };
 
 /**
  * @brief Check the currently present prefixes in the module for collision with the new one.
  *
- * @param[in] ctx yang parser context.
+ * @param[in] ctx Context for logging.
  * @param[in] module Schema tree to check.
  * @param[in] value Newly added prefix value (including its location to distinguish collision with itself).
  * @return LY_EEXIST when prefix is already used in the module, LY_SUCCESS otherwise
@@ -58,13 +85,21 @@
 /**
  * @brief Check date string (4DIGIT "-" 2DIGIT "-" 2DIGIT)
  *
- * @param[in] ctx Context to store log message.
+ * @param[in] ctx Optional context for logging.
  * @param[in] date Date string to check (non-necessarily terminated by \0)
  * @param[in] date_len Length of the date string, 10 expected.
  * @param[in] stmt Statement name for error message.
  * @return LY_ERR value.
  */
-LY_ERR lysp_check_date(struct ly_ctx *ctx, const char *date, int date_len, const char *stmt);
+LY_ERR lysp_check_date(struct ly_parser_ctx *ctx, const char *date, int date_len, const char *stmt);
+
+/**
+ * @brief Check names of typedefs in the parsed module to detect collisions.
+ *
+ * @param[in] ctx Parser context, module where the type is being defined is taken from here.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_check_typedefs(struct ly_parser_ctx *ctx);
 
 /**
  * @brief Just move the newest revision into the first position, does not sort the rest
@@ -73,6 +108,160 @@
 void lysp_sort_revisions(struct lysp_revision *revs);
 
 /**
+ * @brief Find type specified type definition
+ *
+ * @param[in] id Name of the type including possible prefix. Module where the prefix is being searched is start_module.
+ * @param[in] start_node Context node where the type is being instantiated to be able to search typedefs in parents.
+ * @param[in] start_module Module where the type is being instantiated for search for typedefs.
+ * @param[out] type Built-in type identifier of the id. If #LY_TYPE_UNKNOWN, tpdf is expected to contain found YANG schema typedef statement.
+ * @param[out] tpdf Found type definition.
+ * @param[out] node Node where the found typedef is defined, NULL in case of a top-level typedef.
+ * @param[out] module Module where the found typedef is being defined, NULL in case of built-in YANG types.
+ */
+LY_ERR lysp_type_find(const char *id, struct lysp_node *start_node, struct lysp_module *start_module,
+                      LY_DATA_TYPE *type, const struct lysp_tpdf **tpdf, struct lysp_node **node, struct lysp_module **module);
+
+/**
+ * @brief Find and parse module of the given name.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] name Name of the module to load.
+ * @param[in] revison Optional revision of the module to load. If NULL, the newest revision is loaded.
+ * @param[in] implement Flag if the loaded module is supposed to be marked as implemented.
+ * @param[in] require_parsed Flag to require parsed module structure in case the module is already in the context,
+ * but only the compiled structure is available.
+ * @param[out] mod Parsed module structure.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_load_module(struct ly_ctx *ctx, const char *name, const char *revision, int implement, int require_parsed, struct lys_module **mod);
+
+/**
+ * @brief Parse included submodule into the simply parsed YANG module.
+ *
+ * @param[in] ctx parser context
+ * @param[in] mod Module including a submodule.
+ * @param[in,out] inc Include structure holding all available information about the include statement, the parsed
+ * submodule is stored into this structure.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_load_submodule(struct ly_parser_ctx *ctx, struct lysp_module *mod, struct lysp_include *inc);
+
+/**
+ * @brief Get address of a node's actions list if any.
+ *
+ * Decides the node's type and in case it has an actions list, returns its address.
+ * @param[in] node Node to check.
+ * @return Address of the node's actions member if any, NULL otherwise.
+ */
+struct lysp_action **lysp_node_actions_p(struct lysp_node *node);
+
+/**
+ * @brief Get address of a node's notifications list if any.
+ *
+ * Decides the node's type and in case it has a notifications list, returns its address.
+ * @param[in] node Node to check.
+ * @return Address of the node's notifs member if any, NULL otherwise.
+ */
+struct lysp_notif **lysp_node_notifs_p(struct lysp_node *node);
+
+/**
+ * @brief Get address of a node's child pointer if any.
+ *
+ * Decides the node's type and in case it has a children list, returns its address.
+ * @param[in] node Node to check.
+ * @return Address of the node's child member if any, NULL otherwise.
+ */
+struct lysp_node **lysp_node_children_p(struct lysp_node *node);
+
+/**
+ * @brief Get address of a node's child pointer if any.
+ *
+ * Decides the node's type and in case it has a children list, returns its address.
+ * @param[in] node Node to check.
+ * @return Address of the node's child member if any, NULL otherwise.
+ */
+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.
+ * @param[in] ctx libyang context to search.
+ * @param[in] mod Parsed schema structure.
+ * @return Corresponding lys_module structure for the given parsed schema structure.
+ */
+struct lys_module *lysp_find_module(struct ly_ctx *ctx, const struct lysp_module *mod);
+
+/**
+ * @brief Find the module referenced by prefix in the provided parsed mod.
+ *
+ * @param[in] mod Schema module where the prefix was used.
+ * @param[in] prefix Prefix used to reference a module.
+ * @param[in] len Length of the prefix since it is not necessary NULL-terminated.
+ * @return Pointer to the module or NULL if the module is not found.
+ */
+struct lysp_module *lysp_module_find_prefix(const struct lysp_module *mod, const char *prefix, size_t len);
+
+/**
+ * @brief Find the module referenced by prefix in the provided compiled mod.
+ *
+ * @param[in] mod Schema module where the prefix was used.
+ * @param[in] prefix Prefix used to reference a module.
+ * @param[in] len Length of the prefix since it is not necessary NULL-terminated.
+ * @return Pointer to the module or NULL if the module is not found.
+ */
+struct lysc_module *lysc_module_find_prefix(const struct lysc_module *mod, const char *prefix, size_t len);
+
+/**
+ * @brief Check statement's status for invalid combination.
+ *
+ * The modX parameters are used just to determine if both flags are in the same module,
+ * so any of the schema module structure can be used, but both modules must be provided
+ * in the same type.
+ *
+ * @param[in] ctx Compile context for logging.
+ * @param[in] flags1 Flags of the referencing node.
+ * @param[in] mod1 Module of the referencing node,
+ * @param[in] name1 Schema node name of the referencing node.
+ * @param[in] flags2 Flags of the referenced node.
+ * @param[in] mod2 Module of the referenced node,
+ * @param[in] name2 Schema node name of the referenced node.
+ * @return LY_ERR value
+ */
+LY_ERR lysc_check_status(struct lysc_ctx *ctx,
+                         uint16_t flags1, void *mod1, const char *name1,
+                         uint16_t flags2, void *mod2, const char *name2);
+
+/**
+ * @brief Parse a node-identifier.
+ *
+ * node-identifier     = [prefix ":"] identifier
+ *
+ * @param[in, out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
+ * @param[out] prefix Node's prefix, NULL if there is not any.
+ * @param[out] prefix_len Length of the node's prefix, 0 if there is not any.
+ * @param[out] name Node's name.
+ * @param[out] nam_len Length of the node's name.
+ * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid character in the id.
+ */
+LY_ERR lys_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len);
+
+/**
+ * @brief Find the node according to the given descendant schema node id.
+ * Used in unique, refine and uses's augment statements
+ *
+ * @param[in] ctx Compile context
+ * @param[in] nodeid Descendant-schema-nodeid (according to the YANG grammar)
+ * @param[in] nodeid_len Length of the given nodeid, if it is not NULL-terminated string.
+ * @param[in] context_node Node where the nodeid is specified to correctly resolve prefixes and to start searching.
+ * @param[in] nodetype Optional (can be 0) restriction for target's nodetype. If target exists, but does not match
+ * the given nodetype, LY_EDENIED is returned, but no error message is printed. The value can be even an ORed value to allow
+ * multiple nodetypes.
+ * @param[out] target Found target node if any.
+ * @return LY_ERR values - LY_ENOTFOUND, LY_EVALID, LY_EDENIED or LY_SUCCESS.
+ */
+LY_ERR lys_resolve_descendant_schema_nodeid(struct lysc_ctx *ctx, const char *nodeid, size_t nodeid_len, const struct lysc_node *context_node,
+                                            int nodetype, const struct lysc_node **target);
+
+/**
  * @brief Find the module referenced by prefix in the provided mod.
  *
  * @param[in] mod Schema module where the prefix was used.
@@ -80,7 +269,125 @@
  * @param[in] len Length of the prefix since it is not necessary NULL-terminated.
  * @return Pointer to the module or NULL if the module is not found.
  */
-struct lysc_module *lysc_module_find_prefix(struct lysc_module *mod, const char *prefix, size_t len);
+struct lys_module *lys_module_find_prefix(const struct lys_module *mod, const char *prefix, size_t len);
+
+/**
+ * @brief Stringify schema nodetype.
+ * @param[in] nodetype Nodetype to stringify.
+ * @return Constant string with the name of the node's type.
+ */
+const char *lys_nodetype2str(uint16_t nodetype);
+
+/**
+ * @brief Parse YANG module and submodule from a string.
+ *
+ * In contrast to public lys_parse_mem(), also submodules can be parsed here. However,
+ * while the modules are added into the context, submodules not. The latest_revision
+ * flag is updated in both cases.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] data The string containing the dumped data model in the specified
+ * format.
+ * @param[in] format Format of the input data (YANG or YIN).
+ * @param[in] implement Flag if the schema is supposed to be marked as implemented.
+ * @param[in] main_ctx Parser context of the main module in case of parsing submodule.
+ * @param[in] custom_check Callback to check the parsed schema before it is accepted.
+ * @param[in] check_data Caller's data to pass to the custom_check callback.
+ * @return Pointer to the data model structure or NULL on error.
+ */
+struct lys_module *lys_parse_mem_(struct ly_ctx *ctx, const char *data, LYS_INFORMAT format, int implement, struct ly_parser_ctx *main_ctx,
+                                  LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, void *data), void *check_data);
+
+/**
+ * @brief Parse YANG module and submodule from a file descriptor.
+ *
+ * In contrast to public lys_parse_mem(), also submodules can be parsed here. However,
+ * while the modules are added into the context, submodules not. The latest_revision
+ * flag is updated in both cases.
+ *
+ * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] fd File descriptor of a regular file (e.g. sockets are not supported) containing the schema
+ *            in the specified format.
+ * @param[in] format Format of the input data (YANG or YIN).
+ * @param[in] implement Flag if the schema is supposed to be marked as implemented.
+ * @param[in] main_ctx Parser context of the main module in case of parsing submodule.
+ * @param[in] custom_check Callback to check the parsed schema before it is accepted.
+ * @param[in] check_data Caller's data to pass to the custom_check callback.
+ * @return Pointer to the data model structure or NULL on error.
+ */
+struct lys_module *lys_parse_fd_(struct ly_ctx *ctx, int fd, LYS_INFORMAT format, int implement, struct ly_parser_ctx *main_ctx,
+                                 LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, void *data), void *check_data);
+
+/**
+ * @brief Parse YANG module and submodule from a file descriptor.
+ *
+ * In contrast to public lys_parse_mem(), also submodules can be parsed here. However,
+ * while the modules are added into the context, submodules not. The latest_revision
+ * flag is updated in both cases.
+ *
+ * \note Current implementation supports only reading data from standard (disk) file, not from sockets, pipes, etc.
+ *
+ * @brief REad a schema into the specified context from a file.
+ *
+ * @param[in] ctx libyang context where to process the data model.
+ * @param[in] path Path to the file with the model in the specified format.
+ * @param[in] format Format of the input data (YANG or YIN).
+ * @param[in] implement Flag if the schema is supposed to be marked as implemented.
+ * @param[in] main_ctx Parser context of the main module in case of parsing submodule.
+ * @param[in] custom_check Callback to check the parsed schema before it is accepted.
+ * @param[in] check_data Caller's data to pass to the custom_check callback.
+ * @return Pointer to the data model structure or NULL on error.
+ */
+struct lys_module *lys_parse_path_(struct ly_ctx *ctx, const char *path, LYS_INFORMAT format, int implement, struct ly_parser_ctx *main_ctx,
+                                   LY_ERR (*custom_check)(struct ly_ctx *ctx, struct lysp_module *mod, void *data), void *check_data);
+
+/**
+ * @brief Load the (sub)module into the context.
+ *
+ * This function does not check the presence of the (sub)module in context, it should be done before calling this function.
+ *
+ * module_name and submodule_name are alternatives - only one of the
+ *
+ * @param[in] ctx libyang context where to work.
+ * @param[in] name Name of the (sub)module to load.
+ * @param[in] revision Optional revision of the (sub)module to load, if NULL the newest revision is being loaded.
+ * @param[in] implement Flag if the (sub)module is supposed to be marked as implemented.
+ * @param[in] main_ctx Parser context of the main module in case of loading submodule.
+ * @param[out] result Parsed YANG schema tree of the requested module. If it is a module, it is already in the context!
+ * @return LY_ERR value, in case of LY_SUCCESS, the \arg result is always provided.
+ */
+LY_ERR lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, int implement, struct ly_parser_ctx *main_ctx,
+                            struct lys_module **result);
+
+/**
+ * @brief Make the module implemented.
+ * Does not check for collision in context, it must be done before calling the function, this is a simple switch.
+ * @param[in] mod Module to make implemented.
+ */
+void lys_module_implement(struct lys_module *mod);
+
+/**
+ * @brief Free the compiled type structure.
+ * @param[in] ctx libyang context where the string data resides in a dictionary.
+ * @param[in,out] type Compiled type structure to be freed. The structure has refcount, so it is freed only in case the value is decreased to 0.
+ */
+void lysc_type_free(struct ly_ctx *ctx, struct lysc_type *type);
+
+/**
+ * @brief Free the compiled node structure.
+ * @param[in] ctx libyang context where the string data resides in a dictionary.
+ * @param[in,out] node Compiled node structure to be freed.
+ */
+void lysc_node_free(struct ly_ctx *ctx, struct lysc_node *node);
+
+/**
+ * @brief Free the compiled schema structure.
+ * @param[in,out] module Compiled schema module structure to free.
+ * @param[in] private_destructor Function to remove private data from the compiled schema tree.
+ */
+void lysc_module_free(struct lysc_module *module, void (*private_destructor)(const struct lysc_node *node, void *priv));
 
 /**
  * @brief Free the schema structure. It just frees, it does not remove the schema from its context.
@@ -92,6 +399,10 @@
 /**
  * @brief
  */
-LY_ERR yang_parse(struct ly_ctx *ctx, const char *data, struct lysp_module **mod_p);
+LY_ERR yang_parse(struct ly_parser_ctx *ctx, const char *data, struct lysp_module **mod_p);
 
+/**
+ * @brief match yang keyword
+ */
+enum yang_keyword match_keyword(const char *data);
 #endif /* LY_TREE_SCHEMA_INTERNAL_H_ */
diff --git a/src/xml.c b/src/xml.c
index db25d70..be943cd 100644
--- a/src/xml.c
+++ b/src/xml.c
@@ -12,7 +12,7 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _POSIX_C_SOURCE 200809L /* strndup() */
+#include "common.h"
 
 #include <assert.h>
 #include <ctype.h>
@@ -22,31 +22,6 @@
 
 #include "libyang.h"
 #include "xml.h"
-#include "common.h"
-
-/* Macro to test if character is whitespace */
-#define is_xmlws(c) (c == 0x20 || c == 0x9 || c == 0xa || c == 0xd)
-
-/* Macro to test if character is allowed to be a first character of an qualified identifier */
-#define is_xmlqnamestartchar(c) ((c >= 'a' && c <= 'z') || c == '_' || \
-        (c >= 'A' && c <= 'Z') || /* c == ':' || */ \
-        (c >= 0x370 && c <= 0x1fff && c != 0x37e ) || \
-        (c >= 0xc0 && c <= 0x2ff && c != 0xd7 && c != 0xf7) || c == 0x200c || \
-        c == 0x200d || (c >= 0x2070 && c <= 0x218f) || \
-        (c >= 0x2c00 && c <= 0x2fef) || (c >= 0x3001 && c <= 0xd7ff) || \
-        (c >= 0xf900 && c <= 0xfdcf) || (c >= 0xfdf0 && c <= 0xfffd) || \
-        (c >= 0x10000 && c <= 0xeffff))
-
-/* Macro to test if character is allowed to be used in an qualified identifier */
-#define is_xmlqnamechar(c) ((c >= 'a' && c <= 'z') || c == '_' || c == '-' || \
-        (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || /* c == ':' || */ \
-        c == '.' || c == 0xb7 || (c >= 0x370 && c <= 0x1fff && c != 0x37e ) ||\
-        (c >= 0xc0 && c <= 0x2ff && c != 0xd7 && c != 0xf7) || c == 0x200c || \
-        c == 0x200d || (c >= 0x300 && c <= 0x36f) || \
-        (c >= 0x2070 && c <= 0x218f) || (c >= 0x2030f && c <= 0x2040) || \
-        (c >= 0x2c00 && c <= 0x2fef) || (c >= 0x3001 && c <= 0xd7ff) || \
-        (c >= 0xf900 && c <= 0xfdcf) || (c >= 0xfdf0 && c <= 0xfffd) || \
-        (c >= 0x10000 && c <= 0xeffff))
 
 /* Move input p by s characters, if EOF log with lyxml_context c */
 #define move_input(c,p,s) p += s; LY_CHECK_ERR_RET(!p[0], LOGVAL(c->ctx, LY_VLOG_LINE, &c->line, LY_VCODE_EOF), LY_EVALID)
diff --git a/src/xml.h b/src/xml.h
index f42b27e..5172c48 100644
--- a/src/xml.h
+++ b/src/xml.h
@@ -20,6 +20,30 @@
 #include "context.h"
 #include "set.h"
 
+/* Macro to test if character is whitespace */
+#define is_xmlws(c) (c == 0x20 || c == 0x9 || c == 0xa || c == 0xd)
+
+/* Macro to test if character is allowed to be a first character of an qualified identifier */
+#define is_xmlqnamestartchar(c) ((c >= 'a' && c <= 'z') || c == '_' || \
+        (c >= 'A' && c <= 'Z') || /* c == ':' || */ \
+        (c >= 0x370 && c <= 0x1fff && c != 0x37e ) || \
+        (c >= 0xc0 && c <= 0x2ff && c != 0xd7 && c != 0xf7) || c == 0x200c || \
+        c == 0x200d || (c >= 0x2070 && c <= 0x218f) || \
+        (c >= 0x2c00 && c <= 0x2fef) || (c >= 0x3001 && c <= 0xd7ff) || \
+        (c >= 0xf900 && c <= 0xfdcf) || (c >= 0xfdf0 && c <= 0xfffd) || \
+        (c >= 0x10000 && c <= 0xeffff))
+
+/* Macro to test if character is allowed to be used in an qualified identifier */
+#define is_xmlqnamechar(c) ((c >= 'a' && c <= 'z') || c == '_' || c == '-' || \
+        (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || /* c == ':' || */ \
+        c == '.' || c == 0xb7 || (c >= 0x370 && c <= 0x1fff && c != 0x37e ) ||\
+        (c >= 0xc0 && c <= 0x2ff && c != 0xd7 && c != 0xf7) || c == 0x200c || \
+        c == 0x200d || (c >= 0x300 && c <= 0x36f) || \
+        (c >= 0x2070 && c <= 0x218f) || (c >= 0x2030f && c <= 0x2040) || \
+        (c >= 0x2c00 && c <= 0x2fef) || (c >= 0x3001 && c <= 0xd7ff) || \
+        (c >= 0xf900 && c <= 0xfdcf) || (c >= 0xfdf0 && c <= 0xfffd) || \
+        (c >= 0x10000 && c <= 0xeffff))
+
 struct lyxml_ns {
     const char *element;  /* element where the namespace is defined */
     char *prefix;         /* prefix of the namespace, NULL for the default namespace */
diff --git a/src/xpath.c b/src/xpath.c
new file mode 100644
index 0000000..4a3792d
--- /dev/null
+++ b/src/xpath.c
@@ -0,0 +1,362 @@
+/**
+ * @file xpath.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief YANG XPath evaluation functions
+ *
+ * Copyright (c) 2015 - 2017 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "common.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+#include <errno.h>
+#include <math.h>
+#include <pcre.h>
+
+#include "xpath.h"
+#include "xml.h"
+
+/**
+ * @brief Parse NCName.
+ *
+ * @param[in] ncname Name to parse.
+ *
+ * @return Length of \p ncname valid bytes.
+ */
+static size_t
+parse_ncname(const char *ncname)
+{
+    unsigned int uc;
+    size_t size, len = 0;
+
+    LY_CHECK_RET(ly_getutf8(&ncname, &uc, &size), 0);
+    if (!is_xmlqnamestartchar(uc) || (uc == ':')) {
+        return len;
+    }
+
+    do {
+        len += size;
+        LY_CHECK_RET(ly_getutf8(&ncname, &uc, &size), 0);
+    } while (is_xmlqnamechar(uc) && (uc != ':'));
+
+    return len;
+}
+
+/**
+ * @brief Add \p token into the expression \p exp.
+ *
+ * @param[in] ctx libyang context to log in.
+ * @param[in] exp Expression to use.
+ * @param[in] token Token to add.
+ * @param[in] expr_pos Token position in the XPath expression.
+ * @param[in] tok_len Token length in the XPath expression.
+ * @return LY_ERR value
+ */
+static LY_ERR
+exp_add_token(struct ly_ctx *ctx, struct lyxp_expr *exp, enum lyxp_token token, uint16_t expr_pos, uint16_t tok_len)
+{
+    uint32_t prev;
+
+    if (exp->used == exp->size) {
+        prev = exp->size;
+        exp->size += LYXP_EXPR_SIZE_STEP;
+        if (prev > exp->size) {
+            LOGINT(ctx);
+            return LY_EINT;
+        }
+
+        exp->tokens = ly_realloc(exp->tokens, exp->size * sizeof *exp->tokens);
+        LY_CHECK_ERR_RET(!exp->tokens, LOGMEM(ctx), LY_EMEM);
+        exp->tok_pos = ly_realloc(exp->tok_pos, exp->size * sizeof *exp->tok_pos);
+        LY_CHECK_ERR_RET(!exp->tok_pos, LOGMEM(ctx), LY_EMEM);
+        exp->tok_len = ly_realloc(exp->tok_len, exp->size * sizeof *exp->tok_len);
+        LY_CHECK_ERR_RET(!exp->tok_len, LOGMEM(ctx), LY_EMEM);
+    }
+
+    exp->tokens[exp->used] = token;
+    exp->tok_pos[exp->used] = expr_pos;
+    exp->tok_len[exp->used] = tok_len;
+    ++exp->used;
+    return LY_SUCCESS;
+}
+
+void
+lyxp_expr_free(struct ly_ctx *ctx, struct lyxp_expr *expr)
+{
+    uint16_t i;
+
+    if (!expr) {
+        return;
+    }
+
+    lydict_remove(ctx, expr->expr);
+    free(expr->tokens);
+    free(expr->tok_pos);
+    free(expr->tok_len);
+    if (expr->repeat) {
+        for (i = 0; i < expr->used; ++i) {
+            free(expr->repeat[i]);
+        }
+    }
+    free(expr->repeat);
+    free(expr);
+}
+
+struct lyxp_expr *
+lyxp_expr_parse(struct ly_ctx *ctx, const char *expr)
+{
+    struct lyxp_expr *ret;
+    size_t parsed = 0, tok_len, ncname_len;
+    enum lyxp_token tok_type;
+    int prev_function_check = 0;
+
+    if (strlen(expr) > UINT16_MAX) {
+        LOGERR(ctx, LY_EINVAL, "XPath expression cannot be longer than %ud characters.", UINT16_MAX);
+        return NULL;
+    }
+
+    /* init lyxp_expr structure */
+    ret = calloc(1, sizeof *ret);
+    LY_CHECK_ERR_GOTO(!ret, LOGMEM(ctx), error);
+    ret->expr = lydict_insert(ctx, expr, strlen(expr));
+    LY_CHECK_ERR_GOTO(!ret->expr, LOGMEM(ctx), error);
+    ret->used = 0;
+    ret->size = LYXP_EXPR_SIZE_START;
+    ret->tokens = malloc(ret->size * sizeof *ret->tokens);
+    LY_CHECK_ERR_GOTO(!ret->tokens, LOGMEM(ctx), error);
+
+    ret->tok_pos = malloc(ret->size * sizeof *ret->tok_pos);
+    LY_CHECK_ERR_GOTO(!ret->tok_pos, LOGMEM(ctx), error);
+
+    ret->tok_len = malloc(ret->size * sizeof *ret->tok_len);
+    LY_CHECK_ERR_GOTO(!ret->tok_len, LOGMEM(ctx), error);
+
+    while (is_xmlws(expr[parsed])) {
+        ++parsed;
+    }
+
+    do {
+        if (expr[parsed] == '(') {
+
+            /* '(' */
+            tok_len = 1;
+            tok_type = LYXP_TOKEN_PAR1;
+
+            if (prev_function_check && ret->used && (ret->tokens[ret->used - 1] == LYXP_TOKEN_NAMETEST)) {
+                /* it is a NodeType/FunctionName after all */
+                if (((ret->tok_len[ret->used - 1] == 4)
+                        && (!strncmp(&expr[ret->tok_pos[ret->used - 1]], "node", 4)
+                        || !strncmp(&expr[ret->tok_pos[ret->used - 1]], "text", 4))) ||
+                        ((ret->tok_len[ret->used - 1] == 7)
+                        && !strncmp(&expr[ret->tok_pos[ret->used - 1]], "comment", 7))) {
+                    ret->tokens[ret->used - 1] = LYXP_TOKEN_NODETYPE;
+                } else {
+                    ret->tokens[ret->used - 1] = LYXP_TOKEN_FUNCNAME;
+                }
+                prev_function_check = 0;
+            }
+
+        } else if (expr[parsed] == ')') {
+
+            /* ')' */
+            tok_len = 1;
+            tok_type = LYXP_TOKEN_PAR2;
+
+        } else if (expr[parsed] == '[') {
+
+            /* '[' */
+            tok_len = 1;
+            tok_type = LYXP_TOKEN_BRACK1;
+
+        } else if (expr[parsed] == ']') {
+
+            /* ']' */
+            tok_len = 1;
+            tok_type = LYXP_TOKEN_BRACK2;
+
+        } else if (!strncmp(&expr[parsed], "..", 2)) {
+
+            /* '..' */
+            tok_len = 2;
+            tok_type = LYXP_TOKEN_DDOT;
+
+        } else if ((expr[parsed] == '.') && (!isdigit(expr[parsed + 1]))) {
+
+            /* '.' */
+            tok_len = 1;
+            tok_type = LYXP_TOKEN_DOT;
+
+        } else if (expr[parsed] == '@') {
+
+            /* '@' */
+            tok_len = 1;
+            tok_type = LYXP_TOKEN_AT;
+
+        } else if (expr[parsed] == ',') {
+
+            /* ',' */
+            tok_len = 1;
+            tok_type = LYXP_TOKEN_COMMA;
+
+        } else if (expr[parsed] == '\'') {
+
+            /* Literal with ' */
+            for (tok_len = 1; (expr[parsed + tok_len] != '\0') && (expr[parsed + tok_len] != '\''); ++tok_len);
+            LY_CHECK_ERR_GOTO(expr[parsed + tok_len] == '\0',
+                              LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOE, expr[parsed], &expr[parsed]), error);
+            ++tok_len;
+            tok_type = LYXP_TOKEN_LITERAL;
+
+        } else if (expr[parsed] == '\"') {
+
+            /* Literal with " */
+            for (tok_len = 1; (expr[parsed + tok_len] != '\0') && (expr[parsed + tok_len] != '\"'); ++tok_len);
+            LY_CHECK_ERR_GOTO(expr[parsed + tok_len] == '\0',
+                              LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOE, expr[parsed], &expr[parsed]), error);
+            ++tok_len;
+            tok_type = LYXP_TOKEN_LITERAL;
+
+        } else if ((expr[parsed] == '.') || (isdigit(expr[parsed]))) {
+
+            /* Number */
+            for (tok_len = 0; isdigit(expr[parsed + tok_len]); ++tok_len);
+            if (expr[parsed + tok_len] == '.') {
+                ++tok_len;
+                for (; isdigit(expr[parsed + tok_len]); ++tok_len);
+            }
+            tok_type = LYXP_TOKEN_NUMBER;
+
+        } else if (expr[parsed] == '/') {
+
+            /* Operator '/', '//' */
+            if (!strncmp(&expr[parsed], "//", 2)) {
+                tok_len = 2;
+            } else {
+                tok_len = 1;
+            }
+            tok_type = LYXP_TOKEN_OPERATOR_PATH;
+
+        } else if  (!strncmp(&expr[parsed], "!=", 2) || !strncmp(&expr[parsed], "<=", 2)
+                || !strncmp(&expr[parsed], ">=", 2)) {
+
+            /* Operator '!=', '<=', '>=' */
+            tok_len = 2;
+            tok_type = LYXP_TOKEN_OPERATOR_COMP;
+
+        } else if (expr[parsed] == '|') {
+
+            /* Operator '|' */
+            tok_len = 1;
+            tok_type = LYXP_TOKEN_OPERATOR_UNI;
+
+        } else if ((expr[parsed] == '+') || (expr[parsed] == '-')) {
+
+            /* Operator '+', '-' */
+            tok_len = 1;
+            tok_type = LYXP_TOKEN_OPERATOR_MATH;
+
+        } else if ((expr[parsed] == '=') || (expr[parsed] == '<') || (expr[parsed] == '>')) {
+
+            /* Operator '=', '<', '>' */
+            tok_len = 1;
+            tok_type = LYXP_TOKEN_OPERATOR_COMP;
+
+        } else if (ret->used && (ret->tokens[ret->used - 1] != LYXP_TOKEN_AT)
+                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_PAR1)
+                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_BRACK1)
+                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_COMMA)
+                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPERATOR_LOG)
+                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPERATOR_COMP)
+                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPERATOR_MATH)
+                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPERATOR_UNI)
+                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPERATOR_PATH)) {
+
+            /* Operator '*', 'or', 'and', 'mod', or 'div' */
+            if (expr[parsed] == '*') {
+                tok_len = 1;
+                tok_type = LYXP_TOKEN_OPERATOR_MATH;
+
+            } else if (!strncmp(&expr[parsed], "or", 2)) {
+                tok_len = 2;
+                tok_type = LYXP_TOKEN_OPERATOR_LOG;
+
+            } else if (!strncmp(&expr[parsed], "and", 3)) {
+                tok_len = 3;
+                tok_type = LYXP_TOKEN_OPERATOR_LOG;
+
+            } else if (!strncmp(&expr[parsed], "mod", 3) || !strncmp(&expr[parsed], "div", 3)) {
+                tok_len = 3;
+                tok_type = LYXP_TOKEN_OPERATOR_MATH;
+
+            } else if (prev_function_check) {
+                LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Invalid character 0x%x, perhaps \"%.*s\" is supposed to be a function call.",
+                       expr[parsed], &expr[parsed], ret->tok_len[ret->used - 1], &ret->expr[ret->tok_pos[ret->used - 1]]);
+                goto error;
+            } else {
+                LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INEXPR, expr[parsed], &expr[parsed]);
+                goto error;
+            }
+        } else if (expr[parsed] == '*') {
+
+            /* NameTest '*' */
+            tok_len = 1;
+            tok_type = LYXP_TOKEN_NAMETEST;
+
+        } else {
+
+            /* NameTest (NCName ':' '*' | QName) or NodeType/FunctionName */
+            ncname_len = parse_ncname(&expr[parsed]);
+            LY_CHECK_ERR_GOTO(!ncname_len, LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INEXPR, expr[parsed], &expr[parsed]), error);
+            tok_len = ncname_len;
+
+            if (expr[parsed + tok_len] == ':') {
+                ++tok_len;
+                if (expr[parsed + tok_len] == '*') {
+                    ++tok_len;
+                } else {
+                    ncname_len = parse_ncname(&expr[parsed + tok_len]);
+                    LY_CHECK_ERR_GOTO(!ncname_len, LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INEXPR, expr[parsed], &expr[parsed]), error);
+                    tok_len += ncname_len;
+                }
+                /* remove old flag to prevent ambiguities */
+                prev_function_check = 0;
+                tok_type = LYXP_TOKEN_NAMETEST;
+            } else {
+                /* there is no prefix so it can still be NodeType/FunctionName, we can't finally decide now */
+                prev_function_check = 1;
+                tok_type = LYXP_TOKEN_NAMETEST;
+            }
+        }
+
+        /* store the token, move on to the next one */
+        LY_CHECK_GOTO(exp_add_token(ctx, ret, tok_type, parsed, tok_len), error);
+        parsed += tok_len;
+        while (is_xmlws(expr[parsed])) {
+            ++parsed;
+        }
+
+    } while (expr[parsed]);
+
+    /* prealloc repeat */
+    ret->repeat = calloc(ret->size, sizeof *ret->repeat);
+    LY_CHECK_ERR_GOTO(!ret->repeat, LOGMEM(ctx), error);
+
+    return ret;
+
+error:
+    lyxp_expr_free(ctx, ret);
+    return NULL;
+}
+
diff --git a/src/xpath.h b/src/xpath.h
new file mode 100644
index 0000000..df3a4f9
--- /dev/null
+++ b/src/xpath.h
@@ -0,0 +1,359 @@
+/**
+ * @file xpath.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief YANG XPath evaluation functions header
+ *
+ * Copyright (c) 2015 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef _XPATH_H
+#define _XPATH_H
+
+#include <stdint.h>
+
+#include "libyang.h"
+#include "tree_schema.h"
+#if 0
+#include "tree_data.h"
+#endif
+/*
+ * XPath evaluator fully compliant with http://www.w3.org/TR/1999/REC-xpath-19991116/
+ * except the following restrictions in the grammar.
+ *
+ * PARSED GRAMMAR
+ *
+ * Full axes are not supported, abbreviated forms must be used,
+ * variables are not supported, "id()" function is not supported,
+ * and processing instruction and comment nodes are not supported,
+ * which is also reflected in the grammar. Undefined rules and
+ * constants are tokens.
+ *
+ * Modified full grammar:
+ *
+ * [1] Expr ::= OrExpr // just an alias
+ *
+ * [2] LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
+ * [3] AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath
+ * [4] RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step
+ * [5] Step ::= '@'? NodeTest Predicate* | '.' | '..'
+ * [6] NodeTest ::= NameTest | NodeType '(' ')'
+ * [7] Predicate ::= '[' Expr ']'
+ * [8] PrimaryExpr ::= '(' Expr ')' | Literal | Number | FunctionCall
+ * [9] FunctionCall ::= FunctionName '(' ( Expr ( ',' Expr )* )? ')'
+ * [10] PathExpr ::= LocationPath | PrimaryExpr Predicate*
+ *                 | PrimaryExpr Predicate* '/' RelativeLocationPath
+ *                 | PrimaryExpr Predicate* '//' RelativeLocationPath
+ * [11] OrExpr ::= AndExpr | OrExpr 'or' AndExpr
+ * [12] AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
+ * [13] EqualityExpr ::= RelationalExpr | EqualityExpr '=' RelationalExpr
+ *                     | EqualityExpr '!=' RelationalExpr
+ * [14] RelationalExpr ::= AdditiveExpr
+ *                       | RelationalExpr '<' AdditiveExpr
+ *                       | RelationalExpr '>' AdditiveExpr
+ *                       | RelationalExpr '<=' AdditiveExpr
+ *                       | RelationalExpr '>=' AdditiveExpr
+ * [15] AdditiveExpr ::= MultiplicativeExpr
+ *                     | AdditiveExpr '+' MultiplicativeExpr
+ *                     | AdditiveExpr '-' MultiplicativeExpr
+ * [16] MultiplicativeExpr ::= UnaryExpr
+ *                     | MultiplicativeExpr '*' UnaryExpr
+ *                     | MultiplicativeExpr 'div' UnaryExpr
+ *                     | MultiplicativeExpr 'mod' UnaryExpr
+ * [17] UnaryExpr ::= UnionExpr | '-' UnaryExpr
+ * [18] UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
+ */
+
+/* expression tokens allocation */
+#define LYXP_EXPR_SIZE_START 10
+#define LYXP_EXPR_SIZE_STEP 5
+
+/* XPath matches allocation */
+#define LYXP_SET_SIZE_START 2
+#define LYXP_SET_SIZE_STEP 2
+
+/* building string when casting */
+#define LYXP_STRING_CAST_SIZE_START 64
+#define LYXP_STRING_CAST_SIZE_STEP 16
+
+/**
+ * @brief Tokens that can be in an XPath expression.
+ */
+enum lyxp_token {
+    LYXP_TOKEN_NONE = 0,
+    LYXP_TOKEN_PAR1,          /* '(' */
+    LYXP_TOKEN_PAR2,          /* ')' */
+    LYXP_TOKEN_BRACK1,        /* '[' */
+    LYXP_TOKEN_BRACK2,        /* ']' */
+    LYXP_TOKEN_DOT,           /* '.' */
+    LYXP_TOKEN_DDOT,          /* '..' */
+    LYXP_TOKEN_AT,            /* '@' */
+    LYXP_TOKEN_COMMA,         /* ',' */
+    /* LYXP_TOKEN_DCOLON,      * '::' * axes not supported */
+    LYXP_TOKEN_NAMETEST,      /* NameTest */
+    LYXP_TOKEN_NODETYPE,      /* NodeType */
+    LYXP_TOKEN_FUNCNAME,      /* FunctionName */
+    LYXP_TOKEN_OPERATOR_LOG,  /* Operator 'and', 'or' */
+    LYXP_TOKEN_OPERATOR_COMP, /* Operator '=', '!=', '<', '<=', '>', '>=' */
+    LYXP_TOKEN_OPERATOR_MATH, /* Operator '+', '-', '*', 'div', 'mod', '-' (unary) */
+    LYXP_TOKEN_OPERATOR_UNI,  /* Operator '|' */
+    LYXP_TOKEN_OPERATOR_PATH, /* Operator '/', '//' */
+    /* LYXP_TOKEN_AXISNAME,    * AxisName * axes not supported */
+    LYXP_TOKEN_LITERAL,       /* Literal - with either single or double quote */
+    LYXP_TOKEN_NUMBER         /* Number */
+};
+
+/**
+ * @brief XPath (sub)expressions that can be repeated.
+ */
+enum lyxp_expr_type {
+    LYXP_EXPR_NONE = 0,
+    LYXP_EXPR_OR,
+    LYXP_EXPR_AND,
+    LYXP_EXPR_EQUALITY,
+    LYXP_EXPR_RELATIONAL,
+    LYXP_EXPR_ADDITIVE,
+    LYXP_EXPR_MULTIPLICATIVE,
+    LYXP_EXPR_UNARY,
+    LYXP_EXPR_UNION,
+};
+
+/**
+ * @brief Structure holding a parsed XPath expression.
+ */
+struct lyxp_expr {
+    enum lyxp_token *tokens; /* array of tokens */
+    uint16_t *tok_pos;       /* array of the token offsets in expr */
+    uint16_t *tok_len;       /* array of token lengths in expr */
+    enum lyxp_expr_type **repeat; /* array of expression types that this token begins and is repeated ended with 0,
+                                     more in the comment after this declaration */
+    uint16_t used;           /* used array items */
+    uint16_t size;           /* allocated array items */
+
+    const char *expr;        /* the original XPath expression */
+};
+
+/*
+ * lyxp_expr repeat
+ *
+ * This value is NULL for all the tokens that do not begin an
+ * expression which can be repeated. Otherwise it is an array
+ * of expression types that this token begins. These values
+ * are used during evaluation to know whether we need to
+ * duplicate the current context or not and to decide what
+ * the current expression is (for example, if we are only
+ * starting the parsing and the first token has no repeat,
+ * we do not parse it as an OrExpr but directly as PathExpr).
+ * Examples:
+ *
+ * Expression: "/ *[key1 and key2 or key1 < key2]"
+ * Tokens: '/',  '*',  '[',  NameTest,  'and', NameTest, 'or', NameTest,        '<',  NameTest, ']'
+ * Repeat: NULL, NULL, NULL, [AndExpr,  NULL,  NULL,     NULL, [RelationalExpr, NULL, NULL,     NULL
+ *                            OrExpr,                           0],
+ *                            0],
+ *
+ * Expression: "//node[key and node2]/key | /cont"
+ * Tokens: '//',       'NameTest', '[',  'NameTest', 'and', 'NameTest', ']',  '/',  'NameTest', '|',  '/',  'NameTest'
+ * Repeat: [UnionExpr, NULL,       NULL, [AndExpr,   NULL,  NULL,       NULL, NULL, NULL,       NULL, NULL, NULL
+ *          0],                           0],
+ *
+ * Operators between expressions which this concerns:
+ *     'or', 'and', '=', '!=', '<', '>', '<=', '>=', '+', '-', '*', 'div', 'mod', '|'
+ */
+
+/**
+ * @brief Supported types of (partial) XPath results.
+ */
+enum lyxp_set_type {
+    LYXP_SET_EMPTY = 0,
+    LYXP_SET_NODE_SET,
+    LYXP_SET_SNODE_SET,
+    LYXP_SET_BOOLEAN,
+    LYXP_SET_NUMBER,
+    LYXP_SET_STRING
+};
+#if 0
+#ifdef LY_ENABLED_CACHE
+
+/**
+ * @brief Item stored in an XPath set hash table.
+ */
+struct lyxp_set_hash_node {
+    struct lyd_node *node;
+    enum lyxp_node_type type;
+} _PACKED;
+
+#endif
+
+/**
+ * @brief XPath set - (partial) result.
+ */
+struct lyxp_set {
+    enum lyxp_set_type type;
+    union {
+        struct lyxp_set_node {
+            struct lyd_node *node;
+            enum lyxp_node_type type;
+            uint32_t pos;
+        } *nodes;
+        struct lyxp_set_snode {
+            struct lys_node *snode;
+            enum lyxp_node_type type;
+            /* 0 - snode was traversed, but not currently in the context,
+             * 1 - snode currently in context,
+             * 2 - snode in context and just added, so skip it for the current operation,
+             * >=3 - snode is not in context because we are in a predicate and this snode was used/will be used later */
+            uint32_t in_ctx;
+        } *snodes;
+        struct lyxp_set_attr {
+            struct lyd_attr *attr;
+            enum lyxp_node_type type;
+            uint32_t pos; /* if node_type is LYXP_SET_NODE_ATTR, it is the parent node position */
+        } *attrs;
+        char *str;
+        long double num;
+        int bool;
+    } val;
+
+    /* this is valid only for type LYXP_SET_NODE_SET and LYXP_SET_SNODE_SET */
+    uint32_t used;
+    uint32_t size;
+#ifdef LY_ENABLED_CACHE
+    struct hash_table *ht;
+#endif
+    /* this is valid only for type LYXP_SET_NODE_SET */
+    uint32_t ctx_pos;
+    uint32_t ctx_size;
+};
+
+/**
+ * @brief Evaluate the XPath expression \p expr on data. Be careful when using this function, the result can often
+ * be confusing without thorough understanding of XPath evaluation rules defined in RFC 6020.
+ *
+ * @param[in] expr XPath expression to evaluate. Must be in JSON format (prefixes are model names).
+ * @param[in] cur_node Current (context) data node. If the node has #LYD_VAL_INUSE flag, it is considered dummy (intended
+ * for but not restricted to evaluation with the LYXP_WHEN flag).
+ * @param[in] cur_node_type Current (context) data node type. For every standard case use #LYXP_NODE_ELEM. But there are
+ * cases when the context node \p cur_node is actually supposed to be the XML root, there is no such data node. So, in
+ * this case just pass the first top-level node into \p cur_node and use an enum value for this kind of root
+ * (#LYXP_NODE_ROOT_CONFIG if \p cur_node has config true, otherwise #LYXP_NODE_ROOT). #LYXP_NODE_TEXT and #LYXP_NODE_ATTR can also be used,
+ * but there are no use-cases in YANG.
+ * @param[in] local_mod Local module relative to the \p expr. Used only to determine the internal canonical value for identities.
+ * @param[out] set Result set. Must be valid and in the same libyang context as \p cur_node.
+ * To be safe, always either zero or cast the \p set to empty. After done using, either cast
+ * the \p set to empty (if allocated statically) or free it (if allocated dynamically) to
+ * prevent memory leaks.
+ * @param[in] options Whether to apply some evaluation restrictions.
+ * LYXP_MUST - apply must data tree access restrictions.
+ * LYXP_WHEN - apply when data tree access restrictions and consider LYD_WHEN flags in data nodes.
+ *
+ * @return EXIT_SUCCESS on success, EXIT_FAILURE on unresolved when dependency, -1 on error.
+ */
+int lyxp_eval(const char *expr, const struct lyd_node *cur_node, enum lyxp_node_type cur_node_type,
+              const struct lys_module *local_mod, struct lyxp_set *set, int options);
+
+/**
+ * @brief Get all the partial XPath nodes (atoms) that are required for \p expr to be evaluated.
+ *
+ * If any LYXP_SNODE* options is set, only fatal errors are printed, otherwise they are downgraded
+ * to warnings.
+ *
+ * @param[in] expr XPath expression to be evaluated. Must be in JSON format (prefixes are model names).
+ * @param[in] cur_snode Current (context) schema node.
+ * @param[in] cur_snode_type Current (context) schema node type.
+ * @param[out] set Result set. Must be valid and in the same libyang context as \p cur_snode.
+ * To be safe, always either zero or cast the \p set to empty. After done using, either cast
+ * the \p set to empty (if allocated statically) or free it (if allocated dynamically) to
+ * prevent memory leaks.
+ * @param[in] options Whether to apply some evaluation restrictions, one flag must always be used.
+ * LYXP_SNODE - no special data tree access modifiers.
+ * LYXP_SNODE_MUST - apply must data tree access restrictions.
+ * LYXP_SNODE_WHEN - apply when data tree access restrictions.
+ * LYXP_SNODE_OUTPUT - search RPC/action output instead input
+ * @param[out] ctx_snode Actual context node for the expression (it often changes for "when" expressions).
+ *
+ * @return EXIT_SUCCESS on success, -1 on error.
+ */
+int lyxp_atomize(const char *expr, const struct lys_node *cur_snode, enum lyxp_node_type cur_snode_type,
+                 struct lyxp_set *set, int options, const struct lys_node **ctx_snode);
+
+/* these are used only internally */
+#define LYXP_SNODE 0x04
+#define LYXP_SNODE_MUST 0x08
+#define LYXP_SNODE_WHEN 0x10
+#define LYXP_SNODE_OUTPUT 0x20
+
+#define LYXP_SNODE_ALL 0x1C
+
+/**
+ * @brief Works like lyxp_atomize(), but it is executed on all the when and must expressions
+ * which the node has.
+ *
+ * @param[in] node Node to examine.
+ * @param[in,out] set Resulting set of atoms merged from all the expressions.
+ * Will be cleared before use.
+ * @param[in] set_ext_dep_flags Whether to set #LYS_XPCONF_DEP or #LYS_XPSTATE_DEP for conditions that
+ * require foreign configuration or state subtree and also for the node itself, if it has any such condition.
+ *
+ * @return EXIT_SUCCESS on success, -1 on error.
+ */
+int lyxp_node_atomize(const struct lys_node *node, struct lyxp_set *set, int set_ext_dep_flags);
+#endif
+/**
+ * @brief Check syntax of all the XPath expressions of the node.
+ *
+ * @param[in] node Node to examine.
+ *
+ * @return LY_ERR value.
+ */
+LY_ERR lyxp_node_check_syntax(const struct lysc_node *node);
+#if 0
+/**
+ * @brief Cast XPath set to another type.
+ *        Indirectly context position aware.
+ *
+ * @param[in] set Set to cast.
+ * @param[in] target Target type to cast \p set into.
+ * @param[in] cur_node Current (context) data node. Cannot be NULL.
+ * @param[in] local_mod Local expression module.
+ * @param[in] options Whether to apply some evaluation restrictions.
+ *
+ * @return EXIT_SUCCESS on success, -1 on error.
+ */
+int lyxp_set_cast(struct lyxp_set *set, enum lyxp_set_type target, const struct lyd_node *cur_node,
+                  const struct lys_module *local_mod, int options);
+
+/**
+ * @brief Free contents of an XPath \p set.
+ *
+ * @param[in] set Set to free.
+ */
+void lyxp_set_free(struct lyxp_set *set);
+#endif
+/**
+ * @brief Parse an XPath expression into a structure of tokens.
+ *        Logs directly.
+ *
+ * http://www.w3.org/TR/1999/REC-xpath-19991116/ section 3.7
+ *
+ * @param[in] ctx Context for errors.
+ * @param[in] expr XPath expression to parse. It is duplicated.
+ *
+ * @return Filled expression structure or NULL on error.
+ */
+struct lyxp_expr *lyxp_expr_parse(struct ly_ctx *ctx, const char *expr);
+
+/**
+ * @brief Frees a parsed XPath expression. \p expr should not be used afterwards.
+ *
+ * @param[in] ctx libyang context of the expression.
+ * @param[in] expr Expression to free.
+ */
+void lyxp_expr_free(struct ly_ctx *ctx, struct lyxp_expr *expr);
+
+#endif /* _XPATH_H */