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

TyRoXx / NonlocalityOS / 15651039133

14 Jun 2025 10:19AM UTC coverage: 76.604% (+0.3%) from 76.336%
15651039133

Pull #293

github

web-flow
Merge 39ef69af6 into b9c75376a
Pull Request #293: GH-292: Integers

77 of 95 new or added lines in 8 files covered. (81.05%)

6 existing lines in 2 files now uncovered.

4142 of 5407 relevant lines covered (76.6%)

2239.33 hits per line

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

76.1
/lambda_compiler/src/parsing.rs
1
use crate::{
2
    ast::{self, LambdaParameter},
3
    compilation::{CompilerError, SourceLocation},
4
    tokenization::{IntegerBase, Token, TokenContent},
5
};
6
use lambda::name::{Name, NamespaceId};
7

8
#[derive(Debug)]
9
pub struct ParserError {
10
    pub message: String,
11
    pub location: SourceLocation,
12
}
13

14
impl ParserError {
15
    pub fn new(message: String, location: SourceLocation) -> Self {
8✔
16
        Self { message, location }
17
    }
18
}
19

20
impl std::fmt::Display for ParserError {
21
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
8✔
22
        write!(f, "{}", &self.message)
8✔
23
    }
24
}
25

26
pub type ParserResult<T> = std::result::Result<T, ParserError>;
27

28
pub fn pop_next_non_whitespace_token<'t>(
820✔
29
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
30
) -> Option<&'t Token> {
31
    let token = peek_next_non_whitespace_token(tokens);
820✔
32
    if token.is_some() {
1,640✔
33
        tokens.next();
820✔
34
    }
35

36
    token
820✔
37
}
38

39
pub fn peek_next_non_whitespace_token<'t>(
2,210✔
40
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
41
) -> Option<&'t Token> {
42
    loop {
×
43
        let next = tokens.peek();
2,903✔
44
        match next {
2,903✔
45
            Some(token) => match token.content {
2,903✔
46
                TokenContent::Whitespace => {
×
47
                    tokens.next();
693✔
48
                    continue;
693✔
49
                }
50

51
                TokenContent::Identifier(_)
×
52
                | TokenContent::Assign
×
53
                | TokenContent::LeftParenthesis
×
54
                | TokenContent::RightParenthesis
×
55
                | TokenContent::LeftBracket
×
56
                | TokenContent::RightBracket
×
57
                | TokenContent::LeftBrace
×
58
                | TokenContent::RightBrace
×
59
                | TokenContent::Dot
×
60
                | TokenContent::Colon
×
61
                | TokenContent::Quotes(_)
×
62
                | TokenContent::FatArrow
×
63
                | TokenContent::Comma
×
64
                | TokenContent::Comment(_)
×
NEW
65
                | TokenContent::Integer(_, _)
×
66
                | TokenContent::EndOfFile => return Some(token),
2,210✔
67
            },
68
            None => return None,
×
69
        }
70
    }
71
}
72

73
fn expect_right_brace(
23✔
74
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
75
) -> ParserResult<()> {
76
    match peek_next_non_whitespace_token(tokens) {
23✔
77
        Some(non_whitespace) => match &non_whitespace.content {
23✔
78
            TokenContent::Comment(_) => todo!(),
79
            TokenContent::Whitespace => unreachable!(),
80
            TokenContent::Identifier(_) => todo!(),
81
            TokenContent::Assign => todo!(),
82
            TokenContent::LeftParenthesis => todo!(),
83
            TokenContent::RightParenthesis => todo!(),
84
            TokenContent::LeftBracket => todo!(),
85
            TokenContent::RightBracket => todo!(),
86
            TokenContent::LeftBrace => todo!(),
87
            TokenContent::RightBrace => {
88
                pop_next_non_whitespace_token(tokens);
23✔
89
                Ok(())
23✔
90
            }
91
            TokenContent::Dot => todo!(),
92
            TokenContent::Colon => todo!(),
93
            TokenContent::Quotes(_) => todo!(),
94
            TokenContent::FatArrow => todo!(),
95
            TokenContent::Comma => todo!(),
96
            TokenContent::Integer(_, _) => todo!(),
97
            TokenContent::EndOfFile => todo!(),
98
        },
99
        None => todo!(),
100
    }
101
}
102

