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

wirenboard / wb-mqtt-serial / 2

29 Dec 2025 12:28PM UTC coverage: 76.817% (+4.0%) from 72.836%
2

Pull #1045

github

54aa0c
pgasheev
up changelog
Pull Request #1045: Fix firmware version in WB-M1W2 template

6873 of 9161 branches covered (75.02%)

12966 of 16879 relevant lines covered (76.82%)

1651.61 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)
109,234✔
14
    {
15
        return (type == TTokenType::Equal) || (type == TTokenType::NotEqual) || (type == TTokenType::Greater) ||
74,020✔
16
               (type == TTokenType::Less) || (type == TTokenType::GreaterEqual) || (type == TTokenType::LessEqual) ||
69,244✔
17
               (type == TTokenType::Or) || (type == TTokenType::And);
183,254✔
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)
439,364✔
26
    {
27
        switch (type) {
439,364✔
28
            case TAstNodeType::Greater:
29,308✔
29
            case TAstNodeType::Less:
30
            case TAstNodeType::GreaterEqual:
31
            case TAstNodeType::LessEqual:
32
                return 9;
29,308✔
33
            case TAstNodeType::Equal:
207,704✔
34
            case TAstNodeType::NotEqual:
35
                return 10;
207,704✔
36
            case TAstNodeType::And:
29,342✔
37
                return 14;
29,342✔
38
            case TAstNodeType::Or:
173,010✔
39
                return 15;
173,010✔
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,
185,800✔
48
                                             std::vector<std::unique_ptr<TAstNode>>::iterator end)
49
    {
50
        if (begin + 1 == end) {
185,800✔
51
            return std::move(*begin);
109,230✔
52
        }
53
        // Iterate over even nodes (operators)
54
        auto rootIt = begin + 1;
76,570✔
55
        for (auto it = rootIt; it != end && it + 1 != end; it += 2) {
296,252✔
56
            if (GetPriority((*rootIt)->GetType()) <= GetPriority((*it)->GetType())) {
219,682✔
57
                rootIt = it;
148,828✔
58
            }
59
        }
60
        std::unique_ptr<TAstNode> root(std::move(*rootIt));
153,140✔
61
        root->SetLeft(TreeByPriority(begin, rootIt));
76,570✔
62
        root->SetRight(TreeByPriority(rootIt + 1, end));
76,570✔
63
        return root;
76,570✔
64
    }
65

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

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

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

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

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

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

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

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

181
TAstNode::TAstNode(const TToken& token)
175,726✔
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}};
351,452✔
193
    auto it = types.find(token.Type);
175,726✔
194
    if (it != types.end()) {
175,726✔
195
        Type = it->second;
175,726✔
196
    } else {
197
        throw std::runtime_error("Can't make AST node");
×
198
    }
199
    Value = token.Value;
175,726✔
200
}
175,726✔
201

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

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

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

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

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

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

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

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

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

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

