path FEATURE new compiled path structure (#1108)

Refactoring includes using it for
instance-identifier and for checking leafref,
it is evaluated using stanrad XPath. Predicates
used for lyd_new_list2(), tests included.
diff --git a/src/xpath.c b/src/xpath.c
index 27fb0f7..f73988b 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -32,6 +32,7 @@
 #include "context.h"
 #include "dict.h"
 #include "hash_table.h"
+#include "path.h"
 #include "plugins_types.h"
 #include "printer.h"
 #include "printer_data.h"
@@ -40,8 +41,8 @@
 #include "tree_schema_internal.h"
 #include "xml.h"
 
-static LY_ERR reparse_or_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *exp_idx);
-static LY_ERR eval_expr_select(struct lyxp_expr *exp, uint16_t *exp_idx, enum lyxp_expr_type etype, struct lyxp_set *set, int options);
+static LY_ERR reparse_or_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx);
+static LY_ERR eval_expr_select(struct lyxp_expr *exp, uint16_t *tok_idx, enum lyxp_expr_type etype, struct lyxp_set *set, int options);
 
 /**
  * @brief Print the type of an XPath \p set.
@@ -250,7 +251,7 @@
 
     case LYXP_SET_BOOLEAN:
         LOGDBG(LY_LDGXPATH, "set BOOLEAN");
-        LOGDBG(LY_LDGXPATH, "\t%s", (set->val.bool ? "true" : "false"));
+        LOGDBG(LY_LDGXPATH, "\t%s", (set->val.bln ? "true" : "false"));
         break;
 
     case LYXP_SET_STRING:
@@ -866,7 +867,7 @@
     lyxp_set_free_content(set);
 
     set->type = LYXP_SET_BOOLEAN;
-    set->val.bool = boolean;
+    set->val.bln = boolean;
 }
 
 /**
@@ -899,7 +900,7 @@
         LY_CHECK_ERR_RET(!trg->val.scnodes, LOGMEM(src->ctx); memset(trg, 0, sizeof *trg), );
         memcpy(trg->val.scnodes, src->val.scnodes, src->used * sizeof *src->val.scnodes);
     } else if (src->type == LYXP_SET_BOOLEAN) {
-        set_fill_boolean(trg, src->val.bool);
+        set_fill_boolean(trg, src->val.bln);
     } else if (src->type ==  LYXP_SET_NUMBER) {
         set_fill_number(trg, src->val.num);
     } else if (src->type == LYXP_SET_STRING) {
@@ -1785,19 +1786,19 @@
  */
 
 LY_ERR
-lyxp_check_token(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint16_t exp_idx, enum lyxp_token want_tok)
+lyxp_check_token(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint16_t tok_idx, enum lyxp_token want_tok)
 {
-    if (exp->used == exp_idx) {
+    if (exp->used == tok_idx) {
         if (ctx) {
             LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOF);
         }
         return LY_EINCOMPLETE;
     }
 
-    if (want_tok && (exp->tokens[exp_idx] != want_tok)) {
+    if (want_tok && (exp->tokens[tok_idx] != want_tok)) {
         if (ctx) {
             LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INTOK,
-                   lyxp_print_token(exp->tokens[exp_idx]), &exp->expr[exp->tok_pos[exp_idx]]);
+                   lyxp_print_token(exp->tokens[tok_idx]), &exp->expr[exp->tok_pos[tok_idx]]);
         }
         return LY_ENOT;
     }
@@ -1805,22 +1806,33 @@
     return LY_SUCCESS;
 }
 
+LY_ERR
+lyxp_next_token(const struct ly_ctx *ctx, const struct lyxp_expr *exp, uint16_t *tok_idx, enum lyxp_token want_tok)
+{
+    LY_CHECK_RET(lyxp_check_token(ctx, exp, *tok_idx, want_tok));
+
+    /* skip the token */
+    ++(*tok_idx);
+
+    return LY_SUCCESS;
+}
+
 /* just like lyxp_check_token() but tests for 2 tokens */
 static LY_ERR
-exp_check_token2(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t exp_idx, enum lyxp_token want_tok1,
+exp_check_token2(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t tok_idx, enum lyxp_token want_tok1,
                  enum lyxp_token want_tok2)
 {
-    if (exp->used == exp_idx) {
+    if (exp->used == tok_idx) {
         if (ctx) {
             LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOF);
         }
         return LY_EINCOMPLETE;
     }
 
-    if ((exp->tokens[exp_idx] != want_tok1) && (exp->tokens[exp_idx] != want_tok2)) {
+    if ((exp->tokens[tok_idx] != want_tok1) && (exp->tokens[tok_idx] != want_tok2)) {
         if (ctx) {
             LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INTOK,
-                   lyxp_print_token(exp->tokens[exp_idx]), &exp->expr[exp->tok_pos[exp_idx]]);
+                   lyxp_print_token(exp->tokens[tok_idx]), &exp->expr[exp->tok_pos[tok_idx]]);
         }
         return LY_ENOT;
     }
@@ -1832,24 +1844,24 @@
  * @brief Stack operation push on the repeat array.
  *
  * @param[in] exp Expression to use.
- * @param[in] exp_idx Position in the expresion \p exp.
+ * @param[in] tok_idx Position in the expresion \p exp.
  * @param[in] repeat_op_idx Index from \p exp of the operator token. This value is pushed.
  */
 static void
-exp_repeat_push(struct lyxp_expr *exp, uint16_t exp_idx, uint16_t repeat_op_idx)
+exp_repeat_push(struct lyxp_expr *exp, uint16_t tok_idx, uint16_t repeat_op_idx)
 {
     uint16_t i;
 
-    if (exp->repeat[exp_idx]) {
-        for (i = 0; exp->repeat[exp_idx][i]; ++i);
-        exp->repeat[exp_idx] = realloc(exp->repeat[exp_idx], (i + 2) * sizeof *exp->repeat[exp_idx]);
-        LY_CHECK_ERR_RET(!exp->repeat[exp_idx], LOGMEM(NULL), );
-        exp->repeat[exp_idx][i] = repeat_op_idx;
-        exp->repeat[exp_idx][i + 1] = 0;
+    if (exp->repeat[tok_idx]) {
+        for (i = 0; exp->repeat[tok_idx][i]; ++i);
+        exp->repeat[tok_idx] = realloc(exp->repeat[tok_idx], (i + 2) * sizeof *exp->repeat[tok_idx]);
+        LY_CHECK_ERR_RET(!exp->repeat[tok_idx], LOGMEM(NULL), );
+        exp->repeat[tok_idx][i] = repeat_op_idx;
+        exp->repeat[tok_idx][i + 1] = 0;
     } else {
-        exp->repeat[exp_idx] = calloc(2, sizeof *exp->repeat[exp_idx]);
-        LY_CHECK_ERR_RET(!exp->repeat[exp_idx], LOGMEM(NULL), );
-        exp->repeat[exp_idx][0] = repeat_op_idx;
+        exp->repeat[tok_idx] = calloc(2, sizeof *exp->repeat[tok_idx]);
+        LY_CHECK_ERR_RET(!exp->repeat[tok_idx], LOGMEM(NULL), );
+        exp->repeat[tok_idx][0] = repeat_op_idx;
     }
 }
 
@@ -1860,24 +1872,24 @@
  *
  * @param[in] ctx Context for logging.
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @return LY_ERR
  */
 static LY_ERR
-reparse_predicate(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *exp_idx)
+reparse_predicate(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
 {
     LY_ERR rc;
 
-    rc = lyxp_check_token(ctx, exp, *exp_idx, LYXP_TOKEN_BRACK1);
+    rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_BRACK1);
     LY_CHECK_RET(rc);
-    ++(*exp_idx);
+    ++(*tok_idx);
 
-    rc = reparse_or_expr(ctx, exp, exp_idx);
+    rc = reparse_or_expr(ctx, exp, tok_idx);
     LY_CHECK_RET(rc);
 
-    rc = lyxp_check_token(ctx, exp, *exp_idx, LYXP_TOKEN_BRACK2);
+    rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_BRACK2);
     LY_CHECK_RET(rc);
-    ++(*exp_idx);
+    ++(*tok_idx);
 
     return LY_SUCCESS;
 }
@@ -1891,77 +1903,77 @@
  *
  * @param[in] ctx Context for logging.
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression \p exp.
+ * @param[in] tok_idx Position in the expression \p exp.
  * @return LY_ERR (LY_EINCOMPLETE on forward reference)
  */
 static LY_ERR
-reparse_relative_location_path(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *exp_idx)
+reparse_relative_location_path(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
 {
     LY_ERR rc;
 
-    rc = lyxp_check_token(ctx, exp, *exp_idx, LYXP_TOKEN_NONE);
+    rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE);
     LY_CHECK_RET(rc);
 
     goto step;
     do {
         /* '/' or '//' */
-        ++(*exp_idx);
+        ++(*tok_idx);
 
-        rc = lyxp_check_token(ctx, exp, *exp_idx, LYXP_TOKEN_NONE);
+        rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE);
         LY_CHECK_RET(rc);
 step:
         /* Step */
-        switch (exp->tokens[*exp_idx]) {
+        switch (exp->tokens[*tok_idx]) {
         case LYXP_TOKEN_DOT:
-            ++(*exp_idx);
+            ++(*tok_idx);
             break;
 
         case LYXP_TOKEN_DDOT:
-            ++(*exp_idx);
+            ++(*tok_idx);
             break;
 
         case LYXP_TOKEN_AT:
-            ++(*exp_idx);
+            ++(*tok_idx);
 
-            rc = lyxp_check_token(ctx, exp, *exp_idx, LYXP_TOKEN_NONE);
+            rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE);
             LY_CHECK_RET(rc);
-            if ((exp->tokens[*exp_idx] != LYXP_TOKEN_NAMETEST) && (exp->tokens[*exp_idx] != LYXP_TOKEN_NODETYPE)) {
+            if ((exp->tokens[*tok_idx] != LYXP_TOKEN_NAMETEST) && (exp->tokens[*tok_idx] != LYXP_TOKEN_NODETYPE)) {
                 LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INTOK,
-                       lyxp_print_token(exp->tokens[*exp_idx]), &exp->expr[exp->tok_pos[*exp_idx]]);
+                       lyxp_print_token(exp->tokens[*tok_idx]), &exp->expr[exp->tok_pos[*tok_idx]]);
                 return LY_EVALID;
             }
             /* fall through */
         case LYXP_TOKEN_NAMETEST:
-            ++(*exp_idx);
+            ++(*tok_idx);
             goto reparse_predicate;
             break;
 
         case LYXP_TOKEN_NODETYPE:
-            ++(*exp_idx);
+            ++(*tok_idx);
 
             /* '(' */
-            rc = lyxp_check_token(ctx, exp, *exp_idx, LYXP_TOKEN_PAR1);
+            rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR1);
             LY_CHECK_RET(rc);
-            ++(*exp_idx);
+            ++(*tok_idx);
 
             /* ')' */
-            rc = lyxp_check_token(ctx, exp, *exp_idx, LYXP_TOKEN_PAR2);
+            rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR2);
             LY_CHECK_RET(rc);
-            ++(*exp_idx);
+            ++(*tok_idx);
 
 reparse_predicate:
             /* Predicate* */
-            while (!lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_BRACK1)) {
-                rc = reparse_predicate(ctx, exp, exp_idx);
+            while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
+                rc = reparse_predicate(ctx, exp, tok_idx);
                 LY_CHECK_RET(rc);
             }
             break;
         default:
             LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INTOK,
-                   lyxp_print_token(exp->tokens[*exp_idx]), &exp->expr[exp->tok_pos[*exp_idx]]);
+                   lyxp_print_token(exp->tokens[*tok_idx]), &exp->expr[exp->tok_pos[*tok_idx]]);
             return LY_EVALID;
         }
-    } while (!exp_check_token2(NULL, exp, *exp_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH));
+    } while (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH));
 
     return LY_SUCCESS;
 }
@@ -1973,32 +1985,31 @@
  *
  * @param[in] ctx Context for logging.
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression \p exp.
+ * @param[in] tok_idx Position in the expression \p exp.
  * @return LY_ERR
  */
 static LY_ERR
-reparse_absolute_location_path(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *exp_idx)
+reparse_absolute_location_path(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
 {
     LY_ERR rc;
 
-    rc = exp_check_token2(ctx, exp, *exp_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH);
-    LY_CHECK_RET(rc);
+    LY_CHECK_RET(exp_check_token2(ctx, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH));
 
     /* '/' RelativeLocationPath? */
-    if (exp->tokens[*exp_idx] == LYXP_TOKEN_OPER_PATH) {
+    if (exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH) {
         /* '/' */
-        ++(*exp_idx);
+        ++(*tok_idx);
 
-        if (lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_NONE)) {
+        if (lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_NONE)) {
             return LY_SUCCESS;
         }
-        switch (exp->tokens[*exp_idx]) {
+        switch (exp->tokens[*tok_idx]) {
         case LYXP_TOKEN_DOT:
         case LYXP_TOKEN_DDOT:
         case LYXP_TOKEN_AT:
         case LYXP_TOKEN_NAMETEST:
         case LYXP_TOKEN_NODETYPE:
-            rc = reparse_relative_location_path(ctx, exp, exp_idx);
+            rc = reparse_relative_location_path(ctx, exp, tok_idx);
             LY_CHECK_RET(rc);
             /* fall through */
         default:
@@ -2008,9 +2019,9 @@
     /* '//' RelativeLocationPath */
     } else {
         /* '//' */
-        ++(*exp_idx);
+        ++(*tok_idx);
 
-        rc = reparse_relative_location_path(ctx, exp, exp_idx);
+        rc = reparse_relative_location_path(ctx, exp, tok_idx);
         LY_CHECK_RET(rc);
     }
 
@@ -2024,198 +2035,198 @@
  *
  * @param[in] ctx Context for logging.
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @return LY_ERR
  */
 static LY_ERR
-reparse_function_call(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *exp_idx)
+reparse_function_call(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
 {
     int min_arg_count = -1, max_arg_count, arg_count;
-    uint16_t func_exp_idx;
+    uint16_t func_tok_idx;
     LY_ERR rc;
 
-    rc = lyxp_check_token(ctx, exp, *exp_idx, LYXP_TOKEN_FUNCNAME);
+    rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_FUNCNAME);
     LY_CHECK_RET(rc);
-    func_exp_idx = *exp_idx;
-    switch (exp->tok_len[*exp_idx]) {
+    func_tok_idx = *tok_idx;
+    switch (exp->tok_len[*tok_idx]) {
     case 3:
-        if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "not", 3)) {
+        if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "not", 3)) {
             min_arg_count = 1;
             max_arg_count = 1;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "sum", 3)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "sum", 3)) {
             min_arg_count = 1;
             max_arg_count = 1;
         }
         break;
     case 4:
-        if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "lang", 4)) {
+        if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "lang", 4)) {
             min_arg_count = 1;
             max_arg_count = 1;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "last", 4)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "last", 4)) {
             min_arg_count = 0;
             max_arg_count = 0;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "name", 4)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "name", 4)) {
             min_arg_count = 0;
             max_arg_count = 1;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "true", 4)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "true", 4)) {
             min_arg_count = 0;
             max_arg_count = 0;
         }
         break;
     case 5:
