Completed
Push — master ( 066343...e5de66 )
by Nico
01:23
created

BaseNode   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 155
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 29
lcom 1
cbo 6
dl 0
loc 155
ccs 74
cts 74
cp 1
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
getNode() 0 1 ?
B hasMethodCall() 0 23 5
B getMethod() 0 26 3
A getMethodName() 0 8 2
A getArrayItems() 0 4 1
A getArguments() 0 4 1
A getCurrentNode() 0 4 1
A getParser() 0 4 1
C getCommaSeparatedValues() 0 44 11
A isIgnoredToken() 0 8 3
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\Rules\AST\Nodes;
11
12
use nicoSWD\Rules\AST;
13
use nicoSWD\Rules\AST\TokenCollection;
14
use nicoSWD\Rules\Parser;
15
use nicoSWD\Rules\TokenType;
16
use nicoSWD\Rules\Core\CallableFunction;
17
use nicoSWD\Rules\Exceptions\ParserException;
18
use nicoSWD\Rules\Tokens;
19
use nicoSWD\Rules\Tokens\BaseToken;
20
use nicoSWD\Rules\Tokens\TokenComment;
21
use nicoSWD\Rules\Tokens\TokenMethod;
22
use nicoSWD\Rules\Tokens\TokenNewline;
23
use nicoSWD\Rules\Tokens\TokenSpace;
24
25
abstract class BaseNode
26
{
27
    /** @var AST */
28
    protected $ast;
29
30
    /** @var string */
31
    protected $methodName = '';
32
33
    /** @var int */
34
    protected $methodOffset = 0;
35
36 204
    public function __construct(AST $ast)
37
    {
38 204
        $this->ast = $ast;
39 204
    }
40
41
    abstract public function getNode(): BaseToken;
42
43
    /**
44
     * Looks ahead, but does not move the pointer.
45
     */
46 192
    protected function hasMethodCall(): bool
47
    {
48 192
        $stackClone = $this->ast->getStack()->getClone();
49
50 192
        while ($stackClone->valid()) {
51 192
            $stackClone->next();
52
53 192
            if (!$token = $stackClone->current()) {
54 98
                break;
55 190
            } elseif ($this->isIgnoredToken($token)) {
56 128
                continue;
57 190
            } elseif ($token instanceof TokenMethod) {
58 108
                $this->methodName = $token->getValue();
59 108
                $this->methodOffset = $token->getOffset();
60
61 108
                return true;
62
            } else {
63 170
                break;
64
            }
65
        }
66
67 172
        return false;
68
    }
69
70 108
    public function getMethod(BaseToken $token): CallableFunction
71
    {
72 108
        $methodName = $this->getMethodName();
73 108
        $methodClass = '\nicoSWD\Rules\Core\Methods\\' . ucfirst($methodName);
74
75 108
        if (!class_exists($methodClass)) {
76 2
            $current = $this->getCurrentNode();
77
78 2
            throw new ParserException(sprintf(
79 2
                'undefined is not a function at position %d on line %d',
80 2
                $current->getPosition(),
81 2
                $current->getLine()
82
            ));
83
        }
84
85
        /** @var CallableFunction $instance */
86 106
        $instance = new $methodClass($token);
87
88 106
        if ($instance->getName() !== $methodName) {
89 2
            throw new ParserException(
90 2
                'undefined is not a function'
91
            );
92
        }
93
94 104
        return $instance;
95
    }
96
97 108
    private function getMethodName(): string
98
    {
99
        do {
100 108
            $this->ast->next();
101 108
        } while ($this->getCurrentNode()->getOffset() < $this->methodOffset);
102
103 108
        return trim(ltrim(rtrim($this->methodName, "\r\n("), '.'));
104
    }
105
106 52
    public function getArrayItems(): TokenCollection
107
    {
108 52
        return $this->getCommaSeparatedValues(TokenType::SQUARE_BRACKETS);
109
    }
110
111 126
    public function getArguments(): TokenCollection
112
    {
113 126
        return $this->getCommaSeparatedValues(TokenType::PARENTHESES);
114
    }
115
116 178
    public function getCurrentNode()
117
    {
118 178
        return $this->ast->getStack()->current();
119
    }
120
121 28
    public function getParser(): Parser
122
    {
123 28
        return $this->ast->parser;
124
    }
125
126 148
    private function getCommaSeparatedValues(int $stopAt): TokenCollection
127
    {
128 148
        $commaExpected = false;
129 148
        $items = new TokenCollection();
130
131
        do {
132 148
            $this->ast->next();
133
134 148
            if (!$current = $this->ast->current()) {
135 4
                throw new ParserException('Unexpected end of string');
136
            }
137
138 148
            if ($current->getType() === TokenType::VALUE) {
139 124
                if ($commaExpected) {
140 4
                    throw ParserException::unexpectedToken($current);
141
                }
142
143 124
                $commaExpected = true;
144 124
                $items->attach($current);
145 148
            } elseif ($current instanceof Tokens\TokenComma) {
146 60
                if (!$commaExpected) {
147 4
                    throw ParserException::unexpectedToken($current);
148
                }
149
150 60
                $commaExpected = false;
151 148
            } elseif ($current->getType() === $stopAt) {
152 132
                break;
153 64
            } elseif (!$this->isIgnoredToken($current)) {
154 4
                throw ParserException::unexpectedToken($current);
155
            }
156 124
        } while ($this->ast->valid());
157
158 132
        if (!$commaExpected && $items->count() > 0) {
159 6
            throw new ParserException(sprintf(
160 6
                'Unexpected "," at position %d on line %d',
161 6
                $current->getPosition(),
162 6
                $current->getLine()
163
            ));
164
        }
165
166 126
        $items->rewind();
167
168 126
        return $items;
169
    }
170
171 190
    private function isIgnoredToken(BaseToken $token): bool
172
    {
173
        return (
174 190
            $token instanceof TokenSpace ||
175 190
            $token instanceof TokenNewline ||
176 190
            $token instanceof TokenComment
177
        );
178
    }
179
}
180