Skip to main content

runmat_runtime/
user_functions.rs

1use crate::RuntimeError;
2use runmat_builtins::Value;
3use runmat_hir::{CallableFallbackPolicy, CallableIdentity, SourceId};
4use runmat_thread_local::runmat_thread_local;
5use std::cell::RefCell;
6use std::future::Future;
7use std::pin::Pin;
8use std::sync::Arc;
9
10pub type UserFunctionFuture = Pin<Box<dyn Future<Output = Result<Value, RuntimeError>>>>;
11pub type FunctionInvoker = dyn Fn(usize, &[Value], usize) -> UserFunctionFuture + Send + Sync;
12pub type FunctionResolver = dyn Fn(&str) -> Option<usize> + Send + Sync;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct SourceFunctionInfo {
16    pub source_id: SourceId,
17    pub name: String,
18    pub function: usize,
19}
20
21#[derive(Debug, Clone)]
22pub struct CallableRequest {
23    identity: CallableIdentity,
24    fallback_policy: CallableFallbackPolicy,
25    args: Vec<Value>,
26    requested_outputs: usize,
27}
28
29impl CallableRequest {
30    pub fn semantic(function: usize, args: Vec<Value>, requested_outputs: usize) -> Self {
31        Self {
32            identity: CallableIdentity::BoundFunction(runmat_hir::FunctionId(function)),
33            fallback_policy: CallableFallbackPolicy::None,
34            args,
35            requested_outputs,
36        }
37    }
38
39    pub fn resolved(
40        identity: CallableIdentity,
41        fallback_policy: CallableFallbackPolicy,
42        args: Vec<Value>,
43        requested_outputs: usize,
44    ) -> Self {
45        Self {
46            identity,
47            fallback_policy,
48            args,
49            requested_outputs,
50        }
51    }
52}
53
54runmat_thread_local! {
55    static SEMANTIC_FUNCTION_INVOKER: RefCell<Option<Arc<FunctionInvoker>>> =
56        const { RefCell::new(None) };
57    static SEMANTIC_FUNCTION_RESOLVER: RefCell<Option<Arc<FunctionResolver>>> =
58        const { RefCell::new(None) };
59    static SOURCE_FUNCTION_CATALOG: RefCell<Option<Arc<Vec<SourceFunctionInfo>>>> =
60        const { RefCell::new(None) };
61    static ACTIVE_SEMANTIC_FUNCTION_STACK: RefCell<Vec<usize>> =
62        const { RefCell::new(Vec::new()) };
63}
64
65pub struct FunctionInvokerGuard {
66    previous: Option<Arc<FunctionInvoker>>,
67}
68
69pub struct FunctionResolverGuard {
70    previous: Option<Arc<FunctionResolver>>,
71}
72
73pub struct SourceFunctionCatalogGuard {
74    previous: Option<Arc<Vec<SourceFunctionInfo>>>,
75}
76
77pub struct ActiveSemanticFunctionGuard;
78
79impl Drop for FunctionInvokerGuard {
80    fn drop(&mut self) {
81        let previous = self.previous.take();
82        SEMANTIC_FUNCTION_INVOKER.with(|slot| {
83            *slot.borrow_mut() = previous;
84        });
85    }
86}
87
88impl Drop for FunctionResolverGuard {
89    fn drop(&mut self) {
90        let previous = self.previous.take();
91        SEMANTIC_FUNCTION_RESOLVER.with(|slot| {
92            *slot.borrow_mut() = previous;
93        });
94    }
95}
96
97impl Drop for SourceFunctionCatalogGuard {
98    fn drop(&mut self) {
99        let previous = self.previous.take();
100        SOURCE_FUNCTION_CATALOG.with(|slot| {
101            *slot.borrow_mut() = previous;
102        });
103    }
104}
105
106impl Drop for ActiveSemanticFunctionGuard {
107    fn drop(&mut self) {
108        ACTIVE_SEMANTIC_FUNCTION_STACK.with(|slot| {
109            slot.borrow_mut().pop();
110        });
111    }
112}
113
114pub fn install_semantic_function_invoker(
115    invoker: Option<Arc<FunctionInvoker>>,
116) -> FunctionInvokerGuard {
117    let previous =
118        SEMANTIC_FUNCTION_INVOKER.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), invoker));
119    FunctionInvokerGuard { previous }
120}
121
122pub fn install_semantic_function_resolver(
123    resolver: Option<Arc<FunctionResolver>>,
124) -> FunctionResolverGuard {
125    let previous = SEMANTIC_FUNCTION_RESOLVER
126        .with(|slot| std::mem::replace(&mut *slot.borrow_mut(), resolver));
127    FunctionResolverGuard { previous }
128}
129
130pub fn install_source_function_catalog(
131    catalog: Option<Arc<Vec<SourceFunctionInfo>>>,
132) -> SourceFunctionCatalogGuard {
133    let previous =
134        SOURCE_FUNCTION_CATALOG.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), catalog));
135    SourceFunctionCatalogGuard { previous }
136}
137
138pub fn push_active_semantic_function(function: usize) -> ActiveSemanticFunctionGuard {
139    ACTIVE_SEMANTIC_FUNCTION_STACK.with(|slot| {
140        slot.borrow_mut().push(function);
141    });
142    ActiveSemanticFunctionGuard
143}
144
145pub fn current_semantic_function_invoker() -> Option<Arc<FunctionInvoker>> {
146    SEMANTIC_FUNCTION_INVOKER.with(|slot| slot.borrow().clone())
147}
148
149pub fn current_semantic_function_resolver() -> Option<Arc<FunctionResolver>> {
150    SEMANTIC_FUNCTION_RESOLVER.with(|slot| slot.borrow().clone())
151}
152
153pub fn current_active_semantic_function() -> Option<usize> {
154    ACTIVE_SEMANTIC_FUNCTION_STACK.with(|slot| slot.borrow().last().copied())
155}
156
157pub fn source_functions_for(source_id: SourceId) -> Vec<SourceFunctionInfo> {
158    SOURCE_FUNCTION_CATALOG.with(|slot| {
159        slot.borrow()
160            .as_ref()
161            .map(|catalog| {
162                catalog
163                    .iter()
164                    .filter(|info| info.source_id == source_id)
165                    .cloned()
166                    .collect::<Vec<_>>()
167            })
168            .unwrap_or_default()
169    })
170}
171
172pub async fn try_call_semantic_function(
173    function: usize,
174    args: &[Value],
175    requested_outputs: usize,
176) -> Option<Result<Value, RuntimeError>> {
177    let invoker = SEMANTIC_FUNCTION_INVOKER.with(|slot| slot.borrow().clone());
178    let invoker = invoker?;
179    Some(invoker(function, args, requested_outputs).await)
180}
181
182pub async fn try_call_semantic_function_by_name(
183    name: &str,
184    args: &[Value],
185    requested_outputs: usize,
186) -> Option<Result<Value, RuntimeError>> {
187    let function = resolve_semantic_function_by_name(name)?;
188    try_call_semantic_function(function, args, requested_outputs).await
189}
190
191pub fn resolve_semantic_function_by_name(name: &str) -> Option<usize> {
192    let resolver = SEMANTIC_FUNCTION_RESOLVER.with(|slot| slot.borrow().clone())?;
193    resolver(name)
194}
195
196pub async fn try_call_semantic_descriptor(
197    request: CallableRequest,
198) -> Option<Result<Value, RuntimeError>> {
199    let CallableRequest {
200        identity,
201        fallback_policy,
202        args,
203        requested_outputs,
204    } = request;
205    if let CallableIdentity::BoundFunction(function) = identity {
206        return try_call_semantic_function(function.0, &args, requested_outputs).await;
207    }
208    if !fallback_policy.allows_semantic_name_resolution_for(&identity) {
209        return None;
210    }
211    let name = fallback_policy.resolution_name_for(&identity)?;
212    if matches!(identity, CallableIdentity::DynamicName(_))
213        && runmat_builtins::get_class(&name).is_some()
214    {
215        // Constructor calls for class names must flow through runtime constructor dispatch,
216        // not generic semantic name resolution.
217        return None;
218    }
219    try_call_semantic_function_by_name(&name, &args, requested_outputs).await
220}