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

wirenboard / wb-mqtt-serial / 1

20 Oct 2026 04:57AM UTC coverage: 86.817% (+14.0%) from 72.836%
1

Pull #1281

github

fab311
KraPete
Fix device model fallback in device/LoadConfig RPC

The short model register was never read because the extended register
error was converted to TRPCException. The RPC error now says that
the device model could not be read.
Pull Request #1281: Fix device model fallback in device/LoadConfig RPC

31180 of 80922 branches covered (38.53%)

74 of 76 new or added lines in 2 files covered. (97.37%)

1379 existing lines in 84 files now uncovered.

22464 of 25875 relevant lines covered (86.82%)

5205.91 hits per line

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

91.62
/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)
268,606 ✔
14
    {
15
        return (type == TTokenType::Equal) || (type == TTokenType::NotEqual) || (type == TTokenType::Greater) ||
188,836 ✔
16
               (type == TTokenType::Less) || (type == TTokenType::GreaterEqual) || (type == TTokenType::LessEqual) ||
175,268 ✔
17
               (type == TTokenType::Or) || (type == TTokenType::And);
457,442 ✔
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)
1,016,856 ✔
26
    {
27
        switch (type) {
1,016,856 !
28
            case TAstNodeType::Greater:
82,942 ✔
29
            case TAstNodeType::Less:
30
            case TAstNodeType::GreaterEqual:
31
            case TAstNodeType::LessEqual:
32
                return 9;
82,942 ✔
33
            case TAstNodeType::Equal:
479,422 ✔
34
            case TAstNodeType::NotEqual:
35
                return 10;
479,422 ✔
36
            case TAstNodeType::And:
89,482 ✔
37
                return 14;
89,482 ✔
38
            case TAstNodeType::Or:
365,010 ✔
39
                return 15;
365,010 ✔
40
            default:
×
UNCOV
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,
457,118 ✔
48
                                             std::vector<std::unique_ptr<TAstNode>>::iterator end)
49
    {
50
        if (begin + 1 == end) {
457,118 ✔
51
            return std::move(*begin);
268,602 ✔
52
        }
53
        // Iterate over even nodes (operators)
54
        auto rootIt = begin + 1;
188,516 ✔
55
        for (auto it = rootIt; it != end && it + 1 != end; it += 2) {
696,944 !
56
            if (GetPriority((*rootIt)->GetType()) <= GetPriority((*it)->GetType())) {
508,428 ✔
57
                rootIt = it;
351,000 ✔
58
            }
59
        }
60
        std::unique_ptr<TAstNode> root(std::move(*rootIt));
188,516 ✔
61
        root->SetLeft(TreeByPriority(begin, rootIt));
188,516 !
62
        root->SetRight(TreeByPriority(rootIt + 1, end));
188,516 !
63
        return root;
188,516 ✔
64
    }
188,516 ✔
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 !
UNCOV
74
    }
×
75

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

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

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

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

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

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

176
    std::vector<std::string> GetDependencies(const TAstNode* expression)
801,538 ✔
177
    {
178
        std::set<std::string> dependencies;
801,538 ✔
179
        if (expression) {
801,538 ✔
180
            if (expression->GetType() == TAstNodeType::Ident) {
509,014 ✔
181
                dependencies.insert(expression->GetValue());
142,664 !
182
            } else {
183
                auto leftDeps = GetDependencies(expression->GetLeft());
366,350 !
184
                dependencies.insert(leftDeps.begin(), leftDeps.end());
366,350 !
185
                auto rightDeps = GetDependencies(expression->GetRight());
366,350 !
186
                dependencies.insert(rightDeps.begin(), rightDeps.end());
366,350 !
187
            }
366,350 ✔
188
        }
189
        return std::vector<std::string>(dependencies.begin(), dependencies.end());
2,404,614 !
190
    }
801,538 ✔
191
}
192

193
TToken::TToken(TTokenType type, size_t pos, const std::string& value): Type(type), Value(value), Pos(pos)
552,106 ✔
194
{}
552,106 ✔
195

196
TAstNode::TAstNode(const TToken& token)
429,176 ✔
197
{
198
    const std::unordered_map<TTokenType, TAstNodeType> types = {{TTokenType::Number, TAstNodeType::Number},
199
                                                                {TTokenType::Ident, TAstNodeType::Ident},
200
                                                                {TTokenType::Equal, TAstNodeType::Equal},
201
                                                                {TTokenType::NotEqual, TAstNodeType::NotEqual},
202
                                                                {TTokenType::Greater, TAstNodeType::Greater},
203
                                                                {TTokenType::Less, TAstNodeType::Less},
204
                                                                {TTokenType::GreaterEqual, TAstNodeType::GreaterEqual},
205
                                                                {TTokenType::LessEqual, TAstNodeType::LessEqual},
206
                                                                {TTokenType::Or, TAstNodeType::Or},
207
                                                                {TTokenType::And, TAstNodeType::And}};
858,352 !
208
    auto it = types.find(token.Type);
429,176 !
209
    if (it != types.end()) {
429,176 !
210
        Type = it->second;
429,176 ✔
211
    } else {
UNCOV
212
        throw std::runtime_error("Can't make AST node");
×
213
    }
214
    Value = token.Value;
429,176 !
215
}
429,176 ✔
216

