use crate::{ArenaIndex, ArenaResult, ArenaError, Lisp, Value};
pub trait FromLisp<const N: usize>: Sized {
fn from_lisp(lisp: &Lisp<N>, idx: ArenaIndex) -> ArenaResult<Self>;
}
pub trait ToLisp<const N: usize> {
fn to_lisp(&self, lisp: &Lisp<N>) -> ArenaResult<ArenaIndex>;
}
impl<const N: usize> FromLisp<N> for isize {
fn from_lisp(lisp: &Lisp<N>, idx: ArenaIndex) -> ArenaResult<Self> {
match lisp.get(idx)? {
Value::Number(n) => Ok(n),
_ => Err(ArenaError::InvalidIndex),
}
}
}
impl<const N: usize> ToLisp<N> for isize {
fn to_lisp(&self, lisp: &Lisp<N>) -> ArenaResult<ArenaIndex> {
lisp.number(*self)
}
}
impl<const N: usize> FromLisp<N> for bool {
fn from_lisp(lisp: &Lisp<N>, idx: ArenaIndex) -> ArenaResult<Self> {
match lisp.get(idx)? {
Value::True => Ok(true),
Value::False => Ok(false),
_ => Ok(true),
}
}
}
impl<const N: usize> ToLisp<N> for bool {
fn to_lisp(&self, lisp: &Lisp<N>) -> ArenaResult<ArenaIndex> {
lisp.boolean(*self)
}
}
impl<const N: usize> FromLisp<N> for () {
fn from_lisp(lisp: &Lisp<N>, idx: ArenaIndex) -> ArenaResult<Self> {
match lisp.get(idx)? {
Value::Nil => Ok(()),
_ => Err(ArenaError::InvalidIndex),
}
}
}
impl<const N: usize> ToLisp<N> for () {
fn to_lisp(&self, lisp: &Lisp<N>) -> ArenaResult<ArenaIndex> {
lisp.nil()
}
}
impl<const N: usize> FromLisp<N> for char {
fn from_lisp(lisp: &Lisp<N>, idx: ArenaIndex) -> ArenaResult<Self> {
match lisp.get(idx)? {
Value::Char(c) => Ok(c),
_ => Err(ArenaError::InvalidIndex),
}
}
}
impl<const N: usize> ToLisp<N> for char {
fn to_lisp(&self, lisp: &Lisp<N>) -> ArenaResult<ArenaIndex> {
lisp.char(*self)
}
}
impl<const N: usize> FromLisp<N> for ArenaIndex {
fn from_lisp(_lisp: &Lisp<N>, idx: ArenaIndex) -> ArenaResult<Self> {
Ok(idx)
}
}
impl<const N: usize> ToLisp<N> for ArenaIndex {
fn to_lisp(&self, _lisp: &Lisp<N>) -> ArenaResult<ArenaIndex> {
Ok(*self)
}
}
pub const MAX_NATIVE_FUNCTIONS: usize = 64;
pub type NativeFn<const N: usize> = fn(&Lisp<N>, ArenaIndex) -> ArenaResult<ArenaIndex>;
#[derive(Clone, Copy)]
pub struct NativeEntry<const N: usize> {
pub name: &'static str,
pub func: NativeFn<N>,
}
pub struct NativeRegistry<const N: usize> {
entries: [Option<NativeEntry<N>>; MAX_NATIVE_FUNCTIONS],
count: usize,
}
impl<const N: usize> NativeRegistry<N> {
pub const fn new() -> Self {
NativeRegistry {
entries: [None; MAX_NATIVE_FUNCTIONS],
count: 0,
}
}
pub fn register(&mut self, name: &'static str, func: NativeFn<N>) {
assert!(self.count < MAX_NATIVE_FUNCTIONS, "Native function registry is full");
self.entries[self.count] = Some(NativeEntry { name, func });
self.count += 1;
}
pub fn lookup(&self, name: &str) -> Option<NativeFn<N>> {
for entry in &self.entries[..self.count] {
if let Some(e) = entry
&& e.name == name
{
return Some(e.func);
}
}
None
}
pub fn names(&self) -> impl Iterator<Item = &'static str> + '_ {
self.entries[..self.count]
.iter()
.filter_map(|e| e.as_ref().map(|e| e.name))
}
pub fn len(&self) -> usize {
self.count
}
pub fn is_empty(&self) -> bool {
self.count == 0
}
pub fn lookup_by_id(&self, id: usize) -> Option<NativeFn<N>> {
if id < self.count {
self.entries[id].map(|e| e.func)
} else {
None
}
}
pub fn name_by_id(&self, id: usize) -> Option<&'static str> {
if id < self.count {
self.entries[id].map(|e| e.name)
} else {
None
}
}
}
impl<const N: usize> Default for NativeRegistry<N> {
fn default() -> Self {
Self::new()
}
}
pub fn extract_arg<const N: usize, T: FromLisp<N>>(
lisp: &Lisp<N>,
args: ArenaIndex,
) -> ArenaResult<(T, ArenaIndex)> {
let head = lisp.car(args)?;
let tail = lisp.cdr(args)?;
let value = T::from_lisp(lisp, head)?;
Ok((value, tail))
}
pub fn args_empty<const N: usize>(lisp: &Lisp<N>, args: ArenaIndex) -> ArenaResult<bool> {
Ok(lisp.get(args)?.is_nil())
}
pub fn count_args<const N: usize>(lisp: &Lisp<N>, mut args: ArenaIndex) -> ArenaResult<usize> {
let mut count = 0;
loop {
match lisp.get(args)? {
Value::Nil => return Ok(count),
Value::Cons { .. } => {
count += 1;
args = lisp.cdr(args)?;
}
_ => return Err(ArenaError::InvalidIndex),
}
}
}