-        if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "count", 5)) {
+        if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "count", 5)) {
             min_arg_count = 1;
             max_arg_count = 1;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "false", 5)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "false", 5)) {
             min_arg_count = 0;
             max_arg_count = 0;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "floor", 5)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "floor", 5)) {
             min_arg_count = 1;
             max_arg_count = 1;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "round", 5)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "round", 5)) {
             min_arg_count = 1;
             max_arg_count = 1;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "deref", 5)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "deref", 5)) {
             min_arg_count = 1;
             max_arg_count = 1;
         }
         break;
     case 6:
-        if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "concat", 6)) {
+        if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "concat", 6)) {
             min_arg_count = 2;
             max_arg_count = INT_MAX;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "number", 6)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "number", 6)) {
             min_arg_count = 0;
             max_arg_count = 1;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "string", 6)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "string", 6)) {
             min_arg_count = 0;
             max_arg_count = 1;
         }
         break;
     case 7:
-        if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "boolean", 7)) {
+        if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "boolean", 7)) {
             min_arg_count = 1;
             max_arg_count = 1;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "ceiling", 7)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "ceiling", 7)) {
             min_arg_count = 1;
             max_arg_count = 1;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "current", 7)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "current", 7)) {
             min_arg_count = 0;
             max_arg_count = 0;
         }
         break;
     case 8:
-        if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "contains", 8)) {
+        if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "contains", 8)) {
             min_arg_count = 2;
             max_arg_count = 2;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "position", 8)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "position", 8)) {
             min_arg_count = 0;
             max_arg_count = 0;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "re-match", 8)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "re-match", 8)) {
             min_arg_count = 2;
             max_arg_count = 2;
         }
         break;
     case 9:
-        if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "substring", 9)) {
+        if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring", 9)) {
             min_arg_count = 2;
             max_arg_count = 3;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "translate", 9)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "translate", 9)) {
             min_arg_count = 3;
             max_arg_count = 3;
         }
         break;
     case 10:
-        if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "local-name", 10)) {
+        if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "local-name", 10)) {
             min_arg_count = 0;
             max_arg_count = 1;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "enum-value", 10)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "enum-value", 10)) {
             min_arg_count = 1;
             max_arg_count = 1;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "bit-is-set", 10)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "bit-is-set", 10)) {
             min_arg_count = 2;
             max_arg_count = 2;
         }
         break;
     case 11:
-        if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "starts-with", 11)) {
+        if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "starts-with", 11)) {
             min_arg_count = 2;
             max_arg_count = 2;
         }
         break;
     case 12:
-        if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "derived-from", 12)) {
+        if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "derived-from", 12)) {
             min_arg_count = 2;
             max_arg_count = 2;
         }
         break;
     case 13:
-        if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "namespace-uri", 13)) {
+        if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "namespace-uri", 13)) {
             min_arg_count = 0;
             max_arg_count = 1;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "string-length", 13)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "string-length", 13)) {
             min_arg_count = 0;
             max_arg_count = 1;
         }
         break;
     case 15:
-        if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "normalize-space", 15)) {
+        if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "normalize-space", 15)) {
             min_arg_count = 0;
             max_arg_count = 1;
-        } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "substring-after", 15)) {
+        } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring-after", 15)) {
             min_arg_count = 2;
             max_arg_count = 2;
         }
         break;
     case 16:
-        if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "substring-before", 16)) {
+        if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring-before", 16)) {
             min_arg_count = 2;
             max_arg_count = 2;
         }
         break;
     case 20:
-        if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "derived-from-or-self", 20)) {
+        if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "derived-from-or-self", 20)) {
             min_arg_count = 2;
             max_arg_count = 2;
         }
         break;
     }
     if (min_arg_count == -1) {
-        LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INFUNC, exp->tok_len[*exp_idx], &exp->expr[exp->tok_pos[*exp_idx]]);
+        LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INFUNC, exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]]);
         return LY_EINVAL;
     }
-    ++(*exp_idx);
+    ++(*tok_idx);
 
     /* '(' */
-    rc = lyxp_check_token(ctx, exp, *exp_idx, LYXP_TOKEN_PAR1);
+    rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR1);
     LY_CHECK_RET(rc);
-    ++(*exp_idx);
+    ++(*tok_idx);
 
     /* ( Expr ( ',' Expr )* )? */
     arg_count = 0;
-    rc = lyxp_check_token(ctx, exp, *exp_idx, LYXP_TOKEN_NONE);
+    rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE);
     LY_CHECK_RET(rc);
-    if (exp->tokens[*exp_idx] != LYXP_TOKEN_PAR2) {
+    if (exp->tokens[*tok_idx] != LYXP_TOKEN_PAR2) {
         ++arg_count;
-        rc = reparse_or_expr(ctx, exp, exp_idx);
+        rc = reparse_or_expr(ctx, exp, tok_idx);
         LY_CHECK_RET(rc);
     }
-    while (!lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_COMMA)) {
-        ++(*exp_idx);
+    while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_COMMA)) {
+        ++(*tok_idx);
 
         ++arg_count;
-        rc = reparse_or_expr(ctx, exp, exp_idx);
+        rc = reparse_or_expr(ctx, exp, tok_idx);
         LY_CHECK_RET(rc);
     }
 
     /* ')' */
-    rc = lyxp_check_token(ctx, exp, *exp_idx, LYXP_TOKEN_PAR2);
+    rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR2);
     LY_CHECK_RET(rc);
-    ++(*exp_idx);
+    ++(*tok_idx);
 
     if ((arg_count < min_arg_count) || (arg_count > max_arg_count)) {
-        LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INARGCOUNT, arg_count, exp->tok_len[func_exp_idx],
-               &exp->expr[exp->tok_pos[func_exp_idx]]);
+        LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INARGCOUNT, arg_count, exp->tok_len[func_tok_idx],
+               &exp->expr[exp->tok_pos[func_tok_idx]]);
         return LY_EVALID;
     }
 
@@ -2233,29 +2244,29 @@
  *
  * @param[in] ctx Context for logging.
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @return LY_ERR
  */
 static LY_ERR
-reparse_path_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *exp_idx)
+reparse_path_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
 {
     LY_ERR rc;
 
-    if (lyxp_check_token(ctx, exp, *exp_idx, LYXP_TOKEN_NONE)) {
+    if (lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_NONE)) {
         return LY_EVALID;
     }
 
-    switch (exp->tokens[*exp_idx]) {
+    switch (exp->tokens[*tok_idx]) {
     case LYXP_TOKEN_PAR1:
         /* '(' Expr ')' Predicate* */
-        ++(*exp_idx);
+        ++(*tok_idx);
 
-        rc = reparse_or_expr(ctx, exp, exp_idx);
+        rc = reparse_or_expr(ctx, exp, tok_idx);
         LY_CHECK_RET(rc);
 
-        rc = lyxp_check_token(ctx, exp, *exp_idx, LYXP_TOKEN_PAR2);
+        rc = lyxp_check_token(ctx, exp, *tok_idx, LYXP_TOKEN_PAR2);
         LY_CHECK_RET(rc);
-        ++(*exp_idx);
+        ++(*tok_idx);
         goto predicate;
         break;
     case LYXP_TOKEN_DOT:
@@ -2264,34 +2275,34 @@
     case LYXP_TOKEN_NAMETEST:
     case LYXP_TOKEN_NODETYPE:
         /* RelativeLocationPath */
-        rc = reparse_relative_location_path(ctx, exp, exp_idx);
+        rc = reparse_relative_location_path(ctx, exp, tok_idx);
         LY_CHECK_RET(rc);
         break;
     case LYXP_TOKEN_FUNCNAME:
         /* FunctionCall */
-        rc = reparse_function_call(ctx, exp, exp_idx);
+        rc = reparse_function_call(ctx, exp, tok_idx);
         LY_CHECK_RET(rc);
         goto predicate;
         break;
     case LYXP_TOKEN_OPER_PATH:
     case LYXP_TOKEN_OPER_RPATH:
         /* AbsoluteLocationPath */
-        rc = reparse_absolute_location_path(ctx, exp, exp_idx);
+        rc = reparse_absolute_location_path(ctx, exp, tok_idx);
         LY_CHECK_RET(rc);
         break;
     case LYXP_TOKEN_LITERAL:
         /* Literal */
-        ++(*exp_idx);
+        ++(*tok_idx);
         goto predicate;
         break;
     case LYXP_TOKEN_NUMBER:
         /* Number */
-        ++(*exp_idx);
+        ++(*tok_idx);
         goto predicate;
         break;
     default:
         LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INTOK,
-               lyxp_print_token(exp->tokens[*exp_idx]), &exp->expr[exp->tok_pos[*exp_idx]]);
+               lyxp_print_token(exp->tokens[*tok_idx]), &exp->expr[exp->tok_pos[*tok_idx]]);
         return LY_EVALID;
     }
 
@@ -2299,18 +2310,18 @@
 
 predicate:
     /* Predicate* */
-    while (!lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_BRACK1)) {
-        rc = reparse_predicate(ctx, exp, exp_idx);
+    while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
+        rc = reparse_predicate(ctx, exp, tok_idx);
         LY_CHECK_RET(rc);
     }
 
     /* ('/' or '//') RelativeLocationPath */
-    if (!exp_check_token2(NULL, exp, *exp_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH)) {
+    if (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH)) {
 
         /* '/' or '//' */
-        ++(*exp_idx);
+        ++(*tok_idx);
 
-        rc = reparse_relative_location_path(ctx, exp, exp_idx);
+        rc = reparse_relative_location_path(ctx, exp, tok_idx);
         LY_CHECK_RET(rc);
     }
 
@@ -2325,34 +2336,34 @@
  *
  * @param[in] ctx Context for logging.
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @return LY_ERR
  */
 static LY_ERR
-reparse_unary_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *exp_idx)
+reparse_unary_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
 {
     uint16_t prev_exp;
     LY_ERR rc;
 
     /* ('-')* */
-    prev_exp = *exp_idx;
-    while (!lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_OPER_MATH)
-            && (exp->expr[exp->tok_pos[*exp_idx]] == '-')) {
+    prev_exp = *tok_idx;
+    while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_MATH)
+            && (exp->expr[exp->tok_pos[*tok_idx]] == '-')) {
         exp_repeat_push(exp, prev_exp, LYXP_EXPR_UNARY);
-        ++(*exp_idx);
+        ++(*tok_idx);
     }
 
     /* PathExpr */
-    prev_exp = *exp_idx;
-    rc = reparse_path_expr(ctx, exp, exp_idx);
+    prev_exp = *tok_idx;
+    rc = reparse_path_expr(ctx, exp, tok_idx);
     LY_CHECK_RET(rc);
 
     /* ('|' PathExpr)* */
-    while (!lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_OPER_UNI)) {
+    while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_UNI)) {
         exp_repeat_push(exp, prev_exp, LYXP_EXPR_UNION);
-        ++(*exp_idx);
+        ++(*tok_idx);
 
-        rc = reparse_path_expr(ctx, exp, exp_idx);
+        rc = reparse_path_expr(ctx, exp, tok_idx);
         LY_CHECK_RET(rc);
     }
 
@@ -2372,37 +2383,37 @@
  *
  * @param[in] ctx Context for logging.
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @return LY_ERR
  */
 static LY_ERR
-reparse_additive_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *exp_idx)
+reparse_additive_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
 {
     uint16_t prev_add_exp, prev_mul_exp;
     LY_ERR rc;
 
-    prev_add_exp = *exp_idx;
+    prev_add_exp = *tok_idx;
     goto reparse_multiplicative_expr;
 
     /* ('+' / '-' MultiplicativeExpr)* */
-    while (!lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_OPER_MATH)
-            && ((exp->expr[exp->tok_pos[*exp_idx]] == '+') || (exp->expr[exp->tok_pos[*exp_idx]] == '-'))) {
+    while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_MATH)
+            && ((exp->expr[exp->tok_pos[*tok_idx]] == '+') || (exp->expr[exp->tok_pos[*tok_idx]] == '-'))) {
         exp_repeat_push(exp, prev_add_exp, LYXP_EXPR_ADDITIVE);
-        ++(*exp_idx);
+        ++(*tok_idx);
 
 reparse_multiplicative_expr:
         /* UnaryExpr */
-        prev_mul_exp = *exp_idx;
-        rc = reparse_unary_expr(ctx, exp, exp_idx);
+        prev_mul_exp = *tok_idx;
+        rc = reparse_unary_expr(ctx, exp, tok_idx);
         LY_CHECK_RET(rc);
 
         /* ('*' / 'div' / 'mod' UnaryExpr)* */
-        while (!lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_OPER_MATH)
-                && ((exp->expr[exp->tok_pos[*exp_idx]] == '*') || (exp->tok_len[*exp_idx] == 3))) {
+        while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_MATH)
+                && ((exp->expr[exp->tok_pos[*tok_idx]] == '*') || (exp->tok_len[*tok_idx] == 3))) {
             exp_repeat_push(exp, prev_mul_exp, LYXP_EXPR_MULTIPLICATIVE);
-            ++(*exp_idx);
+            ++(*tok_idx);
 
-            rc = reparse_unary_expr(ctx, exp, exp_idx);
+            rc = reparse_unary_expr(ctx, exp, tok_idx);
             LY_CHECK_RET(rc);
         }
     }
