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

TyRoXx / NonlocalityOS / 15531419334

09 Jun 2025 09:32AM UTC coverage: 75.667% (+1.0%) from 74.71%
15531419334

push

github

TyRoXx
GH-286: test that parameter types do not capture variables

3884 of 5133 relevant lines covered (75.67%)

2156.81 hits per line

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

75.59
/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 {
7✔
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 {
7✔
22
        write!(f, "{}", &self.message)
7✔
23
    }
24
}
25

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

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

36
    token
558✔
37
}
38

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

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

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

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

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

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

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

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

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

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

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

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

342
fn parse_expression_start<'t>(
262✔
343
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
344
    local_namespace: &NamespaceId,
345
) -> ParserResult<ast::Expression> {
346
    match peek_next_non_whitespace_token(tokens) {
262✔
347
        Some(non_whitespace) => match &non_whitespace.content {
262✔
348
            TokenContent::Comment(_) => unreachable!(),
349
            TokenContent::Whitespace => unreachable!(),
350
            TokenContent::Identifier(identifier) => {
143✔
351
                pop_next_non_whitespace_token(tokens);
143✔
352
                if identifier.as_str() == "let" {
143✔
353
                    parse_let(tokens, local_namespace, &non_whitespace.location)
16✔
354
                } else if identifier.as_str() == "type_of" {
127✔
355
                    parse_type_of(tokens, local_namespace, &non_whitespace.location)
11✔
356
                } else {
357
                    Ok(ast::Expression::Identifier(
116✔
358
                        Name::new(*local_namespace, identifier.clone()),
116✔
359
                        non_whitespace.location,
116✔
360
                    ))
361
                }
362
            }
363
            TokenContent::Assign => todo!(),
364
            TokenContent::LeftParenthesis => {
×
365
                pop_next_non_whitespace_token(tokens);
46✔
366
                parse_lambda(tokens, local_namespace)
46✔
367
            }
368
            TokenContent::RightParenthesis => Err(ParserError::new(
1✔
369
                "Expected expression, found right parenthesis.".to_string(),
1✔
370
                non_whitespace.location,
1✔
371
            )),
372
            TokenContent::LeftBracket => {
×
373
                pop_next_non_whitespace_token(tokens);
36✔
374
                parse_tree_construction(tokens, local_namespace)
36✔
375
            }
376
            TokenContent::RightBracket => Err(ParserError::new(
×
377
                "Expected expression, found right bracket.".to_string(),
×
378
                non_whitespace.location,
×
379
            )),
380
            TokenContent::LeftBrace => {
×
381
                pop_next_non_whitespace_token(tokens);
14✔
382
                parse_braces(tokens, local_namespace)
14✔
383
            }
384
            TokenContent::RightBrace => todo!(),
385
            TokenContent::Dot => todo!(),
386
            TokenContent::Colon => todo!(),
387
            TokenContent::Quotes(content) => {
19✔
388
                pop_next_non_whitespace_token(tokens);
19✔
389
                Ok(ast::Expression::StringLiteral(
19✔
390
                    content.clone(),
19✔
391
                    non_whitespace.location,
19✔
392
                ))
393
            }
394
            TokenContent::FatArrow => Err(ParserError::new(
1✔
395
                "Expected expression, found fat arrow.".to_string(),
1✔
396
                non_whitespace.location,
1✔
397
            )),
398
            TokenContent::Comma => Err(ParserError::new(
1✔
399
                "Expected expression, found comma.".to_string(),
1✔
400
                non_whitespace.location,
1✔
401
            )),
402
            TokenContent::EndOfFile => Err(ParserError::new(
1✔
403
                "Expected expression, got end of file.".to_string(),
1✔
404
                non_whitespace.location,
1✔
405
            )),
406
        },
407
        None => todo!(),
408
    }
409
}
410

