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

vla5924-practice / compiler-project / 13882632105

16 Mar 2025 10:53AM UTC coverage: 77.273% (+1.1%) from 76.198%
13882632105

Pull #220

github

web-flow
Merge 82e5a7d0f into b0d9df536
Pull Request #220: Support lists as function arguments

66 of 85 new or added lines in 5 files covered. (77.65%)

108 existing lines in 3 files now uncovered.

4369 of 5654 relevant lines covered (77.27%)

248.14 hits per line

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

88.11
/compiler/lib/frontend/parser/parser.cpp
1
#include "parser/parser.hpp"
2

3
#include <cassert>
4
#include <functional>
5
#include <iterator>
6
#include <memory>
7
#include <stack>
8
#include <stdexcept>
9
#include <string>
10
#include <unordered_map>
11
#include <variant>
12

13
#include "compiler/ast/node.hpp"
14
#include "compiler/ast/node_type.hpp"
15
#include "compiler/ast/types.hpp"
16
#include "compiler/utils/error_buffer.hpp"
17

18
#include "lexer/token.hpp"
19
#include "lexer/token_types.hpp"
20
#include "parser/parser_context.hpp"
21
#include "parser/parser_error.hpp"
22
#include "parser/type_registry.hpp"
23

24
using namespace ast;
25
using namespace lexer;
26
using namespace parser;
27

