Skip to main content

cl_interpret/
builtin.rs

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