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 std::fmt::Display for Builtin {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        f.write_str(self.desc)
47    }
48}
49
50impl super::Callable for Builtin {
51    fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
52        (self.func)(interpreter, args)
53    }
54
55    fn name(&self) -> cl_ast::Sym {
56        self.name.into()
57    }
58}
59
60/// Turns a function definition into a [Builtin].
61///
62/// ```rust
63/// # use cl_interpret::{builtin::builtin, convalue::ConValue};
64/// let my_builtin = builtin! {
65///     /// Use the `@env` suffix to bind the environment!
66///     /// (needed for recursive calls)
67///     fn my_builtin(ConValue::Bool(b), rest @ ..) @env {
68///         // This is all Rust code!
69///         eprintln!("my_builtin({b}, ..)");
70///         match rest {
71///             [] => Ok(ConValue::Empty),
72///             _ => my_builtin(env, rest), // Can be called as a normal function!
73///         }
74///     }
75/// };
76/// ```
77pub macro builtin(
78    $(#[$($meta:tt)*])*
79    fn $name:ident ($($arg:pat),*$(,)?) $(@$env:tt)? $body:block
80) {{
81    $(#[$($meta)*])*
82    fn $name(_env: &mut Environment, _args: &[ConValue]) -> IResult<ConValue> {
83        // Set up the builtin! environment
84        $(#[allow(unused)]let $env = _env;)?
85        // Allow for single argument `fn foo(args @ ..)` pattern
86        #[allow(clippy::redundant_at_rest_pattern, irrefutable_let_patterns)]
87        let [$($arg),*] = _args else {
88            Err($crate::error::Error::TypeError())?
89        };
90        $body.map(Into::into)
91    }
92    Builtin {
93        name: stringify!($name),
94        desc: stringify![builtin fn $name($($arg),*)],
95        func: &$name,
96    }
97}}
98
99/// Constructs an array of [Builtin]s from pseudo-function definitions
100pub macro builtins($(
101    $(#[$($meta:tt)*])*
102    fn $name:ident ($($args:tt)*) $(@$env:tt)? $body:block
103)*) {
104    [$(builtin!($(#[$($meta)*])* fn $name ($($args)*) $(@$env)? $body)),*]
105}
106
107/// Creates an [Error::BuiltinError] using interpolation of runtime expressions.
108/// See [std::format].
109pub macro error_format ($($t:tt)*) {
110    $crate::error::Error::BuiltinError(format!($($t)*))
111}
112
113pub const Builtins: &[Builtin] = &builtins![
114    /// Unstable variadic format function
115    fn fmt(args @ ..) {
116        use std::fmt::Write;
117        let mut out = String::new();
118        if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) {
119            eprintln!("{e}");
120        }
121        Ok(out)
122    }
123
124    /// Prints the arguments in-order, with no separators
125    fn print(args @ ..) {
126        let mut out = stdout().lock();
127        args.iter().try_for_each(|arg| write!(out, "{arg}") ).ok();
128        Ok(())
129    }
130
131    /// Prints the arguments in-order, followed by a newline
132    fn println(args @ ..) {
133        let mut out = stdout().lock();
134        args.iter().try_for_each(|arg| write!(out, "{arg}") ).ok();
135        writeln!(out).ok();
136        Ok(())
137    }
138
139    /// Debug-prints the argument, returning a copy
140    fn dbg(arg) {
141        println!("{arg:?}");
142        Ok(arg.clone())
143    }
144
145    /// Debug-prints the argument
146    fn dbgp(args @ ..) {
147        let mut out = stdout().lock();
148        args.iter().try_for_each(|arg| writeln!(out, "{arg:#?}") ).ok();
149        Ok(())
150    }
151
152    fn panic(args @ ..) @env {
153        use std::fmt::Write;
154        let mut out = String::new();
155        if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) {
156            println!("{e}");
157        }
158        let mut stdout = stdout().lock();
159        write!(stdout, "Explicit panic: `").ok();
160        args.iter().try_for_each(|arg| write!(stdout, "{arg}") ).ok();
161        writeln!(stdout, "`").ok();
162        Err(Error::Panic(out))?;
163        Ok(())
164    }
165
166    /// Dumps the environment
167    fn dump() @env {
168        println!("{env}");
169        Ok(())
170    }
171
172    /// Gets all global variables in the environment
173    fn globals() @env {
174        let globals = env.globals();
175        Ok(ConValue::Slice(globals.base, globals.binds.len()))
176    }
177
178    fn builtins() @env {
179        let len = env.globals().binds.len();
180        for builtin in 0..len {
181            if let Some(value @ ConValue::Builtin(_)) = env.get_id(builtin) {
182                println!("{builtin}: {value}")
183            }
184        }
185        Ok(())
186    }
187
188    fn alloca(ConValue::Int(len)) @env {
189        Ok(env.alloca(ConValue::Empty, *len as usize))
190    }
191
192    /// Returns the length of the input list as a [ConValue::Int]
193    fn len(list) @env {
194        Ok(match list {
195            ConValue::Empty => 0,
196            ConValue::Str(s) => s.chars().count() as _,
197            ConValue::String(s) => s.chars().count() as _,
198            ConValue::Ref(r) => {
199                return len(env, &[env.get_id(*r).ok_or(Error::StackOverflow(*r))?.clone()])
200            }
201            ConValue::Slice(_, len) => *len as _,
202            ConValue::Array(arr) => arr.len() as _,
203            ConValue::Tuple(t) => t.len() as _,
204            _ => Err(Error::TypeError())?,
205        })
206    }
207
208    fn push(ConValue::Ref(index), item) @env{
209        let Some(ConValue::Array(v)) = env.get_id_mut(*index) else {
210            Err(Error::TypeError())?
211        };
212
213        let mut items = std::mem::take(v).into_vec();
214        items.push(item.clone());
215        *v = items.into_boxed_slice();
216
217        Ok(ConValue::Empty)
218    }
219
220    fn chars(string) @env {
221        Ok(match string {
222            ConValue::Str(s) => ConValue::Array(s.chars().map(Into::into).collect()),
223            ConValue::String(s) => ConValue::Array(s.chars().map(Into::into).collect()),
224            ConValue::Ref(r) => {
225                return chars(env, &[env.get_id(*r).ok_or(Error::StackOverflow(*r))?.clone()])
226            }
227            _ => Err(Error::TypeError())?,
228        })
229    }
230
231    fn dump_symbols() {
232        println!("{}", cl_structures::intern::string_interner::StringInterner::global());
233        Ok(ConValue::Empty)
234    }
235
236    fn slice_of(ConValue::Ref(arr), ConValue::Int(start)) {
237        Ok(ConValue::Slice(*arr, *start as usize))
238    }
239
240    /// Returns a shark
241    fn shark() {
242        Ok('\u{1f988}')
243    }
244];
245
246pub const Math: &[Builtin] = &builtins![
247    /// Multiplication `a * b`
248    fn mul(lhs, rhs) {
249        Ok(match (lhs, rhs) {
250            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
251            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
252            _ => Err(Error::TypeError())?
253        })
254    }
255
256    /// Division `a / b`
257    fn div(lhs, rhs) {
258        Ok(match (lhs, rhs){
259            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
260            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
261            _ => Err(Error::TypeError())?
262        })
263    }
264
265    /// Remainder `a % b`
266    fn rem(lhs, rhs) {
267        Ok(match (lhs, rhs) {
268            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
269            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
270            _ => Err(Error::TypeError())?,
271        })
272    }
273
274    /// Addition `a + b`
275    fn add(lhs, rhs) {
276        Ok(match (lhs, rhs) {
277            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
278            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
279            (ConValue::Str(a), ConValue::Str(b)) => (a.to_string() + b).into(),
280            (ConValue::Str(a), ConValue::String(b)) => (a.to_string() + b).into(),
281            (ConValue::String(a), ConValue::Str(b)) => (a.to_string() + b).into(),
282            (ConValue::String(a), ConValue::String(b)) => (a.to_string() + b).into(),
283            (ConValue::Str(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(*c); s.into() }
284            (ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(*c); s.into() }
285            (ConValue::Char(a), ConValue::Char(b)) => {
286                ConValue::String([a, b].into_iter().collect::<String>())
287            }
288            _ => Err(Error::TypeError())?
289        })
290    }
291
292    /// Subtraction `a - b`
293    fn sub(lhs, rhs) {
294        Ok(match (lhs, rhs) {
295            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
296            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b),
297            _ => Err(Error::TypeError())?,
298        })
299    }
300
301    /// Shift Left `a << b`
302    fn shl(lhs, rhs) {
303        Ok(match (lhs, rhs) {
304            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
305            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b),
306            _ => Err(Error::TypeError())?,
307        })
308    }
309
310    /// Shift Right `a >> b`
311    fn shr(lhs, rhs) {
312        Ok(match (lhs, rhs) {
313            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
314            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b),
315            _ => Err(Error::TypeError())?,
316        })
317    }
318
319    /// Bitwise And `a & b`
320    fn and(lhs, rhs) {
321        Ok(match (lhs, rhs) {
322            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
323            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
324            (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
325            _ => Err(Error::TypeError())?,
326        })
327    }
328
329    /// Bitwise Or `a | b`
330    fn or(lhs, rhs) {
331        Ok(match (lhs, rhs) {
332            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
333            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
334            (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
335            _ => Err(Error::TypeError())?,
336        })
337    }
338
339    /// Bitwise Exclusive Or `a ^ b`
340    fn xor(lhs, rhs) {
341        Ok(match (lhs, rhs) {
342            (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
343            (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
344            (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
345            _ => Err(Error::TypeError())?,
346        })
347    }
348
349    #[allow(non_snake_case)]
350    fn RangeExc(start, end) @env {
351        Ok(ConValue::TupleStruct("RangeExc".into(), Box::new(Box::new([start.clone(), end.clone()]))))
352    }
353
354    #[allow(non_snake_case)]
355    fn RangeInc(start, end) @env {
356        Ok(ConValue::TupleStruct("RangeInc".into(), Box::new(Box::new([start.clone(), end.clone()]))))
357    }
358
359    #[allow(non_snake_case)]
360    fn RangeTo(end) @env {
361        Ok(ConValue::TupleStruct("RangeTo".into(), Box::new(Box::new([end.clone()]))))
362    }
363
364    #[allow(non_snake_case)]
365    fn RangeToInc(end) @env {
366        Ok(ConValue::TupleStruct("RangeToInc".into(), Box::new(Box::new([end.clone()]))))
367    }
368
369    /// Negates the ConValue
370    fn neg(tail) {
371        Ok(match tail {
372            ConValue::Empty => ConValue::Empty,
373            ConValue::Int(v) => ConValue::Int(v.wrapping_neg()),
374            ConValue::Float(v) => ConValue::Float(-v),
375            _ => Err(Error::TypeError())?,
376        })
377    }
378
379    /// Inverts the ConValue
380    fn not(tail) {
381        Ok(match tail {
382            ConValue::Empty => ConValue::Empty,
383            ConValue::Int(v) => ConValue::Int(!v),
384            ConValue::Bool(v) => ConValue::Bool(!v),
385            _ => Err(Error::TypeError())?,
386        })
387    }
388
389    /// Compares two values
390    fn cmp(head, tail) {
391        Ok(ConValue::Int(match (head, tail) {
392            (ConValue::Int(a), ConValue::Int(b)) => a.cmp(b) as _,
393            (ConValue::Bool(a), ConValue::Bool(b)) => a.cmp(b) as _,
394            (ConValue::Char(a), ConValue::Char(b)) => a.cmp(b) as _,
395            (ConValue::Str(a), ConValue::Str(b)) => a.cmp(b) as _,
396            (ConValue::Str(a), ConValue::String(b)) => a.to_ref().cmp(b.as_str()) as _,
397            (ConValue::String(a), ConValue::Str(b)) => a.as_str().cmp(b.to_ref()) as _,
398            (ConValue::String(a), ConValue::String(b)) => a.cmp(b) as _,
399            _ => Err(error_format!("Incomparable values: {head}, {tail}"))?
400        }))
401    }
402
403    /// Does the opposite of `&`
404    fn deref(tail) @env {
405        Ok(match tail {
406            ConValue::Ref(v) => env.get_id(*v).cloned().ok_or(Error::StackOverflow(*v))?,
407            _ => tail.clone(),
408        })
409    }
410];