@@ -2423,35 +2434,35 @@
  *
  * @param[in] ctx Context for logging.
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @return LY_ERR
  */
 static LY_ERR
-reparse_equality_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *exp_idx)
+reparse_equality_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
 {
     uint16_t prev_eq_exp, prev_rel_exp;
     LY_ERR rc;
 
-    prev_eq_exp = *exp_idx;
+    prev_eq_exp = *tok_idx;
     goto reparse_additive_expr;
 
     /* ('=' / '!=' RelationalExpr)* */
-    while (!exp_check_token2(NULL, exp, *exp_idx, LYXP_TOKEN_OPER_EQUAL, LYXP_TOKEN_OPER_NEQUAL)) {
+    while (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_EQUAL, LYXP_TOKEN_OPER_NEQUAL)) {
         exp_repeat_push(exp, prev_eq_exp, LYXP_EXPR_EQUALITY);
-        ++(*exp_idx);
+        ++(*tok_idx);
 
 reparse_additive_expr:
         /* AdditiveExpr */
-        prev_rel_exp = *exp_idx;
-        rc = reparse_additive_expr(ctx, exp, exp_idx);
+        prev_rel_exp = *tok_idx;
+        rc = reparse_additive_expr(ctx, exp, tok_idx);
         LY_CHECK_RET(rc);
 
         /* ('<' / '>' / '<=' / '>=' AdditiveExpr)* */
-        while (!lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_OPER_COMP)) {
+        while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_COMP)) {
             exp_repeat_push(exp, prev_rel_exp, LYXP_EXPR_RELATIONAL);
-            ++(*exp_idx);
+            ++(*tok_idx);
 
-            rc = reparse_additive_expr(ctx, exp, exp_idx);
+            rc = reparse_additive_expr(ctx, exp, tok_idx);
             LY_CHECK_RET(rc);
         }
     }
@@ -2467,35 +2478,35 @@
  *
  * @param[in] ctx Context for logging.
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @return LY_ERR
  */
 static LY_ERR
-reparse_or_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *exp_idx)
+reparse_or_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx)
 {
     uint16_t prev_or_exp, prev_and_exp;
     LY_ERR rc;
 
-    prev_or_exp = *exp_idx;
+    prev_or_exp = *tok_idx;
     goto reparse_equality_expr;
 
     /* ('or' AndExpr)* */
-    while (!lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_OPER_LOG) && (exp->tok_len[*exp_idx] == 2)) {
+    while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_LOG) && (exp->tok_len[*tok_idx] == 2)) {
         exp_repeat_push(exp, prev_or_exp, LYXP_EXPR_OR);
-        ++(*exp_idx);
+        ++(*tok_idx);
 
 reparse_equality_expr:
         /* EqualityExpr */
-        prev_and_exp = *exp_idx;
-        rc = reparse_equality_expr(ctx, exp, exp_idx);
+        prev_and_exp = *tok_idx;
+        rc = reparse_equality_expr(ctx, exp, tok_idx);
         LY_CHECK_RET(rc);
 
         /* ('and' EqualityExpr)* */
-        while (!lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_OPER_LOG) && (exp->tok_len[*exp_idx] == 3)) {
+        while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_LOG) && (exp->tok_len[*tok_idx] == 3)) {
             exp_repeat_push(exp, prev_and_exp, LYXP_EXPR_AND);
-            ++(*exp_idx);
+            ++(*tok_idx);
 
-            rc = reparse_equality_expr(ctx, exp, exp_idx);
+            rc = reparse_equality_expr(ctx, exp, tok_idx);
             LY_CHECK_RET(rc);
         }
     }
@@ -2593,16 +2604,24 @@
 }
 
 struct lyxp_expr *
-lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr)
+lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr, size_t expr_len, int reparse)
 {
     struct lyxp_expr *ret;
     size_t parsed = 0, tok_len;
     long int ncname_len;
     enum lyxp_token tok_type;
     int prev_function_check = 0;
-    uint16_t exp_idx = 0;
+    uint16_t tok_idx = 0;
 
-    if (strlen(expr) > UINT16_MAX) {
+    if (!expr[0]) {
+        LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOF);
+        return NULL;
+    }
+
+    if (!expr_len) {
+        expr_len = strlen(expr);
+    }
+    if (expr_len > UINT16_MAX) {
         LOGERR(ctx, LY_EINVAL, "XPath expression cannot be longer than %ud characters.", UINT16_MAX);
         return NULL;
     }
@@ -2610,7 +2629,7 @@
     /* init lyxp_expr structure */
     ret = calloc(1, sizeof *ret);
     LY_CHECK_ERR_GOTO(!ret, LOGMEM(ctx), error);
-    ret->expr = lydict_insert(ctx, expr, strlen(expr));
+    ret->expr = lydict_insert(ctx, expr, expr_len);
     LY_CHECK_ERR_GOTO(!ret->expr, LOGMEM(ctx), error);
     ret->used = 0;
     ret->size = LYXP_EXPR_SIZE_START;
@@ -2623,6 +2642,9 @@
     ret->tok_len = malloc(ret->size * sizeof *ret->tok_len);
     LY_CHECK_ERR_GOTO(!ret->tok_len, LOGMEM(ctx), error);
 
+    /* make expr 0-terminated */
+    expr = ret->expr;
+
     while (is_xmlws(expr[parsed])) {
         ++parsed;
     }
@@ -2844,16 +2866,18 @@
 
     } while (expr[parsed]);
 
-    /* prealloc repeat */
-    ret->repeat = calloc(ret->size, sizeof *ret->repeat);
-    LY_CHECK_ERR_GOTO(!ret->repeat, LOGMEM(ctx), error);
+    if (reparse) {
+        /* prealloc repeat */
+        ret->repeat = calloc(ret->size, sizeof *ret->repeat);
+        LY_CHECK_ERR_GOTO(!ret->repeat, LOGMEM(ctx), error);
 
-    /* fill repeat */
-    LY_CHECK_GOTO(reparse_or_expr(ctx, ret, &exp_idx), error);
-    if (ret->used > exp_idx) {
-        LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Unparsed characters \"%s\" left at the end of an XPath expression.",
-               &ret->expr[ret->tok_pos[exp_idx]]);
-        goto error;
+        /* fill repeat */
+        LY_CHECK_GOTO(reparse_or_expr(ctx, ret, &tok_idx), error);
+        if (ret->used > tok_idx) {
+            LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Unparsed characters \"%s\" left at the end of an XPath expression.",
+                &ret->expr[ret->tok_pos[tok_idx]]);
+            goto error;
+        }
     }
 
     print_expr_struct_debug(ret);
@@ -2865,6 +2889,55 @@
     return NULL;
 }
 
+struct lyxp_expr *
+lyxp_expr_dup(const struct ly_ctx *ctx, const struct lyxp_expr *exp)
+{
+    struct lyxp_expr *dup;
+    uint32_t i, j;
+
+    dup = calloc(1, sizeof *dup);
+    LY_CHECK_ERR_GOTO(!dup, LOGMEM(ctx), error);
+
+    dup->tokens = malloc(exp->used * sizeof *dup->tokens);
+    LY_CHECK_ERR_GOTO(!dup->tokens, LOGMEM(ctx), error);
+    memcpy(dup->tokens, exp->tokens, exp->used * sizeof *dup->tokens);
+
+    dup->tok_pos = malloc(exp->used * sizeof *dup->tok_pos);
+    LY_CHECK_ERR_GOTO(!dup->tok_pos, LOGMEM(ctx), error);
+    memcpy(dup->tok_pos, exp->tok_pos, exp->used * sizeof *dup->tok_pos);
+
+    dup->tok_len = malloc(exp->used * sizeof *dup->tok_len);
+    LY_CHECK_ERR_GOTO(!dup->tok_len, LOGMEM(ctx), error);
+    memcpy(dup->tok_len, exp->tok_len, exp->used * sizeof *dup->tok_len);
+
+    dup->repeat = malloc(exp->used * sizeof *dup->repeat);
+    LY_CHECK_ERR_GOTO(!dup->repeat, LOGMEM(ctx), error);
+    for (i = 0; i < exp->used; ++i) {
+        if (!exp->repeat[i]) {
+            dup->repeat[i] = NULL;
+        } else {
+            for (j = 0; exp->repeat[i][j]; ++j);
+            /* the ending 0 as well */
+            ++j;
+
+            dup->repeat[i] = malloc(j * sizeof *dup->repeat);
+            LY_CHECK_ERR_GOTO(!dup->repeat[i], LOGMEM(ctx), error);
+            memcpy(dup->repeat[i], exp->repeat[i], j * sizeof **dup->repeat);
+            dup->repeat[i][j - 1] = 0;
+        }
+    }
+
+    dup->used = exp->used;
+    dup->size = exp->used;
+    dup->expr = lydict_insert(ctx, exp->expr, 0);
+
+    return dup;
+
+error:
+    lyxp_expr_free(ctx, dup);
+    return NULL;
+}
+
 /*
  * warn functions
  *
@@ -3529,11 +3602,12 @@
 static LY_ERR
 xpath_deref(struct lyxp_set **args, uint16_t UNUSED(arg_count), struct lyxp_set *set, int options)
 {
-    struct lysc_ctx cctx;
     struct lyd_node_term *leaf;
     struct lysc_node_leaf *sleaf = NULL;
+    struct lysc_type_leafref *lref;
     const struct lysc_node *target;
-    const struct lyd_node *node;
+    struct ly_path *p;
+    struct lyd_node *node;
     char *errmsg = NULL;
     const char *val;
     int dynamic;
@@ -3550,12 +3624,24 @@
         }
         set_scnode_clear_ctx(set);
         if (sleaf && (sleaf->type->basetype == LY_TYPE_LEAFREF)) {
-            cctx.ctx = set->ctx;
-            rc = lys_compile_leafref_validate(&cctx, (struct lysc_node *)sleaf, (struct lysc_type_leafref *)sleaf->type, &target);
-            /* it was already validated, it must succeed */
-            if (rc == LY_SUCCESS) {
-                lyxp_set_scnode_insert_node(set, target, LYXP_NODE_ELEM);
+            lref = (struct lysc_type_leafref *)sleaf->type;
+
+            /* it was already evaluated on schema, it must succeed */
+            if (set->format == LYD_JSON) {
+                rc = ly_path_compile(sleaf->module, (struct lysc_node *)sleaf, lref->path, LY_PATH_LREF_TRUE,
+                                     lydjson_resolve_prefix, NULL, LYD_JSON, &p);
+            } else {
+                assert(set->format == LYD_SCHEMA);
+                rc = ly_path_compile(sleaf->module, (struct lysc_node *)sleaf, lref->path, LY_PATH_LREF_TRUE,
+                                     lys_resolve_prefix, lref->path_context, LYD_SCHEMA, &p);
             }
+            assert(!rc);
+
+            /* get the target node */
+            target = p[LY_ARRAY_SIZE(p) - 1].node;
+            ly_path_free(set->ctx, p);
+
+            lyxp_set_scnode_insert_node(set, target, LYXP_NODE_ELEM);
         }
 
         return rc;
