cl_interpret/
builtin.rs

1#![allow(non_upper_case_globals)]
2
3use crate::{
4    convalue::ConValue,
5    env::Environment,
6    error::{Error, IResult},
7};
8use std::io::{Write, stdout};
9
10/// A function built into the interpreter.
11#[derive(Clone, Copy)]
12pub struct Builtin {
13    /// An identifier to be used during registration
14    pub name: &'static str,
15    /// The signature, displayed when the builtin is printed
16    pub desc: &'static str,
17    /// The function to be run when called
18    pub func: &'static dyn Fn(&mut Environment, &[ConValue]) -> IResult<ConValue>,
19}
20
21impl Builtin {
22    /// Constructs a new Builtin
23    pub const fn new(
24        name: &'static str,
25        desc: &'static str,
26        func: &'static impl Fn(&mut Environment, &[ConValue]) -> IResult<ConValue>,
27    ) -> Builtin {
28        Builtin { name, desc, func }
29    }
30
31    pub const fn description(&self) -> &'static str {
32        self.desc
33    }
34}
35
36impl std::fmt::Debug for Builtin {
37    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38        f.debug_struct("Builtin")
39            .field("description", &self.desc)
40            .finish_non_exhaustive()
41    }
42}
43
44impl super::Callable for Builtin {
45    fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
46        (self.func)(interpreter, args)
47    }
48
49    fn name(&self) -> cl_ast::Sym {
50        self.name.into()
51    }
52}
53
54/// Turns a function definition into a [Builtin].
55///
56/// ```rust
57/// # use cl_interpret::{builtin::builtin, convalue::ConValue};
58/// let my_builtin = builtin! {
59///     /// Use the `@env` suffix to bind the environment!
60///     /// (needed for recursive calls)
61///     fn my_builtin(ConValue::Bool(b), rest @ ..) @env {
62///         // This is all Rust code!
63///         eprintln!("my_builtin({b}, ..)");
64///         match rest {
65///             [] => Ok(ConValue::Empty),
66///             _ => my_builtin(env, rest), // Can be called as a normal function!
67///         }
68///     }
69/// };
70/// ```
71pub macro builtin(
72    $(#[$($meta:tt)*])*
73    fn $name:ident ($($arg:pat),*$(,)?) $(@$env:tt)? $body:block
74) {{
75    $(#[$($meta)*])*
76    fn $name(_env: &mut Environment, _args: &[ConValue]) -> IResult<ConValue> {
77        // Set up the builtin! environment
78        $(#[allow(unused)]let $env = _env;)?
79        // Allow for single argument `fn foo(args @ ..)` pattern
80        #[allow(clippy::redundant_at_rest_pattern, irrefutable_let_patterns)]
81        let [$($arg),*] = _args else {
82            Err($crate::error::Error::TypeError())?
83        };
84        $body.map(Into::into)
85    }
86    Builtin {
87        name: stringify!($name),
88        desc: stringify![builtin fn $name($($arg),*)],
89        func: &$name,
90    }
91}}
92
93/// Constructs an array of [Builtin]s from pseudo-function definitions
94pub macro builtins($(
95    $(#[$($meta:tt)*])*
96    fn $name:ident ($($args:tt)*) $(@$env:tt)? $body:block
97)*) {
98    [$(builtin!($(#[$($meta)*])* fn $name ($($args)*) $(@$env)? $body)),*]
99}
100
101/// Creates an [Error::BuiltinError] using interpolation of runtime expressions.
102/// See [std::format].
103pub macro error_format ($($t:tt)*) {
104    $crate::error::Error::BuiltinError(format!($($t)*))
105}
106
107pub const Builtins: &[Builtin] = &builtins![
108    /// Unstable variadic format function
109    fn fmt(args @ ..) {
110        use std::fmt::Write;
111        let mut out = String::new();
112        if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) {
113            eprintln!("{e}");
114        }
115        Ok(out)
116    }
117
118    /// Prints the arguments in-order, with no separators
119    fn print(args @ ..) {
120        let mut out = stdout().lock();
121        args.iter().try_for_each(|arg| write!(out, "{arg}") ).ok();
122        Ok(())
123    }
124
125    /// Prints the arguments in-order, followed by a newline
126    fn println(args @ ..) {
127        let mut out = stdout().lock();
128        args.iter().try_for_each(|arg| write!(out, "{arg}") ).ok();
129        writeln!(out).ok();
130        Ok(())
131    }
132
133    /// Debug-prints the argument, returning a copy
134    fn dbg(arg) {
135        println!("{arg:?}");
136        Ok(arg.clone())
137    }
138
139    /// Debug-prints the argument
140    fn dbgp(args @ ..) {
141        let mut out = stdout().lock();
142        args.iter().try_for_each(|arg| writeln!(out, "{arg:#?}") ).ok();
143        Ok(())
144    }
145
146    fn panic(message) {
147        Err(error_format!("Panic: {message}"))?;
148        Ok(())
149    }
150
151    /// Dumps the environment
152    fn dump() @env {
153        println!("{env}");
154        Ok(())
155    }
156
157    fn builtins() @env {
158        for builtin in env.globals().values().flatten().filter(|v| matches!(v, ConValue::Builtin(_))) {
159            println!("{builtin}")
160        }
161        Ok(())
162    }
163
164    /// Returns the length of the input list as a [ConValue::Int]
165    fn len(list) @env {
166        Ok(match list {
167            ConValue::Empty => 0,
168            ConValue::String(s) => s.chars().count() as _,
169            ConValue::Ref(r) => {
170                return len(env, &[env.get_id(*r).ok_or(Error::StackOverflow(*r))?.clone()])
171            }
172            ConValue::Array(t) => t.len() as _,
173            ConValue::Tuple(t) => t.len() as _,
174            _ => Err(Error::TypeError())?,
175        })
176    }
177
178    fn chars(string) @env {
179        Ok(match string {
180            ConValue::String(s) => ConValue::Array(s.chars().map(Into::into).collect()),
181            ConValue::Ref(r) => {
182                return chars(env, &[env.get_id(*r).ok_or(Error::StackOverflow(*r))?.clone()])
183            }
184            _ => Err(Error::TypeError())?,
185        })
186    }
187
188    fn dump_symbols() {
189        println!("{}", cl_structures::intern::string_interner::StringInterner::global());
190        Ok(ConValue::Empty)
191    }
192
193    fn slice_of(ConValue::Ref(arr), ConValue::Int(start)) {
194        Ok(ConValue::Slice(*arr, *start as usize))
195    }
196
197    /// Returns a shark
198    fn shark() {
199        Ok('\u{1f988}')
200    }
201];
202
203pub const Math: &[Builtin] = &builtins![
204    /// Multiplication `a * b`
205    fn mul(lhs, rhs) {
206        Ok(match (lhs, rhs) {
207            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
208            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
209            _ => Err(Error::TypeError())?
210        })
211    }
212
213    /// Division `a / b`
214    fn div(lhs, rhs) {
215        Ok(match (lhs, rhs){
216            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
217            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
218            _ => Err(Error::TypeError())?
219        })
220    }
221
222    /// Remainder `a % b`
223    fn rem(lhs, rhs) {
224        Ok(match (lhs, rhs) {
225            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
226            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
227            _ => Err(Error::TypeError())?,
228        })
229    }
230
231    /// Addition `a + b`
232    fn add(lhs, rhs) {
233        Ok(match (lhs, rhs) {
234            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
235            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
236            (ConValue::String(a), ConValue::String(b)) => (a.to_string() + &b.to_string()).into(),
237            _ => Err(Error::TypeError())?
238        })
239    }
240
241    /// Subtraction `a - b`
242    fn sub(lhs, rhs) {
243        Ok(match (lhs, rhs) {
244            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
245            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b),
246            _ => Err(Error::TypeError())?,
247        })
248    }
249
250    /// Shift Left `a << b`
251    fn shl(lhs, rhs) {
252        Ok(match (lhs, rhs) {
253            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
254            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b),
255            _ => Err(Error::TypeError())?,
256        })
257    }
258
259    /// Shift Right `a >> b`
260    fn shr(lhs, rhs) {
261        Ok(match (lhs, rhs) {
262            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
263            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b),
264            _ => Err(Error::TypeError())?,
265        })
266    }
267
268    /// Bitwise And `a & b`
269    fn and(lhs, rhs) {
270        Ok(match (lhs, rhs) {
271            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
272            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
273            (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
274            _ => Err(Error::TypeError())?,
275        })
276    }
277
278    /// Bitwise Or `a | b`
279    fn or(lhs, rhs) {
280        Ok(match (lhs, rhs) {
281            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
282            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
283            (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
284            _ => Err(Error::TypeError())?,
285        })
286    }
287
288    /// Bitwise Exclusive Or `a ^ b`
289    fn xor(lhs, rhs) {
290        Ok(match (lhs, rhs) {
291            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
292            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
293            (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
294            _ => Err(Error::TypeError())?,
295        })
296    }
297
298    #[allow(non_snake_case)]
299    fn RangeExc(start, end) {
300        Ok(ConValue::TupleStruct(Box::new((
301            "RangeExc", Box::new([start.clone(), end.clone()])
302        ))))
303    }
304
305    #[allow(non_snake_case)]
306    fn RangeInc(start, end) {
307        Ok(ConValue::TupleStruct(Box::new((
308            "RangeInc", Box::new([start.clone(), end.clone()])
309        ))))
310    }
311
312    #[allow(non_snake_case)]
313    fn RangeTo(end) {
314        Ok(ConValue::TupleStruct(Box::new((
315            "RangeInc", Box::new([end.clone()])
316        ))))
317    }
318
319    /// Negates the ConValue
320    fn neg(tail) {
321        Ok(match tail {
322            ConValue::Empty => ConValue::Empty,
323            ConValue::Int(v) => ConValue::Int(v.wrapping_neg()),
324            ConValue::Float(v) => ConValue::Float(-v),
325            _ => Err(Error::TypeError())?,
326        })
327    }
328
329    /// Inverts the ConValue
330    fn not(tail) {
331        Ok(match tail {
332            ConValue::Empty => ConValue::Empty,
333            ConValue::Int(v) => ConValue::Int(!v),
334            ConValue::Bool(v) => ConValue::Bool(!v),
335            _ => Err(Error::TypeError())?,
336        })
337    }
338
339    /// Compares two values
340    fn cmp(head, tail) {
341        Ok(ConValue::Int(match (head, tail) {
342            (ConValue::Int(a), ConValue::Int(b)) => a.cmp(b) as _,
343            (ConValue::Bool(a), ConValue::Bool(b)) => a.cmp(b) as _,
344            (ConValue::Char(a), ConValue::Char(b)) => a.cmp(b) as _,
345            (ConValue::String(a), ConValue::String(b)) => a.cmp(b) as _,
346            _ => Err(error_format!("Incomparable values: {head}, {tail}"))?
347        }))
348    }
349
350    /// Does the opposite of `&`
351    fn deref(tail) @env {
352        Ok(match tail {
353            ConValue::Ref(v) => env.get_id(*v).cloned().ok_or(Error::StackOverflow(*v))?,
354            _ => tail.clone(),
355        })
356    }
357];