You are here

function ctools_math_expr::pfx in Chaos Tool Suite (ctools) 6

Same name and namespace in other branches
  1. 7 includes/math-expr.inc \ctools_math_expr::pfx()
1 call to ctools_math_expr::pfx()
ctools_math_expr::evaluate in includes/math-expr.inc

File

includes/math-expr.inc, line 295

Class

ctools_math_expr

Code

function pfx($tokens, $vars = array()) {
  if ($tokens == false) {
    return false;
  }
  $stack = new ctools_math_expr_stack();
  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();
}