103
fn try_skip_left_parenthesis(
14✔
104
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
105
) -> bool {
106
    match peek_next_non_whitespace_token(tokens) {
14✔
107
        Some(non_whitespace) => match &non_whitespace.content {
14✔
108
            TokenContent::Comment(_) => todo!(),
109
            TokenContent::Whitespace => unreachable!(),
110
            TokenContent::Identifier(_) => false,
1✔
111
            TokenContent::Assign => todo!(),
112
            TokenContent::LeftParenthesis => {
113
                pop_next_non_whitespace_token(tokens);
13✔
114
                true
13✔
115
            }
116
            TokenContent::RightParenthesis => todo!(),
117
            TokenContent::LeftBracket => false,
×
118
            TokenContent::RightBracket => todo!(),
119
            TokenContent::LeftBrace => false,
×
120
            TokenContent::RightBrace => todo!(),
121
            TokenContent::Dot => todo!(),
122
            TokenContent::Colon => todo!(),
123
            TokenContent::Quotes(_) => false,
×
124
            TokenContent::FatArrow => todo!(),
125
            TokenContent::Comma => false,
×
126
            TokenContent::Integer(_, _) => todo!(),
127
            TokenContent::EndOfFile => todo!(),
128
        },
129
        None => todo!(),
130
    }
131
}
132

133
fn try_skip_right_parenthesis(
154✔
134
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
135
) -> bool {
136
    match peek_next_non_whitespace_token(tokens) {
154✔
137
        Some(non_whitespace) => match &non_whitespace.content {
154✔
138
            TokenContent::Comment(_) => todo!(),
139
            TokenContent::Whitespace => unreachable!(),
140
            TokenContent::Identifier(_) => false,
26✔
141
            TokenContent::Assign => todo!(),
142
            TokenContent::LeftParenthesis => todo!(),
143
            TokenContent::RightParenthesis => {
144
                pop_next_non_whitespace_token(tokens);
92✔
145
                true
92✔
146
            }
147
            TokenContent::LeftBracket => false,
×
148
            TokenContent::RightBracket => todo!(),
149
            TokenContent::LeftBrace => false,
×
150
            TokenContent::RightBrace => todo!(),
151
            TokenContent::Dot => todo!(),
152
            TokenContent::Colon => todo!(),
153
            TokenContent::Quotes(_) => false,
18✔
154
            TokenContent::FatArrow => todo!(),
155
            TokenContent::Comma => false,
14✔
156
            TokenContent::Integer(_, _) => false,
4✔
157
            TokenContent::EndOfFile => todo!(),
158
        },
159
        None => todo!(),
160
    }
161
}
162

163
fn try_skip_assign(tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>) -> bool {
35✔
164
    match peek_next_non_whitespace_token(tokens) {
35✔
165
        Some(non_whitespace) => match &non_whitespace.content {
35✔
166
            TokenContent::Comment(_) => todo!(),
167
            TokenContent::Whitespace => unreachable!(),
168
            TokenContent::Identifier(_) => false,
×
169
            TokenContent::Assign => {
170
                pop_next_non_whitespace_token(tokens);
35✔
171
                true
35✔
172
            }
173
            TokenContent::LeftParenthesis => todo!(),
174
            TokenContent::RightParenthesis => todo!(),
175
            TokenContent::LeftBracket => todo!(),
176
            TokenContent::RightBracket => todo!(),
177
            TokenContent::LeftBrace => false,
×
178
            TokenContent::RightBrace => todo!(),
179
            TokenContent::Dot => todo!(),
180
            TokenContent::Colon => todo!(),
181
            TokenContent::Quotes(_) => false,
×
182
            TokenContent::FatArrow => todo!(),
183
            TokenContent::Comma => false,
×
184
            TokenContent::Integer(_, _) => todo!(),
185
            TokenContent::EndOfFile => todo!(),
186
        },
187
        None => todo!(),
188
    }
189
}
190

