php - Variables infix to prefix to postfix -
i searched on internet implementation in converting not numbers expressions variable expressions infix notation prefix , postfix. searches did weren't successful. want see if there implementation yet in php modify support more operators not (-,*,+,=).
for example convert:
a+b/c*(p/c)
while keeping variable names, , not having enter numbers evaluate them.
i have found implementation in evalmath class provided miles kaufmann here : http://www.phpclasses.org/package/2695-php-safely-evaluate-mathematical-expressions.html
infix postfix code :
// convert infix postfix notation function nfx($expr) { $index = 0; $stack = new evalmathstack; $output = array(); // postfix form of expression, passed pfx() $expr = trim(strtolower($expr)); $ops = array('+', '-', '*', '/', '^', '_'); $ops_r = array('+'=>0,'-'=>0,'*'=>0,'/'=>0,'^'=>1); // right-associative operator? $ops_p = array('+'=>0,'-'=>0,'*'=>1,'/'=>1,'_'=>1,'^'=>2); // operator precedence $expecting_op = false; // use in syntax-checking expression // , determining when - negation if (preg_match("/[^\w\s+*^\/()\.,-]/", $expr, $matches)) { // make sure characters return $this->trigger("illegal character '{$matches[0]}'"); } while(1) { // 1 infinite loop ;) $op = substr($expr, $index, 1); // first character @ current index // find out if we're @ beginning of number/variable/function/parenthesis/operand $ex = preg_match('/^([a-z]\w*\(?|\d+(?:\.\d*)?|\.\d+|\()/', substr($expr, $index), $match); //=============== if ($op == '-' , !$expecting_op) { // negation instead of minus? $stack->push('_'); // put negation on stack $index++; } elseif ($op == '_') { // have explicitly deny this, because it's legal on stack return $this->trigger("illegal character '_'"); // not in input expression //=============== } elseif ((in_array($op, $ops) or $ex) , $expecting_op) { // putting operator on stack? if ($ex) { // expecting operator have number/variable/function/opening parethesis? $op = '*'; $index--; // it's implicit multiplication } // heart of algorithm: while($stack->count > 0 , ($o2 = $stack->last()) , in_array($o2, $ops) , ($ops_r[$op] ? $ops_p[$op] < $ops_p[$o2] : $ops_p[$op] <= $ops_p[$o2])) { $output[] = $stack->pop(); // pop stuff off stack output } // many thanks: http://en.wikipedia.org/wiki/reverse_polish_notation#the_algorithm_in_detail $stack->push($op); // put our operator onto stack $index++; $expecting_op = false; //=============== } elseif ($op == ')' , $expecting_op) { // ready close parenthesis? while (($o2 = $stack->pop()) != '(') { // pop off stack last ( if (is_null($o2)) return $this->trigger("unexpected ')'"); else $output[] = $o2; } if (preg_match("/^([a-z]\w*)\($/", $stack->last(2), $matches)) { // did close function? $fnn = $matches[1]; // function name $arg_count = $stack->pop(); // see how many arguments there (cleverly stored on stack, thank you) $output[] = $stack->pop(); // pop function , push onto output if (in_array($fnn, $this->fb)) { // check argument count if($arg_count > 1) return $this->trigger("too many arguments ($arg_count given, 1 expected)"); } elseif (array_key_exists($fnn, $this->f)) { if ($arg_count != count($this->f[$fnn]['args'])) return $this->trigger("wrong number of arguments ($arg_count given, " . count($this->f[$fnn]['args']) . " expected)"); } else { // did somehow push non-function on stack? should never happen return $this->trigger("internal error"); } } $index++; //=============== } elseif ($op == ',' , $expecting_op) { // did finish function argument? while (($o2 = $stack->pop()) != '(') { if (is_null($o2)) return $this->trigger("unexpected ','"); // oops, never had ( else $output[] = $o2; // pop argument expression stuff , push onto output } // make sure there function if (!preg_match("/^([a-z]\w*)\($/", $stack->last(2), $matches)) return $this->trigger("unexpected ','"); $stack->push($stack->pop()+1); // increment argument count $stack->push('('); // put ( on, we'll need pop again $index++; $expecting_op = false; //=============== } elseif ($op == '(' , !$expecting_op) { $stack->push('('); // easy $index++; $allow_neg = true; //=============== } elseif ($ex , !$expecting_op) { // have function/variable/number? $expecting_op = true; $val = $match[1]; if (preg_match("/^([a-z]\w*)\($/", $val, $matches)) { // may func, or variable w/ implicit multiplication against parentheses... if (in_array($matches[1], $this->fb) or array_key_exists($matches[1], $this->f)) { // it's func $stack->push($val); $stack->push(1); $stack->push('('); $expecting_op = false; } else { // it's var w/ implicit multiplication $val = $matches[1]; $output[] = $val; } } else { // it's plain old var or num $output[] = $val; } $index += strlen($val); //=============== } elseif ($op == ')') { // miscellaneous error checking return $this->trigger("unexpected ')'"); } elseif (in_array($op, $ops) , !$expecting_op) { return $this->trigger("unexpected operator '$op'"); } else { // don't want know did here return $this->trigger("an unexpected error occured"); } if ($index == strlen($expr)) { if (in_array($op, $ops)) { // did end operator? bad. return $this->trigger("operator '$op' lacks operand"); } else { break; } } while (substr($expr, $index, 1) == ' ') { // step index past whitespace (pretty turns whitespace $index++; // implicit multiplication if no operator there) } } while (!is_null($op = $stack->pop())) { // pop off stack , push onto output if ($op == '(') return $this->trigger("expecting ')'"); // if there (s on stack, ()s unbalanced $output[] = $op; } return $output; }
Comments
Post a Comment