xpath CHANGE refactor lyxp_expr_parse() to return LY_ERR

To correctly inherit LY_ERR value and detect errors, it is necessary to
refactor lyxp_expr_parse() to return LY_ERR value and return the
processed expression via an output parameter.
diff --git a/src/context.c b/src/context.c
index 876240c..08ae49f 100644
--- a/src/context.c
+++ b/src/context.c
@@ -546,8 +546,8 @@
     }
 
     /* parse */
-    exp = lyxp_expr_parse(ctx, data_path, strlen(data_path), 0);
-    LY_CHECK_GOTO(!exp, cleanup);
+    ret = lyxp_expr_parse(ctx, data_path, strlen(data_path), 0, &exp);
+    LY_CHECK_GOTO(ret, cleanup);
 
     /* compile */
     oper = output ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT;
diff --git a/src/path.c b/src/path.c
index bbd9ac5..03d735a 100644
--- a/src/path.c
+++ b/src/path.c
@@ -228,7 +228,8 @@
 ly_path_parse(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *str_path, size_t path_len,
         uint8_t begin, uint8_t lref, uint8_t prefix, uint8_t pred, struct lyxp_expr **expr)
 {
-    struct lyxp_expr *exp;
+    LY_ERR ret = LY_SUCCESS;
+    struct lyxp_expr *exp = NULL;
     uint16_t tok_idx, cur_len;
     const char *cur_node, *prev_prefix = NULL, *ptr;
 
@@ -239,8 +240,7 @@
     assert((pred == LY_PATH_PRED_KEYS) || (pred == LY_PATH_PRED_SIMPLE) || (pred == LY_PATH_PRED_LEAFREF));
 
     /* parse as a generic XPath expression */
-    exp = lyxp_expr_parse(ctx, str_path, path_len, 1);
-    LY_CHECK_GOTO(!exp, error);
+    LY_CHECK_GOTO(ret = lyxp_expr_parse(ctx, str_path, path_len, 1, &exp), error);
     tok_idx = 0;
 
     if (begin == LY_PATH_BEGIN_EITHER) {
@@ -249,17 +249,17 @@
             /* '..' */
             while ((lref == LY_PATH_LREF_TRUE) && !lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_DDOT)) {
                 /* '/' */
-                LY_CHECK_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), error);
+                LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID, error);
             }
         }
     } else {
         /* '/' */
-        LY_CHECK_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), error);
+        LY_CHECK_ERR_GOTO(lyxp_next_token(ctx, exp, &tok_idx, LYXP_TOKEN_OPER_PATH), ret = LY_EVALID, error);
     }
 
     do {
         /* NameTest */
-        LY_CHECK_GOTO(lyxp_check_token(ctx, exp, tok_idx, LYXP_TOKEN_NAMETEST), error);
+        LY_CHECK_ERR_GOTO(lyxp_check_token(ctx, exp, tok_idx, LYXP_TOKEN_NAMETEST), ret = LY_EVALID, error);
 
         /* check prefix based on the options */
         cur_node = exp->expr + exp->tok_pos[tok_idx];
@@ -267,6 +267,7 @@
         if (prefix == LY_PATH_PREFIX_MANDATORY) {
             if (!strnstr(cur_node, ":", cur_len)) {
                 LOGVAL_P(ctx, ctx_node, LYVE_XPATH, "Prefix missing for \"%.*s\" in path.", cur_len, cur_node);
+                ret = LY_EVALID;
                 goto error;
             }
         } else if (prefix == LY_PATH_PREFIX_STRICT_INHERIT) {
@@ -274,6 +275,7 @@
                 /* the first node must have a prefix */
                 if (!strnstr(cur_node, ":", cur_len)) {
                     LOGVAL_P(ctx, ctx_node, LYVE_XPATH, "Prefix missing for \"%.*s\" in path.", cur_len, cur_node);
+                    ret = LY_EVALID;
                     goto error;
                 }
 
@@ -285,6 +287,7 @@
                 if (ptr) {
                     if (!strncmp(prev_prefix, cur_node, ptr - cur_node) && (prev_prefix[ptr - cur_node] == ':')) {
                         LOGVAL_P(ctx, ctx_node, LYVE_XPATH, "Duplicate prefix for \"%.*s\" in path.", cur_len, cur_node);
+                        ret = LY_EVALID;
                         goto error;
                     }
 
@@ -297,7 +300,7 @@
         ++tok_idx;
 
         /* Predicate* */
-        LY_CHECK_GOTO(ly_path_check_predicate(ctx, ctx_node, exp, &tok_idx, prefix, pred), error);
+        LY_CHECK_GOTO(ret = ly_path_check_predicate(ctx, ctx_node, exp, &tok_idx, prefix, pred), error);
 
         /* '/' */
     } while (!lyxp_next_token(NULL, exp, &tok_idx, LYXP_TOKEN_OPER_PATH));
@@ -306,6 +309,7 @@
     if (exp->used > tok_idx) {
         LOGVAL_P(ctx, ctx_node, LYVE_XPATH, "Unparsed characters \"%s\" left at the end of path.",
                  exp->expr + exp->tok_pos[tok_idx]);
+        ret = LY_EVALID;
         goto error;
     }
 
@@ -314,30 +318,31 @@
 
 error:
     lyxp_expr_free(ctx, exp);
-    return LY_EVALID;
+    return ret;
 }
 
 LY_ERR
 ly_path_parse_predicate(const struct ly_ctx *ctx, const struct lysc_node *cur_node, const char *str_path,
         size_t path_len, uint8_t prefix, uint8_t pred, struct lyxp_expr **expr)
 {
-    struct lyxp_expr *exp;
+    LY_ERR ret = LY_SUCCESS;
+    struct lyxp_expr *exp = NULL;
     uint16_t tok_idx;
 
     assert((prefix == LY_PATH_PREFIX_OPTIONAL) || (prefix == LY_PATH_PREFIX_MANDATORY));
     assert((pred == LY_PATH_PRED_KEYS) || (pred == LY_PATH_PRED_SIMPLE) || (pred == LY_PATH_PRED_LEAFREF));
 
     /* parse as a generic XPath expression */
-    exp = lyxp_expr_parse(ctx, str_path, path_len, 0);
-    LY_CHECK_GOTO(!exp, error);
+    LY_CHECK_GOTO(ret = lyxp_expr_parse(ctx, str_path, path_len, 0, &exp), error);
     tok_idx = 0;
 
-    LY_CHECK_GOTO(ly_path_check_predicate(ctx, cur_node, exp, &tok_idx, prefix, pred), error);
+    LY_CHECK_GOTO(ret = ly_path_check_predicate(ctx, cur_node, exp, &tok_idx, prefix, pred), error);
 
     /* trailing token check */
     if (exp->used > tok_idx) {
         LOGVAL_P(ctx, cur_node, LYVE_XPATH, "Unparsed characters \"%s\" left at the end of predicate.",
                  exp->expr + exp->tok_pos[tok_idx]);
+        ret = LY_EVALID;
         goto error;
     }
 
@@ -346,7 +351,7 @@
 
 error:
     lyxp_expr_free(ctx, exp);
-    return LY_EVALID;
+    return ret;
 }
 
 /**
diff --git a/src/tree_data.c b/src/tree_data.c
index 1b59493..e68314d 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -3341,7 +3341,7 @@
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyxp_set xp_set;
-    struct lyxp_expr *exp;
+    struct lyxp_expr *exp = NULL;
     uint32_t i;
 
     LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
@@ -3349,8 +3349,8 @@
     memset(&xp_set, 0, sizeof xp_set);
 
     /* compile expression */
-    exp = lyxp_expr_parse((struct ly_ctx *)LYD_CTX(ctx_node), xpath, 0, 1);
-    LY_CHECK_ERR_GOTO(!exp, ret = LY_EINVAL, cleanup);
+    ret = lyxp_expr_parse((struct ly_ctx *)LYD_CTX(ctx_node), xpath, 0, 1, &exp);
+    LY_CHECK_GOTO(ret, cleanup);
 
     /* evaluate expression */
     ret = lyxp_eval(exp, LY_PREF_JSON, ctx_node->schema->module, ctx_node, LYXP_NODE_ELEM, ctx_node, &xp_set, 0);
diff --git a/src/tree_schema.c b/src/tree_schema.c
index fb77d58..43597db 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -301,7 +301,7 @@
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyxp_set xp_set;
-    struct lyxp_expr *exp;
+    struct lyxp_expr *exp = NULL;
     uint32_t i;
 
     LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
@@ -312,8 +312,8 @@
     memset(&xp_set, 0, sizeof xp_set);
 
     /* compile expression */
-    exp = lyxp_expr_parse(ctx_node->module->ctx, xpath, 0, 1);
-    LY_CHECK_ERR_GOTO(!exp, ret = LY_EINVAL, cleanup);
+    ret = lyxp_expr_parse(ctx_node->module->ctx, xpath, 0, 1, &exp);
+    LY_CHECK_GOTO(ret, cleanup);
 
     /* atomize expression */
     ret = lyxp_atomize(exp, LY_PREF_JSON, ctx_node->module, ctx_node, LYXP_NODE_ELEM, &xp_set, options);
@@ -346,7 +346,7 @@
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyxp_set xp_set;
-    struct lyxp_expr *exp;
+    struct lyxp_expr *exp = NULL;
     uint32_t i;
 
     LY_CHECK_ARG_RET(NULL, ctx_node, xpath, set, LY_EINVAL);
@@ -357,8 +357,8 @@
     memset(&xp_set, 0, sizeof xp_set);
 
     /* compile expression */
-    exp = lyxp_expr_parse(ctx_node->module->ctx, xpath, 0, 1);
-    LY_CHECK_ERR_GOTO(!exp, ret = LY_EINVAL, cleanup);
+    ret = lyxp_expr_parse(ctx_node->module->ctx, xpath, 0, 1, &exp);
+    LY_CHECK_GOTO(ret, cleanup);
 
     /* atomize expression */
     ret = lyxp_atomize(exp, LY_PREF_JSON, ctx_node->module, ctx_node, LYXP_NODE_ELEM, &xp_set, options);
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 61edac2..720351a 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -909,13 +909,13 @@
     LY_ERR ret = LY_SUCCESS;
 
     *when = calloc(1, sizeof **when);
+    LY_CHECK_ERR_RET(!(*when), LOGMEM(ctx->ctx), LY_EMEM);
     (*when)->refcount = 1;
-    (*when)->cond = lyxp_expr_parse(ctx->ctx, when_p->cond, 0, 1);
+    LY_CHECK_RET(lyxp_expr_parse(ctx->ctx, when_p->cond, 0, 1, &(*when)->cond));
     (*when)->module = ctx->mod_def;
     (*when)->context = lysc_xpath_context(node);
     DUP_STRING_GOTO(ctx->ctx, when_p->dsc, (*when)->dsc, ret, done);
     DUP_STRING_GOTO(ctx->ctx, when_p->ref, (*when)->ref, ret, done);
-    LY_CHECK_ERR_GOTO(!(*when)->cond, ret = ly_errcode(ctx->ctx), done);
     COMPILE_EXTS_GOTO(ctx, when_p->exts, (*when)->exts, (*when), LYEXT_PAR_WHEN, ret, done);
     (*when)->flags = flags & LYS_STATUS_MASK;
 
@@ -935,8 +935,7 @@
 {
     LY_ERR ret = LY_SUCCESS;
 
-    must->cond = lyxp_expr_parse(ctx->ctx, must_p->arg, 0, 1);
-    LY_CHECK_ERR_GOTO(!must->cond, ret = ly_errcode(ctx->ctx), done);
+    LY_CHECK_RET(lyxp_expr_parse(ctx->ctx, must_p->arg, 0, 1, &must->cond));
     must->module = ctx->mod_def;
     DUP_STRING_GOTO(ctx->ctx, must_p->eapptag, must->eapptag, ret, done);
     DUP_STRING_GOTO(ctx->ctx, must_p->emsg, must->emsg, ret, done);
diff --git a/src/xpath.c b/src/xpath.c
index bc23eae..4691984 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -2653,146 +2653,150 @@
     free(expr);
 }
 
-struct lyxp_expr *
-lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr, size_t expr_len, ly_bool reparse)
+LY_ERR
+lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len, ly_bool reparse, struct lyxp_expr **expr_p)
 {
-    struct lyxp_expr *ret;
+    LY_ERR ret = LY_SUCCESS;
+    struct lyxp_expr *expr;
     size_t parsed = 0, tok_len;
     enum lyxp_token tok_type;
     ly_bool prev_function_check = 0;
     uint16_t tok_idx = 0;
 
-    if (!expr[0]) {
+    assert(expr_p);
+
+    if (!expr_str[0]) {
         LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOF);
-        return NULL;
+        return LY_EVALID;
     }
 
     if (!expr_len) {
-        expr_len = strlen(expr);
+        expr_len = strlen(expr_str);
     }
     if (expr_len > UINT16_MAX) {
-        LOGERR(ctx, LY_EINVAL, "XPath expression cannot be longer than %ud characters.", UINT16_MAX);
-        return NULL;
+        LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "XPath expression cannot be longer than %ud characters.", UINT16_MAX);
+        return LY_EVALID;
     }
 
     /* init lyxp_expr structure */
-    ret = calloc(1, sizeof *ret);
-    LY_CHECK_ERR_GOTO(!ret, LOGMEM(ctx), error);
-    LY_CHECK_GOTO(lydict_insert(ctx, expr, expr_len, &ret->expr), error);
-    LY_CHECK_ERR_GOTO(!ret->expr, LOGMEM(ctx), error);
-    ret->used = 0;
-    ret->size = LYXP_EXPR_SIZE_START;
-    ret->tokens = malloc(ret->size * sizeof *ret->tokens);
-    LY_CHECK_ERR_GOTO(!ret->tokens, LOGMEM(ctx), error);
+    expr = calloc(1, sizeof *expr);
+    LY_CHECK_ERR_GOTO(!expr, LOGMEM(ctx); ret = LY_EMEM, error);
+    LY_CHECK_GOTO(ret = lydict_insert(ctx, expr_str, expr_len, &expr->expr), error);
+    expr->used = 0;
+    expr->size = LYXP_EXPR_SIZE_START;
+    expr->tokens = malloc(expr->size * sizeof *expr->tokens);
+    LY_CHECK_ERR_GOTO(!expr->tokens, LOGMEM(ctx); ret = LY_EMEM, error);
 
-    ret->tok_pos = malloc(ret->size * sizeof *ret->tok_pos);
-    LY_CHECK_ERR_GOTO(!ret->tok_pos, LOGMEM(ctx), error);
+    expr->tok_pos = malloc(expr->size * sizeof *expr->tok_pos);
+    LY_CHECK_ERR_GOTO(!expr->tok_pos, LOGMEM(ctx); ret = LY_EMEM, error);
 
-    ret->tok_len = malloc(ret->size * sizeof *ret->tok_len);
-    LY_CHECK_ERR_GOTO(!ret->tok_len, LOGMEM(ctx), error);
+    expr->tok_len = malloc(expr->size * sizeof *expr->tok_len);
+    LY_CHECK_ERR_GOTO(!expr->tok_len, LOGMEM(ctx); ret = LY_EMEM, error);
 
     /* make expr 0-terminated */
-    expr = ret->expr;
+    expr_str = expr->expr;
 
-    while (is_xmlws(expr[parsed])) {
+    while (is_xmlws(expr_str[parsed])) {
         ++parsed;
     }
 
     do {
-        if (expr[parsed] == '(') {
+        if (expr_str[parsed] == '(') {
 
             /* '(' */
             tok_len = 1;
             tok_type = LYXP_TOKEN_PAR1;
 
-            if (prev_function_check && ret->used && (ret->tokens[ret->used - 1] == LYXP_TOKEN_NAMETEST)) {
+            if (prev_function_check && expr->used && (expr->tokens[expr->used - 1] == LYXP_TOKEN_NAMETEST)) {
                 /* it is a NodeType/FunctionName after all */
-                if (((ret->tok_len[ret->used - 1] == 4)
-                        && (!strncmp(&expr[ret->tok_pos[ret->used - 1]], "node", 4)
-                        || !strncmp(&expr[ret->tok_pos[ret->used - 1]], "text", 4))) ||
-                        ((ret->tok_len[ret->used - 1] == 7)
-                        && !strncmp(&expr[ret->tok_pos[ret->used - 1]], "comment", 7))) {
-                    ret->tokens[ret->used - 1] = LYXP_TOKEN_NODETYPE;
+                if (((expr->tok_len[expr->used - 1] == 4)
+                        && (!strncmp(&expr_str[expr->tok_pos[expr->used - 1]], "node", 4)
+                        || !strncmp(&expr_str[expr->tok_pos[expr->used - 1]], "text", 4))) ||
+                        ((expr->tok_len[expr->used - 1] == 7)
+                        && !strncmp(&expr_str[expr->tok_pos[expr->used - 1]], "comment", 7))) {
+                    expr->tokens[expr->used - 1] = LYXP_TOKEN_NODETYPE;
                 } else {
-                    ret->tokens[ret->used - 1] = LYXP_TOKEN_FUNCNAME;
+                    expr->tokens[expr->used - 1] = LYXP_TOKEN_FUNCNAME;
                 }
                 prev_function_check = 0;
             }
 
-        } else if (expr[parsed] == ')') {
+        } else if (expr_str[parsed] == ')') {
 
             /* ')' */
             tok_len = 1;
             tok_type = LYXP_TOKEN_PAR2;
 
-        } else if (expr[parsed] == '[') {
+        } else if (expr_str[parsed] == '[') {
 
             /* '[' */
             tok_len = 1;
             tok_type = LYXP_TOKEN_BRACK1;
 
-        } else if (expr[parsed] == ']') {
+        } else if (expr_str[parsed] == ']') {
 
             /* ']' */
             tok_len = 1;
             tok_type = LYXP_TOKEN_BRACK2;
 
-        } else if (!strncmp(&expr[parsed], "..", 2)) {
+        } else if (!strncmp(&expr_str[parsed], "..", 2)) {
 
             /* '..' */
             tok_len = 2;
             tok_type = LYXP_TOKEN_DDOT;
 
-        } else if ((expr[parsed] == '.') && (!isdigit(expr[parsed + 1]))) {
+        } else if ((expr_str[parsed] == '.') && (!isdigit(expr_str[parsed + 1]))) {
 
             /* '.' */
             tok_len = 1;
             tok_type = LYXP_TOKEN_DOT;
 
-        } else if (expr[parsed] == '@') {
+        } else if (expr_str[parsed] == '@') {
 
             /* '@' */
             tok_len = 1;
             tok_type = LYXP_TOKEN_AT;
 
-        } else if (expr[parsed] == ',') {
+        } else if (expr_str[parsed] == ',') {
 
             /* ',' */
             tok_len = 1;
             tok_type = LYXP_TOKEN_COMMA;
 
-        } else if (expr[parsed] == '\'') {
+        } else if (expr_str[parsed] == '\'') {
 
             /* Literal with ' */
-            for (tok_len = 1; (expr[parsed + tok_len] != '\0') && (expr[parsed + tok_len] != '\''); ++tok_len) {}
-            LY_CHECK_ERR_GOTO(expr[parsed + tok_len] == '\0',
-                              LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOE, expr[parsed], &expr[parsed]), error);
+            for (tok_len = 1; (expr_str[parsed + tok_len] != '\0') && (expr_str[parsed + tok_len] != '\''); ++tok_len) {}
+            LY_CHECK_ERR_GOTO(expr_str[parsed + tok_len] == '\0',
+                              LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOE, expr_str[parsed], &expr_str[parsed]); ret = LY_EVALID,
+                              error);
             ++tok_len;
             tok_type = LYXP_TOKEN_LITERAL;
 
-        } else if (expr[parsed] == '\"') {
+        } else if (expr_str[parsed] == '\"') {
 
             /* Literal with " */
-            for (tok_len = 1; (expr[parsed + tok_len] != '\0') && (expr[parsed + tok_len] != '\"'); ++tok_len) {}
-            LY_CHECK_ERR_GOTO(expr[parsed + tok_len] == '\0',
-                              LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOE, expr[parsed], &expr[parsed]), error);
+            for (tok_len = 1; (expr_str[parsed + tok_len] != '\0') && (expr_str[parsed + tok_len] != '\"'); ++tok_len) {}
+            LY_CHECK_ERR_GOTO(expr_str[parsed + tok_len] == '\0',
+                              LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_EOE, expr_str[parsed], &expr_str[parsed]); ret = LY_EVALID,
+                              error);
             ++tok_len;
             tok_type = LYXP_TOKEN_LITERAL;
 
-        } else if ((expr[parsed] == '.') || (isdigit(expr[parsed]))) {
+        } else if ((expr_str[parsed] == '.') || (isdigit(expr_str[parsed]))) {
 
             /* Number */
-            for (tok_len = 0; isdigit(expr[parsed + tok_len]); ++tok_len) {}
-            if (expr[parsed + tok_len] == '.') {
+            for (tok_len = 0; isdigit(expr_str[parsed + tok_len]); ++tok_len) {}
+            if (expr_str[parsed + tok_len] == '.') {
                 ++tok_len;
-                for ( ; isdigit(expr[parsed + tok_len]); ++tok_len) {}
+                for ( ; isdigit(expr_str[parsed + tok_len]); ++tok_len) {}
             }
             tok_type = LYXP_TOKEN_NUMBER;
 
-        } else if (expr[parsed] == '/') {
+        } else if (expr_str[parsed] == '/') {
 
             /* Operator '/', '//' */
-            if (!strncmp(&expr[parsed], "//", 2)) {
+            if (!strncmp(&expr_str[parsed], "//", 2)) {
                 tok_len = 2;
                 tok_type = LYXP_TOKEN_OPER_RPATH;
             } else {
@@ -2800,81 +2804,83 @@
                 tok_type = LYXP_TOKEN_OPER_PATH;
             }
 
-        } else if (!strncmp(&expr[parsed], "!=", 2)) {
+        } else if (!strncmp(&expr_str[parsed], "!=", 2)) {
 
             /* Operator '!=' */
             tok_len = 2;
             tok_type = LYXP_TOKEN_OPER_NEQUAL;
 
-        } else if (!strncmp(&expr[parsed], "<=", 2) || !strncmp(&expr[parsed], ">=", 2)) {
+        } else if (!strncmp(&expr_str[parsed], "<=", 2) || !strncmp(&expr_str[parsed], ">=", 2)) {
 
             /* Operator '<=', '>=' */
             tok_len = 2;
             tok_type = LYXP_TOKEN_OPER_COMP;
 
-        } else if (expr[parsed] == '|') {
+        } else if (expr_str[parsed] == '|') {
 
             /* Operator '|' */
             tok_len = 1;
             tok_type = LYXP_TOKEN_OPER_UNI;
 
-        } else if ((expr[parsed] == '+') || (expr[parsed] == '-')) {
+        } else if ((expr_str[parsed] == '+') || (expr_str[parsed] == '-')) {
 
             /* Operator '+', '-' */
             tok_len = 1;
             tok_type = LYXP_TOKEN_OPER_MATH;
 
-        } else if (expr[parsed] == '=') {
+        } else if (expr_str[parsed] == '=') {
 
             /* Operator '=' */
             tok_len = 1;
             tok_type = LYXP_TOKEN_OPER_EQUAL;
 
-        } else if ((expr[parsed] == '<') || (expr[parsed] == '>')) {
+        } else if ((expr_str[parsed] == '<') || (expr_str[parsed] == '>')) {
 
             /* Operator '<', '>' */
             tok_len = 1;
             tok_type = LYXP_TOKEN_OPER_COMP;
 
-        } else if (ret->used && (ret->tokens[ret->used - 1] != LYXP_TOKEN_AT)
-                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_PAR1)
-                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_BRACK1)
-                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_COMMA)
-                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPER_LOG)
-                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPER_EQUAL)
-                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPER_NEQUAL)
-                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPER_COMP)
-                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPER_MATH)
-                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPER_UNI)
-                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPER_PATH)
-                && (ret->tokens[ret->used - 1] != LYXP_TOKEN_OPER_RPATH)) {
+        } else if (expr->used && (expr->tokens[expr->used - 1] != LYXP_TOKEN_AT)
+                && (expr->tokens[expr->used - 1] != LYXP_TOKEN_PAR1)
+                && (expr->tokens[expr->used - 1] != LYXP_TOKEN_BRACK1)
+                && (expr->tokens[expr->used - 1] != LYXP_TOKEN_COMMA)
+                && (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_LOG)
+                && (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_EQUAL)
+                && (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_NEQUAL)
+                && (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_COMP)
+                && (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_MATH)
+                && (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_UNI)
+                && (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_PATH)
+                && (expr->tokens[expr->used - 1] != LYXP_TOKEN_OPER_RPATH)) {
 
             /* Operator '*', 'or', 'and', 'mod', or 'div' */
-            if (expr[parsed] == '*') {
+            if (expr_str[parsed] == '*') {
                 tok_len = 1;
                 tok_type = LYXP_TOKEN_OPER_MATH;
 
-            } else if (!strncmp(&expr[parsed], "or", 2)) {
+            } else if (!strncmp(&expr_str[parsed], "or", 2)) {
                 tok_len = 2;
                 tok_type = LYXP_TOKEN_OPER_LOG;
 
-            } else if (!strncmp(&expr[parsed], "and", 3)) {
+            } else if (!strncmp(&expr_str[parsed], "and", 3)) {
                 tok_len = 3;
                 tok_type = LYXP_TOKEN_OPER_LOG;
 
-            } else if (!strncmp(&expr[parsed], "mod", 3) || !strncmp(&expr[parsed], "div", 3)) {
+            } else if (!strncmp(&expr_str[parsed], "mod", 3) || !strncmp(&expr_str[parsed], "div", 3)) {
                 tok_len = 3;
                 tok_type = LYXP_TOKEN_OPER_MATH;
 
             } else if (prev_function_check) {
                 LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_XPATH, "Invalid character 0x%x ('%c'), perhaps \"%.*s\" is supposed to be a function call.",
-                       expr[parsed], expr[parsed], ret->tok_len[ret->used - 1], &ret->expr[ret->tok_pos[ret->used - 1]]);
+                       expr_str[parsed], expr_str[parsed], expr->tok_len[expr->used - 1], &expr->expr[expr->tok_pos[expr->used - 1]]);
+                ret = LY_EVALID;
                 goto error;
             } else {
-                LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INEXPR, parsed + 1, expr);
+                LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INEXPR, parsed + 1, expr_str);
+                ret = LY_EVALID;
                 goto error;
             }
-        } else if (expr[parsed] == '*') {
+        } else if (expr_str[parsed] == '*') {
 
             /* NameTest '*' */
             tok_len = 1;
@@ -2883,17 +2889,21 @@
         } else {
 
             /* NameTest (NCName ':' '*' | QName) or NodeType/FunctionName */
-            long int ncname_len = parse_ncname(&expr[parsed]);
-            LY_CHECK_ERR_GOTO(ncname_len < 0, LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INEXPR, parsed - ncname_len + 1, expr), error);
+            long int ncname_len = parse_ncname(&expr_str[parsed]);
+            LY_CHECK_ERR_GOTO(ncname_len < 0,
+                              LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INEXPR, parsed - ncname_len + 1, expr_str); ret = LY_EVALID,
+                              error);
             tok_len = ncname_len;
 
-            if (expr[parsed + tok_len] == ':') {
+            if (expr_str[parsed + tok_len] == ':') {
                 ++tok_len;
-                if (expr[parsed + tok_len] == '*') {
+                if (expr_str[parsed + tok_len] == '*') {
                     ++tok_len;
                 } else {
-                    ncname_len = parse_ncname(&expr[parsed + tok_len]);
-                    LY_CHECK_ERR_GOTO(ncname_len < 0, LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INEXPR, parsed - ncname_len + 1, expr), error);
+                    ncname_len = parse_ncname(&expr_str[parsed + tok_len]);
+                    LY_CHECK_ERR_GOTO(ncname_len < 0,
+                                      LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_XP_INEXPR, parsed - ncname_len + 1, expr_str); ret = LY_EVALID,
+                                      error);
                     tok_len += ncname_len;
                 }
                 /* remove old flag to prevent ambiguities */
@@ -2907,35 +2917,36 @@
         }
 
         /* store the token, move on to the next one */
