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

TyRoXx / NonlocalityOS / 14953868655

11 May 2025 08:12AM UTC coverage: 72.045% (+0.3%) from 71.79%
14953868655

Pull #245

github

web-flow
Merge 3db9fcca6 into 8fd7aed23
Pull Request #245: 244 multiple parameters actually work

64 of 73 new or added lines in 4 files covered. (87.67%)

1 existing line in 1 file now uncovered.

3139 of 4357 relevant lines covered (72.04%)

2249.32 hits per line

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

73.29
/lambda_compiler/src/parsing.rs
1
use crate::{
2
    ast,
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 {
3✔
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 {
3✔
22
        write!(f, "{}", &self.message)
3✔
23
    }
24
}
25

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

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

36
    token
207✔
37
}
38

39
pub fn peek_next_non_whitespace_token<'t>(
587✔
40
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
41
) -> Option<&'t Token> {
42
    loop {
×
43
        let next = tokens.peek();
693✔
44
        match next {
693✔
45
            Some(token) => match token.content {
693✔
46
                TokenContent::Whitespace => {
×
47
                    tokens.next();
106✔
48
                    continue;
106✔
49
                }
50
                TokenContent::Identifier(_)
×
51
                | TokenContent::Assign
×
52
                | TokenContent::LeftParenthesis
×
53
                | TokenContent::RightParenthesis
×
54
                | TokenContent::LeftBracket
×
55
                | TokenContent::RightBracket
×
56
                | TokenContent::Dot
×
57
                | TokenContent::Quotes(_)
×
58
                | TokenContent::FatArrow
×
NEW
59
                | TokenContent::Comma
×
60
                | TokenContent::EndOfFile => return Some(token),
587✔
61
            },
UNCOV
62
            None => return None,
×
63
        }
64
    }
65
}
66

67
fn expect_right_parenthesis(tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>) {
3✔
68
    match pop_next_non_whitespace_token(tokens) {
3✔
69
        Some(non_whitespace) => match &non_whitespace.content {
3✔
70
            TokenContent::Whitespace => todo!(),
71
            TokenContent::Identifier(_) => todo!(),
72
            TokenContent::Assign => todo!(),
73
            TokenContent::LeftParenthesis => todo!(),
74
            TokenContent::RightParenthesis => {}
3✔
75
            TokenContent::LeftBracket => todo!(),
76
            TokenContent::RightBracket => todo!(),
77
            TokenContent::Dot => todo!(),
78
            TokenContent::Quotes(_) => todo!(),
79
            TokenContent::FatArrow => todo!(),
80
            TokenContent::Comma => todo!(),
81
            TokenContent::EndOfFile => todo!(),
82
        },
83
        None => todo!(),
84
    }
85
}
86

87
fn try_skip_right_parenthesis(
22✔
88
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
89
) -> bool {
90
    match peek_next_non_whitespace_token(tokens) {
22✔
91
        Some(non_whitespace) => match &non_whitespace.content {
22✔
92
            TokenContent::Whitespace => todo!(),
93
            TokenContent::Identifier(_) => false,
1✔
94
            TokenContent::Assign => todo!(),
95
            TokenContent::LeftParenthesis => todo!(),
96
            TokenContent::RightParenthesis => {
97
                pop_next_non_whitespace_token(tokens);
21✔
98
                true
21✔
99
            }
100
            TokenContent::LeftBracket => todo!(),
101
            TokenContent::RightBracket => todo!(),
102
            TokenContent::Dot => todo!(),
103
            TokenContent::Quotes(_) => todo!(),
104
            TokenContent::FatArrow => todo!(),
105
            TokenContent::Comma => todo!(),
106
            TokenContent::EndOfFile => todo!(),
107
        },
108
        None => todo!(),
109
    }
110
}
111

