xpath FEATURE XPath variable implementation added
diff --git a/tests/utests/basic/test_xpath.c b/tests/utests/basic/test_xpath.c
index 7be4ccf..74e310f 100644
--- a/tests/utests/basic/test_xpath.c
+++ b/tests/utests/basic/test_xpath.c
@@ -487,6 +487,261 @@
     lyd_free_all(tree);
 }
 
+static void
+test_variables(void **state)
+{
+    struct lyd_node *tree, *node;
+    struct ly_set *set;
+    const char *data;
+    struct lyxp_var *vars = NULL;
+
+#define LOCAL_SETUP(DATA, TREE) \
+    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, DATA, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &TREE)); \
+    assert_non_null(TREE);
+
+#define SET_NODE(NODE, SET, INDEX) \
+    assert_non_null(SET); \
+    assert_true(INDEX < SET->count); \
+    NODE = SET->objs[INDEX];
+
+#define LOCAL_TEARDOWN(SET, TREE, VARS) \
+    ly_set_free(SET, NULL); \
+    lyd_free_all(TREE); \
+    lyxp_vars_free(VARS); \
+    vars = NULL;
+
+    /* Eval variable to number. */
+    data =
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a1</a>\n"
+            "    <b>b1</b>\n"
+            "    <c>c1</c>\n"
+            "</l1>"
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a2</a>\n"
+            "    <b>b2</b>\n"
+            "    <c>c2</c>\n"
+            "</l1>";
+    LOCAL_SETUP(data, tree);
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "2"));
+    assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set));
+    SET_NODE(node, set, 0);
+    assert_string_equal(lyd_get_value(node), "a2");
+    LOCAL_TEARDOWN(set, tree, vars);
+
+    /* Eval variable to string. */
+    data =
+            "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+    LOCAL_SETUP(data, tree);
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\"mstr\""));
+    assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo[text() = $var]", vars, &set));
+    SET_NODE(node, set, 0);
+    assert_string_equal(lyd_get_value(node), "mstr");
+    LOCAL_TEARDOWN(set, tree, vars);
+
+    /* Eval variable to set of nodes. */
+    data =
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a1</a>\n"
+            "    <b>b1</b>\n"
+            "</l1>"
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a2</a>\n"
+            "    <b>b2</b>\n"
+            "    <c>c2</c>\n"
+            "</l1>";
+    LOCAL_SETUP(data, tree);
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "c"));
+    assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set));
+    SET_NODE(node, set, 0);
+    assert_string_equal(lyd_get_value(node), "a2");
+    LOCAL_TEARDOWN(set, tree, vars);
+
+    /* Variable in union expr. */
+    data =
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a1</a>\n"
+            "    <b>b1</b>\n"
+            "    <c>c1</c>\n"
+            "</l1>"
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a2</a>\n"
+            "    <b>b2</b>\n"
+            "    <c>c2</c>\n"
+            "</l1>"
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a3</a>\n"
+            "    <b>b3</b>\n"
+            "    <c>c3</c>\n"
+            "</l1>";
+    LOCAL_SETUP(data, tree);
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "c[../a = 'a3']"));
+    assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[c[../a = 'a1'] | $var]/a", vars, &set));
+    SET_NODE(node, set, 0);
+    assert_string_equal(lyd_get_value(node), "a1");
+    SET_NODE(node, set, 1);
+    assert_string_equal(lyd_get_value(node), "a3");
+    assert_int_equal(set->count, 2);
+    LOCAL_TEARDOWN(set, tree, vars);
+
+    /* Predicate after variable. */
+    data =
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a1</a>\n"
+            "    <b>b1</b>\n"
+            "    <c>c1</c>\n"
+            "</l1>"
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a2</a>\n"
+            "    <b>b2</b>\n"
+            "    <c>c2</c>\n"
+            "</l1>";
+    LOCAL_SETUP(data, tree);
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "c"));
+    assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var[../a = 'a1']]/a", vars, &set));
+    SET_NODE(node, set, 0);
+    assert_string_equal(lyd_get_value(node), "a1");
+    LOCAL_TEARDOWN(set, tree, vars);
+
+    /* Variable in variable. */
+    data =
+            "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+    LOCAL_SETUP(data, tree);
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "$var2"));
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "\"mstr\""));
+    assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo[text() = $var]", vars, &set));
+    SET_NODE(node, set, 0);
+    assert_string_equal(lyd_get_value(node), "mstr");
+    LOCAL_TEARDOWN(set, tree, vars);
+
+    /* Compare two variables. */
+    data =
+            "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+    LOCAL_SETUP(data, tree);
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "\"str\""));
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var2", "\"str\""));
+    assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo[$var1 = $var2]", vars, &set));
+    SET_NODE(node, set, 0);
+    assert_string_equal(lyd_get_value(node), "mstr");
+    LOCAL_TEARDOWN(set, tree, vars);
+
+    /* Arithmetic operation with variable. */
+    data =
+            "<foo2 xmlns=\"urn:tests:a\">4</foo2>";
+    LOCAL_SETUP(data, tree);
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "2"));
+    assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/foo2[.= ($var1 * 2)]", vars, &set));
+    SET_NODE(node, set, 0);
+    assert_string_equal(lyd_get_value(node), "4");
+    LOCAL_TEARDOWN(set, tree, vars);
+
+    /* Variable as function parameter. */
+    data =
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a1</a>\n"
+            "    <b>b1</b>\n"
+            "    <c>c1</c>\n"
+            "</l1>"
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a2</a>\n"
+            "    <b>b2</b>\n"
+            "</l1>";
+    LOCAL_SETUP(data, tree);
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "./c"));
+    assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[count($var) = 1]/a", vars, &set));
+    SET_NODE(node, set, 0);
+    assert_string_equal(lyd_get_value(node), "a1");
+    LOCAL_TEARDOWN(set, tree, vars);
+
+    /* Variable in path expr. */
+    /* NOTE: The variable can only be at the beginning of the expression path. */
+    data =
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a1</a>\n"
+            "    <b>b1</b>\n"
+            "    <c>c1</c>\n"
+            "</l1>"
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a2</a>\n"
+            "    <b>b2</b>\n"
+            "</l1>";
+    LOCAL_SETUP(data, tree);
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "/l1"));
+    assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var/a]", vars, &set));
+    assert_int_equal(set->count, 2);
+    LOCAL_TEARDOWN(set, tree, vars);
+
+    /* Variable as function. */
+    data =
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a1</a>\n"
+            "    <b>b1</b>\n"
+            "    <c>c1</c>\n"
+            "</l1>"
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a2</a>\n"
+            "    <b>b2</b>\n"
+            "</l1>";
+    LOCAL_SETUP(data, tree);
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "position()"));
+    assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var = 2]/a", vars, &set));
+    SET_NODE(node, set, 0);
+    assert_string_equal(lyd_get_value(node), "a2");
+    LOCAL_TEARDOWN(set, tree, vars);
+
+    /* Dynamic change of value. */
+    data =
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a1</a>\n"
+            "    <b>b1</b>\n"
+            "    <c>c1</c>\n"
+            "</l1>"
+            "<l1 xmlns=\"urn:tests:a\">\n"
+            "    <a>a2</a>\n"
+            "    <b>b2</b>\n"
+            "    <c>c2</c>\n"
+            "</l1>";
+    LOCAL_SETUP(data, tree);
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "1"));
+    assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set));
+    SET_NODE(node, set, 0);
+    assert_string_equal(lyd_get_value(node), "a1");
+    ly_set_free(set, NULL);
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "2"));
+    assert_int_equal(LY_SUCCESS, lyd_find_xpath2(tree, "/l1[$var]/a", vars, &set));
+    SET_NODE(node, set, 0);
+    assert_string_equal(lyd_get_value(node), "a2");
+    LOCAL_TEARDOWN(set, tree, vars);
+
+    /* Variable not defined. */
+    data =
+            "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+    LOCAL_SETUP(data, tree);
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var1", "\"mstr\""));
+    assert_int_equal(LY_ENOTFOUND, lyd_find_xpath2(tree, "/foo[text() = $var55]", vars, &set));
+    LOCAL_TEARDOWN(set, tree, vars);
+
+    /* Syntax error in value. */
+    data =
+            "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+    LOCAL_SETUP(data, tree);
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\""));
+    assert_int_equal(LY_EVALID, lyd_find_xpath2(tree, "/foo[$var]", vars, &set));
+    LOCAL_TEARDOWN(set, tree, vars);
+
+    /* Prefix is not supported. */
+    data =
+            "<foo xmlns=\"urn:tests:a\">mstr</foo>";
+    LOCAL_SETUP(data, tree);
+    assert_int_equal(LY_SUCCESS, lyxp_vars_set(&vars, "var", "\""));
+    assert_int_equal(LY_EVALID, lyd_find_xpath2(tree, "/foo[$pref:var]", vars, &set));
+    assert_string_equal("Variable with prefix is not supported.", _UC->err_msg);
+    LOCAL_TEARDOWN(set, tree, vars);
+
+#undef LOCAL_SETUP
+#undef LOCAL_TEARDOWN
+}
+
 int
 main(void)
 {
@@ -500,6 +755,7 @@
         UTEST(test_canonize, setup),
         UTEST(test_derived_from, setup),
         UTEST(test_augment, setup),
+        UTEST(test_variables, setup),
     };
 
     return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tests/utests/schema/test_tree_schema_compile.c b/tests/utests/schema/test_tree_schema_compile.c
index bb8985e..b87f237 100644
--- a/tests/utests/schema/test_tree_schema_compile.c
+++ b/tests/utests/schema/test_tree_schema_compile.c
@@ -2072,10 +2072,10 @@
     assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module xx {namespace urn:xx;prefix xx;\n"
             "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"
             "leaf ifname{type leafref{ path \"../interface/name\";}}leaf test{type string;}\n"
-            "leaf address {type leafref{ path \"/interface[name = current()/../$node]/ip\";}}}",
+            "leaf address {type leafref{ path \"/interface[name = current()/../#node]/ip\";}}}",
             LYS_IN_YANG, &mod));
     CHECK_LOG_CTX("Parsing module \"xx\" failed.", NULL,
-            "Invalid character '$'[32] of expression '/interface[name = current()/../$node]/ip'.", "Line number 4.");
+            "Invalid character '#'[32] of expression '/interface[name = current()/../#node]/ip'.", "Line number 4.");
 
     assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module yy {namespace urn:yy;prefix yy;\n"
             "list interface{key name;leaf name{type string;}leaf ip {type string;}}\n"