-        LY_CHECK_GOTO(exp_add_token(ctx, ret, tok_type, parsed, tok_len), error);
+        LY_CHECK_GOTO(ret = exp_add_token(ctx, expr, tok_type, parsed, tok_len), error);
         parsed += tok_len;
-        while (is_xmlws(expr[parsed])) {
+        while (is_xmlws(expr_str[parsed])) {
             ++parsed;
         }
 
-    } while (expr[parsed]);
+    } while (expr_str[parsed]);
 
     if (reparse) {
         /* prealloc repeat */
-        ret->repeat = calloc(ret->size, sizeof *ret->repeat);
-        LY_CHECK_ERR_GOTO(!ret->repeat, LOGMEM(ctx), error);
+        expr->repeat = calloc(expr->size, sizeof *expr->repeat);
+        LY_CHECK_ERR_GOTO(!expr->repeat, LOGMEM(ctx); ret = LY_EMEM, error);
 
         /* fill repeat */
-        LY_CHECK_GOTO(reparse_or_expr(ctx, ret, &tok_idx), error);
-        if (ret->used > tok_idx) {
+        LY_CHECK_ERR_GOTO(reparse_or_expr(ctx, expr, &tok_idx), ret = LY_EVALID, error);
+        if (expr->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]]);
+                &expr->expr[expr->tok_pos[tok_idx]]);
+            ret = LY_EVALID;
             goto error;
         }
     }
 
