4
Copyright (c) 2022 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,
6
https://bmstu.codes/lsx/simodo/loom
9
#include "ScriptSemantics_abstract.h"
10
#include "simodo/interpret/AnalyzeException.h"
12
#include "simodo/variable/FunctionWrapper.h"
13
#include "simodo/variable/matrix_operations.h"
14
#include "simodo/inout/convert/functions.h"
15
#include "simodo/inout/format/fmt.h"
23
namespace simodo::interpret
25
void ScriptSemantics_abstract::unary(ScriptOperationCode opcode, const inout::TokenLocation & location)
27
variable::Variable & var = stack().variable(stack().top());
28
variable::Variable & target = var.origin();
30
if (target.value().isError())
33
/// \todo Убедиться в необходимости этой проверки
34
if (target.variant().index() == std::variant_npos)
37
if (opcode == ScriptOperationCode::Not) {
40
if (target.type() == variable::ValueType::Bool) {
41
value = !target.value().getBool();
44
variable::Variable var = inter().expr().convertVariable(target, variable::ValueType::Bool);
46
value = !var.value().getBool();
49
/// \todo Можно оптимизировать, если на стеке ссылочный тип
51
stack().push({u"", value, location});
55
if (opcode == ScriptOperationCode::Minus
56
|| opcode == ScriptOperationCode::Plus) {
57
if (target.type() == variable::ValueType::Array) {
58
if (opcode == ScriptOperationCode::Plus)
61
if (var.type() == variable::ValueType::Ref) {
62
variable::Value a = target.value().copy();
63
variable::minusMatrix(a);
65
stack().push({u"", a, location});
68
variable::minusMatrix(target.value());
69
target.setLocation(location);
73
if (target.type() == variable::ValueType::Int) {
74
if (opcode == ScriptOperationCode::Plus)
77
int64_t value = -target.value().getInt();
78
/// \todo Можно оптимизировать, если на стеке не ссылочный тип (см. массив)
80
stack().push({u"", value, location});
83
if (target.type() == variable::ValueType::Float) {
84
if (opcode == ScriptOperationCode::Plus)
87
double value = -target.value().getFloat();
88
/// \todo Можно оптимизировать, если на стеке не ссылочный тип (см. массив)
90
stack().push({u"", value, location});
95
throw AnalyzeException("ScriptSemantics_abstract::unary",
96
location.makeLocation(inter().files()),
97
inout::fmt("The %1 operation for type %2 is not defined")
98
.arg(getSblOperationCodeName(opcode))
99
.arg(variable::getValueTypeName(target.type())));
102
void ScriptSemantics_abstract::logical(ScriptOperationCode opcode, const inout::TokenLocation & location)
104
variable::Variable & op1 = stack().variable(stack().top(1)).origin();
105
variable::Variable & op2 = stack().variable(stack().top(0)).origin();
107
if (op1.value().isError() || op2.value().isError()) {
109
stack().push(variable::error_variable().copyVariable());
115
if (op1.type() == variable::ValueType::Bool && op2.type() ==variable:: ValueType::Bool)
116
res = performLogicalOperation(opcode, op1.value(), op2.value());
117
else if (op1.type() == variable::ValueType::Bool)
118
res = performLogicalOperation(opcode, op1.value(), inter().expr().convertVariable(op2,variable::ValueType::Bool).value());
119
else if (op2.type() == variable::ValueType::Bool)
120
res = performLogicalOperation(opcode, inter().expr().convertVariable(op1,variable::ValueType::Bool).value(), op2.value());
122
res = performLogicalOperation(opcode,
123
inter().expr().convertVariable(op1,variable::ValueType::Bool).value(),
124
inter().expr().convertVariable(op2,variable::ValueType::Bool).value());
127
stack().push({{}, res, location, {}});
130
void ScriptSemantics_abstract::compare(ScriptOperationCode opcode, const inout::TokenLocation & location)
132
variable::Variable & op1_origin = stack().variable(stack().top(1)).origin();
133
variable::Variable & op2_origin = stack().variable(stack().top(0)).origin();
135
if (op1_origin.value().isError() || op2_origin.value().isError()) {
137
stack().push(variable::error_variable().copyVariable());
141
variable::ValueType target_type = getType4TypeConversion(opcode, op1_origin.type(), op2_origin.type());
143
variable::Variable op1 = target_type == variable::ValueType::Null
145
: inter().expr().convertVariable(op1_origin, target_type);
146
variable::Variable op2 = target_type == variable::ValueType::Null
148
: inter().expr().convertVariable(op2_origin, target_type);
153
case ScriptOperationCode::Equal:
154
res = performCompareEqual(op1.value(), op2.value());
156
case ScriptOperationCode::NotEqual:
157
res = !performCompareEqual(op1.value(), op2.value()).getBool();
159
case ScriptOperationCode::Less:
160
res = performCompareLess(op1.value(), op2.value());
162
case ScriptOperationCode::LessOrEqual:
163
res = performCompareLessOrEqual(op1.value(), op2.value());
165
case ScriptOperationCode::More:
166
res = !performCompareLessOrEqual(op1.value(), op2.value()).getBool();
168
case ScriptOperationCode::MoreOrEqual:
169
res = !performCompareLess(op1.value(), op2.value()).getBool();
175
if (res.type() == variable::ValueType::Null)
176
throw AnalyzeException("ScriptSemantics_abstract::compare",
177
location.makeLocation(inter().files()),
178
inout::fmt("For operation %1, the use of types %2 and %3 is not provided")
179
.arg(getSblOperationCodeName(opcode))
180
.arg(getValueTypeName(op1.type()))
181
.arg(getValueTypeName(op2.type())));
184
stack().push({{}, res, location, {}});
187
void ScriptSemantics_abstract::arithmetic(ScriptOperationCode opcode, const inout::TokenLocation & location)
189
variable::Variable & op1_origin = stack().variable(stack().top(1)).origin();
190
variable::Variable & op2_origin = stack().variable(stack().top(0)).origin();
192
if (op1_origin.value().isError() || op2_origin.value().isError()) {
194
stack().push(variable::error_variable().copyVariable());
198
if (op1_origin.value().isArray() || op2_origin.value().isArray()) {
199
arithmeticMatrix(opcode, location);
203
variable::ValueType target_type = getType4TypeConversion(opcode, op1_origin.type(), op2_origin.type());
205
if (target_type == variable::ValueType::Null) {
206
throw AnalyzeException("ScriptSemantics_abstract::arithmetic",
207
location.makeLocation(inter().files()),
208
inout::fmt("For operation %1, the use of types %2 and %3 is not provided")
209
.arg(getSblOperationCodeName(opcode))
210
.arg(getValueTypeName(op1_origin.type()))
211
.arg(getValueTypeName(op2_origin.type())));
214
variable::Variable op1 = inter().expr().convertVariable(op1_origin, target_type);
215
variable::Variable op2 = inter().expr().convertVariable(op2_origin, target_type);
217
variable::Value res = performArithmeticOperation(opcode, op1.value(), op2.value());
220
stack().push({{}, res, location, {}});
222
if (res.type() == variable::ValueType::Null)
223
throw AnalyzeException("ScriptSemantics_abstract::arithmetic",
224
location.makeLocation(inter().files()),
225
inout::fmt("For operation %1, the use of types %2 and %3 is not provided")
226
.arg(getSblOperationCodeName(opcode))
227
.arg(getValueTypeName(op1.type()))
228
.arg(getValueTypeName(op2.type())));
231
void ScriptSemantics_abstract::arithmeticMatrix(ScriptOperationCode opcode, const inout::TokenLocation & location)
233
variable::Variable & var1 = stack().variable(stack().top(1));
234
variable::Variable & var2 = stack().variable(stack().top(0));
235
variable::Variable & var1_origin = var1.origin();
236
variable::Variable & var2_origin = var2.origin();
238
/// \todo Требуется оптимизация!
239
/// Закомментированный код - пример оптимизации. Но этот пример не полный для данного метода
240
// if (var.type() == variable::ValueType::Ref) {
241
// variable::Value a = target.value().copy();
242
// variable::minusMatrix(a);
244
// stack().push({u"", a, location});
247
// variable::minusMatrix(target.value());
248
// target.setLocation(location);
253
case ScriptOperationCode::Addition:
254
case ScriptOperationCode::Subtraction:
255
case ScriptOperationCode::AssignmentAddition:
256
case ScriptOperationCode::AssignmentSubtraction:
257
if (var1_origin.value().isArray() && var2_origin.value().isArray()) {
258
variable::Value target_matrix = var1_origin.value().copy();
259
variable::Value second_matrix = var2_origin.value().copy();
261
if (opcode == ScriptOperationCode::Subtraction
262
|| opcode == ScriptOperationCode::AssignmentSubtraction)
263
variable::minusMatrix(second_matrix);
265
variable::additionMatrixMatrix(target_matrix, second_matrix);
268
stack().push({{}, target_matrix, location});
272
case ScriptOperationCode::Division:
273
case ScriptOperationCode::AssignmentDivision:
274
if (var1_origin.value().isArray() && !var2_origin.value().isArray()) {
275
variable::Value target_matrix = var1_origin.value().copy();
276
variable::Value divider { 1.0 / inter().expr().convertVariable(var2_origin, variable::ValueType::Float).value().getFloat() };
278
variable::multiplicationMatrixScalar(target_matrix, divider);
281
stack().push({{}, target_matrix, location});
285
case ScriptOperationCode::Multiplication:
286
case ScriptOperationCode::AssignmentMultiplication:
287
if (var1_origin.value().isArray() && var2_origin.value().isArray()) {
288
variable::Value target_matrix = var1_origin.value().copy();
290
variable::multiplicationMatrixMatrix(target_matrix, var2_origin.value());
293
stack().push({{}, target_matrix, location});
295
else if (var1_origin.value().isArray()) {
296
variable::Value target_matrix = var1_origin.value().copy();
298
variable::multiplicationMatrixScalar(target_matrix, var2_origin.value());
301
stack().push({{}, target_matrix, location});
304
variable::Value target_matrix = var2_origin.value().copy();
306
variable::multiplicationMatrixScalar(target_matrix, var1_origin.value());
309
stack().push({{}, target_matrix, location});
316
throw AnalyzeException("ScriptSemantics_abstract::performMatrixArithmeticOperation",
317
location.makeLocation(inter().files()),
318
inout::fmt("For operation %1, the use of types %2 and %3 is not provided")
319
.arg(getSblOperationCodeName(opcode))
320
.arg(getValueTypeName(var1_origin.type()))
321
.arg(getValueTypeName(var2_origin.type())));
324
variable::ValueType ScriptSemantics_abstract::getType4TypeConversion(ScriptOperationCode /*opcode*/, variable::ValueType type1, variable::ValueType type2) const
329
if ((type1 == variable::ValueType::Int && type2 == variable::ValueType::Float) ||
330
(type1 == variable::ValueType::Float && type2 == variable::ValueType::Int))
331
return variable::ValueType::Float;
333
if (type1 == variable::ValueType::String || type2 == variable::ValueType::String)
334
return variable::ValueType::String;
336
return variable::ValueType::Null;
339
bool ScriptSemantics_abstract::maybeContract(const variable::Variable & var) const
341
const variable::Variable & spec_origin = var.spec().object()->getVariableByName(variable::SPEC_ORIGIN);
343
if (/*var.type() != variable::ValueType::Record ||*/ spec_origin.type() != variable::ValueType::String)
346
std::u16string o_str = spec_origin.value().getString();
348
if (o_str != variable::SPEC_ORIGIN_CONTRACT
349
&& o_str != variable::SPEC_ORIGIN_STRUCTURE
350
&& o_str != variable::SPEC_ORIGIN_MODULE)
356
variable::Value ScriptSemantics_abstract::performLogicalOperation(ScriptOperationCode opcode, const variable::Value & op1, const variable::Value & op2) const
358
if (op1.variant().index() == std::variant_npos)
359
return {variable::ValueType::Bool};
361
bool v1 = op1.getBool();
362
bool v2 = op2.getBool();
366
case ScriptOperationCode::Or:
367
return {bool {v1 || v2}};
368
case ScriptOperationCode::And:
369
return {bool {v1 && v2}};
377
variable::Value ScriptSemantics_abstract::performCompareEqual(const variable::Value & op1, const variable::Value & op2) const
379
if (op1.isNull() || op2.isNull())
380
return op1.type() == op2.type();
384
case variable::ValueType::Bool:
385
if (op1.variant().index() != std::variant_npos && op2.variant().index() != std::variant_npos)
386
return op1.getBool() == op2.getBool();
388
return variable::ValueType::Bool;
389
case variable::ValueType::Int:
390
if (op1.variant().index() != std::variant_npos && op2.variant().index() != std::variant_npos)
391
return op1.getInt() == op2.getInt();
393
return variable::ValueType::Bool;
394
case variable::ValueType::Float:
395
if (op1.variant().index() != std::variant_npos && op2.variant().index() != std::variant_npos)
397
double val1 = op1.getFloat();
398
double val2 = op2.getFloat();
400
// Учёт погрешности вычислений с плавающей точкой.
401
// Машинный эпсилон (разница между 1.0 и следующим представимым значением для double)
402
// должен быть масштабирован до величины используемых значений и умножен на желаемую
403
// точность в ULP (единицы на последнем месте). ULP нужно подбирать в зависимости от
404
// степени накапливаемой погрешности.
405
/// \todo Подобрать оптимальное значение ULP
406
return std::abs(val1 - val2) <= DBL_EPSILON * std::abs(val1+val2) * 2;
409
return variable::ValueType::Bool;
410
case variable::ValueType::String:
411
if (op1.variant().index() != std::variant_npos && op2.variant().index() != std::variant_npos)
412
return op1.getString() == op2.getString();
414
return variable::ValueType::Bool;
422
variable::Value ScriptSemantics_abstract::performCompareLess(const variable::Value & op1, const variable::Value & op2) const
424
if (op1.isNull() || op2.isNull())
429
case variable::ValueType::Int:
430
if (op1.variant().index() != std::variant_npos && op2.variant().index() != std::variant_npos)
431
return op1.getInt() < op2.getInt();
433
return variable::ValueType::Bool;
434
case variable::ValueType::Float:
435
if (op1.variant().index() != std::variant_npos && op2.variant().index() != std::variant_npos)
436
return op1.getFloat() < op2.getFloat();
438
return variable::ValueType::Bool;
439
case variable::ValueType::String:
440
if (op1.variant().index() != std::variant_npos && op2.variant().index() != std::variant_npos)
441
return op1.getString() < op2.getString();
443
return variable::ValueType::Bool;
451
variable::Value ScriptSemantics_abstract::performCompareLessOrEqual(const variable::Value & op1, const variable::Value & op2) const
453
if (op1.isNull() || op2.isNull())
458
case variable::ValueType::Int:
459
if (op1.variant().index() != std::variant_npos && op2.variant().index() != std::variant_npos)
460
return op1.getInt() <= op2.getInt();
462
return variable::ValueType::Bool;
463
case variable::ValueType::Float:
464
if (op1.variant().index() != std::variant_npos && op2.variant().index() != std::variant_npos)
465
return op1.getFloat() <= op2.getFloat();
467
return variable::ValueType::Bool;
468
case variable::ValueType::String:
469
if (op1.variant().index() != std::variant_npos && op2.variant().index() != std::variant_npos)
470
return op1.getString() <= op2.getString();
472
return variable::ValueType::Bool;
480
variable::Value ScriptSemantics_abstract::performArithmeticOperation(ScriptOperationCode opcode, const variable::Value & op1, const variable::Value & op2) const
482
if (op1.variant().index() == std::variant_npos || op2.variant().index() == std::variant_npos)
487
case ScriptOperationCode::Addition:
488
case ScriptOperationCode::AssignmentAddition:
490
case variable::ValueType::Int:
491
return op1.getInt() + op2.getInt();
492
case variable::ValueType::Float:
493
return op1.getFloat() + op2.getFloat();
494
case variable::ValueType::String:
495
return op1.getString() + op2.getString();
500
case ScriptOperationCode::Subtraction:
501
case ScriptOperationCode::AssignmentSubtraction:
503
case variable::ValueType::Int:
504
return op1.getInt() - op2.getInt();
505
case variable::ValueType::Float:
506
return op1.getFloat() - op2.getFloat();
511
case ScriptOperationCode::Multiplication:
512
case ScriptOperationCode::AssignmentMultiplication:
514
case variable::ValueType::Int:
515
return op1.getInt() * op2.getInt();
516
case variable::ValueType::Float:
517
return op1.getFloat() * op2.getFloat();
522
case ScriptOperationCode::Division:
523
case ScriptOperationCode::AssignmentDivision:
525
case variable::ValueType::Int:
526
if (op2.getInt() == 0)
527
throw std::overflow_error(inout::fmt("Деление на ноль"));
528
return op1.getInt() / op2.getInt();
529
case variable::ValueType::Float: {
530
double val2 = op2.getFloat();
531
if (std::abs(val2) <= DBL_EPSILON * std::abs(val2) * 2)
532
throw std::overflow_error(inout::fmt("Деление на ноль"));
533
return op1.getFloat() / op2.getFloat();
539
case ScriptOperationCode::Modulo:
540
case ScriptOperationCode::AssignmentModulo:
542
case variable::ValueType::Int:
544
int64_t op1i = op1.getInt();
545
int64_t op2i = op2.getInt();
547
throw std::overflow_error(inout::fmt("Деление на ноль"));
554
case ScriptOperationCode::Power:
556
double base = op1.type() == variable::ValueType::Int
557
? double(op1.getInt())
561
case variable::ValueType::Int: {
562
double res = pow(base, op2.getInt());
565
case variable::ValueType::Float: {
566
double res = pow(base, op2.getFloat());