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

TyRoXx / NonlocalityOS / 15320443667

29 May 2025 09:10AM UTC coverage: 72.844% (+0.09%) from 72.751%
15320443667

Pull #262

github

web-flow
Merge 640ef9b92 into b29009a9e
Pull Request #262: GH-257: support local variables

54 of 73 new or added lines in 4 files covered. (73.97%)

16 existing lines in 1 file now uncovered.

3345 of 4592 relevant lines covered (72.84%)

2219.36 hits per line

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

92.45
/lambda_compiler/src/type_checking.rs
1
use crate::{
2
    ast::{self, LambdaParameter},
3
    compilation::{CompilerOutput, SourceLocation},
4
};
5
use astraea::{storage::StoreError, tree::Tree};
6
use lambda::{
7
    expressions::{DeepExpression, Expression},
8
    name::Name,
9
};
10
use std::{collections::BTreeMap, sync::Arc};
11

12
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
13
pub enum Type {
14
    Any,
15
    String,
16
    TreeWithKnownChildTypes(Vec<Type>),
17
    Function {
18
        parameters: Vec<Type>,
19
        return_type: Box<Type>,
20
    },
21
}
22

23
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
24
pub struct TypedExpression {
25
    pub expression: DeepExpression,
26
    pub type_: Type,
27
}
28

29
impl TypedExpression {
30
    pub fn new(expression: DeepExpression, type_: Type) -> Self {
116✔
31
        Self { expression, type_ }
32
    }
33
}
34

35
fn check_tree_construction_or_argument_list(
16✔
36
    arguments: &[ast::Expression],
37
    environment_builder: &mut EnvironmentBuilder,
38
) -> Result<CompilerOutput, StoreError> {
39
    let mut errors = Vec::new();
16✔
40
    let mut checked_arguments = Vec::new();
16✔
41
    let mut argument_types = Vec::new();
16✔
42
    for argument in arguments {
56✔
43
        let output = check_types(argument, environment_builder)?;
40✔
44
        errors.extend(output.errors);
45
        if let Some(checked) = output.entry_point {
20✔
46
            checked_arguments.push(Arc::new(checked.expression));
47
            argument_types.push(checked.type_);
48
        } else {
49
            return Ok(CompilerOutput::new(None, errors));
×
50
        }
51
    }
52
    Ok(CompilerOutput {
16✔
53
        entry_point: Some(TypedExpression::new(
16✔
54
            lambda::expressions::DeepExpression(lambda::expressions::Expression::ConstructTree(
16✔
55
                checked_arguments,
16✔
56
            )),
57
            Type::TreeWithKnownChildTypes(argument_types),
16✔
58
        )),
59
        errors,
16✔
60
    })
61
}
62

63
pub struct LocalVariable {
64
    parameter_index: u16,
65
    type_: Type,
66
}
67

68
impl LocalVariable {
69
    pub fn new(parameter_index: u16, type_: Type) -> Self {
24✔
70
        Self {
71
            parameter_index,
72
            type_,
73
        }
74
    }
75
}
76

77
pub struct LambdaScope {
78
    names: BTreeMap<Name, LocalVariable>,
79
    captures: Vec<TypedExpression>,
80
}
81

82
impl LambdaScope {
83
    pub fn new(parameters: &[TypeCheckedLambdaParameter]) -> Self {
34✔
84
        let mut names = BTreeMap::new();
34✔
85
        for (index, parameter) in parameters.iter().enumerate() {
58✔
86
            let checked_index: u16 = index.try_into().expect("TODO handle too many parameters");
87
            names.insert(
88
                parameter.name.clone(),
89
                LocalVariable::new(checked_index, parameter.type_.clone()),
90
            );
91
        }
92
        Self {
93
            names,
94
            captures: Vec::new(),
34✔
95
        }
96
    }
97

98
    pub fn find_parameter_index(&self, parameter_name: &Name) -> Option<(u16, Type)> {
31✔
99
        self.names
31✔
100
            .get(parameter_name)
31✔
101
            .map(|variable| (variable.parameter_index, variable.type_.clone()))
53✔
102
    }
103

104
    pub fn capture(&mut self, expression: TypedExpression) -> CompilerOutput {
9✔
105
        let index = self
9✔
106
            .captures
9✔
107
            .len()
108
            .try_into()
109
            .expect("TODO handle too many captures");
110
        self.captures.push(expression);
9✔
111
        CompilerOutput::new(
112
            Some(TypedExpression::new(
9✔
113
                lambda::expressions::DeepExpression(
9✔
114
                    lambda::expressions::Expression::make_get_child(
9✔
115
                        Arc::new(DeepExpression(
9✔
116
                            lambda::expressions::Expression::make_environment(),
9✔
117
                        )),
118
                        index,
9✔
119
                    ),
120
                ),
121
                self.captures.last().unwrap().type_.clone(),
9✔
122
            )),
123
            Vec::new(),
9✔
124
        )
125
    }
126

127
    pub fn leave(self) -> Vec<TypedExpression> {
34✔
128
        self.captures
34✔
129
    }
130
}
131

