Merge remote-tracking branch 'upstream/libyang2' into libyang2
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c592d44..346527b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -216,6 +216,7 @@
     src/tree_schema_compile.c
     src/tree_schema_helpers.c
     src/parser_yang.c
+    src/parser_yin.c
     src/printer.c
     src/printer_schema.c
     src/printer_yang.c
diff --git a/src/common.c b/src/common.c
index 3c36baa..8e1c53c 100644
--- a/src/common.c
+++ b/src/common.c
@@ -100,6 +100,8 @@
     [YANG_SEMICOLON] = ";",
     [YANG_LEFT_BRACE] = "{",
     [YANG_RIGHT_BRACE] = "}",
+    [YIN_TEXT] = "text",
+    [YIN_VALUE] = "value",
 };
 
 const char *const lyext_substmt_list[] = {
diff --git a/src/common.h b/src/common.h
index 1c18a13..3ca8856 100644
--- a/src/common.h
+++ b/src/common.h
@@ -191,6 +191,11 @@
 #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_INVAL_YIN   LYVE_SYNTAX_YIN, "Invalid value \"%s\" of \"%s\"."
+#define LY_VCODE_DUPELEM     LYVE_SYNTAX_YIN, "Duplicate element \"%s\"."
+#define LY_VCODE_INCHILDSTMT_YIN LYVE_SYNTAX_YIN, "Invalid element \"%.*s\" as a child of \"%.*s\"."
+#define LY_VCODE_MISSATTR    LYVE_SYNTAX_YIN, "Missing mandatory child element \"%s\" of %s element ."
+#define LY_VCODE_UNEXP_SUBELEM LYVE_SYNTAX_YIN, "Unexpected child element \"%.*s\" of %s element."
 #define LY_VCODE_XP_EOE      LYVE_XPATH, "Unterminated string delimited with %c (%.15s)."
 #define LY_VCODE_XP_INEXPR   LYVE_XPATH, "Invalid character number %u of expression \'%s\'."
 #define LY_VCODE_DEV_NODETYPE LYVE_REFERENCE, "Invalid deviation of %s node - it is not possible to %s \"%s\" property."
@@ -305,7 +310,10 @@
     YANG_SEMICOLON,
     YANG_LEFT_BRACE,
     YANG_RIGHT_BRACE,
-    YANG_CUSTOM
+    YANG_CUSTOM,
+
+    YIN_TEXT,
+    YIN_VALUE
 };
 
 /* list of the YANG statements strings */
diff --git a/src/log.h b/src/log.h
index 26376c0..8bff336 100644
--- a/src/log.h
+++ b/src/log.h
@@ -166,6 +166,7 @@
     LYVE_SUCCESS = 0,  /**< no error */
     LYVE_SYNTAX,       /**< generic syntax error */
     LYVE_SYNTAX_YANG,  /**< YANG-related syntax error */
+    LYVE_SYNTAX_YIN,   /**< YIN-related syntax error */
     LYVE_REFERENCE,    /**< invalid referencing or using an item */
     LYVE_XPATH,        /**< invalid XPath expression */
     LYVE_SEMANTICS,    /**< generic semantic error */
diff --git a/src/parser_yang.c b/src/parser_yang.c
index dabe200..6452b10 100644
--- a/src/parser_yang.c
+++ b/src/parser_yang.c
@@ -31,38 +31,6 @@
 #include "tree_schema.h"
 #include "tree_schema_internal.h"
 
-/* Macro to check YANG's yang-char grammar rule */
-#define is_yangutf8char(c) ((c >= 0x20 && c <= 0xd77) || c == 0x09 || c == 0x0a || c == 0x0d || \
-        (c >= 0xe000 && c <= 0xfdcf) || (c >= 0xfdf0 && c <= 0xfffd) || \
-        (c >= 0x10000 && c <= 0x1fffd) || (c >= 0x20000 && c <= 0x2fffd) || \
-        (c >= 0x30000 && c <= 0x3fffd) || (c >= 0x40000 && c <= 0x2fffd) || \
-        (c >= 0x50000 && c <= 0x5fffd) || (c >= 0x60000 && c <= 0x6fffd) || \
-        (c >= 0x70000 && c <= 0x7fffd) || (c >= 0x80000 && c <= 0x8fffd) || \
-        (c >= 0x90000 && c <= 0x9fffd) || (c >= 0xa0000 && c <= 0xafffd) || \
-        (c >= 0xb0000 && c <= 0xbfffd) || (c >= 0xc0000 && c <= 0xcfffd) || \
-        (c >= 0xd0000 && c <= 0xdfffd) || (c >= 0xe0000 && c <= 0xefffd) || \
-        (c >= 0xf0000 && c <= 0xffffd) || (c >= 0x100000 && c <= 0x10fffd))
-
-/**
- * @brief Try to find object with MEMBER string matching the IDENT in the given ARRAY.
- * Macro logs an error message and returns LY_EVALID in case of existence of a matching object.
- *
- * @param[in] CTX yang parser context for logging.
- * @param[in] ARRAY [sized array](@ref sizedarrays) of a generic objects with member named MEMBER to search.
- * @param[in] MEMBER Name of the member of the objects in the ARRAY to compare.
- * @param[in] STMT Name of the compared YANG statements for logging.
- * @param[in] IDENT String trying to find in the ARRAY's objects inside the MEMBER member.
- */
-#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; \
-            } \
-        } \
-    }
-
 /**
  * @brief Insert WORD into the libyang context's dictionary and store as TARGET.
  * @param[in] CTX yang parser context to access libyang context.
@@ -102,7 +70,7 @@
         return LY_SUCCESS; \
     } \
     if (KW != YANG_LEFT_BRACE) { \
-        LOGVAL_YANG(CTX, LYVE_SYNTAX_YANG, "Invalid keyword \"%s\", expected \";\" or \"{\".", ly_stmt2str(KW)); \
+        LOGVAL_PARSER(CTX, LYVE_SYNTAX_YANG, "Invalid keyword \"%s\", expected \";\" or \"{\".", ly_stmt2str(KW)); \
         return LY_EVALID; \
     } \
     for (ERR = get_keyword(CTX, DATA, &KW, &WORD, &WORD_LEN); \
@@ -117,12 +85,7 @@
  * @param[in] PARENT parent statement where the KW is present - for logging.
  */
 #define YANG_CHECK_STMTVER2_RET(CTX, KW, PARENT) \
-    if ((CTX)->mod_version < 2) {LOGVAL_YANG((CTX), LY_VCODE_INCHILDSTMT2, KW, PARENT); return LY_EVALID;}
-
-#define YANG_CHECK_NONEMPTY(CTX, OBJECT, VALUE_LEN, STMT) \
-    if (!VALUE_LEN) { \
-        LOGWRN((CTX)->ctx, "Empty argument of %s statement does not make sense.", STMT); \
-    }
+    if ((CTX)->mod_version < 2) {LOGVAL_PARSER((CTX), LY_VCODE_INCHILDSTMT2, KW, PARENT); return LY_EVALID;}
 
 LY_ERR parse_container(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
 LY_ERR parse_uses(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_node **siblings);
@@ -169,62 +132,6 @@
 }
 
 /**
- * @brief Check that \p c is valid UTF8 code point for YANG string.
- *
- * @param[in] ctx yang parser context for logging.
- * @param[in] c UTF8 code point of a character to check.
- * @return LY_ERR values.
- */
-static LY_ERR
-check_stringchar(struct lys_parser_ctx *ctx, unsigned int c)
-{
-    if (!is_yangutf8char(c)) {
-        LOGVAL_YANG(ctx, LY_VCODE_INCHAR, c);
-        return LY_EVALID;
-    }
-    return LY_SUCCESS;
-}
-
-/**
- * @brief Check that \p c is valid UTF8 code point for YANG identifier.
- *
- * @param[in] ctx yang parser context for logging.
- * @param[in] c UTF8 code point of a character to check.
- * @param[in] first Flag to check the first character of an identifier, which is more restricted.
- * @param[in,out] prefix Storage for internally used flag in case of possible prefixed identifiers:
- * 0 - colon not yet found (no prefix)
- * 1 - \p c is the colon character
- * 2 - prefix already processed, now processing the identifier
- *
- * If the identifier cannot be prefixed, NULL is expected.
- * @return LY_ERR values.
- */
-LY_ERR
-check_identifierchar(struct lys_parser_ctx *ctx, unsigned int c, int first, int *prefix)
-{
-    if (first || (prefix && (*prefix) == 1)) {
-        if (!is_yangidentstartchar(c)) {
-            LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Invalid identifier first character '%c'.", c);
-            return LY_EVALID;
-        }
-        if (prefix) {
-            if (first) {
-                (*prefix) = 0;
-            } else {
-                (*prefix) = 2;
-            }
-        }
-    } else if (c == ':' && prefix && (*prefix) == 0) {
-        (*prefix) = 1;
-    } else if (!is_yangidentchar(c)) {
-        LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Invalid identifier character '%c'.", c);
-        return LY_EVALID;
-    }
-
-    return LY_SUCCESS;
-}
-
-/**
  * @brief Store a single UTF8 character. It depends whether in a dynamically-allocated buffer or just as a pointer to the data.
  *
  * @param[in] ctx yang parser context for logging.
@@ -237,14 +144,17 @@
  * @param[in,out] word_b Word buffer. Is kept NULL as long as it is not requested (word is a substring of the data).
  * @param[in,out] buf_len Current length of \p word_b.
  * @param[in] need_buf Flag if the dynamically allocated buffer is required.
+ * @param[in,out] prefix Storage for internally used flag in case of possible prefixed identifiers:
+ * 0 - colon not yet found (no prefix)
+ * 1 - \p c is the colon character
+ * 2 - prefix already processed, now processing the identifier
  *
  * @return LY_ERR values.
  */
 LY_ERR
-buf_store_char(struct lys_parser_ctx *ctx, const char **input, enum yang_arg arg,
-               char **word_p, size_t *word_len, char **word_b, size_t *buf_len, int need_buf)
+buf_store_char(struct lys_parser_ctx *ctx, const char **input, enum yang_arg arg, char **word_p,
+               size_t *word_len, char **word_b, size_t *buf_len, int need_buf, int *prefix)
 {
-    int prefix = 0;
     unsigned int c;
     size_t len;
 
@@ -253,7 +163,7 @@
 
     /* get UTF8 code point (and number of bytes coding the character) */
     LY_CHECK_ERR_RET(ly_getutf8(input, &c, &len),
-                     LOGVAL_YANG(ctx, LY_VCODE_INCHAR, (*input)[-len]), LY_EVALID);
+                     LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, (*input)[-len]), LY_EVALID);
     (*input) -= len;
     if (c == '\n') {
         ctx->indent = 0;
@@ -265,14 +175,14 @@
     /* check character validity */
     switch (arg) {
     case Y_IDENTIF_ARG:
-        LY_CHECK_RET(check_identifierchar(ctx, c, !(*word_len), NULL));
+        LY_CHECK_RET(lysp_check_identifierchar(ctx, c, !(*word_len), NULL));
         break;
     case Y_PREF_IDENTIF_ARG:
-        LY_CHECK_RET(check_identifierchar(ctx, c, !(*word_len), &prefix));
+        LY_CHECK_RET(lysp_check_identifierchar(ctx, c, !(*word_len), prefix));
         break;
     case Y_STR_ARG:
     case Y_MAYBE_STR_ARG:
-        LY_CHECK_RET(check_stringchar(ctx, c));
+        LY_CHECK_RET(lysp_check_stringchar(ctx, c));
         break;
     }
 
@@ -370,7 +280,7 @@
     }
 
     if (!**data && (comment > 1)) {
-        LOGVAL_YANG(ctx, LYVE_SYNTAX, "Unexpected end-of-input, non-terminated comment.");
+        LOGVAL_PARSER(ctx, LYVE_SYNTAX, "Unexpected end-of-input, non-terminated comment.");
         return LY_EVALID;
     }
 
@@ -402,6 +312,7 @@
      *         5 - string continues after +, skipping whitespaces */
     unsigned int string, block_indent = 0, current_indent = 0, need_buf = 0;
     const char *c;
+    int prefix = 0;
 
     if (**data == '\"') {
         string = 2;
@@ -423,7 +334,7 @@
                 break;
             default:
                 /* check and store character */
-                LY_CHECK_RET(buf_store_char(ctx, data, arg, word_p, word_len, word_b, buf_len, need_buf));
+                LY_CHECK_RET(buf_store_char(ctx, data, arg, word_p, word_len, word_b, buf_len, need_buf, &prefix));
                 break;
             }
             break;
@@ -445,7 +356,7 @@
                     MOVE_INPUT(ctx, data, 1);
                 } else {
                     /* check and store character */
-                    LY_CHECK_RET(buf_store_char(ctx, data, arg, word_p, word_len, word_b, buf_len, need_buf));
+                    LY_CHECK_RET(buf_store_char(ctx, data, arg, word_p, word_len, word_b, buf_len, need_buf, &prefix));
                 }
                 break;
             case '\t':
@@ -456,12 +367,12 @@
                     for (; current_indent > block_indent; --current_indent, --ctx->indent) {
                         /* store leftover spaces from the tab */
                         c = " ";
-                        LY_CHECK_RET(buf_store_char(ctx, &c, arg, word_p, word_len, word_b, buf_len, need_buf));
+                        LY_CHECK_RET(buf_store_char(ctx, &c, arg, word_p, word_len, word_b, buf_len, need_buf, &prefix));
                     }
                     ++(*data);
                 } else {
                     /* check and store character */
-                    LY_CHECK_RET(buf_store_char(ctx, data, arg, word_p, word_len, word_b, buf_len, need_buf));
+                    LY_CHECK_RET(buf_store_char(ctx, data, arg, word_p, word_len, word_b, buf_len, need_buf, &prefix));
                     /* additional characters for indentation - only 1 was count in buf_store_char */
                     ctx->indent += 7;
                 }
@@ -481,7 +392,7 @@
                 }
 
                 /* check and store character */
-                LY_CHECK_RET(buf_store_char(ctx, data, arg, word_p, word_len, word_b, buf_len, need_buf));
+                LY_CHECK_RET(buf_store_char(ctx, data, arg, word_p, word_len, word_b, buf_len, need_buf, &prefix));
 
                 /* maintain line number */
                 ++ctx->line;
@@ -494,7 +405,7 @@
                 current_indent = block_indent;
 
                 /* check and store character */
-                LY_CHECK_RET(buf_store_char(ctx, data, arg, word_p, word_len, word_b, buf_len, need_buf));
+                LY_CHECK_RET(buf_store_char(ctx, data, arg, word_p, word_len, word_b, buf_len, need_buf, &prefix));
                 break;
             }
             break;
@@ -514,12 +425,12 @@
                 c = *data;
                 break;
             default:
-                LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Double-quoted string unknown special character '\\%c'.", **data);
+                LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Double-quoted string unknown special character '\\%c'.", **data);
                 return LY_EVALID;
             }
 
             /* check and store character */
-            LY_CHECK_RET(buf_store_char(ctx, &c, arg, word_p, word_len, word_b, buf_len, need_buf));
+            LY_CHECK_RET(buf_store_char(ctx, &c, arg, word_p, word_len, word_b, buf_len, need_buf, &prefix));
 
             string = 2;
             ++(*data);
@@ -561,7 +472,7 @@
                 break;
             default:
                 /* it must be quoted again */
-                LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Both string parts divided by '+' must be quoted.");
+                LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Both string parts divided by '+' must be quoted.");
                 return LY_EVALID;
             }
             MOVE_INPUT(ctx, data, 1);
@@ -574,7 +485,7 @@
 string_end:
     if (arg <= Y_PREF_IDENTIF_ARG && !(*word_len)) {
         /* empty identifier */
-        LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Statement argument is required.");
+        LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Statement argument is required.");
         return LY_EVALID;
     }
     return LY_SUCCESS;
@@ -599,7 +510,7 @@
              uint16_t *flags, char **word_p, char **word_b, size_t *word_len)
 {
     size_t buf_len = 0;
-
+    int prefix = 0;
     /* word buffer - dynamically allocated */
     *word_b = NULL;
 
@@ -613,7 +524,7 @@
         case '\"':
             if (*word_len) {
                 /* invalid - quotes cannot be in unquoted string and only optsep, ; or { can follow it */
-                LOGVAL_YANG(ctx, LY_VCODE_INSTREXP, 1, *data,
+                LOGVAL_PARSER(ctx, LY_VCODE_INSTREXP, 1, *data,
                             "unquoted string character, optsep, semicolon or opening brace");
                 return LY_EVALID;
             }
@@ -633,7 +544,7 @@
                 LY_CHECK_RET(skip_comment(ctx, data, 2));
             } else {
                 /* not a comment after all */
-                LY_CHECK_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, &prefix));
             }
             break;
         case ' ':
@@ -673,21 +584,21 @@
                 goto str_end;
             }
 
-            LOGVAL_YANG(ctx, LY_VCODE_INSTREXP, 1, *data, "an argument");
+            LOGVAL_PARSER(ctx, LY_VCODE_INSTREXP, 1, *data, "an argument");
             return LY_EVALID;
         case '}':
             /* invalid - braces cannot be in unquoted string (opening braces terminates the string and can follow it) */
-            LOGVAL_YANG(ctx, LY_VCODE_INSTREXP, 1, *data,
+            LOGVAL_PARSER(ctx, LY_VCODE_INSTREXP, 1, *data,
                         "unquoted string character, optsep, semicolon or opening brace");
             return LY_EVALID;
         default:
-            LY_CHECK_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, &prefix));
             break;
         }
     }
 
     /* unexpected end of loop */
-    LOGVAL_YANG(ctx, LY_VCODE_EOF);
+    LOGVAL_PARSER(ctx, LY_VCODE_EOF);
     return LY_EVALID;
 
 str_end:
@@ -740,7 +651,7 @@
                 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 '/'.");
+                LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Invalid identifier first character '/'.");
                 return LY_EVALID;
             }
             continue;
@@ -765,197 +676,14 @@
         ++(*data);
     }
 
-#define IF_KW(STR, LEN, STMT) if (!strncmp(*(data), STR, LEN)) {MOVE_INPUT(ctx, data, LEN);*kw=STMT;}
-#define IF_KW_PREFIX(STR, LEN) if (!strncmp(*(data), STR, LEN)) {MOVE_INPUT(ctx, data, LEN);
-#define IF_KW_PREFIX_END }
-
 keyword_start:
     word_start = *data;
-    *kw = YANG_NONE;
+    *kw = lysp_match_kw(ctx, data);
 
-    /* read the keyword itself */
-    switch (**data) {
-    case 'a':
-        MOVE_INPUT(ctx, 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
-        break;
-    case 'b':
-        MOVE_INPUT(ctx, data, 1);
-        IF_KW("ase", 3, YANG_BASE)
-        else IF_KW("elongs-to", 9, YANG_BELONGS_TO)
-        else IF_KW("it", 2, YANG_BIT)
-        break;
-    case 'c':
-        MOVE_INPUT(ctx, 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
-        break;
-    case 'd':
-        MOVE_INPUT(ctx, 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
-        break;
-    case 'e':
-        MOVE_INPUT(ctx, 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)
-        break;
-    case 'f':
-        MOVE_INPUT(ctx, data, 1);
-        IF_KW("eature", 6, YANG_FEATURE)
-        else IF_KW("raction-digits", 14, YANG_FRACTION_DIGITS)
-        break;
-    case 'g':
-        MOVE_INPUT(ctx, data, 1);
-        IF_KW("rouping", 7, YANG_GROUPING)
-        break;
-    case 'i':
-        MOVE_INPUT(ctx, 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
-        break;
-    case 'k':
-        MOVE_INPUT(ctx, data, 1);
-        IF_KW("ey", 2, YANG_KEY)
-        break;
-    case 'l':
-        MOVE_INPUT(ctx, 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)
-        break;
-    case 'm':
-        MOVE_INPUT(ctx, 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
-        break;
-    case 'n':
-        MOVE_INPUT(ctx, data, 1);
-        IF_KW("amespace", 8, YANG_NAMESPACE)
-        else IF_KW("otification", 11, YANG_NOTIFICATION)
-        break;
-    case 'o':
-        MOVE_INPUT(ctx, 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)
-        break;
-    case 'p':
-        MOVE_INPUT(ctx, 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
-        break;
-    case 'r':
-        MOVE_INPUT(ctx, 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)
-        break;
-    case 's':
-        MOVE_INPUT(ctx, data, 1);
-        IF_KW("tatus", 5, YANG_STATUS)
-        else IF_KW("ubmodule", 8, YANG_SUBMODULE)
-        break;
-    case 't':
-        MOVE_INPUT(ctx, data, 1);
-        IF_KW("ypedef", 6, YANG_TYPEDEF)
-        else IF_KW("ype", 3, YANG_TYPE)
-        break;
-    case 'u':
-        MOVE_INPUT(ctx, 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)
-        break;
-    case 'v':
-        MOVE_INPUT(ctx, data, 1);
-        IF_KW("alue", 4, YANG_VALUE)
-        break;
-    case 'w':
-        MOVE_INPUT(ctx, data, 1);
-        IF_KW("hen", 3, YANG_WHEN)
-        break;
-    case 'y':
-        MOVE_INPUT(ctx, data, 1);
-        IF_KW("ang-version", 11, YANG_YANG_VERSION)
-        else IF_KW("in-element", 10, YANG_YIN_ELEMENT)
-        break;
-    case ';':
-        MOVE_INPUT(ctx, data, 1);
-        *kw = YANG_SEMICOLON;
+    if (*kw == YANG_SEMICOLON || *kw == YANG_LEFT_BRACE || *kw == YANG_RIGHT_BRACE) {
         goto success;
-    case '{':
-        MOVE_INPUT(ctx, data, 1);
-        *kw = YANG_LEFT_BRACE;
-        goto success;
-    case '}':
-        MOVE_INPUT(ctx, data, 1);
-        *kw = YANG_RIGHT_BRACE;
-        goto success;
-    default:
-        break;
     }
 
-#undef IF_KW
-#undef IF_KW_PREFIX
-#undef IF_KW_PREFIX_END
-
     if (*kw != YANG_NONE) {
         /* make sure we have the whole keyword */
         switch (**data) {
@@ -979,7 +707,7 @@
             /* fallthrough */
         default:
             MOVE_INPUT(ctx, data, 1);
-            LOGVAL_YANG(ctx, LY_VCODE_INSTREXP, (int)(*data - word_start), word_start,
+            LOGVAL_PARSER(ctx, LY_VCODE_INSTREXP, (int)(*data - word_start), word_start,
                         "a keyword followed by a separator");
             return LY_EVALID;
         }
@@ -989,19 +717,19 @@
 extension:
         while (**data && (**data != ' ') && (**data != '\t') && (**data != '\n') && (**data != '{') && (**data != ';')) {
             LY_CHECK_ERR_RET(ly_getutf8(data, &c, &len),
-                             LOGVAL_YANG(ctx, LY_VCODE_INCHAR, (*data)[-len]), LY_EVALID);
+                             LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, (*data)[-len]), LY_EVALID);
             ++ctx->indent;
             /* check character validity */
-            LY_CHECK_RET(check_identifierchar(ctx, c, *data - len == word_start ? 1 : 0, &prefix));
+            LY_CHECK_RET(lysp_check_identifierchar(ctx, c, *data - len == word_start ? 1 : 0, &prefix));
         }
         if (!**data) {
-            LOGVAL_YANG(ctx, LY_VCODE_EOF);
+            LOGVAL_PARSER(ctx, LY_VCODE_EOF);
             return LY_EVALID;
         }
 
         /* prefix is mandatory for extension instances */
         if (prefix != 2) {
-            LOGVAL_YANG(ctx, LY_VCODE_INSTREXP, (int)(*data - word_start), word_start, "a keyword");
+            LOGVAL_PARSER(ctx, LY_VCODE_INSTREXP, (int)(*data - word_start), word_start, "a keyword");
             return LY_EVALID;
         }
 
@@ -1133,7 +861,7 @@
     enum yang_keyword kw;
 
     if (*value) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, lyext_substmt2str(substmt));
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, lyext_substmt2str(substmt));
         return LY_EVALID;
     }
 
@@ -1149,7 +877,7 @@
             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));
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), lyext_substmt2str(substmt));
             return LY_EVALID;
         }
     }
@@ -1175,7 +903,7 @@
     enum yang_keyword kw;
 
     if (*version) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, "yang-version");
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "yang-version");
         return LY_EVALID;
     }
 
@@ -1187,7 +915,7 @@
     } else if ((word_len == 3) && !strncmp(word, "1.1", word_len)) {
         *version = LYS_VERSION_1_1;
     } else {
-        LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "yang-version");
+        LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "yang-version");
         free(buf);
         return LY_EVALID;
     }
@@ -1199,7 +927,7 @@
             LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_VERSION, 0, exts));
             break;
         default:
-            LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "yang-version");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "yang-version");
             return LY_EVALID;
         }
     }
@@ -1226,7 +954,7 @@
     enum yang_keyword kw;
 
     if (*belongsto) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, "belongs-to");
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "belongs-to");
         return LY_EVALID;
     }
 
@@ -1244,7 +972,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "belongs-to");
             return LY_EVALID;
         }
     }
@@ -1252,7 +980,7 @@
 checks:
     /* mandatory substatements */
     if (!*prefix) {
-        LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "prefix", "belongs-to");
+        LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "prefix", "belongs-to");
         return LY_EVALID;
     }
     return ret;
@@ -1277,7 +1005,7 @@
     enum yang_keyword kw;
 
     if (rev[0]) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, "revision-date");
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "revision-date");
         return LY_EVALID;
     }
 