112
fn expect_fat_arrow(tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>) {
21✔
113
    match pop_next_non_whitespace_token(tokens) {
21✔
114
        Some(non_whitespace) => match &non_whitespace.content {
21✔
115
            TokenContent::Whitespace => todo!(),
116
            TokenContent::Identifier(_identifier) => todo!(),
117
            TokenContent::Assign => todo!(),
118
            TokenContent::LeftParenthesis => todo!(),
119
            TokenContent::RightParenthesis => todo!(),
120
            TokenContent::LeftBracket => todo!(),
121
            TokenContent::RightBracket => todo!(),
122
            TokenContent::Dot => todo!(),
123
            TokenContent::Quotes(_) => todo!(),
124
            TokenContent::FatArrow => {}
21✔
125
            TokenContent::Comma => todo!(),
126
            TokenContent::EndOfFile => todo!(),
127
        },
128
        None => todo!(),
129
    }
130
}
131

132
fn expect_comma(tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>) {
21✔
133
    match pop_next_non_whitespace_token(tokens) {
21✔
134
        Some(non_whitespace) => match &non_whitespace.content {
21✔
135
            TokenContent::Whitespace => todo!(),
136
            TokenContent::Identifier(_) => todo!(),
137
            TokenContent::Assign => todo!(),
138
            TokenContent::LeftParenthesis => todo!(),
139
            TokenContent::RightParenthesis => todo!(),
140
            TokenContent::LeftBracket => todo!(),
141
            TokenContent::RightBracket => todo!(),
142
            TokenContent::Dot => todo!(),
143
            TokenContent::Quotes(_) => todo!(),
144
            TokenContent::FatArrow => todo!(),
145
            TokenContent::Comma => {}
21✔
146
            TokenContent::EndOfFile => todo!(),
147
        },
148
        None => todo!(),
149
    }
150
}
151

152
fn skip_right_bracket(tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>) -> bool {
104✔
153
    let maybe_right_bracket = peek_next_non_whitespace_token(tokens);
104✔
154
    match maybe_right_bracket {
104✔
155
        Some(token) => match &token.content {
104✔
156
            TokenContent::Whitespace => unreachable!(),
157
            TokenContent::Identifier(_) => false,
43✔
158
            TokenContent::Assign => false,
×
159
            TokenContent::LeftParenthesis => false,
×
160
            TokenContent::RightParenthesis => false,
×
161
            TokenContent::LeftBracket => false,
2✔
162
            TokenContent::RightBracket => {
163
                tokens.next();
29✔
164
                true
29✔
165
            }
166
            TokenContent::Dot => false,
×
167
            TokenContent::Quotes(_) => false,
9✔
168
            TokenContent::FatArrow => false,
×
169
            TokenContent::Comma => false,
21✔
170
            TokenContent::EndOfFile => todo!(),
171
        },
172
        None => false,
×
173
    }
174
}
175

176
fn parse_tree_construction(
29✔
177
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
178
    local_namespace: &NamespaceId,
179
) -> ParserResult<ast::Expression> {
180
    let mut elements = Vec::new();
29✔
181
    loop {
182
        if skip_right_bracket(tokens) {
61✔
183
            break;
18✔
184
        }
185
        if !elements.is_empty() {
64✔
186
            expect_comma(tokens);
21✔
187
        }
188
        if skip_right_bracket(tokens) {
189
            break;
11✔
190
        }
191
        let element = parse_expression(tokens, local_namespace)?;
32✔
192
        elements.push(element);
193
    }
194
    Ok(ast::Expression::ConstructTree(elements))
29✔
195
}
196

197
fn parse_expression_start<'t>(
102✔
198
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
199
    local_namespace: &NamespaceId,