132
pub struct EnvironmentBuilder {
133
    lambda_layers: Vec<LambdaScope>,
134
}
135

136
impl Default for EnvironmentBuilder {
137
    fn default() -> Self {
×
138
        Self::new()
×
139
    }
140
}
141

142
impl EnvironmentBuilder {
143
    pub fn new() -> Self {
47✔
144
        Self {
145
            lambda_layers: Vec::new(),
47✔
146
        }
147
    }
148

149
    pub fn is_empty(&self) -> bool {
26✔
150
        self.lambda_layers.is_empty()
26✔
151
    }
152

153
    pub fn enter_lambda_body(&mut self, parameters: &[TypeCheckedLambdaParameter]) {
34✔
154
        self.lambda_layers.push(LambdaScope::new(parameters));
34✔
155
    }
156

157
    pub fn leave_lambda_body(&mut self) -> Vec<TypedExpression> {
34✔
158
        let top_scope = self.lambda_layers.pop().unwrap();
34✔
159
        top_scope.leave()
34✔
160
    }
161

162
    pub fn read(&mut self, identifier: &Name, location: &SourceLocation) -> CompilerOutput {
24✔
163
        Self::read_down(&mut self.lambda_layers, identifier, location)
24✔
164
    }
165

166
    fn read_down(
33✔
167
        layers: &mut [LambdaScope],
168
        identifier: &Name,
169
        location: &SourceLocation,
170
    ) -> CompilerOutput {
171
        let layer_count = layers.len();
33✔
172
        if let Some(last) = layers.last_mut() {
64✔
173
            if let Some((parameter_index, parameter_type)) = last.find_parameter_index(identifier) {
22✔
174
                return CompilerOutput::new(
175
                    Some(TypedExpression::new(
176
                        lambda::expressions::DeepExpression(
177
                            lambda::expressions::Expression::make_get_child(
178
                                Arc::new(lambda::expressions::DeepExpression(
179
                                    lambda::expressions::Expression::make_argument(),
180
                                )),
181
                                parameter_index,
182
                            ),
183
                        ),
184
                        parameter_type,
185
                    )),
186
                    Vec::new(),
187
                );
188
            } else if layer_count > 1 {
9✔
189
                let result = Self::read_down(&mut layers[..layer_count - 1], identifier, location);
9✔
190
                if result.entry_point.is_some() {
9✔
191
                    return layers
9✔
192
                        .last_mut()
9✔
193
                        .unwrap()
9✔
194
                        .capture(result.entry_point.unwrap());
9✔
195
                }
196
                return result;
×
197
            }
198
        }
199
        CompilerOutput::new(
200
            None,
2✔
201
            vec![crate::compilation::CompilerError::new(
2✔
202
                format!("Identifier {identifier} not found"),
2✔
203
                *location,
2✔
204
            )],
205
        )
206
    }
207
}
208

209
pub fn evaluate_type_at_compile_time(_expression: &DeepExpression) -> Type {
×
210
    todo!()
211
}
212

213
pub struct TypeCheckedLambdaParameter {
214
    pub name: Name,
215
    pub source_location: SourceLocation,
216
    pub type_: Type,
217
}
218

219
impl TypeCheckedLambdaParameter {
220
    pub fn new(name: Name, source_location: SourceLocation, type_: Type) -> Self {
3✔
221
        Self {
222
            name,
223
            source_location,
224
            type_,
225
        }
226
    }
227
}
228

