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::Socket(x), Value::Socket(y)) => Rc::ptr_eq(x, y),
57 (Value::Pup(x), Value::Pup(y)) => Rc::ptr_eq(x, y),
59 (Value::Bowl(x), Value::Bowl(y)) => Rc::ptr_eq(x, y),
60 (Value::Int(_), _)
64 | (Value::Float(_), _)
65 | (Value::Str(_), _)
66 | (Value::Bool(_), _)
67 | (Value::None, _)
68 | (Value::List(_), _)
69 | (Value::Dict(_), _)
70 | (Value::Object(_), _)
71 | (Value::Function(_), _)
72 | (Value::Class(_), _)
73 | (Value::BoundMethod(_), _)
74 | (Value::Error(_), _)
75 | (Value::Socket(_), _)
76 | (Value::Pup(_), _)
77 | (Value::Bowl(_), _) => false,
78 }
79}
80
81pub fn eq(a: Value, b: Value) -> DogeResult {
83 Ok(Value::Bool(values_equal(&a, &b)))
84}
85
86pub fn ne(a: Value, b: Value) -> DogeResult {
88 Ok(Value::Bool(!values_equal(&a, &b)))
89}
90
91pub(crate) fn slice_contains(items: &[Value], target: &Value) -> bool {
94 items.iter().any(|element| values_equal(element, target))
95}
96
97pub fn in_(needle: Value, container: Value) -> DogeResult {
102 let found = match &container {
103 Value::List(items) => slice_contains(&items.borrow(), &needle),
104 Value::Dict(entries) => match &needle {
107 Value::Str(k) => entries.borrow().contains_key(k.as_ref()),
108 _ => false,
109 },
110 Value::Str(haystack) => match &needle {
111 Value::Str(sub) => haystack.contains(sub.as_ref()),
112 _ => {
113 return Err(DogeError::type_error(format!(
114 "can only check if a Str is in a Str, not {}",
115 needle.describe()
116 )));
117 }
118 },
119 Value::Int(_)
120 | Value::Float(_)
121 | Value::Bool(_)
122 | Value::None
123 | Value::Object(_)
124 | Value::Function(_)
125 | Value::Class(_)
126 | Value::BoundMethod(_)
127 | Value::Error(_)
128 | Value::Socket(_)
129 | Value::Pup(_)
130 | Value::Bowl(_) => {
131 return Err(DogeError::type_error(format!(
132 "in wants a List, Dict, or Str on the right, not {}",
133 container.describe()
134 )));
135 }
136 };
137 Ok(Value::Bool(found))
138}
139
140pub fn not_in(needle: Value, container: Value) -> DogeResult {
143 match in_(needle, container)? {
144 Value::Bool(found) => Ok(Value::Bool(!found)),
145 _ => unreachable!("in_ always yields a Bool"),
146 }
147}
148
149pub(crate) fn order(a: &Value, b: &Value) -> DogeResult<Ordering> {
153 if let (Some(x), Some(y)) = (as_f64(a), as_f64(b)) {
154 return x.partial_cmp(&y).ok_or_else(|| {
155 DogeError::type_error(format!(
156 "cannot compare {} and {}",
157 a.describe(),
158 b.describe()
159 ))
160 });
161 }
162 if let (Value::Str(x), Value::Str(y)) = (a, b) {
163 return Ok(x.as_ref().cmp(y.as_ref()));
164 }
165 Err(DogeError::type_error(format!(
166 "cannot compare {} and {}",
167 a.describe(),
168 b.describe()
169 )))
170}
171
172pub fn lt(a: Value, b: Value) -> DogeResult {
174 Ok(Value::Bool(order(&a, &b)? == Ordering::Less))
175}
176
177pub fn le(a: Value, b: Value) -> DogeResult {
179 Ok(Value::Bool(matches!(
180 order(&a, &b)?,
181 Ordering::Less | Ordering::Equal
182 )))
183}
184
185pub fn gt(a: Value, b: Value) -> DogeResult {
187 Ok(Value::Bool(order(&a, &b)? == Ordering::Greater))
188}
189
190pub fn ge(a: Value, b: Value) -> DogeResult {
192 Ok(Value::Bool(matches!(
193 order(&a, &b)?,
194 Ordering::Greater | Ordering::Equal
195 )))
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn bound_methods_equal_only_on_the_same_receiver_and_name() {
204 let a = Value::object(0, "Shibe");
205 let b = Value::object(0, "Shibe");
206 assert!(values_equal(
208 &Value::bound_method(a.clone(), "speak"),
209 &Value::bound_method(a.clone(), "speak"),
210 ));
211 assert!(!values_equal(
213 &Value::bound_method(a.clone(), "speak"),
214 &Value::bound_method(a.clone(), "wag"),
215 ));
216 assert!(!values_equal(
218 &Value::bound_method(a, "speak"),
219 &Value::bound_method(b, "speak"),
220 ));
221 }
222}