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        let mut value = list;
235        while let ConValue::Ref(r) = value {
236            value = r.get(env)?;
237        }
238        Ok(match value {
239            ConValue::Empty => 0,
240            ConValue::Str(s) => s.chars().count() as _,
241            ConValue::String(s) => s.chars().count() as _,
242            ConValue::Slice(_, len) => *len as _,
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.typename()))?,
246        })
247    }
248
249    fn push(ConValue::Ref(index), item) @env{
250        let v = match index.get_mut(env)? {
251            ConValue::Array(v) => v,
252            other => Err(Error::TypeError("An array", other.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        let mut value = string;
277        while let ConValue::Ref(r) = value {
278            value = r.get(env)?;
279        }
280        Ok(match value {
281            ConValue::Str(s) => ConValue::Array(s.chars().map(Into::into).collect()),
282            ConValue::String(s) => ConValue::Array(s.chars().map(Into::into).collect()),
283            _ => Err(Error::TypeError("string", string.typename()))?,
284        })
285    }
286
287    /// Invokes a function with the given arguments
288    fn invoke(function, args) @env {
289        match args {
290            ConValue::Empty => function.call(env, &[]),
291            ConValue::Array(args) | ConValue::Tuple(args) => function.call(env, args),
292            _ => function.call(env, std::slice::from_ref(args)),
293        }
294    }
295
296    fn dump_symbols() {
297        println!("{}", cl_structures::intern::string_interner::StringInterner::global());
298        Ok(ConValue::Empty)
299    }
300
301    /// Builtin hex function to use when hex.cl isn't loaded
302    fn fmt_hex(ConValue::Int(v)) {
303        Ok(format!("{v:x}"))
304    }
305
306    /// Builtin oct function to use when hex.cl isn't loaded
307    fn fmt_oct(ConValue::Int(v)) {
308        Ok(format!("{v:o}"))
309    }
310
311    /// Builtin bin function to use when hex.cl isn't loaded
312    fn fmt_bin(ConValue::Int(v)) {
313        Ok(format!("{v:b}"))
314    }
315
316    fn catch_panic(lambda, args @ ..) @env {
317        match lambda.call(env, args) {
318            Err(Error { kind: ErrorKind::Panic(e, ..), ..}) => {
319                println!("Caught panic!");
320                Ok(ConValue::String(e))
321            },
322            other => other,
323        }
324    }
325
326    /// Returns a shark
327    fn shark() {
328        Ok('\u{1f988}')
329    }
330];
331
332pub const Math: &[Builtin] = &builtins![
333    /// Multiplication `a * b`
334    fn mul(lhs, rhs) {
335        Ok(match (lhs, rhs) {
336            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
337            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
338            _ => Err(Error::TypeError("type implements Mul", lhs.typename()))?,
339        })
340    }
341
342    /// Division `a / b`
343    fn div(lhs, rhs) {
344        Ok(match (lhs, rhs){
345            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
346            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
347            _ => Err(Error::TypeError("type implements Div", lhs.typename()))?,
348        })
349    }
350
351    /// Remainder `a % b`
352    fn rem(lhs, rhs) {
353        Ok(match (lhs, rhs) {
354            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
355            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
356            _ => Err(Error::TypeError("type implements Rem", lhs.typename()))?,
357        })
358    }
359
360    /// Addition `a + b`
361    fn add(lhs, rhs) {
362        Ok(match (lhs, rhs) {
363            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
364            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
365            (ConValue::Str(a), ConValue::Str(b)) => (a.to_string() + b).into(),
366            (ConValue::Str(a), ConValue::String(b)) => (a.to_string() + b).into(),
367            (ConValue::String(a), ConValue::Str(b)) => (a.to_string() + b).into(),
368            (ConValue::String(a), ConValue::String(b)) => (a.to_string() + b).into(),
369            (ConValue::Str(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(*c); s.into() }
370            (ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(*c); s.into() }
371            (ConValue::Char(a), ConValue::Char(b)) => {
372                ConValue::String([a, b].into_iter().collect())
373            }
374            _ => Err(Error::TypeError("type implements Add", lhs.typename()))?,
375        })
376    }
377
378    /// Subtraction `a - b`
379    fn sub(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            _ => Err(Error::TypeError("type implements Sub", lhs.typename()))?,
384        })
385    }
386
387    /// Shift Left `a << b`
388    fn shl(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::Int(a), b) => Err(Error::TypeError("int", b.typename()))?,
393            _ => Err(Error::TypeError("type implements Shl", lhs.typename()))?,
394        })
395    }
396
397    /// Shift Right `a >> b`
398    fn shr(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::Int(a), b) => Err(Error::TypeError("int", b.typename()))?,
403            _ => Err(Error::TypeError("type implements Shr", lhs.typename()))?,
404        })
405    }
406
407    /// Bitwise And `a & b`
408    fn and(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 BitAnd", lhs.typename()))?,
414        })
415    }
416
417    /// Bitwise Or `a | b`
418    fn or(lhs, rhs) {
419        Ok(match (lhs, rhs) {
420            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
421            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
422            (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
423            _ => Err(Error::TypeError("type implements BitOr", lhs.typename()))?,
424        })
425    }
426
427    /// Bitwise Exclusive Or `a ^ b`
428    fn xor(lhs, rhs) {
429        Ok(match (lhs, rhs) {
430            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
431            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
432            (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
433            _ => Err(Error::TypeError("type implements BitXor", lhs.typename()))?,
434        })
435    }
436
437    /// Negates the ConValue
438    fn neg(tail) {
439        Ok(match tail {
440            ConValue::Empty => ConValue::Empty,
441            ConValue::Int(v) => ConValue::Int(-v),
442            ConValue::Float(v) => ConValue::Float(-v),
443            _ => Err(Error::TypeError("type implements Neg", tail.typename()))?,
444        })
445    }
446
447    /// Inverts the ConValue
448    fn not(tail) {
449        Ok(match tail {
450            ConValue::Empty => ConValue::Empty,
451            ConValue::Int(v) => ConValue::Int(!v),
452            ConValue::Bool(v) => ConValue::Bool(!v),
453            _ => Err(Error::TypeError("type implements Not", tail.typename()))?,
454        })
455    }
456
457    /// Compares two values
458    fn cmp(head, tail) {
459        Ok(ConValue::Int(match (head, tail) {
460            (ConValue::Int(a), ConValue::Int(b)) => a.cmp(b) as _,
461            (ConValue::Bool(a), ConValue::Bool(b)) => a.cmp(b) as _,
462            (ConValue::Char(a), ConValue::Char(b)) => a.cmp(b) as _,
463            (ConValue::Str(a), ConValue::Str(b)) => a.cmp(b) as _,
464            (ConValue::Str(a), ConValue::String(b)) => a.to_ref().cmp(b.as_str()) as _,
465            (ConValue::String(a), ConValue::Str(b)) => a.as_str().cmp(b.to_ref()) as _,
466            (ConValue::String(a), ConValue::String(b)) => a.cmp(b) as _,
467            _ => Err(error_format!("Incomparable values: {head}, {tail}"))?
468        }))
469    }
470
471    /// Does the opposite of `&`
472    fn deref(tail) @env {
473        let mut value = tail;
474        while let ConValue::Ref(r) = value {
475            value = r.get(env)?;
476        }
477        Ok(value.clone())
478    }
479];