Back to home page

OSCL-LXR

 
 

    


0001 /* Simple expression parser */
0002 %{
0003 #define YYDEBUG 1
0004 #include <assert.h>
0005 #include <math.h>
0006 #include <stdlib.h>
0007 #include "util/debug.h"
0008 #define IN_EXPR_Y 1
0009 #include "expr.h"
0010 %}
0011 
0012 %define api.pure full
0013 
0014 %parse-param { double *final_val }
0015 %parse-param { struct expr_parse_ctx *ctx }
0016 %parse-param { bool compute_ids }
0017 %parse-param {void *scanner}
0018 %lex-param {void* scanner}
0019 
0020 %union {
0021         double   num;
0022         char    *str;
0023         struct ids {
0024                 /*
0025                  * When creating ids, holds the working set of event ids. NULL
0026                  * implies the set is empty.
0027                  */
0028                 struct hashmap *ids;
0029                 /*
0030                  * The metric value. When not creating ids this is the value
0031                  * read from a counter, a constant or some computed value. When
0032                  * creating ids the value is either a constant or BOTTOM. NAN is
0033                  * used as the special BOTTOM value, representing a "set of all
0034                  * values" case.
0035                  */
0036                 double val;
0037         } ids;
0038 }
0039 
0040 %token ID NUMBER MIN MAX IF ELSE LITERAL D_RATIO SOURCE_COUNT EXPR_ERROR
0041 %left MIN MAX IF
0042 %left '|'
0043 %left '^'
0044 %left '&'
0045 %left '<' '>'
0046 %left '-' '+'
0047 %left '*' '/' '%'
0048 %left NEG NOT
0049 %type <num> NUMBER LITERAL
0050 %type <str> ID
0051 %destructor { free ($$); } <str>
0052 %type <ids> expr if_expr
0053 %destructor { ids__free($$.ids); } <ids>
0054 
0055 %{
0056 static void expr_error(double *final_val __maybe_unused,
0057                        struct expr_parse_ctx *ctx __maybe_unused,
0058                        bool compute_ids __maybe_unused,
0059                        void *scanner,
0060                        const char *s)
0061 {
0062         pr_debug("%s\n", s);
0063 }
0064 
0065 /*
0066  * During compute ids, the special "bottom" value uses NAN to represent the set
0067  * of all values. NAN is selected as it isn't a useful constant value.
0068  */
0069 #define BOTTOM NAN
0070 
0071 /* During computing ids, does val represent a constant (non-BOTTOM) value? */
0072 static bool is_const(double val)
0073 {
0074         return isfinite(val);
0075 }
0076 
0077 static struct ids union_expr(struct ids ids1, struct ids ids2)
0078 {
0079         struct ids result = {
0080                 .val = BOTTOM,
0081                 .ids = ids__union(ids1.ids, ids2.ids),
0082         };
0083         return result;
0084 }
0085 
0086 static struct ids handle_id(struct expr_parse_ctx *ctx, char *id,
0087                             bool compute_ids, bool source_count)
0088 {
0089         struct ids result;
0090 
0091         if (!compute_ids) {
0092                 /*
0093                  * Compute the event's value from ID. If the ID isn't known then
0094                  * it isn't used to compute the formula so set to NAN.
0095                  */
0096                 struct expr_id_data *data;
0097 
0098                 result.val = NAN;
0099                 if (expr__resolve_id(ctx, id, &data) == 0) {
0100                         result.val = source_count
0101                                 ? expr_id_data__source_count(data)
0102                                 : expr_id_data__value(data);
0103                 }
0104                 result.ids = NULL;
0105                 free(id);
0106         } else {
0107                 /*
0108                  * Set the value to BOTTOM to show that any value is possible
0109                  * when the event is computed. Create a set of just the ID.
0110                  */
0111                 result.val = BOTTOM;
0112                 result.ids = ids__new();
0113                 if (!result.ids || ids__insert(result.ids, id)) {
0114                         pr_err("Error creating IDs for '%s'", id);
0115                         free(id);
0116                 }
0117         }
0118         return result;
0119 }
0120 
0121 /*
0122  * If we're not computing ids or $1 and $3 are constants, compute the new
0123  * constant value using OP. Its invariant that there are no ids.  If computing
0124  * ids for non-constants union the set of IDs that must be computed.
0125  */
0126 #define BINARY_LONG_OP(RESULT, OP, LHS, RHS)                            \
0127         if (!compute_ids || (is_const(LHS.val) && is_const(RHS.val))) { \
0128                 assert(LHS.ids == NULL);                                \
0129                 assert(RHS.ids == NULL);                                \
0130                 RESULT.val = (long)LHS.val OP (long)RHS.val;            \
0131                 RESULT.ids = NULL;                                      \
0132         } else {                                                        \
0133                 RESULT = union_expr(LHS, RHS);                          \
0134         }
0135 
0136 #define BINARY_OP(RESULT, OP, LHS, RHS)                                 \
0137         if (!compute_ids || (is_const(LHS.val) && is_const(RHS.val))) { \
0138                 assert(LHS.ids == NULL);                                \
0139                 assert(RHS.ids == NULL);                                \
0140                 RESULT.val = LHS.val OP RHS.val;                        \
0141                 RESULT.ids = NULL;                                      \
0142         } else {                                                        \
0143                 RESULT = union_expr(LHS, RHS);                          \
0144         }
0145 
0146 %}
0147 %%
0148 
0149 start: if_expr
0150 {
0151         if (compute_ids)
0152                 ctx->ids = ids__union($1.ids, ctx->ids);
0153 
0154         if (final_val)
0155                 *final_val = $1.val;
0156 }
0157 ;
0158 
0159 if_expr: expr IF expr ELSE expr
0160 {
0161         if (fpclassify($3.val) == FP_ZERO) {
0162                 /*
0163                  * The IF expression evaluated to 0 so treat as false, take the
0164                  * ELSE and discard everything else.
0165                  */
0166                 $$.val = $5.val;
0167                 $$.ids = $5.ids;
0168                 ids__free($1.ids);
0169                 ids__free($3.ids);
0170         } else if (!compute_ids || is_const($3.val)) {
0171                 /*
0172                  * If ids aren't computed then treat the expression as true. If
0173                  * ids are being computed and the IF expr is a non-zero
0174                  * constant, then also evaluate the true case.
0175                  */
0176                 $$.val = $1.val;
0177                 $$.ids = $1.ids;
0178                 ids__free($3.ids);
0179                 ids__free($5.ids);
0180         } else if ($1.val == $5.val) {
0181                 /*
0182                  * LHS == RHS, so both are an identical constant. No need to
0183                  * evaluate any events.
0184                  */
0185                 $$.val = $1.val;
0186                 $$.ids = NULL;
0187                 ids__free($1.ids);
0188                 ids__free($3.ids);
0189                 ids__free($5.ids);
0190         } else {
0191                 /*
0192                  * Value is either the LHS or RHS and we need the IF expression
0193                  * to compute it.
0194                  */
0195                 $$ = union_expr($1, union_expr($3, $5));
0196         }
0197 }
0198 | expr
0199 ;
0200 
0201 expr: NUMBER
0202 {
0203         $$.val = $1;
0204         $$.ids = NULL;
0205 }
0206 | ID                            { $$ = handle_id(ctx, $1, compute_ids, /*source_count=*/false); }
0207 | SOURCE_COUNT '(' ID ')'       { $$ = handle_id(ctx, $3, compute_ids, /*source_count=*/true); }
0208 | expr '|' expr { BINARY_LONG_OP($$, |, $1, $3); }
0209 | expr '&' expr { BINARY_LONG_OP($$, &, $1, $3); }
0210 | expr '^' expr { BINARY_LONG_OP($$, ^, $1, $3); }
0211 | expr '<' expr { BINARY_OP($$, <, $1, $3); }
0212 | expr '>' expr { BINARY_OP($$, >, $1, $3); }
0213 | expr '+' expr { BINARY_OP($$, +, $1, $3); }
0214 | expr '-' expr { BINARY_OP($$, -, $1, $3); }
0215 | expr '*' expr { BINARY_OP($$, *, $1, $3); }
0216 | expr '/' expr
0217 {
0218         if (fpclassify($3.val) == FP_ZERO) {
0219                 pr_debug("division by zero\n");
0220                 YYABORT;
0221         } else if (!compute_ids || (is_const($1.val) && is_const($3.val))) {
0222                 assert($1.ids == NULL);
0223                 assert($3.ids == NULL);
0224                 $$.val = $1.val / $3.val;
0225                 $$.ids = NULL;
0226         } else {
0227                 /* LHS and/or RHS need computing from event IDs so union. */
0228                 $$ = union_expr($1, $3);
0229         }
0230 }
0231 | expr '%' expr
0232 {
0233         if (fpclassify($3.val) == FP_ZERO) {
0234                 pr_debug("division by zero\n");
0235                 YYABORT;
0236         } else if (!compute_ids || (is_const($1.val) && is_const($3.val))) {
0237                 assert($1.ids == NULL);
0238                 assert($3.ids == NULL);
0239                 $$.val = (long)$1.val % (long)$3.val;
0240                 $$.ids = NULL;
0241         } else {
0242                 /* LHS and/or RHS need computing from event IDs so union. */
0243                 $$ = union_expr($1, $3);
0244         }
0245 }
0246 | D_RATIO '(' expr ',' expr ')'
0247 {
0248         if (fpclassify($5.val) == FP_ZERO) {
0249                 /*
0250                  * Division by constant zero always yields zero and no events
0251                  * are necessary.
0252                  */
0253                 assert($5.ids == NULL);
0254                 $$.val = 0.0;
0255                 $$.ids = NULL;
0256                 ids__free($3.ids);
0257         } else if (!compute_ids || (is_const($3.val) && is_const($5.val))) {
0258                 assert($3.ids == NULL);
0259                 assert($5.ids == NULL);
0260                 $$.val = $3.val / $5.val;
0261                 $$.ids = NULL;
0262         } else {
0263                 /* LHS and/or RHS need computing from event IDs so union. */
0264                 $$ = union_expr($3, $5);
0265         }
0266 }
0267 | '-' expr %prec NEG
0268 {
0269         $$.val = -$2.val;
0270         $$.ids = $2.ids;
0271 }
0272 | '(' if_expr ')'
0273 {
0274         $$ = $2;
0275 }
0276 | MIN '(' expr ',' expr ')'
0277 {
0278         if (!compute_ids) {
0279                 $$.val = $3.val < $5.val ? $3.val : $5.val;
0280                 $$.ids = NULL;
0281         } else {
0282                 $$ = union_expr($3, $5);
0283         }
0284 }
0285 | MAX '(' expr ',' expr ')'
0286 {
0287         if (!compute_ids) {
0288                 $$.val = $3.val > $5.val ? $3.val : $5.val;
0289                 $$.ids = NULL;
0290         } else {
0291                 $$ = union_expr($3, $5);
0292         }
0293 }
0294 | LITERAL
0295 {
0296         $$.val = $1;
0297         $$.ids = NULL;
0298 }
0299 ;
0300 
0301 %%