Skip to main content

rl_lang/interpreter/
native.rs

1//! The native function binding system - [`Module`], [`NativeFn`], and the
2//! [`IntoNativeFn`] / [`FromValue`] / [`IntoValue`] trait machinery.
3//!
4//! # How it works
5//!
6//! Stdlib functions are plain Rust functions. The [`IntoNativeFn`] trait
7//! (implemented by a macro for up to 10 arguments) wraps them into [`NativeFn`]
8//! closures that handle arity checking and argument extraction automatically.
9//!
10//! Two variants exist:
11//! - **Infallible** (`IntoNativeFn<(A1, A2, ...)>`) - for functions that return `R: IntoValue`
12//! - **Fallible** (`IntoNativeFn<(Fallible<(A1, A2, ...)>,)>`) - for functions returning `Result<R, Error>`
13//! - **Spanned** (`IntoNativeFn<(Spanned<(A1, A2, ...)>,)>`) - fallible functions that also receive the call [`Span`]
14//!
15//! # Example
16//!
17//! ```rust
18//! use rl_lang::interpreter::{evaluator::Evaluator, native::Module};
19//!
20//! fn my_fn(_: &mut Evaluator, x: i64, y: i64) -> i64 { x + y }
21//! Module::new("math").with_function("add", my_fn);
22//! ```
23
24use crate::ast::statements::TypeAnnotation;
25use crate::interpreter::evaluator::Evaluator;
26use crate::interpreter::values::Value;
27use crate::utils::errors::{Error, Reason};
28use crate::utils::span::Span;
29use std::collections::HashMap;
30use std::sync::Arc;
31
32/// A thread-safe, heap-allocated native function callable from rl.
33pub type NativeFn =
34    Arc<dyn Fn(&mut Evaluator, Vec<Value>, Span) -> Result<Value, Error> + Send + Sync>;
35
36/// A named collection of [`NativeFn`]s, optionally containing sub-[`Module`]s.
37pub struct Module {
38    /// The module name as used in import paths (e.g. `"io"`, `"math"`).
39    pub name: String,
40    /// Named functions registered in this module.
41    pub functions: HashMap<String, NativeFn>,
42    /// Named submodules (e.g. `math::consts`).
43    pub submodules: HashMap<String, Module>,
44}
45
46impl Module {
47    pub fn new(name: impl Into<String>) -> Self {
48        Self {
49            name: name.into(),
50            functions: HashMap::new(),
51            submodules: HashMap::new(),
52        }
53    }
54
55    /// Registers a typed Rust function using the [`IntoNativeFn`] trait.
56    pub fn with_function<F, A>(mut self, name: impl Into<String>, f: F) -> Self
57    where
58        F: IntoNativeFn<A>,
59    {
60        self.functions.insert(name.into(), f.into_native());
61        self
62    }
63
64    /// Registers a raw `fn(&mut Evaluator, Vec<Value>, Span) -> Result<Value, Error>` directly,
65    /// bypassing the [`IntoNativeFn`] machinery (used for variadic functions like `print`).
66    pub fn with_raw_function<F>(mut self, name: impl Into<String>, f: F) -> Self
67    where
68        F: Fn(&mut Evaluator, Vec<Value>, Span) -> Result<Value, Error> + Send + Sync + 'static,
69    {
70        self.functions.insert(name.into(), Arc::new(f));
71        self
72    }
73
74    /// Registers a submodule.
75    pub fn with_module(mut self, m: Module) -> Self {
76        self.submodules.insert(m.name.clone(), m);
77        self
78    }
79
80    /// Walks the module tree along `path`, returning the [`NativeFn`] at the leaf,
81    /// or `None` if any segment is missing.
82    pub fn resolve(&self, path: &[String]) -> Option<&NativeFn> {
83        if path.is_empty() {
84            return None;
85        }
86        let mut module = self;
87        for seg in &path[..path.len() - 1] {
88            module = module.submodules.get(seg)?;
89        }
90        module.functions.get(&path[path.len() - 1])
91    }
92}
93
94/// Extracts the static [`TypeAnnotation`] for a Rust type.
95/// Used by [`IntoValue`] for `Vec<T>` to set the array's `items_type`.
96pub trait ValueType {
97    fn type_annotation() -> TypeAnnotation;
98}
99
100impl ValueType for i64 {
101    fn type_annotation() -> TypeAnnotation {
102        TypeAnnotation::Int
103    }
104}
105
106impl ValueType for f64 {
107    fn type_annotation() -> TypeAnnotation {
108        TypeAnnotation::Float
109    }
110}
111
112impl ValueType for String {
113    fn type_annotation() -> TypeAnnotation {
114        TypeAnnotation::String
115    }
116}
117
118impl ValueType for bool {
119    fn type_annotation() -> TypeAnnotation {
120        TypeAnnotation::Bool
121    }
122}
123
124impl ValueType for char {
125    fn type_annotation() -> TypeAnnotation {
126        TypeAnnotation::Char
127    }
128}
129
130impl<T: ValueType> ValueType for Vec<T> {
131    fn type_annotation() -> TypeAnnotation {
132        TypeAnnotation::Array(Box::new(T::type_annotation()))
133    }
134}
135
136/// Converts a [`Value`] into a typed Rust value, or returns a runtime error.
137/// Implemented for `i64`, `f64`, `String`, `bool`, `char`, `Vec<T>`, and `Value` itself.
138pub trait FromValue: Sized {
139    fn from_value(v: Value, span: Span) -> Result<Self, Error>;
140}
141
142impl FromValue for Value {
143    fn from_value(v: Value, _span: Span) -> Result<Self, Error> {
144        Ok(v)
145    }
146}
147
148impl FromValue for i64 {
149    fn from_value(v: Value, span: Span) -> Result<Self, Error> {
150        match v {
151            Value::Integer(i) => Ok(i),
152            Value::Byte(b) => Ok(b as i64),
153            other => Err(Error::at(
154                Reason::Runtime,
155                format!("expected integer, got {}", other.type_name()),
156                span,
157            )),
158        }
159    }
160}
161
162impl FromValue for f64 {
163    fn from_value(v: Value, span: Span) -> Result<Self, Error> {
164        match v {
165            Value::Float(f) => Ok(f),
166            other => Err(Error::at(
167                Reason::Runtime,
168                format!("expected float, got {}", other.type_name()),
169                span,
170            )),
171        }
172    }
173}
174
175impl FromValue for String {
176    fn from_value(v: Value, span: Span) -> Result<Self, Error> {
177        match v {
178            Value::String(s) => Ok(s),
179            other => Err(Error::at(
180                Reason::Runtime,
181                format!("expected string, got {}", other.type_name()),
182                span,
183            )),
184        }
185    }
186}
187
188impl FromValue for bool {
189    fn from_value(v: Value, span: Span) -> Result<Self, Error> {
190        match v {
191            Value::Bool(b) => Ok(b),
192            other => Err(Error::at(
193                Reason::Runtime,
194                format!("expected bool, got {}", other.type_name()),
195                span,
196            )),
197        }
198    }
199}
200
201impl FromValue for char {
202    fn from_value(v: Value, span: Span) -> Result<Self, Error> {
203        match v {
204            Value::Char(c) => Ok(c),
205            other => Err(Error::at(
206                Reason::Runtime,
207                format!("expected char, got {}", other.type_name()),
208                span,
209            )),
210        }
211    }
212}
213
214impl<T: FromValue> FromValue for Vec<T> {
215    fn from_value(v: Value, span: Span) -> Result<Self, Error> {
216        match v {
217            Value::Values { items, .. } => items
218                .into_iter()
219                .map(|item| T::from_value(item, span))
220                .collect(),
221            other => Err(Error::at(
222                Reason::Runtime,
223                format!("expected array, got {}", other.type_name()),
224                span,
225            )),
226        }
227    }
228}
229
230/// Converts a typed Rust value into a [`Value`].
231/// Implemented for `()` (→ `Null`), `i64`, `f64`, `String`, `bool`, `char`, `Vec<T>`, and `Value`.
232pub trait IntoValue {
233    fn into_value(self) -> Value;
234}
235
236impl IntoValue for Value {
237    fn into_value(self) -> Value {
238        self
239    }
240}
241
242impl IntoValue for () {
243    fn into_value(self) -> Value {
244        Value::Null
245    }
246}
247
248impl IntoValue for i64 {
249    fn into_value(self) -> Value {
250        Value::Integer(self)
251    }
252}
253
254impl IntoValue for f64 {
255    fn into_value(self) -> Value {
256        Value::Float(self)
257    }
258}
259
260impl IntoValue for String {
261    fn into_value(self) -> Value {
262        Value::String(self)
263    }
264}
265
266impl IntoValue for bool {
267    fn into_value(self) -> Value {
268        Value::Bool(self)
269    }
270}
271
272impl IntoValue for char {
273    fn into_value(self) -> Value {
274        Value::Char(self)
275    }
276}
277
278impl<T: IntoValue + ValueType> IntoValue for Vec<T> {
279    fn into_value(self) -> Value {
280        Value::Values {
281            items_type: T::type_annotation(),
282            items: self.into_iter().map(T::into_value).collect(),
283        }
284    }
285}
286
287/// Wraps a typed Rust function into a [`NativeFn`] with automatic arity checking
288/// and argument extraction. Implemented by macro for 0–10 arguments.
289pub trait IntoNativeFn<Args> {
290    fn into_native(self) -> NativeFn;
291}
292
293impl<F, R> IntoNativeFn<()> for F
294where
295    F: Fn(&mut Evaluator) -> R + Send + Sync + 'static,
296    R: IntoValue,
297{
298    fn into_native(self) -> NativeFn {
299        Arc::new(
300            move |rt: &mut Evaluator, args: Vec<Value>, span: Span| -> Result<Value, Error> {
301                if !args.is_empty() {
302                    return Err(Error::at(
303                        Reason::Runtime,
304                        format!("expected 0 argument(s), got {}", args.len()),
305                        span,
306                    ));
307                }
308                Ok(self(rt).into_value())
309            },
310        )
311    }
312}
313
314/// Marker type for fallible native functions (returning `Result<R, Error>`).
315pub struct Fallible<T>(std::marker::PhantomData<T>);
316
317macro_rules! impl_into_native_fn {
318    ($count:literal, $(($ty:ident, $var:ident)),+) => {
319        impl<F, R, $($ty),+> IntoNativeFn<($($ty,)+)> for F
320        where
321            F: Fn(&mut Evaluator, $($ty),+) -> R + Send + Sync + 'static,
322            R: IntoValue,
323            $($ty: FromValue),+
324        {
325            fn into_native(self) -> NativeFn {
326                Arc::new(move |rt: &mut Evaluator, args: Vec<Value>, span: Span| -> Result<Value, Error> {
327                    if args.len() != $count {
328                        return Err(Error::at(
329                            Reason::Runtime,
330                            format!("expected {} argument(s), got {}", $count, args.len()),
331                            span, ));
332                    }
333                    let mut iter = args.into_iter();
334                    $(let $var = <$ty>::from_value(iter.next().unwrap(), span)?;)+
335                    Ok(self(rt, $($var),+).into_value())
336                })
337            }
338        }
339
340        impl<F, R, $($ty),+> IntoNativeFn<(Fallible<($($ty,)+)>,)> for F
341        where
342            F: Fn(&mut Evaluator, $($ty),+) -> Result<R, Error> + Send + Sync + 'static,
343            R: IntoValue,
344            $($ty: FromValue),+
345        {
346            fn into_native(self) -> NativeFn {
347                Arc::new(move |rt: &mut Evaluator, args: Vec<Value>, span: Span| -> Result<Value, Error> {
348                    if args.len() != $count {
349                        return Err(Error::at(
350                            Reason::Runtime,
351                            format!("expected {} argument(s), got {}", $count, args.len()),
352                            span, ));
353                    }
354                    let mut iter = args.into_iter();
355                    $(let $var = <$ty>::from_value(iter.next().unwrap(), span)?;)+
356                    Ok(self(rt, $($var),+)?.into_value())
357                })
358            }
359        }
360    };
361}
362impl_into_native_fn!(1, (A1, a1));
363impl_into_native_fn!(2, (A1, a1), (A2, a2));
364impl_into_native_fn!(3, (A1, a1), (A2, a2), (A3, a3));
365impl_into_native_fn!(4, (A1, a1), (A2, a2), (A3, a3), (A4, a4));
366impl_into_native_fn!(5, (A1, a1), (A2, a2), (A3, a3), (A4, a4), (A5, a5));
367impl_into_native_fn!(
368    6,
369    (A1, a1),
370    (A2, a2),
371    (A3, a3),
372    (A4, a4),
373    (A5, a5),
374    (A6, a6)
375);
376impl_into_native_fn!(
377    7,
378    (A1, a1),
379    (A2, a2),
380    (A3, a3),
381    (A4, a4),
382    (A5, a5),
383    (A6, a6),
384    (A7, a7)
385);
386impl_into_native_fn!(
387    8,
388    (A1, a1),
389    (A2, a2),
390    (A3, a3),
391    (A4, a4),
392    (A5, a5),
393    (A6, a6),
394    (A7, a7),
395    (A8, a8)
396);
397impl_into_native_fn!(
398    9,
399    (A1, a1),
400    (A2, a2),
401    (A3, a3),
402    (A4, a4),
403    (A5, a5),
404    (A6, a6),
405    (A7, a7),
406    (A8, a8),
407    (A9, a9)
408);
409impl_into_native_fn!(
410    10,
411    (A1, a1),
412    (A2, a2),
413    (A3, a3),
414    (A4, a4),
415    (A5, a5),
416    (A6, a6),
417    (A7, a7),
418    (A8, a8),
419    (A9, a9),
420    (A10, a10)
421);
422
423/// Marker type for spanned fallible native functions (also receiving the call [`Span`]).
424pub struct Spanned<T>(std::marker::PhantomData<T>);
425
426macro_rules! impl_into_native_fn_spanned {
427    ($count:literal, $(($ty:ident, $var:ident)),+) => {
428        impl<F, R, $($ty),+> IntoNativeFn<(Spanned<($($ty,)+)>,)> for F
429        where
430            F: Fn(&mut Evaluator, $($ty),+, Span) -> Result<R, Error> + Send + Sync + 'static,
431            R: IntoValue,
432            $($ty: FromValue),+
433        {
434            fn into_native(self) -> NativeFn {
435                Arc::new(move |rt: &mut Evaluator, args: Vec<Value>, span: Span| -> Result<Value, Error> {
436                    if args.len() != $count {
437                        return Err(rt.err(
438                            format!("expected {} argument(s), got {}", $count, args.len()),
439                            span,
440                        ));
441                    }
442                    let mut iter = args.into_iter();
443                    $(let $var = <$ty>::from_value(iter.next().unwrap(), span)?;)+
444                    Ok(self(rt, $($var),+, span)?.into_value())
445                })
446            }
447        }
448    };
449}
450
451impl_into_native_fn_spanned!(1, (A1, a1));
452impl_into_native_fn_spanned!(2, (A1, a1), (A2, a2));
453impl_into_native_fn_spanned!(3, (A1, a1), (A2, a2), (A3, a3));
454impl_into_native_fn_spanned!(4, (A1, a1), (A2, a2), (A3, a3), (A4, a4));
455impl_into_native_fn_spanned!(5, (A1, a1), (A2, a2), (A3, a3), (A4, a4), (A5, a5));
456impl_into_native_fn_spanned!(
457    6,
458    (A1, a1),
459    (A2, a2),
460    (A3, a3),
461    (A4, a4),
462    (A5, a5),
463    (A6, a6)
464);
465impl_into_native_fn_spanned!(
466    7,
467    (A1, a1),
468    (A2, a2),
469    (A3, a3),
470    (A4, a4),
471    (A5, a5),
472    (A6, a6),
473    (A7, a7)
474);
475impl_into_native_fn_spanned!(
476    8,
477    (A1, a1),
478    (A2, a2),
479    (A3, a3),
480    (A4, a4),
481    (A5, a5),
482    (A6, a6),
483    (A7, a7),
484    (A8, a8)
485);
486impl_into_native_fn_spanned!(
487    9,
488    (A1, a1),
489    (A2, a2),
490    (A3, a3),
491    (A4, a4),
492    (A5, a5),
493    (A6, a6),
494    (A7, a7),
495    (A8, a8),
496    (A9, a9)
497);
498impl_into_native_fn_spanned!(
499    10,
500    (A1, a1),
501    (A2, a2),
502    (A3, a3),
503    (A4, a4),
504    (A5, a5),
505    (A6, a6),
506    (A7, a7),
507    (A8, a8),
508    (A9, a9),
509    (A10, a10)
510);