28
namespace {
29

30
using SubExpression = std::variant<TokenIterator, Node::Ptr>;
31

32
bool isVariableDeclaration(const TokenIterator &tokenIter, const TokenIterator &tokenEnd) {
307✔
33
    if (tokenIter == tokenEnd || std::next(tokenIter) == tokenEnd || std::next(tokenIter, 2) == tokenEnd)
307✔
34
        return false;
11✔
35

36
    const Token &varName = *tokenIter;
296✔
37
    const Token &colon = *std::next(tokenIter);
296✔
38
    const Token &varType = *std::next(tokenIter, 2);
296✔
39
    return varName.type == TokenType::Identifier && colon.is(Special::Colon) || TypeRegistry::isTypename(varType);
296✔
40
}
41

42
enum class OperationType {
43
    Unknown,
44
    Unary,
45
    Binary,
46
};
47

48
enum class ExpressionTokenType {
49
    Unknown,
50
    Operation,
51
    Operand,
52
    OpeningBrace,
53
    ClosingBrace,
54
    RectBrace,
55
};
56

57
OperationType getOperationType(const Token &token) {
672✔
58
    if (token.type == TokenType::Operator) {
672✔
59
        const auto &op = token.op();
652✔
60
        switch (op) {
652✔
61
        case Operator::Add:
652✔
62
        case Operator::Sub:
63
        case Operator::Mult:
64
        case Operator::Div:
65
        case Operator::Equal:
66
        case Operator::NotEqual:
67
        case Operator::Less:
68
        case Operator::Greater:
69
        case Operator::LessEqual:
70
        case Operator::GreaterEqual:
71
        case Operator::Assign:
72
            return OperationType::Binary;
652✔
73
        default:
×
UNCOV
74
            return OperationType::Unknown;
×
75
        }
76
    }
77
    if (token.type == TokenType::Keyword) {
20✔
78
        const auto &kw = token.kw();
20✔
79
        switch (kw) {
20✔
80
        case Keyword::And:
20✔
81
        case Keyword::Or:
82
            return OperationType::Binary;
20✔
83
        case Keyword::Not:
×
84
            return OperationType::Unary;
×
85
        default:
×
UNCOV
86
            return OperationType::Unknown;
×
87
        }
88
    }
UNCOV
89
    return OperationType::Unknown;
×
90
}
91

92
OperationType getOperationType(const Node &node) {
1,132✔
93
    switch (node.type) {
1,132✔
94
    case NodeType::BinaryOperation:
657✔
95
        return OperationType::Binary;
657✔
96
    case NodeType::UnaryOperation:
×
UNCOV
97
        return OperationType::Unary;
×
98
    default:
475✔
99
        return OperationType::Unknown;
475✔
100
    }
101
}
102

103
ExpressionTokenType getExpressionTokenType(const Token &token) {
1,660✔
104
    if (token.type == TokenType::Identifier || token.type == TokenType::IntegerLiteral ||
1,160✔
105
        token.type == TokenType::FloatingPointLiteral || token.type == TokenType::StringLiteral ||
702✔
106
        token.is(Keyword::True) || token.is(Keyword::False))
2,820✔
107
        return ExpressionTokenType::Operand;
1,076✔
108
    if (token.is(Operator::LeftBrace))
584✔
109
        return ExpressionTokenType::OpeningBrace;
46✔
110
    if (token.is(Operator::RightBrace))
538✔
111
        return ExpressionTokenType::ClosingBrace;
23✔
112
    if (token.is(Operator::RectLeftBrace) || token.is(Operator::RectRightBrace))
515✔
113
        return ExpressionTokenType::RectBrace;
62✔
114
    if (getOperationType(token) != OperationType::Unknown)
453✔
115
        return ExpressionTokenType::Operation;
453✔
UNCOV
116
    return ExpressionTokenType::Unknown;
×
117
}
118

119
ExpressionTokenType getExpressionTokenType(const SubExpression &subexpr) {
×
120
    if (std::holds_alternative<Node::Ptr>(subexpr))
×
121
        return ExpressionTokenType::Operand;
×
UNCOV
122
    return getExpressionTokenType(*std::get<TokenIterator>(subexpr));
×
123
}
124

125
BinaryOperation getBinaryOperation(const Token &token) {
381✔
126
    if (token.type == TokenType::Operator) {
381✔
127
        const auto &op = token.op();
370✔
128
        switch (op) {
370✔
129
        case Operator::Add:
134✔
130
            return BinaryOperation::Add;
134✔
131
        case Operator::Sub:
13✔
132
            return BinaryOperation::Sub;
13✔
133
        case Operator::Mult:
48✔
134
            return BinaryOperation::Mult;
48✔
135
        case Operator::Div:
15✔
136
            return BinaryOperation::Div;
15✔
137
        case Operator::Equal:
8✔
138
            return BinaryOperation::Equal;
8✔
139
        case Operator::NotEqual:
×
UNCOV
140
            return BinaryOperation::NotEqual;
×
141
        case Operator::Less:
2✔
142
            return BinaryOperation::Less;
2✔
143
        case Operator::Greater:
6✔
144
            return BinaryOperation::Greater;
6✔
145
        case Operator::LessEqual:
×
146
            return BinaryOperation::LessEqual;
×
147
        case Operator::GreaterEqual:
×
UNCOV
148
            return BinaryOperation::GreaterEqual;
×
149
        case Operator::Assign:
124✔
150
            return BinaryOperation::Assign;
124✔
151
        default:
20✔
152
            return BinaryOperation::Unknown;
20✔
153
        }
154
    }
155
    if (token.type == TokenType::Keyword) {
11✔
156
        const auto &kw = token.kw();
11✔
157
        switch (kw) {
11✔
158
        case Keyword::And:
7✔
159
            return BinaryOperation::And;
7✔
160
        case Keyword::Or:
4✔
161
            return BinaryOperation::Or;
4✔
162
        default:
×
UNCOV
163
            return BinaryOperation::Unknown;
×
164
        }
165
    }
UNCOV
166
    return BinaryOperation::Unknown;
×
167
}
168

169
UnaryOperation getUnaryOperation(const Token &token) {
43✔
170
    if (token.type == TokenType::Operator) {
43✔
171
        const auto &op = token.op();
34✔
172
        switch (op) {
34✔
173
        case Operator::Sub:
34✔
174
            return UnaryOperation::Negative;
34✔
175
        default:
×
UNCOV
176
            return UnaryOperation::Unknown;
×
177
        }
178
    }
179
    if (token.type == TokenType::Keyword) {
9✔
180
        const auto &kw = token.kw();
9✔
181
        switch (kw) {
9✔
182
        case Keyword::Not:
9✔
183
            return UnaryOperation::Not;
9✔
184
        default:
×
UNCOV
185
            return UnaryOperation::Unknown;
×
186
        }
187
    }
UNCOV
188
    return UnaryOperation::Unknown;
×
189
}
190

191
size_t getOperationPriority(const Token &token) {
162✔
192
    BinaryOperation op = getBinaryOperation(token);
162✔
193
    switch (op) {
162✔
194
    case BinaryOperation::Mult:
37✔
195
    case BinaryOperation::Div:
196
        return 10;
37✔
197
    case BinaryOperation::Add:
75✔
198
    case BinaryOperation::Sub:
199
        return 20;
75✔
UNCOV
200
    case BinaryOperation::Less:
×
201
    case BinaryOperation::LessEqual:
202
    case BinaryOperation::Greater:
203
    case BinaryOperation::GreaterEqual:
UNCOV
204
        return 30;
×
205
    case BinaryOperation::Equal:
1✔
206
    case BinaryOperation::NotEqual:
207
        return 35;
1✔
208
    case BinaryOperation::And:
3✔
209
        return 40;
3✔
210
    case BinaryOperation::Or:
2✔
211
        return 50;
2✔
212
    case BinaryOperation::Assign:
24✔
213
        return 60;
24✔
214
    default:
20✔
215
        return -1;
20✔
216
    }
217
    return -1;
218
}
219

220
size_t getOperandCount(OperationType type) {
1,132✔
221
    if (type == OperationType::Binary)
1,132✔
222
        return 2U;
657✔
223
    if (type == OperationType::Unary)
475✔
UNCOV
224
        return 1U;
×
225
    return -1;
475✔
226
}
227

228
bool isFunctionCall(const TokenIterator &tokenIter) {
1,064✔
229
    return tokenIter->type == TokenType::Identifier && std::next(tokenIter)->is(Operator::LeftBrace);
1,064✔
230
}
231

232
bool isListAccessor(const TokenIterator &tokenIter) {
978✔
233
    return tokenIter->type == TokenType::Identifier && std::next(tokenIter)->is(Operator::RectLeftBrace);
978✔
234
}
235

236
bool canBeUnaryOperation(const TokenIterator &tokenIter) {
908✔
237
    if (tokenIter->type == TokenType::Operator) {
908✔
238
        return tokenIter->op() == Operator::Sub || tokenIter->op() == Operator::Add;
355✔
239
    }
240
    if (tokenIter->type == TokenType::Keyword) {
553✔
241
        return tokenIter->kw() == Keyword::Not;
19✔
242
    }
243
    return false;
534✔
244
}
245

246
bool isUnaryOperation(const TokenIterator &tokenIter, const ExpressionTokenType &prevTokenIter) {
908✔
247
    return canBeUnaryOperation(tokenIter) &&
1,023✔
248
           (prevTokenIter == ExpressionTokenType::Operation || prevTokenIter == ExpressionTokenType::OpeningBrace);
1,023✔
249
}
250

251
std::string unescapeStringLiteral(const std::string &str) {
19✔
252
    std::string result;
19✔
253
    result.reserve(str.size());
19✔
254
    std::string::value_type prev = '\0';
19✔
255
    bool backslash = false;
19✔
256
    for (auto c : str) {
59✔
257
        if (!backslash && prev == '\\') {
40✔
258
            backslash = false;
7✔
259
            if (c == '\\')
7✔
260
                backslash = true;
1✔
261
            else if (c == 'n')
6✔
262
                result.back() = '\n';
6✔
263
            else if (c == 'r')
×
264
                result.back() = '\r';
×
265
            else if (c == 't')
×
UNCOV
266
                result.back() = '\t';
×
267
            else
UNCOV
268
                result.push_back(c);
×
269
        } else {
270
            result.push_back(c);
33✔
271
        }
272
        prev = c;
40✔
273
    }
274
    result.shrink_to_fit();
19✔
275
    return result;
19✔
UNCOV
276
}
×
277

278
void buildExpressionSubtree(std::stack<SubExpression> &postfixForm, const Node::Ptr &root, ErrorBuffer &errors) {
475✔
279
    Node::Ptr currNode = root;
475✔
280
    while (!postfixForm.empty()) {
1,388✔
281
        const SubExpression &subexpr = postfixForm.top();
913✔
282
        if (std::holds_alternative<TokenIterator>(subexpr)) {
913✔
283
            const Token &token = *std::get<TokenIterator>(subexpr);
757✔
284
            ExpressionTokenType expType = getExpressionTokenType(token);
757✔
285
            if (expType == ExpressionTokenType::Operation) {
757✔
286
                OperationType opType = getOperationType(token);
219✔
287
                if (opType == OperationType::Binary) {
219✔
288
                    currNode = ParserContext::unshiftChildNode(currNode, NodeType::BinaryOperation, token.ref);
219✔
289
                    currNode->value = getBinaryOperation(token);
219✔
290
                } else if (opType == OperationType::Unary) {
×
UNCOV
291
                    currNode = ParserContext::unshiftChildNode(currNode, NodeType::UnaryOperation, token.ref);
×
292
                } else {
UNCOV
293
                    errors.push<ParserError>(token,
×
294
                                             "Unknown operator found in expression, it must be either unary or binary");
295
                }
296
            } else if (expType == ExpressionTokenType::Operand) {
538✔
297
                if (token.type == TokenType::Identifier) {
538✔
298
                    Node::Ptr node = ParserContext::unshiftChildNode(currNode, NodeType::VariableName, token.ref);
250✔
299
                    node->value = token.id();
250✔
300
                } else if (token.type == TokenType::IntegerLiteral) {
538✔
301
                    Node::Ptr node =
302
                        ParserContext::unshiftChildNode(currNode, NodeType::IntegerLiteralValue, token.ref);
229✔
303
                    try {
304
                        node->value = std::stol(token.literal().c_str());
231✔
305
                    } catch (const std::out_of_range &) {
1✔
306
                        errors.push<ParserError>(token,
1✔
307
                                                 "Failed to convert integer literal. The number is out of range");
308
                    }
1✔
309
                } else if (token.type == TokenType::FloatingPointLiteral) {
288✔
310
                    Node::Ptr node =
311
                        ParserContext::unshiftChildNode(currNode, NodeType::FloatingPointLiteralValue, token.ref);
36✔
312
                    try {
313
                        node->value = std::stod(token.literal());
36✔
314
                    } catch (const std::out_of_range &) {
2✔
315
                        errors.push<ParserError>(token, "Failed to convert float literal. The number is out of range");
2✔
316
                    }
2✔
317
                } else if (token.type == TokenType::StringLiteral) {
59✔
318
                    Node::Ptr node = ParserContext::unshiftChildNode(currNode, NodeType::StringLiteralValue, token.ref);
19✔
319
                    node->value = unescapeStringLiteral(token.literal());
19✔
320
                } else if (token.is(Keyword::False) || token.is(Keyword::True)) {
23✔
321
                    Node::Ptr node =
322
                        ParserContext::unshiftChildNode(currNode, NodeType::BooleanLiteralValue, token.ref);
4✔
323
                    if (token.is(Keyword::True))
4✔
324
                        node->value = true;
2✔
325
                    else if (token.is(Keyword::False))
2✔
326
                        node->value = false;
2✔
327
                }
4✔
328
            }
329
        } else {
330
            // can be FunctionCall node and list ListAccessor
331
            Node::Ptr callNode = std::get<Node::Ptr>(subexpr);
156✔
332
            assert(callNode->type == NodeType::FunctionCall or callNode->type == NodeType::ListAccessor or
156✔
333
                   callNode->type == NodeType::UnaryOperation);
334
            callNode->parent = currNode;
156✔
335
            currNode->children.push_front(callNode);
156✔
336
        }
156✔
337
        while (currNode->children.size() >= getOperandCount(getOperationType(*currNode)))
1,132✔
338
            currNode = currNode->parent;
219✔
339
        postfixForm.pop();
913✔
340
    }
341
}
475✔
342

343
std::stack<SubExpression> generatePostfixForm(TokenIterator tokenIterBegin, TokenIterator tokenIterEnd,
475✔
344
                                              ErrorBuffer &errors) {
345
    std::stack<SubExpression> postfixForm;
475✔
346
    std::stack<TokenIterator> operations;
475✔
347
    ExpressionTokenType prevExpType = ExpressionTokenType::Operation;
475✔
348
    for (auto tokenIter = tokenIterBegin; tokenIter != tokenIterEnd; tokenIter++) {
1,496✔
349
        const Token &token = *tokenIter;
1,021✔
350
        if (isFunctionCall(tokenIter)) {
1,021✔
351
            Node::Ptr funcCallNode = std::make_shared<Node>(NodeType::FunctionCall);
82✔
352
            auto node = ParserContext::pushChildNode(funcCallNode, NodeType::FunctionName, token.ref);
82✔
353
            node->value = token.id();
82✔
354
            auto argsBegin = std::next(tokenIter);
82✔
355
            auto it = argsBegin;
82✔
356
            unsigned nestingLevel = 0;
82✔
357
            do {
358
                if (it->is(Operator::RightBrace))
429✔
359
                    nestingLevel--;
95✔
360
                else if (it->is(Operator::LeftBrace))
334✔
361
                    nestingLevel++;
95✔
362
                it++;
429✔
363
            } while (nestingLevel > 0);
429✔
364
            auto argsEnd = std::prev(it);
82✔
365
            if (std::distance(argsBegin, argsEnd) > 1) {
82✔
366
                auto argsNode = ParserContext::pushChildNode(funcCallNode, NodeType::FunctionArguments, token.ref);
60✔
367
                auto argBegin = std::next(argsBegin);
60✔
368
                for (auto argsIter = argBegin; argsIter != std::next(argsEnd); argsIter++) {
385✔
369
                    if (!argsIter->is(Operator::Comma) && argsIter != argsEnd)
325✔
370
                        continue;
222✔
371
                    const Token &token = *argsIter;
103✔
372
                    std::stack<SubExpression> argPostfixForm = generatePostfixForm(argBegin, argsIter, errors);
103✔
373
                    auto exprNode = ParserContext::pushChildNode(argsNode, NodeType::Expression, token.ref);
103✔
374
                    buildExpressionSubtree(argPostfixForm, exprNode, errors);
103✔
375
                    argBegin = std::next(argsIter);
103✔
376
                }
103✔
377
            }
60✔
378
            postfixForm.emplace(funcCallNode);
82✔
379
            tokenIter = argsEnd;
82✔
380
            prevExpType = ExpressionTokenType::Operand;
82✔
381
            continue;
82✔
382
        }
82✔
383
        if (isListAccessor(tokenIter)) {
939✔
384
            Node::Ptr listAccessorNode = std::make_shared<Node>(NodeType::ListAccessor);
31✔
385
            auto node = ParserContext::pushChildNode(listAccessorNode, NodeType::VariableName, token.ref);
31✔
386
            node->value = token.id();
31✔
387
            auto exprBegin = std::next(tokenIter);
31✔
388
            auto it = exprBegin;
31✔
389
            unsigned nestingLevel = 0;
31✔
390
            do {
391
                if (it->is(Operator::RectRightBrace))
148✔
392
                    nestingLevel--;
40✔
393
                else if (it->is(Operator::RectLeftBrace))
108✔
394
                    nestingLevel++;
40✔
395
                it++;
148✔
396
            } while (nestingLevel > 0);
148✔
397
            std::stack<SubExpression> argPostfixForm = generatePostfixForm(exprBegin, it, errors);
31✔
398
            auto exprNode = ParserContext::pushChildNode(listAccessorNode, NodeType::Expression, token.ref);
31✔
399
            buildExpressionSubtree(argPostfixForm, exprNode, errors);
31✔
400
            postfixForm.emplace(listAccessorNode);
31✔
401
            tokenIter = std::prev(it);
31✔
402
            prevExpType = ExpressionTokenType::Operand;
31✔
403
            continue;
31✔
404
        }
31✔
405
        if (isUnaryOperation(tokenIter, prevExpType)) {
908✔
406
            auto unaryNode = std::make_shared<Node>(NodeType::UnaryOperation);
43✔
407
            unaryNode->value = getUnaryOperation(*tokenIter);
43✔
408
            auto argsBegin = std::next(tokenIter);
43✔
409
            auto it = argsBegin;
43✔
410
            unsigned nestingLevel = 0;
43✔
411
            if (isFunctionCall(it) || isListAccessor(it)) {
43✔
412
                nestingLevel++;
7✔
413
                std::advance(it, 2);
7✔
414
            }
415
            do {
416
                if (it->is(Operator::RightBrace) or it->is(Operator::RectRightBrace))
127✔
417
                    nestingLevel--;
26✔
418
                else if (it->is(Operator::LeftBrace) or it->is(Operator::RectLeftBrace))
101✔
419
                    nestingLevel++;
19✔
420
                it++;
127✔
421
            } while (nestingLevel > 0);
127✔
422
            std::stack<SubExpression> argPostfixForm = generatePostfixForm(argsBegin, it, errors);
43✔
423
            auto exprNode = ParserContext::pushChildNode(unaryNode, NodeType::Expression, token.ref);
43✔
424
            buildExpressionSubtree(argPostfixForm, exprNode, errors);
43✔
425
            postfixForm.emplace(unaryNode);
43✔
426
            tokenIter = std::prev(it);
43✔
427
            prevExpType = ExpressionTokenType::Operand;
43✔
428
            continue;
43✔
429
        }
43✔
430
        ExpressionTokenType expType = getExpressionTokenType(token);
865✔
431
        if (expType == ExpressionTokenType::Operand) {
865✔
432
            postfixForm.emplace(tokenIter);
538✔
433
        } else if (expType == ExpressionTokenType::OpeningBrace) {
327✔
434
            operations.emplace(tokenIter);
23✔
435
        } else if (expType == ExpressionTokenType::ClosingBrace) {
304✔
436
            bool foundBrace = false;
23✔
437
            while (!operations.empty()) {
38✔
438
                if (getExpressionTokenType(*operations.top()) != ExpressionTokenType::OpeningBrace) {
38✔
439
                    postfixForm.emplace(operations.top());
15✔
440
                    operations.pop();
15✔
441
                } else {
442
                    foundBrace = true;
23✔
443
                    break;
23✔
444
                }
445
            }
446
            if (!foundBrace) {
23✔
UNCOV
447
                errors.push<ParserError>(token, "Unexpected closing brance in an expression");
×
448
            }
449
            if (!operations.empty())
23✔
450
                operations.pop(); // remove opening brace
23✔
451
        } else if (expType == ExpressionTokenType::Operation) {
281✔
452
            if (operations.empty() || getOperationPriority(token) < getOperationPriority(*operations.top())) {
219✔
453
                operations.emplace(tokenIter);
205✔
454
            } else {
455
                while (!operations.empty() && getOperationPriority(*operations.top()) <= getOperationPriority(token)) {
28✔
456
                    postfixForm.emplace(operations.top());
14✔
457
                    operations.pop();
14✔
458
                }
459
                operations.emplace(tokenIter);
14✔
460
            }
461
        } else if (expType == ExpressionTokenType::RectBrace) {
62✔
462
            continue;
62✔
463
        } else {
UNCOV
464
            errors.push<ParserError>(token, "Unexpected token inside an expression");
×
465
        }
466
        prevExpType = expType;
803✔
467
    }
468
    while (!operations.empty()) {
665✔
469
        postfixForm.emplace(operations.top());
190✔
470
        operations.pop();
190✔
471
    }
472
    return postfixForm;
950✔
473
}
475✔
474

475
bool isElementaryType(TypeId typeId) {
138✔
476
    return typeId == IntType || typeId == FloatType || typeId == BoolType || typeId == NoneType;
138✔
477
}
478

479
void parseType(ParserContext &ctx) {
154✔
480
    ctx.node = ctx.pushChildNode(NodeType::TypeName);
154✔
481
    ctx.node->value = TypeRegistry::typeId(ctx.token());
154✔
482
    if (ctx.token().is(Keyword::List)) {
154✔
483
        const Token &leftBrace = (ctx.goNextToken(), ctx.token());
9✔
484
        const Token &varTypeList = (ctx.goNextToken(), ctx.token());
9✔
485
        auto typeId = TypeRegistry::typeId(varTypeList);
9✔
486
        const Token &rightBrace = (ctx.goNextToken(), ctx.token());
9✔
487
        if (!leftBrace.is(Operator::RectLeftBrace) || !isElementaryType(typeId) ||
18✔
488
            !rightBrace.is(Operator::RectRightBrace)) {
18✔
NEW
UNCOV
489
            ctx.pushError("Unexpected syntax for list declaration");
×
490
        }
491
        auto node = ctx.pushChildNode(NodeType::TypeName);
9✔
492
        node->value = typeId;
9✔
493
    }
9✔
494
    ctx.goParentNode();
154✔
495
}
154✔
496

497
void parseSimpleStatement(ParserContext &ctx) {
5✔
498
    assert(ctx.tokenIter->is(Keyword::Break) || ctx.tokenIter->is(Keyword::Continue) ||
5✔
499
           ctx.tokenIter->is(Keyword::Pass));
500
    ctx.goNextToken();
5✔
501
    if (!ctx.token().is(Special::EndOfExpression)) {
5✔
502
        ctx.pushError("Unexpected token in a break|continue|pass statement");
×
UNCOV
503
        ctx.goNextExpression();
×
504
    }
505
    ctx.goParentNode();
5✔
506
    ctx.goNextToken();
5✔
507
}
5✔
508

509
void parseBranchRoot(ParserContext &ctx) {
185✔
510
    while (ctx.nestingLevel > 0) {
535✔
511
        if (ctx.tokenIter == ctx.tokenEnd)
535✔
512
            return;
42✔
513
        while (ctx.token().is(Special::EndOfExpression) || ctx.token().is(Special::Colon)) {
875✔
514
            ctx.goNextToken();
472✔
515
            if (ctx.tokenIter == ctx.tokenEnd)
472✔
516
                return;
90✔
517
        }
518
        int currNestingLevel = 0;
403✔
519
        while (ctx.token().is(Special::Indentation)) {
855✔
520
            currNestingLevel++;
452✔
521
            ctx.goNextToken();
452✔
522
        }
523
        if (currNestingLevel > ctx.nestingLevel) {
403✔
UNCOV
524
            ctx.pushError("Unexpected indentation mismatch: " + std::to_string(ctx.nestingLevel) +
×
UNCOV
525
                          " indentation(s) expected, " + std::to_string(currNestingLevel) + " indentation(s) given");
×
526
        } else if (currNestingLevel < ctx.nestingLevel) {
403✔
527
            ctx.goParentNode();
53✔
528
            while (ctx.node->type != NodeType::BranchRoot) {
110✔
529
                if (!ctx.node->parent)
80✔
530
                    break;
23✔
531
                ctx.goParentNode();
57✔
532
            }
533
            ctx.nestingLevel--;
53✔
534
            std::advance(ctx.tokenIter, -currNestingLevel);
53✔
535
            return;
53✔
536
        }
537

538
        const Token &currToken = ctx.token();
350✔
539
        if (currToken.is(Keyword::If)) {
350✔
540
            ctx.node = ctx.pushChildNode(NodeType::IfStatement);
23✔
541
        } else if (currToken.is(Keyword::While)) {
327✔
542
            ctx.node = ctx.pushChildNode(NodeType::WhileStatement);
5✔
543
        } else if (currToken.is(Keyword::For)) {
322✔
544
            ctx.node = ctx.pushChildNode(NodeType::ForStatement);
15✔
545
        } else if (isVariableDeclaration(ctx.tokenIter, ctx.tokenEnd)) {
307✔
546
            ctx.node = ctx.pushChildNode(NodeType::VariableDeclaration);
130✔
547
        } else if (currToken.is(Keyword::Elif) || currToken.is(Keyword::Else)) {
177✔
548
            auto lastNode = ctx.node->children.back();
13✔
549
            if (lastNode->type == NodeType::IfStatement) {
13✔
550
                auto nodeType = currToken.is(Keyword::Elif) ? NodeType::ElifStatement : NodeType::ElseStatement;
13✔
551
                ctx.node = ParserContext::pushChildNode(lastNode, nodeType, currToken.ref);
13✔
552
            } else {
UNCOV
553
                ctx.pushError((currToken.is(Keyword::Elif) ? std::string("elif") : std::string("else")) +
×
554
                              " is not allowed here");
555
            }
556
        } else if (currToken.is(Keyword::Return)) {
177✔
557
            ctx.node = ctx.pushChildNode(NodeType::ReturnStatement);
27✔
558
        } else if (currToken.is(Keyword::Continue)) {
137✔
559
            ctx.node = ctx.pushChildNode(NodeType::ContinueStatement);
1✔
560
            parseSimpleStatement(ctx);
1✔
561
            continue;
1✔
562
        } else if (currToken.is(Keyword::Break)) {
136✔
563
            ctx.node = ctx.pushChildNode(NodeType::BreakStatement);
1✔
564
            parseSimpleStatement(ctx);
1✔
565
            continue;
1✔
566
        } else if (currToken.is(Keyword::Pass)) {
135✔
567
            ctx.node = ctx.pushChildNode(NodeType::PassStatement);
3✔
568
            parseSimpleStatement(ctx);
3✔
569
            continue;
3✔
570
        } else {
571
            ctx.node = ctx.pushChildNode(NodeType::Expression);
132✔
572
        }
573
        ctx.propagate();
345✔
574
    }
575
}
576

577
void parseElifStatement(ParserContext &ctx) {
8✔
578
    assert(ctx.tokenIter->is(Keyword::Elif));
8✔
579
    ctx.goNextToken();
8✔
580
    ctx.node = ctx.pushChildNode(NodeType::Expression);
8✔
581
    ctx.propagate();
8✔
582
    if (!ctx.token().is(Special::Colon)) {
8✔
UNCOV
583
        ctx.pushError("Colon expected here");
×
UNCOV
584
        ctx.goNextExpression();
×
585
    }
586
    ctx.node = ctx.pushChildNode(NodeType::BranchRoot);
8✔
587
    ctx.nestingLevel++;
8✔
588
    ctx.propagate();
8✔
589
}
8✔
590

591
void parseElseStatement(ParserContext &ctx) {
5✔
592
    assert(ctx.tokenIter->is(Keyword::Else));
5✔
593
    ctx.goNextToken();
5✔
594
    if (!ctx.token().is(Special::Colon)) {
5✔
UNCOV
595
        ctx.pushError("Colon expected here");
×
UNCOV
596
        ctx.goNextExpression();
×
597
    }
598
    ctx.node = ctx.pushChildNode(NodeType::BranchRoot);
5✔
599
    ctx.nestingLevel++;
5✔
600
    ctx.propagate();
5✔
601
}
5✔
602

603
void parseExpression(ParserContext &ctx) {
279✔
604
    auto it = ctx.tokenIter;
279✔
605
    while (!it->is(Special::Colon) && !it->is(Special::EndOfExpression))
1,482✔
606
        it++;
1,203✔
607
    const auto &tokenIterBegin = ctx.tokenIter;
279✔
608
    const auto &tokenIterEnd = it;
279✔
609
    std::stack<SubExpression> postfixForm = generatePostfixForm(tokenIterBegin, tokenIterEnd, ctx.errors);
279✔
610
    buildExpressionSubtree(postfixForm, ctx.node, ctx.errors);
279✔
611
    ctx.tokenIter = tokenIterEnd;
279✔
612
    ctx.goParentNode();
279✔
613
}
279✔
614

615
void parseFunctionArguments(ParserContext &ctx) {
129✔
616
    assert(ctx.token().is(Operator::LeftBrace));
129✔
617
    ctx.goNextToken();
129✔
618
    while (!ctx.token().is(Operator::RightBrace)) {
153✔
619
        const Token &argName = ctx.token();
24✔
620
        const Token &colon = (ctx.goNextToken(), ctx.token());
24✔
621
        const Token &argType = (ctx.goNextToken(), ctx.token());
24✔
622
        if (argName.type != TokenType::Identifier || !colon.is(Special::Colon) || !TypeRegistry::isTypename(argType)) {
24✔
UNCOV
623
            ctx.pushError("Function argument declaration is ill-formed");
×
UNCOV
624
            while (!ctx.token().is(Operator::RightBrace) && !ctx.token().is(Special::Colon))
×
625
                ctx.goNextToken();
×
UNCOV
626
            break;
×
627
        }
628
        ctx.node = ctx.pushChildNode(NodeType::FunctionArgument);
24✔
629
        parseType(ctx);
24✔
630
        auto argNameNode = ParserContext::pushChildNode(ctx.node, NodeType::VariableName, argName.ref);
24✔
631
        argNameNode->value = argName.id();
24✔
632
        ctx.goParentNode();
24✔
633
        ctx.goNextToken();
24✔
634
        if (ctx.token().is(Operator::Comma))
24✔
635
            ctx.goNextToken();
10✔
636
    }
24✔
637
    ctx.goParentNode();
129✔
638
    ctx.goNextToken();
129✔
639
}
129✔
640

641
void parseFunctionDefinition(ParserContext &ctx) {
129✔
642
    assert(ctx.tokenIter->is(Keyword::Definition));
129✔
643
    ctx.goNextToken();
129✔
644
    if (ctx.token().type != TokenType::Identifier) {
129✔
UNCOV
645
        ctx.pushError("Given token is not allowed here in function definition");
×
646
    }
647
    ctx.pushChildNode(NodeType::FunctionName)->value = ctx.token().id();
129✔
648
    ctx.goNextToken();
129✔
649
    if (!ctx.token().is(Operator::LeftBrace)) {
129✔
UNCOV
650
        ctx.pushError("Given token is not allowed here in function definition");
×
651
    }
652
    ctx.node = ctx.pushChildNode(NodeType::FunctionArguments);
129✔
653
    ctx.propagate();
129✔
654
    if (!ctx.token().is(Special::Arrow)) {
129✔
UNCOV
655
        ctx.pushError("Function return type is mandatory in its header");
×
656
    }
657
    ctx.goNextToken();
129✔
658
    if (!TypeRegistry::isTypename(ctx.token())) {
129✔
659
        ctx.pushError("Type name not found");
×
660
    }
661
    auto retTypeId = TypeRegistry::typeId(ctx.token());
129✔
662
    if (!isElementaryType(retTypeId))
129✔
NEW
UNCOV
663
        ctx.pushError("Function return type must be one of the following: int, float, bool, None");
×
664
    ctx.pushChildNode(NodeType::FunctionReturnType)->value = retTypeId;
129✔
665
    ctx.goNextToken();
129✔
666
    if (!ctx.token().is(Special::Colon)) {
129✔
UNCOV
667
        ctx.pushError("Colon expected at the end of function header");
×
668
    }
669
    ctx.goNextToken();
129✔
670
    ctx.node = ctx.pushChildNode(NodeType::BranchRoot);
129✔
671
    ctx.nestingLevel = 1;
129✔
672
    ctx.propagate();
129✔
673
}
129✔
674

675
void parseIfStatement(ParserContext &ctx) {
23✔
676
    assert(ctx.tokenIter->is(Keyword::If));
23✔
677
    ctx.goNextToken();
23✔
678
    ctx.node = ctx.pushChildNode(NodeType::Expression);
23✔
679
    ctx.propagate();
23✔
680
    if (!ctx.token().is(Special::Colon)) {
23✔
UNCOV
681
        ctx.pushError("Colon expected here");
×
UNCOV
682
        ctx.goNextExpression();
×
683
    }
684
    ctx.node = ctx.pushChildNode(NodeType::BranchRoot);
23✔
685
    ctx.nestingLevel++;
23✔
686
    ctx.propagate();
23✔
687
}
23✔
688

689
void parseProgramRoot(ParserContext &ctx) {
106✔
690
    while (ctx.tokenIter != ctx.tokenEnd) {
235✔
691
        if (ctx.token().is(Keyword::Definition)) {
129✔
692
            ctx.node = ctx.pushChildNode(NodeType::FunctionDefinition);
129✔
693
            ctx.propagate();
129✔
694
        } else {
UNCOV
695
            ctx.pushError("Function definition was expected");
×
UNCOV
696
            return;
×
697
        }
698
    }
699
}
700

701
void parseReturnStatement(ParserContext &ctx) {
27✔
702
    assert(ctx.tokenIter->is(Keyword::Return));
27✔
703
    ctx.goNextToken();
27✔
704
    if (ctx.token().is(Special::EndOfExpression)) {
27✔
705
        ctx.goParentNode();
13✔
706
        ctx.goNextToken();
13✔
707
        return;
13✔
708
    }
709
    const Token &currToken = ctx.token();
14✔
710
    if (currToken.type != TokenType::FloatingPointLiteral && currToken.type != TokenType::Identifier &&
12✔
711
        currToken.type != TokenType::IntegerLiteral && currToken.type != TokenType::StringLiteral &&
26✔
712
        !currToken.is(Operator::LeftBrace)) {
14✔
UNCOV
713
        ctx.pushError("Expression as function return value was expected");
×
UNCOV
714
        ctx.goNextExpression();
×
UNCOV
715
        return;
×
716
    }
717
    ctx.node = ctx.pushChildNode(NodeType::Expression);
14✔
718
    ctx.propagate();
14✔
719
    ctx.goParentNode();
14✔
720
}
721

722
void parseVariableDeclaration(ParserContext &ctx) {
130✔
723
    const Token &varName = ctx.token();
130✔
724
    ctx.goNextToken();
130✔
725
    const Token &varType = (ctx.goNextToken(), ctx.token());
130✔
726
    parseType(ctx);
130✔
727
    auto node = ctx.pushChildNode(NodeType::VariableName);
130✔
728
    node->value = varName.id();
130✔
729

730
    auto endOfDecl = std::next(ctx.tokenIter);
130✔
731
    if (endOfDecl->is(Special::EndOfExpression)) {
130✔
732
        // declaration without definition
733
        std::advance(ctx.tokenIter, 2);
43✔
734
        ctx.goParentNode();
43✔
735
    } else if (endOfDecl->is(Operator::Assign)) {
87✔
736
        // declaration with definition
737
        ctx.node = ctx.pushChildNode(NodeType::Expression);
87✔
738
        if (varType.is(Keyword::List)) {
87✔
739
            ctx.node = ctx.pushChildNode(NodeType::ListStatement);
7✔
740
        }
741
        std::advance(ctx.tokenIter, 2);
87✔
742
        ctx.propagate();
87✔
743
        ctx.goParentNode();
87✔
744
    } else {
745
        ctx.errors.push<ParserError>(*endOfDecl, "Definition expression or line break was expected");
×
746
    }
747
}
130✔
748

749
void parseWhileStatement(ParserContext &ctx) {
5✔
750
    assert(ctx.tokenIter->is(Keyword::While));
5✔
751
    ctx.goNextToken();
5✔
752
    ctx.node = ctx.pushChildNode(NodeType::Expression);
5✔
753
    ctx.propagate();
5✔
754
    if (!ctx.token().is(Special::Colon)) {
5✔
UNCOV
755
        ctx.pushError("Colon expected here");
×
UNCOV
756
        ctx.goNextExpression();
×
757
    }
758
    ctx.node = ctx.pushChildNode(NodeType::BranchRoot);
5✔
759
    ctx.nestingLevel++;
5✔
760
    ctx.propagate();
5✔
761
}
5✔
762

763
void parseForStatement(ParserContext &ctx) {
15✔
764
    assert(ctx.tokenIter->is(Keyword::For));
15✔
765
    ctx.goNextToken();
15✔
766
    auto forNode = ctx.node;
15✔
767
    auto it = ctx.tokenIter;
15✔
768
    auto forTargets = ParserContext::pushChildNode(forNode, NodeType::ForTargets, ctx.tokenIter->ref);
15✔
769
    while (!it->is(Keyword::In) && !it->is(Special::EndOfExpression)) {
34✔
770
        if (it->type == TokenType::Identifier) {
19✔
771
            auto targetNode = ParserContext::pushChildNode(forTargets, NodeType::VariableName, ctx.tokenIter->ref);
17✔
772
            targetNode->value = it->id();
17✔
773
            it++;
17✔
774
        } else if (it->is(Operator::Comma)) {
19✔
775
            it++;
2✔
776
        } else {
UNCOV
777
            ctx.pushError("Unexpected token in a for statement");
×
778
        }
779
    }
780
    ctx.tokenIter = it;
15✔
781
    ctx.goNextToken();
15✔
782
    ctx.node = ctx.pushChildNode(NodeType::ForIterable);
15✔
783
    ctx.node = ctx.pushChildNode(NodeType::Expression);
15✔
784
    ctx.propagate();
15✔
785
    ctx.goParentNode();
15✔
786
    if (!ctx.token().is(Special::Colon)) {
15✔
787
        ctx.pushError("Colon expected here");
1✔
788
        ctx.goNextExpression();
1✔
789
    }
790
    ctx.node = ctx.pushChildNode(NodeType::BranchRoot);
15✔
791
    ctx.nestingLevel++;
15✔
792
    ctx.propagate();
15✔
793
}
15✔
794

795
void parseListStatement(ParserContext &ctx) {
7✔
796
    assert(ctx.tokenIter->is(Operator::RectLeftBrace));
7✔
797
    while (!ctx.token().is(Operator::RectRightBrace)) {
26✔
798
        ctx.goNextToken();
19✔
799
        auto it = ctx.tokenIter;
19✔
800
        while (!it->is(Operator::Comma) && !it->is(Operator::RectRightBrace))
44✔
801
            it++;
25✔
802
        const auto &tokenIterBegin = ctx.tokenIter;
19✔
803
        const auto &tokenIterEnd = it;
19✔
804
        if (tokenIterEnd->is(Special::EndOfExpression)) {
19✔
UNCOV
805
            ctx.errors.push<ParserError>(*tokenIterEnd, "']' was expected");
×
806
        }
807
        ctx.node = ctx.pushChildNode(NodeType::Expression);
19✔
808
        std::stack<SubExpression> postfixForm = generatePostfixForm(tokenIterBegin, tokenIterEnd, ctx.errors);
19✔
809
        buildExpressionSubtree(postfixForm, ctx.node, ctx.errors);
19✔
810
        ctx.tokenIter = tokenIterEnd;
19✔
811
        ctx.goParentNode();
19✔
812
    }
19✔
813
    ctx.goNextToken();
7✔
814
    if (ctx.token().is(Special::EndOfExpression)) {
7✔
815
        ctx.goParentNode();
5✔
816
    } else if (ctx.token().is(Operator::Mult)) {
2✔
817
        ctx.goNextToken();
2✔
818
        ctx.node = ctx.pushChildNode(NodeType::ListDynamicSize);
2✔
819
        ctx.node = ctx.pushChildNode(NodeType::Expression);
2✔
820
        ctx.propagate();
2✔
821
        ctx.goParentNode();
2✔
822
        ctx.goParentNode();
2✔
823
    } else {
UNCOV
824
        ctx.pushError("Either end of line or '*' was expected");
×
825
    }
826
    ctx.goParentNode();
7✔
827
}
7✔
828

829
// clang-format off
830
#define SUBPARSER(NodeTypeVal) {NodeType::NodeTypeVal, parse##NodeTypeVal}
831

832
static std::unordered_map<NodeType, std::function<void(ParserContext &)>> subparsers = {
833
    SUBPARSER(BranchRoot),
834
    SUBPARSER(ElifStatement),
835
    SUBPARSER(ElseStatement),
836
    SUBPARSER(Expression),
837
    SUBPARSER(FunctionArguments),
838
    SUBPARSER(FunctionDefinition),
839
    SUBPARSER(IfStatement),
840
    SUBPARSER(ProgramRoot),
841
    SUBPARSER(ReturnStatement),
842
    SUBPARSER(VariableDeclaration),
843
    SUBPARSER(WhileStatement),
844
    SUBPARSER(ListStatement),
845
    SUBPARSER(ForStatement),
846
};
847
// clang-format on
848

849
} // namespace
850

851
SyntaxTree Parser::process(const TokenList &tokens) {
106✔
852
    SyntaxTree tree;
106✔
853
    tree.root = std::make_shared<Node>(NodeType::ProgramRoot);
106✔
854

855
    ParserContext ctx = {subparsers, tree.root, tokens.begin(), tokens.end(), 0};
106✔
856
    ctx.propagate();
106✔
857
    if (!ctx.errors.empty()) {
106✔
858
        throw ctx.errors;
4✔
859
    }
860

861
    return tree;
204✔
862
}
110✔
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