Completed
Push — master ( 88ff28...2dc58f )
by Kirill
06:03
created

RuleResolver::resolveCurrent()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 0
cts 12
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 4
nop 2
crap 12
1
<?php
2
/**
3
 * This file is part of Railt package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
declare(strict_types=1);
9
10
namespace Railt\Compiler\Reader\Resolver;
11
12
use Railt\Compiler\Exception\GrammarException;
13
use Railt\Io\Readable;
14
use Railt\Lexer\TokenInterface;
15
use Railt\Parser\Ast\Delegate;
16
17
/**
18
 * Class RuleResolver
19
 */
20
class RuleResolver implements ResolverInterface
21
{
22
    /**
23
     * @var array|array[]
24
     */
25
    private $rules = [];
26
27
    /**
28
     * @var string|null
29
     */
30
    private $current;
31
32
    /**
33
     * @var array|string[]
34
     */
35
    private $keep = [];
36
37
    /**
38
     * @var array|string[]
39
     */
40
    private $delegates = [];
41
42
    /**
43
     * @var array|Readable[]
44
     */
45
    private $files = [];
46
47
    /**
48
     * @param Readable $readable
49
     * @param TokenInterface $token
50
     * @throws \Railt\Io\Exception\ExternalFileException
51
     */
52
    public function resolve(Readable $readable, TokenInterface $token): void
53
    {
54
        if ($this->next($readable, $token)) {
55
            return;
56
        }
57
58
        if (! \array_key_exists($this->current, $this->rules)) {
59
            $this->rules[$this->current] = [];
60
        }
61
62
        if (! \array_key_exists($this->current, $this->files)) {
63
            $this->files[$this->current] = $readable;
64
        }
65
66
        $this->rules[$this->current][] = $token;
67
    }
68
69
    /**
70
     * @param Readable $readable
71
     * @param TokenInterface $token
72
     * @return bool
73
     * @throws \Railt\Io\Exception\ExternalFileException
74
     */
75
    private function next(Readable $readable, TokenInterface $token): bool
76
    {
77
        if ($token->name() === 'T_NODE_DEFINITION') {
78
            $this->resolveCurrent($readable, $token);
79
            return true;
80
        }
81
82
        if ($this->current === null) {
83
            $error = \sprintf('Unprocessed production %s', $token->value(0));
84
            throw (new GrammarException($error))->throwsIn($readable, $token->offset());
85
        }
86
87
        return false;
88
    }
89
90
    /**
91
     * @param Readable $readable
92
     * @param TokenInterface $token
93
     * @throws \Railt\Io\Exception\ExternalFileException
94
     */
95
    private function resolveCurrent(Readable $readable, TokenInterface $token): void
96
    {
97
        [$name, $delegate] = [\trim($token->value(1), '#'), $token->value(2)];
0 ignored issues
show
Bug introduced by
The variable $name does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $delegate does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
98
        $keep          = $token->value(1)[0] === '#';
99
        $this->current = $name;
100
101
        if ($keep) {
102
            $this->keep[] = $this->current;
103
        }
104
105
        if ($delegate) {
106
            $this->resolveDelegate($delegate, $readable, $token);
107
        }
108
    }
109
110
    /**
111
     * @param string $delegate
112
     * @param Readable $readable
113
     * @param TokenInterface $token
114
     * @throws \Railt\Io\Exception\ExternalFileException
115
     */
116
    private function resolveDelegate(string $delegate, Readable $readable, TokenInterface $token): void
117
    {
118
        if (! \class_exists($delegate)) {
119
            $error = 'Could not found delegate class "%s"';
120
            throw (new GrammarException(\sprintf($error, $delegate)))
121
                ->throwsIn($readable, $token->offset());
122
        }
123
124
        if (! \is_subclass_of($delegate, Delegate::class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \Railt\Parser\Ast\Delegate::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
125
            $error = 'Delegate should be an instance of %s, but %s given';
126
            throw (new GrammarException(\sprintf($error, Delegate::class, $delegate)))
127
                ->throwsIn($readable, $token->offset());
128
        }
129
130
        $this->delegates[$this->current] = $delegate;
131
    }
132
133
    /**
134
     * @return array
135
     */
136
    public function getRules(): array
137
    {
138
        return $this->rules;
139
    }
140
141
    /**
142
     * @return array
143
     */
144
    public function getParsedRules(): array
145
    {
146
        return dd((new RulesBuilder($this))->build());
147
    }
148
149
    /**
150
     * @return array|string[]
151
     */
152
    public function getKeep(): array
153
    {
154
        return $this->keep;
155
    }
156
157
    /**
158
     * @return array|string[]
159
     */
160
    public function getDelegates(): array
161
    {
162
        return $this->delegates;
163
    }
164
165
    /**
166
     * @return array
167
     */
168
    public function getFiles(): array
169
    {
170
        return $this->files;
171
    }
172
}
173