rue_compiler/compile/expr/
lambda.rs

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