/** * Implementation of the shunting yard parsing algorithm * * @param array $tokens Token[] array of tokens to process * @retval Node AST of the parsed expression * @throws SyntaxErrorException * @throws ParenthesisMismatchException */ private function shuntingYard(array $tokens) { // Clear the oepratorStack $this->operatorStack = new Stack(); // Clear the operandStack. $this->operandStack = new Stack(); // Remember the last token handled, this is done to recognize unary operators. $lastNode = null; // Loop over the tokens for ($index = 0; $index < count($tokens); $index++) { $token = $tokens[$index]; // echo "current token $token\n"; // echo("operands:" . $this->operandStack . "\n"); // echo("operators: " . $this->operatorStack . "\n\n"); if ($this->rationalFactory) { $node = Node::rationalFactory($token); } else { $node = Node::factory($token); } // Handle closing parentheses if ($token->getType() == TokenType::CloseParenthesis) { $this->handleSubExpression(); } elseif ($node->isTerminal()) { $this->operandStack->push($node); // Push function applications or open parentheses `(` onto the operatorStack } elseif ($node instanceof FunctionNode) { $this->operatorStack->push($node); } elseif ($node instanceof SubExpressionNode) { $this->operatorStack->push($node); // Handle the remaining operators. } elseif ($node instanceof PostfixOperatorNode) { $op = $this->operandStack->pop(); if ($op == NULL) { throw new SyntaxErrorException(); } $this->operandStack->push(new FunctionNode($node->getOperator(), $op)); } elseif ($node instanceof ExpressionNode) { // Check for unary minus and unary plus. $unary = $this->isUnary($node, $lastNode); if ($unary) { switch ($token->getType()) { // Unary +, just ignore it case TokenType::AdditionOperator: $node = null; break; // Unary -, replace the token. // Unary -, replace the token. case TokenType::SubtractionOperator: $node->setOperator('~'); break; } } else { // Pop operators with higher priority while ($node->lowerPrecedenceThan($this->operatorStack->peek())) { $popped = $this->operatorStack->pop(); $popped = $this->handleExpression($popped); $this->operandStack->push($popped); } } if ($node) { $this->operatorStack->push($node); } } // Remember the current token (if it hasn't been nulled, for example being a unary +) if ($node) { $lastNode = $node; } } // Pop remaining operators while (!$this->operatorStack->isEmpty()) { $node = $this->operatorStack->pop(); $node = $this->handleExpression($node); $this->operandStack->push($node); } // Stack should be empty here if ($this->operandStack->count() > 1) { throw new SyntaxErrorException(); } return $this->operandStack->pop(); }