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

BaseNode   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 136
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 57
dl 0
loc 136
ccs 62
cts 62
cp 1
rs 10
c 0
b 0
f 0
wmc 26
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\TokenStream\Node;
9
10
use Closure;
11
use nicoSWD\Rule\Grammar\CallableUserFunctionInterface;
12
use nicoSWD\Rule\TokenStream\Token\BaseToken;
13
use nicoSWD\Rule\TokenStream\Token\Type\Method;
14
use nicoSWD\Rule\TokenStream\Token\Type\Whitespace;
15
use nicoSWD\Rule\TokenStream\TokenCollection;
16
use nicoSWD\Rule\Parser\Exception\ParserException;
17
use nicoSWD\Rule\TokenStream\TokenIterator;
18
use nicoSWD\Rule\TokenStream\Token\TokenType;
19
20
abstract class BaseNode
21
{
22
    private string $methodName;
23
    private int $methodOffset = 0;
24
25
    public function __construct(
26
        protected readonly TokenIterator $tokenStream,
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_STRING, expecting T_VARIABLE on line 26 at column 27
Loading history...
27 264
    ) {
28
    }
29 264
30 264
    /** @throws ParserException */
31
    abstract public function getNode(): BaseToken;
32
33
    protected function hasMethodCall(): bool
34
    {
35 252
        $stack = $this->tokenStream->getStack();
36
        $position = $stack->key();
37 252
        $hasMethod = false;
38 252
39 252
        while ($stack->valid()) {
40
            $stack->next();
41 252
42 252
            /** @var ?BaseToken $token */
43
            $token = $stack->current();
44
45 252
            if (!$token) {
46
                break;
47 252
            } elseif ($token instanceof Method) {
48 112
                $this->methodName = $token->getValue();
49 246
                $this->methodOffset = $stack->key();
50 166
                $hasMethod = true;
51 166
                break;
52 166
            } elseif (!$token instanceof Whitespace) {
53 246
                break;
54 164
            }
55
        }
56 246
57
        $stack->seek($position);
58
59
        return $hasMethod;
60 252
    }
61
62 252
    /** @throws ParserException */
63
    protected function getMethod(BaseToken $token): CallableUserFunctionInterface
64
    {
65
        $this->tokenStream->getStack()->seek($this->methodOffset);
66 166
67
        return $this->tokenStream->getMethod($this->getMethodName(), $token);
68 166
    }
69
70 166
    private function getMethodName(): string
71
    {
72
        return (string) preg_replace('~\W~', '', $this->methodName);
73 166
    }
74
75 166
    /** @throws ParserException */
76
    protected function getFunction(): Closure
77
    {
78
        return $this->tokenStream->getFunction($this->getFunctionName());
79 26
    }
80
81 26
    private function getFunctionName(): string
82
    {
83
        return (string) preg_replace('~\W~', '', $this->getCurrentNode()->getValue());
84 26
    }
85
86 26
    /** @throws ParserException */
87
    protected function getArrayItems(): TokenCollection
88
    {
89
        return $this->getCommaSeparatedValues(TokenType::SQUARE_BRACKET);
90 60
    }
91
92 60
    /** @throws ParserException */
93
    protected function getArguments(): TokenCollection
94
    {
95
        return $this->getCommaSeparatedValues(TokenType::PARENTHESIS);
96 148
    }
97
98 148
    protected function getCurrentNode(): BaseToken
99
    {
100
        return $this->tokenStream->getStack()->current();
101 256
    }
102
103 256
    /** @throws ParserException */
104
    private function getCommaSeparatedValues(TokenType $stopAt): TokenCollection
105
    {
106
        $items = new TokenCollection();
107 172
        $commaExpected = false;
108
109 172
        do {
110 172
            $token = $this->getNextToken();
111
112
            if (TokenType::isValue($token)) {
113 172
                if ($commaExpected) {
114
                    throw ParserException::unexpectedToken($token);
115 172
                }
116 140
117 4
                $commaExpected = true;
118
                $items->attach($token);
119
            } elseif ($token->isOfType(TokenType::COMMA)) {
120 140
                if (!$commaExpected) {
121 140
                    throw ParserException::unexpectedComma($token);
122 172
                }
123 66
124 4
                $commaExpected = false;
125
            } elseif ($token->isOfType($stopAt)) {
126
                break;
127 66
            } elseif (!$token->canBeIgnored()) {
128 172
                throw ParserException::unexpectedToken($token);
129 156
            }
130 70
        } while (true);
131 4
132
        $this->assertNoTrailingComma($commaExpected, $items, $token);
133 140
        $items->rewind();
134
135 156
        return $items;
136 152
    }
137
138 152
    /** @throws ParserException */
139
    private function getNextToken(): BaseToken
140
    {
141
        $this->tokenStream->next();
142 172
143
        if (!$this->tokenStream->valid()) {
144 172
            throw ParserException::unexpectedEndOfString();
145
        }
146 172
147 4
        return $this->tokenStream->current();
148
    }
149
150 172
    /** @throws ParserException */
151
    private function assertNoTrailingComma(bool $commaExpected, TokenCollection $items, BaseToken $token): void
152
    {
153
        if (!$commaExpected && $items->count() > 0) {
154 156
            throw ParserException::unexpectedComma($token);
155
        }
156 156
    }
157
}
158