217
TAstNode::TAstNode(const TAstNodeType& type, const std::string& value): Type(type), Value(value)
4,930 !
218
{}
4,930 ✔
219

220
void TAstNode::SetLeft(std::unique_ptr<TAstNode> node)
188,516 ✔
221
{
222
    Left.swap(node);
188,516 ✔
223
}
188,516 ✔
224

225
void TAstNode::SetRight(std::unique_ptr<TAstNode> node)
193,446 ✔
226
{
227
    Right.swap(node);
193,446 ✔
228
}
193,446 ✔
229

230
const TAstNode* TAstNode::GetLeft() const
367,018 ✔
231
{
232
    return Left.get();
367,018 ✔
233
}
234

235
const TAstNode* TAstNode::GetRight() const
366,918 ✔
236
{
237
    return Right.get();
366,918 ✔
238
}
239

240
const std::string& TAstNode::GetValue() const
143,674 ✔
241
{
242
    return Value;
143,674 ✔
243
}
244

245
TAstNodeType TAstNode::GetType() const
1,527,528 ✔
246
{
247
    return Type;
1,527,528 ✔
248
}
249

250
void TLexer::ThrowUnexpected() const
12 ✔
251
{
252
    if (!SomethingToParse()) {
12 !
UNCOV
253
        throw std::runtime_error("unexpected end of line");
×
254
    }
255
    throw std::runtime_error(std::string("unexpected symbol '") + *Pos + "' at position " +
36 !
256
                             std::to_string(Pos - Start + 1));
48 !
257
}
258

259
bool TLexer::SomethingToParse() const
3,062,738 ✔
260
{
261
    return Pos != End;
3,062,738 ✔
262
}
263

264
bool TLexer::CompareChar(char c) const
188,532 ✔
265
{
266
    return SomethingToParse() && (*Pos == c);
188,532 !
267
}
268

269
std::optional<TToken> TLexer::GetOpOrBracket()
499,970 ✔
270
{
271
    auto tokenStart = Pos - Start;
499,970 ✔
272
    switch (*Pos) {
499,970 ✔
273
        case '=': {
79,780 ✔
274
            ++Pos;
79,780 ✔
275
            if (CompareChar('=')) {
79,780 !
276
                ++Pos;
79,780 ✔
277
                return TToken(TTokenType::Equal, tokenStart);
79,780 !
278
            }
UNCOV
279
            ThrowUnexpected();
×
280
        }
281
        case '!': {
12,662 ✔
282
            ++Pos;
12,662 ✔
283
            if (CompareChar('=')) {
12,662 !
284
                ++Pos;
12,662 ✔
285
                return TToken(TTokenType::NotEqual, tokenStart);
12,662 !
286
            }
UNCOV
287
            ThrowUnexpected();
×
288
        }
289
        case '<': {
3,512 ✔
290
            ++Pos;
3,512 ✔
291
            if (CompareChar('=')) {
3,512 ✔
292
                ++Pos;
3,492 ✔
293
                return TToken(TTokenType::LessEqual, tokenStart);
3,492 !
294
            }
295
            return TToken(TTokenType::Less, tokenStart);
20 !
296
        }
297
        case '>': {
24,010 ✔
298
            ++Pos;
24,010 ✔
299
            if (CompareChar('=')) {
24,010 ✔
300
                ++Pos;
23,124 ✔
301
                return TToken(TTokenType::GreaterEqual, tokenStart);
23,124 !
302
            }
303
            return TToken(TTokenType::Greater, tokenStart);
886 !
304
        }
305
        case '|': {
32,476 ✔
306
            ++Pos;
32,476 ✔
307
            if (CompareChar('|')) {
32,476 !
308
                ++Pos;
32,476 ✔
309
                return TToken(TTokenType::Or, tokenStart);
32,476 !
310
            }
UNCOV
311
            ThrowUnexpected();
×
312
        }
313
        case '&': {
36,092 ✔
314
            ++Pos;
36,092 ✔
315
            if (CompareChar('&')) {
36,092 !
316
                ++Pos;
36,092 ✔
317
                return TToken(TTokenType::And, tokenStart);
36,092 !
318
            }
UNCOV
319
            ThrowUnexpected();
×
320
        }
321
        case '(': {
32,906 ✔
322
            ++Pos;
32,906 ✔
323
            return TToken(TTokenType::LeftBr, tokenStart);
32,906 !
324
        }
325
        case ')': {
32,898 ✔
326
            ++Pos;
32,898 ✔
327
            return TToken(TTokenType::RightBr, tokenStart);
32,898 !
328
        }
329
    }
330
    return nullopt;
245,634 ✔
331
}
332

