summaryrefslogtreecommitdiffstats
path: root/usr.sbin/pmcstudy/eval_expr.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/pmcstudy/eval_expr.c')
-rw-r--r--usr.sbin/pmcstudy/eval_expr.c717
1 files changed, 717 insertions, 0 deletions
diff --git a/usr.sbin/pmcstudy/eval_expr.c b/usr.sbin/pmcstudy/eval_expr.c
new file mode 100644
index 0000000..c225391
--- /dev/null
+++ b/usr.sbin/pmcstudy/eval_expr.c
@@ -0,0 +1,717 @@
+/*-
+ * Copyright (c) 2015 Netflix Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <ctype.h>
+#include "eval_expr.h"
+__FBSDID("$FreeBSD$");
+
+static struct expression *
+alloc_and_hook_expr(struct expression **exp_p, struct expression **last_p)
+{
+ struct expression *ex, *at;
+
+ ex = malloc(sizeof(struct expression));
+ if (ex == NULL) {
+ printf("Out of memory in exp allocation\n");
+ exit(-2);
+ }
+ memset(ex, 0, sizeof(struct expression));
+ if (*exp_p == NULL) {
+ *exp_p = ex;
+ }
+ at = *last_p;
+ if (at == NULL) {
+ /* First one, its last */
+ *last_p = ex;
+ } else {
+ /* Chain it to the end and update last */
+ at->next = ex;
+ ex->prev = at;
+ *last_p = ex;
+ }
+ return (ex);
+}
+
+
+static int
+validate_expr(struct expression *exp, int val1_is_set, int op_is_set, int val2_is_set,
+ int *op_cnt)
+{
+ int val1, op, val2;
+ int open_cnt;
+ val1 = op = val2 = 0;
+ if (val1_is_set) {
+ val1 = 1;
+ }
+ if (op_is_set) {
+ op = 1;
+ }
+ if (val2_is_set) {
+ val2 = 1;
+ }
+ open_cnt = *op_cnt;
+ if (exp == NULL) {
+ /* End of the road */
+ if (val1 && op && val2 && (open_cnt == 0)) {
+ return(0);
+ } else {
+ return(1);
+ }
+ }
+ switch(exp->type) {
+ case TYPE_OP_PLUS:
+ case TYPE_OP_MINUS:
+ case TYPE_OP_MULT:
+ case TYPE_OP_DIVIDE:
+ if (val1 && op && val2) {
+ /* We are at x + y +
+ * collapse back to val/op
+ */
+ val1 = 1;
+ op = 1;
+ val2 = 0;
+ } else if ((op == 0) && (val1)) {
+ op = 1;
+ } else {
+ printf("Op but no val1 set\n");
+ return(-1);
+ }
+ break;
+ case TYPE_PARN_OPEN:
+ if (exp->next == NULL) {
+ printf("NULL after open paren\n");
+ exit(-1);
+ }
+ if ((exp->next->type == TYPE_OP_PLUS) ||
+ (exp->next->type == TYPE_OP_MINUS) ||
+ (exp->next->type == TYPE_OP_DIVIDE) ||
+ (exp->next->type == TYPE_OP_MULT)) {
+ printf("'( OP' -- not allowed\n");
+ return(-1);
+ }
+ if (val1 && (op == 0)) {
+ printf("'Val (' -- not allowed\n");
+ return(-1);
+ }
+ if (val1 && op && val2) {
+ printf("'Val OP Val (' -- not allowed\n");
+ return(-1);
+ }
+ open_cnt++;
+ *op_cnt = open_cnt;
+ if (val1) {
+ if (validate_expr(exp->next, 0, 0, 0, op_cnt) == 0) {
+ val2 = 1;
+ } else {
+ return(-1);
+ }
+ } else {
+ return(validate_expr(exp->next, 0, 0, 0, op_cnt));
+ }
+ break;
+ case TYPE_PARN_CLOSE:
+ open_cnt--;
+ *op_cnt = open_cnt;
+ if (val1 && op && val2) {
+ return(0);
+ } else {
+ printf("Found close paren and not complete\n");
+ return(-1);
+ }
+ break;
+ case TYPE_VALUE_CON:
+ case TYPE_VALUE_PMC:
+ if (val1 == 0) {
+ val1 = 1;
+ } else if (val1 && op) {
+ val2 = 1;
+ } else {
+ printf("val1 set, val2 about to be set op empty\n");
+ return(-1);
+ }
+ break;
+ default:
+ printf("unknown type %d\n", exp->type);
+ exit(-5);
+ break;
+ }
+ return(validate_expr(exp->next, val1, op, val2, op_cnt));
+}
+
+void
+print_exp(struct expression *exp)
+{
+ if (exp == NULL) {
+ printf("\n");
+ return;
+ }
+ switch(exp->type) {
+ case TYPE_OP_PLUS:
+ printf(" + ");
+ break;
+ case TYPE_OP_MINUS:
+ printf(" - ");
+ break;
+ case TYPE_OP_MULT:
+ printf(" * ");
+ break;
+ case TYPE_OP_DIVIDE:
+ printf(" / ");
+ break;
+ case TYPE_PARN_OPEN:
+ printf(" ( ");
+ break;
+ case TYPE_PARN_CLOSE:
+ printf(" ) ");
+ break;
+ case TYPE_VALUE_CON:
+ printf("%f", exp->value);
+ break;
+ case TYPE_VALUE_PMC:
+ printf("%s", exp->name);
+ break;
+ default:
+ printf("Unknown op %d\n", exp->type);
+ break;
+ }
+ print_exp(exp->next);
+}
+
+static void
+walk_back_and_insert_paren(struct expression **beg, struct expression *frm)
+{
+ struct expression *at, *ex;
+
+ /* Setup our new open paren */
+ ex = malloc(sizeof(struct expression));
+ if (ex == NULL) {
+ printf("Out of memory in exp allocation\n");
+ exit(-2);
+ }
+ memset(ex, 0, sizeof(struct expression));
+ ex->type = TYPE_PARN_OPEN;
+ /* Now lets place it */
+ at = frm->prev;
+ if (at == *beg) {
+ /* We are inserting at the head of the list */
+ in_beg:
+ ex->next = at;
+ at->prev = ex;
+ *beg = ex;
+ return;
+ } else if ((at->type == TYPE_VALUE_CON) ||
+ (at->type == TYPE_VALUE_PMC)) {
+ /* Simple case we have a value in the previous position */
+ in_mid:
+ ex->prev = at->prev;
+ ex->prev->next = ex;
+ ex->next = at;
+ at->prev = ex;
+ return;
+ } else if (at->type == TYPE_PARN_CLOSE) {
+ /* Skip through until we reach beg or all ( closes */
+ int par_cnt=1;
+
+ at = at->prev;
+ while(par_cnt) {
+ if (at->type == TYPE_PARN_CLOSE) {
+ par_cnt++;
+ } else if (at->type == TYPE_PARN_OPEN) {
+ par_cnt--;
+ if (par_cnt == 0) {
+ break;
+ }
+ }
+ at = at->prev;
+ }
+ if (at == *beg) {
+ /* At beginning we insert */
+ goto in_beg;
+ } else {
+ goto in_mid;
+ }
+ } else {
+ printf("%s:Unexpected type:%d?\n",
+ __FUNCTION__, at->type);
+ exit(-1);
+ }
+}
+
+static void
+walk_fwd_and_insert_paren(struct expression *frm, struct expression **added)
+{
+ struct expression *at, *ex;
+ /* Setup our new close paren */
+ ex = malloc(sizeof(struct expression));
+ if (ex == NULL) {
+ printf("Out of memory in exp allocation\n");
+ exit(-2);
+ }
+ memset(ex, 0, sizeof(struct expression));
+ ex->type = TYPE_PARN_CLOSE;
+ *added = ex;
+ /* Now lets place it */
+ at = frm->next;
+ if ((at->type == TYPE_VALUE_CON) ||
+ (at->type == TYPE_VALUE_PMC)) {
+ /* Simple case we have a value in the previous position */
+ insertit:
+ ex->next = at->next;
+ ex->prev = at;
+ at->next = ex;
+ return;
+ } else if (at->type == TYPE_PARN_OPEN) {
+ int par_cnt=1;
+ at = at->next;
+ while(par_cnt) {
+ if (at->type == TYPE_PARN_OPEN) {
+ par_cnt++;
+ } else if (at->type == TYPE_PARN_CLOSE) {
+ par_cnt--;
+ if (par_cnt == 0) {
+ break;
+ }
+ }
+ at = at->next;
+ }
+ goto insertit;
+ } else {
+ printf("%s:Unexpected type:%d?\n",
+ __FUNCTION__,
+ at->type);
+ exit(-1);
+ }
+}
+
+
+static void
+add_precendence(struct expression **beg, struct expression *start, struct expression *end)
+{
+ /*
+ * Between start and end add () around any * or /. This
+ * is quite tricky since if there is a () set inside the
+ * list we need to skip over everything in the ()'s considering
+ * that just a value.
+ */
+ struct expression *at, *newone;
+ int open_cnt;
+
+ at = start;
+ open_cnt = 0;
+ while(at != end) {
+ if (at->type == TYPE_PARN_OPEN) {
+ open_cnt++;
+ }
+ if (at->type == TYPE_PARN_CLOSE) {
+ open_cnt--;
+ }
+ if (open_cnt == 0) {
+ if ((at->type == TYPE_OP_MULT) ||
+ (at->type == TYPE_OP_DIVIDE)) {
+ walk_back_and_insert_paren(beg, at);
+ walk_fwd_and_insert_paren(at, &newone);
+ at = newone->next;
+ continue;
+ }
+ }
+ at = at->next;
+ }
+
+}
+
+static void
+set_math_precidence(struct expression **beg, struct expression *exp, struct expression **stopped)
+{
+ struct expression *at, *start, *end;
+ int cnt_lower, cnt_upper;
+ /*
+ * Walk through and set any math precedence to
+ * get proper precedence we insert () around * / over + -
+ */
+ end = NULL;
+ start = at = exp;
+ cnt_lower = cnt_upper = 0;
+ while(at) {
+ if (at->type == TYPE_PARN_CLOSE) {
+ /* Done with that paren */
+ if (stopped) {
+ *stopped = at;
+ }
+ if (cnt_lower && cnt_upper) {
+ /* We have a mixed set ... add precedence between start/end */
+ add_precendence(beg, start, end);
+ }
+ return;
+ }
+ if (at->type == TYPE_PARN_OPEN) {
+ set_math_precidence(beg, at->next, &end);
+ at = end;
+ continue;
+ } else if ((at->type == TYPE_OP_PLUS) ||
+ (at->type == TYPE_OP_MINUS)) {
+ cnt_lower++;
+ } else if ((at->type == TYPE_OP_DIVIDE) ||
+ (at->type == TYPE_OP_MULT)) {
+ cnt_upper++;
+ }
+ at = at->next;
+ }
+ if (cnt_lower && cnt_upper) {
+ add_precendence(beg, start, NULL);
+ }
+}
+
+extern char **valid_pmcs;
+extern int valid_pmc_cnt;
+
+static void
+pmc_name_set(struct expression *at)
+{
+ int i, idx, fnd;
+
+ if (at->name[0] == '%') {
+ /* Special number after $ gives index */
+ idx = strtol(&at->name[1], NULL, 0);
+ if (idx >= valid_pmc_cnt) {
+ printf("Unknown PMC %s -- largest we have is $%d -- can't run your expression\n",
+ at->name, valid_pmc_cnt);
+ exit(-1);
+ }
+ strcpy(at->name, valid_pmcs[idx]);
+ } else {
+ for(i=0, fnd=0; i<valid_pmc_cnt; i++) {
+ if (strcmp(valid_pmcs[i], at->name) == 0) {
+ fnd = 1;
+ break;
+ }
+ }
+ if (!fnd) {
+ printf("PMC %s does not exist on this machine -- can't run your expression\n",
+ at->name);
+ exit(-1);
+ }
+ }
+}
+
+struct expression *
+parse_expression(char *str)
+{
+ struct expression *exp=NULL, *last=NULL, *at;
+ int open_par, close_par;
+ int op_cnt=0;
+ size_t siz, i, x;
+ /*
+ * Walk through a string expression and convert
+ * it to a linked list of actions. We do this by:
+ * a) Counting the open/close paren's, there must
+ * be a matching number.
+ * b) If we have balanced paren's then create a linked list
+ * of the operators, then we validate that expression further.
+ * c) Validating that we have:
+ * val OP val <or>
+ * val OP ( <and>
+ * inside every paran you have a:
+ * val OP val <or>
+ * val OP ( <recursively>
+ * d) A final optional step (not implemented yet) would be
+ * to insert the mathimatical precedence paran's. For
+ * the start we will just do the left to right evaluation and
+ * then later we can add this guy to add paran's to make it
+ * mathimatically correct... i.e instead of 1 + 2 * 3 we
+ * would translate it into 1 + ( 2 * 3).
+ */
+ open_par = close_par = 0;
+ siz = strlen(str);
+ /* No trailing newline please */
+ if (str[(siz-1)] == '\n') {
+ str[(siz-1)] = 0;
+ siz--;
+ }
+ for(i=0; i<siz; i++) {
+ if (str[i] == '(') {
+ open_par++;
+ } else if (str[i] == ')') {
+ close_par++;
+ }
+ }
+ if (open_par != close_par) {
+ printf("Invalid expression '%s' %d open paren's and %d close?\n",
+ str, open_par, close_par);
+ exit(-1);
+ }
+ for(i=0; i<siz; i++) {
+ if (str[i] == '(') {
+ at = alloc_and_hook_expr(&exp, &last);
+ at->type = TYPE_PARN_OPEN;
+ } else if (str[i] == ')') {
+ at = alloc_and_hook_expr(&exp, &last);
+ at->type = TYPE_PARN_CLOSE;
+ } else if (str[i] == ' ') {
+ /* Extra blank */
+ continue;
+ } else if (str[i] == '\t') {
+ /* Extra tab */
+ continue;
+ } else if (str[i] == '+') {
+ at = alloc_and_hook_expr(&exp, &last);
+ at->type = TYPE_OP_PLUS;
+ } else if (str[i] == '-') {
+ at = alloc_and_hook_expr(&exp, &last);
+ at->type = TYPE_OP_MINUS;
+ } else if (str[i] == '/') {
+ at = alloc_and_hook_expr(&exp, &last);
+ at->type = TYPE_OP_DIVIDE;
+ } else if (str[i] == '*') {
+ at = alloc_and_hook_expr(&exp, &last);
+ at->type = TYPE_OP_MULT;
+ } else {
+ /* Its a value or PMC constant */
+ at = alloc_and_hook_expr(&exp, &last);
+ if (isdigit(str[i]) || (str[i] == '.')) {
+ at->type = TYPE_VALUE_CON;
+ } else {
+ at->type = TYPE_VALUE_PMC;
+ }
+ x = 0;
+ while ((str[i] != ' ') &&
+ (str[i] != '\t') &&
+ (str[i] != 0) &&
+ (str[i] != ')') &&
+ (str[i] != '(')) {
+ /* We collect the constant until a space or tab */
+ at->name[x] = str[i];
+ i++;
+ x++;
+ if (x >=(sizeof(at->name)-1)) {
+ printf("Value/Constant too long %d max:%d\n",
+ (int)x, (int)(sizeof(at->name)-1));
+ exit(-3);
+ }
+ }
+ if (str[i] != 0) {
+ /* Need to back up and see the last char since
+ * the for will increment the loop.
+ */
+ i--;
+ }
+ /* Now we have pulled the string, set it up */
+ if (at->type == TYPE_VALUE_CON) {
+ at->state = STATE_FILLED;
+ at->value = strtod(at->name, NULL);
+ } else {
+ pmc_name_set(at);
+ }
+ }
+ }
+ /* Now lets validate its a workable expression */
+ if (validate_expr(exp, 0, 0, 0, &op_cnt)) {
+ printf("Invalid expression\n");
+ exit(-4);
+ }
+ set_math_precidence(&exp, exp, NULL);
+ return (exp);
+}
+
+
+
+static struct expression *
+gather_exp_to_paren_close(struct expression *exp, double *val_fill)
+{
+ /*
+ * I have been given ( ???
+ * so I could see either
+ * (
+ * or
+ * Val Op
+ *
+ */
+ struct expression *lastproc;
+ double val;
+
+ if (exp->type == TYPE_PARN_OPEN) {
+ lastproc = gather_exp_to_paren_close(exp->next, &val);
+ *val_fill = val;
+ } else {
+ *val_fill = run_expr(exp, 0, &lastproc);
+ }
+ return(lastproc);
+}
+
+
+double
+run_expr(struct expression *exp, int initial_call, struct expression **lastone)
+{
+ /*
+ * We expect to find either
+ * a) A Open Paren
+ * or
+ * b) Val-> Op -> Val
+ * or
+ * c) Val-> Op -> Open Paren
+ */
+ double val1, val2, res;
+ struct expression *op, *other_half, *rest;
+
+ if (exp->type == TYPE_PARN_OPEN) {
+ op = gather_exp_to_paren_close(exp->next, &val1);
+ } else if(exp->type == TYPE_VALUE_CON) {
+ val1 = exp->value;
+ op = exp->next;
+ } else if (exp->type == TYPE_VALUE_PMC) {
+ val1 = exp->value;
+ op = exp->next;
+ } else {
+ printf("Illegal value in %s huh?\n", __FUNCTION__);
+ exit(-1);
+ }
+ if (op == NULL) {
+ return (val1);
+ }
+more_to_do:
+ other_half = op->next;
+ if (other_half->type == TYPE_PARN_OPEN) {
+ rest = gather_exp_to_paren_close(other_half->next, &val2);
+ } else if(other_half->type == TYPE_VALUE_CON) {
+ val2 = other_half->value;
+ rest = other_half->next;
+ } else if (other_half->type == TYPE_VALUE_PMC) {
+ val2 = other_half->value;
+ rest = other_half->next;
+ } else {
+ printf("Illegal2 value in %s huh?\n", __FUNCTION__);
+ exit(-1);
+ }
+ switch(op->type) {
+ case TYPE_OP_PLUS:
+ res = val1 + val2;
+ break;
+ case TYPE_OP_MINUS:
+ res = val1 - val2;
+ break;
+ case TYPE_OP_MULT:
+ res = val1 * val2;
+ break;
+ case TYPE_OP_DIVIDE:
+ if (val2 != 0.0)
+ res = val1 / val2;
+ else {
+ printf("Division by zero averted\n");
+ res = 1.0;
+ }
+ break;
+ default:
+ printf("Op is not an operator -- its %d\n",
+ op->type);
+ exit(-1);
+ break;
+ }
+ if (rest == NULL) {
+ if (lastone) {
+ *lastone = NULL;
+ }
+ return (res);
+ }
+ if ((rest->type == TYPE_PARN_CLOSE) && (initial_call == 0)) {
+ if (lastone) {
+ *lastone = rest->next;
+ }
+ return(res);
+ }
+ /* There is more, as in
+ * a + b + c
+ * where we just did a + b
+ * so now it becomes val1 is set to res and
+ * we need to proceed with the rest of it.
+ */
+ val1 = res;
+ op = rest;
+ if ((op->type != TYPE_OP_PLUS) &&
+ (op->type != TYPE_OP_MULT) &&
+ (op->type != TYPE_OP_MINUS) &&
+ (op->type != TYPE_OP_DIVIDE)) {
+ printf("%s ending on type:%d not an op??\n", __FUNCTION__, op->type);
+ return(res);
+ }
+ if (op)
+ goto more_to_do;
+ return (res);
+}
+
+#ifdef STAND_ALONE_TESTING
+
+static double
+calc_expr(struct expression *exp)
+{
+ struct expression *at;
+ double xx;
+
+ /* First clear PMC's setting */
+ for(at = exp; at != NULL; at = at->next) {
+ if (at->type == TYPE_VALUE_PMC) {
+ at->state = STATE_UNSET;
+ }
+ }
+ /* Now for all pmc's make up values .. here is where I would pull them */
+ for(at = exp; at != NULL; at = at->next) {
+ if (at->type == TYPE_VALUE_PMC) {
+ at->value = (random() * 1.0);
+ at->state = STATE_FILLED;
+ if (at->value == 0.0) {
+ /* So we don't have div by 0 */
+ at->value = 1.0;
+ }
+ }
+ }
+ /* Now lets calculate the expression */
+ print_exp(exp);
+ xx = run_expr(exp, 1, NULL);
+ printf("Answer is %f\n", xx);
+ return(xx);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ struct expression *exp;
+ if (argc < 2) {
+ printf("Use %s expression\n", argv[0]);
+ return(-1);
+ }
+ exp = parse_expression(argv[1]);
+ printf("Now the calc\n");
+ calc_expr(exp);
+ return(0);
+}
+
+#endif
OpenPOWER on IntegriCloud