Completed
Push — master ( 317706...046a0a )
by Nico
01:26
created

Parser::handleParenthesisToken()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @license     http://opensource.org/licenses/mit-license.php MIT
7
 * @link        https://github.com/nicoSWD
8
 * @author      Nicolas Oelgart <[email protected]>
9
 */
10
namespace nicoSWD\Rule\Parser;
11
12
use Closure;
13
use nicoSWD\Rule\Compiler\CompilerInterface;
14
use nicoSWD\Rule\Compiler\CompilerFactoryInterface;
15
use nicoSWD\Rule\Compiler\Exception\MissingOperatorException;
16
use nicoSWD\Rule\Expression\ExpressionFactoryInterface;
17
use nicoSWD\Rule\TokenStream\AST;
18
use nicoSWD\Rule\TokenStream\Token\BaseToken;
19
use nicoSWD\Rule\TokenStream\Token\TokenType;
20
use SplStack;
21
22
class Parser
23
{
24
    /** @var AST */
25
    private $ast;
26
    /** @var ExpressionFactoryInterface */
27
    private $expressionFactory;
28
    /** @var CompilerFactoryInterface */
29
    private $compilerFactory;
30
    /** @var BaseToken|null */
31
    private $operator;
32
    /** @var SplStack */
33
    private $values;
34
35 220
    public function __construct(
36
        AST $ast,
37
        ExpressionFactoryInterface $expressionFactory,
38
        CompilerFactoryInterface $compilerFactory
39
    ) {
40 220
        $this->ast = $ast;
41 220
        $this->expressionFactory = $expressionFactory;
42 220
        $this->compilerFactory = $compilerFactory;
43 220
    }
44
45 220
    public function parse(string $rule): string
46
    {
47 220
        $compiler = $this->compilerFactory->create();
48 220
        $this->operator = null;
49 220
        $this->values = new SplStack();
50
51 220
        foreach ($this->ast->getStream($rule) as $token) {
52 186
            $handler = $this->getTokenHandler($token);
53 186
            $handler($compiler);
54
        }
55
56 162
        return $compiler->getCompiledRule();
57
    }
58
59
    private function getTokenHandler(BaseToken $token): Closure
60
    {
61 186
        return function (CompilerInterface $compiler) use ($token) {
62
            $handlers = [
63 186
                TokenType::VALUE       => $this->handleValueToken(),
64 186
                TokenType::INT_VALUE   => $this->handleValueToken(),
65 186
                TokenType::OPERATOR    => $this->handleOperatorToken(),
66 186
                TokenType::LOGICAL     => $this->handleLogicalToken(),
67 186
                TokenType::PARENTHESIS => $this->handleParenthesisToken(),
68 186
                TokenType::SPACE       => $this->handleDummyToken(),
69 186
                TokenType::COMMENT     => $this->handleDummyToken(),
70 186
                TokenType::UNKNOWN     => $this->handleUnknownToken(),
71
            ];
72
73 186
            $handler = $handlers[$token->getType()] ?? $handlers[TokenType::UNKNOWN];
74 186
            $handler($token, $compiler);
75
76 184
            $this->evaluateExpression($compiler);
77 186
        };
78
    }
79
80 184
    private function evaluateExpression(CompilerInterface $compiler)
81
    {
82 184
        if (!isset($this->operator) || $this->values->count() !== 2) {
83 184
            return;
84
        }
85
86 176
        list($rightValue, $leftValue) = $this->values;
87
88
        try {
89 176
            $expression = $this->expressionFactory->createFromOperator($this->operator);
90
91 176
            $compiler->addBoolean(
92 176
                $expression->evaluate($leftValue, $rightValue)
93
            );
94 4
        } catch (MissingOperatorException $e) {
95 2
            throw new Exception\ParserException('Missing operator');
96
        }
97
98
        do {
99 174
            $this->values->pop();
100 174
        } while (!$this->values->isEmpty());
101
102 174
        unset($this->operator);
103 174
    }
104
105
    private function handleValueToken(): Closure
106
    {
107 186
        return function (BaseToken $token) {
108 180
            $this->values->push($token->getValue());
109 186
        };
110
    }
111
112
    private function handleOperatorToken(): Closure
113
    {
114 186
        return function (BaseToken $token) {
115 180
            if (isset($this->operator)) {
116 2
                throw Exception\ParserException::unexpectedToken($token);
117 180
            } elseif ($this->values->isEmpty()) {
118 2
                throw Exception\ParserException::incompleteExpression($token);
119
            }
120
121 178
            $this->operator = $token;
122 186
        };
123
    }
124
125
    private function handleLogicalToken(): Closure
126
    {
127 186
        return function (BaseToken $token, CompilerInterface $compiler) {
128 38
            $compiler->addLogical($token);
129 186
        };
130
    }
131
132
    private function handleParenthesisToken(): Closure
133
    {
134 186
        return function (BaseToken $token, CompilerInterface $compiler) {
135 24
            $compiler->addParentheses($token);
136 186
        };
137
    }
138
139
    private function handleUnknownToken(): Closure
140
    {
141 186
        return function (BaseToken $token) {
142 6
            throw Exception\ParserException::unknownToken($token);
143 186
        };
144
    }
145
146
    private function handleDummyToken(): Closure
147
    {
148 186
        return function () {
149
            // Do nothing
150 186
        };
151
    }
152
}
153