254
std::optional<TToken> TLexer::GetOpOrBracket()
201,720✔
255
{
256
    auto tokenStart = Pos - Start;
201,720✔
257
    switch (*Pos) {
201,720✔
258
        case '=': {
35,246✔
259
            ++Pos;
35,246✔
260
            if (CompareChar('=')) {
35,246✔
261
                ++Pos;
35,246✔
262
                return TToken(TTokenType::Equal, tokenStart);
70,492✔
263
            }
264
            ThrowUnexpected();
×
265
        }
266
        case '!': {
4,510✔
267
            ++Pos;
4,510✔
268
            if (CompareChar('=')) {
4,510✔
269
                ++Pos;
4,510✔
270
                return TToken(TTokenType::NotEqual, tokenStart);
9,020✔
271
            }
272
            ThrowUnexpected();
×
273
        }
274
        case '<': {
1,346✔
275
            ++Pos;
1,346✔
276
            if (CompareChar('=')) {
1,346✔
277
                ++Pos;
1,326✔
278
                return TToken(TTokenType::LessEqual, tokenStart);
2,652✔
279
            }
280
            return TToken(TTokenType::Less, tokenStart);
40✔
281
        }
282
        case '>': {
8,260✔
283
            ++Pos;
8,260✔
284
            if (CompareChar('=')) {
8,260✔
285
                ++Pos;
8,014✔
286
                return TToken(TTokenType::GreaterEqual, tokenStart);
16,028✔
287
            }
288
            return TToken(TTokenType::Greater, tokenStart);
492✔
289
        }
290
        case '|': {
14,818✔
291
            ++Pos;
14,818✔
292
            if (CompareChar('|')) {
14,818✔
293
                ++Pos;
14,818✔
294
                return TToken(TTokenType::Or, tokenStart);
29,636✔
295
            }
296
            ThrowUnexpected();
×
297
        }
298
        case '&': {
12,428✔
299
            ++Pos;
12,428✔
300
            if (CompareChar('&')) {
12,428✔
301
                ++Pos;
12,428✔
302
                return TToken(TTokenType::And, tokenStart);
24,856✔
303
            }
304
            ThrowUnexpected();
×
305
        }
306
        case '(': {
11,996✔
307
            ++Pos;
11,996✔
308
            return TToken(TTokenType::LeftBr, tokenStart);
23,992✔
309
        }
310
        case ')': {
11,988✔
311
            ++Pos;
11,988✔
312
            return TToken(TTokenType::RightBr, tokenStart);
23,976✔
313
        }
314
    }
315
    return nullopt;
101,128✔
316
}
317

318
std::optional<TToken> TLexer::GetNumber()
101,128✔
319
{
320
    std::string value;
202,256✔
321
    auto tokenStart = Pos - Start;
101,128✔
322
    if (*Pos == '-') {
101,128✔
323
        ++Pos;
4✔
324
        value += '-';
4✔
325
    }
326
    if (SomethingToParse() && isdigit(*Pos)) {
101,128✔
327
        value += *Pos;
49,390✔
328
        ++Pos;
49,390✔
329
        for (; SomethingToParse() && isdigit(*Pos); ++Pos) {
116,570✔
330
            value += *Pos;
67,180✔
331
        }
332
        return TToken(TTokenType::Number, tokenStart, value);
98,780✔
333
    }
334
    if (!value.empty()) {
51,738✔
335
        ThrowUnexpected();
×
336
    }
337
    return nullopt;
51,738✔
338
}
339

340
std::optional<TToken> TLexer::GetIdent()
51,738✔
341
{
342
    if (isalpha(*Pos)) {
51,738✔
343
        auto tokenStart = Pos - Start;
51,728✔
344
        std::string identifier;
51,728✔
345
        identifier += *Pos;
51,728✔
346
        ++Pos;
51,728✔
347
        for (; SomethingToParse() && (isalpha(*Pos) || isdigit(*Pos) || *Pos == '_'); ++Pos) {
678,194✔
348
            identifier += *Pos;
626,466✔
349
        }
350
        return TToken(TTokenType::Ident, tokenStart, identifier);
103,456✔
351
    }
352
    return nullopt;
10✔
353
}
354

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

361
    std::vector<TToken> tokens;
22,622✔
362
    while (SomethingToParse()) {
224,332✔
363
        auto token = GetOpOrBracket();
201,730✔
364
        if (token) {
201,720✔
365
            tokens.emplace_back(*token);
100,592✔
366
            continue;
100,592✔
367
        }
368
        token = GetNumber();
101,128✔
369
        if (token) {
101,128✔
370
            tokens.emplace_back(*token);
49,390✔
371
            continue;
49,390✔
372
        }
373
        token = GetIdent();
51,738✔
374
        if (token) {
51,738✔
375
            tokens.emplace_back(*token);
51,728✔
376
        } else {
377
            ThrowUnexpected();
10✔
378
        }
379
    }
380
    tokens.emplace_back(TToken(TTokenType::EOL, Pos - Start));
22,612✔
381
    return tokens;
22,612✔
382
}
383

