loom

Форк
0
/
ScriptSemantics_expression.cpp 
581 строка · 23.8 Кб
1
/*
2
MIT License
3

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

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

9
#include "ScriptSemantics_abstract.h"
10
#include "simodo/interpret/AnalyzeException.h"
11

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"
16

17
#include <memory>
18
#include <cassert>
19
#include <limits>
20
#include <cmath>
21
#include <float.h>
22

23
namespace simodo::interpret
24
{
25
    void ScriptSemantics_abstract::unary(ScriptOperationCode opcode, const inout::TokenLocation & location)
26
    {
27
        variable::Variable & var    = stack().variable(stack().top());
28
        variable::Variable & target = var.origin();
29

30
        if (target.value().isError())
31
            return;
32

33
        /// \todo Убедиться в необходимости этой проверки
34
        if (target.variant().index() == std::variant_npos)
35
            return;
36

37
        if (opcode == ScriptOperationCode::Not) {
38
            bool value;
39

40
            if (target.type() == variable::ValueType::Bool) {
41
                value = !target.value().getBool();
42
            }
43
            else {
44
                variable::Variable var = inter().expr().convertVariable(target, variable::ValueType::Bool);
45

46
                value = !var.value().getBool();
47
            }
48

49
            /// \todo Можно оптимизировать, если на стеке ссылочный тип
50
            stack().pop();
51
            stack().push({u"", value, location});
52
            return;
53
        }
54

55
        if (opcode == ScriptOperationCode::Minus
56
         || opcode == ScriptOperationCode::Plus) {
57
            if (target.type() == variable::ValueType::Array) {
58
                if (opcode == ScriptOperationCode::Plus)
59
                    return;
60

61
                if (var.type() == variable::ValueType::Ref) {
62
                    variable::Value a = target.value().copy();
63
                    variable::minusMatrix(a);
64
                    stack().pop();
65
                    stack().push({u"", a, location});
66
                }
67
                else {
68
                    variable::minusMatrix(target.value());
69
                    target.setLocation(location);
70
                }
71
                return;
72
            }
73
            if (target.type() == variable::ValueType::Int) {
74
                if (opcode == ScriptOperationCode::Plus)
75
                    return;
76

77
                int64_t value = -target.value().getInt();
78
                /// \todo Можно оптимизировать, если на стеке не ссылочный тип (см. массив)
79
                stack().pop();
80
                stack().push({u"", value, location});
81
                return;
82
            }
83
            if (target.type() == variable::ValueType::Float) {
84
                if (opcode == ScriptOperationCode::Plus)
85
                    return;
86

87
                double value = -target.value().getFloat();
88
                /// \todo Можно оптимизировать, если на стеке не ссылочный тип (см. массив)
89
                stack().pop();
90
                stack().push({u"", value, location});
91
                return;
92
            }
93
        }
94

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())));
100
    }
101

102
    void ScriptSemantics_abstract::logical(ScriptOperationCode opcode, const inout::TokenLocation & location)
103
    {
104
        variable::Variable & op1 = stack().variable(stack().top(1)).origin();
105
        variable::Variable & op2 = stack().variable(stack().top(0)).origin();
106

107
        if (op1.value().isError() || op2.value().isError()) {
108
            stack().pop(2);
109
            stack().push(variable::error_variable().copyVariable());
110
            return;
111
        }
112

113
        variable::Value res;
114

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());
121
        else 
122
            res = performLogicalOperation(opcode, 
123
                                        inter().expr().convertVariable(op1,variable::ValueType::Bool).value(), 
124
                                        inter().expr().convertVariable(op2,variable::ValueType::Bool).value());
125
        
126
        stack().pop(2);
127
        stack().push({{}, res, location, {}});
128
    }
129

130
    void ScriptSemantics_abstract::compare(ScriptOperationCode opcode, const inout::TokenLocation & location)
131
    {
132
        variable::Variable & op1_origin = stack().variable(stack().top(1)).origin();
133
        variable::Variable & op2_origin = stack().variable(stack().top(0)).origin();
134

135
        if (op1_origin.value().isError() || op2_origin.value().isError()) {
136
            stack().pop(2);
137
            stack().push(variable::error_variable().copyVariable());
138
            return;
139
        }
140

141
        variable::ValueType target_type = getType4TypeConversion(opcode, op1_origin.type(), op2_origin.type());
142

143
        variable::Variable  op1 = target_type == variable::ValueType::Null
144
                                ? op1_origin
145
                                : inter().expr().convertVariable(op1_origin, target_type);
146
        variable::Variable  op2 = target_type == variable::ValueType::Null
147
                                ? op2_origin
148
                                : inter().expr().convertVariable(op2_origin, target_type);
149
        variable::Value     res;
150

151
        switch(opcode)
152
        {
153
        case ScriptOperationCode::Equal:
154
            res = performCompareEqual(op1.value(), op2.value());
155
            break;
156
        case ScriptOperationCode::NotEqual:
157
            res = !performCompareEqual(op1.value(), op2.value()).getBool();
158
            break;
159
        case ScriptOperationCode::Less:
160
            res = performCompareLess(op1.value(), op2.value());
161
            break;
162
        case ScriptOperationCode::LessOrEqual:
163
            res = performCompareLessOrEqual(op1.value(), op2.value());
164
            break;
165
        case ScriptOperationCode::More:
166
            res = !performCompareLessOrEqual(op1.value(), op2.value()).getBool();
167
            break;
168
        case ScriptOperationCode::MoreOrEqual:
169
            res = !performCompareLess(op1.value(), op2.value()).getBool();
170
            break;
171
        default:
172
            break;
173
        }
174

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())));
182

183
        stack().pop(2);
184
        stack().push({{}, res, location, {}});
185
    }
186

187
    void ScriptSemantics_abstract::arithmetic(ScriptOperationCode opcode, const inout::TokenLocation & location)
188
    {
189
        variable::Variable & op1_origin = stack().variable(stack().top(1)).origin();
190
        variable::Variable & op2_origin = stack().variable(stack().top(0)).origin();
191

192
        if (op1_origin.value().isError() || op2_origin.value().isError()) {
193
            stack().pop(2);
194
            stack().push(variable::error_variable().copyVariable());
195
            return;
196
        }
197

198
        if (op1_origin.value().isArray() || op2_origin.value().isArray()) {
199
            arithmeticMatrix(opcode, location);
200
            return;
201
        }
202

203
        variable::ValueType target_type = getType4TypeConversion(opcode, op1_origin.type(), op2_origin.type());
204

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())));
212
        }
213

214
        variable::Variable  op1 = inter().expr().convertVariable(op1_origin, target_type);
215
        variable::Variable  op2 = inter().expr().convertVariable(op2_origin, target_type);
216
        
217
        variable::Value res = performArithmeticOperation(opcode, op1.value(), op2.value());
218

219
        stack().pop(2);
220
        stack().push({{}, res, location, {}});
221

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())));
229
    }
230

231
    void ScriptSemantics_abstract::arithmeticMatrix(ScriptOperationCode opcode, const inout::TokenLocation & location)
232
    {
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();
237

238
        /// \todo Требуется оптимизация!
239
        /// Закомментированный код - пример оптимизации. Но этот пример не полный для данного метода
240
        // if (var.type() == variable::ValueType::Ref) {
241
        //     variable::Value a = target.value().copy();
242
        //     variable::minusMatrix(a);
243
        //     stack().pop();
244
        //     stack().push({u"", a, location});
245
        // }
246
        // else {
247
        //     variable::minusMatrix(target.value());
248
        //     target.setLocation(location);
249
        // }
250

251
        switch(opcode) 
252
        {
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();
260

261
                if (opcode == ScriptOperationCode::Subtraction
262
                 || opcode == ScriptOperationCode::AssignmentSubtraction)
263
                    variable::minusMatrix(second_matrix);
264

265
                variable::additionMatrixMatrix(target_matrix, second_matrix);
266

267
                stack().pop(2);
268
                stack().push({{}, target_matrix, location});
269
                return;
270
            }
271
            break;
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() };
277

278
                variable::multiplicationMatrixScalar(target_matrix, divider);
279

280
                stack().pop(2);
281
                stack().push({{}, target_matrix, location});
282
                return;
283
            }
284
            break;
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();
289

290
                variable::multiplicationMatrixMatrix(target_matrix, var2_origin.value());
291

292
                stack().pop(2);
293
                stack().push({{}, target_matrix, location});
294
            }
295
            else if (var1_origin.value().isArray()) {
296
                variable::Value target_matrix = var1_origin.value().copy();
297

298
                variable::multiplicationMatrixScalar(target_matrix, var2_origin.value());
299

300
                stack().pop(2);
301
                stack().push({{}, target_matrix, location});
302
            }
303
            else {
304
                variable::Value target_matrix = var2_origin.value().copy();
305

306
                variable::multiplicationMatrixScalar(target_matrix, var1_origin.value());
307

308
                stack().pop(2);
309
                stack().push({{}, target_matrix, location});
310
            }
311
            return;
312
        default:
313
            break;
314
        }
315

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())));
322
    }
323

324
    variable::ValueType ScriptSemantics_abstract::getType4TypeConversion(ScriptOperationCode /*opcode*/, variable::ValueType type1, variable::ValueType type2) const
