Skip to main content

tancore/
eq.rs

1use tan::{
2    context::Context,
3    error::Error,
4    expr::Expr,
5    util::{
6        args::{
7            unpack_bool_arg, unpack_float_arg, unpack_int_arg, unpack_stringable_arg, unpack_u8_arg,
8        },
9        module_util::require_module,
10    },
11};
12
13// #todo support all types!
14
15// #todo add support for eq_array, eq_map
16
17// #todo Not needed any more, remove it!
18// #todo #temp hackish polymorphism helper!
19// pub fn eq_polymorphic(args: &[Expr]) -> Result<Expr, Error> {
20//     let Some(expr) = args.first() else {
21//         return Err(Error::invalid_arguments("malformed equality test", None));
22//     };
23//     match expr.unpack() {
24//         Expr::Int(..) => eq_int(args),
25//         Expr::Bool(..) => eq_bool(args),
26//         Expr::Float(..) => eq_float(args),
27//         Expr::String(..) => eq_string(args),
28//         Expr::Symbol(..) | Expr::KeySymbol(..) | Expr::Type(..) => eq_symbol(args),
29//         Expr::Array(..) => array_eq(args),
30//         _ => Err(Error::invalid_arguments("malformed equality test", None)),
31//     }
32// }
33
34pub fn eq_int(args: &[Expr]) -> Result<Expr, Error> {
35    // Use macros to monomorphise functions? or can we leverage Rust's generics? per viariant? maybe with cost generics?
36    // #todo support overloading,
37    // #todo make equality a method of Expr?
38    // #todo support non-Int types
39    // #todo support multiple arguments.
40
41    // #todo also pass the function name, or at least show the function name upstream.
42    let a = unpack_int_arg(args, 0, "a")?;
43    let b = unpack_int_arg(args, 1, "b")?;
44
45    Ok(Expr::Bool(a == b))
46}
47
48pub fn eq_u8(args: &[Expr]) -> Result<Expr, Error> {
49    // Use macros to monomorphise functions? or can we leverage Rust's generics? per viariant? maybe with cost generics?
50    // #todo support overloading,
51    // #todo make equality a method of Expr?
52    // #todo support non-Int types
53    // #todo support multiple arguments.
54
55    // #todo also pass the function name, or at least show the function name upstream.
56    let a = unpack_u8_arg(args, 0, "a")?;
57    let b = unpack_u8_arg(args, 1, "b")?;
58
59    Ok(Expr::Bool(a == b))
60}
61
62pub fn eq_float(args: &[Expr]) -> Result<Expr, Error> {
63    // Use macros to monomorphise functions? or can we leverage Rust's generics? per viariant? maybe with cost generics?
64    // #todo support overloading,
65    // #todo make equality a method of Expr?
66    // #todo support non-Int types
67    // #todo support multiple arguments.
68
69    let a = unpack_float_arg(args, 0, "a")?;
70    let b = unpack_float_arg(args, 1, "b")?;
71
72    Ok(Expr::Bool(a == b))
73}
74
75pub fn eq_bool(args: &[Expr]) -> Result<Expr, Error> {
76    // #todo check comments in other eq_* functions.
77
78    // #todo also pass the function name, or at least show the function name upstream.
79    let a = unpack_bool_arg(args, 0, "a")?;
80    let b = unpack_bool_arg(args, 1, "b")?;
81
82    Ok(Expr::Bool(a == b))
83}
84
85pub fn eq_string(args: &[Expr]) -> Result<Expr, Error> {
86    // Use macros to monomorphise functions? or can we leverage Rust's generics? per viariant? maybe with cost generics?
87    // #todo support overloading,
88    // #todo make equality a method of Expr?
89    // #todo support non-Int types
90    // #todo support multiple arguments.
91
92    let a = unpack_stringable_arg(args, 0, "a")?;
93    let b = unpack_stringable_arg(args, 1, "b")?;
94
95    Ok(Expr::Bool(a == b))
96}
97
98// #insight handles both (quoted) Symbol and KeySymbol, they are the same thing anyway. Also handles Type.
99pub fn eq_symbol(args: &[Expr]) -> Result<Expr, Error> {
100    // Use macros to monomorphise functions? or can we leverage Rust's generics? per viariant? maybe with cost generics?
101    // #todo support overloading,
102    // #todo make equality a method of Expr?
103    // #todo support non-Int types
104    // #todo support multiple arguments.
105    let [a, b] = args else {
106        return Err(Error::invalid_arguments(
107            "`=` requires at least two arguments",
108            None,
109        ));
110    };
111
112    let Some(a) = a.as_symbolic() else {
113        return Err(Error::invalid_arguments(
114            &format!("`{a}` is not a Symbol"),
115            a.range(),
116        ));
117    };
118
119    let Some(b) = b.as_symbolic() else {
120        return Err(Error::invalid_arguments(
121            &format!("`{b}` is not a Symbol"),
122            b.range(),
123        ));
124    };
125
126    Ok(Expr::Bool(a == b))
127}
128
129// #todo implement not_eq_* with Tan? can be automatically generic!
130
131pub fn not_eq_int(args: &[Expr]) -> Result<Expr, Error> {
132    Ok(Expr::Bool(eq_int(args)?.is_false()))
133}
134
135pub fn not_eq_float(args: &[Expr]) -> Result<Expr, Error> {
136    Ok(Expr::Bool(eq_float(args)?.is_false()))
137}
138
139pub fn not_eq_string(args: &[Expr]) -> Result<Expr, Error> {
140    Ok(Expr::Bool(eq_string(args)?.is_false()))
141}
142
143// #insight handles both (quoted) Symbol and KeySymbol, they are the same thing anyway.
144pub fn not_eq_symbol(args: &[Expr]) -> Result<Expr, Error> {
145    // Use macros to monomorphise functions? or can we leverage Rust's generics? per viariant? maybe with cost generics?
146    // #todo support overloading,
147    // #todo make equality a method of Expr?
148    // #todo support non-Int types
149    // #todo support multiple arguments.
150    let [a, b] = args else {
151        return Err(Error::invalid_arguments(
152            "`!=` requires at least two arguments",
153            None,
154        ));
155    };
156
157    let Some(a) = a.as_symbolic() else {
158        return Err(Error::invalid_arguments(
159            &format!("`{a}` is not a String"),
160            a.range(),
161        ));
162    };
163
164    let Some(b) = b.as_symbolic() else {
165        return Err(Error::invalid_arguments(
166            &format!("`{b}` is not a Symbol"),
167            b.range(),
168        ));
169    };
170
171    Ok(Expr::Bool(a != b))
172}
173
174pub fn int_gt(args: &[Expr]) -> Result<Expr, Error> {
175    // #todo support multiple arguments.
176    let [a, b] = args else {
177        return Err(Error::invalid_arguments(
178            "`>` requires at least two arguments",
179            None,
180        ));
181    };
182
183    let Some(a) = a.as_int() else {
184        return Err(Error::invalid_arguments(
185            &format!("`{a}` is not an Int"),
186            a.range(),
187        ));
188    };
189
190    let Some(b) = b.as_int() else {
191        return Err(Error::invalid_arguments(
192            &format!("`{b}` is not an Int"),
193            b.range(),
194        ));
195    };
196
197    Ok(Expr::Bool(a > b))
198}
199
200pub fn float_gt(args: &[Expr]) -> Result<Expr, Error> {
201    // #todo support multiple arguments.
202    let [a, b] = args else {
203        return Err(Error::invalid_arguments(
204            "`>` requires at least two arguments",
205            None,
206        ));
207    };
208
209    let Some(a) = a.as_float() else {
210        return Err(Error::invalid_arguments(
211            &format!("`{a}` is not a Float"),
212            a.range(),
213        ));
214    };
215
216    let Some(b) = b.as_float() else {
217        return Err(Error::invalid_arguments(
218            &format!("`{b}` is not a Float"),
219            b.range(),
220        ));
221    };
222
223    Ok(Expr::Bool(a > b))
224}
225
226pub fn int_gte(args: &[Expr]) -> Result<Expr, Error> {
227    // #todo Support multiple arguments.
228
229    let a = unpack_int_arg(args, 0, "a")?;
230    let b = unpack_int_arg(args, 1, "b")?;
231
232    Ok(Expr::Bool(a >= b))
233}
234
235pub fn float_gte(args: &[Expr]) -> Result<Expr, Error> {
236    // #todo Support multiple arguments.
237
238    let a = unpack_float_arg(args, 0, "a")?;
239    let b = unpack_float_arg(args, 1, "b")?;
240
241    Ok(Expr::Bool(a >= b))
242}
243
244pub fn int_lt(args: &[Expr]) -> Result<Expr, Error> {
245    // #todo support multiple arguments.
246    let [a, b] = args else {
247        return Err(Error::invalid_arguments(
248            "`<` requires at least two arguments",
249            None,
250        ));
251    };
252
253    let Some(a) = a.as_int() else {
254        return Err(Error::invalid_arguments(
255            &format!("`{a}` is not an Int"),
256            a.range(),
257        ));
258    };
259
260    let Some(b) = b.as_int() else {
261        return Err(Error::invalid_arguments(
262            &format!("`{b}` is not an Int"),
263            b.range(),
264        ));
265    };
266
267    Ok(Expr::Bool(a < b))
268}
269
270pub fn float_lt(args: &[Expr]) -> Result<Expr, Error> {
271    // #todo support multiple arguments.
272
273    let a = unpack_float_arg(args, 0, "a")?;
274    let b = unpack_float_arg(args, 1, "b")?;
275
276    Ok(Expr::Bool(a < b))
277}
278
279pub fn int_lte(args: &[Expr]) -> Result<Expr, Error> {
280    // #todo Support multiple arguments.
281
282    let a = unpack_int_arg(args, 0, "a")?;
283    let b = unpack_int_arg(args, 1, "b")?;
284
285    Ok(Expr::Bool(a <= b))
286}
287
288pub fn float_lte(args: &[Expr]) -> Result<Expr, Error> {
289    // #todo Support multiple arguments.
290
291    let a = unpack_float_arg(args, 0, "a")?;
292    let b = unpack_float_arg(args, 1, "b")?;
293
294    Ok(Expr::Bool(a <= b))
295}
296
297// #todo should we have an explicit module for these functions?
298
299pub fn setup_lib_eq(context: &mut Context) {
300    let module = require_module("prelude", context);
301
302    // module.insert_invocable("=", Expr::foreign_func(&eq_int));
303    module.insert_invocable("=$$Int$$Int", Expr::foreign_func(&eq_int));
304    module.insert_invocable("=$$U8$$U8", Expr::foreign_func(&eq_u8));
305    module.insert_invocable("=$$Bool$$Bool", Expr::foreign_func(&eq_bool));
306    module.insert_invocable("=$$Float$$Float", Expr::foreign_func(&eq_float));
307    module.insert_invocable("=$$String$$String", Expr::foreign_func(&eq_string));
308    // module.insert_invocable("=$$Symbol$$Symbol", Expr::foreign_func(&eq_symbol)));
309    module.insert_invocable("=$$KeySymbol$$KeySymbol", Expr::foreign_func(&eq_symbol));
310    // #todo #hack this is nasty!
311    module.insert_invocable("=$$Type$$Type", Expr::foreign_func(&eq_symbol));
312    module.insert_invocable("=$$Type$$String", Expr::foreign_func(&eq_symbol));
313    module.insert_invocable("=$$Type$$KeySymbol", Expr::foreign_func(&eq_symbol));
314
315    module.insert_invocable("!=$$Int$$Int", Expr::foreign_func(&not_eq_int));
316    module.insert_invocable("!=$$Float$$Float", Expr::foreign_func(&not_eq_float));
317    module.insert_invocable("!=$$String$$String", Expr::foreign_func(&not_eq_string));
318    module.insert_invocable("!=$$Symbol$$Symbol", Expr::foreign_func(&not_eq_symbol));
319    module.insert_invocable(
320        "!=$$KeySymbol$$KeySymbol",
321        Expr::foreign_func(&not_eq_symbol),
322    );
323
324    module.insert_invocable(">$$Int$$Int", Expr::foreign_func(&int_gt));
325    module.insert_invocable(">$$Float$$Float", Expr::foreign_func(&float_gt));
326    module.insert_invocable(">=$$Int$$Int", Expr::foreign_func(&int_gte));
327    module.insert_invocable(">=$$Float$$Float", Expr::foreign_func(&float_gte));
328    module.insert_invocable("<$$Int$$Int", Expr::foreign_func(&int_lt));
329    module.insert_invocable("<$$Float$$Float", Expr::foreign_func(&float_lt));
330    module.insert_invocable("<=$$Int$$Int", Expr::foreign_func(&int_lte));
331    module.insert_invocable("<=$$Float$$Float", Expr::foreign_func(&float_lte));
332}