333
std::optional<TToken> TLexer::GetNumber()
245,634 ✔
334
{
335
    std::string value;
245,634 ✔
336
    auto tokenStart = Pos - Start;
245,634 ✔
337
    if (*Pos == '-') {
245,634 ✔
338
        ++Pos;
4 ✔
339
        value += '-';
4 !
340
    }
341
    if (SomethingToParse() && isdigit(*Pos)) {
245,634 !
342
        value += *Pos;
120,130 !
343
        ++Pos;
120,130 ✔
344
        for (; SomethingToParse() && isdigit(*Pos); ++Pos) {
255,002 ✔
345
            value += *Pos;
134,872 !
346
        }
347
        return TToken(TTokenType::Number, tokenStart, value);
120,130 !
348
    }
349
    if (!value.empty()) {
125,504 !
UNCOV
350
        ThrowUnexpected();
×
351
    }
352
    return nullopt;
125,504 ✔
353
}
245,634 ✔
354

355
std::optional<TToken> TLexer::GetIdent()
125,504 ✔
356
{
357
    if (isalpha(*Pos)) {
125,504 ✔
358
        auto tokenStart = Pos - Start;
125,492 ✔
359
        std::string identifier;
125,492 ✔
360
        identifier += *Pos;
125,492 !
361
        ++Pos;
125,492 ✔
362
        for (; SomethingToParse() && (isalpha(*Pos) || isdigit(*Pos) || *Pos == '_'); ++Pos) {
1,821,440 ✔
363
            identifier += *Pos;
1,695,948 !
364
        }
365
        return TToken(TTokenType::Ident, tokenStart, identifier);
125,492 !
366
    }
125,492 ✔
367
    return nullopt;
12 ✔
368
}
369

370
std::vector<TToken> TLexer::GetTokens(const std::string& str)
52,160 ✔
371
{
372
    Start = str.begin();
52,160 ✔
373
    Pos = Start;
52,160 ✔
374
    End = str.end();
52,160 ✔
375

376
    std::vector<TToken> tokens;
52,160 ✔
377
    while (SomethingToParse()) {
552,118 ✔
378
        auto token = GetOpOrBracket();
499,970 !
379
        if (token) {
499,970 ✔
380
            tokens.emplace_back(*token);
254,336 !
381
            continue;
254,336 ✔
382
        }
383
        token = GetNumber();
245,634 !
384
        if (token) {
245,634 ✔
385
            tokens.emplace_back(*token);
120,130 !
386
            continue;
120,130 ✔
387
        }
388
        token = GetIdent();
125,504 !
389
        if (token) {
125,504 ✔
390
            tokens.emplace_back(*token);
125,492 !
391
        } else {
392
            ThrowUnexpected();
12 !
393
        }
394
    }
499,970 ✔
395
    tokens.emplace_back(TToken(TTokenType::EOL, Pos - Start));
52,148 !
396
    return tokens;
52,148 ✔
397
}
12 ✔
398

399
std::unique_ptr<TAstNode> TParser::ParseFunction()
268,628 ✔
400
{
401
    if (Token->Type != TTokenType::Ident) {
268,628 ✔
402
        return nullptr;
148,080 ✔
403
    }
404
    auto nextToken = Token + 1;
120,548 ✔
405
    if (nextToken->Type != TTokenType::LeftBr) {
120,548 ✔
406
        return nullptr;
115,612 ✔
407
    }
408
    ++nextToken;
4,936 ✔
409
    if (nextToken->Type != TTokenType::Ident) {
4,936 ✔
410
        ThrowMissingIdentifierError(nextToken->Pos);
4 !
411
    }
412
    if (Token->Value != "isDefined") {
4,932 !
413
        ThrowUnknownFunctionError(Token->Value, Token->Pos);
2 !
414
    }
415
    auto node = std::make_unique<TAstNode>(TAstNodeType::Func, Token->Value);
4,930 !
416
    node->SetRight(std::make_unique<TAstNode>(*nextToken));
4,930 !
417
    ++nextToken;
4,930 ✔
418
    if (nextToken->Type != TTokenType::RightBr) {
4,930 ✔
419
        ThrowMissingRightBracketError(nextToken->Pos);
4 !
420
    }
421
    Token = ++nextToken;
4,926 ✔
422
    return node;
4,926 ✔
423
}
4,930 ✔
424