@@ -3573,24 +3659,15 @@
         if (sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST)) {
             if (sleaf->type->basetype == LY_TYPE_LEAFREF) {
                 /* find leafref target */
-                val = lyd_value2str(leaf, &dynamic);
-                node = ly_type_find_leafref(set->ctx, sleaf->type, val, strlen(val), (struct lyd_node *)leaf,
-                                            set->tree, &leaf->value, &errmsg);
-                if (dynamic) {
-                    free((char *)val);
-                }
-                if (!node) {
-                    LOGERR(set->ctx, LY_EINVAL, errmsg);
+                if (ly_type_find_leafref((struct lysc_type_leafref *)sleaf->type, (struct lyd_node *)leaf,
+                                         &leaf->value, set->tree, &node, &errmsg)) {
+                    LOGERR(set->ctx, LY_EVALID, errmsg);
                     free(errmsg);
-                    return LY_EINVAL;
+                    return LY_EVALID;
                 }
-
-                /* insert it */
-                set_insert_node(set, node, 0, LYXP_NODE_ELEM, 0);
             } else {
                 assert(sleaf->type->basetype == LY_TYPE_INST);
-                node = (struct lyd_node *)lyd_target(leaf->value.target, set->tree);
-                if (!node) {
+                if (ly_path_eval(leaf->value.target, set->tree, &node)) {
                     val = lyd_value2str(leaf, &dynamic);
                     LOGERR(set->ctx, LY_EVALID, "Invalid instance-identifier \"%s\" value - required instance not found.", val);
                     if (dynamic) {
@@ -3599,6 +3676,9 @@
                     return LY_EVALID;
                 }
             }
+
+            /* insert it */
+            set_insert_node(set, node, 0, LYXP_NODE_ELEM, 0);
         }
     }
 
@@ -4366,7 +4446,7 @@
     }
 
     lyxp_set_cast(args[0], LYXP_SET_BOOLEAN);
-    if (args[0]->val.bool) {
+    if (args[0]->val.bln) {
         set_fill_boolean(set, 0);
     } else {
         set_fill_boolean(set, 1);
@@ -5181,7 +5261,7 @@
             LOGINT_RET(set->ctx);
         }
 
-        /* Check for errors and non-implemented modules, as they are not valid */
+        /* check for errors and non-implemented modules, as they are not valid */
         if (!mod || !mod->implemented) {
             if (set->type == LYXP_SET_SCNODE_SET) {
                 LOGVAL(set->ctx, LY_VLOG_LYSC, set->ctx_scnode, LY_VCODE_XP_INMOD, pref_len, *qname);
@@ -5196,9 +5276,12 @@
     } else if (((*qname)[0] == '*') && (*qname_len == 1)) {
         /* all modules - special case */
         mod = NULL;
+    } else if (set->type == LYXP_SET_SCNODE_SET) {
+        /* current node module */
+        mod = set->ctx_scnode->module;
     } else {
-        /* local module */
-        mod = set->local_mod;
+        /* current node module */
+        mod = set->ctx_node->schema->module;
     }
 
     *moveto_mod = mod;
@@ -5394,35 +5477,41 @@
  *
  * @param[in,out] set Set to use.
  * @param[in] scnode Matching node schema.
- * @param[in] list_inst If @p scnode is ::LYS_LIST, the matching list instance. Ignored otherwise.
- * @param[in] llist_val If @p scnode is ::LYS_LEAFLIST, the matching leaf-list value. Ignored otherwise.
- * @param[in] llist_val_len Length of @p llist_val.
+ * @param[in] predicates If @p scnode is ::LYS_LIST or ::LYS_LEAFLIST, the predicates specifying a single instance.
  * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-moveto_node_hash(struct lyxp_set *set, const struct lysc_node *scnode, const struct lyd_node *list_inst,
-                 const char *llist_val, uint16_t llist_val_len)
+moveto_node_hash(struct lyxp_set *set, const struct lysc_node *scnode, const struct ly_path_predicate *predicates)
 {
+    LY_ERR ret = LY_SUCCESS;
     uint32_t i;
     int replaced;
     const struct lyd_node *siblings;
-    struct lyd_node *sub;
+    struct lyd_node *sub, *inst = NULL;
 
-    assert(scnode && ((scnode->nodetype != LYS_LIST) || list_inst) && ((scnode->nodetype != LYS_LEAFLIST) || llist_val));
+    assert(scnode && (!(scnode->nodetype & (LYS_LIST | LYS_LEAFLIST)) || predicates));
 
     if (!set) {
-        return LY_SUCCESS;
+        goto cleanup;
     }
 
     if (set->type != LYXP_SET_NODE_SET) {
         LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INOP_1, "path operator", print_set_type(set));
-        return LY_EVALID;
+        ret = LY_EVALID;
+        goto cleanup;
     }
 
     /* context check for all the nodes since we have the schema node */
     if ((set->root_type == LYXP_NODE_ROOT_CONFIG) && (scnode->flags & LYS_CONFIG_R)) {
         lyxp_set_free_content(set);
-        return LY_SUCCESS;
+        goto cleanup;
+    }
+
+    /* create specific data instance if needed */
+    if (scnode->nodetype == LYS_LIST) {
+        LY_CHECK_GOTO(ret = lyd_create_list(scnode, predicates, &inst), cleanup);
+    } else if (scnode->nodetype == LYS_LEAFLIST) {
+        LY_CHECK_GOTO(ret = lyd_create_term2(scnode, &predicates[0].value, &inst), cleanup);
     }
 
     for (i = 0; i < set->used; ) {
@@ -5440,15 +5529,16 @@
         }
 
         /* find the node using hashes */
-        if (scnode->nodetype == LYS_LIST) {
-            lyd_find_sibling_first(siblings, list_inst, &sub);
+        if (inst) {
+            lyd_find_sibling_first(siblings, inst, &sub);
         } else {
-            lyd_find_sibling_val(siblings, scnode, llist_val, llist_val_len, &sub);
+            lyd_find_sibling_val(siblings, scnode, NULL, 0, &sub);
         }
 
         /* when check */
         if (sub && moveto_when_check(sub)) {
-            return LY_EINCOMPLETE;
+            ret = LY_EINCOMPLETE;
+            goto cleanup;
         }
 
         if (sub) {
@@ -5468,7 +5558,9 @@
         }
     }
 
-    return LY_SUCCESS;
+cleanup:
+    lyd_free_tree(inst);
+    return ret;
 }
 
 /**
@@ -6350,7 +6442,8 @@
     int64_t i;
     LY_ERR rc;
 
-    iter1.type = LYXP_SET_NODE_SET;
+    memset(&iter1, 0, sizeof iter1);
+    memset(&iter2, 0, sizeof iter2);
 
     /* iterative evaluation with node-sets */
     if ((set1->type == LYXP_SET_NODE_SET) || (set2->type == LYXP_SET_NODE_SET)) {
@@ -6376,7 +6469,7 @@
                 }
 
                 /* lazy evaluation until true */
-                if (iter1.val.bool) {
+                if (iter1.val.bln) {
                     set_fill_boolean(set1, 1);
                     return LY_SUCCESS;
                 }
@@ -6407,7 +6500,7 @@
                 lyxp_set_free_content(&iter2);
 
                 /* lazy evaluation until true */
-                if (iter1.val.bool) {
+                if (iter1.val.bln) {
                     set_fill_boolean(set1, 1);
                     return LY_SUCCESS;
                 }
@@ -6442,7 +6535,7 @@
     /* compute result */
     if (op[0] == '=') {
         if (set1->type == LYXP_SET_BOOLEAN) {
-            result = (set1->val.bool == set2->val.bool);
+            result = (set1->val.bln == set2->val.bln);
         } else if (set1->type == LYXP_SET_NUMBER) {
             result = (set1->val.num == set2->val.num);
         } else {
@@ -6451,7 +6544,7 @@
         }
     } else if (op[0] == '!') {
         if (set1->type == LYXP_SET_BOOLEAN) {
-            result = (set1->val.bool != set2->val.bool);
+            result = (set1->val.bln != set2->val.bln);
         } else if (set1->type == LYXP_SET_NUMBER) {
             result = (set1->val.num != set2->val.num);
         } else {
@@ -6560,14 +6653,14 @@
  * [9] Predicate ::= '[' Expr ']'
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
  * @param[in] options XPath options.
  * @param[in] parent_pos_pred Whether parent predicate was a positional one.
  * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-eval_predicate(struct lyxp_expr *exp, uint16_t *exp_idx, struct lyxp_set *set, int options, int parent_pos_pred)
+eval_predicate(struct lyxp_expr *exp, uint16_t *tok_idx, struct lyxp_set *set, int options, int parent_pos_pred)
 {
     LY_ERR rc;
     uint16_t i, orig_exp;
@@ -6578,12 +6671,12 @@
 
     /* '[' */
     LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-           lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-    ++(*exp_idx);
+           lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+    ++(*tok_idx);
 
     if (!set) {
 only_parse:
-        rc = eval_expr_select(exp, exp_idx, 0, NULL, options);
+        rc = eval_expr_select(exp, tok_idx, 0, NULL, options);
         LY_CHECK_RET(rc);
     } else if (set->type == LYXP_SET_NODE_SET) {
         /* we (possibly) need the set sorted, it can affect the result (if the predicate result is a number) */
@@ -6594,7 +6687,7 @@
             goto only_parse;
         }
 
-        orig_exp = *exp_idx;
+        orig_exp = *tok_idx;
         orig_pos = 0;
         orig_size = set->used;
         orig_parent = NULL;
@@ -6613,9 +6706,9 @@
 
             set2.ctx_pos = orig_pos;
             set2.ctx_size = orig_size;
-            *exp_idx = orig_exp;
+            *tok_idx = orig_exp;
 
-            rc = eval_expr_select(exp, exp_idx, 0, &set2, options);
+            rc = eval_expr_select(exp, tok_idx, 0, &set2, options);
             if (rc != LY_SUCCESS) {
                 lyxp_set_free_content(&set2);
                 return rc;
@@ -6632,7 +6725,7 @@
             lyxp_set_cast(&set2, LYXP_SET_BOOLEAN);
 
             /* predicate satisfied or not? */
-            if (!set2.val.bool) {
+            if (!set2.val.bln) {
                 set_remove_node_none(set, i);
             }
         }
@@ -6650,7 +6743,7 @@
             goto only_parse;
         }
 
-        orig_exp = *exp_idx;
+        orig_exp = *tok_idx;
 
         /* set special in_ctx to all the valid snodes */
         pred_in_ctx = set_scnode_new_in_ctx(set);
@@ -6662,9 +6755,9 @@
             }
             set->val.scnodes[i].in_ctx = 1;
 
-            *exp_idx = orig_exp;
+            *tok_idx = orig_exp;
 
-            rc = eval_expr_select(exp, exp_idx, 0, set, options);
+            rc = eval_expr_select(exp, tok_idx, 0, set, options);
             LY_CHECK_RET(rc);
 
             set->val.scnodes[i].in_ctx = pred_in_ctx;
@@ -6683,24 +6776,24 @@
         set2.type = LYXP_SET_NODE_SET;
         set_fill_set(&set2, set);
 
-        rc = eval_expr_select(exp, exp_idx, 0, &set2, options);
+        rc = eval_expr_select(exp, tok_idx, 0, &set2, options);
         if (rc != LY_SUCCESS) {
             lyxp_set_free_content(&set2);
             return rc;
         }
 
         lyxp_set_cast(&set2, LYXP_SET_BOOLEAN);
-        if (!set2.val.bool) {
+        if (!set2.val.bln) {
             lyxp_set_free_content(set);
         }
         lyxp_set_free_content(&set2);
     }
 
     /* ']' */
-    assert(exp->tokens[*exp_idx] == LYXP_TOKEN_BRACK2);
+    assert(exp->tokens[*tok_idx] == LYXP_TOKEN_BRACK2);
     LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-           lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-    ++(*exp_idx);
+           lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+    ++(*tok_idx);
 
     return LY_SUCCESS;
 }
@@ -6709,126 +6802,130 @@
  * @brief Evaluate Literal. Logs directly on error.
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
  */
 static void
-eval_literal(struct lyxp_expr *exp, uint16_t *exp_idx, struct lyxp_set *set)
+eval_literal(struct lyxp_expr *exp, uint16_t *tok_idx, struct lyxp_set *set)
 {
     if (set) {
-        if (exp->tok_len[*exp_idx] == 2) {
+        if (exp->tok_len[*tok_idx] == 2) {
             set_fill_string(set, "", 0);
         } else {
-            set_fill_string(set, &exp->expr[exp->tok_pos[*exp_idx] + 1], exp->tok_len[*exp_idx] - 2);
+            set_fill_string(set, &exp->expr[exp->tok_pos[*tok_idx] + 1], exp->tok_len[*tok_idx] - 2);
         }
     }
     LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-    ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+    ++(*tok_idx);
 }
 
 /**
- * @brief Check and parse a predicate following a leaf-list to extract its value.
- * On success all the used tokens are skipped.
+ * @brief Try to compile list or leaf-list predicate in the known format to be used for hash-based instance search.
  *
- * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
- * @param[out] val Leaf-list value on success.
- * @param[out] val_len Length of @p val.
- * @return LY_ERR
+ * @param[in] exp Full parsed XPath expression.
+ * @param[in,out] tok_idx Index in @p exp at the beginning of the predicate, is updated on success.
+ * @param[in] scnode Found schema node as context for the predicate.
+ * @param[in] format Format of any prefixes in key names/values.
+ * @param[out] predicates Parsed predicates.
+ * @param[out] pred_type Type of @p predicates.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR on any error.
  */
 static LY_ERR
-eval_name_test_parse_leaflist_predicate(struct lyxp_expr *exp, uint16_t *exp_idx, const char **val, uint16_t *val_len)
+eval_name_test_try_compile_predicates(struct lyxp_expr *exp, uint16_t *tok_idx, const struct lysc_node *scnode,
+                                      LYD_FORMAT format, struct ly_path_predicate **predicates, enum ly_path_pred_type *pred_type)
 {
-    uint16_t cur_idx;
-
-    cur_idx = *exp_idx;
-
-    /* '[' */
-    LY_CHECK_RET(lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_BRACK1));
-    ++cur_idx;
-
-    /* '.' */
-    LY_CHECK_RET(lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_DOT));
-    ++cur_idx;
-
-    /* '=' */
-    LY_CHECK_RET(lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_OPER_EQUAL));
-    ++cur_idx;
-
-    /* value */
-    LY_CHECK_RET(lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_LITERAL));
-    ++cur_idx;
-
-    /* ']' */
-    LY_CHECK_RET(lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_BRACK2));
-    ++cur_idx;
-
-    /* success */
-    *val = &exp->expr[exp->tok_pos[cur_idx - 2] + 1];
-    *val_len = exp->tok_len[cur_idx - 2] - 2;
-    *exp_idx = cur_idx;
-    return LY_SUCCESS;
-}
-
-/**
- * @brief Check and parse a predicate following a list to make sure all the tokens are as expected (all key values defined).
- * On success all the used tokens are skipped.
- *
- * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
- * @param[in] slist Schema node of the list.
- * @param[out] val Leaf-list value on success.
- * @param[out] val_len Length of @p val.
- * @return LY_ERR
- */
-static LY_ERR
-eval_name_test_parse_list_predicate(struct lyxp_expr *exp, uint16_t *exp_idx, const struct lysc_node *slist,
-                                    const char **keys, uint16_t *keys_len)
-{
-    uint16_t key_count, i, cur_idx;
+    LY_ERR ret = LY_SUCCESS;
+    uint16_t key_count, e_idx, pred_idx = 0;
     const struct lysc_node *key;
+    size_t pred_len;
+    int prev_lo;
+    struct lyxp_expr *exp2 = NULL;
 
-    if (slist->flags & LYS_KEYLESS) {
-        /* invalid */
-        return LY_EINVAL;
-    }
+    assert(scnode->nodetype & (LYS_LIST | LYS_LEAFLIST));
 
-    /* get key count */
-    key_count = 0;
-    for (key = lysc_node_children(slist, 0); key && (key->flags & LYS_KEY); key = key->next) {
-        ++key_count;
-    }
+    if (scnode->nodetype == LYS_LIST) {
+        /* get key count */
+        if (scnode->flags & LYS_KEYLESS) {
+            return LY_EINVAL;
+        }
+        for (key_count = 0, key = lysc_node_children(scnode, 0); key && (key->flags & LYS_KEY); key = key->next, ++key_count);
+        assert(key_count);
 
-    /* briefly check the predicate for each key */
-    cur_idx = *exp_idx;
-    for (i = 0; i < key_count; ++i) {
+        /* learn where the predicates end */
+        e_idx = *tok_idx;
+        while (key_count) {
+            /* '[' */
+            if (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK1)) {
+                return LY_EINVAL;
+            }
+            ++e_idx;
+
+            /* ']' */
+            while (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK2)) {
+                ++e_idx;
+            }
+            ++e_idx;
+
+            /* another presumably key predicate parsed */
+            --key_count;
+        }
+
+        if (key_count) {
+            /* some keys missing for sure */
+            return LY_EINVAL;
+        }
+    } else {
+        /* learn just where this single predicate ends */
+        e_idx = *tok_idx;
+
         /* '[' */
-        LY_CHECK_RET(lyxp_check_token(NULL, exp, cur_idx, LYXP_TOKEN_BRACK1));
-        ++cur_idx;
-
-        /* key-name */
-        LY_CHECK_RET(lyxp_check_token(NULL, exp, cur_idx, LYXP_TOKEN_NAMETEST));
-        ++cur_idx;
-
-        /* '=' */
-        LY_CHECK_RET(lyxp_check_token(NULL, exp, cur_idx, LYXP_TOKEN_OPER_EQUAL));
-        ++cur_idx;
-
-        /* key-value */
-        LY_CHECK_RET(lyxp_check_token(NULL, exp, cur_idx, LYXP_TOKEN_LITERAL));
-        ++cur_idx;
+        if (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK1)) {
+            return LY_EINVAL;
+        }
+        ++e_idx;
 
         /* ']' */
-        LY_CHECK_RET(lyxp_check_token(NULL, exp, cur_idx, LYXP_TOKEN_BRACK2));
-        ++cur_idx;
+        while (lyxp_check_token(NULL, exp, e_idx, LYXP_TOKEN_BRACK2)) {
+            ++e_idx;
+        }
+        ++e_idx;
     }
 
