use tan::{
context::Context,
error::Error,
expr::Expr,
util::{
args::{unpack_float_arg, unpack_float_range_arg},
module_util::require_module,
},
};
pub fn range_int_new(args: &[Expr]) -> Result<Expr, Error> {
let [start, end, ..] = args else {
return Err(Error::invalid_arguments(
"requires `start`, `end` arguments",
None,
));
};
let Some(start) = start.as_int() else {
return Err(Error::invalid_arguments(
&format!("start=`{start}` is not Int"),
start.range(),
));
};
let Some(end) = end.as_int() else {
return Err(Error::invalid_arguments(
&format!("end=`{end}` is not Int"),
end.range(),
));
};
let step = if let Some(step) = args.get(2) {
let Some(step) = step.as_int() else {
return Err(Error::invalid_arguments(
&format!("step=`{step}` is not Int"),
step.range(),
));
};
step
} else if end >= start {
1
} else {
-1
};
Ok(Expr::IntRange(start, end, step))
}
pub fn range_float_new(args: &[Expr]) -> Result<Expr, Error> {
let [start, end, ..] = args else {
return Err(Error::invalid_arguments(
"requires `start`, `end`, and `step` arguments",
None,
));
};
let Some(start) = start.as_float() else {
return Err(Error::invalid_arguments(
&format!("start=`{start}` is not Float"),
start.range(),
));
};
let Some(end) = end.as_float() else {
return Err(Error::invalid_arguments(
&format!("end=`{end}` is not Float"),
end.range(),
));
};
let step = if let Some(step) = args.get(2) {
let Some(step) = step.as_float() else {
return Err(Error::invalid_arguments(
&format!("step=`{step}` is not Float"),
step.range(),
));
};
step
} else if end >= start {
1.0
} else {
-1.0
};
Ok(Expr::FloatRange(start, end, step))
}
pub fn range_float_contains(args: &[Expr]) -> Result<Expr, Error> {
let range = unpack_float_range_arg(args, 0, "range")?;
let value = unpack_float_arg(args, 1, "value")?;
Ok(Expr::Bool(range.contains(&value)))
}
pub fn setup_lib_range(context: &mut Context) {
let module = require_module("prelude", context);
module.insert_invocable("Range", Expr::foreign_func(&range_int_new));
module.insert_invocable("Range$$Int$$Int", Expr::foreign_func(&range_int_new));
module.insert_invocable("Range$$Int$$Int$$Int", Expr::foreign_func(&range_int_new));
module.insert_invocable("Range$$Float$$Float", Expr::foreign_func(&range_float_new));
module.insert_invocable(
"Range$$Float$$Float$$Float",
Expr::foreign_func(&range_float_new),
);
module.insert_invocable(
"contains?$$(Range Float)$$Float",
Expr::foreign_func(&range_float_contains),
);
module.insert_invocable("surrounds?", Expr::foreign_func(&range_float_contains));
module.insert_invocable(
"surrounds?$$(Range Float)$$Float",
Expr::foreign_func(&range_float_contains),
);
}