191
fn expect_fat_arrow(tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>) {
60✔
192
    match pop_next_non_whitespace_token(tokens) {
60✔
193
        Some(non_whitespace) => match &non_whitespace.content {
60✔
194
            TokenContent::Comment(_) => todo!(),
195
            TokenContent::Whitespace => unreachable!(),
196
            TokenContent::Identifier(_identifier) => todo!(),
197
            TokenContent::Assign => todo!(),
198
            TokenContent::LeftParenthesis => todo!(),
199
            TokenContent::RightParenthesis => todo!(),
200
            TokenContent::LeftBracket => todo!(),
201
            TokenContent::RightBracket => todo!(),
202
            TokenContent::LeftBrace => todo!(),
203
            TokenContent::RightBrace => todo!(),
204
            TokenContent::Dot => todo!(),
205
            TokenContent::Colon => todo!(),
206
            TokenContent::Quotes(_) => todo!(),
207
            TokenContent::FatArrow => {}
60✔
208
            TokenContent::Comma => todo!(),
209
            TokenContent::Integer(_, _) => todo!(),
210
            TokenContent::EndOfFile => todo!(),
211
        },
212
        None => todo!(),
213
    }
214
}
215

216
fn expect_comma(tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>) {
53✔
217
    match pop_next_non_whitespace_token(tokens) {
53✔
218
        Some(non_whitespace) => match &non_whitespace.content {
53✔
219
            TokenContent::Comment(_) => todo!(),
220
            TokenContent::Whitespace => unreachable!(),
221
            TokenContent::Identifier(_) => todo!(),
222
            TokenContent::Assign => todo!(),
223
            TokenContent::LeftParenthesis => todo!(),
224
            TokenContent::RightParenthesis => todo!(),
225
            TokenContent::LeftBracket => todo!(),
226
            TokenContent::RightBracket => todo!(),
227
            TokenContent::LeftBrace => todo!(),
228
            TokenContent::RightBrace => todo!(),
229
            TokenContent::Dot => todo!(),
230
            TokenContent::Colon => todo!(),
231
            TokenContent::Quotes(_) => todo!(),
232
            TokenContent::FatArrow => todo!(),
233
            TokenContent::Comma => {}
53✔
234
            TokenContent::Integer(_, _) => todo!(),
235
            TokenContent::EndOfFile => todo!(),
236
        },
237
        None => todo!(),
238
    }
239
}
240

241
fn skip_right_bracket(tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>) -> bool {
184✔
242
    let maybe_right_bracket = peek_next_non_whitespace_token(tokens);
184✔
243
    match maybe_right_bracket {
184✔
244
        Some(token) => match &token.content {
184✔
245
            TokenContent::Comment(_) => todo!(),
246
            TokenContent::Whitespace => unreachable!(),
247
            TokenContent::Identifier(_) => false,
87✔
248
            TokenContent::Assign => false,
×
249
            TokenContent::LeftParenthesis => false,
×
250
            TokenContent::RightParenthesis => false,
×
251
            TokenContent::LeftBracket => false,
2✔
252
            TokenContent::RightBracket => {
253
                tokens.next();
43✔
254
                true
43✔
255
            }
256
            TokenContent::LeftBrace => todo!(),
257
            TokenContent::RightBrace => todo!(),
258
            TokenContent::Dot => false,
×
259
            TokenContent::Colon => todo!(),
260
            TokenContent::Quotes(_) => false,
11✔
261
            TokenContent::FatArrow => false,
×
262
            TokenContent::Comma => false,
41✔
263
            TokenContent::Integer(_, _) => todo!(),
264
            TokenContent::EndOfFile => todo!(),
265
        },
266
        None => false,
×
267
    }
268
}
269

