yin parser CHANGE check relative order of module and submodule subelements
diff --git a/src/common.h b/src/common.h
index 7b81b1c..c51d862 100644
--- a/src/common.h
+++ b/src/common.h
@@ -200,6 +200,7 @@
 #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_INDEV_YIN   LYVE_SYNTAX_YIN, "Deviate of this type doesn't allow \"%s\" as it's sub-element."
+#define LY_VCODE_INORDER_YIN LYVE_SYNTAX_YIN, "Invalid order of %s\'s subelements \"%s\" can't appear after \"%s\"."
 
 #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\'."
@@ -620,6 +621,4 @@
     LY_CHECK_ERR_RET(!(NEW_ITEM), LOGMEM(CTX), LY_EMEM); \
     LY_LIST_INSERT(LIST, NEW_ITEM, LINKER)
 
-#define LY_LIST_NEW_GOTO(CTX, LIST, NEW_ITEM, LINKER, GOTO) \
-
-#endif /* LY_COMMON_H_ */
\ No newline at end of file
+#endif /* LY_COMMON_H_ */
diff --git a/src/parser_yin.c b/src/parser_yin.c
index 30c4e84..647a6f5 100644
--- a/src/parser_yin.c
+++ b/src/parser_yin.c
@@ -85,8 +85,6 @@
     } 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;
         }
@@ -2522,6 +2520,80 @@
     }
 }
 
+static LY_ERR
+kw2kw_group(struct yin_parser_ctx *ctx, enum yang_keyword kw, enum yang_module_stmt *group)
+{
+    switch (kw) {
+        /* module header */
+        case YANG_NONE:
+        case YANG_NAMESPACE:
+        case YANG_PREFIX:
+        case YANG_BELONGS_TO:
+        case YANG_YANG_VERSION:
+            *group = Y_MOD_MODULE_HEADER;
+            break;
+        /* linkage */
+        case YANG_INCLUDE:
+        case YANG_IMPORT:
+            *group = Y_MOD_LINKAGE;
+            break;
+        /* meta */
+        case YANG_ORGANIZATION:
+        case YANG_CONTACT:
+        case YANG_DESCRIPTION:
+        case YANG_REFERENCE:
+            *group = Y_MOD_META;
+            break;
+        /* revision */
+        case YANG_REVISION:
+            *group = Y_MOD_REVISION;
+            break;
+        /* body */
+        case YANG_ANYDATA:
+        case YANG_ANYXML:
+        case YANG_AUGMENT:
+        case YANG_CHOICE:
+        case YANG_CONTAINER:
+        case YANG_DEVIATION:
+        case YANG_EXTENSION:
+        case YANG_FEATURE:
+        case YANG_GROUPING:
+        case YANG_IDENTITY:
+        case YANG_LEAF:
+        case YANG_LEAF_LIST:
+        case YANG_LIST:
+        case YANG_NOTIFICATION:
+        case YANG_RPC:
+        case YANG_TYPEDEF:
+        case YANG_USES:
+        case YANG_CUSTOM:
+            *group = Y_MOD_BODY;
+            break;
+        default:
+            LOGINT(ctx->xml_ctx.ctx);
+            return LY_EINT;
+    }
+
+    return LY_SUCCESS;
+}
+
+static LY_ERR
+yin_check_relative_order(struct yin_parser_ctx *ctx, enum yang_keyword kw, enum yang_keyword next_kw, enum yang_keyword parrent)
+{
+    assert(parrent == YANG_MODULE || parrent == YANG_SUBMODULE);
+    enum yang_module_stmt gr, next_gr;
+
+    LY_CHECK_RET(kw2kw_group(ctx, kw, &gr));
+    LY_CHECK_RET(kw2kw_group(ctx, next_kw, &next_gr));
+
+    if (gr > next_gr) {
+        LOGVAL_PARSER((struct lys_parser_ctx *)ctx, LY_VCODE_INORDER_YIN, ly_stmt2str(parrent), ly_stmt2str(next_kw), ly_stmt2str(kw));
+        return LY_EVALID;
+    }
+
+    return LY_SUCCESS;
+}
+
 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)
@@ -2532,7 +2604,7 @@
     size_t out_len = 0;
     int dynamic = 0;
     struct yin_arg_record *attrs = NULL;
-    enum yang_keyword kw = YANG_NONE;
+    enum yang_keyword kw = YANG_NONE, last_kw = YANG_NONE;
     struct yin_subelement *subelem = NULL;
     struct lysp_type *type, *nested_type;
 
@@ -2551,6 +2623,7 @@
                 }
                 ret = yin_load_attributes(ctx, data, &attrs);
                 LY_CHECK_GOTO(ret, cleanup);
+                last_kw = kw;
                 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 */
@@ -2565,7 +2638,10 @@
                     goto cleanup;
                 }
 
-                /* TODO check relative order */
+                if (current_element == YANG_MODULE || current_element == YANG_SUBMODULE) {
+                    ret = yin_check_relative_order(ctx, last_kw, kw, current_element);
+                    LY_CHECK_GOTO(ret, cleanup);
+                }
 
                 /* flag check */
                 if ((subelem->flags & YIN_SUBELEM_UNIQUE) && (subelem->flags & YIN_SUBELEM_PARSED)) {
diff --git a/tests/src/test_parser_yin.c b/tests/src/test_parser_yin.c
index 3674383..355022a 100644
--- a/tests/src/test_parser_yin.c
+++ b/tests/src/test_parser_yin.c
@@ -49,6 +49,10 @@
 void lysp_submodule_free(struct ly_ctx *ctx, struct lysp_submodule *submod);
 void lysp_import_free(struct ly_ctx *ctx, struct lysp_import *import);
 
+/* wrapping element used for mocking has nothing to do with real module structure */
+#define ELEMENT_WRAPPER_START "<status xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">"
+#define ELEMENT_WRAPPER_END "</status>"
+
 struct state {
     struct ly_ctx *ctx;
     struct lys_module *mod;
@@ -580,16 +584,16 @@
     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\">"
+    data = ELEMENT_WRAPPER_START
                 "<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>";
+           ELEMENT_WRAPPER_END;
     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);
+    ret = yin_parse_content(st->yin_ctx, subelems2, 2, &data, YANG_STATUS, NULL, &exts);
     assert_int_equal(ret, LY_EVALID);
-    logbuf_assert("Redefinition of text element in module element. Line number 1.");
+    logbuf_assert("Redefinition of text element in status element. Line number 1.");
     lydict_remove(st->ctx, prefix_value);
     lydict_remove(st->ctx, value);
     st = reset_state(state);
@@ -597,32 +601,31 @@
     attrs = NULL;
 
     /* test first subelem */
-    data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">"
+    data = ELEMENT_WRAPPER_START
                 "<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>";
+           ELEMENT_WRAPPER_END;
     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);
+    ret = yin_parse_content(st->yin_ctx, subelems3, 2, &data, YANG_STATUS, NULL, &exts);
     assert_int_equal(ret, LY_EVALID);
-    logbuf_assert("Subelement text of module element must be defined as first subelement. Line number 1.");
+    logbuf_assert("Subelement text of status 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>";
+    data = ELEMENT_WRAPPER_START ELEMENT_WRAPPER_END;
     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);
+    ret = yin_parse_content(st->yin_ctx, subelems4, 1, &data, YANG_STATUS, NULL, &exts);
     assert_int_equal(ret, LY_EVALID);
-    logbuf_assert("Missing mandatory subelement prefix of module element. Line number 1.");
+    logbuf_assert("Missing mandatory subelement prefix of status element. Line number 1.");
     LY_ARRAY_FREE(attrs);
 
     st->finished_correctly = true;
@@ -642,9 +645,6 @@
     st->finished_correctly = true;
 }
 
-#define ELEMENT_WRAPPER_START "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\">"
-#define ELEMENT_WRAPPER_END "</module>"
-
 /* helper function to simplify unit test of each element using parse_content function */
 LY_ERR
 test_element_helper(struct state *st, const char **data, void *dest, const char **text,
@@ -3940,6 +3940,27 @@
     FREE_ARRAY(st->yin_ctx, attrs, free_arg_rec);
     attrs = NULL;
 
+    /* incorrect subelem order */
+    st->yin_ctx->xml_ctx.status = LYXML_ELEMENT;
+    lys_mod = calloc(1, sizeof *lys_mod);
+    lysp_mod = calloc(1, sizeof *lysp_mod);
+    lys_mod->ctx = st->ctx;
+    lysp_mod->mod = lys_mod;
+    data = "<module xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"mod\">"
+                "<feature name=\"feature\"/>\n"
+                "<namespace uri=\"ns\"/>"
+                "<prefix value=\"pref\"/>"
+                "<yang-version value=\"1.1\"/>"
+           "</module>";
+    assert_int_equal(lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len), LY_SUCCESS);
+    assert_int_equal(yin_load_attributes(st->yin_ctx, &data, &attrs), LY_SUCCESS);
+    assert_int_equal(yin_parse_mod(st->yin_ctx, attrs, &data, lysp_mod), LY_EVALID);
+    logbuf_assert("Invalid order of module\'s subelements \"namespace\" can\'t appear after \"feature\". Line number 30.");
+    lysp_module_free(lysp_mod);
+    lys_module_free(lys_mod, NULL);
+    FREE_ARRAY(st->yin_ctx, attrs, free_arg_rec);
+    attrs = NULL;
+
     st->finished_correctly = true;
 }
 
@@ -4050,6 +4071,22 @@
     FREE_ARRAY(st->yin_ctx, attrs, free_arg_rec);
     attrs = NULL;
 
+    /* incorrect subelem order */
+    st->yin_ctx->xml_ctx.status = LYXML_ELEMENT;
+    lysp_submod = calloc(1, sizeof *lysp_submod);
+    data = "<submodule xmlns=\"urn:ietf:params:xml:ns:yang:yin:1\" name=\"submod\">"
+                "<yang-version value=\"1.0\"/>"
+                "<reference><text>ref</text></reference>\n"
+                "<belongs-to module=\"mod-name\"><prefix value=\"pref\"/></belongs-to>"
+           "</submodule>";
+    assert_int_equal(lyxml_get_element(&st->yin_ctx->xml_ctx, &data, &prefix.value, &prefix.len, &name.value, &name.len), LY_SUCCESS);
+    assert_int_equal(yin_load_attributes(st->yin_ctx, &data, &attrs), LY_SUCCESS);
+    assert_int_equal(yin_parse_submod(st->yin_ctx, attrs, &data, lysp_submod), LY_EVALID);
+    logbuf_assert("Invalid order of submodule's subelements \"belongs-to\" can't appear after \"reference\". Line number 28.");
+    lysp_submodule_free(st->ctx, lysp_submod);
+    FREE_ARRAY(st->yin_ctx, attrs, free_arg_rec);
+    attrs = NULL;
+
     st->finished_correctly = true;
 }