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