Issues (54)

src/Parser/Parser.php (1 issue)

Labels
Severity
1
<?php declare(strict_types=1);
2
3
/**
4
 * @license     http://opensource.org/licenses/mit-license.php MIT
5
 * @link        https://github.com/nicoSWD
6
 * @author      Nicolas Oelgart <[email protected]>
7
 */
8
namespace nicoSWD\Rule\Parser;
9
10
use Closure;
11
use nicoSWD\Rule\Compiler\CompilerFactoryInterface;
12
use nicoSWD\Rule\Compiler\CompilerInterface;
13
use nicoSWD\Rule\Expression\ExpressionFactoryInterface;
14
use nicoSWD\Rule\TokenStream\AST;
15
use nicoSWD\Rule\TokenStream\Token\BaseToken;
16
use nicoSWD\Rule\TokenStream\Token\TokenType;
17
18
class Parser
19
{
20
    private ?BaseToken $operator;
21
    private array $values = [];
22
23
    public function __construct(
24
        private AST $ast,
25
        private ExpressionFactoryInterface $expressionFactory,
26
        private CompilerFactoryInterface $compilerFactory
27
    ) {
28
    }
29
30
    public function parse(string $rule): string
31
    {
32
        $compiler = $this->compilerFactory->create();
33 282
        $this->resetState();
34
35
        foreach ($this->ast->getStream($rule) as $token) {
36
            $handler = $this->getHandlerForType($token->getType());
37
            $handler($token, $compiler);
38 282
39 282
            if ($this->expressionCanBeEvaluated()) {
40 282
                $this->evaluateExpression($compiler);
41 282
            }
42
        }
43 282
44
        return $compiler->getCompiledRule();
45 282
    }
46 282
47
    private function getHandlerForType(int $tokenType): Closure
48 282
    {
49 210
        return match ($tokenType) {
50 210
            TokenType::VALUE, TokenType::INT_VALUE => $this->handleValueToken(),
51
            TokenType::OPERATOR => $this->handleOperatorToken(),
52 208
            TokenType::LOGICAL => $this->handleLogicalToken(),
53 204
            TokenType::PARENTHESIS => $this->handleParenthesisToken(),
54
            TokenType::COMMENT, TokenType::SPACE => $this->handleDummyToken(),
55
            default => $this->handleUnknownToken(),
56
        };
57 186
    }
58
59
    private function evaluateExpression(CompilerInterface $compiler): void
60 210
    {
61
        $expression = $this->expressionFactory->createFromOperator($this->operator);
62 210
63 210
        $compiler->addBoolean(
64 210
            $expression->evaluate(...$this->values)
65 210
        );
66 210
67 210
        $this->resetState();
68 210
    }
69 210
70 210
    private function expressionCanBeEvaluated(): bool
71 210
    {
72
        return count($this->values) === 2;
73
    }
74
75 210
    private function handleValueToken(): Closure
76
    {
77
        return fn (BaseToken $token) => $this->values[] = $token->getValue();
78 200
    }
79
80 200
    private function handleLogicalToken(): Closure
81
    {
82 200
        return fn (BaseToken $token, CompilerInterface $compiler) => $compiler->addLogical($token);
83 200
    }
84
85
    private function handleParenthesisToken(): Closure
86 198
    {
87 198
        return fn (BaseToken $token, CompilerInterface $compiler) => $compiler->addParentheses($token);
88
    }
89 208
90
    private function handleUnknownToken(): Closure
91 208
    {
92
        return fn (BaseToken $token) => throw Exception\ParserException::unknownToken($token);
0 ignored issues
show
A parse error occurred: Syntax error, unexpected T_THROW on line 92 at column 40
Loading history...
93
    }
94 210
95
    private function handleOperatorToken(): Closure
96
    {
97 204
        return function (BaseToken $token): void {
98 210
            if (isset($this->operator)) {
99
                throw Exception\ParserException::unexpectedToken($token);
100
            } elseif (empty($this->values)) {
101 210
                throw Exception\ParserException::incompleteExpression($token);
102
            }
103
104 204
            $this->operator = $token;
105 2
        };
106 204
    }
107 2
108
    private function handleDummyToken(): Closure
109
    {
110 202
        return function (): void {
111 210
            // Do nothing
112
        };
113
    }
114 210
115
    private function resetState(): void
116
    {
117 38
        $this->operator = null;
118 210
        $this->values = [];
119
    }
120
}
121