425
std::unique_ptr<TAstNode> TParser::ParseOperand()
268,628 ✔
426
{
427
    auto fn = ParseFunction();
268,628 ✔
428
    if (fn) {
268,618 ✔
429
        return fn;
4,926 ✔
430
    }
431
    if (Token->Type == TTokenType::Number || Token->Type == TTokenType::Ident) {
263,692 ✔
432
        auto node = std::make_unique<TAstNode>(*Token);
235,726 !
433
        ++Token;
235,726 ✔
434
        return node;
235,726 ✔
435
    }
235,726 ✔
436
    auto node = ParseConditionWithBrackets();
27,966 ✔
437
    if (node) {
27,960 ✔
438
        return node;
27,954 ✔
439
    }
440
    ThrowMissingIdentifierOrNumberOrLeftBracketError(Token->Pos);
6 !
UNCOV
441
    return nullptr;
×
442
}
268,624 ✔
443

444
std::unique_ptr<TAstNode> TParser::ParseConditionWithBrackets()
27,966 ✔
445
{
446
    if (Token->Type != TTokenType::LeftBr) {
27,966 ✔
447
        return nullptr;
6 ✔
448
    }
449
    std::vector<std::unique_ptr<TAstNode>> tokens;
27,960 ✔
450
    ++Token;
27,960 ✔
451
    auto res = ParseExpression();
27,960 !
452
    if (Token->Type != TTokenType::RightBr) {
27,960 ✔
453
        ThrowMissingRightBracketError(Token->Pos);
6 !
454
    }
455
    ++Token;
27,954 ✔
456
    return res;
27,954 ✔
457
}
27,966 ✔
458

459
std::unique_ptr<TAstNode> TParser::ParseCondition()
80,108 ✔
460
{
461
    std::vector<std::unique_ptr<TAstNode>> tokens;
80,108 ✔
462
    tokens.emplace_back(ParseOperand());
80,108 !
463
    while (IsOperator(Token->Type)) {
268,606 ✔
464
        tokens.emplace_back(std::make_unique<TAstNode>(*Token));
188,520 !
465
        ++Token;
188,520 ✔
466
        tokens.emplace_back(ParseOperand());
188,520 !
467
    }
468
    return TreeByPriority(tokens.begin(), tokens.end());
160,172 !
469
}
80,108 ✔
470

471
std::unique_ptr<TAstNode> TParser::ParseExpression()
80,108 ✔
472
{
473
    auto root = ParseCondition();
80,108 ✔
474
    if (root) {
80,086 !
475
        return root;
80,086 ✔
476
    }
UNCOV
477
    root = ParseConditionWithBrackets();
×
UNCOV
478
    if (root) {
×
479
        return root;
×
480
    }
UNCOV
481
    ThrowMissingIdentifierOrNumberOrLeftBracketError(Token->Pos);
×
UNCOV
482
    return nullptr;
×
483
}
80,086 ✔
484

485
std::unique_ptr<TAstNode> TParser::Parse(const std::string& str)
52,160 ✔
486
{
487
    TLexer lexer;
52,160 ✔
488
    auto tokens = lexer.GetTokens(str);
52,160 ✔
489
    Token = tokens.cbegin();
52,148 ✔
490
    auto root = ParseExpression();
52,148 ✔
491
    if (Token->Type != TTokenType::EOL) {
52,126 ✔
492
        if (root) {
16 !
493
            ThrowMissingOperatorError(Token->Pos);
16 !
494
        }
UNCOV
495
        ThrowParserError("unexpected symbol at position ", Token->Pos);
×
496
    }
497
    return root;
52,110 ✔
498
}
52,164 ✔
499

500
bool Expressions::Eval(const Expressions::TAstNode* expr, const IParams& params)
376 ✔
501
{
502
    auto res = EvalImpl(expr, params);
376 !
503
    return res && res.value();
752 !
504
}
505

506
std::vector<std::string> Expressions::GetDependencies(const std::string& expression, TExpressionsCache& cache)
76,526 ✔
507
{
508
    if (expression.empty()) {
76,526 ✔
509
        return {};
7,688 ✔
510
    }
511
    auto it = cache.find(expression);
68,838 !
512
    if (it == cache.end()) {
68,838 ✔
513
        TParser parser;
34,272 ✔
514
        it = cache.emplace(expression, parser.Parse(expression)).first;
34,272 !
515
    }
516
    return ::GetDependencies(it->second.get());
68,838 !
517
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc