Skip to main content

cl_interpret/
builtin.rs

1//! Interpreter [Builtin] functions, and [builtin] macro to define them
2#![allow(non_upper_case_globals)]
3
4use cl_ast::types::Symbol;
5
6use crate::{
7    Callable,
8    convalue::ConValue,
9    env::Environment,
10    error::{Error, ErrorKind, IResult},
11    place::Place,
12};
13use std::io::{Write, stdout};
14
15/// A function built into the interpreter.
16#[derive(Clone, Copy)]
17pub struct Builtin {
18    /// An identifier to be used during registration
19    pub name: &'static str,
20    /// The signature, displayed when the builtin is printed
21    pub desc: &'static str,
22    /// The function to be run when called
23    pub func: &'static dyn Fn(&mut Environment, &[ConValue]) -> IResult<ConValue>,
24}
25
26impl Builtin {
27    /// Constructs a new Builtin
28    pub const fn new(
29        name: &'static str,
30        desc: &'static str,
31        func: &'static impl Fn(&mut Environment, &[ConValue]) -> IResult<ConValue>,
32    ) -> Builtin {
33        Builtin { name, desc, func }
34    }
35
36    pub const fn description(&self) -> &'static str {
37        self.desc
38    }
39}
40
41impl std::fmt::Debug for Builtin {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        f.debug_struct("Builtin")
44            .field("description", &self.desc)
45            .finish_non_exhaustive()
46    }
47}
48
49impl std::fmt::Display for Builtin {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        f.write_str(self.desc)
52    }
53}
54
55impl super::Callable for Builtin {
56    fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
57        (self.func)(interpreter, args)
58    }
59
60    fn name(&self) -> Option<Symbol> {
61        Some(self.name.into())
62    }
63}
64
65/// Turns a function definition into a [Builtin].
66///
67/// ```rust
68/// # use cl_interpret::{builtin::builtin, convalue::ConValue};
69/// let my_builtin = builtin! {
70///     /// Use the `@env` suffix to bind the environment!
71///     /// (needed for recursive calls)
72///     fn my_builtin(ConValue::Bool(b), rest @ ..) @env {
73///         // This is all Rust code!
74///         eprintln!("my_builtin({b}, ..)");
75///         match rest {
76///             [] => Ok(ConValue::Empty),
77///             _ => my_builtin(env, rest), // Can be called as a normal function!
78///         }
79///     }
80/// };
81/// ```
82pub macro builtin(
83    $(#[$($meta:tt)*])*
84    fn $name:ident ($($arg:pat),*$(,)?) $(@$env:tt)? $body:block
85) {{
86    $(#[$($meta)*])*
87    fn $name(_env: &mut Environment, _args: &[ConValue]) -> IResult<ConValue> {
88        // Set up the builtin! environment
89        $(#[allow(unused)]let $env = _env;)?
90        // Allow for single argument `fn foo(args @ ..)` pattern
91        #[allow(clippy::redundant_at_rest_pattern, irrefutable_let_patterns)]
92        let [$($arg),*] = _args else {
93            Err($crate::error::Error::TypeError(
94                concat!("(", $(stringify!($arg,),)* ")"),
95                $crate::typeinfo::Model::Any.intern()
96            ))?
97        };
98        $body.map(Into::into)
99    }
100    Builtin {
101        name: stringify!($name),
102        desc: stringify![builtin fn $name($($arg),*)],
103        func: &$name,
104    }
105}}
106
107/// Constructs an array of [Builtin]s from pseudo-function definitions
108pub macro builtins($(
109    $(#[$($meta:tt)*])*
110    fn $name:ident ($($args:tt)*) $(@$env:tt)? $body:block
111)*) {
112    [$(builtin!($(#[$($meta)*])* fn $name ($($args)*) $(@$env)? $body)),*]
113}
114
115/// Creates an [Error::BuiltinError] using interpolation of runtime expressions.
116/// See [std::format].
117pub macro error_format ($($t:tt)*) {
118    $crate::error::Error::BuiltinError(format!($($t)*))
119}
120
121pub const Builtins: &[Builtin] = &builtins![
122    /// Unstable variadic format function
123    fn fmt(args @ ..) @env {
124        use std::fmt::Write;
125        let mut out = String::new();
126
127        for mut arg in args.iter() {
128            while let ConValue::Ref(r) = arg {
129                arg = r.get(env)?;
130            }
131            if let Err(e) = write!(out, "{arg}") {
132                eprintln!("{e}");
133            }
134        }
135        Ok(out)
136    }
137
138    /// Prints the arguments in-order, with no separators
139    fn print(args @ ..) @env {
140        let mut out = stdout().lock();
141        for mut arg in args.iter() {
142            while let ConValue::Ref(r) = arg {
143                arg = r.get(env)?;
144            }
145            write!(out, "{arg}").ok();
146        }
147        Ok(())
148    }
149
150    /// Prints the arguments in-order, followed by a newline
151    fn println(args @ ..) @env {
152        let mut out = stdout().lock();
153        for mut arg in args.iter() {
154            while let ConValue::Ref(r) = arg {
155                arg = r.get(env)?;
156            }
157            write!(out, "{arg}").ok();
158        }
159        writeln!(out).ok();
160        Ok(())
161    }
162
163    /// Debug-prints the argument, returning a copy
164    fn dbg(arg) {
165        println!("{arg:?}");
166        Ok(arg.clone())
167    }
168
169    /// Debug-prints the argument
170    fn dbgp(args @ ..) {
171        let mut out = stdout().lock();
172        args.iter().try_for_each(|arg| writeln!(out, "{arg:#?}") ).ok();
173        Ok(())
174    }
175
176    fn bind(ConValue::Str(name), value) @env {
177        env.bind(*name, value.clone());
178        Ok(())
179    }
180
181    /// Constructs a reference from a raw integer
182    fn raw_ref(ConValue::Int(index)) {
183        Ok(ConValue::Ref(Place::from_index(*index as _)))
184    }
185
186    fn panic(args @ ..) @env {
187        use std::fmt::Write;
188        let mut stdout = stdout().lock();
189        let mut out = String::from("Explicit panic: ");
190        if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) {
191            writeln!(stdout, "{e}").ok();
192        }
193        writeln!(stdout, "{out}");
194        Err(Error::Panic(out))?;
195        Ok(())
196    }
197
198    fn todo(args @ ..) @env {
199        use std::fmt::Write;
200        let mut stdout = stdout().lock();
201        let mut out = String::from("Not yet implemented: ");
202        if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) {
203            writeln!(stdout, "{e}").ok();
204        }
205        writeln!(stdout, "{out}");
206        Err(Error::Panic(out))?;
207        Ok(())
208    }
209
210    /// Dumps the environment
211    fn dump() @env {
212        println!("{env}");
213        Ok(())
214    }
215
216    fn backtrace() @env {
217        println!("Backtrace:\n{}", env.backtrace());
218        Ok(())
219    }
220
221    fn host_backtrace() {
222        println!("Host backtrace:\n{}", std::backtrace::Backtrace::force_capture());
223        Ok(())
224    }
225
226    fn builtins() @env {
227        let len = env.globals().binds.len();
228        for builtin in 0..len {
229            if let Some(value @ ConValue::Builtin(_)) = env.get_id(builtin) {
230                println!("{builtin}: {value}")
231            }
232        }
233        Ok(())
234    }
235
236    /// Returns the length of the input list as a [ConValue::Int]
237    fn len(list) @env {
238        Ok(match list.dereference_in(env)? {
239            ConValue::Empty => 0,
240            ConValue::Str(s) => s.chars().count() as _,
241            ConValue::String(s) => s.chars().count() as _,
242            &ConValue::Slice(_, start, end) => end as i128 - start as i128,
243            ConValue::Array(arr) => arr.len() as _,
244            ConValue::Tuple(t) => t.len() as _,
245            other => Err(Error::TypeError("A type with a length", other.type_of()))?,
246        })
247    }
248
249    fn push(ConValue::Ref(index), item) @env{
250        let mut index = index.get_mut(env)?;
251        while let ConValue::Ref(r) = index {
252            index = r.clone().get_mut(env)?;
253        }
254        let ConValue::Array(v) = index else {
255            Err(Error::TypeError("An array", index.type_of()))?
256        };
257
258        let mut items = std::mem::take(v).into_vec();
259        items.push(item.clone());
260        *v = items.into_boxed_slice();
261
262        Ok(ConValue::Empty)
263    }
264
265    fn pop(ConValue::Ref(index)) @env {
266        let v = match index.get_mut(env)? {
267            ConValue::Array(v) => v,
268            other => Err(Error::TypeError("An array", other.type_of()))?,
269        };
270
271        let mut items = std::mem::take(v).into_vec();
272        let out = items.pop().unwrap_or(ConValue::Empty);
273        *v = items.into_boxed_slice();
274
275        Ok(out)
276    }
277
278    fn chars(string) @env {
279        Ok(match string.dereference_in(env)? {
280            ConValue::Str(s) => ConValue::Array(s.chars().map(Into::into).collect()),
281            ConValue::String(s) => ConValue::Array(s.chars().map(Into::into).collect()),
282            _ => Err(Error::TypeError("string", string.type_of()))?,
283        })
284    }
285
286    /// Invokes a function with the given arguments
287    fn invoke(function, args) @env {
288        match args {
289            ConValue::Empty => function.call(env, &[]),
290            ConValue::Array(args) | ConValue::Tuple(args) => function.call(env, args),
291            _ => function.call(env, std::slice::from_ref(args)),
292        }
293    }
294
295    fn dump_symbols() {
296        println!("{}", cl_structures::intern::string_interner::StringInterner::global());
297        Ok(ConValue::Empty)
298    }
299
300    fn catch_panic(lambda, args @ ..) @env {
301        match lambda.call(env, args) {
302            Err(Error { kind: ErrorKind::Panic(e, ..), ..}) => {
303                println!("Caught panic!");
304                Ok(ConValue::String(e))
305            },
306            other => other,
307        }
308    }
309
310    /// Returns a shark
311    fn shark() {
312        Ok('\u{1f988}')
313    }
314];
315
316pub const Math: &[Builtin] = &builtins![
317    /// Multiplication `a * b`
318    fn mul(lhs, rhs) {
319        Ok(match (lhs, rhs) {
320            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
321            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
322            _ => Err(Error::TypeError("type implements Mul", lhs.type_of()))?,
323        })
324    }
325
326    /// Division `a / b`
327    fn div(lhs, rhs) {
328        Ok(match (lhs, rhs){
329            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
330            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
331            _ => Err(Error::TypeError("type implements Div", lhs.type_of()))?,
332        })
333    }
334
335    /// Remainder `a % b`
336    fn rem(lhs, rhs) {
337        Ok(match (lhs, rhs) {
338            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
339            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
340            _ => Err(Error::TypeError("type implements Rem", lhs.type_of()))?,
341        })
342    }
343
344    /// Addition `a + b`
345    fn add(lhs, rhs) {
346        Ok(match (lhs, rhs) {
347            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
348            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
349            (ConValue::Str(a), ConValue::Str(b)) => (a.to_string() + b).into(),
350            (ConValue::Str(a), ConValue::String(b)) => (a.to_string() + b).into(),
351            (ConValue::String(a), ConValue::Str(b)) => (a.to_string() + b).into(),
352            (ConValue::String(a), ConValue::String(b)) => (a.to_string() + b).into(),
353            (ConValue::Str(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(*c); s.into() }
354            (ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(*c); s.into() }
355            (ConValue::Char(a), ConValue::Char(b)) => {
356                ConValue::String([a, b].into_iter().collect())
357            }
358            _ => Err(Error::TypeError("type implements Add", lhs.type_of()))?,
359        })
360    }
361
362    /// Subtraction `a - b`
363    fn sub(lhs, rhs) {
364        Ok(match (lhs, rhs) {
365            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
366            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b),
367            _ => Err(Error::TypeError("type implements Sub", lhs.type_of()))?,
368        })
369    }
370
371    /// Shift Left `a << b`
372    fn shl(lhs, rhs) {
373        Ok(match (lhs, rhs) {
374            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
375            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b),
376            (ConValue::Int(a), b) => Err(Error::TypeError("int", b.type_of()))?,
377            _ => Err(Error::TypeError("type implements Shl", lhs.type_of()))?,
378        })
379    }
380
381    /// Shift Right `a >> b`
382    fn shr(lhs, rhs) {
383        Ok(match (lhs, rhs) {
384            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
385            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b),
386            (ConValue::Int(a), b) => Err(Error::TypeError("int", b.type_of()))?,
387            _ => Err(Error::TypeError("type implements Shr", lhs.type_of()))?,
388        })
389    }
390
391    /// Bitwise And `a & b`
392    fn and(lhs, rhs) {
393        Ok(match (lhs, rhs) {
394            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
395            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
396            (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
397            _ => Err(Error::TypeError("type implements BitAnd", lhs.type_of()))?,
398        })
399    }
400
401    /// Bitwise Or `a | b`
402    fn or(lhs, rhs) {
403        Ok(match (lhs, rhs) {
404            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
405            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
406            (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
407            _ => Err(Error::TypeError("type implements BitOr", lhs.type_of()))?,
408        })
409    }
410
411    /// Bitwise Exclusive Or `a ^ b`
412    fn xor(lhs, rhs) {
413        Ok(match (lhs, rhs) {
414            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
415            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
416            (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
417            _ => Err(Error::TypeError("type implements BitXor", lhs.type_of()))?,
418        })
419    }
420
421    /// Negates the ConValue
422    fn neg(tail) {
423        Ok(match tail {
424            ConValue::Empty => ConValue::Empty,
425            ConValue::Int(v) => ConValue::Int(-v),
426            ConValue::Float(v) => ConValue::Float(-v),
427            _ => Err(Error::TypeError("type implements Neg", tail.type_of()))?,
428        })
429    }
430
431    /// Inverts the ConValue
432    fn not(tail) {
433        Ok(match tail {
434            ConValue::Empty => ConValue::Empty,
435            ConValue::Int(v) => ConValue::Int(!v),
436            ConValue::Bool(v) => ConValue::Bool(!v),
437            _ => Err(Error::TypeError("type implements Not", tail.type_of()))?,
438        })
439    }
440
441    /// Compares two values
442    fn cmp(head, tail) {
443        Ok(ConValue::Int(match (head, tail) {
444            (ConValue::Int(a), ConValue::Int(b)) => a.cmp(b) as _,
445            (ConValue::Bool(a), ConValue::Bool(b)) => a.cmp(b) as _,
446            (ConValue::Char(a), ConValue::Char(b)) => a.cmp(b) as _,
447            (ConValue::Str(a), ConValue::Str(b)) => a.cmp(b) as _,
448            (ConValue::Str(a), ConValue::String(b)) => a.to_ref().cmp(b.as_str()) as _,
449            (ConValue::String(a), ConValue::Str(b)) => a.as_str().cmp(b.to_ref()) as _,
450            (ConValue::String(a), ConValue::String(b)) => a.cmp(b) as _,
451            _ => Err(error_format!("Incomparable values: {head}, {tail}"))?
452        }))
453    }
454
455    /// Does the opposite of `&`
456    fn deref(tail) @env {
457        Ok(tail.dereference_in(env)?.clone())
458    }
459];