-    /* success */
-    *keys = &exp->expr[exp->tok_pos[*exp_idx]];
-    *keys_len = (exp->expr + exp->tok_pos[cur_idx - 1] + exp->tok_len[cur_idx - 1]) - *keys;
-    *exp_idx = cur_idx;
-    return LY_SUCCESS;
+    /* get the joined length of all the keys predicates/of the single leaf-list predicate */
+    pred_len = (exp->tok_pos[e_idx - 1] + exp->tok_len[e_idx - 1]) - exp->tok_pos[*tok_idx];
+
+    /* turn logging off */
+    prev_lo = ly_log_options(0);
+
+    /* parse the predicate(s) */
+    LY_CHECK_GOTO(ret = ly_path_parse_predicate(scnode->module->ctx, exp->expr + exp->tok_pos[*tok_idx], pred_len,
+                                                LY_PATH_PREFIX_OPTIONAL, LY_PATH_PRED_SIMPLE, &exp2), cleanup);
+
+    /* compile */
+    switch (format) {
+    case LYD_SCHEMA:
+        ret = ly_path_compile_predicate(scnode->module, scnode, exp2, &pred_idx, lys_resolve_prefix, scnode->module,
+                                        LYD_SCHEMA, predicates, pred_type);
+        break;
+    case LYD_JSON:
+        ret = ly_path_compile_predicate(scnode->module, scnode, exp2, &pred_idx, lydjson_resolve_prefix, NULL, LYD_JSON,
+                                        predicates, pred_type);
+        break;
+    case LYD_XML:
+        ret = LY_EINT;
+        goto cleanup;
+    }
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* success, the predicate must include all the needed information for hash-based search */
+    *tok_idx = e_idx;
+
+cleanup:
+    ly_log_options(prev_lo);
+    lyxp_expr_free(scnode->module->ctx, exp2);
+    return ret;
 }
 
 /**
@@ -6839,7 +6936,7 @@
  * [7] NameTest ::= '*' | NCName ':' '*' | QName
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in] attr_axis Whether to search attributes or standard nodes.
  * @param[in] all_desc Whether to search all the descendants or children only.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
@@ -6847,28 +6944,29 @@
  * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-eval_name_test_with_predicate(struct lyxp_expr *exp, uint16_t *exp_idx, int attr_axis, int all_desc, struct lyxp_set *set,
+eval_name_test_with_predicate(struct lyxp_expr *exp, uint16_t *tok_idx, int attr_axis, int all_desc, struct lyxp_set *set,
                               int options)
 {
     int i;
     char *path;
-    const char *ncname = NULL, *key_val = NULL;
-    uint16_t ncname_len, key_val_len, prev_exp_idx;
+    const char *ncname, *ncname_dict = NULL;
+    uint16_t ncname_len;
     const struct lys_module *moveto_mod;
     const struct lysc_node *scnode = NULL, *tmp;
-    struct lyd_node *list_inst = NULL;
+    struct ly_path_predicate *predicates = NULL;
+    enum ly_path_pred_type pred_type = 0;
     LY_ERR rc = LY_SUCCESS;
 
     LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-           lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-    ++(*exp_idx);
+           lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+    ++(*tok_idx);
 
     if (!set) {
         goto moveto;
     }
 
-    ncname = &exp->expr[exp->tok_pos[*exp_idx - 1]];
-    ncname_len = exp->tok_len[*exp_idx - 1];
+    ncname = &exp->expr[exp->tok_pos[*tok_idx - 1]];
+    ncname_len = exp->tok_len[*tok_idx - 1];
 
     /* parse (and skip) module name */
     rc = moveto_resolve_model(&ncname, &ncname_len, set, &moveto_mod);
@@ -6903,38 +7001,19 @@
             }
         }
 
-        if (scnode && (scnode->nodetype == LYS_LIST)) {
-            /* make sure all the tokens are right */
-            prev_exp_idx = *exp_idx;
-            if (eval_name_test_parse_list_predicate(exp, exp_idx, scnode, &key_val, &key_val_len)) {
-                scnode = NULL;
-            }
-
-            /* try to create a list instance */
-            if (scnode && lyd_create_list(scnode, key_val, key_val_len, set->format, 0, &list_inst)) {
-                /* for whatever reason the list failed to be created, just use standard name comparison and
-                 * parse predicate normally */
-                *exp_idx = prev_exp_idx;
-                scnode = NULL;
-            }
-        } else if (scnode && (scnode->nodetype == LYS_LEAFLIST)) {
-            /* make sure we know the leaf-list value */
-            if (eval_name_test_parse_leaflist_predicate(exp, exp_idx, &key_val, &key_val_len)) {
-                /* value could not be recognized, use standard name comparison */
+        if (scnode && (scnode->nodetype & (LYS_LIST | LYS_LEAFLIST))) {
+            /* try to create the predicates */
+            if (eval_name_test_try_compile_predicates(exp, tok_idx, scnode, set->format, &predicates, &pred_type)) {
+                /* hashes cannot be used */
                 scnode = NULL;
             }
         }
     }
 
-    if (!scnode) {
-        /* we are not able to match based on a schema node */
-        if (!moveto_mod) {
-            /* '*', all nodes match */
-            ncname = NULL;
-        } else {
-            /* insert name into dictionary for efficient comparison */
-            ncname = lydict_insert(set->ctx, ncname, ncname_len);
-        }
+    if (!scnode && moveto_mod) {
+        /* we are not able to match based on a schema node and not all the modules match,
+         * use dictionary for efficient comparison */
+        ncname_dict = lydict_insert(set->ctx, ncname, ncname_len);
     }
 
 moveto:
@@ -6944,18 +7023,18 @@
             set_scnode_clear_ctx(set);
         } else {
             if (all_desc) {
-                rc = moveto_attr_alldesc(set, moveto_mod, ncname);
+                rc = moveto_attr_alldesc(set, moveto_mod, ncname_dict);
             } else {
-                rc = moveto_attr(set, moveto_mod, ncname);
+                rc = moveto_attr(set, moveto_mod, ncname_dict);
             }
             LY_CHECK_GOTO(rc, cleanup);
         }
     } else {
         if (set && (options & LYXP_SCNODE_ALL)) {
             if (all_desc) {
-                rc = moveto_scnode_alldesc(set, moveto_mod, ncname, options);
+                rc = moveto_scnode_alldesc(set, moveto_mod, ncname_dict, options);
             } else {
-                rc = moveto_scnode(set, moveto_mod, ncname, options);
+                rc = moveto_scnode(set, moveto_mod, ncname_dict, options);
             }
             LY_CHECK_GOTO(rc, cleanup);
 
@@ -6967,19 +7046,19 @@
             if (i == -1) {
                 path = lysc_path(set->ctx_scnode, LYSC_PATH_LOG, NULL, 0);
                 LOGWRN(set->ctx, "Schema node \"%.*s\" not found (%.*s) with context node \"%s\".",
-                       exp->tok_len[*exp_idx], &exp->expr[exp->tok_pos[*exp_idx]],
-                       exp->tok_pos[*exp_idx] + exp->tok_len[*exp_idx], exp->expr, path);
+                       exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]],
+                       exp->tok_pos[*tok_idx] + exp->tok_len[*tok_idx], exp->expr, path);
                 free(path);
             }
         } else {
             if (all_desc) {
-                rc = moveto_node_alldesc(set, moveto_mod, ncname);
+                rc = moveto_node_alldesc(set, moveto_mod, ncname_dict);
             } else {
                 if (scnode) {
                     /* we can find the nodes using hashes */
-                    rc = moveto_node_hash(set, scnode, list_inst, key_val, key_val_len);
+                    rc = moveto_node_hash(set, scnode, predicates);
                 } else {
-                    rc = moveto_node(set, moveto_mod, ncname);
+                    rc = moveto_node(set, moveto_mod, ncname_dict);
                 }
             }
             LY_CHECK_GOTO(rc, cleanup);
@@ -6987,16 +7066,16 @@
     }
 
     /* Predicate* */
-    while (!lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_BRACK1)) {
-        rc = eval_predicate(exp, exp_idx, set, options, 1);
+    while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
+        rc = eval_predicate(exp, tok_idx, set, options, 1);
         LY_CHECK_RET(rc);
     }
 
 cleanup:
     if (set) {
-        lydict_remove(set->ctx, ncname);
+        lydict_remove(set->ctx, ncname_dict);
+        ly_path_predicates_free(set->ctx, pred_type, scnode, predicates);
     }
-    lyd_free_tree(list_inst);
     return rc;
 }
 
@@ -7008,7 +7087,7 @@
  * [8] NodeType ::= 'text' | 'node'
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in] attr_axis Whether to search attributes or standard nodes.
  * @param[in] all_desc Whether to search all the descendants or children only.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
@@ -7016,7 +7095,7 @@
  * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-eval_node_type_with_predicate(struct lyxp_expr *exp, uint16_t *exp_idx, int attr_axis, int all_desc,
+eval_node_type_with_predicate(struct lyxp_expr *exp, uint16_t *tok_idx, int attr_axis, int all_desc,
                               struct lyxp_set *set, int options)
 {
     LY_ERR rc;
@@ -7026,40 +7105,40 @@
     (void)all_desc;
 
     if (set) {
-        assert(exp->tok_len[*exp_idx] == 4);
+        assert(exp->tok_len[*tok_idx] == 4);
         if (set->type == LYXP_SET_SCNODE_SET) {
             set_scnode_clear_ctx(set);
             /* just for the debug message below */
             set = NULL;
         } else {
-            if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "node", 4)) {
+            if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "node", 4)) {
                 rc = xpath_node(NULL, 0, set, options);
             } else {
-                assert(!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "text", 4));
+                assert(!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "text", 4));
                 rc = xpath_text(NULL, 0, set, options);
             }
             LY_CHECK_RET(rc);
         }
     }
     LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-           lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-    ++(*exp_idx);
+           lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+    ++(*tok_idx);
 
     /* '(' */
-    assert(exp->tokens[*exp_idx] == LYXP_TOKEN_PAR1);
+    assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR1);
     LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-           lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-    ++(*exp_idx);
+           lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+    ++(*tok_idx);
 
     /* ')' */
-    assert(exp->tokens[*exp_idx] == LYXP_TOKEN_PAR2);
+    assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR2);
     LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-           lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-    ++(*exp_idx);
+           lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+    ++(*tok_idx);
 
     /* Predicate* */
-    while (!lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_BRACK1)) {
-        rc = eval_predicate(exp, exp_idx, set, options, 1);
+    while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
+        rc = eval_predicate(exp, tok_idx, set, options, 1);
         LY_CHECK_RET(rc);
     }
 
