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

TyRoXx / NonlocalityOS / 15532211159

09 Jun 2025 10:16AM UTC coverage: 76.046% (+0.4%) from 75.667%
15532211159

Pull #289

github

web-flow
Merge 95a86e2cb into 1c6ef0cf3
Pull Request #289: GH-288: make code formatter good enough for the examples

92 of 96 new or added lines in 7 files covered. (95.83%)

1 existing line in 1 file now uncovered.

3962 of 5210 relevant lines covered (76.05%)

2261.95 hits per line

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

75.86
/lambda_compiler/src/parsing.rs
1
use crate::{
2
    ast::{self, LambdaParameter},
3
    compilation::{CompilerError, SourceLocation},
4
    tokenization::{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>(
770✔
29
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
30
) -> Option<&'t Token> {
31
    let token = peek_next_non_whitespace_token(tokens);
770✔
32
    if token.is_some() {
1,540✔
33
        tokens.next();
770✔
34
    }
35

36
    token
770✔
37
}
38

39
pub fn peek_next_non_whitespace_token<'t>(
2,080✔
40
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
41
) -> Option<&'t Token> {
42
    loop {
×
43
        let next = tokens.peek();
2,699✔
44
        match next {
2,699✔
45
            Some(token) => match token.content {
2,699✔
NEW
46
                TokenContent::Whitespace => {
×
47
                    tokens.next();
619✔
48
                    continue;
619✔
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
×
NEW
64
                | TokenContent::Comment(_)
×
65
                | TokenContent::EndOfFile => return Some(token),
2,080✔
66
            },
67
            None => return None,
×
68
        }
69
    }
70
}
71

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

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

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

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

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

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

234
fn skip_right_bracket(tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>) -> bool {
170✔
235
    let maybe_right_bracket = peek_next_non_whitespace_token(tokens);
170✔
236
    match maybe_right_bracket {
170✔
237
        Some(token) => match &token.content {
170✔
238
            TokenContent::Comment(_) => todo!(),
239
            TokenContent::Whitespace => unreachable!(),
240
            TokenContent::Identifier(_) => false,
79✔
241
            TokenContent::Assign => false,
×
242
            TokenContent::LeftParenthesis => false,
×
243
            TokenContent::RightParenthesis => false,
×
244
            TokenContent::LeftBracket => false,
2✔
245
            TokenContent::RightBracket => {
246
                tokens.next();
41✔
247
                true
41✔
248
            }
249
            TokenContent::LeftBrace => todo!(),
250
            TokenContent::RightBrace => todo!(),
251
            TokenContent::Dot => false,
×
252
            TokenContent::Colon => todo!(),
253
            TokenContent::Quotes(_) => false,
11✔
254
            TokenContent::FatArrow => false,
×
255
            TokenContent::Comma => false,
37✔
256
            TokenContent::EndOfFile => todo!(),
257
        },
258
        None => false,
×
259
    }
260
}
261

262
fn parse_tree_construction(
41✔
263
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
264
    local_namespace: &NamespaceId,
265
) -> ParserResult<ast::Expression> {
266
    let mut elements = Vec::new();
41✔
267
    loop {
268
        if skip_right_bracket(tokens) {
100✔
269
            break;
30✔
270
        }
271
        if !elements.is_empty() {
107✔
272
            expect_comma(tokens);
37✔
273
        }
274
        if skip_right_bracket(tokens) {
275
            break;
11✔
276
        }
277
        let element = parse_expression(tokens, local_namespace)?;
59✔
278
        elements.push(element);
279
    }
280
    Ok(ast::Expression::ConstructTree(elements))
41✔
281
}
282

283
fn parse_braces(
21✔
284
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
285
    local_namespace: &NamespaceId,
286
) -> ParserResult<ast::Expression> {
287
    let content = parse_expression(tokens, local_namespace)?;
42✔
288
    expect_right_brace(tokens)?;
×
289
    Ok(ast::Expression::Braces(Box::new(content)))
21✔
290
}
291

292
fn parse_let(
29✔
293
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
294
    local_namespace: &NamespaceId,
295
    let_location: &SourceLocation,
296
) -> ParserResult<ast::Expression> {
297
    let (name, location) = match try_pop_identifier(tokens) {
58✔
298
        Some((name, location)) => (name, location),
29✔
299
        None => {
300
            return Err(ParserError::new(
×
301
                "Expected identifier after 'let' keyword.".to_string(),
×
302
                *let_location,
×
303
            ))
304
        }
305
    };
306
    if !try_skip_assign(tokens) {
29✔
307
        return Err(ParserError::new(
×
308
            "Expected '=' after 'let' identifier.".to_string(),
×
309
            *let_location,
×
310
        ));
311
    }
312
    let value = parse_expression(tokens, local_namespace)?;
58✔
313
    let body = parse_expression(tokens, local_namespace)?;
29✔
314
    Ok(ast::Expression::Let {
315
        name: Name::new(*local_namespace, name),
316
        location,
317
        value: Box::new(value),
318
        body: Box::new(body),
319
    })
320
}
321

