compose_library/foundations/
func.rs

1use crate::diag::{SourceResult, StrResult, bail};
2use crate::foundations::args::Args;
3use crate::vm::Vm;
4use crate::{Sink, Trace, Value};
5use compose_error_codes::E0010_UNCAPTURED_VARIABLE;
6use compose_library::diag::{Spanned, error};
7use compose_library::{Scope, UntypedRef};
8use compose_macros::{cast, ty};
9use compose_syntax::ast::{AstNode};
10use compose_syntax::{Label, Span, SyntaxNode, ast};
11use compose_utils::Static;
12use ecow::{EcoString, eco_format, eco_vec};
13use std::collections::HashMap;
14use std::fmt;
15use std::sync::LazyLock;
16use tap::Tap;
17
18#[derive(Clone, Debug, PartialEq)]
19#[ty(cast)]
20pub struct Func {
21    pub kind: FuncKind,
22    pub span: Span,
23}
24
25impl Func {
26    pub(crate) fn spanned(mut self, span: Span) -> Func {
27        if self.span.is_detached() {
28            self.span = span;
29        }
30        self
31    }
32
33    pub(crate) fn named(mut self, name: Spanned<EcoString>) -> Func {
34        if let FuncKind::Closure(closure) = &mut self.kind {
35            closure.unresolved_captures.remove(&name.value);
36            closure.name = Some(name);
37        }
38        self
39    }
40
41    pub(crate) fn resolve(&self) -> SourceResult<()> {
42        match &self.kind {
43            FuncKind::Closure(closure) => {
44                closure.resolve_captures()?;
45            }
46            FuncKind::Native(_) => {}
47        }
48        Ok(())
49    }
50
51    pub(crate) fn span(&self) -> Span {
52        self.span
53    }
54}
55
56impl fmt::Display for Func {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        match &self.kind {
59            FuncKind::Native(native) => write!(f, "{}", native.0.name),
60            FuncKind::Closure(closure) => closure.fmt(f),
61        }
62    }
63}
64
65impl Trace for Func {
66    fn visit_refs(&self, f: &mut dyn FnMut(UntypedRef)) {
67        match &self.kind {
68            FuncKind::Native(native) => {
69                native.scope.visit_refs(f);
70            }
71            FuncKind::Closure(closure) => {
72                closure.captured.visit_refs(f);
73                closure.defaults.iter().for_each(|v| v.visit_refs(f));
74            }
75        }
76    }
77}
78
79#[derive(Clone, Debug, PartialEq)]
80pub enum FuncKind {
81    Native(Static<NativeFuncData>),
82    Closure(Box<Closure>),
83}
84
85impl Func {
86    pub fn call(&self, vm: &mut dyn Vm, args: Args) -> SourceResult<Value> {
87        vm.call_func(self, args)
88    }
89
90    pub fn scope(&self) -> Option<&'static Scope> {
91        match &self.kind {
92            FuncKind::Native(native) => Some(&native.0.scope),
93            FuncKind::Closure(_) => None,
94        }
95    }
96
97    pub fn name(&self) -> Option<&str> {
98        match &self.kind {
99            FuncKind::Native(native) => Some(native.0.name),
100            FuncKind::Closure(_) => None,
101        }
102    }
103
104    pub fn field(&self, field: &str, access_span: Span, sink: &mut Sink) -> StrResult<&Value> {
105        let scope = self
106            .scope()
107            .ok_or("Cannot access fields on user-defined functions")?;
108        match scope.get(field) {
109            Some(binding) => Ok(binding.read_checked(access_span, sink)),
110            None => match self.name() {
111                Some(name) => bail!("function `{name}` does not contain field `{field}`"),
112                None => bail!("Function does not contain field `{field}`"),
113            },
114        }
115    }
116
117    pub fn path(&self, path: &str, access_span: Span, sink: &mut Sink) -> StrResult<&Value> {
118        let scope = self
119            .scope()
120            .ok_or("Cannot access fields on user-defined functions")?;
121        match scope.get(path) {
122            Some(binding) => Ok(binding.read_checked(access_span, sink)),
123            None => match self.name() {
124                Some(name) => bail!("function `{name}` does not contain associated field `{path}`"),
125                None => bail!("Function does not contain associated field `{path}`"),
126            },
127        }
128    }
129
130    pub fn is_associated_function(&self) -> bool {
131        match self.kind {
132            FuncKind::Native(n) => match n.fn_type {
133                FuncType::Method => false,
134                FuncType::MethodMut => false,
135                FuncType::Associated => true,
136            },
137            FuncKind::Closure(_) => false,
138        }
139    }
140
141    pub fn requires_mut_self(&self) -> bool {
142        match self.kind {
143            FuncKind::Native(n) => match n.fn_type {
144                FuncType::Method => false,
145                FuncType::MethodMut => true,
146                FuncType::Associated => false,
147            },
148            FuncKind::Closure(_) => false,
149        }
150    }
151}
152
153pub trait NativeFunc {
154    fn data() -> &'static NativeFuncData;
155}
156
157#[derive(Debug)]
158pub struct NativeFuncData {
159    pub closure: fn(&mut dyn Vm, &mut Args) -> SourceResult<Value>,
160    pub name: &'static str,
161    pub scope: LazyLock<&'static Scope>,
162    pub fn_type: FuncType,
163}
164
165impl NativeFuncData {
166    pub fn call(&self, vm: &mut dyn Vm, mut args: Args) -> SourceResult<Value> {
167        (self.closure)(vm, &mut args)
168    }
169}
170
171#[derive(Debug, Clone)]
172pub struct Closure {
173    pub node: SyntaxNode,
174    pub defaults: Vec<Value>,
175    pub num_pos_params: usize,
176    pub name: Option<Spanned<EcoString>>,
177    pub captured: Scope,
178    pub unresolved_captures: HashMap<EcoString, Span>,
179}
180
181impl PartialEq for Closure {
182    fn eq(&self, other: &Self) -> bool {
183        self.node == other.node
184    }
185}
186
187impl Closure {
188    pub fn resolve_captures(&self) -> SourceResult<()> {
189        if !self.unresolved_captures.is_empty() {
190            let mut captures = self.unresolved_captures.iter();
191
192            let names = self
193                .unresolved_captures
194                .keys()
195                .map(|name| name.trim())
196                .collect::<Vec<_>>()
197                .tap_mut(|v| v.sort_unstable())
198                .join(", ");
199
200            let params = self
201                .node
202                .cast::<ast::Lambda>()
203                .expect("Closure contains non lambda node")
204                .params()
205                .children()
206                .map(|p| p.to_untyped().to_text())
207                .collect::<Vec<_>>()
208                .join(", ");
209
210            let (first_name, first_span) = captures.next().expect("unresolved captures was checked to be non-empty");
211
212            let mut err = error!(*first_span, "closure uses outer variables that are not captured";
213                label_message: "outer variable `{first_name}` used here";
214                hint: "explicitly capture them by adding them to a capture list: `{{ |{names}| {params}=> ... }}`";
215                code: &E0010_UNCAPTURED_VARIABLE
216            );
217
218            for (name, span) in captures {
219                err = err.with_label(Label::primary(
220                    *span,
221                    eco_format!("outer variable `{name}` used here"),
222                ));
223            }
224
225            return Err(eco_vec!(err));
226        }
227        Ok(())
228    }
229}
230
231impl fmt::Display for Closure {
232    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233        let closure: ast::Lambda = self.node.cast().expect("closure");
234        let params = closure.params().to_untyped().to_text();
235
236        write!(f, "{} => ...", params)
237    }
238}
239
240#[derive(Debug)]
241pub enum FuncType {
242    Method,
243    MethodMut,
244    Associated,
245}
246
247impl From<FuncKind> for Func {
248    fn from(value: FuncKind) -> Self {
249        Self {
250            span: Span::detached(),
251            kind: value,
252        }
253    }
254}
255
256impl From<&'static NativeFuncData> for Func {
257    fn from(data: &'static NativeFuncData) -> Self {
258        FuncKind::Native(Static(data)).into()
259    }
260}
261
262impl From<Closure> for Func {
263    fn from(closure: Closure) -> Self {
264        FuncKind::Closure(Box::new(closure)).into()
265    }
266}
267
268#[derive(Debug)]
269pub struct ParamInfo {
270    pub name: &'static str,
271}
272
273cast! {
274    &'static NativeFuncData,
275    self => Func::from(self).into_value(),
276}