270
fn parse_tree_construction(
43✔
271
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
272
    local_namespace: &NamespaceId,
273
    location: &SourceLocation,
274
) -> ParserResult<ast::Expression> {
275
    let mut elements = Vec::new();
43✔
276
    loop {
277
        if skip_right_bracket(tokens) {
108✔
278
            break;
32✔
279
        }
280
        if !elements.is_empty() {
117✔
281
            expect_comma(tokens);
41✔
282
        }
283
        if skip_right_bracket(tokens) {
284
            break;
11✔
285
        }
286
        let element = parse_expression(tokens, local_namespace)?;
65✔
287
        elements.push(element);
288
    }
289
    Ok(ast::Expression::ConstructTree(elements, *location))
43✔
290
}
291

292
fn parse_braces(
23✔
293
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
294
    local_namespace: &NamespaceId,
295
) -> ParserResult<ast::Expression> {
296
    let content = parse_expression(tokens, local_namespace)?;
46✔
297
    expect_right_brace(tokens)?;
×
298
    Ok(ast::Expression::Braces(Box::new(content)))
23✔
299
}
300

301
fn parse_let(
35✔
302
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
303
    local_namespace: &NamespaceId,
304
    let_location: &SourceLocation,
305
) -> ParserResult<ast::Expression> {
306
    let (name, location) = match try_pop_identifier(tokens) {
70✔
307
        Some((name, location)) => (name, location),
35✔
308
        None => {
309
            return Err(ParserError::new(
×
310
                "Expected identifier after 'let' keyword.".to_string(),
×
311
                *let_location,
×
312
            ))
313
        }
314
    };
315
    if !try_skip_assign(tokens) {
35✔
316
        return Err(ParserError::new(
×
317
            "Expected '=' after 'let' identifier.".to_string(),
×
318
            *let_location,
×
319
        ));
320
    }
321
    let value = parse_expression(tokens, local_namespace)?;
70✔
322
    let body = parse_expression(tokens, local_namespace)?;
35✔
323
    Ok(ast::Expression::Let {
324
        name: Name::new(*local_namespace, name),
325
        location,
326
        value: Box::new(value),
327
        body: Box::new(body),
328
    })
329
}
330

331
fn parse_type_of(
14✔
332
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
333
    local_namespace: &NamespaceId,
334
    type_of_location: &SourceLocation,
335
) -> ParserResult<ast::Expression> {
336
    if !try_skip_left_parenthesis(tokens) {
14✔
337
        return Err(ParserError::new(
1✔
338
            "Expected '(' after 'type_of' keyword.".to_string(),
1✔
339
            *type_of_location,
1✔
340
        ));
341
    }
342
    let expression = parse_expression(tokens, local_namespace)?;
26✔
343
    if !try_skip_right_parenthesis(tokens) {
344
        return Err(ParserError::new(
1✔
345
            "Expected ')' after expression in 'type_of'.".to_string(),
1✔
346
            expression.source_location(),
1✔
347
        ));
348
    }
349
    Ok(ast::Expression::TypeOf(Box::new(expression)))
12✔
350
}
351

352
fn parse_comment(
8✔
353
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
354
    content: &str,
355
    local_namespace: &NamespaceId,
356
    comment_location: &SourceLocation,
357
) -> ParserResult<ast::Expression> {
358
    let expression = parse_expression(tokens, local_namespace)?;
16✔
359
    Ok(ast::Expression::Comment(
360
        content.to_string(),
361
        Box::new(expression),
362
        *comment_location,
363
    ))
364
}
365

366
fn parse_integer(
6✔
367
    value: i64,
368
    base: IntegerBase,
369
    integer_location: &SourceLocation,
370
) -> ParserResult<ast::Expression> {
371
    Ok(ast::Expression::IntegerLiteral(
6✔
372
        value,
6✔
373
        base,
6✔
374
        *integer_location,
6✔
375
    ))
376
}
377

378
fn parse_expression_start<'t>(
379✔
379
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
380
    local_namespace: &NamespaceId,
