Esempio n. 1
0
 function pfx($tokens, $vars = array())
 {
     if ($tokens == false) {
         return false;
     }
     $stack = new EvalMathStack();
     foreach ($tokens as $token) {
         // nice and easy
         // if the token is a binary operator, pop two values off the stack, do the operation, and push the result back on
         if (in_array($token, array('+', '-', '*', '/', '^'))) {
             if (is_null($op2 = $stack->pop())) {
                 return $this->trigger("internal error");
             }
             if (is_null($op1 = $stack->pop())) {
                 return $this->trigger("internal error");
             }
             switch ($token) {
                 case '+':
                     $stack->push($op1 + $op2);
                     break;
                 case '-':
                     $stack->push($op1 - $op2);
                     break;
                 case '*':
                     $stack->push($op1 * $op2);
                     break;
                 case '/':
                     if ($op2 == 0) {
                         return $this->trigger("division by zero");
                     }
                     $stack->push($op1 / $op2);
                     break;
                 case '^':
                     $stack->push(pow($op1, $op2));
                     break;
             }
             // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
         } elseif ($token == "_") {
             $stack->push(-1 * $stack->pop());
             // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
         } elseif (preg_match("/^([a-z]\\w*)\\(\$/", $token, $matches)) {
             // it's a function!
             $fnn = $matches[1];
             if (in_array($fnn, $this->fb)) {
                 // built-in function:
                 if (is_null($op1 = $stack->pop())) {
                     return $this->trigger("internal error");
                 }
                 $fnn = preg_replace("/^arc/", "a", $fnn);
                 // for the 'arc' trig synonyms
                 if ($fnn == 'ln') {
                     $fnn = 'log';
                 }
                 eval('$stack->push(' . $fnn . '($op1));');
                 // perfectly safe eval()
             } elseif (array_key_exists($fnn, $this->f)) {
                 // user function
                 // get args
                 $args = array();
                 for ($i = count($this->f[$fnn]['args']) - 1; $i >= 0; $i--) {
                     if (is_null($args[$this->f[$fnn]['args'][$i]] = $stack->pop())) {
                         return $this->trigger("internal error");
                     }
                 }
                 $stack->push($this->pfx($this->f[$fnn]['func'], $args));
                 // yay... recursion!!!!
             }
             // if the token is a number or variable, push it on the stack
         } else {
             if (is_numeric($token)) {
                 $stack->push($token);
             } elseif (array_key_exists($token, $this->v)) {
                 $stack->push($this->v[$token]);
             } elseif (array_key_exists($token, $vars)) {
                 $stack->push($vars[$token]);
             } else {
                 return $this->trigger("undefined variable '{$token}'");
             }
         }
     }
     // when we're out of tokens, the stack should have a single element, the final result
     if ($stack->count != 1) {
         return $this->trigger("internal error");
     }
     return $stack->pop();
 }
 function pfx($tokens, $vars = array())
 {
     if ($tokens == false) {
         return false;
     }
     $stack = new EvalMathStack();
     foreach ($tokens as $token) {
         // nice and easy
         // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
         if (is_array($token)) {
             // it's a function!
             $fnn = $token['fnn'];
             $count = $token['argcount'];
             if (in_array($fnn, $this->fb)) {
                 // built-in function:
                 if (is_null($op1 = $stack->pop())) {
                     return $this->trigger("internal error");
                 }
                 $fnn = preg_replace("/^arc/", "a", $fnn);
                 // for the 'arc' trig synonyms
                 if ($fnn == 'ln') {
                     $fnn = 'log';
                 }
                 eval('$stack->push(' . $fnn . '($op1));');
                 // perfectly safe eval()
             } elseif (array_key_exists($fnn, $this->fc)) {
                 // calc emulation function
                 // get args
                 $args = array();
                 for ($i = $count - 1; $i >= 0; $i--) {
                     if (is_null($args[] = $stack->pop())) {
                         return $this->trigger("internal error");
                     }
                 }
                 $res = call_user_func(array('EvalMathCalcEmul', $fnn), $args);
                 if ($res === FALSE) {
                     return $this->trigger("internal error");
                 }
                 $stack->push($res);
             } elseif (array_key_exists($fnn, $this->f)) {
                 // user function
                 // get args
                 $args = array();
                 for ($i = count($this->f[$fnn]['args']) - 1; $i >= 0; $i--) {
                     if (is_null($args[$this->f[$fnn]['args'][$i]] = $stack->pop())) {
                         return $this->trigger("internal error");
                     }
                 }
                 $stack->push($this->pfx($this->f[$fnn]['func'], $args));
                 // yay... recursion!!!!
             }
             // if the token is a binary operator, pop two values off the stack, do the operation, and push the result back on
         } elseif (in_array($token, array('+', '-', '*', '/', '^'), true)) {
             if (is_null($op2 = $stack->pop())) {
                 return $this->trigger("internal error");
             }
             if (is_null($op1 = $stack->pop())) {
                 return $this->trigger("internal error");
             }
             switch ($token) {
                 case '+':
                     $this->index += 1;
                     $stack->push($this->index);
                     $this->data[$this->index] = array_merge($this->data[$op1], $this->data[$op2]);
                     break;
                 case '-':
                     $this->index += 1;
                     $stack->push($this->index);
                     $this->data[$this->index] = array_diff($this->data[$op1], $this->data[$op2]);
                     break;
                 case '*':
                     $this->index += 1;
                     $stack->push($this->index);
                     $this->data[$this->index] = array_intersect($this->data[$op1], $this->data[$op2]);
                     break;
             }
             // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
         } elseif ($token == "_") {
             $stack->push(-1 * $stack->pop());
             // if the token is a number or variable, push it on the stack
         } else {
             if (is_numeric($token)) {
                 $stack->push($token);
             } elseif (array_key_exists($token, $this->v)) {
                 $stack->push($this->v[$token]);
             } elseif (array_key_exists($token, $vars)) {
                 $stack->push($vars[$token]);
             } else {
                 return $this->trigger("undefined variable '{$token}'");
             }
         }
     }
     // when we're out of tokens, the stack should have a single element, the final result
     if ($stack->count != 1) {
         return $this->trigger("internal error");
     }
     $last = $stack->pop();
     if (isset($this->data[$last])) {
         return $this->data[$last];
     } else {
         return false;
     }
 }
