rue_compiler/compile/expr/
lambda.rs

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