381
) -> ParserResult<ast::Expression> {
382
    match peek_next_non_whitespace_token(tokens) {
379✔
383
        Some(non_whitespace) => match &non_whitespace.content {
379✔
384
            TokenContent::Whitespace => unreachable!(),
385
            TokenContent::Identifier(identifier) => {
204✔
386
                pop_next_non_whitespace_token(tokens);
204✔
387
                if identifier.as_str() == "let" {
204✔
388
                    parse_let(tokens, local_namespace, &non_whitespace.location)
35✔
389
                } else if identifier.as_str() == "type_of" {
169✔
390
                    parse_type_of(tokens, local_namespace, &non_whitespace.location)
14✔
391
                } else {
392
                    Ok(ast::Expression::Identifier(
155✔
393
                        Name::new(*local_namespace, identifier.clone()),
155✔
394
                        non_whitespace.location,
155✔
395
                    ))
396
                }
397
            }
398
            TokenContent::Assign => todo!(),
399
            TokenContent::LeftParenthesis => {
×
400
                pop_next_non_whitespace_token(tokens);
62✔
401
                parse_lambda(tokens, local_namespace)
62✔
402
            }
403
            TokenContent::RightParenthesis => Err(ParserError::new(
1✔
404
                "Expected expression, found right parenthesis.".to_string(),
1✔
405
                non_whitespace.location,
1✔
406
            )),
407
            TokenContent::LeftBracket => {
×
408
                pop_next_non_whitespace_token(tokens);
43✔
409
                parse_tree_construction(tokens, local_namespace, &non_whitespace.location)
43✔
410
            }
411
            TokenContent::RightBracket => Err(ParserError::new(
×
412
                "Expected expression, found right bracket.".to_string(),
×
413
                non_whitespace.location,
×
414
            )),
415
            TokenContent::LeftBrace => {
×
416
                pop_next_non_whitespace_token(tokens);
23✔
417
                parse_braces(tokens, local_namespace)
23✔
418
            }
419
            TokenContent::RightBrace => todo!(),
420
            TokenContent::Dot => todo!(),
421
            TokenContent::Colon => todo!(),
422
            TokenContent::Quotes(content) => {
28✔
423
                pop_next_non_whitespace_token(tokens);
28✔
424
                Ok(ast::Expression::StringLiteral(
28✔
425
                    content.clone(),
28✔
426
                    non_whitespace.location,
28✔
427
                ))
428
            }
429
            TokenContent::FatArrow => Err(ParserError::new(
1✔
430
                "Expected expression, found fat arrow.".to_string(),
1✔
431
                non_whitespace.location,
1✔
432
            )),
433
            TokenContent::Comma => Err(ParserError::new(
1✔
434
                "Expected expression, found comma.".to_string(),
1✔
435
                non_whitespace.location,
1✔
436
            )),
437
            TokenContent::EndOfFile => Err(ParserError::new(
2✔
438
                "Expected expression, got end of file.".to_string(),
2✔
439
                non_whitespace.location,
2✔
440
            )),
441
            TokenContent::Comment(content) => {
8✔
442
                pop_next_non_whitespace_token(tokens);
8✔
443
                parse_comment(tokens, content, local_namespace, &non_whitespace.location)
8✔
444
            }
445
            TokenContent::Integer(value, base) => {
6✔
446
                pop_next_non_whitespace_token(tokens);
6✔
447
                parse_integer(*value, *base, &non_whitespace.location)
6✔
448
            }
449
        },
450
        None => todo!(),
451
    }
452
}
453

454
fn parse_apply(
21✔
455
    callee: ast::Expression,
456
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
457
    local_namespace: &NamespaceId,
458
) -> ParserResult<ast::Expression> {
459
    let mut arguments = Vec::new();
21✔
460
    loop {
461
        if try_skip_right_parenthesis(tokens) {
50✔
462
            break;
20✔
463
        }
464
        if !arguments.is_empty() {
42✔
465
            expect_comma(tokens);
12✔
466
        }
467
        if try_skip_right_parenthesis(tokens) {
468
            break;
×
469
        }
470
        let argument = parse_expression(tokens, local_namespace)?;
30✔
471
        arguments.push(argument);
472
    }
473
    Ok(ast::Expression::Apply {
20✔
474
        callee: Box::new(callee),
20✔
475
        arguments,
20✔
476
    })
477
}
478

479
pub fn parse_expression<'t>(
379✔
480
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
481
    local_namespace: &NamespaceId,
