Test Failed
Push — master ( ecd78b...d05c81 )
by Kirill
02:43
created

Frontend::collect()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 16
rs 9.7333
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 Psr\Log\LoggerAwareInterface;
13
use Psr\Log\LoggerAwareTrait;
14
use Psr\Log\LoggerInterface;
15
use Railt\Io\Readable;
16
use Railt\Parser\Ast\NodeInterface;
17
use Railt\Parser\Ast\RuleInterface;
18
use Railt\Parser\Exception\UnexpectedTokenException;
19
use Railt\Parser\Exception\UnrecognizedTokenException;
20
use Railt\SDL\Exception\SyntaxException;
21
use Railt\SDL\Frontend\AST\ProvidesOpcode;
22
use Railt\SDL\Frontend\IR\Opcode;
23
use Railt\SDL\Frontend\IR\Opcode\OpenOpcode;
24
use Railt\SDL\Frontend\IR\OpcodeHeap;
25
use Railt\SDL\Frontend\IR\OpcodeInterface;
26
27
/**
28
 * Class Frontend
29
 */
30
class Frontend implements LoggerAwareInterface
31
{
32
    use LoggerAwareTrait;
33
34
    /**
35
     * @var Parser
36
     */
37
    private $parser;
38
39
    /**
40
     * @var Analyzer
41
     */
42
    private $analyzer;
43
44
    /**
45
     * Frontend constructor.
46
     */
47
    public function __construct()
48
    {
49
        $this->parser  = new Parser();
50
        $this->analyzer = new Analyzer();
51
    }
52
53
    /**
54
     * @param Readable $file
55
     * @return iterable|OpcodeInterface[]
56
     * @throws SyntaxException
57
     */
58
    public function load(Readable $file): iterable
59
    {
60
        $context = new Context();
61
62
        $opcodes = $this->collect($file, $context);
63
        $opcodes = $this->analyzer->analyze($opcodes);
0 ignored issues
show
Documentation introduced by
$opcodes is of type object<Railt\SDL\Frontend\IR\OpcodeHeap>, but the function expects a object<Railt\SDL\Frontend\iterable>.

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...
64
65
        return $opcodes;
66
    }
67
68
    /**
69
     * @param LoggerInterface $logger
70
     * @return Frontend
71
     */
72
    public function setLogger(LoggerInterface $logger): Frontend
73
    {
74
        $this->logger = $logger;
75
        $this->analyzer->setLogger($logger);
76
77
        return $this;
78
    }
79
80
    /**
81
     * @param Readable $file
82
     * @param Context $context
83
     * @return iterable|OpcodeInterface[]
84
     * @throws SyntaxException
85
     */
86
    private function collect(Readable $file, Context $context): iterable
87
    {
88
        $heap = new OpcodeHeap($context);
89
90
        $heap->add(new OpenOpcode($file), $file);
91
92
        $iterator = $this->bypass($this->parse($file), $context);
93
94
        while ($iterator->valid()) {
95
            [$ast, $opcode] = [$iterator->key(), $iterator->current()];
0 ignored issues
show
Bug introduced by
The variable $ast 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 $opcode 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...
96
97
            $iterator->send($heap->add($opcode, $file, $ast->getOffset()));
98
        }
99
100
        return $heap;
101
    }
102
103
    /**
104
     * @param NodeInterface $node
105
     * @param Context $context
106
     * @return iterable|Opcode[]|\Generator
107
     */
108
    private function bypass(NodeInterface $node, Context $context): \Generator
109
    {
110
        foreach ($node as $child) {
0 ignored issues
show
Bug introduced by
The expression $node of type object<Railt\Parser\Ast\NodeInterface> is not traversable.
Loading history...
111
            $current = $context->current();
112
113
            if ($child instanceof ProvidesOpcode) {
114
                yield from $this->extract($child, $context);
115
            }
116
117
            if ($child instanceof RuleInterface) {
118
                yield from $this->bypass($child, $context);
119
            }
120
121
            if ($current !== $context->current()) {
122
                $context->close();
123
            }
124
        }
125
    }
126
127
    /**
128
     * @param ProvidesOpcode $provider
129
     * @param Context $context
130
     * @return \Generator|OpcodeInterface[]
131
     */
132
    private function extract(ProvidesOpcode $provider, Context $context): \Generator
133
    {
134
        /** @var \Generator $iterator */
135
        $iterator = $provider->getOpcodes($context);
136
137
        while ($iterator->valid()) {
138
            $key = $iterator->key();
139
140
            $iterator->send(yield $key instanceof RuleInterface ? $key : $provider => $iterator->current());
141
        }
142
    }
143
144
    /**
145
     * @param Readable $file
146
     * @return RuleInterface
147
     * @throws SyntaxException
148
     */
149
    private function parse(Readable $file): RuleInterface
150
    {
151
        try {
152
            return $this->parser->parse($file);
153
        } catch (UnexpectedTokenException | UnrecognizedTokenException $e) {
154
            $error = new SyntaxException($e->getMessage(), $e->getCode());
155
            $error->throwsIn($file, $e->getLine(), $e->getColumn());
156
157
            throw $error;
158
        }
159
    }
160
}
161