322
fn parse_type_of(
16✔
323
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
324
    local_namespace: &NamespaceId,
325
    type_of_location: &SourceLocation,
326
) -> ParserResult<ast::Expression> {
327
    if !try_skip_left_parenthesis(tokens) {
16✔
328
        return Err(ParserError::new(
1✔
329
            "Expected '(' after 'type_of' keyword.".to_string(),
1✔
330
            *type_of_location,
1✔
331
        ));
332
    }
333
    let expression = parse_expression(tokens, local_namespace)?;
30✔
334
    if !try_skip_right_parenthesis(tokens) {
335
        return Err(ParserError::new(
1✔
336
            "Expected ')' after expression in 'type_of'.".to_string(),
1✔
337
            expression.source_location(),
1✔
338
        ));
339
    }
340
    Ok(ast::Expression::TypeOf(Box::new(expression)))
14✔
341
}
342

343
fn parse_comment(
8✔
344
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
345
    content: &str,
346
    local_namespace: &NamespaceId,
347
    comment_location: &SourceLocation,
348
) -> ParserResult<ast::Expression> {
349
    let expression = parse_expression(tokens, local_namespace)?;
16✔
350
    Ok(ast::Expression::Comment(
351
        content.to_string(),
352
        Box::new(expression),
353
        *comment_location,
354
    ))
355
}
356

357
fn parse_expression_start<'t>(
355✔
358
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
359
    local_namespace: &NamespaceId,
360
) -> ParserResult<ast::Expression> {
361
    match peek_next_non_whitespace_token(tokens) {
355✔
362
        Some(non_whitespace) => match &non_whitespace.content {
355✔
363
            TokenContent::Whitespace => unreachable!(),
364
            TokenContent::Identifier(identifier) => {
190✔
365
                pop_next_non_whitespace_token(tokens);
190✔
366
                if identifier.as_str() == "let" {
190✔
367
                    parse_let(tokens, local_namespace, &non_whitespace.location)
29✔
368
                } else if identifier.as_str() == "type_of" {
161✔
369
                    parse_type_of(tokens, local_namespace, &non_whitespace.location)
16✔
370
                } else {
371
                    Ok(ast::Expression::Identifier(
145✔
372
                        Name::new(*local_namespace, identifier.clone()),
145✔
373
                        non_whitespace.location,
145✔
374
                    ))
375
                }
376
            }
377
            TokenContent::Assign => todo!(),
378
            TokenContent::LeftParenthesis => {
×
379
                pop_next_non_whitespace_token(tokens);
58✔
380
                parse_lambda(tokens, local_namespace)
58✔
381
            }
382
            TokenContent::RightParenthesis => Err(ParserError::new(
1✔
383
                "Expected expression, found right parenthesis.".to_string(),
1✔
384
                non_whitespace.location,
1✔
385
            )),
386
            TokenContent::LeftBracket => {
×
387
                pop_next_non_whitespace_token(tokens);
41✔
388
                parse_tree_construction(tokens, local_namespace)
41✔
389
            }
390
            TokenContent::RightBracket => Err(ParserError::new(
×
391
                "Expected expression, found right bracket.".to_string(),
×
392
                non_whitespace.location,
×
393
            )),
394
            TokenContent::LeftBrace => {
×
395
                pop_next_non_whitespace_token(tokens);
21✔
396
                parse_braces(tokens, local_namespace)
21✔
397
            }
398
            TokenContent::RightBrace => todo!(),
399
            TokenContent::Dot => todo!(),
400
            TokenContent::Colon => todo!(),
401
            TokenContent::Quotes(content) => {
32✔
402
                pop_next_non_whitespace_token(tokens);
32✔
403
                Ok(ast::Expression::StringLiteral(
32✔
404
                    content.clone(),
32✔
405
                    non_whitespace.location,
32✔
406
                ))
407
            }
408
            TokenContent::FatArrow => Err(ParserError::new(
1✔
409
                "Expected expression, found fat arrow.".to_string(),
1✔
410
                non_whitespace.location,
1✔
411
            )),
412
            TokenContent::Comma => Err(ParserError::new(
1✔
413
                "Expected expression, found comma.".to_string(),
1✔
414
                non_whitespace.location,
1✔
415
            )),
416
            TokenContent::EndOfFile => Err(ParserError::new(
2✔
417
                "Expected expression, got end of file.".to_string(),
2✔
418
                non_whitespace.location,
2✔
419
            )),
420
            TokenContent::Comment(content) => {
8✔
421
                pop_next_non_whitespace_token(tokens);
8✔
422
                parse_comment(tokens, content, local_namespace, &non_whitespace.location)
8✔
423
            }
424
        },
425
        None => todo!(),
426
    }
427
}
428