@@ -7074,14 +7153,14 @@
  * [6] NodeTest ::= NameTest | NodeType '(' ')'
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in] all_desc Whether to search all the descendants or children only.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
  * @param[in] options XPath options.
  * @return LY_ERR (YL_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-eval_relative_location_path(struct lyxp_expr *exp, uint16_t *exp_idx, int all_desc, struct lyxp_set *set, int options)
+eval_relative_location_path(struct lyxp_expr *exp, uint16_t *tok_idx, int all_desc, struct lyxp_set *set, int options)
 {
     int attr_axis;
     LY_ERR rc;
@@ -7089,30 +7168,30 @@
     goto step;
     do {
         /* evaluate '/' or '//' */
-        if (exp->tok_len[*exp_idx] == 1) {
+        if (exp->tok_len[*tok_idx] == 1) {
             all_desc = 0;
         } else {
-            assert(exp->tok_len[*exp_idx] == 2);
+            assert(exp->tok_len[*tok_idx] == 2);
             all_desc = 1;
         }
         LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-        ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+        ++(*tok_idx);
 
 step:
         /* evaluate abbreviated axis '@'? if any */
-        if (exp->tokens[*exp_idx] == LYXP_TOKEN_AT) {
+        if (exp->tokens[*tok_idx] == LYXP_TOKEN_AT) {
             attr_axis = 1;
 
             LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-            ++(*exp_idx);
+                   lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+            ++(*tok_idx);
         } else {
             attr_axis = 0;
         }
 
         /* Step */
-        switch (exp->tokens[*exp_idx]) {
+        switch (exp->tokens[*tok_idx]) {
         case LYXP_TOKEN_DOT:
             /* evaluate '.' */
             if (set && (options & LYXP_SCNODE_ALL)) {
@@ -7122,8 +7201,8 @@
             }
             LY_CHECK_RET(rc);
             LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-            ++(*exp_idx);
+                   lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+            ++(*tok_idx);
             break;
 
         case LYXP_TOKEN_DDOT:
@@ -7135,26 +7214,26 @@
             }
             LY_CHECK_RET(rc);
             LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-            ++(*exp_idx);
+                   lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+            ++(*tok_idx);
             break;
 
         case LYXP_TOKEN_NAMETEST:
             /* evaluate NameTest Predicate* */
-            rc = eval_name_test_with_predicate(exp, exp_idx, attr_axis, all_desc, set, options);
+            rc = eval_name_test_with_predicate(exp, tok_idx, attr_axis, all_desc, set, options);
             LY_CHECK_RET(rc);
             break;
 
         case LYXP_TOKEN_NODETYPE:
             /* evaluate NodeType Predicate* */
-            rc = eval_node_type_with_predicate(exp, exp_idx, attr_axis, all_desc, set, options);
+            rc = eval_node_type_with_predicate(exp, tok_idx, attr_axis, all_desc, set, options);
             LY_CHECK_RET(rc);
             break;
 
         default:
             LOGINT_RET(set ? set->ctx : NULL);
         }
-    } while (!exp_check_token2(NULL, exp, *exp_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH));
+    } while (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH));
 
     return LY_SUCCESS;
 }
@@ -7165,13 +7244,13 @@
  * [3] AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
  * @param[in] options XPath options.
  * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-eval_absolute_location_path(struct lyxp_expr *exp, uint16_t *exp_idx, struct lyxp_set *set, int options)
+eval_absolute_location_path(struct lyxp_expr *exp, uint16_t *tok_idx, struct lyxp_set *set, int options)
 {
     int all_desc;
     LY_ERR rc;
@@ -7182,23 +7261,23 @@
     }
 
     /* '/' RelativeLocationPath? */
-    if (exp->tok_len[*exp_idx] == 1) {
+    if (exp->tok_len[*tok_idx] == 1) {
         /* evaluate '/' - deferred */
         all_desc = 0;
         LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-        ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+        ++(*tok_idx);
 
-        if (lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_NONE)) {
+        if (lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_NONE)) {
             return LY_SUCCESS;
         }
-        switch (exp->tokens[*exp_idx]) {
+        switch (exp->tokens[*tok_idx]) {
         case LYXP_TOKEN_DOT:
         case LYXP_TOKEN_DDOT:
         case LYXP_TOKEN_AT:
         case LYXP_TOKEN_NAMETEST:
         case LYXP_TOKEN_NODETYPE:
-            rc = eval_relative_location_path(exp, exp_idx, all_desc, set, options);
+            rc = eval_relative_location_path(exp, tok_idx, all_desc, set, options);
             LY_CHECK_RET(rc);
             break;
         default:
@@ -7210,10 +7289,10 @@
         /* evaluate '//' - deferred so as not to waste memory by remembering all the nodes */
         all_desc = 1;
         LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-        ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+        ++(*tok_idx);
 
-        rc = eval_relative_location_path(exp, exp_idx, all_desc, set, options);
+        rc = eval_relative_location_path(exp, tok_idx, all_desc, set, options);
         LY_CHECK_RET(rc);
     }
 
@@ -7226,13 +7305,13 @@
  * [11] FunctionCall ::= FunctionName '(' ( Expr ( ',' Expr )* )? ')'
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
  * @param[in] options XPath options.
  * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-eval_function_call(struct lyxp_expr *exp, uint16_t *exp_idx, struct lyxp_set *set, int options)
+eval_function_call(struct lyxp_expr *exp, uint16_t *tok_idx, struct lyxp_set *set, int options)
 {
     LY_ERR rc;
     LY_ERR (*xpath_func)(struct lyxp_set **, uint16_t, struct lyxp_set *, int) = NULL;
@@ -7241,135 +7320,135 @@
 
     if (set) {
         /* FunctionName */
-        switch (exp->tok_len[*exp_idx]) {
+        switch (exp->tok_len[*tok_idx]) {
         case 3:
-            if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "not", 3)) {
+            if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "not", 3)) {
                 xpath_func = &xpath_not;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "sum", 3)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "sum", 3)) {
                 xpath_func = &xpath_sum;
             }
             break;
         case 4:
-            if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "lang", 4)) {
+            if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "lang", 4)) {
                 xpath_func = &xpath_lang;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "last", 4)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "last", 4)) {
                 xpath_func = &xpath_last;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "name", 4)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "name", 4)) {
                 xpath_func = &xpath_name;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "true", 4)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "true", 4)) {
                 xpath_func = &xpath_true;
             }
             break;
         case 5:
-            if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "count", 5)) {
+            if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "count", 5)) {
                 xpath_func = &xpath_count;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "false", 5)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "false", 5)) {
                 xpath_func = &xpath_false;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "floor", 5)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "floor", 5)) {
                 xpath_func = &xpath_floor;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "round", 5)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "round", 5)) {
                 xpath_func = &xpath_round;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "deref", 5)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "deref", 5)) {
                 xpath_func = &xpath_deref;
             }
             break;
         case 6:
-            if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "concat", 6)) {
+            if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "concat", 6)) {
                 xpath_func = &xpath_concat;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "number", 6)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "number", 6)) {
                 xpath_func = &xpath_number;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "string", 6)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "string", 6)) {
                 xpath_func = &xpath_string;
             }
             break;
         case 7:
-            if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "boolean", 7)) {
+            if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "boolean", 7)) {
                 xpath_func = &xpath_boolean;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "ceiling", 7)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "ceiling", 7)) {
                 xpath_func = &xpath_ceiling;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "current", 7)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "current", 7)) {
                 xpath_func = &xpath_current;
             }
             break;
         case 8:
-            if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "contains", 8)) {
+            if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "contains", 8)) {
                 xpath_func = &xpath_contains;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "position", 8)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "position", 8)) {
                 xpath_func = &xpath_position;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "re-match", 8)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "re-match", 8)) {
                 xpath_func = &xpath_re_match;
             }
             break;
         case 9:
-            if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "substring", 9)) {
+            if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring", 9)) {
                 xpath_func = &xpath_substring;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "translate", 9)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "translate", 9)) {
                 xpath_func = &xpath_translate;
             }
             break;
         case 10:
-            if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "local-name", 10)) {
+            if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "local-name", 10)) {
                 xpath_func = &xpath_local_name;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "enum-value", 10)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "enum-value", 10)) {
                 xpath_func = &xpath_enum_value;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "bit-is-set", 10)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "bit-is-set", 10)) {
                 xpath_func = &xpath_bit_is_set;
             }
             break;
         case 11:
-            if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "starts-with", 11)) {
+            if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "starts-with", 11)) {
                 xpath_func = &xpath_starts_with;
             }
             break;
         case 12:
-            if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "derived-from", 12)) {
+            if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "derived-from", 12)) {
                 xpath_func = &xpath_derived_from;
             }
             break;
         case 13:
-            if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "namespace-uri", 13)) {
+            if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "namespace-uri", 13)) {
                 xpath_func = &xpath_namespace_uri;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "string-length", 13)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "string-length", 13)) {
                 xpath_func = &xpath_string_length;
             }
             break;
         case 15:
-            if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "normalize-space", 15)) {
+            if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "normalize-space", 15)) {
                 xpath_func = &xpath_normalize_space;
-            } else if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "substring-after", 15)) {
+            } else if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring-after", 15)) {
                 xpath_func = &xpath_substring_after;
             }
             break;
         case 16:
-            if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "substring-before", 16)) {
+            if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "substring-before", 16)) {
                 xpath_func = &xpath_substring_before;
             }
             break;
         case 20:
-            if (!strncmp(&exp->expr[exp->tok_pos[*exp_idx]], "derived-from-or-self", 20)) {
+            if (!strncmp(&exp->expr[exp->tok_pos[*tok_idx]], "derived-from-or-self", 20)) {
                 xpath_func = &xpath_derived_from_or_self;
             }
             break;
         }
 
         if (!xpath_func) {
-            LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INFUNC, exp->tok_len[*exp_idx], &exp->expr[exp->tok_pos[*exp_idx]]);
+            LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INFUNC, exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]]);
             return LY_EVALID;
         }
     }
 
     LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-    ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+    ++(*tok_idx);
 
     /* '(' */
-    assert(exp->tokens[*exp_idx] == LYXP_TOKEN_PAR1);
+    assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR1);
     LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-    ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+    ++(*tok_idx);
 
     /* ( Expr ( ',' Expr )* )? */
-    if (exp->tokens[*exp_idx] != LYXP_TOKEN_PAR2) {
+    if (exp->tokens[*tok_idx] != LYXP_TOKEN_PAR2) {
         if (set) {
             args = malloc(sizeof *args);
             LY_CHECK_ERR_GOTO(!args, LOGMEM(set->ctx); rc = LY_EMEM, cleanup);
@@ -7380,17 +7459,17 @@
                 goto cleanup;
             }
 
-            rc = eval_expr_select(exp, exp_idx, 0, args[0], options);
+            rc = eval_expr_select(exp, tok_idx, 0, args[0], options);
             LY_CHECK_GOTO(rc, cleanup);
         } else {
-            rc = eval_expr_select(exp, exp_idx, 0, NULL, options);
+            rc = eval_expr_select(exp, tok_idx, 0, NULL, options);
             LY_CHECK_GOTO(rc, cleanup);
         }
     }
-    while (!lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_COMMA)) {
+    while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_COMMA)) {
         LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-        ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+        ++(*tok_idx);
 
         if (set) {
             ++arg_count;
@@ -7403,19 +7482,19 @@
                 goto cleanup;
             }
 
-            rc = eval_expr_select(exp, exp_idx, 0, args[arg_count - 1], options);
+            rc = eval_expr_select(exp, tok_idx, 0, args[arg_count - 1], options);
             LY_CHECK_GOTO(rc, cleanup);
         } else {
-            rc = eval_expr_select(exp, exp_idx, 0, NULL, options);
+            rc = eval_expr_select(exp, tok_idx, 0, NULL, options);
             LY_CHECK_GOTO(rc, cleanup);
         }
     }
 
     /* ')' */
-    assert(exp->tokens[*exp_idx] == LYXP_TOKEN_PAR2);
+    assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR2);
     LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-    ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+    ++(*tok_idx);
 
     if (set) {
         /* evaluate function */
@@ -7446,28 +7525,28 @@
  *
  * @param[in] ctx Context for errors.
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
  * @return LY_ERR
  */
 static LY_ERR
-eval_number(struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *exp_idx, struct lyxp_set *set)
+eval_number(struct ly_ctx *ctx, struct lyxp_expr *exp, uint16_t *tok_idx, struct lyxp_set *set)
 {
     long double num;
     char *endptr;
 
     if (set) {
         errno = 0;
-        num = strtold(&exp->expr[exp->tok_pos[*exp_idx]], &endptr);
+        num = strtold(&exp->expr[exp->tok_pos[*tok_idx]], &endptr);
         if (errno) {
-            LOGVAL(ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INTOK, "Unknown", &exp->expr[exp->tok_pos[*exp_idx]]);
+            LOGVAL(ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INTOK, "Unknown", &exp->expr[exp->tok_pos[*tok_idx]]);
             LOGVAL(ctx, LY_VLOG_LYD, set->ctx_node, LYVE_XPATH, "Failed to convert \"%.*s\" into a long double (%s).",
-                   exp->tok_len[*exp_idx], &exp->expr[exp->tok_pos[*exp_idx]], strerror(errno));
+                   exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]], strerror(errno));
             return LY_EVALID;
-        } else if (endptr - &exp->expr[exp->tok_pos[*exp_idx]] != exp->tok_len[*exp_idx]) {
-            LOGVAL(ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INTOK, "Unknown", &exp->expr[exp->tok_pos[*exp_idx]]);
+        } else if (endptr - &exp->expr[exp->tok_pos[*tok_idx]] != exp->tok_len[*tok_idx]) {
+            LOGVAL(ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INTOK, "Unknown", &exp->expr[exp->tok_pos[*tok_idx]]);
             LOGVAL(ctx, LY_VLOG_LYD, set->ctx_node, LYVE_XPATH, "Failed to convert \"%.*s\" into a long double.",
-                   exp->tok_len[*exp_idx], &exp->expr[exp->tok_pos[*exp_idx]]);
+                   exp->tok_len[*tok_idx], &exp->expr[exp->tok_pos[*tok_idx]]);
             return LY_EVALID;
         }
 
@@ -7475,8 +7554,8 @@
     }
 
     LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-    ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+    ++(*tok_idx);
     return LY_SUCCESS;
 }
 
@@ -7490,35 +7569,35 @@
  * [10] PrimaryExpr ::= '(' Expr ')' | Literal | Number | FunctionCall
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
  * @param[in] options XPath options.
  * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-eval_path_expr(struct lyxp_expr *exp, uint16_t *exp_idx, struct lyxp_set *set, int options)
