GNU Linux-libre 4.14.290-gnu1
[releases.git] / tools / perf / util / expr.y
1 /* Simple expression parser */
2 %{
3 #include "util.h"
4 #include "util/debug.h"
5 #define IN_EXPR_Y 1
6 #include "expr.h"
7 #include "smt.h"
8 #include <string.h>
9
10 #define MAXIDLEN 256
11 %}
12
13 %define api.pure full
14
15 %parse-param { double *final_val }
16 %parse-param { struct parse_ctx *ctx }
17 %parse-param { const char **pp }
18 %lex-param { const char **pp }
19
20 %union {
21         double num;
22         char id[MAXIDLEN+1];
23 }
24
25 %token <num> NUMBER
26 %token <id> ID
27 %token MIN MAX IF ELSE SMT_ON
28 %left MIN MAX IF
29 %left '|'
30 %left '^'
31 %left '&'
32 %left '-' '+'
33 %left '*' '/' '%'
34 %left NEG NOT
35 %type <num> expr if_expr
36
37 %{
38 static int expr__lex(YYSTYPE *res, const char **pp);
39
40 static void expr__error(double *final_val __maybe_unused,
41                        struct parse_ctx *ctx __maybe_unused,
42                        const char **pp __maybe_unused,
43                        const char *s)
44 {
45         pr_debug("%s\n", s);
46 }
47
48 static int lookup_id(struct parse_ctx *ctx, char *id, double *val)
49 {
50         int i;
51
52         for (i = 0; i < ctx->num_ids; i++) {
53                 if (!strcasecmp(ctx->ids[i].name, id)) {
54                         *val = ctx->ids[i].val;
55                         return 0;
56                 }
57         }
58         return -1;
59 }
60
61 %}
62 %%
63
64 all_expr: if_expr                       { *final_val = $1; }
65         ;
66
67 if_expr:
68         expr IF expr ELSE expr { $$ = $3 ? $1 : $5; }
69         | expr
70         ;
71
72 expr:     NUMBER
73         | ID                    { if (lookup_id(ctx, $1, &$$) < 0) {
74                                         pr_debug("%s not found\n", $1);
75                                         YYABORT;
76                                   }
77                                 }
78         | expr '|' expr         { $$ = (long)$1 | (long)$3; }
79         | expr '&' expr         { $$ = (long)$1 & (long)$3; }
80         | expr '^' expr         { $$ = (long)$1 ^ (long)$3; }
81         | expr '+' expr         { $$ = $1 + $3; }
82         | expr '-' expr         { $$ = $1 - $3; }
83         | expr '*' expr         { $$ = $1 * $3; }
84         | expr '/' expr         { if ($3 == 0) YYABORT; $$ = $1 / $3; }
85         | expr '%' expr         { if ((long)$3 == 0) YYABORT; $$ = (long)$1 % (long)$3; }
86         | '-' expr %prec NEG    { $$ = -$2; }
87         | '(' if_expr ')'       { $$ = $2; }
88         | MIN '(' expr ',' expr ')' { $$ = $3 < $5 ? $3 : $5; }
89         | MAX '(' expr ',' expr ')' { $$ = $3 > $5 ? $3 : $5; }
90         | SMT_ON                 { $$ = smt_on() > 0; }
91         ;
92
93 %%
94
95 static int expr__symbol(YYSTYPE *res, const char *p, const char **pp)
96 {
97         char *dst = res->id;
98         const char *s = p;
99
100         if (*p == '#')
101                 *dst++ = *p++;
102
103         while (isalnum(*p) || *p == '_' || *p == '.' || *p == ':' || *p == '@' || *p == '\\') {
104                 if (p - s >= MAXIDLEN)
105                         return -1;
106                 /*
107                  * Allow @ instead of / to be able to specify pmu/event/ without
108                  * conflicts with normal division.
109                  */
110                 if (*p == '@')
111                         *dst++ = '/';
112                 else if (*p == '\\')
113                         *dst++ = *++p;
114                 else
115                         *dst++ = *p;
116                 p++;
117         }
118         *dst = 0;
119         *pp = p;
120         dst = res->id;
121         switch (dst[0]) {
122         case 'm':
123                 if (!strcmp(dst, "min"))
124                         return MIN;
125                 if (!strcmp(dst, "max"))
126                         return MAX;
127                 break;
128         case 'i':
129                 if (!strcmp(dst, "if"))
130                         return IF;
131                 break;
132         case 'e':
133                 if (!strcmp(dst, "else"))
134                         return ELSE;
135                 break;
136         case '#':
137                 if (!strcasecmp(dst, "#smt_on"))
138                         return SMT_ON;
139                 break;
140         }
141         return ID;
142 }
143
144 static int expr__lex(YYSTYPE *res, const char **pp)
145 {
146         int tok;
147         const char *s;
148         const char *p = *pp;
149
150         while (isspace(*p))
151                 p++;
152         s = p;
153         switch (*p++) {
154         case '#':
155         case 'a' ... 'z':
156         case 'A' ... 'Z':
157                 return expr__symbol(res, p - 1, pp);
158         case '0' ... '9': case '.':
159                 res->num = strtod(s, (char **)&p);
160                 tok = NUMBER;
161                 break;
162         default:
163                 tok = *s;
164                 break;
165         }
166         *pp = p;
167         return tok;
168 }
169
170 /* Caller must make sure id is allocated */
171 void expr__add_id(struct parse_ctx *ctx, const char *name, double val)
172 {
173         int idx;
174         assert(ctx->num_ids < MAX_PARSE_ID);
175         idx = ctx->num_ids++;
176         ctx->ids[idx].name = name;
177         ctx->ids[idx].val = val;
178 }
179
180 void expr__ctx_init(struct parse_ctx *ctx)
181 {
182         ctx->num_ids = 0;
183 }
184
185 static bool already_seen(const char *val, const char *one, const char **other,
186                          int num_other)
187 {
188         int i;
189
190         if (one && !strcasecmp(one, val))
191                 return true;
192         for (i = 0; i < num_other; i++)
193                 if (!strcasecmp(other[i], val))
194                         return true;
195         return false;
196 }
197
198 int expr__find_other(const char *p, const char *one, const char ***other,
199                      int *num_otherp)
200 {
201         const char *orig = p;
202         int err = -1;
203         int num_other;
204
205         *other = malloc((EXPR_MAX_OTHER + 1) * sizeof(char *));
206         if (!*other)
207                 return -1;
208
209         num_other = 0;
210         for (;;) {
211                 YYSTYPE val;
212                 int tok = expr__lex(&val, &p);
213                 if (tok == 0) {
214                         err = 0;
215                         break;
216                 }
217                 if (tok == ID && !already_seen(val.id, one, *other, num_other)) {
218                         if (num_other >= EXPR_MAX_OTHER - 1) {
219                                 pr_debug("Too many extra events in %s\n", orig);
220                                 break;
221                         }
222                         (*other)[num_other] = strdup(val.id);
223                         if (!(*other)[num_other])
224                                 return -1;
225                         num_other++;
226                 }
227         }
228         (*other)[num_other] = NULL;
229         *num_otherp = num_other;
230         if (err) {
231                 *num_otherp = 0;
232                 free(*other);
233                 *other = NULL;
234         }
235         return err;
236 }