429
fn parse_apply(
21✔
430
    callee: ast::Expression,
431
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
432
    local_namespace: &NamespaceId,
433
) -> ParserResult<ast::Expression> {
434
    let mut arguments = Vec::new();
21✔
435
    loop {
436
        if try_skip_right_parenthesis(tokens) {
52✔
437
            break;
20✔
438
        }
439
        if !arguments.is_empty() {
46✔
440
            expect_comma(tokens);
14✔
441
        }
442
        if try_skip_right_parenthesis(tokens) {
443
            break;
×
444
        }
445
        let argument = parse_expression(tokens, local_namespace)?;
32✔
446
        arguments.push(argument);
447
    }
448
    Ok(ast::Expression::Apply {
20✔
449
        callee: Box::new(callee),
20✔
450
        arguments,
20✔
451
    })
452
}
453

454
pub fn parse_expression<'t>(
355✔
455
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
456
    local_namespace: &NamespaceId,
457
) -> ParserResult<ast::Expression> {
458
    let start = parse_expression_start(tokens, local_namespace)?;
710✔
459
    match peek_next_non_whitespace_token(tokens) {
×
460
        Some(more) => match &more.content {
343✔
461
            TokenContent::Whitespace => unreachable!(),
462
            TokenContent::Identifier(_) => Ok(start),
23✔
463
            TokenContent::Assign => Ok(start),
×
464
            TokenContent::LeftParenthesis => {
×
465
                tokens.next();
21✔
466
                parse_apply(start, tokens, local_namespace)
21✔
467
            }
468
            TokenContent::RightParenthesis => Ok(start),
41✔
469
            TokenContent::LeftBracket => Ok(start),
4✔
470
            TokenContent::RightBracket => Ok(start),
22✔
471
            TokenContent::LeftBrace => todo!(),
472
            TokenContent::RightBrace => Ok(start),
58✔
473
            TokenContent::Dot => todo!(),
474
            TokenContent::Colon => todo!(),
475
            TokenContent::Quotes(_) => todo!(),
476
            TokenContent::FatArrow => todo!(),
477
            TokenContent::Comma => Ok(start),
65✔
478
            TokenContent::EndOfFile => Ok(start),
103✔
479
            TokenContent::Comment(_) => Ok(start),
6✔
480
        },
481
        None => todo!(),
482
    }
483
}
484

485
fn try_pop_identifier(
110✔
486
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
487
) -> Option<(String, SourceLocation)> {
488
    match peek_next_non_whitespace_token(tokens) {
110✔
489
        Some(non_whitespace) => match &non_whitespace.content {
110✔
490
            TokenContent::Comment(_) => todo!(),
491
            TokenContent::Whitespace => unreachable!(),
492
            TokenContent::Identifier(identifier) => {
84✔
493
                pop_next_non_whitespace_token(tokens);
84✔
494
                Some((identifier.clone(), non_whitespace.location))
84✔
495
            }
496
            TokenContent::Assign => todo!(),
497
            TokenContent::LeftParenthesis => todo!(),
498
            TokenContent::RightParenthesis => None,
26✔
499
            TokenContent::LeftBracket => todo!(),
500
            TokenContent::RightBracket => todo!(),
501
            TokenContent::LeftBrace => todo!(),
502
            TokenContent::RightBrace => todo!(),
503
            TokenContent::Dot => todo!(),
504
            TokenContent::Colon => todo!(),
505
            TokenContent::Quotes(_) => todo!(),
506
            TokenContent::FatArrow => todo!(),
507
            TokenContent::Comma => None,
×
508
            TokenContent::EndOfFile => None,
×
509
        },
510
        None => None,
×
511
    }
512
}
513

