linux/tools/perf/util/expr.y
Andi Kleen 075167363f perf tools: Add a simple expression parser for JSON
Add a simple expression parser good enough to parse JSON relation
expressions. The parser is implemented using bison.

This is just intended as an simple parser for internal usage in the
event lists, not the beginning of a "perf scripting language"

v2: Use expr__ prefix instead of expr_
    Support multiple free variables for parser

Committer note:

The v2 patch had:

  %define api.pure full

In expr.y, that is a feature introduced in bison 2.7, to have reentrant
parsers, not using global variables, which would make tools/perf stop
building with the bison version shipped in older distros, so Andi
realised that the other parsers (e.g. parse-events.y) were using:

  %pure-parser

Which is present in older versions of bison and fits the bill.

I added:

  CFLAGS_expr-bison.o += -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w

To finally make it build, copying what was there for pmu-bison.o,
another parser.

Signed-off-by: Andi Kleen <ak@linux.intel.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Link: http://lkml.kernel.org/r/20170320201711.14142-8-andi@firstfloor.org
[ stdlib.h is needed in tests/expr.c for free() fixing build in systems such as ubuntu:16.04-x-s390 ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-03-23 11:39:27 -03:00

174 lines
3.1 KiB
Plaintext

/* 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;
}