+eval_path_expr(struct lyxp_expr *exp, uint16_t *tok_idx, struct lyxp_set *set, int options)
 {
     int all_desc, parent_pos_pred;
     LY_ERR rc;
 
-    switch (exp->tokens[*exp_idx]) {
+    switch (exp->tokens[*tok_idx]) {
     case LYXP_TOKEN_PAR1:
         /* '(' Expr ')' */
 
         /* '(' */
         LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-        ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+        ++(*tok_idx);
 
         /* Expr */
-        rc = eval_expr_select(exp, exp_idx, 0, set, options);
+        rc = eval_expr_select(exp, tok_idx, 0, set, options);
         LY_CHECK_RET(rc);
 
         /* ')' */
-        assert(exp->tokens[*exp_idx] == LYXP_TOKEN_PAR2);
+        assert(exp->tokens[*tok_idx] == LYXP_TOKEN_PAR2);
         LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-        ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+        ++(*tok_idx);
 
         parent_pos_pred = 0;
         goto predicate;
@@ -7529,16 +7608,16 @@
     case LYXP_TOKEN_NAMETEST:
     case LYXP_TOKEN_NODETYPE:
         /* RelativeLocationPath */
-        rc = eval_relative_location_path(exp, exp_idx, 0, set, options);
+        rc = eval_relative_location_path(exp, tok_idx, 0, set, options);
         LY_CHECK_RET(rc);
         break;
 
     case LYXP_TOKEN_FUNCNAME:
         /* FunctionCall */
         if (!set) {
-            rc = eval_function_call(exp, exp_idx, NULL, options);
+            rc = eval_function_call(exp, tok_idx, NULL, options);
         } else {
-            rc = eval_function_call(exp, exp_idx, set, options);
+            rc = eval_function_call(exp, tok_idx, set, options);
         }
         LY_CHECK_RET(rc);
 
@@ -7548,7 +7627,7 @@
     case LYXP_TOKEN_OPER_PATH:
     case LYXP_TOKEN_OPER_RPATH:
         /* AbsoluteLocationPath */
-        rc = eval_absolute_location_path(exp, exp_idx, set, options);
+        rc = eval_absolute_location_path(exp, tok_idx, set, options);
         LY_CHECK_RET(rc);
         break;
 
@@ -7558,9 +7637,9 @@
             if (set) {
                 set_scnode_clear_ctx(set);
             }
-            eval_literal(exp, exp_idx, NULL);
+            eval_literal(exp, tok_idx, NULL);
         } else {
-            eval_literal(exp, exp_idx, set);
+            eval_literal(exp, tok_idx, set);
         }
 
         parent_pos_pred = 1;
@@ -7572,9 +7651,9 @@
             if (set) {
                 set_scnode_clear_ctx(set);
             }
-            rc = eval_number(NULL, exp, exp_idx, NULL);
+            rc = eval_number(NULL, exp, tok_idx, NULL);
         } else {
-            rc = eval_number(set->ctx, exp, exp_idx, set);
+            rc = eval_number(set->ctx, exp, tok_idx, set);
         }
         LY_CHECK_RET(rc);
 
@@ -7582,8 +7661,8 @@
         goto predicate;
 
     default:
-        LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INTOK, lyxp_print_token(exp->tokens[*exp_idx]),
-               &exp->expr[exp->tok_pos[*exp_idx]]);
+        LOGVAL(set->ctx, LY_VLOG_LYD, set->ctx_node, LY_VCODE_XP_INTOK, lyxp_print_token(exp->tokens[*tok_idx]),
+               &exp->expr[exp->tok_pos[*tok_idx]]);
         return LY_EVALID;
     }
 
@@ -7591,26 +7670,26 @@
 
 predicate:
     /* Predicate* */
-    while (!lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_BRACK1)) {
-        rc = eval_predicate(exp, exp_idx, set, options, parent_pos_pred);
+    while (!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_BRACK1)) {
+        rc = eval_predicate(exp, tok_idx, set, options, parent_pos_pred);
         LY_CHECK_RET(rc);
     }
 
     /* ('/' or '//') RelativeLocationPath */
-    if (!exp_check_token2(NULL, exp, *exp_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH)) {
+    if (!exp_check_token2(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_PATH, LYXP_TOKEN_OPER_RPATH)) {
 
         /* evaluate '/' or '//' */
-        if (exp->tokens[*exp_idx] == LYXP_TOKEN_OPER_PATH) {
+        if (exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_PATH) {
             all_desc = 0;
         } else {
             all_desc = 1;
         }
 
         LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-        ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+        ++(*tok_idx);
 
-        rc = eval_relative_location_path(exp, exp_idx, all_desc, set, options);
+        rc = eval_relative_location_path(exp, tok_idx, all_desc, set, options);
         LY_CHECK_RET(rc);
     }
 
@@ -7623,14 +7702,14 @@
  * [20] UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in] repeat How many times this expression is repeated.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
  * @param[in] options XPath options.
  * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-eval_union_expr(struct lyxp_expr *exp, uint16_t *exp_idx, uint16_t repeat, struct lyxp_set *set, int options)
+eval_union_expr(struct lyxp_expr *exp, uint16_t *tok_idx, uint16_t repeat, struct lyxp_set *set, int options)
 {
     LY_ERR rc = LY_SUCCESS;
     struct lyxp_set orig_set, set2;
@@ -7643,24 +7722,24 @@
 
     set_fill_set(&orig_set, set);
 
-    rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_UNION, set, options);
+    rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNION, set, options);
     LY_CHECK_GOTO(rc, cleanup);
 
     /* ('|' PathExpr)* */
     for (i = 0; i < repeat; ++i) {
-        assert(exp->tokens[*exp_idx] == LYXP_TOKEN_OPER_UNI);
+        assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_UNI);
         LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-        ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+        ++(*tok_idx);
 
         if (!set) {
-            rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_UNION, NULL, options);
+            rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNION, NULL, options);
             LY_CHECK_GOTO(rc, cleanup);
             continue;
         }
 
         set_fill_set(&set2, &orig_set);
-        rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_UNION, &set2, options);
+        rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNION, &set2, options);
         LY_CHECK_GOTO(rc, cleanup);
 
         /* eval */
@@ -7684,14 +7763,14 @@
  * [19] UnaryExpr ::= UnionExpr | '-' UnaryExpr
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in] repeat How many times this expression is repeated.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
  * @param[in] options XPath options.
  * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-eval_unary_expr(struct lyxp_expr *exp, uint16_t *exp_idx, uint16_t repeat, struct lyxp_set *set, int options)
+eval_unary_expr(struct lyxp_expr *exp, uint16_t *tok_idx, uint16_t repeat, struct lyxp_set *set, int options)
 {
     LY_ERR rc;
     uint16_t this_op, i;
@@ -7699,16 +7778,16 @@
     assert(repeat);
 
     /* ('-')+ */
-    this_op = *exp_idx;
+    this_op = *tok_idx;
     for (i = 0; i < repeat; ++i) {
-        assert(!lyxp_check_token(NULL, exp, *exp_idx, LYXP_TOKEN_OPER_MATH) && (exp->expr[exp->tok_pos[*exp_idx]] == '-'));
+        assert(!lyxp_check_token(NULL, exp, *tok_idx, LYXP_TOKEN_OPER_MATH) && (exp->expr[exp->tok_pos[*tok_idx]] == '-'));
 
         LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-        ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+        ++(*tok_idx);
     }
 
-    rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_UNARY, set, options);
+    rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_UNARY, set, options);
     LY_CHECK_RET(rc);
 
     if (set && (repeat % 2)) {
@@ -7732,14 +7811,14 @@
  *                     | MultiplicativeExpr 'mod' UnaryExpr
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in] repeat How many times this expression is repeated.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
  * @param[in] options XPath options.
  * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-eval_multiplicative_expr(struct lyxp_expr *exp, uint16_t *exp_idx, uint16_t repeat, struct lyxp_set *set, int options)
+eval_multiplicative_expr(struct lyxp_expr *exp, uint16_t *tok_idx, uint16_t repeat, struct lyxp_set *set, int options)
 {
     LY_ERR rc;
     uint16_t this_op;
@@ -7753,26 +7832,26 @@
 
     set_fill_set(&orig_set, set);
 
-    rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_MULTIPLICATIVE, set, options);
+    rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_MULTIPLICATIVE, set, options);
     LY_CHECK_GOTO(rc, cleanup);
 
     /* ('*' / 'div' / 'mod' UnaryExpr)* */
     for (i = 0; i < repeat; ++i) {
-        this_op = *exp_idx;
+        this_op = *tok_idx;
 
-        assert(exp->tokens[*exp_idx] == LYXP_TOKEN_OPER_MATH);
+        assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_MATH);
         LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-        ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+        ++(*tok_idx);
 
         if (!set) {
-            rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_MULTIPLICATIVE, NULL, options);
+            rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_MULTIPLICATIVE, NULL, options);
             LY_CHECK_GOTO(rc, cleanup);
             continue;
         }
 
         set_fill_set(&set2, &orig_set);
-        rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_MULTIPLICATIVE, &set2, options);
+        rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_MULTIPLICATIVE, &set2, options);
         LY_CHECK_GOTO(rc, cleanup);
 
         /* eval */
@@ -7800,14 +7879,14 @@
  *                     | AdditiveExpr '-' MultiplicativeExpr
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in] repeat How many times this expression is repeated.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
  * @param[in] options XPath options.
  * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-eval_additive_expr(struct lyxp_expr *exp, uint16_t *exp_idx, uint16_t repeat, struct lyxp_set *set, int options)
+eval_additive_expr(struct lyxp_expr *exp, uint16_t *tok_idx, uint16_t repeat, struct lyxp_set *set, int options)
 {
     LY_ERR rc;
     uint16_t this_op;
@@ -7821,26 +7900,26 @@
 
     set_fill_set(&orig_set, set);
 
-    rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_ADDITIVE, set, options);
+    rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_ADDITIVE, set, options);
     LY_CHECK_GOTO(rc, cleanup);
 
     /* ('+' / '-' MultiplicativeExpr)* */
     for (i = 0; i < repeat; ++i) {
-        this_op = *exp_idx;
+        this_op = *tok_idx;
 
-        assert(exp->tokens[*exp_idx] == LYXP_TOKEN_OPER_MATH);
+        assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_MATH);
         LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-        ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+        ++(*tok_idx);
 
         if (!set) {
-            rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_ADDITIVE, NULL, options);
+            rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_ADDITIVE, NULL, options);
             LY_CHECK_GOTO(rc, cleanup);
             continue;
         }
 
         set_fill_set(&set2, &orig_set);
-        rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_ADDITIVE, &set2, options);
+        rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_ADDITIVE, &set2, options);
         LY_CHECK_GOTO(rc, cleanup);
 
         /* eval */
@@ -7870,14 +7949,14 @@
  *                       | RelationalExpr '>=' AdditiveExpr
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in] repeat How many times this expression is repeated.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
  * @param[in] options XPath options.
  * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-eval_relational_expr(struct lyxp_expr *exp, uint16_t *exp_idx, uint16_t repeat, struct lyxp_set *set, int options)
+eval_relational_expr(struct lyxp_expr *exp, uint16_t *tok_idx, uint16_t repeat, struct lyxp_set *set, int options)
 {
     LY_ERR rc;
     uint16_t this_op;
@@ -7891,26 +7970,26 @@
 
     set_fill_set(&orig_set, set);
 
-    rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_RELATIONAL, set, options);
+    rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_RELATIONAL, set, options);
     LY_CHECK_GOTO(rc, cleanup);
 
     /* ('<' / '>' / '<=' / '>=' AdditiveExpr)* */
     for (i = 0; i < repeat; ++i) {
-        this_op = *exp_idx;
+        this_op = *tok_idx;
 
-        assert(exp->tokens[*exp_idx] == LYXP_TOKEN_OPER_COMP);
+        assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_COMP);
         LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-        ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+        ++(*tok_idx);
 
         if (!set) {
-            rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_RELATIONAL, NULL, options);
+            rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_RELATIONAL, NULL, options);
             LY_CHECK_GOTO(rc, cleanup);
             continue;
         }
 
         set_fill_set(&set2, &orig_set);
-        rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_RELATIONAL, &set2, options);
+        rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_RELATIONAL, &set2, options);
         LY_CHECK_GOTO(rc, cleanup);
 
         /* eval */
@@ -7937,14 +8016,14 @@
  *                     | EqualityExpr '!=' RelationalExpr
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in] repeat How many times this expression is repeated.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
  * @param[in] options XPath options.
  * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-eval_equality_expr(struct lyxp_expr *exp, uint16_t *exp_idx, uint16_t repeat, struct lyxp_set *set, int options)
+eval_equality_expr(struct lyxp_expr *exp, uint16_t *tok_idx, uint16_t repeat, struct lyxp_set *set, int options)
 {
     LY_ERR rc;
     uint16_t this_op;
@@ -7958,33 +8037,33 @@
 
     set_fill_set(&orig_set, set);
 
-    rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_EQUALITY, set, options);
+    rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_EQUALITY, set, options);
     LY_CHECK_GOTO(rc, cleanup);
 
     /* ('=' / '!=' RelationalExpr)* */
     for (i = 0; i < repeat; ++i) {
-        this_op = *exp_idx;
+        this_op = *tok_idx;
 
-        assert((exp->tokens[*exp_idx] == LYXP_TOKEN_OPER_EQUAL) || (exp->tokens[*exp_idx] == LYXP_TOKEN_OPER_NEQUAL));
+        assert((exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_EQUAL) || (exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_NEQUAL));
         LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (set ? "parsed" : "skipped"),
-               lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-        ++(*exp_idx);
+               lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+        ++(*tok_idx);
 
         if (!set) {
-            rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_EQUALITY, NULL, options);
+            rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_EQUALITY, NULL, options);
             LY_CHECK_GOTO(rc, cleanup);
             continue;
         }
 
         set_fill_set(&set2, &orig_set);
-        rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_EQUALITY, &set2, options);
+        rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_EQUALITY, &set2, options);
         LY_CHECK_GOTO(rc, cleanup);
 
         /* eval */
         if (options & LYXP_SCNODE_ALL) {
             warn_operands(set->ctx, set, &set2, 0, exp->expr, exp->tok_pos[this_op - 1]);
-            warn_equality_value(exp, set, *exp_idx - 1, this_op - 1, *exp_idx - 1);
-            warn_equality_value(exp, &set2, this_op - 1, this_op - 1, *exp_idx - 1);
+            warn_equality_value(exp, set, *tok_idx - 1, this_op - 1, *tok_idx - 1);
+            warn_equality_value(exp, &set2, this_op - 1, this_op - 1, *tok_idx - 1);
             lyxp_set_scnode_merge(set, &set2);
             set_scnode_clear_ctx(set);
         } else {
@@ -8005,14 +8084,14 @@
  * [14] AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in] repeat How many times this expression is repeated.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
  * @param[in] options XPath options.
  * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-eval_and_expr(struct lyxp_expr *exp, uint16_t *exp_idx, uint16_t repeat, struct lyxp_set *set, int options)