229
pub fn check_lambda_parameters(
31✔
230
    parameters: &[LambdaParameter],
231
) -> Result<Vec<TypeCheckedLambdaParameter>, StoreError> {
232
    let mut checked_parameters = Vec::new();
31✔
233
    for parameter in parameters {
73✔
234
        let mut environment_builder = EnvironmentBuilder::new();
21✔
235
        let parameter_type: Type = match &parameter.type_annotation {
42✔
236
            Some(type_annotation) => {
×
237
                let checked_type = check_types(type_annotation, &mut environment_builder)?;
×
238
                assert!(environment_builder.is_empty());
239
                if let Some(checked) = checked_type.entry_point {
×
240
                    evaluate_type_at_compile_time(&checked.expression)
241
                } else {
242
                    todo!()
243
                }
244
            }
245
            None => {
246
                // If no type annotation is provided, we assume the type is `Any`.
247
                Type::Any
21✔
248
            }
249
        };
250
        checked_parameters.push(TypeCheckedLambdaParameter {
251
            name: parameter.name.clone(),
252
            source_location: parameter.source_location,
253
            type_: parameter_type,
254
        });
255
    }
256
    Ok(checked_parameters)
31✔
257
}
258

259
pub fn check_lambda(
31✔
260
    parameters: &[LambdaParameter],
261
    body: &ast::Expression,
262
    environment_builder: &mut EnvironmentBuilder,
263
) -> Result<CompilerOutput, StoreError> {
264
    let checked_parameters = check_lambda_parameters(parameters)?;
62✔
265
    environment_builder.enter_lambda_body(&checked_parameters[..]);
266
    let body_result = check_types(body, environment_builder);
267
    // TODO: use RAII or something?
268
    let environment = environment_builder.leave_lambda_body();
269
    let environment_expressions = environment
270
        .into_iter()
271
        .map(|typed_expression| Arc::new(typed_expression.expression))
8✔
272
        .collect();
273
    let body_output = body_result?;
31✔
274
    match body_output.entry_point {
275
        Some(body_checked) => Ok(CompilerOutput {
30✔
276
            entry_point: Some(TypedExpression::new(
30✔
277
                lambda::expressions::DeepExpression(lambda::expressions::Expression::Lambda {
30✔
278
                    environment: Arc::new(DeepExpression(Expression::make_construct_tree(
30✔
279
                        environment_expressions,
30✔
280
                    ))),
281
                    body: Arc::new(body_checked.expression),
30✔
282
                }),
283
                Type::Function {
30✔
284
                    parameters: checked_parameters
30✔
285
                        .into_iter()
30✔
286
                        .map(|parameter| parameter.type_)
50✔
287
                        .collect(),
30✔
288
                    return_type: Box::new(body_checked.type_),
30✔
289
                },
290
            )),
291
            errors: body_output.errors,
30✔
292
        }),
293
        None => Ok(CompilerOutput::new(None, body_output.errors)),
1✔
294
    }
295
}
296

NEW
297
pub fn check_braces(
×
298
    expression: &[ast::Expression],
299
    environment_builder: &mut EnvironmentBuilder,
300
) -> Result<CompilerOutput, StoreError> {
NEW
301
    if expression.len() != 1 {
×
302
        todo!()
303
    }
NEW
304
    check_types(&expression[0], environment_builder)
×
305
}
306

307
pub fn check_let(
3✔
308
    name: &Name,
309
    location: &SourceLocation,
310
    value: &ast::Expression,
311
    body: &ast::Expression,
312
    environment_builder: &mut EnvironmentBuilder,
313
) -> Result<CompilerOutput, StoreError> {
314
    let value_checked = check_types(value, environment_builder)?;
6✔
315
    if !value_checked.errors.is_empty() {
316
        todo!()
317
    }
318
    let value_checked_unwrapped = value_checked.entry_point.unwrap();
3✔
319
    let checked_parameters = vec![TypeCheckedLambdaParameter::new(
3✔
320
        name.clone(),
3✔
321
        *location,
3✔
322
        value_checked_unwrapped.type_.clone(),
3✔
323
    )];
324
    environment_builder.enter_lambda_body(&checked_parameters[..]);
3✔
325
    let body_result = check_types(body, environment_builder);
3✔
326
    // TODO: use RAII or something?
327
    let environment = environment_builder.leave_lambda_body();
3✔
328
    let environment_expressions = environment
3✔
329
        .into_iter()
330
        .map(|typed_expression| Arc::new(typed_expression.expression))
4✔
331
        .collect();
332
    let body_output = body_result?;
6✔
333
    match body_output.entry_point {
334
        Some(body_checked) => Ok(CompilerOutput {
3✔
335
            entry_point: Some(TypedExpression::new(
3✔
336
                lambda::expressions::DeepExpression(lambda::expressions::Expression::make_apply(
3✔
337
                    Arc::new(lambda::expressions::DeepExpression(
3✔
338
                        lambda::expressions::Expression::Lambda {
3✔
339
                            environment: Arc::new(DeepExpression(Expression::make_construct_tree(
3✔
340
                                environment_expressions,
3✔
341
                            ))),
342
                            body: Arc::new(body_checked.expression),
3✔
343
                        },
344
                    )),
345
                    Arc::new(lambda::expressions::DeepExpression(
3✔
346
                        lambda::expressions::Expression::make_construct_tree(vec![Arc::new(
3✔
347
                            value_checked_unwrapped.expression,
3✔
348
                        )]),
349
                    )),
350
                )),
351
                body_checked.type_,
3✔
352
            )),
353
            errors: body_output.errors,
3✔
354
        }),
NEW
355
        None => Ok(CompilerOutput::new(None, body_output.errors)),
×
356
    }
357
}
358

