aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/libfyaml/src/lib/fy-walk.c
diff options
context:
space:
mode:
authorDaniil Cherednik <dan.cherednik@gmail.com>2023-05-05 11:09:01 +0300
committerDaniil Cherednik <dan.cherednik@gmail.com>2023-05-05 11:09:01 +0300
commitb5a989b16cafa8a3b3bc076f1097a0eda6f48c06 (patch)
tree4da744117a5aab37758921fa43b95a3068e5aec1 /contrib/libs/libfyaml/src/lib/fy-walk.c
parentfc1cffcfa7f0497a1f97b384a24bcbf23362f3be (diff)
downloadydb-b5a989b16cafa8a3b3bc076f1097a0eda6f48c06.tar.gz
Ydb stable 23-1-2623.1.26
x-stable-origin-commit: 22184a7e157553d447f17a2dffc4ea2d32dfd74d
Diffstat (limited to 'contrib/libs/libfyaml/src/lib/fy-walk.c')
-rw-r--r--contrib/libs/libfyaml/src/lib/fy-walk.c5236
1 files changed, 5236 insertions, 0 deletions
diff --git a/contrib/libs/libfyaml/src/lib/fy-walk.c b/contrib/libs/libfyaml/src/lib/fy-walk.c
new file mode 100644
index 0000000000..d76125071e
--- /dev/null
+++ b/contrib/libs/libfyaml/src/lib/fy-walk.c
@@ -0,0 +1,5236 @@
+/*
+ * fy-walk.c - path walker
+ *
+ * Copyright (c) 2021 Pantelis Antoniou <pantelis.antoniou@konsulko.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
+#include <unistd.h>
+#endif
+#include <math.h>
+#include <limits.h>
+
+#include <libfyaml.h>
+
+#include "fy-parse.h"
+#include "fy-doc.h"
+#include "fy-walk.h"
+
+#include "fy-utils.h"
+
+#undef DEBUG_EXPR
+// #define DEBUG_EXPR
+
+const char *fy_walk_result_type_txt[FWRT_COUNT] = {
+ [fwrt_none] = "none",
+ [fwrt_node_ref] = "node-ref",
+ [fwrt_number] = "number",
+ [fwrt_string] = "string",
+ [fwrt_doc] = "doc",
+ [fwrt_refs] = "refs",
+};
+
+void fy_walk_result_dump(struct fy_walk_result *fwr, struct fy_diag *diag, enum fy_error_type errlevel, int level,
+ const char *fmt, ...);
+
+void fy_walk_result_vdump(struct fy_walk_result *fwr, struct fy_diag *diag, enum fy_error_type errlevel, int level,
+ const char *fmt, va_list ap)
+{
+ struct fy_walk_result *fwr2;
+ char *banner;
+ char *texta = NULL;
+ const char *text = "";
+ size_t len;
+ bool save_on_error;
+ char buf[30];
+ int rc __FY_DEBUG_UNUSED__;
+
+ if (!diag)
+ return;
+
+ if (errlevel < diag->cfg.level)
+ return;
+
+ save_on_error = diag->on_error;
+ diag->on_error = true;
+
+ if (fmt) {
+ banner = NULL;
+ rc = vasprintf(&banner, fmt, ap);
+ assert(rc != -1);
+ assert(banner);
+ fy_diag_diag(diag, errlevel, "%-*s%s", level*2, "", banner);
+ free(banner);
+ }
+ if (!fwr)
+ goto out;
+
+ switch (fwr->type) {
+ case fwrt_none:
+ text="";
+ break;
+ case fwrt_node_ref:
+ texta = fy_node_get_path(fwr->fyn);
+ assert(texta);
+ text = texta;
+ break;
+ case fwrt_number:
+ snprintf(buf, sizeof(buf), "%f", fwr->number);
+ text = buf;
+ break;
+ case fwrt_string:
+ text = fwr->string;
+ break;
+ case fwrt_doc:
+ texta = fy_emit_document_to_string(fwr->fyd, FYECF_WIDTH_INF | FYECF_INDENT_DEFAULT | FYECF_MODE_FLOW_ONELINE);
+ assert(texta);
+ text = texta;
+ break;
+ case fwrt_refs:
+ text="";
+ break;
+ }
+
+ len = strlen(text);
+
+ fy_diag_diag(diag, errlevel, "%-*s%s%s%.*s",
+ (level + 1) * 2, "",
+ fy_walk_result_type_txt[fwr->type],
+ len ? " " : "",
+ (int)len, text);
+
+ if (texta)
+ free(texta);
+
+ if (fwr->type == fwrt_refs) {
+ for (fwr2 = fy_walk_result_list_head(&fwr->refs); fwr2; fwr2 = fy_walk_result_next(&fwr->refs, fwr2))
+ fy_walk_result_dump(fwr2, diag, errlevel, level + 1, NULL);
+ }
+out:
+ diag->on_error = save_on_error;
+}
+
+void fy_walk_result_dump(struct fy_walk_result *fwr, struct fy_diag *diag, enum fy_error_type errlevel, int level,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ fy_walk_result_vdump(fwr, diag, errlevel, level, fmt, ap);
+ va_end(ap);
+}
+
+/* NOTE that walk results do not take references and it is invalid to
+ * use _any_ call that modifies the document structure
+ */
+struct fy_walk_result *fy_walk_result_alloc_rl(struct fy_walk_result_list *fwrl)
+{
+ struct fy_walk_result *fwr = NULL;
+
+ if (fwrl)
+ fwr = fy_walk_result_list_pop(fwrl);
+
+ if (!fwr) {
+ fwr = malloc(sizeof(*fwr));
+ if (!fwr)
+ return NULL;
+ memset(fwr, 0, sizeof(*fwr));
+ }
+ fwr->type = fwrt_none;
+ return fwr;
+}
+
+struct fy_walk_result *fy_walk_result_clone_rl(struct fy_walk_result_list *fwrl, struct fy_walk_result *fwr)
+{
+ struct fy_walk_result *fwrn = NULL, *fwrn2 = NULL, *fwrn3;
+
+ if (!fwr)
+ return NULL;
+
+ fwrn = fy_walk_result_alloc_rl(fwrl);
+ if (!fwrn)
+ return NULL;
+
+ fwrn->type = fwr->type;
+ switch (fwr->type) {
+ case fwrt_none:
+ break;
+ case fwrt_node_ref:
+ fwrn->fyn = fwr->fyn;
+ break;
+ case fwrt_number:
+ fwrn->number = fwr->number;
+ break;
+ case fwrt_string:
+ fwrn->string = strdup(fwr->string);
+ if (!fwrn->string)
+ goto err_out;
+ break;
+ case fwrt_doc:
+ fwrn->fyd = fy_document_clone(fwr->fyd);
+ if (!fwrn->fyd)
+ goto err_out;
+ break;
+ case fwrt_refs:
+
+ fy_walk_result_list_init(&fwrn->refs);
+
+ for (fwrn2 = fy_walk_result_list_head(&fwr->refs); fwrn2;
+ fwrn2 = fy_walk_result_next(&fwr->refs, fwrn2)) {
+
+ fwrn3 = fy_walk_result_clone_rl(fwrl, fwrn2);
+ if (!fwrn3)
+ goto err_out;
+
+ fy_walk_result_list_add_tail(&fwrn->refs, fwrn3);
+ }
+ break;
+ }
+ return fwrn;
+err_out:
+ if (fwrn)
+ fy_walk_result_free_rl(fwrl, fwrn);
+ return NULL;
+}
+
+struct fy_walk_result *fy_walk_result_clone(struct fy_walk_result *fwr)
+{
+ struct fy_walk_result_list *fwrl;
+
+ if (!fwr)
+ return NULL;
+
+ fwrl = fy_path_exec_walk_result_rl(fwr->fypx);
+ return fy_walk_result_clone_rl(fwrl, fwr);
+}
+
+void fy_walk_result_clean_rl(struct fy_walk_result_list *fwrl, struct fy_walk_result *fwr)
+{
+ struct fy_walk_result *fwrn;
+
+ if (!fwr)
+ return;
+
+ switch (fwr->type) {
+ case fwrt_none:
+ break;
+ case fwrt_node_ref:
+ case fwrt_number:
+ break;
+ case fwrt_string:
+ if (fwr->string)
+ free(fwr->string);
+ break;
+ case fwrt_doc:
+ if (fwr->fyd)
+ fy_document_destroy(fwr->fyd);
+ break;
+ case fwrt_refs:
+ while ((fwrn = fy_walk_result_list_pop(&fwr->refs)) != NULL)
+ fy_walk_result_free_rl(fwrl, fwrn);
+ break;
+ }
+
+ fwr->type = fwrt_none;
+}
+
+void fy_walk_result_clean(struct fy_walk_result *fwr)
+{
+ struct fy_walk_result_list *fwrl;
+
+ if (!fwr)
+ return;
+
+ fwrl = fy_path_exec_walk_result_rl(fwr->fypx);
+ fy_walk_result_clean_rl(fwrl, fwr);
+}
+
+void fy_walk_result_free_rl(struct fy_walk_result_list *fwrl, struct fy_walk_result *fwr)
+{
+ struct fy_path_exec *fypx;
+
+ if (!fwr)
+ return;
+
+ fypx = fwr->fypx;
+
+ fy_walk_result_clean_rl(fwrl, fwr);
+
+ if (fwrl)
+ fy_walk_result_list_push(fwrl, fwr);
+ else
+ free(fwr);
+
+ fy_path_exec_unref(fypx); /* NULL is OK */
+}
+
+void fy_walk_result_free(struct fy_walk_result *fwr)
+{
+ struct fy_walk_result_list *fwrl;
+
+ if (!fwr)
+ return;
+
+ fwrl = fy_path_exec_walk_result_rl(fwr->fypx);
+ fy_walk_result_free_rl(fwrl, fwr);
+}
+
+void fy_walk_result_list_free_rl(struct fy_walk_result_list *fwrl, struct fy_walk_result_list *results)
+{
+ struct fy_walk_result *fwr;
+
+ while ((fwr = fy_walk_result_list_pop(results)) != NULL)
+ fy_walk_result_free_rl(fwrl, fwr);
+}
+
+struct fy_walk_result *fy_walk_result_vcreate_rl(struct fy_walk_result_list *fwrl, enum fy_walk_result_type type, va_list ap)
+{
+ struct fy_walk_result *fwr = NULL;
+
+ if ((unsigned int)type >= FWRT_COUNT)
+ goto err_out;
+
+ fwr = fy_walk_result_alloc_rl(fwrl);
+ if (!fwr)
+ goto err_out;
+
+ fwr->type = type;
+
+ switch (fwr->type) {
+ case fwrt_none:
+ break;
+ case fwrt_node_ref:
+ fwr->fyn = va_arg(ap, struct fy_node *);
+ break;
+ case fwrt_number:
+ fwr->number = va_arg(ap, double);
+ break;
+ case fwrt_string:
+ fwr->string = strdup(va_arg(ap, const char *));
+ if (!fwr->string)
+ goto err_out;
+ break;
+ case fwrt_doc:
+ fwr->fyd = va_arg(ap, struct fy_document *);
+ break;
+ case fwrt_refs:
+ fy_walk_result_list_init(&fwr->refs);
+ break;
+ }
+
+ return fwr;
+
+err_out:
+ fy_walk_result_free_rl(fwrl, fwr);
+
+ return NULL;
+}
+
+struct fy_walk_result *fy_walk_result_create_rl(struct fy_walk_result_list *fwrl, enum fy_walk_result_type type, ...)
+{
+ struct fy_walk_result *fwr;
+ va_list ap;
+
+ va_start(ap, type);
+ fwr = fy_walk_result_vcreate_rl(fwrl, type, ap);
+ va_end(ap);
+
+ return fwr;
+}
+
+void fy_walk_result_flatten_internal(struct fy_walk_result *fwr, struct fy_walk_result *fwrf)
+{
+ struct fy_walk_result *fwr2, *fwr2n;
+
+ if (!fwr || !fwrf || fwr->type != fwrt_refs)
+ return;
+
+ for (fwr2 = fy_walk_result_list_head(&fwr->refs); fwr2; fwr2 = fwr2n) {
+
+ fwr2n = fy_walk_result_next(&fwr->refs, fwr2);
+
+ if (fwr2->type != fwrt_refs) {
+ fy_walk_result_list_del(&fwr->refs, fwr2);
+ fy_walk_result_list_add_tail(&fwrf->refs, fwr2);
+ continue;
+ }
+ fy_walk_result_flatten_internal(fwr2, fwrf);
+ }
+}
+
+bool fy_walk_result_has_leaves_only(struct fy_walk_result *fwr)
+{
+ struct fy_walk_result *fwrn;
+
+ if (!fwr || fwr->type != fwrt_refs)
+ return false;
+
+ if (fy_walk_result_list_empty(&fwr->refs))
+ return false;
+
+ for (fwrn = fy_walk_result_list_head(&fwr->refs); fwrn;
+ fwrn = fy_walk_result_next(&fwr->refs, fwrn)) {
+
+ if (fwrn->type == fwrt_refs)
+ return false;
+ }
+
+ return true;
+}
+
+struct fy_walk_result *
+fy_walk_result_flatten_rl(struct fy_walk_result_list *fwrl, struct fy_walk_result *fwr)
+{
+ struct fy_walk_result *fwrf;
+
+ if (!fwr)
+ return NULL;
+
+ fwrf = fy_walk_result_create_rl(fwrl, fwrt_refs);
+ assert(fwrf);
+
+ fy_walk_result_flatten_internal(fwr, fwrf);
+ fy_walk_result_free_rl(fwrl, fwr);
+ return fwrf;
+}
+
+struct fy_walk_result *
+fy_walk_result_flatten(struct fy_walk_result *fwr)
+{
+ struct fy_walk_result_list *fwrl;
+
+ if (!fwr)
+ return NULL;
+
+ fwrl = fy_path_exec_walk_result_rl(fwr->fypx);
+ return fy_walk_result_flatten_rl(fwrl, fwr);
+}
+
+struct fy_node *
+fy_walk_result_node_iterate(struct fy_walk_result *fwr, void **prevp)
+{
+ struct fy_walk_result *fwrn;
+
+ if (!fwr || !prevp)
+ return NULL;
+
+ switch (fwr->type) {
+ case fwrt_node_ref:
+ if (!*prevp) {
+ *prevp = fwr;
+ return fwr->fyn;
+ }
+ *prevp = NULL;
+ return NULL;
+
+ case fwrt_refs:
+ if (!*prevp)
+ fwrn = fy_walk_result_list_head(&fwr->refs);
+ else
+ fwrn = fy_walk_result_next(&fwr->refs, *prevp);
+
+ /* skip over any non node refs */
+ while (fwrn && fwrn->type != fwrt_node_ref)
+ fwrn = fy_walk_result_next(&fwr->refs, fwrn);
+ *prevp = fwrn;
+ return fwrn ? fwrn->fyn : NULL;
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+const char *fy_path_expr_type_txt[FPET_COUNT] = {
+ [fpet_none] = "none",
+ /* */
+ [fpet_root] = "root",
+ [fpet_this] = "this",
+ [fpet_parent] = "parent",
+ [fpet_every_child] = "every-child",
+ [fpet_every_child_r] = "every-child-recursive",
+ [fpet_filter_collection] = "filter-collection",
+ [fpet_filter_scalar] = "filter-scalar",
+ [fpet_filter_sequence] = "filter-sequence",
+ [fpet_filter_mapping] = "filter-mapping",
+ [fpet_filter_unique] = "filter-unique",
+ [fpet_seq_index] = "seq-index",
+ [fpet_seq_slice] = "seq-slice",
+ [fpet_alias] = "alias",
+
+ [fpet_map_key] = "map-key",
+
+ [fpet_multi] = "multi",
+ [fpet_chain] = "chain",
+ [fpet_logical_or] = "logical-or",
+ [fpet_logical_and] = "logical-and",
+
+ [fpet_eq] = "equals",
+ [fpet_neq] = "not-equals",
+ [fpet_lt] = "less-than",
+ [fpet_gt] = "greater-than",
+ [fpet_lte] = "less-or-equal-than",
+ [fpet_gte] = "greater-or-equal-than",
+
+ [fpet_scalar] = "scalar",
+
+ [fpet_plus] = "plus",
+ [fpet_minus] = "minus",
+ [fpet_mult] = "multiply",
+ [fpet_div] = "divide",
+
+ [fpet_lparen] = "left-parentheses",
+ [fpet_rparen] = "right-parentheses",
+ [fpet_method] = "method",
+
+ [fpet_scalar_expr] = "scalar-expression",
+ [fpet_path_expr] = "path-expression",
+ [fpet_arg_separator] = "argument-separator",
+};
+
+struct fy_path_expr *fy_path_expr_alloc(void)
+{
+ struct fy_path_expr *expr = NULL;
+
+ expr = malloc(sizeof(*expr));
+ if (!expr)
+ return NULL;
+ memset(expr, 0, sizeof(*expr));
+ fy_path_expr_list_init(&expr->children);
+
+ return expr;
+}
+
+void fy_path_expr_free(struct fy_path_expr *expr)
+{
+ struct fy_path_expr *exprn;
+
+ if (!expr)
+ return;
+
+ while ((exprn = fy_path_expr_list_pop(&expr->children)) != NULL)
+ fy_path_expr_free(exprn);
+
+ fy_token_unref(expr->fyt);
+
+ free(expr);
+}
+
+struct fy_path_expr *fy_path_expr_alloc_recycle(struct fy_path_parser *fypp)
+{
+ struct fy_path_expr *expr = NULL;
+
+ if (!fypp || fypp->suppress_recycling)
+ expr = fy_path_expr_alloc();
+
+ if (!expr) {
+ expr = fy_path_expr_list_pop(&fypp->expr_recycle);
+ if (expr) {
+ memset(expr, 0, sizeof(*expr));
+ fy_path_expr_list_init(&expr->children);
+ } else
+ expr = fy_path_expr_alloc();
+ }
+
+ if (!expr)
+ return NULL;
+
+ expr->expr_mode = fypp->expr_mode;
+
+ return expr;
+}
+
+void fy_path_expr_free_recycle(struct fy_path_parser *fypp, struct fy_path_expr *expr)
+{
+ struct fy_path_expr *exprn;
+
+ if (!fypp || fypp->suppress_recycling) {
+ fy_path_expr_free(expr);
+ return;
+ }
+
+ while ((exprn = fy_path_expr_list_pop(&expr->children)) != NULL)
+ fy_path_expr_free_recycle(fypp, exprn);
+
+ if (expr->fyt) {
+ fy_token_unref(expr->fyt);
+ expr->fyt = NULL;
+ }
+ fy_path_expr_list_add_tail(&fypp->expr_recycle, expr);
+}
+
+void fy_expr_stack_setup(struct fy_expr_stack *stack)
+{
+ if (!stack)
+ return;
+
+ memset(stack, 0, sizeof(*stack));
+ stack->items = stack->items_static;
+ stack->alloc = ARRAY_SIZE(stack->items_static);
+}
+
+void fy_expr_stack_cleanup(struct fy_expr_stack *stack)
+{
+ if (!stack)
+ return;
+
+ while (stack->top > 0)
+ fy_path_expr_free(stack->items[--stack->top]);
+
+ if (stack->items != stack->items_static)
+ free(stack->items);
+ stack->items = stack->items_static;
+ stack->alloc = ARRAY_SIZE(stack->items_static);
+}
+
+void fy_expr_stack_dump(struct fy_diag *diag, struct fy_expr_stack *stack)
+{
+ struct fy_path_expr *expr;
+ unsigned int i;
+
+ if (!stack)
+ return;
+
+ if (!stack->top)
+ return;
+
+ i = stack->top;
+ do {
+ expr = stack->items[--i];
+ fy_path_expr_dump(expr, diag, FYET_NOTICE, 0, NULL);
+ } while (i > 0);
+}
+
+int fy_expr_stack_size(struct fy_expr_stack *stack)
+{
+ if (!stack || stack->top >= (unsigned int)INT_MAX)
+ return -1;
+ return (int)stack->top;
+}
+
+int fy_expr_stack_push(struct fy_expr_stack *stack, struct fy_path_expr *expr)
+{
+ struct fy_path_expr **items_new;
+ unsigned int alloc;
+ size_t size;
+
+ if (!stack || !expr)
+ return -1;
+
+ assert(stack->items);
+ assert(stack->alloc > 0);
+
+ assert(expr->fyt);
+
+ /* grow the stack if required */
+ if (stack->top >= stack->alloc) {
+ alloc = stack->alloc;
+ size = alloc * sizeof(*items_new);
+
+ if (stack->items == stack->items_static) {
+ items_new = malloc(size * 2);
+ if (items_new)
+ memcpy(items_new, stack->items_static, size);
+ } else
+ items_new = realloc(stack->items, size * 2);
+
+ if (!items_new)
+ return -1;
+
+ stack->alloc = alloc * 2;
+ stack->items = items_new;
+ }
+
+ stack->items[stack->top++] = expr;
+
+ return 0;
+}
+
+struct fy_path_expr *fy_expr_stack_peek_at(struct fy_expr_stack *stack, unsigned int pos)
+{
+ if (!stack || stack->top <= pos)
+ return NULL;
+ return stack->items[stack->top - 1 - pos];
+}
+
+struct fy_path_expr *fy_expr_stack_peek(struct fy_expr_stack *stack)
+{
+ return fy_expr_stack_peek_at(stack, 0);
+}
+
+struct fy_path_expr *fy_expr_stack_pop(struct fy_expr_stack *stack)
+{
+ if (!stack || !stack->top)
+ return NULL;
+
+ return stack->items[--stack->top];
+}
+
+bool fy_token_type_can_be_path_expr(enum fy_token_type type)
+{
+ return type == FYTT_NONE ||
+ type == FYTT_PE_LPAREN ||
+ type == FYTT_PE_RPAREN ||
+ type == FYTT_PE_EQEQ ||
+ type == FYTT_PE_NOTEQ ||
+ type == FYTT_PE_GT ||
+ type == FYTT_PE_LT ||
+ type == FYTT_PE_GTE ||
+ type == FYTT_PE_LTE ||
+ type == FYTT_PE_METHOD;
+}
+
+bool fy_token_type_can_be_before_negative_number(enum fy_token_type type)
+{
+ return type == FYTT_NONE ||
+ type == FYTT_PE_LPAREN ||
+ type == FYTT_PE_RPAREN ||
+ type == FYTT_PE_EQEQ ||
+ type == FYTT_PE_NOTEQ ||
+ type == FYTT_PE_GT ||
+ type == FYTT_PE_LT ||
+ type == FYTT_PE_GTE ||
+ type == FYTT_PE_LTE ||
+ type == FYTT_SE_PLUS ||
+ type == FYTT_SE_MINUS ||
+ type == FYTT_SE_MULT ||
+ type == FYTT_SE_DIV ||
+
+ type == FYTT_SE_METHOD;
+}
+
+const char *fy_expr_mode_txt[FYEM_COUNT] = {
+ [fyem_none] = "none",
+ [fyem_path] = "path",
+ [fyem_scalar] = "scalar",
+};
+
+static struct fy_diag *fy_path_parser_reader_get_diag(struct fy_reader *fyr)
+{
+ struct fy_path_parser *fypp = fy_container_of(fyr, struct fy_path_parser, reader);
+ return fypp->cfg.diag;
+}
+
+static const struct fy_reader_ops fy_path_parser_reader_ops = {
+ .get_diag = fy_path_parser_reader_get_diag,
+};
+
+void fy_path_parser_setup(struct fy_path_parser *fypp, const struct fy_path_parse_cfg *pcfg)
+{
+ if (!fypp)
+ return;
+
+ memset(fypp, 0, sizeof(*fypp));
+ if (pcfg)
+ fypp->cfg = *pcfg;
+ fy_reader_setup(&fypp->reader, &fy_path_parser_reader_ops);
+ fy_token_list_init(&fypp->queued_tokens);
+ fypp->last_queued_token_type = FYTT_NONE;
+
+ fy_expr_stack_setup(&fypp->operators);
+ fy_expr_stack_setup(&fypp->operands);
+
+ fy_path_expr_list_init(&fypp->expr_recycle);
+ fypp->suppress_recycling = (fypp->cfg.flags & FYPPCF_DISABLE_RECYCLING) || getenv("FY_VALGRIND");
+
+ fypp->expr_mode = fyem_path;
+ fypp->paren_nest_level = 0;
+}
+
+void fy_path_parser_cleanup(struct fy_path_parser *fypp)
+{
+ struct fy_path_expr *expr;
+
+ if (!fypp)
+ return;
+
+ fy_expr_stack_cleanup(&fypp->operands);
+ fy_expr_stack_cleanup(&fypp->operators);
+
+ fy_reader_cleanup(&fypp->reader);
+ fy_token_list_unref_all(&fypp->queued_tokens);
+
+ while ((expr = fy_path_expr_list_pop(&fypp->expr_recycle)) != NULL)
+ fy_path_expr_free(expr);
+
+ fypp->last_queued_token_type = FYTT_NONE;
+ fypp->stream_start_produced = false;
+ fypp->stream_end_produced = false;
+ fypp->stream_error = false;
+ fypp->token_activity_counter = 0;
+ fypp->paren_nest_level = 0;
+}
+
+int fy_path_parser_open(struct fy_path_parser *fypp,
+ struct fy_input *fyi, const struct fy_reader_input_cfg *icfg)
+{
+ int ret;
+ if (!fypp)
+ return -1;
+
+ ret = fy_reader_input_open(&fypp->reader, fyi, icfg);
+ if (ret)
+ return ret;
+ /* take a reference to the input */
+ fypp->fyi = fy_input_ref(fyi);
+ return 0;
+}
+
+void fy_path_parser_close(struct fy_path_parser *fypp)
+{
+ if (!fypp)
+ return;
+
+ fy_input_unref(fypp->fyi);
+
+ fy_reader_input_done(&fypp->reader);
+}
+
+struct fy_token *fy_path_token_vqueue(struct fy_path_parser *fypp, enum fy_token_type type, va_list ap)
+{
+ struct fy_token *fyt;
+
+ fyt = fy_token_list_vqueue(&fypp->queued_tokens, type, ap);
+ if (fyt) {
+ fypp->token_activity_counter++;
+ fypp->last_queued_token_type = type;
+ }
+ return fyt;
+}
+
+struct fy_token *fy_path_token_queue(struct fy_path_parser *fypp, enum fy_token_type type, ...)
+{
+ va_list ap;
+ struct fy_token *fyt;
+
+ va_start(ap, type);
+ fyt = fy_path_token_vqueue(fypp, type, ap);
+ va_end(ap);
+
+ return fyt;
+}
+
+int fy_path_fetch_seq_index_or_slice(struct fy_path_parser *fypp, int c)
+{
+ struct fy_reader *fyr;
+ struct fy_token *fyt;
+ bool neg;
+ int i, j, val, nval, digits, indices[2];
+
+ fyr = &fypp->reader;
+
+ /* verify that the called context is correct */
+ assert(fy_is_num(c) || (c == '-' && fy_is_num(fy_reader_peek_at(fyr, 1))));
+
+ i = 0;
+ indices[0] = indices[1] = -1;
+
+ j = 0;
+ while (j < 2) {
+
+ neg = false;
+ if (c == '-') {
+ neg = true;
+ i++;
+ }
+
+ digits = 0;
+ val = 0;
+ while (fy_is_num((c = fy_reader_peek_at(fyr, i)))) {
+ nval = (val * 10) | (c - '0');
+ FYR_PARSE_ERROR_CHECK(fyr, 0, i, FYEM_SCAN,
+ nval >= val && nval >= 0, err_out,
+ "illegal sequence index (overflow)");
+ val = nval;
+ i++;
+ digits++;
+ }
+ FYR_PARSE_ERROR_CHECK(fyr, 0, i, FYEM_SCAN,
+ (val == 0 && digits == 1) || (val > 0), err_out,
+ "bad number");
+ if (neg)
+ val = -val;
+
+ indices[j] = val;
+
+ /* continue only on slice : */
+ if (c == ':') {
+ c = fy_reader_peek_at(fyr, i + 1);
+ if (fy_is_num(c) || (c == '-' && fy_is_num(fy_reader_peek_at(fyr, i + 2)))) {
+ i++;
+ j++;
+ continue;
+ }
+ }
+
+ break;
+ }
+
+ if (j >= 1)
+ fyt = fy_path_token_queue(fypp, FYTT_PE_SEQ_SLICE, fy_reader_fill_atom_a(fyr, i), indices[0], indices[1]);
+ else
+ fyt = fy_path_token_queue(fypp, FYTT_PE_SEQ_INDEX, fy_reader_fill_atom_a(fyr, i), indices[0]);
+
+ fyr_error_check(fyr, fyt, err_out, "fy_path_token_queue() failed\n");
+
+ return 0;
+
+err_out:
+ fypp->stream_error = true;
+ return -1;
+}
+
+int fy_path_fetch_plain_or_method(struct fy_path_parser *fypp, int c,
+ enum fy_token_type fytt_plain,
+ enum fy_token_type fytt_method)
+{
+ struct fy_reader *fyr;
+ struct fy_token *fyt;
+ struct fy_atom *handlep;
+ int i;
+ enum fy_token_type type;
+
+ fyr = &fypp->reader;
+
+ assert(fy_is_first_alpha(c));
+
+ type = fytt_plain;
+
+ i = 1;
+ while (fy_is_alnum(fy_reader_peek_at(fyr, i)))
+ i++;
+
+ if (fy_reader_peek_at(fyr, i) == '(')
+ type = fytt_method;
+
+ handlep = fy_reader_fill_atom_a(fyr, i);
+ if (type == FYTT_SCALAR) {
+ fyt = fy_path_token_queue(fypp, FYTT_SCALAR, handlep, FYSS_PLAIN, NULL);
+ fyr_error_check(fyr, fyt, err_out, "fy_path_token_queue() failed\n");
+ } else {
+ fyt = fy_path_token_queue(fypp, type, handlep, NULL);
+ fyr_error_check(fyr, fyt, err_out, "fy_path_token_queue() failed\n");
+ }
+
+ return 0;
+
+err_out:
+ fypp->stream_error = true;
+ return -1;
+}
+
+int fy_path_fetch_dot_method(struct fy_path_parser *fypp, int c, enum fy_token_type fytt)
+{
+ struct fy_reader *fyr;
+ struct fy_token *fyt;
+ struct fy_atom *handlep;
+ int i;
+
+ fyr = &fypp->reader;
+
+ assert(c == '.');
+ fy_reader_advance(fyr, c);
+ c = fy_reader_peek(fyr);
+ assert(fy_is_first_alpha(c));
+
+ /* verify that the called context is correct */
+ i = 1;
+ while (fy_is_alnum(fy_reader_peek_at(fyr, i)))
+ i++;
+
+ handlep = fy_reader_fill_atom_a(fyr, i);
+
+ fyt = fy_path_token_queue(fypp, fytt, handlep, NULL);
+ fyr_error_check(fyr, fyt, err_out, "fy_path_token_queue() failed\n");
+
+ return 0;
+
+err_out:
+ fypp->stream_error = true;
+ return -1;
+}
+
+int fy_path_fetch_flow_document(struct fy_path_parser *fypp, int c, enum fy_token_type fytt)
+{
+ struct fy_reader *fyr;
+ struct fy_token *fyt;
+ struct fy_document *fyd;
+ struct fy_atom handle;
+ struct fy_parser fyp_data, *fyp = &fyp_data;
+ struct fy_parse_cfg cfg_data, *cfg = NULL;
+ int rc;
+
+ fyr = &fypp->reader;
+
+ /* verify that the called context is correct */
+ assert(fy_is_path_flow_key_start(c));
+
+ fy_reader_fill_atom_start(fyr, &handle);
+
+ cfg = &cfg_data;
+ memset(cfg, 0, sizeof(*cfg));
+ cfg->flags = FYPCF_DEFAULT_PARSE;
+ cfg->diag = fypp->cfg.diag;
+
+ rc = fy_parse_setup(fyp, cfg);
+ fyr_error_check(fyr, !rc, err_out, "fy_parse_setup() failed\n");
+
+ /* associate with reader and set flow mode */
+ fy_parser_set_reader(fyp, fyr);
+ fy_parser_set_flow_only_mode(fyp, true);
+
+ fyd = fy_parse_load_document(fyp);
+
+ /* cleanup the parser no matter what */
+ fy_parse_cleanup(fyp);
+
+ fyr_error_check(fyr, fyd, err_out, "fy_parse_load_document() failed\n");
+
+ fy_reader_fill_atom_end(fyr, &handle);
+
+ /* document is NULL, is a simple key */
+ fyt = fy_path_token_queue(fypp, fytt, &handle, fyd);
+ fyr_error_check(fyr, fyt, err_out, "fy_path_token_queue() failed\n");
+
+ return 0;
+
+err_out:
+ fypp->stream_error = true;
+ return -1;
+}
+
+int fy_path_fetch_flow_map_key(struct fy_path_parser *fypp, int c)
+{
+ return fy_path_fetch_flow_document(fypp, c, FYTT_PE_MAP_KEY);
+}
+
+int fy_path_fetch_flow_scalar(struct fy_path_parser *fypp, int c)
+{
+ struct fy_reader *fyr;
+ struct fy_token *fyt;
+ struct fy_atom handle;
+ bool is_single;
+ int rc = -1;
+
+ fyr = &fypp->reader;
+
+ /* verify that the called context is correct */
+ assert(fy_is_path_flow_scalar_start(c));
+
+ is_single = c == '\'';
+
+ rc = fy_reader_fetch_flow_scalar_handle(fyr, c, 0, &handle, false);
+ if (rc)
+ goto err_out_rc;
+
+ /* document is NULL, is a simple key */
+ fyt = fy_path_token_queue(fypp, FYTT_SCALAR, &handle, is_single ? FYSS_SINGLE_QUOTED : FYSS_DOUBLE_QUOTED);
+ fyr_error_check(fyr, fyt, err_out, "fy_path_token_queue() failed\n");
+
+ return 0;
+
+err_out:
+ rc = -1;
+err_out_rc:
+ fypp->stream_error = true;
+ return rc;
+}
+
+int fy_path_fetch_number(struct fy_path_parser *fypp, int c)
+{
+ struct fy_reader *fyr;
+ struct fy_token *fyt;
+ int i, digits;
+
+ fyr = &fypp->reader;
+
+ /* verify that the called context is correct */
+ assert(fy_is_num(c) || (c == '-' && fy_is_num(fy_reader_peek_at(fyr, 1))));
+
+ i = 0;
+ if (c == '-')
+ i++;
+
+ digits = 0;
+ while (fy_is_num((c = fy_reader_peek_at(fyr, i)))) {
+ i++;
+ digits++;
+ }
+ FYR_PARSE_ERROR_CHECK(fyr, 0, i, FYEM_SCAN,
+ digits > 0, err_out,
+ "bad number");
+
+ fyt = fy_path_token_queue(fypp, FYTT_SCALAR, fy_reader_fill_atom_a(fyr, i), FYSS_PLAIN);
+ fyr_error_check(fyr, fyt, err_out, "fy_path_token_queue() failed\n");
+
+ return 0;
+
+err_out:
+ fypp->stream_error = true;
+ return -1;
+}
+
+int fy_path_fetch_tokens(struct fy_path_parser *fypp)
+{
+ enum fy_token_type type;
+ struct fy_token *fyt;
+ struct fy_reader *fyr;
+ int c, cn, rc, simple_token_count;
+
+ fyr = &fypp->reader;
+ if (!fypp->stream_start_produced) {
+
+ fyt = fy_path_token_queue(fypp, FYTT_STREAM_START, fy_reader_fill_atom_a(fyr, 0));
+ fyr_error_check(fyr, fyt, err_out, "fy_path_token_queue() failed\n");
+
+ fypp->stream_start_produced = true;
+ return 0;
+ }
+
+ /* XXX scan to next token? */
+
+ c = fy_reader_peek(fyr);
+
+ if (fy_is_z(c)) {
+
+ if (c >= 0)
+ fy_reader_advance(fyr, c);
+
+ /* produce stream end continuously */
+ fyt = fy_path_token_queue(fypp, FYTT_STREAM_END, fy_reader_fill_atom_a(fyr, 0));
+ fyr_error_check(fyr, fyt, err_out, "fy_path_token_queue() failed\n");
+
+ return 0;
+ }
+
+ fyt = NULL;
+ type = FYTT_NONE;
+ simple_token_count = 0;
+
+ /* first do the common tokens */
+ switch (c) {
+ case ',':
+ type = FYTT_PE_COMMA;
+ simple_token_count = 1;
+ break;
+
+ case '|':
+ if (fy_reader_peek_at(fyr, 1) == '|') {
+ type = FYTT_PE_BARBAR;
+ simple_token_count = 2;
+ break;
+ }
+ break;
+
+ case '&':
+ if (fy_reader_peek_at(fyr, 1) == '&') {
+ type = FYTT_PE_AMPAMP;
+ simple_token_count = 2;
+ break;
+ }
+ break;
+
+ case '(':
+ type = FYTT_PE_LPAREN;
+ simple_token_count = 1;
+ break;
+
+ case ')':
+ type = FYTT_PE_RPAREN;
+ simple_token_count = 1;
+ break;
+
+ case '=':
+ cn = fy_reader_peek_at(fyr, 1);
+ if (cn == '=') {
+ type = FYTT_PE_EQEQ;
+ simple_token_count = 2;
+ break;
+ }
+ break;
+
+ case '>':
+ cn = fy_reader_peek_at(fyr, 1);
+ if (cn == '=') {
+ type = FYTT_PE_GTE;
+ simple_token_count = 2;
+ break;
+ }
+ type = FYTT_PE_GT;
+ simple_token_count = 1;
+ break;
+
+ case '<':
+ cn = fy_reader_peek_at(fyr, 1);
+ if (cn == '=') {
+ type = FYTT_PE_LTE;
+ simple_token_count = 2;
+ break;
+ }
+ type = FYTT_PE_LT;
+ simple_token_count = 1;
+ break;
+
+ case '!':
+ cn = fy_reader_peek_at(fyr, 1);
+ if (cn == '=') {
+ type = FYTT_PE_NOTEQ;
+ simple_token_count = 2;
+ break;
+ }
+ /* may still be something else */
+ break;
+
+ default:
+ break;
+ }
+
+ if (type != FYTT_NONE)
+ goto do_token;
+
+again:
+
+ switch (fypp->expr_mode) {
+ case fyem_none:
+ assert(0); /* should never happen */
+ break;
+
+ case fyem_path:
+
+ switch (c) {
+ case '/':
+ type = FYTT_PE_SLASH;
+ simple_token_count = 1;
+ break;
+
+ case '^':
+ type = FYTT_PE_ROOT;
+ simple_token_count = 1;
+ break;
+
+ case ':':
+ type = FYTT_PE_SIBLING;
+ simple_token_count = 1;
+ break;
+
+ case '$':
+ type = FYTT_PE_SCALAR_FILTER;
+ simple_token_count = 1;
+ break;
+
+ case '%':
+ type = FYTT_PE_COLLECTION_FILTER;
+ simple_token_count = 1;
+ break;
+
+ case '[':
+ if (fy_reader_peek_at(fyr, 1) == ']') {
+ type = FYTT_PE_SEQ_FILTER;
+ simple_token_count = 2;
+ }
+ break;
+
+ case '{':
+ if (fy_reader_peek_at(fyr, 1) == '}') {
+ type = FYTT_PE_MAP_FILTER;
+ simple_token_count = 2;
+ }
+ break;
+
+
+ case '.':
+ cn = fy_reader_peek_at(fyr, 1);
+ if (cn == '.') {
+ type = FYTT_PE_PARENT;
+ simple_token_count = 2;
+ } else if (!fy_is_first_alpha(cn)) {
+ type = FYTT_PE_THIS;
+ simple_token_count = 1;
+ }
+ break;
+
+ case '*':
+ if (fy_reader_peek_at(fyr, 1) == '*') {
+ type = FYTT_PE_EVERY_CHILD_R;
+ simple_token_count = 2;
+ } else if (!fy_is_first_alpha(fy_reader_peek_at(fyr, 1))) {
+ type = FYTT_PE_EVERY_CHILD;
+ simple_token_count = 1;
+ } else {
+ type = FYTT_PE_ALIAS;
+ simple_token_count = 2;
+ while (fy_is_alnum(fy_reader_peek_at(fyr, simple_token_count)))
+ simple_token_count++;
+ }
+ break;
+
+ case '!':
+ cn = fy_reader_peek_at(fyr, 1);
+ if (cn == '=') {
+ type = FYTT_PE_NOTEQ;
+ simple_token_count = 2;
+ break;
+ }
+ type = FYTT_PE_UNIQUE_FILTER;
+ simple_token_count = 1;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case fyem_scalar:
+
+
+ /* it is possible for the expression to be a path
+ * we only detect a few cases (doing all too complex)
+ * (/ , (./ , (*
+ */
+ if (fy_token_type_can_be_path_expr(fypp->last_queued_token_type)) {
+ cn = fy_reader_peek_at(fyr, 1);
+ if (c == '/' ||
+ (c == '.' && (cn == '/' || cn == ')' || cn == '>' || cn == '<' || cn == '!' || cn == '='))) {
+ fypp->expr_mode = fyem_path;
+#ifdef DEBUG_EXPR
+ fyr_notice(fyr, "switching to path expr\n");
+#endif
+ goto again;
+ }
+ }
+
+ switch (c) {
+ case '+':
+ type = FYTT_SE_PLUS;
+ simple_token_count = 1;
+ break;
+
+ case '-':
+ cn = fy_reader_peek_at(fyr, 1);
+ if (fy_is_num(cn) &&
+ fy_token_type_can_be_before_negative_number(fypp->last_queued_token_type))
+ break;
+
+ type = FYTT_SE_MINUS;
+ simple_token_count = 1;
+ break;
+
+ case '*':
+ type = FYTT_SE_MULT;
+ simple_token_count = 1;
+ break;
+
+ case '/':
+ type = FYTT_SE_DIV;
+ simple_token_count = 1;
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+
+do_token:
+ /* simple tokens */
+ if (simple_token_count > 0) {
+ fyt = fy_path_token_queue(fypp, type, fy_reader_fill_atom_a(fyr, simple_token_count));
+ fyr_error_check(fyr, fyt, err_out, "fy_path_token_queue() failed\n");
+
+ return 0;
+ }
+
+ switch (fypp->expr_mode) {
+ case fyem_none:
+ assert(0); /* should never happen */
+ break;
+
+ case fyem_path:
+ if (fy_is_first_alpha(c))
+ return fy_path_fetch_plain_or_method(fypp, c, FYTT_PE_MAP_KEY, FYTT_PE_METHOD);
+
+ if (fy_is_path_flow_key_start(c))
+ return fy_path_fetch_flow_map_key(fypp, c);
+
+ if (fy_is_num(c) || (c == '-' && fy_is_num(fy_reader_peek_at(fyr, 1))))
+ return fy_path_fetch_seq_index_or_slice(fypp, c);
+
+ if (c == '.' && fy_is_first_alpha(fy_reader_peek_at(fyr, 1)))
+ return fy_path_fetch_dot_method(fypp, c, FYTT_PE_METHOD);
+
+ break;
+
+ case fyem_scalar:
+
+ if (fy_is_first_alpha(c))
+ return fy_path_fetch_plain_or_method(fypp, c, FYTT_SCALAR, FYTT_SE_METHOD);
+
+ if (fy_is_path_flow_scalar_start(c))
+ return fy_path_fetch_flow_scalar(fypp, c);
+
+ if (fy_is_num(c) || (c == '-' && fy_is_num(fy_reader_peek_at(fyr, 1))))
+ return fy_path_fetch_number(fypp, c);
+
+#if 0
+ if (c == '.' && fy_is_first_alpha(fy_reader_peek_at(fyr, 1)))
+ return fy_path_fetch_dot_method(fypp, c, FYTT_SE_METHOD);
+#endif
+
+ break;
+ }
+
+ FYR_PARSE_ERROR(fyr, 0, 1, FYEM_SCAN, "bad path expression starts here c=%d", c);
+
+err_out:
+ fypp->stream_error = true;
+ rc = -1;
+ return rc;
+}
+
+struct fy_token *fy_path_scan_peek(struct fy_path_parser *fypp, struct fy_token *fyt_prev)
+{
+ struct fy_token *fyt;
+ struct fy_reader *fyr;
+ int rc, last_token_activity_counter;
+
+ fyr = &fypp->reader;
+
+ /* nothing if stream end produced (and no stream end token in queue) */
+ if (!fyt_prev && fypp->stream_end_produced && fy_token_list_empty(&fypp->queued_tokens)) {
+
+ fyt = fy_token_list_head(&fypp->queued_tokens);
+ if (fyt && fyt->type == FYTT_STREAM_END)
+ return fyt;
+
+ return NULL;
+ }
+
+ for (;;) {
+ if (!fyt_prev)
+ fyt = fy_token_list_head(&fypp->queued_tokens);
+ else
+ fyt = fy_token_next(&fypp->queued_tokens, fyt_prev);
+ if (fyt)
+ break;
+
+ /* on stream error we're done */
+ if (fypp->stream_error)
+ return NULL;
+
+ /* keep track of token activity, if it didn't change
+ * after the fetch tokens call, the state machine is stuck
+ */
+ last_token_activity_counter = fypp->token_activity_counter;
+
+ /* fetch more then */
+ rc = fy_path_fetch_tokens(fypp);
+ if (rc) {
+ fy_error(fypp->cfg.diag, "fy_path_fetch_tokens() failed\n");
+ goto err_out;
+ }
+ if (last_token_activity_counter == fypp->token_activity_counter) {
+ fy_error(fypp->cfg.diag, "out of tokens and failed to produce anymore");
+ goto err_out;
+ }
+ }
+
+ switch (fyt->type) {
+ case FYTT_STREAM_START:
+ fypp->stream_start_produced = true;
+ break;
+ case FYTT_STREAM_END:
+ fypp->stream_end_produced = true;
+
+ rc = fy_reader_input_done(fyr);
+ if (rc) {
+ fy_error(fypp->cfg.diag, "fy_parse_input_done() failed");
+ goto err_out;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return fyt;
+
+err_out:
+ fypp->stream_error = true;
+ return NULL;
+}
+
+struct fy_token *fy_path_scan_remove(struct fy_path_parser *fypp, struct fy_token *fyt)
+{
+ if (!fypp || !fyt)
+ return NULL;
+
+ fy_token_list_del(&fypp->queued_tokens, fyt);
+
+ return fyt;
+}
+
+struct fy_token *fy_path_scan_remove_peek(struct fy_path_parser *fypp, struct fy_token *fyt)
+{
+ fy_token_unref(fy_path_scan_remove(fypp, fyt));
+
+ return fy_path_scan_peek(fypp, NULL);
+}
+
+struct fy_token *fy_path_scan(struct fy_path_parser *fypp)
+{
+ return fy_path_scan_remove(fypp, fy_path_scan_peek(fypp, NULL));
+}
+
+void fy_path_expr_dump(struct fy_path_expr *expr, struct fy_diag *diag, enum fy_error_type errlevel, int level, const char *banner)
+{
+ struct fy_path_expr *expr2;
+ const char *style = "";
+ const char *text;
+ size_t len;
+ bool save_on_error;
+
+ if (errlevel < diag->cfg.level)
+ return;
+
+ save_on_error = diag->on_error;
+ diag->on_error = true;
+
+ if (banner)
+ fy_diag_diag(diag, errlevel, "%-*s%s", level*2, "", banner);
+
+ text = fy_token_get_text(expr->fyt, &len);
+
+ style = "";
+ if (expr->type == fpet_scalar) {
+ switch (fy_scalar_token_get_style(expr->fyt)) {
+ case FYSS_SINGLE_QUOTED:
+ style = "'";
+ break;
+ case FYSS_DOUBLE_QUOTED:
+ style = "\"";
+ break;
+ default:
+ style = "";
+ break;
+ }
+ }
+
+ fy_diag_diag(diag, errlevel, "> %-*s%s:%s %s%.*s%s",
+ level*2, "",
+ fy_path_expr_type_txt[expr->type],
+ fy_expr_mode_txt[expr->expr_mode],
+ style, (int)len, text, style);
+
+ for (expr2 = fy_path_expr_list_head(&expr->children); expr2; expr2 = fy_path_expr_next(&expr->children, expr2))
+ fy_path_expr_dump(expr2, diag, errlevel, level + 1, NULL);
+
+ diag->on_error = save_on_error;
+}
+
+static struct fy_node *
+fy_path_expr_to_node_internal(struct fy_document *fyd, struct fy_path_expr *expr)
+{
+ struct fy_path_expr *expr2;
+ const char *style = "";
+ const char *text;
+ size_t len;
+ struct fy_node *fyn = NULL, *fyn2, *fyn_seq = NULL;
+ int rc;
+
+ text = fy_token_get_text(expr->fyt, &len);
+
+ /* by default use double quoted style */
+ style = "\"";
+ switch (expr->type) {
+ case fpet_scalar:
+ switch (fy_scalar_token_get_style(expr->fyt)) {
+ case FYSS_SINGLE_QUOTED:
+ style = "'";
+ break;
+ case FYSS_DOUBLE_QUOTED:
+ style = "\"";
+ break;
+ default:
+ style = "";
+ break;
+ }
+ break;
+
+ case fpet_map_key:
+ /* no styles for complex map keys */
+ if (expr->fyt->map_key.fyd)
+ style = "";
+ break;
+
+ default:
+ break;
+ }
+
+ /* list is empty this is a terminal */
+ if (fy_path_expr_list_empty(&expr->children) &&
+ expr->type != fpet_method) {
+
+ fyn = fy_node_buildf(fyd, "%s: %s%.*s%s",
+ fy_path_expr_type_txt[expr->type],
+ style, (int)len, text, style);
+ if (!fyn)
+ return NULL;
+
+ return fyn;
+ }
+
+ fyn = fy_node_create_mapping(fyd);
+ if (!fyn)
+ goto err_out;
+
+ fyn_seq = fy_node_create_sequence(fyd);
+ if (!fyn_seq)
+ goto err_out;
+
+ for (expr2 = fy_path_expr_list_head(&expr->children); expr2; expr2 = fy_path_expr_next(&expr->children, expr2)) {
+ fyn2 = fy_path_expr_to_node_internal(fyd, expr2);
+ if (!fyn2)
+ goto err_out;
+ rc = fy_node_sequence_append(fyn_seq, fyn2);
+ if (rc)
+ goto err_out;
+ }
+
+ if (expr->type != fpet_method) {
+ rc = fy_node_mapping_append(fyn,
+ fy_node_create_scalar(fyd, fy_path_expr_type_txt[expr->type], FY_NT),
+ fyn_seq);
+ } else {
+ rc = fy_node_mapping_append(fyn,
+ fy_node_create_scalarf(fyd, "%s()", expr->fym->name),
+ fyn_seq);
+ }
+ if (rc)
+ goto err_out;
+
+ return fyn;
+
+err_out:
+ fy_node_free(fyn_seq);
+ fy_node_free(fyn);
+ return NULL;
+}
+
+struct fy_document *fy_path_expr_to_document(struct fy_path_expr *expr)
+{
+ struct fy_document *fyd = NULL;
+
+ if (!expr)
+ return NULL;
+
+ fyd = fy_document_create(NULL);
+ if (!fyd)
+ return NULL;
+
+ fyd->root = fy_path_expr_to_node_internal(fyd, expr);
+ if (!fyd->root)
+ goto err_out;
+
+ return fyd;
+
+err_out:
+ fy_document_destroy(fyd);
+ return NULL;
+}
+
+enum fy_path_expr_type fy_map_token_to_path_expr_type(enum fy_token_type type, enum fy_expr_mode mode)
+{
+ switch (type) {
+ case FYTT_PE_ROOT:
+ return fpet_root;
+ case FYTT_PE_THIS:
+ return fpet_this;
+ case FYTT_PE_PARENT:
+ case FYTT_PE_SIBLING: /* sibling maps to a chain of fpet_parent */
+ return fpet_parent;
+ case FYTT_PE_MAP_KEY:
+ return fpet_map_key;
+ case FYTT_PE_SEQ_INDEX:
+ return fpet_seq_index;
+ case FYTT_PE_SEQ_SLICE:
+ return fpet_seq_slice;
+ case FYTT_PE_EVERY_CHILD:
+ return fpet_every_child;
+ case FYTT_PE_EVERY_CHILD_R:
+ return fpet_every_child_r;
+ case FYTT_PE_ALIAS:
+ return fpet_alias;
+ case FYTT_PE_SCALAR_FILTER:
+ return fpet_filter_scalar;
+ case FYTT_PE_COLLECTION_FILTER:
+ return fpet_filter_collection;
+ case FYTT_PE_SEQ_FILTER:
+ return fpet_filter_sequence;
+ case FYTT_PE_MAP_FILTER:
+ return fpet_filter_mapping;
+ case FYTT_PE_UNIQUE_FILTER:
+ return fpet_filter_unique;
+ case FYTT_PE_COMMA:
+ return mode == fyem_path ? fpet_multi : fpet_arg_separator;
+ case FYTT_PE_SLASH:
+ return fpet_chain;
+ case FYTT_PE_BARBAR:
+ return fpet_logical_or;
+ case FYTT_PE_AMPAMP:
+ return fpet_logical_and;
+
+ case FYTT_PE_EQEQ:
+ return fpet_eq;
+ case FYTT_PE_NOTEQ:
+ return fpet_neq;
+ case FYTT_PE_LT:
+ return fpet_lt;
+ case FYTT_PE_GT:
+ return fpet_gt;
+ case FYTT_PE_LTE:
+ return fpet_lte;
+ case FYTT_PE_GTE:
+ return fpet_gte;
+
+ case FYTT_SCALAR:
+ return fpet_scalar;
+
+ case FYTT_SE_PLUS:
+ return fpet_plus;
+ case FYTT_SE_MINUS:
+ return fpet_minus;
+ case FYTT_SE_MULT:
+ return fpet_mult;
+ case FYTT_SE_DIV:
+ return fpet_div;
+
+ case FYTT_PE_LPAREN:
+ return fpet_lparen;
+ case FYTT_PE_RPAREN:
+ return fpet_rparen;
+
+ case FYTT_SE_METHOD:
+ case FYTT_PE_METHOD:
+ return fpet_method;
+
+ default:
+ /* note parentheses do not have an expression */
+ assert(0);
+ break;
+ }
+ return fpet_none;
+}
+
+bool fy_token_type_is_operand(enum fy_token_type type)
+{
+ return type == FYTT_PE_ROOT ||
+ type == FYTT_PE_THIS ||
+ type == FYTT_PE_PARENT ||
+ type == FYTT_PE_MAP_KEY ||
+ type == FYTT_PE_SEQ_INDEX ||
+ type == FYTT_PE_SEQ_SLICE ||
+ type == FYTT_PE_EVERY_CHILD ||
+ type == FYTT_PE_EVERY_CHILD_R ||
+ type == FYTT_PE_ALIAS ||
+
+ type == FYTT_SCALAR;
+}
+
+bool fy_token_type_is_operator(enum fy_token_type type)
+{
+ return type == FYTT_PE_SLASH ||
+ type == FYTT_PE_SCALAR_FILTER ||
+ type == FYTT_PE_COLLECTION_FILTER ||
+ type == FYTT_PE_SEQ_FILTER ||
+ type == FYTT_PE_MAP_FILTER ||
+ type == FYTT_PE_UNIQUE_FILTER ||
+ type == FYTT_PE_SIBLING ||
+ type == FYTT_PE_COMMA ||
+ type == FYTT_PE_BARBAR ||
+ type == FYTT_PE_AMPAMP ||
+ type == FYTT_PE_LPAREN ||
+ type == FYTT_PE_RPAREN ||
+
+ type == FYTT_PE_EQEQ ||
+ type == FYTT_PE_NOTEQ ||
+ type == FYTT_PE_LT ||
+ type == FYTT_PE_GT ||
+ type == FYTT_PE_LTE ||
+ type == FYTT_PE_GTE ||
+
+ type == FYTT_SE_PLUS ||
+ type == FYTT_SE_MINUS ||
+ type == FYTT_SE_MULT ||
+ type == FYTT_SE_DIV;
+}
+
+bool fy_token_type_is_operand_or_operator(enum fy_token_type type)
+{
+ return fy_token_type_is_operand(type) ||
+ fy_token_type_is_operator(type);
+}
+
+int fy_path_expr_type_prec(enum fy_path_expr_type type)
+{
+ switch (type) {
+ default:
+ return -1; /* terminals */
+ case fpet_filter_collection:
+ case fpet_filter_scalar:
+ case fpet_filter_sequence:
+ case fpet_filter_mapping:
+ case fpet_filter_unique:
+ return 5;
+ case fpet_logical_or:
+ case fpet_logical_and:
+ return 4;
+ case fpet_multi:
+ return 11;
+ case fpet_eq:
+ case fpet_neq:
+ case fpet_lt:
+ case fpet_gt:
+ case fpet_lte:
+ case fpet_gte:
+ return 7;
+ case fpet_mult:
+ case fpet_div:
+ return 9;
+ case fpet_plus:
+ case fpet_minus:
+ return 8;
+ case fpet_chain:
+ return 10;
+ case fpet_lparen:
+ case fpet_rparen:
+ case fpet_method:
+ return 1000;
+ case fpet_arg_separator:
+ return 1; /* lowest */
+ }
+ return -1;
+}
+
+static inline FY_UNUSED void
+dump_operand_stack(struct fy_path_parser *fypp)
+{
+ return fy_expr_stack_dump(fypp->cfg.diag, &fypp->operands);
+}
+
+static inline int
+push_operand(struct fy_path_parser *fypp, struct fy_path_expr *expr)
+{
+ return fy_expr_stack_push(&fypp->operands, expr);
+}
+
+static inline FY_UNUSED struct fy_path_expr *
+peek_operand_at(struct fy_path_parser *fypp, unsigned int pos)
+{
+ return fy_expr_stack_peek_at(&fypp->operands, pos);
+}
+
+static inline FY_UNUSED struct fy_path_expr *
+peek_operand(struct fy_path_parser *fypp)
+{
+ return fy_expr_stack_peek(&fypp->operands);
+}
+
+static inline FY_UNUSED struct fy_path_expr *
+pop_operand(struct fy_path_parser *fypp)
+{
+ return fy_expr_stack_pop(&fypp->operands);
+}
+
+#define PREFIX 0
+#define INFIX 1
+#define SUFFIX 2
+
+int fy_token_type_operator_placement(enum fy_token_type type)
+{
+ switch (type) {
+ case FYTT_PE_SLASH: /* SLASH is special at the start of the expression */
+ case FYTT_PE_COMMA:
+ case FYTT_PE_BARBAR:
+ case FYTT_PE_AMPAMP:
+ case FYTT_PE_EQEQ:
+ case FYTT_PE_NOTEQ:
+ case FYTT_PE_LT:
+ case FYTT_PE_GT:
+ case FYTT_PE_LTE:
+ case FYTT_PE_GTE:
+ case FYTT_SE_PLUS:
+ case FYTT_SE_MINUS:
+ case FYTT_SE_MULT:
+ case FYTT_SE_DIV:
+ return INFIX;
+ case FYTT_PE_SCALAR_FILTER:
+ case FYTT_PE_COLLECTION_FILTER:
+ case FYTT_PE_SEQ_FILTER:
+ case FYTT_PE_MAP_FILTER:
+ case FYTT_PE_UNIQUE_FILTER:
+ return SUFFIX;
+ case FYTT_PE_SIBLING:
+ return PREFIX;
+ default:
+ break;
+ }
+ return -1;
+}
+
+const struct fy_mark *fy_path_expr_start_mark(struct fy_path_expr *expr)
+{
+ if (!expr)
+ return NULL;
+
+ return fy_token_start_mark(expr->fyt);
+}
+
+const struct fy_mark *fy_path_expr_end_mark(struct fy_path_expr *expr)
+{
+ if (!expr)
+ return NULL;
+
+ return fy_token_end_mark(expr->fyt);
+}
+
+struct fy_token *
+expr_to_token_mark(struct fy_path_expr *expr, struct fy_input *fyi)
+{
+ const struct fy_mark *ms, *me;
+ struct fy_atom handle;
+
+ if (!expr || !fyi)
+ return NULL;
+
+ ms = fy_path_expr_start_mark(expr);
+ assert(ms);
+ me = fy_path_expr_end_mark(expr);
+ assert(me);
+
+ memset(&handle, 0, sizeof(handle));
+ handle.start_mark = *ms;
+ handle.end_mark = *me;
+ handle.fyi = fyi;
+ handle.style = FYAS_PLAIN;
+ handle.chomp = FYAC_CLIP;
+
+ return fy_token_create(FYTT_INPUT_MARKER, &handle);
+}
+
+struct fy_token *
+expr_lr_to_token_mark(struct fy_path_expr *exprl, struct fy_path_expr *exprr, struct fy_input *fyi)
+{
+ const struct fy_mark *ms, *me;
+ struct fy_atom handle;
+
+ if (!exprl || !exprr || !fyi)
+ return NULL;
+
+ ms = fy_path_expr_start_mark(exprl);
+ assert(ms);
+ me = fy_path_expr_end_mark(exprr);
+ assert(me);
+
+ memset(&handle, 0, sizeof(handle));
+ handle.start_mark = *ms;
+ handle.end_mark = *me;
+ handle.fyi = fyi;
+ handle.style = FYAS_PLAIN;
+ handle.chomp = FYAC_CLIP;
+
+ return fy_token_create(FYTT_INPUT_MARKER, &handle);
+}
+
+int
+fy_path_expr_order(struct fy_path_expr *expr1, struct fy_path_expr *expr2)
+{
+ const struct fy_mark *m1 = NULL, *m2 = NULL;
+
+ if (expr1)
+ m1 = fy_path_expr_start_mark(expr1);
+
+ if (expr2)
+ m2 = fy_path_expr_start_mark(expr2);
+
+ if (m1 == m2)
+ return 0;
+
+ if (!m1)
+ return -1;
+
+ if (!m2)
+ return 1;
+
+ return m1->input_pos == m2->input_pos ? 0 :
+ m1->input_pos < m2->input_pos ? -1 : 1;
+}
+
+int push_operand_lr(struct fy_path_parser *fypp,
+ enum fy_path_expr_type type,
+ struct fy_path_expr *exprl, struct fy_path_expr *exprr,
+ bool optimize)
+{
+ struct fy_reader *fyr;
+ struct fy_path_expr *expr = NULL, *exprt;
+ const struct fy_mark *ms = NULL, *me = NULL;
+ struct fy_atom handle;
+ int ret;
+
+ optimize = false;
+ assert(exprl || exprr);
+
+ fyr = &fypp->reader;
+
+#if 0
+ fyr_notice(fyr, ">>> %s <%s> l=<%s> r=<%s>\n", __func__,
+ fy_path_expr_type_txt[type],
+ exprl ?fy_path_expr_type_txt[exprl->type] : "NULL",
+ exprr ?fy_path_expr_type_txt[exprr->type] : "NULL");
+#endif
+
+ expr = fy_path_expr_alloc_recycle(fypp);
+ fyr_error_check(fyr, expr, err_out,
+ "fy_path_expr_alloc_recycle() failed\n");
+
+ expr->type = type;
+ expr->fyt = NULL;
+
+ if (exprl) {
+ assert(exprl->fyt);
+ ms = fy_token_start_mark(exprl->fyt);
+ assert(ms);
+ } else {
+ ms = fy_token_start_mark(exprr->fyt);
+ assert(ms);
+ }
+
+ if (exprr) {
+ assert(exprr->fyt);
+ me = fy_token_end_mark(exprr->fyt);
+ assert(me);
+ } else {
+ me = fy_token_end_mark(exprr->fyt);
+ assert(me);
+ }
+
+ assert(ms && me);
+
+ memset(&handle, 0, sizeof(handle));
+ handle.start_mark = *ms;
+ handle.end_mark = *me;
+ handle.fyi = fypp->fyi;
+ handle.style = FYAS_PLAIN;
+ handle.chomp = FYAC_CLIP;
+
+ if (exprl) {
+ if (type == exprl->type && fy_path_expr_type_is_mergeable(type)) {
+ while ((exprt = fy_path_expr_list_pop(&exprl->children)) != NULL) {
+ fy_path_expr_list_add_tail(&expr->children, exprt);
+ exprt->parent = expr;
+ }
+ fy_path_expr_free_recycle(fypp, exprl);
+ } else {
+ fy_path_expr_list_add_tail(&expr->children, exprl);
+ exprl->parent = expr;
+ }
+ exprl = NULL;
+ }
+
+ if (exprr) {
+ if (type == exprr->type && fy_path_expr_type_is_mergeable(type)) {
+ while ((exprt = fy_path_expr_list_pop(&exprr->children)) != NULL) {
+ fy_path_expr_list_add_tail(&expr->children, exprt);
+ exprt->parent = expr;
+ }
+ fy_path_expr_free_recycle(fypp, exprr);
+ } else {
+ fy_path_expr_list_add_tail(&expr->children, exprr);
+ exprr->parent = expr;
+ }
+ exprr = NULL;
+ }
+
+ expr->fyt = fy_token_create(FYTT_INPUT_MARKER, &handle);
+ fyr_error_check(fyr, expr->fyt, err_out,
+ "expr_to_token_mark() failed\n");
+
+ ret = push_operand(fypp, expr);
+ fyr_error_check(fyr, !ret, err_out,
+ "push_operand() failed\n");
+
+#ifdef DEBUG_EXPR
+ FYR_TOKEN_DIAG(fyr, expr->fyt,
+ FYDF_NOTICE, FYEM_PARSE, "pushed operand lr");
+#endif
+
+ return 0;
+err_out:
+ fy_path_expr_free(expr);
+ fy_path_expr_free(exprl);
+ fy_path_expr_free(exprr);
+ return -1;
+}
+
+enum fy_method_idx {
+ fymi_test,
+ fymi_sum,
+ fymi_this,
+ fymi_parent,
+ fymi_root,
+ fymi_any,
+ fymi_all,
+ fymi_select,
+ fymi_key,
+ fymi_value,
+ fymi_index,
+ fymi_null,
+};
+
+static struct fy_walk_result *
+common_builtin_ref_exec(const struct fy_method *fym,
+ struct fy_path_exec *fypx, int level,
+ struct fy_path_expr *expr,
+ struct fy_walk_result *input,
+ struct fy_walk_result **args, int nargs);
+
+static struct fy_walk_result *
+common_builtin_collection_exec(const struct fy_method *fym,
+ struct fy_path_exec *fypx, int level,
+ struct fy_path_expr *expr,
+ struct fy_walk_result *input,
+ struct fy_walk_result **args, int nargs);
+
+
+static struct fy_walk_result *
+test_exec(const struct fy_method *fym,
+ struct fy_path_exec *fypx, int level,
+ struct fy_path_expr *expr,
+ struct fy_walk_result *input,
+ struct fy_walk_result **args, int nargs);
+
+static struct fy_walk_result *
+sum_exec(const struct fy_method *fym,
+ struct fy_path_exec *fypx, int level,
+ struct fy_path_expr *expr,
+ struct fy_walk_result *input,
+ struct fy_walk_result **args, int nargs);
+
+static const struct fy_method fy_methods[] = {
+ [fymi_test] = {
+ .name = "test",
+ .len = 4,
+ .mode = fyem_scalar,
+ .nargs = 1,
+ .exec = test_exec,
+ },
+ [fymi_sum] = {
+ .name = "sum",
+ .len = 3,
+ .mode = fyem_scalar,
+ .nargs = 2,
+ .exec = sum_exec,
+ },
+ [fymi_this] = {
+ .name = "this",
+ .len = 4,
+ .mode = fyem_path,
+ .nargs = 0,
+ .exec = common_builtin_ref_exec,
+ },
+ [fymi_parent] = {
+ .name = "parent",
+ .len = 6,
+ .mode = fyem_path,
+ .nargs = 0,
+ .exec = common_builtin_ref_exec,
+ },
+ [fymi_root] = {
+ .name = "root",
+ .len = 4,
+ .mode = fyem_path,
+ .nargs = 0,
+ .exec = common_builtin_ref_exec,
+ },
+ [fymi_any] = {
+ .name = "any",
+ .len = 3,
+ .mode = fyem_path,
+ .nargs = 1,
+ .exec = common_builtin_collection_exec,
+ },
+ [fymi_all] = {
+ .name = "all",
+ .len = 3,
+ .mode = fyem_path,
+ .nargs = 1,
+ .exec = common_builtin_collection_exec,
+ },
+ [fymi_select] = {
+ .name = "select",
+ .len = 6,
+ .mode = fyem_path,
+ .nargs = 1,
+ .exec = common_builtin_collection_exec,
+ },
+ [fymi_key] = {
+ .name = "key",
+ .len = 3,
+ .mode = fyem_path,
+ .nargs = 1,
+ .exec = common_builtin_ref_exec,
+ },
+ [fymi_value] = {
+ .name = "value",
+ .len = 5,
+ .mode = fyem_path,
+ .nargs = 1,
+ .exec = common_builtin_ref_exec,
+ },
+ [fymi_index] = {
+ .name = "index",
+ .len = 5,
+ .mode = fyem_path,
+ .nargs = 1,
+ .exec = common_builtin_ref_exec,
+ },
+ [fymi_null] = {
+ .name = "null",
+ .len = 4,
+ .mode = fyem_path,
+ .nargs = 0,
+ .exec = common_builtin_ref_exec,
+ },
+};
+
+static inline int fy_method_to_builtin_idx(const struct fy_method *fym)
+{
+ if (!fym || fym < fy_methods || fym >= &fy_methods[ARRAY_SIZE(fy_methods)])
+ return -1;
+ return fym - fy_methods;
+}
+
+static struct fy_walk_result *
+common_builtin_ref_exec(const struct fy_method *fym,
+ struct fy_path_exec *fypx, int level,
+ struct fy_path_expr *expr,
+ struct fy_walk_result *input,
+ struct fy_walk_result **args, int nargs)
+{
+ enum fy_method_idx midx;
+ struct fy_walk_result *output = NULL;
+ struct fy_walk_result *fwr, *fwrn;
+ struct fy_node *fyn, *fynt;
+ int i;
+
+ if (!fypx || !input)
+ goto out;
+
+ i = fy_method_to_builtin_idx(fym);
+ if (i < 0)
+ goto out;
+ midx = (enum fy_method_idx)i;
+
+ switch (midx) {
+ case fymi_key:
+ case fymi_value:
+ if (!args || nargs != 1 || !args[0] || args[0]->type != fwrt_string)
+ goto out;
+ break;
+ case fymi_index:
+ if (!args || nargs != 1 || !args[0] || args[0]->type != fwrt_number)
+ goto out;
+ break;
+ case fymi_root:
+ case fymi_parent:
+ case fymi_this:
+ case fymi_null:
+ if (nargs != 0)
+ goto out;
+ break;
+ default:
+ goto out;
+ }
+
+ output = fy_path_exec_walk_result_create(fypx, fwrt_refs);
+ assert(output);
+
+ for (fwr = fy_walk_result_iter_start(input); fwr;
+ fwr = fy_walk_result_iter_next(input, fwr)) {
+
+ if (fwr->type != fwrt_node_ref || !fwr->fyn)
+ continue;
+
+ fynt = fwr->fyn;
+
+ switch (midx) {
+ case fymi_key:
+ case fymi_value:
+ case fymi_index:
+ case fymi_this:
+ case fymi_null:
+ /* dereference alias */
+ if (fy_node_is_alias(fynt)) {
+ // fprintf(stderr, "%s: %s calling fy_node_alias_resolve_by_ypath()\n", __func__, fy_node_get_path_alloca(fyn));
+ fynt = fy_node_alias_resolve_by_ypath(fynt);
+ }
+ break;
+ default:
+ break;
+ }
+
+ fyn = NULL;
+ switch (midx) {
+ case fymi_key:
+ if (!fy_node_is_mapping(fynt))
+ break;
+ fyn = fy_node_mapping_lookup_key_by_string(fynt, args[0]->string, FY_NT);
+ break;
+
+ case fymi_value:
+ if (!fy_node_is_mapping(fynt))
+ break;
+ fyn = fy_node_mapping_lookup_by_string(fynt, args[0]->string, FY_NT);
+ break;
+
+ case fymi_index:
+ if (!fy_node_is_sequence(fynt))
+ break;
+ fyn = fy_node_sequence_get_by_index(fynt, (int)args[0]->number);
+ break;
+
+ case fymi_root:
+ fyn = fy_document_root(fy_node_document(fynt));
+ break;
+
+ case fymi_parent:
+ fyn = fy_node_get_parent(fynt);
+ break;
+
+ case fymi_null:
+ if (!fy_node_is_mapping(fynt))
+ break;
+ fyn = fy_node_mapping_lookup_value_by_null_key(fynt);
+ break;
+
+ case fymi_this:
+ fyn = fynt;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!fyn)
+ continue;
+
+ fwrn = fy_path_exec_walk_result_create(fypx, fwrt_node_ref, fyn);
+ assert(fwrn);
+
+ fy_walk_result_list_add_tail(&output->refs, fwrn);
+ }
+
+ /* output convert zero to NULL, singular to node_ref */
+ if (output && output->type == fwrt_refs) {
+ if (fy_walk_result_list_empty(&output->refs)) {
+ fy_walk_result_free(output);
+ output = NULL;
+ } else if (fy_walk_result_list_is_singular(&output->refs)) {
+ fwr = fy_walk_result_list_pop(&output->refs);
+ assert(fwr);
+ fy_walk_result_free(output);
+ output = fwr;
+ }
+ }
+
+out:
+ fy_walk_result_free(input);
+ if (args) {
+ for (i = 0; i < nargs; i++)
+ fy_walk_result_free(args[i]);
+ }
+ return output;
+}
+
+static struct fy_walk_result *
+common_builtin_collection_exec(const struct fy_method *fym,
+ struct fy_path_exec *fypx, int level,
+ struct fy_path_expr *expr,
+ struct fy_walk_result *input,
+ struct fy_walk_result **args, int nargs)
+{
+ enum fy_method_idx midx;
+ struct fy_walk_result *output = NULL;
+ struct fy_walk_result *fwr, *fwrn, *fwrt;
+ struct fy_path_expr *expr_arg;
+ bool match, done;
+ int input_count, match_count;
+ int i;
+
+ if (!fypx || !input)
+ goto out;
+
+ i = fy_method_to_builtin_idx(fym);
+ if (i < 0)
+ goto out;
+ midx = (enum fy_method_idx)i;
+
+ switch (midx) {
+ case fymi_any:
+ case fymi_all:
+ case fymi_select:
+ if (!args || nargs != 1 || !args[0])
+ goto out;
+ break;
+ default:
+ goto out;
+ }
+
+ expr_arg = fy_path_expr_list_head(&expr->children);
+ assert(expr_arg);
+
+ /* only handle inputs of node and refs */
+ if (input->type != fwrt_node_ref && input->type != fwrt_refs)
+ goto out;
+
+ output = NULL;
+
+ switch (midx) {
+ case fymi_select:
+ output = fy_path_exec_walk_result_create(fypx, fwrt_refs);
+ assert(output);
+ break;
+ default:
+ break;
+ }
+
+
+ done = false;
+ match_count = input_count = 0;
+ for (fwr = fy_walk_result_iter_start(input); fwr && !done; fwr = fy_walk_result_iter_next(input, fwr)) {
+
+ input_count++;
+
+ fwrt = fy_walk_result_clone(fwr);
+ assert(fwrt);
+
+ fwrn = fy_path_expr_execute(fypx, level + 1, expr_arg, fwrt, expr->type);
+
+ match = fwrn != NULL;
+ if (match)
+ match_count++;
+
+ switch (midx) {
+ case fymi_any:
+ /* on any match, we're done */
+ if (match)
+ done = true;
+ break;
+
+ case fymi_all:
+ /* on any non match, we're done */
+ if (!match)
+ done = true;
+ break;
+
+ case fymi_select:
+ /* select only works on node refs */
+ if (fwr->type != fwrt_node_ref)
+ break;
+ if (match) {
+ fwrt = fy_walk_result_clone(fwr);
+ assert(fwrt);
+ fy_walk_result_list_add_tail(&output->refs, fwrt);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (fwrn)
+ fy_walk_result_free(fwrn);
+ }
+
+ switch (midx) {
+ case fymi_any:
+ if (input_count > 0 && match_count <= input_count) {
+ output = input;
+ input = NULL;
+ }
+ break;
+ case fymi_all:
+ if (input_count > 0 && match_count == input_count) {
+ output = input;
+ input = NULL;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* output convert zero to NULL, singular to node_ref */
+ if (output && output->type == fwrt_refs) {
+ if (fy_walk_result_list_empty(&output->refs)) {
+ fy_walk_result_free(output);
+ output = NULL;
+ } else if (fy_walk_result_list_is_singular(&output->refs)) {
+ fwr = fy_walk_result_list_pop(&output->refs);
+ assert(fwr);
+ fy_walk_result_free(output);
+ output = fwr;
+ }
+ }
+
+out:
+ if (input)
+ fy_walk_result_free(input);
+ if (args) {
+ for (i = 0; i < nargs; i++)
+ fy_walk_result_free(args[i]);
+ }
+
+ return output;
+}
+
+static struct fy_walk_result *
+test_exec(const struct fy_method *fym,
+ struct fy_path_exec *fypx, int level,
+ struct fy_path_expr *expr,
+ struct fy_walk_result *input,
+ struct fy_walk_result **args, int nargs)
+{
+ int i;
+ struct fy_walk_result *output = NULL;
+
+ if (!fypx || !args || nargs != 1)
+ goto out;
+
+ /* require a single number argument */
+ if (!args[0] || args[0]->type != fwrt_number)
+ goto out;
+
+ /* reuse argument */
+ output = args[0];
+ args[0] = NULL;
+
+ /* add 1 to the number */
+ output->number += 1;
+
+out:
+ fy_walk_result_free(input);
+ if (args) {
+ for (i = 0; i < nargs; i++)
+ fy_walk_result_free(args[i]);
+ }
+ return output;
+}
+
+static struct fy_walk_result *
+sum_exec(const struct fy_method *fym,
+ struct fy_path_exec *fypx, int level,
+ struct fy_path_expr *expr,
+ struct fy_walk_result *input,
+ struct fy_walk_result **args, int nargs)
+{
+ int i;
+ struct fy_walk_result *output = NULL;
+
+ if (!fypx || !args || nargs != 2)
+ goto out;
+
+ /* require two number argument */
+ if (!args[0] || args[0]->type != fwrt_number ||
+ !args[1] || args[1]->type != fwrt_number)
+ goto out;
+
+ /* reuse argument */
+ output = args[0];
+ args[0] = NULL;
+
+ /* add 1 to the number */
+ output->number += args[1]->number;
+
+out:
+ fy_walk_result_free(input);
+ if (args) {
+ for (i = 0; i < nargs; i++)
+ fy_walk_result_free(args[i]);
+ }
+ return output;
+}
+
+int evaluate_method(struct fy_path_parser *fypp, struct fy_path_expr *exprm,
+ struct fy_path_expr *exprl, struct fy_path_expr *exprr)
+{
+ struct fy_reader *fyr;
+ struct fy_path_expr *exprt;
+ struct fy_token *fyt;
+ const char *text;
+ size_t len;
+ const struct fy_method *fym;
+ unsigned int i, count;
+ int ret;
+
+ fyr = &fypp->reader;
+
+#ifdef DEBUG_EXPR
+ FYR_TOKEN_DIAG(fyr, exprm->fyt,
+ FYDF_NOTICE, FYEM_PARSE, "evaluating method");
+#endif
+
+ text = fy_token_get_text(exprm->fyt, &len);
+ fyr_error_check(fyr, text, err_out,
+ "fy_token_get_text() failed\n");
+
+ for (i = 0, fym = fy_methods; i < ARRAY_SIZE(fy_methods); i++, fym++) {
+ if (fym->len == len && !memcmp(text, fym->name, len))
+ break;
+ }
+
+ FYR_TOKEN_ERROR_CHECK(fyr, exprm->fyt, FYEM_PARSE,
+ i < ARRAY_SIZE(fy_methods), err_out,
+ "invalid method %.*s\n", (int)len, text);
+
+ /* reuse exprm */
+ count = 0;
+ while ((exprt = fy_expr_stack_peek(&fypp->operands)) != NULL &&
+ fy_path_expr_order(exprm, exprt) < 0) {
+
+ exprt = fy_expr_stack_pop(&fypp->operands);
+ assert(exprt);
+
+#ifdef DEBUG_EXPR
+ FYR_TOKEN_DIAG(fyr, exprt->fyt,
+ FYDF_NOTICE, FYEM_PARSE, "poped argument %d", count);
+#endif
+
+ /* add in reverse order */
+ fy_path_expr_list_add(&exprm->children, exprt);
+ exprt->parent = exprm;
+ count++;
+
+ }
+
+ if (exprr) {
+ fyt = expr_lr_to_token_mark(exprm, exprr, fypp->fyi);
+ fyr_error_check(fyr, fyt, err_out,
+ "expr_lr_to_token_mark() failed\n");
+
+ fy_token_unref(exprm->fyt);
+ exprm->fyt = fyt;
+ }
+
+ FYR_TOKEN_ERROR_CHECK(fyr, exprm->fyt, FYEM_PARSE,
+ fym->nargs == count, err_out,
+ "too %s argument for method %s, expected %d, got %d\n",
+ fym->nargs < count ? "many" : "few",
+ fym->name, fym->nargs, count);
+
+ exprm->fym = fym;
+
+ if (exprl)
+ fy_path_expr_free_recycle(fypp, exprl);
+ if (exprr)
+ fy_path_expr_free_recycle(fypp, exprr);
+
+ /* and push as an operand */
+ ret = push_operand(fypp, exprm);
+ fyr_error_check(fyr, !ret, err_out,
+ "push_operand() failed\n");
+
+#ifdef DEBUG_EXPR
+ FYR_TOKEN_DIAG(fyr, exprm->fyt,
+ FYDF_NOTICE, FYEM_PARSE, "pushed operand evaluate_method");
+#endif
+ return 0;
+
+err_out:
+ /* we don't need the parentheses operators */
+ fy_path_expr_free_recycle(fypp, exprm);
+ if (exprl)
+ fy_path_expr_free_recycle(fypp, exprl);
+ if (exprr)
+ fy_path_expr_free_recycle(fypp, exprr);
+
+ return -1;
+}
+
+int evaluate_new(struct fy_path_parser *fypp)
+{
+ struct fy_reader *fyr;
+ struct fy_path_expr *expr = NULL, *expr_peek, *exprt;
+ struct fy_path_expr *exprl = NULL, *exprr = NULL, *chain = NULL, *exprm = NULL;
+ struct fy_path_expr *parent = NULL;
+ struct fy_token *fyt;
+ enum fy_path_expr_type type, etype;
+ int ret;
+
+ fyr = &fypp->reader;
+
+ expr = fy_expr_stack_pop(&fypp->operators);
+ fyr_error_check(fyr, expr, err_out,
+ "pop_operator() failed to find token operator to evaluate\n");
+
+ assert(expr->fyt);
+
+#ifdef DEBUG_EXPR
+ FYR_TOKEN_DIAG(fyr, expr->fyt,
+ FYDF_NOTICE, FYEM_PARSE, "poped operator expression");
+#endif
+
+ exprl = NULL;
+ exprr = NULL;
+ type = expr->type;
+ switch (type) {
+
+ case fpet_chain:
+
+ /* dump_operand_stack(fypp); */
+ /* dump_operator_stack(fypp); */
+
+ /* peek the next operator */
+ expr_peek = fy_expr_stack_peek(&fypp->operators);
+
+ /* pop the top in either case */
+ exprr = fy_expr_stack_pop(&fypp->operands);
+ if (!exprr) {
+ // fyr_notice(fyr, "ROOT value (with no arguments)\n");
+
+ /* conver to root and push to operands */
+ expr->type = fpet_root;
+
+ ret = push_operand(fypp, expr);
+ fyr_error_check(fyr, !ret, err_out,
+ "push_operand() failed\n");
+ return 0;
+ }
+
+#ifdef DEBUG_EXPR
+ FYR_TOKEN_DIAG(fyr, exprr->fyt,
+ FYDF_NOTICE, FYEM_PARSE, "exprr");
+#endif
+
+ /* expression is to the left, that means it's a root chain */
+ if (fy_path_expr_order(expr, exprr) < 0 &&
+ (!(exprl = fy_expr_stack_peek(&fypp->operands)) ||
+ (expr_peek && fy_path_expr_order(exprl, expr_peek) <= 0))) {
+
+ // fyr_notice(fyr, "ROOT operator (with arguments)\n");
+
+ exprl = fy_path_expr_alloc_recycle(fypp);
+ fyr_error_check(fyr, exprl, err_out,
+ "fy_path_expr_alloc_recycle() failed\n");
+ exprl->type = fpet_root;
+
+ /* move token to the root */
+ exprl->fyt = expr->fyt;
+ expr->fyt = NULL;
+
+ } else if (!(exprl = fy_expr_stack_pop(&fypp->operands))) {
+
+ // fyr_notice(fyr, "COLLECTION operator\n");
+
+ exprl = exprr;
+
+ exprr = fy_path_expr_alloc_recycle(fypp);
+ fyr_error_check(fyr, exprr, err_out,
+ "fy_path_expr_alloc_recycle() failed\n");
+ exprr->type = fpet_filter_collection;
+
+ /* move token to the filter collection */
+ exprr->fyt = expr->fyt;
+ expr->fyt = NULL;
+
+ } else {
+ assert(exprr && exprl);
+
+ // fyr_notice(fyr, "CHAIN operator\n");
+ }
+
+ /* we don't need the chain operator now */
+ fy_path_expr_free_recycle(fypp, expr);
+ expr = NULL;
+
+ ret = push_operand_lr(fypp, fpet_chain, exprl, exprr, true);
+ fyr_error_check(fyr, !ret, err_out,
+ "push_operand_lr() failed\n");
+ return 0;
+
+ case fpet_multi:
+ case fpet_logical_or:
+ case fpet_logical_and:
+
+ case fpet_eq:
+ case fpet_neq:
+ case fpet_lt:
+ case fpet_gt:
+ case fpet_lte:
+ case fpet_gte:
+
+ case fpet_plus:
+ case fpet_minus:
+ case fpet_mult:
+ case fpet_div:
+
+ exprl = NULL;
+ exprr = NULL;
+ exprt = fy_expr_stack_peek(&fypp->operators);
+
+ // fyr_error(fyr, "mode=%s top-mode=%s\n",
+ // fy_expr_mode_txt[fypp->expr_mode],
+ // exprt ? fy_expr_mode_txt[exprt->expr_mode] : "");
+
+#if 0
+ exprr = fy_expr_stack_peek(&fypp->operands);
+ if (exprr && exprt && fy_path_expr_order(exprr, exprt) <= 0)
+ exprr = NULL;
+ else
+#endif
+ exprr = fy_expr_stack_pop(&fypp->operands);
+ fyr_error_check(fyr, exprr, err_out,
+ "fy_expr_stack_pop() failed for exprr\n");
+
+#if 0
+ exprl = fy_expr_stack_peek_at(&fypp->operands, 1);
+ if (exprl && exprt && fy_path_expr_order(exprl, exprt) <= 0)
+ exprl = NULL;
+ else
+#endif
+ exprl = fy_expr_stack_pop(&fypp->operands);
+ fyr_error_check(fyr, exprl, err_out,
+ "fy_expr_stack_pop() failed for exprl\n");
+
+ /* we don't need the operator now */
+ fy_path_expr_free_recycle(fypp, expr);
+ expr = NULL;
+
+ ret = push_operand_lr(fypp, type, exprl, exprr, true);
+ fyr_error_check(fyr, !ret, err_out,
+ "push_operand_lr() failed\n");
+
+ break;
+
+ case fpet_filter_collection:
+ case fpet_filter_scalar:
+ case fpet_filter_sequence:
+ case fpet_filter_mapping:
+ case fpet_filter_unique:
+
+ exprl = fy_expr_stack_pop(&fypp->operands);
+ FYR_TOKEN_ERROR_CHECK(fyr, expr->fyt, FYEM_PARSE,
+ exprl, err_out,
+ "filter operator without argument");
+
+ exprr = fy_path_expr_alloc_recycle(fypp);
+ fyr_error_check(fyr, exprr, err_out,
+ "fy_path_expr_alloc_recycle() failed\n");
+ exprr->type = type;
+
+ /* move token to the filter collection */
+ exprr->fyt = expr->fyt;
+ expr->fyt = NULL;
+
+ /* we don't need the operator now */
+ fy_path_expr_free_recycle(fypp, expr);
+ expr = NULL;
+
+ /* push as a chain */
+ ret = push_operand_lr(fypp, fpet_chain, exprl, exprr, true);
+ fyr_error_check(fyr, !ret, err_out,
+ "push_operand_lr() failed\n");
+
+ break;
+
+ case fpet_lparen:
+ abort();
+ assert(0);
+
+#ifdef DEBUG_EXPR
+ FYR_TOKEN_DIAG(fyr, expr->fyt,
+ FYDF_NOTICE, FYEM_PARSE, "(");
+#endif
+
+ return 0;
+
+ case fpet_arg_separator:
+
+ /* separator is right hand side of the expression now */
+ exprr = expr;
+ expr = NULL;
+
+ /* evaluate until we hit a match to the rparen */
+ exprl = fy_expr_stack_peek(&fypp->operators);
+
+ if (!fy_path_expr_type_is_lparen(exprl->type)) {
+ ret = evaluate_new(fypp);
+ if (ret)
+ goto err_out;
+ }
+ exprl = NULL;
+
+ fy_path_expr_free_recycle(fypp, exprr);
+ exprr = NULL;
+
+ break;
+
+ case fpet_rparen:
+
+ /* rparen is right hand side of the expression now */
+ exprr = expr;
+ expr = NULL;
+
+ /* evaluate until we hit a match to the rparen */
+ while ((exprl = fy_expr_stack_peek(&fypp->operators)) != NULL) {
+
+ if (fy_path_expr_type_is_lparen(exprl->type))
+ break;
+
+ ret = evaluate_new(fypp);
+ if (ret)
+ goto err_out;
+ }
+
+ FYR_TOKEN_ERROR_CHECK(fyr, exprr->fyt, FYEM_PARSE,
+ exprl, err_out,
+ "missing matching left parentheses");
+
+ exprl = fy_expr_stack_pop(&fypp->operators);
+ assert(exprl);
+
+ exprt = fy_expr_stack_peek(&fypp->operands);
+
+ etype = exprl->expr_mode == fyem_scalar ? fpet_scalar_expr : fpet_path_expr;
+
+ /* already is an expression, reuse */
+ if (exprt && exprt->type == etype) {
+
+ fyt = expr_lr_to_token_mark(exprl, exprr, fypp->fyi);
+ fyr_error_check(fyr, fyt, err_out,
+ "expr_lr_to_token_mark() failed\n");
+ fy_token_unref(exprt->fyt);
+ exprt->fyt = fyt;
+ exprt->expr_mode = exprl->expr_mode;
+
+ /* we don't need the parentheses operators */
+ fy_path_expr_free_recycle(fypp, exprl);
+ exprl = NULL;
+ fy_path_expr_free_recycle(fypp, exprr);
+ exprr = NULL;
+
+ return 0;
+ }
+
+ /* if it's method, evaluate */
+ exprm = fy_expr_stack_peek(&fypp->operators);
+ if (exprm && exprm->type == fpet_method) {
+
+ exprm = fy_expr_stack_pop(&fypp->operators);
+ assert(exprm);
+
+ return evaluate_method(fypp, exprm, exprl, exprr);
+ }
+
+ expr = fy_path_expr_alloc_recycle(fypp);
+ fyr_error_check(fyr, expr, err_out,
+ "fy_path_expr_alloc_recycle() failed\n");
+ expr->type = etype;
+ expr->expr_mode = exprl->expr_mode;
+
+ expr->fyt = expr_lr_to_token_mark(exprl, exprr, fypp->fyi);
+
+ exprt = fy_expr_stack_pop(&fypp->operands);
+
+ FYR_TOKEN_ERROR_CHECK(fyr, exprr->fyt, FYEM_PARSE,
+ exprt, err_out,
+ "empty expression in parentheses");
+
+ fy_path_expr_list_add_tail(&expr->children, exprt);
+ exprt->parent = expr;
+
+ /* pop all operands that after exprl */
+ while ((exprt = fy_expr_stack_peek(&fypp->operands)) != NULL &&
+ fy_path_expr_order(exprt, exprl) >= 0) {
+#ifdef DEBUG_EXPR
+ FYR_TOKEN_DIAG(fyr, exprt->fyt,
+ FYDF_NOTICE, FYEM_PARSE, "discarding argument");
+#endif
+ fy_path_expr_free_recycle(fypp, fy_expr_stack_pop(&fypp->operands));
+ }
+
+ if (exprl->expr_mode != fyem_none) {
+ fypp->expr_mode = exprl->expr_mode;
+#ifdef DEBUG_EXPR
+ fyr_notice(fyr, "poping expr_mode %s\n", fy_expr_mode_txt[fypp->expr_mode]);
+#endif
+ }
+
+ /* we don't need the parentheses operators */
+ fy_path_expr_free_recycle(fypp, exprl);
+ exprl = NULL;
+ fy_path_expr_free_recycle(fypp, exprr);
+ exprr = NULL;
+
+ ret = push_operand(fypp, expr);
+ fyr_error_check(fyr, !ret, err_out,
+ "push_operand() failed\n");
+
+#ifdef DEBUG_EXPR
+ FYR_TOKEN_DIAG(fyr, expr->fyt,
+ FYDF_NOTICE, FYEM_PARSE, "pushed operand evaluate_new");
+#endif
+ return 0;
+
+ case fpet_method:
+ return evaluate_method(fypp, expr, NULL, NULL);
+
+ /* shoud never */
+ case fpet_scalar_expr:
+ case fpet_path_expr:
+ assert(0);
+ abort();
+
+ default:
+ fyr_error(fyr, "Unknown expression %s\n", fy_path_expr_type_txt[expr->type]);
+ goto err_out;
+ }
+
+ return 0;
+
+err_out:
+
+#ifdef DEBUG_EXPR
+ if (expr)
+ fy_path_expr_dump(expr, fypp->cfg.diag, FYET_NOTICE, 0, "expr:");
+ if (exprl)
+ fy_path_expr_dump(exprl, fypp->cfg.diag, FYET_NOTICE, 0, "exprl:");
+ if (exprr)
+ fy_path_expr_dump(exprr, fypp->cfg.diag, FYET_NOTICE, 0, "exprr:");
+ if (chain)
+ fy_path_expr_dump(chain, fypp->cfg.diag, FYET_NOTICE, 0, "chain:");
+ if (parent)
+ fy_path_expr_dump(parent, fypp->cfg.diag, FYET_NOTICE, 0, "parent:");
+
+ fy_notice(fypp->cfg.diag, "operator stack\n");
+ fy_expr_stack_dump(fypp->cfg.diag, &fypp->operators);
+ fy_notice(fypp->cfg.diag, "operand stack\n");
+ fy_expr_stack_dump(fypp->cfg.diag, &fypp->operands);
+#endif
+
+ fy_path_expr_free(expr);
+ fy_path_expr_free(exprl);
+ fy_path_expr_free(exprr);
+ fy_path_expr_free(chain);
+ fy_path_expr_free(parent);
+
+ return -1;
+}
+
+int fy_path_check_expression_alias(struct fy_path_parser *fypp, struct fy_path_expr *expr)
+{
+ struct fy_reader *fyr;
+ struct fy_path_expr *exprn;
+ int rc;
+
+ if (!expr)
+ return 0;
+
+ fyr = &fypp->reader;
+
+ /* an alias with a parent.. must be the first one */
+ if (expr->type == fpet_alias && expr->parent) {
+
+ exprn = fy_path_expr_list_head(&expr->parent->children);
+
+ /* an alias may only be the first of a path expression */
+ FYR_TOKEN_ERROR_CHECK(fyr, expr->fyt, FYEM_PARSE,
+ expr == exprn, err_out,
+ "alias is not first in the path expresion");
+ }
+
+ for (exprn = fy_path_expr_list_head(&expr->children); exprn;
+ exprn = fy_path_expr_next(&expr->children, exprn)) {
+
+ rc = fy_path_check_expression_alias(fypp, exprn);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+
+err_out:
+ return -1;
+}
+
+/* check expression for validity */
+int fy_path_check_expression(struct fy_path_parser *fypp, struct fy_path_expr *expr)
+{
+ int rc;
+
+ rc = fy_path_check_expression_alias(fypp, expr);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+struct fy_path_expr *
+fy_path_parse_expression(struct fy_path_parser *fypp)
+{
+ struct fy_reader *fyr;
+ struct fy_token *fyt = NULL;
+ enum fy_token_type fytt;
+ struct fy_path_expr *expr, *expr_top, *exprt;
+ enum fy_expr_mode old_scan_mode, prev_scan_mode;
+ int ret, rc;
+#ifdef DEBUG_EXPR
+ char *dbg;
+#endif
+
+ /* the parser must be in the correct state */
+ if (!fypp || fy_expr_stack_size(&fypp->operators) > 0 || fy_expr_stack_size(&fypp->operands) > 0)
+ return NULL;
+
+ fyr = &fypp->reader;
+
+ /* find stream start */
+ fyt = fy_path_scan_peek(fypp, NULL);
+ FYR_PARSE_ERROR_CHECK(fyr, 0, 1, FYEM_PARSE,
+ fyt && fyt->type == FYTT_STREAM_START, err_out,
+ "no tokens available or start without stream start");
+
+ /* remove stream start */
+ fy_token_unref(fy_path_scan_remove(fypp, fyt));
+ fyt = NULL;
+
+ prev_scan_mode = fypp->expr_mode;
+
+ while ((fyt = fy_path_scan_peek(fypp, NULL)) != NULL) {
+
+ if (fyt->type == FYTT_STREAM_END)
+ break;
+
+#ifdef DEBUG_EXPR
+ fy_token_debug_text_a(fyt, &dbg);
+ FYR_TOKEN_DIAG(fyr, fyt, FYET_NOTICE, FYEM_PARSE, "next token %s", dbg);
+#endif
+ fytt = fyt->type;
+
+ /* create an expression in either operator/operand case */
+ expr = fy_path_expr_alloc_recycle(fypp);
+ fyr_error_check(fyr, expr, err_out,
+ "fy_path_expr_alloc_recycle() failed\n");
+
+ expr->fyt = fy_path_scan_remove(fypp, fyt);
+ /* this it the first attempt, it might not be the final one */
+ expr->type = fy_map_token_to_path_expr_type(fyt->type, fypp->expr_mode);
+ fyt = NULL;
+
+#ifdef DEBUG_EXPR
+ fy_path_expr_dump(expr, fypp->cfg.diag, FYET_NOTICE, 0, "-> expr");
+#endif
+
+ if (prev_scan_mode != fypp->expr_mode) {
+#ifdef DEBUG_EXPR
+ fyr_warning(fyr, "switched expr_mode %s -> %s\n",
+ fy_expr_mode_txt[prev_scan_mode],
+ fy_expr_mode_txt[fypp->expr_mode]);
+#endif
+ expr_top = fy_expr_stack_peek(&fypp->operators);
+
+#ifdef DEBUG_EXPR
+ if (expr_top)
+ fy_path_expr_dump(expr_top, fypp->cfg.diag, FYET_NOTICE, 0, NULL);
+#endif
+
+ if (expr_top && fy_path_expr_type_is_lparen(expr_top->type) &&
+ expr_top->expr_mode != fypp->expr_mode) {
+#ifdef DEBUG_EXPR
+ fyr_warning(fyr, "switched top lparen expr_mode %s -> %s\n",
+ fy_expr_mode_txt[expr_top->expr_mode],
+ fy_expr_mode_txt[fypp->expr_mode]);
+ expr_top->expr_mode = fypp->expr_mode;
+#endif
+ }
+ }
+
+ prev_scan_mode = fypp->expr_mode;
+
+ /* if it's an operand convert it to expression and push */
+ if (fy_token_type_is_operand(fytt)) {
+
+ ret = fy_expr_stack_push(&fypp->operands, expr);
+ fyr_error_check(fyr, !ret, err_out, "push_operand() failed\n");
+ expr = NULL;
+
+ continue;
+ }
+
+ /* specials for SLASH */
+
+ if (expr->fyt->type == FYTT_PE_SLASH) {
+
+ /* try to get next token */
+ fyt = fy_path_scan_peek(fypp, NULL);
+ if (!fyt) {
+ if (!fypp->stream_error) {
+ (void)fy_path_fetch_tokens(fypp);
+ fyt = fy_path_scan_peek(fypp, NULL);
+ }
+ }
+
+ /* last token, it means it's a collection filter (or a root) */
+ if (!fyt || fyt->type == FYTT_STREAM_END || fyt->type == FYTT_PE_RPAREN) {
+
+ exprt = fy_expr_stack_peek(&fypp->operands);
+
+ /* if no argument exists it's a root */
+ if (!exprt) {
+ expr->type = fpet_root;
+
+ ret = fy_expr_stack_push(&fypp->operands, expr);
+ fyr_error_check(fyr, !ret, err_out, "push_operand() failed\n");
+ expr = NULL;
+ continue;
+ }
+
+ expr->type = fpet_filter_collection;
+ }
+ }
+
+#ifdef DEBUG_EXPR
+ fy_notice(fypp->cfg.diag, "operator stack (before)\n");
+ fy_expr_stack_dump(fypp->cfg.diag, &fypp->operators);
+ fy_notice(fypp->cfg.diag, "operand stack (before)\n");
+ fy_expr_stack_dump(fypp->cfg.diag, &fypp->operands);
+#endif
+
+ old_scan_mode = fypp->expr_mode;
+
+ /* for rparen, need to push before */
+ if (expr->type == fpet_rparen) {
+
+ FYR_TOKEN_ERROR_CHECK(fyr, expr->fyt, FYEM_PARSE,
+ fypp->paren_nest_level > 0, err_out,
+ "Mismatched right parentheses");
+
+ fypp->paren_nest_level--;
+
+ ret = fy_expr_stack_push(&fypp->operators, expr);
+ fyr_error_check(fyr, !ret, err_out, "push_operator() failed\n");
+ expr = NULL;
+
+ ret = evaluate_new(fypp);
+ /* evaluate will print diagnostic on error */
+ if (ret < 0)
+ goto err_out;
+
+ } else if (fy_path_expr_type_is_lparen(expr->type)) {
+
+ expr->expr_mode = fypp->expr_mode;
+ fypp->expr_mode = fyem_scalar;
+
+ fypp->paren_nest_level++;
+
+ /* push the operator */
+ ret = fy_expr_stack_push(&fypp->operators, expr);
+ fyr_error_check(fyr, !ret, err_out, "push_operator() failed\n");
+ expr = NULL;
+
+#ifdef DEBUG_EXPR
+ if (old_scan_mode != fypp->expr_mode)
+ fyr_notice(fyr, "expr_mode %s -> %s\n",
+ fy_expr_mode_txt[old_scan_mode],
+ fy_expr_mode_txt[fypp->expr_mode]);
+#endif
+ } else {
+ switch (fypp->expr_mode) {
+ case fyem_none:
+ assert(0); /* should never happen */
+ break;
+
+ case fyem_path:
+ if (fy_path_expr_type_is_conditional(expr->type)) {
+ /* switch to scalar mode */
+ fypp->expr_mode = fyem_scalar;
+ break;
+ }
+ break;
+ case fyem_scalar:
+ if (expr->type == fpet_root) {
+ fypp->expr_mode = fyem_path;
+ break;
+ }
+
+ /* div out of parentheses, it's a chain */
+ if (expr->type == fpet_div && fypp->paren_nest_level == 0) {
+ expr->type = fpet_chain;
+ fypp->expr_mode = fyem_path;
+
+ /* mode change means evaluate */
+ ret = evaluate_new(fypp);
+ /* evaluate will print diagnostic on error */
+ if (ret < 0)
+ goto err_out;
+
+ break;
+ }
+ break;
+ }
+
+ if (old_scan_mode != fypp->expr_mode) {
+#ifdef DEBUG_EXPR
+ fyr_notice(fyr, "expr_mode %s -> %s\n",
+ fy_expr_mode_txt[old_scan_mode],
+ fy_expr_mode_txt[fypp->expr_mode]);
+#endif
+ }
+
+ ret = -1;
+ while ((expr_top = fy_expr_stack_peek(&fypp->operators)) != NULL &&
+ fy_path_expr_type_prec(expr->type) <= fy_path_expr_type_prec(expr_top->type) &&
+ !fy_path_expr_type_is_lparen(expr_top->type)) {
+
+ ret = evaluate_new(fypp);
+ /* evaluate will print diagnostic on error */
+ if (ret < 0)
+ goto err_out;
+ }
+
+ /* push the operator */
+ ret = fy_expr_stack_push(&fypp->operators, expr);
+ fyr_error_check(fyr, !ret, err_out, "push_operator() failed\n");
+ expr = NULL;
+ }
+
+#ifdef DEBUG_EXPR
+ fy_notice(fypp->cfg.diag, "operator stack (after)\n");
+ fy_expr_stack_dump(fypp->cfg.diag, &fypp->operators);
+ fy_notice(fypp->cfg.diag, "operand stack (after)\n");
+ fy_expr_stack_dump(fypp->cfg.diag, &fypp->operands);
+#endif
+
+ prev_scan_mode = fypp->expr_mode;
+ }
+
+ if (fypp->stream_error)
+ goto err_out;
+
+ FYR_PARSE_ERROR_CHECK(fyr, 0, 1, FYEM_PARSE,
+ fypp->stream_error || (fyt && fyt->type == FYTT_STREAM_END), err_out,
+ "stream ended without STREAM_END");
+
+ /* remove stream end */
+ fy_token_unref(fy_path_scan_remove(fypp, fyt));
+ fyt = NULL;
+
+#if 0
+ FYR_PARSE_ERROR_CHECK(fyr, 0, 1, FYEM_PARSE,
+ fypp->paren_nest_level == 0, err_out,
+ "Missing right parenthesis");
+#endif
+
+ /* drain */
+ while ((expr_top = fy_expr_stack_peek(&fypp->operators)) != NULL &&
+ !fy_path_expr_type_is_lparen(expr_top->type)) {
+
+ ret = evaluate_new(fypp);
+ /* evaluate will print diagnostic on error */
+ if (ret < 0)
+ goto err_out;
+
+ }
+
+#ifdef DEBUG_EXPR
+ expr_top = fy_expr_stack_peek(&fypp->operators);
+ if (expr_top)
+ fy_path_expr_dump(expr_top, fypp->cfg.diag, FYET_NOTICE, 0, "operator top left");
+#endif
+
+ expr = fy_expr_stack_pop(&fypp->operands);
+
+ FYR_PARSE_ERROR_CHECK(fyr, 0, 1, FYEM_PARSE,
+ expr != NULL, err_out,
+ "No operands left on operand stack");
+
+ FYR_TOKEN_ERROR_CHECK(fyr, expr->fyt, FYEM_PARSE,
+ fy_expr_stack_size(&fypp->operands) == 0, err_out,
+ "Operand stack contains more than 1 value at end");
+
+ /* check the expression for validity */
+ rc = fy_path_check_expression(fypp, expr);
+ if (rc) {
+ fy_path_expr_free(expr);
+ expr = NULL;
+ }
+
+ return expr;
+
+err_out:
+#ifdef DEBUG_EXPR
+ fy_notice(fypp->cfg.diag, "operator stack (error)\n");
+ fy_expr_stack_dump(fypp->cfg.diag, &fypp->operators);
+ fy_notice(fypp->cfg.diag, "operand stack (error)\n");
+ fy_expr_stack_dump(fypp->cfg.diag, &fypp->operands);
+#endif
+ fypp->stream_error = true;
+ return NULL;
+}
+
+static struct fy_node *
+fy_path_expr_execute_single_result(struct fy_diag *diag, struct fy_path_expr *expr, struct fy_node *fyn)
+{
+ struct fy_token *fyt;
+ struct fy_anchor *fya;
+ const char *text;
+ size_t len;
+
+ assert(expr);
+
+ switch (expr->type) {
+ case fpet_root:
+ return fyn->fyd->root;
+
+ case fpet_this:
+ if (fy_node_is_alias(fyn)) {
+ // fprintf(stderr, "%s:%d %s calling fy_node_alias_resolve_by_ypath()\n", __func__, __LINE__, fy_node_get_path_alloca(fyn));
+ fyn = fy_node_alias_resolve_by_ypath(fyn);
+ }
+
+ return fyn;
+
+ case fpet_parent:
+ return fy_node_get_parent(fyn);
+
+ case fpet_alias:
+ fyt = expr->fyt;
+ assert(fyt);
+ assert(fyt->type == FYTT_PE_ALIAS);
+
+ text = fy_token_get_text(fyt, &len);
+ if (!text || len < 1)
+ break;
+
+ if (*text == '*') {
+ text++;
+ len--;
+ }
+ fya = fy_document_lookup_anchor(fyn->fyd, text, len);
+ if (!fya)
+ break;
+ return fya->fyn;
+
+ case fpet_seq_index:
+ fyt = expr->fyt;
+ assert(fyt);
+ assert(fyt->type == FYTT_PE_SEQ_INDEX);
+
+ if (fy_node_is_alias(fyn)) {
+ // fprintf(stderr, "%s:%d %s calling fy_node_alias_resolve_by_ypath()\n", __func__, __LINE__, fy_node_get_path_alloca(fyn));
+ fyn = fy_node_alias_resolve_by_ypath(fyn);
+ }
+
+ /* only on sequence */
+ if (!fy_node_is_sequence(fyn))
+ break;
+
+ return fy_node_sequence_get_by_index(fyn, fyt->seq_index.index);
+
+ case fpet_map_key:
+ fyt = expr->fyt;
+ assert(fyt);
+ assert(fyt->type == FYTT_PE_MAP_KEY);
+
+ if (fy_node_is_alias(fyn)) {
+ // fprintf(stderr, "%s:%d %s calling fy_node_alias_resolve_by_ypath()\n", __func__, __LINE__, fy_node_get_path_alloca(fyn));
+ fyn = fy_node_alias_resolve_by_ypath(fyn);
+ }
+
+ if (!fy_node_is_mapping(fyn))
+ break;
+
+ if (!fyt->map_key.fyd) {
+ /* simple key */
+ text = fy_token_get_text(fyt, &len);
+ if (!text || len < 1)
+ break;
+ return fy_node_mapping_lookup_value_by_simple_key(fyn, text, len);
+ }
+
+ return fy_node_mapping_lookup_value_by_key(fyn, fyt->map_key.fyd->root);
+
+ case fpet_filter_scalar:
+ if (!(fy_node_is_scalar(fyn) || fy_node_is_alias(fyn)))
+ break;
+ return fyn;
+
+ case fpet_filter_collection:
+
+ if (fy_node_is_alias(fyn)) {
+ // fprintf(stderr, "%s:%d %s calling fy_node_alias_resolve_by_ypath()\n", __func__, __LINE__, fy_node_get_path_alloca(fyn));
+ fyn = fy_node_alias_resolve_by_ypath(fyn);
+ }
+
+ if (!(fy_node_is_mapping(fyn) || fy_node_is_sequence(fyn)))
+ break;
+ return fyn;
+
+ case fpet_filter_sequence:
+
+ if (fy_node_is_alias(fyn)) {
+ // fprintf(stderr, "%s:%d %s calling fy_node_alias_resolve_by_ypath()\n", __func__, __LINE__, fy_node_get_path_alloca(fyn));
+ fyn = fy_node_alias_resolve_by_ypath(fyn);
+ }
+
+ if (!fy_node_is_sequence(fyn))
+ break;
+ return fyn;
+
+ case fpet_filter_mapping:
+
+ if (fy_node_is_alias(fyn)) {
+ // fprintf(stderr, "%s:%d %s calling fy_node_alias_resolve_by_ypath()\n", __func__, __LINE__, fy_node_get_path_alloca(fyn));
+ fyn = fy_node_alias_resolve_by_ypath(fyn);
+ }
+
+ if (!fy_node_is_mapping(fyn))
+ break;
+ return fyn;
+
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static double
+token_number(struct fy_token *fyt)
+{
+ const char *value;
+
+ if (!fyt || fyt->type != FYTT_SCALAR || (value = fy_token_get_text0(fyt)) == NULL)
+ return NAN;
+ return strtod(value, NULL);
+}
+
+void fy_path_exec_cleanup(struct fy_path_exec *fypx)
+{
+ if (!fypx)
+ return;
+
+ fy_walk_result_free(fypx->result);
+ fypx->result = NULL;
+ fypx->fyn_start = NULL;
+}
+
+/* publicly exported methods */
+struct fy_path_parser *fy_path_parser_create(const struct fy_path_parse_cfg *pcfg)
+{
+ struct fy_path_parser *fypp;
+
+ fypp = malloc(sizeof(*fypp));
+ if (!fypp)
+ return NULL;
+ fy_path_parser_setup(fypp, pcfg);
+ return fypp;
+}
+
+void fy_path_parser_destroy(struct fy_path_parser *fypp)
+{
+ if (!fypp)
+ return;
+ fy_path_parser_cleanup(fypp);
+ free(fypp);
+}
+
+int fy_path_parser_reset(struct fy_path_parser *fypp)
+{
+ if (!fypp)
+ return -1;
+ fy_path_parser_cleanup(fypp);
+ return 0;
+}
+
+struct fy_path_expr *
+fy_path_parse_expr_from_string(struct fy_path_parser *fypp,
+ const char *str, size_t len)
+{
+ struct fy_path_expr *expr = NULL;
+ struct fy_input *fyi = NULL;
+ int rc;
+
+ if (!fypp || !str || !len)
+ return NULL;
+
+ fy_path_parser_reset(fypp);
+
+ fyi = fy_input_from_data(str, len, NULL, false);
+ if (!fyi) {
+ fy_error(fypp->cfg.diag, "failed to create ypath input from %.*s\n",
+ (int)len, str);
+ goto err_out;
+ }
+
+ rc = fy_path_parser_open(fypp, fyi, NULL);
+ if (rc) {
+ fy_error(fypp->cfg.diag, "failed to open path parser input from %.*s\n",
+ (int)len, str);
+ goto err_out;
+ }
+
+ expr = fy_path_parse_expression(fypp);
+ if (!expr) {
+ fy_error(fypp->cfg.diag, "failed to parse path expression %.*s\n",
+ (int)len, str);
+ goto err_out;
+ }
+
+ fy_path_parser_close(fypp);
+
+ fy_input_unref(fyi);
+
+ return expr;
+
+err_out:
+ fy_path_expr_free(expr);
+ fy_path_parser_close(fypp);
+ fy_input_unref(fyi);
+ return NULL;
+}
+
+struct fy_path_expr *
+fy_path_expr_build_from_string(const struct fy_path_parse_cfg *pcfg,
+ const char *str, size_t len)
+{
+ struct fy_path_parser fypp_data, *fypp = &fypp_data;
+ struct fy_path_expr *expr = NULL;
+
+ if (!str)
+ return NULL;
+
+ fy_path_parser_setup(fypp, pcfg);
+ expr = fy_path_parse_expr_from_string(fypp, str, len);
+ fy_path_parser_cleanup(fypp);
+
+ return expr;
+}
+
+struct fy_path_exec *fy_path_exec_create(const struct fy_path_exec_cfg *xcfg)
+{
+ struct fy_path_exec *fypx;
+
+ fypx = malloc(sizeof(*fypx));
+ if (!fypx)
+ return NULL;
+
+ memset(fypx, 0, sizeof(*fypx));
+ if (xcfg)
+ fypx->cfg = *xcfg;
+ fypx->fwr_recycle = NULL; /* initially no recycling list */
+ fypx->refs = 1;
+
+ fypx->supress_recycling = !!(fypx->cfg.flags & FYPXCF_DISABLE_RECYCLING) ||
+ (getenv("FY_VALGRIND") &&
+ !getenv("FY_VALGRIND_RECYCLING"));
+ return fypx;
+}
+
+struct fy_path_exec *fy_path_exec_create_on_document(struct fy_document *fyd)
+{
+ struct fy_path_exec_cfg xcfg_local, *xcfg = &xcfg_local;
+ struct fy_path_exec *fypx;
+
+ memset(xcfg, 0, sizeof(*xcfg));
+ xcfg->diag = fyd ? fyd->diag : NULL;
+
+ xcfg->flags = (fyd->parse_cfg.flags & FYPCF_DISABLE_RECYCLING) ?
+ FYPXCF_DISABLE_RECYCLING : 0;
+
+ fypx = fy_path_exec_create(xcfg);
+ if (!fypx)
+ return NULL;
+ return fypx;
+}
+
+void fy_path_exec_destroy(struct fy_path_exec *fypx)
+{
+ if (!fypx)
+ return;
+ fy_path_exec_cleanup(fypx);
+ free(fypx);
+}
+
+int fy_path_exec_reset(struct fy_path_exec *fypx)
+{
+ if (!fypx)
+ return -1;
+ fy_path_exec_cleanup(fypx);
+ return 0;
+}
+
+struct fy_walk_result *fy_walk_result_simplify(struct fy_walk_result *fwr)
+{
+ struct fy_walk_result *fwr2;
+#if 0
+ struct fy_walk_result *fwrf;
+ bool recursive;
+#endif
+
+ /* no fwr */
+ if (!fwr)
+ return NULL;
+
+ /* non recursive */
+ if (fwr->type != fwrt_refs)
+ return fwr;
+
+ /* refs, if empty, return NULL */
+ if (fy_walk_result_list_empty(&fwr->refs)) {
+ fy_walk_result_free(fwr);
+ return NULL;
+ }
+
+ /* single element, switch it out */
+ if (fy_walk_result_list_is_singular(&fwr->refs)) {
+ fwr2 = fy_walk_result_list_pop(&fwr->refs);
+ assert(fwr2);
+
+ fy_walk_result_free(fwr);
+ fwr = fwr2;
+ }
+
+ return fwr;
+#if 0
+ /* non recursive return immediately */
+ if (fwr->type != fwrt_refs)
+ return fwr;
+
+ /* flatten if recursive */
+ recursive = false;
+ for (fwr2 = fy_walk_result_list_head(&fwr->refs); fwr2;
+ fwr2 = fy_walk_result_next(&fwr->refs, fwr2)) {
+
+ /* refs, recursive */
+ if (fwr2->type == fwrt_refs) {
+ recursive = true;
+ break;
+ }
+ }
+
+ if (!recursive)
+ return fwr;
+
+ fwrf = fy_path_exec_walk_result_create(fypx, fwrt_refs);
+ assert(fwrf);
+
+ fy_walk_result_flatten_internal(fwr, fwrf);
+
+ fy_walk_result_free(fwr);
+ return fwrf;
+#endif
+
+}
+
+int fy_walk_result_all_children_recursive_internal(struct fy_path_exec *fypx, struct fy_node *fyn, struct fy_walk_result *output)
+{
+ struct fy_node *fyni;
+ struct fy_walk_result *fwr;
+ void *prevp;
+ int ret;
+
+ if (!fyn)
+ return 0;
+
+ assert(output);
+ assert(output->type == fwrt_refs);
+
+ /* this node */
+ fwr = fy_path_exec_walk_result_create(fypx, fwrt_node_ref, fyn);
+ if (!fwr)
+ return -1;
+ fy_walk_result_list_add_tail(&output->refs, fwr);
+
+ if (fy_node_is_scalar(fyn))
+ return 0;
+
+ prevp = NULL;
+ while ((fyni = fy_node_collection_iterate(fyn, &prevp)) != NULL) {
+ ret = fy_walk_result_all_children_recursive_internal(fypx, fyni, output);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+bool
+fy_walk_result_compare_simple(struct fy_path_exec *fypx, enum fy_path_expr_type type,
+ struct fy_walk_result *fwrl, struct fy_walk_result *fwrr)
+{
+ struct fy_token *fyt;
+ struct fy_walk_result *fwrt;
+ const char *str;
+ bool match;
+
+ /* both NULL */
+ if (!fwrl && !fwrr) {
+ switch (type) {
+ case fpet_eq:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ /* any NULL */
+ if (!fwrl || !fwrr) {
+ switch (type) {
+ case fpet_neq:
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ /* both are non NULL */
+
+ /* none should be multiple */
+ assert(fwrl->type != fwrt_refs && fwrr->type != fwrt_refs);
+
+ /* both are the same type */
+ if (fwrl->type == fwrr->type) {
+
+ switch (fwrl->type) {
+ case fwrt_none:
+ abort(); /* should never happen */
+ break;
+
+ case fwrt_node_ref:
+ switch (type) {
+ case fpet_eq:
+ /* simple and fast direct node comparison */
+ if (fwrl->fyn == fwrr->fyn)
+ return true;
+ return fy_node_compare(fwrl->fyn, fwrr->fyn);
+ case fpet_neq:
+ /* simple and fast direct node comparison */
+ if (fwrl->fyn != fwrr->fyn)
+ return true;
+ return !fy_node_compare(fwrl->fyn, fwrr->fyn);
+ default:
+ break;
+ }
+ break;
+
+ case fwrt_refs:
+ assert(0); /* should not get here */
+ break;
+
+ case fwrt_doc:
+ switch (type) {
+ case fpet_eq:
+ case fpet_neq:
+ match = false;
+ if (fwrl->fyd == fwrr->fyd)
+ match = true;
+ else if (!fwrl->fyd || !fwrr->fyd)
+ match = false;
+ else
+ match = fy_node_compare(fwrl->fyd->root, fwrr->fyd->root);
+ if (type == fpet_neq)
+ match = !match;
+ return match;
+ default:
+ break;
+ }
+ break;
+
+ case fwrt_number:
+ switch (type) {
+ case fpet_eq:
+ return fwrl->number == fwrr->number;
+ case fpet_neq:
+ return fwrl->number != fwrr->number;
+ case fpet_lt:
+ return fwrl->number < fwrr->number;
+ case fpet_gt:
+ return fwrl->number > fwrr->number;
+ case fpet_lte:
+ return fwrl->number <= fwrr->number;
+ case fpet_gte:
+ return fwrl->number >= fwrr->number;
+ default:
+ break;
+ }
+ break;
+
+ case fwrt_string:
+ switch (type) {
+ case fpet_eq:
+ return strcmp(fwrl->string, fwrr->string) == 0;
+ case fpet_neq:
+ return strcmp(fwrl->string, fwrr->string) != 0;
+ case fpet_lt:
+ return strcmp(fwrl->string, fwrr->string) < 0;
+ case fpet_gt:
+ return strcmp(fwrl->string, fwrr->string) > 0;
+ case fpet_lte:
+ return strcmp(fwrl->string, fwrr->string) <= 0;
+ case fpet_gte:
+ return strcmp(fwrl->string, fwrr->string) >= 0;
+ default:
+ break;
+ }
+ break;
+ }
+ return false;
+ }
+
+ /* only handle the node refs at the left */
+ if (fwrr->type == fwrt_node_ref) {
+ switch (type) {
+ case fpet_lt:
+ type = fpet_gte;
+ break;
+ case fpet_gt:
+ type = fpet_lte;
+ break;
+ case fpet_lte:
+ type = fpet_gt;
+ break;
+ case fpet_gte:
+ type = fpet_lt;
+ break;
+ default:
+ break;
+ }
+
+ /* swap left with right */
+ return fy_walk_result_compare_simple(fypx, type, fwrr, fwrl);
+ }
+
+ switch (fwrl->type) {
+ case fwrt_node_ref:
+
+ /* non scalar mode, only returns true for non-eq */
+ if (!fy_node_is_scalar(fwrl->fyn)) {
+ /* XXX case of rhs being a document not handled */
+ return type == fpet_neq;
+ }
+
+ fyt = fy_node_get_scalar_token(fwrl->fyn);
+ assert(fyt);
+
+ str = fy_token_get_text0(fyt);
+ assert(str);
+
+ fwrt = NULL;
+ /* node ref against */
+ switch (fwrr->type) {
+ case fwrt_string:
+ /* create a new temporary walk result */
+ fwrt = fy_path_exec_walk_result_create(fypx, fwrt_string, str);
+ assert(fwrt);
+
+ break;
+
+ case fwrt_number:
+ /* if it's not a number return true only for non-eq */
+ if (!fy_token_is_number(fyt))
+ return type == fpet_neq;
+
+ /* create a new temporary walk result */
+ fwrt = fy_path_exec_walk_result_create(fypx, fwrt_number, strtod(str, NULL));
+ assert(fwrt);
+ break;
+
+ default:
+ break;
+ }
+
+ if (!fwrt)
+ return false;
+
+ match = fy_walk_result_compare_simple(fypx, type, fwrt, fwrr);
+
+ /* free the temporary result */
+ fy_walk_result_free(fwrt);
+
+ return match;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+struct fy_walk_result *
+fy_walk_result_arithmetic_simple(struct fy_path_exec *fypx,
+ struct fy_path_expr *expr,
+ struct fy_path_expr *exprl, struct fy_walk_result *fwrl,
+ struct fy_path_expr *exprr, struct fy_walk_result *fwrr)
+{
+ struct fy_diag *diag;
+ struct fy_walk_result *output = NULL;
+ char *str;
+ size_t len, len1, len2;
+
+ if (!fwrl || !fwrr)
+ goto out;
+
+ diag = fypx->cfg.diag;
+
+ /* node refs are not handled yet */
+ if (fwrl->type == fwrt_node_ref || fwrr->type == fwrt_node_ref)
+ goto out;
+
+ /* same type */
+ if (fwrl->type == fwrr->type) {
+
+ switch (fwrl->type) {
+
+ case fwrt_string:
+ /* for strings, only concatenation */
+ if (expr->type != fpet_plus)
+ break;
+ len1 = strlen(fwrl->string);
+ len2 = strlen(fwrr->string);
+ len = len1 + len2;
+ str = malloc(len + 1);
+ assert(str);
+ memcpy(str, fwrl->string, len1);
+ memcpy(str + len1, fwrr->string, len2);
+ str[len] = '\0';
+
+ free(fwrl->string);
+ fwrl->string = str;
+
+ /* reuse */
+ output = fwrl;
+ fwrl = NULL;
+
+ break;
+
+ case fwrt_number:
+ /* reuse fwrl */
+ output = fwrl;
+ switch (expr->type) {
+ case fpet_plus:
+ output->number = fwrl->number + fwrr->number;
+ break;
+ case fpet_minus:
+ output->number = fwrl->number - fwrr->number;
+ break;
+ case fpet_mult:
+ output->number = fwrl->number * fwrr->number;
+ break;
+ case fpet_div:
+ output->number = fwrr->number ? (fwrl->number / fwrr->number) : INFINITY;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ fwrl = NULL;
+ break;
+
+ default:
+ fy_error(diag, "fwrl->type=%s\n", fy_walk_result_type_txt[fwrl->type]);
+ assert(0);
+ break;
+ }
+ }
+
+out:
+ fy_walk_result_free(fwrl);
+ fy_walk_result_free(fwrr);
+ return output;
+}
+
+struct fy_walk_result *
+fy_walk_result_conditional_simple(struct fy_path_exec *fypx,
+ struct fy_path_expr *expr,
+ struct fy_path_expr *exprl, struct fy_walk_result *fwrl,
+ struct fy_path_expr *exprr, struct fy_walk_result *fwrr)
+{
+ bool match;
+
+ match = fy_walk_result_compare_simple(fypx, expr->type, fwrl, fwrr);
+
+ if (!match) {
+ fy_walk_result_free(fwrl);
+ fy_walk_result_free(fwrr);
+ return NULL;
+ }
+
+ /* path expr, return left hand side result */
+ fy_walk_result_free(fwrr);
+ return fwrl;
+}
+
+struct fy_walk_result *
+fy_walk_result_lhs_rhs(struct fy_path_exec *fypx,
+ struct fy_path_expr *expr,
+ struct fy_path_expr *exprl, struct fy_walk_result *fwrl,
+ struct fy_path_expr *exprr, struct fy_walk_result *fwrr)
+{
+ struct fy_walk_result *output = NULL, *fwr, *fwrrt, *fwrlt, *fwrlc, *fwrrc;
+ struct fy_walk_result *outputl = NULL, *outputr = NULL;
+
+ assert(expr);
+ assert(exprl);
+ assert(exprr);
+
+ /* only supports those */
+ if (!fy_path_expr_type_is_conditional(expr->type) &&
+ !fy_path_expr_type_is_arithmetic(expr->type))
+ goto out;
+
+ /* both NULL */
+ if (!fwrl && !fwrr)
+ goto out;
+
+ /* any NULL */
+ if (!fwrl || !fwrr) {
+ if (expr->type == fpet_neq) {
+ output = fwrl;
+ fwrl = NULL;
+ }
+ goto out;
+ }
+
+ output = fy_path_exec_walk_result_create(fypx, fwrt_refs);
+ assert(output);
+
+ for (fwrlt = fy_walk_result_iter_start(fwrl); fwrlt;
+ fwrlt = fy_walk_result_iter_next(fwrl, fwrlt)) {
+
+ /* for recursive ones */
+ if (fwrlt->type == fwrt_refs) {
+
+ fwrlc = fy_walk_result_clone(fwrlt);
+ assert(fwrlc);
+ fwrrc = fy_walk_result_clone(fwrr);
+ assert(fwrrc);
+
+ outputl = fy_walk_result_lhs_rhs(fypx, expr, exprl, fwrlc, exprr, fwrrc);
+ if (outputl)
+ fy_walk_result_list_add_tail(&output->refs, outputl);
+ else
+ fy_walk_result_free(outputl);
+ continue;
+ }
+
+ /* non-recursive case */
+ for (fwrrt = fy_walk_result_iter_start(fwrr); fwrrt;
+ fwrrt = fy_walk_result_iter_next(fwrr, fwrrt)) {
+
+ /* for recursive ones */
+ if (fwrrt->type == fwrt_refs) {
+
+ fwrlc = fy_walk_result_clone(fwrlt);
+ assert(fwrlc);
+ fwrrc = fy_walk_result_clone(fwrrt);
+ assert(fwrrc);
+
+ outputr = fy_walk_result_lhs_rhs(fypx, expr, exprl, fwrlc, exprr, fwrrc);
+ if (outputr)
+ fy_walk_result_list_add_tail(&output->refs, outputr);
+ else
+ fy_walk_result_free(outputr);
+ continue;
+ }
+
+
+ fwrlc = fy_walk_result_clone(fwrlt);
+ assert(fwrlc);
+ fwrrc = fy_walk_result_clone(fwrrt);
+ assert(fwrrc);
+
+ fwr = NULL;
+
+ if (fy_path_expr_type_is_conditional(expr->type))
+ fwr = fy_walk_result_conditional_simple(fypx, expr, exprl, fwrlc, exprr, fwrrc);
+ else if (fy_path_expr_type_is_arithmetic(expr->type))
+ fwr = fy_walk_result_arithmetic_simple(fypx, expr, exprl, fwrlc, exprr, fwrrc);
+ else {
+ assert(0);
+ }
+
+ fwrlc = NULL;
+ fwrrc = NULL;
+
+ if (fwr)
+ fy_walk_result_list_add_tail(&output->refs, fwr);
+ }
+ }
+
+out:
+ fy_walk_result_free(fwrl);
+ fy_walk_result_free(fwrr);
+
+ return fy_walk_result_simplify(output);
+}
+
+struct fy_path_expr *
+fy_scalar_walk_result_to_expr(struct fy_path_exec *fypx, struct fy_walk_result *fwr, enum fy_path_expr_type ptype)
+{
+ struct fy_input *fyit = NULL;
+ struct fy_path_expr *exprt = NULL;
+ struct fy_atom handle;
+ bool collection_addressing;
+ char *buf;
+ int rc __FY_DEBUG_UNUSED__;
+
+ exprt = NULL;
+
+ if (!fwr)
+ return NULL;
+
+ collection_addressing = ptype == fpet_chain || ptype == fpet_multi;
+
+ switch (fwr->type) {
+ case fwrt_string:
+ fyit = fy_input_from_malloc_data(fwr->string, FY_NT, &handle, true);
+ assert(fyit);
+
+ fwr->string = NULL;
+ fy_walk_result_free(fwr);
+ fwr = NULL;
+
+ exprt = fy_path_expr_alloc();
+ assert(exprt);
+ if (collection_addressing) {
+ exprt->type = fpet_map_key;
+ exprt->fyt = fy_token_create(FYTT_PE_MAP_KEY, &handle, NULL);
+ assert(exprt->fyt);
+ } else {
+ exprt->type = fpet_scalar;
+ exprt->fyt = fy_token_create(FYTT_SCALAR, &handle, FYSS_PLAIN, NULL);
+ assert(exprt->fyt);
+ }
+ break;
+
+ case fwrt_number:
+
+ rc = asprintf(&buf, "%d", (int)fwr->number);
+ assert(rc != -1);
+
+ fyit = fy_input_from_malloc_data(buf, FY_NT, &handle, true);
+ assert(fyit);
+
+ exprt = fy_path_expr_alloc();
+ assert(exprt);
+ if (collection_addressing) {
+ exprt->type = fpet_seq_index;
+ exprt->fyt = fy_token_create(FYTT_PE_SEQ_INDEX, &handle, (int)fwr->number);
+ assert(exprt->fyt);
+ } else {
+ exprt->type = fpet_scalar;
+ exprt->fyt = fy_token_create(FYTT_SCALAR, &handle, FYSS_PLAIN, NULL);
+ assert(exprt->fyt);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ fy_walk_result_free(fwr);
+ fy_input_unref(fyit);
+
+ return exprt;
+}
+
+struct fy_walk_result *
+fy_path_expr_execute(struct fy_path_exec *fypx, int level, struct fy_path_expr *expr,
+ struct fy_walk_result *input, enum fy_path_expr_type ptype)
+{
+ struct fy_diag *diag;
+ struct fy_walk_result *fwr, *fwrn, *fwrt, *fwrtn;
+ struct fy_walk_result *output = NULL, *input1, *output1, *input2, *output2;
+ struct fy_path_expr *exprn, *exprl, *exprr;
+ struct fy_node *fyn, *fynn, *fyni;
+ struct fy_token *fyt;
+ int start, end, count, i;
+ bool match;
+ struct fy_path_expr *exprt;
+ unsigned int nargs;
+ struct fy_walk_result **fwr_args;
+ void *prevp;
+ int rc __FY_DEBUG_UNUSED__;
+
+ /* error */
+ if (!fypx || !expr)
+ goto out;
+
+ diag = fypx->cfg.diag;
+
+#ifdef DEBUG_EXPR
+ if (input)
+ fy_walk_result_dump(input, diag, FYET_NOTICE, level, "input %s\n", fy_path_expr_type_txt[expr->type]);
+#endif
+
+ /* recursive */
+ if (input && input->type == fwrt_refs && !fy_path_expr_type_handles_refs(expr->type)) {
+
+ output = fy_path_exec_walk_result_create(fypx, fwrt_refs);
+ assert(output);
+
+ while ((fwr = fy_walk_result_list_pop(&input->refs)) != NULL) {
+
+ fwrn = fy_path_expr_execute(fypx, level + 1, expr, fwr, ptype);
+ if (fwrn)
+ fy_walk_result_list_add_tail(&output->refs, fwrn);
+ }
+ fy_walk_result_free(input);
+ input = NULL;
+ goto out;
+ }
+
+
+ /* single result case is common enough to optimize */
+ if (fy_path_expr_type_is_single_result(expr->type)) {
+
+ if (input && input->type == fwrt_node_ref) {
+
+ fynn = fy_path_expr_execute_single_result(diag, expr, input->fyn);
+ if (!fynn)
+ goto out;
+
+ fy_walk_result_clean(input);
+ output = input;
+ output->type = fwrt_node_ref;
+ output->fyn = fynn;
+ input = NULL;
+ }
+
+ goto out;
+ }
+
+ /* handle the remaining multi result cases */
+ switch (expr->type) {
+
+ case fpet_chain:
+
+ if (!input)
+ goto out;
+
+ /* iterate over each chain item */
+ output = input;
+ input = NULL;
+ for (exprn = fy_path_expr_list_head(&expr->children); exprn;
+ exprn = fy_path_expr_next(&expr->children, exprn)) {
+
+ output = fy_path_expr_execute(fypx, level + 1, exprn, output, expr->type);
+ if (!output)
+ break;
+ }
+
+ break;
+
+ case fpet_multi:
+
+ if (!input)
+ goto out;
+
+ /* allocate a refs output result */
+ output = fy_path_exec_walk_result_create(fypx, fwrt_refs);
+ assert(output);
+
+ /* iterate over each multi item */
+ for (exprn = fy_path_expr_list_head(&expr->children); exprn;
+ exprn = fy_path_expr_next(&expr->children, exprn)) {
+
+ input2 = fy_walk_result_clone(input);
+ assert(input2);
+
+ output2 = fy_path_expr_execute(fypx, level + 1, exprn, input2, expr->type);
+ if (!output2)
+ continue;
+
+ fy_walk_result_list_add_tail(&output->refs, output2);
+ }
+ fy_walk_result_free(input);
+ input = NULL;
+ break;
+
+ case fpet_every_child:
+
+ if (!input)
+ goto out;
+
+ /* only valid for node ref */
+ if (input->type != fwrt_node_ref)
+ break;
+
+ fyn = input->fyn;
+
+ /* every scalar/alias is a single result (although it should not happen) */
+ if (fy_node_is_scalar(fyn) || fy_node_is_alias(fyn)) {
+ output = input;
+ input = NULL;
+ break;
+
+ }
+
+ /* re-use input for output root */
+ fy_walk_result_clean(input);
+ output = input;
+ input = NULL;
+
+ output->type = fwrt_refs;
+ fy_walk_result_list_init(&output->refs);
+
+ prevp = NULL;
+ while ((fyni = fy_node_collection_iterate(fyn, &prevp)) != NULL) {
+ fwr = fy_path_exec_walk_result_create(fypx, fwrt_node_ref, fyni);
+ assert(fwr);
+
+ fy_walk_result_list_add_tail(&output->refs, fwr);
+ }
+
+ break;
+
+ case fpet_every_child_r:
+
+ if (!input)
+ goto out;
+
+ /* only valid for node ref */
+ if (input->type != fwrt_node_ref)
+ break;
+
+ fyn = input->fyn;
+
+ /* re-use input for output root */
+ fy_walk_result_clean(input);
+ output = input;
+ input = NULL;
+
+ output->type = fwrt_refs;
+ fy_walk_result_list_init(&output->refs);
+
+ rc = fy_walk_result_all_children_recursive_internal(fypx, fyn, output);
+ assert(!rc);
+
+ break;
+
+ case fpet_seq_slice:
+
+ if (!input)
+ goto out;
+
+ /* only valid for node ref on a sequence */
+ if (input->type != fwrt_node_ref || !fy_node_is_sequence(input->fyn)) {
+ break;
+ }
+ fyn = input->fyn;
+
+ fyt = expr->fyt;
+ assert(fyt);
+ assert(fyt->type == FYTT_PE_SEQ_SLICE);
+
+ start = fyt->seq_slice.start_index;
+ end = fyt->seq_slice.end_index;
+ count = fy_node_sequence_item_count(fyn);
+
+ /* don't handle negative slices yet */
+ if (start < 0 || end < 1 || start >= end)
+ break;
+
+ if (count < end)
+ end = count;
+
+ /* re-use input for output root */
+ fy_walk_result_clean(input);
+ output = input;
+ input = NULL;
+
+ output->type = fwrt_refs;
+ fy_walk_result_list_init(&output->refs);
+
+ for (i = start; i < end; i++) {
+
+ fynn = fy_node_sequence_get_by_index(fyn, i);
+ if (!fynn)
+ continue;
+
+ fwr = fy_path_exec_walk_result_create(fypx, fwrt_node_ref, fynn);
+ assert(fwr);
+
+ fy_walk_result_list_add_tail(&output->refs, fwr);
+ }
+
+ break;
+
+ case fpet_eq:
+ case fpet_neq:
+ case fpet_lt:
+ case fpet_gt:
+ case fpet_lte:
+ case fpet_gte:
+ case fpet_plus:
+ case fpet_minus:
+ case fpet_mult:
+ case fpet_div:
+
+ exprl = fy_path_expr_lhs(expr);
+ assert(exprl);
+
+ exprr = fy_path_expr_rhs(expr);
+ assert(exprr);
+
+ if (input) {
+ input1 = fy_walk_result_clone(input);
+ assert(input1);
+
+ input2 = input;
+ input = NULL;
+
+ } else {
+ input1 = NULL;
+ input2 = NULL;
+ }
+
+ /* execute LHS and RHS */
+ output1 = fy_path_expr_execute(fypx, level + 1, exprl, input1, expr->type);
+ output2 = fy_path_expr_execute(fypx, level + 1, exprr, input2, expr->type);
+
+ output = fy_walk_result_lhs_rhs(fypx, expr, exprl, output1, exprr, output2);
+
+ break;
+
+ case fpet_scalar:
+
+ /* duck typing! */
+ if (fy_token_is_number(expr->fyt)) {
+ output = fy_path_exec_walk_result_create(fypx, fwrt_number, token_number(expr->fyt));
+ assert(output);
+ } else {
+ output = fy_path_exec_walk_result_create(fypx, fwrt_string, fy_token_get_text0(expr->fyt));
+ assert(output);
+ }
+
+ fy_walk_result_free(input);
+ input = NULL;
+
+ break;
+
+ case fpet_logical_or:
+
+ /* return the first that is not NULL */
+ for (exprn = fy_path_expr_list_head(&expr->children); exprn;
+ exprn = fy_path_expr_next(&expr->children, exprn)) {
+
+ if (input) {
+ input1 = fy_walk_result_clone(input);
+ assert(input1);
+ } else {
+ input1 = NULL;
+ }
+
+ output = fy_path_expr_execute(fypx, level + 1, exprn, input1, expr->type);
+ if (output)
+ break;
+ }
+ break;
+
+ case fpet_logical_and:
+ output = NULL;
+
+ /* return the last that was not NULL */
+ for (exprn = fy_path_expr_list_head(&expr->children); exprn;
+ exprn = fy_path_expr_next(&expr->children, exprn)) {
+
+ if (input) {
+ input1 = fy_walk_result_clone(input);
+ assert(input1);
+ } else {
+ input1 = NULL;
+ }
+
+ output1 = fy_path_expr_execute(fypx, level + 1, exprn, input1, expr->type);
+ if (output1) {
+ fy_walk_result_free(output);
+ output = output1;
+ } else
+ break;
+ }
+ break;
+
+ case fpet_filter_unique:
+
+ if (!input)
+ goto out;
+
+ /* flatten input */
+ input = fy_walk_result_flatten(input);
+ assert(input); /* must work */
+
+ /* for non refs, return input */
+ if (input->type != fwrt_refs) {
+ output = input;
+ input = NULL;
+ break;
+ }
+
+ /* remove duplicates filter */
+ for (fwr = fy_walk_result_list_head(&input->refs); fwr;
+ fwr = fy_walk_result_next(&input->refs, fwr)) {
+
+ /* do not check recursively */
+ if (fwr->type == fwrt_refs)
+ continue;
+
+ /* check the entries from this point forward */
+ for (fwrt = fy_walk_result_next(&input->refs, fwr); fwrt; fwrt = fwrtn) {
+
+ fwrtn = fy_walk_result_next(&input->refs, fwrt);
+
+ /* do not check recursively (or the same result) */
+ if (fwrt->type == fwrt_refs)
+ continue;
+
+ assert(fwrt != fwr);
+
+ match = fy_walk_result_compare_simple(fypx, fpet_eq, fwr, fwrt);
+
+ if (match) {
+ fy_walk_result_list_del(&input->refs, fwrt);
+ fy_walk_result_free(fwrt);
+ }
+ }
+ }
+ output = input;
+ input = NULL;
+
+ break;
+
+ case fpet_scalar_expr:
+
+ exprl = fy_path_expr_list_head(&expr->children);
+ if (!exprl) {
+ fy_warning(diag, "%s:%d\n", __FILE__, __LINE__);
+ goto out;
+ }
+
+ output = fy_path_expr_execute(fypx, level + 1, exprl, NULL, ptype);
+ if (!output) {
+ fy_warning(diag, "%s:%d\n", __FILE__, __LINE__);
+ goto out;
+ }
+
+ exprt = fy_scalar_walk_result_to_expr(fypx, output, ptype);
+ output = NULL;
+ if (!exprt) {
+ fy_warning(diag, "%s:%d\n", __FILE__, __LINE__);
+ break;
+ }
+
+ output = fy_path_expr_execute(fypx, level + 1, exprt, input, ptype);
+ if (!output) {
+ fy_warning(diag, "%s:%d\n", __FILE__, __LINE__);
+ }
+ input = NULL;
+
+ fy_path_expr_free(exprt);
+ break;
+
+ case fpet_path_expr:
+ exprl = fy_path_expr_list_head(&expr->children);
+ if (!exprl)
+ goto out;
+
+ output = fy_path_expr_execute(fypx, level + 1, exprl, input, ptype);
+ input = NULL;
+ break;
+
+ case fpet_method:
+
+ assert(expr->fym);
+
+ /* execute the arguments */
+ nargs = expr->fym->nargs;
+ if (nargs > 0) {
+ fwr_args = FY_ALLOCA(sizeof(*fwr_args) * nargs);
+ memset(fwr_args, 0, sizeof(*fwr_args) * nargs);
+ for (i = 0, exprt = fy_path_expr_list_head(&expr->children); exprt;
+ exprt = fy_path_expr_next(&expr->children, exprt), i++) {
+
+ if (input) {
+ input1 = fy_walk_result_clone(input);
+ assert(input1);
+ } else
+ input1 = NULL;
+
+ fwr_args[i] = fy_path_expr_execute(fypx, level + 1, exprt, input1, expr->type);
+ }
+ } else
+ fwr_args = NULL;
+
+ output = expr->fym->exec(expr->fym, fypx, level + 1, expr, input, fwr_args, nargs);
+ input = NULL;
+
+ break;
+
+ default:
+ fy_error(diag, "%s\n", fy_path_expr_type_txt[expr->type]);
+ assert(0);
+ break;
+ }
+
+out:
+ fy_walk_result_free(input);
+ output = fy_walk_result_simplify(output);
+
+#ifdef DEBUG_EXPR
+ if (output)
+ fy_walk_result_dump(output, diag, FYET_NOTICE, level, "output %s\n", fy_path_expr_type_txt[expr->type]);
+#endif
+ return output;
+}
+
+static int fy_path_exec_execute_internal(struct fy_path_exec *fypx,
+ struct fy_path_expr *expr, struct fy_node *fyn_start)
+{
+ struct fy_walk_result *fwr;
+
+ if (!fypx || !expr || !fyn_start)
+ return -1;
+
+ fy_walk_result_free(fypx->result);
+ fypx->result = NULL;
+
+ fwr = fy_path_exec_walk_result_create(fypx, fwrt_node_ref, fyn_start);
+ assert(fwr);
+
+ fwr = fy_path_expr_execute(fypx, 0, expr, fwr, fpet_none);
+ if (!fwr)
+ return 0;
+
+ /* flatten results */
+ if (fwr->type == fwrt_refs) {
+ fwr = fy_walk_result_flatten(fwr);
+ if (!fwr)
+ return -1;
+ }
+ fypx->result = fwr;
+
+ return 0;
+}
+
+int fy_path_exec_execute(struct fy_path_exec *fypx, struct fy_path_expr *expr, struct fy_node *fyn_start)
+{
+ if (!fypx || !expr || !fyn_start)
+ return -1;
+
+ fypx->fyn_start = fyn_start;
+ return fy_path_exec_execute_internal(fypx, expr, fypx->fyn_start);
+}
+
+struct fy_node *
+fy_path_exec_results_iterate(struct fy_path_exec *fypx, void **prevp)
+{
+ struct fy_walk_result *fwr;
+
+ if (!fypx || !prevp)
+ return NULL;
+
+ if (!fypx->result)
+ return NULL;
+
+ if (fypx->result->type != fwrt_refs) {
+ fwr = fypx->result;
+
+ if (fwr->type != fwrt_node_ref)
+ return NULL;
+
+ if (!*prevp) {
+ *prevp = fwr;
+ return fwr->fyn;
+ }
+ *prevp = NULL;
+ return NULL;
+ }
+
+ /* loop over non node refs for now */
+ do {
+ if (!*prevp)
+ fwr = fy_walk_result_list_head(&fypx->result->refs);
+ else
+ fwr = fy_walk_result_next(&fypx->result->refs, *prevp);
+ *prevp = fwr;
+ } while (fwr && fwr->type != fwrt_node_ref);
+
+ return fwr ? fwr->fyn : NULL;
+}
+
+struct fy_walk_result *
+fy_path_exec_take_results(struct fy_path_exec *fypx)
+{
+ struct fy_walk_result *fwr;
+
+ if (!fypx || !fypx->result)
+ return NULL;
+ fwr = fypx->result;
+ fypx->result = NULL;
+ return fwr;
+}
+
+struct fy_walk_result *
+fy_path_exec_walk_result_vcreate(struct fy_path_exec *fypx, enum fy_walk_result_type type, va_list ap)
+{
+ struct fy_walk_result_list *fwrl;
+
+ if (!fypx)
+ return NULL;
+
+ fwrl = fy_path_exec_walk_result_rl(fypx);
+ return fy_walk_result_vcreate_rl(fwrl, type, ap);
+}
+
+struct fy_walk_result *
+fy_path_exec_walk_result_create(struct fy_path_exec *fypx, enum fy_walk_result_type type, ...)
+{
+ struct fy_walk_result_list *fwrl;
+ struct fy_walk_result *fwr;
+ va_list ap;
+
+ if (!fypx)
+ return NULL;
+
+ fwrl = fy_path_exec_walk_result_rl(fypx);
+
+ va_start(ap, type);
+ fwr = fy_walk_result_vcreate_rl(fwrl, type, ap);
+ va_end(ap);
+
+ if (!fwr)
+ return NULL;
+
+ fwr->fypx = fy_path_exec_ref(fypx);
+
+ return fwr;
+}
+
+void
+fy_path_exec_walk_result_free(struct fy_path_exec *fypx, struct fy_walk_result *fwr)
+{
+ struct fy_walk_result_list *fwrl;
+
+ fwrl = fypx ? fy_path_exec_walk_result_rl(fypx) : NULL;
+ fy_walk_result_free_rl(fwrl, fwr);
+}
+
+int fy_document_setup_path_expr_data(struct fy_document *fyd)
+{
+ struct fy_path_parse_cfg pcfg_local, *pcfg = &pcfg_local;
+ struct fy_path_expr_document_data *pxdd;
+
+ if (!fyd || fyd->pxdd)
+ return 0;
+
+ pxdd = malloc(sizeof(*pxdd));
+ if (!pxdd)
+ goto err_no_mem;
+
+ memset(pxdd, 0, sizeof(*pxdd));
+
+ fy_walk_result_list_init(&pxdd->fwr_recycle);
+
+ memset(pcfg, 0, sizeof(*pcfg));
+ pcfg->diag = fyd->diag;
+ pxdd->fypp = fy_path_parser_create(pcfg);
+ if (!pxdd->fypp)
+ goto err_no_fypp;
+
+ fyd->pxdd = pxdd;
+
+ return 0;
+
+err_no_fypp:
+ free(pxdd);
+err_no_mem:
+ return -1;
+}
+
+void fy_document_cleanup_path_expr_data(struct fy_document *fyd)
+{
+ struct fy_path_expr_document_data *pxdd;
+ struct fy_walk_result *fwr;
+
+ if (!fyd || !fyd->pxdd)
+ return;
+
+ pxdd = fyd->pxdd;
+
+ fy_path_parser_destroy(pxdd->fypp);
+
+ while ((fwr = fy_walk_result_list_pop(&pxdd->fwr_recycle)) != NULL)
+ free(fwr);
+
+ free(fyd->pxdd);
+ fyd->pxdd = NULL;
+}
+
+int fy_node_setup_path_expr_data(struct fy_node *fyn)
+{
+ struct fy_path_expr_document_data *pxdd;
+ struct fy_path_expr_node_data *pxnd;
+ const char *text;
+ size_t len;
+ char *alloc = NULL;
+ int rc;
+
+ if (!fyn || fyn->pxnd)
+ return 0;
+
+ /* only on alias nodes */
+ if (!fy_node_is_alias(fyn))
+ return 0;
+
+ /* a document must exist */
+ if (!fyn->fyd)
+ return -1;
+
+ if (!fyn->fyd->pxdd) {
+ rc = fy_document_setup_path_expr_data(fyn->fyd);
+ if (rc)
+ return rc;
+ }
+ pxdd = fyn->fyd->pxdd;
+ assert(pxdd);
+
+ pxnd = malloc(sizeof(*pxnd));
+ if (!pxnd)
+ goto err_no_mem;
+ memset(pxnd, 0, sizeof(*pxnd));
+
+ text = fy_token_get_text(fyn->scalar, &len);
+ if (!text)
+ goto err_no_text;
+
+ if (!fy_is_first_alpha(*text)) {
+ pxnd->fyi = fy_input_from_data(text, len, NULL, false);
+ if (!pxnd->fyi)
+ goto err_no_input;
+ } else {
+ alloc = malloc(len + 2);
+ if (!alloc)
+ goto err_no_input;
+ alloc[0] = '*';
+ memcpy(alloc + 1, text, len);
+ alloc[len + 1] = '\0';
+
+ pxnd->fyi = fy_input_from_malloc_data(alloc, len + 1, NULL, false);
+ if (!pxnd->fyi)
+ goto err_no_input;
+ }
+
+ fy_path_parser_reset(pxdd->fypp);
+
+ rc = fy_path_parser_open(pxdd->fypp, pxnd->fyi, NULL);
+ if (rc)
+ goto err_no_open;
+
+ pxnd->expr = fy_path_parse_expression(pxdd->fypp);
+ if (!pxnd->expr)
+ goto err_parse;
+
+ fy_path_parser_close(pxdd->fypp);
+
+ fyn->pxnd = pxnd;
+
+ return 0;
+err_parse:
+ fy_path_parser_close(pxdd->fypp);
+err_no_open:
+ fy_input_unref(pxnd->fyi);
+err_no_input:
+ if (alloc)
+ free(alloc);
+err_no_text:
+ free(pxnd);
+err_no_mem:
+ return -1;
+}
+
+void fy_node_cleanup_path_expr_data(struct fy_node *fyn)
+{
+ struct fy_path_expr_node_data *pxnd;
+
+ if (!fyn || !fyn->pxnd)
+ return;
+
+ pxnd = fyn->pxnd;
+
+ if (pxnd->expr)
+ fy_path_expr_free(pxnd->expr);
+
+ if (pxnd->fyi)
+ fy_input_unref(pxnd->fyi);
+
+ free(pxnd);
+ fyn->pxnd = NULL;
+}
+
+struct fy_walk_result *
+fy_node_alias_resolve_by_ypath_result(struct fy_node *fyn)
+{
+ struct fy_document *fyd;
+ struct fy_path_expr_document_data *pxdd = NULL;
+ struct fy_path_expr_node_data *pxnd = NULL;
+ struct fy_walk_result *fwr;
+ struct fy_anchor *fya;
+ struct fy_path_exec *fypx = NULL;
+ int rc;
+ char* path;
+
+ if (!fyn || !fy_node_is_alias(fyn))
+ return NULL;
+
+ fyd = fyn->fyd;
+ if (!fyd)
+ return NULL;
+
+ /* simple */
+ fya = fy_document_lookup_anchor_by_token(fyd, fyn->scalar);
+ if (fya) {
+
+ fwr = fy_path_exec_walk_result_create(fypx, fwrt_node_ref, fya->fyn);
+ fyd_error_check(fyd, fwr, err_out,
+ "fy_walk_result_alloc_rl() failed");
+
+ return fwr;
+ }
+
+ /* ok, complex, setup the node data */
+ rc = fy_node_setup_path_expr_data(fyn);
+ fyd_error_check(fyd, !rc, err_out,
+ "fy_node_setup_path_expr_data() failed");
+
+ pxnd = fyn->pxnd;
+ assert(pxnd);
+
+ pxdd = fyd->pxdd;
+ assert(pxdd);
+
+ if (pxnd->traversals++ > 0) {
+ fy_node_get_path_alloca(fyn, &path);
+ FYD_NODE_ERROR(fyd, fyn, FYEM_DOC,
+ "recursive reference detected at %s\n",
+ path);
+ pxnd->traversals--;
+ return NULL;
+ }
+
+ fypx = fy_path_exec_create_on_document(fyd);
+ fyd_error_check(fyd, !rc, err_out,
+ "fy_path_exec_create_on_document() failed");
+
+ fy_path_exec_set_result_recycle_list(fypx, &pxdd->fwr_recycle);
+
+#if 0
+ {
+ struct fy_document *fyd_pe;
+ const char *text;
+ size_t len;
+
+ text = fy_token_get_text(fyn->scalar, &len);
+ if (text) {
+ fyd_pe = fy_path_expr_to_document(pxnd->expr);
+ if (fyd_pe) {
+ fprintf(stderr, "%s: %.*s\n", __func__, (int)len, text);
+ fy_document_default_emit_to_fp(fyd_pe, stderr);
+ fy_document_destroy(fyd_pe);
+ }
+ }
+ }
+#endif
+
+ // fprintf(stderr, "%s: %s 2\n", __func__, fy_node_get_path_alloca(fyn));
+
+ /* execute, starting at this */
+ rc = fy_path_exec_execute(fypx, pxnd->expr, fyn);
+ fyd_error_check(fyd, !rc, err_out,
+ "fy_path_exec_execute() failed");
+
+ // fprintf(stderr, "%s: %s 3\n", __func__, fy_node_get_path_alloca(fyn));
+
+ fwr = fy_path_exec_take_results(fypx);
+
+ fy_path_exec_unref(fypx);
+
+ pxnd->traversals--;
+
+ if (!fwr)
+ return NULL;
+
+ // fprintf(stderr, "%s: %s 4\n", __func__, fy_node_get_path_alloca(fyn));
+
+ return fwr;
+
+err_out:
+ if (pxnd)
+ pxnd->traversals--;
+ fy_path_exec_unref(fypx); /* NULL OK */
+ return NULL;
+}
+
+struct fy_node *fy_node_alias_resolve_by_ypath(struct fy_node *fyn)
+{
+ struct fy_anchor *fya;
+ struct fy_walk_result *fwr;
+ void *iterp;
+
+ if (!fyn || !fy_node_is_alias(fyn))
+ return NULL;
+
+ /* simple and common enough to do it now */
+ fya = fy_document_lookup_anchor_by_token(fyn->fyd, fyn->scalar);
+ if (fya)
+ return fya->fyn;
+
+ fwr = fy_node_alias_resolve_by_ypath_result(fyn);
+ if (!fwr)
+ return NULL;
+
+ iterp = NULL;
+ fyn = fy_walk_result_node_iterate(fwr, &iterp);
+
+ fy_walk_result_free(fwr);
+
+ return fyn;
+}
+
+struct fy_walk_result *
+fy_node_by_ypath_result(struct fy_node *fyn, const char *path, size_t len)
+{
+ struct fy_path_expr_document_data *pxdd;
+ struct fy_document *fyd;
+ struct fy_walk_result *fwr;
+ struct fy_anchor *fya;
+ struct fy_input *fyi;
+ struct fy_path_expr *expr;
+ struct fy_path_exec *fypx = NULL;
+ int rc;
+
+ if (!fyn || !path || !len)
+ return NULL;
+
+ fyd = fyn->fyd;
+ if (!fyd)
+ return NULL;
+
+ if (len == FY_NT)
+ len = strlen(path);
+
+ /* simple */
+ fya = fy_document_lookup_anchor(fyn->fyd, path, len);
+ if (fya) {
+ fwr = fy_path_exec_walk_result_create(fypx, fwrt_node_ref, fya->fyn);
+ fyd_error_check(fyd, fwr, err_out,
+ "fy_walk_result_alloc_rl() failed");
+ return fwr;
+ }
+
+ /* ok, complex, setup the document data */
+ rc = fy_document_setup_path_expr_data(fyd);
+ fyd_error_check(fyd, !rc, err_setup,
+ "fy_node_setup_path_expr_data() failed");
+
+ pxdd = fyd->pxdd;
+ assert(pxdd);
+
+ fyi = fy_input_from_data(path, len, NULL, false);
+ fyd_error_check(fyd, fyi, err_no_input,
+ "fy_input_from_data() failed");
+
+ fy_path_parser_reset(pxdd->fypp);
+
+ rc = fy_path_parser_open(pxdd->fypp, fyi, NULL);
+ fyd_error_check(fyd, !rc, err_no_open,
+ "fy_path_parser_open() failed");
+
+ expr = fy_path_parse_expression(pxdd->fypp);
+ fyd_error_check(fyd, expr, err_parse,
+ "fy_path_parse_expression() failed");
+
+ fy_path_parser_close(pxdd->fypp);
+
+ fypx = fy_path_exec_create_on_document(fyd);
+ fyd_error_check(fyd, !rc, err_no_fypx,
+ "fy_path_exec_create_on_document() failed");
+
+ /* execute, starting at this */
+ rc = fy_path_exec_execute(fypx, expr, fyn);
+ fyd_error_check(fyd, !rc, err_exec,
+ "fy_path_parse_expression() failed");
+
+ fwr = fy_path_exec_take_results(fypx);
+
+ fy_path_exec_unref(fypx);
+
+ fy_path_expr_free(expr);
+ fy_input_unref(fyi);
+
+ return fwr;
+
+err_exec:
+ fy_path_expr_free(expr);
+err_no_fypx:
+ fy_path_exec_unref(fypx);
+err_parse:
+ fy_path_parser_close(pxdd->fypp);
+
+err_no_open:
+ fy_input_unref(fyi);
+
+err_no_input:
+err_setup:
+err_out:
+ return NULL;
+}
+
+struct fy_node *fy_node_by_ypath(struct fy_node *fyn, const char *path, size_t len)
+{
+ struct fy_walk_result *fwr;
+ struct fy_anchor *fya;
+ void *iterp;
+
+ if (!fyn || !path || !len)
+ return NULL;
+
+ /* simple */
+ fya = fy_document_lookup_anchor(fyn->fyd, path, len);
+ if (fya)
+ return fya->fyn;
+
+ fwr = fy_node_by_ypath_result(fyn, path, len);
+ if (!fwr)
+ return NULL;
+
+ iterp = NULL;
+ fyn = fy_walk_result_node_iterate(fwr, &iterp);
+
+ fy_walk_result_free(fwr);
+
+ return fyn;
+}