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(); }
/** * 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; }