GCC Code Coverage Report


src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
1766 / 1812, 2 excl
97.5%
Branches:
1183 / 1406, 364 excl
84.1%
Functions:
303 / 303
100.0%

parser/parser.spice
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 // Std imports
4 import "std/data/stack";
5 import "std/data/vector";
6 import "std/data/pair";
7 import "std/text/format";
8 import "std/type/type-conversion";
9
10 // Own imports
11 import "@/exception/fatal-error";
12 import "@/compiler-pass";
13 import "@/source-file";
14 import "@/global/global-resource-manager";
15 import "@/lexer/lexer";
16 import "@/lexer/token";
17 import "@/reader/code-loc";
18 import "@/ast/ast-nodes";
19 import "@/ast/attributes";
20 import "@/symboltablebuilder/qual-type";
21 import "@/symboltablebuilder/super-type";
22 import "@/symboltablebuilder/type-qualifiers";
23 import "@/exception/parser-error";
24
25 // External functions
26 ext f<unsigned long> strtoull(string, char**, int);
27
28 /**
29 * Check if the given identifier is a reserved keyword
30 *
31 * @param identifier Identifier to check
32 * @return Reserved or not
33 */
34 530994 public f<bool> isReservedKeyword(const String& identifier) {
35
5/8
✓ Branch 0 taken 530992 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 530992 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 530992 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 530992 times.
530994 return identifier == "new" || identifier == "stash" || identifier == "pick" || identifier == "sync" || identifier == "class";
36 }
37
38 // Generic types
39 type T dyn;
40
41 /**
42 * Builds the AST of a source file from its token stream.
43 */
44 3951 public type Parser struct {
45 compose public CompilerPass compilerPass
46 Lexer& lexer
47 Stack<ASTNode*> parentStack
48 // Number of additional '>' tokens that are still pending due to a previously split SHR ('>>') token.
49 // This is needed to close nested generic type lists, since the lexer merges '>>' into a single token.
50 3963 unsigned int pendingGreaters = 0
51 // Whether a '<' is still pending due to a previously split SHL ('<<') token. This is needed for the template type list
52 // of a generic less operator, e.g. 'operator<<T>(...)', since the lexer merges '<<' into a single token.
53 3963 bool pendingLess = false
54 // Whether a field data type is currently parsed. Its template types are field types as well
55 3963 bool isParsingFieldType = false
56 3963 unsigned long currentNodeId = 0l
57 Vector<IVisitable*> switchStmts
58 }
59
60 3963 public p Parser.ctor(GlobalResourceManager& resourceManager, SourceFile* sourceFile, Lexer& lexer) {
61 3963 this.compilerPass = CompilerPass(resourceManager, sourceFile);
62 3963 this.lexer = lexer;
63 // Make sure the current token is a relevant (non-comment) token
64 3963 this.skipIgnoredTokens();
65 3963 }
66
67 2230956 f<T*> Parser.createNode<T>() {
68 2230956 ASTNode* parent = nil<ASTNode*>;
69 2230956 const bool isRootNode = this.parentStack.isEmpty();
70
78/156
✓ Branch 0 taken 3963 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1034 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 28114 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 17650 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 45763 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 3233 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 327 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 26 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 466 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 3071 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 159 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 4289 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 5229 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 2518 times.
✗ Branch 28 not taken.
✓ Branch 29 taken 12758 times.
✗ Branch 30 not taken.
✓ Branch 31 taken 4405 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 435 times.
✗ Branch 34 not taken.
✓ Branch 35 taken 1865 times.
✗ Branch 36 not taken.
✓ Branch 37 taken 50 times.
✗ Branch 38 not taken.
✓ Branch 39 taken 25175 times.
✗ Branch 40 not taken.
✓ Branch 41 taken 1397 times.
✗ Branch 42 not taken.
✓ Branch 43 taken 39 times.
✗ Branch 44 not taken.
✓ Branch 45 taken 196 times.
✗ Branch 46 not taken.
✓ Branch 47 taken 20 times.
✗ Branch 48 not taken.
✓ Branch 49 taken 126 times.
✗ Branch 50 not taken.
✓ Branch 51 taken 92800 times.
✗ Branch 52 not taken.
✓ Branch 53 taken 30564 times.
✗ Branch 54 not taken.
✓ Branch 55 taken 5157 times.
✗ Branch 56 not taken.
✓ Branch 57 taken 3071 times.
✗ Branch 58 not taken.
✓ Branch 59 taken 33175 times.
✗ Branch 60 not taken.
✓ Branch 61 taken 61637 times.
✗ Branch 62 not taken.
✓ Branch 63 taken 466 times.
✗ Branch 64 not taken.
✓ Branch 65 taken 1749 times.
✗ Branch 66 not taken.
✓ Branch 67 taken 7861 times.
✗ Branch 68 not taken.
✓ Branch 69 taken 749 times.
✗ Branch 70 not taken.
✓ Branch 71 taken 64128 times.
✗ Branch 72 not taken.
✓ Branch 73 taken 83812 times.
✗ Branch 74 not taken.
✓ Branch 75 taken 125596 times.
✗ Branch 76 not taken.
✓ Branch 77 taken 150594 times.
✗ Branch 78 not taken.
✓ Branch 79 taken 1540 times.
✗ Branch 80 not taken.
✓ Branch 81 taken 1574 times.
✗ Branch 82 not taken.
✓ Branch 83 taken 21 times.
✗ Branch 84 not taken.
✓ Branch 85 taken 3135 times.
✗ Branch 86 not taken.
✓ Branch 87 taken 3601 times.
✗ Branch 88 not taken.
✓ Branch 89 taken 201 times.
✗ Branch 90 not taken.
✓ Branch 91 taken 40235 times.
✗ Branch 92 not taken.
✓ Branch 93 taken 741 times.
✗ Branch 94 not taken.
✓ Branch 95 taken 762 times.
✗ Branch 96 not taken.
✓ Branch 97 taken 16 times.
✗ Branch 98 not taken.
✓ Branch 99 taken 4080 times.
✗ Branch 100 not taken.
✓ Branch 101 taken 12449 times.
✗ Branch 102 not taken.
✓ Branch 103 taken 7028 times.
✗ Branch 104 not taken.
✓ Branch 105 taken 398583 times.
✗ Branch 106 not taken.
✓ Branch 107 taken 91119 times.
✗ Branch 108 not taken.
✓ Branch 109 taken 80964 times.
✗ Branch 110 not taken.
✓ Branch 111 taken 80976 times.
✗ Branch 112 not taken.
✓ Branch 113 taken 180 times.
✗ Branch 114 not taken.
✓ Branch 115 taken 1538 times.
✗ Branch 116 not taken.
✓ Branch 117 taken 67 times.
✗ Branch 118 not taken.
✓ Branch 119 taken 73 times.
✗ Branch 120 not taken.
✓ Branch 121 taken 4 times.
✗ Branch 122 not taken.
✓ Branch 123 taken 198427 times.
✗ Branch 124 not taken.
✓ Branch 125 taken 198426 times.
✗ Branch 126 not taken.
✓ Branch 127 taken 72874 times.
✗ Branch 128 not taken.
✓ Branch 129 taken 706 times.
✗ Branch 130 not taken.
✓ Branch 131 taken 30299 times.
✗ Branch 132 not taken.
✓ Branch 133 taken 2600 times.
✗ Branch 134 not taken.
✓ Branch 135 taken 4054 times.
✗ Branch 136 not taken.
✓ Branch 137 taken 2130 times.
✗ Branch 138 not taken.
✓ Branch 139 taken 430 times.
✗ Branch 140 not taken.
✓ Branch 141 taken 77 times.
✗ Branch 142 not taken.
✓ Branch 143 taken 260 times.
✗ Branch 144 not taken.
✓ Branch 145 taken 27120 times.
✗ Branch 146 not taken.
✓ Branch 147 taken 16416 times.
✗ Branch 148 not taken.
✓ Branch 149 taken 607 times.
✗ Branch 150 not taken.
✓ Branch 151 taken 14994 times.
✗ Branch 152 not taken.
✓ Branch 153 taken 3335 times.
✗ Branch 154 not taken.
✓ Branch 155 taken 105647 times.
2230956 if !isRootNode {
71 2226993 parent = this.parentStack.top();
72 2226993 }
73
74 // Reserve storage in the pool and construct the node in place. We construct in place (instead of
75 // copying a prebuilt node) so that AST nodes may hold non-copyable members such as their children.
76 2230956 T* node = this.resourceManager.astNodeAlloc.reserve<T>();
77 unsafe {
78 2230956 __placement_new<T>(node, this.lexer.getCodeLoc());
79 }
80 2230956 this.resourceManager.nodeToNodeId.upsert(getNodeAddress(cast<ASTNode*>(node)), this.currentNodeId++);
81 // Upcast to the composed ASTNode base. This is what the parent/child book-keeping operates on.
82 2230956 ASTNode* nodeBase = cast<ASTNode*>(node);
83 // Remember the node as IVisitable*, so generic child traversal can dispatch to its concrete type
84 2230956 nodeBase.visitable = cast<IVisitable*>(node);
85
86 // If this is not the entry node, we need to add the new node to its parent
87
78/156
✓ Branch 0 taken 3963 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1034 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 28114 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 17650 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 45763 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 3233 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 327 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 26 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 466 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 3071 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 159 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 4289 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 5229 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 2518 times.
✗ Branch 28 not taken.
✓ Branch 29 taken 12758 times.
✗ Branch 30 not taken.
✓ Branch 31 taken 4405 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 435 times.
✗ Branch 34 not taken.
✓ Branch 35 taken 1865 times.
✗ Branch 36 not taken.
✓ Branch 37 taken 50 times.
✗ Branch 38 not taken.
✓ Branch 39 taken 25175 times.
✗ Branch 40 not taken.
✓ Branch 41 taken 1397 times.
✗ Branch 42 not taken.
✓ Branch 43 taken 39 times.
✗ Branch 44 not taken.
✓ Branch 45 taken 196 times.
✗ Branch 46 not taken.
✓ Branch 47 taken 20 times.
✗ Branch 48 not taken.
✓ Branch 49 taken 126 times.
✗ Branch 50 not taken.
✓ Branch 51 taken 92800 times.
✗ Branch 52 not taken.
✓ Branch 53 taken 30564 times.
✗ Branch 54 not taken.
✓ Branch 55 taken 5157 times.
✗ Branch 56 not taken.
✓ Branch 57 taken 3071 times.
✗ Branch 58 not taken.
✓ Branch 59 taken 33175 times.
✗ Branch 60 not taken.
✓ Branch 61 taken 61637 times.
✗ Branch 62 not taken.
✓ Branch 63 taken 466 times.
✗ Branch 64 not taken.
✓ Branch 65 taken 1749 times.
✗ Branch 66 not taken.
✓ Branch 67 taken 7861 times.
✗ Branch 68 not taken.
✓ Branch 69 taken 749 times.
✗ Branch 70 not taken.
✓ Branch 71 taken 64128 times.
✗ Branch 72 not taken.
✓ Branch 73 taken 83812 times.
✗ Branch 74 not taken.
✓ Branch 75 taken 125596 times.
✗ Branch 76 not taken.
✓ Branch 77 taken 150594 times.
✗ Branch 78 not taken.
✓ Branch 79 taken 1540 times.
✗ Branch 80 not taken.
✓ Branch 81 taken 1574 times.
✗ Branch 82 not taken.
✓ Branch 83 taken 21 times.
✗ Branch 84 not taken.
✓ Branch 85 taken 3135 times.
✗ Branch 86 not taken.
✓ Branch 87 taken 3601 times.
✗ Branch 88 not taken.
✓ Branch 89 taken 201 times.
✗ Branch 90 not taken.
✓ Branch 91 taken 40235 times.
✗ Branch 92 not taken.
✓ Branch 93 taken 741 times.
✗ Branch 94 not taken.
✓ Branch 95 taken 762 times.
✗ Branch 96 not taken.
✓ Branch 97 taken 16 times.
✗ Branch 98 not taken.
✓ Branch 99 taken 4080 times.
✗ Branch 100 not taken.
✓ Branch 101 taken 12449 times.
✗ Branch 102 not taken.
✓ Branch 103 taken 7028 times.
✗ Branch 104 not taken.
✓ Branch 105 taken 398583 times.
✗ Branch 106 not taken.
✓ Branch 107 taken 91119 times.
✗ Branch 108 not taken.
✓ Branch 109 taken 80964 times.
✗ Branch 110 not taken.
✓ Branch 111 taken 80976 times.
✗ Branch 112 not taken.
✓ Branch 113 taken 180 times.
✗ Branch 114 not taken.
✓ Branch 115 taken 1538 times.
✗ Branch 116 not taken.
✓ Branch 117 taken 67 times.
✗ Branch 118 not taken.
✓ Branch 119 taken 73 times.
✗ Branch 120 not taken.
✓ Branch 121 taken 4 times.
✗ Branch 122 not taken.
✓ Branch 123 taken 198427 times.
✗ Branch 124 not taken.
✓ Branch 125 taken 198426 times.
✗ Branch 126 not taken.
✓ Branch 127 taken 72874 times.
✗ Branch 128 not taken.
✓ Branch 129 taken 706 times.
✗ Branch 130 not taken.
✓ Branch 131 taken 30299 times.
✗ Branch 132 not taken.
✓ Branch 133 taken 2600 times.
✗ Branch 134 not taken.
✓ Branch 135 taken 4054 times.
✗ Branch 136 not taken.
✓ Branch 137 taken 2130 times.
✗ Branch 138 not taken.
✓ Branch 139 taken 430 times.
✗ Branch 140 not taken.
✓ Branch 141 taken 77 times.
✗ Branch 142 not taken.
✓ Branch 143 taken 260 times.
✗ Branch 144 not taken.
✓ Branch 145 taken 27120 times.
✗ Branch 146 not taken.
✓ Branch 147 taken 16416 times.
✗ Branch 148 not taken.
✓ Branch 149 taken 607 times.
✗ Branch 150 not taken.
✓ Branch 151 taken 14994 times.
✗ Branch 152 not taken.
✓ Branch 153 taken 3335 times.
✗ Branch 154 not taken.
✓ Branch 155 taken 105647 times.
2230956 if !isRootNode {
88 2226993 parent.addChild(nodeBase);
89 2226993 }
90
91 // This node is the parent for its children
92 2230956 this.parentStack.push(nodeBase);
93
94 2230956 return node;
95 }
96
97 2230901 #[ignoreUnusedReturnValue]
98 2230901 f<T*> Parser.concludeNode<T>(T* node) {
99 2230901 assert !this.parentStack.isEmpty();
100 2230901 ASTNode* nodeBase = cast<ASTNode*>(node);
101 2230901 assert this.parentStack.top() == nodeBase;
102 2230901 this.parentStack.pop();
103 // The node ends with the last token, that was consumed for it
104 2230901 const Token& stopToken = this.lexer.getPreviousRelevantToken();
105 2230901 nodeBase.codeLoc.sourceIntervalStop = cast<long>(stopToken.endOffset) - 1l;
106 2230901 return node;
107 }
108
109 // Create a new node, which takes the place of the most recently parsed child of the current parent node and adopts it.
110 // This is used for expressions, where the node type is only known after the first operand was parsed. Pass-through
111 // expressions (e.g. an additive expression with only one operand) therefore never get a node.
112 207969 f<T*> Parser.createNodeWrappingLastChild<T>() {
113 207969 assert !this.parentStack.isEmpty();
114 207969 ASTNode* parent = this.parentStack.top();
115 207969 assert !parent.children.isEmpty();
116 207969 ASTNode* child = parent.children.back();
117 207969 parent.children.popBack();
118
119 // Create the node in place of the child. It starts where its first child starts
120 207969 T* node = this.createNode<T>();
121 207969 ASTNode* nodeBase = cast<ASTNode*>(node);
122 207969 nodeBase.codeLoc = child.codeLoc;
123
124 // Adopt the child
125 207969 nodeBase.addChild(child);
126
127 207969 return node;
128 }
129
130 // Move a child of the given parent node to the end of its children. The children of a node are listed in the order of its
131 // members, which differs from the parse order for some nodes. This is used to establish that order.
132 21 p Parser.moveChildToEnd(ASTNode* parent, ASTNode* child) {
133 21 const unsigned long childCount = parent.children.getSize();
134
1/2
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
21 for unsigned long i = 0l; i < childCount; i++ {
135
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 if parent.children.get(i) == child {
136 21 parent.children.removeAt(i);
137 21 parent.children.pushBack(child);
138 21 return;
139 }
140 ✗ }
141 ✗ assert false; // Child not found
142 }
143
144 227446 f<IVisitable*> Parser.concludeExprNode<T>(T* node) {
145 227446 this.concludeNode(node);
146 227446 return cast<IVisitable*>(node);
147 }
148
149 inline f<bool> Parser.currentTokenIs(TokenType expectedTokenType) {
150 return this.lexer.getToken().tokenType == expectedTokenType;
151 }
152
153 3010983 f<bool> Parser.currentTokenIsOneOf(TokenType[] expectedTokenTypeArray, unsigned long size) {
154
2/2
✓ Branch 0 taken 10940617 times.
✓ Branch 1 taken 2785699 times.
13726316 for unsigned long i = 0l; i < size; i++ {
155
4/4
✓ Branch 0 taken 225284 times.
✓ Branch 1 taken 10715333 times.
✓ Branch 2 taken 225284 times.
✓ Branch 3 taken 10715333 times.
21881234 if this.currentTokenIs(expectedTokenTypeArray[i]) {
156 225284 return true;
157 }
158 10715333 }
159 2785699 return false;
160 }
161
162 // =================================================== Token stream helpers ===================================================
163
164 inline f<bool> Parser.isIgnoredTokenType(TokenType tokenType) {
165 return tokenType == TokenType::LINE_COMMENT || tokenType == TokenType::BLOCK_COMMENT || tokenType == TokenType::DOC_COMMENT;
166 }
167
168 2568070 p Parser.skipIgnoredTokens() {
169
4/4
✓ Branch 0 taken 2616987 times.
✓ Branch 1 taken 46816 times.
✓ Branch 2 taken 95733 times.
✓ Branch 3 taken 2568070 times.
5280790 while this.isIgnoredTokenType(this.lexer.getToken().tokenType) {
170 95733 this.lexer.advance();
171 95733 }
172 2568070 }
173
174 // Advance to the next relevant token, transparently skipping comment tokens.
175 2564107 p Parser.advance() {
176 2564107 this.lexer.advance();
177 2564107 this.skipIgnoredTokens();
178 2564107 }
179
180 // Assert that the current token has the expected type and advance afterwards.
181 1654356 p Parser.expect(TokenType expectedTokenType) {
182
4/4
✓ Branch 0 taken 1654354 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1654354 times.
✓ Branch 3 taken 2 times.
3308712 if !this.currentTokenIs(expectedTokenType) {
183 2 const Token& token = this.lexer.getToken();
184 2 const String message = "Unexpected token '" + token.text + "'";
185 2 this.throwParserError(token.codeLoc, ParserErrorType::PARSING_FAILED, message.getRaw());
186 2 }
187 1654356 this.advance();
188 1654356 }
189
190 // Consume the current token, if it has the expected type.
191 inline f<bool> Parser.consumeIf(TokenType expectedTokenType) {
192 if !this.currentTokenIs(expectedTokenType) {
193 return false;
194 }
195 this.advance();
196 return true;
197 }
198
199 // Return the type of the token at the given offset, where offset 0 is the current token.
200 // Comment tokens are skipped, so the offset counts relevant tokens only. The lexer resolves this
201 // in O(1) via its precomputed comment-free index, which keeps speculative lookahead cheap.
202 4479612 f<TokenType> Parser.tokenAt(unsigned long offset) {
203 4479612 return this.lexer.peekRelevantType(offset);
204 }
205
206 // Check if a generic type list opens at the current position. A '<' might still be pending from a split SHL ('<<') token.
207 45763 f<bool> Parser.isGenericOpenAhead() {
208
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 45762 times.
45763 return this.pendingLess || this.currentTokenIs(TokenType::LESS);
209 }
210
211 // Open a generic type list. Uses up a pending '<' from a split SHL ('<<') token, if there is one.
212 6897 p Parser.consumeGenericOpen() {
213
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 6896 times.
6897 if this.pendingLess {
214 1 this.pendingLess = false;
215 1 return;
216 }
217 6896 this.expect(TokenType::LESS);
218 6896 }
219
220 // Close a generic type list. Handles the case where the lexer merged two closing
221 // angle brackets of nested generic lists into a single SHR ('>>') token.
222 79917 p Parser.consumeGenericClose() {
223 // A previously split SHR still provides one '>' for us
224
2/2
✓ Branch 0 taken 2678 times.
✓ Branch 1 taken 77239 times.
79917 if this.pendingGreaters > 0 {
225 2678 this.pendingGreaters--;
226 2678 return;
227 }
228 // A single SHR token closes two generic lists at once
229
4/4
✓ Branch 0 taken 2678 times.
✓ Branch 1 taken 74561 times.
✓ Branch 2 taken 2678 times.
✓ Branch 3 taken 74561 times.
154478 if this.currentTokenIs(TokenType::SHR) {
230 2678 this.pendingGreaters = 1;
231 2678 this.advance();
232 2678 return;
233 }
234 74561 this.expect(TokenType::GREATER);
235 74561 }
236
237 // ===================================================== Lookahead matchers ====================================================
238 // These functions inspect (but do not consume) upcoming tokens to disambiguate grammar alternatives.
239 // They return the offset right after the matched construct, or -1 if no match was possible.
240
241 inline f<bool> Parser.isQualifierType(TokenType tokenType) {
242 return tokenType == TokenType::CONST || tokenType == TokenType::SIGNED || tokenType == TokenType::UNSIGNED
243 || tokenType == TokenType::INLINE || tokenType == TokenType::PUBLIC || tokenType == TokenType::HEAP
244 || tokenType == TokenType::COMPOSE;
245 }
246
247 inline f<bool> Parser.isBaseDataTypeKeyword(TokenType tokenType) {
248 return tokenType == TokenType::TYPE_DOUBLE || tokenType == TokenType::TYPE_INT || tokenType == TokenType::TYPE_SHORT
249 || tokenType == TokenType::TYPE_LONG || tokenType == TokenType::TYPE_BYTE || tokenType == TokenType::TYPE_CHAR
250 || tokenType == TokenType::TYPE_STRING || tokenType == TokenType::TYPE_BOOL || tokenType == TokenType::TYPE_DYN;
251 }
252
253 inline f<bool> Parser.isConstantStart(TokenType tokenType) {
254 return tokenType == TokenType::DOUBLE_LIT || tokenType == TokenType::INT_LIT || tokenType == TokenType::SHORT_LIT
255 || tokenType == TokenType::LONG_LIT || tokenType == TokenType::CHAR_LIT || tokenType == TokenType::STRING_LIT
256 || tokenType == TokenType::TRUE || tokenType == TokenType::FALSE;
257 }
258
259 inline f<bool> Parser.isAssignOp(TokenType tokenType) {
260 return tokenType == TokenType::ASSIGN || tokenType == TokenType::PLUS_EQUAL || tokenType == TokenType::MINUS_EQUAL
261 || tokenType == TokenType::MUL_EQUAL || tokenType == TokenType::DIV_EQUAL || tokenType == TokenType::REM_EQUAL
262 || tokenType == TokenType::SHL_EQUAL || tokenType == TokenType::SHR_EQUAL || tokenType == TokenType::AND_EQUAL
263 || tokenType == TokenType::OR_EQUAL || tokenType == TokenType::XOR_EQUAL;
264 }
265
266 // Check if the current token is a minus sign that belongs to a negative numeric constant
267 inline f<bool> Parser.isNegativeNumericConstantAhead() {
268 if !this.currentTokenIs(TokenType::MINUS) {
269 return false;
270 }
271 const TokenType nextTokenType = this.tokenAt(1l);
272 return nextTokenType == TokenType::DOUBLE_LIT || nextTokenType == TokenType::INT_LIT
273 || nextTokenType == TokenType::SHORT_LIT || nextTokenType == TokenType::LONG_LIT;
274 }
275
276 // Skip a balanced angle-bracket group starting at the given offset (which must be a LESS token).
277 // Accounts for SHL ('<<') and SHR ('>>') tokens which open/close two levels at once.
278 17529 f<long> Parser.matchAngleGroup(long offset) {
279 17529 long depth = 0l;
280
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 61667 times.
61667 while true {
281 61667 const TokenType tokenType = this.tokenAt(cast<unsigned long>(offset));
282
2/2
✓ Branch 0 taken 54463 times.
✓ Branch 1 taken 7204 times.
61667 if !this.isTypeLstToken(tokenType) {
283 7204 return -1l; // This token can not be part of a type list, so this is no generic type list
284
2/2
✓ Branch 0 taken 17891 times.
✓ Branch 1 taken 36572 times.
54463 } else if tokenType == TokenType::LESS {
285 17891 depth++;
286
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36572 times.
54463 } else if tokenType == TokenType::SHL {
287 ✗ depth += 2l;
288
2/2
✓ Branch 0 taken 10323 times.
✓ Branch 1 taken 26249 times.
36572 } else if tokenType == TokenType::GREATER {
289 10323 depth--;
290
2/2
✓ Branch 0 taken 26076 times.
✓ Branch 1 taken 173 times.
36572 } else if tokenType == TokenType::SHR {
291 173 depth -= 2l;
292 54463 }
293 54463 offset++;
294
2/2
✓ Branch 0 taken 44138 times.
✓ Branch 1 taken 10325 times.
54463 if depth <= 0l {
295 10325 return offset;
296 }
297 44138 }
298 ✗ panic(Error("Unreachable")); // The loop above only exits via return
299 }
300
301 // Get the text of the current identifier token and check that it is not reserved. The token is not consumed.
302 530994 f<String> Parser.getIdentifier(bool isTypeIdentifier) {
303 530994 const Token& token = this.lexer.getToken();
304 530994 const String& identifier = token.text;
305
306 // Check if the list of reserved keywords contains the given identifier
307
2/2
✓ Branch 0 taken 530992 times.
✓ Branch 1 taken 2 times.
530994 if isReservedKeyword(identifier) {
308 2 const String message = "'" + identifier + "' is a reserved keyword. Please use another name instead";
309 2 this.throwParserError(token.codeLoc, ParserErrorType::RESERVED_KEYWORD, message.getRaw());
310 2 }
311
312 // Check if the identifier is a type identifier and is reserved
313
6/6
✓ Branch 0 taken 38595 times.
✓ Branch 1 taken 492399 times.
✓ Branch 2 taken 37190 times.
✓ Branch 3 taken 1405 times.
✓ Branch 4 taken 530993 times.
✓ Branch 5 taken 1 time.
530994 if isTypeIdentifier && !this.sourceFile.isStdFile && isReservedTypeName(identifier) {
314 1 const String message = "'" + identifier + "' is a reserved type name. Please use another one instead";
315 1 this.throwParserError(token.codeLoc, ParserErrorType::RESERVED_TYPENAME, message.getRaw());
316 1 }
317
318 530994 return identifier;
319 }
320
321 // Check if the given token type can appear within a type list (typeLst), including the enclosing angle brackets.
322 61667 f<bool> Parser.isTypeLstToken(TokenType tokenType) {
323
8/8
✓ Branch 0 taken 61454 times.
✓ Branch 1 taken 213 times.
✓ Branch 2 taken 1545 times.
✓ Branch 3 taken 60122 times.
✓ Branch 4 taken 59899 times.
✓ Branch 5 taken 223 times.
✓ Branch 6 taken 5860 times.
✓ Branch 7 taken 55807 times.
183020 if this.isQualifierType(tokenType) || this.isBaseDataTypeKeyword(tokenType) {
324 5860 return true;
325 }
326 const dyn options = [
327 TokenType::IDENTIFIER, TokenType::TYPE_IDENTIFIER, TokenType::SCOPE_ACCESS, TokenType::COMMA, TokenType::LESS,
328 TokenType::GREATER, TokenType::SHL, TokenType::SHR, TokenType::MUL, TokenType::BITWISE_AND, TokenType::LBRACKET,
329 55807 TokenType::RBRACKET, TokenType::INT_LIT, TokenType::F, TokenType::P, TokenType::LPAREN, TokenType::RPAREN
330 ];
331
2/2
✓ Branch 0 taken 347641 times.
✓ Branch 1 taken 7204 times.
354845 for unsigned long i = 0l; i < len(options); i++ {
332
2/2
✓ Branch 0 taken 48603 times.
✓ Branch 1 taken 299038 times.
347641 if tokenType == options[i] {
333 48603 return true;
334 }
335 299038 }
336 7204 return false;
337 }
338
339 // Skip a balanced group delimited by the given open/close token types, starting at an open token.
340 4280 f<long> Parser.matchBalancedGroup(long offset, TokenType openType, TokenType closeType) {
341 4280 long depth = 0l;
342
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23915 times.
23915 while true {
343 23915 const TokenType tokenType = this.tokenAt(cast<unsigned long>(offset));
344
1/2
✓ Branch 0 taken 23915 times.
✗ Branch 1 not taken.
23915 if tokenType == TokenType::EOF {
345 ✗ return -1l;
346
2/2
✓ Branch 0 taken 4640 times.
✓ Branch 1 taken 19275 times.
23915 } else if tokenType == openType {
347 4640 depth++;
348
2/2
✓ Branch 0 taken 14635 times.
✓ Branch 1 taken 4640 times.
23915 } else if tokenType == closeType {
349 4640 depth--;
350 23915 }
351 23915 offset++;
352
2/2
✓ Branch 0 taken 19635 times.
✓ Branch 1 taken 4280 times.
23915 if depth <= 0l {
353 4280 return offset;
354 }
355 19635 }
356 ✗ panic(Error("Unreachable")); // The loop above only exits via return
357 }
358
359 // Try to match a dataType at the given offset. Returns the offset right after the dataType, or -1.
360 88947 f<long> Parser.matchDataType(long offset) {
361 // Qualifiers
362
4/4
✓ Branch 0 taken 102232 times.
✓ Branch 1 taken 9906 times.
✓ Branch 2 taken 23191 times.
✓ Branch 3 taken 88947 times.
214370 while this.isQualifierType(this.tokenAt(cast<unsigned long>(offset))) {
363 23191 offset++;
364 23191 }
365 // Base data type
366 88947 const TokenType baseType = this.tokenAt(cast<unsigned long>(offset));
367
4/4
✓ Branch 0 taken 88774 times.
✓ Branch 1 taken 173 times.
✓ Branch 2 taken 17068 times.
✓ Branch 3 taken 71879 times.
177721 if this.isBaseDataTypeKeyword(baseType) {
368 17068 offset++;
369
4/4
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 71829 times.
✓ Branch 2 taken 104 times.
✓ Branch 3 taken 71775 times.
88947 } else if baseType == TokenType::F || baseType == TokenType::P {
370 104 offset++;
371
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 50 times.
104 if this.tokenAt(cast<unsigned long>(offset)) == TokenType::LESS {
372 50 offset = this.matchAngleGroup(offset);
373
1/2
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
50 if offset < 0l { return -1l; }
374 50 }
375
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 104 times.
104 if this.tokenAt(cast<unsigned long>(offset)) != TokenType::LPAREN { return -1l; }
376 104 offset = this.matchBalancedGroup(offset, TokenType::LPAREN, TokenType::RPAREN);
377
1/2
✓ Branch 0 taken 104 times.
✗ Branch 1 not taken.
104 if offset < 0l { return -1l; }
378
4/4
✓ Branch 0 taken 63766 times.
✓ Branch 1 taken 8009 times.
✓ Branch 2 taken 71711 times.
✓ Branch 3 taken 64 times.
71879 } else if baseType == TokenType::IDENTIFIER || baseType == TokenType::TYPE_IDENTIFIER {
379 // (IDENTIFIER SCOPE_ACCESS)*
380
4/4
✓ Branch 0 taken 8016 times.
✓ Branch 1 taken 63800 times.
✓ Branch 2 taken 105 times.
✓ Branch 3 taken 71711 times.
71816 while this.tokenAt(cast<unsigned long>(offset)) == TokenType::IDENTIFIER && this.tokenAt(cast<unsigned long>(offset + 1l)) == TokenType::SCOPE_ACCESS {
381 105 offset += 2l;
382 105 }
383
2/2
✓ Branch 0 taken 63695 times.
✓ Branch 1 taken 8016 times.
71711 if this.tokenAt(cast<unsigned long>(offset)) != TokenType::TYPE_IDENTIFIER { return -1l; }
384 8016 offset++;
385
2/2
✓ Branch 0 taken 5641 times.
✓ Branch 1 taken 2375 times.
8016 if this.tokenAt(cast<unsigned long>(offset)) == TokenType::LESS {
386 2375 offset = this.matchAngleGroup(offset);
387
1/2
✓ Branch 0 taken 2375 times.
✗ Branch 1 not taken.
2375 if offset < 0l { return -1l; }
388 2375 }
389 8016 } else {
390 64 return -1l;
391 8120 }
392 // Pointer / reference / array suffixes
393
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 32106 times.
32106 while true {
394 32106 const TokenType suffix = this.tokenAt(cast<unsigned long>(offset));
395
4/4
✓ Branch 0 taken 4205 times.
✓ Branch 1 taken 27901 times.
✓ Branch 2 taken 6701 times.
✓ Branch 3 taken 25405 times.
32106 if suffix == TokenType::MUL || suffix == TokenType::BITWISE_AND {
396 6701 offset++;
397
2/2
✓ Branch 0 taken 25188 times.
✓ Branch 1 taken 217 times.
32106 } else if suffix == TokenType::LBRACKET {
398 217 offset = this.matchBalancedGroup(offset, TokenType::LBRACKET, TokenType::RBRACKET);
399
1/2
✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
217 if offset < 0l { return -1l; }
400 217 } else {
401 25188 break;
402 217 }
403 6918 }
404 25188 return offset;
405 }
406
407 // A statement is a declaration if it starts with a dataType immediately followed by an identifier (the variable name).
408 88947 f<bool> Parser.isDeclStmtAhead() {
409 88947 const long afterType = this.matchDataType(0l);
410
2/2
✓ Branch 0 taken 63759 times.
✓ Branch 1 taken 25188 times.
88947 return afterType >= 0l && this.tokenAt(cast<unsigned long>(afterType)) == TokenType::IDENTIFIER;
411 }
412
413 // Match the name path of a function call: (IDENTIFIER ::)* (IDENTIFIER .)* (IDENTIFIER | TYPE_IDENTIFIER) (< typeLst >)?
414 313478 f<long> Parser.matchFctCallPath(long offset) {
415
4/4
✓ Branch 0 taken 20457 times.
✓ Branch 1 taken 293258 times.
✓ Branch 2 taken 237 times.
✓ Branch 3 taken 313478 times.
313715 while this.tokenAt(cast<unsigned long>(offset)) == TokenType::IDENTIFIER && this.tokenAt(cast<unsigned long>(offset + 1l)) == TokenType::SCOPE_ACCESS {
416 237 offset += 2l;
417 237 }
418
4/4
✓ Branch 0 taken 20457 times.
✓ Branch 1 taken 412210 times.
✓ Branch 2 taken 119189 times.
✓ Branch 3 taken 313478 times.
432667 while this.tokenAt(cast<unsigned long>(offset)) == TokenType::IDENTIFIER && this.tokenAt(cast<unsigned long>(offset + 1l)) == TokenType::DOT {
419 119189 offset += 2l;
420 119189 }
421 313478 const TokenType name = this.tokenAt(cast<unsigned long>(offset));
422
3/4
✓ Branch 0 taken 293021 times.
✓ Branch 1 taken 20457 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 313478 times.
313478 if name != TokenType::IDENTIFIER && name != TokenType::TYPE_IDENTIFIER { return -1l; }
423 313478 offset++;
424
2/2
✓ Branch 0 taken 301253 times.
✓ Branch 1 taken 12225 times.
313478 if this.tokenAt(cast<unsigned long>(offset)) == TokenType::LESS {
425 12225 offset = this.matchAngleGroup(offset);
426
2/2
✓ Branch 0 taken 5021 times.
✓ Branch 1 taken 7204 times.
12225 if offset < 0l { return -1l; }
427 5021 }
428 306274 return offset;
429 }
430
431 // Match the name path of a struct instantiation: (IDENTIFIER ::)* TYPE_IDENTIFIER (< typeLst >)?
432 315016 f<long> Parser.matchStructPath(long offset) {
433
4/4
✓ Branch 0 taken 21995 times.
✓ Branch 1 taken 293265 times.
✓ Branch 2 taken 244 times.
✓ Branch 3 taken 315016 times.
315260 while this.tokenAt(cast<unsigned long>(offset)) == TokenType::IDENTIFIER && this.tokenAt(cast<unsigned long>(offset + 1l)) == TokenType::SCOPE_ACCESS {
434 244 offset += 2l;
435 244 }
436
2/2
✓ Branch 0 taken 293021 times.
✓ Branch 1 taken 21995 times.
315016 if this.tokenAt(cast<unsigned long>(offset)) != TokenType::TYPE_IDENTIFIER { return -1l; }
437 21995 offset++;
438
2/2
✓ Branch 0 taken 20306 times.
✓ Branch 1 taken 1689 times.
21995 if this.tokenAt(cast<unsigned long>(offset)) == TokenType::LESS {
439 1689 offset = this.matchAngleGroup(offset);
440
1/2
✓ Branch 0 taken 1689 times.
✗ Branch 1 not taken.
1689 if offset < 0l { return -1l; }
441 1689 }
442 21995 return offset;
443 }
444
445 313478 f<bool> Parser.isFctCallAhead() {
446 313478 const long after = this.matchFctCallPath(0l);
447
2/2
✓ Branch 0 taken 7204 times.
✓ Branch 1 taken 306274 times.
313478 return after >= 0l && this.tokenAt(cast<unsigned long>(after)) == TokenType::LPAREN;
448 }
449
450 // A struct path followed by a brace group is a struct instantiation, unless the brace group is a statement list. This is
451 // the case for e.g. 'if i == MAX_COUNT { return; }', where the brace group belongs to the if statement.
452 315016 f<bool> Parser.isStructInstantiationAhead() {
453 315016 const long lbraceOffset = this.matchStructPath(0l);
454
4/4
✓ Branch 0 taken 293021 times.
✓ Branch 1 taken 21995 times.
✓ Branch 2 taken 311181 times.
✓ Branch 3 taken 3835 times.
315016 if lbraceOffset < 0l || this.tokenAt(cast<unsigned long>(lbraceOffset)) != TokenType::LBRACE {
455 311181 return false;
456 }
457
458 // A statement list may start with a statement keyword, which can not start an argument list
459 3835 const TokenType firstToken = this.tokenAt(cast<unsigned long>(lbraceOffset + 1l));
460
4/4
✓ Branch 0 taken 184 times.
✓ Branch 1 taken 3651 times.
✓ Branch 2 taken 184 times.
✓ Branch 3 taken 3651 times.
3835 if this.isStmtStartKeyword(firstToken) || firstToken == TokenType::LBRACE {
461 184 return false;
462 }
463
464 // A statement list contains semicolons on its top level, while an argument list does not
465 3651 long offset = lbraceOffset;
466 3651 long depth = 0l;
467
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42839 times.
42839 while true {
468 42839 const TokenType tokenType = this.tokenAt(cast<unsigned long>(offset));
469
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42839 times.
42839 if tokenType == TokenType::EOF {
470 ✗ return false;
471
2/2
✓ Branch 0 taken 4035 times.
✓ Branch 1 taken 38804 times.
42839 } else if tokenType == TokenType::LBRACE {
472 4035 depth++;
473
2/2
✓ Branch 0 taken 3460 times.
✓ Branch 1 taken 35344 times.
42839 } else if tokenType == TokenType::RBRACE {
474 3460 depth--;
475
4/4
✓ Branch 0 taken 34769 times.
✓ Branch 1 taken 575 times.
✓ Branch 2 taken 34769 times.
✓ Branch 3 taken 575 times.
38804 } else if tokenType == TokenType::SEMICOLON && depth == 1l {
476 575 return false;
477 42264 }
478 42264 offset++;
479
2/2
✓ Branch 0 taken 3076 times.
✓ Branch 1 taken 39188 times.
42264 if depth == 0l {
480 3076 break;
481 }
482 39188 }
483
484 // An empty brace group is only a struct instantiation, if the expression can continue after it
485
2/2
✓ Branch 0 taken 106 times.
✓ Branch 1 taken 2970 times.
3076 if offset == lbraceOffset + 2l {
486 106 const TokenType afterBraces = this.tokenAt(cast<unsigned long>(offset));
487
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
106 return afterBraces != TokenType::ELSE && !this.isStmtStartToken(afterBraces);
488 }
489 2970 return true;
490 }
491
492 // Check if the given token type is a keyword that can only appear at the start of a statement
493 3941 f<bool> Parser.isStmtStartKeyword(TokenType tokenType) {
494 const dyn options = [
495 TokenType::RETURN, TokenType::IF, TokenType::FOR, TokenType::FOREACH, TokenType::WHILE, TokenType::DO,
496 TokenType::SWITCH, TokenType::CASE, TokenType::DEFAULT, TokenType::BREAK, TokenType::CONTINUE,
497 3941 TokenType::FALLTHROUGH, TokenType::ASSERT, TokenType::UNSAFE
498 ];
499
2/2
✓ Branch 0 taken 53673 times.
✓ Branch 1 taken 3813 times.
57486 for unsigned long i = 0l; i < len(options); i++ {
500
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 53545 times.
53673 if tokenType == options[i] {
501 128 return true;
502 }
503 53545 }
504
5/6
✓ Branch 0 taken 3757 times.
✓ Branch 1 taken 56 times.
✓ Branch 2 taken 56 times.
✓ Branch 3 taken 3757 times.
✓ Branch 4 taken 3757 times.
✗ Branch 5 not taken.
11327 return this.isQualifierType(tokenType) || this.isBaseDataTypeKeyword(tokenType);
505 }
506
507 // Check if the given token type can start a statement, but can not continue an expression
508 106 f<bool> Parser.isStmtStartToken(TokenType tokenType) {
509
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
✓ Branch 2 taken 106 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 106 times.
212 if this.isStmtStartKeyword(tokenType) || this.isConstantStart(tokenType) {
510 ✗ return true;
511 }
512
3/6
✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 106 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 106 times.
✗ Branch 5 not taken.
212 return tokenType == TokenType::IDENTIFIER || tokenType == TokenType::TYPE_IDENTIFIER || tokenType == TokenType::NIL
513
3/6
✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 106 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 106 times.
106 || tokenType == TokenType::CAST || tokenType == TokenType::F || tokenType == TokenType::P || tokenType == TokenType::EOF;
514 }
515
516 // A parenthesized group is a lambda expression if it is followed by an arrow ('->').
517 2385 f<bool> Parser.isLambdaExprAhead() {
518 2385 const long after = this.matchBalancedGroup(0l, TokenType::LPAREN, TokenType::RPAREN);
519
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2385 times.
2385 return after >= 0l && this.tokenAt(cast<unsigned long>(after)) == TokenType::ARROW;
520 }
521
522 3951 public f<ASTEntryNode*> Parser.parse() {
523 3951 dyn entryNode = this.createNode<ASTEntryNode>();
524
525
4/4
✓ Branch 0 taken 67656 times.
✓ Branch 1 taken 3951 times.
✓ Branch 2 taken 67656 times.
✓ Branch 3 taken 3951 times.
139263 while !this.currentTokenIs(TokenType::EOF) {
526 67656 this.parseTopLevelDef(entryNode);
527 67656 }
528
529 // The children of the entry node are listed grouped by kind: mod attrs, import defs and top level defs
530 3951 this.groupEntryChildren(entryNode);
531
532 // Hand over all switch statements of this file
533
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 3951 times.
3989 foreach IVisitable* switchStmt : this.switchStmts {
534 38 entryNode.switchStmts.pushBack(switchStmt);
535 38 }
536
537 3951 return this.concludeNode(entryNode);
538 }
539
540 3951 p Parser.groupEntryChildren(ASTEntryNode* entryNode) {
541 3951 ASTNode* entryNodeBase = cast<ASTNode*>(entryNode);
542 3951 Vector<ASTNode*> modAttrs;
543 3951 Vector<ASTNode*> importDefs;
544 3951 Vector<ASTNode*> topLevelDefs;
545
2/2
✓ Branch 0 taken 67642 times.
✓ Branch 1 taken 3951 times.
71593 foreach ASTNode* child : entryNodeBase.children {
546
2/2
✓ Branch 0 taken 1539 times.
✓ Branch 1 taken 66103 times.
67642 if this.containsVisitable(entryNode.modAttrs, child.visitable) {
547 1539 modAttrs.pushBack(child);
548
2/2
✓ Branch 0 taken 2518 times.
✓ Branch 1 taken 63585 times.
67642 } else if this.containsVisitable(entryNode.importDefs, child.visitable) {
549 2518 importDefs.pushBack(child);
550 2518 } else {
551 63585 topLevelDefs.pushBack(child);
552 66103 }
553 67642 }
554 3951 entryNodeBase.children.clear();
555
2/2
✓ Branch 0 taken 1539 times.
✓ Branch 1 taken 3951 times.
5490 foreach ASTNode* child : modAttrs {
556 1539 entryNodeBase.children.pushBack(child);
557 1539 }
558
2/2
✓ Branch 0 taken 2518 times.
✓ Branch 1 taken 3951 times.
6469 foreach ASTNode* child : importDefs {
559 2518 entryNodeBase.children.pushBack(child);
560 2518 }
561
2/2
✓ Branch 0 taken 63585 times.
✓ Branch 1 taken 3951 times.
67536 foreach ASTNode* child : topLevelDefs {
562 63585 entryNodeBase.children.pushBack(child);
563 63585 }
564 3951 }
565
566 133745 f<bool> Parser.containsVisitable(const Vector<IVisitable*>& visitables, IVisitable* visitable) {
567
2/2
✓ Branch 0 taken 111312 times.
✓ Branch 1 taken 129688 times.
241000 foreach IVisitable* candidate : visitables {
568
2/2
✓ Branch 0 taken 4057 times.
✓ Branch 1 taken 107255 times.
111312 if candidate == visitable {
569 4057 return true;
570 }
571 107255 }
572 129688 return false;
573 }
574
575 // entry: (mainFunctionDef | functionDef | procedureDef | structDef | interfaceDef | unionDef | enumDef
576 // | genericTypeDef | aliasDef | globalVarDef | importDef | extDecl | modAttr)* EOF;
577 67656 p Parser.parseTopLevelDef(ASTEntryNode* entryNode) {
578 // Constructs that are unambiguous from their first token
579
4/4
✓ Branch 0 taken 1540 times.
✓ Branch 1 taken 66116 times.
✓ Branch 2 taken 1540 times.
✓ Branch 3 taken 66116 times.
135312 if this.currentTokenIs(TokenType::MOD_ATTR_PREAMBLE) {
580 1540 entryNode.modAttrs.pushBack(cast<IVisitable*>(this.parseModAttr()));
581 1540 return;
582 }
583
4/4
✓ Branch 0 taken 2518 times.
✓ Branch 1 taken 63598 times.
✓ Branch 2 taken 2518 times.
✓ Branch 3 taken 63598 times.
132232 if this.currentTokenIs(TokenType::IMPORT) {
584 2518 entryNode.importDefs.pushBack(cast<IVisitable*>(this.parseImportDef()));
585 2518 return;
586 }
587
588 // Determine the discriminating token, skipping an optional topLevelDefAttr ('#[...]') and a qualifier list
589 63598 long offset = 0l;
590
2/2
✓ Branch 0 taken 62024 times.
✓ Branch 1 taken 1574 times.
63598 if this.tokenAt(cast<unsigned long>(offset)) == TokenType::FCT_ATTR_PREAMBLE {
591 1574 offset++; // '#'
592
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1574 times.
1574 if this.tokenAt(cast<unsigned long>(offset)) == TokenType::LBRACKET {
593 1574 offset = this.matchBalancedGroup(offset, TokenType::LBRACKET, TokenType::RBRACKET);
594 1574 }
595 1574 }
596
4/4
✓ Branch 0 taken 124771 times.
✓ Branch 1 taken 4339 times.
✓ Branch 2 taken 65512 times.
✓ Branch 3 taken 63598 times.
253881 while this.isQualifierType(this.tokenAt(cast<unsigned long>(offset))) {
597 65512 offset++;
598 65512 }
599 63598 const TokenType disc = this.tokenAt(cast<unsigned long>(offset));
600
601
2/2
✓ Branch 0 taken 5229 times.
✓ Branch 1 taken 58369 times.
63598 if disc == TokenType::EXT {
602 5229 entryNode.topLevelDefs.pushBack(cast<IVisitable*>(this.parseExtDecl()));
603
2/2
✓ Branch 0 taken 17650 times.
✓ Branch 1 taken 40719 times.
63598 } else if disc == TokenType::P {
604 17650 entryNode.topLevelDefs.pushBack(cast<IVisitable*>(this.parseProcDef()));
605
2/2
✓ Branch 0 taken 29148 times.
✓ Branch 1 taken 11571 times.
58369 } else if disc == TokenType::F {
606 // mainFunctionDef: 'f' '<' 'int' '>' 'main' ...; everything else starting with 'f' is a functionDef
607
2/2
✓ Branch 0 taken 29147 times.
✓ Branch 1 taken 1 time.
29148 const bool isMain = this.tokenAt(cast<unsigned long>(offset + 1l)) == TokenType::LESS
608
2/2
✓ Branch 0 taken 1608 times.
✓ Branch 1 taken 27539 times.
29147 && this.tokenAt(cast<unsigned long>(offset + 2l)) == TokenType::TYPE_INT
609
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1602 times.
1608 && this.tokenAt(cast<unsigned long>(offset + 3l)) == TokenType::GREATER
610 29148 && this.tokenAt(cast<unsigned long>(offset + 4l)) == TokenType::MAIN;
611
2/2
✓ Branch 0 taken 1034 times.
✓ Branch 1 taken 28114 times.
29148 if isMain {
612 1034 entryNode.topLevelDefs.pushBack(cast<IVisitable*>(this.parseMainFctDef()));
613 1034 } else {
614 28114 entryNode.topLevelDefs.pushBack(cast<IVisitable*>(this.parseFctDef()));
615 28114 }
616
2/2
✓ Branch 0 taken 7282 times.
✓ Branch 1 taken 4289 times.
40719 } else if disc == TokenType::TYPE {
617 // structDef | interfaceDef | unionDef | enumDef | genericTypeDef | aliasDef
618 // After 'type' TYPE_IDENTIFIER, decide based on the following keyword / generic list
619 7282 long keywordOffset = offset + 2l;
620 7282 TokenType after = this.tokenAt(cast<unsigned long>(keywordOffset));
621
2/2
✓ Branch 0 taken 6092 times.
✓ Branch 1 taken 1190 times.
7282 if after == TokenType::LESS {
622 // Generic struct or interface: skip the generic list and look at the keyword behind it
623 1190 keywordOffset = this.matchAngleGroup(keywordOffset);
624
1/2
✓ Branch 0 taken 1190 times.
✗ Branch 1 not taken.
1190 after = keywordOffset < 0l ? TokenType::INVALID : this.tokenAt(cast<unsigned long>(keywordOffset));
625 1190 }
626
2/2
✓ Branch 0 taken 466 times.
✓ Branch 1 taken 6816 times.
7282 if after == TokenType::ENUM {
627 466 entryNode.topLevelDefs.pushBack(cast<IVisitable*>(this.parseEnumDef()));
628
2/2
✓ Branch 0 taken 159 times.
✓ Branch 1 taken 6657 times.
7282 } else if after == TokenType::ALIAS {
629 159 entryNode.topLevelDefs.pushBack(cast<IVisitable*>(this.parseAliasDef()));
630
2/2
✓ Branch 0 taken 327 times.
✓ Branch 1 taken 6330 times.
6816 } else if after == TokenType::INTERFACE {
631 327 entryNode.topLevelDefs.pushBack(cast<IVisitable*>(this.parseInterfaceDef()));
632
2/2
✓ Branch 0 taken 3233 times.
✓ Branch 1 taken 3097 times.
6657 } else if after == TokenType::STRUCT {
633 3233 entryNode.topLevelDefs.pushBack(cast<IVisitable*>(this.parseStructDef()));
634
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 3071 times.
6330 } else if after == TokenType::UNION {
635 26 entryNode.topLevelDefs.pushBack(cast<IVisitable*>(this.parseUnionDef()));
636 26 } else {
637 3071 entryNode.topLevelDefs.pushBack(cast<IVisitable*>(this.parseGenericTypeDef()));
638 6816 }
639 7282 } else {
640 4289 entryNode.topLevelDefs.pushBack(cast<IVisitable*>(this.parseGlobalVarDef()));
641 58369 }
642 63598 }
643
644 1034 public f<ASTMainFctDefNode*> Parser.parseMainFctDef() {
645 1034 dyn mainFctDefNode = this.createNode<ASTMainFctDefNode>();
646
647 // Parse topLevelDefAttr
648
4/4
✓ Branch 0 taken 1033 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1033 times.
✓ Branch 3 taken 1 time.
2068 if this.currentTokenIs(TokenType::FCT_ATTR_PREAMBLE) {
649 // The attributes of the main function keep the default target
650 1 mainFctDefNode.attrs = this.parseTopLevelDefAttr(AttrTarget::TARGET_INVALID);
651 1 }
652
653 // Consume expected tokens 'f<int> main'
654 1034 this.expect(TokenType::F);
655 1034 this.expect(TokenType::LESS);
656 1034 this.expect(TokenType::TYPE_INT);
657 1034 this.consumeGenericClose();
658 1034 this.expect(TokenType::MAIN);
659
660 // Parse paramLst
661 1034 ASTParamLstNode* paramLst = this.parseParenthesizedParamLst();
662 1034 mainFctDefNode.takesArgs = paramLst != nil<ASTParamLstNode*>;
663 1034 mainFctDefNode.paramLst = paramLst;
664
665 // Parse stmtLst
666 1034 ASTStmtLstNode* body = this.parseStmtLst();
667 1034 mainFctDefNode.body = body;
668
669 1034 return this.concludeNode(mainFctDefNode);
670 }
671
672 28114 public f<ASTFctDefNode*> Parser.parseFctDef() {
673 28114 dyn fctDefNode = this.createNode<ASTFctDefNode>();
674
675 // Parse topLevelDefAttr
676
4/4
✓ Branch 0 taken 27001 times.
✓ Branch 1 taken 1113 times.
✓ Branch 2 taken 27001 times.
✓ Branch 3 taken 1113 times.
56228 if this.currentTokenIs(TokenType::FCT_ATTR_PREAMBLE) {
677 1113 fctDefNode.attrs = this.parseTopLevelDefAttr(AttrTarget::TARGET_FCT_PROC);
678 1113 }
679
680 // Parse qualifierLst
681
4/4
✓ Branch 0 taken 1369 times.
✓ Branch 1 taken 26745 times.
✓ Branch 2 taken 1369 times.
✓ Branch 3 taken 26745 times.
56228 if !this.currentTokenIs(TokenType::F) {
682 26745 fctDefNode.qualifierLst = this.parseQualifierLst();
683 26745 }
684
685 28114 this.expect(TokenType::F);
686 28114 this.expect(TokenType::LESS);
687 28114 fctDefNode.returnType = this.parseDataType();
688 28114 fctDefNode.returnType.isReturnType = true;
689 28114 this.consumeGenericClose();
690 28114 fctDefNode.name = this.parseFctName();
691 28114 fctDefNode.isMethod = fctDefNode.name.nameFragments.getSize() > 1l;
692
693 // Parse typeLst (template type list)
694
2/2
✓ Branch 0 taken 24768 times.
✓ Branch 1 taken 3346 times.
28114 if this.isGenericOpenAhead() {
695 3346 fctDefNode.hasTemplateTypes = true;
696 3346 this.consumeGenericOpen();
697 3346 fctDefNode.templateTypeLst = this.parseTypeLst();
698 3346 this.consumeGenericClose();
699 3346 }
700
701 // Parse paramLst
702 28114 ASTParamLstNode* paramLst = this.parseParenthesizedParamLst();
703 28114 fctDefNode.hasParams = paramLst != nil<ASTParamLstNode*>;
704 28114 fctDefNode.paramLst = paramLst;
705
706 // Parse stmtLst
707 28114 ASTStmtLstNode* body = this.parseStmtLst();
708 28114 fctDefNode.body = body;
709
710 28114 return this.concludeNode(fctDefNode);
711 }
712
713 17650 public f<ASTProcDefNode*> Parser.parseProcDef() {
714 17650 dyn procDefNode = this.createNode<ASTProcDefNode>();
715
716 // Parse topLevelDefAttr
717
4/4
✓ Branch 0 taken 17647 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 17647 times.
✓ Branch 3 taken 3 times.
35300 if this.currentTokenIs(TokenType::FCT_ATTR_PREAMBLE) {
718 3 procDefNode.attrs = this.parseTopLevelDefAttr(AttrTarget::TARGET_FCT_PROC);
719 3 }
720
721 // Parse qualifierLst
722
4/4
✓ Branch 0 taken 3284 times.
✓ Branch 1 taken 14366 times.
✓ Branch 2 taken 3284 times.
✓ Branch 3 taken 14366 times.
35300 if !this.currentTokenIs(TokenType::P) {
723 14366 procDefNode.qualifierLst = this.parseQualifierLst();
724 14366 }
725
726 17650 this.expect(TokenType::P);
727 17650 procDefNode.name = this.parseFctName();
728 17650 procDefNode.isMethod = procDefNode.name.nameFragments.getSize() > 1l;
729 // Note: isCtor is derived later, by SymbolTableBuilder (it is set from the last name fragment being
730 // CTOR_FUNCTION_NAME during symbol-table building, not parsing).
731
732 // Parse typeLst (template type list)
733
2/2
✓ Branch 0 taken 14099 times.
✓ Branch 1 taken 3551 times.
17650 if this.isGenericOpenAhead() {
734 3551 procDefNode.hasTemplateTypes = true;
735 3551 this.consumeGenericOpen();
736 3551 procDefNode.templateTypeLst = this.parseTypeLst();
737 3551 this.consumeGenericClose();
738 3551 }
739
740 // Parse paramLst
741 17650 ASTParamLstNode* paramLst = this.parseParenthesizedParamLst();
742 17650 procDefNode.hasParams = paramLst != nil<ASTParamLstNode*>;
743 17650 procDefNode.paramLst = paramLst;
744
745 // Parse stmtLst
746 17650 ASTStmtLstNode* body = this.parseStmtLst();
747 17650 procDefNode.body = body;
748
749 17650 return this.concludeNode(procDefNode);
750 }
751
752 45763 f<ASTFctNameNode*> Parser.parseFctName() {
753 45763 dyn fctNameNode = this.createNode<ASTFctNameNode>();
754
755
4/4
✓ Branch 0 taken 39489 times.
✓ Branch 1 taken 6274 times.
✓ Branch 2 taken 6274 times.
✓ Branch 3 taken 39489 times.
91526 if this.consumeIf(TokenType::OPERATOR) {
756 6274 const String opName = this.parseOverloadableOp();
757 6274 fctNameNode.name = opName;
758 6274 fctNameNode.fqName = opName;
759 6274 fctNameNode.nameFragments.pushBack(opName);
760 6274 } else {
761
4/4
✓ Branch 0 taken 12869 times.
✓ Branch 1 taken 26620 times.
✓ Branch 2 taken 12869 times.
✓ Branch 3 taken 26620 times.
78978 if this.currentTokenIs(TokenType::TYPE_IDENTIFIER) {
762 26620 const String structName = this.getIdentifier(true);
763 26620 fctNameNode.structName = structName;
764 26620 fctNameNode.nameFragments.pushBack(structName);
765 26620 this.expect(TokenType::TYPE_IDENTIFIER);
766 26620 this.expect(TokenType::DOT);
767 26620 }
768 39489 const String name = this.getIdentifier(false);
769 39489 fctNameNode.name = name;
770 39489 fctNameNode.nameFragments.pushBack(name);
771 39489 this.expect(TokenType::IDENTIFIER);
772
773 // Build the fully-qualified name (name fragments joined by MEMBER_ACCESS_TOKEN)
774
2/2
✓ Branch 0 taken 26620 times.
✓ Branch 1 taken 12869 times.
39489 if !fctNameNode.structName.isEmpty() {
775 26620 fctNameNode.fqName = fctNameNode.structName + MEMBER_ACCESS_TOKEN + name;
776 26620 } else {
777 12869 fctNameNode.fqName = name;
778 12869 }
779 39489 }
780
781 45763 return this.concludeNode(fctNameNode);
782 }
783
784 // overloadableOp: PLUS | MINUS | MUL | DIV | EQUAL | NOT_EQUAL | LESS | GREATER | LESS_EQUAL | GREATER_EQUAL | LESS LESS
785 // | GREATER GREATER | BITWISE_AND | BITWISE_OR | BITWISE_XOR | BITWISE_NOT | PLUS_EQUAL | MINUS_EQUAL | MUL_EQUAL
786 // | DIV_EQUAL | PLUS_PLUS | MINUS_MINUS | LBRACKET RBRACKET | ASSIGN;
787 // Note: the lexer merges '<<' and '>>' into single SHL / SHR tokens. Returns the mangled op.* function
788 // name for the matched operator.
789 6274 f<String> Parser.parseOverloadableOp() {
790 6274 const TokenType tokenType = this.lexer.getToken().tokenType;
791
792 // Subscript operator '[]'
793
4/4
✓ Branch 0 taken 5016 times.
✓ Branch 1 taken 1258 times.
✓ Branch 2 taken 1258 times.
✓ Branch 3 taken 5016 times.
12548 if this.consumeIf(TokenType::LBRACKET) {
794 1258 this.expect(TokenType::RBRACKET);
795 1258 return String(OP_FCT_SUBSCRIPT);
796 }
797
798 // All remaining overloadable operators are a single token
799 5016 this.advance();
800
23/24
✓ Branch 0 taken 270 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 530 times.
✓ Branch 3 taken 41 times.
✓ Branch 4 taken 1274 times.
✓ Branch 5 taken 1269 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 2 times.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 4 times.
✓ Branch 12 taken 4 times.
✓ Branch 13 taken 4 times.
✓ Branch 14 taken 4 times.
✓ Branch 15 taken 388 times.
✓ Branch 16 taken 124 times.
✓ Branch 17 taken 266 times.
✓ Branch 18 taken 41 times.
✓ Branch 19 taken 154 times.
✓ Branch 20 taken 124 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 61 times.
✓ Branch 23 taken 446 times.
5016 switch tokenType {
801 270 case TokenType::PLUS: { return String(OP_FCT_PLUS); }
802 2 case TokenType::MINUS: { return String(OP_FCT_MINUS); }
803 530 case TokenType::MUL: { return String(OP_FCT_MUL); }
804 41 case TokenType::DIV: { return String(OP_FCT_DIV); }
805 1274 case TokenType::EQUAL: { return String(OP_FCT_EQUAL); }
806 1269 case TokenType::NOT_EQUAL: { return String(OP_FCT_NOT_EQUAL); }
807 2 case TokenType::LESS: { return String(OP_FCT_LESS); }
808 2 case TokenType::GREATER: { return String(OP_FCT_GREATER); }
809 2 case TokenType::LESS_EQUAL: { return String(OP_FCT_LESS_EQUAL); }
810 2 case TokenType::GREATER_EQUAL: { return String(OP_FCT_GREATER_EQUAL); }
811 case TokenType::SHL: {
812 // '<<' is the shift left operator, if the parameter list or a template type list follows. Otherwise, it is the less
813 // operator, followed by the opening '<' of a template type list (e.g. 'operator<<T>(...)'), which stays pending
814 61 const TokenType nextTokenType = this.lexer.getToken().tokenType;
815
4/4
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 60 times.
✓ Branch 3 taken 1 time.
61 if nextTokenType == TokenType::LPAREN || nextTokenType == TokenType::LESS {
816 60 return String(OP_FCT_SHL);
817 }
818 1 this.pendingLess = true;
819 1 return String(OP_FCT_LESS);
820 }
821 2 case TokenType::SHR: { return String(OP_FCT_SHR); }
822 4 case TokenType::BITWISE_AND: { return String(OP_FCT_BITWISE_AND); }
823 4 case TokenType::BITWISE_OR: { return String(OP_FCT_BITWISE_OR); }
824 4 case TokenType::BITWISE_XOR: { return String(OP_FCT_BITWISE_XOR); }
825 4 case TokenType::BITWISE_NOT: { return String(OP_FCT_BITWISE_NOT); }
826 388 case TokenType::PLUS_EQUAL: { return String(OP_FCT_PLUS_EQUAL); }
827 124 case TokenType::MINUS_EQUAL: { return String(OP_FCT_MINUS_EQUAL); }
828 266 case TokenType::MUL_EQUAL: { return String(OP_FCT_MUL_EQUAL); }
829 41 case TokenType::DIV_EQUAL: { return String(OP_FCT_DIV_EQUAL); }
830 154 case TokenType::PLUS_PLUS: { return String(OP_FCT_POSTFIX_PLUS_PLUS); }
831 124 case TokenType::MINUS_MINUS: { return String(OP_FCT_POSTFIX_MINUS_MINUS); }
832 446 case TokenType::ASSIGN: { return String(OP_FCT_ASSIGN); }
833 }
834 − return String(); // LCOV_EXCL_LINE
835 }
836
837 // Parse an optional paramLst, enclosed in parentheses. Returns nil, if the parentheses are empty.
838 46941 f<ASTParamLstNode*> Parser.parseParenthesizedParamLst() {
839 46941 this.expect(TokenType::LPAREN);
840 46941 ASTParamLstNode* paramLst = nil<ASTParamLstNode*>;
841
4/4
✓ Branch 0 taken 13766 times.
✓ Branch 1 taken 33175 times.
✓ Branch 2 taken 13766 times.
✓ Branch 3 taken 33175 times.
93882 if !this.currentTokenIs(TokenType::RPAREN) {
842 33175 paramLst = this.parseParamLst();
843 33175 }
844 46941 this.expect(TokenType::RPAREN);
845 46941 return paramLst;
846 }
847
848 // Parse a name prefix (IDENTIFIER separator)* and append its fragments to the fragment list and the fully-qualified name.
849 236363 p Parser.parseNamePrefix(TokenType separatorType, string separator, Vector<String>& fragments, String& fqName) {
850
6/6
✓ Branch 0 taken 96957 times.
✓ Branch 1 taken 174933 times.
✓ Branch 2 taken 96957 times.
✓ Branch 3 taken 174933 times.
✓ Branch 4 taken 35527 times.
✓ Branch 5 taken 236363 times.
307417 while this.currentTokenIs(TokenType::IDENTIFIER) && this.tokenAt(1l) == separatorType {
851 35527 const String fragment = this.getIdentifier(false);
852 35527 fragments.pushBack(fragment);
853 35527 fqName += fragment;
854 35527 fqName += separator;
855 35527 this.expect(TokenType::IDENTIFIER);
856 35527 this.expect(separatorType);
857 35527 }
858 236363 }
859
860 // Override the type id of a struct, interface or union, if the fixed type id attribute is set
861 456 p Parser.applyFixedTypeId(ASTTopLevelDefAttrNode* attrs, unsigned long& typeId) {
862 456 const String fixedTypeIdKey = String(ATTR_CORE_COMPILER_FIXED_TYPE_ID);
863
2/2
✓ Branch 0 taken 202 times.
✓ Branch 1 taken 254 times.
456 if attrs.attrLst.hasAttr(fixedTypeIdKey) {
864 254 const CompileTimeValue value = attrs.attrLst.getAttrValueByName(fixedTypeIdKey);
865 254 typeId = cast<unsigned long>(value.getIntValue());
866 254 }
867 456 }
868
869 3232 f<ASTStructDefNode*> Parser.parseStructDef() {
870 3232 dyn structDefNode = this.createNode<ASTStructDefNode>();
871 3232 structDefNode.typeId = this.resourceManager.getNextCustomTypeId();
872
873 // Parse topLevelDefAttr
874
4/4
✓ Branch 0 taken 3030 times.
✓ Branch 1 taken 202 times.
✓ Branch 2 taken 3030 times.
✓ Branch 3 taken 202 times.
6464 if this.currentTokenIs(TokenType::FCT_ATTR_PREAMBLE) {
875 202 ASTTopLevelDefAttrNode* attrs = this.parseTopLevelDefAttr(AttrTarget::TARGET_STRUCT);
876 202 structDefNode.attrs = attrs;
877 // Check if a custom type id was set
878 202 this.applyFixedTypeId(attrs, structDefNode.typeId);
879 202 }
880
881 // Parse qualifierLst
882
4/4
✓ Branch 0 taken 1311 times.
✓ Branch 1 taken 1921 times.
✓ Branch 2 taken 1311 times.
✓ Branch 3 taken 1921 times.
6464 if !this.currentTokenIs(TokenType::TYPE) {
883 1921 structDefNode.qualifierLst = this.parseQualifierLst();
884 1921 }
885
886 3232 this.expect(TokenType::TYPE);
887 3232 structDefNode.structName = this.getIdentifier(true);
888 3232 this.expect(TokenType::TYPE_IDENTIFIER);
889
890 // Parse typeLst (template type list)
891
4/4
✓ Branch 0 taken 2306 times.
✓ Branch 1 taken 926 times.
✓ Branch 2 taken 2306 times.
✓ Branch 3 taken 926 times.
6464 if this.currentTokenIs(TokenType::LESS) {
892 926 structDefNode.hasTemplateTypes = true;
893 926 this.expect(TokenType::LESS);
894 926 structDefNode.templateTypeLst = this.parseTypeLst();
895 926 this.consumeGenericClose();
896 926 }
897
898 3232 this.expect(TokenType::STRUCT);
899
900 // Parse typeLst (implemented interfaces)
901
4/4
✓ Branch 0 taken 2807 times.
✓ Branch 1 taken 425 times.
✓ Branch 2 taken 2807 times.
✓ Branch 3 taken 425 times.
6464 if this.currentTokenIs(TokenType::COLON) {
902 425 structDefNode.hasInterfaces = true;
903 425 this.expect(TokenType::COLON);
904 425 structDefNode.interfaceTypeLst = this.parseTypeLst();
905 425 }
906
907 // Parse fieldLst
908 3232 this.expect(TokenType::LBRACE);
909
4/4
✓ Branch 0 taken 3232 times.
✓ Branch 1 taken 7804 times.
✓ Branch 2 taken 3232 times.
✓ Branch 3 taken 7804 times.
18840 while !this.currentTokenIs(TokenType::RBRACE) {
910 7804 structDefNode.fields.pushBack(cast<IVisitable*>(this.parseField()));
911 7804 }
912 3232 this.expect(TokenType::RBRACE);
913
914 6464 return this.concludeNode(structDefNode);
915 3232 }
916
917 327 f<ASTInterfaceDefNode*> Parser.parseInterfaceDef() {
918 327 dyn interfaceDefNode = this.createNode<ASTInterfaceDefNode>();
919 327 interfaceDefNode.typeId = this.resourceManager.getNextCustomTypeId();
920
921 // Parse topLevelDefAttr
922
4/4
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 254 times.
✓ Branch 2 taken 73 times.
✓ Branch 3 taken 254 times.
654 if this.currentTokenIs(TokenType::FCT_ATTR_PREAMBLE) {
923 254 ASTTopLevelDefAttrNode* attrs = this.parseTopLevelDefAttr(AttrTarget::TARGET_INTERFACE);
924 254 interfaceDefNode.attrs = attrs;
925 // Check if a custom type id was set
926 254 this.applyFixedTypeId(attrs, interfaceDefNode.typeId);
927 254 }
928
929 // Parse qualifierLst
930
4/4
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 284 times.
✓ Branch 2 taken 43 times.
✓ Branch 3 taken 284 times.
654 if !this.currentTokenIs(TokenType::TYPE) {
931 284 interfaceDefNode.qualifierLst = this.parseQualifierLst();
932 284 }
933
934 327 this.expect(TokenType::TYPE);
935 327 interfaceDefNode.interfaceName = this.getIdentifier(true);
936 327 this.expect(TokenType::TYPE_IDENTIFIER);
937
938 // Parse typeLst (template type list)
939
4/4
✓ Branch 0 taken 67 times.
✓ Branch 1 taken 260 times.
✓ Branch 2 taken 67 times.
✓ Branch 3 taken 260 times.
654 if this.currentTokenIs(TokenType::LESS) {
940 260 interfaceDefNode.hasTemplateTypes = true;
941 260 this.expect(TokenType::LESS);
942 260 interfaceDefNode.templateTypeLst = this.parseTypeLst();
943 260 this.consumeGenericClose();
944 260 }
945
946 327 this.expect(TokenType::INTERFACE);
947
948 // Parse signatures
949 327 this.expect(TokenType::LBRACE);
950
6/6
✓ Branch 0 taken 327 times.
✓ Branch 1 taken 749 times.
✓ Branch 2 taken 327 times.
✓ Branch 3 taken 749 times.
✓ Branch 4 taken 327 times.
✓ Branch 5 taken 749 times.
1825 while !this.currentTokenIs(TokenType::RBRACE) && !this.currentTokenIs(TokenType::EOF) {
951 749 interfaceDefNode.signatures.pushBack(cast<IVisitable*>(this.parseSignature()));
952 749 }
953 327 this.expect(TokenType::RBRACE);
954
955 654 return this.concludeNode(interfaceDefNode);
956 327 }
957
958 26 f<ASTUnionDefNode*> Parser.parseUnionDef() {
959 26 dyn unionDefNode = this.createNode<ASTUnionDefNode>();
960 26 unionDefNode.typeId = this.resourceManager.getNextCustomTypeId();
961
962 // Parse topLevelDefAttr
963
2/4
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
52 if this.currentTokenIs(TokenType::FCT_ATTR_PREAMBLE) {
964 ✗ ASTTopLevelDefAttrNode* attrs = this.parseTopLevelDefAttr(AttrTarget::TARGET_UNION);
965 ✗ unionDefNode.attrs = attrs;
966 // Check if a custom type id was set
967 ✗ this.applyFixedTypeId(attrs, unionDefNode.typeId);
968 ✗ }
969
970 // Parse qualifierLst
971
4/4
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 1 time.
52 if !this.currentTokenIs(TokenType::TYPE) {
972 1 unionDefNode.qualifierLst = this.parseQualifierLst();
973 1 }
974
975 26 this.expect(TokenType::TYPE);
976 26 unionDefNode.unionName = this.getIdentifier(true);
977 26 this.expect(TokenType::TYPE_IDENTIFIER);
978
979 // Parse typeLst (template type list)
980
4/4
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 4 times.
52 if this.currentTokenIs(TokenType::LESS) {
981 4 unionDefNode.hasTemplateTypes = true;
982 4 this.expect(TokenType::LESS);
983 4 unionDefNode.templateTypeLst = this.parseTypeLst();
984 4 this.consumeGenericClose();
985 4 }
986
987 26 this.expect(TokenType::UNION);
988
989 // Parse fieldLst
990 26 this.expect(TokenType::LBRACE);
991
4/4
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 57 times.
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 57 times.
140 while !this.currentTokenIs(TokenType::RBRACE) {
992 57 unionDefNode.fields.pushBack(cast<IVisitable*>(this.parseField()));
993 57 }
994 26 this.expect(TokenType::RBRACE);
995
996 52 return this.concludeNode(unionDefNode);
997 26 }
998
999 466 f<ASTEnumDefNode*> Parser.parseEnumDef() {
1000 466 dyn enumDefNode = this.createNode<ASTEnumDefNode>();
1001 466 enumDefNode.typeId = this.resourceManager.getNextCustomTypeId();
1002
1003 // Parse qualifierLst
1004
4/4
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 426 times.
✓ Branch 2 taken 40 times.
✓ Branch 3 taken 426 times.
932 if !this.currentTokenIs(TokenType::TYPE) {
1005 426 enumDefNode.qualifierLst = this.parseQualifierLst();
1006 426 }
1007
1008 466 this.expect(TokenType::TYPE);
1009 466 enumDefNode.enumName = this.getIdentifier(true);
1010 466 this.expect(TokenType::TYPE_IDENTIFIER);
1011 466 this.expect(TokenType::ENUM);
1012
1013 // Parse enumItemLst
1014 466 this.expect(TokenType::LBRACE);
1015 466 ASTEnumItemLstNode* itemLst = this.parseEnumItemLst();
1016 466 enumDefNode.itemLst = itemLst;
1017 // Back-link every item to its enclosing enum def
1018
2/2
✓ Branch 0 taken 466 times.
✓ Branch 1 taken 1749 times.
2215 foreach ASTEnumItemNode* item : itemLst.items {
1019 1749 item.enumDef = enumDefNode;
1020 1749 }
1021 466 this.expect(TokenType::RBRACE);
1022
1023 932 return this.concludeNode(enumDefNode);
1024 466 }
1025
1026 3071 f<ASTGenericTypeDefNode*> Parser.parseGenericTypeDef() {
1027 3071 dyn genericTypeDefNode = this.createNode<ASTGenericTypeDefNode>();
1028
1029 3071 this.expect(TokenType::TYPE);
1030 3071 genericTypeDefNode.typeName = this.getIdentifier(true);
1031 3071 this.expect(TokenType::TYPE_IDENTIFIER);
1032 3071 genericTypeDefNode.typeAltsLst = cast<IVisitable*>(this.parseTypeAltsLst());
1033 3071 this.expect(TokenType::SEMICOLON);
1034
1035 6142 return this.concludeNode(genericTypeDefNode);
1036 3071 }
1037
1038 159 f<ASTAliasDefNode*> Parser.parseAliasDef() {
1039 159 dyn aliasDefNode = this.createNode<ASTAliasDefNode>();
1040 159 aliasDefNode.typeId = this.resourceManager.getNextCustomTypeId();
1041
1042 // Parse qualifierLst
1043
4/4
✓ Branch 0 taken 99 times.
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 99 times.
✓ Branch 3 taken 60 times.
318 if !this.currentTokenIs(TokenType::TYPE) {
1044 60 aliasDefNode.qualifierLst = this.parseQualifierLst();
1045 60 }
1046
1047 159 this.expect(TokenType::TYPE);
1048 159 aliasDefNode.aliasName = this.getIdentifier(true);
1049 159 this.expect(TokenType::TYPE_IDENTIFIER);
1050 159 this.expect(TokenType::ALIAS);
1051 159 const unsigned long dataTypeStartIdx = this.lexer.getTokenIndex();
1052 159 aliasDefNode.dataType = this.parseDataType();
1053 159 aliasDefNode.dataTypeString = this.lexer.getTokenText(dataTypeStartIdx, this.lexer.getTokenIndex());
1054 159 this.expect(TokenType::SEMICOLON);
1055
1056 318 return this.concludeNode(aliasDefNode);
1057 159 }
1058
1059 4288 f<ASTGlobalVarDefNode*> Parser.parseGlobalVarDef() {
1060 4288 dyn globalVarDefNode = this.createNode<ASTGlobalVarDefNode>();
1061
1062 4288 globalVarDefNode.dataType = this.parseDataType();
1063 4288 globalVarDefNode.dataType.isGlobalType = true;
1064 4288 globalVarDefNode.varName = this.getIdentifier(true);
1065 4288 this.expect(TokenType::TYPE_IDENTIFIER);
1066
1067 // Parse initializer
1068
4/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 4279 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 4279 times.
8576 if this.consumeIf(TokenType::ASSIGN) {
1069 4279 globalVarDefNode.hasValue = true;
1070 4279 globalVarDefNode.constant = this.parseConstant();
1071 4279 }
1072
1073 4288 this.expect(TokenType::SEMICOLON);
1074
1075 8576 return this.concludeNode(globalVarDefNode);
1076 4288 }
1077
1078 5229 f<ASTExtDeclNode*> Parser.parseExtDecl() {
1079 5229 dyn extDeclNode = this.createNode<ASTExtDeclNode>();
1080
1081 // Parse topLevelDefAttr
1082
4/4
✓ Branch 0 taken 5228 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 5228 times.
✓ Branch 3 taken 1 time.
10458 if this.currentTokenIs(TokenType::FCT_ATTR_PREAMBLE) {
1083 1 extDeclNode.attrs = this.parseTopLevelDefAttr(AttrTarget::TARGET_EXT_DECL);
1084 1 }
1085
1086 5229 this.expect(TokenType::EXT);
1087
1088 // Parse parse function prefix / procedure prefix
1089
4/4
✓ Branch 0 taken 679 times.
✓ Branch 1 taken 4550 times.
✓ Branch 2 taken 4550 times.
✓ Branch 3 taken 679 times.
10458 if this.consumeIf(TokenType::F) {
1090 4550 this.expect(TokenType::LESS);
1091 4550 extDeclNode.returnType = this.parseDataType();
1092 4550 extDeclNode.returnType.isReturnType = true;
1093 4550 this.consumeGenericClose();
1094 4550 } else {
1095 679 this.expect(TokenType::P);
1096 679 }
1097
1098 // Parse external function name
1099 5229 extDeclNode.extFunctionName = this.getIdentifier(false);
1100
4/4
✓ Branch 0 taken 798 times.
✓ Branch 1 taken 4431 times.
✓ Branch 2 taken 4431 times.
✓ Branch 3 taken 798 times.
10458 if !this.consumeIf(TokenType::IDENTIFIER) {
1101 798 this.expect(TokenType::TYPE_IDENTIFIER);
1102 798 }
1103
1104 // Parse typeLstWithEllipsis
1105 5229 this.expect(TokenType::LPAREN);
1106
4/4
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 5157 times.
✓ Branch 2 taken 72 times.
✓ Branch 3 taken 5157 times.
10458 if !this.currentTokenIs(TokenType::RPAREN) {
1107 5157 extDeclNode.hasArgs = true;
1108 5157 extDeclNode.argTypeLst = cast<IVisitable*>(this.parseTypeLstWithEllipsis());
1109 5157 }
1110 5229 this.expect(TokenType::RPAREN);
1111
1112 5229 this.expect(TokenType::SEMICOLON);
1113
1114 10458 return this.concludeNode(extDeclNode);
1115 5229 }
1116
1117 2518 f<ASTImportDefNode*> Parser.parseImportDef() {
1118 2518 dyn importDefNode = this.createNode<ASTImportDefNode>();
1119
1120 2518 this.expect(TokenType::IMPORT);
1121 // The lexer already strips the surrounding quotes for STRING_LIT tokens, so .text is the raw path.
1122 2518 importDefNode.importPath = this.lexer.getToken().text;
1123 2518 this.expect(TokenType::STRING_LIT);
1124
1125
4/4
✓ Branch 0 taken 2413 times.
✓ Branch 1 taken 105 times.
✓ Branch 2 taken 105 times.
✓ Branch 3 taken 2413 times.
5036 if this.consumeIf(TokenType::AS) {
1126 105 importDefNode.importName = this.getIdentifier(false);
1127 105 this.expect(TokenType::IDENTIFIER);
1128 105 } else {
1129 // If no name is given, use the path as the name
1130 2413 importDefNode.importName = importDefNode.importPath;
1131 2413 }
1132 2518 this.expect(TokenType::SEMICOLON);
1133
1134 2518 return this.concludeNode(importDefNode);
1135 }
1136
1137 12758 f<ASTUnsafeBlockNode*> Parser.parseUnsafeBlock() {
1138 12758 dyn unsafeBlockNode = this.createNode<ASTUnsafeBlockNode>();
1139
1140 12758 this.expect(TokenType::UNSAFE);
1141 12758 ASTStmtLstNode* body = this.parseStmtLst();
1142 12758 unsafeBlockNode.body = body;
1143
1144 12758 return this.concludeNode(unsafeBlockNode);
1145 }
1146
1147 4405 f<ASTForLoopNode*> Parser.parseForLoop() {
1148 4405 dyn forLoopNode = this.createNode<ASTForLoopNode>();
1149
1150 4405 this.expect(TokenType::FOR);
1151
1152 // Parse head
1153
4/4
✓ Branch 0 taken 4399 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 4399 times.
8810 if this.consumeIf(TokenType::LPAREN) {
1154 6 this.parseForHead(forLoopNode);
1155 6 this.expect(TokenType::RPAREN);
1156 6 } else {
1157 4399 this.parseForHead(forLoopNode);
1158 4399 }
1159
1160 // Parse stmtLst
1161 4405 forLoopNode.body = this.parseStmtLst();
1162
1163 4405 return this.concludeNode(forLoopNode);
1164 }
1165
1166 4405 p Parser.parseForHead(ASTForLoopNode* forLoopNode) {
1167 4405 forLoopNode.initDecl = this.parseDeclStmt();
1168 4405 this.expect(TokenType::SEMICOLON);
1169 4405 forLoopNode.condAssign = this.parseAssignExpr();
1170 4405 this.expect(TokenType::SEMICOLON);
1171 4405 forLoopNode.incAssign = this.parseAssignExpr();
1172 4405 }
1173
1174 435 f<ASTForeachLoopNode*> Parser.parseForeachLoop() {
1175 435 dyn foreachLoopNode = this.createNode<ASTForeachLoopNode>();
1176
1177 435 this.expect(TokenType::FOREACH);
1178
1179 // Parse head
1180
2/4
✓ Branch 0 taken 435 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 435 times.
870 if this.consumeIf(TokenType::LPAREN) {
1181 ✗ this.parseForeachHead(foreachLoopNode);
1182 ✗ this.expect(TokenType::RPAREN);
1183 ✗ } else {
1184 435 this.parseForeachHead(foreachLoopNode);
1185 435 }
1186
1187 // Parse stmtLst
1188 435 foreachLoopNode.body = this.parseStmtLst();
1189
1190 435 return this.concludeNode(foreachLoopNode);
1191 }
1192
1193 435 p Parser.parseForeachHead(ASTForeachLoopNode* foreachLoopNode) {
1194 435 ASTDeclStmtNode* itemVarDecl = this.parseDeclStmt();
1195
4/4
✓ Branch 0 taken 412 times.
✓ Branch 1 taken 23 times.
✓ Branch 2 taken 412 times.
✓ Branch 3 taken 23 times.
870 if this.currentTokenIs(TokenType::COMMA) {
1196 // Two declarations given: the first is the index, the second is the item
1197 23 foreachLoopNode.idxVarDecl = itemVarDecl;
1198 23 this.expect(TokenType::COMMA);
1199 23 itemVarDecl = this.parseDeclStmt();
1200 23 }
1201 435 foreachLoopNode.itemVarDecl = itemVarDecl;
1202 435 itemVarDecl.isForEachItem = true;
1203 435 this.expect(TokenType::COLON);
1204 435 foreachLoopNode.iteratorAssign = this.parseAssignExpr();
1205 435 }
1206
1207 1865 f<ASTWhileLoopNode*> Parser.parseWhileLoop() {
1208 1865 dyn whileLoopNode = this.createNode<ASTWhileLoopNode>();
1209
1210 1865 this.expect(TokenType::WHILE);
1211
1212 // Parse head
1213 1865 whileLoopNode.condition = this.parseAssignExpr();
1214
1215 // Parse stmtLst
1216 1865 whileLoopNode.body = this.parseStmtLst();
1217
1218 1865 return this.concludeNode(whileLoopNode);
1219 }
1220
1221 50 f<ASTDoWhileLoopNode*> Parser.parseDoWhileLoop() {
1222 50 dyn doWhileLoopNode = this.createNode<ASTDoWhileLoopNode>();
1223
1224 50 this.expect(TokenType::DO);
1225
1226 // Parse stmtLst
1227 50 doWhileLoopNode.body = this.parseStmtLst();
1228
1229 // Parse foot
1230 50 this.expect(TokenType::WHILE);
1231 50 doWhileLoopNode.condition = this.parseAssignExpr();
1232 50 this.expect(TokenType::SEMICOLON);
1233
1234 50 return this.concludeNode(doWhileLoopNode);
1235 }
1236
1237 25175 f<ASTIfStmtNode*> Parser.parseIfStmt() {
1238 25175 dyn ifStmtNode = this.createNode<ASTIfStmtNode>();
1239
1240 25175 this.expect(TokenType::IF);
1241
1242 // Parse head
1243 25175 ifStmtNode.condition = this.parseAssignExpr();
1244
1245 // Parse stmtLst
1246 25175 ifStmtNode.thenBody = this.parseStmtLst();
1247
1248 // Parse else
1249
4/4
✓ Branch 0 taken 23778 times.
✓ Branch 1 taken 1397 times.
✓ Branch 2 taken 23778 times.
✓ Branch 3 taken 1397 times.
50350 if this.currentTokenIs(TokenType::ELSE) {
1250 1397 ifStmtNode.elseStmt = this.parseElseStmt();
1251 1397 }
1252
1253 25175 return this.concludeNode(ifStmtNode);
1254 }
1255
1256 1397 f<ASTElseStmtNode*> Parser.parseElseStmt() {
1257 1397 dyn elseStmtNode = this.createNode<ASTElseStmtNode>();
1258
1259 1397 this.expect(TokenType::ELSE);
1260
1261
4/4
✓ Branch 0 taken 563 times.
✓ Branch 1 taken 834 times.
✓ Branch 2 taken 563 times.
✓ Branch 3 taken 834 times.
2794 if this.currentTokenIs(TokenType::IF) { // Else if
1262 563 elseStmtNode.isElseIf = true;
1263 563 elseStmtNode.ifStmt = this.parseIfStmt();
1264 563 } else { // Else
1265 834 elseStmtNode.body = this.parseStmtLst();
1266 834 }
1267
1268 1397 return this.concludeNode(elseStmtNode);
1269 }
1270
1271 38 f<ASTSwitchStmtNode*> Parser.parseSwitchStmt() {
1272 38 dyn switchStmtNode = this.createNode<ASTSwitchStmtNode>();
1273
1274 38 this.expect(TokenType::SWITCH);
1275 38 switchStmtNode.assignExpr = this.parseAssignExpr();
1276 38 this.expect(TokenType::LBRACE);
1277
4/4
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 196 times.
✓ Branch 2 taken 38 times.
✓ Branch 3 taken 196 times.
430 while this.currentTokenIs(TokenType::CASE) {
1278 196 switchStmtNode.caseBranches.pushBack(cast<IVisitable*>(this.parseCaseBranch()));
1279 196 }
1280
4/4
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 20 times.
76 if this.currentTokenIs(TokenType::DEFAULT) {
1281 20 switchStmtNode.hasDefaultBranch = true;
1282 20 switchStmtNode.defaultBranch = cast<IVisitable*>(this.parseDefaultBranch());
1283 20 }
1284 38 this.expect(TokenType::RBRACE);
1285
1286 // Remember the switch statement, so that later passes can access all switch statements without walking the whole AST
1287 38 this.switchStmts.pushBack(cast<IVisitable*>(switchStmtNode));
1288
1289 38 return this.concludeNode(switchStmtNode);
1290 }
1291
1292 196 f<ASTCaseBranchNode*> Parser.parseCaseBranch() {
1293 196 dyn caseBranchNode = this.createNode<ASTCaseBranchNode>();
1294
1295 196 this.expect(TokenType::CASE);
1296 196 caseBranchNode.caseConstants.pushBack(cast<IVisitable*>(this.parseCaseConstant()));
1297
4/4
✓ Branch 0 taken 195 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 196 times.
400 while this.consumeIf(TokenType::COMMA) {
1298 4 caseBranchNode.caseConstants.pushBack(cast<IVisitable*>(this.parseCaseConstant()));
1299 4 }
1300 196 this.expect(TokenType::COLON);
1301 196 caseBranchNode.body = this.parseStmtLst();
1302
1303 196 return this.concludeNode(caseBranchNode);
1304 }
1305
1306 20 f<ASTDefaultBranchNode*> Parser.parseDefaultBranch() {
1307 20 dyn defaultBranchNode = this.createNode<ASTDefaultBranchNode>();
1308
1309 20 this.expect(TokenType::DEFAULT);
1310 20 this.expect(TokenType::COLON);
1311 20 defaultBranchNode.body = this.parseStmtLst();
1312
1313 20 return this.concludeNode(defaultBranchNode);
1314 }
1315
1316 126 f<ASTAnonymousBlockStmtNode*> Parser.parseAnonymousBlockStmt() {
1317 126 dyn anonymousBlockStmtNode = this.createNode<ASTAnonymousBlockStmtNode>();
1318
1319 126 anonymousBlockStmtNode.body = this.parseStmtLst();
1320
1321 126 return this.concludeNode(anonymousBlockStmtNode);
1322 }
1323
1324 // stmtLst: LBRACE (stmt | forLoop | foreachLoop | whileLoop | doWhileLoop | ifStmt | switchStmt
1325 // | assertStmt | unsafeBlock | anonymousBlockStmt)* RBRACE;
1326 92800 f<ASTStmtLstNode*> Parser.parseStmtLst() {
1327 92800 dyn stmtLstNode = this.createNode<ASTStmtLstNode>();
1328
1329 92800 this.expect(TokenType::LBRACE);
1330
6/6
✓ Branch 0 taken 92800 times.
✓ Branch 1 taken 179071 times.
✓ Branch 2 taken 92800 times.
✓ Branch 3 taken 179071 times.
✓ Branch 4 taken 179071 times.
✓ Branch 5 taken 92800 times.
450942 while !this.currentTokenIs(TokenType::RBRACE) && !this.currentTokenIs(TokenType::EOF) {
1331
4/4
✓ Branch 0 taken 4405 times.
✓ Branch 1 taken 174666 times.
✓ Branch 2 taken 4405 times.
✓ Branch 3 taken 174666 times.
358142 if this.currentTokenIs(TokenType::FOR) {
1332 4405 stmtLstNode.statements.pushBack(cast<IVisitable*>(this.parseForLoop()));
1333
4/4
✓ Branch 0 taken 435 times.
✓ Branch 1 taken 174231 times.
✓ Branch 2 taken 435 times.
✓ Branch 3 taken 174231 times.
353737 } else if this.currentTokenIs(TokenType::FOREACH) {
1334 435 stmtLstNode.statements.pushBack(cast<IVisitable*>(this.parseForeachLoop()));
1335
4/4
✓ Branch 0 taken 1865 times.
✓ Branch 1 taken 172366 times.
✓ Branch 2 taken 1865 times.
✓ Branch 3 taken 172366 times.
348897 } else if this.currentTokenIs(TokenType::WHILE) {
1336 1865 stmtLstNode.statements.pushBack(cast<IVisitable*>(this.parseWhileLoop()));
1337
4/4
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 172316 times.
✓ Branch 2 taken 50 times.
✓ Branch 3 taken 172316 times.
346597 } else if this.currentTokenIs(TokenType::DO) {
1338 50 stmtLstNode.statements.pushBack(cast<IVisitable*>(this.parseDoWhileLoop()));
1339
4/4
✓ Branch 0 taken 24612 times.
✓ Branch 1 taken 147704 times.
✓ Branch 2 taken 24612 times.
✓ Branch 3 taken 147704 times.
344682 } else if this.currentTokenIs(TokenType::IF) {
1340 24612 stmtLstNode.statements.pushBack(cast<IVisitable*>(this.parseIfStmt()));
1341
4/4
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 147665 times.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 147665 times.
320020 } else if this.currentTokenIs(TokenType::SWITCH) {
1342 39 stmtLstNode.statements.pushBack(cast<IVisitable*>(this.parseSwitchStmt()));
1343
4/4
✓ Branch 0 taken 4080 times.
✓ Branch 1 taken 143585 times.
✓ Branch 2 taken 4080 times.
✓ Branch 3 taken 143585 times.
295369 } else if this.currentTokenIs(TokenType::ASSERT) {
1344 4080 stmtLstNode.statements.pushBack(cast<IVisitable*>(this.parseAssertStmt()));
1345
4/4
✓ Branch 0 taken 12758 times.
✓ Branch 1 taken 130827 times.
✓ Branch 2 taken 12758 times.
✓ Branch 3 taken 130827 times.
291250 } else if this.currentTokenIs(TokenType::UNSAFE) {
1346 12758 stmtLstNode.statements.pushBack(cast<IVisitable*>(this.parseUnsafeBlock()));
1347
4/4
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 130701 times.
✓ Branch 2 taken 126 times.
✓ Branch 3 taken 130701 times.
274412 } else if this.currentTokenIs(TokenType::LBRACE) {
1348 126 stmtLstNode.statements.pushBack(cast<IVisitable*>(this.parseAnonymousBlockStmt()));
1349 126 } else {
1350 130701 stmtLstNode.statements.pushBack(this.parseStmt());
1351 174666 }
1352 179071 }
1353 92800 stmtLstNode.closingBraceCodeLoc = this.lexer.getCodeLoc();
1354 92800 this.expect(TokenType::RBRACE);
1355
1356 92800 return this.concludeNode(stmtLstNode);
1357 }
1358
1359 25407 f<ASTTypeLstNode*> Parser.parseTypeLst() {
1360 25407 dyn typeLstNode = this.createNode<ASTTypeLstNode>();
1361
1362 25407 typeLstNode.dataTypes.pushBack(this.parseDataType());
1363
4/4
✓ Branch 0 taken 25407 times.
✓ Branch 1 taken 5813 times.
✓ Branch 2 taken 5813 times.
✓ Branch 3 taken 25407 times.
62440 while this.consumeIf(TokenType::COMMA) {
1364 5813 typeLstNode.dataTypes.pushBack(this.parseDataType());
1365 5813 }
1366
1367 25407 return this.concludeNode(typeLstNode);
1368 }
1369
1370 // typeLstWithEllipsis: typeLst (COMMA ELLIPSIS)?;
1371 5157 f<ASTTypeLstWithEllipsisNode*> Parser.parseTypeLstWithEllipsis() {
1372 5157 dyn typeLstWithEllipsisNode = this.createNode<ASTTypeLstWithEllipsisNode>();
1373
1374 // Parse typeLst. A comma, which is followed by an ellipsis, does not belong to the type list
1375 5157 dyn typeLstNode = this.createNode<ASTTypeLstNode>();
1376 5157 typeLstNode.dataTypes.pushBack(this.parseDataType());
1377
6/6
✓ Branch 0 taken 4440 times.
✓ Branch 1 taken 6676 times.
✓ Branch 2 taken 4440 times.
✓ Branch 3 taken 6676 times.
✓ Branch 4 taken 5959 times.
✓ Branch 5 taken 5157 times.
17075 while this.currentTokenIs(TokenType::COMMA) && this.tokenAt(1l) != TokenType::ELLIPSIS {
1378 5959 this.expect(TokenType::COMMA);
1379 5959 typeLstNode.dataTypes.pushBack(this.parseDataType());
1380 5959 }
1381 5157 typeLstWithEllipsisNode.typeLst = this.concludeNode(typeLstNode);
1382
1383 // Parse optional ellipsis
1384
4/4
✓ Branch 0 taken 4440 times.
✓ Branch 1 taken 717 times.
✓ Branch 2 taken 4440 times.
✓ Branch 3 taken 717 times.
10314 if this.consumeIf(TokenType::COMMA) {
1385 717 this.expect(TokenType::ELLIPSIS);
1386 717 typeLstWithEllipsisNode.hasEllipsis = true;
1387 717 }
1388
1389 5157 return this.concludeNode(typeLstWithEllipsisNode);
1390 }
1391
1392 3071 f<ASTTypeAltsLstNode*> Parser.parseTypeAltsLst() {
1393 3071 dyn typeAltsLstNode = this.createNode<ASTTypeAltsLstNode>();
1394
1395 3071 typeAltsLstNode.dataTypes.pushBack(this.parseDataType());
1396
4/4
✓ Branch 0 taken 3071 times.
✓ Branch 1 taken 2443 times.
✓ Branch 2 taken 2443 times.
✓ Branch 3 taken 3071 times.
11028 while this.consumeIf(TokenType::BITWISE_OR) {
1397 2443 typeAltsLstNode.dataTypes.pushBack(this.parseDataType());
1398 2443 }
1399
1400 3071 return this.concludeNode(typeAltsLstNode);
1401 }
1402
1403 33175 f<ASTParamLstNode*> Parser.parseParamLst() {
1404 33175 dyn paramLstNode = this.createNode<ASTParamLstNode>();
1405
1406 33175 paramLstNode.params.pushBack(this.parseDeclStmt());
1407
4/4
✓ Branch 0 taken 33175 times.
✓ Branch 1 taken 20955 times.
✓ Branch 2 taken 20955 times.
✓ Branch 3 taken 33175 times.
108260 while this.consumeIf(TokenType::COMMA) {
1408 20955 paramLstNode.params.pushBack(this.parseDeclStmt());
1409 20955 }
1410
1411 // Mark each declaration as a function parameter
1412
2/2
✓ Branch 0 taken 54130 times.
✓ Branch 1 taken 33175 times.
87305 foreach ASTDeclStmtNode* param : paramLstNode.params {
1413 54130 param.isFctParam = true;
1414 54130 param.dataType.isParamType = true;
1415 54130 }
1416
1417 33175 return this.concludeNode(paramLstNode);
1418 }
1419
1420 61637 f<ASTArgLstNode*> Parser.parseArgLst() {
1421 61637 dyn argLstNode = this.createNode<ASTArgLstNode>();
1422
1423 61637 argLstNode.args.pushBack(this.parseAssignExpr());
1424
4/4
✓ Branch 0 taken 61637 times.
✓ Branch 1 taken 40697 times.
✓ Branch 2 taken 40697 times.
✓ Branch 3 taken 61637 times.
204668 while this.consumeIf(TokenType::COMMA) {
1425 40697 argLstNode.args.pushBack(this.parseAssignExpr());
1426 40697 }
1427
1428 61637 return this.concludeNode(argLstNode);
1429 }
1430
1431 466 f<ASTEnumItemLstNode*> Parser.parseEnumItemLst() {
1432 466 dyn enumItemLstNode = this.createNode<ASTEnumItemLstNode>();
1433
1434 466 enumItemLstNode.items.pushBack(this.parseEnumItem());
1435
4/4
✓ Branch 0 taken 466 times.
✓ Branch 1 taken 1283 times.
✓ Branch 2 taken 1283 times.
✓ Branch 3 taken 466 times.
3498 while this.consumeIf(TokenType::COMMA) {
1436 1283 enumItemLstNode.items.pushBack(this.parseEnumItem());
1437 1283 }
1438
1439 466 return this.concludeNode(enumItemLstNode);
1440 }
1441
1442 1749 f<ASTEnumItemNode*> Parser.parseEnumItem() {
1443 1749 dyn enumItemNode = this.createNode<ASTEnumItemNode>();
1444
1445 1749 enumItemNode.itemName = this.getIdentifier(false);
1446 1749 this.expect(TokenType::TYPE_IDENTIFIER);
1447
4/4
✓ Branch 0 taken 592 times.
✓ Branch 1 taken 1157 times.
✓ Branch 2 taken 592 times.
✓ Branch 3 taken 1157 times.
3498 if this.consumeIf(TokenType::ASSIGN) {
1448 1157 enumItemNode.hasValue = true;
1449 1157 enumItemNode.itemValue = cast<unsigned int>(this.parseNumeric(this.lexer.getToken(), false, 32));
1450 1157 this.expect(TokenType::INT_LIT);
1451 1157 }
1452
1453 3498 return this.concludeNode(enumItemNode);
1454 1749 }
1455
1456 7861 f<ASTFieldNode*> Parser.parseField() {
1457 7861 dyn fieldNode = this.createNode<ASTFieldNode>();
1458
1459 // Mark the data type and all its template types as field types
1460 7861 this.isParsingFieldType = true;
1461 7861 fieldNode.dataType = this.parseDataType();
1462 7861 this.isParsingFieldType = false;
1463 7861 fieldNode.fieldName = this.getIdentifier(false);
1464 7861 this.expect(TokenType::IDENTIFIER);
1465
1466
4/4
✓ Branch 0 taken 5957 times.
✓ Branch 1 taken 1904 times.
✓ Branch 2 taken 5957 times.
✓ Branch 3 taken 1904 times.
15722 if this.consumeIf(TokenType::ASSIGN) {
1467 1904 fieldNode.defaultValue = this.parseTernaryExpr();
1468 1904 }
1469
1470 15722 return this.concludeNode(fieldNode);
1471 7861 }
1472
1473 749 f<ASTSignatureNode*> Parser.parseSignature() {
1474 749 dyn signatureNode = this.createNode<ASTSignatureNode>();
1475
1476 // Parse qualifierLst
1477 749 const dyn options = [TokenType::F, TokenType::P];
1478
2/2
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 697 times.
749 if !this.currentTokenIsOneOf(options, len(options)) {
1479 697 signatureNode.qualifierLst = this.parseQualifierLst();
1480 697 }
1481
1482
4/4
✓ Branch 0 taken 167 times.
✓ Branch 1 taken 582 times.
✓ Branch 2 taken 582 times.
✓ Branch 3 taken 167 times.
1498 if this.consumeIf(TokenType::F) {
1483 582 this.expect(TokenType::LESS);
1484 582 signatureNode.hasReturnType = true;
1485 582 signatureNode.signatureType = SignatureType::TYPE_FUNCTION;
1486 582 signatureNode.signatureQualifiers = getTypeQualifiersOf(SuperType::TY_FUNCTION);
1487 582 signatureNode.returnType = this.parseDataType();
1488 582 this.consumeGenericClose();
1489 582 } else {
1490 167 this.expect(TokenType::P);
1491 167 signatureNode.signatureType = SignatureType::TYPE_PROCEDURE;
1492 167 signatureNode.signatureQualifiers = getTypeQualifiersOf(SuperType::TY_PROCEDURE);
1493 167 }
1494
1495 // Parse function name
1496 749 signatureNode.methodName = this.getIdentifier(false);
1497 749 this.expect(TokenType::IDENTIFIER);
1498
1499 // Parse typeLst (template type list)
1500
4/4
✓ Branch 0 taken 355 times.
✓ Branch 1 taken 394 times.
✓ Branch 2 taken 355 times.
✓ Branch 3 taken 394 times.
1498 if this.consumeIf(TokenType::LESS) {
1501 394 signatureNode.hasTemplateTypes = true;
1502 394 signatureNode.templateTypeLst = this.parseTypeLst();
1503 394 this.consumeGenericClose();
1504 394 }
1505
1506 // Parse typeLst
1507 749 this.expect(TokenType::LPAREN);
1508
4/4
✓ Branch 0 taken 730 times.
✓ Branch 1 taken 19 times.
✓ Branch 2 taken 730 times.
✓ Branch 3 taken 19 times.
1498 if !this.currentTokenIs(TokenType::RPAREN) {
1509 19 signatureNode.hasParams = true;
1510 19 signatureNode.paramTypeLst = this.parseTypeLst();
1511 19 }
1512 749 this.expect(TokenType::RPAREN);
1513
1514 749 this.expect(TokenType::SEMICOLON);
1515
1516 1498 return this.concludeNode(signatureNode);
1517 749 }
1518
1519 // stmt: (declStmt | exprStmt | returnStmt | breakStmt | continueStmt | fallthroughStmt) SEMICOLON;
1520 // Returns the actual matched node (as IVisitable*), not a generic wrapper.
1521 130701 f<IVisitable*> Parser.parseStmt() {
1522 130701 IVisitable* stmtNode = nil<IVisitable*>;
1523
1524
4/4
✓ Branch 0 taken 40235 times.
✓ Branch 1 taken 90466 times.
✓ Branch 2 taken 40235 times.
✓ Branch 3 taken 90466 times.
261402 if this.currentTokenIs(TokenType::RETURN) {
1525 40235 stmtNode = this.parseReturnStmt();
1526
4/4
✓ Branch 0 taken 741 times.
✓ Branch 1 taken 89725 times.
✓ Branch 2 taken 741 times.
✓ Branch 3 taken 89725 times.
221167 } else if this.currentTokenIs(TokenType::BREAK) {
1527 741 stmtNode = this.parseBreakStmt();
1528
4/4
✓ Branch 0 taken 762 times.
✓ Branch 1 taken 88963 times.
✓ Branch 2 taken 762 times.
✓ Branch 3 taken 88963 times.
180191 } else if this.currentTokenIs(TokenType::CONTINUE) {
1529 762 stmtNode = this.parseContinueStmt();
1530
4/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 88947 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 88947 times.
178688 } else if this.currentTokenIs(TokenType::FALLTHROUGH) {
1531 16 stmtNode = this.parseFallthroughStmt();
1532
2/2
✓ Branch 0 taken 24819 times.
✓ Branch 1 taken 64128 times.
88963 } else if this.isDeclStmtAhead() {
1533 24819 stmtNode = this.parseDeclStmt();
1534 24819 } else {
1535 // exprStmt
1536 64128 dyn exprStmtNode = this.createNode<ASTExprStmtNode>();
1537 64128 exprStmtNode.expr = this.parseAssignExpr();
1538 64128 stmtNode = this.concludeNode(exprStmtNode);
1539 90466 }
1540 130701 this.expect(TokenType::SEMICOLON);
1541
1542 130701 return stmtNode;
1543 }
1544
1545 83810 f<ASTDeclStmtNode*> Parser.parseDeclStmt() {
1546 83810 dyn declStmtNode = this.createNode<ASTDeclStmtNode>();
1547
1548 // Parse dataType
1549 83810 declStmtNode.dataType = this.parseDataType();
1550
1551 83810 declStmtNode.varName = this.getIdentifier(false);
1552 83810 this.expect(TokenType::IDENTIFIER);
1553
1554 // Parse initializer
1555
4/4
✓ Branch 0 taken 53272 times.
✓ Branch 1 taken 30538 times.
✓ Branch 2 taken 53272 times.
✓ Branch 3 taken 30538 times.
167620 if this.consumeIf(TokenType::ASSIGN) {
1556 30538 declStmtNode.hasAssignment = true;
1557 30538 declStmtNode.assignExpr = this.parseAssignExpr();
1558 30538 }
1559
1560 167620 return this.concludeNode(declStmtNode);
1561 83810 }
1562
1563 125596 f<ASTQualifierLstNode*> Parser.parseQualifierLst() {
1564 125596 dyn qualifierLstNode = this.createNode<ASTQualifierLstNode>();
1565
1566 125596 const dyn qualifierSelect = [TokenType::CONST, TokenType::SIGNED, TokenType::UNSIGNED, TokenType::INLINE, TokenType::PUBLIC, TokenType::HEAP, TokenType::COMPOSE];
1567 125596 do {
1568 150594 qualifierLstNode.qualifiers.pushBack(this.parseQualifier());
1569
2/2
✓ Branch 0 taken 125596 times.
✓ Branch 1 taken 24998 times.
150594 } while this.currentTokenIsOneOf(qualifierSelect, len(qualifierSelect));
1570
1571 // Check if qualifier combination is invalid
1572 125596 bool seenSignedOrUnsigned = false;
1573
2/2
✓ Branch 0 taken 150593 times.
✓ Branch 1 taken 125596 times.
276189 foreach ASTQualifierNode* qualifier : qualifierLstNode.qualifiers {
1574 // Check if we have both, signed and unsigned qualifier
1575
4/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 150586 times.
✓ Branch 2 taken 110865 times.
✓ Branch 3 taken 39728 times.
150593 if qualifier.qualifierType != QualifierType::SIGNED && qualifier.qualifierType != QualifierType::UNSIGNED {
1576 110865 continue;
1577 }
1578
2/2
✓ Branch 0 taken 39727 times.
✓ Branch 1 taken 1 time.
39728 if seenSignedOrUnsigned {
1579 1 const ASTNode* qualifierBase = cast<ASTNode*>(qualifier);
1580 1 this.throwParserError(qualifierBase.codeLoc, ParserErrorType::INVALID_QUALIFIER_COMBINATION, "A variable can not be signed and unsigned");
1581 1 }
1582 39728 seenSignedOrUnsigned = true;
1583 39728 }
1584
1585 125596 return this.concludeNode(qualifierLstNode);
1586 }
1587
1588 150594 f<ASTQualifierNode*> Parser.parseQualifier() {
1589 150594 dyn qualifierNode = this.createNode<ASTQualifierNode>();
1590
1591
4/4
✓ Branch 0 taken 118071 times.
✓ Branch 1 taken 32523 times.
✓ Branch 2 taken 32523 times.
✓ Branch 3 taken 118071 times.
301188 if this.consumeIf(TokenType::CONST) {
1592 32523 qualifierNode.qualifierType = QualifierType::CONST;
1593
4/4
✓ Branch 0 taken 118063 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 118063 times.
268665 } else if this.consumeIf(TokenType::SIGNED) {
1594 8 qualifierNode.qualifierType = QualifierType::SIGNED;
1595
4/4
✓ Branch 0 taken 78342 times.
✓ Branch 1 taken 39721 times.
✓ Branch 2 taken 39721 times.
✓ Branch 3 taken 78342 times.
236134 } else if this.consumeIf(TokenType::UNSIGNED) {
1596 39721 qualifierNode.qualifierType = QualifierType::UNSIGNED;
1597
4/4
✓ Branch 0 taken 65316 times.
✓ Branch 1 taken 13026 times.
✓ Branch 2 taken 13026 times.
✓ Branch 3 taken 65316 times.
196405 } else if this.consumeIf(TokenType::INLINE) {
1598 13026 qualifierNode.qualifierType = QualifierType::INLINE;
1599
4/4
✓ Branch 0 taken 18596 times.
✓ Branch 1 taken 46720 times.
✓ Branch 2 taken 46720 times.
✓ Branch 3 taken 18596 times.
143658 } else if this.consumeIf(TokenType::PUBLIC) {
1600 46720 qualifierNode.qualifierType = QualifierType::PUBLIC;
1601
4/4
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 18577 times.
✓ Branch 2 taken 18577 times.
✓ Branch 3 taken 19 times.
83912 } else if this.consumeIf(TokenType::HEAP) {
1602 18577 qualifierNode.qualifierType = QualifierType::HEAP;
1603
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
18615 } else if this.consumeIf(TokenType::COMPOSE) {
1604 19 qualifierNode.qualifierType = QualifierType::COMPOSE;
1605 19 } else {
1606 ✗ assert false;
1607 118071 }
1608
1609 150594 return this.concludeNode(qualifierNode);
1610 }
1611
1612 1540 f<ASTModAttrNode*> Parser.parseModAttr() {
1613 1540 dyn modAttrNode = this.createNode<ASTModAttrNode>();
1614
1615 1540 this.expect(TokenType::MOD_ATTR_PREAMBLE);
1616 1540 this.expect(TokenType::LBRACKET);
1617 1540 modAttrNode.attrLst = this.parseAttrLst();
1618 1540 this.expect(TokenType::RBRACKET);
1619
1620 // Tell the attributes that they are module attributes
1621
2/2
✓ Branch 0 taken 1984 times.
✓ Branch 1 taken 1540 times.
3524 foreach ASTAttrNode* attr : modAttrNode.attrLst.attributes {
1622 1984 attr.target = AttrTarget::TARGET_MODULE;
1623 1984 }
1624
1625 1540 return this.concludeNode(modAttrNode);
1626 }
1627
1628 1574 f<ASTTopLevelDefAttrNode*> Parser.parseTopLevelDefAttr(AttrTarget target) {
1629 1574 dyn topLevelDefAttrNode = this.createNode<ASTTopLevelDefAttrNode>();
1630
1631 1574 this.expect(TokenType::FCT_ATTR_PREAMBLE);
1632 1574 this.expect(TokenType::LBRACKET);
1633 1574 topLevelDefAttrNode.attrLst = this.parseAttrLst();
1634 1574 this.expect(TokenType::RBRACKET);
1635
1636 // Tell the attributes which construct they are attached to (function, struct, ...)
1637
2/2
✓ Branch 0 taken 1595 times.
✓ Branch 1 taken 1574 times.
3169 foreach ASTAttrNode* attr : topLevelDefAttrNode.attrLst.attributes {
1638 1595 attr.target = target;
1639 1595 }
1640
1641 1574 return this.concludeNode(topLevelDefAttrNode);
1642 }
1643
1644 21 f<ASTLambdaAttrNode*> Parser.parseLambdaAttr() {
1645 21 dyn lambdaAttrNode = this.createNode<ASTLambdaAttrNode>();
1646
1647 21 this.expect(TokenType::LBRACKET);
1648 21 this.expect(TokenType::LBRACKET);
1649 21 lambdaAttrNode.attrLst = this.parseAttrLst();
1650 21 this.expect(TokenType::RBRACKET);
1651 21 this.expect(TokenType::RBRACKET);
1652
1653 // Tell the attributes that they are lambda attributes
1654
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 21 times.
42 foreach ASTAttrNode* attr : lambdaAttrNode.attrLst.attributes {
1655 21 attr.target = AttrTarget::TARGET_LAMBDA;
1656 21 }
1657
1658 21 return this.concludeNode(lambdaAttrNode);
1659 }
1660
1661 3135 f<ASTAttrLstNode*> Parser.parseAttrLst() {
1662 3135 dyn attrLstNode = this.createNode<ASTAttrLstNode>();
1663
1664 3135 attrLstNode.attributes.pushBack(this.parseAttr());
1665
4/4
✓ Branch 0 taken 3134 times.
✓ Branch 1 taken 466 times.
✓ Branch 2 taken 465 times.
✓ Branch 3 taken 3135 times.
7200 while this.consumeIf(TokenType::COMMA) {
1666 465 attrLstNode.attributes.pushBack(this.parseAttr());
1667 465 }
1668
1669 3135 return this.concludeNode(attrLstNode);
1670 }
1671
1672 3601 f<ASTAttrNode*> Parser.parseAttr() {
1673 3601 dyn attrNode = this.createNode<ASTAttrNode>();
1674
1675 // Parse attribute name
1676 3601 String key = this.lexer.getToken().text;
1677 3601 this.expect(TokenType::IDENTIFIER);
1678
4/4
✓ Branch 0 taken 3601 times.
✓ Branch 1 taken 4990 times.
✓ Branch 2 taken 4990 times.
✓ Branch 3 taken 3601 times.
17182 while this.consumeIf(TokenType::DOT) {
1679 4990 key += ".";
1680 4990 key += this.lexer.getToken().text;
1681 4990 this.expect(TokenType::IDENTIFIER);
1682 4990 }
1683 3601 attrNode.key = key;
1684
1685 // Parse attribute value
1686
4/4
✓ Branch 0 taken 1137 times.
✓ Branch 1 taken 2464 times.
✓ Branch 2 taken 2464 times.
✓ Branch 3 taken 1137 times.
7202 if this.consumeIf(TokenType::ASSIGN) {
1687 // Classify the value's type from the upcoming literal token
1688 // (values other than string/int/bool are rejected by later stages, not enforced here).
1689 2464 const TokenType valueTok = this.lexer.getToken().tokenType;
1690
2/2
✓ Branch 0 taken 496 times.
✓ Branch 1 taken 1968 times.
2464 if valueTok == TokenType::STRING_LIT {
1691 496 attrNode.attrType = AttrType::TYPE_STRING;
1692
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 1713 times.
2464 } else if valueTok == TokenType::INT_LIT {
1693 255 attrNode.attrType = AttrType::TYPE_INT;
1694
4/4
✓ Branch 0 taken 1701 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 1712 times.
✓ Branch 3 taken 1 time.
1968 } else if valueTok == TokenType::TRUE || valueTok == TokenType::FALSE {
1695 1712 attrNode.attrType = AttrType::TYPE_BOOL;
1696 1712 } else {
1697 1 this.throwParserError(this.lexer.getCodeLoc(), ParserErrorType::INVALID_ATTR_VALUE_TYPE, "Invalid attribute value type");
1698 1968 }
1699 2464 attrNode.value = this.parseConstant();
1700 2464 } else {
1701 // If no value is given, use the bool type
1702 1137 attrNode.attrType = AttrType::TYPE_BOOL;
1703 1137 }
1704
1705 7202 return this.concludeNode(attrNode);
1706 3601 }
1707
1708 201 f<ASTCaseConstantNode*> Parser.parseCaseConstant() {
1709 201 dyn caseConstantNode = this.createNode<ASTCaseConstantNode>();
1710
1711 201 const dyn optionsVarExpr = [TokenType::IDENTIFIER, TokenType::TYPE_IDENTIFIER];
1712
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 103 times.
201 if this.currentTokenIsOneOf(optionsVarExpr, len(optionsVarExpr)) {
1713 98 String fqIdentifier;
1714 // Optional (IDENTIFIER SCOPE_ACCESS) prefix
1715
2/4
✓ Branch 0 taken 98 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 98 times.
✗ Branch 3 not taken.
196 if this.currentTokenIs(TokenType::IDENTIFIER) {
1716 ✗ const String fragment = this.getIdentifier(false);
1717 ✗ caseConstantNode.identifierFragments.pushBack(fragment);
1718 ✗ fqIdentifier += fragment;
1719 ✗ fqIdentifier += SCOPE_ACCESS_TOKEN;
1720 ✗ this.expect(TokenType::IDENTIFIER);
1721 ✗ this.expect(TokenType::SCOPE_ACCESS);
1722 ✗ }
1723 98 const String typeFragment = this.getIdentifier(false);
1724 98 caseConstantNode.identifierFragments.pushBack(typeFragment);
1725 98 fqIdentifier += typeFragment;
1726 98 this.expect(TokenType::TYPE_IDENTIFIER);
1727
4/4
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 96 times.
✓ Branch 2 taken 96 times.
✓ Branch 3 taken 98 times.
388 while this.consumeIf(TokenType::SCOPE_ACCESS) {
1728 96 const String fragment = this.getIdentifier(false);
1729 96 caseConstantNode.identifierFragments.pushBack(fragment);
1730 96 fqIdentifier += SCOPE_ACCESS_TOKEN;
1731 96 fqIdentifier += fragment;
1732 96 this.expect(TokenType::TYPE_IDENTIFIER);
1733 96 }
1734 98 caseConstantNode.fqIdentifier = fqIdentifier;
1735 98 } else {
1736 103 caseConstantNode.constant = this.parseConstant();
1737 103 }
1738
1739 201 return this.concludeNode(caseConstantNode);
1740 }
1741
1742 40235 f<ASTReturnStmtNode*> Parser.parseReturnStmt() {
1743 40235 dyn returnStmtNode = this.createNode<ASTReturnStmtNode>();
1744
1745 40235 this.expect(TokenType::RETURN);
1746
4/4
✓ Branch 0 taken 4402 times.
✓ Branch 1 taken 35833 times.
✓ Branch 2 taken 4402 times.
✓ Branch 3 taken 35833 times.
80470 if !this.currentTokenIs(TokenType::SEMICOLON) {
1747 35833 returnStmtNode.hasReturnValue = true;
1748 35833 returnStmtNode.assignExpr = this.parseAssignExpr();
1749 35833 }
1750
1751 40235 return this.concludeNode(returnStmtNode);
1752 }
1753
1754 741 f<ASTBreakStmtNode*> Parser.parseBreakStmt() {
1755 741 dyn breakStmtNode = this.createNode<ASTBreakStmtNode>();
1756
1757 741 this.expect(TokenType::BREAK);
1758
4/4
✓ Branch 0 taken 725 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 725 times.
✓ Branch 3 taken 16 times.
1482 if this.currentTokenIs(TokenType::INT_LIT) {
1759 16 const String& breakTimes = this.lexer.getToken().text;
1760 16 breakStmtNode.breakTimes = toInt(breakTimes.getRaw());
1761 16 this.expect(TokenType::INT_LIT);
1762 16 }
1763
1764 741 return this.concludeNode(breakStmtNode);
1765 }
1766
1767 762 f<ASTContinueStmtNode*> Parser.parseContinueStmt() {
1768 762 dyn continueStmtNode = this.createNode<ASTContinueStmtNode>();
1769
1770 762 this.expect(TokenType::CONTINUE);
1771
4/4
✓ Branch 0 taken 139 times.
✓ Branch 1 taken 623 times.
✓ Branch 2 taken 139 times.
✓ Branch 3 taken 623 times.
1524 if this.currentTokenIs(TokenType::INT_LIT) {
1772 623 const String& continueTimes = this.lexer.getToken().text;
1773 623 continueStmtNode.continueTimes = toInt(continueTimes.getRaw());
1774 623 this.expect(TokenType::INT_LIT);
1775 623 }
1776
1777 762 return this.concludeNode(continueStmtNode);
1778 }
1779
1780 16 f<ASTFallthroughStmtNode*> Parser.parseFallthroughStmt() {
1781 16 dyn fallthroughStmtNode = this.createNode<ASTFallthroughStmtNode>();
1782
1783 16 this.expect(TokenType::FALLTHROUGH);
1784
1785 16 return this.concludeNode(fallthroughStmtNode);
1786 }
1787
1788 4080 f<ASTAssertStmtNode*> Parser.parseAssertStmt() {
1789 4080 dyn assertStmtNode = this.createNode<ASTAssertStmtNode>();
1790
1791 4080 this.expect(TokenType::ASSERT);
1792 4080 const unsigned long exprStartIdx = this.lexer.getTokenIndex();
1793 4080 assertStmtNode.assignExpr = this.parseAssignExpr();
1794 4080 assertStmtNode.expressionString = this.lexer.getSourceCode(exprStartIdx, this.lexer.getTokenIndex());
1795 4080 this.expect(TokenType::SEMICOLON);
1796
1797 8160 return this.concludeNode(assertStmtNode);
1798 4080 }
1799
1800 // assignExpr: prefixUnaryExpr assignOp assignExpr | ternaryExpr;
1801 // A valid assignment lhs (prefixUnaryExpr) is also a valid ternaryExpr, so we parse a ternaryExpr first
1802 // and only treat it as an assignment if an assignment operator follows.
1803 331563 f<IVisitable*> Parser.parseAssignExpr() {
1804 // Parse lhs
1805 331563 IVisitable* lhs = this.parseTernaryExpr();
1806
1807 // Pass-through if this is no assignment
1808
4/4
✓ Branch 0 taken 304880 times.
✓ Branch 1 taken 26683 times.
✓ Branch 2 taken 301264 times.
✓ Branch 3 taken 30299 times.
636443 if !this.isAssignOp(this.lexer.getToken().tokenType) {
1809 301264 return lhs;
1810 }
1811
1812 30299 dyn assignExprNode = this.createNodeWrappingLastChild<ASTAssignExprNode>();
1813 30299 assignExprNode.lhs = lhs;
1814
1815 // Parse assign operator
1816 30299 assignExprNode.op = this.parseAssignOp();
1817
1818 // Parse rhs
1819 30299 assignExprNode.rhs = this.parseAssignExpr();
1820
1821 30299 return this.concludeExprNode(assignExprNode);
1822 }
1823
1824 // assignOp: ASSIGN | PLUS_EQUAL | MINUS_EQUAL | MUL_EQUAL | DIV_EQUAL | REM_EQUAL | SHL_EQUAL | SHR_EQUAL | AND_EQUAL | OR_EQUAL | XOR_EQUAL;
1825 30299 f<AssignOp> Parser.parseAssignOp() {
1826 30299 const TokenType tokenType = this.lexer.getToken().tokenType;
1827 30299 this.advance();
1828
11/12
✓ Branch 0 taken 26683 times.
✓ Branch 1 taken 2856 times.
✓ Branch 2 taken 179 times.
✓ Branch 3 taken 144 times.
✓ Branch 4 taken 97 times.
✓ Branch 5 taken 50 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 11 times.
✓ Branch 8 taken 21 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 23 times.
✓ Branch 11 taken 231 times.
30299 switch tokenType {
1829 26683 case TokenType::ASSIGN: { return AssignOp::OP_ASSIGN; }
1830 2856 case TokenType::PLUS_EQUAL: { return AssignOp::OP_PLUS_EQUAL; }
1831 179 case TokenType::MINUS_EQUAL: { return AssignOp::OP_MINUS_EQUAL; }
1832 144 case TokenType::MUL_EQUAL: { return AssignOp::OP_MUL_EQUAL; }
1833 97 case TokenType::DIV_EQUAL: { return AssignOp::OP_DIV_EQUAL; }
1834 50 case TokenType::REM_EQUAL: { return AssignOp::OP_REM_EQUAL; }
1835 4 case TokenType::SHL_EQUAL: { return AssignOp::OP_SHL_EQUAL; }
1836 11 case TokenType::SHR_EQUAL: { return AssignOp::OP_SHR_EQUAL; }
1837 21 case TokenType::AND_EQUAL: { return AssignOp::OP_AND_EQUAL; }
1838 23 case TokenType::OR_EQUAL: { return AssignOp::OP_OR_EQUAL; }
1839 231 case TokenType::XOR_EQUAL: { return AssignOp::OP_XOR_EQUAL; }
1840 }
1841 − panic(Error("Unknown assign operator")); // LCOV_EXCL_LINE
1842 }
1843
1844 // ternaryExpr: logicalOrExpr (QUESTION_MARK logicalOrExpr? COLON logicalOrExpr)?;
1845 333467 f<IVisitable*> Parser.parseTernaryExpr() {
1846 // Parse condition
1847 333467 IVisitable* condition = this.parseLogicalOrExpr();
1848
1849 // Pass-through if this is no ternary
1850
4/4
✓ Branch 0 taken 330867 times.
✓ Branch 1 taken 2600 times.
✓ Branch 2 taken 330867 times.
✓ Branch 3 taken 2600 times.
666934 if !this.currentTokenIs(TokenType::QUESTION_MARK) {
1851 330867 return condition;
1852 }
1853
1854 2600 dyn ternaryExprNode = this.createNodeWrappingLastChild<ASTTernaryExprNode>();
1855 2600 ternaryExprNode.condition = condition;
1856
1857 // Parse both branches. The true branch is omitted in the shortened form (COND ?: FALSE_EXPR)
1858 2600 this.expect(TokenType::QUESTION_MARK);
1859
4/4
✓ Branch 0 taken 2598 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2598 times.
5200 if this.consumeIf(TokenType::COLON) {
1860 2 ternaryExprNode.isShortened = true;
1861 2 ternaryExprNode.falseExpr = this.parseLogicalOrExpr();
1862 2 } else {
1863 2598 ternaryExprNode.trueExpr = this.parseLogicalOrExpr();
1864 2598 this.expect(TokenType::COLON);
1865 2598 ternaryExprNode.falseExpr = this.parseLogicalOrExpr();
1866 2598 }
1867
1868 2600 return this.concludeExprNode(ternaryExprNode);
1869 }
1870
1871 // logicalOrExpr: logicalAndExpr (LOGICAL_OR logicalAndExpr)*;
1872 338665 f<IVisitable*> Parser.parseLogicalOrExpr() {
1873 // Parse first operand
1874 338665 IVisitable* firstOperand = this.parseLogicalAndExpr();
1875
1876 // Pass-through if there is only one operand
1877
4/4
✓ Branch 0 taken 334611 times.
✓ Branch 1 taken 4054 times.
✓ Branch 2 taken 334611 times.
✓ Branch 3 taken 4054 times.
677330 if !this.currentTokenIs(TokenType::LOGICAL_OR) {
1878 334611 return firstOperand;
1879 }
1880
1881 4054 dyn logicalOrExprNode = this.createNodeWrappingLastChild<ASTLogicalOrExprNode>();
1882 4054 logicalOrExprNode.operands.pushBack(firstOperand);
1883
1884 // Parse remaining operands
1885
4/4
✓ Branch 0 taken 4054 times.
✓ Branch 1 taken 5054 times.
✓ Branch 2 taken 5054 times.
✓ Branch 3 taken 4054 times.
18216 while this.consumeIf(TokenType::LOGICAL_OR) {
1886 5054 logicalOrExprNode.operands.pushBack(this.parseLogicalAndExpr());
1887 5054 }
1888
1889 4054 return this.concludeExprNode(logicalOrExprNode);
1890 }
1891
1892 // logicalAndExpr: bitwiseOrExpr (LOGICAL_AND bitwiseOrExpr)*;
1893 343719 f<IVisitable*> Parser.parseLogicalAndExpr() {
1894 // Parse first operand
1895 343719 IVisitable* firstOperand = this.parseBitwiseOrExpr();
1896
1897 // Pass-through if there is only one operand
1898
4/4
✓ Branch 0 taken 341589 times.
✓ Branch 1 taken 2130 times.
✓ Branch 2 taken 341589 times.
✓ Branch 3 taken 2130 times.
687438 if !this.currentTokenIs(TokenType::LOGICAL_AND) {
1899 341589 return firstOperand;
1900 }
1901
1902 2130 dyn logicalAndExprNode = this.createNodeWrappingLastChild<ASTLogicalAndExprNode>();
1903 2130 logicalAndExprNode.operands.pushBack(firstOperand);
1904
1905 // Parse remaining operands
1906
4/4
✓ Branch 0 taken 2130 times.
✓ Branch 1 taken 2376 times.
✓ Branch 2 taken 2376 times.
✓ Branch 3 taken 2130 times.
9012 while this.consumeIf(TokenType::LOGICAL_AND) {
1907 2376 logicalAndExprNode.operands.pushBack(this.parseBitwiseOrExpr());
1908 2376 }
1909
1910 2130 return this.concludeExprNode(logicalAndExprNode);
1911 }
1912
1913 // bitwiseOrExpr: bitwiseXorExpr (BITWISE_OR bitwiseXorExpr)*;
1914 346095 f<IVisitable*> Parser.parseBitwiseOrExpr() {
1915 // Parse first operand
1916 346095 IVisitable* firstOperand = this.parseBitwiseXorExpr();
1917
1918 // Pass-through if there is only one operand
1919
4/4
✓ Branch 0 taken 345665 times.
✓ Branch 1 taken 430 times.
✓ Branch 2 taken 345665 times.
✓ Branch 3 taken 430 times.
692190 if !this.currentTokenIs(TokenType::BITWISE_OR) {
1920 345665 return firstOperand;
1921 }
1922
1923 430 dyn bitwiseOrExprNode = this.createNodeWrappingLastChild<ASTBitwiseOrExprNode>();
1924 430 bitwiseOrExprNode.operands.pushBack(firstOperand);
1925
1926 // Parse remaining operands
1927
4/4
✓ Branch 0 taken 430 times.
✓ Branch 1 taken 434 times.
✓ Branch 2 taken 434 times.
✓ Branch 3 taken 430 times.
1728 while this.consumeIf(TokenType::BITWISE_OR) {
1928 434 bitwiseOrExprNode.operands.pushBack(this.parseBitwiseXorExpr());
1929 434 }
1930
1931 430 return this.concludeExprNode(bitwiseOrExprNode);
1932 }
1933
1934 // bitwiseXorExpr: bitwiseAndExpr (BITWISE_XOR bitwiseAndExpr)*;
1935 346529 f<IVisitable*> Parser.parseBitwiseXorExpr() {
1936 // Parse first operand
1937 346529 IVisitable* firstOperand = this.parseBitwiseAndExpr();
1938
1939 // Pass-through if there is only one operand
1940
4/4
✓ Branch 0 taken 346452 times.
✓ Branch 1 taken 77 times.
✓ Branch 2 taken 346452 times.
✓ Branch 3 taken 77 times.
693058 if !this.currentTokenIs(TokenType::BITWISE_XOR) {
1941 346452 return firstOperand;
1942 }
1943
1944 77 dyn bitwiseXorExprNode = this.createNodeWrappingLastChild<ASTBitwiseXorExprNode>();
1945 77 bitwiseXorExprNode.operands.pushBack(firstOperand);
1946
1947 // Parse remaining operands
1948
4/4
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 79 times.
✓ Branch 2 taken 79 times.
✓ Branch 3 taken 77 times.
312 while this.consumeIf(TokenType::BITWISE_XOR) {
1949 79 bitwiseXorExprNode.operands.pushBack(this.parseBitwiseAndExpr());
1950 79 }
1951
1952 77 return this.concludeExprNode(bitwiseXorExprNode);
1953 }
1954
1955 // bitwiseAndExpr: equalityExpr (BITWISE_AND equalityExpr)*;
1956 346608 f<IVisitable*> Parser.parseBitwiseAndExpr() {
1957 // Parse first operand
1958 346608 IVisitable* firstOperand = this.parseEqualityExpr();
1959
1960 // Pass-through if there is only one operand
1961
4/4
✓ Branch 0 taken 346348 times.
✓ Branch 1 taken 260 times.
✓ Branch 2 taken 346348 times.
✓ Branch 3 taken 260 times.
693216 if !this.currentTokenIs(TokenType::BITWISE_AND) {
1962 346348 return firstOperand;
1963 }
1964
1965 260 dyn bitwiseAndExprNode = this.createNodeWrappingLastChild<ASTBitwiseAndExprNode>();
1966 260 bitwiseAndExprNode.operands.pushBack(firstOperand);
1967
1968 // Parse remaining operands
1969
4/4
✓ Branch 0 taken 260 times.
✓ Branch 1 taken 262 times.
✓ Branch 2 taken 262 times.
✓ Branch 3 taken 260 times.
1044 while this.consumeIf(TokenType::BITWISE_AND) {
1970 262 bitwiseAndExprNode.operands.pushBack(this.parseEqualityExpr());
1971 262 }
1972
1973 260 return this.concludeExprNode(bitwiseAndExprNode);
1974 }
1975
1976 // equalityExpr: relationalExpr ((EQUAL | NOT_EQUAL) relationalExpr)?;
1977 346870 f<IVisitable*> Parser.parseEqualityExpr() {
1978 // Parse first operand
1979 346870 IVisitable* firstOperand = this.parseRelationalExpr();
1980
1981 // Pass-through if there is only one operand
1982 346870 const dyn options = [TokenType::EQUAL, TokenType::NOT_EQUAL];
1983
2/2
✓ Branch 0 taken 319750 times.
✓ Branch 1 taken 27120 times.
346870 if !this.currentTokenIsOneOf(options, len(options)) {
1984 319750 return firstOperand;
1985 }
1986
1987 27120 dyn equalityExprNode = this.createNodeWrappingLastChild<ASTEqualityExprNode>();
1988 27120 equalityExprNode.operands.pushBack(firstOperand);
1989
1990 // Extract operator
1991
4/4
✓ Branch 0 taken 22763 times.
✓ Branch 1 taken 4357 times.
✓ Branch 2 taken 22763 times.
✓ Branch 3 taken 4357 times.
54240 equalityExprNode.op = this.currentTokenIs(TokenType::EQUAL) ? EqualityOp::OP_EQUAL : EqualityOp::OP_NOT_EQUAL;
1992 27120 this.advance();
1993
1994 // Parse second operand
1995 27120 equalityExprNode.operands.pushBack(this.parseRelationalExpr());
1996
1997 27120 return this.concludeExprNode(equalityExprNode);
1998 }
1999
2000 // relationalExpr: shiftExpr ((LESS | GREATER | LESS_EQUAL | GREATER_EQUAL) shiftExpr)?;
2001 373990 f<IVisitable*> Parser.parseRelationalExpr() {
2002 // Parse first operand
2003 373990 IVisitable* firstOperand = this.parseShiftExpr();
2004
2005 // Pass-through if there is only one operand
2006 373990 const dyn options = [TokenType::LESS, TokenType::GREATER, TokenType::LESS_EQUAL, TokenType::GREATER_EQUAL];
2007
2/2
✓ Branch 0 taken 357574 times.
✓ Branch 1 taken 16416 times.
373990 if !this.currentTokenIsOneOf(options, len(options)) {
2008 357574 return firstOperand;
2009 }
2010
2011 16416 dyn relationalExprNode = this.createNodeWrappingLastChild<ASTRelationalExprNode>();
2012 16416 relationalExprNode.operands.pushBack(firstOperand);
2013
2014 // Extract operator
2015 16416 const TokenType tokenType = this.lexer.getToken().tokenType;
2016 16416 this.advance();
2017
4/5
✗ Branch 0 not taken.
✓ Branch 1 taken 7305 times.
✓ Branch 2 taken 4023 times.
✓ Branch 3 taken 1604 times.
✓ Branch 4 taken 3484 times.
16416 switch tokenType {
2018 7305 case TokenType::LESS: { relationalExprNode.op = RelationalOp::OP_LESS; }
2019 4023 case TokenType::GREATER: { relationalExprNode.op = RelationalOp::OP_GREATER; }
2020 1604 case TokenType::LESS_EQUAL: { relationalExprNode.op = RelationalOp::OP_LESS_EQUAL; }
2021 3484 case TokenType::GREATER_EQUAL: { relationalExprNode.op = RelationalOp::OP_GREATER_EQUAL; }
2022 }
2023
2024 // Parse second operand
2025 16416 relationalExprNode.operands.pushBack(this.parseShiftExpr());
2026
2027 16416 return this.concludeExprNode(relationalExprNode);
2028 }
2029
2030 // shiftExpr: additiveExpr ((LESS LESS | GREATER GREATER) additiveExpr)*;
2031 // The lexer merges '<<' and '>>' into single SHL / SHR tokens.
2032 390406 f<IVisitable*> Parser.parseShiftExpr() {
2033 // Parse first operand
2034 390406 IVisitable* firstOperand = this.parseAdditiveExpr();
2035
2036 // Pass-through if there is only one operand
2037 390406 const dyn options = [TokenType::SHL, TokenType::SHR];
2038
2/2
✓ Branch 0 taken 389799 times.
✓ Branch 1 taken 607 times.
390406 if !this.currentTokenIsOneOf(options, len(options)) {
2039 389799 return firstOperand;
2040 }
2041
2042 607 dyn shiftExprNode = this.createNodeWrappingLastChild<ASTShiftExprNode>();
2043 607 shiftExprNode.operands.pushBack(firstOperand);
2044
2045 // Parse remaining operands
2046
2/2
✓ Branch 0 taken 730 times.
✓ Branch 1 taken 607 times.
1337 while this.currentTokenIsOneOf(options, len(options)) {
2047 // Extract operator
2048
4/4
✓ Branch 0 taken 432 times.
✓ Branch 1 taken 298 times.
✓ Branch 2 taken 432 times.
✓ Branch 3 taken 298 times.
1460 const ShiftOp op = this.currentTokenIs(TokenType::SHL) ? ShiftOp::OP_SHIFT_LEFT : ShiftOp::OP_SHIFT_RIGHT;
2049 730 shiftExprNode.opQueue.push(Pair<ShiftOp, QualType>(op, QualType(SuperType::TY_INVALID)));
2050 730 this.advance();
2051
2052 // Parse operand
2053 730 shiftExprNode.operands.pushBack(this.parseAdditiveExpr());
2054 730 }
2055
2056 607 return this.concludeExprNode(shiftExprNode);
2057 }
2058
2059 // additiveExpr: multiplicativeExpr ((PLUS | MINUS) multiplicativeExpr)*;
2060 391136 f<IVisitable*> Parser.parseAdditiveExpr() {
2061 // Parse first operand
2062 391136 IVisitable* firstOperand = this.parseMultiplicativeExpr();
2063
2064 // Pass-through if there is only one operand
2065 391136 const dyn options = [TokenType::PLUS, TokenType::MINUS];
2066
2/2
✓ Branch 0 taken 376142 times.
✓ Branch 1 taken 14994 times.
391136 if !this.currentTokenIsOneOf(options, len(options)) {
2067 376142 return firstOperand;
2068 }
2069
2070 14994 dyn additiveExprNode = this.createNodeWrappingLastChild<ASTAdditiveExprNode>();
2071 14994 additiveExprNode.operands.pushBack(firstOperand);
2072
2073 // Parse remaining operands
2074
2/2
✓ Branch 0 taken 16504 times.
✓ Branch 1 taken 14994 times.
31498 while this.currentTokenIsOneOf(options, len(options)) {
2075 // Extract operator
2076
4/4
✓ Branch 0 taken 10848 times.
✓ Branch 1 taken 5656 times.
✓ Branch 2 taken 10848 times.
✓ Branch 3 taken 5656 times.
33008 const AdditiveOp op = this.currentTokenIs(TokenType::PLUS) ? AdditiveOp::OP_PLUS : AdditiveOp::OP_MINUS;
2077 16504 additiveExprNode.opQueue.push(Pair<AdditiveOp, QualType>(op, QualType(SuperType::TY_INVALID)));
2078 16504 this.advance();
2079
2080 // Parse operand
2081 16504 additiveExprNode.operands.pushBack(this.parseMultiplicativeExpr());
2082 16504 }
2083
2084 14994 return this.concludeExprNode(additiveExprNode);
2085 }
2086
2087 // multiplicativeExpr: castExpr ((MUL | DIV | REM) castExpr)*;
2088 407640 f<IVisitable*> Parser.parseMultiplicativeExpr() {
2089 // Parse first operand
2090 407640 IVisitable* firstOperand = this.parseCastExpr();
2091
2092 // Pass-through if there is only one operand
2093 407640 const dyn options = [TokenType::MUL, TokenType::DIV, TokenType::REM];
2094
2/2
✓ Branch 0 taken 404305 times.
✓ Branch 1 taken 3335 times.
407640 if !this.currentTokenIsOneOf(options, len(options)) {
2095 404305 return firstOperand;
2096 }
2097
2098 3335 dyn multiplicativeExprNode = this.createNodeWrappingLastChild<ASTMultiplicativeExprNode>();
2099 3335 multiplicativeExprNode.operands.pushBack(firstOperand);
2100
2101 // Parse remaining operands
2102
2/2
✓ Branch 0 taken 3389 times.
✓ Branch 1 taken 3335 times.
6724 while this.currentTokenIsOneOf(options, len(options)) {
2103 // Extract operator
2104 3389 MultiplicativeOp op = MultiplicativeOp::OP_REM;
2105
4/4
✓ Branch 0 taken 2841 times.
✓ Branch 1 taken 548 times.
✓ Branch 2 taken 2841 times.
✓ Branch 3 taken 548 times.
6778 if this.currentTokenIs(TokenType::MUL) {
2106 2841 op = MultiplicativeOp::OP_MUL;
2107
4/4
✓ Branch 0 taken 264 times.
✓ Branch 1 taken 284 times.
✓ Branch 2 taken 264 times.
✓ Branch 3 taken 284 times.
3937 } else if this.currentTokenIs(TokenType::DIV) {
2108 284 op = MultiplicativeOp::OP_DIV;
2109 548 }
2110 3389 multiplicativeExprNode.opQueue.push(Pair<MultiplicativeOp, QualType>(op, QualType(SuperType::TY_INVALID)));
2111 3389 this.advance();
2112
2113 // Parse operand
2114 3389 multiplicativeExprNode.operands.pushBack(this.parseCastExpr());
2115 3389 }
2116
2117 3335 return this.concludeExprNode(multiplicativeExprNode);
2118 }
2119
2120 // castExpr: prefixUnaryExpr | CAST LESS dataType GREATER LPAREN assignExpr RPAREN;
2121 411032 f<IVisitable*> Parser.parseCastExpr() {
2122 // Pass-through if this is no cast
2123
4/4
✓ Branch 0 taken 398583 times.
✓ Branch 1 taken 12449 times.
✓ Branch 2 taken 398583 times.
✓ Branch 3 taken 12449 times.
822064 if !this.currentTokenIs(TokenType::CAST) {
2124 398583 return this.parsePrefixUnaryExpr();
2125 }
2126
2127 12449 dyn castExprNode = this.createNode<ASTCastExprNode>();
2128
2129 12449 this.expect(TokenType::CAST);
2130 12449 this.expect(TokenType::LESS);
2131 12449 castExprNode.dataType = this.parseDataType();
2132 12449 this.consumeGenericClose();
2133 12449 this.expect(TokenType::LPAREN);
2134 12449 castExprNode.assignExpr = this.parseAssignExpr();
2135 12449 this.expect(TokenType::RPAREN);
2136 12449 castExprNode.isCast = true;
2137
2138 12449 return this.concludeExprNode(castExprNode);
2139 }
2140
2141 // prefixUnaryExpr: postfixUnaryExpr | (MINUS | PLUS_PLUS | MINUS_MINUS | NOT | BITWISE_NOT | MUL | BITWISE_AND) prefixUnaryExpr;
2142 405611 f<IVisitable*> Parser.parsePrefixUnaryExpr() {
2143 // Pass-through if there is no prefix operator. A minus sign directly in front of a numeric literal belongs to the
2144 // constant, because the grammar prefers the postfixUnaryExpr alternative in that case.
2145 405611 const dyn options = [TokenType::MINUS, TokenType::PLUS_PLUS, TokenType::MINUS_MINUS, TokenType::NOT, TokenType::BITWISE_NOT, TokenType::MUL, TokenType::BITWISE_AND];
2146
6/6
✓ Branch 0 taken 394217 times.
✓ Branch 1 taken 11394 times.
✓ Branch 2 taken 6902 times.
✓ Branch 3 taken 4492 times.
✓ Branch 4 taken 398583 times.
✓ Branch 5 taken 7028 times.
417005 if !this.currentTokenIsOneOf(options, len(options)) || this.isNegativeNumericConstantAhead() {
2147 398583 return this.parsePostfixUnaryExpr();
2148 }
2149
2150 7028 dyn prefixUnaryExprNode = this.createNode<ASTPrefixUnaryExprNode>();
2151
2152 // Extract operator
2153 7028 const TokenType tokenType = this.lexer.getToken().tokenType;
2154 7028 this.advance();
2155
7/8
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 106 times.
✓ Branch 2 taken 4145 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 126 times.
✓ Branch 5 taken 30 times.
✓ Branch 6 taken 954 times.
✓ Branch 7 taken 1650 times.
7028 switch tokenType {
2156 126 case TokenType::MINUS: { prefixUnaryExprNode.op = PrefixUnaryOp::OP_MINUS; }
2157 17 case TokenType::PLUS_PLUS: { prefixUnaryExprNode.op = PrefixUnaryOp::OP_PLUS_PLUS; }
2158 106 case TokenType::MINUS_MINUS: { prefixUnaryExprNode.op = PrefixUnaryOp::OP_MINUS_MINUS; }
2159 4145 case TokenType::NOT: { prefixUnaryExprNode.op = PrefixUnaryOp::OP_NOT; }
2160 30 case TokenType::BITWISE_NOT: { prefixUnaryExprNode.op = PrefixUnaryOp::OP_BITWISE_NOT; }
2161 954 case TokenType::MUL: { prefixUnaryExprNode.op = PrefixUnaryOp::OP_DEREFERENCE; }
2162 1650 case TokenType::BITWISE_AND: { prefixUnaryExprNode.op = PrefixUnaryOp::OP_ADDRESS_OF; }
2163 }
2164
2165 // Parse operand
2166 7028 prefixUnaryExprNode.prefixUnaryExpr = this.parsePrefixUnaryExpr();
2167
2168 7028 return this.concludeExprNode(prefixUnaryExprNode);
2169 }
2170
2171 // postfixUnaryExpr: atomicExpr | postfixUnaryExpr (LBRACKET assignExpr RBRACKET | DOT IDENTIFIER | PLUS_PLUS | MINUS_MINUS | NOT);
2172 // The rule is left-recursive, so each postfix operator wraps the expression parsed so far into a new node.
2173 398583 f<IVisitable*> Parser.parsePostfixUnaryExpr() {
2174 398583 IVisitable* expr = cast<IVisitable*>(this.parseAtomicExpr());
2175
2176 398583 const dyn options = [TokenType::LBRACKET, TokenType::DOT, TokenType::PLUS_PLUS, TokenType::MINUS_MINUS, TokenType::NOT];
2177
2/2
✓ Branch 0 taken 105647 times.
✓ Branch 1 taken 398583 times.
504230 while this.currentTokenIsOneOf(options, len(options)) {
2178 105647 dyn postfixUnaryExprNode = this.createNodeWrappingLastChild<ASTPostfixUnaryExprNode>();
2179 105647 postfixUnaryExprNode.postfixUnaryExpr = expr;
2180
2181 // Extract operator
2182
4/4
✓ Branch 0 taken 92501 times.
✓ Branch 1 taken 13146 times.
✓ Branch 2 taken 13146 times.
✓ Branch 3 taken 92501 times.
211294 if this.consumeIf(TokenType::LBRACKET) {
2183 13146 postfixUnaryExprNode.op = PostfixUnaryOp::OP_SUBSCRIPT;
2184 13146 postfixUnaryExprNode.subscriptIndexExpr = this.parseAssignExpr();
2185 13146 this.expect(TokenType::RBRACKET);
2186
4/4
✓ Branch 0 taken 8118 times.
✓ Branch 1 taken 84383 times.
✓ Branch 2 taken 84383 times.
✓ Branch 3 taken 8118 times.
198148 } else if this.consumeIf(TokenType::DOT) {
2187 84383 postfixUnaryExprNode.op = PostfixUnaryOp::OP_MEMBER_ACCESS;
2188 84383 postfixUnaryExprNode.identifier = this.getIdentifier(false);
2189 84383 this.expect(TokenType::IDENTIFIER);
2190
4/4
✓ Branch 0 taken 1092 times.
✓ Branch 1 taken 7026 times.
✓ Branch 2 taken 7026 times.
✓ Branch 3 taken 1092 times.
100619 } else if this.consumeIf(TokenType::PLUS_PLUS) {
2191 7026 postfixUnaryExprNode.op = PostfixUnaryOp::OP_PLUS_PLUS;
2192
4/4
✓ Branch 0 taken 95 times.
✓ Branch 1 taken 997 times.
✓ Branch 2 taken 997 times.
✓ Branch 3 taken 95 times.
9210 } else if this.consumeIf(TokenType::MINUS_MINUS) {
2193 997 postfixUnaryExprNode.op = PostfixUnaryOp::OP_MINUS_MINUS;
2194 997 } else {
2195 95 this.expect(TokenType::NOT);
2196 95 postfixUnaryExprNode.op = PostfixUnaryOp::OP_ERR_PROPAGATION;
2197 92501 }
2198
2199 105647 expr = this.concludeExprNode(postfixUnaryExprNode);
2200 105647 }
2201
2202 398583 return expr;
2203 }
2204
2205 // atomicExpr: constant | value | (IDENTIFIER | TYPE_IDENTIFIER) (SCOPE_ACCESS (IDENTIFIER | TYPE_IDENTIFIER))* | LPAREN assignExpr RPAREN;
2206 398583 f<ASTAtomicExprNode*> Parser.parseAtomicExpr() {
2207 398583 dyn atomicExprNode = this.createNode<ASTAtomicExprNode>();
2208
2209 398583 const TokenType cur = this.lexer.getToken().tokenType;
2210
8/8
✓ Branch 0 taken 397419 times.
✓ Branch 1 taken 1164 times.
✓ Branch 2 taken 69753 times.
✓ Branch 3 taken 328830 times.
✓ Branch 4 taken 324464 times.
✓ Branch 5 taken 4366 times.
✓ Branch 6 taken 74119 times.
✓ Branch 7 taken 324464 times.
1124832 if this.isConstantStart(cur) || this.isNegativeNumericConstantAhead() {
2211 74119 atomicExprNode.constant = this.parseConstant();
2212
4/4
✓ Branch 0 taken 322079 times.
✓ Branch 1 taken 2385 times.
✓ Branch 2 taken 2381 times.
✓ Branch 3 taken 322083 times.
398583 } else if cur == TokenType::LPAREN && !this.isLambdaExprAhead() {
2213 2381 this.expect(TokenType::LPAREN);
2214 2381 atomicExprNode.assignExpr = this.parseAssignExpr();
2215 2381 this.expect(TokenType::RPAREN);
2216
8/8
✓ Branch 0 taken 293077 times.
✓ Branch 1 taken 29006 times.
✓ Branch 2 taken 313478 times.
✓ Branch 3 taken 8605 times.
✓ Branch 4 taken 80976 times.
✓ Branch 5 taken 232502 times.
✓ Branch 6 taken 230964 times.
✓ Branch 7 taken 91119 times.
324464 } else if (cur == TokenType::IDENTIFIER || cur == TokenType::TYPE_IDENTIFIER) && !this.isFctCallAhead() && !this.isStructInstantiationAhead() {
2217 // Plain (scoped) variable or constant access
2218 230964 const String firstFragment = this.getIdentifier(false);
2219 230964 String fqIdentifier = firstFragment;
2220 230964 atomicExprNode.identifierFragments.pushBack(firstFragment);
2221 230964 this.advance();
2222
4/4
✓ Branch 0 taken 230964 times.
✓ Branch 1 taken 2338 times.
✓ Branch 2 taken 2338 times.
✓ Branch 3 taken 230964 times.
466604 while this.consumeIf(TokenType::SCOPE_ACCESS) {
2223 2338 const String fragment = this.getIdentifier(false);
2224
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 2338 times.
✓ Branch 2 taken 2338 times.
✗ Branch 3 not taken.
4676 if !this.consumeIf(TokenType::TYPE_IDENTIFIER) {
2225 ✗ this.expect(TokenType::IDENTIFIER);
2226 ✗ }
2227 2338 atomicExprNode.identifierFragments.pushBack(fragment);
2228 2338 fqIdentifier += SCOPE_ACCESS_TOKEN;
2229 2338 fqIdentifier += fragment;
2230 2338 }
2231 230964 atomicExprNode.fqIdentifier = fqIdentifier;
2232 230964 } else {
2233 91119 atomicExprNode.value = cast<IVisitable*>(this.parseValue());
2234 324464 }
2235
2236 398583 return this.concludeNode(atomicExprNode);
2237 }
2238
2239 // value: fctCall | arrayInitialization | structInstantiation | lambdaFunc | lambdaProc | lambdaExpr | NIL LESS dataType GREATER;
2240 91119 f<ASTValueNode*> Parser.parseValue() {
2241 91119 dyn valueNode = this.createNode<ASTValueNode>();
2242
2243
4/4
✓ Branch 0 taken 82838 times.
✓ Branch 1 taken 8281 times.
✓ Branch 2 taken 8281 times.
✓ Branch 3 taken 82838 times.
182238 if this.consumeIf(TokenType::NIL) {
2244 8281 this.expect(TokenType::LESS);
2245 8281 valueNode.isNil = true;
2246 8281 valueNode.nilType = this.parseDataType();
2247 8281 this.consumeGenericClose();
2248
4/4
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 82658 times.
✓ Branch 2 taken 180 times.
✓ Branch 3 taken 82658 times.
173957 } else if this.currentTokenIs(TokenType::LBRACKET) {
2249 180 valueNode.arrayInitialization = cast<IVisitable*>(this.parseArrayInitialization());
2250
4/4
✓ Branch 0 taken 67 times.
✓ Branch 1 taken 82591 times.
✓ Branch 2 taken 67 times.
✓ Branch 3 taken 82591 times.
165496 } else if this.currentTokenIs(TokenType::F) {
2251 67 valueNode.lambdaFunc = cast<IVisitable*>(this.parseLambdaFunc());
2252
4/4
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 82518 times.
✓ Branch 2 taken 73 times.
✓ Branch 3 taken 82518 times.
165249 } else if this.currentTokenIs(TokenType::P) {
2253 73 valueNode.lambdaProc = cast<IVisitable*>(this.parseLambdaProc());
2254
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 82514 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 82514 times.
165109 } else if this.currentTokenIs(TokenType::LPAREN) {
2255 4 valueNode.lambdaExpr = cast<IVisitable*>(this.parseLambdaExpr());
2256
2/2
✓ Branch 0 taken 1538 times.
✓ Branch 1 taken 80976 times.
82518 } else if this.isStructInstantiationAhead() {
2257 1538 valueNode.structInstantiation = cast<IVisitable*>(this.parseStructInstantiation());
2258 1538 } else {
2259 80976 valueNode.fctCall = cast<IVisitable*>(this.parseFctCall());
2260 82838 }
2261
2262 91119 return this.concludeNode(valueNode);
2263 }
2264
2265 // constant: MINUS? (DOUBLE_LIT | INT_LIT | SHORT_LIT | LONG_LIT) | CHAR_LIT | STRING_LIT | TRUE | FALSE;
2266 80964 f<ASTConstantNode*> Parser.parseConstant() {
2267 80964 dyn constantNode = this.createNode<ASTConstantNode>();
2268
2269 // Detect an optional leading minus sign for numeric literals
2270
2/2
✓ Branch 0 taken 76550 times.
✓ Branch 1 taken 4414 times.
161928 const bool isNegative = this.consumeIf(TokenType::MINUS);
2271
2272 80964 const Token& token = this.lexer.getToken();
2273
4/4
✓ Branch 0 taken 1351 times.
✓ Branch 1 taken 79613 times.
✓ Branch 2 taken 1351 times.
✓ Branch 3 taken 79613 times.
161928 if this.currentTokenIs(TokenType::DOUBLE_LIT) {
2274 1351 constantNode.primitiveValueType = PrimitiveValueType::TYPE_DOUBLE;
2275 1351 const double value = toDouble(token.text.getRaw());
2276
2/2
✓ Branch 0 taken 132 times.
✓ Branch 1 taken 1219 times.
1351 constantNode.compileTimeValue.setDoubleValue(isNegative ? -value : value);
2277 1351 this.expect(TokenType::DOUBLE_LIT);
2278
4/4
✓ Branch 0 taken 16704 times.
✓ Branch 1 taken 62909 times.
✓ Branch 2 taken 16704 times.
✓ Branch 3 taken 62909 times.
160577 } else if this.currentTokenIs(TokenType::INT_LIT) {
2279 16704 constantNode.primitiveValueType = PrimitiveValueType::TYPE_INT;
2280 16704 constantNode.isUnsigned = token.text.endsWith("u");
2281 16704 constantNode.compileTimeValue.setIntValue(cast<int>(this.parseNumeric(token, isNegative, 32)));
2282 16704 this.expect(TokenType::INT_LIT);
2283
4/4
✓ Branch 0 taken 2233 times.
✓ Branch 1 taken 60676 times.
✓ Branch 2 taken 2233 times.
✓ Branch 3 taken 60676 times.
142522 } else if this.currentTokenIs(TokenType::SHORT_LIT) {
2284 2233 constantNode.primitiveValueType = PrimitiveValueType::TYPE_SHORT;
2285 2233 constantNode.isUnsigned = token.text.endsWith("u");
2286 2233 constantNode.compileTimeValue.setShortValue(cast<short>(this.parseNumeric(token, isNegative, 16)));
2287 2233 this.expect(TokenType::SHORT_LIT);
2288
4/4
✓ Branch 0 taken 29377 times.
✓ Branch 1 taken 31299 times.
✓ Branch 2 taken 29377 times.
✓ Branch 3 taken 31299 times.
123585 } else if this.currentTokenIs(TokenType::LONG_LIT) {
2289 29377 constantNode.primitiveValueType = PrimitiveValueType::TYPE_LONG;
2290 29377 constantNode.isUnsigned = token.text.endsWith("u");
2291 29377 constantNode.compileTimeValue.setLongValue(this.parseNumeric(token, isNegative, 64));
2292 29377 this.expect(TokenType::LONG_LIT);
2293
4/4
✓ Branch 0 taken 9889 times.
✓ Branch 1 taken 21410 times.
✓ Branch 2 taken 9889 times.
✓ Branch 3 taken 21410 times.
91975 } else if this.currentTokenIs(TokenType::CHAR_LIT) {
2294 9889 constantNode.primitiveValueType = PrimitiveValueType::TYPE_CHAR;
2295 9889 constantNode.compileTimeValue.setCharValue(this.parseChar(token));
2296 9889 this.expect(TokenType::CHAR_LIT);
2297
4/4
✓ Branch 0 taken 14663 times.
✓ Branch 1 taken 6747 times.
✓ Branch 2 taken 14663 times.
✓ Branch 3 taken 6747 times.
52709 } else if this.currentTokenIs(TokenType::STRING_LIT) {
2298 // Save a pointer to the string in the compile time value
2299 14663 constantNode.primitiveValueType = PrimitiveValueType::TYPE_STRING;
2300 14663 constantNode.compileTimeValue.setStringValueOffset(this.resourceManager.compileTimeStringValues.getSize());
2301 // Add the string to the global compile time string list
2302 14663 this.resourceManager.compileTimeStringValues.pushBack(parseString(token.text));
2303 14663 this.expect(TokenType::STRING_LIT);
2304
4/4
✓ Branch 0 taken 3783 times.
✓ Branch 1 taken 2964 times.
✓ Branch 2 taken 3783 times.
✓ Branch 3 taken 2964 times.
28157 } else if this.currentTokenIs(TokenType::TRUE) {
2305 3783 constantNode.primitiveValueType = PrimitiveValueType::TYPE_BOOL;
2306 3783 constantNode.compileTimeValue.setBoolValue(true);
2307 3783 this.expect(TokenType::TRUE);
2308
4/4
✓ Branch 0 taken 2961 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 2961 times.
✓ Branch 3 taken 3 times.
9711 } else if this.currentTokenIs(TokenType::FALSE) {
2309 2961 constantNode.primitiveValueType = PrimitiveValueType::TYPE_BOOL;
2310 2961 constantNode.compileTimeValue.setBoolValue(false);
2311 2961 this.expect(TokenType::FALSE);
2312 2961 } else {
2313 3 const String message = "Unexpected token '" + token.text + "'";
2314 3 this.throwParserError(token.codeLoc, ParserErrorType::PARSING_FAILED, message.getRaw());
2315 79613 }
2316
2317 80964 return this.concludeNode(constantNode);
2318 }
2319
2320 // fctCall: (IDENTIFIER SCOPE_ACCESS)* (IDENTIFIER DOT)* (IDENTIFIER | TYPE_IDENTIFIER) (LESS typeLst GREATER)? LPAREN argLst? RPAREN;
2321 80975 f<ASTFctCallNode*> Parser.parseFctCall() {
2322 80975 dyn fctCallNode = this.createNode<ASTFctCallNode>();
2323
2324 // Scope prefix: (IDENTIFIER SCOPE_ACCESS)*
2325 80975 String fqFunctionName;
2326 80975 this.parseNamePrefix(TokenType::SCOPE_ACCESS, SCOPE_ACCESS_TOKEN, fctCallNode.functionNameFragments, fqFunctionName);
2327 // Member prefix: (IDENTIFIER DOT)*
2328 80975 this.parseNamePrefix(TokenType::DOT, MEMBER_ACCESS_TOKEN, fctCallNode.functionNameFragments, fqFunctionName);
2329 // Final name segment
2330 80975 const String fragment = this.lexer.getToken().text;
2331 80975 fctCallNode.functionNameFragments.pushBack(fragment);
2332 80975 fqFunctionName += fragment;
2333
4/4
✓ Branch 0 taken 69703 times.
✓ Branch 1 taken 11272 times.
✓ Branch 2 taken 11272 times.
✓ Branch 3 taken 69703 times.
161950 if !this.consumeIf(TokenType::TYPE_IDENTIFIER) {
2334 69703 this.expect(TokenType::IDENTIFIER);
2335 69703 }
2336 80975 fctCallNode.fqFunctionName = fqFunctionName;
2337
2338 // Template type list
2339
4/4
✓ Branch 0 taken 75984 times.
✓ Branch 1 taken 4991 times.
✓ Branch 2 taken 75984 times.
✓ Branch 3 taken 4991 times.
161950 if this.consumeIf(TokenType::LESS) {
2340 4991 fctCallNode.hasTemplateTypes = true;
2341 4991 fctCallNode.templateTypeLst = this.parseTypeLst();
2342 4991 this.consumeGenericClose();
2343 4991 }
2344 // Argument list
2345 80975 this.expect(TokenType::LPAREN);
2346
4/4
✓ Branch 0 taken 21002 times.
✓ Branch 1 taken 59973 times.
✓ Branch 2 taken 21002 times.
✓ Branch 3 taken 59973 times.
161950 if !this.currentTokenIs(TokenType::RPAREN) {
2347 59973 fctCallNode.hasArgs = true;
2348 59973 fctCallNode.argLst = this.parseArgLst();
2349 59973 }
2350 80975 this.expect(TokenType::RPAREN);
2351
2352 161950 return this.concludeNode(fctCallNode);
2353 80975 }
2354
2355 // arrayInitialization: LBRACKET argLst? RBRACKET;
2356 180 f<ASTArrayInitializationNode*> Parser.parseArrayInitialization() {
2357 180 dyn arrayInitializationNode = this.createNode<ASTArrayInitializationNode>();
2358
2359 180 this.expect(TokenType::LBRACKET);
2360
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 179 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 179 times.
360 if !this.currentTokenIs(TokenType::RBRACKET) {
2361 179 arrayInitializationNode.itemLst = this.parseArgLst();
2362 179 }
2363 180 this.expect(TokenType::RBRACKET);
2364
2365 180 return this.concludeNode(arrayInitializationNode);
2366 }
2367
2368 // structInstantiation: (IDENTIFIER SCOPE_ACCESS)* TYPE_IDENTIFIER (LESS typeLst GREATER)? LBRACE argLst? RBRACE;
2369 1538 f<ASTStructInstantiationNode*> Parser.parseStructInstantiation() {
2370 1538 dyn structInstantiationNode = this.createNode<ASTStructInstantiationNode>();
2371
2372 // Scope prefix: (IDENTIFIER SCOPE_ACCESS)*
2373 1538 String fqStructName;
2374 1538 this.parseNamePrefix(TokenType::SCOPE_ACCESS, SCOPE_ACCESS_TOKEN, structInstantiationNode.structNameFragments, fqStructName);
2375 1538 const String structName = this.lexer.getToken().text;
2376 1538 structInstantiationNode.structNameFragments.pushBack(structName);
2377 1538 fqStructName += structName;
2378 1538 this.expect(TokenType::TYPE_IDENTIFIER);
2379 1538 structInstantiationNode.fqStructName = fqStructName;
2380
2381
4/4
✓ Branch 0 taken 1508 times.
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 1508 times.
✓ Branch 3 taken 30 times.
3076 if this.consumeIf(TokenType::LESS) {
2382 30 structInstantiationNode.hasTemplateTypes = true;
2383 30 structInstantiationNode.templateTypeLst = this.parseTypeLst();
2384 30 this.consumeGenericClose();
2385 30 }
2386
2387 1538 this.expect(TokenType::LBRACE);
2388
4/4
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 1485 times.
✓ Branch 2 taken 53 times.
✓ Branch 3 taken 1485 times.
3076 if !this.currentTokenIs(TokenType::RBRACE) {
2389 1485 structInstantiationNode.fieldLst = this.parseArgLst();
2390 1485 }
2391 1538 this.expect(TokenType::RBRACE);
2392
2393 3076 return this.concludeNode(structInstantiationNode);
2394 1538 }
2395
2396 // lambdaFunc: F LESS dataType GREATER LPAREN paramLst? RPAREN lambdaAttr? stmtLst;
2397 67 f<ASTLambdaFuncNode*> Parser.parseLambdaFunc() {
2398 67 dyn lambdaFuncNode = this.createNode<ASTLambdaFuncNode>();
2399
2400 67 this.expect(TokenType::F);
2401 67 this.expect(TokenType::LESS);
2402 67 lambdaFuncNode.returnType = this.parseDataType();
2403 67 this.consumeGenericClose();
2404
2405 // Parse paramLst
2406 67 ASTParamLstNode* paramLst = this.parseParenthesizedParamLst();
2407 67 lambdaFuncNode.hasParams = paramLst != nil<ASTParamLstNode*>;
2408 67 lambdaFuncNode.paramLst = paramLst;
2409
2410 // Parse lambda attrs
2411
2/4
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67 times.
✗ Branch 3 not taken.
134 if this.currentTokenIs(TokenType::LBRACKET) {
2412 ✗ lambdaFuncNode.lambdaAttr = this.parseLambdaAttr();
2413 ✗ }
2414
2415 // Parse body
2416 67 lambdaFuncNode.body = cast<IVisitable*>(this.parseStmtLst());
2417
2418 // The lambda attrs are listed after the body in the children of the node
2419
1/2
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
67 if lambdaFuncNode.lambdaAttr != nil<ASTLambdaAttrNode*> {
2420 ✗ this.moveChildToEnd(cast<ASTNode*>(lambdaFuncNode), cast<ASTNode*>(lambdaFuncNode.lambdaAttr));
2421 ✗ }
2422
2423 67 return this.concludeNode(lambdaFuncNode);
2424 }
2425
2426 // lambdaProc: P LPAREN paramLst? RPAREN lambdaAttr? stmtLst;
2427 73 f<ASTLambdaProcNode*> Parser.parseLambdaProc() {
2428 73 dyn lambdaProcNode = this.createNode<ASTLambdaProcNode>();
2429
2430 73 this.expect(TokenType::P);
2431
2432 // Parse paramLst
2433 73 ASTParamLstNode* paramLst = this.parseParenthesizedParamLst();
2434 73 lambdaProcNode.hasParams = paramLst != nil<ASTParamLstNode*>;
2435 73 lambdaProcNode.paramLst = paramLst;
2436
2437 // Parse lambda attrs
2438
4/4
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 52 times.
✓ Branch 3 taken 21 times.
146 if this.currentTokenIs(TokenType::LBRACKET) {
2439 21 lambdaProcNode.lambdaAttr = this.parseLambdaAttr();
2440 21 }
2441
2442 // Parse body
2443 73 lambdaProcNode.body = cast<IVisitable*>(this.parseStmtLst());
2444
2445 // The lambda attrs are listed after the body in the children of the node
2446
2/2
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 21 times.
73 if lambdaProcNode.lambdaAttr != nil<ASTLambdaAttrNode*> {
2447 21 this.moveChildToEnd(cast<ASTNode*>(lambdaProcNode), cast<ASTNode*>(lambdaProcNode.lambdaAttr));
2448 21 }
2449
2450 73 return this.concludeNode(lambdaProcNode);
2451 }
2452
2453 // lambdaExpr: LPAREN paramLst? RPAREN ARROW assignExpr;
2454 4 f<ASTLambdaExprNode*> Parser.parseLambdaExpr() {
2455 4 dyn lambdaExprNode = this.createNode<ASTLambdaExprNode>();
2456
2457 // Parse paramLst
2458 4 ASTParamLstNode* paramLst = this.parseParenthesizedParamLst();
2459 4 lambdaExprNode.hasParams = paramLst != nil<ASTParamLstNode*>;
2460 4 lambdaExprNode.paramLst = paramLst;
2461
2462 4 this.expect(TokenType::ARROW);
2463 4 lambdaExprNode.lambdaExpr = this.parseAssignExpr();
2464
2465 4 return this.concludeNode(lambdaExprNode);
2466 }
2467
2468 // dataType: qualifierLst? baseDataType (MUL | BITWISE_AND | LBRACKET (INT_LIT | TYPE_IDENTIFIER)? RBRACKET)*;
2469 198426 f<ASTDataTypeNode*> Parser.parseDataType() {
2470 198426 dyn dataTypeNode = this.createNode<ASTDataTypeNode>();
2471 198426 dataTypeNode.isFieldType = this.isParsingFieldType;
2472
2473
4/4
✓ Branch 0 taken 168163 times.
✓ Branch 1 taken 30263 times.
✓ Branch 2 taken 117330 times.
✓ Branch 3 taken 81096 times.
366589 if this.isQualifierType(this.lexer.getToken().tokenType) {
2474 81096 dataTypeNode.qualifierLst = this.parseQualifierLst();
2475 81096 }
2476 198426 dataTypeNode.baseDataType = this.parseBaseDataType();
2477
2478 // Parse type modifiers. If a '>' of a split SHR ('>>') token is still pending, it closes the enclosing generic list
2479 // first, so the following modifiers belong to the enclosing data type (e.g. the '&' in 'Vector<Vector<int>>&')
2480
2/2
✓ Branch 0 taken 2679 times.
✓ Branch 1 taken 250972 times.
253651 while this.pendingGreaters == 0 {
2481
4/4
✓ Branch 0 taken 30865 times.
✓ Branch 1 taken 220107 times.
✓ Branch 2 taken 30865 times.
✓ Branch 3 taken 220107 times.
501944 if this.currentTokenIs(TokenType::MUL) { // Pointer
2482 30865 this.expect(TokenType::MUL);
2483 30865 dataTypeNode.tmQueue.push(TypeModifier(TypeModifierType::TYPE_PTR, false, 0, String()));
2484
4/4
✓ Branch 0 taken 23643 times.
✓ Branch 1 taken 196464 times.
✓ Branch 2 taken 23643 times.
✓ Branch 3 taken 196464 times.
471079 } else if this.currentTokenIs(TokenType::BITWISE_AND) { // Reference
2485 23643 this.expect(TokenType::BITWISE_AND);
2486 23643 dataTypeNode.tmQueue.push(TypeModifier(TypeModifierType::TYPE_REF, false, 0, String()));
2487
4/4
✓ Branch 0 taken 717 times.
✓ Branch 1 taken 195747 times.
✓ Branch 2 taken 717 times.
✓ Branch 3 taken 195747 times.
416571 } else if this.currentTokenIs(TokenType::LBRACKET) { // Array
2488 717 this.expect(TokenType::LBRACKET);
2489 717 bool hasSize = false;
2490 717 unsigned int hardcodedSize = 0;
2491 717 String sizeVarName;
2492
4/4
✓ Branch 0 taken 214 times.
✓ Branch 1 taken 503 times.
✓ Branch 2 taken 214 times.
✓ Branch 3 taken 503 times.
1434 if this.currentTokenIs(TokenType::INT_LIT) {
2493 214 hasSize = true;
2494 214 const String& sizeStr = this.lexer.getToken().text;
2495 214 hardcodedSize = cast<unsigned int>(toInt(sizeStr.getRaw()));
2496 214 this.expect(TokenType::INT_LIT);
2497
4/4
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 405 times.
✓ Branch 2 taken 98 times.
✓ Branch 3 taken 405 times.
1220 } else if this.currentTokenIs(TokenType::TYPE_IDENTIFIER) {
2498 405 hasSize = true;
2499 405 sizeVarName = this.getIdentifier(true);
2500 405 this.expect(TokenType::TYPE_IDENTIFIER);
2501 503 }
2502 717 this.expect(TokenType::RBRACKET);
2503 717 dataTypeNode.tmQueue.push(TypeModifier(TypeModifierType::TYPE_ARRAY, hasSize, hardcodedSize, sizeVarName));
2504 717 } else {
2505 195747 break;
2506 24360 }
2507 55225 }
2508
2509 198426 return this.concludeNode(dataTypeNode);
2510 }
2511
2512 // baseDataType: TYPE_DOUBLE | TYPE_INT | TYPE_SHORT | TYPE_LONG | TYPE_BYTE | TYPE_CHAR | TYPE_STRING | TYPE_BOOL | TYPE_DYN | customDataType | functionDataType;
2513 198426 f<ASTBaseDataTypeNode*> Parser.parseBaseDataType() {
2514 198426 dyn baseDataTypeNode = this.createNode<ASTBaseDataTypeNode>();
2515
2516 198426 const TokenType tokenType = this.lexer.getToken().tokenType;
2517
2/2
✓ Branch 0 taken 1423 times.
✓ Branch 1 taken 197003 times.
198426 if tokenType == TokenType::TYPE_DOUBLE {
2518 1423 baseDataTypeNode.baseType = BaseDataTypeType::TYPE_DOUBLE;
2519 1423 this.advance();
2520
2/2
✓ Branch 0 taken 17378 times.
✓ Branch 1 taken 179625 times.
198426 } else if tokenType == TokenType::TYPE_INT {
2521 17378 baseDataTypeNode.baseType = BaseDataTypeType::TYPE_INT;
2522 17378 this.advance();
2523
2/2
✓ Branch 0 taken 3210 times.
✓ Branch 1 taken 176415 times.
197003 } else if tokenType == TokenType::TYPE_SHORT {
2524 3210 baseDataTypeNode.baseType = BaseDataTypeType::TYPE_SHORT;
2525 3210 this.advance();
2526
2/2
✓ Branch 0 taken 34651 times.
✓ Branch 1 taken 141764 times.
179625 } else if tokenType == TokenType::TYPE_LONG {
2527 34651 baseDataTypeNode.baseType = BaseDataTypeType::TYPE_LONG;
2528 34651 this.advance();
2529
2/2
✓ Branch 0 taken 12969 times.
✓ Branch 1 taken 128795 times.
176415 } else if tokenType == TokenType::TYPE_BYTE {
2530 12969 baseDataTypeNode.baseType = BaseDataTypeType::TYPE_BYTE;
2531 12969 this.advance();
2532
2/2
✓ Branch 0 taken 21951 times.
✓ Branch 1 taken 106844 times.
141764 } else if tokenType == TokenType::TYPE_CHAR {
2533 21951 baseDataTypeNode.baseType = BaseDataTypeType::TYPE_CHAR;
2534 21951 this.advance();
2535
2/2
✓ Branch 0 taken 17968 times.
✓ Branch 1 taken 88876 times.
128795 } else if tokenType == TokenType::TYPE_STRING {
2536 17968 baseDataTypeNode.baseType = BaseDataTypeType::TYPE_STRING;
2537 17968 this.advance();
2538
2/2
✓ Branch 0 taken 13663 times.
✓ Branch 1 taken 75213 times.
106844 } else if tokenType == TokenType::TYPE_BOOL {
2539 13663 baseDataTypeNode.baseType = BaseDataTypeType::TYPE_BOOL;
2540 13663 this.advance();
2541
2/2
✓ Branch 0 taken 1633 times.
✓ Branch 1 taken 73580 times.
88876 } else if tokenType == TokenType::TYPE_DYN {
2542 1633 baseDataTypeNode.baseType = BaseDataTypeType::TYPE_DYN;
2543 1633 this.advance();
2544
4/4
✓ Branch 0 taken 414 times.
✓ Branch 1 taken 73166 times.
✓ Branch 2 taken 706 times.
✓ Branch 3 taken 72874 times.
75213 } else if tokenType == TokenType::F || tokenType == TokenType::P {
2545 706 baseDataTypeNode.baseType = BaseDataTypeType::TYPE_FUNCTION;
2546 706 baseDataTypeNode.functionDataType = cast<IVisitable*>(this.parseFunctionDataType());
2547 706 } else {
2548 72874 baseDataTypeNode.baseType = BaseDataTypeType::TYPE_CUSTOM;
2549 72874 baseDataTypeNode.customDataType = this.parseCustomDataType();
2550 197003 }
2551
2552 198426 return this.concludeNode(baseDataTypeNode);
2553 }
2554
2555 // customDataType: (IDENTIFIER SCOPE_ACCESS)* TYPE_IDENTIFIER (LESS typeLst GREATER)?;
2556 72873 f<ASTCustomDataTypeNode*> Parser.parseCustomDataType() {
2557 72873 dyn customDataTypeNode = this.createNode<ASTCustomDataTypeNode>();
2558
2559 // Parse type name: (IDENTIFIER SCOPE_ACCESS)* TYPE_IDENTIFIER
2560 72873 String fqTypeName;
2561 72873 this.parseNamePrefix(TokenType::SCOPE_ACCESS, SCOPE_ACCESS_TOKEN, customDataTypeNode.typeNameFragments, fqTypeName);
2562 72873 const String typeName = this.lexer.getToken().text;
2563 72873 customDataTypeNode.typeNameFragments.pushBack(typeName);
2564 72873 fqTypeName += typeName;
2565 72873 this.expect(TokenType::TYPE_IDENTIFIER);
2566 72873 customDataTypeNode.fqTypeName = fqTypeName;
2567
2568 // Parse typeLst (template type list)
2569
4/4
✓ Branch 0 taken 61948 times.
✓ Branch 1 taken 10925 times.
✓ Branch 2 taken 61948 times.
✓ Branch 3 taken 10925 times.
145746 if this.consumeIf(TokenType::LESS) {
2570 10925 customDataTypeNode.templateTypeLst = cast<IVisitable*>(this.parseTypeLst());
2571 10925 this.consumeGenericClose();
2572 10925 }
2573
2574 145746 return this.concludeNode(customDataTypeNode);
2575 72873 }
2576
2577 // functionDataType: (F LESS dataType GREATER | P) LPAREN typeLst? RPAREN;
2578 706 f<ASTFunctionDataTypeNode*> Parser.parseFunctionDataType() {
2579 706 dyn functionDataTypeNode = this.createNode<ASTFunctionDataTypeNode>();
2580
2581 // Field types are only propagated to template types, not to the param and return types of function types
2582 706 const bool wasParsingFieldType = this.isParsingFieldType;
2583 706 this.isParsingFieldType = false;
2584
2585
4/4
✓ Branch 0 taken 292 times.
✓ Branch 1 taken 414 times.
✓ Branch 2 taken 414 times.
✓ Branch 3 taken 292 times.
1412 if this.consumeIf(TokenType::F) {
2586 414 this.expect(TokenType::LESS);
2587 414 functionDataTypeNode.isFunction = true;
2588 414 functionDataTypeNode.returnType = this.parseDataType();
2589 414 this.consumeGenericClose();
2590 414 } else {
2591 292 this.expect(TokenType::P);
2592 292 }
2593
2594 // Parse typeLst (param types)
2595 706 this.expect(TokenType::LPAREN);
2596
4/4
✓ Branch 0 taken 170 times.
✓ Branch 1 taken 536 times.
✓ Branch 2 taken 170 times.
✓ Branch 3 taken 536 times.
1412 if !this.currentTokenIs(TokenType::RPAREN) {
2597 536 functionDataTypeNode.paramTypeLst = this.parseTypeLst();
2598 536 }
2599 706 this.expect(TokenType::RPAREN);
2600
2601 706 this.isParsingFieldType = wasParsingFieldType;
2602
2603 706 return this.concludeNode(functionDataTypeNode);
2604 }
2605
2606 // ======================================================= Literal helpers =======================================================
2607
2608 /**
2609 * Parse an integer literal token (INT_LIT, SHORT_LIT or LONG_LIT) and check it against the limits of the target type
2610 *
2611 * @param token Literal token. Its text contains the optional unsigned suffix 'u', but not the 's' / 'l' suffixes
2612 * @param isNegative Whether the literal is preceded by a minus sign
2613 * @param bitWidth Bit width of the target type (16, 32 or 64)
2614 * @return Parsed number
2615 */
2616 49471 f<long> Parser.parseNumeric(const Token& token, bool isNegative, unsigned int bitWidth) {
2617 49471 String input = token.text;
2618
2619 // Set to signed if the input string does not end with 'u'
2620 49471 const bool isUnsigned = input.endsWith("u");
2621
2/2
✓ Branch 0 taken 45759 times.
✓ Branch 1 taken 3712 times.
49471 if isUnsigned {
2622 3712 input = input.getSubstring(0ul, cast<long>(input.getLength()) - 1l);
2623 3712 }
2624
2625 // Detect the base
2626 49471 int base = 10;
2627 49471 String digits = input;
2628
4/4
✓ Branch 0 taken 45261 times.
✓ Branch 1 taken 4210 times.
✓ Branch 2 taken 48163 times.
✓ Branch 3 taken 1308 times.
49471 if input.getLength() >= 3l && input[0l] == '0' {
2629 1308 const char baseChar = toLower(input[1l]);
2630
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1308 times.
1308 if baseChar == 'd' {
2631 ✗ digits = input.getSubstring(2ul);
2632
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1308 times.
1308 } else if baseChar == 'b' {
2633 ✗ base = 2;
2634 ✗ digits = input.getSubstring(2ul);
2635
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1308 times.
✓ Branch 2 taken 574 times.
✓ Branch 3 taken 734 times.
1308 } else if baseChar == 'h' || baseChar == 'x' {
2636 574 base = 16;
2637 574 digits = input.getSubstring(2ul);
2638
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 734 times.
1308 } else if baseChar == 'o' {
2639 734 base = 8;
2640 734 digits = input.getSubstring(2ul);
2641 1308 }
2642 1308 }
2643
2644 // Parse the magnitude as unsigned, so that values like 2^63 (the absolute value of the min long) fit
2645 49471 const unsigned long magnitude = strtoull(digits.getRaw(), nil<char**>, base);
2646
2647 // Check the limits of the target type
2648
2/2
✓ Branch 0 taken 29377 times.
✓ Branch 1 taken 20094 times.
49471 if bitWidth == 64 {
2649 29377 const unsigned long maxNegMagnitude = 9223372036854775808ul; // 2^63
2650
2/2
✓ Branch 0 taken 3909 times.
✓ Branch 1 taken 25468 times.
29377 if isNegative {
2651
1/2
✓ Branch 0 taken 3909 times.
✗ Branch 1 not taken.
3909 if magnitude > maxNegMagnitude {
2652 ✗ this.throwParserError(token.codeLoc, ParserErrorType::NUMBER_OUT_OF_RANGE, "The provided number is out of range");
2653 ✗ }
2654
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3907 times.
3909 if magnitude == maxNegMagnitude {
2655 2 return -9223372036854775807l - 1l; // Min long
2656 }
2657 3907 const long positiveNumber = cast<long>(magnitude);
2658 7814 return -positiveNumber;
2659 }
2660
3/4
✓ Branch 0 taken 1710 times.
✓ Branch 1 taken 23758 times.
✓ Branch 2 taken 25468 times.
✗ Branch 3 not taken.
25468 if !isUnsigned && magnitude > maxNegMagnitude - 1ul {
2661 ✗ this.throwParserError(token.codeLoc, ParserErrorType::NUMBER_OUT_OF_RANGE, "The provided number is out of range");
2662 ✗ }
2663 50936 return cast<long>(magnitude);
2664 }
2665 20094 const long signedUpperLimit = (1l << cast<long>(bitWidth - 1)) - 1l;
2666
2/2
✓ Branch 0 taken 2002 times.
✓ Branch 1 taken 18092 times.
20094 const long upperLimit = isUnsigned ? (1l << cast<long>(bitWidth)) - 1l : signedUpperLimit;
2667
2/2
✓ Branch 0 taken 2002 times.
✓ Branch 1 taken 18092 times.
20094 const long lowerLimit = isUnsigned ? 0l : -signedUpperLimit - 1l;
2668
2/2
✓ Branch 0 taken 20093 times.
✓ Branch 1 taken 1 time.
20094 if magnitude > cast<unsigned long>(upperLimit) + 1ul {
2669 1 this.throwParserError(token.codeLoc, ParserErrorType::NUMBER_OUT_OF_RANGE, "The provided number is out of range");
2670 1 }
2671 20094 long number = cast<long>(magnitude);
2672
2/2
✓ Branch 0 taken 19721 times.
✓ Branch 1 taken 373 times.
20094 if isNegative {
2673 373 number = -number;
2674 373 }
2675
3/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 20093 times.
✓ Branch 2 taken 20094 times.
✗ Branch 3 not taken.
20094 if number < lowerLimit || number > upperLimit {
2676 ✗ this.throwParserError(token.codeLoc, ParserErrorType::NUMBER_OUT_OF_RANGE, "The provided number is out of range");
2677 ✗ }
2678 40188 return number;
2679 49471 }
2680
2681 /**
2682 * Parse a char literal token. The lexer keeps escape sequences as is, so they are resolved here
2683 *
2684 * @param token Char literal token, without the surrounding quotes
2685 * @return Char value
2686 */
2687 9890 f<char> Parser.parseChar(const Token& token) {
2688 9890 String input = token.text;
2689
2/2
✓ Branch 0 taken 6251 times.
✓ Branch 1 taken 3639 times.
9890 if input.getLength() == 1l { // Normal char literals
2690 12502 return input[0l];
2691 }
2692
2693
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3638 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 3638 times.
3639 if input.getLength() == 2l && input[0l] == '\\' { // Char literals with escape sequence
2694 3638 const char escapedChar = input[1l];
2695
10/11
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 46 times.
✓ Branch 3 taken 405 times.
✓ Branch 4 taken 338 times.
✓ Branch 5 taken 315 times.
✓ Branch 6 taken 9 times.
✓ Branch 7 taken 11 times.
✓ Branch 8 taken 1 time.
✓ Branch 9 taken 18 times.
✓ Branch 10 taken 2468 times.
3638 switch escapedChar {
2696 27 case '\'': { return '\''; }
2697 ✗ case '"': { return '"'; }
2698 46 case '\\': { return '\\'; }
2699 405 case 'n': { return '\n'; }
2700 338 case 'r': { return '\r'; }
2701 315 case 't': { return '\t'; }
2702 9 case 'b': { return '\b'; }
2703 18 case 'f': { return '\f'; }
2704 11 case 'v': { return '\v'; }
2705 2468 case '0': { return '\0'; }
2706 }
2707 1 const String message = "Invalid escape sequence '" + input + "'";
2708 1 this.throwParserError(token.codeLoc, ParserErrorType::INVALID_CHAR_LITERAL, message.getRaw());
2709 1 }
2710
2711 2 const String message = "Invalid char literal '" + input + "'";
2712 2 this.throwParserError(token.codeLoc, ParserErrorType::INVALID_CHAR_LITERAL, message.getRaw());
2713 2 return '\0';
2714 9890 }
2715
2716 /**
2717 * Resolve the escape sequences of a string literal. The lexer already strips the surrounding quotes.
2718 *
2719 * @param input String literal content
2720 * @return String value
2721 */
2722 14663 f<String> parseString(const String& input) {
2723 14663 String stringValue = input;
2724 14663 replaceEscapeChars(stringValue);
2725 14663 return stringValue;
2726 }
2727
2728 /**
2729 * Replace escape sequences like \n or octal escape sequences like \033 in the given string with their char values
2730 *
2731 * @param input String to resolve the escape sequences in
2732 */
2733 14663 p replaceEscapeChars(String& input) {
2734 14663 String output;
2735 14663 unsigned long readIndex = 0l;
2736 14663 const unsigned long length = input.getLength();
2737
2738
2/2
✓ Branch 0 taken 233408 times.
✓ Branch 1 taken 14663 times.
248071 while readIndex < length {
2739 233408 const char c = input[readIndex];
2740
4/4
✓ Branch 0 taken 229175 times.
✓ Branch 1 taken 4233 times.
✓ Branch 2 taken 229175 times.
✓ Branch 3 taken 4233 times.
233408 if c == '\\' && readIndex + 1l < length {
2741 4233 const char next = input[readIndex + 1l];
2742 4233 const char escapedChar = getEscapedChar(next);
2743
2/2
✓ Branch 0 taken 4193 times.
✓ Branch 1 taken 40 times.
4233 if escapedChar != '\0' {
2744 4193 output += escapedChar;
2745 4193 readIndex += 2l;
2746 4193 continue;
2747 }
2748
2749 // Handle octal escape sequences (up to 3 digits)
2750
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 40 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 37 times.
40 if next >= '0' && next <= '7' {
2751 37 int value = 0;
2752 37 unsigned long octalDigits = 0l;
2753
2754 // Look ahead up to 3 digits
2755
4/4
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 111 times.
✓ Branch 2 taken 37 times.
✓ Branch 3 taken 111 times.
148 for unsigned long i = 1l; i <= 3l && readIndex + i < length; i++ {
2756 111 const char oc = input[readIndex + i];
2757
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 111 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 111 times.
111 if oc < '0' || oc > '7' {
2758 ✗ break;
2759 }
2760 111 value = value << 3 | (cast<int>(oc) - cast<int>('0')); // Multiply by 8 and add digit
2761 111 octalDigits++;
2762 111 }
2763
2764
1/2
✓ Branch 0 taken 37 times.
✗ Branch 1 not taken.
37 if octalDigits > 0l {
2765 37 output += cast<char>(value);
2766 37 readIndex += 1l + octalDigits; // Backslash + octal digits
2767 37 continue;
2768 }
2769 ✗ }
2770 3 }
2771
2772 // Copy current character
2773 229178 output += c;
2774 229178 readIndex++;
2775 229178 }
2776
2777 14663 input = output;
2778 14663 }
2779
2780 /**
2781 * Get the char value of a simple escape sequence like \n
2782 *
2783 * @param c Char after the backslash
2784 * @return Char value or '\0' if this is no simple escape sequence
2785 */
2786 4233 f<char> getEscapedChar(char c) {
2787
7/12
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 3599 times.
✓ Branch 5 taken 45 times.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 148 times.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 369 times.
4233 switch c {
2788 18 case 'a': { return cast<char>(7); } // Bell
2789 ✗ case 'b': { return '\b'; }
2790 ✗ case 'f': { return '\f'; }
2791 3599 case 'n': { return '\n'; }
2792 45 case 'r': { return '\r'; }
2793 14 case 't': { return '\t'; }
2794 ✗ case 'v': { return '\v'; }
2795 148 case '\\': { return '\\'; }
2796 ✗ case '?': { return '?'; }
2797 ✗ case '\'': { return '\''; }
2798 369 case '"': { return '"'; }
2799 }
2800 40 return '\0';
2801 }
2802
2803 /**
2804 * Abort parsing with a parser error
2805 *
2806 * @param codeLoc Code location of the error
2807 * @param errorType Type of the error
2808 * @param message Error message suffix
2809 */
2810 12 p Parser.throwParserError(const CodeLoc& codeLoc, ParserErrorType errorType, string message) {
2811 12 const ParserError parserError = ParserError(codeLoc, errorType, message);
2812 12 abortWithError(parserError.getMessage());
2813 12 }
2814