Example #1
0
 public function evaluate()
 {
     $output = new Stack();
     for ($i = 0; $i < count($this->tokens); $i++) {
         $t = $this->tokens[$i];
         if ($t->getType() == Token::TOKEN_NUMBER) {
             $output->push($t->getValue());
         } else {
             if ($t->getType() == Token::TOKEN_VARIABLE) {
                 $name = $t->getName();
                 if (!isset($this->variables[$name])) {
                     throw new ExpressionException("No value has been set for the setVariable '" . $name . "'.");
                 }
                 $value = $this->variables[$name];
                 $output->push($value);
             } else {
                 if ($t->getType() == Token::TOKEN_OPERATOR) {
                     $op = $t;
                     if ($output->size() < $op->getOperator()->getNumOperands()) {
                         throw new ExpressionException("Invalid number of operands available");
                     }
                     if ($op->getOperator()->getNumOperands() == 2) {
                         /* pop the operands and push the result of the operation */
                         $rightArg = $output->pop();
                         $leftArg = $output->pop();
                         $output->push($op->getOperator()->apply([$leftArg, $rightArg]));
                     } else {
                         if ($op->getOperator()->getNumOperands() == 1) {
                             /* pop the operand and push the result of the operation */
                             $arg = $output->pop();
                             $output->push($op->getOperator()->apply([$arg]));
                         }
                     }
                 } else {
                     if ($t->getType() == Token::TOKEN_FUNCTION) {
                         /** @var FunctionToken $func */
                         $func = $t;
                         if ($func->getFunction()->getNumArguments() > $func->getPassedArgumentCount()) {
                             throw new ExpressionException("Function " . $func->getFunction()->getName() . " requires at least " . $func->getFunction()->getNumArguments() . " arguments (" . $func->getPassedArgumentCount() . " passed)");
                         }
                         if ($output->size() < $func->getFunction()->getNumArguments()) {
                             throw new ExpressionException("Invalid number of arguments available");
                         }
                         /* collect the arguments from the stack */
                         $args = [];
                         for ($j = 0; $j < $func->getPassedArgumentCount(); $j++) {
                             $args[$j] = $output->pop();
                         }
                         $output->push($func->getFunction()->apply($this->reverseInPlace($args)));
                     }
                 }
             }
         }
     }
     if ($output->size() > 1) {
         throw new ExpressionException("Invalid number of items on the output queue. Might be caused by an invalid number of arguments for a function.");
     }
     return $output->pop();
 }
Example #2
0
 /**
  * Convert a Set of tokens from infix to reverse polish notation
  * @param string $expression the expression to convert
  * @param array $userFunctions the custom functions used
  * @param array $userOperators the custom operators used
  * @param array $variableNames
  * @return array of tokens containing the result
  */
 public static function convertToRPN($expression, $userFunctions, $userOperators, $variableNames)
 {
     $stack = new Stack();
     $functionStack = new Stack();
     $output = [];
     $tokenizer = new Tokenizer($expression, $userFunctions, $userOperators, $variableNames);
     while ($tokenizer->hasNext()) {
         $token = $tokenizer->nextToken();
         switch ($token->getType()) {
             case Token::TOKEN_NUMBER:
             case Token::TOKEN_VARIABLE:
                 $output[] = $token;
                 break;
             case Token::TOKEN_FUNCTION:
                 $stack->add($token);
                 $functionStack->add($token);
                 break;
             case Token::TOKEN_SEPARATOR:
                 // store argument count, easier than easy
                 if (!$functionStack->isEmpty()) {
                     $functionToken = $functionStack->peek();
                     $functionToken->incPassedArgumentCount();
                 }
                 while (!$stack->isEmpty() && $stack->peek()->getType() != Token::TOKEN_PARENTHESES_OPEN) {
                     $output[] = $stack->pop();
                 }
                 if ($stack->isEmpty() || $stack->peek()->getType() != Token::TOKEN_PARENTHESES_OPEN) {
                     throw new ExpressionException("Misplaced function separator ',' or mismatched parentheses");
                 }
                 break;
             case Token::TOKEN_OPERATOR:
                 while (!$stack->isEmpty() && $stack->peek()->getType() == Token::TOKEN_OPERATOR) {
                     $o1 = $token;
                     $o2 = $stack->peek();
                     if ($o1->getOperator()->getNumOperands() == 1 && $o2->getOperator()->getNumOperands() == 2) {
                         break;
                     } else {
                         if ($o1->getOperator()->isLeftAssociative() && $o1->getOperator()->getPrecedence() <= $o2->getOperator()->getPrecedence() || $o1->getOperator()->getPrecedence() < $o2->getOperator()->getPrecedence()) {
                             $output[] = $stack->pop();
                         } else {
                             break;
                         }
                     }
                 }
                 $stack->push($token);
                 break;
             case Token::TOKEN_PARENTHESES_OPEN:
                 $stack->push($token);
                 break;
             case Token::TOKEN_PARENTHESES_CLOSE:
                 while ($stack->peek()->getType() != Token::TOKEN_PARENTHESES_OPEN) {
                     $output[] = $stack->pop();
                 }
                 $stack->pop();
                 if (!$stack->isEmpty() && $stack->peek()->getType() == Token::TOKEN_FUNCTION) {
                     $output[] = $stack->pop();
                     $functionStack->pop();
                 }
                 break;
             default:
                 throw new ExpressionException("Unknown Token type encountered. This should not happen");
         }
     }
     while (!$stack->isEmpty()) {
         $t = $stack->pop();
         if ($t->getType() == Token::TOKEN_PARENTHESES_CLOSE || $t->getType() == Token::TOKEN_PARENTHESES_OPEN) {
             throw new ExpressionException("Mismatched parentheses detected. Please check the expression");
         } else {
             $output[] = $t;
         }
     }
     return $output;
 }