blob: 954556bea36ef758ef31c5f7a1e723cc8fe50db0 [file] [log] [blame]
/* Simple expression parser */
%{
#include "util.h"
#include "util/debug.h"
#define IN_EXPR_Y 1
#include "expr.h"
#include <string.h>
#define MAXIDLEN 256
%}
%pure-parser
%parse-param { double *final_val }
%parse-param { struct parse_ctx *ctx }
%parse-param { const char **pp }
%lex-param { const char **pp }
%union {
double num;
char id[MAXIDLEN+1];
}
%token <num> NUMBER
%token <id> ID
%left '|'
%left '^'
%left '&'
%left '-' '+'
%left '*' '/' '%'
%left NEG NOT
%type <num> expr
%{
static int expr__lex(YYSTYPE *res, const char **pp);
static void expr__error(double *final_val __maybe_unused,
struct parse_ctx *ctx __maybe_unused,
const char **pp __maybe_unused,
const char *s)
{
pr_debug("%s\n", s);
}
static int lookup_id(struct parse_ctx *ctx, char *id, double *val)
{
int i;
for (i = 0; i < ctx->num_ids; i++) {
if (!strcasecmp(ctx->ids[i].name, id)) {
*val = ctx->ids[i].val;
return 0;
}
}
return -1;
}
%}
%%
all_expr: expr { *final_val = $1; }
;
expr: NUMBER
| ID { if (lookup_id(ctx, $1, &$$) < 0) {
pr_debug("%s not found", $1);
YYABORT;
}
}
| expr '+' expr { $$ = $1 + $3; }
| expr '-' expr { $$ = $1 - $3; }
| expr '*' expr { $$ = $1 * $3; }
| expr '/' expr { if ($3 == 0) YYABORT; $$ = $1 / $3; }
| expr '%' expr { if ((long)$3 == 0) YYABORT; $$ = (long)$1 % (long)$3; }
| '-' expr %prec NEG { $$ = -$2; }
| '(' expr ')' { $$ = $2; }
;
%%
static int expr__symbol(YYSTYPE *res, const char *p, const char **pp)
{
char *dst = res->id;
const char *s = p;
while (isalnum(*p) || *p == '_' || *p == '.') {
if (p - s >= MAXIDLEN)
return -1;
*dst++ = *p++;
}
*dst = 0;
*pp = p;
return ID;
}
static int expr__lex(YYSTYPE *res, const char **pp)
{
int tok;
const char *s;
const char *p = *pp;
while (isspace(*p))
p++;
s = p;
switch (*p++) {
case 'a' ... 'z':
case 'A' ... 'Z':
return expr__symbol(res, p - 1, pp);
case '0' ... '9': case '.':
res->num = strtod(s, (char **)&p);
tok = NUMBER;
break;
default:
tok = *s;
break;
}
*pp = p;
return tok;
}
/* Caller must make sure id is allocated */
void expr__add_id(struct parse_ctx *ctx, const char *name, double val)
{
int idx;
assert(ctx->num_ids < MAX_PARSE_ID);
idx = ctx->num_ids++;
ctx->ids[idx].name = name;
ctx->ids[idx].val = val;
}
void expr__ctx_init(struct parse_ctx *ctx)
{
ctx->num_ids = 0;
}
int expr__find_other(const char *p, const char *one, const char ***other,
int *num_otherp)
{
const char *orig = p;
int err = -1;
int num_other;
*other = malloc((EXPR_MAX_OTHER + 1) * sizeof(char *));
if (!*other)
return -1;
num_other = 0;
for (;;) {
YYSTYPE val;
int tok = expr__lex(&val, &p);
if (tok == 0) {
err = 0;
break;
}
if (tok == ID && strcasecmp(one, val.id)) {
if (num_other >= EXPR_MAX_OTHER - 1) {
pr_debug("Too many extra events in %s\n", orig);
break;
}
(*other)[num_other] = strdup(val.id);
if (!(*other)[num_other])
return -1;
num_other++;
}
}
(*other)[num_other] = NULL;
*num_otherp = num_other;
if (err) {
*num_otherp = 0;
free(*other);
*other = NULL;
}
return err;
}