Esempio n. 3
0
 function pfx($tokens, $vars = array())
 {
     if ($tokens == false) {
         return false;
     }
     $stack = new EvalMathStack();
     foreach ($tokens as $token) {
         // nice and easy
         // if the token is a function, pop arguments off the stack, hand them to the function, and push the result back on
         if (is_array($token)) {
             // it's a function!
             $fnn = $token['fnn'];
             $count = $token['argcount'];
             if (in_array($fnn, $this->fb)) {
                 // built-in function:
                 if (is_null($op1 = $stack->pop())) {
                     return $this->trigger($this->get_string('internalerror', 'mathslib'));
                 }
                 $fnn = preg_replace("/^arc/", "a", $fnn);
                 // for the 'arc' trig synonyms
                 if ($fnn == 'ln') {
                     $fnn = 'log';
                 }
                 eval('$stack->push(' . $fnn . '($op1));');
                 // perfectly safe eval()
             } elseif (array_key_exists($fnn, $this->fc)) {
                 // calc emulation function
                 // get args
                 $args = array();
                 for ($i = $count - 1; $i >= 0; $i--) {
                     if (is_null($args[] = $stack->pop())) {
                         return $this->trigger($this->get_string('internalerror', 'mathslib'));
                     }
                 }
                 $res = call_user_func_array(array('EvalMathFuncs', $fnn), array_reverse($args));
                 if ($res === FALSE) {
                     return $this->trigger($this->get_string('internalerror', 'mathslib'));
                 }
                 $stack->push($res);
             } elseif (array_key_exists($fnn, $this->f)) {
                 // user function
                 // get args
                 $args = array();
                 for ($i = count($this->f[$fnn]['args']) - 1; $i >= 0; $i--) {
                     if (is_null($args[$this->f[$fnn]['args'][$i]] = $stack->pop())) {
                         return $this->trigger($this->get_string('internalerror', 'mathslib'));
                     }
                 }
                 $stack->push($this->pfx($this->f[$fnn]['func'], $args));
                 // yay... recursion!!!!
             }
             // if the token is a binary operator, pop two values off the stack, do the operation, and push the result back on
         } elseif (in_array($token, array('+', '-', '*', '/', '^'), true)) {
             if (is_null($op2 = $stack->pop())) {
                 return $this->trigger($this->get_string('internalerror', 'mathslib'));
             }
             if (is_null($op1 = $stack->pop())) {
                 return $this->trigger($this->get_string('internalerror', 'mathslib'));
             }
             switch ($token) {
                 case '+':
                     $stack->push($op1 + $op2);
                     break;
                 case '-':
                     $stack->push($op1 - $op2);
                     break;
                 case '*':
                     $stack->push($op1 * $op2);
                     break;
                 case '/':
                     if ($op2 == 0) {
                         return $this->trigger($this->get_string('divisionbyzero', 'mathslib'));
                     }
                     $stack->push($op1 / $op2);
                     break;
                 case '^':
                     $stack->push(pow($op1, $op2));
                     break;
             }
             // if the token is a unary operator, pop one value off the stack, do the operation, and push it back on
         } elseif ($token == "_") {
             $stack->push(-1 * $stack->pop());
             // if the token is a number or variable, push it on the stack
         } else {
             if (is_numeric($token)) {
                 $stack->push($token);
             } elseif (array_key_exists($token, $this->v)) {
                 $stack->push($this->v[$token]);
             } elseif (array_key_exists($token, $vars)) {
                 $stack->push($vars[$token]);
             } else {
                 // If variable is undefined, create it with value 0
                 $this->v[$token] = 0;
                 $stack->push($this->v[$token]);
                 //return $this->trigger($this->get_string('undefinedvariable', 'mathslib', $token));
             }
         }
     }
     // when we're out of tokens, the stack should have a single element, the final result
     if ($stack->count != 1) {
         return $this->trigger($this->get_string('internalerror', 'mathslib'));
     }
     return $stack->pop();
 }