384
std::unique_ptr<TAstNode> TParser::ParseFunction()
109,256✔
385
{
386
    if (Token->Type != TTokenType::Ident) {
109,256✔
387
        return nullptr;
59,450✔
388
    }
389
    auto nextToken = Token + 1;
49,806✔
390
    if (nextToken->Type != TTokenType::LeftBr) {
49,806✔
391
        return nullptr;
47,912✔
392
    }
393
    ++nextToken;
1,894✔
394
    if (nextToken->Type != TTokenType::Ident) {
1,894✔
395
        ThrowMissingIdentifierError(nextToken->Pos);
4✔
396
    }
397
    if (Token->Value != "isDefined") {
1,890✔
398
        ThrowUnknownFunctionError(Token->Value, Token->Pos);
2✔
399
    }
400
    auto node = std::make_unique<TAstNode>(TAstNodeType::Func, Token->Value);
3,776✔
401
    node->SetRight(std::make_unique<TAstNode>(*nextToken));
1,888✔
402
    ++nextToken;
1,888✔
403
    if (nextToken->Type != TTokenType::RightBr) {
1,888✔
404
        ThrowMissingRightBracketError(nextToken->Pos);
4✔
405
    }
406
    Token = ++nextToken;
1,884✔
407
    return node;
1,884✔
408
}
409

410
std::unique_ptr<TAstNode> TParser::ParseOperand()
109,256✔
411
{
412
    auto fn = ParseFunction();
218,502✔
413
    if (fn) {
109,246✔
414
        return fn;
1,884✔
415
    }
416
    if (Token->Type == TTokenType::Number || Token->Type == TTokenType::Ident) {
107,362✔
417
        auto node = std::make_unique<TAstNode>(*Token);
194,528✔
418
        ++Token;
97,264✔
419
        return node;
97,264✔
420
    }
421
    auto node = ParseConditionWithBrackets();
20,190✔
422
    if (node) {
10,092✔
423
        return node;
10,086✔
424
    }
425
    ThrowMissingIdentifierOrNumberOrLeftBracketError(Token->Pos);
6✔
426
    return nullptr;
×
427
}
428

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

444
std::unique_ptr<TAstNode> TParser::ParseCondition()
32,682✔
445
{
446
    std::vector<std::unique_ptr<TAstNode>> tokens;
65,364✔
447
    tokens.emplace_back(ParseOperand());
32,682✔
448
    while (IsOperator(Token->Type)) {
109,234✔
449
        tokens.emplace_back(std::make_unique<TAstNode>(*Token));
76,574✔
450
        ++Token;
76,574✔
451
        tokens.emplace_back(ParseOperand());
76,574✔
452
    }
453
    return TreeByPriority(tokens.begin(), tokens.end());
65,320✔
454
}
455

456
std::unique_ptr<TAstNode> TParser::ParseExpression()
32,682✔
457
{
458
    auto root = ParseCondition();
65,342✔
459
    if (root) {
32,660✔
460
        return root;
32,660✔
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)
22,600✔
471
{
472
    TLexer lexer;
22,600✔
473
    auto tokens = lexer.GetTokens(str);
45,190✔
474
    Token = tokens.cbegin();
22,590✔
475
    auto root = ParseExpression();
22,590✔
476
    if (Token->Type != TTokenType::EOL) {
22,568✔
477
        if (root) {
16✔
478
            ThrowMissingOperatorError(Token->Pos);
16✔
479
        }
480
        ThrowParserError("unexpected symbol at position ", Token->Pos);
×
481
    }
482
    return root;
45,104✔
483
}
484

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

491
std::vector<std::string> Expressions::GetDependencies(const TAstNode* expression)
251,498✔
492
{
493
    std::set<std::string> dependencies;
502,996✔
494
    if (expression) {
251,498✔
495
        if (expression->GetType() == TAstNodeType::Ident) {
159,322✔
496
            dependencies.insert(expression->GetValue());
45,080✔
497
        } else {
498
            auto leftDeps = GetDependencies(expression->GetLeft());
228,484✔
499
            dependencies.insert(leftDeps.begin(), leftDeps.end());
114,242✔
500
            auto rightDeps = GetDependencies(expression->GetRight());
228,484✔
501
            dependencies.insert(rightDeps.begin(), rightDeps.end());
114,242✔
502
        }
503
    }
504
    return std::vector<std::string>(dependencies.begin(), dependencies.end());
502,996✔
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