use crate::{
Number, TulispObject,
cons::Cons,
context::Scope,
error::Error,
object::{
Span,
wrappers::{
TulispFn,
generic::{Shared, SyncSend},
},
},
};
use std::{
any::Any,
convert::TryInto,
fmt::{Display, Write},
};
#[doc(hidden)]
#[derive(Debug, Clone)]
pub(crate) struct DefunParam {
pub(crate) param: TulispObject,
pub(crate) is_rest: bool,
pub(crate) is_optional: bool,
}
impl std::fmt::Display for DefunParam {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"{}, rest:{}, opt: {}",
self.param, self.is_rest, self.is_optional
))
}
}
#[doc(hidden)]
#[derive(Debug, Default, Clone)]
pub struct DefunParams {
params: Vec<DefunParam>,
scope: Scope,
}
impl std::fmt::Display for DefunParams {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("params:\n")?;
for param in &self.params {
f.write_fmt(format_args!(" param: {param}\n"))?;
}
Ok(())
}
}
impl TryFrom<TulispObject> for DefunParams {
type Error = Error;
fn try_from(params: TulispObject) -> Result<Self, Self::Error> {
if !params.listp() {
return Err(Error::syntax_error(
"Parameter list needs to be a list".to_string(),
));
}
let mut def_params = DefunParams::default();
let mut params_iter = params.base_iter();
let mut is_optional = false;
let mut is_rest = false;
while let Some(param) = params_iter.next() {
let name = param.as_symbol()?;
if name == "&optional" {
is_optional = true;
continue;
} else if name == "&rest" {
is_optional = false;
is_rest = true;
continue;
}
def_params.scope.scope.push(param.clone());
def_params.params.push(DefunParam {
param,
is_rest,
is_optional,
});
if is_rest {
if params_iter.next().is_some() {
return Err(Error::type_mismatch(
"Too many &rest parameters".to_string(),
));
}
break;
}
}
Ok(def_params)
}
}
impl DefunParams {
pub(crate) fn iter(&self) -> std::slice::Iter<'_, DefunParam> {
self.params.iter()
}
pub(crate) fn unbind(&self) -> Result<(), Error> {
self.scope.remove_all()
}
}
#[derive(Default, Clone, Debug)]
pub struct SymbolBindings {
name: String,
constant: bool,
has_global: bool,
items: Vec<TulispObject>,
}
impl SymbolBindings {
#[inline(always)]
pub(crate) fn set(&mut self, to_set: TulispObject) -> Result<(), Error> {
if self.constant {
return Err(Error::undefined(format!(
"Can't set constant symbol: {}",
self.name
)));
}
if self.items.is_empty() {
self.has_global = true;
self.items.push(to_set);
} else {
*self.items.last_mut().unwrap() = to_set;
}
Ok(())
}
#[inline(always)]
pub(crate) fn set_global(&mut self, to_set: TulispObject) -> Result<(), Error> {
if self.constant {
return Err(Error::undefined(format!(
"Can't set constant symbol: {}",
self.name
)));
}
self.has_global = true;
if self.items.is_empty() {
self.items.push(to_set);
} else {
*self.items.first_mut().unwrap() = to_set;
}
Ok(())
}
#[inline(always)]
pub(crate) fn set_scope(&mut self, to_set: TulispObject) -> Result<(), Error> {
if self.constant {
return Err(Error::undefined(format!(
"Can't set constant symbol: {}",
self.name
)));
}
self.items.push(to_set);
Ok(())
}
#[inline(always)]
pub(crate) fn set_unchecked(&mut self, to_set: TulispObject) {
*self.items.last_mut().unwrap() = to_set;
}
#[inline(always)]
pub(crate) fn unset(&mut self) -> Result<(), Error> {
if self.items.is_empty() {
return Err(Error::uninitialized(format!(
"Can't unbind from unassigned symbol: {}",
self.name
)));
}
self.items.pop();
Ok(())
}
#[inline(always)]
pub(crate) fn boundp(&self) -> bool {
!self.items.is_empty()
}
#[inline(always)]
pub(crate) fn get(&self) -> Result<TulispObject, Error> {
if self.items.is_empty() {
return Err(Error::uninitialized(format!(
"Variable definition is void: {}",
self.name
)));
}
Ok(self.items.last().unwrap().clone())
}
#[inline(always)]
pub(crate) fn get_as_number(&self) -> Result<crate::Number, Error> {
let Some(item) = self.items.last() else {
return Err(Error::type_mismatch(format!(
"Variable definition is void: {}",
self.name
)));
};
item.as_number()
}
#[inline(always)]
pub(crate) fn get_as_bool(&self) -> Result<bool, Error> {
let Some(item) = self.items.last() else {
return Err(Error::type_mismatch(format!(
"Variable definition is void: {}",
self.name
)));
};
Ok(item.is_truthy())
}
#[inline(always)]
pub(crate) fn is_constant(&self) -> bool {
self.constant
}
}
pub trait TulispAny: Any + Display + SyncSend {}
impl std::fmt::Debug for dyn TulispAny {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TulispAny({})", self)
}
}
impl<T: Any + Display + SyncSend> TulispAny for T {}
#[doc(hidden)]
#[derive(Clone)]
pub enum TulispValue {
Nil,
T,
Symbol {
value: SymbolBindings,
},
LexicalBinding {
value: SymbolBindings,
symbol: TulispObject,
},
Number {
value: Number,
},
String {
value: String,
},
List {
cons: Cons,
ctxobj: Option<TulispObject>,
},
Quote {
value: TulispObject,
},
Sharpquote {
value: TulispObject,
},
Backquote {
value: TulispObject,
},
Unquote {
value: TulispObject,
},
Splice {
value: TulispObject,
},
Any(Shared<dyn TulispAny>),
Func(Shared<dyn TulispFn>),
Macro(Shared<dyn TulispFn>),
Defmacro {
params: DefunParams,
body: TulispObject,
},
Lambda {
params: DefunParams,
body: TulispObject,
},
Bounce,
}
impl std::fmt::Debug for TulispValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Nil => write!(f, "Nil"),
Self::T => write!(f, "T"),
Self::Symbol { value } => f
.debug_struct("Symbol")
.field("name", &value.name)
.field("value", value)
.finish(),
Self::LexicalBinding { value, symbol } => f
.debug_struct("LexicalBinding")
.field("symbol", symbol)
.field("value", value)
.finish(),
Self::Number { value } => f.debug_struct("Number").field("value", value).finish(),
Self::String { value } => f.debug_struct("String").field("value", value).finish(),
Self::List { cons, ctxobj } => f
.debug_struct("List")
.field("cons", cons)
.field("ctxobj", ctxobj)
.finish(),
Self::Quote { value } => f.debug_struct("Quote").field("value", value).finish(),
Self::Sharpquote { value } => {
f.debug_struct("Sharpquote").field("value", value).finish()
}
Self::Backquote { value } => f.debug_struct("Backquote").field("value", value).finish(),
Self::Unquote { value } => f.debug_struct("Unquote").field("value", value).finish(),
Self::Splice { value } => f.debug_struct("Splice").field("value", value).finish(),
Self::Any(arg0) => write!(f, "Any({:?} = {})", arg0.type_id(), arg0),
Self::Func(_) => write!(f, "Func"),
Self::Macro(_) => write!(f, "Macro"),
Self::Defmacro { params, body } => f
.debug_struct("Defmacro")
.field("params", params)
.field("body", body)
.finish(),
Self::Lambda { params, body } => f
.debug_struct("Defun")
.field("params", params)
.field("body", body)
.finish(),
Self::Bounce => write!(f, "Bounce"),
}
}
}
impl PartialEq for TulispValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Symbol { value: l0, .. }, Self::Symbol { value: r0, .. }) => l0.name == r0.name,
(Self::Number { value: l0, .. }, Self::Number { value: r0, .. }) => l0 == r0,
(Self::String { value: l0, .. }, Self::String { value: r0, .. }) => l0 == r0,
(Self::List { cons: l_cons, .. }, Self::List { cons: r_cons, .. }) => l_cons == r_cons,
(Self::Quote { value: l0, .. }, Self::Quote { value: r0, .. }) => l0.equal(r0),
(Self::Sharpquote { value: l0, .. }, Self::Sharpquote { value: r0, .. }) => {
l0.equal(r0)
}
(Self::Backquote { value: l0, .. }, Self::Backquote { value: r0, .. }) => l0.equal(r0),
(Self::Unquote { value: l0, .. }, Self::Unquote { value: r0, .. }) => l0.equal(r0),
(Self::Splice { value: l0, .. }, Self::Splice { value: r0, .. }) => l0.equal(r0),
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}
fn fmt_list(mut vv: TulispObject, f: &mut std::fmt::Formatter<'_>) -> Result<(), Error> {
if let Err(e) = f.write_char('(') {
return Err(Error::type_mismatch(format!("When trying to 'fmt': {}", e)));
};
let mut add_space = false;
loop {
let rest = vv.cdr()?;
if !add_space {
add_space = true;
} else if let Err(e) = f.write_char(' ') {
return Err(Error::type_mismatch(format!("When trying to 'fmt': {}", e)));
};
write!(f, "{}", vv.car()?)
.map_err(|e| Error::type_mismatch(format!("When trying to 'fmt': {}", e)))?;
if rest.null() {
break;
} else if !rest.consp() {
write!(f, " . {}", rest)
.map_err(|e| Error::type_mismatch(format!("When trying to 'fmt': {}", e)))?;
break;
};
vv = rest;
}
if let Err(e) = f.write_char(')') {
return Err(Error::type_mismatch(format!("When trying to 'fmt': {}", e)));
};
Ok(())
}
impl std::fmt::Display for TulispValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TulispValue::Bounce => f.write_str("Bounce"),
TulispValue::Nil => f.write_str("nil"),
TulispValue::Symbol { value } => f.write_str(&value.name),
TulispValue::LexicalBinding { value, .. } => f.write_str(&value.name),
TulispValue::Number { value, .. } => f.write_fmt(format_args!("{}", value)),
TulispValue::String { value, .. } => f.write_fmt(format_args!(r#""{}""#, value)),
vv @ TulispValue::List { .. } => {
fmt_list(vv.clone().into_ref(None), f).unwrap_or(());
Ok(())
}
TulispValue::Quote { value, .. } => f.write_fmt(format_args!("'{}", value)),
TulispValue::Backquote { value, .. } => f.write_fmt(format_args!("`{}", value)),
TulispValue::Unquote { value, .. } => f.write_fmt(format_args!(",{}", value)),
TulispValue::Splice { value, .. } => f.write_fmt(format_args!(",@{}", value)),
TulispValue::Sharpquote { value, .. } => f.write_fmt(format_args!("#'{}", value)),
TulispValue::Any(value) => f.write_fmt(format_args!("{}", value)),
TulispValue::T => f.write_str("t"),
TulispValue::Func(_) => f.write_str("Func"),
TulispValue::Macro(_) => f.write_str("Macro"),
TulispValue::Defmacro { .. } => f.write_str("Defmacro"),
TulispValue::Lambda { .. } => f.write_str("Defun"),
}
}
}
impl TulispValue {
#[inline(always)]
pub(crate) fn symbol(name: String, constant: bool) -> TulispValue {
TulispValue::Symbol {
value: SymbolBindings {
name,
constant,
has_global: false,
items: Default::default(),
},
}
}
#[inline(always)]
pub(crate) fn lexical_binding(symbol: TulispObject) -> TulispValue {
let name = symbol.to_string();
TulispValue::LexicalBinding {
symbol,
value: SymbolBindings {
name,
constant: false,
has_global: false,
items: Default::default(),
},
}
}
#[inline(always)]
pub(crate) fn set(&mut self, to_set: TulispObject) -> Result<(), Error> {
if let TulispValue::Symbol { value, .. } | TulispValue::LexicalBinding { value, .. } = self
{
value.set(to_set)
} else {
Err(Error::type_mismatch(
"Can bind values only to Symbols".to_string(),
))
}
}
#[inline(always)]
pub(crate) fn set_global(&mut self, to_set: TulispObject) -> Result<(), Error> {
if let TulispValue::Symbol { value, .. } | TulispValue::LexicalBinding { value, .. } = self
{
value.set_global(to_set)
} else {
Err(Error::type_mismatch(
"Can bind values only to Symbols".to_string(),
))
}
}
#[inline(always)]
pub(crate) fn set_scope(&mut self, to_set: TulispObject) -> Result<(), Error> {
if let TulispValue::Symbol { value, .. } | TulispValue::LexicalBinding { value, .. } = self
{
value.set_scope(to_set)
} else {
Err(Error::type_mismatch(format!(
"Expected Symbol: Can't assign to {self}"
)))
}
}
#[inline(always)]
pub(crate) fn set_unchecked(&mut self, to_set: TulispObject) {
if let TulispValue::Symbol { value, .. } | TulispValue::LexicalBinding { value, .. } = self
{
value.set_unchecked(to_set)
}
}
#[inline(always)]
pub(crate) fn unset(&mut self) -> Result<(), Error> {
if let TulispValue::Symbol { value, .. } | TulispValue::LexicalBinding { value, .. } = self
{
value.unset()
} else {
Err(Error::type_mismatch(
"Can unbind only from Symbols".to_string(),
))
}
}
#[inline(always)]
pub(crate) fn is_lexically_bound(&self) -> bool {
match self {
TulispValue::Symbol { value } => {
(value.has_global && value.items.len() > 1)
|| (!value.has_global && !value.items.is_empty())
}
TulispValue::LexicalBinding { .. } => true,
_ => false,
}
}
#[inline(always)]
pub(crate) fn lex_symbol_eq(&self, other: &TulispObject) -> bool {
let TulispValue::LexicalBinding { symbol, .. } = self else {
return false;
};
if let TulispValue::LexicalBinding { symbol: other, .. } = &other.inner_ref().0 {
symbol.eq(other)
} else {
symbol.eq(other)
}
}
#[inline(always)]
pub(crate) fn get(&self) -> Result<TulispObject, Error> {
if let TulispValue::Symbol { value, .. } | TulispValue::LexicalBinding { value, .. } = self
{
if value.is_constant() {
return Ok(self.clone().into_ref(None));
}
value.get()
} else {
Err(Error::type_mismatch(
"Can get only from Symbols".to_string(),
))
}
}
#[inline(always)]
pub(crate) fn keywordp(&self) -> bool {
if let TulispValue::Symbol { value, .. } = self {
value.is_constant()
} else {
false
}
}
#[inline(always)]
pub(crate) fn boundp(&self) -> bool {
if let TulispValue::Symbol { value, .. } | TulispValue::LexicalBinding { value, .. } = self
{
value.boundp()
} else {
false
}
}
#[inline(always)]
pub(crate) fn push(&mut self, val: TulispObject) -> Result<(), Error> {
self.push_with_meta(val, None, None)
}
#[inline(always)]
pub(crate) fn push_with_meta(
&mut self,
val: TulispObject,
span_in: Option<Span>,
ctxobj: Option<TulispObject>,
) -> Result<(), Error> {
if let TulispValue::List { cons, .. } = self {
cons.push_with_meta(val.clone(), span_in, ctxobj)
.map_err(|e| e.with_trace(val))?;
Ok(())
} else if self.null() {
let cons = Cons::new(val, TulispObject::nil());
*self = TulispValue::List { cons, ctxobj };
Ok(())
} else {
Err(Error::type_mismatch("unable to push".to_string()))
}
}
#[inline(always)]
pub(crate) fn append(&mut self, val: TulispObject) -> Result<(), Error> {
if let TulispValue::List { cons, .. } = self {
cons.append(val.clone()).map_err(|e| e.with_trace(val))?;
Ok(())
} else if self.null() {
if !val.null() {
*self = TulispValue::List {
cons: val
.as_list_cons()
.unwrap_or_else(|| Cons::new(val, TulispObject::nil())),
ctxobj: None,
};
}
Ok(())
} else {
Err(Error::type_mismatch(format!("unable to append: {}", val)))
}
}
#[inline(always)]
pub fn into_ref(self, span: Option<Span>) -> TulispObject {
TulispObject::new(self, span)
}
#[inline(always)]
pub(crate) fn as_list_cons(&self) -> Option<Cons> {
match self {
TulispValue::List { cons, .. } => Some(cons.clone()),
_ => None,
}
}
#[inline(always)]
pub(crate) fn as_symbol(&self) -> Result<String, Error> {
match self {
TulispValue::Symbol { value } | TulispValue::LexicalBinding { value, .. } => {
Ok(value.name.to_string())
}
_ => Err(Error::type_mismatch(format!(
"Expected symbol, got: {}",
self
))),
}
}
#[inline(always)]
pub(crate) fn as_float(&self) -> Result<f64, Error> {
match self {
TulispValue::Number {
value: Number::Float(value),
..
} => Ok(*value),
t => Err(Error::type_mismatch(format!("Expected number, got: {}", t))),
}
}
#[inline(always)]
pub(crate) fn try_float(&self) -> Result<f64, Error> {
match self {
TulispValue::Number {
value: Number::Float(value),
..
} => Ok(*value),
TulispValue::Number {
value: Number::Int(value),
..
} => Ok(*value as f64),
t => Err(Error::type_mismatch(format!("Expected number, got: {}", t))),
}
}
#[inline(always)]
pub(crate) fn as_int(&self) -> Result<i64, Error> {
match self {
TulispValue::Number {
value: Number::Int(value),
..
} => Ok(*value),
t => Err(Error::type_mismatch(format!("Expected integer: {}", t))),
}
}
#[inline(always)]
pub(crate) fn try_int(&self) -> Result<i64, Error> {
match self {
TulispValue::Number {
value: Number::Float(value),
..
} => Ok(value.trunc() as i64),
TulispValue::Number {
value: Number::Int(value),
..
} => Ok(*value),
t => Err(Error::type_mismatch(format!("Expected number, got {}", t))),
}
}
#[inline(always)]
pub(crate) fn as_number(&self) -> Result<Number, Error> {
match self {
TulispValue::Number { value, .. } => Ok(*value),
t => Err(Error::type_mismatch(format!("Expected number, got: {}", t))),
}
}
#[inline(always)]
pub(crate) fn is_truthy(&self) -> bool {
!self.null()
}
#[inline(always)]
pub(crate) fn null(&self) -> bool {
matches!(self, TulispValue::Nil)
}
#[inline(always)]
pub(crate) fn is_bounced(&self) -> bool {
match self {
TulispValue::List { cons, .. } => cons.car().is_bounce(),
_ => false,
}
}
#[inline(always)]
pub(crate) fn is_bounce(&self) -> bool {
matches!(self, TulispValue::Bounce)
}
#[inline(always)]
pub(crate) fn consp(&self) -> bool {
matches!(self, TulispValue::List { .. })
}
#[inline(always)]
pub(crate) fn listp(&self) -> bool {
matches!(self, TulispValue::List { .. } | TulispValue::Nil)
}
#[inline(always)]
pub(crate) fn integerp(&self) -> bool {
matches!(
self,
TulispValue::Number {
value: Number::Int(..)
}
)
}
#[inline(always)]
pub(crate) fn floatp(&self) -> bool {
matches!(
self,
TulispValue::Number {
value: Number::Float(..)
}
)
}
#[inline(always)]
pub(crate) fn numberp(&self) -> bool {
matches!(self, TulispValue::Number { .. })
}
#[inline(always)]
pub(crate) fn stringp(&self) -> bool {
matches!(self, TulispValue::String { .. })
}
#[inline(always)]
pub(crate) fn symbolp(&self) -> bool {
matches!(
self,
TulispValue::Symbol { .. } | TulispValue::LexicalBinding { .. }
)
}
#[inline(always)]
pub(crate) fn as_string(&self) -> Result<String, Error> {
match self {
TulispValue::String { value, .. } => Ok(value.to_owned()),
_ => Err(Error::type_mismatch(format!(
"Expected string, got: {}",
self
))),
}
}
#[inline(always)]
pub(crate) fn as_any(&self) -> Result<Shared<dyn TulispAny>, Error> {
match self {
TulispValue::Any(value) => Ok(value.clone()),
_ => Err(Error::type_mismatch(format!(
"Expected Any(Shared<dyn TulispAny>), got: {}",
self
))),
}
}
#[inline(always)]
pub(crate) fn fmt_string(&self) -> String {
match self {
TulispValue::String { value, .. } => value.to_owned(),
s => s.to_string(),
}
}
#[inline(always)]
pub(crate) fn with_ctxobj(&mut self, in_ctxobj: Option<TulispObject>) -> &mut Self {
if let TulispValue::List { ctxobj, .. } = self {
*ctxobj = in_ctxobj
}
self
}
#[inline(always)]
pub(crate) fn ctxobj(&self) -> Option<TulispObject> {
match self {
TulispValue::List { ctxobj, .. } => ctxobj.to_owned(),
_ => None,
}
}
#[inline(always)]
pub(crate) fn take(&mut self) -> TulispValue {
std::mem::replace(self, TulispValue::Nil)
}
}
impl TryInto<f64> for TulispValue {
type Error = Error;
fn try_into(self) -> Result<f64, Error> {
self.try_float()
}
}
impl TryInto<i64> for TulispValue {
type Error = Error;
fn try_into(self) -> Result<i64, Error> {
self.as_int()
}
}
impl TryFrom<TulispValue> for bool {
type Error = Error;
fn try_from(value: TulispValue) -> Result<Self, Self::Error> {
Ok(value.is_truthy())
}
}
impl From<i64> for TulispValue {
fn from(value: i64) -> Self {
TulispValue::Number {
value: value.into(),
}
}
}
impl From<f64> for TulispValue {
fn from(value: f64) -> Self {
TulispValue::Number {
value: value.into(),
}
}
}
impl From<&str> for TulispValue {
fn from(value: &str) -> Self {
TulispValue::String {
value: value.to_owned(),
}
}
}
impl From<String> for TulispValue {
fn from(value: String) -> Self {
TulispValue::String { value }
}
}
impl From<bool> for TulispValue {
fn from(value: bool) -> Self {
match value {
true => TulispValue::T,
false => TulispValue::Nil,
}
}
}
impl From<Number> for TulispValue {
fn from(value: Number) -> Self {
TulispValue::Number { value }
}
}
impl From<Shared<dyn TulispAny>> for TulispValue {
fn from(value: Shared<dyn TulispAny>) -> Self {
TulispValue::Any(value)
}
}
impl FromIterator<TulispObject> for TulispValue {
fn from_iter<T: IntoIterator<Item = TulispObject>>(iter: T) -> Self {
let mut list = TulispValue::Nil;
for item in iter {
let _ = list.push(item);
}
list
}
}
macro_rules! make_cxr {
($name:ident, $step:expr) => {
#[inline(always)]
pub(crate) fn $name(&self) -> Result<TulispObject, Error> {
self.cxr($step)
}
};
}
macro_rules! make_cxr_and_then {
($name:ident, $($step:tt)+) => {
#[inline(always)]
pub(crate) fn $name<Out: Default>(
&self,
func: impl FnOnce(&TulispObject) -> Result<Out, Error>,
) -> Result<Out, Error> {
match self {
TulispValue::List { cons, .. } => cons.$($step)+(func),
TulispValue::Nil => Ok(Out::default()),
_ => Err(Error::type_mismatch(
format!("cxr: Not a Cons: {}", self),
)),
}
}
};
}
impl TulispValue {
#[inline(always)]
fn cxr(
&self,
step: impl Fn(&Cons) -> Result<TulispObject, Error>,
) -> Result<TulispObject, Error> {
match self {
TulispValue::List { cons, .. } => step(cons),
TulispValue::Nil => Ok(TulispObject::nil()),
_ => Err(Error::type_mismatch(format!("cxr: Not a Cons: {}", self))),
}
}
make_cxr!(car, |x| Ok(x.car().clone()));
make_cxr!(cdr, |x| Ok(x.cdr().clone()));
make_cxr!(caar, |x| x.car().car());
make_cxr!(cadr, |x| x.cdr().car());
make_cxr!(cdar, |x| x.car().cdr());
make_cxr!(cddr, |x| x.cdr().cdr());
make_cxr!(caaar, |x| x.car().caar());
make_cxr!(caadr, |x| x.cdr().caar());
make_cxr!(cadar, |x| x.car().cadr());
make_cxr!(caddr, |x| x.cdr().cadr());
make_cxr!(cdaar, |x| x.car().cdar());
make_cxr!(cdadr, |x| x.cdr().cdar());
make_cxr!(cddar, |x| x.car().cddr());
make_cxr!(cdddr, |x| x.cdr().cddr());
make_cxr!(caaaar, |x| x.car().caaar());
make_cxr!(caaadr, |x| x.cdr().caaar());
make_cxr!(caadar, |x| x.car().caadr());
make_cxr!(caaddr, |x| x.cdr().caadr());
make_cxr!(cadaar, |x| x.car().cadar());
make_cxr!(cadadr, |x| x.cdr().cadar());
make_cxr!(caddar, |x| x.car().caddr());
make_cxr!(cadddr, |x| x.cdr().caddr());
make_cxr!(cdaaar, |x| x.car().cdaar());
make_cxr!(cdaadr, |x| x.cdr().cdaar());
make_cxr!(cdadar, |x| x.car().cdadr());
make_cxr!(cdaddr, |x| x.cdr().cdadr());
make_cxr!(cddaar, |x| x.car().cddar());
make_cxr!(cddadr, |x| x.cdr().cddar());
make_cxr!(cdddar, |x| x.car().cdddr());
make_cxr!(cddddr, |x| x.cdr().cdddr());
#[inline(always)]
pub(crate) fn car_and_then<Out: Default>(
&self,
func: impl FnOnce(&TulispObject) -> Result<Out, Error>,
) -> Result<Out, Error> {
match self {
TulispValue::List { cons, .. } => func(cons.car()),
TulispValue::Nil => Ok(Out::default()),
_ => Err(Error::type_mismatch(format!("cxr: Not a Cons: {}", self))),
}
}
#[inline(always)]
pub(crate) fn cdr_and_then<Out: Default>(
&self,
func: impl FnOnce(&TulispObject) -> Result<Out, Error>,
) -> Result<Out, Error> {
match self {
TulispValue::List { cons, .. } => func(cons.cdr()),
TulispValue::Nil => Ok(Out::default()),
_ => Err(Error::type_mismatch(format!("cxr: Not a Cons: {}", self))),
}
}
make_cxr_and_then!(caar_and_then, car().car_and_then);
make_cxr_and_then!(cadr_and_then, cdr().car_and_then);
make_cxr_and_then!(cdar_and_then, car().cdr_and_then);
make_cxr_and_then!(cddr_and_then, cdr().cdr_and_then);
make_cxr_and_then!(caaar_and_then, car().caar_and_then);
make_cxr_and_then!(caadr_and_then, cdr().caar_and_then);
make_cxr_and_then!(cadar_and_then, car().cadr_and_then);
make_cxr_and_then!(caddr_and_then, cdr().cadr_and_then);
make_cxr_and_then!(cdaar_and_then, car().cdar_and_then);
make_cxr_and_then!(cdadr_and_then, cdr().cdar_and_then);
make_cxr_and_then!(cddar_and_then, car().cddr_and_then);
make_cxr_and_then!(cdddr_and_then, cdr().cddr_and_then);
make_cxr_and_then!(caaaar_and_then, car().caaar_and_then);
make_cxr_and_then!(caaadr_and_then, cdr().caaar_and_then);
make_cxr_and_then!(caadar_and_then, car().caadr_and_then);
make_cxr_and_then!(caaddr_and_then, cdr().caadr_and_then);
make_cxr_and_then!(cadaar_and_then, car().cadar_and_then);
make_cxr_and_then!(cadadr_and_then, cdr().cadar_and_then);
make_cxr_and_then!(caddar_and_then, car().caddr_and_then);
make_cxr_and_then!(cadddr_and_then, cdr().caddr_and_then);
make_cxr_and_then!(cdaaar_and_then, car().cdaar_and_then);
make_cxr_and_then!(cdaadr_and_then, cdr().cdaar_and_then);
make_cxr_and_then!(cdadar_and_then, car().cdadr_and_then);
make_cxr_and_then!(cdaddr_and_then, cdr().cdadr_and_then);
make_cxr_and_then!(cddaar_and_then, car().cddar_and_then);
make_cxr_and_then!(cddadr_and_then, cdr().cddar_and_then);
make_cxr_and_then!(cdddar_and_then, car().cdddr_and_then);
make_cxr_and_then!(cddddr_and_then, cdr().cdddr_and_then);
}