514
fn try_skip_comma(tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>) -> bool {
54✔
515
    match peek_next_non_whitespace_token(tokens) {
54✔
516
        Some(non_whitespace) => match &non_whitespace.content {
54✔
517
            TokenContent::Comment(_) => todo!(),
518
            TokenContent::Whitespace => unreachable!(),
519
            TokenContent::Identifier(_identifier) => false,
1✔
520
            TokenContent::Assign => todo!(),
521
            TokenContent::LeftParenthesis => todo!(),
522
            TokenContent::RightParenthesis => false,
30✔
523
            TokenContent::LeftBracket => todo!(),
524
            TokenContent::RightBracket => todo!(),
525
            TokenContent::LeftBrace => todo!(),
526
            TokenContent::RightBrace => todo!(),
527
            TokenContent::Dot => todo!(),
528
            TokenContent::Colon => todo!(),
529
            TokenContent::Quotes(_) => todo!(),
530
            TokenContent::FatArrow => todo!(),
531
            TokenContent::Comma => {
532
                pop_next_non_whitespace_token(tokens);
23✔
533
                true
23✔
534
            }
535
            TokenContent::EndOfFile => false,
×
536
        },
537
        None => false,
×
538
    }
539
}
540

541
fn try_skip_colon(tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>) -> bool {
55✔
542
    match peek_next_non_whitespace_token(tokens) {
55✔
543
        Some(non_whitespace) => match &non_whitespace.content {
55✔
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,
18✔
550
            TokenContent::LeftBracket => todo!(),
551
            TokenContent::RightBracket => todo!(),
552
            TokenContent::LeftBrace => todo!(),
553
            TokenContent::RightBrace => todo!(),
554
            TokenContent::Dot => todo!(),
555
            TokenContent::Colon => {
556
                pop_next_non_whitespace_token(tokens);
27✔
557
                true
27✔
558
            }
559
            TokenContent::Quotes(_) => todo!(),
560
            TokenContent::FatArrow => todo!(),
561
            TokenContent::Comma => false,
9✔
562
            TokenContent::EndOfFile => false,
×
563
        },
564
        None => false,
×
565
    }
566
}
567

568
fn parse_lambda<'t>(
58✔
569
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
570
    local_namespace: &NamespaceId,
571
) -> ParserResult<ast::Expression> {
572
    let mut parameters = Vec::new();
58✔
573
    while let Some((parameter_name, parameter_location)) = try_pop_identifier(tokens) {
136✔
574
        let namespaced_name = Name::new(*local_namespace, parameter_name);
×
575
        let mut type_annotation = None;
×
576
        if try_skip_colon(tokens) {
×
577
            type_annotation = Some(parse_expression(tokens, local_namespace)?);
54✔
578
        }
579
        parameters.push(LambdaParameter::new(
54✔
580
            namespaced_name,
54✔
581
            parameter_location,
54✔
582
            type_annotation,
54✔
583
        ));
584
        if !try_skip_comma(tokens) {
54✔
585
            break;
31✔
586
        }
587
    }
588
    if !try_skip_right_parenthesis(tokens) {
57✔
589
        let next_token = peek_next_non_whitespace_token(tokens).unwrap();
1✔
590
        return Err(ParserError::new(
1✔
591
            "Expected comma or right parenthesis in lambda parameter list.".to_string(),
1✔
592
            next_token.location,
1✔
593
        ));
594
    }
595
    expect_fat_arrow(tokens);
56✔
596
    let body = parse_expression(tokens, local_namespace)?;
112✔
597
    Ok(ast::Expression::Lambda {
×
598
        parameters,
×
599
        body: Box::new(body),
×
600
    })
601
}
602

603
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
604
pub struct ParserOutput {
605
    pub entry_point: Option<ast::Expression>,
606
    pub errors: Vec<CompilerError>,
607
}
608

609
impl ParserOutput {
610
    pub fn new(entry_point: Option<ast::Expression>, errors: Vec<CompilerError>) -> ParserOutput {
38✔
611
        ParserOutput {
612
            entry_point,
613
            errors,
614
        }
615
    }
616
}
617

618
pub fn parse_expression_tolerantly<'t>(
31✔
619
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
620
    local_namespace: &NamespaceId,
621
) -> ParserOutput {
622
    let mut errors = Vec::new();
31✔
623
    let entry_point_result = parse_expression(tokens, local_namespace);
31✔
624
    match entry_point_result {
31✔
625
        Ok(entry_point) => ParserOutput::new(Some(entry_point), errors),
23✔
626
        Err(error) => {
8✔
627
            errors.push(CompilerError::new(
8✔
628
                format!("Parser error: {}", &error),
8✔
629
                error.location,
8✔
630
            ));
631
            ParserOutput::new(None, errors)
8✔
632
        }
633
    }
634
}
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