#[macro_export]
macro_rules! extract_args {
($self:expr, $args:ident, $var:ident) => {
let $var = $self.lisp.car($args)?;
};
($self:expr, $args:ident, $var:ident, $($rest:ident),+) => {
let $var = $self.lisp.car($args)?;
#[allow(unused_variables)]
let $args = $self.lisp.cdr($args)?;
$crate::extract_args!($self, $args, $($rest),+)
};
}
#[macro_export]
macro_rules! builtin_unary_pred {
($self:expr, $args:expr, $check:expr) => {{
let arg = $self.lisp.car($args)?;
let val = $self.lisp.get(arg)?;
$self.lisp.boolean($check(val)).map_err(Into::into)
}};
}
#[macro_export]
macro_rules! builtin_numeric_pred {
($self:expr, $args:expr, $call_expr:expr, |$n:ident| $int_check:expr, |$f:ident| $float_check:expr) => {{
let arg = $self.lisp.car($args)?;
match $self.lisp.get(arg)? {
Value::Number($n) => $self.lisp.boolean($int_check).map_err(Into::into),
Value::Float($f) => $self.lisp.boolean($float_check).map_err(Into::into),
v => Err($self.type_error($call_expr, "number", v.type_name())),
}
}};
($self:expr, $args:expr, $call_expr:expr, |$n:ident| $check:expr) => {{
let $n = $self.get_int($self.lisp.car($args)?, $call_expr)?;
$self.lisp.boolean($check).map_err(Into::into)
}};
}
#[macro_export]
macro_rules! builtin_rounding_op {
($self:expr, $args:expr, $call_expr:expr, $float_op:expr) => {{
let arg = $self.lisp.car($args)?;
match $self.lisp.get(arg)? {
Value::Number(n) => $self.lisp.number(n).map_err(Into::into),
Value::Float(f) => {
let result = $float_op(f);
$self.lisp.float(result).map_err(Into::into)
}
v => Err($self.type_error($call_expr, "number", v.type_name())),
}
}};
}
#[macro_export]
macro_rules! builtin_div_op {
($self:expr, $args:expr, $call_expr:expr, $op:expr) => {{
let a = $self.get_int($self.lisp.car($args)?, $call_expr)?;
let b = $self.get_int($self.lisp.car($self.lisp.cdr($args)?)?, $call_expr)?;
if b == 0 {
return Err($self.make_error($crate::ErrorKind::DivisionByZero, $call_expr));
}
$self.lisp.number($op(a, b)).map_err(Into::into)
}};
}
#[macro_export]
macro_rules! builtin_unary_num {
($self:expr, $args:expr, $call_expr:expr, |$n:ident : isize| $int_op:expr, |$f:ident : fsize| $float_op:expr) => {{
let arg = $self.lisp.car($args)?;
match $self.lisp.get(arg)? {
Value::Number($n) => $self.lisp.number($int_op).map_err(Into::into),
Value::Float($f) => $self.lisp.float($float_op).map_err(Into::into),
v => Err($self.type_error($call_expr, "number", v.type_name())),
}
}};
}
#[macro_export]
macro_rules! builtin_unary_int {
($self:expr, $args:expr, $call_expr:expr, $op:expr) => {{
let n = $self.get_int($self.lisp.car($args)?, $call_expr)?;
$self.lisp.number($op(n)).map_err(Into::into)
}};
}
#[macro_export]
macro_rules! builtin_char_to_int {
($self:expr, $args:expr, $call_expr:expr) => {{
let c = $self.get_char($self.lisp.car($args)?, $call_expr)?;
$self.lisp.number(c as isize).map_err(Into::into)
}};
}
#[macro_export]
macro_rules! builtin_char_transform {
($self:expr, $args:expr, $call_expr:expr, $from_low:expr, $from_high:expr, $from_base:expr, $to_base:expr) => {{
let c = $self.get_char($self.lisp.car($args)?, $call_expr)?;
let result = if c >= $from_low && c <= $from_high {
((c as u8) - $from_base + $to_base) as char
} else {
c
};
$self.lisp.char(result).map_err(Into::into)
}};
}
#[macro_export]
macro_rules! binary_int_cmp {
($self:expr, $a:expr, $b:expr, $call_expr:expr, $cmp:expr) => {{
let val_a = $self.lisp.get($a)?;
let val_b = $self.lisp.get($b)?;
let result = match (val_a, val_b) {
(Value::Number(x), Value::Number(y)) => $cmp(x as $crate::fsize, y as $crate::fsize),
(Value::Number(x), Value::Float(y)) => $cmp(x as $crate::fsize, y),
(Value::Float(x), Value::Number(y)) => $cmp(x, y as $crate::fsize),
(Value::Float(x), Value::Float(y)) => $cmp(x, y),
(v, _) if !v.is_number() => return Err($self.type_error($call_expr, "number", v.type_name())),
(_, v) => return Err($self.type_error($call_expr, "number", v.type_name())),
};
$self.lisp.boolean(result).map_err(Into::into)
}};
}
#[macro_export]
macro_rules! binary_int_op {
($self:expr, $a:expr, $b:expr, $call_expr:expr, $int_op:expr, $float_op:expr) => {{
let val_a = $self.lisp.get($a)?;
let val_b = $self.lisp.get($b)?;
match (val_a, val_b) {
(Value::Number(x), Value::Number(y)) => {
match $int_op(x, y) {
Some(n) => $self.lisp.number(n).map_err(Into::into),
None => Err($self.make_error($crate::ErrorKind::DivisionByZero, $call_expr)),
}
}
(Value::Number(x), Value::Float(y)) => {
$self.lisp.float($float_op(x as $crate::fsize, y)).map_err(Into::into)
}
(Value::Float(x), Value::Number(y)) => {
$self.lisp.float($float_op(x, y as $crate::fsize)).map_err(Into::into)
}
(Value::Float(x), Value::Float(y)) => {
$self.lisp.float($float_op(x, y)).map_err(Into::into)
}
(v, _) if !v.is_number() => Err($self.type_error($call_expr, "number", v.type_name())),
(_, v) => Err($self.type_error($call_expr, "number", v.type_name())),
}
}};
}
#[macro_export]
macro_rules! binary_div_op {
($self:expr, $a:expr, $b:expr, $call_expr:expr, $int_op:expr, $float_op:expr) => {{
let val_a = $self.lisp.get($a)?;
let val_b = $self.lisp.get($b)?;
match (val_a, val_b) {
(Value::Number(x), Value::Number(y)) => {
if y == 0 {
return Err($self.make_error($crate::ErrorKind::DivisionByZero, $call_expr));
}
$self.lisp.number($int_op(x, y)).map_err(Into::into)
}
(Value::Number(x), Value::Float(y)) => {
if y == 0.0 {
return Err($self.make_error($crate::ErrorKind::DivisionByZero, $call_expr));
}
$self.lisp.float($float_op(x as $crate::fsize, y)).map_err(Into::into)
}
(Value::Float(x), Value::Number(y)) => {
if y == 0 {
return Err($self.make_error($crate::ErrorKind::DivisionByZero, $call_expr));
}
$self.lisp.float($float_op(x, y as $crate::fsize)).map_err(Into::into)
}
(Value::Float(x), Value::Float(y)) => {
if y == 0.0 {
return Err($self.make_error($crate::ErrorKind::DivisionByZero, $call_expr));
}
$self.lisp.float($float_op(x, y)).map_err(Into::into)
}
(v, _) if !v.is_number() => Err($self.type_error($call_expr, "number", v.type_name())),
(_, v) => Err($self.type_error($call_expr, "number", v.type_name())),
}
}};
}
#[macro_export]
macro_rules! register_native {
($name:ident, () -> $ret:ty, $body:block) => {
pub fn $name<const N: usize>(
lisp: &$crate::Lisp<N>,
_args: $crate::ArenaIndex,
) -> $crate::ArenaResult<$crate::ArenaIndex> {
let _ = lisp;
let result: $ret = $body;
$crate::ToLisp::to_lisp(&result, lisp)
}
};
($name:ident, ($arg1:ident : $ty1:ty) -> $ret:ty, $body:block) => {
pub fn $name<const N: usize>(
lisp: &$crate::Lisp<N>,
args: $crate::ArenaIndex,
) -> $crate::ArenaResult<$crate::ArenaIndex> {
let ($arg1, _rest): ($ty1, _) = $crate::extract_arg(lisp, args)?;
let result: $ret = $body;
$crate::ToLisp::to_lisp(&result, lisp)
}
};
($name:ident, ($arg1:ident : $ty1:ty, $arg2:ident : $ty2:ty) -> $ret:ty, $body:block) => {
pub fn $name<const N: usize>(
lisp: &$crate::Lisp<N>,
args: $crate::ArenaIndex,
) -> $crate::ArenaResult<$crate::ArenaIndex> {
let ($arg1, rest): ($ty1, _) = $crate::extract_arg(lisp, args)?;
let ($arg2, _rest): ($ty2, _) = $crate::extract_arg(lisp, rest)?;
let result: $ret = $body;
$crate::ToLisp::to_lisp(&result, lisp)
}
};
($name:ident, ($arg1:ident : $ty1:ty, $arg2:ident : $ty2:ty, $arg3:ident : $ty3:ty) -> $ret:ty, $body:block) => {
pub fn $name<const N: usize>(
lisp: &$crate::Lisp<N>,
args: $crate::ArenaIndex,
) -> $crate::ArenaResult<$crate::ArenaIndex> {
let ($arg1, rest): ($ty1, _) = $crate::extract_arg(lisp, args)?;
let ($arg2, rest): ($ty2, _) = $crate::extract_arg(lisp, rest)?;
let ($arg3, _rest): ($ty3, _) = $crate::extract_arg(lisp, rest)?;
let result: $ret = $body;
$crate::ToLisp::to_lisp(&result, lisp)
}
};
($name:ident, ($arg1:ident : $ty1:ty, $arg2:ident : $ty2:ty, $arg3:ident : $ty3:ty, $arg4:ident : $ty4:ty) -> $ret:ty, $body:block) => {
pub fn $name<const N: usize>(
lisp: &$crate::Lisp<N>,
args: $crate::ArenaIndex,
) -> $crate::ArenaResult<$crate::ArenaIndex> {
let ($arg1, rest): ($ty1, _) = $crate::extract_arg(lisp, args)?;
let ($arg2, rest): ($ty2, _) = $crate::extract_arg(lisp, rest)?;
let ($arg3, rest): ($ty3, _) = $crate::extract_arg(lisp, rest)?;
let ($arg4, _rest): ($ty4, _) = $crate::extract_arg(lisp, rest)?;
let result: $ret = $body;
$crate::ToLisp::to_lisp(&result, lisp)
}
};
($name:ident @with_lisp, () -> $ret:ty, $body:block) => {
#[allow(unused_variables)]
pub fn $name<const N: usize>(
lisp: &$crate::Lisp<N>,
args: $crate::ArenaIndex,
) -> $crate::ArenaResult<$crate::ArenaIndex> {
let result: $ret = $body;
$crate::ToLisp::to_lisp(&result, lisp)
}
};
($name:ident @with_lisp, ($arg1:ident : $ty1:ty) -> $ret:ty, $body:block) => {
#[allow(unused_variables)]
pub fn $name<const N: usize>(
lisp: &$crate::Lisp<N>,
args: $crate::ArenaIndex,
) -> $crate::ArenaResult<$crate::ArenaIndex> {
let ($arg1, args): ($ty1, _) = $crate::extract_arg(lisp, args)?;
let result: $ret = $body;
$crate::ToLisp::to_lisp(&result, lisp)
}
};
($name:ident @with_lisp, ($arg1:ident : $ty1:ty, $arg2:ident : $ty2:ty) -> $ret:ty, $body:block) => {
#[allow(unused_variables)]
pub fn $name<const N: usize>(
lisp: &$crate::Lisp<N>,
args: $crate::ArenaIndex,
) -> $crate::ArenaResult<$crate::ArenaIndex> {
let ($arg1, args): ($ty1, _) = $crate::extract_arg(lisp, args)?;
let ($arg2, args): ($ty2, _) = $crate::extract_arg(lisp, args)?;
let result: $ret = $body;
$crate::ToLisp::to_lisp(&result, lisp)
}
};
($name:ident @with_lisp, ($arg1:ident : $ty1:ty, $arg2:ident : $ty2:ty, $arg3:ident : $ty3:ty) -> $ret:ty, $body:block) => {
#[allow(unused_variables)]
pub fn $name<const N: usize>(
lisp: &$crate::Lisp<N>,
args: $crate::ArenaIndex,
) -> $crate::ArenaResult<$crate::ArenaIndex> {
let ($arg1, args): ($ty1, _) = $crate::extract_arg(lisp, args)?;
let ($arg2, args): ($ty2, _) = $crate::extract_arg(lisp, args)?;
let ($arg3, args): ($ty3, _) = $crate::extract_arg(lisp, args)?;
let result: $ret = $body;
$crate::ToLisp::to_lisp(&result, lisp)
}
};
($name:ident @with_lisp, ($arg1:ident : $ty1:ty, $arg2:ident : $ty2:ty, $arg3:ident : $ty3:ty, $arg4:ident : $ty4:ty) -> $ret:ty, $body:block) => {
#[allow(unused_variables)]
pub fn $name<const N: usize>(
lisp: &$crate::Lisp<N>,
args: $crate::ArenaIndex,
) -> $crate::ArenaResult<$crate::ArenaIndex> {
let ($arg1, args): ($ty1, _) = $crate::extract_arg(lisp, args)?;
let ($arg2, args): ($ty2, _) = $crate::extract_arg(lisp, args)?;
let ($arg3, args): ($ty3, _) = $crate::extract_arg(lisp, args)?;
let ($arg4, args): ($ty4, _) = $crate::extract_arg(lisp, args)?;
let result: $ret = $body;
$crate::ToLisp::to_lisp(&result, lisp)
}
};
}