Passed
Pull Request — master (#22)
by Nico
18:37 queued 03:42
created

StandardCompiler   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 113
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 50
dl 0
loc 113
ccs 51
cts 51
cp 1
rs 10
c 0
b 0
f 0
wmc 25
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\Compiler;
9
10
use nicoSWD\Rule\Compiler\Exception\MissingOperatorException;
11
use nicoSWD\Rule\Evaluator\Boolean;
12
use nicoSWD\Rule\Evaluator\Operator;
13
use nicoSWD\Rule\Parser\Exception\ParserException;
14
use nicoSWD\Rule\TokenStream\Token\BaseToken;
15
use nicoSWD\Rule\TokenStream\Token\TokenAnd;
16
use nicoSWD\Rule\TokenStream\Token\TokenOpeningParenthesis;
17
use nicoSWD\Rule\TokenStream\Token\Type\Logical;
18
use nicoSWD\Rule\TokenStream\Token\Type\Parenthesis;
19
20
final class StandardCompiler implements CompilerInterface
21
{
22
    private const OPENING_PARENTHESIS = '(';
23
    private const CLOSING_PARENTHESIS = ')';
24
25
    private string $output = '';
26
    private int $openParenthesis = 0;
27
    private int $closedParenthesis = 0;
28
29
    /** @throws ParserException */
30
    public function getCompiledRule(): string
31
    {
32
        if ($this->isIncompleteCondition()) {
33
            throw new ParserException('Incomplete condition');
34 184
        } elseif (!$this->numParenthesesMatch()) {
35
            throw new ParserException('Missing closing parenthesis');
36 184
        }
37 4
38 180
        return $this->output;
39 2
    }
40
41
    private function openParenthesis(): void
42 178
    {
43
        $this->openParenthesis++;
44
        $this->output .= self::OPENING_PARENTHESIS;
45 20
    }
46
47 20
    /** @throws ParserException */
48 20
    private function closeParenthesis(): void
49 20
    {
50
        if ($this->openParenthesis < 1) {
51 20
            throw new ParserException('Missing opening parenthesis');
52
        }
53 20
54 2
        $this->closedParenthesis++;
55
        $this->output .= self::CLOSING_PARENTHESIS;
56
    }
57 18
58 18
    /** @throws ParserException */
59 18
    public function addParentheses(BaseToken & Parenthesis $token): void
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_STRING, expecting T_VARIABLE on line 59 at column 47
Loading history...
60
    {
61 22
        if ($token instanceof TokenOpeningParenthesis) {
62
            if (!$this->expectOpeningParenthesis()) {
63 22
                throw ParserException::unexpectedToken($token);
64 20
            }
65 4
            $this->openParenthesis();
66
        } else {
67 20
            $this->closeParenthesis();
68
        }
69 20
    }
70
71 20
    /** @throws ParserException */
72
    public function addLogical(BaseToken & Logical $token): void
73 36
    {
74
        if (Operator::tryFrom($this->getLastChar()) !== null) {
75 36
            throw ParserException::unexpectedToken($token);
76
        }
77 36
78 2
        if ($token instanceof TokenAnd) {
79
            $this->output .= Operator::LOGICAL_AND->value;
80
        } else {
81 36
            $this->output .= Operator::LOGICAL_OR->value;
82 34
        }
83
    }
84 14
85
    /** @throws MissingOperatorException */
86 36
    public function addBoolean(bool $bool): void
87
    {
88 196
        if (Boolean::tryFrom($this->getLastChar()) !== null) {
89
            throw new MissingOperatorException();
90 196
        }
91
92 196
        $this->output .= Boolean::fromBool($bool)->value;
93 2
    }
94
95
    private function numParenthesesMatch(): bool
96 196
    {
97 196
        return $this->openParenthesis === $this->closedParenthesis;
98
    }
99 180
100
    private function isIncompleteCondition(): bool
101 180
    {
102
        return Operator::tryFrom($this->getLastChar()) !== null;
103
    }
104 184
105
    private function expectOpeningParenthesis(): bool
106 184
    {
107
        $lastChar = $this->getLastChar();
108
109 184
        return
110 184
            $lastChar === '' ||
111
            $lastChar === self::OPENING_PARENTHESIS ||
112
            Operator::tryFrom($lastChar) !== null;
113
    }
114 20
115
    private function getLastChar(): string
116 20
    {
117
        return substr($this->output, offset: -1);
118
    }
119
}
120