Interpreter::run()   F
last analyzed

Complexity

Conditions 42
Paths 235

Size

Total Lines 173
Code Lines 96

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 96
c 0
b 0
f 0
dl 0
loc 173
rs 2.8958
cc 42
nc 235
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace VLF;
4
5
/**
6
 * Интерпретатор AST VLF разметки
7
 */
8
class Interpreter
9
{
10
    static array $objects = []; // Массив созданных объектов (название => объект)
11
12
    static bool $throw_errors = true; // Выводить ли ошибки интерпретации
13
    static bool $allow_multimethods_calls = true; // Можно ли использовать многоуровневые вызовы методов (->method1->method2)
14
15
    private static array $imported_styles = [];
16
17
    /**
18
     * Интерпретирование синтаксического дерева
19
     * 
20
     * @param AST $tree - Абстрактное Синтаксическое Дерево (АСД), сгенерированное VLF Parser'ом
21
     * [@param array $parent = null] - нода-родитель дерева (системная настройка)
22
     * 
23
     * @return array - возвращает список созданных объектов
24
     */
25
    public static function run (AST $tree, Node $parent = null): array
26
    {
27
        foreach ($tree->getNodes () as $node)
28
        {
29
            switch ($node->type)
30
            {
31
                case OBJECT_DEFINITION:
32
                    $class = $node->args['class'];
33
                    $name  = $node->args['name'];
34
                    $args  = [];
35
36
                    if (isset (self::$objects[$name]))
37
                        break;
38
39
                    if (isset ($node->args['args']))
40
                    {
41
                        $args = $node->args['args'];
42
43
                        foreach ($args as $arg_id => $arg)
44
                            $args[$arg_id] = self::formatLine ($arg, self::$objects);
45
                    }
46
47
                    try
48
                    {
49
                        self::$objects[$name] = eval ("namespace VoidEngine; return new $class (". implode (', ', $args) .");");
50
51
                        try
52
                        {
53
                            self::$objects[$name]->name = $name;
54
                        }
55
56
                        catch (\Throwable $e) {}
57
                    }
58
59
                    catch (\Throwable $e)
60
                    {
61
                        if (self::$throw_errors)
62
                            throw new \Exception ('Interpeter couldn\'t create object "'. $class .'" with name "'. $name .'" at line "'. $node->line .'". Exception info:'. "\n\n". (string) $e, 0, $e);
63
                    }
64
                break;
65
66
                case PROPERTY_SET:
67
                    if ($parent !== null)
68
                    {
69
                        $name = $parent->args['name'];
70
71
                        $propertyName  = $node->args['name'];
72
                        $propertyValue = $node->args['value'];
73
                        $preset        = '';
74
75
                        if (preg_match ('/function \((.*)\) use \((.*)\)/', $propertyValue))
76
                        {
77
                            $use = substr ($propertyValue, strpos ($propertyValue, 'use'));
78
                            $use = $ouse = substr ($use, ($pos = strpos ($use, '(') + 1), strpos ($use, ')') - $pos);
79
                            $use = explode (' ', $use);
80
81
                            foreach ($use as $id => $useParam)  
82
                                if (isset (self::$objects[$useParam]) && $use[$id + 1][0] == '$')
83
                                {
84
                                    $fname = $use[$id + 1];
85
86
                                    if (substr ($fname, strlen ($fname) - 1) == ',')
87
                                        $fname = substr ($fname, 0, -1);
88
89
                                    $preset .= "$fname = $useParam; ";
90
91
                                    unset ($use[$id]);
92
                                }
93
94
                            $preset        = self::formatLine ($preset, self::$objects);
95
                            $propertyValue = self::formatLine (str_replace ($ouse, implode (' ', $use), $propertyValue), self::$objects);
96
                        }
97
98
                        else $propertyValue = self::formatLine ($propertyValue, self::$objects);
99
100
                        try
101
                        {
102
							if (strpos ($propertyName, '->') !== false && self::$allow_multimethods_calls)
103
                                eval ('namespace VoidEngine; '. $preset .' _c('. self::$objects[$name]->selector .')->'. $propertyName .' = '. $propertyValue .';');
104
                            
105
                            else self::$objects[$name]->$propertyName = eval ("namespace VoidEngine; $preset return $propertyValue;");
106
                        }
107
108
                        catch (\Throwable $e)
109
                        {
110
                            if (self::$throw_errors)
111
                                throw new \Exception ('Interpeter couldn\'t set property "'. $propertyName .'" with value "'. $propertyValue .'" at line "'. $node->line .'". Exception info:'. "\n\n". (string) $e, 0, $e);
112
                        }
113
                    }
114
115
                    elseif (self::$throw_errors)
116
                        throw new \Exception ('Setting property to an non-object at line "'. $node->line);
117
                break;
118
119
                case METHOD_CALL:
120
                    if ($parent !== null)
121
                    {
122
                        $name = $parent->args['name'];
123
124
                        $methodName = $node->args['name'];
125
                        $methodArgs = $node->args['args'];
126
127
                        foreach ($methodArgs as $arg_id => $arg)
128
                            $methodArgs[$arg_id] = self::formatLine ($arg, self::$objects);
129
130
                        try
131
                        {
132
                            if (strpos ($methodName, '->') !== false && self::$allow_multimethods_calls)
133
                                eval ('namespace VoidEngine; _c('. self::$objects[$name]->selector .')->'. $methodName .' ('. implode (', ', $methodArgs) .');');
134
135
                            elseif (sizeof ($methodArgs) > 0)
136
                                self::$objects[$name]->$methodName (...eval ('namespace VoidEngine; return ['. implode (', ', $methodArgs) .'];'));
137
138
                            else self::$objects[$name]->$methodName ();
139
                        }
140
141
                        catch (\Throwable $e)
142
                        {
143
                            if (self::$throw_errors)
144
                                throw new \Exception ('Interpeter couldn\'t call method "'. $methodName .'" with arguments '. json_encode ($methodArgs) .' at line "'. $node->line .'". Exception info:'. "\n\n". (string) $e, 0, $e);
145
                        }
146
                    }
147
148
                    elseif (self::$throw_errors)
149
                        throw new \Exception ('Calling method to an non-object at line "'. $node->line .'"');
150
                break;
151
152
                case STYLES_IMPORTING:
153
                    foreach ($node->args['imports'] as $style)
154
                    {
155
                        $path = eval ('namespace VoidEngine; return '. self::formatLine ($style, self::$objects) .';');
156
157
                        if (!file_exists ($path))
158
                        {
159
                            if (self::$throw_errors)
160
                                throw new \Exception ('Trying to import nonexistent style at line "'. $node->line .'"');
161
                        }
162
                        
163
                        elseif (!isset (self::$imported_styles[$path]))
164
                        {
165
                            \VLF\VST\Interpreter::run (\VLF\VST\Parser::parse (file_get_contents ($path)));
166
167
                            self::$imported_styles[$path] = true;
168
                        }
169
                    }
170
                break;
171
172
                case RUNTIME_EXECUTION:
173
                    eval (self::formatLine ($node->args['code'], self::$objects));
174
                break;
175
            }
176
177
            $nodes = $node->getNodes ();
178
179
            if ($node->type == OBJECT_DEFINITION)
180
            {
181
                if (isset (\VLF\VST\Interpreter::$default_styles[$node->args['class']]))
182
                    $nodes = array_merge ($nodes, \VLF\VST\Interpreter::$default_styles[$node->args['class']]);
183
184
                if (isset ($node->args['styles']))
185
                    foreach ($node->args['styles'] as $style)
186
                        if (isset (\VLF\VST\Interpreter::$styles[$style]))
187
                            $nodes = array_merge ($nodes, \VLF\VST\Interpreter::$styles[$style]);
188
189
                        elseif (self::$throw_errors)
190
                            throw new \Exception ('Trying to set undefined style "'. $style .'" to object at line "'. $node->line .'"');
191
            }
192
193
            self::$objects = self::run (new AST (array_map (
194
                fn ($node) => $node->export (), $nodes)), $node);
195
        }
196
197
        return self::$objects;
198
    }
199
200
    /**
201
     * Форматирование строки
202
     * 
203
     * @param string $line - строка для форматирования
204
     * [@param array $objects = []] - список объектов, которые будут участвовать в форматировании
205
     * 
206
     * @return string - возвращает форматированную строку
207
     */
208
    public static function formatLine (string $line, array $objects = []): string
209
    {
210
        if (sizeof ($objects) > 0)
211
        {
212
            $len     = strlen ($line);
213
            $newLine = '';
214
215
            $replacement = array_map (function ($object)
216
            {
217
                return \VoidEngine\Components::exists ($object->selector) !== false ? 
218
                    '\VoidEngine\_c('. $object->selector .')' :
219
                    'unserialize (\''. serialize ($object) .'\')';
220
            }, $objects);
221
222
            $replacement = array_map (function ($name)
223
            {
224
                return strlen ($name = trim ($name)) + substr_count ($name, '_');
225
            }, $omap = array_flip ($replacement));
0 ignored issues
show
Bug introduced by
It seems like $omap = array_flip($replacement) can also be of type null; however, parameter $arr1 of array_map() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

225
            }, /** @scrutinizer ignore-type */ $omap = array_flip ($replacement));
Loading history...
226
227
            arsort ($replacement);
228
229
            $nReplacement = [];
230
231
            foreach ($replacement as $replaceTo => $nLn)
232
                $nReplacement[$omap[$replaceTo]] = $replaceTo;
233
234
            $replacement = $nReplacement;
235
            $blacklist   = array_flip (['\'', '"', '$']);
236
237
            for ($i = 0; $i < $len; ++$i)
238
            {
239
                $replaced = false;
240
241
                foreach ($replacement as $name => $replaceAt)
242
                    if (substr ($line, $i, ($l = strlen ($name))) == $name && !isset ($blacklist[$line[$i - 1]]))
243
                    {
244
                        $newLine .= $replaceAt;
245
246
                        $i += $l - 1;
247
                        $replaced = true;
248
249
                        break;
250
                    }
251
252
                if (!$replaced)
253
                    $newLine .= $line[$i];
254
            }
255
256
            $line = $newLine;
257
        }
258
259
        return $line;
260
    }
261
	
262
	public static function clear (): void
263
	{
264
		self::$objects = [];
265
		self::$imported_styles = [];
266
	}
267
}
268