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

wirenboard / wb-mqtt-serial / 714

31 Oct 2025 05:56AM UTC coverage: 76.986% (+3.6%) from 73.348%
714

push

github

web-flow
Allow using domain names in RPC requests (#1010)

6860 of 9092 branches covered (75.45%)

12949 of 16820 relevant lines covered (76.99%)

750.66 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)
48,485✔
14
    {
15
        return (type == TTokenType::Equal) || (type == TTokenType::NotEqual) || (type == TTokenType::Greater) ||
33,364✔
16
               (type == TTokenType::Less) || (type == TTokenType::GreaterEqual) || (type == TTokenType::LessEqual) ||
31,000✔
17
               (type == TTokenType::Or) || (type == TTokenType::And);
81,849✔
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)
171,406✔
26
    {
27
        switch (type) {
171,406✔
28
            case TAstNodeType::Greater:
13,694✔
29
            case TAstNodeType::Less:
30
            case TAstNodeType::GreaterEqual:
31
            case TAstNodeType::LessEqual:
32
                return 9;
13,694✔
33
            case TAstNodeType::Equal:
84,348✔
34
            case TAstNodeType::NotEqual:
35
                return 10;
84,348✔
36
            case TAstNodeType::And:
13,915✔
37
                return 14;
13,915✔
38
            case TAstNodeType::Or:
59,449✔
39
                return 15;
59,449✔
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,
81,930✔
48
                                             std::vector<std::unique_ptr<TAstNode>>::iterator end)
49
    {
50
        if (begin + 1 == end) {
81,930✔
51
            return std::move(*begin);
48,483✔
52
        }
53
        // Iterate over even nodes (operators)
54
        auto rootIt = begin + 1;
33,447✔
55
        for (auto it = rootIt; it != end && it + 1 != end; it += 2) {
119,150✔
56
            if (GetPriority((*rootIt)->GetType()) <= GetPriority((*it)->GetType())) {
85,703✔
57
                rootIt = it;
59,912✔
58
            }
59
        }
60
        std::unique_ptr<TAstNode> root(std::move(*rootIt));
66,894✔
61
        root->SetLeft(TreeByPriority(begin, rootIt));
33,447✔
62
        root->SetRight(TreeByPriority(rootIt + 1, end));
33,447✔
63
        return root;
33,447✔
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)
99,681✔
179
{}
99,681✔
180

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

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

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

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

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

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

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

230
TAstNodeType TAstNode::GetType() const
244,690✔
231
{
232
    return Type;
244,690✔
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
536,103✔
245
{
246
    return Pos != End;
536,103✔
247
}
248

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

254
std::optional<TToken> TLexer::GetOpOrBracket()
89,054✔
255
{
256
    auto tokenStart = Pos - Start;
89,054✔
257
    switch (*Pos) {
89,054✔
258
        case '=': {
15,137✔
259
            ++Pos;
15,137✔
260
            if (CompareChar('=')) {
15,137✔
261
                ++Pos;
15,137✔
262
                return TToken(TTokenType::Equal, tokenStart);
30,274✔
263
            }
264
            ThrowUnexpected();
×
265
        }
266
        case '!': {
2,231✔
267
            ++Pos;
2,231✔
268
            if (CompareChar('=')) {
2,231✔
269
                ++Pos;
2,231✔
270
                return TToken(TTokenType::NotEqual, tokenStart);
4,462✔
271
            }
272
            ThrowUnexpected();
×
273
        }
274
        case '<': {
553✔
275
            ++Pos;
553✔
276
            if (CompareChar('=')) {
553✔
277
                ++Pos;
543✔
278
                return TToken(TTokenType::LessEqual, tokenStart);
1,086✔
279
            }
280
            return TToken(TTokenType::Less, tokenStart);
20✔
281
        }
282
        case '>': {
4,010✔
283
            ++Pos;
4,010✔
284
            if (CompareChar('=')) {
4,010✔
285
                ++Pos;
3,887✔
286
                return TToken(TTokenType::GreaterEqual, tokenStart);
7,774✔
287
            }
288
            return TToken(TTokenType::Greater, tokenStart);
246✔
289
        }
290
        case '|': {
5,693✔
291
            ++Pos;
5,693✔
292
            if (CompareChar('|')) {
5,693✔
293
                ++Pos;
5,693✔
294
                return TToken(TTokenType::Or, tokenStart);
11,386✔
295
            }
296
            ThrowUnexpected();
×
297
        }
298
        case '&': {
5,842✔
299
            ++Pos;
5,842✔
300
            if (CompareChar('&')) {
5,842✔
301
                ++Pos;
5,842✔
302
                return TToken(TTokenType::And, tokenStart);
11,684✔
303
            }
304
            ThrowUnexpected();
×
305
        }
306
        case '(': {
5,306✔
307
            ++Pos;
5,306✔
308
            return TToken(TTokenType::LeftBr, tokenStart);
10,612✔
309
        }
310
        case ')': {
5,302✔
311
            ++Pos;
5,302✔
312
            return TToken(TTokenType::RightBr, tokenStart);
10,604✔
313
        }
314
    }
315
    return nullopt;
44,980✔
316
}
317

318
std::optional<TToken> TLexer::GetNumber()
44,980✔
319
{
320
    std::string value;
89,960✔
321
    auto tokenStart = Pos - Start;
44,980✔
322
    if (*Pos == '-') {
44,980✔
323
        ++Pos;
2✔
324
        value += '-';
2✔
325
    }
326
    if (SomethingToParse() && isdigit(*Pos)) {
44,980✔
327
        value += *Pos;
21,945✔
328
        ++Pos;
21,945✔
329
        for (; SomethingToParse() && isdigit(*Pos); ++Pos) {
48,815✔
330
            value += *Pos;
26,870✔
331
        }
332
        return TToken(TTokenType::Number, tokenStart, value);
43,890✔
333
    }
334
    if (!value.empty()) {
23,035✔
335
        ThrowUnexpected();
×
336
    }
337
    return nullopt;
23,035✔
338
}
339

340
std::optional<TToken> TLexer::GetIdent()
23,035✔
341
{
342
    if (isalpha(*Pos)) {
23,035✔
343
        auto tokenStart = Pos - Start;
23,030✔
344
        std::string identifier;
23,030✔
345
        identifier += *Pos;
23,030✔
346
        ++Pos;
23,030✔
347
        for (; SomethingToParse() && (isalpha(*Pos) || isdigit(*Pos) || *Pos == '_'); ++Pos) {
309,151✔
348
            identifier += *Pos;
286,121✔
349
        }
350
        return TToken(TTokenType::Ident, tokenStart, identifier);
46,060✔
351
    }
352
    return nullopt;
5✔
353
}
354

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

361
    std::vector<TToken> tokens;
10,637✔
362
    while (SomethingToParse()) {
99,686✔
363
        auto token = GetOpOrBracket();
89,059✔
364
        if (token) {
89,054✔
365
            tokens.emplace_back(*token);
44,074✔
366
            continue;
44,074✔
367
        }
368
        token = GetNumber();
44,980✔
369
        if (token) {
44,980✔
370
            tokens.emplace_back(*token);
21,945✔
371
            continue;
21,945✔
372
        }
373
        token = GetIdent();
23,035✔
374
        if (token) {
23,035✔
375
            tokens.emplace_back(*token);
23,030✔
376
        } else {
377
            ThrowUnexpected();
5✔
378
        }
379
    }
380
    tokens.emplace_back(TToken(TTokenType::EOL, Pos - Start));
10,632✔
381
    return tokens;
10,632✔
382
}
383

384
std::unique_ptr<TAstNode> TParser::ParseFunction()
48,496✔
385
{
386
    if (Token->Type != TTokenType::Ident) {
48,496✔
387
        return nullptr;
26,355✔
388
    }
389
    auto nextToken = Token + 1;
22,141✔
390
    if (nextToken->Type != TTokenType::LeftBr) {
22,141✔
391
        return nullptr;
21,266✔
392
    }
393
    ++nextToken;
875✔
394
    if (nextToken->Type != TTokenType::Ident) {
875✔
395
        ThrowMissingIdentifierError(nextToken->Pos);
2✔
396
    }
397
    if (Token->Value != "isDefined") {
873✔
398
        ThrowUnknownFunctionError(Token->Value, Token->Pos);
1✔
399
    }
400
    auto node = std::make_unique<TAstNode>(TAstNodeType::Func, Token->Value);
1,744✔
401
    node->SetRight(std::make_unique<TAstNode>(*nextToken));
872✔
402
    ++nextToken;
872✔
403
    if (nextToken->Type != TTokenType::RightBr) {
872✔
404
        ThrowMissingRightBracketError(nextToken->Pos);
2✔
405
    }
406
    Token = ++nextToken;
870✔
407
    return node;
870✔
408
}
409

410
std::unique_ptr<TAstNode> TParser::ParseOperand()
48,496✔
411
{
412
    auto fn = ParseFunction();
96,987✔
413
    if (fn) {
48,491✔
414
        return fn;
870✔
415
    }
416
    if (Token->Type == TTokenType::Number || Token->Type == TTokenType::Ident) {
47,621✔
417
        auto node = std::make_unique<TAstNode>(*Token);
86,384✔
418
        ++Token;
43,192✔
419
        return node;
43,192✔
420
    }
421
    auto node = ParseConditionWithBrackets();
8,855✔
422
    if (node) {
4,426✔
423
        return node;
4,423✔
424
    }
425
    ThrowMissingIdentifierOrNumberOrLeftBracketError(Token->Pos);
3✔
426
    return nullptr;
×
427
}
428

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

444
std::unique_ptr<TAstNode> TParser::ParseCondition()
15,047✔
445
{
446
    std::vector<std::unique_ptr<TAstNode>> tokens;
30,094✔
447
    tokens.emplace_back(ParseOperand());
15,047✔
448
    while (IsOperator(Token->Type)) {
48,485✔
449
        tokens.emplace_back(std::make_unique<TAstNode>(*Token));
33,449✔
450
        ++Token;
33,449✔
451
        tokens.emplace_back(ParseOperand());
33,449✔
452
    }
453
    return TreeByPriority(tokens.begin(), tokens.end());
30,072✔
454
}
455

456
std::unique_ptr<TAstNode> TParser::ParseExpression()
15,047✔
457
{
458
    auto root = ParseCondition();
30,083✔
459
    if (root) {
15,036✔
460
        return root;
15,036✔
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)
10,626✔
471
{
472
    TLexer lexer;
10,626✔
473
    auto tokens = lexer.GetTokens(str);
21,247✔
474
    Token = tokens.cbegin();
10,621✔
475
    auto root = ParseExpression();
10,621✔
476
    if (Token->Type != TTokenType::EOL) {
10,610✔
477
        if (root) {
8✔
478
            ThrowMissingOperatorError(Token->Pos);
8✔
479
        }
480
        ThrowParserError("unexpected symbol at position ", Token->Pos);
×
481
    }
482
    return root;
21,204✔
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)
115,278✔
492
{
493
    std::set<std::string> dependencies;
230,556✔
494
    if (expression) {
115,278✔
495
        if (expression->GetType() == TAstNodeType::Ident) {
72,840✔
496
            dependencies.insert(expression->GetValue());
20,727✔
497
        } else {
498
            auto leftDeps = GetDependencies(expression->GetLeft());
104,226✔
499
            dependencies.insert(leftDeps.begin(), leftDeps.end());
52,113✔
500
            auto rightDeps = GetDependencies(expression->GetRight());
104,226✔
501
            dependencies.insert(rightDeps.begin(), rightDeps.end());
52,113✔
502
        }
503
    }
504
    return std::vector<std::string>(dependencies.begin(), dependencies.end());
230,556✔
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