Esempio n. 4
0
 function pfx($tokens, $vars = array())
 {
     if ($tokens == false) {
         return false;
     }
     $stack = new EvalMathStack();
     foreach ($tokens as $token) {
         if (in_array($token, array('+', '-', '*', '/', '^'))) {
             if (is_null($op2 = $stack->pop())) {
                 return $this->trigger("internal error");
             }
             if (is_null($op1 = $stack->pop())) {
                 return $this->trigger("internal error");
             }
             switch ($token) {
                 case '+':
                     $stack->push($op1 + $op2);
                     break;
                 case '-':
                     $stack->push($op1 - $op2);
                     break;
                 case '*':
                     $stack->push($op1 * $op2);
                     break;
                 case '/':
                     if ($op2 == 0) {
                         return $this->trigger("division by zero");
                     }
                     $stack->push($op1 / $op2);
                     break;
                 case '^':
                     $stack->push(pow($op1, $op2));
                     break;
             }
         } elseif ($token == "_") {
             $stack->push(-1 * $stack->pop());
         } elseif (preg_match("/^([a-z]\\w*)\\(\$/", $token, $matches)) {
             $fnn = $matches[1];
             if (in_array($fnn, $this->fb)) {
                 if (is_null($op1 = $stack->pop())) {
                     return $this->trigger("internal error");
                 }
                 $fnn = preg_replace("/^arc/", "a", $fnn);
                 if ($fnn == 'ln') {
                     $fnn = 'log';
                 }
                 eval('$stack->push(' . $fnn . '($op1));');
             } elseif (array_key_exists($fnn, $this->f)) {
                 $args = array();
                 for ($i = count($this->f[$fnn]['args']) - 1; $i >= 0; $i--) {
                     if (is_null($args[$this->f[$fnn]['args'][$i]] = $stack->pop())) {
                         return $this->trigger("internal error");
                     }
                 }
                 $stack->push($this->pfx($this->f[$fnn]['func'], $args));
             }
         } else {
             if (is_numeric($token)) {
                 $stack->push($token);
             } elseif (array_key_exists($token, $this->v)) {
                 $stack->push($this->v[$token]);
             } elseif (array_key_exists($token, $vars)) {
                 $stack->push($vars[$token]);
             } else {
                 return $this->trigger("undefined variable '{$token}'");
             }
         }
     }
     if ($stack->count != 1) {
         return $this->trigger("internal error");
     }
     return $stack->pop();
 }