-    print_expr_struct_debug(ret);
-
-    return ret;
+    print_expr_struct_debug(expr);
+    *expr_p = expr;
+    return LY_SUCCESS;
 
 error:
-    lyxp_expr_free(ctx, ret);
-    return NULL;
+    lyxp_expr_free(ctx, expr);
+    return ret;
 }
 
 struct lyxp_expr *
diff --git a/src/xpath.h b/src/xpath.h
index e74f48b..20498cd 100644
--- a/src/xpath.h
+++ b/src/xpath.h
@@ -374,13 +374,16 @@
  * https://www.w3.org/TR/1999/REC-xpath-19991116/#exprlex
  *
  * @param[in] ctx Context for errors.
- * @param[in] expr XPath expression to parse. It is duplicated.
+ * @param[in] expr_str XPath expression to parse. It is duplicated.
  * @param[in] expr_len Length of @p expr, can be 0 if @p expr is 0-terminated.
  * @param[in] reparse Whether to re-parse the expression to finalize full XPath parsing and fill
  * information about expressions and their operators (fill repeat).
- * @return Filled expression structure or NULL on error.
+ * @param[out] expr_p Pointer to return the filled expression structure.
+ * @return LY_SUCCESS in case of success.
+ * @return LY_EMEM in case of memory allocation failure.
+ * @return LY_EVALID in case of invalid XPath expression in @p expr_str.
  */
-struct lyxp_expr *lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr, size_t expr_len, ly_bool reparse);
+LY_ERR lyxp_expr_parse(const struct ly_ctx *ctx, const char *expr_str, size_t expr_len, ly_bool reparse, struct lyxp_expr **expr_p);
 
 /**
  * @brief Duplicate parsed XPath expression.