| /* Simple expression parser */ | 
 | %{ | 
 | #include "util.h" | 
 | #include "util/debug.h" | 
 | #define IN_EXPR_Y 1 | 
 | #include "expr.h" | 
 | #include "smt.h" | 
 | #include <string.h> | 
 |  | 
 | #define MAXIDLEN 256 | 
 | %} | 
 |  | 
 | %pure-parser | 
 | %parse-param { double *final_val } | 
 | %parse-param { struct parse_ctx *ctx } | 
 | %parse-param { const char **pp } | 
 | %lex-param { const char **pp } | 
 |  | 
 | %union { | 
 | 	double num; | 
 | 	char id[MAXIDLEN+1]; | 
 | } | 
 |  | 
 | %token <num> NUMBER | 
 | %token <id> ID | 
 | %token MIN MAX IF ELSE SMT_ON | 
 | %left MIN MAX IF | 
 | %left '|' | 
 | %left '^' | 
 | %left '&' | 
 | %left '-' '+' | 
 | %left '*' '/' '%' | 
 | %left NEG NOT | 
 | %type <num> expr if_expr | 
 |  | 
 | %{ | 
 | static int expr__lex(YYSTYPE *res, const char **pp); | 
 |  | 
 | static void expr__error(double *final_val __maybe_unused, | 
 | 		       struct parse_ctx *ctx __maybe_unused, | 
 | 		       const char **pp __maybe_unused, | 
 | 		       const char *s) | 
 | { | 
 | 	pr_debug("%s\n", s); | 
 | } | 
 |  | 
 | static int lookup_id(struct parse_ctx *ctx, char *id, double *val) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < ctx->num_ids; i++) { | 
 | 		if (!strcasecmp(ctx->ids[i].name, id)) { | 
 | 			*val = ctx->ids[i].val; | 
 | 			return 0; | 
 | 		} | 
 | 	} | 
 | 	return -1; | 
 | } | 
 |  | 
 | %} | 
 | %% | 
 |  | 
 | all_expr: if_expr			{ *final_val = $1; } | 
 | 	; | 
 |  | 
 | if_expr: | 
 | 	expr IF expr ELSE expr { $$ = $3 ? $1 : $5; } | 
 | 	| expr | 
 | 	; | 
 |  | 
 | expr:	  NUMBER | 
 | 	| ID			{ if (lookup_id(ctx, $1, &$$) < 0) { | 
 | 					pr_debug("%s not found\n", $1); | 
 | 					YYABORT; | 
 | 				  } | 
 | 				} | 
 | 	| expr '|' expr		{ $$ = (long)$1 | (long)$3; } | 
 | 	| expr '&' expr		{ $$ = (long)$1 & (long)$3; } | 
 | 	| expr '^' expr		{ $$ = (long)$1 ^ (long)$3; } | 
 | 	| expr '+' expr		{ $$ = $1 + $3; } | 
 | 	| expr '-' expr		{ $$ = $1 - $3; } | 
 | 	| expr '*' expr		{ $$ = $1 * $3; } | 
 | 	| expr '/' expr		{ if ($3 == 0) YYABORT; $$ = $1 / $3; } | 
 | 	| expr '%' expr		{ if ((long)$3 == 0) YYABORT; $$ = (long)$1 % (long)$3; } | 
 | 	| '-' expr %prec NEG	{ $$ = -$2; } | 
 | 	| '(' if_expr ')'	{ $$ = $2; } | 
 | 	| MIN '(' expr ',' expr ')' { $$ = $3 < $5 ? $3 : $5; } | 
 | 	| MAX '(' expr ',' expr ')' { $$ = $3 > $5 ? $3 : $5; } | 
 | 	| SMT_ON		 { $$ = smt_on() > 0; } | 
 | 	; | 
 |  | 
 | %% | 
 |  | 
 | static int expr__symbol(YYSTYPE *res, const char *p, const char **pp) | 
 | { | 
 | 	char *dst = res->id; | 
 | 	const char *s = p; | 
 |  | 
 | 	if (*p == '#') | 
 | 		*dst++ = *p++; | 
 |  | 
 | 	while (isalnum(*p) || *p == '_' || *p == '.' || *p == ':' || *p == '@' || *p == '\\') { | 
 | 		if (p - s >= MAXIDLEN) | 
 | 			return -1; | 
 | 		/* | 
 | 		 * Allow @ instead of / to be able to specify pmu/event/ without | 
 | 		 * conflicts with normal division. | 
 | 		 */ | 
 | 		if (*p == '@') | 
 | 			*dst++ = '/'; | 
 | 		else if (*p == '\\') | 
 | 			*dst++ = *++p; | 
 | 		else | 
 | 			*dst++ = *p; | 
 | 		p++; | 
 | 	} | 
 | 	*dst = 0; | 
 | 	*pp = p; | 
 | 	dst = res->id; | 
 | 	switch (dst[0]) { | 
 | 	case 'm': | 
 | 		if (!strcmp(dst, "min")) | 
 | 			return MIN; | 
 | 		if (!strcmp(dst, "max")) | 
 | 			return MAX; | 
 | 		break; | 
 | 	case 'i': | 
 | 		if (!strcmp(dst, "if")) | 
 | 			return IF; | 
 | 		break; | 
 | 	case 'e': | 
 | 		if (!strcmp(dst, "else")) | 
 | 			return ELSE; | 
 | 		break; | 
 | 	case '#': | 
 | 		if (!strcasecmp(dst, "#smt_on")) | 
 | 			return SMT_ON; | 
 | 		break; | 
 | 	} | 
 | 	return ID; | 
 | } | 
 |  | 
 | static int expr__lex(YYSTYPE *res, const char **pp) | 
 | { | 
 | 	int tok; | 
 | 	const char *s; | 
 | 	const char *p = *pp; | 
 |  | 
 | 	while (isspace(*p)) | 
 | 		p++; | 
 | 	s = p; | 
 | 	switch (*p++) { | 
 | 	case '#': | 
 | 	case 'a' ... 'z': | 
 | 	case 'A' ... 'Z': | 
 | 		return expr__symbol(res, p - 1, pp); | 
 | 	case '0' ... '9': case '.': | 
 | 		res->num = strtod(s, (char **)&p); | 
 | 		tok = NUMBER; | 
 | 		break; | 
 | 	default: | 
 | 		tok = *s; | 
 | 		break; | 
 | 	} | 
 | 	*pp = p; | 
 | 	return tok; | 
 | } | 
 |  | 
 | /* Caller must make sure id is allocated */ | 
 | void expr__add_id(struct parse_ctx *ctx, const char *name, double val) | 
 | { | 
 | 	int idx; | 
 | 	assert(ctx->num_ids < MAX_PARSE_ID); | 
 | 	idx = ctx->num_ids++; | 
 | 	ctx->ids[idx].name = name; | 
 | 	ctx->ids[idx].val = val; | 
 | } | 
 |  | 
 | void expr__ctx_init(struct parse_ctx *ctx) | 
 | { | 
 | 	ctx->num_ids = 0; | 
 | } | 
 |  | 
 | static bool already_seen(const char *val, const char *one, const char **other, | 
 | 			 int num_other) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	if (one && !strcasecmp(one, val)) | 
 | 		return true; | 
 | 	for (i = 0; i < num_other; i++) | 
 | 		if (!strcasecmp(other[i], val)) | 
 | 			return true; | 
 | 	return false; | 
 | } | 
 |  | 
 | int expr__find_other(const char *p, const char *one, const char ***other, | 
 | 		     int *num_otherp) | 
 | { | 
 | 	const char *orig = p; | 
 | 	int err = -1; | 
 | 	int num_other; | 
 |  | 
 | 	*other = malloc((EXPR_MAX_OTHER + 1) * sizeof(char *)); | 
 | 	if (!*other) | 
 | 		return -1; | 
 |  | 
 | 	num_other = 0; | 
 | 	for (;;) { | 
 | 		YYSTYPE val; | 
 | 		int tok = expr__lex(&val, &p); | 
 | 		if (tok == 0) { | 
 | 			err = 0; | 
 | 			break; | 
 | 		} | 
 | 		if (tok == ID && !already_seen(val.id, one, *other, num_other)) { | 
 | 			if (num_other >= EXPR_MAX_OTHER - 1) { | 
 | 				pr_debug("Too many extra events in %s\n", orig); | 
 | 				break; | 
 | 			} | 
 | 			(*other)[num_other] = strdup(val.id); | 
 | 			if (!(*other)[num_other]) | 
 | 				return -1; | 
 | 			num_other++; | 
 | 		} | 
 | 	} | 
 | 	(*other)[num_other] = NULL; | 
 | 	*num_otherp = num_other; | 
 | 	if (err) { | 
 | 		*num_otherp = 0; | 
 | 		free(*other); | 
 | 		*other = NULL; | 
 | 	} | 
 | 	return err; | 
 | } |