411
fn parse_apply(
13✔
412
    callee: ast::Expression,
413
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
414
    local_namespace: &NamespaceId,
415
) -> ParserResult<ast::Expression> {
416
    let mut arguments = Vec::new();
13✔
417
    loop {
418
        if try_skip_right_parenthesis(tokens) {
30✔
419
            break;
12✔
420
        }
421
        if !arguments.is_empty() {
25✔
422
            expect_comma(tokens);
7✔
423
        }
424
        if try_skip_right_parenthesis(tokens) {
425
            break;
×
426
        }
427
        let argument = parse_expression(tokens, local_namespace)?;
18✔
428
        arguments.push(argument);
429
    }
430
    Ok(ast::Expression::Apply {
12✔
431
        callee: Box::new(callee),
12✔
432
        arguments,
12✔
433
    })
434
}
435

436
pub fn parse_expression<'t>(
262✔
437
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
438
    local_namespace: &NamespaceId,
439
) -> ParserResult<ast::Expression> {
440
    let start = parse_expression_start(tokens, local_namespace)?;
524✔
441
    match peek_next_non_whitespace_token(tokens) {
×
442
        Some(more) => match &more.content {
252✔
443
            TokenContent::Comment(_) => unreachable!(),
444
            TokenContent::Whitespace => unreachable!(),
445
            TokenContent::Identifier(_) => Ok(start),
16✔
446
            TokenContent::Assign => Ok(start),
×
447
            TokenContent::LeftParenthesis => {
×
448
                tokens.next();
13✔
449
                parse_apply(start, tokens, local_namespace)
13✔
450
            }
451
            TokenContent::RightParenthesis => Ok(start),
26✔
452
            TokenContent::LeftBracket => Ok(start),
2✔
453
            TokenContent::RightBracket => Ok(start),
17✔
454
            TokenContent::LeftBrace => todo!(),
455
            TokenContent::RightBrace => Ok(start),
30✔
456
            TokenContent::Dot => todo!(),
457
            TokenContent::Colon => todo!(),
458
            TokenContent::Quotes(_) => todo!(),
459
            TokenContent::FatArrow => todo!(),
460
            TokenContent::Comma => Ok(start),
48✔
461
            TokenContent::EndOfFile => Ok(start),
100✔
462
        },
463
        None => todo!(),
464
    }
465
}
466

467
fn try_pop_identifier(
79✔
468
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
469
) -> Option<(String, SourceLocation)> {
470
    match peek_next_non_whitespace_token(tokens) {
79✔
471
        Some(non_whitespace) => match &non_whitespace.content {
79✔
472
            TokenContent::Comment(_) => unreachable!(),
473
            TokenContent::Whitespace => unreachable!(),
474
            TokenContent::Identifier(identifier) => {
59✔
475
                pop_next_non_whitespace_token(tokens);
59✔
476
                Some((identifier.clone(), non_whitespace.location))
59✔
477
            }
478
            TokenContent::Assign => todo!(),
479
            TokenContent::LeftParenthesis => todo!(),
480
            TokenContent::RightParenthesis => None,
20✔
481
            TokenContent::LeftBracket => todo!(),
482
            TokenContent::RightBracket => todo!(),
483
            TokenContent::LeftBrace => todo!(),
484
            TokenContent::RightBrace => todo!(),
485
            TokenContent::Dot => todo!(),
486
            TokenContent::Colon => todo!(),
487
            TokenContent::Quotes(_) => todo!(),
488
            TokenContent::FatArrow => todo!(),
489
            TokenContent::Comma => None,
×
490
            TokenContent::EndOfFile => None,
×
491
        },
492
        None => None,
×
493
    }
494
}
495

496
fn try_skip_comma(tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>) -> bool {
42✔
497
    match peek_next_non_whitespace_token(tokens) {
42✔
498
        Some(non_whitespace) => match &non_whitespace.content {
42✔
499
            TokenContent::Comment(_) => unreachable!(),
500
            TokenContent::Whitespace => unreachable!(),
501
            TokenContent::Identifier(_identifier) => false,
1✔
502
            TokenContent::Assign => todo!(),
503
            TokenContent::LeftParenthesis => todo!(),
504
            TokenContent::RightParenthesis => false,
24✔
505
            TokenContent::LeftBracket => todo!(),
506
            TokenContent::RightBracket => todo!(),
507
            TokenContent::LeftBrace => todo!(),
508
            TokenContent::RightBrace => todo!(),
509
            TokenContent::Dot => todo!(),
510
            TokenContent::Colon => todo!(),
511
            TokenContent::Quotes(_) => todo!(),
512
            TokenContent::FatArrow => todo!(),
513
            TokenContent::Comma => {
514
                pop_next_non_whitespace_token(tokens);
17✔
515
                true
17✔
516
            }
517
            TokenContent::EndOfFile => false,
×
518
        },
519
        None => false,
×
520
    }
521
}
522

