107516736SAndi Kleen /* Simple expression parser */ 207516736SAndi Kleen %{ 307516736SAndi Kleen #include "util.h" 407516736SAndi Kleen #include "util/debug.h" 58520a98dSArnaldo Carvalho de Melo #include <stdlib.h> // strtod() 607516736SAndi Kleen #define IN_EXPR_Y 1 707516736SAndi Kleen #include "expr.h" 8d73bad06SAndi Kleen #include "smt.h" 98520a98dSArnaldo Carvalho de Melo #include <assert.h> 1007516736SAndi Kleen #include <string.h> 1107516736SAndi Kleen 1207516736SAndi Kleen #define MAXIDLEN 256 1307516736SAndi Kleen %} 1407516736SAndi Kleen 15*fc8c0a99SJiri Olsa %define api.pure full 16*fc8c0a99SJiri Olsa 1707516736SAndi Kleen %parse-param { double *final_val } 1807516736SAndi Kleen %parse-param { struct parse_ctx *ctx } 1907516736SAndi Kleen %parse-param { const char **pp } 2007516736SAndi Kleen %lex-param { const char **pp } 2107516736SAndi Kleen 2207516736SAndi Kleen %union { 2307516736SAndi Kleen double num; 2407516736SAndi Kleen char id[MAXIDLEN+1]; 2507516736SAndi Kleen } 2607516736SAndi Kleen 2707516736SAndi Kleen %token <num> NUMBER 2807516736SAndi Kleen %token <id> ID 29d73bad06SAndi Kleen %token MIN MAX IF ELSE SMT_ON 30d73bad06SAndi Kleen %left MIN MAX IF 3107516736SAndi Kleen %left '|' 3207516736SAndi Kleen %left '^' 3307516736SAndi Kleen %left '&' 3407516736SAndi Kleen %left '-' '+' 3507516736SAndi Kleen %left '*' '/' '%' 3607516736SAndi Kleen %left NEG NOT 37d73bad06SAndi Kleen %type <num> expr if_expr 3807516736SAndi Kleen 3907516736SAndi Kleen %{ 4007516736SAndi Kleen static int expr__lex(YYSTYPE *res, const char **pp); 4107516736SAndi Kleen 4207516736SAndi Kleen static void expr__error(double *final_val __maybe_unused, 4307516736SAndi Kleen struct parse_ctx *ctx __maybe_unused, 4407516736SAndi Kleen const char **pp __maybe_unused, 4507516736SAndi Kleen const char *s) 4607516736SAndi Kleen { 4707516736SAndi Kleen pr_debug("%s\n", s); 4807516736SAndi Kleen } 4907516736SAndi Kleen 5007516736SAndi Kleen static int lookup_id(struct parse_ctx *ctx, char *id, double *val) 5107516736SAndi Kleen { 5207516736SAndi Kleen int i; 5307516736SAndi Kleen 5407516736SAndi Kleen for (i = 0; i < ctx->num_ids; i++) { 5507516736SAndi Kleen if (!strcasecmp(ctx->ids[i].name, id)) { 5607516736SAndi Kleen *val = ctx->ids[i].val; 5707516736SAndi Kleen return 0; 5807516736SAndi Kleen } 5907516736SAndi Kleen } 6007516736SAndi Kleen return -1; 6107516736SAndi Kleen } 6207516736SAndi Kleen 6307516736SAndi Kleen %} 6407516736SAndi Kleen %% 6507516736SAndi Kleen 66d73bad06SAndi Kleen all_expr: if_expr { *final_val = $1; } 67d73bad06SAndi Kleen ; 68d73bad06SAndi Kleen 69d73bad06SAndi Kleen if_expr: 70d73bad06SAndi Kleen expr IF expr ELSE expr { $$ = $3 ? $1 : $5; } 71d73bad06SAndi Kleen | expr 7207516736SAndi Kleen ; 7307516736SAndi Kleen 7407516736SAndi Kleen expr: NUMBER 7507516736SAndi Kleen | ID { if (lookup_id(ctx, $1, &$$) < 0) { 76c295036bSAndi Kleen pr_debug("%s not found\n", $1); 7707516736SAndi Kleen YYABORT; 7807516736SAndi Kleen } 7907516736SAndi Kleen } 80d73bad06SAndi Kleen | expr '|' expr { $$ = (long)$1 | (long)$3; } 81d73bad06SAndi Kleen | expr '&' expr { $$ = (long)$1 & (long)$3; } 82d73bad06SAndi Kleen | expr '^' expr { $$ = (long)$1 ^ (long)$3; } 8307516736SAndi Kleen | expr '+' expr { $$ = $1 + $3; } 8407516736SAndi Kleen | expr '-' expr { $$ = $1 - $3; } 8507516736SAndi Kleen | expr '*' expr { $$ = $1 * $3; } 8607516736SAndi Kleen | expr '/' expr { if ($3 == 0) YYABORT; $$ = $1 / $3; } 8707516736SAndi Kleen | expr '%' expr { if ((long)$3 == 0) YYABORT; $$ = (long)$1 % (long)$3; } 8807516736SAndi Kleen | '-' expr %prec NEG { $$ = -$2; } 89d73bad06SAndi Kleen | '(' if_expr ')' { $$ = $2; } 90d73bad06SAndi Kleen | MIN '(' expr ',' expr ')' { $$ = $3 < $5 ? $3 : $5; } 91d73bad06SAndi Kleen | MAX '(' expr ',' expr ')' { $$ = $3 > $5 ? $3 : $5; } 92d73bad06SAndi Kleen | SMT_ON { $$ = smt_on() > 0; } 9307516736SAndi Kleen ; 9407516736SAndi Kleen 9507516736SAndi Kleen %% 9607516736SAndi Kleen 9707516736SAndi Kleen static int expr__symbol(YYSTYPE *res, const char *p, const char **pp) 9807516736SAndi Kleen { 9907516736SAndi Kleen char *dst = res->id; 10007516736SAndi Kleen const char *s = p; 10107516736SAndi Kleen 102d73bad06SAndi Kleen if (*p == '#') 103d73bad06SAndi Kleen *dst++ = *p++; 104d73bad06SAndi Kleen 105d73bad06SAndi Kleen while (isalnum(*p) || *p == '_' || *p == '.' || *p == ':' || *p == '@' || *p == '\\') { 10607516736SAndi Kleen if (p - s >= MAXIDLEN) 10707516736SAndi Kleen return -1; 108d73bad06SAndi Kleen /* 109d73bad06SAndi Kleen * Allow @ instead of / to be able to specify pmu/event/ without 110d73bad06SAndi Kleen * conflicts with normal division. 111d73bad06SAndi Kleen */ 112d73bad06SAndi Kleen if (*p == '@') 113d73bad06SAndi Kleen *dst++ = '/'; 114d73bad06SAndi Kleen else if (*p == '\\') 115d73bad06SAndi Kleen *dst++ = *++p; 116d73bad06SAndi Kleen else 117d73bad06SAndi Kleen *dst++ = *p; 118d73bad06SAndi Kleen p++; 11907516736SAndi Kleen } 12007516736SAndi Kleen *dst = 0; 12107516736SAndi Kleen *pp = p; 122d73bad06SAndi Kleen dst = res->id; 123d73bad06SAndi Kleen switch (dst[0]) { 124d73bad06SAndi Kleen case 'm': 125d73bad06SAndi Kleen if (!strcmp(dst, "min")) 126d73bad06SAndi Kleen return MIN; 127d73bad06SAndi Kleen if (!strcmp(dst, "max")) 128d73bad06SAndi Kleen return MAX; 129d73bad06SAndi Kleen break; 130d73bad06SAndi Kleen case 'i': 131d73bad06SAndi Kleen if (!strcmp(dst, "if")) 132d73bad06SAndi Kleen return IF; 133d73bad06SAndi Kleen break; 134d73bad06SAndi Kleen case 'e': 135d73bad06SAndi Kleen if (!strcmp(dst, "else")) 136d73bad06SAndi Kleen return ELSE; 137d73bad06SAndi Kleen break; 138d73bad06SAndi Kleen case '#': 139d73bad06SAndi Kleen if (!strcasecmp(dst, "#smt_on")) 140d73bad06SAndi Kleen return SMT_ON; 141d73bad06SAndi Kleen break; 142d73bad06SAndi Kleen } 14307516736SAndi Kleen return ID; 14407516736SAndi Kleen } 14507516736SAndi Kleen 14607516736SAndi Kleen static int expr__lex(YYSTYPE *res, const char **pp) 14707516736SAndi Kleen { 14807516736SAndi Kleen int tok; 14907516736SAndi Kleen const char *s; 15007516736SAndi Kleen const char *p = *pp; 15107516736SAndi Kleen 15207516736SAndi Kleen while (isspace(*p)) 15307516736SAndi Kleen p++; 15407516736SAndi Kleen s = p; 15507516736SAndi Kleen switch (*p++) { 156d73bad06SAndi Kleen case '#': 15707516736SAndi Kleen case 'a' ... 'z': 15807516736SAndi Kleen case 'A' ... 'Z': 15907516736SAndi Kleen return expr__symbol(res, p - 1, pp); 16007516736SAndi Kleen case '0' ... '9': case '.': 16107516736SAndi Kleen res->num = strtod(s, (char **)&p); 16207516736SAndi Kleen tok = NUMBER; 16307516736SAndi Kleen break; 16407516736SAndi Kleen default: 16507516736SAndi Kleen tok = *s; 16607516736SAndi Kleen break; 16707516736SAndi Kleen } 16807516736SAndi Kleen *pp = p; 16907516736SAndi Kleen return tok; 17007516736SAndi Kleen } 17107516736SAndi Kleen 17207516736SAndi Kleen /* Caller must make sure id is allocated */ 17307516736SAndi Kleen void expr__add_id(struct parse_ctx *ctx, const char *name, double val) 17407516736SAndi Kleen { 17507516736SAndi Kleen int idx; 17607516736SAndi Kleen assert(ctx->num_ids < MAX_PARSE_ID); 17707516736SAndi Kleen idx = ctx->num_ids++; 17807516736SAndi Kleen ctx->ids[idx].name = name; 17907516736SAndi Kleen ctx->ids[idx].val = val; 18007516736SAndi Kleen } 18107516736SAndi Kleen 18207516736SAndi Kleen void expr__ctx_init(struct parse_ctx *ctx) 18307516736SAndi Kleen { 18407516736SAndi Kleen ctx->num_ids = 0; 18507516736SAndi Kleen } 18607516736SAndi Kleen 187d66dccdbSAndi Kleen static bool already_seen(const char *val, const char *one, const char **other, 188d66dccdbSAndi Kleen int num_other) 189d66dccdbSAndi Kleen { 190d66dccdbSAndi Kleen int i; 191d66dccdbSAndi Kleen 192d66dccdbSAndi Kleen if (one && !strcasecmp(one, val)) 193d66dccdbSAndi Kleen return true; 194d66dccdbSAndi Kleen for (i = 0; i < num_other; i++) 195d66dccdbSAndi Kleen if (!strcasecmp(other[i], val)) 196d66dccdbSAndi Kleen return true; 197d66dccdbSAndi Kleen return false; 198d66dccdbSAndi Kleen } 199d66dccdbSAndi Kleen 20007516736SAndi Kleen int expr__find_other(const char *p, const char *one, const char ***other, 20107516736SAndi Kleen int *num_otherp) 20207516736SAndi Kleen { 20307516736SAndi Kleen const char *orig = p; 20407516736SAndi Kleen int err = -1; 20507516736SAndi Kleen int num_other; 20607516736SAndi Kleen 20707516736SAndi Kleen *other = malloc((EXPR_MAX_OTHER + 1) * sizeof(char *)); 20807516736SAndi Kleen if (!*other) 20907516736SAndi Kleen return -1; 21007516736SAndi Kleen 21107516736SAndi Kleen num_other = 0; 21207516736SAndi Kleen for (;;) { 21307516736SAndi Kleen YYSTYPE val; 21407516736SAndi Kleen int tok = expr__lex(&val, &p); 21507516736SAndi Kleen if (tok == 0) { 21607516736SAndi Kleen err = 0; 21707516736SAndi Kleen break; 21807516736SAndi Kleen } 219d66dccdbSAndi Kleen if (tok == ID && !already_seen(val.id, one, *other, num_other)) { 22007516736SAndi Kleen if (num_other >= EXPR_MAX_OTHER - 1) { 22107516736SAndi Kleen pr_debug("Too many extra events in %s\n", orig); 22207516736SAndi Kleen break; 22307516736SAndi Kleen } 22407516736SAndi Kleen (*other)[num_other] = strdup(val.id); 22507516736SAndi Kleen if (!(*other)[num_other]) 22607516736SAndi Kleen return -1; 22707516736SAndi Kleen num_other++; 22807516736SAndi Kleen } 22907516736SAndi Kleen } 23007516736SAndi Kleen (*other)[num_other] = NULL; 23107516736SAndi Kleen *num_otherp = num_other; 23207516736SAndi Kleen if (err) { 23307516736SAndi Kleen *num_otherp = 0; 23407516736SAndi Kleen free(*other); 23507516736SAndi Kleen *other = NULL; 23607516736SAndi Kleen } 23707516736SAndi Kleen return err; 23807516736SAndi Kleen } 239