325
    {
326
        if (type1 == type2)
327
            return type1;
328

329
        if ((type1 == variable::ValueType::Int && type2 == variable::ValueType::Float) ||
330
            (type1 == variable::ValueType::Float && type2 == variable::ValueType::Int))
331
            return variable::ValueType::Float;
332

333
        if (type1 == variable::ValueType::String || type2 == variable::ValueType::String)
334
            return variable::ValueType::String;
335

336
        return variable::ValueType::Null;
337
    }
338

339
    bool ScriptSemantics_abstract::maybeContract(const variable::Variable & var) const
340
    {
341
        const variable::Variable & spec_origin = var.spec().object()->getVariableByName(variable::SPEC_ORIGIN);
342

343
        if (/*var.type() != variable::ValueType::Record ||*/ spec_origin.type() != variable::ValueType::String)
344
            return false;
345

346
        std::u16string o_str = spec_origin.value().getString();
347

348
        if (o_str != variable::SPEC_ORIGIN_CONTRACT
349
         && o_str != variable::SPEC_ORIGIN_STRUCTURE 
350
         && o_str != variable::SPEC_ORIGIN_MODULE)
351
            return false;
352

353
        return true;
354
    }
355

356
    variable::Value ScriptSemantics_abstract::performLogicalOperation(ScriptOperationCode opcode, const variable::Value & op1, const variable::Value & op2) const
