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 DynamicFunctionLoadFuture =
12    Pin<Box<dyn Future<Output = Option<Result<Value, RuntimeError>>>>>;
13pub type FunctionInvoker = dyn Fn(usize, &[Value], usize) -> UserFunctionFuture + Send + Sync;
14pub type FunctionResolver = dyn Fn(&str) -> Option<usize> + Send + Sync;
15pub type DynamicFunctionLoader =
16    dyn Fn(String, Vec<Value>, usize) -> DynamicFunctionLoadFuture + Send + Sync;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct SourceFunctionInfo {
20    pub source_id: SourceId,
21    pub name: String,
22    pub function: usize,
23}
24
25#[derive(Debug, Clone)]
26pub struct CallableRequest {
27    identity: CallableIdentity,
28    fallback_policy: CallableFallbackPolicy,
29    args: Vec<Value>,
30    requested_outputs: usize,
31}
32
33impl CallableRequest {
34    pub fn semantic(function: usize, args: Vec<Value>, requested_outputs: usize) -> Self {
35        Self {
36            identity: CallableIdentity::BoundFunction(runmat_hir::FunctionId(function)),
37            fallback_policy: CallableFallbackPolicy::None,
38            args,
39            requested_outputs,
40        }
41    }
42
43    pub fn resolved(
44        identity: CallableIdentity,
45        fallback_policy: CallableFallbackPolicy,
46        args: Vec<Value>,
47        requested_outputs: usize,
48    ) -> Self {
49        Self {
50            identity,
51            fallback_policy,
52            args,
53            requested_outputs,
54        }
55    }
56}
57
58runmat_thread_local! {
59    static SEMANTIC_FUNCTION_INVOKER: RefCell<Option<Arc<FunctionInvoker>>> =
60        const { RefCell::new(None) };
61    static SEMANTIC_FUNCTION_RESOLVER: RefCell<Option<Arc<FunctionResolver>>> =
62        const { RefCell::new(None) };
63    static ACTIVE_RUNTIME_CONTEXT: RefCell<Option<Arc<RuntimeContext>>> =
64        const { RefCell::new(None) };
65    static SOURCE_FUNCTION_CATALOG: RefCell<Option<Arc<Vec<SourceFunctionInfo>>>> =
66        const { RefCell::new(None) };
67    static ACTIVE_SEMANTIC_FUNCTION_STACK: RefCell<Vec<usize>> =
68        const { RefCell::new(Vec::new()) };
69}
70
71pub struct FunctionInvokerGuard {
72    previous: Option<Arc<FunctionInvoker>>,
73}
74
75pub struct FunctionResolverGuard {
76    previous: Option<Arc<FunctionResolver>>,
77}
78
79pub struct RuntimeContextGuard {
80    previous: Option<Arc<RuntimeContext>>,
81}
82
83pub struct SourceFunctionCatalogGuard {
84    previous: Option<Arc<Vec<SourceFunctionInfo>>>,
85}
86
87pub struct ActiveSemanticFunctionGuard;
88
89impl Drop for FunctionInvokerGuard {
90    fn drop(&mut self) {
91        let previous = self.previous.take();
92        SEMANTIC_FUNCTION_INVOKER.with(|slot| {
93            *slot.borrow_mut() = previous;
94        });
95    }
96}
97
98impl Drop for FunctionResolverGuard {
99    fn drop(&mut self) {
100        let previous = self.previous.take();
101        SEMANTIC_FUNCTION_RESOLVER.with(|slot| {
102            *slot.borrow_mut() = previous;
103        });
104    }
105}
106
107impl Drop for RuntimeContextGuard {
108    fn drop(&mut self) {
109        let previous = self.previous.take();
110        ACTIVE_RUNTIME_CONTEXT.with(|slot| {
111            *slot.borrow_mut() = previous;
112        });
113    }
114}
115
116impl Drop for SourceFunctionCatalogGuard {
117    fn drop(&mut self) {
118        let previous = self.previous.take();
119        SOURCE_FUNCTION_CATALOG.with(|slot| {
120            *slot.borrow_mut() = previous;
121        });
122    }
123}
124
125impl Drop for ActiveSemanticFunctionGuard {
126    fn drop(&mut self) {
127        ACTIVE_SEMANTIC_FUNCTION_STACK.with(|slot| {
128            slot.borrow_mut().pop();
129        });
130    }
131}
132
133pub fn install_semantic_function_invoker(
134    invoker: Option<Arc<FunctionInvoker>>,
135) -> FunctionInvokerGuard {
136    let previous =
137        SEMANTIC_FUNCTION_INVOKER.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), invoker));
138    FunctionInvokerGuard { previous }
139}
140
141pub fn install_semantic_function_resolver(
142    resolver: Option<Arc<FunctionResolver>>,
143) -> FunctionResolverGuard {
144    let previous = SEMANTIC_FUNCTION_RESOLVER
145        .with(|slot| std::mem::replace(&mut *slot.borrow_mut(), resolver));
146    FunctionResolverGuard { previous }
147}
148
149pub struct RuntimeContext {
150    search_path: Arc<crate::builtins::common::path_state::SearchPath>,
151    dynamic_function_loader: Option<Arc<DynamicFunctionLoader>>,
152}
153
154impl RuntimeContext {
155    pub fn new(search_path: Arc<crate::builtins::common::path_state::SearchPath>) -> Self {
156        Self {
157            search_path,
158            dynamic_function_loader: None,
159        }
160    }
161
162    pub fn with_dynamic_function_loader(mut self, loader: Arc<DynamicFunctionLoader>) -> Self {
163        self.dynamic_function_loader = Some(loader);
164        self
165    }
166
167    pub fn search_path(&self) -> &Arc<crate::builtins::common::path_state::SearchPath> {
168        &self.search_path
169    }
170}
171
172pub fn install_runtime_context(context: Arc<RuntimeContext>) -> RuntimeContextGuard {
173    let previous = ACTIVE_RUNTIME_CONTEXT.with(|slot| slot.borrow_mut().replace(context));
174    RuntimeContextGuard { previous }
175}
176
177pub fn active_runtime_context() -> Option<Arc<RuntimeContext>> {
178    ACTIVE_RUNTIME_CONTEXT.with(|slot| slot.borrow().clone())
179}
180
181pub fn install_source_function_catalog(
182    catalog: Option<Arc<Vec<SourceFunctionInfo>>>,
183) -> SourceFunctionCatalogGuard {
184    let previous =
185        SOURCE_FUNCTION_CATALOG.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), catalog));
186    SourceFunctionCatalogGuard { previous }
187}
188
189pub fn push_active_semantic_function(function: usize) -> ActiveSemanticFunctionGuard {
190    ACTIVE_SEMANTIC_FUNCTION_STACK.with(|slot| {
191        slot.borrow_mut().push(function);
192    });
193    ActiveSemanticFunctionGuard
194}
195
196pub fn current_semantic_function_invoker() -> Option<Arc<FunctionInvoker>> {
197    SEMANTIC_FUNCTION_INVOKER.with(|slot| slot.borrow().clone())
198}
199
200pub fn current_semantic_function_resolver() -> Option<Arc<FunctionResolver>> {
201    SEMANTIC_FUNCTION_RESOLVER.with(|slot| slot.borrow().clone())
202}
203
204pub fn current_active_semantic_function() -> Option<usize> {
205    ACTIVE_SEMANTIC_FUNCTION_STACK.with(|slot| slot.borrow().last().copied())
206}
207
208pub fn source_functions_for(source_id: SourceId) -> Vec<SourceFunctionInfo> {
209    SOURCE_FUNCTION_CATALOG.with(|slot| {
210        slot.borrow()
211            .as_ref()
212            .map(|catalog| {
213                catalog
214                    .iter()
215                    .filter(|info| info.source_id == source_id)
216                    .cloned()
217                    .collect::<Vec<_>>()
218            })
219            .unwrap_or_default()
220    })
221}
222
223pub async fn try_call_semantic_function(
224    function: usize,
225    args: &[Value],
226    requested_outputs: usize,
227) -> Option<Result<Value, RuntimeError>> {
228    let invoker = SEMANTIC_FUNCTION_INVOKER.with(|slot| slot.borrow().clone());
229    let invoker = invoker?;
230    Some(invoker(function, args, requested_outputs).await)
231}
232
233pub async fn try_call_semantic_function_by_name(
234    name: &str,
235    args: &[Value],
236    requested_outputs: usize,
237) -> Option<Result<Value, RuntimeError>> {
238    let function = resolve_semantic_function_by_name(name)?;
239    try_call_semantic_function(function, args, requested_outputs).await
240}
241
242pub fn resolve_semantic_function_by_name(name: &str) -> Option<usize> {
243    let resolver = SEMANTIC_FUNCTION_RESOLVER.with(|slot| slot.borrow().clone())?;
244    resolver(name)
245}
246
247pub async fn try_load_and_call_dynamic_function(
248    name: String,
249    args: Vec<Value>,
250    requested_outputs: usize,
251) -> Option<Result<Value, RuntimeError>> {
252    let loader = active_runtime_context()?.dynamic_function_loader.clone()?;
253    loader(name, args, requested_outputs).await
254}
255
256pub async fn try_call_semantic_descriptor(
257    request: CallableRequest,
258) -> Option<Result<Value, RuntimeError>> {
259    let CallableRequest {
260        identity,
261        fallback_policy,
262        args,
263        requested_outputs,
264    } = request;
265    if let CallableIdentity::BoundFunction(function) = identity {
266        return try_call_semantic_function(function.0, &args, requested_outputs).await;
267    }
268    if !fallback_policy.allows_semantic_name_resolution_for(&identity) {
269        return None;
270    }
271    let name = fallback_policy.resolution_name_for(&identity)?;
272    if matches!(identity, CallableIdentity::DynamicName(_))
273        && runmat_builtins::get_class(&name).is_some()
274    {
275        // Constructor calls for class names must flow through runtime constructor dispatch,
276        // not generic semantic name resolution.
277        return None;
278    }
279    if let Some(result) = try_call_semantic_function_by_name(&name, &args, requested_outputs).await
280    {
281        return Some(result);
282    }
283    if matches!(
284        identity,
285        CallableIdentity::DynamicName(_)
286            | CallableIdentity::Imported(_)
287            | CallableIdentity::ExternalName(_)
288    ) {
289        return try_load_and_call_dynamic_function(name, args, requested_outputs).await;
290    }
291    None
292}