• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wirenboard / wb-mqtt-serial / 6

13 Jan 2026 06:31AM UTC coverage: 76.817% (+4.0%) from 72.836%
6

Pull #1049

github

8b2ffc
Ilia1S
Remove fw from groups
Pull Request #1049: WB-MR templates: Add delays

6873 of 9161 branches covered (75.02%)

12966 of 16879 relevant lines covered (76.82%)

830.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.86
/src/expression_evaluator.cpp
1
#include "expression_evaluator.h"
2

3
#include <set>
4
#include <stdexcept>
5
#include <unordered_map>
6
#include <vector>
7

8
using namespace std;
9
using namespace Expressions;
10

11
namespace
12
{
13
    bool IsOperator(TTokenType type)
54,921✔
14
    {
15
        return (type == TTokenType::Equal) || (type == TTokenType::NotEqual) || (type == TTokenType::Greater) ||
37,310✔
16
               (type == TTokenType::Less) || (type == TTokenType::GreaterEqual) || (type == TTokenType::LessEqual) ||
34,774✔
17
               (type == TTokenType::Or) || (type == TTokenType::And);
92,231✔
18
    }
19

20
    /**
21
     * @brief Get operator priority according to
22
     *        https://en.cppreference.com/w/cpp/language/operator_precedence
23
     *        Lower value, higher priority
24
     */
25
    size_t GetPriority(TAstNodeType type)
219,998✔
26
    {
27
        switch (type) {
219,998✔
28
            case TAstNodeType::Greater:
14,950✔
29
            case TAstNodeType::Less:
30
            case TAstNodeType::GreaterEqual:
31
            case TAstNodeType::LessEqual:
32
                return 9;
14,950✔
33
            case TAstNodeType::Equal:
103,868✔
34
            case TAstNodeType::NotEqual:
35
                return 10;
103,868✔
36
            case TAstNodeType::And:
14,671✔
37
                return 14;
14,671✔
38
            case TAstNodeType::Or:
86,509✔
39
                return 15;
86,509✔
40
            default:
×
41
                return 0;
×
42
        }
43
    }
44

45
    // Input ast node sequence must be valid. It must have odd length.
46
    // Odd nodes are numbers, identifiers, functions or expression trees, even values are operators.
47
    std::unique_ptr<TAstNode> TreeByPriority(std::vector<std::unique_ptr<TAstNode>>::iterator begin,
93,358✔
48
                                             std::vector<std::unique_ptr<TAstNode>>::iterator end)
49
    {
50
        if (begin + 1 == end) {
93,358✔
51
            return std::move(*begin);
54,919✔
52
        }
53
        // Iterate over even nodes (operators)
54
        auto rootIt = begin + 1;
38,439✔
55
        for (auto it = rootIt; it != end && it + 1 != end; it += 2) {
148,438✔
56
            if (GetPriority((*rootIt)->GetType()) <= GetPriority((*it)->GetType())) {
109,999✔
57
                rootIt = it;
74,570✔
58
            }
59
        }
60
        std::unique_ptr<TAstNode> root(std::move(*rootIt));
76,878✔
61
        root->SetLeft(TreeByPriority(begin, rootIt));
38,439✔
62
        root->SetRight(TreeByPriority(rootIt + 1, end));
38,439✔
63
        return root;
38,439✔
64
    }
65

66
    void ThrowParserError(const std::string& msg, size_t pos)
19✔
67
    {
68
        throw std::runtime_error(msg + std::to_string(pos + 1));
19✔
69
    }
70

71
    void ThrowMissingRightBracketError(size_t pos)
5✔
72
    {
73
        ThrowParserError("right bracket expected at position ", pos);
10✔
74
    }
75

76
    void ThrowMissingOperatorError(size_t pos)
8✔
77
    {
78
        ThrowParserError("operator expected at position ", pos);
16✔
79
    }
80

81
    void ThrowMissingIdentifierError(size_t pos)
2✔
82
    {
83
        ThrowParserError("identifier expected at position ", pos);
4✔
84
    }
85

86
    void ThrowMissingIdentifierOrNumberOrLeftBracketError(size_t pos)
3✔
87
    {
88
        ThrowParserError("identifier, number or left bracket are expected at position ", pos);
6✔
89
    }
90

91
    void ThrowUnknownFunctionError(const std::string& name, size_t pos)
1✔
92
    {
93
        ThrowParserError("unknown function " + name + " at position ", pos);
2✔
94
    }
95

96
    optional<int32_t> EvalFunction(const TAstNode* expr, const IParams& params)
4✔
97
    {
98
        return params.Get(expr->GetRight()->GetValue()).has_value();
8✔
99
    }
100

101
    optional<int32_t> EvalImpl(const TAstNode* expr, const IParams& params)
247✔
102
    {
103
        if (!expr) {
247✔
104
            throw std::runtime_error("undefined token");
×
105
        }
106
        switch (expr->GetType()) {
247✔
107
            case TAstNodeType::Number:
66✔
108
                return optional<int32_t>(atoi(expr->GetValue().c_str()));
132✔
109
            case TAstNodeType::Equal: {
72✔
110
                auto v1 = EvalImpl(expr->GetLeft(), params);
72✔
111
                if (!v1) {
72✔
112
                    return false;
16✔
113
                }
114
                auto v2 = EvalImpl(expr->GetRight(), params);
56✔
115
                return v2 ? v1 == v2 : false;
112✔
116
            }
117
            case TAstNodeType::NotEqual: {
2✔
118
                auto v1 = EvalImpl(expr->GetLeft(), params);
2✔
119
                if (!v1)
2✔
120
                    return true;
×
121
                auto v2 = EvalImpl(expr->GetRight(), params);
2✔
122
                return v2 ? v1 != v2 : true;
4✔
123
            }
124
            case TAstNodeType::Greater: {
2✔
125
                auto v1 = EvalImpl(expr->GetLeft(), params);
2✔
126
                if (!v1)
2✔
127
                    return false;
×
128
                auto v2 = EvalImpl(expr->GetRight(), params);
2✔
129
                return v2 ? v1 > v2 : false;
4✔
130
            }
131
            case TAstNodeType::Less: {
2✔
132
                auto v1 = EvalImpl(expr->GetLeft(), params);
2✔
133
                if (!v1)
2✔
134
                    return false;
×
135
                auto v2 = EvalImpl(expr->GetRight(), params);
2✔
136
                return v2 ? v1 < v2 : false;
4✔
137
            }
138
            case TAstNodeType::GreaterEqual: {
2✔
139
                auto v1 = EvalImpl(expr->GetLeft(), params);
2✔
140
                if (!v1)
2✔
141
                    return false;
×
142
                auto v2 = EvalImpl(expr->GetRight(), params);
2✔
143
                return v2 ? v1 >= v2 : false;
4✔
144
            }
145
            case TAstNodeType::LessEqual: {
2✔
146
                auto v1 = EvalImpl(expr->GetLeft(), params);
2✔
147
                if (!v1)
2✔
148
                    return false;
×
149
                auto v2 = EvalImpl(expr->GetRight(), params);
2✔
150
                return v2 ? v1 <= v2 : false;
4✔
151
            }
152
            case TAstNodeType::Or: {
3✔
153
                auto v1 = EvalImpl(expr->GetLeft(), params);
3✔
154
                if (!v1)
3✔
155
                    return false;
×
156
                auto v2 = EvalImpl(expr->GetRight(), params);
3✔
157
                return v2 ? v1.value() || v2.value() : false;
6✔
158
            }
159
            case TAstNodeType::And: {
10✔
160
                auto v1 = EvalImpl(expr->GetLeft(), params);
10✔
161
                if (!v1)
10✔
162
                    return false;
×
163
                auto v2 = EvalImpl(expr->GetRight(), params);
10✔
164
                return v2 ? v1.value() && v2.value() : false;
20✔
165
            }
166
            case TAstNodeType::Ident: {
82✔
167
                return params.Get(expr->GetValue());
82✔
168
            }
169
            case TAstNodeType::Func: {
4✔
170
                return EvalFunction(expr, params);
4✔
171
            }
172
        }
173
        return nullopt;
×
174
    }
175

176
}
177

