brintos

brintos / linux-shallow public Read only

0
0
Text · 8.4 KiB · e364790 Raw
382 lines · plain
1/* Simple expression parser */2%{3#ifndef NDEBUG4#define YYDEBUG 15#endif6#include <assert.h>7#include <math.h>8#include <stdlib.h>9#include "util/debug.h"10#define IN_EXPR_Y 111#include "expr.h"12#include "expr-bison.h"13int expr_lex(YYSTYPE * yylval_param , void *yyscanner);14%}15 16%define api.pure full17 18%parse-param { double *final_val }19%parse-param { struct expr_parse_ctx *ctx }20%parse-param { bool compute_ids }21%parse-param {void *scanner}22%lex-param {void* scanner}23 24%union {25	double	 num;26	char	*str;27	struct ids {28		/*29		 * When creating ids, holds the working set of event ids. NULL30		 * implies the set is empty.31		 */32		struct hashmap *ids;33		/*34		 * The metric value. When not creating ids this is the value35		 * read from a counter, a constant or some computed value. When36		 * creating ids the value is either a constant or BOTTOM. NAN is37		 * used as the special BOTTOM value, representing a "set of all38		 * values" case.39		 */40		double val;41	} ids;42}43 44%token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT HAS_EVENT STRCMP_CPUID_STR EXPR_ERROR45%left MIN MAX IF46%left '|'47%left '^'48%left '&'49%left '<' '>'50%left '-' '+'51%left '*' '/' '%'52%left NEG NOT53%type <num> NUMBER LITERAL54%type <str> ID55%destructor { free ($$); } <str>56%type <ids> expr if_expr57%destructor { ids__free($$.ids); } <ids>58 59%{60static void expr_error(double *final_val __maybe_unused,61		       struct expr_parse_ctx *ctx __maybe_unused,62		       bool compute_ids __maybe_unused,63		       void *scanner __maybe_unused,64		       const char *s)65{66	pr_debug("%s\n", s);67}68 69/*70 * During compute ids, the special "bottom" value uses NAN to represent the set71 * of all values. NAN is selected as it isn't a useful constant value.72 */73#define BOTTOM NAN74 75/* During computing ids, does val represent a constant (non-BOTTOM) value? */76static bool is_const(double val)77{78	return isfinite(val);79}80 81static struct ids union_expr(struct ids ids1, struct ids ids2)82{83	struct ids result = {84		.val = BOTTOM,85		.ids = ids__union(ids1.ids, ids2.ids),86	};87	return result;88}89 90static struct ids handle_id(struct expr_parse_ctx *ctx, char *id,91			    bool compute_ids, bool source_count)92{93	struct ids result;94 95	if (!compute_ids) {96		/*97		 * Compute the event's value from ID. If the ID isn't known then98		 * it isn't used to compute the formula so set to NAN.99		 */100		struct expr_id_data *data;101 102		result.val = NAN;103		if (expr__resolve_id(ctx, id, &data) == 0) {104			result.val = source_count105				? expr_id_data__source_count(data)106				: expr_id_data__value(data);107		}108		result.ids = NULL;109		free(id);110	} else {111		/*112		 * Set the value to BOTTOM to show that any value is possible113		 * when the event is computed. Create a set of just the ID.114		 */115		result.val = BOTTOM;116		result.ids = ids__new();117		if (!result.ids || ids__insert(result.ids, id)) {118			pr_err("Error creating IDs for '%s'", id);119			free(id);120		}121	}122	return result;123}124 125/*126 * If we're not computing ids or $1 and $3 are constants, compute the new127 * constant value using OP. Its invariant that there are no ids.  If computing128 * ids for non-constants union the set of IDs that must be computed.129 */130#define BINARY_OP(RESULT, OP, LHS, RHS)					\131	if (!compute_ids || (is_const(LHS.val) && is_const(RHS.val))) { \132		assert(LHS.ids == NULL);				\133		assert(RHS.ids == NULL);				\134		if (isnan(LHS.val) || isnan(RHS.val)) {			\135			RESULT.val = NAN;				\136		} else {						\137			RESULT.val = LHS.val OP RHS.val;		\138		}							\139		RESULT.ids = NULL;					\140	} else {							\141	        RESULT = union_expr(LHS, RHS);				\142	}143 144%}145%%146 147start: if_expr148{149	if (compute_ids)150		ctx->ids = ids__union($1.ids, ctx->ids);151 152	if (final_val)153		*final_val = $1.val;154}155;156 157if_expr: expr IF expr ELSE if_expr158{159	if (fpclassify($3.val) == FP_ZERO) {160		/*161		 * The IF expression evaluated to 0 so treat as false, take the162		 * ELSE and discard everything else.163		 */164		$$.val = $5.val;165		$$.ids = $5.ids;166		ids__free($1.ids);167		ids__free($3.ids);168	} else if (!compute_ids || is_const($3.val)) {169		/*170		 * If ids aren't computed then treat the expression as true. If171		 * ids are being computed and the IF expr is a non-zero172		 * constant, then also evaluate the true case.173		 */174		$$.val = $1.val;175		$$.ids = $1.ids;176		ids__free($3.ids);177		ids__free($5.ids);178	} else if ($1.val == $5.val) {179		/*180		 * LHS == RHS, so both are an identical constant. No need to181		 * evaluate any events.182		 */183		$$.val = $1.val;184		$$.ids = NULL;185		ids__free($1.ids);186		ids__free($3.ids);187		ids__free($5.ids);188	} else {189		/*190		 * Value is either the LHS or RHS and we need the IF expression191		 * to compute it.192		 */193		$$ = union_expr($1, union_expr($3, $5));194	}195}196| expr197;198 199expr: NUMBER200{201	$$.val = $1;202	$$.ids = NULL;203}204| ID				{ $$ = handle_id(ctx, $1, compute_ids, /*source_count=*/false); }205| SOURCE_COUNT '(' ID ')'	{ $$ = handle_id(ctx, $3, compute_ids, /*source_count=*/true); }206| HAS_EVENT '(' ID ')'207{208	$$.val = expr__has_event(ctx, compute_ids, $3);209	$$.ids = NULL;210	free($3);211}212| STRCMP_CPUID_STR '(' ID ')'213{214	$$.val = expr__strcmp_cpuid_str(ctx, compute_ids, $3);215	$$.ids = NULL;216	free($3);217}218| expr '|' expr219{220	if (is_const($1.val) && is_const($3.val)) {221		assert($1.ids == NULL);222		assert($3.ids == NULL);223		$$.ids = NULL;224		$$.val = (fpclassify($1.val) == FP_ZERO && fpclassify($3.val) == FP_ZERO) ? 0 : 1;225	} else if (is_const($1.val)) {226		assert($1.ids == NULL);227		if (fpclassify($1.val) == FP_ZERO) {228			$$ = $3;229		} else {230			$$.val = 1;231			$$.ids = NULL;232			ids__free($3.ids);233		}234	} else if (is_const($3.val)) {235		assert($3.ids == NULL);236		if (fpclassify($3.val) == FP_ZERO) {237			$$ = $1;238		} else {239			$$.val = 1;240			$$.ids = NULL;241			ids__free($1.ids);242		}243	} else {244		$$ = union_expr($1, $3);245	}246}247| expr '&' expr248{249	if (is_const($1.val) && is_const($3.val)) {250		assert($1.ids == NULL);251		assert($3.ids == NULL);252		$$.val = (fpclassify($1.val) != FP_ZERO && fpclassify($3.val) != FP_ZERO) ? 1 : 0;253		$$.ids = NULL;254	} else if (is_const($1.val)) {255		assert($1.ids == NULL);256		if (fpclassify($1.val) != FP_ZERO) {257			$$ = $3;258		} else {259			$$.val = 0;260			$$.ids = NULL;261			ids__free($3.ids);262		}263	} else if (is_const($3.val)) {264		assert($3.ids == NULL);265		if (fpclassify($3.val) != FP_ZERO) {266			$$ = $1;267		} else {268			$$.val = 0;269			$$.ids = NULL;270			ids__free($1.ids);271		}272	} else {273		$$ = union_expr($1, $3);274	}275}276| expr '^' expr277{278	if (is_const($1.val) && is_const($3.val)) {279		assert($1.ids == NULL);280		assert($3.ids == NULL);281		$$.val = (fpclassify($1.val) == FP_ZERO) != (fpclassify($3.val) == FP_ZERO) ? 1 : 0;282		$$.ids = NULL;283	} else {284		$$ = union_expr($1, $3);285	}286}287| expr '<' expr { BINARY_OP($$, <, $1, $3); }288| expr '>' expr { BINARY_OP($$, >, $1, $3); }289| expr '+' expr { BINARY_OP($$, +, $1, $3); }290| expr '-' expr { BINARY_OP($$, -, $1, $3); }291| expr '*' expr { BINARY_OP($$, *, $1, $3); }292| expr '/' expr293{294	if (fpclassify($3.val) == FP_ZERO) {295		pr_debug("division by zero\n");296		assert($3.ids == NULL);297		if (compute_ids)298			ids__free($1.ids);299		$$.val = NAN;300		$$.ids = NULL;301	} else if (!compute_ids || (is_const($1.val) && is_const($3.val))) {302		assert($1.ids == NULL);303		assert($3.ids == NULL);304		$$.val = $1.val / $3.val;305		$$.ids = NULL;306	} else {307		/* LHS and/or RHS need computing from event IDs so union. */308		$$ = union_expr($1, $3);309	}310}311| expr '%' expr312{313	if (fpclassify($3.val) == FP_ZERO) {314		pr_debug("division by zero\n");315		YYABORT;316	} else if (!compute_ids || (is_const($1.val) && is_const($3.val))) {317		assert($1.ids == NULL);318		assert($3.ids == NULL);319		$$.val = (long)$1.val % (long)$3.val;320		$$.ids = NULL;321	} else {322		/* LHS and/or RHS need computing from event IDs so union. */323		$$ = union_expr($1, $3);324	}325}326| D_RATIO '(' expr ',' expr ')'327{328	if (fpclassify($5.val) == FP_ZERO) {329		/*330		 * Division by constant zero always yields zero and no events331		 * are necessary.332		 */333		assert($5.ids == NULL);334		$$.val = 0.0;335		$$.ids = NULL;336		ids__free($3.ids);337	} else if (!compute_ids || (is_const($3.val) && is_const($5.val))) {338		assert($3.ids == NULL);339		assert($5.ids == NULL);340		$$.val = $3.val / $5.val;341		$$.ids = NULL;342	} else {343		/* LHS and/or RHS need computing from event IDs so union. */344		$$ = union_expr($3, $5);345	}346}347| '-' expr %prec NEG348{349	$$.val = -$2.val;350	$$.ids = $2.ids;351}352| '(' if_expr ')'353{354	$$ = $2;355}356| MIN '(' expr ',' expr ')'357{358	if (!compute_ids) {359		$$.val = $3.val < $5.val ? $3.val : $5.val;360		$$.ids = NULL;361	} else {362		$$ = union_expr($3, $5);363	}364}365| MAX '(' expr ',' expr ')'366{367	if (!compute_ids) {368		$$.val = $3.val > $5.val ? $3.val : $5.val;369		$$.ids = NULL;370	} else {371		$$ = union_expr($3, $5);372	}373}374| LITERAL375{376	$$.val = $1;377	$$.ids = NULL;378}379;380 381%%382