Ejemplo n.º 1
0
 /**
  * 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();
 }