rue_compiler/compile/expr/
lambda.rs

1use log::debug;
2use rue_ast::{AstLambdaExpr, AstNode};
3use rue_diagnostic::DiagnosticKind;
4use rue_hir::{FunctionSymbol, Hir, ParameterSymbol, Scope, Symbol, Value};
5use rue_types::{FunctionType, Type, TypeId, Union};
6
7use crate::{Compiler, compile_expr, compile_type};
8
9pub fn compile_lambda_expr(
10    ctx: &mut Compiler,
11    expr: &AstLambdaExpr,
12    expected_type: Option<TypeId>,
13) -> Value {
14    let expected_functions = if let Some(ty) = expected_type {
15        rue_types::extract_functions(ctx.types_mut(), ty)
16    } else {
17        vec![]
18    };
19
20    let scope = ctx.alloc_scope(Scope::new());
21
22    ctx.push_scope(scope);
23
24    let mut parameters = Vec::new();
25    let mut params = Vec::new();
26    let mut nil_terminated = true;
27
28    let len = expr.parameters().count();
29
30    for (i, param) in expr.parameters().enumerate() {
31        let is_spread = if let Some(spread) = param.spread() {
32            if i == len - 1 {
33                true
34            } else {
35                ctx.diagnostic(&spread, DiagnosticKind::NonFinalSpread);
36                false
37            }
38        } else {
39            false
40        };
41
42        if is_spread {
43            nil_terminated = false;
44        }
45
46        let ty = if let Some(ty) = param.ty() {
47            compile_type(ctx, &ty)
48        } else if !expected_functions.is_empty()
49            && let params = expected_functions
50                .iter()
51                .filter_map(|function| function.params.get(i).copied())
52                .collect::<Vec<_>>()
53            && !params.is_empty()
54        {
55            if params.len() == 1 {
56                params[0]
57            } else {
58                ctx.alloc_type(Type::Union(Union::new(params)))
59            }
60        } else {
61            debug!("Unresolved lambda parameter type due to missing inference");
62            ctx.diagnostic(
63                param.syntax(),
64                DiagnosticKind::CannotInferParameterType(param.name().unwrap().text().to_string()),
65            );
66            ctx.builtins().unresolved.ty
67        };
68
69        let symbol = ctx.alloc_symbol(Symbol::Parameter(ParameterSymbol {
70            name: param.name(),
71            ty,
72        }));
73
74        if let Some(name) = param.name() {
75            if ctx.last_scope().symbol(name.text()).is_some() {
76                ctx.diagnostic(
77                    &name,
78                    DiagnosticKind::DuplicateSymbol(name.text().to_string()),
79                );
80            }
81
82            ctx.last_scope_mut()
83                .insert_symbol(name.text().to_string(), symbol, false);
84        }
85
86        parameters.push(symbol);
87        params.push(ty);
88    }
89
90    let return_type = expr.ty().map(|ty| compile_type(ctx, &ty));
91
92    let body = if let Some(body) = expr.body() {
93        let result = compile_expr(ctx, &body, return_type);
94        if let Some(return_type) = return_type {
95            ctx.assign_type(expr.syntax(), result.ty, return_type);
96        }
97        result
98    } else {
99        debug!("Unresolved lambda body");
100        ctx.builtins().unresolved.clone()
101    };
102
103    let return_type = return_type.unwrap_or(body.ty);
104
105    let ty = ctx.alloc_type(Type::Function(FunctionType {
106        params,
107        nil_terminated,
108        ret: return_type,
109    }));
110
111    ctx.pop_scope();
112
113    let symbol = ctx.alloc_symbol(Symbol::Function(FunctionSymbol {
114        name: None,
115        ty,
116        scope,
117        vars: vec![],
118        parameters,
119        nil_terminated,
120        return_type,
121        body: body.hir,
122        inline: false,
123    }));
124
125    let hir = ctx.alloc_hir(Hir::Lambda(symbol));
126
127    Value::new(hir, ty)
128}