200
) -> ParserResult<ast::Expression> {
201
    match peek_next_non_whitespace_token(tokens) {
102✔
202
        Some(non_whitespace) => match &non_whitespace.content {
102✔
203
            TokenContent::Whitespace => todo!(),
204
            TokenContent::Identifier(identifier) => {
43✔
205
                pop_next_non_whitespace_token(tokens);
43✔
206
                Ok(ast::Expression::Identifier(Name::new(
43✔
207
                    *local_namespace,
43✔
208
                    identifier.clone(),
43✔
209
                )))
210
            }
211
            TokenContent::Assign => todo!(),
NEW
212
            TokenContent::LeftParenthesis => {
×
213
                pop_next_non_whitespace_token(tokens);
22✔
214
                parse_lambda(tokens, local_namespace)
22✔
215
            }
216
            TokenContent::RightParenthesis => Err(ParserError::new(
1✔
217
                "Expected expression, found right parenthesis.".to_string(),
1✔
218
                non_whitespace.location,
1✔
219
            )),
NEW
220
            TokenContent::LeftBracket => {
×
221
                pop_next_non_whitespace_token(tokens);
29✔
222
                parse_tree_construction(tokens, local_namespace)
29✔
223
            }
224
            TokenContent::RightBracket => Err(ParserError::new(
×
225
                "Expected expression, found right bracket.".to_string(),
×
NEW
226
                non_whitespace.location,
×
227
            )),
228
            TokenContent::Dot => todo!(),
229
            TokenContent::Quotes(content) => {
6✔
230
                pop_next_non_whitespace_token(tokens);
6✔
231
                Ok(ast::Expression::StringLiteral(content.clone()))
6✔
232
            }
233
            TokenContent::FatArrow => todo!(),
234
            TokenContent::Comma => todo!(),
235
            TokenContent::EndOfFile => Err(ParserError::new(
1✔
236
                "Expected expression, got end of file.".to_string(),
1✔
237
                non_whitespace.location,
1✔
238
            )),
239
        },
240
        None => todo!(),
241
    }
242
}
243

244
pub fn parse_expression<'t>(
102✔
245
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
246
    local_namespace: &NamespaceId,
247
) -> ParserResult<ast::Expression> {
248
    let start = parse_expression_start(tokens, local_namespace)?;
204✔
249
    match peek_next_non_whitespace_token(tokens) {
×
250
        Some(more) => match &more.content {
98✔
251
            TokenContent::Whitespace => unreachable!(),
252
            TokenContent::Identifier(_) => Ok(start),
×
253
            TokenContent::Assign => Ok(start),
×
254
            TokenContent::LeftParenthesis => {
×
255
                tokens.next();
4✔
256
                let argument = parse_expression(tokens, local_namespace)?;
8✔
257
                expect_right_parenthesis(tokens);
×
258
                Ok(ast::Expression::Apply {
×
259
                    callee: Box::new(start),
×
260
                    argument: Box::new(argument),
×
261
                })
262
            }
263
            TokenContent::RightParenthesis => Ok(start),
5✔
264
            TokenContent::LeftBracket => todo!(),
265
            TokenContent::RightBracket => Ok(start),
11✔
266
            TokenContent::Dot => todo!(),
267
            TokenContent::Quotes(_) => todo!(),
268
            TokenContent::FatArrow => todo!(),
269
            TokenContent::Comma => Ok(start),
21✔
270
            TokenContent::EndOfFile => Ok(start),
57✔
271
        },
272
        None => todo!(),
273
    }
274
}
275

276
fn try_pop_identifier(
31✔
277
    tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>,
278
) -> Option<String> {
279
    match peek_next_non_whitespace_token(tokens) {
31✔
280
        Some(non_whitespace) => match &non_whitespace.content {
31✔
281
            TokenContent::Whitespace => todo!(),
282
            TokenContent::Identifier(identifier) => {
22✔
283
                pop_next_non_whitespace_token(tokens);
22✔
284
                Some(identifier.clone())
22✔
285
            }
286
            TokenContent::Assign => todo!(),
287
            TokenContent::LeftParenthesis => todo!(),
288
            TokenContent::RightParenthesis => None,
9✔
289
            TokenContent::LeftBracket => todo!(),
290
            TokenContent::RightBracket => todo!(),
291
            TokenContent::Dot => todo!(),
292
            TokenContent::Quotes(_) => todo!(),
293
            TokenContent::FatArrow => todo!(),
NEW
294
            TokenContent::Comma => None,
×
NEW
295
            TokenContent::EndOfFile => None,
×
296
        },
297
        None => None,
×
298
    }
299
}
300

