loom

Форк
0
/
ScriptAnalyzer.cpp 
397 строк · 15.5 Кб
1
/*
2
MIT License
3

4
Copyright (c) 2022 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,
5

6
https://bmstu.codes/lsx/simodo/loom
7
*/
8

9
#include "ScriptAnalyzer.h"
10
#include "simodo/interpret/AnalyzeException.h"
11
#include "simodo/inout/convert/functions.h"
12
#include "simodo/inout/format/fmt.h"
13

14
#include "simodo/interpret/StackOfNames.h"
15

16
#include <cassert>
17

18
namespace simodo::interpret
19
{
20
    namespace 
21
    {
22
        SemanticDataCollector_null null_collector;
23
    }
24

25
    ScriptAnalyzer::ScriptAnalyzer(ModuleManagement_interface & module_management)
26
        : ScriptSemantics_abstract(module_management)
27
        , _collector(null_collector)
28
    {
29
    }
30

31
    ScriptAnalyzer::ScriptAnalyzer(ModuleManagement_interface & module_management, SemanticDataCollector_interface & collector)
32
        : ScriptSemantics_abstract(module_management)
33
        , _collector(collector)
34
    {
35
    }
36

37
    bool ScriptAnalyzer::checkInterpretType(InterpretType interpret_type) const
38
    {
39
        return interpret_type == InterpretType::Analyzer;
40
    }
41

42
    InterpretState ScriptAnalyzer::performOperation(const ast::Node & op)
43
    {
44
        try
45
        {
46
            return ScriptSemantics_abstract::performOperation(op);
47
        }
48
        catch(const AnalyzeException & e)
49
        {
50
            inter().reporter().reportError(e.location(), e.what());
51
            _number_of_mistakes ++;
52
        }
53
        catch(const bormental::DrBormental & e)
54
        {
55
            const ast::Node * node_p = inter().lookupOperationNode();
56
            inout::Location   loc   = node_p 
57
                                    ? node_p->token().makeLocation(inter().files()) 
58
                                    : inout::null_location;
59

60
            inter().reporter().reportWarning(
61
                                    loc, 
62
                                    inout::fmt("Danger of error '%1'").arg(e.what()));
63
        }
64
        catch(const std::exception & e)
65
        {
66
            const ast::Node * node_p = inter().lookupOperationNode();
67
            inout::Location   loc   = node_p 
68
                                    ? node_p->token().makeLocation(inter().files()) 
69
                                    : inout::null_location;
70

71
            inter().reporter().reportWarning(
72
                                    loc, 
73
                                    inout::fmt("Danger of error '%1'").arg(e.what()));
74
        }
75

76
        if (_number_of_mistakes >= MAX_NUMBER_OF_MISTAKES) 
77
            throw AnalyzeException("ScriptAnalyzer::performOperation", 
78
                                    op.token().makeLocation(inter().files()), 
79
                                    inout::fmt("The number of errors has exceeded the allowable limit"));
80
        
81
        return InterpretState::Flow;
82
    }
83

84
    InterpretState ScriptAnalyzer::before_start()
85
    {
86
        return ScriptSemantics_abstract::before_start();
87
    }
88

89
    InterpretState ScriptAnalyzer::before_finish(InterpretState state)
90
    {
91
        return ScriptSemantics_abstract::before_finish(state);
92
    }
93

94
    /// \note Код оставил закомментированным в надежде на оптимизацию в будущем
95
    // InterpretState ScriptAnalyzer::executePushVariable(const inout::Token & variable_name)
96
    // {
97
    //     InterpretState state           = ScriptSemantics_abstract::executePushVariable(variable_name);
98
    //     const variable::Variable & var = inter().table().variable(inter().table().top());
99

100
    //     if (!var.origin().name().isError())
101
    //         _collector.collectNameUsed(var.origin(), variable_name.location());
102

103
    //     return state;
104
    // }
105

106
    /// \note Код оставил закомментированным в надежде на оптимизацию в будущем
107
    // InterpretState ScriptAnalyzer::executeObjectElement(const ast::Node & op)
108
    // {
109

110
    // }
111

112
    /// \note Код оставил закомментированным в надежде на оптимизацию в будущем
113
    // InterpretState ScriptAnalyzer::executeFunctionCall(const ast::Node & op)
114
    // {
115
    //     return ScriptSemantics_abstract::executeFunctionCall(op);
116
    // }
117

118
    InterpretState ScriptAnalyzer::executePrint(bool /*need_detailed_report*/)
119
    {
120
        inter().stack().pop();
121

122
        return InterpretState::Flow;
123
    }
124

125
    /// \note Код оставил закомментированным в надежде на оптимизацию в будущем
126
    // InterpretState ScriptAnalyzer::executeBlock(bool is_beginning_of_block, const ast::Node & op)
127
    // {
128
    //     if (is_beginning_of_block)
129
    //         ;
130
    //         // _collector.collectFrameBegin(op.operation_symbol().location, FrameType::CodeBlock);
131
    //     else {
132
    //         /// \todo Добавить сбор информации о локальных переменных
133

134
    //         // _collector.collectFrameEnd(op.operation_symbol().location);
135
    //     }
136

137
    //     return ScriptSemantics_abstract::executeBlock(is_beginning_of_block, op);
138
    // }
139

140
    InterpretState ScriptAnalyzer::executeImport(const inout::Token & path)
141
    {
142
        InterpretState state = ScriptSemantics_abstract::executeImport(path);
143

144
        notifyDeclared(inter().stack().variable(inter().stack().top()));
145

146
        return state;
147
    }
148

149
    InterpretState ScriptAnalyzer::executeContract(const ast::Node & op)
150
    {
151
        InterpretState state = ScriptSemantics_abstract::executeContract(op);
152

153
        notifyDeclared(inter().stack().variable(inter().stack().top()));
154

155
        return state;
156
    }
157

158
    /// \note Код оставил закомментированным в надежде на оптимизацию в будущем
159
    // InterpretState ScriptAnalyzer::executeDeclaration(const ast::Node & op)
160
    // {
161
    //     InterpretState state = ScriptSemantics_abstract::executeDeclaration(op);
162

163
    //     // notifyDeclared(inter().table().variable(inter().table().top()));
164

165
    //     // assert(stack().index_over_top() > 0);
166

167
    //     // const variable::Variable & v = stack().variable(stack().top());
168
    //     // _declared.back() = v.name();
169

170
    //     return state;
171
    // }
172

173
    InterpretState ScriptAnalyzer::executeDeclarationCompletion()
174
    {
175
        InterpretState state = ScriptSemantics_abstract::executeDeclarationCompletion();
176

177
        return state;
178
    }
179

180
    InterpretState ScriptAnalyzer::executeFunctionDefinitionEnd(const ast::Node & op)
181
    {
182
        name_index_t               function_index    = stack().top();
183
        const variable::Variable & function_variable = stack().variable(function_index).origin();
184

185
        if (function_variable.type() == variable::ValueType::Null)
186
            return InterpretState::Flow;
187

188
        assert(function_variable.type() == variable::ValueType::Function);
189

190
        const std::shared_ptr<variable::Object> function_object = function_variable.value().getObject();
191
        assert(function_object->variables().size() >= 2);
192

193
        _functions_for_analyze.push_back(function_variable);
194
        
195
        return ScriptSemantics_abstract::executeFunctionDefinitionEnd(op);
196
    }
197

198
    /// \note Код оставил закомментированным в надежде на оптимизацию в будущем
199
    // InterpretState ScriptAnalyzer::executePostAssignment(const ast::Node & op)
200
    // {
201
    //     InterpretState state = ScriptSemantics_abstract::executePostAssignment(op);
202

203
    //     // checkFunctions();
204

205
    //     return state;
206
    // }
207

208
    InterpretState ScriptAnalyzer::executeCheckState(const ast::Node & op)
209
    {
210
        checkFunctions();
211

212
        return ScriptSemantics_abstract::executeCheckState(op);
213
    }
214

215
    /// \note Код оставил закомментированным в надежде на оптимизацию в будущем
216
    // InterpretState ScriptAnalyzer::executeAssignment(const ast::Node & op)
217
    // {
218
    //     InterpretState state = ScriptSemantics_abstract::executeAssignment(op);
219

220
    //     name_index_t               var_index = stack().top();
221
    //     const variable::Variable & var       = stack().variable(var_index).origin();
222

223
    //     if (var.origin().value().isFunction()) {
224
    //         eraseFunction(var.origin());
225
    //         _functions_for_analyze.push_back(var.origin());
226
    //     }
227

228
    //     return state;
229
    // }
230

231
    InterpretState ScriptAnalyzer::executeConditional(ScriptOperationCode code, const ast::Node & op_true, const ast::Node * op_false)
232
    {
233
        name_index_t       condition_index    = stack().top();
234
        variable::Variable condition_variable = stack().variable(condition_index).origin();
235

236
        try
237
        {
238
            if (condition_variable.type() != variable::ValueType::Bool
239
             && !condition_variable.value().isError()) {
240
                condition_variable = inter().expr().convertVariable(condition_variable, variable::ValueType::Bool);
241

242
                if (condition_variable.value().isError())
243
                    return InterpretState::Flow;
244
            }
245

246
            inter().stack().pop();
247

248
            inter().addFlowLayer(op_true, 
249
                    [this, op_false](const FlowLayerInfo &) 
250
                    {
251
                        if (op_false)
252
                            inter().addFlowLayer(*op_false);
253
                    });
254

255
            return InterpretState::Flow;
256
        }
257
        catch(...)
258
        {
259
            if (code == ScriptOperationCode::Ternary)
260
                inter().stack().pop(inter().stack().top() - condition_index);
261

262
            throw;
263
        }
264

265
        return InterpretState::Flow;
266
    }
267

268
    void ScriptAnalyzer::notifyDeclared(const variable::Variable & var)
269
    {
270
        _collector.collectNameDeclared(var);
271
    }
272

273
    void ScriptAnalyzer::notifyInitiated(const variable::Variable & var)
274
    {
275
        _collector.collectNameInitiated(var);
276

277
        if (var.origin().value().isFunction()) {
278
            eraseFunction(var.origin());
279
            _functions_for_analyze.push_back(var.origin());
280
        }
281
    }
282

283
    void ScriptAnalyzer::notifyNameUsed(const variable::Variable & variable, const inout::TokenLocation & location)
284
    {
285
        _collector.collectNameUsed(variable, location);
286
    }
287

288
    void ScriptAnalyzer::notifyRef(const inout::Token & token, const std::u16string & ref)
289
    {
290
        _collector.collectRef(token, ref);
291
    }
292

293
    void ScriptAnalyzer::notifyBeforeFunctionCalling(const variable::Variable & function)
294
    {
295
        eraseFunction(function);
296
    }
297

298
    void ScriptAnalyzer::notifyRemoteFunctionLaunch(const inout::TokenLocation & location)
299
    {
300
        _collector.collectRemoteFunctionLaunch(location);
301
    }
302

303
    void ScriptAnalyzer::checkFunctions()
304
    {
305
        while(!_functions_for_analyze.empty()) {
306
            variable::Variable function_variable = _functions_for_analyze.back();
307
            _functions_for_analyze.pop_back();
308

309
            assert(function_variable.type() == variable::ValueType::Function);
310

311
            std::shared_ptr<variable::Object> function_object = function_variable.value().getObject();
312
            assert(function_object->variables().size() >= 2);
313

314
            stack().push(function_variable);
315

316
            variable::Variable &         function_core     = function_object->getVariableByIndex_mutable(0);
317
            variable::InternalFunction & internal_function = 
318
                    const_cast<variable::InternalFunction &>(std::get<variable::InternalFunction>(function_core.value().variant()));
319
            boundary_index_t             boundary_index    = stack().startBoundary(BoundScope::Local, BOUNDARY_MARK_FUNCTION_FRAME);
320

321
            /// \note Тут можно пересобирать self, чтобы были видны все члены модуля. 
322
            /// Но это не совсем корректно (и почему-то не работает). 
323
            /// Оставил этот код закомментированным, пока.
324
            // internal_function.parent = inter().expr().makeSelf();
325

326
            for(size_t i=2; i < function_object->variables().size(); ++i) {
327
                const variable::Variable & v = function_object->variables()[i];
328
                inter().stack().push(v);
329
            }
330

331
            const variable::Value & tethered_value = function_variable.origin().spec().object()->find(variable::SPEC_TETHERED);
332

333
            if (tethered_value.isBool() && tethered_value.getBool()) {
334
                boundary_index_t module_bound_index = stack().findNearestMarkedBoundary(BOUNDARY_MARK_MODULE_FRAME, BoundScope::Global);
335
                assert(module_bound_index != UNDEFINED_BOUNDARY_INDEX);  
336
                const auto [begin,end] = stack().boundaryLowAndTop(module_bound_index);
337
                for(name_index_t i = begin; i < end; ++i)
338
                    stack().pushRef(stack().variable(i), function_variable.location());
339
            }
340
            else
341
                for(const variable::Variable & closure : internal_function.closures.variables()) {
342
                    if (closure.name() == SELF_VARIABLE_NAME) {
343
                        /// \note `self` обрабатывается "хардкодом" в executePushVariable
344
                        /// Здесь код пока оставил закомментированным
345
                        // assert(internal_function.parent);
346
                        // variable::Variable self = { SELF_VARIABLE_NAME, internal_function.parent, closure.location() };
347
                        // inter().stack().push(self);
348
                        // notifyDeclared(self);
349
                    }
350
                    else
351
                        inter().stack().push(closure);
352

353
                    // notifyNameUsed(closure, closure.location());
354
                }
355

356
            variable::Variable   declared_return_variable = function_object->variables()[1];
357
            variable::ValueType  declared_return_type     = declared_return_variable.type();
358
            inout::TokenLocation function_location        = declared_return_variable.location();
359

360
            inter().stack().setBoundaryToGlobal(boundary_index);
361
            inter().expr().setReturnValue({variable::ValueType::Null});
362
            inter().addFlowLayer(
363
                    *internal_function.code, 
364
                    boundary_index,
365
                    [this, boundary_index, declared_return_type, function_location](const FlowLayerInfo & flow) 
366
                    {
367
                        inter().stack().clearBoundary(boundary_index);
368
                        inter().stack().pop(); 
369

370
                        if (flow.error_sign) 
371
                            return;
372

373
                        if (declared_return_type != variable::ValueType::Null
374
                         && inter().expr().return_value().isNull() && !inter().expr().return_value().isError())
375
                            throw AnalyzeException("ScriptSemantics_abstract::executeReturn", 
376
                                                    function_location.makeLocation(inter().files()), 
377
                                                    inout::fmt("The function must return a value"));
378
                    });
379
        }
380
    }
381

382
    void ScriptAnalyzer::eraseFunction(const variable::Variable & function)
383
    {
384
        if (!function.value().isFunction())
385
            return;
386

387
        auto it = std::find_if(_functions_for_analyze.begin(), _functions_for_analyze.end(),
388
                            [function](const variable::Variable & f)
389
                            {
390
                                return f.value().isFunction() && f.value().getFunction().get() == function.value().getFunction().get();
391
                            });
392

393
        if (it != _functions_for_analyze.end())
394
            _functions_for_analyze.erase(it);
395
    }
396

397
}

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.