Beispiel #1
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?
             $expectingOperand = false;
             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;
 }
 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, '>' => 0, '<' => 0, '=' => 0);
     // right-associative operator?
     $ops_p = array('+' => 0, '-' => 0, '*' => 1, '/' => 1, '_' => 1, '^' => 2, '>' => 0, '<' => 0, '=' => 0);
     // 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(MoodleTranslations::get_string('illegalcharactergeneral', 'mathslib', $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('/^(' . self::$namepat . '\\(?|\\d+(?:\\.\\d*)?(?:(e[+-]?)\\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(MoodleTranslations::get_string('illegalcharacterunderscore', 'mathslib'));
             // 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?
                 if (!$this->allowimplicitmultiplication) {
                     return $this->trigger(MoodleTranslations::get_string('implicitmultiplicationnotallowed', 'mathslib'));
                 } else {
                     // it's an implicit multiplication
                     $op = '*';
                     $index--;
                 }
             }
             // 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(MoodleTranslations::get_string('unexpectedclosingbracket', 'mathslib'));
                 } else {
                     $output[] = $o2;
                 }
             }
             if (preg_match('/^(' . self::$namepat . ')\\($/', $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)
                 $fn = $stack->pop();
                 $output[] = array('fn' => $fn, 'fnn' => $fnn, 'argcount' => $arg_count);
                 // send function to output
                 if (in_array($fnn, $this->fb)) {
                     // check the argument count
                     if ($arg_count > 1) {
                         $a = new stdClass();
                         $a->expected = 1;
                         $a->given = $arg_count;
                         return $this->trigger(MoodleTranslations::get_string('wrongnumberofarguments', 'mathslib', $a));
                     }
                 } elseif (array_key_exists($fnn, $this->fc)) {
                     $counts = $this->fc[$fnn];
                     if (in_array(-1, $counts) and $arg_count > 0) {
                     } elseif (!in_array($arg_count, $counts)) {
                         $a = new stdClass();
                         $a->expected = implode('/', $this->fc[$fnn]);
                         $a->given = $arg_count;
                         return $this->trigger(MoodleTranslations::get_string('wrongnumberofarguments', 'mathslib', $a));
                     }
                 } elseif (array_key_exists($fnn, $this->f)) {
                     if ($arg_count != count($this->f[$fnn]['args'])) {
                         $a = new stdClass();
                         $a->expected = count($this->f[$fnn]['args']);
                         $a->given = $arg_count;
                         return $this->trigger(MoodleTranslations::get_string('wrongnumberofarguments', 'mathslib', $a));
                     }
                 } else {
                     // did we somehow push a non-function on the stack? this should never happen
                     return $this->trigger(MoodleTranslations::get_string('internalerror', 'mathslib'));
                 }
             }
             $index++;
             //===============
         } elseif ($op == ',' and $expecting_op) {
             // did we just finish a function argument?
             while (($o2 = $stack->pop()) != '(') {
                 if (is_null($o2)) {
                     return $this->trigger(MoodleTranslations::get_string('unexpectedcomma', 'mathslib'));
                 } else {
                     $output[] = $o2;
                 }
                 // pop the argument expression stuff and push onto the output
             }
             // make sure there was a function
             if (!preg_match('/^(' . self::$namepat . ')\\($/', $stack->last(2), $matches)) {
                 return $this->trigger(MoodleTranslations::get_string('unexpectedcomma', 'mathslib'));
             }
             $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('/^(' . self::$namepat . ')\\($/', $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) or array_key_exists($matches[1], $this->fc)) {
                     // 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 == ')') {
             //it could be only custom function with no params or general error
             if ($stack->last() != '(' or $stack->last(2) != 1) {
                 return $this->trigger(MoodleTranslations::get_string('unexpectedclosingbracket', 'mathslib'));
             }
             if (preg_match('/^(' . self::$namepat . ')\\($/', $stack->last(3), $matches)) {
                 // did we just close a function?
                 $stack->pop();
                 // (
                 $stack->pop();
                 // 1
                 $fn = $stack->pop();
                 $fnn = $matches[1];
                 // get the function name
                 if (isset($this->fc[$fnn])) {
                     $counts = $this->fc[$fnn];
                 } else {
                     $counts = array(1);
                 }
                 // default count for built-in functions
                 if (!in_array(0, $counts)) {
                     $a = new stdClass();
                     $a->expected = $counts;
                     $a->given = 0;
                     return $this->trigger(MoodleTranslations::get_string('wrongnumberofarguments', 'mathslib', $a));
                 }
                 $output[] = array('fn' => $fn, 'fnn' => $fnn, 'argcount' => 0);
                 // send function to output
                 $index++;
                 $expecting_op = true;
             } else {
                 return $this->trigger(MoodleTranslations::get_string('unexpectedclosingbracket', 'mathslib'));
             }
             //===============
         } elseif (in_array($op, $ops) and !$expecting_op) {
             // miscellaneous error checking
             return $this->trigger(MoodleTranslations::get_string('unexpectedoperator', 'mathslib', $op));
         } else {
             // I don't even want to know what you did to get here
             return $this->trigger(MoodleTranslations::get_string('anunexpectederroroccured', 'mathslib'));
         }
         if ($index == strlen($expr)) {
             if (in_array($op, $ops)) {
                 // did we end with an operator? bad.
                 return $this->trigger(MoodleTranslations::get_string('operatorlacksoperand', 'mathslib', $op));
             } 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(MoodleTranslations::get_string('expectingaclosingbracket', 'mathslib'));
         }
         // if there are (s on the stack, ()s were unbalanced
         $output[] = $op;
     }
     return $output;
 }
Beispiel #3
0
 function nfx($expr)
 {
     $index = 0;
     $stack = new EvalMathStack();
     $output = array();
     $expr = trim(strtolower($expr));
     $ops = array('+', '-', '*', '/', '^', '_');
     $ops_r = array('+' => 0, '-' => 0, '*' => 0, '/' => 0, '^' => 1);
     $ops_p = array('+' => 0, '-' => 0, '*' => 1, '/' => 1, '_' => 1, '^' => 2);
     $expecting_op = false;
     if (preg_match("/[^\\w\\s+*^\\/()\\.,-]/", $expr, $matches)) {
         return $this->trigger("illegal character '{$matches[0]}'");
     }
     while (1) {
         $op = substr($expr, $index, 1);
         $ex = preg_match('/^([a-z]\\w*\\(?|\\d+(?:\\.\\d*)?|\\.\\d+|\\()/', substr($expr, $index), $match);
         if ($op == '-' and !$expecting_op) {
             $stack->push('_');
             $index++;
         } elseif ($op == '_') {
             return $this->trigger("illegal character '_'");
         } elseif ((in_array($op, $ops) or $ex) and $expecting_op) {
             if ($ex) {
                 $op = '*';
                 $index--;
             }
             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();
             }
             $stack->push($op);
             $index++;
             $expecting_op = false;
         } elseif ($op == ')' and $expecting_op) {
             while (($o2 = $stack->pop()) != '(') {
                 if (is_null($o2)) {
                     return $this->trigger("unexpected ')'");
                 } else {
                     $output[] = $o2;
                 }
             }
             if (preg_match("/^([a-z]\\w*)\\(\$/", $stack->last(2), $matches)) {
                 $fnn = $matches[1];
                 $arg_count = $stack->pop();
                 $output[] = $stack->pop();
                 if (in_array($fnn, $this->fb)) {
                     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 {
                     return $this->trigger("internal error");
                 }
             }
             $index++;
         } elseif ($op == ',' and $expecting_op) {
             while (($o2 = $stack->pop()) != '(') {
                 if (is_null($o2)) {
                     return $this->trigger("unexpected ','");
                 } else {
                     $output[] = $o2;
                 }
             }
             if (!preg_match("/^([a-z]\\w*)\\(\$/", $stack->last(2), $matches)) {
                 return $this->trigger("unexpected ','");
             }
             $stack->push($stack->pop() + 1);
             $stack->push('(');
             $index++;
             $expecting_op = false;
         } elseif ($op == '(' and !$expecting_op) {
             $stack->push('(');
             $index++;
             $allow_neg = true;
         } elseif ($ex and !$expecting_op) {
             $expecting_op = true;
             $val = $match[1];
             if (preg_match("/^([a-z]\\w*)\\(\$/", $val, $matches)) {
                 if (in_array($matches[1], $this->fb) or array_key_exists($matches[1], $this->f)) {
                     $stack->push($val);
                     $stack->push(1);
                     $stack->push('(');
                     $expecting_op = false;
                 } else {
                     $val = $matches[1];
                     $output[] = $val;
                 }
             } else {
                 $output[] = $val;
             }
             $index += strlen($val);
         } elseif ($op == ')') {
             return $this->trigger("unexpected ')'");
         } elseif (in_array($op, $ops) and !$expecting_op) {
             return $this->trigger("unexpected operator '{$op}'");
         } else {
             return $this->trigger("an unexpected error occured");
         }
         if ($index == strlen($expr)) {
             if (in_array($op, $ops)) {
                 return $this->trigger("operator '{$op}' lacks operand");
             } else {
                 break;
             }
         }
         while (substr($expr, $index, 1) == ' ') {
             $index++;
         }
     }
     while (!is_null($op = $stack->pop())) {
         if ($op == '(') {
             return $this->trigger("expecting ')'");
         }
         $output[] = $op;
     }
     return $output;
 }
 /**
  * Convert infix to postfix notation
  *
  * @param string $expr
  *   The expression to convert
  *
  * @return array
  *   An array with the elements of the expression
  */
 private function infixToPostfix($expr)
 {
     $index = 0;
     $stack = new EvalMathStack();
     $output = array();
     $ops = array('+', '-', '*', '/', '^', '_');
     $opsRight = array('+' => 0, '-' => 0, '*' => 0, '/' => 0, '^' => 1);
     $opsPrecedence = array('+' => 0, '-' => 0, '*' => 1, '/' => 1, '_' => 1, '^' => 2);
     $expectingOp = FALSE;
     $inFunction = 0;
     $funcHasArg = array();
     if (preg_match("/[^\\w\\s+*^\\/()\\.,-]/", $expr, $matches)) {
         return $this->trigger(t('illegal character "%c"', array('%c' => $matches[0])));
     }
     while (1) {
         $op = drupal_substr($expr, $index, 1);
         $ex = preg_match('/^([a-z]\\w*\\(?|\\d+(?:\\.\\d*)?(?:[Ee][+-]?\\d*)?|\\.\\d+|\\()/', drupal_substr($expr, $index), $match);
         if ($op == '-' and !$expectingOp) {
             $stack->push('_');
             $index++;
         } elseif ($op == '_') {
             return $this->trigger(t('illegal character "_"'));
         } elseif ((in_array($op, $ops) || $ex) && $expectingOp) {
             if ($ex) {
                 $op = '*';
                 $index--;
             }
             while ($stack->getCount() > 0 && ($o2 = $stack->last()) && in_array($o2, $ops) && ($opsRight[$op] ? $opsPrecedence[$op] < $opsPrecedence[$o2] : $opsPrecedence[$op] <= $opsPrecedence[$o2])) {
                 $output[] = $stack->pop();
             }
             $stack->push($op);
             $index++;
             $expectingOp = FALSE;
         } elseif ($op == ')' && ($expectingOp || $inFunction)) {
             while (($o2 = $stack->pop()) != '(') {
                 if (is_NULL($o2)) {
                     return $this->trigger(t('unexpected ) found'));
                 } else {
                     $output[] = $o2;
                 }
             }
             if (preg_match("/^([a-z]\\w*)\\(\$/", $stack->last(2), $matches)) {
                 $fnn = $matches[1];
                 $argCount = $stack->pop();
                 if ($funcHasArg[$inFunction]) {
                     $argCount++;
                 }
                 $output[] = $stack->pop();
                 // pop the function and push onto the output
                 if (array_key_exists($fnn, $this->funcAliases)) {
                     // Check if the function is an alias
                     $fnn = $this->funcAliases[$fnn];
                 }
                 if (array_key_exists($fnn, $this->funcBuildIn)) {
                     // check the argument count
                     if ($argCount != $this->funcBuildIn[$fnn]) {
                         return $this->trigger(t('wrong number of arguments (@gc given, @ec expected)', array('@gc' => $argCount, '@ec' => $this->funcBuildIn[$fnn])));
                     }
                 } elseif (array_key_exists($fnn, $this->funcUser)) {
                     if ($argCount != count($this->funcUser[$fnn]['args'])) {
                         return $this->trigger(t('wrong number of arguments (@gc given, @ec expected)', array('@gc' => $argCount, '@ec' => count($this->funcUser[$fnn]['args']))));
                     }
                 } else {
                     return $this->trigger(t('internal error, not a function'));
                 }
                 $inFunction--;
             }
             $index++;
         } elseif ($op == ',' and $expectingOp) {
             while (($o2 = $stack->pop()) != '(') {
                 if (is_NULL($o2)) {
                     return $this->trigger(t('unexpected , found'));
                 } else {
                     $output[] = $o2;
                 }
             }
             if (!preg_match("/^([a-z]\\w*)\\(\$/", $stack->last(2), $matches)) {
                 return $this->trigger(t('unexpected , found'));
             }
             $stack->push($stack->pop() + 1);
             $stack->push('(');
             $index++;
             $expectingOp = FALSE;
         } elseif ($op == '(' and !$expectingOp) {
             $stack->push('(');
             $index++;
             $allow_neg = TRUE;
         } elseif ($ex and !$expectingOp) {
             $expectingOp = TRUE;
             $val = $match[1];
             if (preg_match("/^([a-z]\\w*)\\(\$/", $val, $matches)) {
                 if (array_key_exists($matches[1], $this->funcAliases) || array_key_exists($matches[1], $this->funcBuildIn) || array_key_exists($matches[1], $this->funcUser)) {
                     $stack->push($val);
                     $stack->push(0);
                     $stack->push('(');
                     $expectingOp = FALSE;
                     // If we are in a function, it'll have at least one argument, this one.
                     if ($inFunction) {
                         $funcHasArg[$inFunction] = TRUE;
                     }
                     $inFunction++;
                     $funcHasArg[$inFunction] = FALSE;
                 } else {
                     if (!array_key_exists($matches[1], $this->variables)) {
                         return $this->trigger(t('unknown variable or function "%f"', array('%f' => $matches[1])));
                     }
                     $val = $matches[1];
                     $output[] = $val;
                     // If we are in a function, it'll have at least one argument, this one.
                     if ($inFunction) {
                         $funcHasArg[$inFunction] = TRUE;
                     }
                 }
             } else {
                 $output[] = $val;
                 // If we are in a function, it'll have at least one argument, this one.
                 if ($inFunction) {
                     $funcHasArg[$inFunction] = TRUE;
                 }
             }
             $index += drupal_strlen($val);
         } elseif ($op == ')') {
             return $this->trigger(t('unexpected ) found'));
         } elseif (in_array($op, $ops) and !$expectingOp) {
             return $this->trigger(t('unexpected operator "%op"', array('%op' => $op)));
         } else {
             return $this->trigger(t('an unexpected error occured'));
         }
         if ($index == drupal_strlen($expr)) {
             if (in_array($op, $ops)) {
                 return $this->trigger(t('operator "%op" lacks operand', array('%op' => $op)));
             } else {
                 break;
             }
         }
         while (drupal_substr($expr, $index, 1) == ' ') {
             $index++;
         }
     }
     while (!is_NULL($op = $stack->pop())) {
         if ($op == '(') {
             return $this->trigger(t('expecting ) but none found'));
         }
         $output[] = $op;
     }
     return $output;
 }