178
TToken::TToken(TTokenType type, size_t pos, const std::string& value): Type(type), Value(value), Pos(pos)
112,775✔
179
{}
112,775✔
180

181
TAstNode::TAstNode(const TToken& token)
88,321✔
182
{
183
    const std::unordered_map<TTokenType, TAstNodeType> types = {{TTokenType::Number, TAstNodeType::Number},
184
                                                                {TTokenType::Ident, TAstNodeType::Ident},
185
                                                                {TTokenType::Equal, TAstNodeType::Equal},
186
                                                                {TTokenType::NotEqual, TAstNodeType::NotEqual},
187
                                                                {TTokenType::Greater, TAstNodeType::Greater},
188
                                                                {TTokenType::Less, TAstNodeType::Less},
189
                                                                {TTokenType::GreaterEqual, TAstNodeType::GreaterEqual},
190
                                                                {TTokenType::LessEqual, TAstNodeType::LessEqual},
191
                                                                {TTokenType::Or, TAstNodeType::Or},
192
                                                                {TTokenType::And, TAstNodeType::And}};
176,642✔
193
    auto it = types.find(token.Type);
88,321✔
194
    if (it != types.end()) {
88,321✔
195
        Type = it->second;
88,321✔
196
    } else {
197
        throw std::runtime_error("Can't make AST node");
×
198
    }
199
    Value = token.Value;
88,321✔
200
}
88,321✔
201