482
) -> ParserResult<ast::Expression> {
483
    let start = parse_expression_start(tokens, local_namespace)?;
758✔
484
    match peek_next_non_whitespace_token(tokens) {
×
485
        Some(more) => match &more.content {
367✔
486
            TokenContent::Whitespace => unreachable!(),
487
            TokenContent::Identifier(_) => Ok(start),
27✔
488
            TokenContent::Assign => Ok(start),
×
489
            TokenContent::LeftParenthesis => {
×
490
                tokens.next();
21✔
491
                parse_apply(start, tokens, local_namespace)
21✔
492
            }
493
            TokenContent::RightParenthesis => Ok(start),
43✔
494
            TokenContent::LeftBracket => Ok(start),
8✔
495
            TokenContent::RightBracket => Ok(start),
22✔
496
            TokenContent::LeftBrace => todo!(),
497
            TokenContent::RightBrace => Ok(start),
66✔
498
            TokenContent::Dot => todo!(),
499
            TokenContent::Colon => todo!(),
500
            TokenContent::Quotes(_) => todo!(),
501
            TokenContent::FatArrow => todo!(),
502
            TokenContent::Comma => Ok(start),
67✔
503
            TokenContent::EndOfFile => Ok(start),
107✔
NEW
504
            TokenContent::Integer(_, _) => Ok(start),
×
505
            TokenContent::Comment(_) => Ok(start),
6✔
506
        },
507
        None => todo!(),
508
    }
509
}
510

511
fn try_pop_identifier(
120✔
512
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
513
) -> Option<(String, SourceLocation)> {
514
    match peek_next_non_whitespace_token(tokens) {
120✔
515
        Some(non_whitespace) => match &non_whitespace.content {
120✔
516
            TokenContent::Comment(_) => todo!(),
517
            TokenContent::Whitespace => unreachable!(),
518
            TokenContent::Identifier(identifier) => {
92✔
519
                pop_next_non_whitespace_token(tokens);
92✔
520
                Some((identifier.clone(), non_whitespace.location))
92✔
521
            }
522
            TokenContent::Assign => todo!(),
523
            TokenContent::LeftParenthesis => todo!(),
524
            TokenContent::RightParenthesis => None,
28✔
525
            TokenContent::LeftBracket => todo!(),
526
            TokenContent::RightBracket => todo!(),
527
            TokenContent::LeftBrace => todo!(),
528
            TokenContent::RightBrace => todo!(),
529
            TokenContent::Dot => todo!(),
530
            TokenContent::Colon => todo!(),
531
            TokenContent::Quotes(_) => todo!(),
532
            TokenContent::FatArrow => todo!(),
533
            TokenContent::Comma => None,
×
534
            TokenContent::Integer(_, _) => todo!(),
UNCOV
535
            TokenContent::EndOfFile => None,
×
536
        },
537
        None => None,
×
538
    }
539
}
540

541
fn try_skip_comma(tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>) -> bool {
56✔
542
    match peek_next_non_whitespace_token(tokens) {
56✔
543
        Some(non_whitespace) => match &non_whitespace.content {
56✔
544
            TokenContent::Comment(_) => todo!(),
545
            TokenContent::Whitespace => unreachable!(),
546
            TokenContent::Identifier(_identifier) => false,
1✔
547
            TokenContent::Assign => todo!(),
548
            TokenContent::LeftParenthesis => todo!(),
549
            TokenContent::RightParenthesis => false,
32✔
550
            TokenContent::LeftBracket => todo!(),
551
            TokenContent::RightBracket => todo!(),
552
            TokenContent::LeftBrace => todo!(),
553
            TokenContent::RightBrace => todo!(),
554
            TokenContent::Dot => todo!(),
555
            TokenContent::Colon => todo!(),
556
            TokenContent::Quotes(_) => todo!(),
557
            TokenContent::FatArrow => todo!(),
558
            TokenContent::Comma => {
559
                pop_next_non_whitespace_token(tokens);
23✔
560
                true
23✔
561
            }
562
            TokenContent::Integer(_, _) => todo!(),
UNCOV
563
            TokenContent::EndOfFile => false,
×
564
        },
565
        None => false,
×
566
    }
567
}
568

