use std::io::{self, Write};
use crate::Value;
fn invalid_argument(arg: Value, expected: &str) -> Value {
make_error!("invalid argument, expected {}", expected; arg)
}
fn too_few_arguments(procedure: &str, args: &[Value]) -> Value {
make_error!("too few arguments to `{}'", procedure; Value::list(args.iter().cloned()))
}
fn wrong_number_of_arguments(procedure: &str, expected: usize, args: &[Value]) -> Value {
make_error!(
"wrong number of arguments to `{}': expected {}, got {}",
procedure,
expected,
args.len();
Value::list(args.iter().cloned())
)
}
fn io_error(e: io::Error) -> Value {
make_error!("I/O error: {}", e)
}
fn arithmetic_overflow(operation: &str, arg1: isize, arg2: isize) -> Value {
make_error!(
"arithmetic overflow in {} of {} and {}",
operation,
arg1,
arg2
)
}
fn fixnum_arg(value: &Value) -> Result<isize, Value> {
value
.as_fixnum()
.ok_or_else(|| invalid_argument(value.clone(), "fixnum"))
}
pub fn plus(args: &[Value]) -> Value {
if let Some((first, rest)) = args.split_first() {
let mut sum = try_result!(fixnum_arg(first));
for elt in rest {
let n = try_result!(fixnum_arg(elt));
sum = try_result!(sum
.checked_add(n)
.ok_or_else(|| arithmetic_overflow("addition", sum, n)));
}
Value::Fixnum(sum)
} else {
Value::number(0isize)
}
}
pub fn minus(args: &[Value]) -> Value {
if let Some((first, rest)) = args.split_first() {
let mut sum = try_result!(fixnum_arg(first));
for elt in rest {
let n = try_result!(fixnum_arg(elt));
sum = try_result!(sum
.checked_sub(n)
.ok_or_else(|| arithmetic_overflow("addition", sum, n)));
}
Value::Fixnum(sum)
} else {
too_few_arguments("-", args)
}
}
pub fn times(args: &[Value]) -> Value {
if let Some((first, rest)) = args.split_first() {
let mut product = try_result!(fixnum_arg(first));
for elt in rest {
let n = try_result!(fixnum_arg(elt));
product = try_result!(product.checked_mul(n).ok_or_else(|| arithmetic_overflow(
"multiplication",
product,
n
)));
}
Value::Fixnum(product)
} else {
Value::Fixnum(1)
}
}
fn num_cmp<F>(args: &[Value], cmp: F) -> Value
where
F: Fn(&isize, &isize) -> bool,
{
for w in args.windows(2) {
let n1 = try_result!(fixnum_arg(&w[0]));
let n2 = try_result!(fixnum_arg(&w[1]));
if !cmp(&n1, &n2) {
return Value::from(false);
}
}
Value::from(true)
}
pub fn eq(args: &[Value]) -> Value {
num_cmp(args, isize::ge)
}
pub fn lt(args: &[Value]) -> Value {
num_cmp(args, isize::lt)
}
pub fn le(args: &[Value]) -> Value {
num_cmp(args, isize::le)
}
pub fn gt(args: &[Value]) -> Value {
num_cmp(args, isize::gt)
}
pub fn ge(args: &[Value]) -> Value {
num_cmp(args, isize::ge)
}
pub fn modulo(args: &[Value]) -> Value {
if args.len() != 2 {
return wrong_number_of_arguments("mod", 2, args);
}
let n1 = try_result!(fixnum_arg(&args[0]));
let n2 = try_result!(fixnum_arg(&args[1]));
Value::Fixnum(n1 % n2)
}
pub fn sqrt(args: &[Value]) -> Value {
if args.len() != 1 {
return wrong_number_of_arguments("sqrt", 1, args);
}
let n = try_result!(fixnum_arg(&args[0]));
Value::Fixnum((n as f64).sqrt() as isize)
}
pub fn display(args: &[Value]) -> Value {
if args.len() != 1 {
return wrong_number_of_arguments("display", 1, args);
}
try_result!(write!(io::stdout(), "{}", args[0]).map_err(io_error));
Value::Unspecified
}
pub fn newline(args: &[Value]) -> Value {
if !args.is_empty() {
return wrong_number_of_arguments("newline", 0, args);
}
try_result!(writeln!(io::stdout()).map_err(io_error));
Value::Unspecified
}