#[macro_export]
macro_rules! list {
(@push $ret:ident, $item:expr) => {
$ret.push($item)
};
(@push $ret:ident, @ $item:expr) => {
$item.deep_copy().and_then(|ret| $ret.append(ret))
};
(@push $ret:ident, $item:expr, $($items:tt)+) => {
list!(@push $ret, $item).and_then(|ret|
list!(@push ret, $($items)+))
};
(@push $ret:ident, @ $item:expr, $($items:tt)+) => {
list!(@push $ret, @ $item).and_then(|ret|
list!(@push ret, $($items)+))
};
(, $($items:tt)+) => { list!($($items)+) };
($($items:tt)+) => {{
let ret = TulispObject::nil();
list!(@push ret, $($items)+)
.and_then(|ret| Ok(ret.to_owned()))
}};
() => { TulispObject::nil() }
}
#[macro_export]
macro_rules! destruct_bind {
(@reqr $vv:ident, $var:ident) => {
if !$vv.consp() {
return Err($crate::Error::type_mismatch(
"Too few arguments".to_string()
));
}
let $var = $vv.car()?;
let $vv = $vv.cdr()?;
};
(@reqr $vv:ident, $var:ident $($vars:tt)+) => {
destruct_bind!(@reqr $vv, $var);
destruct_bind!(@reqr $vv, $($vars)+);
};
(@reqr $vv:ident,) => {};
(@no-rest $vv:ident) => {
if !$vv.null() {
return Err($crate::Error::type_mismatch(
"Too many arguments".to_string()
));
}
};
(@rest $rest:ident $vv:ident) => {
let $rest = $vv;
};
(@optvar $vv:ident, $var:ident) => {
let ($var, $vv) = if !$vv.null() {
($vv.car()?, $vv.cdr()?)
} else {
(TulispObject::nil(), TulispObject::nil())
};
};
(@optvar $vv:ident, $var:ident $($vars:ident)+) => {
destruct_bind!(@optvar $vv, $var);
destruct_bind!(@optvar $vv, $($vars)+)
};
(@impl ($($vars:ident)+) = $vv:ident) => {
destruct_bind!(@reqr $vv, $($vars)+);
destruct_bind!(@no-rest $vv);
};
(@impl ($($vars:ident)* &optional $($optvars:ident)+) = $vv:ident) => {
destruct_bind!(@reqr $vv, $($vars)*);
destruct_bind!(@optvar $vv, $($optvars)+);
destruct_bind!(@no-rest $vv);
};
(@impl ($($vars:ident)* &rest $rest:ident) = $vv:ident) => {
destruct_bind!(@reqr $vv, $($vars)*);
destruct_bind!(@rest $rest $vv);
};
(@impl ($($vars:ident)* &optional $($optvars:ident)+ &rest $rest:ident) = $vv:ident) => {
destruct_bind!(@reqr $vv, $($vars)*);
destruct_bind!(@optvar $vv, $($optvars)+);
destruct_bind!(@rest $rest $vv);
};
(($($rest:tt)*) = $vv:ident) => {
destruct_bind!(@impl ($($rest)*) = $vv);
};
}
#[macro_export]
macro_rules! destruct_eval_bind {
(@reqr $ctx:ident, $vv:ident, $var:ident) => {
if !$vv.consp() {
return Err($crate::Error::type_mismatch(
"Too few arguments".to_string()
));
}
let $var = $vv.car_and_then(|x| $ctx.eval(x))?;
let $vv = $vv.cdr()?;
};
(@reqr $ctx:ident, $vv:ident, $var:ident $($vars:tt)+) => {
destruct_eval_bind!(@reqr $ctx, $vv, $var);
destruct_eval_bind!(@reqr $ctx, $vv, $($vars)+);
};
(@reqr $ctx:ident, $vv:ident,) => {};
(@no-rest $ctx:ident, $vv:ident) => {
if !$vv.null() {
return Err($crate::Error::type_mismatch(
"Too many arguments".to_string()
));
}
};
(@rest $ctx:ident, $rest:ident $vv:ident) => {
let $rest = $ctx.eval_each(&$vv)?;
};
(@optvar $ctx:ident, $vv:ident, $var:ident) => {
let ($var, $vv) = if !$vv.null() {
($vv.car_and_then(|x| $ctx.eval(x))?, $vv.cdr()?)
} else {
(TulispObject::nil(), TulispObject::nil())
};
};
(@optvar $ctx:ident, $vv:ident, $var:ident $($vars:ident)+) => {
destruct_eval_bind!(@optvar $ctx, $vv, $var);
destruct_eval_bind!(@optvar $ctx, $vv, $($vars)+)
};
(@impl $ctx:ident, ($($vars:ident)+) = $vv:ident) => {
destruct_eval_bind!(@reqr $ctx, $vv, $($vars)+);
destruct_eval_bind!(@no-rest $ctx, $vv);
};
(@impl $ctx:ident, ($($vars:ident)* &optional $($optvars:ident)+) = $vv:ident) => {
destruct_eval_bind!(@reqr $ctx, $vv, $($vars)*);
destruct_eval_bind!(@optvar $ctx, $vv, $($optvars)+);
destruct_eval_bind!(@no-rest $ctx, $vv);
};
(@impl $ctx:ident, ($($vars:ident)* &rest $rest:ident) = $vv:ident) => {
destruct_eval_bind!(@reqr $ctx, $vv, $($vars)*);
destruct_eval_bind!(@rest $ctx, $rest $vv);
};
(@impl $ctx:ident, ($($vars:ident)* &optional $($optvars:ident)+ &rest $rest:ident) = $vv:ident) => {
destruct_eval_bind!(@reqr $ctx, $vv, $($vars)*);
destruct_eval_bind!(@optvar $ctx, $vv, $($optvars)+);
destruct_eval_bind!(@rest $ctx, $rest $vv);
};
($ctx:ident, ($($rest:tt)*) = $vv:ident) => {
destruct_eval_bind!(@impl $ctx, ($($rest)*) = $vv);
};
}
#[macro_export]
macro_rules! intern {
($( #[$meta:meta] )*
$vis:vis struct $struct_name:ident {
$($name:ident : $symbol:expr),+ $(,)?
}) => {
$( #[$meta] )*
$vis struct $struct_name {
$(pub $name: $crate::TulispObject),+
}
impl $struct_name {
fn new(ctx: &mut $crate::TulispContext) -> Self {
let ret = $struct_name {
$($name: ctx.intern($symbol),)+
};
ret
}
}
};
($ctx: ident => {$($name:ident : $symbol:expr),+ $(,)?}) => {{
$crate::intern!(pub(crate) struct Keywords {$($name : $symbol),+});
Keywords::new($ctx)
}};
}