/*- * 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 #include #include #include #include #include #include #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; iname) == 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 * val OP ( * inside every paran you have a: * val OP val * val OP ( * d) A final optional step (not implemented yet) would be * to insert the mathematical 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; itype = 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