1use super::JsValue;
4use super::coerce::{to_int32, to_number, to_string, to_uint32};
5
6pub fn add(left: &JsValue, right: &JsValue) -> JsValue {
12 if matches!(left, JsValue::String(_)) || matches!(right, JsValue::String(_)) {
14 let ls = to_string(left);
15 let rs = to_string(right);
16 return JsValue::String(ls + &rs);
17 }
18 JsValue::Number(to_number(left) + to_number(right))
19}
20
21pub fn sub(left: &JsValue, right: &JsValue) -> JsValue {
23 JsValue::Number(to_number(left) - to_number(right))
24}
25
26pub fn mul(left: &JsValue, right: &JsValue) -> JsValue {
28 JsValue::Number(to_number(left) * to_number(right))
29}
30
31pub fn div(left: &JsValue, right: &JsValue) -> JsValue {
33 JsValue::Number(to_number(left) / to_number(right))
34}
35
36pub fn rem(left: &JsValue, right: &JsValue) -> JsValue {
38 JsValue::Number(to_number(left) % to_number(right))
39}
40
41pub fn exp(left: &JsValue, right: &JsValue) -> JsValue {
43 JsValue::Number(to_number(left).powf(to_number(right)))
44}
45
46pub fn strict_eq(left: &JsValue, right: &JsValue) -> JsValue {
52 JsValue::Boolean(strict_eq_bool(left, right))
53}
54
55pub fn strict_eq_bool(left: &JsValue, right: &JsValue) -> bool {
57 match (left, right) {
58 (JsValue::Number(a), JsValue::Number(b)) => {
59 if a.is_nan() || b.is_nan() {
61 return false;
62 }
63 a == b
64 }
65 (JsValue::String(a), JsValue::String(b)) => a == b,
66 (JsValue::Boolean(a), JsValue::Boolean(b)) => a == b,
67 (JsValue::Null, JsValue::Null) => true,
68 (JsValue::Undefined, JsValue::Undefined) => true,
69 _ => false, }
71}
72
73pub fn strict_ne(left: &JsValue, right: &JsValue) -> JsValue {
75 JsValue::Boolean(!strict_eq_bool(left, right))
76}
77
78pub fn lt(left: &JsValue, right: &JsValue) -> Option<JsValue> {
80 abstract_compare(left, right).map(JsValue::Boolean)
81}
82
83pub fn gt(left: &JsValue, right: &JsValue) -> Option<JsValue> {
85 abstract_compare(right, left).map(JsValue::Boolean)
86}
87
88pub fn le(left: &JsValue, right: &JsValue) -> Option<JsValue> {
90 abstract_compare(right, left).map(|r| JsValue::Boolean(!r))
91}
92
93pub fn ge(left: &JsValue, right: &JsValue) -> Option<JsValue> {
95 abstract_compare(left, right).map(|r| JsValue::Boolean(!r))
96}
97
98fn abstract_compare(left: &JsValue, right: &JsValue) -> Option<bool> {
101 if let (JsValue::String(a), JsValue::String(b)) = (left, right) {
103 return Some(a < b);
104 }
105 let a = to_number(left);
107 let b = to_number(right);
108 if a.is_nan() || b.is_nan() {
109 return None; }
111 Some(a < b)
112}
113
114pub fn bit_and(left: &JsValue, right: &JsValue) -> JsValue {
120 JsValue::Number(f64::from(to_int32(left) & to_int32(right)))
121}
122
123pub fn bit_or(left: &JsValue, right: &JsValue) -> JsValue {
125 JsValue::Number(f64::from(to_int32(left) | to_int32(right)))
126}
127
128pub fn bit_xor(left: &JsValue, right: &JsValue) -> JsValue {
130 JsValue::Number(f64::from(to_int32(left) ^ to_int32(right)))
131}
132
133pub fn shl(left: &JsValue, right: &JsValue) -> JsValue {
139 let l = to_int32(left);
140 let r = to_uint32(right) & 0x1F;
141 JsValue::Number(f64::from(l << r))
142}
143
144pub fn shr(left: &JsValue, right: &JsValue) -> JsValue {
146 let l = to_int32(left);
147 let r = to_uint32(right) & 0x1F;
148 JsValue::Number(f64::from(l >> r))
149}
150
151pub fn ushr(left: &JsValue, right: &JsValue) -> JsValue {
153 let l = to_uint32(left);
154 let r = to_uint32(right) & 0x1F;
155 JsValue::Number(f64::from(l >> r))
156}
157
158pub fn neg(val: &JsValue) -> JsValue {
164 JsValue::Number(-to_number(val))
165}
166
167pub fn pos(val: &JsValue) -> JsValue {
169 JsValue::Number(to_number(val))
170}
171
172pub fn not(val: &JsValue) -> JsValue {
174 JsValue::Boolean(val.is_falsy())
175}
176
177pub fn bit_not(val: &JsValue) -> JsValue {
179 JsValue::Number(f64::from(!to_int32(val)))
180}
181
182pub fn type_of(val: &JsValue) -> JsValue {
184 JsValue::String(val.type_of().to_string())
185}
186
187pub fn void_op(_val: &JsValue) -> JsValue {
189 JsValue::Undefined
190}
191
192#[cfg(test)]
197mod tests {
198 use super::*;
199
200 fn n(v: f64) -> JsValue { JsValue::Number(v) }
201 fn s(v: &str) -> JsValue { JsValue::String(v.into()) }
202 fn b(v: bool) -> JsValue { JsValue::Boolean(v) }
203
204 #[test]
205 fn test_add_numbers() {
206 assert_eq!(add(&n(1.0), &n(2.0)), n(3.0));
207 assert_eq!(add(&n(-1.0), &n(1.0)), n(0.0));
208 }
209
210 #[test]
211 fn test_add_string_concat() {
212 assert_eq!(add(&s("hello"), &s(" world")), s("hello world"));
213 assert_eq!(add(&s("x"), &n(1.0)), s("x1"));
214 assert_eq!(add(&n(1.0), &s("x")), s("1x"));
215 assert_eq!(add(&s(""), &JsValue::Null), s("null"));
216 assert_eq!(add(&s(""), &b(true)), s("true"));
217 }
218
219 #[test]
220 fn test_arithmetic() {
221 assert_eq!(sub(&n(5.0), &n(3.0)), n(2.0));
222 assert_eq!(mul(&n(3.0), &n(4.0)), n(12.0));
223 assert_eq!(div(&n(10.0), &n(3.0)), n(10.0 / 3.0));
224 assert_eq!(rem(&n(10.0), &n(3.0)), n(1.0));
225 assert_eq!(exp(&n(2.0), &n(10.0)), n(1024.0));
226 }
227
228 #[test]
229 fn test_strict_equality() {
230 assert_eq!(strict_eq(&n(1.0), &n(1.0)), b(true));
231 assert_eq!(strict_eq(&n(1.0), &n(2.0)), b(false));
232 assert_eq!(strict_eq(&s("a"), &s("a")), b(true));
233 assert_eq!(strict_eq(&n(1.0), &s("1")), b(false)); assert_eq!(strict_eq(&JsValue::Null, &JsValue::Null), b(true));
235 assert_eq!(strict_eq(&JsValue::Null, &JsValue::Undefined), b(false));
236 assert_eq!(strict_eq(&n(f64::NAN), &n(f64::NAN)), b(false));
238 }
239
240 #[test]
241 fn test_comparison() {
242 assert_eq!(lt(&n(1.0), &n(2.0)), Some(b(true)));
243 assert_eq!(gt(&n(2.0), &n(1.0)), Some(b(true)));
244 assert_eq!(le(&n(1.0), &n(1.0)), Some(b(true)));
245 assert_eq!(ge(&n(1.0), &n(1.0)), Some(b(true)));
246 assert_eq!(lt(&s("a"), &s("b")), Some(b(true)));
248 assert_eq!(gt(&s("b"), &s("a")), Some(b(true)));
249 assert_eq!(lt(&n(f64::NAN), &n(1.0)), None);
251 }
252
253 #[test]
254 fn test_bitwise() {
255 assert_eq!(bit_and(&n(255.0), &n(15.0)), n(15.0));
256 assert_eq!(bit_or(&n(240.0), &n(15.0)), n(255.0));
257 assert_eq!(bit_xor(&n(255.0), &n(15.0)), n(240.0));
258 }
259
260 #[test]
261 fn test_shift() {
262 assert_eq!(shl(&n(1.0), &n(8.0)), n(256.0));
263 assert_eq!(shr(&n(256.0), &n(4.0)), n(16.0));
264 assert_eq!(ushr(&n(-1.0), &n(0.0)), n(4294967295.0));
265 }
266
267 #[test]
268 fn test_unary() {
269 assert_eq!(neg(&n(5.0)), n(-5.0));
270 assert_eq!(pos(&s("42")), n(42.0));
271 assert_eq!(not(&b(true)), b(false));
272 assert_eq!(not(&n(0.0)), b(true));
273 assert_eq!(bit_not(&n(0.0)), n(-1.0));
274 assert_eq!(type_of(&n(1.0)), s("number"));
275 assert_eq!(void_op(&n(1.0)), JsValue::Undefined);
276 }
277}