Esempio n. 5
0
function nfx($expr)
{
    $index = 0;
    $stack = new EvalMathStack();
    $output = array();
    // postfix form of expression, to be passed to 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;
    // we use this in syntax-checking the expression
    // and determining when a - is a negation
    if (preg_match("/[^\\w\\s+*^\\/()\\.,-]/", $expr, $matches)) {
        // make sure the characters are all good
        return $this->trigger("illegal character '{$matches[0]}'");
    }
    while (1) {
        // 1 Infinite Loop ;)
        $op = substr($expr, $index, 1);
        // get the first character at the current index
        // find out if we're currently at the beginning of a number/variable/function/parenthesis/operand
        $ex = preg_match('/^([a-z]\\w*\\(?|\\d+(?:\\.\\d*)?|\\.\\d+|\\()/', substr($expr, $index), $match);
        //===============
        if ($op == '-' and !$expecting_op) {
            // is it a negation instead of a minus?
            $stack->push('_');
            // put a negation on the stack
            $index++;
        } elseif ($op == '_') {
            // we have to explicitly deny this, because it's legal on the stack
            return $this->trigger("illegal character '_'");
            // but not in the input expression
            //===============
        } elseif ((in_array($op, $ops) or $ex) and $expecting_op) {
            // are we putting an operator on the stack?
            if ($ex) {
                // are we expecting an operator but have a number/variable/function/opening parethesis?
                $op = '*';
                $index--;
                // it's an implicit multiplication
            }
            // heart of the algorithm:
            while ($stack->count > 0 and $o2 = $stack->last() and in_array($o2, $ops) and $ops_r[$op] ? $ops_p[$op] < $ops_p[$o2] : $ops_p[$op] <= $ops_p[$o2]) {
                $output[] = $stack->pop();
                // pop stuff off the stack into the output
            }
            // many thanks: http://en.wikipedia.org/wiki/Reverse_Polish_notation#The_algorithm_in_detail
            $stack->push($op);
            // finally put OUR operator onto the stack
            $index++;
            $expecting_op = false;
            //===============
        } elseif ($op == ')' and $expecting_op) {
            // ready to close a parenthesis?
            while (($o2 = $stack->pop()) != '(') {
                // pop off the stack back to the last (
                if (is_null($o2)) {
                    return $this->trigger("unexpected ')'");
                } else {
                    $output[] = $o2;
                }
            }
            if (preg_match("/^([a-z]\\w*)\\(\$/", $stack->last(2), $matches)) {
                // did we just close a function?
                $fnn = $matches[1];
                // get the function name
                $arg_count = $stack->pop();
                // see how many arguments there were (cleverly stored on the stack, thank you)
                $output[] = $stack->pop();
                // pop the function and push onto the output
                if (in_array($fnn, $this->fb)) {
                    // check the 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 we somehow push a non-function on the stack? this should never happen
                    return $this->trigger("internal error");
                }
            }
            $index++;
            //===============
        } elseif ($op == ',' and $expecting_op) {
            // did we just finish a function argument?
            while (($o2 = $stack->pop()) != '(') {
                if (is_null($o2)) {
                    return $this->trigger("unexpected ','");
                } else {
                    $output[] = $o2;
                }
                // pop the argument expression stuff and push onto the output
            }
            // make sure there was a function
            if (!preg_match("/^([a-z]\\w*)\\(\$/", $stack->last(2), $matches)) {
                return $this->trigger("unexpected ','");
            }
            $stack->push($stack->pop() + 1);
            // increment the argument count
            $stack->push('(');
            // put the ( back on, we'll need to pop back to it again
            $index++;
            $expecting_op = false;
            //===============
        } elseif ($op == '(' and !$expecting_op) {
            $stack->push('(');
            // that was easy
            $index++;
            $allow_neg = true;
            //===============
        } elseif ($ex and !$expecting_op) {
            // do we now have a function/variable/number?
            $expecting_op = true;
            $val = $match[1];
            if (preg_match("/^([a-z]\\w*)\\(\$/", $val, $matches)) {
                // may be 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 a func
                    $stack->push($val);
                    $stack->push(1);
                    $stack->push('(');
                    $expecting_op = false;
                } else {
                    // it's a var w/ implicit multiplication
                    $val = $matches[1];
                    $output[] = $val;
                }
            } else {
                // it's a plain old var or num
                $output[] = $val;
            }
            $index += strlen($val);
            //===============
        } elseif ($op == ')') {
            // miscellaneous error checking
            return $this->trigger("unexpected ')'");
        } elseif (in_array($op, $ops) and !$expecting_op) {
            return $this->trigger("unexpected operator '{$op}'");
        } else {
            // I don't even want to know what you did to get here
            return $this->trigger("an unexpected error occured");
        }
        if ($index == strlen($expr)) {
            if (in_array($op, $ops)) {
                // did we end with an operator? bad.
                return $this->trigger("operator '{$op}' lacks operand");
            } else {
                break;
            }
        }
        while (substr($expr, $index, 1) == ' ') {
            // step the index past whitespace (pretty much turns whitespace
            $index++;
            // into implicit multiplication if no operator is there)
        }
    }
    while (!is_null($op = $stack->pop())) {
        // pop everything off the stack and push onto output
        if ($op == '(') {
            return $this->trigger("expecting ')'");
        }
        // if there are (s on the stack, ()s were unbalanced
        $output[] = $op;
    }
    return $output;
}
Esempio n. 6
0
 /**
  * Evaluate postfix notation
  *
  * @param array $tokens
  *   The list of tokens that make up the expression.
  * @param array $vars
  *   The list of variables set previously.
  *
  * @return number
  *   The final result of the evaluation.
  */
 private function postfixEvaluate($tokens, $vars = array())
 {
     if ($tokens == FALSE) {
         return FALSE;
     }
     $stack = new EvalMathStack();
     foreach ($tokens as $token) {
         if (in_array($token, array('+', '-', '*', '/', '^'))) {
             if (is_NULL($op2 = $stack->pop())) {
                 return $this->trigger(t('internal error'));
             }
             if (is_NULL($op1 = $stack->pop())) {
                 return $this->trigger(t('internal error'));
             }
             switch ($token) {
                 case '+':
                     $stack->push($op1 + $op2);
                     break;
                 case '-':
                     $stack->push($op1 - $op2);
                     break;
                 case '*':
                     $stack->push($op1 * $op2);
                     break;
                 case '/':
                     if ($op2 == 0) {
                         return $this->trigger(t('division by zero'));
                     }
                     $stack->push($op1 / $op2);
                     break;
                 case '^':
                     $stack->push(pow($op1, $op2));
                     break;
             }
         } elseif ($token == "_") {
             $stack->push(-1 * $stack->pop());
         } elseif (preg_match("/^([a-z]\\w*)\\(\$/", $token, $matches)) {
             $fnn = $matches[1];
             if (array_key_exists($fnn, $this->funcAliases)) {
                 $fnn = $this->funcAliases[$fnn];
             }
             if (array_key_exists($fnn, $this->funcBuildIn)) {
                 $argCount = $this->funcBuildIn[$fnn];
                 $args = array();
                 for ($i = $argCount; $i > 0; $i--) {
                     $arg = $stack->pop();
                     if (is_NULL($arg)) {
                         return $this->trigger(t('internal error: argument is null'));
                     }
                     $args[] = $arg;
                     $args = array_reverse($args);
                 }
                 $stack->push(call_user_func_array($fnn, $args));
             } elseif (array_key_exists($fnn, $this->funcUser)) {
                 $args = array();
                 for ($i = count($this->funcUser[$fnn]['args']) - 1; $i >= 0; $i--) {
                     if (is_NULL($args[$this->funcUser[$fnn]['args'][$i]] = $stack->pop())) {
                         return $this->trigger(t('internal error: argument is null'));
                     }
                 }
                 $stack->push($this->postfixEvaluate($this->funcUser[$fnn]['func'], $args));
             }
         } else {
             if (is_numeric($token)) {
                 $stack->push($token);
             } elseif (array_key_exists($token, $this->variables)) {
                 $stack->push($this->variables[$token]);
             } elseif (array_key_exists($token, $vars)) {
                 $stack->push($vars[$token]);
             } else {
                 return $this->trigger(t('undefined variable "%token"', array('%token' => $token)));
             }
         }
     }
     if ($stack->getCount() != 1) {
         return $this->trigger(t('internal error, stack not empty'));
     }
     return $stack->pop();
 }