1use std::cmp::Ordering as CmpOrdering;
9use std::fmt::Write as _;
10use std::hash::Hasher;
11
12use num_bigint::BigInt;
13use num_integer::Integer;
14use num_traits::{Signed, ToPrimitive, Zero};
15use rustc_hash::FxHasher;
16
17use crate::class::{heap_class, ids, Class, ObjKind};
18use crate::dict::Probe;
19use crate::mutator::{panic, Mutator, RtResult, Trap};
20use crate::object::*;
21use crate::ownership::{read_list, with_read};
22use crate::symbol::{intern, Sym};
23use crate::value::{fits_small, Value};
24
25pub trait Interop {
27 fn mutator(&mut self) -> &mut Mutator;
28 fn call_method(&mut self, recv: Value, name: Sym, args: &[Value]) -> RtResult<Option<Value>>;
31}
32
33pub struct Syms {
34 pub eq: Sym,
35 pub hash: Sym,
36 pub cmp: Sym,
37 pub to_s: Sym,
38 pub inspect: Sym,
39}
40
41pub static SYMS: std::sync::LazyLock<Syms> = std::sync::LazyLock::new(|| Syms {
42 eq: intern("=="),
43 hash: intern("hash"),
44 cmp: intern("<=>"),
45 to_s: intern("to_s"),
46 inspect: intern("inspect"),
47});
48
49pub fn hash_bytes(b: &[u8]) -> u64 {
50 let mut h = FxHasher::default();
51 h.write(b);
52 h.write_usize(b.len());
53 mix(h.finish())
54}
55
56#[inline]
57fn mix(mut x: u64) -> u64 {
58 x ^= x >> 30;
60 x = x.wrapping_mul(0xbf58_476d_1ce4_e5b9);
61 x ^= x >> 27;
62 x = x.wrapping_mul(0x94d0_49bb_1331_11eb);
63 x ^ (x >> 31)
64}
65
66#[inline]
67pub fn hash_int(i: i64) -> u64 {
68 mix(i as u64 ^ 0x5151_5151)
69}
70
71fn user_class(h: &catallaxy_gc::Header) -> &'static Class {
72 unsafe { heap_class(h) }
73}
74
75fn is_user_instance(cls: &Class) -> bool {
76 cls.kind == ObjKind::Instance && cls.id >= ids::FIRST_USER
77}
78
79pub fn is_int(v: Value) -> bool {
83 v.is_small_int() || (v.is_ptr() && kind_of(v) == ObjKind::BigInt)
84}
85
86fn bigint_ref(v: Value) -> Option<&'static BigInt> {
88 if v.is_ptr() && kind_of(v) == ObjKind::BigInt {
89 Some(&unsafe { as_bigint(v) }.value)
90 } else {
91 None
92 }
93}
94
95pub fn to_bigint(v: Value) -> BigInt {
96 if v.is_small_int() {
97 BigInt::from(v.as_small_int())
98 } else {
99 bigint_ref(v).cloned().unwrap_or_default()
100 }
101}
102
103pub fn int_to_f64(v: Value) -> f64 {
104 if v.is_small_int() {
105 v.as_small_int() as f64
106 } else {
107 bigint_ref(v).and_then(|b| b.to_f64()).unwrap_or(f64::NAN)
108 }
109}
110
111#[inline]
113pub fn int_as_i64(v: Value) -> Option<i64> {
114 if v.is_small_int() {
115 Some(v.as_small_int())
116 } else {
117 bigint_ref(v).and_then(|b| b.to_i64())
118 }
119}
120
121#[inline]
122pub fn int_add(m: &mut Mutator, a: Value, b: Value) -> Value {
123 if Value::both_small_ints(a, b) {
124 return m.int(a.as_small_int() + b.as_small_int());
125 }
126 m.int_from_big(to_bigint(a) + to_bigint(b))
127}
128
129#[inline]
130pub fn int_sub(m: &mut Mutator, a: Value, b: Value) -> Value {
131 if Value::both_small_ints(a, b) {
132 return m.int(a.as_small_int() - b.as_small_int());
133 }
134 m.int_from_big(to_bigint(a) - to_bigint(b))
135}
136
137#[inline]
138pub fn int_mul(m: &mut Mutator, a: Value, b: Value) -> Value {
139 if Value::both_small_ints(a, b) {
140 if let Some(p) = a.as_small_int().checked_mul(b.as_small_int()) {
141 return m.int(p);
142 }
143 }
144 m.int_from_big(to_bigint(a) * to_bigint(b))
145}
146
147fn zero_div<T>() -> RtResult<T> {
148 panic("ZeroDivisionError: divided by 0")
149}
150
151pub fn int_div(m: &mut Mutator, a: Value, b: Value) -> RtResult<Value> {
153 if Value::both_small_ints(a, b) {
154 let (x, y) = (a.as_small_int(), b.as_small_int());
155 if y == 0 {
156 return zero_div();
157 }
158 return Ok(m.int(Integer::div_floor(&x, &y)));
159 }
160 let y = to_bigint(b);
161 if y.is_zero() {
162 return zero_div();
163 }
164 Ok(m.int_from_big(to_bigint(a).div_floor(&y)))
165}
166
167pub fn int_mod(m: &mut Mutator, a: Value, b: Value) -> RtResult<Value> {
169 if Value::both_small_ints(a, b) {
170 let (x, y) = (a.as_small_int(), b.as_small_int());
171 if y == 0 {
172 return zero_div();
173 }
174 return Ok(m.int(Integer::mod_floor(&x, &y)));
175 }
176 let y = to_bigint(b);
177 if y.is_zero() {
178 return zero_div();
179 }
180 Ok(m.int_from_big(to_bigint(a).mod_floor(&y)))
181}
182
183pub fn int_pow(m: &mut Mutator, a: Value, b: Value) -> RtResult<Value> {
184 let Some(e) = int_as_i64(b) else { return panic("exponent too large") };
185 if e < 0 {
186 return panic("negative exponent for Int ** Int; use Float");
187 }
188 if e > u32::MAX as i64 {
189 return panic("exponent too large");
190 }
191 if let Some(x) = a.is_small_int().then(|| a.as_small_int()) {
192 if let Some(p) = x.checked_pow(e as u32) {
193 return Ok(m.int(p));
194 }
195 }
196 Ok(m.int_from_big(num_traits::pow(to_bigint(a), e as usize)))
197}
198
199pub fn int_neg(m: &mut Mutator, a: Value) -> Value {
200 if a.is_small_int() {
201 return m.int(-a.as_small_int());
202 }
203 m.int_from_big(-to_bigint(a))
204}
205
206pub fn int_bitop(m: &mut Mutator, op: u8, a: Value, b: Value) -> RtResult<Value> {
207 if Value::both_small_ints(a, b) {
208 let (x, y) = (a.as_small_int(), b.as_small_int());
209 let r = match op {
210 b'&' => x & y,
211 b'|' => x | y,
212 b'^' => x ^ y,
213 b'<' => {
214 if y < 0 {
215 return panic("negative shift count");
216 }
217 if y < 16 && fits_small(x << y) && (x << y) >> y == x {
218 x << y
219 } else {
220 return Ok(m.int_from_big(BigInt::from(x) << (y as usize)));
221 }
222 }
223 b'>' => {
224 if y < 0 {
225 return panic("negative shift count");
226 }
227 x >> y.min(63)
228 }
229 _ => unreachable!(),
230 };
231 return Ok(m.int(r));
232 }
233 let (x, y) = (to_bigint(a), to_bigint(b));
234 let r = match op {
235 b'&' => x & y,
236 b'|' => x | y,
237 b'^' => x ^ y,
238 b'<' | b'>' => {
239 let Some(n) = y.to_usize() else { return panic("invalid shift count") };
240 if op == b'<' {
241 x << n
242 } else {
243 x >> n
244 }
245 }
246 _ => unreachable!(),
247 };
248 Ok(m.int_from_big(r))
249}
250
251pub fn int_cmp(a: Value, b: Value) -> CmpOrdering {
252 if Value::both_small_ints(a, b) {
253 return a.as_small_int().cmp(&b.as_small_int());
254 }
255 to_bigint(a).cmp(&to_bigint(b))
256}
257
258pub fn float_to_int(m: &mut Mutator, f: f64) -> RtResult<Value> {
260 if !f.is_finite() {
261 return panic(format!("FloatDomainError: {}", format_float(f)));
262 }
263 let t = f.trunc();
264 if t.abs() < 1e15 {
265 return Ok(m.int(t as i64));
266 }
267 use num_traits::FromPrimitive;
268 Ok(m.int_from_big(BigInt::from_f64(t).unwrap()))
269}
270
271fn num_f64(v: Value) -> Option<f64> {
272 if v.is_float() {
273 Some(v.as_f64())
274 } else if is_int(v) {
275 Some(int_to_f64(v))
276 } else {
277 None
278 }
279}
280
281pub fn format_float(f: f64) -> String {
286 if f.is_nan() {
287 return "NaN".into();
288 }
289 if f.is_infinite() {
290 return if f > 0.0 { "Infinity".into() } else { "-Infinity".into() };
291 }
292 let a = f.abs();
293 if a != 0.0 && !(1e-4..1e16).contains(&a) {
294 let s = format!("{:e}", f);
296 let (mant, exp) = s.split_once('e').unwrap();
297 let mant = if mant.contains('.') { mant.to_string() } else { format!("{mant}.0") };
298 let e: i32 = exp.parse().unwrap();
299 return format!("{mant}e{}{:02}", if e < 0 { '-' } else { '+' }, e.abs());
300 }
301 let s = format!("{}", f);
302 if s.contains('.') {
303 s
304 } else {
305 format!("{s}.0")
306 }
307}
308
309pub fn format_int(v: Value) -> String {
310 if v.is_small_int() {
311 v.as_small_int().to_string()
312 } else {
313 to_bigint(v).to_string()
314 }
315}
316
317pub fn quote_str(out: &mut String, s: &str) {
319 out.push('"');
320 for c in s.chars() {
321 match c {
322 '"' => out.push_str("\\\""),
323 '\\' => out.push_str("\\\\"),
324 '\n' => out.push_str("\\n"),
325 '\t' => out.push_str("\\t"),
326 '\r' => out.push_str("\\r"),
327 '\0' => out.push_str("\\0"),
328 '#' => out.push('#'),
329 c if (c as u32) < 0x20 => {
330 let _ = write!(out, "\\u{{{:x}}}", c as u32);
331 }
332 c => out.push(c),
333 }
334 }
335 out.push('"');
336}
337
338pub fn to_s(ix: &mut dyn Interop, v: Value) -> RtResult<String> {
340 if let Some(s) = str_of(v) {
341 return Ok(s.to_string());
342 }
343 let mut out = String::new();
344 write_value(ix, v, &mut out, false, 0)?;
345 Ok(out)
346}
347
348pub fn inspect(ix: &mut dyn Interop, v: Value) -> RtResult<String> {
350 let mut out = String::new();
351 write_value(ix, v, &mut out, true, 0)?;
352 Ok(out)
353}
354
355fn write_value(ix: &mut dyn Interop, v: Value, out: &mut String, quoted: bool, depth: usize) -> RtResult<()> {
356 if depth > 64 {
357 out.push_str("...");
358 return Ok(());
359 }
360 if v.is_float() {
361 out.push_str(&format_float(v.as_f64()));
362 return Ok(());
363 }
364 if v.is_small_int() {
365 let _ = write!(out, "{}", v.as_small_int());
366 return Ok(());
367 }
368 if v.is_nil() {
369 out.push_str("nil");
370 return Ok(());
371 }
372 if v.is_bool() {
373 out.push_str(if v.is_true() { "true" } else { "false" });
374 return Ok(());
375 }
376 if !v.is_ptr() {
377 out.push_str("<?>");
378 return Ok(());
379 }
380 let h = unsafe { &*v.as_ptr() };
381 let cls = user_class(h);
382 if cls.id >= ids::FIRST_USER || cls.kind == ObjKind::Instance && cls.id != ids::RANGE {
384 let name = if quoted && cls.lookup(SYMS.inspect).is_some() { SYMS.inspect } else { SYMS.to_s };
385 if cls.lookup(name).is_some() {
386 if let Some(r) = ix.call_method(v, name, &[])? {
387 match str_of(r) {
388 Some(s) => out.push_str(s),
389 None => return panic(format!("{}#{} must return a String", cls.name, name)),
390 }
391 return Ok(());
392 }
393 }
394 }
395 let m = ix.mutator() as *mut Mutator;
396 let m = unsafe { &*m };
397 match cls.kind {
398 ObjKind::Str => {
399 let s = unsafe { as_str_obj(v) }.as_str();
400 if quoted {
401 quote_str(out, s)
402 } else {
403 out.push_str(s)
404 }
405 }
406 ObjKind::BigInt => out.push_str(&format_int(v)),
407 ObjKind::List => {
408 out.push('[');
409 let mut i = 0;
410 loop {
411 let item = read_list(m, v)?.get(i);
412 let Some(item) = item else { break };
413 if i > 0 {
414 out.push_str(", ");
415 }
416 write_value(ix, item, out, true, depth + 1)?;
417 i += 1;
418 }
419 out.push(']');
420 }
421 ObjKind::Tuple => {
422 out.push('(');
423 let elems = unsafe { as_tuple(v) }.elems();
424 for (i, &e) in elems.iter().enumerate() {
425 if i > 0 {
426 out.push_str(", ");
427 }
428 write_value(ix, e, out, true, depth + 1)?;
429 }
430 if elems.len() == 1 {
431 out.push(',');
432 }
433 out.push(')');
434 }
435 ObjKind::Dict | ObjKind::Set => {
436 let is_set = cls.kind == ObjKind::Set;
437 out.push_str(if is_set { "Set[" } else { "[" });
438 let n = with_read(m, v, || unsafe { as_dict(v).table().raw_len() })?;
439 let mut first = true;
440 for i in 0..n {
441 let e = with_read(m, v, || unsafe { as_dict(v).table().raw_entry(i) })?;
442 let Some(e) = e else { continue };
443 if !first {
444 out.push_str(", ");
445 }
446 first = false;
447 write_value(ix, e.key, out, true, depth + 1)?;
448 if !is_set {
449 out.push_str(": ");
450 write_value(ix, e.value, out, true, depth + 1)?;
451 }
452 }
453 if first && !is_set {
454 out.push(':');
455 }
456 out.push(']');
457 }
458 ObjKind::Instance if cls.id == ids::RANGE => {
459 let r = unsafe { as_instance(v) };
460 write_value(ix, r.get(0), out, true, depth + 1)?;
461 out.push_str(if r.get(2).is_true() { "..." } else { ".." });
462 write_value(ix, r.get(1), out, true, depth + 1)?;
463 }
464 ObjKind::Instance => {
465 let o = unsafe { as_instance(v) };
466 out.push_str(&cls.name);
467 let n = cls.nfields();
468 if cls.variant_tag.is_some() {
469 if n > 0 {
470 out.push('(');
471 for i in 0..n {
472 if i > 0 {
473 out.push_str(", ");
474 }
475 write_value(ix, o.get(i), out, true, depth + 1)?;
476 }
477 out.push(')');
478 }
479 } else {
480 out.push('(');
481 for i in 0..n {
482 if i > 0 {
483 out.push_str(", ");
484 }
485 let _ = write!(out, "{}: ", cls.field_names[i]);
486 let fv = if h.state() == catallaxy_gc::ObjState::Shared { o.get_acquire(i) } else { o.get(i) };
487 write_value(ix, fv, out, true, depth + 1)?;
488 }
489 out.push(')');
490 }
491 }
492 ObjKind::StringBuilder => {
493 let s = with_read(m, v, || unsafe { (*as_sb(v).buf.get()).clone() })?;
494 if quoted {
495 quote_str(out, &s)
496 } else {
497 out.push_str(&s)
498 }
499 }
500 ObjKind::Closure => out.push_str("#<fn>"),
501 _ => {
502 let _ = write!(out, "#<{}>", cls.name);
503 }
504 }
505 Ok(())
506}
507
508pub fn values_eq(ix: &mut dyn Interop, a: Value, b: Value) -> RtResult<bool> {
514 if a.0 == b.0 {
515 return Ok(!(a.is_float() && a.as_f64().is_nan()));
516 }
517 if a.is_float() || b.is_float() || a.is_small_int() || b.is_small_int() {
518 return Ok(match (num_f64(a), num_f64(b)) {
519 (Some(x), Some(y)) if a.is_float() || b.is_float() => x == y,
520 _ => is_int(a) && is_int(b) && int_cmp(a, b) == CmpOrdering::Equal,
521 });
522 }
523 if !a.is_ptr() || !b.is_ptr() {
524 return Ok(false);
525 }
526 let (ha, hb) = unsafe { (&*a.as_ptr(), &*b.as_ptr()) };
527 let (ca, cb) = (user_class(ha), user_class(hb));
528 if is_user_instance(ca) && ca.lookup(SYMS.eq).is_some() {
529 if let Some(r) = ix.call_method(a, SYMS.eq, &[b])? {
530 return Ok(r.is_true());
531 }
532 }
533 if ca.kind != cb.kind {
534 return Ok(false);
535 }
536 let m = unsafe { &*(ix.mutator() as *mut Mutator) };
537 match ca.kind {
538 ObjKind::Str => Ok(unsafe { as_str_obj(a).as_str() == as_str_obj(b).as_str() }),
539 ObjKind::BigInt => Ok(unsafe { as_bigint(a).value == as_bigint(b).value }),
540 ObjKind::Tuple => {
541 let (ta, tb) = unsafe { (as_tuple(a), as_tuple(b)) };
542 if ta.len != tb.len {
543 return Ok(false);
544 }
545 for i in 0..ta.len {
546 if !values_eq(ix, ta.elems()[i], tb.elems()[i])? {
547 return Ok(false);
548 }
549 }
550 Ok(true)
551 }
552 ObjKind::List => {
553 let len = |v: Value| read_list(m, v).map(|l| l.len());
554 if len(a)? != len(b)? {
555 return Ok(false);
556 }
557 let mut i = 0;
558 loop {
559 let x = read_list(m, a)?.get(i);
560 let y = read_list(m, b)?.get(i);
561 match (x, y) {
562 (Some(x), Some(y)) => {
563 if !values_eq(ix, x, y)? {
564 return Ok(false);
565 }
566 }
567 (None, None) => return Ok(true),
568 _ => return Ok(false),
569 }
570 i += 1;
571 }
572 }
573 ObjKind::Dict | ObjKind::Set => {
574 let len = |v: Value| with_read(m, v, || unsafe { as_dict(v).table().len() });
575 if len(a)? != len(b)? {
576 return Ok(false);
577 }
578 let n = with_read(m, a, || unsafe { as_dict(a).table().raw_len() })?;
579 for i in 0..n {
580 let e = with_read(m, a, || unsafe { as_dict(a).table().raw_entry(i) })?;
581 let Some(e) = e else { continue };
582 match dict_lookup(ix, b, e.key)? {
583 None => return Ok(false),
584 Some(idx) => {
585 if ca.kind == ObjKind::Dict {
586 let bv = with_read(m, b, || unsafe { as_dict(b).table().entry(idx).value })?;
587 if !values_eq(ix, e.value, bv)? {
588 return Ok(false);
589 }
590 }
591 }
592 }
593 }
594 Ok(true)
595 }
596 ObjKind::Instance => {
597 if ca.id != cb.id || !(ca.variant_tag.is_some() || ca.id == ids::RANGE) {
599 return Ok(false);
600 }
601 let (oa, ob) = unsafe { (as_instance(a), as_instance(b)) };
602 for i in 0..ca.nfields() {
603 if !values_eq(ix, oa.get(i), ob.get(i))? {
604 return Ok(false);
605 }
606 }
607 Ok(true)
608 }
609 _ => Ok(false),
610 }
611}
612
613pub fn hash_value(ix: &mut dyn Interop, v: Value) -> RtResult<u64> {
615 if v.is_small_int() {
616 return Ok(hash_int(v.as_small_int()));
617 }
618 if v.is_float() {
619 let f = v.as_f64();
620 if f.fract() == 0.0 && f.abs() < 1e15 {
621 return Ok(hash_int(f as i64));
622 }
623 return Ok(mix(f.to_bits()));
624 }
625 if !v.is_ptr() {
626 return Ok(mix(v.0));
627 }
628 let h = unsafe { &*v.as_ptr() };
629 let cls = user_class(h);
630 let m = unsafe { &*(ix.mutator() as *mut Mutator) };
631 match cls.kind {
632 ObjKind::Str => Ok(unsafe { as_str_obj(v) }.hash_code()),
633 ObjKind::BigInt => {
634 let b = &unsafe { as_bigint(v) }.value;
635 Ok(hash_bytes(&b.to_signed_bytes_le()))
636 }
637 ObjKind::Tuple => {
638 let mut acc = 0x7475_706c_u64;
639 for &e in unsafe { as_tuple(v) }.elems() {
640 acc = mix(acc ^ hash_value(ix, e)?);
641 }
642 Ok(acc)
643 }
644 ObjKind::List => {
645 let mut acc = 0x6c69_7374_u64;
646 let mut i = 0;
647 loop {
648 let x = read_list(m, v)?.get(i);
649 let Some(x) = x else { break };
650 acc = mix(acc ^ hash_value(ix, x)?);
651 i += 1;
652 }
653 Ok(acc)
654 }
655 ObjKind::Dict | ObjKind::Set => {
656 let mut acc = 0x6469_6374_u64;
657 let n = with_read(m, v, || unsafe { as_dict(v).table().raw_len() })?;
658 for i in 0..n {
659 let e = with_read(m, v, || unsafe { as_dict(v).table().raw_entry(i) })?;
660 if let Some(e) = e {
661 let hv = if cls.kind == ObjKind::Dict { hash_value(ix, e.value)? } else { 0 };
662 acc ^= mix(e.hash ^ hv.rotate_left(17));
663 }
664 }
665 Ok(acc)
666 }
667 ObjKind::Instance => {
668 if is_user_instance(cls) && cls.lookup(SYMS.hash).is_some() {
669 if let Some(r) = ix.call_method(v, SYMS.hash, &[])? {
670 return Ok(if r.is_small_int() { hash_int(r.as_small_int()) } else { mix(r.0) });
671 }
672 }
673 if cls.variant_tag.is_some() || cls.id == ids::RANGE {
674 let o = unsafe { as_instance(v) };
675 let mut acc = mix(cls.id as u64);
676 for i in 0..cls.nfields() {
677 acc = mix(acc ^ hash_value(ix, o.get(i))?);
678 }
679 return Ok(acc);
680 }
681 Ok(mix(h.identity_hash() as u64))
682 }
683 _ => Ok(mix(h.identity_hash() as u64)),
684 }
685}
686
687pub fn dict_lookup(ix: &mut dyn Interop, dict: Value, key: Value) -> RtResult<Option<usize>> {
689 let hash = hash_value(ix, key)?;
690 dict_lookup_hashed(ix, dict, key, hash)
691}
692
693pub fn dict_lookup_hashed(ix: &mut dyn Interop, dict: Value, key: Value, hash: u64) -> RtResult<Option<usize>> {
694 let m = unsafe { &*(ix.mutator() as *mut Mutator) };
695 loop {
696 let (probe, version) =
697 with_read(m, dict, || unsafe { let t = as_dict(dict).table(); (t.probe(hash, key), t.version()) })?;
698 match probe {
699 Probe::Found(i) => return Ok(Some(i)),
700 Probe::Absent => return Ok(None),
701 Probe::Check(cands) => {
702 let mut found = None;
703 for idx in cands {
704 let k = with_read(m, dict, || unsafe { as_dict(dict).table().entry(idx).key })?;
705 if values_eq(ix, k, key)? {
706 found = Some(idx);
707 break;
708 }
709 }
710 let still = with_read(m, dict, || unsafe { as_dict(dict).table().version() })?;
711 if still == version {
712 return Ok(found);
713 }
714 }
716 }
717 }
718}
719
720pub fn compare(ix: &mut dyn Interop, a: Value, b: Value) -> RtResult<Option<CmpOrdering>> {
722 if is_int(a) && is_int(b) {
723 return Ok(Some(int_cmp(a, b)));
724 }
725 if let (Some(x), Some(y)) = (num_f64(a), num_f64(b)) {
726 return Ok(x.partial_cmp(&y));
727 }
728 if !a.is_ptr() || !b.is_ptr() {
729 if a.is_bool() && b.is_bool() {
730 return Ok(Some(a.is_true().cmp(&b.is_true())));
731 }
732 return Ok(None);
733 }
734 let (ca, cb) = unsafe { (heap_class(a.as_ptr()), heap_class(b.as_ptr())) };
735 if is_user_instance(ca) && ca.lookup(SYMS.cmp).is_some() {
736 if let Some(r) = ix.call_method(a, SYMS.cmp, &[b])? {
737 return Ok(int_as_i64(r).map(|i| i.cmp(&0)));
738 }
739 }
740 if ca.kind != cb.kind {
741 return Ok(None);
742 }
743 let m = unsafe { &*(ix.mutator() as *mut Mutator) };
744 match ca.kind {
745 ObjKind::Str => Ok(Some(unsafe { as_str_obj(a).as_str().cmp(as_str_obj(b).as_str()) })),
746 ObjKind::Tuple | ObjKind::List => {
747 let get = |v: Value, i: usize| -> RtResult<Option<Value>> {
748 if ca.kind == ObjKind::Tuple {
749 Ok(unsafe { as_tuple(v) }.elems().get(i).copied())
750 } else {
751 read_list(m, v).map(|l| l.get(i))
752 }
753 };
754 let mut i = 0;
755 loop {
756 match (get(a, i)?, get(b, i)?) {
757 (None, None) => return Ok(Some(CmpOrdering::Equal)),
758 (None, Some(_)) => return Ok(Some(CmpOrdering::Less)),
759 (Some(_), None) => return Ok(Some(CmpOrdering::Greater)),
760 (Some(x), Some(y)) => match compare(ix, x, y)? {
761 Some(CmpOrdering::Equal) => {}
762 other => return Ok(other),
763 },
764 }
765 i += 1;
766 }
767 }
768 ObjKind::Instance if ca.variant_tag.is_some() && ca.enum_id == cb.enum_id => {
769 let (ta, tb) = (ca.variant_tag.unwrap(), cb.variant_tag.unwrap());
770 if ta != tb {
771 return Ok(Some(ta.cmp(&tb)));
772 }
773 let (oa, ob) = unsafe { (as_instance(a), as_instance(b)) };
774 for i in 0..ca.nfields() {
775 match compare(ix, oa.get(i), ob.get(i))? {
776 Some(CmpOrdering::Equal) => {}
777 other => return Ok(other),
778 }
779 }
780 Ok(Some(CmpOrdering::Equal))
781 }
782 _ => Ok(None),
783 }
784}
785
786pub fn compare_or_panic(ix: &mut dyn Interop, a: Value, b: Value) -> RtResult<CmpOrdering> {
788 match compare(ix, a, b)? {
789 Some(o) => Ok(o),
790 None => Err(Trap::Panic(format!(
791 "ArgumentError: comparison of {} with {} failed",
792 crate::class::class_of(a).name,
793 crate::class::class_of(b).name
794 ))),
795 }
796}
797
798pub fn sign_of(v: Value) -> i64 {
799 if v.is_small_int() {
800 v.as_small_int().signum()
801 } else {
802 let b = to_bigint(v);
803 if b.is_negative() {
804 -1
805 } else if b.is_zero() {
806 0
807 } else {
808 1
809 }
810 }
811}