You are here

public function ctools_math_expr::pfx in Chaos Tool Suite (ctools) 7

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

Evaluate a prefix-operator stack expression.

Parameters

array $tokens: The array of token values to evaluate. A token is a string value representing either an operation to perform, a variable, or a value. Literal values are checked using is_numeric(), or a value that starts with a double-quote; functions and variables by existence in the appropriate tables. If FALSE is passed in the function terminates immediately, returning FALSE.

array $vars: Additional variable values to use when evaluating the expression. These variables do not override internal variables with the same name.

Return value

bool|mixed The expression's value, otherwise FALSE is returned if there is an error detected unless php error handling intervenes: see suppress_error.

1 call to ctools_math_expr::pfx()
ctools_math_expr::evaluate in includes/math-expr.inc
Evaluate the expression.

File

includes/math-expr.inc, line 652
=============================================================================.

Class

ctools_math_expr
ctools_math_expr Class.

Code

public function pfx(array $tokens, array $vars = array()) {
  if ($tokens == FALSE) {
    return FALSE;
  }
  $stack = new ctools_math_expr_stack();
  foreach ($tokens as $token) {

    // If the token is a binary operator, pop two values off the stack, do
    // the operation, and push the result back on again.
    if (in_array($token, $this->binaryops)) {
      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;
        case '==':
          $stack
            ->push((int) ($op1 == $op2));
          break;
        case '!=':
          $stack
            ->push((int) ($op1 != $op2));
          break;
        case '<=':
          $stack
            ->push((int) ($op1 <= $op2));
          break;
        case '<':
          $stack
            ->push((int) ($op1 < $op2));
          break;
        case '>=':
          $stack
            ->push((int) ($op1 >= $op2));
          break;
        case '>':
          $stack
            ->push((int) ($op1 > $op2));
          break;
      }
    }
    elseif ($token === "_") {
      $stack
        ->push(-1 * $stack
        ->pop());
    }
    elseif (preg_match("/^([a-z]\\w*)\\(\$/", $token, $matches)) {
      $fnn = $matches[1];

      // Check for a built-in function.
      if (isset($this->funcs[$fnn])) {
        $args = array();

        // Collect all required args from the stack.
        for ($i = 0; $i < $this->funcs[$fnn]['arguments']; $i++) {
          if (is_null($op1 = $stack
            ->pop())) {
            return $this
              ->trigger("function {$fnn} missing argument {$i}");
          }
          $args[] = $op1;
        }

        // If func allows additional args, collect them too, stopping on a
        // NULL arg.
        if (!empty($this->funcs[$fnn]['max arguments'])) {
          for (; $i < $this->funcs[$fnn]['max arguments']; $i++) {
            $arg = $stack
              ->pop();
            if (!isset($arg)) {
              break;
            }
            $args[] = $arg;
          }
        }
        $stack
          ->push(call_user_func_array($this->funcs[$fnn]['function'], array_reverse($args)));
      }
      elseif (isset($fnn, $this->userfuncs)) {
        $args = array();
        for ($i = count($this->userfuncs[$fnn]['args']) - 1; $i >= 0; $i--) {
          $value = $stack
            ->pop();
          $args[$this->userfuncs[$fnn]['args'][$i]] = $value;
          if (is_null($value)) {
            return $this
              ->trigger('internal error');
          }
        }

        // yay... recursion!!!!
        $stack
          ->push($this
          ->pfx($this->userfuncs[$fnn]['func'], $args));
      }
    }
    else {
      if (is_numeric($token) || $token[0] == '"') {
        $stack
          ->push($token);
      }
      elseif (array_key_exists($token, $this->vars)) {
        $stack
          ->push($this->vars[$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();
}