Completed
Pull Request — master (#4)
by Nico
02:45 queued 31s
created

BaseNode   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 176
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 97.53%

Importance

Changes 0
Metric Value
wmc 27
lcom 1
cbo 6
dl 0
loc 176
ccs 79
cts 81
cp 0.9753
rs 10
c 0
b 0
f 0

9 Methods

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