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(concat!("(", $(stringify!($arg,),)* ")"), "something weird"))?
94        };
95        $body.map(Into::into)
96    }
97    Builtin {
98        name: stringify!($name),
99        desc: stringify![builtin fn $name($($arg),*)],
100        func: &$name,
101    }
102}}
103
104/// Constructs an array of [Builtin]s from pseudo-function definitions
105pub macro builtins($(
106    $(#[$($meta:tt)*])*
107    fn $name:ident ($($args:tt)*) $(@$env:tt)? $body:block
108)*) {
109    [$(builtin!($(#[$($meta)*])* fn $name ($($args)*) $(@$env)? $body)),*]
110}
111
112/// Creates an [Error::BuiltinError] using interpolation of runtime expressions.
113/// See [std::format].
114pub macro error_format ($($t:tt)*) {
115    $crate::error::Error::BuiltinError(format!($($t)*))
116}
117
118pub const Builtins: &[Builtin] = &builtins![
119    /// Unstable variadic format function
120    fn fmt(args @ ..) @env {
121        use std::fmt::Write;
122        let mut out = String::new();
123
124        for mut arg in args.iter() {
125            while let ConValue::Ref(r) = arg {
126                arg = r.get(env)?;
127            }
128            if let Err(e) = write!(out, "{arg}") {
129                eprintln!("{e}");
130            }
131        }
132        Ok(out)
133    }
134
135    /// Prints the arguments in-order, with no separators
136    fn print(args @ ..) @env {
137        let mut out = stdout().lock();
138        for mut arg in args.iter() {
139            while let ConValue::Ref(r) = arg {
140                arg = r.get(env)?;
141            }
142            write!(out, "{arg}").ok();
143        }
144        Ok(())
145    }
146
147    /// Prints the arguments in-order, followed by a newline
148    fn println(args @ ..) @env {
149        let mut out = stdout().lock();
150        for mut arg in args.iter() {
151            while let ConValue::Ref(r) = arg {
152                arg = r.get(env)?;
153            }
154            write!(out, "{arg}").ok();
155        }
156        writeln!(out).ok();
157        Ok(())
158    }
159
160    /// Debug-prints the argument, returning a copy
161    fn dbg(arg) {
162        println!("{arg:?}");
163        Ok(arg.clone())
164    }
165
166    /// Debug-prints the argument
167    fn dbgp(args @ ..) {
168        let mut out = stdout().lock();
169        args.iter().try_for_each(|arg| writeln!(out, "{arg:#?}") ).ok();
170        Ok(())
171    }
172
173    fn bind(ConValue::Str(name), value) @env {
174        env.bind(*name, value.clone());
175        Ok(())
176    }
177
178    /// Constructs a reference from a raw integer
179    fn raw_ref(ConValue::Int(index)) {
180        Ok(ConValue::Ref(Place::from_index(*index as _)))
181    }
182
183    fn panic(args @ ..) @env {
184        use std::fmt::Write;
185        let mut stdout = stdout().lock();
186        let mut out = String::from("Explicit panic: ");
187        if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) {
188            writeln!(stdout, "{e}").ok();
189        }
190        writeln!(stdout, "{out}");
191        Err(Error::Panic(out))?;
192        Ok(())
193    }
194
195    fn todo(args @ ..) @env {
196        use std::fmt::Write;
197        let mut stdout = stdout().lock();
198        let mut out = String::from("Not yet implemented: ");
199        if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) {
200            writeln!(stdout, "{e}").ok();
201        }
202        writeln!(stdout, "{out}");
203        Err(Error::Panic(out))?;
204        Ok(())
205    }
206
207    /// Dumps the environment
208    fn dump() @env {
209        println!("{env}");
210        Ok(())
211    }
212
213    fn backtrace() @env {
214        println!("Backtrace:\n{}", env.backtrace());
215        Ok(())
216    }
217
218    fn host_backtrace() {
219        println!("Host backtrace:\n{}", std::backtrace::Backtrace::force_capture());
220        Ok(())
221    }
222
223    fn builtins() @env {
224        let len = env.globals().binds.len();
225        for builtin in 0..len {
226            if let Some(value @ ConValue::Builtin(_)) = env.get_id(builtin) {
227                println!("{builtin}: {value}")
228            }
229        }
230        Ok(())
231    }
232
233    /// Returns the length of the input list as a [ConValue::Int]
234    fn len(list) @env {
235        Ok(match list.dereference_in(env)? {
236            ConValue::Empty => 0,
237            ConValue::Str(s) => s.chars().count() as _,
238            ConValue::String(s) => s.chars().count() as _,
239            ConValue::Slice(_, len) => *len as _,
240            ConValue::Array(arr) => arr.len() as _,
241            ConValue::Tuple(t) => t.len() as _,
242            other => Err(Error::TypeError("A type with a length", other.typename()))?,
243        })
244    }
245
246    fn push(ConValue::Ref(index), item) @env{
247        let mut index = index.get_mut(env)?;
248        while let ConValue::Ref(r) = index {
249            index = r.clone().get_mut(env)?;
250        }
251        let ConValue::Array(v) = index else {
252            Err(Error::TypeError("An array", index.typename()))?
253        };
254
255        let mut items = std::mem::take(v).into_vec();
256        items.push(item.clone());
257        *v = items.into_boxed_slice();
258
259        Ok(ConValue::Empty)
260    }
261
262    fn pop(ConValue::Ref(index)) @env {
263        let v = match index.get_mut(env)? {
264            ConValue::Array(v) => v,
265            other => Err(Error::TypeError("An array", other.typename()))?,
266        };
267
268        let mut items = std::mem::take(v).into_vec();
269        let out = items.pop().unwrap_or(ConValue::Empty);
270        *v = items.into_boxed_slice();
271
272        Ok(out)
273    }
274
275    fn chars(string) @env {
276        Ok(match string.dereference_in(env)? {
277            ConValue::Str(s) => ConValue::Array(s.chars().map(Into::into).collect()),
278            ConValue::String(s) => ConValue::Array(s.chars().map(Into::into).collect()),
279            _ => Err(Error::TypeError("string", string.typename()))?,
280        })
281    }
282
283    /// Invokes a function with the given arguments
284    fn invoke(function, args) @env {
285        match args {
286            ConValue::Empty => function.call(env, &[]),
287            ConValue::Array(args) | ConValue::Tuple(args) => function.call(env, args),
288            _ => function.call(env, std::slice::from_ref(args)),
289        }
290    }
291
292    fn dump_symbols() {
293        println!("{}", cl_structures::intern::string_interner::StringInterner::global());
294        Ok(ConValue::Empty)
295    }
296
297    fn catch_panic(lambda, args @ ..) @env {
298        match lambda.call(env, args) {
299            Err(Error { kind: ErrorKind::Panic(e, ..), ..}) => {
300                println!("Caught panic!");
301                Ok(ConValue::String(e))
302            },
303            other => other,
304        }
305    }
306
307    /// Returns a shark
308    fn shark() {
309        Ok('\u{1f988}')
310    }
311];
312
313pub const Math: &[Builtin] = &builtins![
314    /// Multiplication `a * b`
315    fn mul(lhs, rhs) {
316        Ok(match (lhs, rhs) {
317            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
318            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
319            _ => Err(Error::TypeError("type implements Mul", lhs.typename()))?,
320        })
321    }
322
323    /// Division `a / b`
324    fn div(lhs, rhs) {
325        Ok(match (lhs, rhs){
326            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
327            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
328            _ => Err(Error::TypeError("type implements Div", lhs.typename()))?,
329        })
330    }
331
332    /// Remainder `a % b`
333    fn rem(lhs, rhs) {
334        Ok(match (lhs, rhs) {
335            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
336            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
337            _ => Err(Error::TypeError("type implements Rem", lhs.typename()))?,
338        })
339    }
340
341    /// Addition `a + b`
342    fn add(lhs, rhs) {
343        Ok(match (lhs, rhs) {
344            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
345            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
346            (ConValue::Str(a), ConValue::Str(b)) => (a.to_string() + b).into(),
347            (ConValue::Str(a), ConValue::String(b)) => (a.to_string() + b).into(),
348            (ConValue::String(a), ConValue::Str(b)) => (a.to_string() + b).into(),
349            (ConValue::String(a), ConValue::String(b)) => (a.to_string() + b).into(),
350            (ConValue::Str(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(*c); s.into() }
351            (ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(*c); s.into() }
352            (ConValue::Char(a), ConValue::Char(b)) => {
353                ConValue::String([a, b].into_iter().collect())
354            }
355            _ => Err(Error::TypeError("type implements Add", lhs.typename()))?,
356        })
357    }
358
359    /// Subtraction `a - b`
360    fn sub(lhs, rhs) {
361        Ok(match (lhs, rhs) {
362            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
363            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b),
364            _ => Err(Error::TypeError("type implements Sub", lhs.typename()))?,
365        })
366    }
367
368    /// Shift Left `a << b`
369    fn shl(lhs, rhs) {
370        Ok(match (lhs, rhs) {
371            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
372            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b),
373            (ConValue::Int(a), b) => Err(Error::TypeError("int", b.typename()))?,
374            _ => Err(Error::TypeError("type implements Shl", lhs.typename()))?,
375        })
376    }
377
378    /// Shift Right `a >> b`
379    fn shr(lhs, rhs) {
380        Ok(match (lhs, rhs) {
381            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
382            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b),
383            (ConValue::Int(a), b) => Err(Error::TypeError("int", b.typename()))?,
384            _ => Err(Error::TypeError("type implements Shr", lhs.typename()))?,
385        })
386    }
387
388    /// Bitwise And `a & b`
389    fn and(lhs, rhs) {
390        Ok(match (lhs, rhs) {
391            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
392            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
393            (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
394            _ => Err(Error::TypeError("type implements BitAnd", lhs.typename()))?,
395        })
396    }
397
398    /// Bitwise Or `a | b`
399    fn or(lhs, rhs) {
400        Ok(match (lhs, rhs) {
401            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
402            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
403            (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
404            _ => Err(Error::TypeError("type implements BitOr", lhs.typename()))?,
405        })
406    }
407
408    /// Bitwise Exclusive Or `a ^ b`
409    fn xor(lhs, rhs) {
410        Ok(match (lhs, rhs) {
411            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
412            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
413            (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
414            _ => Err(Error::TypeError("type implements BitXor", lhs.typename()))?,
415        })
416    }
417
418    /// Negates the ConValue
419    fn neg(tail) {
420        Ok(match tail {
421            ConValue::Empty => ConValue::Empty,
422            ConValue::Int(v) => ConValue::Int(-v),
423            ConValue::Float(v) => ConValue::Float(-v),
424            _ => Err(Error::TypeError("type implements Neg", tail.typename()))?,
425        })
426    }
427
428    /// Inverts the ConValue
429    fn not(tail) {
430        Ok(match tail {
431            ConValue::Empty => ConValue::Empty,
432            ConValue::Int(v) => ConValue::Int(!v),
433            ConValue::Bool(v) => ConValue::Bool(!v),
434            _ => Err(Error::TypeError("type implements Not", tail.typename()))?,
435        })
436    }
437
438    /// Compares two values
439    fn cmp(head, tail) {
440        Ok(ConValue::Int(match (head, tail) {
441            (ConValue::Int(a), ConValue::Int(b)) => a.cmp(b) as _,
442            (ConValue::Bool(a), ConValue::Bool(b)) => a.cmp(b) as _,
443            (ConValue::Char(a), ConValue::Char(b)) => a.cmp(b) as _,
444            (ConValue::Str(a), ConValue::Str(b)) => a.cmp(b) as _,
445            (ConValue::Str(a), ConValue::String(b)) => a.to_ref().cmp(b.as_str()) as _,
446            (ConValue::String(a), ConValue::Str(b)) => a.as_str().cmp(b.to_ref()) as _,
447            (ConValue::String(a), ConValue::String(b)) => a.cmp(b) as _,
448            _ => Err(error_format!("Incomparable values: {head}, {tail}"))?
449        }))
450    }
451
452    /// Does the opposite of `&`
453    fn deref(tail) @env {
454        Ok(tail.dereference_in(env)?.clone())
455    }
456];