Passed
Push — master ( e6bc2a...a7212f )
by Kirill
04:45
created

Compiler::autoload()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
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;
11
12
use Railt\Io\Exception\ExternalFileException;
13
use Railt\Io\File;
14
use Railt\Io\Readable;
15
use Railt\Reflection\Contracts\Definition;
16
use Railt\Reflection\Contracts\Document as DocumentInterface;
17
use Railt\Reflection\Contracts\Reflection as ReflectionInterface;
18
use Railt\Reflection\Dictionary\CallbackDictionary;
19
use Railt\Reflection\Document;
20
use Railt\Reflection\Reflection;
21
use Railt\SDL\Compiler\Backend;
22
use Railt\SDL\Compiler\Frontend;
23
use Railt\SDL\Compiler\Process;
24
use Railt\SDL\Exception\InternalErrorException;
25
use Railt\SDL\Exception\SyntaxException;
26
use Railt\SDL\Exception\TypeException;
27
use Railt\SDL\TypeLoader\TypeLoaderInterface;
28
29
/**
30
 * Class Compiler
31
 */
32
class Compiler implements CompilerInterface
33
{
34
    /**
35
     * @var ReflectionInterface
36
     */
37
    private $reflection;
38
39
    /**
40
     * @var Frontend
41
     */
42
    private $frontend;
43
44
    /**
45
     * @var Backend
46
     */
47
    private $backend;
48
49
    /**
50
     * @var Process
51
     */
52
    private $process;
53
54
    /**
55
     * @var array|TypeLoaderInterface[]
56
     */
57
    private $loaders = [];
58
59
    /**
60
     * Compiler constructor.
61
     */
62
    public function __construct()
63
    {
64
        $this->process = new Process();
65
        $this->frontend = new Frontend();
66
        $this->backend = new Backend($this->process);
67
        $this->reflection = new Reflection(new CallbackDictionary($this->typeLoader()));
68
    }
69
70
    /**
71
     * @param TypeLoaderInterface $loader
72
     */
73
    public function autoload(TypeLoaderInterface $loader): void
74
    {
75
        $this->loaders[] = $loader;
76
    }
77
78
    /**
79
     * @return \Closure
80
     */
81
    private function typeLoader(): \Closure
82
    {
83
        return function (string $type, Definition $from = null): ?DocumentInterface {
84
            foreach ($this->loaders as $loader) {
85
                if ($file = $loader->load($type, $from)) {
86
                    return $this->compile($file);
87
                }
88
            }
89
90
            return null;
91
        };
92
    }
93
94
    /**
95
     * @param Readable $schema
96
     * @return DocumentInterface
97
     * @throws ExternalFileException
98
     * @throws InternalErrorException
99
     */
100
    public function compile(Readable $schema): DocumentInterface
101
    {
102
        try {
103
            $document = $this->runCompiler($schema);
104
105
            $this->process->run();
106
107
            return $document;
108
        } catch (ExternalFileException $e) {
109
            $class = \get_class($e);
110
111
            /** @var ExternalFileException $exception */
112
            $exception = new $class($e->getMessage());
113
            $exception->throwsIn($schema, $e->getLine(), $e->getColumn());
114
115
            throw $exception;
116
        }
117
    }
118
119
    /**
120
     * @param Readable $schema
121
     * @return DocumentInterface
122
     * @throws InternalErrorException
123
     * @throws SyntaxException
124
     * @throws TypeException
125
     */
126
    private function runCompiler(Readable $schema): DocumentInterface
127
    {
128
        return $this->run(clone $this->reflection, $schema);
129
    }
130
131
    /**
132
     * @param ReflectionInterface $reflection
133
     * @param Readable $schema
134
     * @return DocumentInterface
135
     * @throws InternalErrorException
136
     * @throws SyntaxException
137
     * @throws TypeException
138
     */
139
    private function run(ReflectionInterface $reflection, Readable $schema): DocumentInterface
140
    {
141
        return $this->backend->each(
142
            $this->backend->context($this->document($reflection, $schema)),
143
            $this->frontend->exec($schema)
144
        );
145
    }
146
147
    /**
148
     * @param ReflectionInterface $reflection
149
     * @param Readable $schema
150
     * @return DocumentInterface
151
     */
152
    private function document(ReflectionInterface $reflection, Readable $schema): DocumentInterface
153
    {
154
        return new Document($reflection, $schema);
155
    }
156
157
    /**
158
     * @param Readable $schema
159
     * @return ReflectionInterface
160
     * @throws InternalErrorException
161
     * @throws SyntaxException
162
     * @throws TypeException
163
     */
164
    public function preload(Readable $schema): ReflectionInterface
165
    {
166
        $this->run($this->reflection, $schema);
167
168
        return $this->reflection;
169
    }
170
}
171