569
fn try_skip_colon(tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>) -> bool {
57✔
570
    match peek_next_non_whitespace_token(tokens) {
57✔
571
        Some(non_whitespace) => match &non_whitespace.content {
57✔
572
            TokenContent::Comment(_) => todo!(),
573
            TokenContent::Whitespace => unreachable!(),
574
            TokenContent::Identifier(_identifier) => false,
1✔
575
            TokenContent::Assign => todo!(),
576
            TokenContent::LeftParenthesis => todo!(),
577
            TokenContent::RightParenthesis => false,
18✔
578
            TokenContent::LeftBracket => todo!(),
579
            TokenContent::RightBracket => todo!(),
580
            TokenContent::LeftBrace => todo!(),
581
            TokenContent::RightBrace => todo!(),
582
            TokenContent::Dot => todo!(),
583
            TokenContent::Colon => {
584
                pop_next_non_whitespace_token(tokens);
29✔
585
                true
29✔
586
            }
587
            TokenContent::Quotes(_) => todo!(),
588
            TokenContent::FatArrow => todo!(),
589
            TokenContent::Comma => false,
9✔
590
            TokenContent::Integer(_, _) => todo!(),
UNCOV
591
            TokenContent::EndOfFile => false,
×
592
        },
593
        None => false,
×
594
    }
595
}
596

597
fn parse_lambda<'t>(
62✔
598
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
599
    local_namespace: &NamespaceId,
600
) -> ParserResult<ast::Expression> {
601
    let mut parameters = Vec::new();
62✔
602
    while let Some((parameter_name, parameter_location)) = try_pop_identifier(tokens) {
142✔
603
        let namespaced_name = Name::new(*local_namespace, parameter_name);
×
604
        let mut type_annotation = None;
×
605
        if try_skip_colon(tokens) {
×
606
            type_annotation = Some(parse_expression(tokens, local_namespace)?);
58✔
607
        }
608
        parameters.push(LambdaParameter::new(
56✔
609
            namespaced_name,
56✔
610
            parameter_location,
56✔
611
            type_annotation,
56✔
612
        ));
613
        if !try_skip_comma(tokens) {
56✔
614
            break;
33✔
615
        }
616
    }
617
    if !try_skip_right_parenthesis(tokens) {
61✔
618
        let next_token = peek_next_non_whitespace_token(tokens).unwrap();
1✔
619
        return Err(ParserError::new(
1✔
620
            "Expected comma or right parenthesis in lambda parameter list.".to_string(),
1✔
621
            next_token.location,
1✔
622
        ));
623
    }
624
    expect_fat_arrow(tokens);
60✔
625
    let body = parse_expression(tokens, local_namespace)?;
120✔
626
    Ok(ast::Expression::Lambda {
×
627
        parameters,
×
628
        body: Box::new(body),
×
629
    })
630
}
631

632
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
633
pub struct ParserOutput {
634
    pub entry_point: Option<ast::Expression>,
635
    pub errors: Vec<CompilerError>,
636
}
637

638
impl ParserOutput {
639
    pub fn new(entry_point: Option<ast::Expression>, errors: Vec<CompilerError>) -> ParserOutput {
40✔
640
        ParserOutput {
641
            entry_point,
642
            errors,
643
        }
644
    }
645
}
646

647
pub fn parse_expression_tolerantly<'t>(
33✔
648
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
649
    local_namespace: &NamespaceId,
650
) -> ParserOutput {
651
    let mut errors = Vec::new();
33✔
652
    let entry_point_result = parse_expression(tokens, local_namespace);
33✔
653
    match entry_point_result {
33✔
654
        Ok(entry_point) => ParserOutput::new(Some(entry_point), errors),
25✔
655
        Err(error) => {
8✔
656
            errors.push(CompilerError::new(
8✔
657
                format!("Parser error: {}", &error),
8✔
658
                error.location,
8✔
659
            ));
660
            ParserOutput::new(None, errors)
8✔
661
        }
662
    }
663
}
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