Completed
Push — master ( ffffde...8cec06 )
by Nico
01:31
created

BaseNode::getMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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