4
Copyright (c) 2021 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,
6
https://bmstu.codes/lsx/simodo
9
#include "simodo/interpret/Interpret.h"
10
#include "simodo/interpret/AnalyzeException.h"
12
#include "simodo/loom/Loom_interface.h"
13
#include "simodo/bormental/DrBormental.h"
14
#include "simodo/inout/convert/functions.h"
15
#include "simodo/inout/format/fmt.h"
22
using namespace simodo;
24
void moveValue(variable::Variable & to, const variable::Variable & from)
26
if (to.type() == variable::ValueType::Ref) {
27
variable::VariableRef ref = to.value().getRef();
28
ref.setValue(from.origin().value());
31
to.setValue(from.origin().value());
34
void copyValue(variable::Variable & to, const variable::Variable & from)
36
if (to.type() == variable::ValueType::Ref) {
37
variable::VariableRef ref = to.value().getRef();
38
ref.setValue(from.origin().value().copy());
41
to.setValue(from.origin().value().copy());
46
namespace simodo::interpret
48
void Interpret::assignVariable(variable::Variable & target, const variable::Variable & source, bool need_copy)
50
// Правила присвоения и перемещения:
51
// 1. Если в качестве источника (правой части присваивания) выступает переменная с признаком ошибки,
52
// то распространяем ошибку на целевую переменную.
53
// 2. Если в качестве целевой переменной выступает null, значит она не задана можно тупо присвоить.
54
// 3. Если в качестве источника выступает ссылка, это значит это ссылка на уже объявленную
55
// переменную и мы должны скопировать.
56
// 4. Если лежит не ссылка, то это значит источником является временная переменная, которую
57
// можно частично перенести.
58
// 5. Если источником является ссылка с пометкой SPEC_REFERENCE, значит нужно целевую
59
// переменную сделать ссылкой на источник.
61
variable::Variable & target_origin = target.origin();
62
const variable::Variable & source_origin = source.origin();
65
if (source_origin.value().isError()) {
66
target_origin.setValue(source_origin.value());
71
if (target_origin.type() == variable::ValueType::Null) {
72
if (source_origin.type() != variable::ValueType::Null)
73
target_origin.setValue(source_origin.value());
78
if (source.value().isRef())
81
// 4. (учитывается за счёт задания need_copy = false в параметрах функции по умолчанию)
84
if (source.value().isRef()) //-V581
86
const variable::Value & ref_value = source.spec().find(variable::SPEC_REFERENCE);
87
if (ref_value.isBool() && ref_value.getBool()) {
88
target_origin.setValue(source_origin.value());
93
switch(target_origin.type())
95
case variable::ValueType::Array:
97
copyValue(target, source_origin);
99
moveValue(target, source_origin);
101
case variable::ValueType::Object:
102
assignObject(target_origin, source_origin, need_copy);
104
case variable::ValueType::Function:
105
target_origin.setValue(source_origin.value());
106
target_origin.setSpec(source_origin.spec());
110
copyValue(target, convertVariable(source,target_origin.type(), need_copy));
112
moveValue(target, convertVariable(source,target_origin.type(), need_copy));
117
void Interpret::assignArray(variable::Variable & /*target_origin*/, const variable::Variable & /*source_origin*/,
120
throw bormental::DrBormental("OperationEngine::assignArray", inout::fmt("Unsupported"));
123
void Interpret::assignObject(variable::Variable & target_origin, const variable::Variable & source_origin,
126
assert(target_origin.type() != variable::ValueType::Ref);
127
assert(source_origin.type() != variable::ValueType::Ref);
129
if (source_origin.type() != variable::ValueType::Object) {
130
assignObject(target_origin, convertVariable(source_origin,variable::ValueType::Object), need_copy);
134
if (target_origin.type() == variable::ValueType::Null) {
135
target_origin.setValue(source_origin.value());
139
if (target_origin.type() != variable::ValueType::Object)
140
throw bormental::DrBormental("assignObject",
141
inout::fmt("Incorrect internal type of variable '%1'")
142
.arg(target_origin.name()));
144
// С этого момента левая и правая части являются объектами
146
const std::shared_ptr<variable::Object> rvalue_record = source_origin.value().getObject();
147
std::shared_ptr<variable::Object> lvalue_record = target_origin.value().getObject();
149
for(const variable::Variable & rv : rvalue_record->variables()) {
150
variable::Variable & lv = lvalue_record->getVariableByName_mutable(rv.name());
151
if (!lv.name().empty()) {
152
assignVariable(lv.origin(), rv.origin(), need_copy);
156
/// \todo Нужно решить как лучше быть:
157
/// 1. Добавлять недостающие элементы (как в JS/TS)
158
/// 2. Заставлять пользователя самому добавлять не нужные ему элементы (иначе - ошибка), а потом затирать
159
/// 3. Игнорировать ситуацию
161
// Пробуем найти сеттер
162
const variable::Variable & setter = lvalue_record->getVariableByName(u"set_" + rv.name());
164
if (!setter.name().empty() && setter.origin().type() == variable::ValueType::Function)
165
throw AnalyzeException( "OperationEngine::assignObject",
166
rv.location().makeLocation(files()),
167
inout::fmt("Using a setter '%1' when assigning an object is not supported")
170
throw AnalyzeException( "OperationEngine::assignObject",
171
rv.location().makeLocation(files()),
172
inout::fmt("Element '%1' was not found in the target structure")
177
variable::Variable Interpret::convertVariable(const variable::Variable & var, variable::ValueType type,
178
bool need_copy) const
180
if (var.origin().type() == type) {
181
/// \todo Нужно оптимизировать!?
183
return var.copyVariable();
190
case variable::ValueType::Null:
192
case variable::ValueType::String:
193
if (var.origin().variant().index() == std::variant_npos)
194
return { var.origin().name(), type, var.location(), var.spec() };
195
return { {}, toString(var.origin().value()), var.location(), {} };
196
case variable::ValueType::Float:
197
if (var.origin().type() == variable::ValueType::Int) {
198
if (var.origin().variant().index() == std::variant_npos)
199
return { var.origin().name(), type, var.location(), var.spec() };
202
static_cast<double>(std::get<int64_t>(var.origin().value().variant())),
207
case variable::ValueType::Array:
208
if (var.origin().type() == variable::ValueType::Null)
210
std::make_shared<variable::Array>(),
213
case variable::ValueType::Function:
219
std::u16string variable_name = var.origin().name();
221
if (variable_name.empty())
222
throw AnalyzeException("Interpret::convertVariable",
223
var.location().makeLocation(files()),
224
inout::fmt("Invalid type conversion from %1 to %2 of internal variable")
225
.arg(getValueTypeName(var.origin().type()))
226
.arg(getValueTypeName(type)));
228
throw AnalyzeException("Interpret::convertVariable",
229
var.location().makeLocation(files()),
230
inout::fmt("Invalid type conversion from %1 to %2 of variable '%3'")
231
.arg(getValueTypeName(var.origin().type()))
232
.arg(getValueTypeName(type))
233
.arg(variable_name));
236
const variable::Variable & Interpret::getContextFunction() const
238
static variable::Variable null {};
240
boundary_index_t function_bound = stack().findNearestMarkedBoundary(BOUNDARY_MARK_FUNCTION_FRAME,BoundScope::Global);
241
if (function_bound == UNDEFINED_BOUNDARY_INDEX)
244
const auto [begin,end] = stack().boundaryLowAndTop(function_bound);
245
name_index_t function_index = begin-1;
246
const variable::Variable & function_variable = stack().variable(function_index).origin();
248
assert(function_variable.type() == variable::ValueType::Function);
250
return function_variable.origin();
253
std::shared_ptr<variable::Object> Interpret::makeSelf()
255
boundary_index_t module_bound = stack().findNearestMarkedBoundary(BOUNDARY_MARK_MODULE_FRAME, BoundScope::Global);
256
if (module_bound == UNDEFINED_BOUNDARY_INDEX)
259
auto [begin_index, end_index] = stack().boundaryLowAndTop(module_bound);
260
variable::VariableSet_t vars;
262
for(name_index_t i = begin_index; i < end_index; ++i) {
263
variable::Variable & v = stack().variable(i);
267
return std::make_shared<variable::Object>(vars);
270
void Interpret::callPreparedFunction(
271
Interpret_interface * p_fiber,
272
boundary_index_t boundary_index,
273
Notification_interface & notify,
280
if(p_fiber != this) {
282
[[maybe_unused]] Interpret * p_inter = static_cast<Interpret *>(p_fiber);
284
auto [begin, end] { stack().boundaryLowAndTop(boundary_index) };
285
name_index_t function_index { begin - 1 };
286
const variable::Variable & function { stack().variable(function_index) };
288
name_index_t another_function_index = p_fiber->stack().find(function.origin().name());
290
assert(another_function_index != UNDEFINED_NAME_INDEX);
292
p_fiber->stack().push({u"", p_fiber->stack().variable(another_function_index).makeReference(), function.location()});
293
boundary_index_t another_boundary_index = p_fiber->stack().startBoundary();
294
for(name_index_t i=begin; i < end; ++i) {
295
variable::Variable & v = stack().variable(i);
296
if (v.origin().value().isFunction())
297
p_fiber->stack().push(v.origin());
299
p_fiber->stack().push(v.copyVariable());
302
stack().clearBoundary(boundary_index);
304
stack().push(variable::null_variable()); // Имитируем возвращаемое значение для нашей нити
306
boundary_index = another_boundary_index;
307
remote_calling = true;
310
remote_calling = false;
312
auto [begin, end] { p_fiber->stack().boundaryLowAndTop(boundary_index) };
313
name_index_t function_index { begin - 1 };
314
const variable::Variable & function { p_fiber->stack().variable(function_index) };
316
variable::FunctionWrapper func = function.origin();
317
variable::ValueType declared_return_type = func.getReturnDeclarationVariable().type();
318
inout::TokenLocation function_location = func.function_variable().location();
320
p_fiber->expr().callPreparedFunction(
324
[p_fiber, boundary_index, declared_return_type, function_location, remote_calling](const FlowLayerInfo & flow)
326
p_fiber->stack().clearBoundary(boundary_index);
327
p_fiber->stack().pop();
329
if (!remote_calling) {
330
/// \todo Наследие настроено на то, что вызывающая сторона очищает возвращаемое значение
331
/// со своего стека. Вызов функции в другом потоке приводит к тому, что возвращаемое значение
332
/// остаётся на стеке другого потока. Пока не понятно как лучше сделать.
333
/// На данный момент просто не выкладываю на стек, если вызов в другом потоке.
334
p_fiber->stack().push({u"", p_fiber->expr().return_value(), flow.code_flow.token().location()});
340
if (declared_return_type != variable::ValueType::Null
341
&& p_fiber->expr().return_value().isNull() && !p_fiber->expr().return_value().isError())
342
throw AnalyzeException("Interpret::callPreparedFunction",
343
function_location.makeLocation(p_fiber->files()),
344
inout::fmt("The function must return a value"));
348
void Interpret::callPreparedFunction(
349
boundary_index_t boundary_index,
350
Notification_interface & notify,
352
const std::function<void(const FlowLayerInfo &)> callback)
355
auto [begin, end] { stack().boundaryLowAndTop(boundary_index) };
356
name_index_t function_index { begin - 1 };
357
const variable::Variable & function { stack().variable(function_index) };
358
const variable::Variable & function_origin { function.origin() };
359
variable::VariableSetWrapper_mutable args { stack().makeVariableSetWrapper() };
360
variable::FunctionWrapper func { function_origin };
361
variable::VariableSetWrapper decl { func.getArgumentDeclarationVariables() };
363
if (args.size() != decl.size())
364
throw AnalyzeException("Interpret::callPreparedFunction",
365
function.location().makeLocation(files()),
366
inout::fmt("The function call '%1' contains an incorrect number of arguments")
367
.arg(function.name()));
369
for(size_t i=0; i < decl.size(); ++i) {
370
if (args[i].origin().value().isError() && type() != InterpretType::Preview)
372
else if (args[i].origin().type() != decl[i].type() && decl[i].type() != variable::ValueType::Null)
373
args[i] = convertVariable(args[i], decl[i].type());
375
args[i].setName(decl[i].name());
378
if (func.getCallingAddressVariable().type() == variable::ValueType::ExtFunction) {
379
variable::Value return_value;
382
return_value = func.invoke(args);
384
return_value = func.getReturnDeclarationVariable().value();
386
stack().clearBoundary(boundary_index);
388
stack().push({u"", return_value, function.location()});
392
assert(func.getCallingAddressVariable().type() == variable::ValueType::IntFunction);
394
_return_value = {variable::ValueType::Null};
397
const variable::InternalFunction & internal_function
398
= std::get<variable::InternalFunction>(func.getCallingAddressVariable().value().variant());
400
const variable::Value & tethered_value = function_origin.spec().object()->find(variable::SPEC_TETHERED);
402
/// \attention Этот фрагмент необходимо целиком переписать!!!
404
if (tethered_value.isBool() && tethered_value.getBool()) {
405
boundary_index_t module_bound_index = stack().findNearestMarkedBoundary(BOUNDARY_MARK_MODULE_FRAME, BoundScope::Global);
406
assert(module_bound_index != UNDEFINED_BOUNDARY_INDEX);
407
const auto [begin,end] = stack().boundaryLowAndTop(module_bound_index);
408
for(name_index_t i = begin; i < end; ++i)
409
stack().pushRef(stack().variable(i), function.location());
412
for(const variable::Variable & closure : internal_function.closures.variables())
413
if (closure.name() == SELF_VARIABLE_NAME) {
414
std::shared_ptr<variable::Object> self_object = internal_function.parent;
418
variable::Variable self = {SELF_VARIABLE_NAME, self_object, closure.location()};
421
notify.notifyDeclared(self);
424
if (closure.name() == function_origin.name())
425
stack().push(function_origin);
427
stack().push(closure);
429
notify.notifyDeclared(closure);
432
stack().setBoundaryToGlobal(boundary_index);
434
bool need_to_stretch = _layer_stack.empty();
436
addFlowLayer(*internal_function.code, boundary_index, callback);
438
notify.notifyBeforeFunctionCalling(function_origin);
444
stack().clearBoundary(boundary_index);
446
variable::Variable return_variable = func.getReturnDeclarationVariable();
447
stack().push(return_variable);
451
// Перед выходом восстанавливаем ожидаемое состояние выполнения команды
452
stack().clearBoundary(boundary_index);
454
stack().push(variable::error_variable());
458
void Interpret::print(bool need_detailed_report) const
460
const variable::Variable var = _stack.variable(_stack.top()).origin();
461
std::u16string str = toString(var.value(), need_detailed_report, false);
463
if (need_detailed_report && !var.spec().object()->variables().empty())
464
str += u"#" + toString(var.spec().object(), need_detailed_report);
466
reporter().reportInformation(inout::toU8(str));