523
fn try_skip_colon(tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>) -> bool {
43✔
524
    match peek_next_non_whitespace_token(tokens) {
43✔
525
        Some(non_whitespace) => match &non_whitespace.content {
43✔
526
            TokenContent::Comment(_) => unreachable!(),
527
            TokenContent::Whitespace => unreachable!(),
528
            TokenContent::Identifier(_identifier) => false,
1✔
529
            TokenContent::Assign => todo!(),
530
            TokenContent::LeftParenthesis => todo!(),
531
            TokenContent::RightParenthesis => false,
17✔
532
            TokenContent::LeftBracket => todo!(),
533
            TokenContent::RightBracket => todo!(),
534
            TokenContent::LeftBrace => todo!(),
535
            TokenContent::RightBrace => todo!(),
536
            TokenContent::Dot => todo!(),
537
            TokenContent::Colon => {
538
                pop_next_non_whitespace_token(tokens);
16✔
539
                true
16✔
540
            }
541
            TokenContent::Quotes(_) => todo!(),
542
            TokenContent::FatArrow => todo!(),
543
            TokenContent::Comma => false,
9✔
544
            TokenContent::EndOfFile => false,
×
545
        },
546
        None => false,
×
547
    }
548
}
549

550
fn parse_lambda<'t>(
46✔
551
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
552
    local_namespace: &NamespaceId,
553
) -> ParserResult<ast::Expression> {
554
    let mut parameters = Vec::new();
46✔
555
    while let Some((parameter_name, parameter_location)) = try_pop_identifier(tokens) {
106✔
556
        let namespaced_name = Name::new(*local_namespace, parameter_name);
×
557
        let mut type_annotation = None;
×
558
        if try_skip_colon(tokens) {
×
559
            type_annotation = Some(parse_expression(tokens, local_namespace)?);
32✔
560
        }
561
        parameters.push(LambdaParameter::new(
42✔
562
            namespaced_name,
42✔
563
            parameter_location,
42✔
564
            type_annotation,
42✔
565
        ));
566
        if !try_skip_comma(tokens) {
42✔
567
            break;
25✔
568
        }
569
    }
570
    if !try_skip_right_parenthesis(tokens) {
45✔
571
        let next_token = peek_next_non_whitespace_token(tokens).unwrap();
1✔
572
        return Err(ParserError::new(
1✔
573
            "Expected comma or right parenthesis in lambda parameter list.".to_string(),
1✔
574
            next_token.location,
1✔
575
        ));
576
    }
577
    expect_fat_arrow(tokens);
44✔
578
    let body = parse_expression(tokens, local_namespace)?;
88✔
579
    Ok(ast::Expression::Lambda {
×
580
        parameters,
×
581
        body: Box::new(body),
×
582
    })
583
}
584

585
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
586
pub struct ParserOutput {
587
    pub entry_point: Option<ast::Expression>,
588
    pub errors: Vec<CompilerError>,
589
}
590

591
impl ParserOutput {
592
    pub fn new(entry_point: Option<ast::Expression>, errors: Vec<CompilerError>) -> ParserOutput {
31✔
593
        ParserOutput {
594
            entry_point,
595
            errors,
596
        }
597
    }
598
}
599

600
pub fn parse_expression_tolerantly<'t>(
25✔
601
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
602
    local_namespace: &NamespaceId,
603
) -> ParserOutput {
604
    let mut errors = Vec::new();
25✔
605
    let entry_point_result = parse_expression(tokens, local_namespace);
25✔
606
    match entry_point_result {
25✔
607
        Ok(entry_point) => ParserOutput::new(Some(entry_point), errors),
18✔
608
        Err(error) => {
7✔
609
            errors.push(CompilerError::new(
7✔
610
                format!("Parser error: {}", &error),
7✔
611
                error.location,
7✔
612
            ));
613
            ParserOutput::new(None, errors)
7✔
614
        }
615
    }
616
}
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