301
fn try_skip_comma(tokens: &mut std::iter::Peekable<std::slice::Iter<'_, Token>>) -> bool {
22✔
302
    match peek_next_non_whitespace_token(tokens) {
22✔
303
        Some(non_whitespace) => match &non_whitespace.content {
22✔
304
            TokenContent::Whitespace => todo!(),
305
            TokenContent::Identifier(_identifier) => false,
1✔
306
            TokenContent::Assign => todo!(),
307
            TokenContent::LeftParenthesis => todo!(),
308
            TokenContent::RightParenthesis => false,
12✔
309
            TokenContent::LeftBracket => todo!(),
310
            TokenContent::RightBracket => todo!(),
311
            TokenContent::Dot => todo!(),
312
            TokenContent::Quotes(_) => todo!(),
313
            TokenContent::FatArrow => todo!(),
314
            TokenContent::Comma => {
315
                pop_next_non_whitespace_token(tokens);
9✔
316
                true
9✔
317
            }
NEW
318
            TokenContent::EndOfFile => false,
×
319
        },
NEW
320
        None => false,
×
321
    }
322
}
323

324
fn parse_lambda<'t>(
22✔
325
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
326
    local_namespace: &NamespaceId,
327
) -> ParserResult<ast::Expression> {
328
    let mut parameter_names = Vec::new();
22✔
329
    while let Some(name) = try_pop_identifier(tokens) {
53✔
330
        let parameter_name: Name = Name::new(*local_namespace, name);
×
331
        parameter_names.push(parameter_name);
×
NEW
332
        if !try_skip_comma(tokens) {
×
333
            break;
13✔
334
        }
335
    }
336
    if !try_skip_right_parenthesis(tokens) {
22✔
337
        let next_token = peek_next_non_whitespace_token(tokens).unwrap();
1✔
338
        return Err(ParserError::new(
1✔
339
            "Expected comma or right parenthesis in lambda parameter list.".to_string(),
1✔
340
            next_token.location,
1✔
341
        ));
342
    }
343
    expect_fat_arrow(tokens);
21✔
344
    let body = parse_expression(tokens, local_namespace)?;
42✔
345
    Ok(ast::Expression::Lambda {
×
346
        parameter_names,
×
347
        body: Box::new(body),
×
348
    })
349
}
350

351
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
352
pub struct ParserOutput {
353
    pub entry_point: Option<ast::Expression>,
354
    pub errors: Vec<CompilerError>,
355
}
356

357
impl ParserOutput {
358
    pub fn new(entry_point: Option<ast::Expression>, errors: Vec<CompilerError>) -> ParserOutput {
14✔
359
        ParserOutput {
360
            entry_point,
361
            errors,
362
        }
363
    }
364
}
365

366
pub fn parse_expression_tolerantly<'t>(
12✔
367
    tokens: &mut std::iter::Peekable<std::slice::Iter<'t, Token>>,
368
    local_namespace: &NamespaceId,
369
) -> ParserOutput {
370
    let mut errors = Vec::new();
12✔
371
    let entry_point_result = parse_expression(tokens, local_namespace);
12✔
372
    match entry_point_result {
12✔
373
        Ok(entry_point) => ParserOutput::new(Some(entry_point), errors),
9✔
374
        Err(error) => {
3✔
375
            errors.push(CompilerError::new(
3✔
376
                format!("Parser error: {}", &error),
3✔
377
                error.location,
3✔
378
            ));
379
            ParserOutput::new(None, errors)
3✔
380
        }
381
    }
382
}
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