1use std::cmp::Ordering;
2use std::rc::Rc;
3
4use super::as_f64;
5use crate::error::{DogeError, DogeResult};
6use crate::value::Value;
7
8pub fn values_equal(a: &Value, b: &Value) -> bool {
11 match (a, b) {
12 (Value::Int(x), Value::Int(y)) => x == y,
13 (Value::Float(x), Value::Float(y)) => x == y,
14 (Value::Int(x), Value::Float(y)) => (*x as f64) == *y,
15 (Value::Float(x), Value::Int(y)) => *x == (*y as f64),
16 (Value::Str(x), Value::Str(y)) => x == y,
17 (Value::Bool(x), Value::Bool(y)) => x == y,
18 (Value::None, Value::None) => true,
19 (Value::List(x), Value::List(y)) => {
20 let (xb, yb) = (x.borrow(), y.borrow());
21 xb.len() == yb.len() && xb.iter().zip(yb.iter()).all(|(p, q)| values_equal(p, q))
22 }
23 (Value::Dict(x), Value::Dict(y)) => {
24 let (xb, yb) = (x.borrow(), y.borrow());
25 xb.len() == yb.len()
26 && xb
27 .iter()
28 .all(|(k, v)| yb.get(k).is_some_and(|w| values_equal(v, w)))
29 }
30 (Value::Object(x), Value::Object(y)) => Rc::ptr_eq(x, y),
32 (Value::Function(x), Value::Function(y)) => {
36 x.fn_id == y.fn_id
37 && x.captures.len() == y.captures.len()
38 && x.captures
39 .iter()
40 .zip(y.captures.iter())
41 .all(|(p, q)| Rc::ptr_eq(p, q))
42 }
43 (Value::Class(x), Value::Class(y)) => x.fn_id == y.fn_id,
46 (Value::BoundMethod(x), Value::BoundMethod(y)) => {
49 x.method == y.method && values_equal(&x.receiver, &y.receiver)
50 }
51 (Value::Error(x), Value::Error(y)) => {
53 x.kind == y.kind && x.message == y.message && x.file == y.file && x.line == y.line
54 }
55 (Value::Int(_), _)
59 | (Value::Float(_), _)
60 | (Value::Str(_), _)
61 | (Value::Bool(_), _)
62 | (Value::None, _)
63 | (Value::List(_), _)
64 | (Value::Dict(_), _)
65 | (Value::Object(_), _)
66 | (Value::Function(_), _)
67 | (Value::Class(_), _)
68 | (Value::BoundMethod(_), _)
69 | (Value::Error(_), _) => false,
70 }
71}
72
73pub fn eq(a: Value, b: Value) -> DogeResult {
75 Ok(Value::Bool(values_equal(&a, &b)))
76}
77
78pub fn ne(a: Value, b: Value) -> DogeResult {
80 Ok(Value::Bool(!values_equal(&a, &b)))
81}
82
83pub(crate) fn slice_contains(items: &[Value], target: &Value) -> bool {
86 items.iter().any(|element| values_equal(element, target))
87}
88
89pub fn in_(needle: Value, container: Value) -> DogeResult {
94 let found = match &container {
95 Value::List(items) => slice_contains(&items.borrow(), &needle),
96 Value::Dict(entries) => match &needle {
99 Value::Str(k) => entries.borrow().contains_key(k.as_ref()),
100 _ => false,
101 },
102 Value::Str(haystack) => match &needle {
103 Value::Str(sub) => haystack.contains(sub.as_ref()),
104 _ => {
105 return Err(DogeError::type_error(format!(
106 "can only check if a Str is in a Str, not {}",
107 needle.describe()
108 )));
109 }
110 },
111 Value::Int(_)
112 | Value::Float(_)
113 | Value::Bool(_)
114 | Value::None
115 | Value::Object(_)
116 | Value::Function(_)
117 | Value::Class(_)
118 | Value::BoundMethod(_)
119 | Value::Error(_) => {
120 return Err(DogeError::type_error(format!(
121 "in wants a List, Dict, or Str on the right, not {}",
122 container.describe()
123 )));
124 }
125 };
126 Ok(Value::Bool(found))
127}
128
129pub fn not_in(needle: Value, container: Value) -> DogeResult {
132 match in_(needle, container)? {
133 Value::Bool(found) => Ok(Value::Bool(!found)),
134 _ => unreachable!("in_ always yields a Bool"),
135 }
136}
137
138pub(crate) fn order(a: &Value, b: &Value) -> DogeResult<Ordering> {
142 if let (Some(x), Some(y)) = (as_f64(a), as_f64(b)) {
143 return x.partial_cmp(&y).ok_or_else(|| {
144 DogeError::type_error(format!(
145 "cannot compare {} and {}",
146 a.describe(),
147 b.describe()
148 ))
149 });
150 }
151 if let (Value::Str(x), Value::Str(y)) = (a, b) {
152 return Ok(x.as_ref().cmp(y.as_ref()));
153 }
154 Err(DogeError::type_error(format!(
155 "cannot compare {} and {}",
156 a.describe(),
157 b.describe()
158 )))
159}
160
161pub fn lt(a: Value, b: Value) -> DogeResult {
163 Ok(Value::Bool(order(&a, &b)? == Ordering::Less))
164}
165
166pub fn le(a: Value, b: Value) -> DogeResult {
168 Ok(Value::Bool(matches!(
169 order(&a, &b)?,
170 Ordering::Less | Ordering::Equal
171 )))
172}
173
174pub fn gt(a: Value, b: Value) -> DogeResult {
176 Ok(Value::Bool(order(&a, &b)? == Ordering::Greater))
177}
178
179pub fn ge(a: Value, b: Value) -> DogeResult {
181 Ok(Value::Bool(matches!(
182 order(&a, &b)?,
183 Ordering::Greater | Ordering::Equal
184 )))
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn bound_methods_equal_only_on_the_same_receiver_and_name() {
193 let a = Value::object(0, "Shibe");
194 let b = Value::object(0, "Shibe");
195 assert!(values_equal(
197 &Value::bound_method(a.clone(), "speak"),
198 &Value::bound_method(a.clone(), "speak"),
199 ));
200 assert!(!values_equal(
202 &Value::bound_method(a.clone(), "speak"),
203 &Value::bound_method(a.clone(), "wag"),
204 ));
205 assert!(!values_equal(
207 &Value::bound_method(a, "speak"),
208 &Value::bound_method(b, "speak"),
209 ));
210 }
211}