BaseNode::getCommaSeparatedValues()   B
last analyzed

Complexity

Conditions 8
Paths 7

Size

Total Lines 32
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 8

Importance

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