^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) #include <stdbool.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) #include <assert.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) #include <errno.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) #include <stdlib.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) #include <string.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #include "metricgroup.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include "debug.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include "expr.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include "expr-bison.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include "expr-flex.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/zalloc.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <ctype.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #ifdef PARSER_DEBUG
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) extern int expr_debug;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) static size_t key_hash(const void *key, void *ctx __maybe_unused)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) const char *str = (const char *)key;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) size_t hash = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) while (*str != '\0') {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) hash *= 31;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) hash += *str;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) str++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) return hash;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) static bool key_equal(const void *key1, const void *key2,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) void *ctx __maybe_unused)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) return !strcmp((const char *)key1, (const char *)key2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) /* Caller must make sure id is allocated */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) int expr__add_id(struct expr_parse_ctx *ctx, const char *id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) struct expr_id_data *data_ptr = NULL, *old_data = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) char *old_key = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) data_ptr = malloc(sizeof(*data_ptr));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) if (!data_ptr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) data_ptr->parent = ctx->parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) ret = hashmap__set(&ctx->ids, id, data_ptr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) (const void **)&old_key, (void **)&old_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) free(data_ptr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) free(old_key);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) free(old_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) /* Caller must make sure id is allocated */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) int expr__add_id_val(struct expr_parse_ctx *ctx, const char *id, double val)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) struct expr_id_data *data_ptr = NULL, *old_data = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) char *old_key = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) data_ptr = malloc(sizeof(*data_ptr));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) if (!data_ptr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) data_ptr->val = val;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) data_ptr->is_ref = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) ret = hashmap__set(&ctx->ids, id, data_ptr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) (const void **)&old_key, (void **)&old_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) free(data_ptr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) free(old_key);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) free(old_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) struct expr_id_data *data_ptr = NULL, *old_data = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) char *old_key = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) char *name, *p;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) data_ptr = zalloc(sizeof(*data_ptr));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) if (!data_ptr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) name = strdup(ref->metric_name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) if (!name) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) free(data_ptr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) * The jevents tool converts all metric expressions
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) * to lowercase, including metric references, hence
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) * we need to add lowercase name for metric, so it's
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) * properly found.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) for (p = name; *p; p++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) *p = tolower(*p);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) * Intentionally passing just const char pointers,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) * originally from 'struct pmu_event' object.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) * We don't need to change them, so there's no
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) * need to create our own copy.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) data_ptr->ref.metric_name = ref->metric_name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) data_ptr->ref.metric_expr = ref->metric_expr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) data_ptr->ref.counted = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) data_ptr->is_ref = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) ret = hashmap__set(&ctx->ids, name, data_ptr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) (const void **)&old_key, (void **)&old_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) free(data_ptr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) pr_debug2("adding ref metric %s: %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) ref->metric_name, ref->metric_expr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) free(old_key);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) free(old_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) int expr__get_id(struct expr_parse_ctx *ctx, const char *id,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) struct expr_id_data **data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) return hashmap__find(&ctx->ids, id, (void **)data) ? 0 : -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) int expr__resolve_id(struct expr_parse_ctx *ctx, const char *id,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) struct expr_id_data **datap)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) struct expr_id_data *data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) if (expr__get_id(ctx, id, datap) || !*datap) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) pr_debug("%s not found\n", id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) data = *datap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) pr_debug2("lookup: is_ref %d, counted %d, val %f: %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) data->is_ref, data->ref.counted, data->val, id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) if (data->is_ref && !data->ref.counted) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) data->ref.counted = true;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) pr_debug("processing metric: %s ENTRY\n", id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) if (expr__parse(&data->val, ctx, data->ref.metric_expr, 1)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) pr_debug("%s failed to count\n", id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) pr_debug("processing metric: %s EXIT: %f\n", id, data->val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) void expr__del_id(struct expr_parse_ctx *ctx, const char *id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) struct expr_id_data *old_val = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) char *old_key = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) hashmap__delete(&ctx->ids, id,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) (const void **)&old_key, (void **)&old_val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) free(old_key);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) free(old_val);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) void expr__ctx_init(struct expr_parse_ctx *ctx)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) hashmap__init(&ctx->ids, key_hash, key_equal, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) void expr__ctx_clear(struct expr_parse_ctx *ctx)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) struct hashmap_entry *cur;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) size_t bkt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) hashmap__for_each_entry((&ctx->ids), cur, bkt) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) free((char *)cur->key);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) free(cur->value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) hashmap__clear(&ctx->ids);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) static int
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) __expr__parse(double *val, struct expr_parse_ctx *ctx, const char *expr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) int start, int runtime)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) struct expr_scanner_ctx scanner_ctx = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) .start_token = start,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) .runtime = runtime,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) YY_BUFFER_STATE buffer;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) void *scanner;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) pr_debug2("parsing metric: %s\n", expr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) ret = expr_lex_init_extra(&scanner_ctx, &scanner);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) buffer = expr__scan_string(expr, scanner);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) #ifdef PARSER_DEBUG
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) expr_debug = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) expr_set_debug(1, scanner);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) ret = expr_parse(val, ctx, scanner);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) expr__flush_buffer(buffer, scanner);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) expr__delete_buffer(buffer, scanner);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) expr_lex_destroy(scanner);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) int expr__parse(double *final_val, struct expr_parse_ctx *ctx,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) const char *expr, int runtime)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) return __expr__parse(final_val, ctx, expr, EXPR_PARSE, runtime) ? -1 : 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) int expr__find_other(const char *expr, const char *one,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) struct expr_parse_ctx *ctx, int runtime)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) int ret = __expr__parse(NULL, ctx, expr, EXPR_OTHER, runtime);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) if (one)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) expr__del_id(ctx, one);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) }