357
    {
358
        if (op1.variant().index() == std::variant_npos)
359
            return {variable::ValueType::Bool};
360

361
        bool v1 = op1.getBool();
362
        bool v2 = op2.getBool();
363

364
        switch(opcode)
365
        {
366
        case ScriptOperationCode::Or:
367
            return {bool {v1 || v2}};
368
        case ScriptOperationCode::And:
369
            return {bool {v1 && v2}};
370
        default:
371
            break;
372
        }
373

374
        return {};
375
    }
376

377
    variable::Value ScriptSemantics_abstract::performCompareEqual(const variable::Value & op1, const variable::Value & op2) const
378
    {
379
        if (op1.isNull() || op2.isNull())
380
            return op1.type() == op2.type(); 
381

382
        switch(op1.type())
383
        {
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();
387
            else
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();
392
            else
393
                return variable::ValueType::Bool;
394
        case variable::ValueType::Float:
395
            if (op1.variant().index() != std::variant_npos && op2.variant().index() != std::variant_npos)
396
            {
397
                double val1 = op1.getFloat();
398
                double val2 = op2.getFloat();
399

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;
407
            }
408
            else
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();
413
            else
414
                return variable::ValueType::Bool;
415
        default:
416
            break;
417
        }
418

419
        return {};
420
    }