+eval_and_expr(struct lyxp_expr *exp, uint16_t *tok_idx, uint16_t repeat, struct lyxp_set *set, int options)
 {
     LY_ERR rc;
     struct lyxp_set orig_set, set2;
@@ -8025,7 +8104,7 @@
 
     set_fill_set(&orig_set, set);
 
-    rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_AND, set, options);
+    rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_AND, set, options);
     LY_CHECK_GOTO(rc, cleanup);
 
     /* cast to boolean, we know that will be the final result */
@@ -8037,20 +8116,20 @@
 
     /* ('and' EqualityExpr)* */
     for (i = 0; i < repeat; ++i) {
-        assert(exp->tokens[*exp_idx] == LYXP_TOKEN_OPER_LOG);
-        LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (!set || !set->val.bool ? "skipped" : "parsed"),
-            lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-        ++(*exp_idx);
+        assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_LOG);
+        LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (!set || !set->val.bln ? "skipped" : "parsed"),
+            lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+        ++(*tok_idx);
 
         /* lazy evaluation */
-        if (!set || ((set->type == LYXP_SET_BOOLEAN) && !set->val.bool)) {
-            rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_AND, NULL, options);
+        if (!set || ((set->type == LYXP_SET_BOOLEAN) && !set->val.bln)) {
+            rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_AND, NULL, options);
             LY_CHECK_GOTO(rc, cleanup);
             continue;
         }
 
         set_fill_set(&set2, &orig_set);
-        rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_AND, &set2, options);
+        rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_AND, &set2, options);
         LY_CHECK_GOTO(rc, cleanup);
 
         /* eval - just get boolean value actually */
@@ -8075,14 +8154,14 @@
  * [13] OrExpr ::= AndExpr | OrExpr 'or' AndExpr
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in] repeat How many times this expression is repeated.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
  * @param[in] options XPath options.
  * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-eval_or_expr(struct lyxp_expr *exp, uint16_t *exp_idx, uint16_t repeat, struct lyxp_set *set, int options)
+eval_or_expr(struct lyxp_expr *exp, uint16_t *tok_idx, uint16_t repeat, struct lyxp_set *set, int options)
 {
     LY_ERR rc;
     struct lyxp_set orig_set, set2;
@@ -8095,7 +8174,7 @@
 
     set_fill_set(&orig_set, set);
 
-    rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_OR, set, options);
+    rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_OR, set, options);
     LY_CHECK_GOTO(rc, cleanup);
 
     /* cast to boolean, we know that will be the final result */
@@ -8107,14 +8186,14 @@
 
     /* ('or' AndExpr)* */
     for (i = 0; i < repeat; ++i) {
-        assert(exp->tokens[*exp_idx] == LYXP_TOKEN_OPER_LOG);
-        LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (!set || set->val.bool ? "skipped" : "parsed"),
-            lyxp_print_token(exp->tokens[*exp_idx]), exp->tok_pos[*exp_idx]);
-        ++(*exp_idx);
+        assert(exp->tokens[*tok_idx] == LYXP_TOKEN_OPER_LOG);
+        LOGDBG(LY_LDGXPATH, "%-27s %s %s[%u]", __func__, (!set || set->val.bln ? "skipped" : "parsed"),
+            lyxp_print_token(exp->tokens[*tok_idx]), exp->tok_pos[*tok_idx]);
+        ++(*tok_idx);
 
         /* lazy evaluation */
-        if (!set || ((set->type == LYXP_SET_BOOLEAN) && set->val.bool)) {
-            rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_OR, NULL, options);
+        if (!set || ((set->type == LYXP_SET_BOOLEAN) && set->val.bln)) {
+            rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_OR, NULL, options);
             LY_CHECK_GOTO(rc, cleanup);
             continue;
         }
@@ -8122,7 +8201,7 @@
         set_fill_set(&set2, &orig_set);
         /* expr_type cound have been LYXP_EXPR_NONE in all these later calls (except for the first one),
          * but it does not matter */
-        rc = eval_expr_select(exp, exp_idx, LYXP_EXPR_OR, &set2, options);
+        rc = eval_expr_select(exp, tok_idx, LYXP_EXPR_OR, &set2, options);
         LY_CHECK_GOTO(rc, cleanup);
 
         /* eval - just get boolean value actually */
@@ -8142,34 +8221,34 @@
 }
 
 /**
- * @brief Decide what expression is at the pointer @p exp_idx and evaluate it accordingly.
+ * @brief Decide what expression is at the pointer @p tok_idx and evaluate it accordingly.
  *
  * @param[in] exp Parsed XPath expression.
- * @param[in] exp_idx Position in the expression @p exp.
+ * @param[in] tok_idx Position in the expression @p exp.
  * @param[in] etype Expression type to evaluate.
  * @param[in,out] set Context and result set. On NULL the rule is only parsed.
  * @param[in] options XPath options.
  * @return LY_ERR (LY_EINCOMPLETE on unresolved when)
  */
 static LY_ERR
-eval_expr_select(struct lyxp_expr *exp, uint16_t *exp_idx, enum lyxp_expr_type etype, struct lyxp_set *set, int options)
+eval_expr_select(struct lyxp_expr *exp, uint16_t *tok_idx, enum lyxp_expr_type etype, struct lyxp_set *set, int options)
 {
     uint16_t i, count;
     enum lyxp_expr_type next_etype;
     LY_ERR rc;
 
     /* process operator repeats */
-    if (!exp->repeat[*exp_idx]) {
+    if (!exp->repeat[*tok_idx]) {
         next_etype = LYXP_EXPR_NONE;
     } else {
         /* find etype repeat */
-        for (i = 0; exp->repeat[*exp_idx][i] > etype; ++i);
+        for (i = 0; exp->repeat[*tok_idx][i] > etype; ++i);
 
         /* select one-priority lower because etype expression called us */
         if (i) {
-            next_etype = exp->repeat[*exp_idx][i - 1];
+            next_etype = exp->repeat[*tok_idx][i - 1];
             /* count repeats for that expression */
-            for (count = 0; i && exp->repeat[*exp_idx][i - 1] == next_etype; ++count, --i);
+            for (count = 0; i && exp->repeat[*tok_idx][i - 1] == next_etype; ++count, --i);
         } else {
             next_etype = LYXP_EXPR_NONE;
         }
@@ -8178,31 +8257,31 @@
     /* decide what expression are we parsing based on the repeat */
     switch (next_etype) {
     case LYXP_EXPR_OR:
-        rc = eval_or_expr(exp, exp_idx, count, set, options);
+        rc = eval_or_expr(exp, tok_idx, count, set, options);
         break;
     case LYXP_EXPR_AND:
-        rc = eval_and_expr(exp, exp_idx, count, set, options);
+        rc = eval_and_expr(exp, tok_idx, count, set, options);
         break;
     case LYXP_EXPR_EQUALITY:
-        rc = eval_equality_expr(exp, exp_idx, count, set, options);
+        rc = eval_equality_expr(exp, tok_idx, count, set, options);
         break;
     case LYXP_EXPR_RELATIONAL:
-        rc = eval_relational_expr(exp, exp_idx, count, set, options);
+        rc = eval_relational_expr(exp, tok_idx, count, set, options);
         break;
     case LYXP_EXPR_ADDITIVE:
-        rc = eval_additive_expr(exp, exp_idx, count, set, options);
+        rc = eval_additive_expr(exp, tok_idx, count, set, options);
         break;
     case LYXP_EXPR_MULTIPLICATIVE:
-        rc = eval_multiplicative_expr(exp, exp_idx, count, set, options);
+        rc = eval_multiplicative_expr(exp, tok_idx, count, set, options);
         break;
     case LYXP_EXPR_UNARY:
-        rc = eval_unary_expr(exp, exp_idx, count, set, options);
+        rc = eval_unary_expr(exp, tok_idx, count, set, options);
         break;
     case LYXP_EXPR_UNION:
-        rc = eval_union_expr(exp, exp_idx, count, set, options);
+        rc = eval_union_expr(exp, tok_idx, count, set, options);
         break;
     case LYXP_EXPR_NONE:
-        rc = eval_path_expr(exp, exp_idx, set, options);
+        rc = eval_path_expr(exp, tok_idx, set, options);
         break;
     default:
         LOGINT_RET(set->ctx);
@@ -8246,25 +8325,33 @@
 lyxp_eval(struct lyxp_expr *exp, LYD_FORMAT format, const struct lys_module *local_mod, const struct lyd_node *ctx_node,
           enum lyxp_node_type ctx_node_type, const struct lyd_node *tree, struct lyxp_set *set, int options)
 {
-    uint16_t exp_idx = 0;
+    uint16_t tok_idx = 0;
+    const struct lyd_node *real_ctx_node;
     LY_ERR rc;
 
-    LY_CHECK_ARG_RET(NULL, exp, local_mod, set, LY_EINVAL);
+    LY_CHECK_ARG_RET(NULL, exp, local_mod, ctx_node, set, LY_EINVAL);
+
+    if ((ctx_node_type == LYXP_NODE_ROOT) || (ctx_node_type == LYXP_NODE_ROOT_CONFIG)) {
+        /* we always need some context node because it is used for resolving unqualified names */
+        real_ctx_node = NULL;
+    } else {
+        real_ctx_node = ctx_node;
+    }
 
     /* prepare set for evaluation */
-    exp_idx = 0;
+    tok_idx = 0;
     memset(set, 0, sizeof *set);
     set->type = LYXP_SET_NODE_SET;
-    set_insert_node(set, (struct lyd_node *)ctx_node, 0, ctx_node_type, 0);
+    set_insert_node(set, (struct lyd_node *)real_ctx_node, 0, ctx_node_type, 0);
     set->ctx = local_mod->ctx;
     set->ctx_node = ctx_node;
-    set->root_type = lyxp_get_root_type(ctx_node, NULL, options);
+    set->root_type = lyxp_get_root_type(real_ctx_node, NULL, options);
     set->local_mod = local_mod;
     set->tree = tree;
     set->format = format;
 
     /* evaluate */
-    rc = eval_expr_select(exp, &exp_idx, 0, set, options);
+    rc = eval_expr_select(exp, &tok_idx, 0, set, options);
     if (rc != LY_SUCCESS) {
         lyxp_set_free_content(set);
     }
@@ -8418,7 +8505,7 @@
             }
             break;
         case LYXP_SET_BOOLEAN:
-            if (set->val.bool) {
+            if (set->val.bln) {
                 set->val.str = strdup("true");
             } else {
                 set->val.str = strdup("false");
@@ -8451,7 +8538,7 @@
             set->val.num = num;
             break;
         case LYXP_SET_BOOLEAN:
-            if (set->val.bool) {
+            if (set->val.bln) {
                 set->val.num = 1;
             } else {
                 set->val.num = 0;
@@ -8468,27 +8555,27 @@
         switch (set->type) {
         case LYXP_SET_NUMBER:
             if ((set->val.num == 0) || (set->val.num == -0.0f) || isnan(set->val.num)) {
-                set->val.bool = 0;
+                set->val.bln = 0;
             } else {
-                set->val.bool = 1;
+                set->val.bln = 1;
             }
             break;
         case LYXP_SET_STRING:
             if (set->val.str[0]) {
                 lyxp_set_free_content(set);
-                set->val.bool = 1;
+                set->val.bln = 1;
             } else {
                 lyxp_set_free_content(set);
-                set->val.bool = 0;
+                set->val.bln = 0;
             }
             break;
         case LYXP_SET_NODE_SET:
             if (set->used) {
                 lyxp_set_free_content(set);
-                set->val.bool = 1;
+                set->val.bln = 1;
             } else {
                 lyxp_set_free_content(set);
-                set->val.bool = 0;
+                set->val.bln = 0;
             }
             break;
         default:
@@ -8505,24 +8592,31 @@
              enum lyxp_node_type ctx_scnode_type, struct lyxp_set *set, int options)
 {
     struct ly_ctx *ctx;
-    uint16_t exp_idx = 0;
+    const struct lysc_node *real_ctx_scnode;
+    uint16_t tok_idx = 0;
 
-    LY_CHECK_ARG_RET(NULL, exp, local_mod, set, LY_EINVAL);
+    LY_CHECK_ARG_RET(NULL, exp, local_mod, ctx_scnode, set, LY_EINVAL);
 
     ctx = local_mod->ctx;
+    if ((ctx_scnode_type == LYXP_NODE_ROOT) || (ctx_scnode_type == LYXP_NODE_ROOT_CONFIG)) {
+        /* we always need some context node because it is used for resolving unqualified names */
+        real_ctx_scnode = NULL;
+    } else {
+        real_ctx_scnode = ctx_scnode;
+    }
 
     /* prepare set for evaluation */
-    exp_idx = 0;
+    tok_idx = 0;
     memset(set, 0, sizeof *set);
     set->type = LYXP_SET_SCNODE_SET;
-    lyxp_set_scnode_insert_node(set, ctx_scnode, ctx_scnode_type);
+    lyxp_set_scnode_insert_node(set, real_ctx_scnode, ctx_scnode_type);
     set->val.scnodes[0].in_ctx = -2;
     set->ctx = ctx;
     set->ctx_scnode = ctx_scnode;
-    set->root_type = lyxp_get_root_type(NULL, ctx_scnode, options);
+    set->root_type = lyxp_get_root_type(NULL, real_ctx_scnode, options);
     set->local_mod = local_mod;
     set->format = format;
 
     /* evaluate */
-    return eval_expr_select(exp, &exp_idx, 0, set, options);
+    return eval_expr_select(exp, &tok_idx, 0, set, options);
 }