j84reginato/my-eval

View on GitHub
src/Solving/TreePrinter.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

declare(strict_types=1);

namespace MyEval\Solving;

use MyEval\Parsing\Nodes\Operand\BooleanNode;
use MyEval\Parsing\Nodes\Operand\ConstantNode;
use MyEval\Parsing\Nodes\Operand\FloatNode;
use MyEval\Parsing\Nodes\Operand\IntegerNode;
use MyEval\Parsing\Nodes\Operand\RationalNode;
use MyEval\Parsing\Nodes\Operand\VariableNode;
use MyEval\Parsing\Nodes\Operator\FunctionNode;
use MyEval\Parsing\Nodes\Operator\InfixExpressionNode;
use MyEval\Parsing\Nodes\Operator\TernaryExpressionNode;

/**
 * Simple string representation of an AST.
 *
 * Probably most useful for debugging purposes.
 *
 * Implementation of a Visitor, transforming an AST into a string representation of the tree.
 *
 * ## Example:
 *
 * ~~~{.php}
 * use MyEval\StdMathEval;
 * use MyEval\Solving\TreePrinter;
 *
 * $parser = new StdMathEval();
 * $tree = $parser->parse('exp(2x)+xy');
 * printer = new TreePrinter();
 * result = $tree->accept($printer);  // Generates "(+, exp((*, 2:int, x)), (*, x, y))".
 * ~~~
 *
 * or more complex use:
 *
 * ~~~{.php}
 * use MyEval\Lexing\StdMathLexer;
 * use MyEval\Parsing\Parser;
 * use MyEval\Solving\TreePrinter;
 *
 * // Tokenize
 * $lexer = new StdMathLexer();
 * $tokens = $lexer->tokenize('exp(2x)+xy');
 *
 * // Parse
 * $parser = new Parser();
 * $ast = $parser->parse($tokens);
 *
 * // Evaluate
 * printer = new TreePrinter();
 * $value = $ast->accept(printer);
 * ~~~
 */
class TreePrinter implements Visitor, LogicVisitor
{
    /**
     * Print a IntegerNode.
     *
     * @param IntegerNode $node AST to be evaluated.
     *
     * @return string
     */
    public function visitIntegerNode(IntegerNode $node): string
    {
        $val = $node->value;
        return "$val:int";
    }

    /**
     * Print a RationalNode.
     *
     * @param RationalNode $node AST to be evaluated.
     *
     * @return string
     */
    public function visitRationalNode(RationalNode $node): string
    {
        $p = $node->getNumerator();
        $q = $node->getDenominator();
        return "$p/$q:rational";
    }

    /**
     * Print a NumberNode.
     *
     * @param FloatNode $node AST to be evaluated.
     *
     * @return string
     */
    public function visitNumberNode(FloatNode $node): string
    {
        $val = $node->value;
        return "$val:float";
    }

    /**
     * Print a BooleanNode.
     *
     * @param BooleanNode $node AST to be evaluated.
     *
     * @return string
     */
    public function visitBooleanNode(BooleanNode $node): string
    {
        return $node->value ? 'true:bool' : 'false:bool';
    }

    /**
     * Print a VariableNode.
     *
     * @param VariableNode $node AST to be evaluated.
     *
     * @return string
     */
    public function visitVariableNode(VariableNode $node): string
    {
        return $node->value;
    }

    /**
     * Print a ConstantNode.
     *
     * @param ConstantNode $node AST to be evaluated.
     *
     * @return string
     */
    public function visitConstantNode(ConstantNode $node): string
    {
        return $node->value;
    }

    /**
     * Print an ExpressionNode.
     *
     * @param InfixExpressionNode $node AST to be evaluated.
     *
     * @return string
     */
    public function visitInfixExpressionNode(InfixExpressionNode $node): string
    {
        $leftValue = $node->getLeft()?->accept($this);
        $operator  = $node->operator;

        // The operator and the right side are optional, remember?
        if (!$operator) {
            return (string)$leftValue;
        }

        $right = $node->getRight();

        if ($right) {
            $rightValue = $right->accept($this);
            return "($operator, $leftValue, $rightValue)";
        }

        return "($operator, $leftValue)";
    }

    /**
     * Print a IfNode.
     *
     * @param TernaryExpressionNode $node AST to be evaluated.
     *
     * @return string
     */
    public function visitTernaryNode(TernaryExpressionNode $node): string
    {
        $operator   = $node->operator;
        $condiction = $node->getCondition();
        $leftValue  = $node->getLeft()?->accept($this);
        $rightValue = $node->getRight()?->accept($this);

        return "($condiction; $leftValue; $rightValue):$operator";
    }

    /**
     * Print a FunctionNode.
     *
     * @param FunctionNode $node AST to be evaluated.
     *
     * @return string
     */
    public function visitFunctionNode(FunctionNode $node): string
    {
        $functionName = $node->operator;
        $operand      = $node->operand->accept($this);

        return "$functionName($operand)";
    }
}