421

422
    variable::Value ScriptSemantics_abstract::performCompareLess(const variable::Value & op1, const variable::Value & op2) const
423
    {
424
        if (op1.isNull() || op2.isNull())
425
            return false; 
426

427
        switch(op1.type())
428
        {
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();
432
            else
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();
437
            else
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();
442
            else
443
                return variable::ValueType::Bool;
444
        default:
445
            break;
446
        }
447

448
        return {};
449
    }
450

451
    variable::Value ScriptSemantics_abstract::performCompareLessOrEqual(const variable::Value & op1, const variable::Value & op2) const
452
    {
453
        if (op1.isNull() || op2.isNull())
454
            return false; 
455

456
        switch(op1.type())
457
        {
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();
461
            else
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();
466
            else
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();
471
            else
472
                return variable::ValueType::Bool;
473
        default:
474
            break;
475
        }
476

477
        return {};
478
    }
479

480
    variable::Value ScriptSemantics_abstract::performArithmeticOperation(ScriptOperationCode opcode, const variable::Value & op1, const variable::Value & op2) const
481
    {
482
        if (op1.variant().index() == std::variant_npos || op2.variant().index() == std::variant_npos)
483
            return op1.type();
484

485
        switch(opcode) 
486
        {
487
        case ScriptOperationCode::Addition:
488
        case ScriptOperationCode::AssignmentAddition:
489
            switch(op1.type()) {
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();
496
            default:
497
                break;
498
            }
499
            break;
500
        case ScriptOperationCode::Subtraction:
501
        case ScriptOperationCode::AssignmentSubtraction:
502
            switch(op1.type()) {
503
            case variable::ValueType::Int:
504
                return op1.getInt() - op2.getInt();
505
            case variable::ValueType::Float:
506
                return op1.getFloat() - op2.getFloat();
507
            default:
508
                break;
509
            }
510
            break;
511
        case ScriptOperationCode::Multiplication:
512
        case ScriptOperationCode::AssignmentMultiplication:
513
            switch(op1.type()) {
514
            case variable::ValueType::Int:
515
                return op1.getInt() * op2.getInt();
516
            case variable::ValueType::Float:
517
                return op1.getFloat() * op2.getFloat();
518
            default:
519
                break;
520
            }
521
            break;
522
        case ScriptOperationCode::Division:
523
        case ScriptOperationCode::AssignmentDivision:
524
            switch(op1.type()) {
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();
534
                }
535
            default:
536
                break;
537
            }
538
            break;
539
        case ScriptOperationCode::Modulo:
540
        case ScriptOperationCode::AssignmentModulo:
541
            switch(op1.type()) {
542
            case variable::ValueType::Int:
543
            {
544
                int64_t op1i = op1.getInt();
545
                int64_t op2i = op2.getInt();
546
                if (op2i == 0)
547
                    throw std::overflow_error(inout::fmt("Деление на ноль"));
548
                return op1i % op2i;
549
            }
550
            default:
551
                break;
552
            }
553
            break;
554
        case ScriptOperationCode::Power:
555
            {
556
                double base = op1.type() == variable::ValueType::Int
557
                            ? double(op1.getInt())
558
                            : op1.getFloat();
559

560
                switch(op2.type()) {
561
                case variable::ValueType::Int: {
562
                        double res = pow(base, op2.getInt());
563
                        return res;
564
                    }
565
                case variable::ValueType::Float: {
566
                        double res = pow(base, op2.getFloat());
567
                        return res;
568
                    }
569
                default:
570
                    break;
571
                }
572
            }
573
            break;
574
        default:
575
            break;
576
        }
577

578
        return {};
579
    }
580

581
}
582

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

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

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

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