202
TAstNode::TAstNode(const TAstNodeType& type, const std::string& value): Type(type), Value(value)
946✔
203
{}
946✔
204

205
void TAstNode::SetLeft(std::unique_ptr<TAstNode> node)
38,439✔
206
{
207
    Left.swap(node);
38,439✔
208
}
38,439✔
209

210
void TAstNode::SetRight(std::unique_ptr<TAstNode> node)
39,385✔
211
{
212
    Right.swap(node);
39,385✔
213
}
39,385✔
214

215
const TAstNode* TAstNode::GetLeft() const
57,966✔
216
{
217
    return Left.get();
57,966✔
218
}
219

220
const TAstNode* TAstNode::GetRight() const
57,964✔
221
{
222
    return Right.get();
57,964✔
223
}
224

225
const std::string& TAstNode::GetValue() const
23,067✔
226
{
227
    return Value;
23,067✔
228
}
229

230
TAstNodeType TAstNode::GetType() const
301,021✔
231
{
232
    return Type;
301,021✔
233
}
234

235
void TLexer::ThrowUnexpected() const
5✔
236
{
237
    if (!SomethingToParse()) {
5✔
238
        throw std::runtime_error("unexpected end of line");
×
239
    }
240
    throw std::runtime_error(std::string("unexpected symbol '") + *Pos + "' at position " +
15✔
241
                             std::to_string(Pos - Start + 1));
20✔
242
}
243

244
bool TLexer::SomethingToParse() const
603,417✔
245
{
246
    return Pos != End;
603,417✔
247
}
248

249
bool TLexer::CompareChar(char c) const
38,458✔
250
{
251
    return SomethingToParse() && (*Pos == c);
38,458✔
252
}
253

254
std::optional<TToken> TLexer::GetOpOrBracket()
101,324✔
255
{
256
    auto tokenStart = Pos - Start;
101,324✔
257
    switch (*Pos) {
101,324✔
258
        case '=': {
17,627✔
259
            ++Pos;
17,627✔
260
            if (CompareChar('=')) {
17,627✔
261
                ++Pos;
17,627✔
262
                return TToken(TTokenType::Equal, tokenStart);
35,254✔
263
            }
264
            ThrowUnexpected();
×
265
        }
266
        case '!': {
2,255✔
267
            ++Pos;
2,255✔
268
            if (CompareChar('=')) {
2,255✔
269
                ++Pos;
2,255✔
270
                return TToken(TTokenType::NotEqual, tokenStart);
4,510✔
271
            }
272
            ThrowUnexpected();
×
273
        }
274
        case '<': {
673✔
275
            ++Pos;
673✔
276
            if (CompareChar('=')) {
673✔
277
                ++Pos;
663✔
278
                return TToken(TTokenType::LessEqual, tokenStart);
1,326✔
279
            }
280
            return TToken(TTokenType::Less, tokenStart);
20✔
281
        }
282
        case '>': {
4,278✔
283
            ++Pos;
4,278✔
284
            if (CompareChar('=')) {
4,278✔
285
                ++Pos;
4,007✔
286
                return TToken(TTokenType::GreaterEqual, tokenStart);
8,014✔
287
            }
288
            return TToken(TTokenType::Greater, tokenStart);
542✔
289
        }
290
        case '|': {
7,411✔
291
            ++Pos;
7,411✔
292
            if (CompareChar('|')) {
7,411✔
293
                ++Pos;
7,411✔
294
                return TToken(TTokenType::Or, tokenStart);
14,822✔
295
            }
296
            ThrowUnexpected();
×
297
        }
298
        case '&': {
6,214✔
299
            ++Pos;
6,214✔
300
            if (CompareChar('&')) {
6,214✔
301
                ++Pos;
6,214✔
302
                return TToken(TTokenType::And, tokenStart);
12,428✔
303
            }
304
            ThrowUnexpected();
×
305
        }
306
        case '(': {
6,000✔
307
            ++Pos;
6,000✔
308
            return TToken(TTokenType::LeftBr, tokenStart);
12,000✔
309
        }
310
        case ')': {
5,996✔
311
            ++Pos;
5,996✔
312
            return TToken(TTokenType::RightBr, tokenStart);
11,992✔
313
        }
314
    }
315
    return nullopt;
50,870✔
316
}
317