@@ -1300,7 +1028,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "revision-date");
             return LY_EVALID;
         }
     }
@@ -1336,7 +1064,7 @@
     /* 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(module_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);
+        LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Name collision between module and submodule of name \"%s\".", inc->name);
         return LY_EVALID;
     }
 
@@ -1357,7 +1085,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "include");
             return LY_EVALID;
         }
     }
@@ -1410,14 +1138,14 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "import");
             return LY_EVALID;
         }
     }
     LY_CHECK_RET(ret);
 checks:
     /* mandatory substatements */
-    LY_CHECK_ERR_RET(!imp->prefix, LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "prefix", "import"), LY_EVALID);
+    LY_CHECK_ERR_RET(!imp->prefix, LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "prefix", "import"), LY_EVALID);
 
     return ret;
 }
@@ -1465,7 +1193,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "revision");
             return LY_EVALID;
         }
     }
@@ -1507,7 +1235,7 @@
             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));
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), lyext_substmt2str(substmt));
             return LY_EVALID;
         }
     }
@@ -1533,7 +1261,7 @@
     enum yang_keyword kw;
 
     if (*flags & LYS_CONFIG_MASK) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, "config");
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "config");
         return LY_EVALID;
     }
 
@@ -1545,7 +1273,7 @@
     } else if ((word_len == 5) && !strncmp(word, "false", word_len)) {
         *flags |= LYS_CONFIG_R;
     } else {
-        LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "config");
+        LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "config");
         free(buf);
         return LY_EVALID;
     }
@@ -1557,7 +1285,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "config");
             return LY_EVALID;
         }
     }
@@ -1583,7 +1311,7 @@
     enum yang_keyword kw;
 
     if (*flags & LYS_MAND_MASK) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, "mandatory");
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "mandatory");
         return LY_EVALID;
     }
 
@@ -1595,7 +1323,7 @@
     } else if ((word_len == 5) && !strncmp(word, "false", word_len)) {
         *flags |= LYS_MAND_FALSE;
     } else {
-        LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "mandatory");
+        LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "mandatory");
         free(buf);
         return LY_EVALID;
     }
@@ -1607,7 +1335,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "mandatory");
             return LY_EVALID;
         }
     }
@@ -1635,7 +1363,7 @@
     /* get value */
     LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
-    YANG_CHECK_NONEMPTY(ctx, NULL, word_len, ly_stmt2str(restr_kw));
+    YANG_CHECK_NONEMPTY(ctx, word_len, ly_stmt2str(restr_kw));
     INSERT_WORD(ctx, buf, restr->arg, word, word_len);
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
         switch (kw) {
@@ -1655,7 +1383,7 @@
             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));
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), ly_stmt2str(restr_kw));
             return LY_EVALID;
         }
     }
@@ -1700,7 +1428,7 @@
     enum yang_keyword kw;
 
     if (*flags & LYS_STATUS_MASK) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, "status");
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "status");
         return LY_EVALID;
     }
 
@@ -1714,7 +1442,7 @@
     } else if ((word_len == 8) && !strncmp(word, "obsolete", word_len)) {
         *flags |= LYS_STATUS_OBSLT;
     } else {
-        LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "status");
+        LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "status");
         free(buf);
         return LY_EVALID;
     }
@@ -1726,7 +1454,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "status");
             return LY_EVALID;
         }
     }
@@ -1752,7 +1480,7 @@
     struct lysp_when *when;
 
     if (*when_p) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, "when");
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "when");
         return LY_EVALID;
     }
 
@@ -1761,7 +1489,7 @@
 
     /* get value */
     LY_CHECK_ERR_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len), free(when), LY_EMEM);
-    YANG_CHECK_NONEMPTY(ctx, when, word_len, "when");
+    YANG_CHECK_NONEMPTY(ctx, word_len, "when");
     INSERT_WORD(ctx, buf, when->cond, word, word_len);
 
     *when_p = when;
@@ -1778,7 +1506,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "when");
             return LY_EVALID;
         }
     }
@@ -1853,7 +1581,7 @@
             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),
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw),
                    (any->nodetype & LYS_ANYDATA) == LYS_ANYDATA ? ly_stmt2str(YANG_ANYDATA) : ly_stmt2str(YANG_ANYXML));
             return LY_EVALID;
         }
@@ -1885,7 +1613,7 @@
     enum yang_keyword kw;
 
     if (*flags & LYS_SET_VALUE) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(val_kw));
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(val_kw));
         return LY_EVALID;
     }
     *flags |= LYS_SET_VALUE;
@@ -1894,7 +1622,7 @@
     LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &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));
+        LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, ly_stmt2str(val_kw));
         goto error;
     }
 
@@ -1902,23 +1630,23 @@
     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));
+            LOGVAL_PARSER(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));
+            LOGVAL_PARSER(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));
+        LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, ly_stmt2str(val_kw));
         goto error;
     }
     if (errno == ERANGE) {
-        LOGVAL_YANG(ctx, LY_VCODE_OOB, word_len, word, ly_stmt2str(val_kw));
+        LOGVAL_PARSER(ctx, LY_VCODE_OOB, word_len, word, ly_stmt2str(val_kw));
         goto error;
     }
     if (val_kw == YANG_VALUE) {
@@ -1934,7 +1662,7 @@
             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));
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), ly_stmt2str(val_kw));
             return LY_EVALID;
         }
     }
@@ -1960,7 +1688,7 @@
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
-    size_t word_len, u;
+    size_t word_len;
     enum yang_keyword kw;
     struct lysp_type_enum *enm;
 
@@ -1969,29 +1697,13 @@
     /* get value */
     LY_CHECK_RET(get_argument(ctx, data, enum_kw == YANG_ENUM ? Y_STR_ARG : Y_IDENTIF_ARG, NULL, &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;
-                }
-            }
-        }
+        ret = lysp_check_enum_name(ctx, (const char *)word, word_len);
+        LY_CHECK_ERR_RET(ret, free(buf), ret);
     } else { /* YANG_BIT */
 
     }
     if (enum_kw == YANG_ENUM) {
-        YANG_CHECK_NONEMPTY(ctx, NULL, word_len, "enum");
+        YANG_CHECK_NONEMPTY(ctx, word_len, "enum");
     }
     INSERT_WORD(ctx, buf, enm->name, word, word_len);
     CHECK_UNIQUENESS(ctx, *enums, name, ly_stmt2str(enum_kw), enm->name);
@@ -2012,14 +1724,20 @@
             LY_CHECK_RET(parse_status(ctx, data, &enm->flags, &enm->exts));
             break;
         case YANG_VALUE:
+            LY_CHECK_ERR_RET(enum_kw == YANG_BIT, LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw),
+                             ly_stmt2str(enum_kw)), LY_EVALID);
+            LY_CHECK_RET(parse_type_enum_value_pos(ctx, data, kw, &enm->value, &enm->flags, &enm->exts));
+            break;
         case YANG_POSITION:
+            LY_CHECK_ERR_RET(enum_kw == YANG_ENUM, LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw),
+                             ly_stmt2str(enum_kw)), LY_EVALID);
             LY_CHECK_RET(parse_type_enum_value_pos(ctx, data, kw, &enm->value, &enm->flags, &enm->exts));
             break;
         case YANG_CUSTOM:
             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));
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), ly_stmt2str(enum_kw));
             return LY_EVALID;
         }
     }
@@ -2046,7 +1764,7 @@
     enum yang_keyword kw;
 
     if (*fracdig) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, "fraction-digits");
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "fraction-digits");
         return LY_EVALID;
     }
 
@@ -2054,7 +1772,7 @@
     LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     if (!word_len || (word[0] == '0') || !isdigit(word[0])) {
-        LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "fraction-digits");
+        LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "fraction-digits");
         free(buf);
         return LY_EVALID;
     }
@@ -2063,12 +1781,12 @@
     num = strtoul(word, &ptr, 10);
     /* we have not parsed the whole argument */
     if ((size_t)(ptr - word) != word_len) {
-        LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "fraction-digits");
+        LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "fraction-digits");
         free(buf);
         return LY_EVALID;
     }
     if ((errno == ERANGE) || (num > 18)) {
-        LOGVAL_YANG(ctx, LY_VCODE_OOB, word_len, word, "fraction-digits");
+        LOGVAL_PARSER(ctx, LY_VCODE_OOB, word_len, word, "fraction-digits");
         free(buf);
         return LY_EVALID;
     }
@@ -2081,7 +1799,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "fraction-digits");
             return LY_EVALID;
         }
     }
@@ -2109,7 +1827,7 @@
     enum yang_keyword kw;
 
     if (*flags & LYS_SET_REQINST) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, "require-instance");
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "require-instance");
         return LY_EVALID;
     }
     *flags |= LYS_SET_REQINST;
@@ -2120,7 +1838,7 @@
     if ((word_len == 4) && !strncmp(word, "true", word_len)) {
         *reqinst = 1;
     } else if ((word_len != 5) || strncmp(word, "false", word_len)) {
-        LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "require-instance");
+        LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "require-instance");
         free(buf);
         return LY_EVALID;
     }
@@ -2132,7 +1850,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "require-instance");
             return LY_EVALID;
         }
     }
@@ -2158,7 +1876,7 @@
     enum yang_keyword kw;
 
     if ((*pat)[0] == 0x15) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, "modifier");
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "modifier");
         return LY_EVALID;
     }
 
@@ -2166,7 +1884,7 @@
     LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
 
     if ((word_len != 12) || strncmp(word, "invert-match", word_len)) {
-        LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "modifier");
+        LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "modifier");
         free(buf);
         return LY_EVALID;
     }
@@ -2188,7 +1906,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "modifier");
             return LY_EVALID;
         }
     }
@@ -2253,7 +1971,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "pattern");
             return LY_EVALID;
         }
     }
@@ -2279,7 +1997,7 @@
     struct lysp_type *nest_type;
 
     if (type->name) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, "type");
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "type");
         return LY_EVALID;
     }
 
@@ -2307,7 +2025,7 @@
             break;
         case YANG_LENGTH:
             if (type->length) {
-                LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(kw));
+                LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(kw));
                 return LY_EVALID;
             }
             type->length = calloc(1, sizeof *type->length);
@@ -2326,11 +2044,11 @@
             break;
         case YANG_RANGE:
             if (type->range) {
-                LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(kw));
+                LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(kw));
                 return LY_EVALID;
             }
             type->range = calloc(1, sizeof *type->range);
-            LY_CHECK_ERR_RET(!type->range, LOGMEM(ctx->ctx), LY_EVALID);
+            LY_CHECK_ERR_RET(!type->range, LOGMEM(ctx->ctx), LY_EMEM);
 
             LY_CHECK_RET(parse_restr(ctx, data, kw, type->range));
             type->flags |= LYS_SET_RANGE;
@@ -2348,7 +2066,7 @@
             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), "type");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "type");
             return LY_EVALID;
         }
     }
@@ -2432,7 +2150,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "leaf");
             return LY_EVALID;
         }
     }
@@ -2440,11 +2158,11 @@
 checks:
     /* mandatory substatements */
     if (!leaf->type.name) {
-        LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "type", "leaf");
+        LOGVAL_PARSER(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");
+        LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMSCOMB, "mandatory", "default", "leaf");
         return LY_EVALID;
     }
 
@@ -2472,7 +2190,7 @@
     enum yang_keyword kw;
 
     if (*flags & LYS_SET_MAX) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, "max-elements");
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "max-elements");
         return LY_EVALID;
     }
     *flags |= LYS_SET_MAX;
@@ -2481,7 +2199,7 @@
     LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &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");
+        LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "max-elements");
         free(buf);
         return LY_EVALID;
     }
@@ -2491,12 +2209,12 @@
         num = strtoul(word, &ptr, 10);
         /* we have not parsed the whole argument */
         if ((size_t)(ptr - word) != word_len) {
-            LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "max-elements");
+            LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "max-elements");
             free(buf);
             return LY_EVALID;
         }
         if ((errno == ERANGE) || (num > UINT32_MAX)) {
-            LOGVAL_YANG(ctx, LY_VCODE_OOB, word_len, word, "max-elements");
+            LOGVAL_PARSER(ctx, LY_VCODE_OOB, word_len, word, "max-elements");
             free(buf);
             return LY_EVALID;
         }
@@ -2511,7 +2229,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "max-elements");
             return LY_EVALID;
         }
     }
@@ -2539,7 +2257,7 @@
     enum yang_keyword kw;
 
     if (*flags & LYS_SET_MIN) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, "min-elements");
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "min-elements");
         return LY_EVALID;
     }
     *flags |= LYS_SET_MIN;
@@ -2548,7 +2266,7 @@
     LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &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");
+        LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "min-elements");
         free(buf);
         return LY_EVALID;
     }
@@ -2557,12 +2275,12 @@
     num = strtoul(word, &ptr, 10);
     /* we have not parsed the whole argument */
     if ((size_t)(ptr - word) != word_len) {
-        LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "min-elements");
+        LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "min-elements");
         free(buf);
         return LY_EVALID;
     }
     if ((errno == ERANGE) || (num > UINT32_MAX)) {
-        LOGVAL_YANG(ctx, LY_VCODE_OOB, word_len, word, "min-elements");
+        LOGVAL_PARSER(ctx, LY_VCODE_OOB, word_len, word, "min-elements");
         free(buf);
         return LY_EVALID;
     }
@@ -2575,7 +2293,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "min-elements");
             return LY_EVALID;
         }
     }
@@ -2601,7 +2319,7 @@
     enum yang_keyword kw;
 
     if (*flags & LYS_ORDBY_MASK) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, "ordered-by");
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "ordered-by");
         return LY_EVALID;
     }
 
@@ -2613,7 +2331,7 @@
     } else if ((word_len == 4) && !strncmp(word, "user", word_len)) {
         *flags |= LYS_ORDBY_USER;
     } else {
-        LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "ordered-by");
+        LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "ordered-by");
         free(buf);
         return LY_EVALID;
     }
@@ -2625,7 +2343,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "ordered-by");
             return LY_EVALID;
         }
     }
@@ -2716,7 +2434,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "llist");
             return LY_EVALID;
         }
     }
@@ -2724,15 +2442,15 @@
 checks:
     /* mandatory substatements */
     if (!llist->type.name) {
-        LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "type", "leaf-list");
+        LOGVAL_PARSER(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");
+        LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMSCOMB, "min-elements", "default", "leaf-list");
         return LY_EVALID;
     }
     if (llist->max && llist->min > llist->max) {
-        LOGVAL_YANG(ctx, LYVE_SEMANTICS,
+        LOGVAL_PARSER(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;
@@ -2763,7 +2481,7 @@
 
     /* get value */
     LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
-    YANG_CHECK_NONEMPTY(ctx, NULL, word_len, "refine");
+    YANG_CHECK_NONEMPTY(ctx, word_len, "refine");
     INSERT_WORD(ctx, buf, rf->nodeid, word, word_len);
 
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
@@ -2803,7 +2521,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "refine");
             return LY_EVALID;
         }
     }
@@ -2859,7 +2577,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "typedef");
             return LY_EVALID;
         }
     }
@@ -2867,7 +2585,7 @@
 checks:
     /* mandatory substatements */
     if (!tpdf->type.name) {
-        LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "type", "typedef");
+        LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "type", "typedef");
         return LY_EVALID;
     }
 
@@ -2898,7 +2616,7 @@
     enum yang_keyword kw;
 
     if (inout_p->nodetype) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(inout_kw));
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(inout_kw));
         return LY_EVALID;
     }
 
@@ -2947,7 +2665,7 @@
             LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &inout_p->exts));
             break;
         default:
-            LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), ly_stmt2str(inout_kw));
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), ly_stmt2str(inout_kw));
             return LY_EVALID;
         }
     }
@@ -3017,7 +2735,7 @@
             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), parent ? "action" : "rpc");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), parent ? "action" : "rpc");
             return LY_EVALID;
         }
     }
@@ -3109,7 +2827,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "notification");
             return LY_EVALID;
         }
     }
@@ -3202,7 +2920,7 @@
             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), "grouping");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "grouping");
             return LY_EVALID;
         }
     }
@@ -3236,7 +2954,7 @@
 
     /* get value */
     LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
-    YANG_CHECK_NONEMPTY(ctx, NULL, word_len, "augment");
+    YANG_CHECK_NONEMPTY(ctx, word_len, "augment");
     INSERT_WORD(ctx, buf, aug->nodeid, word, word_len);
     aug->nodetype = LYS_AUGMENT;
     aug->parent = parent;
@@ -3299,7 +3017,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "augment");
             return LY_EVALID;
         }
     }
@@ -3477,7 +3195,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "uses");
             return LY_EVALID;
         }
     }
@@ -3572,7 +3290,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "case");
             return LY_EVALID;
         }
     }
@@ -3673,14 +3391,14 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "choice");
             return LY_EVALID;
         }
     }
     LY_CHECK_RET(ret);
 checks:
     if ((choice->flags & LYS_MAND_TRUE) && choice->dflt) {
-        LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMSCOMB, "mandatory", "default", "choice");
+        LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMSCOMB, "mandatory", "default", "choice");
         return LY_EVALID;
     }
     return ret;
@@ -3794,7 +3512,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "container");
             return LY_EVALID;
         }
     }
@@ -3924,7 +3642,7 @@
             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), "list");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "list");
             return LY_EVALID;
         }
     }
@@ -3934,7 +3652,7 @@
     LY_CHECK_RET(parse_finalize_reallocated(ctx, list->groupings, NULL, list->actions, list->notifs));
 
     if (list->max && list->min > list->max) {
-        LOGVAL_YANG(ctx, LYVE_SEMANTICS,
+        LOGVAL_PARSER(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;
@@ -3962,7 +3680,7 @@
     enum yang_keyword kw;
 
     if (*flags & LYS_YINELEM_MASK) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, "yin-element");
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "yin-element");
         return LY_EVALID;
     }
 
@@ -3974,7 +3692,7 @@
     } else if ((word_len == 5) && !strncmp(word, "false", word_len)) {
         *flags |= LYS_YINELEM_FALSE;
     } else {
-        LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "yin-element");
+        LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "yin-element");
         free(buf);
         return LY_EVALID;
     }
@@ -3986,7 +3704,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "yin-element");
             return LY_EVALID;
         }
     }
@@ -3994,7 +3712,7 @@
 }
 
 /**
- * @brief Parse the yin-element statement.
+ * @brief Parse the argument statement.
  *
  * @param[in] ctx yang parser context for logging.
  * @param[in,out] data Data to read from, always moved to currently handled character.
@@ -4013,12 +3731,12 @@
     enum yang_keyword kw;
 
     if (*argument) {
-        LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, "argument");
+        LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, "argument");
         return LY_EVALID;
     }
 
     /* get value */
-    LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
+    LY_CHECK_RET(get_argument(ctx, data, Y_IDENTIF_ARG, NULL, &word, &buf, &word_len));
     INSERT_WORD(ctx, buf, *argument, word, word_len);
 
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret,) {
@@ -4030,7 +3748,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "argument");
             return LY_EVALID;
         }
     }
@@ -4079,7 +3797,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "extension");
             return LY_EVALID;
         }
     }
@@ -4123,7 +3841,7 @@
     } else if ((word_len == 6) && !strncmp(word, "delete", word_len)) {
         dev_mod = LYS_DEV_DELETE;
     } else {
-        LOGVAL_YANG(ctx, LY_VCODE_INVAL, word_len, word, "deviate");
+        LOGVAL_PARSER(ctx, LY_VCODE_INVAL, word_len, word, "deviate");
         free(buf);
         return LY_EVALID;
     }
@@ -4186,7 +3904,7 @@
             switch (dev_mod) {
             case LYS_DEV_NOT_SUPPORTED:
             case LYS_DEV_DELETE:
-                LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
+                LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             default:
                 LY_CHECK_RET(parse_config(ctx, data, d_flags, &d->exts));
@@ -4196,7 +3914,7 @@
         case YANG_DEFAULT:
             switch (dev_mod) {
             case LYS_DEV_NOT_SUPPORTED:
-                LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
+                LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             case LYS_DEV_REPLACE:
                 LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_DEFAULT, 0, &d_rpl->dflt, Y_STR_ARG, &d->exts));
@@ -4210,7 +3928,7 @@
             switch (dev_mod) {
             case LYS_DEV_NOT_SUPPORTED:
             case LYS_DEV_DELETE:
-                LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
+                LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             default:
                 LY_CHECK_RET(parse_mandatory(ctx, data, d_flags, &d->exts));
@@ -4221,7 +3939,7 @@
             switch (dev_mod) {
             case LYS_DEV_NOT_SUPPORTED:
             case LYS_DEV_DELETE:
-                LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
+                LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             default:
                 LY_CHECK_RET(parse_maxelements(ctx, data, d_max, d_flags, &d->exts));
@@ -4232,7 +3950,7 @@
             switch (dev_mod) {
             case LYS_DEV_NOT_SUPPORTED:
             case LYS_DEV_DELETE:
-                LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
+                LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             default:
                 LY_CHECK_RET(parse_minelements(ctx, data, d_min, d_flags, &d->exts));
@@ -4243,7 +3961,7 @@
             switch (dev_mod) {
             case LYS_DEV_NOT_SUPPORTED:
             case LYS_DEV_REPLACE:
-                LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
+                LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             default:
                 LY_CHECK_RET(parse_restrs(ctx, data, kw, d_musts));
@@ -4255,11 +3973,11 @@
             case LYS_DEV_NOT_SUPPORTED:
             case LYS_DEV_ADD:
             case LYS_DEV_DELETE:
-                LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
+                LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             default:
                 if (d_rpl->type) {
-                    LOGVAL_YANG(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(kw));
+                    LOGVAL_PARSER(ctx, LY_VCODE_DUPSTMT, ly_stmt2str(kw));
                     return LY_EVALID;
                 }
                 d_rpl->type = calloc(1, sizeof *d_rpl->type);
@@ -4272,7 +3990,7 @@
             switch (dev_mod) {
             case LYS_DEV_NOT_SUPPORTED:
             case LYS_DEV_REPLACE:
-                LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
+                LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             default:
                 LY_CHECK_RET(parse_text_fields(ctx, data, LYEXT_SUBSTMT_UNIQUE, d_uniques, Y_STR_ARG, &d->exts));
@@ -4282,7 +4000,7 @@
         case YANG_UNITS:
             switch (dev_mod) {
             case LYS_DEV_NOT_SUPPORTED:
-                LOGVAL_YANG(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
+                LOGVAL_PARSER(ctx, LY_VCODE_INDEV, ly_devmod2str(dev_mod), ly_stmt2str(kw));
                 return LY_EVALID;
             default:
                 LY_CHECK_RET(parse_text_field(ctx, data, LYEXT_SUBSTMT_UNITS, 0, d_units, Y_STR_ARG, &d->exts));
@@ -4293,7 +4011,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "deviate");
             return LY_EVALID;
         }
     }
@@ -4322,7 +4040,7 @@
 
     /* get value */
     LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, NULL, &word, &buf, &word_len));
-    YANG_CHECK_NONEMPTY(ctx, NULL, word_len, "deviation");
+    YANG_CHECK_NONEMPTY(ctx, word_len, "deviation");
     INSERT_WORD(ctx, buf, dev->nodeid, word, word_len);
 
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret, goto checks) {
@@ -4340,7 +4058,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "deviation");
             return LY_EVALID;
         }
     }
@@ -4348,7 +4066,7 @@
 checks:
     /* mandatory substatements */
     if (!dev->deviates) {
-        LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "deviate", "deviation");
+        LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "deviate", "deviation");
         return LY_EVALID;
     }
 
@@ -4397,7 +4115,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "feature");
             return LY_EVALID;
         }
     }
@@ -4445,7 +4163,7 @@
             break;
         case YANG_BASE:
             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");
+                LOGVAL_PARSER(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));
@@ -4454,7 +4172,7 @@
             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");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "identity");
             return LY_EVALID;
         }
     }
@@ -4487,7 +4205,7 @@
     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
+        if (mod_stmt > SECTION) {LOGVAL_PARSER(ctx, LY_VCODE_INORD, ly_stmt2str(kw), ly_stmt2str(prev_kw)); return LY_EVALID;}mod_stmt = SECTION
 
         switch (kw) {
         /* module header */
@@ -4641,7 +4359,7 @@
             break;
 
         default:
-            LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "module");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "module");
             return LY_EVALID;
         }
     }
@@ -4653,10 +4371,10 @@
 
     /* mandatory substatements */
     if (!mod->mod->ns) {
-        LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "namespace", "module");
+        LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "namespace", "module");
         return LY_EVALID;
     } else if (!mod->mod->prefix) {
-        LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "prefix", "module");
+        LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "prefix", "module");
         return LY_EVALID;
     }
 
@@ -4664,7 +4382,7 @@
      * a submodule of the same name in the context, no need for revision matching */
     dup = ly_ctx_get_submodule(ctx->ctx, NULL, mod->mod->name, NULL);
     if (dup) {
-        LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Name collision between module and submodule of name \"%s\".", mod->mod->name);
+        LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Name collision between module and submodule of name \"%s\".", mod->mod->name);
         return LY_EVALID;
     }
 
@@ -4697,7 +4415,7 @@
     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
+        if (mod_stmt > SECTION) {LOGVAL_PARSER(ctx, LY_VCODE_INORD, ly_stmt2str(kw), ly_stmt2str(prev_kw)); return LY_EVALID;}mod_stmt = SECTION
 
         switch (kw) {
         /* module header */
@@ -4847,7 +4565,7 @@
             break;
 
         default:
-            LOGVAL_YANG(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "submodule");
+            LOGVAL_PARSER(ctx, LY_VCODE_INCHILDSTMT, ly_stmt2str(kw), "submodule");
             return LY_EVALID;
         }
     }
@@ -4859,7 +4577,7 @@
 
     /* mandatory substatements */
     if (!submod->belongsto) {
-        LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "belongs-to", "submodule");
+        LOGVAL_PARSER(ctx, LY_VCODE_MISSTMT, "belongs-to", "submodule");
         return LY_EVALID;
     }
 
@@ -4867,7 +4585,7 @@
      * a submodule of the same name in the context, no need for revision matching */
     dup = ly_ctx_get_submodule(ctx->ctx, NULL, submod->name, NULL);
     if (dup && strcmp(dup->belongsto, submod->belongsto)) {
-        LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Name collision between submodules of name \"%s\".", dup->name);
+        LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Name collision between submodules of name \"%s\".", dup->name);
         return LY_EVALID;
     }
 
@@ -4892,7 +4610,7 @@
         ret = LY_EINVAL;
         goto cleanup;
     } else if (kw != YANG_SUBMODULE) {
-        LOGVAL_YANG(context, LYVE_SYNTAX, "Invalid keyword \"%s\", expected \"module\" or \"submodule\".",
+        LOGVAL_PARSER(context, LYVE_SYNTAX, "Invalid keyword \"%s\", expected \"module\" or \"submodule\".",
                ly_stmt2str(kw));
         ret = LY_EVALID;
         goto cleanup;
@@ -4911,7 +4629,7 @@
         data++;
     }
     if (*data) {
-        LOGVAL_YANG(context, LYVE_SYNTAX, "Trailing garbage \"%.*s%s\" after submodule, expected end-of-input.",
+        LOGVAL_PARSER(context, LYVE_SYNTAX, "Trailing garbage \"%.*s%s\" after submodule, expected end-of-input.",
                     15, data, strlen(data) > 15 ? "..." : "");
         ret = LY_EVALID;
         goto cleanup;
@@ -4946,7 +4664,7 @@
         ret = LY_EINVAL;
         goto cleanup;
     } else if (kw != YANG_MODULE) {
-        LOGVAL_YANG(context, LYVE_SYNTAX, "Invalid keyword \"%s\", expected \"module\" or \"submodule\".",
+        LOGVAL_PARSER(context, LYVE_SYNTAX, "Invalid keyword \"%s\", expected \"module\" or \"submodule\".",
                ly_stmt2str(kw));
         ret = LY_EVALID;
         goto cleanup;
@@ -4966,7 +4684,7 @@
         data++;
     }
     if (*data) {
-        LOGVAL_YANG(context, LYVE_SYNTAX, "Trailing garbage \"%.*s%s\" after module, expected end-of-input.",
+        LOGVAL_PARSER(context, LYVE_SYNTAX, "Trailing garbage \"%.*s%s\" after module, expected end-of-input.",
                     15, data, strlen(data) > 15 ? "..." : "");
         ret = LY_EVALID;
         goto cleanup;
diff --git a/src/parser_yin.c b/src/parser_yin.c
new file mode 100644
index 0000000..ecff7e6
--- /dev/null
+++ b/src/parser_yin.c
@@ -0,0 +1,1677 @@
+/**
+ * @file parser_yin.c
+ * @author David Sedlák <xsedla1d@stud.fit.vutbr.cz>
+ * @brief YIN parser.
+ *
+ * Copyright (c) 2015 - 2019 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 <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include "context.h"
+#include "dict.h"
+#include "xml.h"
+#include "tree.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "parser_yin.h"
+
+/**
+ * @brief check if given string is URI of yin namespace.
+ * @param ns Namespace URI to check.
+ *
+ * @return true if ns equals YIN_NS_URI false otherwise.
+ */
+#define IS_YIN_NS(ns) (strcmp(ns, YIN_NS_URI) == 0)
+
+static LY_ERR
+yin_parse_config(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data, uint16_t *flags,
+                 struct lysp_ext_instance **exts);
+
+const char *const yin_attr_list[] = {
+    [YIN_ARG_NAME] = "name",
+    [YIN_ARG_TARGET_NODE] = "target-node",
+    [YIN_ARG_MODULE] = "module",
+    [YIN_ARG_VALUE] = "value",
+    [YIN_ARG_TEXT] = "text",
+    [YIN_ARG_CONDITION] = "condition",
+    [YIN_ARG_URI] = "uri",
+    [YIN_ARG_DATE] = "date",
+    [YIN_ARG_TAG] = "tag",
+};
+
+enum yang_keyword
+yin_match_keyword(struct yin_parser_ctx *ctx, const char *name, size_t name_len,
+                  const char *prefix, size_t prefix_len, enum yang_keyword parrent)
+{
+    const char *start = NULL;
+    enum yang_keyword kw = YANG_NONE;
+    const struct lyxml_ns *ns = NULL;
+
+    if (!name || name_len == 0) {
+        return YANG_NONE;
+    }
+
+    ns = lyxml_ns_get(&ctx->xml_ctx, prefix, prefix_len);
+    if (ns) {
+        if (!IS_YIN_NS(ns->uri)) {
+            return YANG_CUSTOM;
+        }
+    } else {
+        /* elements without namespace are automatically unknown */
+        return YANG_NONE;
+    }
+
+    start = name;
+    kw = lysp_match_kw(NULL, &name);
+
+    if (name - start == (long int)name_len) {
+        /* this is done because of collision in yang statement value and yang argument mapped to yin element value */
+        if (kw == YANG_VALUE && parrent == YANG_ERROR_MESSAGE) {
+            return YIN_VALUE;
+        }
+        return kw;
+    } else {
+        if (strncmp(start, "text", name_len) == 0) {
+            return YIN_TEXT;
+        } else if (strncmp(start, "value", name_len) == 0) {
+            return YIN_VALUE;
+        } else {
+            return YANG_NONE;
+        }
+    }
+}
+
+enum YIN_ARGUMENT
+yin_match_argument_name(const char *name, size_t len)
+{
+    enum YIN_ARGUMENT arg = YIN_ARG_UNKNOWN;
+    size_t already_read = 0;
+    LY_CHECK_RET(len == 0, YIN_ARG_NONE);
+
+#define IF_ARG(STR, LEN, STMT) if (!strncmp((name) + already_read, STR, LEN)) {already_read+=LEN;arg=STMT;}
+#define IF_ARG_PREFIX(STR, LEN) if (!strncmp((name) + already_read, STR, LEN)) {already_read+=LEN;
+#define IF_ARG_PREFIX_END }
+
+    switch (*name) {
+    case 'c':
+        already_read += 1;
+        IF_ARG("ondition", 8, YIN_ARG_CONDITION);
+        break;
+
+    case 'd':
+        already_read += 1;
+        IF_ARG("ate", 3, YIN_ARG_DATE);
+        break;
+
+    case 'm':
+        already_read += 1;
+        IF_ARG("odule", 5, YIN_ARG_MODULE);
+        break;
+
+    case 'n':
+        already_read += 1;
+        IF_ARG("ame", 3, YIN_ARG_NAME);
+        break;
+
+    case 't':
+        already_read += 1;
+        IF_ARG_PREFIX("a", 1)
+            IF_ARG("g", 1, YIN_ARG_TAG)
+            else IF_ARG("rget-node", 9, YIN_ARG_TARGET_NODE)
+        IF_ARG_PREFIX_END
+        else IF_ARG("ext", 3, YIN_ARG_TEXT)
+        break;
+
+    case 'u':
+        already_read += 1;
+        IF_ARG("ri", 2, YIN_ARG_URI)
+        break;
+
+    case 'v':
+        already_read += 1;
+        IF_ARG("alue", 4, YIN_ARG_VALUE);
+        break;
+    }
+
+    /* whole argument must be matched */
+    if (already_read != len) {
+        arg = YIN_ARG_UNKNOWN;
+    }
+
+#undef IF_ARG
+#undef IF_ARG_PREFIX
+#undef IF_ARG_PREFIX_END
+
+    return arg;
+}
+
+/**
+ * @brief free argument record, content loaded from lyxml_get_string() can be
+ * dynamically allocated in some cases so it must be also freed.
+ */
+static void free_arg_rec(struct yin_parser_ctx *ctx, struct yin_arg_record *record) {
+    (void)ctx; /* unused */
+    if (record->dynamic_content) {
+        free(record->content);
+    }
+}
+
+LY_ERR
+yin_load_attributes(struct yin_parser_ctx *ctx, const char **data, struct yin_arg_record **attrs)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct yin_arg_record *argument_record = NULL;
+    struct sized_string prefix, name;
+
+    /* load all attributes */
+    while (ctx->xml_ctx.status == LYXML_ATTRIBUTE) {
+        ret = lyxml_get_attribute(&ctx->xml_ctx, data, &prefix.value, &prefix.len, &name.value, &name.len);
+        LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+
+        if (ctx->xml_ctx.status == LYXML_ATTR_CONTENT) {
+            LY_ARRAY_NEW_GOTO(ctx->xml_ctx.ctx, *attrs, argument_record, ret, cleanup);
+            argument_record->name = name.value;
+            argument_record->name_len = name.len;
+            argument_record->prefix = prefix.value;
+            argument_record->prefix_len = prefix.len;
+            ret = lyxml_get_string(&ctx->xml_ctx, data, &argument_record->content, &argument_record->content_len,
+                                   &argument_record->content, &argument_record->content_len, &argument_record->dynamic_content);
+            LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+        }
+    }
+
+cleanup:
+    if (ret != LY_SUCCESS) {
+        FREE_ARRAY(ctx, *attrs, free_arg_rec);
+        *attrs = NULL;
+    }
+    return ret;
+}
+
+LY_ERR
+yin_validate_value(struct yin_parser_ctx *ctx, enum yang_arg val_type, char *val, size_t len)
+{
+    int prefix = 0;
+    unsigned int c;
+    size_t utf8_char_len;
+    size_t already_read = 0;
+    while (already_read < len) {
+        LY_CHECK_ERR_RET(ly_getutf8((const char **)&val, &c, &utf8_char_len),
+                         LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INCHAR, (val)[-utf8_char_len]), LY_EVALID);
+        already_read += utf8_char_len;
+        LY_CHECK_ERR_RET(already_read > len, LOGINT(ctx->xml_ctx.ctx), LY_EINT);
+
+        switch (val_type) {
+        case Y_IDENTIF_ARG:
+            LY_CHECK_RET(lysp_check_identifierchar((struct lys_parser_ctx *)ctx, c, !already_read, NULL));
+            break;
+        case Y_PREF_IDENTIF_ARG:
+            LY_CHECK_RET(lysp_check_identifierchar((struct lys_parser_ctx *)ctx, c, !already_read, &prefix));
+            break;
+        case Y_STR_ARG:
+        case Y_MAYBE_STR_ARG:
+            LY_CHECK_RET(lysp_check_stringchar((struct lys_parser_ctx *)ctx, c));
+            break;
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse yin argument.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs ([Sized array](@ref sizedarrays)) of attributes.
+ * @param[in,out] data Data to read from.
+ * @param[in] arg_type Type of argument that is expected in parsed element (use YIN_ARG_NONE for elements without
+ *            special argument).
+ * @param[out] arg_val Where value of argument should be stored. Can be NULL if arg_type is specified as YIN_ARG_NONE.
+ * @param[in] val_type Type of expected value of attribute.
+ * @param[in] current_element Identification of current element, used for logging.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_attribute(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, enum YIN_ARGUMENT arg_type,
+                    const char **arg_val, enum yang_arg val_type, enum yang_keyword current_element)
+{
+    enum YIN_ARGUMENT arg = YIN_ARG_UNKNOWN;
+    struct yin_arg_record *iter = NULL;
+    bool found = false;
+
+    /* validation of attributes */
+    LY_ARRAY_FOR(attrs, struct yin_arg_record, iter) {
+        /* yin arguments represented as attributes have no namespace, which in this case means no prefix */
+        if (!iter->prefix) {
+            arg = yin_match_argument_name(iter->name, iter->name_len);
+            if (arg == YIN_ARG_NONE) {
+                continue;
+            } else if (arg == arg_type) {
+                LY_CHECK_ERR_RET(found, LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LYVE_SYNTAX_YIN, "Duplicit definition of %s attribute in %s element",
+                                 yin_attr2str(arg), ly_stmt2str(current_element)), LY_EVALID);
+                found = true;
+                LY_CHECK_RET(yin_validate_value(ctx, val_type, iter->content, iter->content_len));
+                if (iter->dynamic_content) {
+                    *arg_val = lydict_insert_zc(ctx->xml_ctx.ctx, iter->content);
+                    LY_CHECK_RET(!(*arg_val), LY_EMEM);
+                    /* string is no longer supposed to be freed when the sized array is freed */
+                    iter->dynamic_content = 0;
+                } else {
+                    *arg_val = lydict_insert(ctx->xml_ctx.ctx, iter->content, iter->content_len);
+                    LY_CHECK_RET(!(*arg_val), LY_EMEM);
+                }
+            } else {
+                LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LYVE_SYNTAX_YIN, "Unexpected attribute \"%.*s\" of %s element.", iter->name_len, iter->name, ly_stmt2str(current_element));
+                return LY_EVALID;
+            }
+        }
+    }
+
+    /* anything else than Y_MAYBE_STR_ARG is mandatory */
+    if (val_type != Y_MAYBE_STR_ARG && !found) {
+        LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LYVE_SYNTAX_YIN, "Missing mandatory attribute %s of %s element.", yin_attr2str(arg_type), ly_stmt2str(current_element));
+        return LY_EVALID;
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Get record with given type. Array must be sorted in ascending order by array[n].type.
+ *
+ * @param[in] type Type of wanted record.
+ * @param[in] array_size Size of array.
+ * @param[in] array Searched array.
+ *
+ * @return Pointer to desired record on success, NULL if element is not in the array.
+ */
+static struct yin_subelement *
+get_record(enum yang_keyword type, signed char array_size, struct yin_subelement *array)
+{
+    signed char left = 0, right = array_size - 1, middle;
+
+    while (left <= right) {
+        middle = left + (right - left) / 2;
+
+        if (array[middle].type == type) {
+            return &array[middle];
+        }
+
+        if (array[middle].type < type) {
+            left = middle + 1;
+        } else {
+            right = middle - 1;
+        }
+    }
+
+    return NULL;
+}
+
+/**
+ * @brief Helper function to check mandatory constraint of subelement.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] subelem_info Array of information about subelements.
+ * @param[in] subelem_info_size Size of subelem_info array.
+ * @param[in] current_element Identification of element that is currently being parsed, used for logging.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_check_subelem_mandatory_constraint(struct yin_parser_ctx *ctx, struct yin_subelement *subelem_info,
+                                       signed char subelem_info_size, enum yang_keyword current_element)
+{
+    for (signed char i = 0; i < subelem_info_size; ++i) {
+        /* if there is element that is mandatory and isn't parsed log error and return LY_EVALID */
+        if (subelem_info[i].flags & YIN_SUBELEM_MANDATORY && !(subelem_info[i].flags & YIN_SUBELEM_PARSED)) {
+            LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LYVE_SYNTAX_YIN, "Missing mandatory subelement %s of %s element.",
+                          ly_stmt2str(subelem_info[i].type), ly_stmt2str(current_element));
+            return LY_EVALID;
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Helper function to check "first" constraint of subelement.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] subelem_info Array of information about subelements.
+ * @param[in] subelem_info_size Size of subelem_info array.
+ * @param[in] current_element Identification of element that is currently being parsed, used for logging.
+ * @param[in] exp_first Record in subelem_info array that is expected to be defined as first subelement.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_check_subelem_first_constraint(struct yin_parser_ctx *ctx, struct yin_subelement *subelem_info,
+                                   signed char subelem_info_size, enum yang_keyword current_element,
+                                   struct yin_subelement *exp_first)
+{
+    for (signed char i = 0; i < subelem_info_size; ++i) {
+        if (subelem_info[i].flags & YIN_SUBELEM_PARSED) {
+            LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LYVE_SYNTAX_YIN, "Subelement %s of %s element must be defined as first subelement.",
+                          ly_stmt2str(exp_first->type), ly_stmt2str(current_element));
+            return LY_EVALID;
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Helper function to check if array of information about subelements is in ascending order.
+ *
+ * @param[in] subelem_info Array of information about subelements.
+ * @param[in] subelem_info_size Size of subelem_info array.
+ *
+ * @return True iff subelem_info array is in ascending order, False otherwise.
+ */
+#ifndef NDEBUG
+static bool
+is_ordered(struct yin_subelement *subelem_info, signed char subelem_info_size)
+{
+    enum yang_keyword current = YANG_NONE; /* 0 (minimal value) */
+
+    for (signed char i = 0; i < subelem_info_size; ++i) {
+        if (subelem_info[i].type <= current) {
+            return false;
+        }
+        current = subelem_info[i].type;
+    }
+
+    return true;
+}
+#endif
+
+/**
+ * @brief Parse simple element without any special constraints and argument mapped to yin attribute,
+ * for example prefix or namespace element.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of current element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in] kw Type of current element.
+ * @param[out] value Where value of attribute should be stored.
+ * @param[in] arg_type Expected type of attribute.
+ * @param[in] arg_val_type Type of expected value of attribute.
+ * @param[in,out] exts Extension instance to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_simple_element(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data, enum yang_keyword kw,
+                         const char **value, enum YIN_ARGUMENT arg_type, enum yang_arg arg_val_type, struct lysp_ext_instance **exts)
+{
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, arg_type, value, arg_val_type, kw));
+    struct yin_subelement subelems[1] = {
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+
+    return yin_parse_content(ctx, subelems, 1, data, kw, NULL, exts);
+}
+
+/**
+ * @brief Parse pattern element.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of current element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in,out] patterns Restrictions to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_pattern(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data,
+                  struct lysp_type *type)
+{
+    const char *real_value = NULL;
+    char *saved_value = NULL;
+    struct lysp_restr *restr;
+
+    LY_ARRAY_NEW_RET(ctx->xml_ctx.ctx, type->patterns, restr, LY_EMEM);
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_VALUE, &real_value, Y_STR_ARG, YANG_PATTERN));
+    size_t len = strlen(real_value);
+
+    saved_value = malloc(len + 2);
+    LY_CHECK_ERR_RET(!saved_value, LOGMEM(ctx->xml_ctx.ctx), LY_EMEM);
+    memmove(saved_value + 1, real_value, len);
+    FREE_STRING(ctx->xml_ctx.ctx, real_value);
+    saved_value[0] = 0x06;
+    saved_value[len + 1] = '\0';
+    restr->arg = lydict_insert_zc(ctx->xml_ctx.ctx, saved_value);
+    LY_CHECK_ERR_RET(!restr->arg, LOGMEM(ctx->xml_ctx.ctx), LY_EMEM);
+    type->flags |= LYS_SET_PATTERN;
+
+    struct yin_subelement subelems[6] = {
+                                            {YANG_DESCRIPTION, &restr->dsc, YIN_SUBELEM_UNIQUE},
+                                            {YANG_ERROR_APP_TAG, &restr->eapptag, YIN_SUBELEM_UNIQUE},
+                                            {YANG_ERROR_MESSAGE, &restr->emsg, YIN_SUBELEM_UNIQUE},
+                                            {YANG_MODIFIER, &restr->arg, YIN_SUBELEM_UNIQUE},
+                                            {YANG_REFERENCE, &restr->ref, YIN_SUBELEM_UNIQUE},
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+    return yin_parse_content(ctx, subelems, 6, data, YANG_PATTERN, NULL, &restr->exts);
+}
+
+/**
+ * @brief Parse enum or bit element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of current element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in] enum_kw Identification of actual keyword, can be set to YANG_BIT or YANG_ENUM.
+ * @param[in,out] enums Enums or bits to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_enum_bit(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data,
+                   enum yang_keyword enum_kw, struct lysp_type *type)
+{
+    assert(enum_kw == YANG_BIT || enum_kw == YANG_ENUM);
+    struct lysp_type_enum *en;
+    LY_ARRAY_NEW_RET(ctx->xml_ctx.ctx, type->enums, en, LY_EMEM);
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_NAME, &en->name, Y_IDENTIF_ARG, enum_kw));
+    type->flags |= (enum_kw == YANG_ENUM) ? LYS_SET_ENUM : LYS_SET_BIT;
+    if (enum_kw == YANG_ENUM) {
+        LY_CHECK_RET(lysp_check_enum_name((struct lys_parser_ctx *)ctx, en->name, strlen(en->name)));
+        YANG_CHECK_NONEMPTY((struct lys_parser_ctx *)ctx, strlen(en->name), "enum");
+    }
+    CHECK_UNIQUENESS((struct lys_parser_ctx *)ctx, type->enums, name, ly_stmt2str(enum_kw), en->name);
+
+    struct yin_subelement subelems[6] = {
+                                            {YANG_DESCRIPTION, &en->dsc, YIN_SUBELEM_UNIQUE},
+                                            {YANG_IF_FEATURE, &en->iffeatures, 0},
+                                            {(enum_kw == YANG_ENUM) ? YANG_VALUE : YANG_POSITION, &en->value, YIN_SUBELEM_UNIQUE},
+                                            {YANG_REFERENCE, &en->ref, YIN_SUBELEM_UNIQUE},
+                                            {YANG_STATUS, &en->flags, YIN_SUBELEM_UNIQUE},
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+    return yin_parse_content(ctx, subelems, 6, data, enum_kw, NULL, &en->exts);
+}
+
+/**
+ * @brief Parse simple element without any special constraints and argument mapped to yin attribute, that can have
+ * more instances, such as base or if-feature.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of current element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in] kw Type of current element.
+ * @param[out] values Parsed values to add to.
+ * @param[in] arg_type Expected type of attribute.
+ * @param[in] arg_val_type Type of expected value of attribute.
+ * @param[in,out] exts Extension instance to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_simple_elements(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data, enum yang_keyword kw,
+                          const char ***values, enum YIN_ARGUMENT arg_type, enum yang_arg arg_val_type, struct lysp_ext_instance **exts)
+{
+    const char **value;
+    LY_ARRAY_NEW_RET(ctx->xml_ctx.ctx, *values, value, LY_EMEM);
+    uint32_t index = LY_ARRAY_SIZE(*values) - 1;
+    struct yin_subelement subelems[1] = {
+                                            {YANG_CUSTOM, &index, 0}
+                                        };
+
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, arg_type, value, arg_val_type, kw));
+
+    return yin_parse_content(ctx, subelems, 1, data, kw, NULL, exts);
+}
+
+/**
+ * @brief Parse require instance element.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of current element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @prama[out] type Type structure to store value, flag and extensions.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_pasrse_reqinstance(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs,
+                       const char **data,  struct lysp_type *type)
+{
+    const char *temp_val = NULL;
+    struct yin_subelement subelems[1] = {
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+
+    type->flags |= LYS_SET_REQINST;
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, YANG_REQUIRE_INSTANCE));
+    if (strcmp(temp_val, "true") == 0) {
+        type->require_instance = 1;
+    } else if (strcmp(temp_val, "false") != 0) {
+        LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_val, "require-instance");
+        FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+        return LY_EVALID;
+    }
+    FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+
+    return yin_parse_content(ctx, subelems, 1, data, YANG_REQUIRE_INSTANCE, NULL, &type->exts);
+}
+
+/**
+ * @brief Parse modifier element.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of current element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in,out] pat Value to write to.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_modifier(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data,
+                   const char **pat, struct lysp_ext_instance **exts)
+{
+    assert(**pat == 0x06);
+    const char *temp_val;
+    char *modified_val;
+    struct yin_subelement subelems[1] = {
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, YANG_MODIFIER));
+    if (strcmp(temp_val, "invert-match") != 0) {
+        LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_val, "modifier");
+        FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+        return LY_EVALID;
+    }
+    lydict_remove(ctx->xml_ctx.ctx, temp_val);
+
+    /* allocate new value */
+    modified_val = malloc(strlen(*pat) + 1);
+    LY_CHECK_ERR_RET(!modified_val, LOGMEM(ctx->xml_ctx.ctx), LY_EMEM);
+    strcpy(modified_val, *pat);
+    lydict_remove(ctx->xml_ctx.ctx, *pat);
+
+    /* modify the new value */
+    modified_val[0] = 0x15;
+    *pat = lydict_insert_zc(ctx->xml_ctx.ctx, modified_val);
+
+    return yin_parse_content(ctx, subelems, 1, data, YANG_MODIFIER, NULL, exts);
+}
+
+/**
+ * @brief Parse a restriction element (length, range or one instance of must).
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of current element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in] restr_kw Identificaton of element that is being parsed, can be set to YANG_MUST, YANG_LENGTH or YANG_RANGE.
+ * @param[in]
+ */
+static LY_ERR
+yin_parse_restriction(struct yin_parser_ctx *ctx,  struct yin_arg_record *attrs, const char **data,
+                      enum yang_keyword restr_kw, struct lysp_restr *restr)
+{
+    assert(restr_kw == YANG_MUST || restr_kw == YANG_LENGTH || restr_kw == YANG_RANGE);
+    struct yin_subelement subelems[5] = {
+                                            {YANG_DESCRIPTION, &restr->dsc, YIN_SUBELEM_UNIQUE},
+                                            {YANG_ERROR_APP_TAG, &restr->eapptag, YIN_SUBELEM_UNIQUE},
+                                            {YANG_ERROR_MESSAGE, &restr->emsg, YIN_SUBELEM_UNIQUE},
+                                            {YANG_REFERENCE, &restr->ref, YIN_SUBELEM_UNIQUE},
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+    /* argument of must is called condition, but argument of length and range is called value */
+    enum YIN_ARGUMENT arg_type = (restr_kw == YANG_MUST) ? YIN_ARG_CONDITION : YIN_ARG_VALUE;
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, arg_type, &restr->arg, Y_STR_ARG, restr_kw));
+
+    return yin_parse_content(ctx, subelems, 5, data, restr_kw, NULL, &restr->exts);
+}
+
+/**
+ * @brief Parse must element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of current element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in,out] restrs Restrictions to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_must(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data, struct lysp_restr **restrs)
+{
+    struct lysp_restr *restr;
+
+    LY_ARRAY_NEW_RET(ctx->xml_ctx.ctx, *restrs, restr, LY_EMEM);
+    return yin_parse_restriction(ctx, attrs, data, YANG_MUST, restr);
+}
+
+/**
+ * @brief Parse position or value element.
+ *
+ * @param[in,out] ctx YIN parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of current element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in] kw Type of current element, can be set to YANG_POSITION or YANG_VALUE.
+ * @param[out] enm Enum structure to save value, flags and extensions.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_value_pos_element(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data,
+                            enum yang_keyword kw, struct lysp_type_enum *enm)
+{
+    assert(kw == YANG_POSITION || kw == YANG_VALUE);
+    const char *temp_val = NULL;
+    char *ptr;
+    long int num;
+    unsigned long int unum;
+
+    /* set value flag */
+    enm->flags |= LYS_SET_VALUE;
+
+    /* get attribute value */
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, kw));
+    if (!temp_val || (temp_val[0] == '+') || ((temp_val[0] == '0') && (temp_val[0] != '\0')) || ((kw == YANG_VALUE) && !strcmp(temp_val, "-0"))) {
+        LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INVAL, strlen(temp_val), temp_val, ly_stmt2str(kw));
+        goto error;
+    }
+
+    /* convert value */
+    errno = 0;
+    if (kw == YANG_VALUE) {
+        num = strtol(temp_val, &ptr, 10);
+        if (num < INT64_C(-2147483648) || num > INT64_C(2147483647)) {
+            LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INVAL, strlen(temp_val), temp_val, ly_stmt2str(kw));
+            goto error;
+        }
+    } else {
+        unum = strtoul(temp_val, &ptr, 10);
+        if (unum > UINT64_C(4294967295)) {
+            LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INVAL, strlen(temp_val), temp_val, ly_stmt2str(kw));
+            goto error;
+        }
+    }
+    /* check if whole argument value was converted */
+    if (*ptr != '\0') {
+        LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INVAL, strlen(temp_val), temp_val, ly_stmt2str(kw));
+    }
+    if (errno == ERANGE) {
+        LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_OOB, strlen(temp_val), temp_val, ly_stmt2str(kw));
+        goto error;
+    }
+    /* save correctly ternary operator can't be used because num and unum have different signes */
+    if (kw == YANG_VALUE) {
+        enm->value = num;
+    } else {
+        enm->value = unum;
+    }
+    FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+
+    /* parse subelements */
+    struct yin_subelement subelems[1] = {
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+    return yin_parse_content(ctx, subelems, 1, data, kw, NULL, &enm->exts);
+
+    error:
+        FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+        return LY_EVALID;
+}
+
+/**
+ * @brief Function to parse meta tags (description, contact, ...) eg. elements with
+ * text element as child
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in] Type of element can be set to YANG_ORGANIZATION or YANG_CONTACT or YANG_DESCRIPTION or YANG_REFERENCE.
+ * @param[out] value Where the content of meta element should be stored.
+ * @param[in,out] exts Extension instance to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_meta_element(struct yin_parser_ctx *ctx, const char **data, enum yang_keyword elem_type,
+                       const char **value, struct lysp_ext_instance **exts)
+{
+    assert(elem_type == YANG_ORGANIZATION || elem_type == YANG_CONTACT || elem_type == YANG_DESCRIPTION || elem_type == YANG_REFERENCE);
+
+    struct yin_subelement subelems[2] = {
+                                            {YANG_CUSTOM, NULL, 0},
+                                            {YIN_TEXT, value, YIN_SUBELEM_MANDATORY | YIN_SUBELEM_UNIQUE | YIN_SUBELEM_FIRST}
+                                        };
+
+    return yin_parse_content(ctx, subelems, 2, data, elem_type, NULL, exts);
+}
+
+/**
+ * @brief Parse error-message element.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in,out] data Data to read from.
+ * @param[out] value Where the content of error-message element should be stored.
+ * @param[in,out] exts Extension instance to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_err_msg_element(struct yin_parser_ctx *ctx, const char **data, const char **value,
+                          struct lysp_ext_instance **exts)
+{
+    struct yin_subelement subelems[2] = {
+                                            {YANG_CUSTOM, NULL, 0},
+                                            {YIN_VALUE, value, YIN_SUBELEM_MANDATORY | YIN_SUBELEM_UNIQUE | YIN_SUBELEM_FIRST}
+                                        };
+
+    return yin_parse_content(ctx, subelems, 2, data, YANG_ERROR_MESSAGE, NULL, exts);
+}
+
+/**
+ * @brief Map keyword type to substatement info.
+ *
+ * @param[in] kw Keyword type.
+ *
+ * @return correct LYEXT_SUBSTMT information.
+ */
+static LYEXT_SUBSTMT
+kw2lyext_substmt(enum yang_keyword kw)
+{
+    switch (kw) {
+    case YANG_ARGUMENT:
+        return LYEXT_SUBSTMT_ARGUMENT;
+    case YANG_BASE:
+        return LYEXT_SUBSTMT_BASE;
+    case YANG_BELONGS_TO:
+        return LYEXT_SUBSTMT_BELONGSTO;
+    case YANG_CONTACT:
+        return LYEXT_SUBSTMT_CONTACT;
+    case YANG_DEFAULT:
+        return LYEXT_SUBSTMT_DEFAULT;
+    case YANG_DESCRIPTION:
+        return LYEXT_SUBSTMT_DESCRIPTION;
+    case YANG_ERROR_APP_TAG:
+        return LYEXT_SUBSTMT_ERRTAG;
+    case YANG_ERROR_MESSAGE:
+        return LYEXT_SUBSTMT_ERRMSG;
+    case YANG_KEY:
+        return LYEXT_SUBSTMT_KEY;
+    case YANG_NAMESPACE:
+        return LYEXT_SUBSTMT_NAMESPACE;
+    case YANG_ORGANIZATION:
+        return LYEXT_SUBSTMT_ORGANIZATION;
+    case YANG_PATH:
+        return LYEXT_SUBSTMT_PATH;
+    case YANG_PREFIX:
+        return LYEXT_SUBSTMT_PREFIX;
+    case YANG_PRESENCE:
+        return LYEXT_SUBSTMT_PRESENCE;
+    case YANG_REFERENCE:
+        return LYEXT_SUBSTMT_REFERENCE;
+    case YANG_REVISION_DATE:
+        return LYEXT_SUBSTMT_REVISIONDATE;
+    case YANG_UNITS:
+        return LYEXT_SUBSTMT_UNITS;
+    case YANG_VALUE:
+        return LYEXT_SUBSTMT_VALUE;
+    case YANG_YANG_VERSION:
+        return LYEXT_SUBSTMT_VERSION;
+    case YANG_MODIFIER:
+        return LYEXT_SUBSTMT_MODIFIER;
+    case YANG_REQUIRE_INSTANCE:
+        return LYEXT_SUBSTMT_REQINSTANCE;
+    case YANG_YIN_ELEMENT:
+        return LYEXT_SUBSTMT_YINELEM;
+    case YANG_CONFIG:
+        return LYEXT_SUBSTMT_CONFIG;
+    case YANG_MANDATORY:
+        return LYEXT_SUBSTMT_MANDATORY;
+    case YANG_ORDERED_BY:
+        return LYEXT_SUBSTMT_ORDEREDBY;
+    case YANG_STATUS:
+        return LYEXT_SUBSTMT_STATUS;
+    case YANG_FRACTION_DIGITS:
+        return LYEXT_SUBSTMT_FRACDIGITS;
+    case YANG_MAX_ELEMENTS:
+        return LYEXT_SUBSTMT_MAX;
+    case YANG_MIN_ELEMENTS:
+        return LYEXT_SUBSTMT_MIN;
+    case YANG_POSITION:
+        return LYEXT_SUBSTMT_POSITION;
+    case YANG_UNIQUE:
+        return LYEXT_SUBSTMT_UNIQUE;
+    case YANG_IF_FEATURE:
+        return LYEXT_SUBSTMT_IFFEATURE;
+    default:
+        return LYEXT_SUBSTMT_SELF;
+    }
+}
+
+/**
+ * @brief Parse belongs-to element.
+ *
+ * @param[in] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of current element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[out] submod Structure of submodule that is being parsed.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values
+ */
+static LY_ERR
+yin_parse_belongs_to(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data,
+                     struct lysp_submodule *submod, struct lysp_ext_instance **exts)
+{
+    struct yin_subelement subelems[2] = {
+                                            {YANG_PREFIX, &submod->prefix, YIN_SUBELEM_MANDATORY | YIN_SUBELEM_UNIQUE},
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_MODULE, &submod->belongsto, Y_IDENTIF_ARG, YANG_BELONGS_TO));
+
+    return yin_parse_content(ctx, subelems, 2, data, YANG_BELONGS_TO, NULL, exts);
+}
+
+LY_ERR
+yin_parse_content(struct yin_parser_ctx *ctx, struct yin_subelement *subelem_info, signed char subelem_info_size,
+                  const char **data, enum yang_keyword current_element, const char **text_content, struct lysp_ext_instance **exts)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct sized_string prefix, name;
+    char *out;
+    size_t out_len;
+    int dynamic;
+    struct yin_arg_record *subelem_attrs = NULL;
+    enum yang_keyword kw = YANG_NONE;
+    struct yin_subelement *subelem_info_rec = NULL;
+    uint32_t index = 0;
+    struct lysp_type *type;
+    assert(is_ordered(subelem_info, subelem_info_size));
+
+    if (ctx->xml_ctx.status == LYXML_ELEM_CONTENT) {
+        ret = lyxml_get_string(&ctx->xml_ctx, data, &out, &out_len, &out, &out_len, &dynamic);
+        /* current element has subelements as content */
+        if (ret == LY_EINVAL) {
+            while (ctx->xml_ctx.status == LYXML_ELEMENT) {
+                ret = lyxml_get_element(&ctx->xml_ctx, data, &prefix.value, &prefix.len, &name.value, &name.len);
+                LY_CHECK_GOTO(ret, cleanup);
+                if (!name.value) {
+                    /* end of current element reached */
+                    break;
+                }
+                ret = yin_load_attributes(ctx, data, &subelem_attrs);
+                LY_CHECK_GOTO(ret, cleanup);
+                kw = yin_match_keyword(ctx, name.value, name.len, prefix.value, prefix.len, current_element);
+
+                /* check if this element can be child of current element */
+                subelem_info_rec = get_record(kw, subelem_info_size, subelem_info);
+                if (!subelem_info_rec) {
+                    LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_UNEXP_SUBELEM, name.len, name.value, ly_stmt2str(current_element));
+                    ret = LY_EVALID;
+                    goto cleanup;
+                }
+
+                /* TODO check relative order */
+
+                /* if element is unique and already defined log error */
+                if ((subelem_info_rec->flags & YIN_SUBELEM_UNIQUE) && (subelem_info_rec->flags & YIN_SUBELEM_PARSED)) {
+                    LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LYVE_SYNTAX_YIN, "Redefinition of %s element in %s element.", ly_stmt2str(kw), ly_stmt2str(current_element));
+                    return LY_EVALID;
+                }
+                if (subelem_info_rec->flags & YIN_SUBELEM_FIRST) {
+                    ret = yin_check_subelem_first_constraint(ctx, subelem_info, subelem_info_size, current_element, subelem_info_rec);
+                    LY_CHECK_GOTO(ret, cleanup);
+                }
+                subelem_info_rec->flags |= YIN_SUBELEM_PARSED;
+
+                switch (kw) {
+                case YANG_CUSTOM:
+                    index = (subelem_info_rec->dest) ? *((uint32_t*)subelem_info_rec->dest) : 0;
+                    ret = yin_parse_extension_instance(ctx, subelem_attrs, data, name2fullname(name.value, prefix.len),
+                                                      namelen2fulllen(name.len, prefix.len),
+                                                      kw2lyext_substmt(current_element), index, exts);
+                    break;
+                case YANG_ACTION:
+                    break;
+                case YANG_ANYDATA:
+                    break;
+                case YANG_ANYXML:
+                    break;
+                case YANG_ARGUMENT:
+                    ret = yin_parse_argument_element(ctx, subelem_attrs, data, (struct yin_argument_meta *)subelem_info_rec->dest, exts);
+                    break;
+                case YANG_AUGMENT:
+                    break;
+                case YANG_BASE:
+                    if (current_element == YANG_IDENTITY) {
+                        type = (struct lysp_type *)subelem_info_rec->dest;
+                        ret = yin_parse_simple_elements(ctx, subelem_attrs, data, kw, &type->bases, YIN_ARG_NAME,
+                                                        Y_PREF_IDENTIF_ARG, exts);
+                        type->flags |= LYS_SET_BASE;
+                    } else if (current_element == YANG_TYPE) {
+                        ret = yin_parse_simple_elements(ctx, subelem_attrs, data, kw, (const char ***)subelem_info_rec->dest,
+                                                        YIN_ARG_NAME, Y_PREF_IDENTIF_ARG, exts);
+                    } else {
+                        LOGINT(ctx->xml_ctx.ctx);
+                        ret = LY_EINT;
+                    }
+                    break;
+                case YANG_BELONGS_TO:
+                    ret = yin_parse_belongs_to(ctx, subelem_attrs, data, (struct lysp_submodule *)subelem_info_rec->dest, exts);
+                    break;
+                case YANG_BIT:
+                case YANG_ENUM:
+                    ret = yin_parse_enum_bit(ctx, subelem_attrs, data, kw, (struct lysp_type *)subelem_info_rec->dest);
+                    break;
+                case YANG_CASE:
+                    break;
+                case YANG_CHOICE:
+                    break;
+                case YANG_CONFIG:
+                    ret = yin_parse_config(ctx, subelem_attrs, data, (uint16_t *)subelem_info_rec->dest, exts);
+                    break;
+                case YANG_CONTACT:
+                case YANG_DESCRIPTION:
+                case YANG_ORGANIZATION:
+                case YANG_REFERENCE:
+                    ret = yin_parse_meta_element(ctx, data, kw, (const char **)subelem_info_rec->dest, exts);
+                    break;
+                case YANG_CONTAINER:
+                    break;
+                case YANG_DEFAULT:
+                    ret = yin_parse_simple_element(ctx, subelem_attrs, data, kw, (const char **)subelem_info_rec->dest,
+                                                   YIN_ARG_VALUE, Y_STR_ARG, exts);
+                    break;
+                case YANG_DEVIATE:
+                    break;
+                case YANG_DEVIATION:
+                    break;
+                case YANG_ERROR_APP_TAG:
+                    ret = yin_parse_simple_element(ctx, subelem_attrs, data, kw, (const char **)subelem_info_rec->dest,
+                                                   YIN_ARG_VALUE, Y_STR_ARG, exts);
+                    break;
+                case YANG_ERROR_MESSAGE:
+                    ret = yin_parse_err_msg_element(ctx, data, (const char **)subelem_info_rec->dest, exts);
+                    break;
+                case YANG_EXTENSION:
+                    ret = yin_parse_extension(ctx, subelem_attrs, data, (struct lysp_ext **)subelem_info_rec->dest);
+                    break;
+                case YANG_FEATURE:
+                    break;
+                case YANG_FRACTION_DIGITS:
+                    break;
+                case YANG_GROUPING:
+                    break;
+                case YANG_IDENTITY:
+                    break;
+                case YANG_IF_FEATURE:
+                    ret = yin_parse_simple_elements(ctx, subelem_attrs, data, kw,
+                                                    (const char ***)subelem_info_rec->dest, YIN_ARG_VALUE, Y_STR_ARG, exts);
+                    break;
+                case YANG_IMPORT:
+                    ret = yin_parse_import(ctx, subelem_attrs, data, (struct lysp_module *)subelem_info_rec->dest);
+                    break;
+                case YANG_INCLUDE:
+                    break;
+                case YANG_INPUT:
+                    break;
+                case YANG_KEY:
+                    break;
+                case YANG_LEAF:
+                    break;
+                case YANG_LEAF_LIST:
+                    break;
+                case YANG_LENGTH:
+                    type = (struct lysp_type *)subelem_info_rec->dest;
+                    type->length = calloc(1, sizeof *type->length);
+                    LY_CHECK_ERR_GOTO(!type->length, LOGMEM(ctx->xml_ctx.ctx); ret = LY_EMEM, cleanup);
+                    ret = yin_parse_restriction(ctx, subelem_attrs, data, kw, type->length);
+                    type->flags |= LYS_SET_LENGTH;
+                    break;
+                case YANG_LIST:
+                    break;
+                case YANG_MANDATORY:
+                    ret = yin_parse_mandatory(ctx, subelem_attrs, data, (uint16_t *)subelem_info_rec->dest, exts);
+                    break;
+                case YANG_MAX_ELEMENTS:
+                    break;
+                case YANG_MIN_ELEMENTS:
+                    break;
+                case YANG_MODIFIER:
+                    ret = yin_parse_modifier(ctx, subelem_attrs, data, (const char **)subelem_info_rec->dest, exts);
+                    break;
+                case YANG_MODULE:
+                    break;
+                case YANG_MUST:
+                    ret = yin_parse_must(ctx, subelem_attrs, data, (struct lysp_restr **)subelem_info_rec->dest);
+                    break;
+                case YANG_NAMESPACE:
+                    ret = yin_parse_simple_element(ctx, subelem_attrs, data, kw, (const char **)subelem_info_rec->dest,
+                                                   YIN_ARG_URI, Y_STR_ARG, exts);
+                    break;
+                case YANG_NOTIFICATION:
+                    break;
+                case YANG_ORDERED_BY:
+                    break;
+                case YANG_OUTPUT:
+                    break;
+                case YANG_PATH:
+                    type = (struct lysp_type *)subelem_info_rec->dest;
+                    ret = yin_parse_simple_element(ctx, subelem_attrs, data, kw, &type->path,
+                                                   YIN_ARG_VALUE, Y_STR_ARG, exts);
+                    type->flags |= LYS_SET_PATH;
+                    break;
+                case YANG_PATTERN:
+                    ret = yin_parse_pattern(ctx, subelem_attrs, data, (struct lysp_type *)subelem_info_rec->dest);
+                    break;
+                case YANG_VALUE:
+                case YANG_POSITION:
+                    ret = yin_parse_value_pos_element(ctx, subelem_attrs, data, kw,
+                                                      (struct lysp_type_enum *)subelem_info_rec->dest);
+                    break;
+                case YANG_PREFIX:
+                    ret = yin_parse_simple_element(ctx, subelem_attrs, data, kw,
+                                                   (const char **)subelem_info_rec->dest, YIN_ARG_VALUE, Y_IDENTIF_ARG, exts);
+                    break;
+                case YANG_PRESENCE:
+                    break;
+                case YANG_RANGE:
+                    type = (struct lysp_type *)subelem_info_rec->dest;
+                    type->range = calloc(1, sizeof *type->range);
+                    LY_CHECK_ERR_GOTO(!type->range, LOGMEM(ctx->xml_ctx.ctx); ret = LY_EMEM, cleanup);
+                    ret = yin_parse_restriction(ctx, subelem_attrs, data, kw, type->range);
+                    type->flags |=  LYS_SET_RANGE;
+                    break;
+                case YANG_REFINE:
+                    break;
+                case YANG_REQUIRE_INSTANCE:
+                    ret = yin_pasrse_reqinstance(ctx, subelem_attrs, data, (struct lysp_type *)subelem_info_rec->dest);
+                    break;
+                case YANG_REVISION:
+                    break;
+                case YANG_REVISION_DATE:
+                    ret = yin_parse_revision_date(ctx, subelem_attrs, data, (char *)subelem_info_rec->dest, exts);
+                    break;
+                case YANG_RPC:
+                    break;
+                case YANG_STATUS:
+                    ret = yin_parse_status(ctx, subelem_attrs, data, (uint16_t *)subelem_info_rec->dest, exts);
+                    break;
+                case YANG_SUBMODULE:
+                    break;
+                case YANG_TYPE:
+                    break;
+                case YANG_TYPEDEF:
+                    break;
+                case YANG_UNIQUE:
+                    ret = yin_parse_simple_elements(ctx, subelem_attrs, data, kw, (const char ***)subelem_info_rec->dest,
+                                                    YIN_ARG_TAG, Y_STR_ARG, exts);
+                    break;
+                case YANG_UNITS:
+                    ret = yin_parse_simple_element(ctx, subelem_attrs, data, kw, (const char **)subelem_info_rec->dest,
+                                                   YIN_ARG_NAME, Y_STR_ARG, exts);
+                    break;
+                case YANG_USES:
+                    break;
+                case YANG_WHEN:
+                    ret = yin_parse_when(ctx, subelem_attrs, data, (struct lysp_when **)subelem_info_rec->dest);
+                    break;
+                case YANG_YANG_VERSION:
+                    ret = yin_parse_yangversion(ctx, subelem_attrs, data, (uint8_t *)subelem_info_rec->dest, exts);
+                    break;
+                case YANG_YIN_ELEMENT:
+                    ret = yin_parse_yin_element_element(ctx, subelem_attrs, data, (uint16_t *)subelem_info_rec->dest, exts);
+                    break;
+                case YIN_TEXT:
+                case YIN_VALUE:
+                    ret = yin_parse_content(ctx, NULL, 0, data, kw, (const char **)subelem_info_rec->dest, NULL);
+                    break;
+                default:
+                    LOGINT(ctx->xml_ctx.ctx);
+                    return LY_EINT;
+                }
+                LY_CHECK_GOTO(ret, cleanup);
+                FREE_ARRAY(ctx, subelem_attrs, free_arg_rec);
+                subelem_attrs = NULL;
+                subelem_info_rec = NULL;
+            }
+        } else {
+            /* elements with text or none content */
+            /* save text content, if text_content isn't set, it's just ignored */
+            if (text_content) {
+                if (dynamic) {
+                    *text_content = lydict_insert_zc(ctx->xml_ctx.ctx, out);
+                    if (!*text_content) {
+                        free(out);
+                        return LY_EMEM;
+                    }
+                } else {
+                    if (out_len == 0) {
+                        *text_content = NULL;
+                    } else {
+                        *text_content = lydict_insert(ctx->xml_ctx.ctx, out, out_len);
+                    }
+                }
+            }
+            /* load closing element */
+            LY_CHECK_RET(lyxml_get_element(&ctx->xml_ctx, data, &prefix.value, &prefix.len, &name.value, &name.len));
+        }
+
+        LY_CHECK_RET(yin_check_subelem_mandatory_constraint(ctx, subelem_info, subelem_info_size, current_element));
+    }
+
+cleanup:
+    FREE_ARRAY(ctx, subelem_attrs, free_arg_rec);
+    return ret;
+}
+
+LY_ERR
+yin_parse_revision_date(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data, char *rev,
+                        struct lysp_ext_instance **exts)
+{
+    const char *temp_rev;
+    struct yin_subelement subelems[1] = {
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_DATE, &temp_rev, Y_STR_ARG, YANG_REVISION_DATE));
+    LY_CHECK_RET(lysp_check_date((struct lys_parser_ctx *)ctx, temp_rev, strlen(temp_rev), "revision-date") != LY_SUCCESS, LY_EVALID);
+
+    strcpy(rev, temp_rev);
+    FREE_STRING(ctx->xml_ctx.ctx, temp_rev);
+
+    return yin_parse_content(ctx, subelems, 1, data, YANG_REVISION_DATE, NULL, exts);
+}
+
+/**
+ * @brief Parse config element.
+ *
+ * @param[in] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of import element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in,out] flags Flags to add to.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_config(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data, uint16_t *flags,
+                 struct lysp_ext_instance **exts)
+{
+    const char *temp_val = NULL;
+    struct yin_subelement subelems[1] = {
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, YANG_CONFIG));
+    if (strcmp(temp_val, "true") == 0) {
+        *flags |= LYS_CONFIG_W;
+    } else if (strcmp(temp_val, "false") == 0) {
+        *flags |= LYS_CONFIG_R;
+    } else {
+        LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_val, "config");
+        FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+        return LY_EVALID;
+    }
+    FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+
+    return yin_parse_content(ctx, subelems, 1, data, YANG_CONFIG, NULL, exts);
+}
+
+LY_ERR
+yin_parse_yangversion(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data, uint8_t *version,
+                      struct lysp_ext_instance **exts)
+{
+    const char *temp_version = NULL;
+    struct yin_subelement subelems[1] = {
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_VALUE, &temp_version, Y_STR_ARG, YANG_YANG_VERSION));
+    if (strcmp(temp_version, "1.0") == 0) {
+        *version = LYS_VERSION_1_0;
+    } else if (strcmp(temp_version, "1.1") == 0) {
+        *version = LYS_VERSION_1_1;
+    } else {
+        LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_version, "yang-version");
+        FREE_STRING(ctx->xml_ctx.ctx, temp_version);
+        return LY_EVALID;
+    }
+    FREE_STRING(ctx->xml_ctx.ctx, temp_version);
+    ctx->mod_version = *version;
+
+    return yin_parse_content(ctx, subelems, 1, data, YANG_YANG_VERSION, NULL, exts);
+}
+
+LY_ERR
+yin_parse_import(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data, struct lysp_module *mod)
+{
+    struct lysp_import *imp;
+    /* allocate new element in sized array for import */
+    LY_ARRAY_NEW_RET(ctx->xml_ctx.ctx, mod->imports, imp, LY_EMEM);
+
+    struct yin_subelement subelems[5] = {
+                                            {YANG_DESCRIPTION, &imp->dsc, YIN_SUBELEM_UNIQUE},
+                                            {YANG_PREFIX, &imp->prefix, YIN_SUBELEM_MANDATORY | YIN_SUBELEM_UNIQUE},
+                                            {YANG_REFERENCE, &imp->ref, YIN_SUBELEM_UNIQUE},
+                                            {YANG_REVISION_DATE, imp->rev, YIN_SUBELEM_UNIQUE},
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+
+    /* parse import attributes */
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_MODULE, &imp->name, Y_IDENTIF_ARG, YANG_IMPORT));
+    LY_CHECK_RET(yin_parse_content(ctx, subelems, 5, data, YANG_IMPORT, NULL, &imp->exts));
+    /* check prefix validity */
+    LY_CHECK_RET(lysp_check_prefix((struct lys_parser_ctx *)ctx, mod->imports, mod->mod->prefix, &imp->prefix), LY_EVALID);
+
+    return yin_parse_content(ctx, subelems, 5, data, YANG_IMPORT, NULL, &imp->exts);
+}
+
+LY_ERR
+yin_parse_mandatory(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data, uint16_t *flags,
+                    struct lysp_ext_instance **exts)
+{
+    const char *temp_val = NULL;
+    struct yin_subelement subelems[1] = {
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, YANG_MANDATORY));
+    if (strcmp(temp_val, "true") == 0) {
+        *flags |= LYS_MAND_TRUE;
+    } else if (strcmp(temp_val, "false") == 0) {
+        *flags |= LYS_MAND_FALSE;
+    } else {
+        LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_val, "mandatory");
+        FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+        return LY_EVALID;
+    }
+    FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+
+    return yin_parse_content(ctx, subelems, 1, data, YANG_MANDATORY, NULL, exts);
+}
+
+LY_ERR
+yin_parse_status(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data, uint16_t *flags,
+                 struct lysp_ext_instance **exts)
+{
+    const char *value = NULL;
+    struct yin_subelement subelems[1] = {
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_VALUE, &value, Y_STR_ARG, YANG_STATUS));
+    if (strcmp(value, "current") == 0) {
+        *flags |= LYS_STATUS_CURR;
+    } else if (strcmp(value, "deprecated") == 0) {
+        *flags |= LYS_STATUS_DEPRC;
+    } else if (strcmp(value, "obsolete") == 0) {
+        *flags |= LYS_STATUS_OBSLT;
+    } else {
+        LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INVAL_YIN, value, "status");
+        FREE_STRING(ctx->xml_ctx.ctx, value);
+        return LY_EVALID;
+    }
+    FREE_STRING(ctx->xml_ctx.ctx, value);
+
+    return yin_parse_content(ctx, subelems, 1, data, YANG_STATUS, NULL, exts);
+}
+
+LY_ERR
+yin_parse_when(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data, struct lysp_when **when_p)
+{
+    struct lysp_when *when;
+    when = calloc(1, sizeof *when);
+    LY_CHECK_ERR_RET(!when, LOGMEM(ctx->xml_ctx.ctx), LY_EMEM);
+    yin_parse_attribute(ctx, attrs, YIN_ARG_CONDITION, &when->cond, Y_STR_ARG, YANG_WHEN);
+    *when_p = when;
+    struct yin_subelement subelems[3] = {
+                                            {YANG_DESCRIPTION, &when->dsc, YIN_SUBELEM_UNIQUE},
+                                            {YANG_REFERENCE, &when->ref, YIN_SUBELEM_UNIQUE},
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+
+    return yin_parse_content(ctx, subelems, 3, data, YANG_WHEN, NULL, &when->exts);
+}
+
+LY_ERR
+yin_parse_yin_element_element(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data,
+                              uint16_t *flags, struct lysp_ext_instance **exts)
+{
+    const char *temp_val = NULL;
+    struct yin_subelement subelems[1] = {
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_VALUE, &temp_val, Y_STR_ARG, YANG_YIN_ELEMENT));
+    if (strcmp(temp_val, "true") == 0) {
+        *flags |= LYS_YINELEM_TRUE;
+    } else if (strcmp(temp_val, "false") == 0) {
+        *flags |= LYS_YINELEM_FALSE;
+    } else {
+        LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INVAL_YIN, temp_val, "yin-element");
+        FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+        return LY_EVALID;
+    }
+    FREE_STRING(ctx->xml_ctx.ctx, temp_val);
+
+    return yin_parse_content(ctx, subelems, 1, data, YANG_YIN_ELEMENT, NULL, exts);
+}
+
+LY_ERR
+yin_parse_extension_instance(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data, const char *ext_name,
+                             int ext_name_len, LYEXT_SUBSTMT subelem, uint32_t subelem_index, struct lysp_ext_instance **exts)
+{
+    LY_ERR ret = LY_SUCCESS;
+    char *out;
+    const char *name, *prefix;
+    size_t out_len, prefix_len, name_len;
+    int dynamic;
+    struct lysp_ext_instance *e;
+    struct lysp_stmt *last_subelem = NULL, *new_subelem = NULL;
+    struct yin_arg_record *iter;
+
+    LY_ARRAY_NEW_RET(ctx->xml_ctx.ctx, *exts, e, LY_EMEM);
+
+    e->yin = 0;
+    /* store name and insubstmt info */
+    e->name = lydict_insert(ctx->xml_ctx.ctx, ext_name, ext_name_len);
+    e->insubstmt = subelem;
+    e->insubstmt_index = subelem_index;
+    e->yin |= LYS_YIN;
+
+    /* store attributes as subelements */
+    LY_ARRAY_FOR_ITER(attrs, struct yin_arg_record, iter) {
+        if (!iter->prefix) {
+            new_subelem = calloc(1, sizeof(*new_subelem));
+            if (!e->child) {
+                e->child = new_subelem;
+            } else {
+                last_subelem->next = new_subelem;
+            }
+            last_subelem = new_subelem;
+
+            last_subelem->flags |= LYS_YIN_ATTR;
+            last_subelem->stmt = lydict_insert(ctx->xml_ctx.ctx, iter->name, iter->name_len);
+            LY_CHECK_ERR_RET(!last_subelem->stmt, LOGMEM(ctx->xml_ctx.ctx), LY_EMEM);
+            if (iter->dynamic_content) {
+                last_subelem->arg = lydict_insert_zc(ctx->xml_ctx.ctx, iter->content);
+                LY_CHECK_ERR_RET(!last_subelem->arg, LOGMEM(ctx->xml_ctx.ctx), LY_EMEM);
+            } else {
+                last_subelem->arg = lydict_insert(ctx->xml_ctx.ctx, iter->content, iter->content_len);
+                LY_CHECK_ERR_RET(!last_subelem->arg, LOGMEM(ctx->xml_ctx.ctx), LY_EMEM);
+            }
+        }
+    }
+
+    /* parse subelements */
+    if (ctx->xml_ctx.status == LYXML_ELEM_CONTENT) {
+        ret = lyxml_get_string(&ctx->xml_ctx, data, &out, &out_len, &out, &out_len, &dynamic);
+        if (ret == LY_EINVAL) {
+            while (ctx->xml_ctx.status == LYXML_ELEMENT) {
+                LY_CHECK_RET(lyxml_get_element(&ctx->xml_ctx, data, &prefix, &prefix_len, &name, &name_len));
+                if (!name) {
+                    /* end of extension instance reached */
+                    break;
+                }
+                LY_CHECK_RET(yin_parse_element_generic(ctx, name, name_len, prefix, prefix_len, data, &new_subelem));
+                if (!e->child) {
+                    e->child = new_subelem;
+                } else {
+                    last_subelem->next = new_subelem;
+                }
+                last_subelem = new_subelem;
+            }
+        } else {
+            /* save text content */
+            if (dynamic) {
+                e->argument = lydict_insert_zc(ctx->xml_ctx.ctx, out);
+                if (!e->argument) {
+                    free(out);
+                    return LY_EMEM;
+                }
+            } else {
+                e->argument = lydict_insert(ctx->xml_ctx.ctx, out, out_len);
+                LY_CHECK_RET(!e->argument, LY_EMEM);
+            }
+            LY_CHECK_RET(lyxml_get_element(&ctx->xml_ctx, data, &prefix, &prefix_len, &name, &name_len));
+            LY_CHECK_RET(name, LY_EINT);
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
+yin_parse_element_generic(struct yin_parser_ctx *ctx, const char *name, size_t name_len, const char *prefix,
+                          size_t prefix_len, const char **data, struct lysp_stmt **element)
+{
+    LY_ERR ret = LY_SUCCESS;
+    const char *temp_prefix, *temp_name;
+    char *out = NULL;
+    size_t out_len, temp_name_len, temp_prefix_len;
+    int dynamic;
+    struct yin_arg_record *subelem_args = NULL;
+    struct lysp_stmt *last = NULL, *new = NULL;
+
+    /* allocate new structure for element */
+    *element = calloc(1, sizeof(**element));
+    (*element)->stmt = lydict_insert(ctx->xml_ctx.ctx, name, name_len);
+    LY_CHECK_ERR_RET(!(*element)->stmt, LOGMEM(ctx->xml_ctx.ctx), LY_EMEM);
+
+    last = (*element)->child;
+    /* load attributes */
+    while(ctx->xml_ctx.status == LYXML_ATTRIBUTE) {
+        /* add new element to linked-list */
+        new = calloc(1, sizeof(*last));
+        LY_CHECK_ERR_GOTO(ret, LOGMEM(ctx->xml_ctx.ctx), err);
+        if (!(*element)->child) {
+            /* save first */
+            (*element)->child = new;
+        } else {
+            last->next = new;
+        }
+        last = new;
+
+        last->flags |= LYS_YIN_ATTR;
+        ret = lyxml_get_attribute(&ctx->xml_ctx, data, &temp_prefix, &prefix_len, &temp_name, &temp_name_len);
+        LY_CHECK_GOTO(ret, err);
+        ret = lyxml_get_string(&ctx->xml_ctx, data, &out, &out_len, &out, &out_len, &dynamic);
+        LY_CHECK_GOTO(ret, err);
+        last->stmt = lydict_insert(ctx->xml_ctx.ctx, temp_name, temp_name_len);
+        LY_CHECK_ERR_GOTO(!last->stmt, LOGMEM(ctx->xml_ctx.ctx); ret = LY_EMEM, err);
+        /* attributes with prefix are ignored */
+        if (!temp_prefix) {
+            if (dynamic) {
+                last->arg = lydict_insert_zc(ctx->xml_ctx.ctx, out);
+                if (!last->arg) {
+                    free(out);
+                    LOGMEM(ctx->xml_ctx.ctx);
+                    ret = LY_EMEM;
+                    goto err;
+                }
+            } else {
+                last->arg = lydict_insert(ctx->xml_ctx.ctx, out, out_len);
+                LY_CHECK_ERR_GOTO(!last->arg, LOGMEM(ctx->xml_ctx.ctx); ret = LY_EMEM, err);
+            }
+        }
+    }
+
+    /* parse content of element */
+    ret = lyxml_get_string(&ctx->xml_ctx, data, &out, &out_len, &out, &out_len, &dynamic);
+    if (ret == LY_EINVAL) {
+        while (ctx->xml_ctx.status == LYXML_ELEMENT) {
+            /* parse subelements */
+            ret = lyxml_get_element(&ctx->xml_ctx, data, &temp_prefix, &temp_prefix_len, &temp_name, &temp_name_len);
+            LY_CHECK_GOTO(ret, err);
+            if (!name) {
+                /* end of element reached */
+                break;
+            }
+            ret = yin_parse_element_generic(ctx, temp_name, temp_name_len, temp_prefix, temp_prefix_len, data, &last->next);
+            LY_CHECK_GOTO(ret, err);
+            last = last->next;
+        }
+    } else {
+        /* save element content */
+        if (out_len != 0) {
+            if (dynamic) {
+                (*element)->arg = lydict_insert_zc(ctx->xml_ctx.ctx, out);
+                if (!(*element)->arg) {
+                    free(out);
+                    LOGMEM(ctx->xml_ctx.ctx);
+                    ret = LY_EMEM;
+                    goto err;
+                }
+            } else {
+                (*element)->arg = lydict_insert(ctx->xml_ctx.ctx, out, out_len);
+                LY_CHECK_ERR_GOTO(!(*element)->arg, LOGMEM(ctx->xml_ctx.ctx); ret = LY_EMEM, err);
+            }
+        }
+        /* read closing tag */
+        ret = lyxml_get_element(&ctx->xml_ctx, data, &temp_prefix, &prefix_len, &temp_name, &temp_name_len);
+        LY_CHECK_GOTO(ret, err);
+    }
+
+    FREE_ARRAY(ctx, subelem_args, free_arg_rec);
+    return LY_SUCCESS;
+
+err:
+    FREE_ARRAY(ctx, subelem_args, free_arg_rec);
+    return ret;
+}
+
+LY_ERR
+yin_parse_argument_element(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data,
+                           struct yin_argument_meta *arg_meta, struct lysp_ext_instance **exts)
+{
+    struct yin_subelement subelems[2] = {
+                                            {YANG_YIN_ELEMENT, arg_meta->flags, YIN_SUBELEM_UNIQUE},
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_NAME, arg_meta->argument, Y_IDENTIF_ARG, YANG_ARGUMENT));
+
+    return yin_parse_content(ctx, subelems, 2, data, YANG_ARGUMENT, NULL, exts);
+}
+
+LY_ERR
+yin_parse_extension(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data, struct lysp_ext **extensions)
+{
+    struct lysp_ext *ex;
+    LY_ARRAY_NEW_RET(ctx->xml_ctx.ctx, *extensions, ex, LY_EMEM);
+    LY_CHECK_RET(yin_parse_attribute(ctx, attrs, YIN_ARG_NAME, &ex->name, Y_IDENTIF_ARG, YANG_EXTENSION));
+
+    struct yin_argument_meta arg_info = {&ex->flags, &ex->argument};
+    struct yin_subelement subelems[5] = {
+                                            {YANG_ARGUMENT, &arg_info, YIN_SUBELEM_UNIQUE},
+                                            {YANG_DESCRIPTION, &ex->dsc, YIN_SUBELEM_UNIQUE},
+                                            {YANG_REFERENCE, &ex->ref, YIN_SUBELEM_UNIQUE},
+                                            {YANG_STATUS, &ex->flags, YIN_SUBELEM_UNIQUE},
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+
+    return yin_parse_content(ctx, subelems, 5, data, YANG_EXTENSION, NULL, &ex->exts);
+}
+
+/**
+ * @brief Parse module substatements.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] mod_attrs Attributes of module element.
+ * @param[in,out] data Data to read from.
+ * @param[out] mod Parsed module structure.
+ *
+ * @return LY_ERR values.
+ */
+static LY_ERR
+yin_parse_mod(struct yin_parser_ctx *ctx, struct yin_arg_record *mod_attrs, const char **data, struct lysp_module **mod)
+{
+    struct yin_subelement subelems[9] = {
+                                            {YANG_CONTACT, &(*mod)->mod->contact, YIN_SUBELEM_UNIQUE},
+                                            {YANG_DESCRIPTION, &(*mod)->mod->dsc, YIN_SUBELEM_UNIQUE},
+                                            {YANG_EXTENSION, &(*mod)->exts, 0},
+                                            {YANG_IMPORT, *mod, 0},
+                                            {YANG_NAMESPACE, &(*mod)->mod->ns, YIN_SUBELEM_MANDATORY | YIN_SUBELEM_UNIQUE},
+                                            {YANG_ORGANIZATION, &(*mod)->mod->org, YIN_SUBELEM_UNIQUE},
+                                            {YANG_PREFIX, &(*mod)->mod->prefix, YIN_SUBELEM_MANDATORY | YIN_SUBELEM_UNIQUE},
+                                            {YANG_REFERENCE, &(*mod)->mod->ref, YIN_SUBELEM_UNIQUE},
+                                            {YANG_CUSTOM, NULL, 0}
+                                        };
+
+    LY_CHECK_RET(yin_parse_attribute(ctx, mod_attrs, YIN_ARG_NAME, &(*mod)->mod->name, Y_IDENTIF_ARG, YANG_MODULE));
+
+    return yin_parse_content(ctx, subelems, 9, data, YANG_MODULE, NULL, &(*mod)->exts);
+}
+
+LY_ERR
+yin_parse_module(struct ly_ctx *ctx, const char *data, struct lys_module *mod)
+{
+    LY_ERR ret = LY_SUCCESS;
+    enum yang_keyword kw = YANG_NONE;
+    struct lysp_module *mod_p = NULL;
+    const char *prefix, *name;
+    size_t prefix_len, name_len;
+
+    struct yin_arg_record *attrs = NULL;
+
+    struct yin_parser_ctx yin_ctx;
+
+    /* initialize context */
+    memset(&yin_ctx, 0, sizeof yin_ctx);
+    yin_ctx.xml_ctx.ctx = ctx;
+    yin_ctx.xml_ctx.line = 1;
+
+
+    /* check submodule */
+    ret = lyxml_get_element(&yin_ctx.xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    LY_CHECK_GOTO(ret, cleanup);
+    ret = yin_load_attributes(&yin_ctx, &data, &attrs);
+    LY_CHECK_GOTO(ret, cleanup);
+    kw = yin_match_keyword(&yin_ctx, name, name_len, prefix, prefix_len, YANG_NONE);
+    if (kw == YANG_SUBMODULE) {
+        LOGERR(ctx, LY_EDENIED, "Input data contains submodule which cannot be parsed directly without its main module.");
+        ret = LY_EINVAL;
+        goto cleanup;
+    } else if (kw != YANG_MODULE) {
+        LOGVAL_PARSER((struct lys_parser_ctx *)&yin_ctx, LYVE_SYNTAX, "Invalid keyword \"%s\", expected \"module\" or \"submodule\".",
+                    ly_stmt2str(kw));
+        ret = LY_EVALID;
+        goto cleanup;
+    }
+
+    /* allocate module */
+    mod_p = calloc(1, sizeof *mod_p);
+    LY_CHECK_ERR_GOTO(!mod_p, LOGMEM(ctx), cleanup);
+    mod_p->mod = mod;
+    mod_p->parsing = 1;
+
+    /* parse module substatements */
+    ret = yin_parse_mod(&yin_ctx, attrs, &data, &mod_p);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    mod_p->parsing = 0;
+    mod->parsed = mod_p;
+
+cleanup:
+    if (ret != LY_SUCCESS) {
+        lysp_module_free(mod_p);
+    }
+    FREE_ARRAY(&yin_ctx, attrs, free_arg_rec);
+    lyxml_context_clear(&yin_ctx.xml_ctx);
+    return ret;
+}
diff --git a/src/parser_yin.h b/src/parser_yin.h
new file mode 100644
index 0000000..8ae07d8
--- /dev/null
+++ b/src/parser_yin.h
@@ -0,0 +1,313 @@
+/**
+ * @file parser_yin.h
+ * @author David Sedlák <xsedla1d@stud.fit.vutbr.cz>
+ * @brief YIN parser.
+ *
+ * Copyright (c) 2015 - 2019 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_PARSER_YIN_H_
+#define LY_PARSER_YIN_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "log.h"
+#include "xml.h"
+
+/* list of yin attribute strings */
+extern const char *const yin_attr_list[];
+#define yin_attr2str(STMT) yin_attr_list[STMT]
+
+#define YIN_NS_URI "urn:ietf:params:xml:ns:yang:yin:1"
+#define name2fullname(name, prefix_len) (prefix_len != 0 ? name - (prefix_len + 1) : name)
+#define namelen2fulllen(name_len, prefix_len) (prefix_len != 0 ? name_len + prefix_len + 1 : name_len)
+
+enum YIN_ARGUMENT {
+    YIN_ARG_UNKNOWN = 0,   /**< parsed argument can not be matched with any supported yin argument keyword */
+    YIN_ARG_NAME,          /**< argument name */
+    YIN_ARG_TARGET_NODE,   /**< argument target-node */
+    YIN_ARG_MODULE,        /**< argument module */
+    YIN_ARG_VALUE,         /**< argument value */
+    YIN_ARG_TEXT,          /**< argument text */
+    YIN_ARG_CONDITION,     /**< argument condition */
+    YIN_ARG_URI,           /**< argument uri */
+    YIN_ARG_DATE,          /**< argument data */
+    YIN_ARG_TAG,           /**< argument tag */
+    YIN_ARG_NONE,          /**< empty (special value) */
+};
+
+/**
+ * @brief structure to store instance of xml attribute
+ */
+struct yin_arg_record {
+    const char *prefix;   /**< start of prefix */
+    size_t prefix_len;    /**< length of prefix */
+    const char *name;     /**< start of name */
+    size_t name_len;      /**< length of name */
+    char *content;        /**< start of content */
+    size_t content_len;   /**< length of content */
+    int dynamic_content;  /**< is set to 1 iff content is dynamically allocated 0 otherwise */
+};
+
+struct yin_parser_ctx {
+    struct lyxml_context xml_ctx;  /**< context for xml parser */
+    uint8_t mod_version;           /**< module's version */
+};
+
+/* flags to encode cardinality of subelement */
+#define YIN_SUBELEM_MANDATORY   0x01    /**< is set when subelement is mandatory */
+#define YIN_SUBELEM_UNIQUE      0x02    /**< is set when subelement is unique */
+#define YIN_SUBELEM_FIRST       0x04    /**< is set when subelement is actually yang argument mapped to yin element */
+
+#define YIN_SUBELEM_PARSED      0x80    /**< is set during parsing when given subelement is encountered for the first
+                                             time to simply check validity of given constraints */
+
+struct yin_subelement {
+    enum yang_keyword type; /**< type of keyword */
+    void *dest;             /**< meta infromation passed to responsible function (mostly information about where parsed subelement should be stored) */
+    uint8_t flags;          /**< describes cardianlity of subelement can be set to YIN_SUBELEM_MANDATORY and YIN_SUBELEM_UNIQUE and YIN_SUBELEM_FIRST */
+};
+
+/* helper structure just to make code look simpler */
+struct sized_string {
+    const char *value;
+    size_t len;
+};
+
+/* Meta information passed to yin_parse_argument function,
+   holds information about where content of argument element will be stored. */
+struct yin_argument_meta {
+    uint16_t *flags;        /**< Argument flags */
+    const char **argument;  /**< Argument value */
+};
+
+/**
+ * @brief Match argument name.
+ *
+ * @param[in] name String representing name.
+ * @param[in] len Lenght of the name.
+ *
+ * @return YIN_ARGUMENT value.
+ */
+enum YIN_ARGUMENT yin_match_argument_name(const char *name, size_t len);
+
+/**
+ * @brief Generic function for content parsing
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] subelem_info array of valid subelement types and meta information,
+ *            array must be ordered by subelem_info->type in ascending order.
+ * @param[in] subelem_info_size Size of subelem_info array.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in] current_element Type of current element.
+ * @param[out] text_content Where the text content of element should be stored if any. Text content is ignored if set to NULL.
+ * @param[in,out] exts Extension instance to add to. Can be se to null if element cannot have extension as subelements.
+
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_content(struct yin_parser_ctx *ctx, struct yin_subelement *subelem_info, signed char subelem_info_size,
+                         const char **data, enum yang_keyword current_element, const char **text_content,
+                         struct lysp_ext_instance **exts);
+
+/**
+ * @brief Parse yang-version element.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of yang-version element.
+ * @param[in] data Data to read from, always moved to currently handled character.
+ * @param[out] version Storage for the parsed information.
+ * @param[in,out] exts Extension instance to add to.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_yangversion(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data, uint8_t *version,
+                             struct lysp_ext_instance **exts);
+
+/**
+ * @brief Check that val is valid UTF8 character sequence of val_type.
+ *        Doesn't check empty string, only character validity.
+ *
+ * @param[in] ctx Yin parser context for logging.
+ * @param[in] val_type Type of the input string to select method of checking character validity.
+ * @param[in] val Input to validate.
+ * @param[in] len Length of input.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR yin_validate_value(struct yin_parser_ctx *ctx, enum yang_arg val_type, char *val, size_t len);
+
+/**
+ * @brief Parse import element.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of import element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in,out] mod Structure of module that is being parsed.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_import(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs,
+                        const char **data, struct lysp_module *mod);
+
+/**
+ * @brief Match yang keyword from yin data.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] name Start of keyword name
+ * @param[in] name_len Lenght of keyword name.
+ * @param[in] prefix Start of keyword prefix.
+ * @param[in] prefix_len lenght of prefix.
+ * @param[in] parrent Identification of parrent element, use YANG_NONE for elements without parrent.
+ *
+ * @return yang_keyword values.
+ */
+enum yang_keyword yin_match_keyword(struct yin_parser_ctx *ctx, const char *name, size_t name_len,
+                                    const char *prefix, size_t prefix_len, enum yang_keyword parrent);
+
+/**
+ * @brief Parse mandatory element.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of status element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in,out] flags Flags to add to.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_mandatory(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data,
+                           uint16_t *flags, struct lysp_ext_instance **exts);
+
+/**
+ * @brief Parse status element.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of status element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in,out] flags Flags to add to.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_status(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data,
+                        uint16_t *flags, struct lysp_ext_instance **exts);
+
+/**
+ * @brief Parse when element.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of when element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[out] when_p When pointer to parse to.
+ */
+LY_ERR yin_parse_when(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data,
+                      struct lysp_when **when_p);
+
+/**
+ * @brief Parse revision date element.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of revision-date element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in,out] rev Array to store the parsed value in.
+ * @param[in,out] exts Extension instances to add to.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_revision_date(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data,
+                               char *rev, struct lysp_ext_instance **exts);
+
+/**
+ * @brief load all attributes of element into ([sized array](@ref sizedarrays)). Caller is suposed to free the array.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[out] attrs ([Sized array](@ref sizedarrays)) of attributes.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR yin_load_attributes(struct yin_parser_ctx *ctx, const char **data, struct yin_arg_record **attrs);
+
+/**
+ * @brief Parse yin-elemenet element.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of yin-element element.
+ * @param[in,out] data Data to read from, always moved to currently handled position.
+ * @param[in,out] flags Flags to add to.
+ * @prama[in,out] exts Extension instance to add to.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_yin_element_element(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data,
+                                     uint16_t *flags, struct lysp_ext_instance **exts);
+
+/**
+ * @brief Parse argument element.
+ *
+ * @param[in,out] xml_ctx Xml context.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of argument element.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in,out] arg_meta Meta information about destionation af prased data.
+ * @param[in,out] exts Extension instance to add to.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_argument_element(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data,
+                                  struct yin_argument_meta *arg_meta, struct lysp_ext_instance **exts);
+
+/**
+ * @brief Parse the extension statement.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of extension element.
+ * @param[in,out] data Data to read from.
+ * @param[in,out] extensions Extensions to add to.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_extension(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs,
+                           const char **data, struct lysp_ext **extensions);
+
+/**
+ * @brief Parse instance of extension.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] attrs [Sized array](@ref sizedarrays) of attributes of extension instance.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[in] ext_name Name of the extension element.
+ * @param[in] ext_name_len Length of extension name.
+ * @param[in] subelem Type of the keyword this extension instance is a subelement of.
+ * @param[in] subelem_index Index of the keyword instance this extension instance is a subelement of
+ * @param[in,out] exts Extension instance to add to.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_extension_instance(struct yin_parser_ctx *ctx, struct yin_arg_record *attrs, const char **data,
+                                    const char *ext_name, int ext_name_len, LYEXT_SUBSTMT subelem,
+                                    uint32_t subelem_index, struct lysp_ext_instance **exts);
+
+/**
+ * @brief Parse yin element into generic structure.
+ *
+ * @param[in,out] ctx Yin parser context for logging and to store current state.
+ * @param[in] name Name of element.
+ * @param[in] name_len Length of elements Name.
+ * @param[in] prefix Element prefix.
+ * @param[in] prefix_len Length of element prefix.
+ * @param[in,out] data Data to read from, always moved to currently handled character.
+ * @param[out] element Where the element structure should be stored.
+ *
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_element_generic(struct yin_parser_ctx *ctx, const char *name, size_t name_len, const char *prefix,
+                                 size_t prefix_len, const char **data, struct lysp_stmt **element);
+
+#endif /* LY_PARSER_YIN_H_*/
diff --git a/src/printer_yang.c b/src/printer_yang.c
index 445afc2..42fcb14 100755
--- a/src/printer_yang.c
+++ b/src/printer_yang.c
@@ -228,7 +228,9 @@
         if (!count) {
             break;
         }
-        if (ext->insubstmt == substmt && ext->insubstmt_index == substmt_index) {
+        if (ext->yin) {
+            ly_print(ctx->out, "%*s%s Model comes from different input format, extensions must be resolved first.", INDENT, ext[u].name);
+        } else if (ext->insubstmt == substmt && ext->insubstmt_index == substmt_index) {
             ypr_open(ctx->out, flag);
             if (ext[u].argument) {
                 ly_print(ctx->out, "%*s%s %s%s", INDENT, ext[u].name, ext[u].argument, ext[u].child ? " {\n" : ";\n");
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 8ed59a4..a8b2d5f 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -124,19 +124,20 @@
     const char *arg;                 /**< statement's argument */
     struct lysp_stmt *next;          /**< link to the next statement */
     struct lysp_stmt *child;         /**< list of the statement's substatements (linked list) */
-    uint16_t flags;
+    uint16_t flags;                  /**<  */
 };
-
+#define LYS_YIN 0x1
 /**
  * @brief YANG extension instance
  */
 struct lysp_ext_instance {
-    const char *name;                /**< extension identifier, including possible prefix */
-    const char *argument;            /**< optional value of the extension's argument */
-    struct lysp_stmt *child;         /**< list of the extension's substatements (linked list) */
-    LYEXT_SUBSTMT insubstmt;         /**< value identifying placement of the extension instance */
-    uint32_t insubstmt_index;        /**< in case the instance is in a substatement, this identifies
-                                          the index of that substatement */
+    const char *name;                       /**< extension identifier, including possible prefix */
+    const char *argument;                   /**< optional value of the extension's argument */
+    struct lysp_stmt *child;                /**< list of the extension's substatements (linked list) */
+    LYEXT_SUBSTMT insubstmt;                /**< value identifying placement of the extension instance */
+    uint32_t insubstmt_index;               /**< in case the instance is in a substatement, this identifies
+                                                 the index of that substatement */
+    uint8_t yin;                            /** flag for YIN source format */
 };
 
 /**
@@ -529,6 +530,8 @@
 #define LYS_SINGLEQUOTED 0x100       /**< flag for single-quoted argument of an extension instance's substatement */
 #define LYS_DOUBLEQUOTED 0x200       /**< flag for double-quoted argument of an extension instance's substatement */
 
+#define LYS_YIN_ATTR     0x1000      /**< flag to identify YIN attribute */
+
 #define LYS_ISENUM       0x200       /**< flag to simply distinguish type in struct lysc_type_bitenum_item */
 
 #define LYS_FLAGS_COMPILED_MASK 0xff /**< mask for flags that maps to the compiled structures */
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index b7fe1a8..60a392b 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -84,7 +84,7 @@
     FREE_ARRAY(ctx, rev->exts, lysp_ext_instance_free);
 }
 
-static void
+void
 lysp_ext_free(struct ly_ctx *ctx, struct lysp_ext *ext)
 {
     FREE_STRING(ctx, ext->name);
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 644b79a..75fd1ba 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -424,6 +424,29 @@
     return LY_ENOTFOUND;
 }
 
+LY_ERR
+lysp_check_enum_name(struct lys_parser_ctx *ctx, const char *name, size_t name_len)
+{
+    if (!name_len) {
+        LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Enum name must not be zero-length.");
+        return LY_EVALID;
+    } else if (isspace(name[0]) || isspace(name[name_len - 1])) {
+        LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Enum name must not have any leading or trailing whitespaces (\"%.*s\").",
+                    name_len, name);
+        return LY_EVALID;
+    } else {
+        for (size_t u = 0; u < name_len; ++u) {
+            if (iscntrl(name[u])) {
+                LOGWRN(ctx->ctx, "Control characters in enum name should be avoided (\"%.*s\", character number %d).",
+                    name_len, name, u + 1);
+                break;
+            }
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
 /*
  * @brief Check name of a new type to avoid name collisions.
  *
@@ -796,6 +819,41 @@
 }
 
 LY_ERR
+lysp_check_stringchar(struct lys_parser_ctx *ctx, unsigned int c)
+{
+    if (!is_yangutf8char(c)) {
+        LOGVAL_PARSER(ctx, LY_VCODE_INCHAR, c);
+        return LY_EVALID;
+    }
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lysp_check_identifierchar(struct lys_parser_ctx *ctx, unsigned int c, int first, int *prefix)
+{
+    if (first || (prefix && (*prefix) == 1)) {
+        if (!is_yangidentstartchar(c)) {
+            LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Invalid identifier first character '%c'.", c);
+            return LY_EVALID;
+        }
+        if (prefix) {
+            if (first) {
+                (*prefix) = 0;
+            } else {
+                (*prefix) = 2;
+            }
+        }
+    } else if (c == ':' && prefix && (*prefix) == 0) {
+        (*prefix) = 1;
+    } else if (!is_yangidentchar(c)) {
+        LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG, "Invalid identifier character '%c'.", c);
+        return LY_EVALID;
+    }
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
 lysp_load_submodule(struct lys_parser_ctx *ctx, struct lysp_module *mod, struct lysp_include *inc)
 {
     struct lysp_submodule *submod = NULL;
@@ -1261,6 +1319,210 @@
     return NULL;
 }
 
+enum yang_keyword
+lysp_match_kw(struct lys_parser_ctx *ctx, const char **data)
+{
+/**
+ * @brief Move the DATA pointer by COUNT items. Also updates the indent value in yang parser context
+ * @param[in] CTX yang parser context to update its indent value.
+ * @param[in,out] DATA pointer to move
+ * @param[in] COUNT number of items for which the DATA pointer is supposed to move on.
+ */
+#define MOVE_IN(CTX, DATA, COUNT) (*(DATA))+=COUNT;if(CTX){(CTX)->indent+=COUNT;}
+#define IF_KW(STR, LEN, STMT) if (!strncmp(*(data), STR, LEN)) {MOVE_IN(ctx, data, LEN);*kw=STMT;}
+#define IF_KW_PREFIX(STR, LEN) if (!strncmp(*(data), STR, LEN)) {MOVE_IN(ctx, data, LEN);
+#define IF_KW_PREFIX_END }
+
+    enum yang_keyword result = YANG_NONE;
+    enum yang_keyword *kw = &result;
+    /* read the keyword itself */
+    switch (**data) {
+    case 'a':
+        MOVE_IN(ctx, 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
+        break;
+    case 'b':
+        MOVE_IN(ctx, data, 1);
+        IF_KW("ase", 3, YANG_BASE)
+        else IF_KW("elongs-to", 9, YANG_BELONGS_TO)
+        else IF_KW("it", 2, YANG_BIT)
+        break;
+    case 'c':
+        MOVE_IN(ctx, 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
+        break;
+    case 'd':
+        MOVE_IN(ctx, 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
+        break;
+    case 'e':
+        MOVE_IN(ctx, 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)
+        break;
+    case 'f':
+        MOVE_IN(ctx, data, 1);
+        IF_KW("eature", 6, YANG_FEATURE)
+        else IF_KW("raction-digits", 14, YANG_FRACTION_DIGITS)
+        break;
+    case 'g':
+        MOVE_IN(ctx, data, 1);
+        IF_KW("rouping", 7, YANG_GROUPING)
+        break;
+    case 'i':
+        MOVE_IN(ctx, 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
+        break;
+    case 'k':
+        MOVE_IN(ctx, data, 1);
+        IF_KW("ey", 2, YANG_KEY)
+        break;
+    case 'l':
+        MOVE_IN(ctx, 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)
+        break;
+    case 'm':
+        MOVE_IN(ctx, 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
+        break;
+    case 'n':
+        MOVE_IN(ctx, data, 1);
+        IF_KW("amespace", 8, YANG_NAMESPACE)
+        else IF_KW("otification", 11, YANG_NOTIFICATION)
+        break;
+    case 'o':
+        MOVE_IN(ctx, 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)
+        break;
+    case 'p':
+        MOVE_IN(ctx, 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
+        break;
+    case 'r':
+        MOVE_IN(ctx, 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)
+        break;
+    case 's':
+        MOVE_IN(ctx, data, 1);
+        IF_KW("tatus", 5, YANG_STATUS)
+        else IF_KW("ubmodule", 8, YANG_SUBMODULE)
+        break;
+    case 't':
+        MOVE_IN(ctx, data, 1);
+        IF_KW("ypedef", 6, YANG_TYPEDEF)
+        else IF_KW("ype", 3, YANG_TYPE)
+        break;
+    case 'u':
+        MOVE_IN(ctx, 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)
+        break;
+    case 'v':
+        MOVE_IN(ctx, data, 1);
+        IF_KW("alue", 4, YANG_VALUE)
+        break;
+    case 'w':
+        MOVE_IN(ctx, data, 1);
+        IF_KW("hen", 3, YANG_WHEN)
+        break;
+    case 'y':
+        MOVE_IN(ctx, data, 1);
+        IF_KW("ang-version", 11, YANG_YANG_VERSION)
+        else IF_KW("in-element", 10, YANG_YIN_ELEMENT)
+        break;
+    default:
+        /* if context is not NULL we are matching keyword from YANG data*/
+        if (ctx) {
+            if (**data == ';') {
+                MOVE_IN(ctx, data, 1);
+                *kw = YANG_SEMICOLON;
+            } else if (**data == '{') {
+                MOVE_IN(ctx, data, 1);
+                *kw = YANG_LEFT_BRACE;
+            } else if (**data == '}') {
+                MOVE_IN(ctx, data, 1);
+                *kw = YANG_RIGHT_BRACE;
+            }
+        }
+        break;
+    }
+
+#undef IF_KW
+#undef IF_KW_PREFIX
+#undef IF_KW_PREFIX_END
+#undef MOVE_IN
+
+    return result;
+}
+
 unsigned int
 lysp_ext_instance_iter(struct lysp_ext_instance *ext, unsigned int index, LYEXT_SUBSTMT substmt)
 {
@@ -1275,4 +1537,3 @@
     return LY_ARRAY_SIZE(ext);
 }
 
-
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 4da52a7..76e5413 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -20,13 +20,50 @@
 #include "set.h"
 #include "tree_schema.h"
 
-#define LOGVAL_YANG(CTX, ...) LOGVAL((CTX)->ctx, LY_VLOG_LINE, &(CTX)->line, __VA_ARGS__)
+#define LOGVAL_PARSER(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 == '.')
 
+/* Macro to check YANG's yang-char grammar rule */
+#define is_yangutf8char(c) ((c >= 0x20 && c <= 0xd77) || c == 0x09 || c == 0x0a || c == 0x0d || \
+                            (c >= 0xe000 && c <= 0xfdcf)   || (c >= 0xfdf0 && c <= 0xfffd)   || \
+                            (c >= 0x10000 && c <= 0x1fffd) || (c >= 0x20000 && c <= 0x2fffd) || \
+                            (c >= 0x30000 && c <= 0x3fffd) || (c >= 0x40000 && c <= 0x2fffd) || \
+                            (c >= 0x50000 && c <= 0x5fffd) || (c >= 0x60000 && c <= 0x6fffd) || \
+                            (c >= 0x70000 && c <= 0x7fffd) || (c >= 0x80000 && c <= 0x8fffd) || \
+                            (c >= 0x90000 && c <= 0x9fffd) || (c >= 0xa0000 && c <= 0xafffd) || \
+                            (c >= 0xb0000 && c <= 0xbfffd) || (c >= 0xc0000 && c <= 0xcfffd) || \
+                            (c >= 0xd0000 && c <= 0xdfffd) || (c >= 0xe0000 && c <= 0xefffd) || \
+                            (c >= 0xf0000 && c <= 0xffffd) || (c >= 0x100000 && c <= 0x10fffd))
+
+/**
+ * @brief Try to find object with MEMBER string matching the IDENT in the given ARRAY.
+ * Macro logs an error message and returns LY_EVALID in case of existence of a matching object.
+ *
+ * @param[in] CTX yang parser context for logging.
+ * @param[in] ARRAY [sized array](@ref sizedarrays) of a generic objects with member named MEMBER to search.
+ * @param[in] MEMBER Name of the member of the objects in the ARRAY to compare.
+ * @param[in] STMT Name of the compared YANG statements for logging.
+ * @param[in] IDENT String trying to find in the ARRAY's objects inside the MEMBER member.
+ */
+#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_PARSER(CTX, LY_VCODE_DUPIDENT, IDENT, STMT); \
+                return LY_EVALID; \
+            } \
+        } \
+    }
+
+#define YANG_CHECK_NONEMPTY(CTX, VALUE_LEN, STMT) \
+    if (!VALUE_LEN) { \
+        LOGWRN((CTX)->ctx, "Empty argument of %s statement does not make sense.", STMT); \
+    }
+
 /**
  * @brief List of YANG statement groups - the (sub)module's substatements
  */
@@ -53,9 +90,9 @@
  */
 struct lys_parser_ctx {
     struct ly_ctx *ctx;
+    uint64_t line;      /**< line number */
     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 */
     uint8_t mod_version; /**< module's version */
 };
@@ -80,6 +117,31 @@
 };
 
 /**
+ * @brief Check that \p c is valid UTF8 code point for YANG string.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] c UTF8 code point of a character to check.
+ * @return LY_ERR values.
+ */
+LY_ERR lysp_check_stringchar(struct lys_parser_ctx *ctx, unsigned int c);
+
+/**
+ * @brief Check that \p c is valid UTF8 code point for YANG identifier.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] c UTF8 code point of a character to check.
+ * @param[in] first Flag to check the first character of an identifier, which is more restricted.
+ * @param[in,out] prefix Storage for internally used flag in case of possible prefixed identifiers:
+ * 0 - colon not yet found (no prefix)
+ * 1 - \p c is the colon character
+ * 2 - prefix already processed, now processing the identifier
+ *
+ * If the identifier cannot be prefixed, NULL is expected.
+ * @return LY_ERR values.
+ */
+LY_ERR lysp_check_identifierchar(struct lys_parser_ctx *ctx, unsigned int c, int first, int *prefix);
+
+/**
  * @brief Check the currently present prefixes in the module for collision with the new one.
  *
  * @param[in] ctx Context for logging.
@@ -117,7 +179,7 @@
 void lysp_sort_revisions(struct lysp_revision *revs);
 
 /**
- * @brief Find type specified type definition
+ * @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.
@@ -131,6 +193,17 @@
                       LY_DATA_TYPE *type, const struct lysp_tpdf **tpdf, struct lysp_node **node, struct lysp_module **module);
 
 /**
+ * @brief Validate enum name.
+ *
+ * @param[in] ctx yang parser context for logging.
+ * @param[in] name String to check.
+ * @param[in] name_len Length of name.
+ *
+ * @return LY_ERR values
+ */
+LY_ERR lysp_check_enum_name(struct lys_parser_ctx *ctx, const char *name, size_t name_len);
+
+/**
  * @brief Find and parse module of the given name.
  *
  * @param[in] ctx libyang context.
@@ -662,6 +735,16 @@
 LY_ERR yang_parse_module(struct lys_parser_ctx *ctx, const char *data, struct lys_module *mod);
 
 /**
+ * @brief Parse module from YIN data.
+ * @param[in] ctx Libyang context.
+ * @param[in] data Input data to be parsed.
+ * @param[in,out] mod Prepared module structure where the parsed information, including the parsed
+ * module structure, will be filled in.
+ * @return LY_ERR values.
+ */
+LY_ERR yin_parse_module(struct ly_ctx *ctx, const char *data, struct lys_module *mod);
+
+/**
  * @brief Make the specific module implemented, use the provided value as flag.
  *
  * @param[in] ctx libyang context to change.
@@ -673,4 +756,14 @@
  */
 LY_ERR ly_ctx_module_implement_internal(struct ly_ctx *ctx, struct lys_module *mod, uint8_t implemented);
 
+/**
+ * @brief match yang keyword
+ *
+ * param[in] ctx yang parser context for logging, can be NULL if keyword is from YIN data.
+ * param[in,out] data Data to read from, always moved to currently handled character.
+ *
+ * return yang_keyword values.
+ */
+enum yang_keyword lysp_match_kw(struct lys_parser_ctx *ctx, const char **data);
+
 #endif /* LY_TREE_SCHEMA_INTERNAL_H_ */
diff --git a/tests/src/CMakeLists.txt b/tests/src/CMakeLists.txt
index a1aa577..ed17333 100644
--- a/tests/src/CMakeLists.txt
+++ b/tests/src/CMakeLists.txt
@@ -5,6 +5,7 @@
     src_hash_table
     src_xml
     src_parser_yang
+    src_parser_yin
     src_tree_schema
     src_tree_schema_compile
     src_tree_schema_helpers
@@ -21,6 +22,7 @@
     " "
     " "
     " "
+    " "
     " ")
 set(tests ${tests} ${local_tests} PARENT_SCOPE)
 set(tests_wraps ${tests_wraps} ${local_tests_wraps} PARENT_SCOPE)
diff --git a/tests/src/test_parser_yang.c b/tests/src/test_parser_yang.c
index 0d6263d..cfe8cd2 100644
--- a/tests/src/test_parser_yang.c
+++ b/tests/src/test_parser_yang.c
@@ -38,13 +38,12 @@
 void lysp_when_free(struct ly_ctx *ctx, struct lysp_when *when);
 
 LY_ERR buf_add_char(struct ly_ctx *ctx, const char **input, size_t len, char **buf, size_t *buf_len, size_t *buf_used);
-LY_ERR buf_store_char(struct lys_parser_ctx *ctx, const char **input, enum yang_arg arg,
-                      char **word_p, size_t *word_len, char **word_b, size_t *buf_len, int need_buf);
+LY_ERR buf_store_char(struct lys_parser_ctx *ctx, const char **input, enum yang_arg arg, char **word_p,
+                      size_t *word_len, char **word_b, size_t *buf_len, int need_buf, int *prefix);
 LY_ERR get_keyword(struct lys_parser_ctx *ctx, const char **data, enum yang_keyword *kw, char **word_p, size_t *word_len);
 LY_ERR get_argument(struct lys_parser_ctx *ctx, const char **data, enum yang_arg arg,
                     uint16_t *flags, char **word_p, char **word_b, size_t *word_len);
 LY_ERR skip_comment(struct lys_parser_ctx *ctx, const char **data, int comment);
-LY_ERR check_identifierchar(struct lys_parser_ctx *ctx, unsigned int c, int first, int *prefix);
 
 LY_ERR parse_action(struct lys_parser_ctx *ctx, const char **data, struct lysp_node *parent, struct lysp_action **actions);
 LY_ERR parse_any(struct lys_parser_ctx *ctx, const char **data, enum yang_keyword kw, struct lysp_node *parent, struct lysp_node **siblings);
@@ -141,10 +140,10 @@
     const char *str;
     char *buf, *p;
     size_t len, size;
-    int prefix;
     struct lys_parser_ctx ctx;
     ctx.ctx = NULL;
     ctx.line = 1;
+    int prefix = 0;
 
     /* storing into buffer */
     str = "abcd";
@@ -161,48 +160,50 @@
     /* invalid first characters */
     len = 0;
     str = "2invalid";
-    assert_int_equal(LY_EVALID, buf_store_char(&ctx, &str, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1));
+    assert_int_equal(LY_EVALID, buf_store_char(&ctx, &str, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix));
     str = ".invalid";
-    assert_int_equal(LY_EVALID, buf_store_char(&ctx, &str, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1));
+    assert_int_equal(LY_EVALID, buf_store_char(&ctx, &str, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix));
     str = "-invalid";
-    assert_int_equal(LY_EVALID, buf_store_char(&ctx, &str, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1));
+    assert_int_equal(LY_EVALID, buf_store_char(&ctx, &str, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix));
     /* invalid following characters */
     len = 3; /* number of characters read before the str content */
     str = "!";
-    assert_int_equal(LY_EVALID, buf_store_char(&ctx, &str, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1));
+    assert_int_equal(LY_EVALID, buf_store_char(&ctx, &str, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix));
     str = ":";
-    assert_int_equal(LY_EVALID, buf_store_char(&ctx, &str, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1));
+    assert_int_equal(LY_EVALID, buf_store_char(&ctx, &str, Y_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix));
     /* valid colon for prefixed identifiers */
     len = size = 0;
     p = NULL;
+    prefix = 0;
     str = "x:id";
-    assert_int_equal(LY_SUCCESS, buf_store_char(&ctx, &str, Y_PREF_IDENTIF_ARG, &p, &len, &buf, &size, 0));
+    assert_int_equal(LY_SUCCESS, buf_store_char(&ctx, &str, Y_PREF_IDENTIF_ARG, &p, &len, &buf, &size, 0, &prefix));
     assert_int_equal(1, len);
     assert_null(buf);
     assert_string_equal(":id", str);
     assert_int_equal('x', p[len - 1]);
-    assert_int_equal(LY_SUCCESS, buf_store_char(&ctx, &str, Y_PREF_IDENTIF_ARG, &p, &len, &buf, &size, 1));
+    assert_int_equal(LY_SUCCESS, buf_store_char(&ctx, &str, Y_PREF_IDENTIF_ARG, &p, &len, &buf, &size, 1, &prefix));
     assert_int_equal(2, len);
     assert_string_equal("id", str);
     assert_int_equal(':', p[len - 1]);
     free(buf);
+    prefix = 0;
 
     /* checking identifiers */
-    assert_int_equal(LY_EVALID, check_identifierchar(&ctx, ':', 0, NULL));
+    assert_int_equal(LY_EVALID, lysp_check_identifierchar(&ctx, ':', 0, NULL));
     logbuf_assert("Invalid identifier character ':'. Line number 1.");
-    assert_int_equal(LY_EVALID, check_identifierchar(&ctx, '#', 1, NULL));
+    assert_int_equal(LY_EVALID, lysp_check_identifierchar(&ctx, '#', 1, NULL));
     logbuf_assert("Invalid identifier first character '#'. Line number 1.");
 
-    assert_int_equal(LY_SUCCESS, check_identifierchar(&ctx, 'a', 1, &prefix));
+    assert_int_equal(LY_SUCCESS, lysp_check_identifierchar(&ctx, 'a', 1, &prefix));
     assert_int_equal(0, prefix);
-    assert_int_equal(LY_SUCCESS, check_identifierchar(&ctx, ':', 0, &prefix));
+    assert_int_equal(LY_SUCCESS, lysp_check_identifierchar(&ctx, ':', 0, &prefix));
     assert_int_equal(1, prefix);
-    assert_int_equal(LY_EVALID, check_identifierchar(&ctx, ':', 0, &prefix));
+    assert_int_equal(LY_EVALID, lysp_check_identifierchar(&ctx, ':', 0, &prefix));
     assert_int_equal(1, prefix);
-    assert_int_equal(LY_SUCCESS, check_identifierchar(&ctx, 'b', 0, &prefix));
+    assert_int_equal(LY_SUCCESS, lysp_check_identifierchar(&ctx, 'b', 0, &prefix));
     assert_int_equal(2, prefix);
     /* second colon is invalid */
-    assert_int_equal(LY_EVALID, check_identifierchar(&ctx, ':', 0, &prefix));
+    assert_int_equal(LY_EVALID, lysp_check_identifierchar(&ctx, ':', 0, &prefix));
     logbuf_assert("Invalid identifier character ':'. Line number 1.");
 }
 
@@ -282,6 +283,10 @@
     assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_STR_ARG, NULL, &word, &buf, &len));
     logbuf_assert("Invalid character sequence \"}\", expected unquoted string character, optsep, semicolon or opening brace. Line number 1.");
 
+    /* invalid identifier-ref-arg-str */
+    str = "pre:pre:value";
+    assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_PREF_IDENTIF_ARG, NULL, &word, &buf, &len));
+
     str = "\"\";"; /* empty identifier is not allowed */
     assert_int_equal(LY_EVALID, get_argument(&ctx, &str, Y_IDENTIF_ARG, NULL, &word, &buf, &len));
     logbuf_assert("Statement argument is required. Line number 1.");
@@ -1057,6 +1062,15 @@
     logbuf_assert("Invalid keyword \"prefix\", expected \"module\" or \"submodule\". Line number 3.");
     mod = mod_renew(&ctx);
 
+    str = "module " SCHEMA_BEGINNING "}";
+    str = "module " SCHEMA_BEGINNING "leaf enum {type enumeration {enum seven { position 7;}}}}";
+    m = mod->mod;
+    free(mod);
+    m->parsed = NULL;
+    assert_int_equal(LY_EVALID, yang_parse_module(&ctx, str, m));
+    logbuf_assert("Invalid keyword \"position\" as a child of \"enum\". Line number 3.");
+    mod = mod_renew(&ctx);
+
     /* extensions */
     TEST_GENERIC("prefix:test;}", mod->exts,
                  assert_string_equal("prefix:test", mod->exts[0].name);
diff --git a/tests/src/test_parser_yin.c b/tests/src/test_parser_yin.c
new file mode 100644
index 0000000..de7600d
--- /dev/null
+++ b/tests/src/test_parser_yin.c
@@ -0,0 +1,971 @@
+/**
+ * @file test_parser_yin.c
+ * @author David Sedlák <xsedla1d@stud.fit.vutbr.cz>
+ * @brief unit tests for functions from parser_yin.c
+ *
+ * Copyright (c) 2015 - 2019 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 <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "../../src/common.h"
+#include "../../src/tree_schema.h"
+#include "../../src/tree_schema_internal.h"
+#include "../../src/parser_yin.h"
+#include "../../src/xml.h"
+
+/* prototypes of static functions */
+void lysp_ext_instance_free(struct ly_ctx *ctx, struct lysp_ext_instance *ext);
+void lysp_ext_free(struct ly_ctx *ctx, struct lysp_ext *ext);
+void lysp_when_free(struct ly_ctx *ctx, struct lysp_when *when);
+
+struct state {
+    struct ly_ctx *ctx;
+    struct lys_module *mod;
+    struct lysp_module *lysp_mod;
+    struct yin_parser_ctx *yin_ctx;
+    bool finished_correctly;
+};
+
+#define BUFSIZE 1024
+char logbuf[BUFSIZE] = {0};
+int store = -1; /* negative for infinite logging, positive for limited logging */
+
+/* set to 0 to printing error messages to stderr instead of checking them in code */
+#define ENABLE_LOGGER_CHECKING 1
+
+#if ENABLE_LOGGER_CHECKING
+static void
+logger(LY_LOG_LEVEL level, const char *msg, const char *path)
+{
+    (void) level; /* unused */
+    if (store) {
+        if (path && path[0]) {
+            snprintf(logbuf, BUFSIZE - 1, "%s %s", msg, path);
+        } else {
+            strncpy(logbuf, msg, BUFSIZE - 1);
+        }
+        if (store > 0) {
+            --store;
+        }
+    }
+}
+#endif
+
+#if ENABLE_LOGGER_CHECKING
+#   define logbuf_assert(str) assert_string_equal(logbuf, str)
+#else
+#   define logbuf_assert(str)
+#endif
+
+#define TEST_DUP_GENERIC(PREFIX, MEMBER, VALUE1, VALUE2, FUNC, RESULT, LINE, CLEANUP) \
+    str = PREFIX MEMBER" "VALUE1";"MEMBER" "VALUE2";} ..."; \
+    assert_int_equal(LY_EVALID, FUNC(&ctx, &str, RESULT)); \
+    logbuf_assert("Duplicate keyword \""MEMBER"\". Line number "LINE"."); \
+    CLEANUP
+
+
+static int
+setup_f(void **state)
+{
+    struct state *st = NULL;
+
+#if ENABLE_LOGGER_CHECKING
+    /* setup logger */
+    ly_set_log_clb(logger, 1);
+#endif
+
+    /* allocate state variable */
+    (*state) = st = calloc(1, sizeof(*st));
+    if (!st) {
+        fprintf(stderr, "Memmory allocation failed");
+        return EXIT_FAILURE;
+    }
+
+    /* create new libyang context */
+    ly_ctx_new(NULL, 0, &st->ctx);
+
+    /* allocate new module */
+    st->mod = calloc(1, sizeof(*st->mod));
+    st->mod->ctx = st->ctx;
+
+    /* allocate new parsed module */
+    st->lysp_mod = calloc(1, sizeof(*st->lysp_mod));
+    st->lysp_mod->mod = calloc(1, sizeof(*st->lysp_mod->mod));
+    st->lysp_mod->mod->ctx = st->ctx;
+
+    /* allocate parser context */
+    st->yin_ctx = calloc(1, sizeof(*st->yin_ctx));
+    st->yin_ctx->xml_ctx.ctx = st->ctx;
+    st->yin_ctx->xml_ctx.line = 1;
+
+    return EXIT_SUCCESS;
+}
+
+static int
+teardown_f(void **state)
+{
+    struct state *st = *(struct state **)state;
+    struct lys_module *temp;
+
+#if ENABLE_LOGGER_CHECKING
+    /* teardown logger */
+    if (!st->finished_correctly && logbuf[0] != '\0') {
+        fprintf(stderr, "%s\n", logbuf);
+    }
+#endif
+
+    temp = st->lysp_mod->mod;
+
+    lyxml_context_clear(&st->yin_ctx->xml_ctx);
+    lys_module_free(st->mod, NULL);
+    lysp_module_free(st->lysp_mod);
+    lys_module_free(temp, NULL);
+    ly_ctx_destroy(st->ctx, NULL);
+    free(st->yin_ctx);
+    free(st);
+
+    return EXIT_SUCCESS;
+}
+
+static struct state*
+reset_state(void **state)
+{
+    ((struct state *)*state)->finished_correctly = true;
+    logbuf[0] = '\0';
+    teardown_f(state);
+    setup_f(state);
+
+    return *state;
+}
+
+void
+logbuf_clean(void)
+{
+    logbuf[0] = '\0';
+}
+
+static void
+test_yin_parse_module(void **state)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct state *st = *state;
+
+    ret = yin_parse_module(st->ctx,
+                    "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\
+                        name=\"example-foo\"\
+                        xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\
+                        xmlns:foo=\"urn:example:foo\"\
+                        xmlns:myext=\"urn:example:extensions\">\
+                        <namespace uri=\"urn:example:foo\" xmlns:myext=\"urn:example:extensions\"/>\
+                        <prefix xmlns:myxt=\"urn:emple:extensions\" value=\"foo\" xmlns:myext=\"urn:example:extensions\"/>\
+                     </module>",
+                st->mod);
+
+    assert_int_equal(ret, LY_SUCCESS);
+    assert_string_equal(st->mod->parsed->mod->name, "example-foo");
+    assert_string_equal(st->mod->parsed->mod->prefix, "foo");
+    assert_string_equal(st->mod->parsed->mod->ns, "urn:example:foo");
+
+    st = reset_state(state);
+    ret = yin_parse_module(st->ctx,
+                           "<module name=\"example-foo\">\
+                                <invalid-tag uri=\"urn:example:foo\"\"/>\
+                            </module>",
+                            st->mod);
+    assert_int_equal(ret, LY_EVALID);
+
+    st = reset_state(state);
+    ret = yin_parse_module(st->ctx,
+                           "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\
+                            </module>",
+                           st->mod);
+    assert_int_equal(ret, LY_EVALID);
+    logbuf_assert("Missing mandatory attribute name of module element. Line number 1.");
+
+    st = reset_state(state);
+    ret = yin_parse_module(st->ctx,
+                    "",
+                st->mod);
+    assert_int_equal(ret, LY_EVALID);
+    logbuf_assert("Invalid keyword \"(null)\", expected \"module\" or \"submodule\". Line number 1.");
+    st->finished_correctly = true;
+}
+
+static void
+test_yin_match_keyword(void **state)
+{
+    struct state *st = *state;
+
+    const char *prefix, *name;
+    struct yin_arg_record *args = NULL;
+    size_t prefix_len, name_len;
+    /* create mock yin namespace in xml context */
+    const char *data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" />";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    yin_load_attributes(st->yin_ctx, &data, &args);
+    LY_ARRAY_FREE(args);
+
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "anydatax", strlen("anydatax"), prefix, prefix_len, YANG_NONE), YANG_NONE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "asdasd", strlen("asdasd"), prefix, prefix_len, YANG_NONE), YANG_NONE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "", 0, prefix, prefix_len, YANG_NONE), YANG_NONE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "anydata", strlen("anydata"), prefix, prefix_len, YANG_NONE), YANG_ANYDATA);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "anyxml", strlen("anyxml"), prefix, prefix_len, YANG_NONE), YANG_ANYXML);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "argument", strlen("argument"), prefix, prefix_len, YANG_NONE), YANG_ARGUMENT);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "augment", strlen("augment"), prefix, prefix_len, YANG_NONE), YANG_AUGMENT);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "base", strlen("base"), prefix, prefix_len, YANG_NONE), YANG_BASE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "belongs-to", strlen("belongs-to"), prefix, prefix_len, YANG_NONE), YANG_BELONGS_TO);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "bit", strlen("bit"), prefix, prefix_len, YANG_NONE), YANG_BIT);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "case", strlen("case"), prefix, prefix_len, YANG_NONE), YANG_CASE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "choice", strlen("choice"), prefix, prefix_len, YANG_NONE), YANG_CHOICE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "config", strlen("config"), prefix, prefix_len, YANG_NONE), YANG_CONFIG);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "contact", strlen("contact"), prefix, prefix_len, YANG_NONE), YANG_CONTACT);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "container", strlen("container"), prefix, prefix_len, YANG_NONE), YANG_CONTAINER);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "default", strlen("default"), prefix, prefix_len, YANG_NONE), YANG_DEFAULT);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "description", strlen("description"), prefix, prefix_len, YANG_NONE), YANG_DESCRIPTION);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "deviate", strlen("deviate"), prefix, prefix_len, YANG_NONE), YANG_DEVIATE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "deviation", strlen("deviation"), prefix, prefix_len, YANG_NONE), YANG_DEVIATION);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "enum", strlen("enum"), prefix, prefix_len, YANG_NONE), YANG_ENUM);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "error-app-tag", strlen("error-app-tag"), prefix, prefix_len, YANG_NONE), YANG_ERROR_APP_TAG);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "error-message", strlen("error-message"), prefix, prefix_len, YANG_NONE), YANG_ERROR_MESSAGE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "extension", strlen("extension"), prefix, prefix_len, YANG_NONE), YANG_EXTENSION);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "feature", strlen("feature"), prefix, prefix_len, YANG_NONE), YANG_FEATURE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "fraction-digits", strlen("fraction-digits"), prefix,  prefix_len, YANG_NONE), YANG_FRACTION_DIGITS);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "grouping", strlen("grouping"), prefix, prefix_len, YANG_NONE), YANG_GROUPING);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "identity", strlen("identity"), prefix, prefix_len, YANG_NONE), YANG_IDENTITY);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "if-feature", strlen("if-feature"), prefix, prefix_len, YANG_NONE), YANG_IF_FEATURE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "import", strlen("import"), prefix, prefix_len, YANG_NONE), YANG_IMPORT);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "include", strlen("include"), prefix, prefix_len, YANG_NONE), YANG_INCLUDE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "input", strlen("input"), prefix, prefix_len, YANG_NONE), YANG_INPUT);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "key", strlen("key"), prefix, prefix_len, YANG_NONE), YANG_KEY);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "leaf", strlen("leaf"), prefix, prefix_len, YANG_NONE), YANG_LEAF);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "leaf-list", strlen("leaf-list"), prefix, prefix_len, YANG_NONE), YANG_LEAF_LIST);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "length", strlen("length"), prefix, prefix_len, YANG_NONE), YANG_LENGTH);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "list", strlen("list"), prefix, prefix_len, YANG_NONE), YANG_LIST);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "mandatory", strlen("mandatory"), prefix, prefix_len, YANG_NONE), YANG_MANDATORY);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "max-elements", strlen("max-elements"), prefix, prefix_len, YANG_NONE), YANG_MAX_ELEMENTS);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "min-elements", strlen("min-elements"), prefix, prefix_len, YANG_NONE), YANG_MIN_ELEMENTS);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "modifier", strlen("modifier"), prefix, prefix_len, YANG_NONE), YANG_MODIFIER);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "module", strlen("module"), prefix, prefix_len, YANG_NONE), YANG_MODULE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "must", strlen("must"), prefix, prefix_len, YANG_NONE), YANG_MUST);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "namespace", strlen("namespace"), prefix, prefix_len, YANG_NONE), YANG_NAMESPACE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "notification", strlen("notification"), prefix, prefix_len, YANG_NONE), YANG_NOTIFICATION);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "ordered-by", strlen("ordered-by"), prefix, prefix_len, YANG_NONE), YANG_ORDERED_BY);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "organization", strlen("organization"), prefix, prefix_len, YANG_NONE), YANG_ORGANIZATION);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "output", strlen("output"), prefix, prefix_len, YANG_NONE), YANG_OUTPUT);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "path", strlen("path"), prefix, prefix_len, YANG_NONE), YANG_PATH);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "pattern", strlen("pattern"), prefix, prefix_len, YANG_NONE), YANG_PATTERN);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "position", strlen("position"), prefix, prefix_len, YANG_NONE), YANG_POSITION);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "prefix", strlen("prefix"), prefix, prefix_len, YANG_NONE), YANG_PREFIX);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "presence", strlen("presence"), prefix, prefix_len, YANG_NONE), YANG_PRESENCE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "range", strlen("range"), prefix, prefix_len, YANG_NONE), YANG_RANGE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "reference", strlen("reference"), prefix, prefix_len, YANG_NONE), YANG_REFERENCE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "refine", strlen("refine"), prefix, prefix_len, YANG_NONE), YANG_REFINE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "require-instance", strlen("require-instance"), prefix, prefix_len, YANG_NONE), YANG_REQUIRE_INSTANCE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "revision", strlen("revision"), prefix, prefix_len, YANG_NONE), YANG_REVISION);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "revision-date", strlen("revision-date"), prefix, prefix_len, YANG_NONE), YANG_REVISION_DATE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "rpc", strlen("rpc"), prefix, prefix_len, YANG_NONE), YANG_RPC);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "status", strlen("status"), prefix, prefix_len, YANG_NONE), YANG_STATUS);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "submodule", strlen("submodule"), prefix, prefix_len, YANG_NONE), YANG_SUBMODULE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "type", strlen("type"), prefix, prefix_len, YANG_NONE), YANG_TYPE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "typedef", strlen("typedef"), prefix, prefix_len, YANG_NONE), YANG_TYPEDEF);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "unique", strlen("unique"), prefix, prefix_len, YANG_NONE), YANG_UNIQUE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "units", strlen("units"), prefix, prefix_len, YANG_NONE), YANG_UNITS);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "uses", strlen("uses"), prefix, prefix_len, YANG_NONE), YANG_USES);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "value", strlen("value"), prefix, prefix_len, YANG_NONE), YANG_VALUE);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "when", strlen("when"), prefix, prefix_len, YANG_NONE), YANG_WHEN);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "yang-version", strlen("yang-version"), prefix, prefix_len, YANG_NONE), YANG_YANG_VERSION);
+    assert_int_equal(yin_match_keyword(st->yin_ctx, "yin-element", strlen("yin-element"), prefix, prefix_len, YANG_NONE), YANG_YIN_ELEMENT);
+
+    st->finished_correctly = true;
+}
+
+static void
+test_yin_match_argument_name(void **state)
+{
+    (void)state; /* unused */
+
+    assert_int_equal(yin_match_argument_name("", 5), YIN_ARG_UNKNOWN);
+    assert_int_equal(yin_match_argument_name("qwertyasd", 5), YIN_ARG_UNKNOWN);
+    assert_int_equal(yin_match_argument_name("conditionasd", 8), YIN_ARG_UNKNOWN);
+    assert_int_equal(yin_match_argument_name("condition", 9), YIN_ARG_CONDITION);
+    assert_int_equal(yin_match_argument_name("date", 4), YIN_ARG_DATE);
+    assert_int_equal(yin_match_argument_name("module", 6), YIN_ARG_MODULE);
+    assert_int_equal(yin_match_argument_name("name", 4), YIN_ARG_NAME);
+    assert_int_equal(yin_match_argument_name("tag", 3), YIN_ARG_TAG);
+    assert_int_equal(yin_match_argument_name("target-node", 11), YIN_ARG_TARGET_NODE);
+    assert_int_equal(yin_match_argument_name("text", 4), YIN_ARG_TEXT);
+    assert_int_equal(yin_match_argument_name("uri", 3), YIN_ARG_URI);
+    assert_int_equal(yin_match_argument_name("value", 5), YIN_ARG_VALUE);
+}
+
+static void
+test_meta(void **state)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct state *st = *state;
+
+    ret = yin_parse_module(st->ctx,"<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"\
+                                        name=\"example-foo\">\
+                                        <prefix value=\"foo\">ignored</prefix>\
+                                        <namespace uri=\"urn:example:foo\" xmlns:myext=\"urn:example:extensions\"/>\
+                                        <organization xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"><text>organization...</text></organization>\
+                                        <contact><text>contact...</text></contact>\
+                                        <description><text>description...</text></description>\
+                                        <reference><text>reference...</text></reference>\
+                                    </module>", st->mod);
+
+    assert_int_equal(ret, LY_SUCCESS);
+    assert_string_equal(st->mod->parsed->mod->org, "organization...");
+    assert_string_equal(st->mod->parsed->mod->contact, "contact...");
+    assert_string_equal(st->mod->parsed->mod->dsc, "description...");
+    assert_string_equal(st->mod->parsed->mod->ref, "reference...");
+
+    st = reset_state(state);
+    ret = yin_parse_module(st->ctx,"<module name=\"example-foo\">\
+                                        <organization test=\"invalid-argument\">organization...</organization>\
+                                    </module>", st->mod);
+    assert_int_equal(ret, LY_EVALID);
+
+    st->finished_correctly = true;
+}
+
+static void
+test_yin_parse_import(void **state)
+{
+    struct state *st = *state;
+    const char *prefix = NULL, *name = NULL;
+    size_t prefix_len = 0, name_len = 0;
+    LY_ERR ret = LY_SUCCESS;
+    struct yin_arg_record *args = NULL;
+
+    const char *data = "<import xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" module=\"a\">\
+                            <prefix value=\"a_mod\"/>\
+                            <revision-date date=\"2015-01-01\"></revision-date>\
+                            <description><text>import description</text></description>\
+                            <reference><text>import reference</text></reference>\
+                        </import>\
+                        \
+                        <import xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" module=\"a\">\
+                            <prefix value=\"a_mod\"/>\
+                            <revision-date date=\"2015-01-01\" />\
+                        </import>";
+    /* first import */
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    yin_load_attributes(st->yin_ctx, &data, &args);
+    st->lysp_mod->mod->prefix = "b-mod";
+    ret = yin_parse_import(st->yin_ctx, args, &data, st->lysp_mod);
+    assert_int_equal(ret, LY_SUCCESS);
+    assert_string_equal(st->lysp_mod->imports->name, "a");
+    assert_string_equal(st->lysp_mod->imports->prefix, "a_mod");
+    assert_string_equal(st->lysp_mod->imports->rev, "2015-01-01");
+    assert_string_equal(st->lysp_mod->imports->dsc, "import description");
+    assert_string_equal(st->lysp_mod->imports->ref, "import reference");
+    LY_ARRAY_FREE(args);
+    args = NULL;
+    st = reset_state(state);
+
+    /* second invalid import */
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    yin_load_attributes(st->yin_ctx, &data, &args);
+    st->lysp_mod->mod->prefix = "a_mod";
+    ret = yin_parse_import(st->yin_ctx, args, &data, st->lysp_mod);
+    assert_int_equal(ret, LY_EVALID);
+    logbuf_assert("Prefix \"a_mod\" already used as module prefix. Line number 1.");
+    LY_ARRAY_FREE(args);
+    args = NULL;
+
+    st = reset_state(state);
+    /* import with unknown child element */
+    data = "<import xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" module=\"a\">\
+                <what value=\"a_mod\"/>\
+            </import>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    yin_load_attributes(st->yin_ctx, &data, &args);
+    st->lysp_mod->mod->prefix = "invalid_mod";
+    ret = yin_parse_import(st->yin_ctx, args, &data, st->lysp_mod);
+    assert_int_equal(ret, LY_EVALID);
+    logbuf_assert("Unexpected child element \"what\" of import element. Line number 1.");
+    LY_ARRAY_FREE(args);
+
+    st->finished_correctly = true;
+}
+
+static void
+test_yin_parse_status(void **state)
+{
+    struct state *st = *state;
+    const char *prefix = NULL, *name = NULL;
+    size_t prefix_len = 0, name_len = 0;
+    LY_ERR ret = LY_SUCCESS;
+    uint16_t flags = 0;
+    struct lysp_ext_instance *exts;
+    struct yin_arg_record *args = NULL;
+
+    /* try all valid values */
+    const char *data = "<status value=\"current\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"/>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    yin_load_attributes(st->yin_ctx, &data, &args);
+    ret = yin_parse_status(st->yin_ctx, args, &data, &flags, &exts);
+    assert_int_equal(st->yin_ctx->xml_ctx.status, LYXML_END);
+    assert_int_equal(ret, LY_SUCCESS);
+    assert_true(flags & LYS_STATUS_CURR);
+    LY_ARRAY_FREE(args);
+    args = NULL;
+
+    st = reset_state(state);
+    flags = 0;
+    data = "<status value=\"deprecated\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"/>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    yin_load_attributes(st->yin_ctx, &data, &args);
+    ret = yin_parse_status(st->yin_ctx, args, &data, &flags, &exts);
+    assert_int_equal(st->yin_ctx->xml_ctx.status, LYXML_END);
+    assert_int_equal(ret, LY_SUCCESS);
+    assert_true(flags & LYS_STATUS_DEPRC);
+    LY_ARRAY_FREE(args);
+    args = NULL;
+
+    st = reset_state(state);
+    flags = 0;
+    data = "<status value=\"obsolete\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"/>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    yin_load_attributes(st->yin_ctx, &data, &args);
+    ret = yin_parse_status(st->yin_ctx, args, &data, &flags, &exts);
+    assert_int_equal(st->yin_ctx->xml_ctx.status, LYXML_END);
+    assert_int_equal(ret, LY_SUCCESS);
+    assert_true(flags & LYS_STATUS_OBSLT);
+    LY_ARRAY_FREE(args);
+    args = NULL;
+
+    /* invalid status value */
+    st = reset_state(state);
+    flags = 0;
+    data = "<status value=\"dunno\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"/>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    yin_load_attributes(st->yin_ctx, &data, &args);
+    ret = yin_parse_status(st->yin_ctx, args, &data, &flags, &exts);
+    assert_int_equal(ret, LY_EVALID);
+    logbuf_assert("Invalid value \"dunno\" of \"status\". Line number 1.");
+    LY_ARRAY_FREE(args);
+
+    st->finished_correctly = true;
+}
+
+static void
+test_yin_parse_extension(void **state)
+{
+    struct state *st = *state;
+    const char *prefix = NULL, *name = NULL;
+    size_t prefix_len = 0, name_len = 0;
+    LY_ERR ret = LY_SUCCESS;
+    struct yin_arg_record *args = NULL;
+    struct lysp_ext *exts = NULL, *iter = NULL;
+
+    const char *data = "<extension name=\"b\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\
+                            <argument name=\"argname\"></argument>\
+                            <description><text>desc</text></description>\
+                            <reference><text>ref</text></reference>\
+                            <status value=\"deprecated\"></status>\
+                        </extension>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    yin_load_attributes(st->yin_ctx, &data, &args);
+    ret = yin_parse_extension(st->yin_ctx, args, &data, &exts);
+    assert_int_equal(st->yin_ctx->xml_ctx.status, LYXML_END);
+    assert_int_equal(ret, LY_SUCCESS);
+    LY_ARRAY_FOR_ITER(exts, struct lysp_ext, iter) {
+        assert_string_equal(iter->name, "b");
+        assert_string_equal(iter->dsc, "desc");
+        assert_string_equal(iter->ref, "ref");
+        assert_string_equal(iter->argument, "argname");
+        assert_true(iter->flags & LYS_STATUS_DEPRC);
+    }
+    lydict_remove(st->ctx, "b");
+    lydict_remove(st->ctx, "desc");
+    lydict_remove(st->ctx, "ref");
+    lydict_remove(st->ctx, "argname");
+    LY_ARRAY_FREE(args);
+    LY_ARRAY_FREE(exts);
+    st->finished_correctly = true;
+}
+
+static void
+test_yin_parse_yin_element_element(void **state)
+{
+    struct state *st = *state;
+    const char *prefix = NULL, *name = NULL;
+    size_t prefix_len = 0, name_len = 0;
+    LY_ERR ret = LY_SUCCESS;
+    uint16_t flags = 0;
+    struct lysp_ext_instance *exts;
+    struct yin_arg_record *args = NULL;
+
+    /* try all valid values */
+    const char *data = "<yin-element value=\"true\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"/>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    yin_load_attributes(st->yin_ctx, &data, &args);
+    ret = yin_parse_yin_element_element(st->yin_ctx, args, &data, &flags, &exts);
+    assert_int_equal(st->yin_ctx->xml_ctx.status, LYXML_END);
+    assert_int_equal(ret, LY_SUCCESS);
+    assert_true(flags & LYS_YINELEM_TRUE);
+    LY_ARRAY_FREE(args);
+    args = NULL;
+
+    st = reset_state(state);
+    flags = 0;
+    data = "<yin-element value=\"false\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"/>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    yin_load_attributes(st->yin_ctx, &data, &args);
+    ret = yin_parse_yin_element_element(st->yin_ctx, args, &data, &flags, &exts);
+    assert_int_equal(st->yin_ctx->xml_ctx.status, LYXML_END);
+    assert_int_equal(ret, LY_SUCCESS);
+    assert_true(flags & LYS_YINELEM_FALSE);
+    LY_ARRAY_FREE(args);
+    args = NULL;
+
+    /* invalid value */
+    st = reset_state(state);
+    flags = 0;
+    data = "<yin-element value=\"invalid\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\"/>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    yin_load_attributes(st->yin_ctx, &data, &args);
+    ret = yin_parse_yin_element_element(st->yin_ctx, args, &data, &flags, &exts);
+    assert_int_equal(ret, LY_EVALID);
+    LY_ARRAY_FREE(args);
+    args = NULL;
+
+    st->finished_correctly = true;
+}
+
+static void
+test_yin_parse_element_generic(void **state)
+{
+    const char *prefix, *name;
+    struct state *st = *state;
+    struct lysp_ext_instance exts;
+    size_t prefix_len, name_len;
+    LY_ERR ret;
+
+    memset(&exts, 0, sizeof(exts));
+
+    const char *data = "<elem attr=\"value\">text_value</elem>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    ret = yin_parse_element_generic(st->yin_ctx, name, name_len, prefix, prefix_len, &data, &exts.child);
+    assert_int_equal(ret, LY_SUCCESS);
+    assert_int_equal(st->yin_ctx->xml_ctx.status, LYXML_END);
+    assert_string_equal(exts.child->stmt, "elem");
+    assert_string_equal(exts.child->arg, "text_value");
+    assert_string_equal(exts.child->child->stmt, "attr");
+    assert_string_equal(exts.child->child->arg, "value");
+    assert_true(exts.child->child->flags & LYS_YIN_ATTR);
+    lysp_ext_instance_free(st->ctx, &exts);
+    st = reset_state(state);
+
+    data = "<elem></elem>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    ret = yin_parse_element_generic(st->yin_ctx, name, name_len, prefix, prefix_len, &data, &exts.child);
+    assert_int_equal(ret, LY_SUCCESS);
+    assert_string_equal(exts.child->stmt, "elem");
+    assert_null(exts.child->child);
+    assert_null(exts.child->arg);
+    assert_int_equal(st->yin_ctx->xml_ctx.status, LYXML_END);
+    lysp_ext_instance_free(st->ctx, &exts);
+
+    st->finished_correctly = true;
+}
+
+static void
+test_yin_parse_extension_instance(void **state)
+{
+    LY_ERR ret;
+    struct state *st = *state;
+    const char *prefix, *name;
+    size_t prefix_len, name_len;
+    struct yin_arg_record *args = NULL;
+    struct lysp_ext_instance *exts = NULL;
+    const char *data = "<ext value1=\"test\" value=\"test2\"><subelem>text</subelem></ext>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    yin_load_attributes(st->yin_ctx, &data, &args);
+    ret = yin_parse_extension_instance(st->yin_ctx, args, &data, name2fullname(name, prefix_len),
+                                       namelen2fulllen(name_len, prefix_len), LYEXT_SUBSTMT_CONTACT, 0, &exts);
+    assert_int_equal(ret, LY_SUCCESS);
+    assert_string_equal(exts->name, "ext");
+    assert_int_equal(exts->insubstmt_index, 0);
+    assert_true(exts->insubstmt == LYEXT_SUBSTMT_CONTACT);
+    assert_true(exts->yin & LYS_YIN);
+    assert_string_equal(exts->child->stmt, "value1");
+    assert_string_equal(exts->child->arg, "test");
+    assert_null(exts->child->child);
+    assert_true(exts->child->flags & LYS_YIN_ATTR);
+    assert_string_equal(exts->child->next->stmt, "value");
+    assert_string_equal(exts->child->next->arg, "test2");
+    assert_null(exts->child->next->child);
+    assert_true(exts->child->next->flags & LYS_YIN_ATTR);
+
+    assert_string_equal(exts->child->next->next->stmt, "subelem");
+    assert_string_equal(exts->child->next->next->arg, "text");
+    assert_null(exts->child->next->next->child);
+    assert_null(exts->child->next->next->next);
+    assert_false(exts->child->next->next->flags & LYS_YIN_ATTR);
+    assert_int_equal(st->yin_ctx->xml_ctx.status, LYXML_END);
+    LY_ARRAY_FREE(args);
+    lysp_ext_instance_free(st->ctx, exts);
+    LY_ARRAY_FREE(exts);
+    exts = NULL;
+    args = NULL;
+    st = reset_state(state);
+
+    data = "<extension-elem />";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix, &prefix_len, &name, &name_len);
+    yin_load_attributes(st->yin_ctx, &data, &args);
+    ret = yin_parse_extension_instance(st->yin_ctx, args, &data, name, name_len, LYEXT_SUBSTMT_CONTACT, 0, &exts);
+    assert_int_equal(ret, LY_SUCCESS);
+    assert_string_equal(exts->name, "extension-elem");
+    assert_null(exts->argument);
+    assert_null(exts->child);
+    assert_int_equal(exts->insubstmt, LYEXT_SUBSTMT_CONTACT);
+    assert_int_equal(exts->insubstmt_index, 0);
+    assert_true(exts->yin & LYS_YIN);
+    assert_int_equal(st->yin_ctx->xml_ctx.status, LYXML_END);
+    LY_ARRAY_FREE(args);
+    lysp_ext_instance_free(st->ctx, exts);
+    LY_ARRAY_FREE(exts);
+    st->finished_correctly = true;
+}
+
+static void
+test_yin_parse_content(void **state)
+{
+    struct state *st = *state;
+    LY_ERR ret = LY_SUCCESS;
+    struct sized_string name, prefix;
+    const char *data = "<prefix value=\"a_mod\" xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">"
+                            "<custom xmlns=\"my-ext\">"
+                                "totally amazing extension"
+                            "</custom>"
+                            "<extension name=\"ext\">"
+                                "<argument name=\"argname\"></argument>"
+                                "<description><text>desc</text></description>"
+                                "<reference><text>ref</text></reference>"
+                                "<status value=\"deprecated\"></status>"
+                            "</extension>"
+                            "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>"
+                            "<if-feature value=\"foo\"></if-feature>"
+                            "<when condition=\"condition...\">"
+                                "<reference><text>when_ref</text></reference>"
+                                "<description><text>when_desc</text></description>"
+                            "</when>"
+                            "<config value=\"true\"/>"
+                            "<error-message>"
+                                "<value>error-msg</value>"
+                            "</error-message>"
+                            "<error-app-tag value=\"err-app-tag\"/>"
+                            "<units name=\"radians\"></units>"
+                            "<default value=\"default-value\"/>"
+                            "<position value=\"25\"></position>"
+                            "<value value=\"-5\"/>"
+                            "<require-instance value=\"true\"></require-instance>"
+                            "<range value=\"5..10\" />"
+                            "<length value=\"baf\"/>"
+                            "<pattern value='pattern'>"
+                                "<modifier value='invert-match'/>"
+                            "</pattern>"
+                        "</prefix>";
+    struct lysp_ext_instance *exts = NULL;
+    const char **if_features = NULL;
+    struct yin_arg_record *attrs = NULL;
+    const char *value, *err_msg, *app_tag, *units, *def;
+    struct lysp_ext *ext_def = NULL;
+    struct lysp_when *when_p = NULL;
+    struct lysp_type_enum pos_enum = {}, val_enum = {};
+    struct lysp_type req_type = {}, range_type = {}, len_type = {}, patter_type = {};
+    uint8_t config = 0;
+
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len);
+    yin_load_attributes(st->yin_ctx, &data, &attrs);
+
+    struct yin_subelement subelems[16] = {
+                                            {YANG_CONFIG, &config, 0},
+                                            {YANG_DEFAULT, &def, 0},
+                                            {YANG_ERROR_APP_TAG, &app_tag, 0},
+                                            {YANG_ERROR_MESSAGE, &err_msg, 0},
+                                            {YANG_EXTENSION, &ext_def, 0},
+                                            {YANG_IF_FEATURE, &if_features, 0},
+                                            {YANG_LENGTH, &len_type, 0},
+                                            {YANG_PATTERN, &patter_type, 0},
+                                            {YANG_POSITION, &pos_enum, 0},
+                                            {YANG_RANGE, &range_type, 0},
+                                            {YANG_REQUIRE_INSTANCE, &req_type, 0},
+                                            {YANG_UNITS, &units, 0},
+                                            {YANG_VALUE, &val_enum, 0},
+                                            {YANG_WHEN, &when_p, 0},
+                                            {YANG_CUSTOM, NULL, 0},
+                                            {YIN_TEXT, &value, 0}
+                                         };
+    ret = yin_parse_content(st->yin_ctx, subelems, 16, &data, YANG_PREFIX, NULL, &exts);
+    assert_int_equal(ret, LY_SUCCESS);
+    assert_int_equal(st->yin_ctx->xml_ctx.status, LYXML_END);
+    /* check parsed values */
+    assert_string_equal(def, "default-value");
+    assert_string_equal(exts->name, "custom");
+    assert_string_equal(exts->argument, "totally amazing extension");
+    assert_string_equal(value, "wsefsdf");
+    assert_string_equal(units, "radians");
+    assert_string_equal(when_p->cond, "condition...");
+    assert_string_equal(when_p->dsc, "when_desc");
+    assert_string_equal(when_p->ref, "when_ref");
+    assert_int_equal(config, LYS_CONFIG_W);
+    assert_int_equal(pos_enum.value, 25);
+    assert_true(pos_enum.flags | LYS_SET_VALUE);
+    assert_int_equal(val_enum.value, -5);
+    assert_true(val_enum.flags | LYS_SET_VALUE);
+    assert_int_equal(req_type.require_instance, 1);
+    assert_true(req_type.flags |= LYS_SET_REQINST);
+    assert_string_equal(range_type.range->arg, "5..10");
+    assert_true(range_type.flags | LYS_SET_RANGE);
+    assert_string_equal(err_msg, "error-msg");
+    assert_string_equal(app_tag, "err-app-tag");
+    assert_string_equal(len_type.length->arg, "baf");
+    assert_true(len_type.flags | LYS_SET_LENGTH);
+    assert_string_equal(patter_type.patterns->arg, "\x015pattern");
+    assert_true(patter_type.flags | LYS_SET_PATTERN);
+    /* cleanup */
+    lysp_ext_instance_free(st->ctx, exts);
+    lysp_when_free(st->ctx, when_p);
+    lysp_ext_free(st->ctx, ext_def);
+    FREE_STRING(st->ctx, *if_features);
+    FREE_STRING(st->ctx, err_msg);
+    FREE_STRING(st->ctx, app_tag);
+    FREE_STRING(st->ctx, units);
+    FREE_STRING(st->ctx, patter_type.patterns->arg);
+    FREE_STRING(st->ctx, def);
+    FREE_STRING(st->ctx, range_type.range->arg);
+    FREE_STRING(st->ctx, len_type.length->arg);
+    FREE_STRING(st->ctx, value);
+    LY_ARRAY_FREE(if_features);
+    LY_ARRAY_FREE(exts);
+    LY_ARRAY_FREE(ext_def);
+    LY_ARRAY_FREE(attrs);
+    LY_ARRAY_FREE(patter_type.patterns);
+    free(when_p);
+    free(range_type.range);
+    free(len_type.length);
+    attrs = NULL;
+    st = reset_state(state);
+
+    /* test unique subelem */
+    const char *prefix_value;
+    struct yin_subelement subelems2[2] = {{YANG_PREFIX, &prefix_value, 0},
+                                         {YIN_TEXT, &value, YIN_SUBELEM_UNIQUE}};
+    data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">"
+                "<prefix value=\"inv_mod\" />"
+                "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>"
+                "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>"
+           "</module>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len);
+    yin_load_attributes(st->yin_ctx, &data, &attrs);
+    ret = yin_parse_content(st->yin_ctx, subelems2, 2, &data, YANG_MODULE, NULL, &exts);
+    assert_int_equal(ret, LY_EVALID);
+    logbuf_assert("Redefinition of text element in module element. Line number 1.");
+    lydict_remove(st->ctx, prefix_value);
+    lydict_remove(st->ctx, value);
+    st = reset_state(state);
+    LY_ARRAY_FREE(attrs);
+    attrs = NULL;
+
+    /* test first subelem */
+    data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">"
+                "<prefix value=\"inv_mod\" />"
+                "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>"
+                "<text xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">wsefsdf</text>"
+           "</module>";
+    struct yin_subelement subelems3[2] = {{YANG_PREFIX, &prefix_value, 0},
+                                         {YIN_TEXT, &value, YIN_SUBELEM_FIRST}};
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len);
+    yin_load_attributes(st->yin_ctx, &data, &attrs);
+    ret = yin_parse_content(st->yin_ctx, subelems3, 2, &data, YANG_MODULE, NULL, &exts);
+    assert_int_equal(ret, LY_EVALID);
+    logbuf_assert("Subelement text of module element must be defined as first subelement. Line number 1.");
+    lydict_remove(st->ctx, prefix_value);
+    st = reset_state(state);
+    LY_ARRAY_FREE(attrs);
+    attrs = NULL;
+
+    /* test mandatory subelem */
+    data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">"
+           "</module>";
+    struct yin_subelement subelems4[1] = {{YANG_PREFIX, &prefix_value, YIN_SUBELEM_MANDATORY}};
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len);
+    yin_load_attributes(st->yin_ctx, &data, &attrs);
+    ret = yin_parse_content(st->yin_ctx, subelems4, 1, &data, YANG_MODULE, NULL, &exts);
+    assert_int_equal(ret, LY_EVALID);
+    logbuf_assert("Missing mandatory subelement prefix of module element. Line number 1.");
+    LY_ARRAY_FREE(attrs);
+
+    st->finished_correctly = true;
+}
+
+static void
+test_yin_parse_yangversion(void **state)
+{
+    struct state *st = *state;
+    LY_ERR ret = LY_SUCCESS;
+    struct sized_string name, prefix;
+    struct yin_arg_record *attrs = NULL;
+    uint8_t version;
+
+    const char *data = "<yang-version xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" value=\"1.0\">\n"
+                       "</yang-version>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len);
+    yin_load_attributes(st->yin_ctx, &data, &attrs);
+    ret = yin_parse_yangversion(st->yin_ctx, attrs, &data, &version, NULL);
+    assert_int_equal(LY_SUCCESS, ret);
+    assert_int_equal(st->yin_ctx->xml_ctx.status, LYXML_END);
+    assert_true(version == LYS_VERSION_1_0);
+    assert_true(st->yin_ctx->mod_version == LYS_VERSION_1_0);
+    LY_ARRAY_FREE(attrs);
+    attrs = NULL;
+    st = reset_state(state);
+
+    data = "<yang-version xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" value=\"1.1\">\n"
+           "</yang-version>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len);
+    yin_load_attributes(st->yin_ctx, &data, &attrs);
+    ret = yin_parse_yangversion(st->yin_ctx, attrs, &data, &version, NULL);
+    assert_int_equal(LY_SUCCESS, ret);
+    assert_int_equal(st->yin_ctx->xml_ctx.status, LYXML_END);
+    assert_true(version == LYS_VERSION_1_1);
+    assert_true(st->yin_ctx->mod_version == LYS_VERSION_1_1);
+    LY_ARRAY_FREE(attrs);
+    attrs = NULL;
+    st = reset_state(state);
+
+    data = "<yang-version xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" value=\"randomvalue\">\n"
+           "</yang-version>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len);
+    yin_load_attributes(st->yin_ctx, &data, &attrs);
+    ret = yin_parse_yangversion(st->yin_ctx, attrs, &data, &version, NULL);
+    assert_int_equal(ret, LY_EVALID);
+    logbuf_assert("Invalid value \"randomvalue\" of \"yang-version\". Line number 1.");
+    LY_ARRAY_FREE(attrs);
+    attrs = NULL;
+    st = reset_state(state);
+
+    data = "<yang-version xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n"
+           "</yang-version>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len);
+    yin_load_attributes(st->yin_ctx, &data, &attrs);
+    ret = yin_parse_yangversion(st->yin_ctx, attrs, &data, &version, NULL);
+    assert_int_equal(ret, LY_EVALID);
+    LY_ARRAY_FREE(attrs);
+    attrs = NULL;
+    logbuf_assert("Missing mandatory attribute value of yang-version element. Line number 1.");
+    st->finished_correctly = true;
+}
+
+static void
+test_yin_parse_mandatory(void **state)
+{
+    struct state *st = *state;
+    LY_ERR ret = LY_SUCCESS;
+    struct sized_string name, prefix;
+    struct yin_arg_record *attrs = NULL;
+    uint16_t man = 0;
+
+    const char *data = "<mandatory xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" value=\"true\">\n"
+                       "</mandatory>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len);
+    yin_load_attributes(st->yin_ctx, &data, &attrs);
+    ret = yin_parse_mandatory(st->yin_ctx, attrs, &data, &man, NULL);
+    assert_int_equal(LY_SUCCESS, ret);
+    assert_int_equal(st->yin_ctx->xml_ctx.status, LYXML_END);
+    assert_true(man == LYS_MAND_TRUE);
+    LY_ARRAY_FREE(attrs);
+    attrs = NULL;
+    man = 0;
+    st = reset_state(state);
+
+    data = "<mandatory xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" value=\"false\" />";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len);
+    yin_load_attributes(st->yin_ctx, &data, &attrs);
+    ret = yin_parse_mandatory(st->yin_ctx, attrs, &data, &man, NULL);
+    assert_int_equal(LY_SUCCESS, ret);
+    assert_int_equal(st->yin_ctx->xml_ctx.status, LYXML_END);
+    assert_true(man == LYS_MAND_FALSE);
+    LY_ARRAY_FREE(attrs);
+    attrs = NULL;
+    man = 0;
+    st = reset_state(state);
+
+    data = "<mandatory xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" value=\"randomvalue\">\n"
+           "</mandatory>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len);
+    yin_load_attributes(st->yin_ctx, &data, &attrs);
+    ret = yin_parse_mandatory(st->yin_ctx, attrs, &data, &man, NULL);
+    assert_int_equal(ret, LY_EVALID);
+    LY_ARRAY_FREE(attrs);
+    logbuf_assert("Invalid value \"randomvalue\" of \"mandatory\". Line number 1.");
+    attrs = NULL;
+    man = 0;
+    st = reset_state(state);
+
+    data = "<mandatory xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">\n"
+           "</mandatory>";
+    lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len);
+    yin_load_attributes(st->yin_ctx, &data, &attrs);
+    ret = yin_parse_mandatory(st->yin_ctx, attrs, &data, &man, NULL);
+    assert_int_equal(ret, LY_EVALID);
+    LY_ARRAY_FREE(attrs);
+    logbuf_assert("Missing mandatory attribute value of mandatory element. Line number 1.");
+    st->finished_correctly = true;
+}
+
+static void
+test_validate_value(void **state)
+{
+    struct state *st = *state;
+    assert_int_equal(yin_validate_value(st->yin_ctx, Y_IDENTIF_ARG, "#invalid", 8), LY_EVALID);
+    logbuf_assert("Invalid identifier character '#'. Line number 1.");
+    assert_int_equal(yin_validate_value(st->yin_ctx, Y_STR_ARG, "", 0), LY_SUCCESS);
+    assert_int_equal(yin_validate_value(st->yin_ctx, Y_IDENTIF_ARG, "pre:b", 5), LY_EVALID);
+    assert_int_equal(yin_validate_value(st->yin_ctx, Y_PREF_IDENTIF_ARG, "pre:b", 5), LY_SUCCESS);
+    assert_int_equal(yin_validate_value(st->yin_ctx, Y_PREF_IDENTIF_ARG, "pre:pre:b", 9), LY_EVALID);
+
+    st->finished_correctly = true;
+}
+
+int
+main(void)
+{
+
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_yin_parse_module, setup_f, teardown_f),
+        cmocka_unit_test_setup_teardown(test_meta, setup_f, teardown_f),
+        cmocka_unit_test_setup_teardown(test_yin_parse_import, setup_f, teardown_f),
+        cmocka_unit_test_setup_teardown(test_yin_parse_status, setup_f, teardown_f),
+        cmocka_unit_test_setup_teardown(test_yin_match_keyword, setup_f, teardown_f),
+        cmocka_unit_test_setup_teardown(test_yin_parse_extension, setup_f, teardown_f),
+        cmocka_unit_test_setup_teardown(test_yin_parse_yin_element_element, setup_f, teardown_f),
+        cmocka_unit_test_setup_teardown(test_yin_parse_element_generic, setup_f, teardown_f),
+        cmocka_unit_test_setup_teardown(test_yin_parse_extension_instance, setup_f, teardown_f),
+        cmocka_unit_test_setup_teardown(test_yin_parse_content, setup_f, teardown_f),
+        cmocka_unit_test_setup_teardown(test_yin_parse_yangversion, setup_f, teardown_f),
+        cmocka_unit_test_setup_teardown(test_yin_parse_mandatory, setup_f, teardown_f),
+        cmocka_unit_test_setup_teardown(test_validate_value, setup_f, teardown_f),
+        cmocka_unit_test(test_yin_match_argument_name),
+    };
+
+    return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index bd00a7e..e5e28ff 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -1478,7 +1478,7 @@
     assert_int_equal(8, ((struct lysc_type_bits*)type)->bits[4].position);
 
     assert_non_null(mod = lys_parse_mem(ctx, "module b {yang-version 1.1;namespace urn:b;prefix b;feature f; typedef mytype {type bits {"
-                                        "bit automin; bit one;bit two; bit seven {value 7;}bit eight;}} leaf l { type mytype {bit eight;bit seven;bit automin;}}}",
+                                        "bit automin; bit one;bit two; bit seven {position 7;}bit eight;}} leaf l { type mytype {bit eight;bit seven;bit automin;}}}",
                                         LYS_IN_YANG));
     type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
     assert_non_null(type);
@@ -1501,8 +1501,8 @@
                                    "bit one {position -1;}}}}", LYS_IN_YANG));
     logbuf_assert("Invalid value \"-1\" of \"position\". Line number 1.");
     assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type bits {"
-                                   "bit one {value 4294967296;}}}}", LYS_IN_YANG));
-    logbuf_assert("Invalid value \"4294967296\" of \"value\". Line number 1.");
+                                   "bit one {position 4294967296;}}}}", LYS_IN_YANG));
+    logbuf_assert("Invalid value \"4294967296\" of \"position\". Line number 1.");
     assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type bits {"
                                    "bit one; bit one;}}}", LYS_IN_YANG));
     logbuf_assert("Duplicate identifier \"one\" of bit statement. Line number 1.");
@@ -1526,7 +1526,7 @@
     assert_null(lys_parse_mem(ctx, "module ee {namespace urn:ee;prefix ee;leaf l {type bits {bit x {position 4294967295;}bit y;}}}", LYS_IN_YANG));
     logbuf_assert("Invalid bits - it is not possible to auto-assign bit position for \"y\" since the highest value is already 4294967295. /ee:l");
 
-    assert_null(lys_parse_mem(ctx, "module ff {namespace urn:ff;prefix ff;leaf l {type bits {bit x {value 1;}bit y {value 1;}}}}", LYS_IN_YANG));
+    assert_null(lys_parse_mem(ctx, "module ff {namespace urn:ff;prefix ff;leaf l {type bits {bit x {position 1;}bit y {position 1;}}}}", LYS_IN_YANG));
     logbuf_assert("Invalid bits - position 1 collide in items \"y\" and \"x\". /ff:l");
 
     assert_null(lys_parse_mem(ctx, "module gg {namespace urn:gg;prefix gg;typedef mytype {type bits;}"
diff --git a/tests/src/test_xml.c b/tests/src/test_xml.c
index 1622b92..ebca0da 100644
--- a/tests/src/test_xml.c
+++ b/tests/src/test_xml.c
@@ -561,6 +561,7 @@
         cmocka_unit_test_setup(test_attribute, logger_setup),
         cmocka_unit_test_setup(test_text, logger_setup),
         cmocka_unit_test_setup(test_ns, logger_setup),
+        cmocka_unit_test(test_simple_xml),
         cmocka_unit_test_setup(test_ns2, logger_setup),
         cmocka_unit_test_setup(test_simple_xml, logger_setup),
     };