107516736SAndi Kleen /* Simple expression parser */ 207516736SAndi Kleen %{ 307516736SAndi Kleen #include "util.h" 407516736SAndi Kleen #include "util/debug.h" 507516736SAndi Kleen #define IN_EXPR_Y 1 607516736SAndi Kleen #include "expr.h" 7d73bad06SAndi Kleen #include "smt.h" 807516736SAndi Kleen #include <string.h> 907516736SAndi Kleen 1007516736SAndi Kleen #define MAXIDLEN 256 1107516736SAndi Kleen %} 1207516736SAndi Kleen 1307516736SAndi Kleen %pure-parser 1407516736SAndi Kleen %parse-param { double *final_val } 1507516736SAndi Kleen %parse-param { struct parse_ctx *ctx } 1607516736SAndi Kleen %parse-param { const char **pp } 1707516736SAndi Kleen %lex-param { const char **pp } 1807516736SAndi Kleen 1907516736SAndi Kleen %union { 2007516736SAndi Kleen double num; 2107516736SAndi Kleen char id[MAXIDLEN+1]; 2207516736SAndi Kleen } 2307516736SAndi Kleen 2407516736SAndi Kleen %token <num> NUMBER 2507516736SAndi Kleen %token <id> ID 26d73bad06SAndi Kleen %token MIN MAX IF ELSE SMT_ON 27d73bad06SAndi Kleen %left MIN MAX IF 2807516736SAndi Kleen %left '|' 2907516736SAndi Kleen %left '^' 3007516736SAndi Kleen %left '&' 3107516736SAndi Kleen %left '-' '+' 3207516736SAndi Kleen %left '*' '/' '%' 3307516736SAndi Kleen %left NEG NOT 34d73bad06SAndi Kleen %type <num> expr if_expr 3507516736SAndi Kleen 3607516736SAndi Kleen %{ 3707516736SAndi Kleen static int expr__lex(YYSTYPE *res, const char **pp); 3807516736SAndi Kleen 3907516736SAndi Kleen static void expr__error(double *final_val __maybe_unused, 4007516736SAndi Kleen struct parse_ctx *ctx __maybe_unused, 4107516736SAndi Kleen const char **pp __maybe_unused, 4207516736SAndi Kleen const char *s) 4307516736SAndi Kleen { 4407516736SAndi Kleen pr_debug("%s\n", s); 4507516736SAndi Kleen } 4607516736SAndi Kleen 4707516736SAndi Kleen static int lookup_id(struct parse_ctx *ctx, char *id, double *val) 4807516736SAndi Kleen { 4907516736SAndi Kleen int i; 5007516736SAndi Kleen 5107516736SAndi Kleen for (i = 0; i < ctx->num_ids; i++) { 5207516736SAndi Kleen if (!strcasecmp(ctx->ids[i].name, id)) { 5307516736SAndi Kleen *val = ctx->ids[i].val; 5407516736SAndi Kleen return 0; 5507516736SAndi Kleen } 5607516736SAndi Kleen } 5707516736SAndi Kleen return -1; 5807516736SAndi Kleen } 5907516736SAndi Kleen 6007516736SAndi Kleen %} 6107516736SAndi Kleen %% 6207516736SAndi Kleen 63d73bad06SAndi Kleen all_expr: if_expr { *final_val = $1; } 64d73bad06SAndi Kleen ; 65d73bad06SAndi Kleen 66d73bad06SAndi Kleen if_expr: 67d73bad06SAndi Kleen expr IF expr ELSE expr { $$ = $3 ? $1 : $5; } 68d73bad06SAndi Kleen | expr 6907516736SAndi Kleen ; 7007516736SAndi Kleen 7107516736SAndi Kleen expr: NUMBER 7207516736SAndi Kleen | ID { if (lookup_id(ctx, $1, &$$) < 0) { 73c295036bSAndi Kleen pr_debug("%s not found\n", $1); 7407516736SAndi Kleen YYABORT; 7507516736SAndi Kleen } 7607516736SAndi Kleen } 77d73bad06SAndi Kleen | expr '|' expr { $$ = (long)$1 | (long)$3; } 78d73bad06SAndi Kleen | expr '&' expr { $$ = (long)$1 & (long)$3; } 79d73bad06SAndi Kleen | expr '^' expr { $$ = (long)$1 ^ (long)$3; } 8007516736SAndi Kleen | expr '+' expr { $$ = $1 + $3; } 8107516736SAndi Kleen | expr '-' expr { $$ = $1 - $3; } 8207516736SAndi Kleen | expr '*' expr { $$ = $1 * $3; } 8307516736SAndi Kleen | expr '/' expr { if ($3 == 0) YYABORT; $$ = $1 / $3; } 8407516736SAndi Kleen | expr '%' expr { if ((long)$3 == 0) YYABORT; $$ = (long)$1 % (long)$3; } 8507516736SAndi Kleen | '-' expr %prec NEG { $$ = -$2; } 86d73bad06SAndi Kleen | '(' if_expr ')' { $$ = $2; } 87d73bad06SAndi Kleen | MIN '(' expr ',' expr ')' { $$ = $3 < $5 ? $3 : $5; } 88d73bad06SAndi Kleen | MAX '(' expr ',' expr ')' { $$ = $3 > $5 ? $3 : $5; } 89d73bad06SAndi Kleen | SMT_ON { $$ = smt_on() > 0; } 9007516736SAndi Kleen ; 9107516736SAndi Kleen 9207516736SAndi Kleen %% 9307516736SAndi Kleen 9407516736SAndi Kleen static int expr__symbol(YYSTYPE *res, const char *p, const char **pp) 9507516736SAndi Kleen { 9607516736SAndi Kleen char *dst = res->id; 9707516736SAndi Kleen const char *s = p; 9807516736SAndi Kleen 99d73bad06SAndi Kleen if (*p == '#') 100d73bad06SAndi Kleen *dst++ = *p++; 101d73bad06SAndi Kleen 102d73bad06SAndi Kleen while (isalnum(*p) || *p == '_' || *p == '.' || *p == ':' || *p == '@' || *p == '\\') { 10307516736SAndi Kleen if (p - s >= MAXIDLEN) 10407516736SAndi Kleen return -1; 105d73bad06SAndi Kleen /* 106d73bad06SAndi Kleen * Allow @ instead of / to be able to specify pmu/event/ without 107d73bad06SAndi Kleen * conflicts with normal division. 108d73bad06SAndi Kleen */ 109d73bad06SAndi Kleen if (*p == '@') 110d73bad06SAndi Kleen *dst++ = '/'; 111d73bad06SAndi Kleen else if (*p == '\\') 112d73bad06SAndi Kleen *dst++ = *++p; 113d73bad06SAndi Kleen else 114d73bad06SAndi Kleen *dst++ = *p; 115d73bad06SAndi Kleen p++; 11607516736SAndi Kleen } 11707516736SAndi Kleen *dst = 0; 11807516736SAndi Kleen *pp = p; 119d73bad06SAndi Kleen dst = res->id; 120d73bad06SAndi Kleen switch (dst[0]) { 121d73bad06SAndi Kleen case 'm': 122d73bad06SAndi Kleen if (!strcmp(dst, "min")) 123d73bad06SAndi Kleen return MIN; 124d73bad06SAndi Kleen if (!strcmp(dst, "max")) 125d73bad06SAndi Kleen return MAX; 126d73bad06SAndi Kleen break; 127d73bad06SAndi Kleen case 'i': 128d73bad06SAndi Kleen if (!strcmp(dst, "if")) 129d73bad06SAndi Kleen return IF; 130d73bad06SAndi Kleen break; 131d73bad06SAndi Kleen case 'e': 132d73bad06SAndi Kleen if (!strcmp(dst, "else")) 133d73bad06SAndi Kleen return ELSE; 134d73bad06SAndi Kleen break; 135d73bad06SAndi Kleen case '#': 136d73bad06SAndi Kleen if (!strcasecmp(dst, "#smt_on")) 137d73bad06SAndi Kleen return SMT_ON; 138d73bad06SAndi Kleen break; 139d73bad06SAndi Kleen } 14007516736SAndi Kleen return ID; 14107516736SAndi Kleen } 14207516736SAndi Kleen 14307516736SAndi Kleen static int expr__lex(YYSTYPE *res, const char **pp) 14407516736SAndi Kleen { 14507516736SAndi Kleen int tok; 14607516736SAndi Kleen const char *s; 14707516736SAndi Kleen const char *p = *pp; 14807516736SAndi Kleen 14907516736SAndi Kleen while (isspace(*p)) 15007516736SAndi Kleen p++; 15107516736SAndi Kleen s = p; 15207516736SAndi Kleen switch (*p++) { 153d73bad06SAndi Kleen case '#': 15407516736SAndi Kleen case 'a' ... 'z': 15507516736SAndi Kleen case 'A' ... 'Z': 15607516736SAndi Kleen return expr__symbol(res, p - 1, pp); 15707516736SAndi Kleen case '0' ... '9': case '.': 15807516736SAndi Kleen res->num = strtod(s, (char **)&p); 15907516736SAndi Kleen tok = NUMBER; 16007516736SAndi Kleen break; 16107516736SAndi Kleen default: 16207516736SAndi Kleen tok = *s; 16307516736SAndi Kleen break; 16407516736SAndi Kleen } 16507516736SAndi Kleen *pp = p; 16607516736SAndi Kleen return tok; 16707516736SAndi Kleen } 16807516736SAndi Kleen 16907516736SAndi Kleen /* Caller must make sure id is allocated */ 17007516736SAndi Kleen void expr__add_id(struct parse_ctx *ctx, const char *name, double val) 17107516736SAndi Kleen { 17207516736SAndi Kleen int idx; 17307516736SAndi Kleen assert(ctx->num_ids < MAX_PARSE_ID); 17407516736SAndi Kleen idx = ctx->num_ids++; 17507516736SAndi Kleen ctx->ids[idx].name = name; 17607516736SAndi Kleen ctx->ids[idx].val = val; 17707516736SAndi Kleen } 17807516736SAndi Kleen 17907516736SAndi Kleen void expr__ctx_init(struct parse_ctx *ctx) 18007516736SAndi Kleen { 18107516736SAndi Kleen ctx->num_ids = 0; 18207516736SAndi Kleen } 18307516736SAndi Kleen 184*d66dccdbSAndi Kleen static bool already_seen(const char *val, const char *one, const char **other, 185*d66dccdbSAndi Kleen int num_other) 186*d66dccdbSAndi Kleen { 187*d66dccdbSAndi Kleen int i; 188*d66dccdbSAndi Kleen 189*d66dccdbSAndi Kleen if (one && !strcasecmp(one, val)) 190*d66dccdbSAndi Kleen return true; 191*d66dccdbSAndi Kleen for (i = 0; i < num_other; i++) 192*d66dccdbSAndi Kleen if (!strcasecmp(other[i], val)) 193*d66dccdbSAndi Kleen return true; 194*d66dccdbSAndi Kleen return false; 195*d66dccdbSAndi Kleen } 196*d66dccdbSAndi Kleen 19707516736SAndi Kleen int expr__find_other(const char *p, const char *one, const char ***other, 19807516736SAndi Kleen int *num_otherp) 19907516736SAndi Kleen { 20007516736SAndi Kleen const char *orig = p; 20107516736SAndi Kleen int err = -1; 20207516736SAndi Kleen int num_other; 20307516736SAndi Kleen 20407516736SAndi Kleen *other = malloc((EXPR_MAX_OTHER + 1) * sizeof(char *)); 20507516736SAndi Kleen if (!*other) 20607516736SAndi Kleen return -1; 20707516736SAndi Kleen 20807516736SAndi Kleen num_other = 0; 20907516736SAndi Kleen for (;;) { 21007516736SAndi Kleen YYSTYPE val; 21107516736SAndi Kleen int tok = expr__lex(&val, &p); 21207516736SAndi Kleen if (tok == 0) { 21307516736SAndi Kleen err = 0; 21407516736SAndi Kleen break; 21507516736SAndi Kleen } 216*d66dccdbSAndi Kleen if (tok == ID && !already_seen(val.id, one, *other, num_other)) { 21707516736SAndi Kleen if (num_other >= EXPR_MAX_OTHER - 1) { 21807516736SAndi Kleen pr_debug("Too many extra events in %s\n", orig); 21907516736SAndi Kleen break; 22007516736SAndi Kleen } 22107516736SAndi Kleen (*other)[num_other] = strdup(val.id); 22207516736SAndi Kleen if (!(*other)[num_other]) 22307516736SAndi Kleen return -1; 22407516736SAndi Kleen num_other++; 22507516736SAndi Kleen } 22607516736SAndi Kleen } 22707516736SAndi Kleen (*other)[num_other] = NULL; 22807516736SAndi Kleen *num_otherp = num_other; 22907516736SAndi Kleen if (err) { 23007516736SAndi Kleen *num_otherp = 0; 23107516736SAndi Kleen free(*other); 23207516736SAndi Kleen *other = NULL; 23307516736SAndi Kleen } 23407516736SAndi Kleen return err; 23507516736SAndi Kleen } 236