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

ricardoboss / STEP / 24085328738

07 Apr 2026 01:58PM UTC coverage: 72.919% (+0.07%) from 72.854%
24085328738

Pull #215

github

web-flow
Merge a16d6caa8 into bfcba1edf
Pull Request #215: Improve parser performance

503 of 637 branches covered (78.96%)

Branch coverage included in aggregate %.

51 of 82 new or added lines in 54 files covered. (62.2%)

2 existing lines in 2 files now uncovered.

3299 of 4577 relevant lines covered (72.08%)

1878.87 hits per line

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

73.81
/StepLang/Parsing/Parser.cs
1
using StepLang.Diagnostics;
2
using StepLang.Parsing.Nodes;
3
using StepLang.Parsing.Nodes.Expressions;
4
using StepLang.Parsing.Nodes.Import;
5
using StepLang.Parsing.Nodes.Statements;
6
using StepLang.Parsing.Nodes.VariableDeclarations;
7
using StepLang.Tokenizing;
8
using StepLang.Utils;
9
using System.Diagnostics;
10

11
namespace StepLang.Parsing;
12

13
public class Parser(IEnumerable<Token> tokenList, DiagnosticCollection diagnostics)
92✔
14
{
15
        private readonly TokenQueue tokens = new(tokenList) { IgnoreMeaningless = true };
92✔
16

17
        public RootNode ParseRoot()
18
        {
19
                var imports = ParseImports();
92✔
20
                var statements = ParseStatements(TokenType.EndOfFile);
92✔
21

22
                return new(imports, statements);
92✔
23
        }
24

25
        private List<IImportNode> ParseImports()
26
        {
27
                var imports = new List<IImportNode>();
92✔
28
                while (tokens.PeekType() is TokenType.ImportKeyword)
97✔
29
                {
30
                        imports.Add(ParseImport());
5✔
31
                }
32

33
                return imports;
92✔
34
        }
35

36
        private IImportNode ParseImport()
37
        {
38
                _ = tokens.Dequeue(TokenType.ImportKeyword);
5✔
39
                var pathResult = tokens.Dequeue(TokenType.LiteralString);
5✔
40
                switch (pathResult)
41
                {
42
                        case Ok<Token> { Value: var path }:
43
                                if (tokens.PeekType() is TokenType.NewLine)
4✔
44
                                {
45
                                        _ = tokens.Dequeue(TokenType.NewLine);
4✔
46
                                }
47

48
                                return new ImportNode(path);
4✔
49
                        case Err<Token> err:
50
                                return diagnostics.AddImportError(err);
1✔
51
                        default:
52
                                throw new InvalidOperationException("Unexpected result type");
×
53
                }
54
        }
55

56
        private List<StatementNode> ParseStatements(TokenType stopTokenType)
57
        {
58
                var statements = new List<StatementNode>();
516✔
59
                TokenType? nextType;
60
                while ((nextType = tokens.PeekType()) != null && nextType != stopTokenType)
4,345✔
61
                {
62
                        if (nextType is TokenType.NewLine)
3,829✔
63
                        {
64
                                _ = tokens.Dequeue(TokenType.NewLine);
2,362✔
65

66
                                continue;
2,362✔
67
                        }
68

69
                        statements.Add(ParseStatement());
1,467✔
70
                }
71

72
                return statements;
516✔
73
        }
74

75
        private StatementNode ParseStatement()
76
        {
77
                var token = tokens.Peek();
1,467✔
78
                switch (token?.Type)
1,467✔
79
                {
80
                        case TokenType.TypeName:
81
                                {
82
                                        var declaration = ParseVariableDeclaration();
380✔
83

84
                                        return new VariableDeclarationStatementNode(declaration);
380✔
85
                                }
86
                        case TokenType.Identifier:
87
                                return ParseIdentifierUsage();
570✔
88
                        case TokenType.UnderscoreSymbol:
89
                                return ParseDiscardStatement();
3✔
90
                        case TokenType.IfKeyword:
91
                                return ParseIfStatement();
167✔
92
                        case TokenType.WhileKeyword:
93
                                return ParseWhileStatement();
44✔
94
                        case TokenType.ForEachKeyword:
95
                                return ParseForeachStatement();
79✔
96
                        case TokenType.ReturnKeyword:
97
                                return ParseReturnStatement();
176✔
98
                        case TokenType.BreakKeyword:
99
                                return ParseBreakStatement();
11✔
100
                        case TokenType.ContinueKeyword:
101
                                return ParseContinueStatement();
30✔
102
                        case TokenType.OpeningCurlyBracket:
103
                                return ParseCodeBlock();
2✔
104
                        case null:
105
                                {
106
                                        _ = tokens.Dequeue();
×
107

108
                                        return diagnostics.AddUnexpectedEndOfTokens(tokens.LastToken);
×
109
                                }
110
                        default:
111
                                {
112
                                        _ = tokens.Dequeue();
5✔
113

114
                                        return diagnostics.AddUnexpectedToken(tokens.LastToken!, TokenType.TypeName, TokenType.Identifier,
5✔
115
                                                TokenType.NewLine, TokenType.LineComment);
5✔
116
                                }
117
                }
118
        }
119

120
        public StatementNode ParseIdentifierUsage()
121
        {
122
                var next = tokens.Peek(1);
570✔
123
                if (next is null)
570✔
124
                {
125
                        _ = tokens.Dequeue(TokenType.Identifier);
×
126

127
                        return diagnostics.AddUnexpectedEndOfTokens(tokens.LastToken);
×
128
                }
129

130
                switch (next.Type)
570✔
131
                {
132
                        case TokenType.EqualsSymbol:
133
                                return ParseVariableAssignment();
54✔
134
                        case TokenType.OpeningParentheses:
135
                                return ParseFunctionCall();
419✔
136
                        case TokenType.OpeningSquareBracket:
137
                                return ParseIndexAssignment();
10✔
138
                }
139

140
                var nextNext = tokens.Peek(2);
87✔
141
                if (nextNext is null)
87✔
142
                {
143
                        _ = tokens.Dequeue(TokenType.Identifier);
2✔
144
                        _ = tokens.Dequeue();
2✔
145

146
                        return diagnostics.AddUnexpectedEndOfTokens(tokens.LastToken);
2✔
147
                }
148

149
                if (next.Type.IsShorthandMathematicalOperation() && nextNext.Type == next.Type)
85✔
150
                {
151
                        return ParseShorthandMathOperation();
68✔
152
                }
153

154
                if (next.Type.IsShorthandMathematicalOperationWithAssignment())
17✔
155
                {
156
                        return ParseShorthandMathOperationExpression();
17✔
157
                }
158

159
                _ = tokens.Dequeue(2);
×
160

161
                return diagnostics.AddUnexpectedToken(tokens.LastToken!, next.Type, TokenType.EqualsSymbol);
×
162
        }
163

164
        private StatementNode ParseDiscardStatement()
165
        {
166
                var result = tokens.Dequeue(TokenType.UnderscoreSymbol);
3✔
167
                if (result is Err<Token> error)
3✔
168
                        return diagnostics.AddError(error);
×
169

170
                var underscore = result.Value;
3✔
171

172
                _ = tokens.Dequeue(TokenType.EqualsSymbol);
3✔
173

174
                var expression = ParseExpression();
3✔
175

176
                return new DiscardStatementNode(underscore.Location, expression);
3✔
177
        }
178

179
        private StatementNode ParseIndexAssignment()
180
        {
181
                var identifierResult = tokens.Dequeue(TokenType.Identifier);
10✔
182
                if (identifierResult is Err<Token> identifierError)
10✔
183
                        return diagnostics.AddError(identifierError);
×
184

185
                var identifier = identifierResult.Value;
10✔
186

187
                _ = tokens.Dequeue(TokenType.OpeningSquareBracket);
10✔
188

189
                var initialIndex = ParseExpression();
10✔
190
                var indexExpressions = new List<IExpressionNode>([initialIndex]);
10✔
191

192
                _ = tokens.Dequeue(TokenType.ClosingSquareBracket);
10✔
193

194
                var postIndexTokenResult = tokens.Dequeue(TokenType.EqualsSymbol, TokenType.OpeningSquareBracket);
10✔
195
                if (postIndexTokenResult is Err<Token> postIndexError)
10✔
196
                        return diagnostics.AddError(postIndexError);
×
197

198
                var postIndexToken = postIndexTokenResult.Value;
10✔
199

200
                while (postIndexToken.Type == TokenType.OpeningSquareBracket)
14✔
201
                {
202
                        var indexExpression = ParseExpression();
4✔
203
                        indexExpressions.Add(indexExpression);
4✔
204

205
                        _ = tokens.Dequeue(TokenType.ClosingSquareBracket);
4✔
206

207
                        postIndexTokenResult = tokens.Dequeue(TokenType.EqualsSymbol, TokenType.OpeningSquareBracket);
4✔
208
                        if (postIndexTokenResult is Err<Token> nestedError)
4✔
209
                                return diagnostics.AddError(nestedError);
×
210

211
                        postIndexToken = postIndexTokenResult.Value;
4✔
212
                }
213

214
                Debug.Assert(postIndexToken.Type == TokenType.EqualsSymbol);
215

216
                var expression = ParseExpression();
10✔
217

218
                return new IdentifierIndexAssignmentNode(identifier, indexExpressions, postIndexToken, expression);
10✔
219
        }
220

221
        private StatementNode ParseShorthandMathOperation()
222
        {
223
                var identifierResult = tokens.Dequeue(TokenType.Identifier);
68✔
224
                if (identifierResult is Err<Token> identifierError)
68✔
225
                        return diagnostics.AddError(identifierError);
×
226

227
                var identifier = identifierResult.Value;
68✔
228

229
                var operationResult = tokens.Dequeue(TokenTypes.ShorthandMathematicalOperations);
68✔
230
                if (operationResult is Err<Token> operationError)
68✔
231
                        return diagnostics.AddError(operationError);
×
232

233
                var operation = operationResult.Value;
68✔
234

235
                _ = tokens.Dequeue(operation.Type);
68✔
236

237
                switch (operation.Type)
68✔
238
                {
239
                        case TokenType.PlusSymbol:
240
                                return new IncrementStatementNode(identifier);
61✔
241
                        case TokenType.MinusSymbol:
242
                                return new DecrementStatementNode(identifier);
7✔
243
                        default:
244
                                return diagnostics.AddUnexpectedToken(operation, TokenType.PlusSymbol, TokenType.MinusSymbol);
×
245
                }
246
        }
247

248
        private StatementNode ParseShorthandMathOperationExpression()
249
        {
250
                var identifierResult = tokens.Dequeue(TokenType.Identifier);
17✔
251
                if (identifierResult is Err<Token> identifierError)
17✔
252
                        return diagnostics.AddError(identifierError);
×
253

254
                var identifier = identifierResult.Value;
17✔
255

256
                var identifierExpression = new IdentifierExpressionNode(identifier);
17✔
257

258
                List<Token> operatorTokens;
259
                try
260
                {
261
                        operatorTokens = PeekContinuousOperators(TokenTypes.ShorthandMathematicalOperationsWithAssignment);
17✔
262
                }
17✔
263
                catch (UnexpectedTokenException e)
×
264
                {
265
                        return diagnostics.AddUnexpectedToken(e.Message, e.Token);
×
266
                }
267

268
                Debug.Assert(operatorTokens.Count > 0);
269

270
                _ = tokens.Dequeue(operatorTokens.Count);
17✔
271
                var assignmentResult = tokens.Dequeue(TokenType.EqualsSymbol);
17✔
272
                if (assignmentResult is Err<Token> assignmentError)
17✔
273
                        return diagnostics.AddError(assignmentError);
×
274

275
                var assignmentToken = assignmentResult.Value;
17✔
276

277
                var expression = ParseExpression();
17✔
278
                var firstOperator = operatorTokens[0];
17✔
279

280
                return firstOperator.Type switch
17✔
281
                {
17✔
282
                        TokenType.PlusSymbol => new VariableAssignmentNode(
12✔
283
                                assignmentToken.Location,
12✔
284
                                identifier,
12✔
285
                                new AddExpressionNode(firstOperator, identifierExpression, expression)
12✔
286
                        ),
12✔
287
                        TokenType.MinusSymbol => new VariableAssignmentNode(
1✔
288
                                assignmentToken.Location,
1✔
289
                                identifier,
1✔
290
                                new SubtractExpressionNode(
1✔
291
                                        firstOperator,
1✔
292
                                        identifierExpression,
1✔
293
                                        expression
1✔
294
                                )
1✔
295
                        ),
1✔
296
                        TokenType.AsteriskSymbol => operatorTokens.Count switch
2✔
297
                        {
2✔
298
                                1 => new VariableAssignmentNode(
1✔
299
                                        assignmentToken.Location,
1✔
300
                                        identifier,
1✔
301
                                        new MultiplyExpressionNode(
1✔
302
                                                firstOperator,
1✔
303
                                                identifierExpression,
1✔
304
                                                expression
1✔
305
                                        )
1✔
306
                                ),
1✔
307
                                2 => operatorTokens[1].Type switch
1✔
308
                                {
1✔
309
                                        TokenType.AsteriskSymbol => new VariableAssignmentNode(
1✔
310
                                                assignmentToken.Location,
1✔
311
                                                identifier,
1✔
312
                                                new PowerExpressionNode(
1✔
313
                                                        firstOperator,
1✔
314
                                                        identifierExpression,
1✔
315
                                                        expression
1✔
316
                                                )
1✔
317
                                        ),
1✔
318
                                        _ => diagnostics.AddUnexpectedToken(operatorTokens[1], TokenType.AsteriskSymbol),
×
319
                                },
1✔
320
                                _ => diagnostics.AddUnexpectedEndOfTokens(firstOperator, "Expected an operator"),
×
321
                        },
2✔
322
                        TokenType.SlashSymbol => new VariableAssignmentNode(
1✔
323
                                assignmentToken.Location,
1✔
324
                                identifier,
1✔
325
                                new DivideExpressionNode(
1✔
326
                                        firstOperator,
1✔
327
                                        identifierExpression,
1✔
328
                                        expression
1✔
329
                                )
1✔
330
                        ),
1✔
331
                        TokenType.PercentSymbol => new VariableAssignmentNode(
1✔
332
                                assignmentToken.Location,
1✔
333
                                identifier,
1✔
334
                                new ModuloExpressionNode(
1✔
335
                                        firstOperator,
1✔
336
                                        identifierExpression,
1✔
337
                                        expression
1✔
338
                                )
1✔
339
                        ),
1✔
340
                        TokenType.PipeSymbol => new VariableAssignmentNode(
×
341
                                assignmentToken.Location,
×
342
                                identifier,
×
343
                                new BitwiseOrExpressionNode(
×
344
                                        firstOperator,
×
345
                                        identifierExpression,
×
346
                                        expression
×
347
                                )
×
348
                        ),
×
349
                        TokenType.AmpersandSymbol => new VariableAssignmentNode(
×
350
                                assignmentToken.Location,
×
351
                                identifier,
×
352
                                new BitwiseAndExpressionNode(
×
353
                                        firstOperator,
×
354
                                        identifierExpression,
×
355
                                        expression
×
356
                                )
×
357
                        ),
×
358
                        TokenType.HatSymbol => new VariableAssignmentNode(
×
359
                                assignmentToken.Location,
×
360
                                identifier,
×
361
                                new BitwiseXorExpressionNode(
×
362
                                        firstOperator,
×
363
                                        identifierExpression,
×
364
                                        expression
×
365
                                )
×
366
                        ),
×
367
                        TokenType.QuestionMarkSymbol => new VariableAssignmentNode(
×
368
                                assignmentToken.Location,
×
369
                                identifier,
×
370
                                new CoalesceExpressionNode(
×
371
                                        firstOperator,
×
372
                                        identifierExpression,
×
373
                                        expression
×
374
                                )
×
375
                        ),
×
376
                        _ => diagnostics.AddUnexpectedToken(firstOperator, TokenType.PlusSymbol, TokenType.MinusSymbol,
×
377
                                TokenType.AsteriskSymbol, TokenType.SlashSymbol, TokenType.PercentSymbol, TokenType.PipeSymbol,
×
378
                                TokenType.AmpersandSymbol, TokenType.HatSymbol, TokenType.QuestionMarkSymbol),
×
379
                };
17✔
380
        }
×
381

382
        private StatementNode ParseForeachStatement()
383
        {
384
                var foreachKeywordResult = tokens.Dequeue(TokenType.ForEachKeyword);
79✔
385
                if (foreachKeywordResult is Err<Token> foreachError)
79✔
386
                        return diagnostics.AddError(foreachError);
×
387

388
                var foreachKeyword = foreachKeywordResult.Value;
79✔
389

390
                _ = tokens.Dequeue(TokenType.OpeningParentheses);
79✔
391

392
                IVariableDeclarationNode? keyDeclaration = null;
79✔
393
                Token? keyIdentifier = null;
79✔
394
                IVariableDeclarationNode? valueDeclaration = null;
79✔
395
                Token? valueIdentifier = null;
79✔
396

397
                var next = tokens.Peek();
79✔
398
                switch (next?.Type)
79✔
399
                {
400
                        case TokenType.TypeName:
401
                                {
402
                                        var firstDeclaration = ParseVariableDeclaration();
78✔
403

404
                                        next = tokens.Peek();
78✔
405
                                        switch (next?.Type)
78✔
406
                                        {
407
                                                case TokenType.ColonSymbol:
408
                                                        {
409
                                                                keyDeclaration = firstDeclaration;
4✔
410

411
                                                                _ = tokens.Dequeue(TokenType.ColonSymbol);
4✔
412

413
                                                                next = tokens.Peek();
4✔
414
                                                                switch (next?.Type)
4✔
415
                                                                {
416
                                                                        case TokenType.TypeName:
417
                                                                                valueDeclaration = ParseVariableDeclaration();
4✔
418
                                                                                break;
4✔
419
                                                                        case TokenType.Identifier:
420
                                                                                var valueIdentifierResult = tokens.Dequeue(TokenType.Identifier);
×
421
                                                                                if (valueIdentifierResult is Err<Token> valueIdentifierError)
×
422
                                                                                        return diagnostics.AddError(valueIdentifierError);
×
423

424
                                                                                valueIdentifier = valueIdentifierResult.Value;
×
425

426
                                                                                break;
×
427
                                                                        case null:
428
                                                                                return diagnostics.AddUnexpectedEndOfTokens(tokens.LastToken);
×
429
                                                                        default:
430
                                                                                _ = tokens.Dequeue(next.Type);
×
431

432
                                                                                return diagnostics.AddUnexpectedToken(next, TokenType.TypeName,
×
433
                                                                                        TokenType.Identifier);
×
434
                                                                }
435

436
                                                                break;
437
                                                        }
438
                                                case TokenType.InKeyword:
439
                                                        valueDeclaration = firstDeclaration;
74✔
440
                                                        break;
74✔
441
                                                case null:
442
                                                        return diagnostics.AddUnexpectedEndOfTokens(tokens.LastToken);
×
443
                                                default:
444
                                                        _ = tokens.Dequeue(next.Type);
×
445

446
                                                        return diagnostics.AddUnexpectedToken(next, TokenType.ColonSymbol, TokenType.InKeyword);
×
447
                                        }
448

449
                                        break;
450
                                }
451
                        case TokenType.Identifier:
452
                                {
453
                                        var firstIdentifierResult = tokens.Dequeue(TokenType.Identifier);
1✔
454
                                        if (firstIdentifierResult is Err<Token> firstIdentifierError)
1✔
455
                                                return diagnostics.AddError(firstIdentifierError);
×
456

457
                                        var firstIdentifier = firstIdentifierResult.Value;
1✔
458

459
                                        next = tokens.Peek();
1✔
460
                                        switch (next?.Type)
1✔
461
                                        {
462
                                                case TokenType.ColonSymbol:
463
                                                        {
464
                                                                keyIdentifier = firstIdentifier;
1✔
465

466
                                                                _ = tokens.Dequeue(TokenType.ColonSymbol);
1✔
467

468
                                                                next = tokens.Peek();
1✔
469
                                                                switch (next?.Type)
1✔
470
                                                                {
471
                                                                        case TokenType.TypeName:
472
                                                                                valueDeclaration = ParseVariableDeclaration();
×
473
                                                                                break;
×
474
                                                                        case TokenType.Identifier:
475
                                                                                var valueIdentifierResult = tokens.Dequeue(TokenType.Identifier);
1✔
476
                                                                                if (valueIdentifierResult is Err<Token> valueIdentifierError)
1✔
477
                                                                                        return diagnostics.AddError(valueIdentifierError);
×
478

479
                                                                                valueIdentifier = valueIdentifierResult.Value;
1✔
480

481
                                                                                break;
1✔
482
                                                                        case null:
483
                                                                                return diagnostics.AddUnexpectedEndOfTokens(tokens.LastToken);
×
484
                                                                        default:
485
                                                                                _ = tokens.Dequeue(next.Type);
×
486

487
                                                                                return diagnostics.AddUnexpectedToken(next, TokenType.TypeName,
×
488
                                                                                        TokenType.Identifier);
×
489
                                                                }
490

491
                                                                break;
492
                                                        }
493
                                                case TokenType.InKeyword:
494
                                                        valueIdentifier = firstIdentifier;
×
495
                                                        break;
×
496
                                                case null:
497
                                                        return diagnostics.AddUnexpectedEndOfTokens(tokens.LastToken);
×
498
                                                default:
499
                                                        _ = tokens.Dequeue(next.Type);
×
500

501
                                                        return diagnostics.AddUnexpectedToken(next, TokenType.ColonSymbol, TokenType.InKeyword);
×
502
                                        }
503

504
                                        break;
505
                                }
506
                        case null:
507
                                return diagnostics.AddUnexpectedEndOfTokens(tokens.LastToken);
×
508
                        default:
509
                                return diagnostics.AddUnexpectedToken(next, TokenType.TypeName, TokenType.Identifier);
×
510
                }
511

512
                if (valueDeclaration is null && valueIdentifier is null)
79✔
513
                {
514
                        _ = tokens.Dequeue();
×
515

516
                        return diagnostics.AddUnexpectedToken("Foreach without value declaration or identifier", tokens.LastToken!);
×
517
                }
518

519
                _ = tokens.Dequeue(TokenType.InKeyword);
79✔
520

521
                var list = ParseExpression();
79✔
522

523
                _ = tokens.Dequeue(TokenType.ClosingParentheses);
79✔
524

525
                var body = ParseCodeBlock();
79✔
526

527
                if (keyDeclaration is not null)
79✔
528
                {
529
                        if (valueDeclaration is not null)
4✔
530
                        {
531
                                return new ForeachDeclareKeyDeclareValueStatementNode(foreachKeyword, keyDeclaration, valueDeclaration,
4✔
532
                                        list, body);
4✔
533
                        }
534

535
                        return new ForeachDeclareKeyValueStatementNode(foreachKeyword, keyDeclaration, valueIdentifier!, list,
×
536
                                body);
×
537
                }
538

539
                if (keyIdentifier is not null)
75✔
540
                {
541
                        if (valueDeclaration is not null)
1✔
542
                        {
543
                                return new ForeachKeyDeclareValueStatementNode(foreachKeyword, keyIdentifier, valueDeclaration, list,
×
544
                                        body);
×
545
                        }
546

547
                        return new ForeachKeyValueStatementNode(foreachKeyword, keyIdentifier, valueIdentifier!, list, body);
1✔
548
                }
549

550
                if (valueDeclaration is not null)
74✔
551
                {
552
                        return new ForeachDeclareValueStatementNode(foreachKeyword, valueDeclaration, list, body);
74✔
553
                }
554

555
                return new ForeachValueStatementNode(foreachKeyword, valueIdentifier!, list, body);
×
556
        }
557

558
        private StatementNode ParseCodeBlock()
559
        {
560
                var openCurlyBraceResult = tokens.Dequeue(TokenType.OpeningCurlyBracket);
424✔
561
                if (openCurlyBraceResult is Err<Token> openError)
424✔
562
                        return diagnostics.AddError(openError);
×
563

564
                var openCurlyBrace = openCurlyBraceResult.Value;
424✔
565

566
                var statements = ParseStatements(TokenType.ClosingCurlyBracket);
424✔
567

568
                var closingCurlyBraceResult = tokens.Dequeue(TokenType.ClosingCurlyBracket);
424✔
569
                if (closingCurlyBraceResult is Err<Token> closingError)
424✔
570
                        return diagnostics.AddError(closingError);
×
571

572
                var closingCurlyBrace = closingCurlyBraceResult.Value;
424✔
573

574
                return new CodeBlockStatementNode(openCurlyBrace, statements, closingCurlyBrace);
424✔
575
        }
576

577
        private StatementNode ParseContinueStatement()
578
        {
579
                var continueTokenResult = tokens.Dequeue(TokenType.ContinueKeyword);
30✔
580
                if (continueTokenResult is Err<Token> continueError)
30✔
581
                        return diagnostics.AddError(continueError);
×
582

583
                return new ContinueStatementNode(continueTokenResult.Value);
30✔
584
        }
585

586
        private StatementNode ParseBreakStatement()
587
        {
588
                var breakTokenResult = tokens.Dequeue(TokenType.BreakKeyword);
11✔
589
                if (breakTokenResult is Err<Token> breakError)
11✔
590
                        return diagnostics.AddError(breakError);
×
591

592
                return new BreakStatementNode(breakTokenResult.Value);
11✔
593
        }
594

595
        private StatementNode ParseReturnStatement()
596
        {
597
                var returnKeywordResult = tokens.Dequeue(TokenType.ReturnKeyword);
176✔
598
                if (returnKeywordResult is Err<Token> returnError)
176✔
599
                        return diagnostics.AddError(returnError);
×
600

601
                var returnKeyword = returnKeywordResult.Value;
176✔
602

603
                if (tokens.PeekType() is TokenType.NewLine)
176✔
604
                {
605
                        return new ReturnStatementNode(returnKeyword);
17✔
606
                }
607

608
                var expression = ParseExpression();
159✔
609

610
                return new ReturnExpressionStatementNode(returnKeyword, expression);
159✔
611
        }
612

613
        private StatementNode ParseWhileStatement()
614
        {
615
                var whileKeywordResult = tokens.Dequeue(TokenType.WhileKeyword);
44✔
616
                if (whileKeywordResult is Err<Token> whileError)
44✔
617
                        return diagnostics.AddError(whileError);
×
618

619
                var whileKeyword = whileKeywordResult.Value;
44✔
620

621
                _ = tokens.Dequeue(TokenType.OpeningParentheses);
44✔
622

623
                var condition = ParseExpression();
44✔
624

625
                _ = tokens.Dequeue(TokenType.ClosingParentheses);
44✔
626

627
                var codeBlock = ParseCodeBlock();
44✔
628

629
                return new WhileStatementNode(whileKeyword, condition, codeBlock);
44✔
630
        }
631

632
        private StatementNode ParseIfStatement()
633
        {
634
                var ifKeywordResult = tokens.Dequeue(TokenType.IfKeyword);
168✔
635
                if (ifKeywordResult is Err<Token> ifError)
168✔
636
                        return diagnostics.AddError(ifError);
×
637

638
                var ifKeyword = ifKeywordResult.Value;
168✔
639

640
                _ = tokens.Dequeue(TokenType.OpeningParentheses);
168✔
641
                var condition = ParseExpression();
168✔
642
                _ = tokens.Dequeue(TokenType.ClosingParentheses);
168✔
643

644
                var codeBlock = ParseCodeBlock();
168✔
645

646
                var conditionBodyMap = new LinkedList<(IExpressionNode, StatementNode)>();
168✔
647
                _ = conditionBodyMap.AddLast((condition, codeBlock));
168✔
648

649
                if (tokens.PeekType() is not TokenType.ElseKeyword)
168✔
650
                {
651
                        return new IfStatementNode(ifKeyword, conditionBodyMap);
151✔
652
                }
653

654
                _ = tokens.Dequeue(TokenType.ElseKeyword);
17✔
655

656
                if (tokens.PeekType() is TokenType.IfKeyword)
17✔
657
                {
658
                        var nested = ParseIfStatement();
1✔
659
                        if (nested is ErrorStatementNode error)
1✔
660
                                return error;
×
661

662
                        if (nested is not IfStatementNode nestedIf)
1✔
663
                                throw new InvalidOperationException("Unexpected statement type");
×
664

665
                        foreach (var (nestedCondition, nestedCodeBlock) in nestedIf.ConditionBodyMap)
6✔
666
                        {
667
                                _ = conditionBodyMap.AddLast((nestedCondition, nestedCodeBlock));
2✔
668
                        }
669

670
                        return new IfStatementNode(ifKeyword, conditionBodyMap);
1✔
671
                }
672

673
                var elseCodeBlock = ParseCodeBlock();
16✔
674

675
                _ = conditionBodyMap.AddLast((LiteralExpressionNode.FromBoolean(true), elseCodeBlock));
16✔
676

677
                return new IfStatementNode(ifKeyword, conditionBodyMap);
16✔
678
        }
679

680
        private CallStatementNode ParseFunctionCall()
681
        {
682
                var callExpression = ParseFunctionCallExpression();
419✔
683

684
                return new(callExpression);
419✔
685
        }
686

687
        private IVariableDeclarationNode ParseVariableDeclaration()
688
        {
689
                var typeResult = tokens.Dequeue(TokenType.TypeName);
625✔
690
                if (typeResult is Err<Token> typeError)
625✔
691
                        return diagnostics.AddVariableDeclarationError(typeError);
×
692

693
                var type = typeResult.Value;
625✔
694

695
                Token? nullabilityIndicator = null;
625✔
696
                if (tokens.Peek() is { Type: TokenType.QuestionMarkSymbol })
625✔
697
                {
698
                        var nullabilityIndicatorResult = tokens.Dequeue(TokenType.QuestionMarkSymbol);
43✔
699
                        if (nullabilityIndicatorResult is Err<Token> nullabilityIndicatorError)
43✔
700
                                return diagnostics.AddVariableDeclarationError(nullabilityIndicatorError);
×
701

702
                        nullabilityIndicator = nullabilityIndicatorResult.Value;
43✔
703
                }
704

705
                var identifierResult = tokens.Dequeue(TokenType.Identifier);
625✔
706
                if (identifierResult is Err<Token> identifierError)
625✔
707
                        return diagnostics.AddVariableDeclarationError(identifierError);
2✔
708

709
                var identifier = identifierResult.Value;
623✔
710

711
                if (tokens.PeekType() is not TokenType.EqualsSymbol)
623✔
712
                {
713
                        if (nullabilityIndicator is not null)
249✔
714
                        {
715
                                return new NullableVariableDeclarationNode([type], nullabilityIndicator, identifier);
5✔
716
                        }
717

718
                        return new VariableDeclarationNode([type], identifier);
244✔
719
                }
720

721
                var assignmentTokenResult = tokens.Dequeue(TokenType.EqualsSymbol);
374✔
722
                if (assignmentTokenResult is Err<Token> assignmentError)
374✔
723
                        return diagnostics.AddVariableDeclarationError(assignmentError);
×
724

725
                var assignmentToken = assignmentTokenResult.Value;
374✔
726

727
                var expression = ParseExpression();
374✔
728

729
                if (nullabilityIndicator is not null)
374✔
730
                {
731
                        return new NullableVariableInitializationNode(assignmentToken.Location, [type],
38✔
732
                                nullabilityIndicator, identifier, expression);
38✔
733
                }
734

735
                return new VariableInitializationNode(assignmentToken.Location, [type], identifier, expression);
336✔
736
        }
737

738
        private StatementNode ParseVariableAssignment()
739
        {
740
                var identifierResult = tokens.Dequeue(TokenType.Identifier);
54✔
741
                if (identifierResult is Err<Token> identifierError)
54✔
742
                        return diagnostics.AddError(identifierError);
×
743

744
                var identifier = identifierResult.Value;
54✔
745

746
                var assignmentTokenResult = tokens.Dequeue(TokenType.EqualsSymbol);
54✔
747
                if (assignmentTokenResult is Err<Token> assignmentError)
54✔
748
                        return diagnostics.AddError(assignmentError);
×
749

750
                var assignmentToken = assignmentTokenResult.Value;
54✔
751

752
                var expression = ParseExpression();
54✔
753

754
                return new VariableAssignmentNode(assignmentToken.Location, identifier, expression);
54✔
755
        }
756

757
        private IExpressionNode ParseExpression(int parentPrecedence = 0)
758
        {
759
                var left = ParsePrimaryExpression();
3,036✔
760

761
                while (tokens.PeekType() is TokenType.OpeningSquareBracket)
3,235✔
762
                {
763
                        var openBracket = tokens.Dequeue(TokenType.OpeningSquareBracket);
199✔
764

765
                        var index = ParseExpression();
199✔
766

767
                        _ = tokens.Dequeue(TokenType.ClosingSquareBracket);
199✔
768

769
                        left = new IndexAccessExpressionNode(openBracket.Value, left, index);
199✔
770
                }
771

772
                while (tokens.PeekType() is { } nextType && nextType.IsOperator())
3,408✔
773
                {
774
                        List<Token> operatorTokens;
775
                        try
776
                        {
777
                                operatorTokens = PeekContinuousOperators(TokenTypes.Operators);
441✔
778
                        }
441✔
779
                        catch (UnexpectedTokenException e)
×
780
                        {
781
                                return diagnostics.AddUnexpectedTokenExpression(e.Message, e.Token);
×
782
                        }
783

784
                        var binaryOperatorResult = ParseExpressionOperator(operatorTokens);
441✔
785
                        if (binaryOperatorResult is Err<BinaryExpressionOperator> binaryOperatorError)
441✔
786
                                return diagnostics.AddErrorExpression(binaryOperatorError);
×
787

788
                        var binaryOperator = binaryOperatorResult.Value;
441✔
789

790
                        var precedence = binaryOperator.Precedence();
441✔
791
                        if (precedence < parentPrecedence)
441✔
792
                        {
793
                                break;
794
                        }
795

796
                        // actually consume the operator
797
                        _ = tokens.Dequeue(operatorTokens.Count);
372✔
798

799
                        var right = ParseExpression(precedence + 1);
372✔
800

801
                        left = Combine(operatorTokens.First(), left, binaryOperator, right);
372✔
802
                }
372✔
803

804
                return left;
3,036✔
805
        }
×
806

807
        private static IExpressionNode Combine(Token @operator, IExpressionNode left,
808
                BinaryExpressionOperator binaryOperator, IExpressionNode right)
809
        {
810
                return binaryOperator switch
372✔
811
                {
372✔
812
                        BinaryExpressionOperator.Add => new AddExpressionNode(@operator, left, right),
55✔
813
                        BinaryExpressionOperator.Coalesce => new CoalesceExpressionNode(@operator, left, right),
3✔
814
                        BinaryExpressionOperator.NotEqual => new NotEqualsExpressionNode(@operator, left, right),
17✔
815
                        BinaryExpressionOperator.Equal => new EqualsExpressionNode(@operator, left, right),
110✔
816
                        BinaryExpressionOperator.Subtract => new SubtractExpressionNode(@operator, left, right),
28✔
817
                        BinaryExpressionOperator.Multiply => new MultiplyExpressionNode(@operator, left, right),
27✔
818
                        BinaryExpressionOperator.Divide => new DivideExpressionNode(@operator, left, right),
3✔
819
                        BinaryExpressionOperator.Modulo => new ModuloExpressionNode(@operator, left, right),
4✔
820
                        BinaryExpressionOperator.Power => new PowerExpressionNode(@operator, left, right),
3✔
821
                        BinaryExpressionOperator.GreaterThan => new GreaterThanExpressionNode(@operator, left, right),
14✔
822
                        BinaryExpressionOperator.LessThan => new LessThanExpressionNode(@operator, left, right),
47✔
823
                        BinaryExpressionOperator.GreaterThanOrEqual => new GreaterThanOrEqualExpressionNode(@operator, left,
14✔
824
                                right),
14✔
825
                        BinaryExpressionOperator.LessThanOrEqual =>
372✔
826
                                new LessThanOrEqualExpressionNode(@operator, left, right),
3✔
827
                        BinaryExpressionOperator.LogicalAnd => new LogicalAndExpressionNode(@operator, left, right),
19✔
828
                        BinaryExpressionOperator.LogicalOr => new LogicalOrExpressionNode(@operator, left, right),
25✔
829
                        BinaryExpressionOperator.BitwiseAnd => new BitwiseAndExpressionNode(@operator, left, right),
×
830
                        BinaryExpressionOperator.BitwiseOr => new BitwiseOrExpressionNode(@operator, left, right),
×
831
                        BinaryExpressionOperator.BitwiseXor => new BitwiseXorExpressionNode(@operator, left, right),
×
832
                        BinaryExpressionOperator.BitwiseShiftLeft => new BitwiseShiftLeftExpressionNode(@operator, left,
×
833
                                right),
×
834
                        BinaryExpressionOperator.BitwiseShiftRight => new BitwiseShiftRightExpressionNode(@operator, left,
×
835
                                right),
×
836
                        BinaryExpressionOperator.BitwiseRotateLeft => new BitwiseRotateLeftExpressionNode(@operator, left,
×
837
                                right),
×
838
                        BinaryExpressionOperator.BitwiseRotateRight => new BitwiseRotateRightExpressionNode(@operator, left,
×
839
                                right),
×
840
                        _ => throw new NotSupportedException("Expression for operator " + binaryOperator.ToSymbol() +
×
841
                                                                                                 " not supported"),
×
842
                };
372✔
843
        }
844

845
        private List<Token> PeekContinuousOperators(TokenType[] allowedOperators)
846
        {
847
                // whitespaces have meaning in operators
848
                var previousIgnoreMeaningless = tokens.IgnoreMeaningless;
458✔
849
                tokens.IgnoreMeaningless = false;
458✔
850

851
                try
852
                {
853
                        var offset = 0;
458✔
854
                        var operators = new List<Token>();
458✔
855
                        while (tokens.Peek(offset) is { Type: not TokenType.EndOfFile } next)
1,621✔
856
                        {
857
                                if (allowedOperators.Contains(next.Type))
1,621✔
858
                                {
859
                                        operators.Add(next);
709✔
860

861
                                        offset++;
709✔
862
                                }
863
                                else if (operators.Count == 0)
912✔
864
                                {
865
                                        if (next.Type is not TokenType.Whitespace)
454✔
866
                                        {
867
                                                throw new UnexpectedTokenException(next, allowedOperators);
×
868
                                        }
869

870
                                        offset++; // skip leading whitespace
454✔
871
                                }
872
                                else
873
                                {
874
                                        break;
875
                                }
876
                        }
877

878
                        return operators;
458✔
879
                }
880
                finally
881
                {
882
                        tokens.IgnoreMeaningless = previousIgnoreMeaningless;
458✔
883
                }
458✔
884
        }
458✔
885

886
        private Result<BinaryExpressionOperator> ParseExpressionOperator(List<Token> operatorTokens)
887
        {
888
                switch (operatorTokens.Count)
441✔
889
                {
890
                        case 3:
891
                                {
892
                                        var (first, second, third) = (operatorTokens[0], operatorTokens[1], operatorTokens[2]);
×
893

894
                                        return first.Type switch
×
895
                                        {
×
896
                                                TokenType.GreaterThanSymbol => second.Type switch
×
897
                                                {
×
898
                                                        TokenType.GreaterThanSymbol => third.Type switch
×
899
                                                        {
×
900
                                                                TokenType.GreaterThanSymbol => BinaryExpressionOperator.BitwiseRotateRight.ToResult(),
×
901
                                                                _ => new UnexpectedTokenException(third, TokenType.GreaterThanSymbol)
×
902
                                                                        .ToErr<BinaryExpressionOperator>(),
×
903
                                                        },
×
904
                                                        _ => new UnexpectedTokenException(second, TokenType.GreaterThanSymbol)
×
905
                                                                .ToErr<BinaryExpressionOperator>(),
×
906
                                                },
×
907
                                                TokenType.LessThanSymbol => second.Type switch
×
908
                                                {
×
909
                                                        TokenType.LessThanSymbol => third.Type switch
×
910
                                                        {
×
911
                                                                TokenType.LessThanSymbol => BinaryExpressionOperator.BitwiseRotateLeft.ToResult(),
×
912
                                                                _ => new UnexpectedTokenException(third, TokenType.LessThanSymbol)
×
913
                                                                        .ToErr<BinaryExpressionOperator>(),
×
914
                                                        },
×
915
                                                        _ => new UnexpectedTokenException(second, TokenType.LessThanSymbol)
×
916
                                                                .ToErr<BinaryExpressionOperator>(),
×
917
                                                },
×
918
                                                _ => new UnexpectedTokenException(first, TokenType.GreaterThanSymbol,
×
919
                                                        TokenType.LessThanSymbol).ToErr<BinaryExpressionOperator>(),
×
920
                                        };
×
921
                                }
922
                        case 2:
923
                                {
924
                                        var (first, second) = (operatorTokens[0], operatorTokens[1]);
250✔
925

926
                                        return first.Type switch
250✔
927
                                        {
250✔
928
                                                TokenType.AsteriskSymbol => second.Type switch
3✔
929
                                                {
3✔
930
                                                        TokenType.AsteriskSymbol => BinaryExpressionOperator.Power.ToResult(),
3✔
931
                                                        _ => new UnexpectedTokenException(second, TokenType.AsteriskSymbol)
×
932
                                                                .ToErr<BinaryExpressionOperator>(),
×
933
                                                },
3✔
934
                                                TokenType.EqualsSymbol => second.Type switch
113✔
935
                                                {
113✔
936
                                                        TokenType.EqualsSymbol => BinaryExpressionOperator.Equal.ToResult(),
113✔
937
                                                        _ => new UnexpectedTokenException(second, TokenType.EqualsSymbol)
×
938
                                                                .ToErr<BinaryExpressionOperator>(),
×
939
                                                },
113✔
940
                                                TokenType.ExclamationMarkSymbol => second.Type switch
17✔
941
                                                {
17✔
942
                                                        TokenType.EqualsSymbol => BinaryExpressionOperator.NotEqual.ToResult(),
17✔
943
                                                        _ => new UnexpectedTokenException(second, TokenType.EqualsSymbol)
×
944
                                                                .ToErr<BinaryExpressionOperator>(),
×
945
                                                },
17✔
946
                                                TokenType.AmpersandSymbol => second.Type switch
36✔
947
                                                {
36✔
948
                                                        TokenType.AmpersandSymbol => BinaryExpressionOperator.LogicalAnd.ToResult(),
36✔
949
                                                        _ => new UnexpectedTokenException(second, TokenType.AmpersandSymbol)
×
950
                                                                .ToErr<BinaryExpressionOperator>(),
×
951
                                                },
36✔
952
                                                TokenType.PipeSymbol => second.Type switch
56✔
953
                                                {
56✔
954
                                                        TokenType.PipeSymbol => BinaryExpressionOperator.LogicalOr.ToResult(),
56✔
955
                                                        _ => new UnexpectedTokenException(second, TokenType.PipeSymbol)
×
956
                                                                .ToErr<BinaryExpressionOperator>(),
×
957
                                                },
56✔
958
                                                TokenType.LessThanSymbol => second.Type switch
3✔
959
                                                {
3✔
960
                                                        TokenType.EqualsSymbol => BinaryExpressionOperator.LessThanOrEqual.ToResult(),
3✔
961
                                                        TokenType.LessThanSymbol => BinaryExpressionOperator.BitwiseShiftLeft.ToResult(),
×
962
                                                        _ => new UnexpectedTokenException(second, TokenType.EqualsSymbol,
×
963
                                                                TokenType.LessThanSymbol).ToErr<BinaryExpressionOperator>(),
×
964
                                                },
3✔
965
                                                TokenType.GreaterThanSymbol => second.Type switch
18✔
966
                                                {
18✔
967
                                                        TokenType.EqualsSymbol => BinaryExpressionOperator.GreaterThanOrEqual.ToResult(),
18✔
968
                                                        TokenType.GreaterThanSymbol => BinaryExpressionOperator.BitwiseShiftRight.ToResult(),
×
969
                                                        _ => new UnexpectedTokenException(second, TokenType.EqualsSymbol,
×
970
                                                                TokenType.GreaterThanSymbol).ToErr<BinaryExpressionOperator>(),
×
971
                                                },
18✔
972
                                                TokenType.QuestionMarkSymbol => second.Type switch
4✔
973
                                                {
4✔
974
                                                        TokenType.QuestionMarkSymbol => BinaryExpressionOperator.Coalesce.ToResult(),
4✔
975
                                                        _ => new UnexpectedTokenException(second, TokenType.QuestionMarkSymbol)
×
976
                                                                .ToErr<BinaryExpressionOperator>(),
×
977
                                                },
4✔
978
                                                _ => new UnexpectedTokenException(first, TokenType.AsteriskSymbol, TokenType.EqualsSymbol,
×
979
                                                                TokenType.ExclamationMarkSymbol, TokenType.AmpersandSymbol, TokenType.PipeSymbol,
×
980
                                                                TokenType.LessThanSymbol, TokenType.GreaterThanSymbol, TokenType.QuestionMarkSymbol)
×
981
                                                        .ToErr<BinaryExpressionOperator>(),
×
982
                                        };
250✔
983
                                }
984
                        case 1:
985
                                {
986
                                        var first = operatorTokens[0];
191✔
987

988
                                        return first.Type switch
191✔
989
                                        {
191✔
990
                                                TokenType.PlusSymbol => BinaryExpressionOperator.Add.ToResult(),
62✔
991
                                                TokenType.MinusSymbol => BinaryExpressionOperator.Subtract.ToResult(),
28✔
992
                                                TokenType.AsteriskSymbol => BinaryExpressionOperator.Multiply.ToResult(),
27✔
993
                                                TokenType.SlashSymbol => BinaryExpressionOperator.Divide.ToResult(),
4✔
994
                                                TokenType.PercentSymbol => BinaryExpressionOperator.Modulo.ToResult(),
5✔
995
                                                TokenType.GreaterThanSymbol => BinaryExpressionOperator.GreaterThan.ToResult(),
14✔
996
                                                TokenType.LessThanSymbol => BinaryExpressionOperator.LessThan.ToResult(),
51✔
997
                                                TokenType.PipeSymbol => BinaryExpressionOperator.BitwiseOr.ToResult(),
×
998
                                                TokenType.AmpersandSymbol => BinaryExpressionOperator.BitwiseAnd.ToResult(),
×
999
                                                TokenType.HatSymbol => BinaryExpressionOperator.BitwiseXor.ToResult(),
×
1000
                                                _ => new UnexpectedTokenException(first, TokenType.PlusSymbol, TokenType.MinusSymbol,
×
1001
                                                        TokenType.AsteriskSymbol, TokenType.SlashSymbol, TokenType.PercentSymbol,
×
1002
                                                        TokenType.GreaterThanSymbol, TokenType.LessThanSymbol, TokenType.PipeSymbol,
×
1003
                                                        TokenType.AmpersandSymbol, TokenType.HatSymbol).ToErr<BinaryExpressionOperator>(),
×
1004
                                        };
191✔
1005
                                }
1006
                        case 0:
1007
                                return new UnexpectedEndOfTokensException(tokens.LastToken, "Expected an operator")
×
1008
                                        .ToErr<BinaryExpressionOperator>();
×
1009
                        default:
1010
                                return new UnexpectedTokenException("Operators can only be chained up to 3 times", operatorTokens[0])
×
1011
                                        .ToErr<BinaryExpressionOperator>();
×
1012
                }
1013
        }
1014

1015
        private IExpressionNode ParsePrimaryExpression()
1016
        {
1017
                var maybeTokenType = tokens.PeekType();
3,036✔
1018
                if (maybeTokenType is not { } tokenType)
3,036✔
1019
                {
1020
                        return diagnostics.AddUnexpectedEndOfTokensExpression(tokens.LastToken);
×
1021
                }
1022

1023
                if (tokenType.IsLiteral())
3,036✔
1024
                {
1025
                        var literalResult = tokens.Dequeue(tokenType);
1,242✔
1026
                        if (literalResult is Err<Token> literalError)
1,242✔
1027
                                return diagnostics.AddErrorExpression(literalError);
×
1028

1029
                        return new LiteralExpressionNode(literalResult.Value);
1,242✔
1030
                }
1031

1032
                switch (tokenType)
1,794✔
1033
                {
1034
                        case TokenType.OpeningParentheses:
1035
                                {
1036
                                        var nextType = tokens.PeekType(1);
121✔
1037
                                        return nextType switch
121✔
1038
                                        {
121✔
1039
                                                TokenType.TypeName => ParseFunctionDefinitionExpression(),
115✔
1040
                                                _ => ParseNestedExpression(),
6✔
1041
                                        };
121✔
1042
                                }
1043
                        case TokenType.Identifier:
1044
                                {
1045
                                        var nextType = tokens.PeekType(1);
1,516✔
1046
                                        return nextType switch
1,516✔
1047
                                        {
1,516✔
1048
                                                TokenType.OpeningParentheses => ParseFunctionCallExpression(),
420✔
1049
                                                _ => ParseIdentifierExpression(),
1,096✔
1050
                                        };
1,516✔
1051
                                }
1052
                        case TokenType.OpeningSquareBracket:
1053
                                return ParseListExpression();
64✔
1054
                        case TokenType.OpeningCurlyBracket:
1055
                                return ParseMapExpression();
34✔
1056
                        case TokenType.MinusSymbol:
1057
                                return ParseNegateExpression();
35✔
1058
                        case TokenType.ExclamationMarkSymbol:
1059
                                return ParseNotExpression();
22✔
1060
                        case TokenType.TypeName
1061
                                when tokens.Peek()?.Value.Equals("null", StringComparison.OrdinalIgnoreCase) ?? false:
×
1062
                                var nullTokenResult = tokens.Dequeue(TokenType.TypeName);
×
1063
                                if (nullTokenResult is Err<Token> nullTokenError)
×
1064
                                        return diagnostics.AddErrorExpression(nullTokenError);
×
1065

NEW
1066
                                return new LiteralExpressionNode(new(TokenType.LiteralNull, nullTokenResult.Value.Value,
×
1067
                                        nullTokenResult.Value.Location));
×
1068
                        default:
1069
                                return diagnostics.AddMissingExpression(tokens.LastToken);
2✔
1070
                }
1071
        }
1072

1073
        private IExpressionNode ParseNotExpression()
1074
        {
1075
                var exclamationMarkResult = tokens.Dequeue(TokenType.ExclamationMarkSymbol);
22✔
1076
                if (exclamationMarkResult is Err<Token> exclamationMarkError)
22✔
1077
                        return diagnostics.AddErrorExpression(exclamationMarkError);
×
1078

1079
                var expression = ParseExpression();
22✔
1080

1081
                return new NotExpressionNode(exclamationMarkResult.Value, expression);
22✔
1082
        }
1083

1084
        private IExpressionNode ParseNegateExpression()
1085
        {
1086
                var minusResult = tokens.Dequeue(TokenType.MinusSymbol);
35✔
1087
                if (minusResult is Err<Token> minusError)
35✔
1088
                        return diagnostics.AddErrorExpression(minusError);
×
1089

1090
                var expression = ParseExpression();
35✔
1091

1092
                return new NegateExpressionNode(minusResult.Value, expression);
35✔
1093
        }
1094

1095
        private IExpressionNode ParseMapExpression()
1096
        {
1097
                var openCurlyBraceResult = tokens.Dequeue(TokenType.OpeningCurlyBracket);
34✔
1098
                if (openCurlyBraceResult is Err<Token> openError)
34✔
1099
                        return diagnostics.AddErrorExpression(openError);
×
1100

1101
                var openCurlyBrace = openCurlyBraceResult.Value;
34✔
1102

1103
                var map = new Dictionary<Token, IExpressionNode>();
34✔
1104

1105
                while (tokens.PeekType() is not TokenType.ClosingCurlyBracket)
107✔
1106
                {
1107
                        var keyResult = tokens.Dequeue(TokenType.LiteralString);
73✔
1108
                        if (keyResult is Err<Token> keyError)
73✔
1109
                                return diagnostics.AddErrorExpression(keyError);
×
1110

1111
                        _ = tokens.Dequeue(TokenType.ColonSymbol);
73✔
1112

1113
                        var value = ParseExpression();
73✔
1114

1115
                        map.Add(keyResult.Value, value);
73✔
1116

1117
                        if (tokens.PeekType() is TokenType.CommaSymbol)
73✔
1118
                        {
1119
                                _ = tokens.Dequeue(TokenType.CommaSymbol);
42✔
1120
                        }
1121
                }
1122

1123
                _ = tokens.Dequeue(TokenType.ClosingCurlyBracket);
34✔
1124

1125
                return new MapExpressionNode(openCurlyBrace, map);
34✔
1126
        }
1127

1128
        private IExpressionNode ParseListExpression()
1129
        {
1130
                var openSquareBracketResult = tokens.Dequeue(TokenType.OpeningSquareBracket);
64✔
1131
                if (openSquareBracketResult is Err<Token> openError)
64✔
1132
                        return diagnostics.AddErrorExpression(openError);
×
1133

1134
                var openSquareBracket = openSquareBracketResult.Value;
64✔
1135

1136
                var list = new List<IExpressionNode>();
64✔
1137

1138
                while (tokens.PeekType() is not TokenType.ClosingSquareBracket)
242✔
1139
                {
1140
                        list.Add(ParseExpression());
178✔
1141

1142
                        if (tokens.PeekType() is TokenType.CommaSymbol)
178✔
1143
                        {
1144
                                _ = tokens.Dequeue(TokenType.CommaSymbol);
143✔
1145
                        }
1146
                }
1147

1148
                _ = tokens.Dequeue(TokenType.ClosingSquareBracket);
64✔
1149

1150
                return new ListExpressionNode(openSquareBracket, list);
64✔
1151
        }
1152

1153
        private IExpressionNode ParseIdentifierExpression()
1154
        {
1155
                var identifierResult = tokens.Dequeue(TokenType.Identifier);
1,096✔
1156
                if (identifierResult is Err<Token> identifierError)
1,096✔
1157
                        return diagnostics.AddErrorExpression(identifierError);
×
1158

1159
                return new IdentifierExpressionNode(identifierResult.Value);
1,096✔
1160
        }
1161

1162
        private IExpressionNode ParseFunctionCallExpression()
1163
        {
1164
                var identifierResult = tokens.Dequeue(TokenType.Identifier);
839✔
1165
                if (identifierResult is Err<Token> identifierError)
839✔
1166
                        return diagnostics.AddErrorExpression(identifierError);
×
1167

1168
                _ = tokens.Dequeue(TokenType.OpeningParentheses);
839✔
1169

1170
                var arguments = ParseCallArguments();
839✔
1171

1172
                _ = tokens.Dequeue(TokenType.ClosingParentheses);
839✔
1173

1174
                return new CallExpressionNode(identifierResult.Value, arguments);
839✔
1175
        }
1176

1177
        private IExpressionNode ParseNestedExpression()
1178
        {
1179
                _ = tokens.Dequeue(TokenType.OpeningParentheses);
6✔
1180

1181
                var expression = ParseExpression();
6✔
1182

1183
                _ = tokens.Dequeue(TokenType.ClosingParentheses);
6✔
1184

1185
                return expression;
6✔
1186
        }
1187

1188
        private IExpressionNode ParseFunctionDefinitionExpression()
1189
        {
1190
                var openParenthesisResult = tokens.Dequeue(TokenType.OpeningParentheses);
115✔
1191
                if (openParenthesisResult is Err<Token> openError)
115✔
1192
                        return diagnostics.AddErrorExpression(openError);
×
1193

1194
                var parameters = new List<IVariableDeclarationNode>();
115✔
1195
                while (tokens.PeekType() is not TokenType.ClosingParentheses)
278✔
1196
                {
1197
                        var declaration = ParseVariableDeclaration();
163✔
1198

1199
                        parameters.Add(declaration);
163✔
1200

1201
                        if (tokens.PeekType() is TokenType.CommaSymbol)
163✔
1202
                        {
1203
                                _ = tokens.Dequeue(TokenType.CommaSymbol);
48✔
1204
                        }
1205
                }
1206

1207
                _ = tokens.Dequeue(TokenType.ClosingParentheses);
115✔
1208

1209
                var body = ParseCodeBlock();
115✔
1210

1211
                if (tokens.PeekType() is not TokenType.OpeningParentheses)
115✔
1212
                {
1213
                        return new FunctionDefinitionExpressionNode(openParenthesisResult.Value, parameters, body);
114✔
1214
                }
1215

1216
                // direct definition call
1217
                _ = tokens.Dequeue(TokenType.OpeningParentheses);
1✔
1218

1219
                var callArguments = ParseCallArguments();
1✔
1220

1221
                _ = tokens.Dequeue(TokenType.ClosingParentheses);
1✔
1222

1223
                return new FunctionDefinitionCallExpressionNode(openParenthesisResult.Value, parameters, body, callArguments);
1✔
1224
        }
1225

1226
        private List<IExpressionNode> ParseCallArguments()
1227
        {
1228
                var arguments = new List<IExpressionNode>();
840✔
1229

1230
                while (tokens.PeekType() is not TokenType.ClosingParentheses)
1,248✔
1231
                {
1232
                        var argumentExpression = ParseExpression();
1,229✔
1233

1234
                        arguments.Add(argumentExpression);
1,229✔
1235

1236
                        if (tokens.PeekType() is TokenType.CommaSymbol)
1,229✔
1237
                        {
1238
                                _ = tokens.Dequeue();
408✔
1239
                        }
1240
                        else
1241
                        {
1242
                                break;
1243
                        }
1244
                }
1245

1246
                return arguments;
840✔
1247
        }
1248
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc