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
Bug
introduced
by
![]() |
|||
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 |