359
pub fn check_types(
96✔
360
    syntax_tree: &ast::Expression,
361
    environment_builder: &mut EnvironmentBuilder,
362
) -> Result<CompilerOutput, StoreError> {
363
    match syntax_tree {
96✔
364
        ast::Expression::Identifier(name, location) => Ok(environment_builder.read(name, location)),
24✔
365
        ast::Expression::StringLiteral(value) => Ok(CompilerOutput::new(
14✔
366
            Some(TypedExpression::new(
14✔
367
                lambda::expressions::DeepExpression(lambda::expressions::Expression::Literal(
14✔
368
                    Tree::from_string(value).unwrap(/*TODO*/),
14✔
369
                )),
370
                Type::String,
14✔
371
            )),
372
            Vec::new(),
14✔
373
        )),
374
        ast::Expression::Apply { callee, arguments } => {
6✔
375
            let callee_output = check_types(callee, environment_builder)?;
12✔
376
            let argument_output = if arguments.len() == 1 {
6✔
377
                // For N=1 we don't need an indirection.
378
                check_types(&arguments[0], environment_builder)?
3✔
379
            } else {
380
                check_tree_construction_or_argument_list(&arguments[..], environment_builder)?
3✔
381
            };
382
            let errors = callee_output
383
                .errors
384
                .into_iter()
385
                .chain(argument_output.errors)
386
                .collect();
387
            match (callee_output.entry_point, argument_output.entry_point) {
388
                (Some(callee_checked), Some(argument_checked)) => {
5✔
389
                    let return_type = match &callee_checked.type_ {
9✔
390
                        Type::Function { return_type, .. } => return_type.as_ref().clone(),
391
                        _ => {
392
                            return Ok(CompilerOutput::new(
1✔
393
                                None,
1✔
394
                                vec![crate::compilation::CompilerError::new(
1✔
395
                                    "Callee is not a function".to_string(),
1✔
396
                                    callee.source_location(),
1✔
397
                                )],
398
                            ))
399
                        }
400
                    };
401
                    // TODO: check argument types against callee parameter types
402
                    Ok(CompilerOutput {
403
                        entry_point: Some(TypedExpression::new(
404
                            lambda::expressions::DeepExpression(
405
                                lambda::expressions::Expression::Apply {
406
                                    callee: Arc::new(callee_checked.expression),
407
                                    argument: Arc::new(argument_checked.expression),
408
                                },
409
                            ),
410
                            return_type,
411
                        )),
412
                        errors,
413
                    })
414
                }
415
                (None, _) | (_, None) => Ok(CompilerOutput::new(None, errors)),
1✔
416
            }
417
        }
418
        ast::Expression::Lambda { parameters, body } => {
31✔
419
            check_lambda(&parameters[..], body, environment_builder)
31✔
420
        }
421
        ast::Expression::ConstructTree(arguments) => {
13✔
422
            check_tree_construction_or_argument_list(&arguments[..], environment_builder)
13✔
423
        }
424
        ast::Expression::Braces(expression) => check_types(expression, environment_builder),
5✔
425
        ast::Expression::Let {
426
            name,
3✔
427
            location,
3✔
428
            value,
3✔
429
            body,
3✔
430
        } => check_let(name, location, value, body, environment_builder),
3✔
431
    }
432
}
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