318
std::optional<TToken> TLexer::GetNumber()
50,870✔
319
{
320
    std::string value;
101,740✔
321
    auto tokenStart = Pos - Start;
50,870✔
322
    if (*Pos == '-') {
50,870✔
323
        ++Pos;
2✔
324
        value += '-';
2✔
325
    }
326
    if (SomethingToParse() && isdigit(*Pos)) {
50,870✔
327
        value += *Pos;
24,847✔
328
        ++Pos;
24,847✔
329
        for (; SomethingToParse() && isdigit(*Pos); ++Pos) {
58,437✔
330
            value += *Pos;
33,590✔
331
        }
332
        return TToken(TTokenType::Number, tokenStart, value);
49,694✔
333
    }
334
    if (!value.empty()) {
26,023✔
335
        ThrowUnexpected();
×
336
    }
337
    return nullopt;
26,023✔
338
}
339

340
std::optional<TToken> TLexer::GetIdent()
26,023✔
341
{
342
    if (isalpha(*Pos)) {
26,023✔
343
        auto tokenStart = Pos - Start;
26,018✔
344
        std::string identifier;
26,018✔
345
        identifier += *Pos;
26,018✔
346
        ++Pos;
26,018✔
347
        for (; SomethingToParse() && (isalpha(*Pos) || isdigit(*Pos) || *Pos == '_'); ++Pos) {
342,867✔
348
            identifier += *Pos;
316,849✔
349
        }
350
        return TToken(TTokenType::Ident, tokenStart, identifier);
52,036✔
351
    }
352
    return nullopt;
5✔
353
}
354

355
std::vector<TToken> TLexer::GetTokens(const std::string& str)
11,461✔
356
{
357
    Start = str.begin();
11,461✔
358
    Pos = Start;
11,461✔
359
    End = str.end();
11,461✔
360

361
    std::vector<TToken> tokens;
11,461✔
362
    while (SomethingToParse()) {
112,780✔
363
        auto token = GetOpOrBracket();
101,329✔
364
        if (token) {
101,324✔
365
            tokens.emplace_back(*token);
50,454✔
366
            continue;
50,454✔
367
        }
368
        token = GetNumber();
50,870✔
369
        if (token) {
50,870✔
370
            tokens.emplace_back(*token);
24,847✔
371
            continue;
24,847✔
372
        }
373
        token = GetIdent();
26,023✔
374
        if (token) {
26,023✔
375
            tokens.emplace_back(*token);
26,018✔
376
        } else {
377
            ThrowUnexpected();
5✔
378
        }
379
    }
380
    tokens.emplace_back(TToken(TTokenType::EOL, Pos - Start));
11,456✔
381
    return tokens;
11,456✔
382
}
383

384
std::unique_ptr<TAstNode> TParser::ParseFunction()
54,932✔
385
{
386
    if (Token->Type != TTokenType::Ident) {
54,932✔
387
        return nullptr;
29,877✔
388
    }
389
    auto nextToken = Token + 1;
25,055✔
390
    if (nextToken->Type != TTokenType::LeftBr) {
25,055✔
391
        return nullptr;
24,106✔
392
    }
393
    ++nextToken;
949✔
394
    if (nextToken->Type != TTokenType::Ident) {
949✔
395
        ThrowMissingIdentifierError(nextToken->Pos);
2✔
396
    }
397
    if (Token->Value != "isDefined") {
947✔
398
        ThrowUnknownFunctionError(Token->Value, Token->Pos);
1✔
399
    }
400
    auto node = std::make_unique<TAstNode>(TAstNodeType::Func, Token->Value);
1,892✔
401
    node->SetRight(std::make_unique<TAstNode>(*nextToken));
946✔
402
    ++nextToken;
946✔
403
    if (nextToken->Type != TTokenType::RightBr) {
946✔
404
        ThrowMissingRightBracketError(nextToken->Pos);
2✔
405
    }
406
    Token = ++nextToken;
944✔
407
    return node;
944✔
408
}
409

