summaryrefslogtreecommitdiff
path: root/tools/perf/util/expr.l
blob: 74b9b59b1aa5c8508639c3e47d2fe2dd0dcb7f12 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
%option prefix="expr_"
%option reentrant
%option bison-bridge

%{
#include <linux/compiler.h>
#include "expr.h"
#include "expr-bison.h"

char *expr_get_text(yyscan_t yyscanner);
YYSTYPE *expr_get_lval(yyscan_t yyscanner);

static int __value(YYSTYPE *yylval, char *str, int base, int token)
{
	u64 num;

	errno = 0;
	num = strtoull(str, NULL, base);
	if (errno)
		return EXPR_ERROR;

	yylval->num = num;
	return token;
}

static int value(yyscan_t scanner, int base)
{
	YYSTYPE *yylval = expr_get_lval(scanner);
	char *text = expr_get_text(scanner);

	return __value(yylval, text, base, NUMBER);
}

/*
 * Allow @ instead of / to be able to specify pmu/event/ without
 * conflicts with normal division.
 */
static char *normalize(char *str, int runtime)
{
	char *ret = str;
	char *dst = str;

	while (*str) {
		if (*str == '@')
			*dst++ = '/';
		else if (*str == '\\')
			*dst++ = *++str;
		 else if (*str == '?') {
			char *paramval;
			int i = 0;
			int size = asprintf(&paramval, "%d", runtime);

			if (size < 0)
				*dst++ = '0';
			else {
				while (i < size)
					*dst++ = paramval[i++];
				free(paramval);
			}
		}
		else
			*dst++ = *str;
		str++;
	}

	*dst = 0x0;
	return ret;
}

static int str(yyscan_t scanner, int token, int runtime)
{
	YYSTYPE *yylval = expr_get_lval(scanner);
	char *text = expr_get_text(scanner);

	yylval->str = normalize(strdup(text), runtime);
	if (!yylval->str)
		return EXPR_ERROR;

	yylval->str = normalize(yylval->str, runtime);
	return token;
}
%}

number		[0-9]+

sch		[-,=]
spec		\\{sch}
sym		[0-9a-zA-Z_\.:@?]+
symbol		{spec}*{sym}*{spec}*{sym}*{spec}*{sym}

%%
	struct expr_scanner_ctx *sctx = expr_get_extra(yyscanner);

	{
		int start_token = sctx->start_token;

		if (sctx->start_token) {
			sctx->start_token = 0;
			return start_token;
		}
	}

max		{ return MAX; }
min		{ return MIN; }
if		{ return IF; }
else		{ return ELSE; }
#smt_on		{ return SMT_ON; }
{number}	{ return value(yyscanner, 10); }
{symbol}	{ return str(yyscanner, ID, sctx->runtime); }
"|"		{ return '|'; }
"^"		{ return '^'; }
"&"		{ return '&'; }
"-"		{ return '-'; }
"+"		{ return '+'; }
"*"		{ return '*'; }
"/"		{ return '/'; }
"%"		{ return '%'; }
"("		{ return '('; }
")"		{ return ')'; }
","		{ return ','; }
.		{ }
%%

int expr_wrap(void *scanner __maybe_unused)
{
	return 1;
}