2025-01-31 03:34:45 -06:00
|
|
|
#![allow(non_upper_case_globals)]
|
2024-02-29 16:48:09 -06:00
|
|
|
|
2025-01-31 03:34:45 -06:00
|
|
|
use crate::{
|
2024-07-09 06:13:55 -05:00
|
|
|
convalue::ConValue,
|
2024-02-29 16:48:09 -06:00
|
|
|
env::Environment,
|
|
|
|
error::{Error, IResult},
|
|
|
|
};
|
2024-04-19 10:49:25 -05:00
|
|
|
use std::{
|
|
|
|
io::{stdout, Write},
|
2024-07-27 18:43:03 -05:00
|
|
|
slice,
|
2024-04-19 10:49:25 -05:00
|
|
|
};
|
2024-02-29 16:48:09 -06:00
|
|
|
|
2025-01-31 03:34:45 -06:00
|
|
|
/// A function built into the interpreter.
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
pub struct Builtin {
|
|
|
|
/// An identifier to be used during registration
|
2025-02-06 21:35:17 -06:00
|
|
|
pub name: &'static str,
|
2025-01-31 03:34:45 -06:00
|
|
|
/// The signature, displayed when the builtin is printed
|
2025-02-06 21:35:17 -06:00
|
|
|
pub desc: &'static str,
|
2025-01-31 03:34:45 -06:00
|
|
|
/// The function to be run when called
|
2025-02-06 21:35:17 -06:00
|
|
|
pub func: &'static dyn Fn(&mut Environment, &[ConValue]) -> IResult<ConValue>,
|
2025-01-31 03:34:45 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Builtin {
|
|
|
|
/// Constructs a new Builtin
|
|
|
|
pub const fn new(
|
|
|
|
name: &'static str,
|
|
|
|
desc: &'static str,
|
|
|
|
func: &'static impl Fn(&mut Environment, &[ConValue]) -> IResult<ConValue>,
|
|
|
|
) -> Builtin {
|
|
|
|
Builtin { name, desc, func }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const fn description(&self) -> &'static str {
|
|
|
|
self.desc
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Debug for Builtin {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
f.debug_struct("Builtin")
|
|
|
|
.field("description", &self.desc)
|
|
|
|
.finish_non_exhaustive()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl super::Callable for Builtin {
|
|
|
|
fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
|
|
|
|
(self.func)(interpreter, args)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn name(&self) -> cl_ast::Sym {
|
|
|
|
self.name.into()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Turns a function definition into a [Builtin].
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// # use cl_interpret::{builtin2::builtin, convalue::ConValue};
|
|
|
|
/// let my_builtin = builtin! {
|
|
|
|
/// /// Use the `@env` suffix to bind the environment!
|
|
|
|
/// /// (needed for recursive calls)
|
|
|
|
/// fn my_builtin(ConValue::Bool(b), rest @ ..) @env {
|
|
|
|
/// // This is all Rust code!
|
|
|
|
/// eprintln!("my_builtin({b}, ..)");
|
|
|
|
/// match rest {
|
|
|
|
/// [] => Ok(ConValue::Empty),
|
|
|
|
/// _ => my_builtin(env, rest), // Can be called as a normal function!
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
/// };
|
|
|
|
/// ```
|
|
|
|
pub macro builtin(
|
|
|
|
$(#[$($meta:tt)*])*
|
|
|
|
fn $name:ident ($($arg:pat),*$(,)?) $(@$env:tt)? $body:block
|
|
|
|
) {{
|
|
|
|
$(#[$($meta)*])*
|
|
|
|
fn $name(_env: &mut Environment, _args: &[ConValue]) -> IResult<ConValue> {
|
|
|
|
// Set up the builtin! environment
|
|
|
|
$(let $env = _env;)?
|
|
|
|
// Allow for single argument `fn foo(args @ ..)` pattern
|
|
|
|
#[allow(clippy::redundant_at_rest_pattern, irrefutable_let_patterns)]
|
|
|
|
let [$($arg),*] = _args else {
|
|
|
|
Err($crate::error::Error::TypeError)?
|
|
|
|
};
|
|
|
|
$body.map(Into::into)
|
|
|
|
}
|
|
|
|
Builtin {
|
|
|
|
name: stringify!($name),
|
|
|
|
desc: stringify![builtin fn $name($($arg),*)],
|
|
|
|
func: &$name,
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
|
|
|
|
/// Constructs an array of [Builtin]s from pseudo-function definitions
|
|
|
|
pub macro builtins($(
|
|
|
|
$(#[$($meta:tt)*])*
|
|
|
|
fn $name:ident ($($args:tt)*) $(@$env:tt)? $body:block
|
|
|
|
)*) {
|
|
|
|
[$(builtin!($(#[$($meta)*])* fn $name ($($args)*) $(@$env)? $body)),*]
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates an [Error::BuiltinDebug] using interpolation of runtime expressions.
|
|
|
|
/// See [std::format].
|
|
|
|
pub macro error_format ($($t:tt)*) {
|
|
|
|
$crate::error::Error::BuiltinDebug(format!($($t)*))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub const Builtins: &[Builtin] = &builtins![
|
2024-07-11 04:42:36 -05:00
|
|
|
/// Unstable variadic format function
|
2025-01-31 03:34:45 -06:00
|
|
|
fn fmt(args @ ..) {
|
2024-07-11 04:42:36 -05:00
|
|
|
use std::fmt::Write;
|
|
|
|
let mut out = String::new();
|
2025-01-31 03:34:45 -06:00
|
|
|
if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) {
|
|
|
|
eprintln!("{e}");
|
2024-07-11 04:42:36 -05:00
|
|
|
}
|
2025-01-31 03:34:45 -06:00
|
|
|
Ok(out)
|
2024-07-11 04:42:36 -05:00
|
|
|
}
|
|
|
|
|
2025-01-31 03:34:45 -06:00
|
|
|
/// Prints the arguments in-order, with no separators
|
|
|
|
fn print(args @ ..) {
|
2024-02-29 16:48:09 -06:00
|
|
|
let mut out = stdout().lock();
|
2025-01-31 03:34:45 -06:00
|
|
|
args.iter().try_for_each(|arg| write!(out, "{arg}") ).ok();
|
|
|
|
Ok(())
|
2024-07-09 06:13:05 -05:00
|
|
|
}
|
2024-07-11 04:43:25 -05:00
|
|
|
|
2025-01-31 03:34:45 -06:00
|
|
|
/// Prints the arguments in-order, followed by a newline
|
|
|
|
fn println(args @ ..) {
|
2024-07-09 06:13:05 -05:00
|
|
|
let mut out = stdout().lock();
|
2025-01-31 03:34:45 -06:00
|
|
|
args.iter().try_for_each(|arg| write!(out, "{arg}") ).ok();
|
2024-02-29 16:48:09 -06:00
|
|
|
writeln!(out).ok();
|
2025-01-31 03:34:45 -06:00
|
|
|
Ok(())
|
2024-02-29 16:48:09 -06:00
|
|
|
}
|
2024-07-11 04:43:25 -05:00
|
|
|
|
2025-01-31 03:34:45 -06:00
|
|
|
/// Debug-prints the argument, returning a copy
|
|
|
|
fn dbg(arg) {
|
|
|
|
println!("{arg:?}");
|
|
|
|
Ok(arg.clone())
|
2024-02-29 16:48:09 -06:00
|
|
|
}
|
2024-07-11 04:43:25 -05:00
|
|
|
|
2025-01-31 03:34:45 -06:00
|
|
|
/// Debug-prints the argument
|
|
|
|
fn dbgp(args @ ..) {
|
2025-01-29 04:16:28 -06:00
|
|
|
let mut out = stdout().lock();
|
2025-01-31 03:34:45 -06:00
|
|
|
args.iter().try_for_each(|arg| writeln!(out, "{arg:#?}") ).ok();
|
|
|
|
Ok(())
|
2025-01-29 04:16:28 -06:00
|
|
|
}
|
|
|
|
|
2025-01-31 03:34:45 -06:00
|
|
|
/// Dumps the environment
|
|
|
|
fn dump() @env {
|
|
|
|
println!("{env}");
|
|
|
|
Ok(())
|
2024-02-29 16:48:09 -06:00
|
|
|
}
|
2024-07-27 18:43:03 -05:00
|
|
|
|
2025-01-31 03:34:45 -06:00
|
|
|
fn builtins() @env {
|
|
|
|
for builtin in env.builtins().values().flatten() {
|
|
|
|
println!("{builtin}");
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the length of the input list as a [ConValue::Int]
|
|
|
|
fn len(list) @env {
|
|
|
|
Ok(match list {
|
2024-07-27 18:43:03 -05:00
|
|
|
ConValue::Empty => 0,
|
|
|
|
ConValue::String(s) => s.chars().count() as _,
|
2025-01-31 03:34:45 -06:00
|
|
|
ConValue::Ref(r) => return len(env, slice::from_ref(r.as_ref())),
|
2024-07-27 18:43:03 -05:00
|
|
|
ConValue::Array(t) => t.len() as _,
|
|
|
|
ConValue::Tuple(t) => t.len() as _,
|
|
|
|
ConValue::RangeExc(start, end) => (end - start) as _,
|
|
|
|
ConValue::RangeInc(start, end) => (end - start + 1) as _,
|
|
|
|
_ => Err(Error::TypeError)?,
|
2025-01-31 03:34:45 -06:00
|
|
|
})
|
2024-07-27 18:43:03 -05:00
|
|
|
}
|
2025-01-28 06:14:05 -06:00
|
|
|
|
2025-02-18 21:49:59 -06:00
|
|
|
fn dump_symbols() {
|
|
|
|
println!("{}", cl_structures::intern::string_interner::StringInterner::global());
|
|
|
|
Ok(ConValue::Empty)
|
|
|
|
}
|
|
|
|
|
2025-01-31 03:34:45 -06:00
|
|
|
/// Returns a shark
|
|
|
|
fn shark() {
|
|
|
|
Ok('\u{1f988}')
|
2025-01-10 06:51:08 -06:00
|
|
|
}
|
2025-01-31 03:34:45 -06:00
|
|
|
];
|
2025-01-29 04:16:28 -06:00
|
|
|
|
2025-01-31 03:34:45 -06:00
|
|
|
pub const Math: &[Builtin] = &builtins![
|
2024-02-29 16:48:09 -06:00
|
|
|
/// Multiplication `a * b`
|
2025-01-31 03:34:45 -06:00
|
|
|
fn mul(lhs, rhs) {
|
2024-02-29 16:48:09 -06:00
|
|
|
Ok(match (lhs, rhs) {
|
|
|
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
|
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
|
|
|
|
_ => Err(Error::TypeError)?
|
|
|
|
})
|
|
|
|
}
|
2024-07-11 04:43:25 -05:00
|
|
|
|
2024-02-29 16:48:09 -06:00
|
|
|
/// Division `a / b`
|
2025-01-31 03:34:45 -06:00
|
|
|
fn div(lhs, rhs) {
|
2024-02-29 16:48:09 -06:00
|
|
|
Ok(match (lhs, rhs){
|
|
|
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
|
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
|
|
|
|
_ => Err(Error::TypeError)?
|
|
|
|
})
|
|
|
|
}
|
2024-07-11 04:43:25 -05:00
|
|
|
|
2024-02-29 16:48:09 -06:00
|
|
|
/// Remainder `a % b`
|
2025-01-31 03:34:45 -06:00
|
|
|
fn rem(lhs, rhs) {
|
2024-02-29 16:48:09 -06:00
|
|
|
Ok(match (lhs, rhs) {
|
|
|
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
|
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
|
|
|
|
_ => Err(Error::TypeError)?,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Addition `a + b`
|
2025-01-31 03:34:45 -06:00
|
|
|
fn add(lhs, rhs) {
|
2024-02-29 16:48:09 -06:00
|
|
|
Ok(match (lhs, rhs) {
|
|
|
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
|
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
|
2024-04-24 19:34:29 -05:00
|
|
|
(ConValue::String(a), ConValue::String(b)) => (a.to_string() + &b.to_string()).into(),
|
2024-02-29 16:48:09 -06:00
|
|
|
_ => Err(Error::TypeError)?
|
|
|
|
})
|
|
|
|
}
|
2024-07-11 04:43:25 -05:00
|
|
|
|
2024-02-29 16:48:09 -06:00
|
|
|
/// Subtraction `a - b`
|
2025-01-31 03:34:45 -06:00
|
|
|
fn sub(lhs, rhs) {
|
2024-02-29 16:48:09 -06:00
|
|
|
Ok(match (lhs, rhs) {
|
|
|
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
|
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b),
|
|
|
|
_ => Err(Error::TypeError)?,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Shift Left `a << b`
|
2025-01-31 03:34:45 -06:00
|
|
|
fn shl(lhs, rhs) {
|
2024-02-29 16:48:09 -06:00
|
|
|
Ok(match (lhs, rhs) {
|
|
|
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
|
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b),
|
|
|
|
_ => Err(Error::TypeError)?,
|
|
|
|
})
|
|
|
|
}
|
2024-07-11 04:43:25 -05:00
|
|
|
|
2024-02-29 16:48:09 -06:00
|
|
|
/// Shift Right `a >> b`
|
2025-01-31 03:34:45 -06:00
|
|
|
fn shr(lhs, rhs) {
|
2024-02-29 16:48:09 -06:00
|
|
|
Ok(match (lhs, rhs) {
|
|
|
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
|
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b),
|
|
|
|
_ => Err(Error::TypeError)?,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Bitwise And `a & b`
|
2025-01-31 03:34:45 -06:00
|
|
|
fn and(lhs, rhs) {
|
2024-02-29 16:48:09 -06:00
|
|
|
Ok(match (lhs, rhs) {
|
|
|
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
|
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
|
|
|
|
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
|
|
|
|
_ => Err(Error::TypeError)?,
|
|
|
|
})
|
|
|
|
}
|
2024-07-11 04:43:25 -05:00
|
|
|
|
2024-02-29 16:48:09 -06:00
|
|
|
/// Bitwise Or `a | b`
|
2025-01-31 03:34:45 -06:00
|
|
|
fn or(lhs, rhs) {
|
2024-02-29 16:48:09 -06:00
|
|
|
Ok(match (lhs, rhs) {
|
|
|
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
|
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
|
|
|
|
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
|
|
|
|
_ => Err(Error::TypeError)?,
|
|
|
|
})
|
|
|
|
}
|
2024-07-11 04:43:25 -05:00
|
|
|
|
2024-02-29 16:48:09 -06:00
|
|
|
/// Bitwise Exclusive Or `a ^ b`
|
2025-01-31 03:34:45 -06:00
|
|
|
fn xor(lhs, rhs) {
|
2024-02-29 16:48:09 -06:00
|
|
|
Ok(match (lhs, rhs) {
|
|
|
|
(ConValue::Empty, ConValue::Empty) => ConValue::Empty,
|
|
|
|
(ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
|
|
|
|
(ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
|
|
|
|
_ => Err(Error::TypeError)?,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Exclusive Range `a..b`
|
2025-01-31 03:34:45 -06:00
|
|
|
fn range_exc(from, to) {
|
|
|
|
let (&ConValue::Int(from), &ConValue::Int(to)) = (from, to) else {
|
2024-02-29 16:48:09 -06:00
|
|
|
Err(Error::TypeError)?
|
|
|
|
};
|
2025-01-31 03:34:45 -06:00
|
|
|
Ok(ConValue::RangeExc(from, to))
|
2024-02-29 16:48:09 -06:00
|
|
|
}
|
2024-07-11 04:43:25 -05:00
|
|
|
|
2024-02-29 16:48:09 -06:00
|
|
|
/// Inclusive Range `a..=b`
|
2025-01-31 03:34:45 -06:00
|
|
|
fn range_inc(from, to) {
|
|
|
|
let (&ConValue::Int(from), &ConValue::Int(to)) = (from, to) else {
|
2024-02-29 16:48:09 -06:00
|
|
|
Err(Error::TypeError)?
|
|
|
|
};
|
2025-01-31 03:34:45 -06:00
|
|
|
Ok(ConValue::RangeInc(from, to))
|
2024-02-29 16:48:09 -06:00
|
|
|
}
|
2025-01-31 03:34:45 -06:00
|
|
|
|
2024-02-29 16:48:09 -06:00
|
|
|
/// Negates the ConValue
|
2025-01-31 03:34:45 -06:00
|
|
|
fn neg(tail) {
|
2024-02-29 16:48:09 -06:00
|
|
|
Ok(match tail {
|
|
|
|
ConValue::Empty => ConValue::Empty,
|
2024-07-09 06:13:55 -05:00
|
|
|
ConValue::Int(v) => ConValue::Int(v.wrapping_neg()),
|
2024-09-18 01:02:09 -05:00
|
|
|
ConValue::Float(v) => ConValue::Float(-v),
|
2024-02-29 16:48:09 -06:00
|
|
|
_ => Err(Error::TypeError)?,
|
|
|
|
})
|
|
|
|
}
|
2024-07-11 04:43:25 -05:00
|
|
|
|
2024-02-29 16:48:09 -06:00
|
|
|
/// Inverts the ConValue
|
2025-01-31 03:34:45 -06:00
|
|
|
fn not(tail) {
|
2024-02-29 16:48:09 -06:00
|
|
|
Ok(match tail {
|
|
|
|
ConValue::Empty => ConValue::Empty,
|
|
|
|
ConValue::Int(v) => ConValue::Int(!v),
|
|
|
|
ConValue::Bool(v) => ConValue::Bool(!v),
|
|
|
|
_ => Err(Error::TypeError)?,
|
|
|
|
})
|
|
|
|
}
|
2024-07-11 04:43:25 -05:00
|
|
|
|
2025-01-31 03:34:45 -06:00
|
|
|
/// Compares two values
|
|
|
|
fn cmp(head, tail) {
|
|
|
|
Ok(ConValue::Int(match (head, tail) {
|
|
|
|
(ConValue::Int(a), ConValue::Int(b)) => a.cmp(b) as _,
|
|
|
|
(ConValue::Bool(a), ConValue::Bool(b)) => a.cmp(b) as _,
|
|
|
|
(ConValue::Char(a), ConValue::Char(b)) => a.cmp(b) as _,
|
|
|
|
(ConValue::String(a), ConValue::String(b)) => a.cmp(b) as _,
|
|
|
|
_ => Err(error_format!("Incomparable values: {head}, {tail}"))?
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2025-01-29 05:07:08 -06:00
|
|
|
/// Does the opposite of `&`
|
2025-01-31 03:34:45 -06:00
|
|
|
fn deref(tail) {
|
|
|
|
use std::rc::Rc;
|
2024-04-19 10:49:25 -05:00
|
|
|
Ok(match tail {
|
|
|
|
ConValue::Ref(v) => Rc::as_ref(v).clone(),
|
|
|
|
_ => tail.clone(),
|
|
|
|
})
|
|
|
|
}
|
2025-01-31 03:34:45 -06:00
|
|
|
];
|