1use tan::{
2 context::Context,
3 error::Error,
4 expr::Expr,
5 util::{
6 args::{unpack_float_arg, unpack_float_range_arg},
7 module_util::require_module,
8 },
9};
10
11pub fn range_int_new(args: &[Expr]) -> Result<Expr, Error> {
35 let [start, end, ..] = args else {
37 return Err(Error::invalid_arguments(
38 "requires `start`, `end` arguments",
39 None,
40 ));
41 };
42
43 let Some(start) = start.as_int() else {
45 return Err(Error::invalid_arguments(
46 &format!("start=`{start}` is not Int"),
47 start.range(),
48 ));
49 };
50
51 let Some(end) = end.as_int() else {
52 return Err(Error::invalid_arguments(
53 &format!("end=`{end}` is not Int"),
54 end.range(),
55 ));
56 };
57
58 let step = if let Some(step) = args.get(2) {
59 let Some(step) = step.as_int() else {
60 return Err(Error::invalid_arguments(
61 &format!("step=`{step}` is not Int"),
62 step.range(),
63 ));
64 };
65 step
66 } else if end >= start {
67 1
68 } else {
69 -1
70 };
71
72 Ok(Expr::IntRange(start, end, step))
74}
75
76pub fn range_float_new(args: &[Expr]) -> Result<Expr, Error> {
77 let [start, end, ..] = args else {
79 return Err(Error::invalid_arguments(
80 "requires `start`, `end`, and `step` arguments",
81 None,
82 ));
83 };
84
85 let Some(start) = start.as_float() else {
87 return Err(Error::invalid_arguments(
88 &format!("start=`{start}` is not Float"),
89 start.range(),
90 ));
91 };
92
93 let Some(end) = end.as_float() else {
94 return Err(Error::invalid_arguments(
95 &format!("end=`{end}` is not Float"),
96 end.range(),
97 ));
98 };
99
100 let step = if let Some(step) = args.get(2) {
101 let Some(step) = step.as_float() else {
102 return Err(Error::invalid_arguments(
103 &format!("step=`{step}` is not Float"),
104 step.range(),
105 ));
106 };
107 step
108 } else if end >= start {
109 1.0
110 } else {
111 -1.0
112 };
113
114 Ok(Expr::FloatRange(start, end, step))
116}
117
118pub fn range_float_contains(args: &[Expr]) -> Result<Expr, Error> {
133 let range = unpack_float_range_arg(args, 0, "range")?;
134 let value = unpack_float_arg(args, 1, "value")?;
135 Ok(Expr::Bool(range.contains(&value)))
136}
137
138pub fn setup_lib_range(context: &mut Context) {
139 let module = require_module("prelude", context);
141
142 module.insert_invocable("Range", Expr::foreign_func(&range_int_new));
143 module.insert_invocable("Range$$Int$$Int", Expr::foreign_func(&range_int_new));
144 module.insert_invocable("Range$$Int$$Int$$Int", Expr::foreign_func(&range_int_new));
145 module.insert_invocable("Range$$Float$$Float", Expr::foreign_func(&range_float_new));
146 module.insert_invocable(
147 "Range$$Float$$Float$$Float",
148 Expr::foreign_func(&range_float_new),
149 );
150
151 module.insert_invocable(
153 "contains?$$(Range Float)$$Float",
154 Expr::foreign_func(&range_float_contains),
155 );
156 module.insert_invocable("surrounds?", Expr::foreign_func(&range_float_contains));
158 module.insert_invocable(
159 "surrounds?$$(Range Float)$$Float",
160 Expr::foreign_func(&range_float_contains),
161 );
162}