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