Test Failed
Push — master ( b7ab7b...94ee03 )
by Kirill
02:59
created

Builder::reduce()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
nc 4
nop 2
dl 0
loc 14
ccs 0
cts 12
cp 0
crap 12
rs 9.7998
c 0
b 0
f 0
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\SDL\Frontend;
11
12
use Railt\Io\Readable;
13
use Railt\Parser\Ast\RuleInterface;
14
use Railt\SDL\Exception\CompilerException;
15
use Railt\SDL\Exception\InternalException;
16
use Railt\SDL\Frontend;
17
use Railt\SDL\Frontend\Context\ContextInterface;
18
use Railt\SDL\Frontend\Context\GlobalContext;
19
use Railt\SDL\Frontend\Context\GlobalContextInterface;
20
use Railt\SDL\IR\SymbolTable;
21
use Railt\SDL\IR\SymbolTable\ValueInterface;
22
use Railt\SDL\IR\SymbolTableInterface;
23
use Railt\SDL\IR\Type\TypeNameInterface;
24
25
/**
26
 * Class Builder
27
 */
28
class Builder
29
{
30
    /**
31
     * @var string[]|Builder\BuilderInterface[]
32
     */
33
    private const DEFAULT_BUILDER_DEFINITIONS = [
34
        Builder\Instruction\ImportBuilder::class,
35
        Builder\Instruction\NamespaceBuilder::class,
36
        Builder\Instruction\VariableBuilder::class,
37
        Builder\Instruction\VariableReassigmentBuilder::class,
38
39
        //
40
        Builder\DefinitionBuilder::class,
41
42
        // Values
43
        Builder\Value\ScalarValueBuilder::class,
44
        Builder\Value\VariableValueBuilder::class,
45
        Builder\Value\TypeInvocationBuilder::class,
46
        Builder\Value\ConstantValueBuilder::class,
47
        Builder\Value\BooleanValueBuilder::class,
48
49
        //
50
        Builder\Common\TypeNameBuilder::class,
51
        Builder\TypeDefinitionBuilder::class,
52
    ];
53
54
    /**
55
     * @var array|Builder\BuilderInterface[]
56
     */
57
    private $builders = [];
58
59
    /**
60
     * @var Frontend
61
     */
62
    private $frontend;
63
64
    /**
65
     * @var SymbolTableInterface
66
     */
67
    private $table;
68
69
    /**
70
     * Builder constructor.
71
     * @param Frontend $frontend
72
     * @param SymbolTable $table
73
     */
74
    public function __construct(Frontend $frontend, SymbolTable $table)
75
    {
76
        $this->table    = $table;
77
        $this->frontend = $frontend;
78
        $this->bootDefaults();
79
    }
80
81
    /**
82
     * @return void
83
     */
84
    private function bootDefaults(): void
85
    {
86
        foreach (self::DEFAULT_BUILDER_DEFINITIONS as $builder) {
87
            $this->builders[] = new $builder($this);
88
        }
89
    }
90
91
    /**
92
     * @param Readable $readable
93
     * @return \Traversable
94
     * @throws \Railt\Io\Exception\ExternalFileException
95
     * @throws \Railt\SDL\Exception\SyntaxException
96
     */
97
    public function load(Readable $readable): iterable
98
    {
99
        return $this->frontend->load($readable);
100
    }
101
102
    /**
103
     * @param Readable $file
104
     * @param iterable $ast
105
     * @return iterable
106
     * @throws \Railt\Io\Exception\ExternalFileException
107
     */
108
    public function build(Readable $file, iterable $ast): iterable
109
    {
110
        $context = new GlobalContext($file, $this->table);
111
112
        foreach ($ast as $child) {
113
            if ($this->filter([$context, $result] = $this->reduce($context, $child))) {
114
                yield $result;
0 ignored issues
show
Bug introduced by
The variable $result 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...
115
            }
116
        }
117
118
119
        echo \str_repeat('=', 60) . "\n";
120
        echo \sprintf('| %5s | %-48s |', 'ID', 'VARIABLE') . "\n";
121
        echo \str_repeat('-', 60) . "\n";
122
        foreach ($this->table as $id => $var) {
123
            echo \sprintf('| %5d | %-48s |', $id, $var) . "\n";
124
        }
125
        echo \str_repeat('=', 60) . "\n";
126
    }
127
128
    /**
129
     * @param mixed $result
130
     * @return bool
131
     */
132
    private function filter($result): bool
0 ignored issues
show
Unused Code introduced by
The parameter $result is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
133
    {
134
        return false;
135
    }
136
137
    /**
138
     * @param ContextInterface $context
139
     * @param RuleInterface $ast
140
     * @return array|iterable<int,ContextInterface|mixed>
0 ignored issues
show
Documentation introduced by
The doc-type array|iterable<int,ContextInterface|mixed> could not be parsed: Expected "|" or "end of type", but got "<" at position 14. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
141
     * @throws \Railt\Io\Exception\ExternalFileException
142
     */
143
    private function reduce(ContextInterface $context, RuleInterface $ast): array
144
    {
145
        try {
146
            $process = $this->resolve($context, $ast);
147
148
            if ($process instanceof \Generator) {
149
                return $this->run($context, $process);
150
            }
151
152
            return [$context, $process];
153
        } catch (CompilerException $e) {
154
            throw $e->throwsIn($context->getFile(), $ast->getOffset());
155
        }
156
    }
157
158
    /**
159
     * @param ContextInterface $context
160
     * @param RuleInterface $ast
161
     * @return mixed|\Traversable|void
162
     * @throws \Railt\Io\Exception\ExternalFileException
163
     */
164
    private function resolve(ContextInterface $context, RuleInterface $ast)
165
    {
166
        foreach ($this->builders as $builder) {
167
            if ($builder->match($ast)) {
168
                return $builder->reduce($context, $ast);
169
            }
170
        }
171
172
        $error = \sprintf('Unrecognized rule %s in (%s)', $ast->getName(), $ast);
173
        throw (new InternalException($error))->throwsIn($context->getFile(), $ast->getOffset());
174
    }
175
176
    /**
177
     * @param ContextInterface $ctx
178
     * @param \Generator $process
179
     * @return array|iterable<int, ContextInterface|mixed>
0 ignored issues
show
Documentation introduced by
The doc-type array|iterable<int, could not be parsed: Expected "|" or "end of type", but got "<" at position 14. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
180
     */
181
    private function run(ContextInterface $ctx, \Generator $process): array
182
    {
183
        while ($process->valid()) {
184
            try {
185
                $value = $process->current();
186
187
                switch (true) {
188
                    case $value instanceof ContextInterface:
189
                        $ctx = $value;
190
                        break;
191
192
                    case $value instanceof RuleInterface:
193
                        [$ctx, $value] = $this->reduce($ctx, $value);
194
                        break;
195
196
                    case $value instanceof ValueInterface:
197
                        $value = $ctx->declare($process->key(), $value);
0 ignored issues
show
Documentation introduced by
$value is of type object<Railt\SDL\IR\SymbolTable\ValueInterface>, but the function expects a null|object<Railt\SDL\IR\Type\TypeInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
198
                        break;
199
200
                    case \is_string($value):
201
                        $value = $ctx->fetch($value);
202
                        break;
203
                }
204
205
                $process->send($value);
206
            } catch (\Throwable $e) {
207
                $process->throw($e);
208
            }
209
        }
210
211
        return [$ctx, $process->getReturn()];
212
    }
213
}
214