xref: /linux/tools/perf/util/expr.y (revision e58e871becec2d3b04ed91c0c16fe8deac9c9dfa)
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 <string.h>
8 
9 #define MAXIDLEN 256
10 %}
11 
12 %pure-parser
13 %parse-param { double *final_val }
14 %parse-param { struct parse_ctx *ctx }
15 %parse-param { const char **pp }
16 %lex-param { const char **pp }
17 
18 %union {
19 	double num;
20 	char id[MAXIDLEN+1];
21 }
22 
23 %token <num> NUMBER
24 %token <id> ID
25 %left '|'
26 %left '^'
27 %left '&'
28 %left '-' '+'
29 %left '*' '/' '%'
30 %left NEG NOT
31 %type <num> expr
32 
33 %{
34 static int expr__lex(YYSTYPE *res, const char **pp);
35 
36 static void expr__error(double *final_val __maybe_unused,
37 		       struct parse_ctx *ctx __maybe_unused,
38 		       const char **pp __maybe_unused,
39 		       const char *s)
40 {
41 	pr_debug("%s\n", s);
42 }
43 
44 static int lookup_id(struct parse_ctx *ctx, char *id, double *val)
45 {
46 	int i;
47 
48 	for (i = 0; i < ctx->num_ids; i++) {
49 		if (!strcasecmp(ctx->ids[i].name, id)) {
50 			*val = ctx->ids[i].val;
51 			return 0;
52 		}
53 	}
54 	return -1;
55 }
56 
57 %}
58 %%
59 
60 all_expr: expr			{ *final_val = $1; }
61 	;
62 
63 expr:	  NUMBER
64 	| ID			{ if (lookup_id(ctx, $1, &$$) < 0) {
65 					pr_debug("%s not found", $1);
66 					YYABORT;
67 				  }
68 				}
69 	| expr '+' expr		{ $$ = $1 + $3; }
70 	| expr '-' expr		{ $$ = $1 - $3; }
71 	| expr '*' expr		{ $$ = $1 * $3; }
72 	| expr '/' expr		{ if ($3 == 0) YYABORT; $$ = $1 / $3; }
73 	| expr '%' expr		{ if ((long)$3 == 0) YYABORT; $$ = (long)$1 % (long)$3; }
74 	| '-' expr %prec NEG	{ $$ = -$2; }
75 	| '(' expr ')'		{ $$ = $2; }
76 	;
77 
78 %%
79 
80 static int expr__symbol(YYSTYPE *res, const char *p, const char **pp)
81 {
82 	char *dst = res->id;
83 	const char *s = p;
84 
85 	while (isalnum(*p) || *p == '_' || *p == '.') {
86 		if (p - s >= MAXIDLEN)
87 			return -1;
88 		*dst++ = *p++;
89 	}
90 	*dst = 0;
91 	*pp = p;
92 	return ID;
93 }
94 
95 static int expr__lex(YYSTYPE *res, const char **pp)
96 {
97 	int tok;
98 	const char *s;
99 	const char *p = *pp;
100 
101 	while (isspace(*p))
102 		p++;
103 	s = p;
104 	switch (*p++) {
105 	case 'a' ... 'z':
106 	case 'A' ... 'Z':
107 		return expr__symbol(res, p - 1, pp);
108 	case '0' ... '9': case '.':
109 		res->num = strtod(s, (char **)&p);
110 		tok = NUMBER;
111 		break;
112 	default:
113 		tok = *s;
114 		break;
115 	}
116 	*pp = p;
117 	return tok;
118 }
119 
120 /* Caller must make sure id is allocated */
121 void expr__add_id(struct parse_ctx *ctx, const char *name, double val)
122 {
123 	int idx;
124 	assert(ctx->num_ids < MAX_PARSE_ID);
125 	idx = ctx->num_ids++;
126 	ctx->ids[idx].name = name;
127 	ctx->ids[idx].val = val;
128 }
129 
130 void expr__ctx_init(struct parse_ctx *ctx)
131 {
132 	ctx->num_ids = 0;
133 }
134 
135 int expr__find_other(const char *p, const char *one, const char ***other,
136 		     int *num_otherp)
137 {
138 	const char *orig = p;
139 	int err = -1;
140 	int num_other;
141 
142 	*other = malloc((EXPR_MAX_OTHER + 1) * sizeof(char *));
143 	if (!*other)
144 		return -1;
145 
146 	num_other = 0;
147 	for (;;) {
148 		YYSTYPE val;
149 		int tok = expr__lex(&val, &p);
150 		if (tok == 0) {
151 			err = 0;
152 			break;
153 		}
154 		if (tok == ID && strcasecmp(one, val.id)) {
155 			if (num_other >= EXPR_MAX_OTHER - 1) {
156 				pr_debug("Too many extra events in %s\n", orig);
157 				break;
158 			}
159 			(*other)[num_other] = strdup(val.id);
160 			if (!(*other)[num_other])
161 				return -1;
162 			num_other++;
163 		}
164 	}
165 	(*other)[num_other] = NULL;
166 	*num_otherp = num_other;
167 	if (err) {
168 		*num_otherp = 0;
169 		free(*other);
170 		*other = NULL;
171 	}
172 	return err;
173 }
174