nicoSWD /
php-rule-parser
| 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
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 |