410
std::unique_ptr<TAstNode> TParser::ParseOperand()
54,932✔
411
{
412
    auto fn = ParseFunction();
109,859✔
413
    if (fn) {
54,927✔
414
        return fn;
944✔
415
    }
416
    if (Token->Type == TTokenType::Number || Token->Type == TTokenType::Ident) {
53,983✔
417
        auto node = std::make_unique<TAstNode>(*Token);
97,868✔
418
        ++Token;
48,934✔
419
        return node;
48,934✔
420
    }
421
    auto node = ParseConditionWithBrackets();
10,095✔
422
    if (node) {
5,046✔
423
        return node;
5,043✔
424
    }
425
    ThrowMissingIdentifierOrNumberOrLeftBracketError(Token->Pos);
3✔
426
    return nullptr;
×
427
}
428

429
std::unique_ptr<TAstNode> TParser::ParseConditionWithBrackets()
5,049✔
430
{
431
    if (Token->Type != TTokenType::LeftBr) {
5,049✔
432
        return nullptr;
3✔
433
    }
434
    std::vector<std::unique_ptr<TAstNode>> tokens;
10,092✔
435
    ++Token;
5,046✔
436
    auto res = ParseExpression();
10,092✔
437
    if (Token->Type != TTokenType::RightBr) {
5,046✔
438
        ThrowMissingRightBracketError(Token->Pos);
3✔
439
    }
440
    ++Token;
5,043✔
441
    return res;
5,043✔
442
}
443

444
std::unique_ptr<TAstNode> TParser::ParseCondition()
16,491✔
445
{
446
    std::vector<std::unique_ptr<TAstNode>> tokens;
32,982✔
447
    tokens.emplace_back(ParseOperand());
16,491✔
448
    while (IsOperator(Token->Type)) {
54,921✔
449
        tokens.emplace_back(std::make_unique<TAstNode>(*Token));
38,441✔
450
        ++Token;
38,441✔
451
        tokens.emplace_back(ParseOperand());
38,441✔
452
    }
453
    return TreeByPriority(tokens.begin(), tokens.end());
32,960✔
454
}
455

456
std::unique_ptr<TAstNode> TParser::ParseExpression()
16,491✔
457
{
458
    auto root = ParseCondition();
32,971✔
459
    if (root) {
16,480✔
460
        return root;
16,480✔
461
    }
462
    root = ParseConditionWithBrackets();
×
463
    if (root) {
×
464
        return root;
×
465
    }
466
    ThrowMissingIdentifierOrNumberOrLeftBracketError(Token->Pos);
×
467
    return nullptr;
×
468
}
469

470
std::unique_ptr<TAstNode> TParser::Parse(const std::string& str)
11,450✔
471
{
472
    TLexer lexer;
11,450✔
473
    auto tokens = lexer.GetTokens(str);
22,895✔
474
    Token = tokens.cbegin();
11,445✔
475
    auto root = ParseExpression();
11,445✔
476
    if (Token->Type != TTokenType::EOL) {
11,434✔
477
        if (root) {
8✔
478
            ThrowMissingOperatorError(Token->Pos);
8✔
479
        }
480
        ThrowParserError("unexpected symbol at position ", Token->Pos);
×
481
    }
482
    return root;
22,852✔
483
}
484

485
bool Expressions::Eval(const Expressions::TAstNode* expr, const IParams& params)
73✔
486
{
487
    auto res = EvalImpl(expr, params);
73✔
488
    return res && res.value();
73✔
489
}
490

491
std::vector<std::string> Expressions::GetDependencies(const TAstNode* expression)
127,250✔
492
{
493
    std::set<std::string> dependencies;
254,500✔
494
    if (expression) {
127,250✔
495
        if (expression->GetType() == TAstNodeType::Ident) {
80,579✔
496
            dependencies.insert(expression->GetValue());
22,788✔
497
        } else {
498
            auto leftDeps = GetDependencies(expression->GetLeft());
115,582✔
499
            dependencies.insert(leftDeps.begin(), leftDeps.end());
57,791✔
500
            auto rightDeps = GetDependencies(expression->GetRight());
115,582✔
501
            dependencies.insert(rightDeps.begin(), rightDeps.end());
57,791✔
502
        }
503
    }
504
    return std::vector<std::string>(dependencies.begin(), dependencies.end());
254,500✔
505
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc