1use core::fmt;
61use smartstring::alias::String;
62use std::borrow::Cow;
63use std::cmp::Ordering;
64
65#[derive(Debug, Copy, Clone, PartialEq)]
74struct TinyArray {
75 bytes: [u8; 14],
76 len: u8,
77}
78
79#[derive(Debug, Copy, Clone, PartialEq)]
80struct Slice {
81 epoch_id: EpochID,
82 index: u32,
83 len: u32,
84}
85
86#[repr(u8)]
93#[derive(Copy, Clone, PartialEq)]
94enum Handle {
95 Int(i64),
96 Real(f64),
97 Date(i64),
98 Var(TinyArray),
99 VarRef(Slice),
100 Atom(TinyArray),
101 AtomRef(Slice),
102 Str(TinyArray),
103 StrRef(Slice),
104 Bin(TinyArray),
105 BinRef(Slice),
106 FuncRef(Slice),
107 ListRef(Slice),
108 ListCRef(Slice),
109 TupleRef(Slice),
110}
111
112#[derive(Copy, Clone, PartialEq)]
127pub struct Term(Handle);
128
129impl AsRef<Term> for Term {
130 fn as_ref(&self) -> &Self {
131 self
132 }
133}
134
135macro_rules! impl_from_integers_for_term {
136 ($($t:ty),* $(,)?) => {$(
137 impl From<$t> for Term {
138 #[inline]
139 fn from(v: $t) -> Self { Term::int(v as i64) }
140 }
141 )*};
142}
143impl_from_integers_for_term!(i8, i16, i32, i64, u8, u16, u32);
144
145macro_rules! impl_from_floats_for_term {
146 ($($t:ty),* $(,)?) => {$(
147 impl From<$t> for Term {
148 #[inline]
149 fn from(v: $t) -> Self { Term::real(v as f64) }
150 }
151 )*};
152}
153impl_from_floats_for_term!(f32, f64);
154
155pub trait IntoTerm {
156 fn into_term(self, arena: &mut Arena) -> Term;
157}
158
159macro_rules! impl_intoterm_for_integers {
160 ($($t:ty),* $(,)?) => {$(
161 impl IntoTerm for $t {
162 #[inline]
163 fn into_term(self, _arena: &mut Arena) -> Term { Term::int(self as i64) }
164 }
165 )*};
166}
167impl_intoterm_for_integers!(i8, i16, i32, i64, u8, u16, u32);
168
169macro_rules! impl_intoterm_for_floats {
170 ($($t:ty),* $(,)?) => {$(
171 impl IntoTerm for $t {
172 #[inline]
173 fn into_term(self, _arena: &mut Arena) -> Term { Term::real(self as f64) }
174 }
175 )*};
176}
177impl_intoterm_for_floats!(f32, f64);
178
179impl<'a> IntoTerm for &'a str {
180 #[inline]
181 fn into_term(self, arena: &mut Arena) -> Term {
182 Term::str(arena, self)
183 }
184}
185
186impl<'a> IntoTerm for &'a [u8] {
187 #[inline]
188 fn into_term(self, arena: &mut Arena) -> Term {
189 Term::bin(arena, self)
190 }
191}
192
193impl<'a> IntoTerm for Cow<'a, str> {
194 #[inline]
195 fn into_term(self, arena: &mut Arena) -> Term {
196 match self {
197 Cow::Borrowed(s) => Term::str(arena, s),
198 Cow::Owned(s) => Term::str(arena, s),
199 }
200 }
201}
202
203impl<'a> IntoTerm for Cow<'a, [u8]> {
204 #[inline]
205 fn into_term(self, arena: &mut Arena) -> Term {
206 match self {
207 Cow::Borrowed(s) => Term::bin(arena, s),
208 Cow::Owned(s) => Term::bin(arena, s),
209 }
210 }
211}
212
213impl IntoTerm for String {
214 #[inline]
215 fn into_term(self, arena: &mut Arena) -> Term {
216 Term::str(arena, &self)
217 }
218}
219
220impl IntoTerm for std::string::String {
221 #[inline]
222 fn into_term(self, arena: &mut Arena) -> Term {
223 Term::str(arena, &self)
224 }
225}
226
227impl IntoTerm for Vec<u8> {
228 #[inline]
229 fn into_term(self, arena: &mut Arena) -> Term {
230 Term::bin(arena, &self)
231 }
232}
233
234impl IntoTerm for Term {
235 #[inline]
236 fn into_term(self, _arena: &mut Arena) -> Term {
237 self
238 }
239}
240
241impl IntoTerm for &Term {
242 #[inline]
243 fn into_term(self, _arena: &mut Arena) -> Term {
244 *self
245 }
246}
247
248impl<F> IntoTerm for F
249where
250 F: FnOnce(&mut Arena) -> Term,
251{
252 #[inline]
253 fn into_term(self, arena: &mut Arena) -> Term {
254 self(arena)
255 }
256}
257
258impl Term {
259 #[inline]
263 pub fn int(i: impl Into<i64>) -> Self {
264 Self(Handle::Int(i.into()))
265 }
266
267 #[inline]
270 pub fn real(f: impl Into<f64>) -> Self {
271 Self(Handle::Real(f.into()))
272 }
273
274 #[inline]
279 pub fn date(ms: impl Into<i64>) -> Self {
280 Self(Handle::Date(ms.into()))
281 }
282
283 #[inline]
288 pub fn atom(arena: &mut Arena, name: impl AsRef<str>) -> Self {
289 let name = name.as_ref();
290 let bytes = name.as_bytes();
291 if bytes.len() <= 14 {
292 let mut buf = [0u8; 14];
293 buf[..bytes.len()].copy_from_slice(bytes);
294 Self(Handle::Atom(TinyArray {
295 bytes: buf,
296 len: bytes.len() as u8,
297 }))
298 } else {
299 Self(Handle::AtomRef(arena.intern_str(name)))
300 }
301 }
302
303 #[inline]
308 pub fn var(arena: &mut Arena, name: impl AsRef<str>) -> Self {
309 let name = name.as_ref();
310 let bytes = name.as_bytes();
311 if bytes.len() <= 14 {
312 let mut buf = [0u8; 14];
313 buf[..bytes.len()].copy_from_slice(bytes);
314 Self(Handle::Var(TinyArray {
315 bytes: buf,
316 len: bytes.len() as u8,
317 }))
318 } else {
319 Self(Handle::VarRef(arena.intern_str(name)))
320 }
321 }
322
323 #[inline]
328 pub fn str(arena: &mut Arena, s: impl AsRef<str>) -> Self {
329 let s = s.as_ref();
330 let bytes = s.as_bytes();
331 if bytes.len() <= 14 {
332 let mut buf = [0u8; 14];
333 buf[..bytes.len()].copy_from_slice(bytes);
334 Self(Handle::Str(TinyArray {
335 bytes: buf,
336 len: bytes.len() as u8,
337 }))
338 } else {
339 Self(Handle::StrRef(arena.intern_str(s)))
340 }
341 }
342
343 #[inline]
347 pub fn bin(arena: &mut Arena, bytes: impl AsRef<[u8]>) -> Self {
348 let bytes = bytes.as_ref();
349 if bytes.len() <= 14 {
350 let mut buf = [0u8; 14];
351 buf[..bytes.len()].copy_from_slice(bytes);
352 Self(Handle::Bin(TinyArray {
353 bytes: buf,
354 len: bytes.len() as u8,
355 }))
356 } else {
357 Self(Handle::BinRef(arena.intern_bytes(bytes)))
358 }
359 }
360
361 #[inline]
367 pub fn func(
368 arena: &mut Arena,
369 functor: impl AsRef<str>,
370 args: impl IntoIterator<Item = impl IntoTerm>,
371 ) -> Self {
372 let functor_atom = Self::atom(arena, functor);
373 let mut args = args.into_iter();
374 let Some(first) = args.next() else {
375 return functor_atom;
376 };
377 Self(Handle::FuncRef(arena.intern_func(
378 functor_atom,
379 std::iter::once(first).chain(args),
380 )))
381 }
382
383 #[inline]
388 pub fn funcv(
389 arena: &mut Arena,
390 terms: impl IntoIterator<Item = impl IntoTerm>,
391 ) -> Result<Self, TermError> {
392 let mut terms = terms.into_iter();
393 let Some(functor_atom) = terms.next() else {
394 return Err(TermError::MissingFunctor);
395 };
396 let functor_atom = functor_atom.into_term(arena);
397 if !functor_atom.is_atom() {
398 return Err(TermError::InvalidFunctor(functor_atom));
399 }
400 let Some(first) = terms.next() else {
401 return Ok(functor_atom);
402 };
403 Ok(Self(Handle::FuncRef(arena.intern_func(
404 functor_atom,
405 std::iter::once(first).chain(terms),
406 ))))
407 }
408
409 #[inline]
412 pub fn list(arena: &mut Arena, terms: impl IntoIterator<Item = impl IntoTerm>) -> Self {
413 let mut terms = terms.into_iter();
414 let Some(first) = terms.next() else {
415 return Self::NIL;
416 };
417 Self(Handle::ListRef(
418 arena.intern_seq(std::iter::once(first).chain(terms)),
419 ))
420 }
421
422 #[inline]
425 pub fn listc(
426 arena: &mut Arena,
427 terms: impl IntoIterator<Item = impl IntoTerm>,
428 tail: impl IntoTerm,
429 ) -> Self {
430 let mut terms = terms.into_iter();
431 let Some(first) = terms.next() else {
432 return Self::NIL;
433 };
434 let tail = tail.into_term(arena);
435 if tail != Term::NIL {
436 Self(Handle::ListCRef(arena.intern_seq_plus_one(
437 std::iter::once(first).chain(terms),
438 tail,
439 )))
440 } else {
441 Self(Handle::ListRef(
442 arena.intern_seq(std::iter::once(first).chain(terms)),
443 ))
444 }
445 }
446
447 #[inline]
450 pub fn tuple(arena: &mut Arena, terms: impl IntoIterator<Item = impl IntoTerm>) -> Self {
451 let mut terms = terms.into_iter();
452 let Some(first) = terms.next() else {
453 return Self::UNIT;
454 };
455 Self(Handle::TupleRef(
456 arena.intern_seq(std::iter::once(first).chain(terms)),
457 ))
458 }
459
460 pub const UNIT: Self = {
464 let buf: [u8; 14] = [b'u', b'n', b'i', b't', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
465 Self(Handle::Atom(TinyArray { bytes: buf, len: 4 }))
466 };
467
468 pub const NIL: Self = {
472 let buf: [u8; 14] = [b'n', b'i', b'l', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
473 Self(Handle::Atom(TinyArray { bytes: buf, len: 3 }))
474 };
475
476 #[inline]
478 pub fn unpack_int(&self, arena: &Arena) -> Result<i64, TermError> {
479 arena.unpack_int(self)
480 }
481
482 #[inline]
484 pub fn unpack_real(&self, arena: &Arena) -> Result<f64, TermError> {
485 arena.unpack_real(self)
486 }
487
488 #[inline]
490 pub fn unpack_date(&self, arena: &Arena) -> Result<i64, TermError> {
491 arena.unpack_date(self)
492 }
493
494 #[inline]
496 pub fn unpack_str<'a>(&'a self, arena: &'a Arena) -> Result<&'a str, TermError> {
497 arena.unpack_str(self)
498 }
499
500 #[inline]
502 pub fn unpack_bin<'a>(&'a self, arena: &'a Arena) -> Result<&'a [u8], TermError> {
503 arena.unpack_bin(self)
504 }
505
506 pub fn unpack_atom<'a>(
508 &'a self,
509 arena: &'a Arena,
510 allowed_names: &[&str],
511 ) -> Result<&'a str, TermError> {
512 arena.unpack_atom(self, allowed_names)
513 }
514
515 pub fn unpack_var<'a>(
517 &'a self,
518 arena: &'a Arena,
519 allowed_names: &[&str],
520 ) -> Result<&'a str, TermError> {
521 arena.unpack_var(self, allowed_names)
522 }
523
524 pub fn unpack_func_any<'a>(
528 &'a self,
529 arena: &'a Arena,
530 allowed_names: &[&str],
531 ) -> Result<(&'a str, &'a [Term]), TermError> {
532 arena.unpack_func_any(self, allowed_names)
533 }
534
535 pub fn unpack_func<'a, const ARITY: usize>(
539 &'a self,
540 arena: &'a Arena,
541 allowed_names: &[&str],
542 ) -> Result<(&'a str, [Term; ARITY]), TermError> {
543 arena.unpack_func(self, allowed_names)
544 }
545
546 pub fn unpack_list<'a>(&'a self, arena: &'a Arena) -> Result<(&'a [Term], Term), TermError> {
549 arena.unpack_list(self)
550 }
551
552 pub fn unpack_tuple_any<'a>(&'a self, arena: &'a Arena) -> Result<&'a [Term], TermError> {
555 arena.unpack_tuple_any(self)
556 }
557
558 pub fn unpack_tuple<const ARITY: usize>(
561 &self,
562 arena: &Arena,
563 ) -> Result<[Term; ARITY], TermError> {
564 arena.unpack_tuple(self)
565 }
566
567 #[inline]
572 pub fn view<'a>(&'a self, arena: &'a Arena) -> Result<View<'a>, TermError> {
573 match &self.0 {
574 Handle::Int(i) => Ok(View::Int(*i)),
575 Handle::Real(f) => Ok(View::Real(*f)),
576 Handle::Date(d) => Ok(View::Date(*d)),
577 Handle::Var(vs) => {
578 let s_bytes = &vs.bytes[..vs.len as usize];
579 let s = unsafe { core::str::from_utf8_unchecked(s_bytes) };
580 Ok(View::Var(s))
581 }
582 Handle::VarRef(vr) => Ok(View::Var(unsafe {
583 core::str::from_utf8_unchecked(
584 arena
585 .byte_slice(vr)
586 .map_err(|_| TermError::InvalidTerm(*self))?,
587 )
588 })),
589 Handle::Atom(a) => {
590 let s_bytes = &a.bytes[..a.len as usize];
591 let s = unsafe { core::str::from_utf8_unchecked(s_bytes) };
592 Ok(View::Atom(s))
593 }
594 Handle::AtomRef(ar) => Ok(View::Atom(unsafe {
595 core::str::from_utf8_unchecked(
596 arena
597 .byte_slice(ar)
598 .map_err(|_| TermError::InvalidTerm(*self))?,
599 )
600 })),
601 Handle::Str(ss) => {
602 let s_bytes = &ss.bytes[..ss.len as usize];
603 let s = unsafe { core::str::from_utf8_unchecked(s_bytes) };
604 Ok(View::Str(s))
605 }
606 Handle::StrRef(sr) => Ok(View::Str(unsafe {
607 core::str::from_utf8_unchecked(
608 arena
609 .byte_slice(sr)
610 .map_err(|_| TermError::InvalidTerm(*self))?,
611 )
612 })),
613 Handle::Bin(bs) => {
614 let b = &bs.bytes[..bs.len as usize];
615 Ok(View::Bin(b))
616 }
617 Handle::BinRef(br) => Ok(View::Bin(
618 arena
619 .byte_slice(br)
620 .map_err(|_| TermError::InvalidTerm(*self))?,
621 )),
622 Handle::FuncRef(fr) => {
623 let slice = arena
624 .term_slice(fr)
625 .map_err(|_| TermError::InvalidTerm(*self))?;
626 let functor = match &slice[0].0 {
628 Handle::Atom(a) => {
629 let s_bytes = &a.bytes[..a.len as usize];
630 unsafe { core::str::from_utf8_unchecked(s_bytes) }
631 }
632 Handle::AtomRef(ar2) => unsafe {
633 core::str::from_utf8_unchecked(
634 arena
635 .byte_slice(ar2)
636 .map_err(|_| TermError::InvalidTerm(*self))?,
637 )
638 },
639 _ => panic!("invalid functor"),
640 };
641 let args = &slice[1..];
642 Ok(View::Func(arena, functor, args))
643 }
644 Handle::ListRef(lr) => {
645 let slice = arena
646 .term_slice(lr)
647 .map_err(|_| TermError::InvalidTerm(*self))?;
648 Ok(View::List(arena, slice, &Term::NIL))
649 }
650 Handle::ListCRef(lr) => {
651 let slice = arena
652 .term_slice(lr)
653 .map_err(|_| TermError::InvalidTerm(*self))?;
654 let last = slice.len() - 1;
655 Ok(View::List(arena, &slice[..last], &slice[last]))
656 }
657 Handle::TupleRef(tr) => {
658 let slice = arena
659 .term_slice(tr)
660 .map_err(|_| TermError::InvalidTerm(*self))?;
661 Ok(View::Tuple(arena, slice))
662 }
663 }
664 }
665
666 #[inline]
669 pub fn is_inline(&self) -> bool {
670 match &self.0 {
671 Handle::Int(_)
672 | Handle::Real(_)
673 | Handle::Date(_)
674 | Handle::Atom(_)
675 | Handle::Var(_)
676 | Handle::Str(_)
677 | Handle::Bin(_) => true,
678 Handle::AtomRef(_)
679 | Handle::VarRef(_)
680 | Handle::StrRef(_)
681 | Handle::BinRef(_)
682 | Handle::FuncRef(_)
683 | Handle::ListRef(_)
684 | Handle::ListCRef(_)
685 | Handle::TupleRef(_) => false,
686 }
687 }
688
689 #[inline]
691 pub fn is_func(&self) -> bool {
692 matches!(self.0, Handle::FuncRef(_))
693 }
694
695 #[inline]
697 pub fn is_list(&self) -> bool {
698 matches!(self.0, Handle::ListRef(_) | Handle::ListCRef(_)) || *self == Self::NIL
699 }
700
701 #[inline]
703 pub fn is_tuple(&self) -> bool {
704 matches!(self.0, Handle::TupleRef(_)) || *self == Self::UNIT
705 }
706
707 #[inline]
709 pub fn is_int(&self) -> bool {
710 matches!(self.0, Handle::Int(_))
711 }
712
713 #[inline]
715 pub fn is_real(&self) -> bool {
716 matches!(self.0, Handle::Real(_))
717 }
718
719 #[inline]
721 pub fn is_date(&self) -> bool {
722 matches!(self.0, Handle::Date(_))
723 }
724
725 #[inline]
727 pub fn is_atom(&self) -> bool {
728 matches!(self.0, Handle::Atom(_) | Handle::AtomRef(_))
729 }
730
731 #[inline]
733 pub fn is_var(&self) -> bool {
734 matches!(self.0, Handle::Var(_) | Handle::VarRef(_))
735 }
736
737 #[inline]
739 pub fn is_number(&self) -> bool {
740 matches!(self.0, Handle::Int(_) | Handle::Real(_) | Handle::Date(_))
741 }
742
743 #[inline]
745 pub fn is_str(&self) -> bool {
746 matches!(self.0, Handle::Str(_) | Handle::StrRef(_))
747 }
748
749 #[inline]
751 pub fn is_bin(&self) -> bool {
752 matches!(self.0, Handle::Bin(_) | Handle::BinRef(_))
753 }
754
755 #[inline]
757 pub fn arity(&self) -> usize {
758 match &self.0 {
759 Handle::Atom(_)
760 | Handle::AtomRef(_)
761 | Handle::Int(_)
762 | Handle::Real(_)
763 | Handle::Date(_)
764 | Handle::Str(_)
765 | Handle::StrRef(_)
766 | Handle::Bin(_)
767 | Handle::BinRef(_) => 0,
768 Handle::FuncRef(Slice { len: n, .. }) => (n - 1) as usize,
769 Handle::TupleRef(Slice { len: n, .. }) => *n as usize,
770 Handle::ListRef(_) | Handle::ListCRef(_) | Handle::Var(_) | Handle::VarRef(_) => 0,
771 }
772 }
773
774 #[inline]
776 pub fn name<'a>(&'a self, arena: &'a Arena) -> Result<&'a str, TermError> {
777 arena.name(self)
778 }
779
780 #[inline]
782 pub fn kind_name(&self) -> &'static str {
783 match &self.0 {
784 Handle::Int(_) => "int",
785 Handle::Real(_) => "real",
786 Handle::Date(_) => "date",
787 Handle::Var(_) | Handle::VarRef(_) => "var",
788 Handle::Atom(_) | Handle::AtomRef(_) => "atom",
789 Handle::Str(_) | Handle::StrRef(_) => "str",
790 Handle::Bin(_) | Handle::BinRef(_) => "bin",
791 Handle::FuncRef(_) => "func",
792 Handle::ListRef(_) | Handle::ListCRef(_) => "list",
793 Handle::TupleRef(_) => "tuple",
794 }
795 }
796}
797
798impl fmt::Debug for Term {
815 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
816 match &self.0 {
817 Handle::Int(i) => f.debug_tuple("Int").field(i).finish(),
818 Handle::Real(r) => f.debug_tuple("Real").field(r).finish(),
819 Handle::Date(d) => f.debug_tuple("Date").field(d).finish(),
820 Handle::Var(v) => {
821 let name =
822 core::str::from_utf8(&v.bytes[..v.len as usize]).unwrap_or("<invalid utf8>");
823 f.debug_struct("Var").field("name", &name).finish()
824 }
825 Handle::VarRef(v) => f
826 .debug_struct("VarRef")
827 .field("epoch_id", &v.epoch_id)
828 .field("index", &v.index)
829 .field("len", &v.len)
830 .finish(),
831 Handle::Atom(a) => {
832 let name =
833 core::str::from_utf8(&a.bytes[..a.len as usize]).unwrap_or("<invalid utf8>");
834 f.debug_struct("Atom").field("name", &name).finish()
835 }
836 Handle::AtomRef(v) => f
837 .debug_struct("AtomRef")
838 .field("epoch_id", &v.epoch_id)
839 .field("index", &v.index)
840 .field("len", &v.len)
841 .finish(),
842 Handle::Str(s) => {
843 let value =
844 core::str::from_utf8(&s.bytes[..s.len as usize]).unwrap_or("<invalid utf8>");
845 f.debug_struct("Str").field("value", &value).finish()
846 }
847 Handle::StrRef(v) => f
848 .debug_struct("StrRef")
849 .field("epoch_id", &v.epoch_id)
850 .field("index", &v.index)
851 .field("len", &v.len)
852 .finish(),
853 Handle::Bin(b) => {
854 let slice = &b.bytes[..b.len as usize];
855 f.debug_struct("Bin").field("bytes", &slice).finish()
856 }
857 Handle::BinRef(v) => f
858 .debug_struct("BinRef")
859 .field("epoch_id", &v.epoch_id)
860 .field("index", &v.index)
861 .field("len", &v.len)
862 .finish(),
863 Handle::FuncRef(v) => f
864 .debug_struct("Func")
865 .field("epoch_id", &v.epoch_id)
866 .field("index", &v.index)
867 .field("len", &v.len)
868 .finish(),
869 Handle::ListRef(v) => f
870 .debug_struct("List")
871 .field("epoch_id", &v.epoch_id)
872 .field("index", &v.index)
873 .field("len", &v.len)
874 .finish(),
875 Handle::ListCRef(v) => f
876 .debug_struct("ListC")
877 .field("epoch_id", &v.epoch_id)
878 .field("index", &v.index)
879 .field("len", &v.len)
880 .finish(),
881 Handle::TupleRef(v) => f
882 .debug_struct("Tuple")
883 .field("epoch_id", &v.epoch_id)
884 .field("index", &v.index)
885 .field("len", &v.len)
886 .finish(),
887 }
888 }
889}
890
891impl fmt::Debug for View<'_> {
892 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
893 match &self {
894 View::Int(i) => f.debug_tuple("Int").field(&i).finish(),
895 View::Real(r) => f.debug_tuple("Real").field(&r).finish(),
896 View::Date(d) => f.debug_tuple("Date").field(&d).finish(),
897 View::Var(v) => f.debug_tuple("Var").field(&v).finish(),
898 View::Atom(a) => f.debug_tuple("Atom").field(&a).finish(),
899 View::Str(s) => f.debug_tuple("Str").field(&s).finish(),
900 View::Bin(b) => f.debug_tuple("Bin").field(&b).finish(),
901 View::Func(a, fr, ts) => f
902 .debug_tuple("Func")
903 .field(&a.arena_id)
904 .field(&fr)
905 .field(&ts.iter().map(|t| t.view(a)).collect::<Vec<_>>())
906 .finish(),
907 View::List(a, ts, tail) => f
908 .debug_tuple("List")
909 .field(&a.arena_id)
910 .field(&ts.iter().map(|t| t.view(a)).collect::<Vec<_>>())
911 .field(&tail.view(a))
912 .finish(),
913 View::Tuple(a, ts) => f
914 .debug_tuple("Tuple")
915 .field(&a.arena_id)
916 .field(&ts.iter().map(|t| t.view(a)).collect::<Vec<_>>())
917 .finish(),
918 }
919 }
920}
921
922#[derive(Clone, Copy)]
932pub enum View<'a> {
933 Int(i64),
935 Real(f64),
937 Date(i64),
939 Var(&'a str),
941 Atom(&'a str),
943 Str(&'a str),
945 Bin(&'a [u8]),
947 Func(&'a Arena, &'a str, &'a [Term]),
952 List(&'a Arena, &'a [Term], &'a Term),
957 Tuple(&'a Arena, &'a [Term]),
961}
962
963#[derive(Default, Clone, Debug)]
1006pub struct Arena {
1007 arena_id: ArenaID,
1009
1010 current_epoch: usize,
1013
1014 epoch_ids: [EpochID; MAX_LIVE_EPOCHS],
1020
1021 bytes: Vec<u8>,
1024
1025 byte_start_by_epoch: [usize; MAX_LIVE_EPOCHS],
1028
1029 terms: Vec<Term>,
1035
1036 term_start_by_epoch: [usize; MAX_LIVE_EPOCHS],
1039}
1040
1041pub const MAX_LIVE_EPOCHS: usize = 8;
1042
1043#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
1044pub struct EpochID(u32); #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
1047struct ArenaID(u32); #[derive(Debug, Clone, Copy)]
1050pub struct ArenaStats {
1051 pub current_epoch: EpochID,
1052 pub bytes_len: usize,
1053 pub terms_len: usize,
1054}
1055
1056impl Arena {
1057 pub fn with_capacity(bytes_capacity: usize, terms_capacity: usize) -> Self {
1059 let mut epoch_ids = [EpochID(0); MAX_LIVE_EPOCHS];
1060 epoch_ids[0] = EpochID(rand::random());
1061
1062 Self {
1063 arena_id: ArenaID(rand::random()),
1064 current_epoch: 0,
1065 epoch_ids,
1066 bytes: Vec::with_capacity(bytes_capacity),
1067 byte_start_by_epoch: [0; MAX_LIVE_EPOCHS],
1068 terms: Vec::with_capacity(terms_capacity),
1069 term_start_by_epoch: [0; MAX_LIVE_EPOCHS],
1070 }
1071 }
1072
1073 pub fn new() -> Self {
1075 Self::with_capacity(4096, 1024)
1076 }
1077
1078 pub fn stats(&self) -> ArenaStats {
1080 ArenaStats {
1081 current_epoch: self.epoch_ids[self.current_epoch],
1082 bytes_len: self.bytes.len(),
1083 terms_len: self.terms.len(),
1084 }
1085 }
1086
1087 pub fn current_epoch(&self) -> EpochID {
1089 self.epoch_ids[self.current_epoch]
1090 }
1091
1092 pub fn begin_epoch(&mut self) -> Result<EpochID, TermError> {
1094 let new_epoch = self.current_epoch + 1;
1095 if new_epoch >= MAX_LIVE_EPOCHS {
1096 return Err(TermError::LiveEpochsExceeded);
1097 }
1098 self.epoch_ids[new_epoch] = EpochID(rand::random());
1099 self.byte_start_by_epoch[new_epoch] = self.bytes.len();
1100 self.term_start_by_epoch[new_epoch] = self.terms.len();
1101 self.current_epoch = new_epoch;
1102 Ok(self.epoch_ids[new_epoch])
1103 }
1104
1105 pub fn clear(&mut self) -> Result<(), TermError> {
1108 self.truncate(self.epoch_ids[0])
1109 }
1110
1111 pub fn truncate_current(&mut self) -> Result<(), TermError> {
1114 self.truncate(self.epoch_ids[self.current_epoch])
1115 }
1116
1117 pub fn truncate(&mut self, epoch_id: EpochID) -> Result<(), TermError> {
1120 let epoch = self
1121 .epoch_index(epoch_id)
1122 .map_err(|_| TermError::InvalidEpoch(epoch_id))?;
1123 self.bytes.truncate(self.byte_start_by_epoch[epoch]);
1124 self.terms.truncate(self.term_start_by_epoch[epoch]);
1125 self.current_epoch = epoch;
1126 Ok(())
1127 }
1128
1129 #[inline]
1131 fn epoch_index(&self, epoch_id: EpochID) -> Result<usize, InternalTermError> {
1132 let Some(epoch) = self.epoch_ids[..=self.current_epoch]
1133 .iter()
1134 .position(|&id| id == epoch_id)
1135 else {
1136 return Err(InternalTermError::InvalidEpoch(epoch_id));
1137 };
1138 Ok(epoch)
1139 }
1140
1141 #[inline]
1144 fn verify_byte_slice(&self, slice: &Slice) -> Result<(), InternalTermError> {
1145 let epoch = self.epoch_index(slice.epoch_id)?;
1146 let epoch_start = self.byte_start_by_epoch[epoch];
1147 let epoch_end = if epoch == self.current_epoch {
1148 self.bytes.len()
1149 } else {
1150 self.byte_start_by_epoch[epoch + 1]
1151 };
1152 if (slice.index as usize) < epoch_start
1153 || (slice.index as usize) + (slice.len as usize) > epoch_end
1154 {
1155 return Err(InternalTermError::InvalidSlice(*slice));
1156 }
1157 Ok(())
1158 }
1159
1160 #[inline]
1163 fn verify_term_slice(&self, slice: &Slice) -> Result<(), InternalTermError> {
1164 let epoch = self.epoch_index(slice.epoch_id)?;
1165 let epoch_start = self.term_start_by_epoch[epoch];
1166 let epoch_end = if epoch == self.current_epoch {
1167 self.terms.len()
1168 } else {
1169 self.term_start_by_epoch[epoch + 1]
1170 };
1171 if (slice.index as usize) < epoch_start
1172 || (slice.index as usize) + (slice.len as usize) > epoch_end
1173 {
1174 return Err(InternalTermError::InvalidSlice(*slice));
1175 }
1176 Ok(())
1177 }
1178
1179 #[inline]
1184 pub fn view<'a>(&'a self, term: &'a Term) -> Result<View<'a>, TermError> {
1185 term.view(self)
1186 }
1187
1188 #[inline]
1190 pub fn term<'a, T: IntoTerm>(&'a mut self, value: T) -> Term {
1191 value.into_term(self)
1192 }
1193
1194 #[inline]
1198 pub fn int(&mut self, i: impl Into<i64>) -> Term {
1199 Term::int(i)
1200 }
1201
1202 #[inline]
1205 pub fn real(&mut self, r: impl Into<f64>) -> Term {
1206 Term::real(r)
1207 }
1208
1209 #[inline]
1212 pub fn date(&mut self, ms: impl Into<i64>) -> Term {
1213 Term::date(ms)
1214 }
1215
1216 #[inline]
1221 pub fn atom(&mut self, name: impl AsRef<str>) -> Term {
1222 Term::atom(self, name)
1223 }
1224
1225 #[inline]
1230 pub fn var(&mut self, name: impl AsRef<str>) -> Term {
1231 Term::var(self, name)
1232 }
1233
1234 #[inline]
1239 pub fn str(&mut self, s: impl AsRef<str>) -> Term {
1240 Term::str(self, s)
1241 }
1242
1243 #[inline]
1247 pub fn bin(&mut self, bytes: impl AsRef<[u8]>) -> Term {
1248 Term::bin(self, bytes)
1249 }
1250
1251 #[inline]
1257 pub fn func(
1258 &mut self,
1259 functor: impl AsRef<str>,
1260 args: impl IntoIterator<Item = impl IntoTerm>,
1261 ) -> Term {
1262 Term::func(self, functor, args)
1263 }
1264
1265 #[inline]
1270 pub fn funcv(
1271 &mut self,
1272 terms: impl IntoIterator<Item = impl IntoTerm>,
1273 ) -> Result<Term, TermError> {
1274 Term::funcv(self, terms)
1275 }
1276
1277 pub fn list(&mut self, terms: impl IntoIterator<Item = impl IntoTerm>) -> Term {
1280 Term::list(self, terms)
1281 }
1282
1283 pub fn listc(
1286 &mut self,
1287 terms: impl IntoIterator<Item = impl IntoTerm>,
1288 tail: impl IntoTerm,
1289 ) -> Term {
1290 Term::listc(self, terms, tail)
1291 }
1292
1293 pub fn tuple(&mut self, terms: impl IntoIterator<Item = impl IntoTerm>) -> Term {
1296 Term::tuple(self, terms)
1297 }
1298
1299 pub const UNIT: Term = Term::UNIT;
1303
1304 pub const NIL: Term = Term::NIL;
1308
1309 #[inline]
1311 pub fn name<'a>(&'a self, term: &'a Term) -> Result<&'a str, TermError> {
1312 match self.view(term)? {
1313 View::Var(name) | View::Atom(name) | View::Func(_, name, _) => Ok(name),
1314 _ => Err(TermError::UnexpectedKind {
1315 expected: "var, atom, func",
1316 found: term.kind_name(),
1317 }),
1318 }
1319 }
1320
1321 pub fn unpack_int(&self, term: &Term) -> Result<i64, TermError> {
1323 match self.view(term)? {
1324 View::Int(v) => Ok(v),
1325 _ => Err(TermError::UnexpectedKind {
1326 expected: "int",
1327 found: term.kind_name(),
1328 }),
1329 }
1330 }
1331
1332 pub fn unpack_real(&self, term: &Term) -> Result<f64, TermError> {
1334 match self.view(term)? {
1335 View::Real(v) => Ok(v),
1336 _ => Err(TermError::UnexpectedKind {
1337 expected: "real",
1338 found: term.kind_name(),
1339 }),
1340 }
1341 }
1342
1343 pub fn unpack_date(&self, term: &Term) -> Result<i64, TermError> {
1345 match self.view(term)? {
1346 View::Date(v) => Ok(v),
1347 _ => Err(TermError::UnexpectedKind {
1348 expected: "date",
1349 found: term.kind_name(),
1350 }),
1351 }
1352 }
1353
1354 pub fn unpack_str<'a>(&'a self, term: &'a Term) -> Result<&'a str, TermError> {
1356 match self.view(term)? {
1357 View::Str(v) => Ok(v),
1358 _ => Err(TermError::UnexpectedKind {
1359 expected: "str",
1360 found: term.kind_name(),
1361 }),
1362 }
1363 }
1364
1365 pub fn unpack_bin<'a>(&'a self, term: &'a Term) -> Result<&'a [u8], TermError> {
1367 match self.view(term)? {
1368 View::Bin(v) => Ok(v),
1369 _ => Err(TermError::UnexpectedKind {
1370 expected: "bin",
1371 found: term.kind_name(),
1372 }),
1373 }
1374 }
1375
1376 pub fn unpack_atom<'a>(
1378 &'a self,
1379 term: &'a Term,
1380 allowed_names: &[&str],
1381 ) -> Result<&'a str, TermError> {
1382 match self.view(term)? {
1383 View::Atom(name) => {
1384 if !allowed_names.is_empty() && !allowed_names.contains(&name) {
1385 return Err(TermError::UnexpectedName(*term));
1386 }
1387 Ok(name)
1388 }
1389 _ => Err(TermError::UnexpectedKind {
1390 expected: "atom",
1391 found: term.kind_name(),
1392 }),
1393 }
1394 }
1395
1396 pub fn unpack_var<'a>(
1398 &'a self,
1399 term: &'a Term,
1400 allowed_names: &[&str],
1401 ) -> Result<&'a str, TermError> {
1402 match self.view(term)? {
1403 View::Var(name) => {
1404 if !allowed_names.is_empty() && !allowed_names.contains(&name) {
1405 return Err(TermError::UnexpectedName(*term));
1406 }
1407 Ok(name)
1408 }
1409 _ => Err(TermError::UnexpectedKind {
1410 expected: "var",
1411 found: term.kind_name(),
1412 }),
1413 }
1414 }
1415
1416 pub fn unpack_func_any<'a>(
1420 &'a self,
1421 term: &'a Term,
1422 allowed_names: &[&str],
1423 ) -> Result<(&'a str, &'a [Term]), TermError> {
1424 match self.view(term)? {
1425 View::Atom(name) => {
1426 if !allowed_names.is_empty() && !allowed_names.contains(&name) {
1427 return Err(TermError::UnexpectedName(*term));
1428 }
1429 Ok((name, &[]))
1430 }
1431 View::Func(_, name, args) => {
1432 if args.is_empty() {
1433 return Err(TermError::InvalidTerm(*term));
1434 }
1435 if !allowed_names.is_empty() && !allowed_names.contains(&name) {
1436 return Err(TermError::UnexpectedName(*term));
1437 }
1438 Ok((name, args))
1439 }
1440 _ => Err(TermError::UnexpectedKind {
1441 expected: "func",
1442 found: term.kind_name(),
1443 }),
1444 }
1445 }
1446
1447 pub fn unpack_func<'a, const ARITY: usize>(
1451 &'a self,
1452 term: &'a Term,
1453 allowed_names: &[&str],
1454 ) -> Result<(&'a str, [Term; ARITY]), TermError> {
1455 let (name, args) = self.unpack_func_any(term, allowed_names)?;
1456 if args.len() != ARITY {
1457 return Err(TermError::UnexpectedArity {
1458 expected: ARITY,
1459 found: args.len(),
1460 });
1461 }
1462 let arr: [_; ARITY] = args.try_into().unwrap();
1463 return Ok((name, arr));
1464 }
1465
1466 pub fn unpack_list<'a>(&'a self, term: &'a Term) -> Result<(&'a [Term], Term), TermError> {
1469 match self.view(term)? {
1470 View::Atom(_) if *term == Term::NIL => Ok((&[], Term::NIL)),
1471 View::List(_, terms, tail) => Ok((terms, *tail)),
1472 _ => Err(TermError::UnexpectedKind {
1473 expected: "list",
1474 found: term.kind_name(),
1475 }),
1476 }
1477 }
1478
1479 pub fn unpack_tuple_any<'a>(&'a self, term: &'a Term) -> Result<&'a [Term], TermError> {
1482 match self.view(term)? {
1483 View::Atom(_) if *term == Term::UNIT => Ok(&[]),
1484 View::Tuple(_, terms) => Ok(terms),
1485 _ => Err(TermError::UnexpectedKind {
1486 expected: "tuple",
1487 found: term.kind_name(),
1488 }),
1489 }
1490 }
1491
1492 pub fn unpack_tuple<const ARITY: usize>(
1495 &self,
1496 term: &Term,
1497 ) -> Result<[Term; ARITY], TermError> {
1498 let terms = self.unpack_tuple_any(term)?;
1499 if terms.len() != ARITY {
1500 return Err(TermError::UnexpectedArity {
1501 expected: ARITY,
1502 found: terms.len(),
1503 });
1504 }
1505 let arr: [_; ARITY] = terms.try_into().unwrap();
1506 return Ok(arr);
1507 }
1508
1509 fn intern_str(&mut self, s: &str) -> Slice {
1512 let index = self.bytes.len();
1513 self.bytes.extend_from_slice(s.as_bytes());
1514 let len = s.len();
1515 Slice {
1516 epoch_id: self.epoch_ids[self.current_epoch],
1517 index: index as u32,
1518 len: len as u32,
1519 }
1520 }
1521
1522 fn intern_bytes(&mut self, bytes: &[u8]) -> Slice {
1524 let index = self.bytes.len();
1525 self.bytes.extend_from_slice(bytes);
1526 let len = bytes.len();
1527 Slice {
1528 epoch_id: self.epoch_ids[self.current_epoch],
1529 index: index as u32,
1530 len: len as u32,
1531 }
1532 }
1533
1534 fn intern_func(
1536 &mut self,
1537 functor: Term,
1538 args: impl IntoIterator<Item = impl IntoTerm>,
1539 ) -> Slice {
1540 let index = self.terms.len();
1541 self.terms.push(functor);
1542 for x in args {
1543 let t = x.into_term(self);
1544 self.terms.push(t);
1545 }
1546 let len = self.terms.len() - index;
1547 Slice {
1548 epoch_id: self.epoch_ids[self.current_epoch],
1549 index: index as u32,
1550 len: len as u32,
1551 }
1552 }
1553
1554 fn intern_seq(&mut self, terms: impl IntoIterator<Item = impl IntoTerm>) -> Slice {
1556 let index = self.terms.len();
1557 for x in terms {
1558 let t = x.into_term(self);
1559 self.terms.push(t);
1560 }
1561 let len = self.terms.len() - index;
1562 Slice {
1563 epoch_id: self.epoch_ids[self.current_epoch],
1564 index: index as u32,
1565 len: len as u32,
1566 }
1567 }
1568
1569 fn intern_seq_plus_one(
1571 &mut self,
1572 terms: impl IntoIterator<Item = impl IntoTerm>,
1573 tail: impl IntoTerm,
1574 ) -> Slice {
1575 let index = self.terms.len();
1576 for x in terms {
1577 let t = x.into_term(self);
1578 self.terms.push(t);
1579 }
1580 let t = tail.into_term(self);
1581 self.terms.push(t);
1582 let len = self.terms.len() - index;
1583 Slice {
1584 epoch_id: self.epoch_ids[self.current_epoch],
1585 index: index as u32,
1586 len: len as u32,
1587 }
1588 }
1589
1590 fn byte_slice<'a>(&'a self, slice: &Slice) -> Result<&'a [u8], InternalTermError> {
1594 self.verify_byte_slice(slice)?;
1595 Ok(&self.bytes[(slice.index as usize)..((slice.index + slice.len) as usize)])
1596 }
1597
1598 fn term_slice<'a>(&'a self, slice: &Slice) -> Result<&'a [Term], InternalTermError> {
1600 self.verify_term_slice(slice)?;
1601 Ok(&self.terms[(slice.index as usize)..((slice.index + slice.len) as usize)])
1602 }
1603}
1604
1605#[derive(Debug, Clone)]
1607pub enum TermError {
1608 InvalidTerm(Term),
1609 LiveEpochsExceeded,
1610 InvalidEpoch(EpochID),
1611 MissingFunctor,
1612 InvalidFunctor(Term),
1613 UnexpectedKind {
1614 expected: &'static str,
1615 found: &'static str,
1616 },
1617 UnexpectedArity {
1618 expected: usize,
1619 found: usize,
1620 },
1621 UnexpectedName(Term),
1622}
1623
1624impl fmt::Display for TermError {
1625 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1626 match self {
1627 TermError::InvalidTerm(term) => {
1628 write!(f, "Invalid term {:?}", term)
1629 }
1630 TermError::LiveEpochsExceeded => {
1631 write!(f, "Epoch overflow")
1632 }
1633 TermError::InvalidEpoch(epoch_id) => {
1634 write!(f, "Invalid epoch {:?}", epoch_id)
1635 }
1636 TermError::MissingFunctor => {
1637 write!(f, "Missing functor")
1638 }
1639 TermError::InvalidFunctor(term) => {
1640 write!(f, "Invalid functor {:?}", term)
1641 }
1642 TermError::UnexpectedKind { expected, found } => {
1643 write!(f, "Type mismatch: expected {}, found {}", expected, found)
1644 }
1645 TermError::UnexpectedArity { expected, found } => {
1646 write!(f, "Arity mismatch: expected {}, found {}", expected, found)
1647 }
1648 TermError::UnexpectedName(term) => {
1649 write!(f, "Unexpected name in {:?}", term)
1650 }
1651 }
1652 }
1653}
1654
1655#[derive(Debug, Clone)]
1657enum InternalTermError {
1658 InvalidEpoch(EpochID),
1659 InvalidSlice(Slice),
1660}
1661
1662impl fmt::Display for InternalTermError {
1663 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1664 match self {
1665 InternalTermError::InvalidEpoch(epoch_id) => {
1666 write!(f, "Invalid epoch {:?}", epoch_id)
1667 }
1668 InternalTermError::InvalidSlice(slice) => {
1669 write!(f, "Invalid slice {:?}", slice)
1670 }
1671 }
1672 }
1673}
1674
1675impl std::error::Error for TermError {}
1676
1677impl<'a> PartialEq for View<'a> {
1678 fn eq(&self, other: &Self) -> bool {
1679 let order_a = kind_order(self);
1680 let order_b = kind_order(other);
1681 if order_a != order_b {
1682 return false;
1683 }
1684 match (self, other) {
1685 (
1687 View::Int(_) | View::Real(_) | View::Date(_),
1688 View::Int(_) | View::Real(_) | View::Date(_),
1689 ) => {
1690 let a = numeric_value(self);
1691 let b = numeric_value(other);
1692 a == b
1693 }
1694 (View::Var(a), View::Var(b)) => a == b,
1696 (View::Atom(a), View::Atom(b)) => a == b,
1698 (View::Str(a), View::Str(b)) => a == b,
1700 (View::Bin(a), View::Bin(b)) => a == b,
1702 (View::Func(arena_a, functor_a, args_a), View::Func(arena_b, functor_b, args_b)) => {
1704 if args_a.len() != args_b.len() {
1705 return false;
1706 }
1707 if functor_a != functor_b {
1708 return false;
1709 }
1710 args_a.iter().zip(args_b.iter()).all(|(a, b)| {
1711 a.view(arena_a).expect("arena mismatch")
1712 == b.view(arena_b).expect("arena mismatch")
1713 })
1714 }
1715 (View::List(arena_a, args_a, tail_a), View::List(arena_b, args_b, tail_b)) => {
1716 if args_a.len() != args_b.len() {
1717 return false;
1718 }
1719 args_a.iter().zip(args_b.iter()).all(|(a, b)| {
1720 a.view(arena_a).expect("arena mismatch")
1721 == b.view(arena_b).expect("arena mismatch")
1722 }) && tail_a.view(arena_a).expect("arena mismatch")
1723 == tail_b.view(arena_b).expect("arena mismatch")
1724 }
1725 (View::Tuple(arena_a, args_a), View::Tuple(arena_b, args_b)) => {
1726 if args_a.len() != args_b.len() {
1727 return false;
1728 }
1729 args_a.iter().zip(args_b.iter()).all(|(a, b)| {
1730 a.view(arena_a).expect("arena mismatch")
1731 == b.view(arena_b).expect("arena mismatch")
1732 })
1733 }
1734 _ => unreachable!(),
1735 }
1736 }
1737}
1738
1739impl<'a> Eq for View<'a> {}
1740
1741impl core::cmp::PartialOrd for View<'_> {
1742 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1743 Some(self.cmp(other))
1744 }
1745}
1746
1747impl core::cmp::Ord for View<'_> {
1748 fn cmp(&self, other: &Self) -> Ordering {
1749 let order_a = kind_order(self);
1750 let order_b = kind_order(other);
1751 if order_a != order_b {
1752 return order_a.cmp(&order_b);
1753 }
1754 match (self, other) {
1755 (
1757 View::Int(_) | View::Real(_) | View::Date(_),
1758 View::Int(_) | View::Real(_) | View::Date(_),
1759 ) => {
1760 let a = numeric_value(self);
1761 let b = numeric_value(other);
1762 a.total_cmp(&b)
1763 }
1764 (View::Var(a), View::Var(b)) => a.cmp(b),
1766 (View::Atom(a), View::Atom(b)) => a.cmp(b),
1768 (View::Str(a), View::Str(b)) => a.cmp(b),
1770 (View::Bin(a), View::Bin(b)) => a.cmp(b),
1772 (View::Func(arena_a, functor_a, args_a), View::Func(arena_b, functor_b, args_b)) => {
1774 let ord = args_a.len().cmp(&args_b.len());
1775 if ord != Ordering::Equal {
1776 return ord;
1777 }
1778 let ord = functor_a.cmp(functor_b);
1779 if ord != Ordering::Equal {
1780 return ord;
1781 }
1782 for (arg_a, arg_b) in args_a.iter().zip(args_b.iter()).map(|(a, b)| {
1783 (
1784 a.view(arena_a).expect("arena mismatch"),
1785 b.view(arena_b).expect("arena mismatch"),
1786 )
1787 }) {
1788 let ord = arg_a.cmp(&arg_b);
1789 if ord != Ordering::Equal {
1790 return ord;
1791 }
1792 }
1793 Ordering::Equal
1794 }
1795 (View::List(arena_a, args_a, tail_a), View::List(arena_b, args_b, tail_b)) => {
1796 let ord = args_a.len().cmp(&args_b.len());
1797 if ord != Ordering::Equal {
1798 return ord;
1799 }
1800 for (arg_a, arg_b) in args_a.iter().zip(args_b.iter()).map(|(a, b)| {
1801 (
1802 a.view(arena_a).expect("arena mismatch"),
1803 b.view(arena_b).expect("arena mismatch"),
1804 )
1805 }) {
1806 let ord = arg_a.cmp(&arg_b);
1807 if ord != Ordering::Equal {
1808 return ord;
1809 }
1810 }
1811 tail_a
1812 .view(arena_a)
1813 .expect("arena mismatch")
1814 .cmp(&tail_b.view(arena_b).expect("arena mismatch"))
1815 }
1816 (View::Tuple(arena_a, args_a), View::Tuple(arena_b, args_b)) => {
1817 let ord = args_a.len().cmp(&args_b.len());
1818 if ord != Ordering::Equal {
1819 return ord;
1820 }
1821 for (arg_a, arg_b) in args_a.iter().zip(args_b.iter()).map(|(a, b)| {
1822 (
1823 a.view(arena_a).expect("arena mismatch"),
1824 b.view(arena_b).expect("arena mismatch"),
1825 )
1826 }) {
1827 let ord = arg_a.cmp(&arg_b);
1828 if ord != Ordering::Equal {
1829 return ord;
1830 }
1831 }
1832 Ordering::Equal
1833 }
1834
1835 _ => unreachable!(),
1836 }
1837 }
1838}
1839
1840fn kind_order(t: &View) -> u8 {
1844 match t {
1845 View::Var(_) => 0,
1846 View::Int(_) => 1,
1847 View::Date(_) => 2,
1848 View::Real(_) => 3,
1849 View::Atom(_) => 4,
1850 View::Str(_) => 5,
1851 View::Func(_, _, _) => 6,
1852 View::Tuple(_, _) => 7,
1853 View::List(_, _, _) => 8,
1854 View::Bin(_) => 9,
1855 }
1856}
1857
1858fn numeric_value(t: &View) -> f64 {
1863 match t {
1864 View::Int(i) => *i as f64,
1865 View::Real(f) => *f,
1866 View::Date(d) => *d as f64,
1867 _ => unreachable!(),
1868 }
1869}
1870
1871#[macro_export]
1873macro_rules! list {
1874 ($($arg:expr),* $(,)?; $tail:expr => $arena:expr) => {
1876 $crate::list!($($arg),* ; $tail)($arena)
1877 };
1878 ($($arg:expr),* $(,)? => $arena:expr) => {
1880 $crate::list!($($arg),*)($arena)
1881 };
1882 ($($arg:expr),* $(,)?; $tail:expr) => { (|__arena: &mut $crate::Arena| {
1884 let __args: &[$crate::Term] = &[$($arg.into_term(__arena)),*];
1885 let __tail: Term = $tail.into_term(__arena);
1886 __arena.listc(__args, __tail)
1887 })};
1888 ($($arg:expr),* $(,)?) => { (|__arena: &mut $crate::Arena| {
1890 let __args: &[$crate::Term] = &[$($arg.into_term(__arena)),*];
1891 __arena.list(__args)
1892 })};
1893}
1894
1895#[macro_export]
1896macro_rules! tuple {
1897 ($($arg:expr),* $(,)? => $arena:expr) => {
1899 $crate::tuple!($($arg),*)($arena)
1900 };
1901 ($($arg:expr),* $(,)?) => { (|__arena: &mut $crate::Arena| {
1903 let __args: &[$crate::Term] = &[$($arg.into_term(__arena)),*];
1904 __arena.tuple(__args)
1905 })};
1906}
1907
1908#[macro_export]
1909macro_rules! func {
1910 ($functor:expr; $($arg:expr),+ $(,)? => $arena:expr) => {
1912 $crate::func!($functor; $($arg),+)($arena)
1913 };
1914 ($functor:expr; $($arg:expr),+ $(,)?) => { (|__arena: &mut $crate::Arena| {
1916 let __args: &[$crate::Term] = &[$($arg.into_term(__arena)),+];
1917 __arena.func($functor, __args)
1918 })};
1919}
1920
1921#[macro_export]
1922macro_rules! atom {
1923 ($functor:expr => $arena:expr) => {
1925 $crate::atom!($functor)($arena)
1926 };
1927 ($functor:expr) => {
1929 (|__arena: &mut $crate::Arena| __arena.atom($functor))
1930 };
1931}
1932
1933#[macro_export]
1934macro_rules! var {
1935 ($name:expr => $arena:expr) => {
1937 $crate::var!($name)($arena)
1938 };
1939 ($name:expr) => {
1941 (|__arena: &mut $crate::Arena| __arena.var($name))
1942 };
1943}
1944
1945#[macro_export]
1946macro_rules! date {
1947 ($value:expr) => {
1948 $crate::Term::date($value)
1949 };
1950}
1951
1952#[macro_export]
1953macro_rules! unit {
1954 () => {
1955 $crate::Term::UNIT
1956 };
1957}
1958
1959#[macro_export]
1960macro_rules! nil {
1961 () => {
1962 $crate::Term::NIL
1963 };
1964}
1965
1966pub struct TermDisplay<'a> {
1975 pub term: &'a Term,
1977 pub arena: &'a Arena,
1979}
1980
1981impl Term {
1982 #[inline]
1990 pub fn display<'a>(&'a self, arena: &'a Arena) -> TermDisplay<'a> {
1991 TermDisplay { term: self, arena }
1992 }
1993}
1994
1995impl<'a> fmt::Display for TermDisplay<'a> {
2009 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2010 fn is_unquoted_atom(s: &str) -> bool {
2011 let mut chars = s.chars();
2012 match chars.next() {
2013 Some(c) if c.is_ascii_lowercase() => {}
2014 _ => return false,
2015 }
2016 chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
2017 }
2018
2019 fn write_atom(f: &mut fmt::Formatter<'_>, s: &str) -> fmt::Result {
2020 if s.is_empty() || !is_unquoted_atom(s) {
2021 let escaped = s.replace('\'', "\\'");
2022 write!(f, "'{}'", escaped)
2023 } else {
2024 write!(f, "{}", s)
2025 }
2026 }
2027
2028 fn write_str_quoted(f: &mut fmt::Formatter<'_>, s: &str) -> fmt::Result {
2029 let mut out = String::new();
2030 out.push('"');
2031 for ch in s.chars() {
2032 match ch {
2033 '\\' => out.push_str("\\\\"),
2034 '"' => out.push_str("\\\""),
2035 '\n' => out.push_str("\\n"),
2036 '\r' => out.push_str("\\r"),
2037 '\t' => out.push_str("\\t"),
2038 c if c.is_control() => out.push_str(&format!("\\x{:02X}\\", c as u32)),
2039 c => out.push(c),
2040 }
2041 }
2042 out.push('"');
2043 f.write_str(&out)
2044 }
2045
2046 fn epoch_to_date_string(epoch_ms: i64, fmt: Option<&str>) -> String {
2047 use chrono::{DateTime, Utc};
2048
2049 let secs = epoch_ms.div_euclid(1000);
2050 let nsecs = (epoch_ms.rem_euclid(1000) * 1_000_000) as u32;
2051
2052 let dt_utc = DateTime::<Utc>::from_timestamp(secs, nsecs).unwrap();
2053
2054 String::from(match fmt {
2055 None => dt_utc.to_rfc3339(),
2056 Some(layout) => dt_utc.format(layout).to_string(),
2057 })
2058 }
2059
2060 fn write_args(f: &mut fmt::Formatter<'_>, arena: &Arena, args: &[Term]) -> fmt::Result {
2061 for (i, t) in args.iter().enumerate() {
2062 if i > 0 {
2063 f.write_str(", ")?;
2064 }
2065 write!(f, "{}", t.display(arena))?;
2066 }
2067 Ok(())
2068 }
2069
2070 match self.term.view(self.arena).map_err(|_e| fmt::Error)? {
2071 View::Int(i) => write!(f, "{i}"),
2072 View::Real(r) => {
2073 if r.fract() == 0.0 {
2074 write!(f, "{:.1}", r)
2075 } else {
2076 write!(f, "{}", r)
2077 }
2078 }
2079 View::Date(epoch) => write!(f, "date({})", epoch_to_date_string(epoch, None)),
2080 View::Str(s) => write_str_quoted(f, s),
2081 View::Bin(bytes) => {
2082 write!(f, "hex{{")?;
2083 for b in bytes {
2084 write!(f, "{:02X}", b)?;
2085 }
2086 write!(f, "}}")
2087 }
2088 View::Atom(a) => write_atom(f, a),
2089 View::Var(v) => write!(f, "{}", v),
2090
2091 View::Func(ar, name, args) => {
2092 if args.is_empty() {
2093 return write!(f, "/* invalid Func */");
2094 }
2095 write_atom(f, name)?;
2096 write!(f, "(")?;
2097 write_args(f, ar, args)?;
2098 write!(f, ")")
2099 }
2100
2101 View::Tuple(ar, items) => {
2102 if items.is_empty() {
2103 write!(f, "()")
2104 } else {
2105 write!(f, "(")?;
2106 write_args(f, ar, items)?;
2107 write!(f, ")")
2108 }
2109 }
2110
2111 View::List(ar, items, tail) => {
2112 if items.is_empty() {
2113 write!(f, "[]")
2114 } else {
2115 write!(f, "[")?;
2116 write_args(f, ar, items)?;
2117 if *tail != Term::NIL {
2118 f.write_str(" | ")?;
2119 write!(f, "{}", tail.display(ar))?;
2120 }
2121 write!(f, "]")
2122 }
2123 }
2124 }
2125 }
2126}
2127
2128#[cfg(test)]
2129mod tests {
2130 use super::*;
2131 use std::fmt::Write;
2132
2133 #[test]
2134 fn term_size_is_16_bytes() {
2135 assert_eq!(core::mem::size_of::<Term>(), 16);
2136 }
2137
2138 #[test]
2139 fn option_term_size_is_16_bytes() {
2140 assert_eq!(core::mem::size_of::<Option<Term>>(), 16);
2141 }
2142
2143 #[test]
2144 fn view_size_is_32_bytes() {
2145 assert_eq!(core::mem::size_of::<View>(), 48);
2146 }
2147
2148 #[test]
2149 fn option_view_size_is_32_bytes() {
2150 assert_eq!(core::mem::size_of::<Option<View>>(), 48);
2151 }
2152
2153 #[test]
2154 fn small_atom_interning() {
2155 let mut arena = Arena::new();
2156 let a1 = Term::atom(&mut arena, "foo");
2157 let a2 = Term::atom(&mut arena, "foo");
2158 assert_eq!(a1, a2);
2159 if let Ok(View::Atom(name)) = a1.view(&arena) {
2160 assert_eq!(name, "foo");
2161 } else {
2162 panic!("wrong view");
2163 }
2164 }
2165
2166 #[test]
2167 fn compound_construction_and_formatting() {
2168 let mut arena = Arena::new();
2169 let a = Term::int(1);
2170 let b = Term::real(2.0);
2171 let c = Term::date(1000);
2172 let d = Term::atom(&mut arena, "hello");
2173 let e = Term::var(&mut arena, "Hello");
2174 let f = Term::str(&mut arena, "A str\ning. Longer string.");
2175 let g = list![d, e, f => &mut arena];
2176 let h = tuple!(f, f => &mut arena);
2177 let p = Term::func(&mut arena, "point", &[a, b, c, d, e, f, g, h]);
2178 let p = func![
2179 "foo";
2180 Term::NIL,
2181 Term::UNIT,
2182 p,
2183 p,
2184 list![],
2185 list![a, b; c],
2186 => &mut arena
2187 ];
2188 dbg!(&p);
2189 dbg!(p.view(&arena).unwrap());
2190 dbg!(arena.stats());
2191 assert!(p.is_func());
2192 if let Ok(View::Func(_, functor, args)) = p.view(&arena) {
2193 assert_eq!(functor, "foo");
2194 assert_eq!(p.arity(), 6);
2195 assert_eq!(args.len(), 6);
2196 } else {
2197 panic!("unexpected view");
2198 }
2199
2200 let s = format!("{}", p.display(&arena));
2201 assert_eq!(
2202 s,
2203 "foo(nil, unit, point(1, 2.0, date(1970-01-01T00:00:01+00:00), hello, Hello, \"A str\\ning. Longer string.\", [hello, Hello, \"A str\\ning. Longer string.\"], (\"A str\\ning. Longer string.\", \"A str\\ning. Longer string.\")), point(1, 2.0, date(1970-01-01T00:00:01+00:00), hello, Hello, \"A str\\ning. Longer string.\", [hello, Hello, \"A str\\ning. Longer string.\"], (\"A str\\ning. Longer string.\", \"A str\\ning. Longer string.\")), nil, [1, 2.0 | date(1970-01-01T00:00:01+00:00)])"
2204 );
2205 }
2206
2207 #[test]
2208 fn view_construction() {
2209 let mut a1 = Arena::new();
2210 let x = a1.atom("Hello, hello, quite long long string, world! X");
2211 dbg!(a1.view(&x).unwrap());
2212 dbg!(a1.stats());
2213 let p = list![x, x => &mut a1];
2214 dbg!(p);
2215 let v = a1.view(&p).unwrap();
2216 dbg!(v);
2217 }
2218
2219 #[test]
2220 #[should_panic]
2221 fn arena_mismatch() {
2222 let a1 = Arena::new();
2223 let mut a2 = Arena::new();
2224 let y = a2.str("Hello, hello, quite long long string, world! Y");
2225 dbg!(a1.view(&y).unwrap());
2226 }
2227
2228 #[test]
2229 #[should_panic]
2230 fn stale_term_str() {
2231 let mut a = Arena::new();
2232 let x = a.str("Hello, hello, quite long long string, world! Y");
2233 dbg!(&a);
2234 a.truncate(a.current_epoch()).unwrap();
2235 dbg!(a.view(&x).unwrap());
2236 }
2237
2238 #[test]
2239 #[should_panic]
2240 fn stale_term_list() {
2241 let mut a = Arena::new();
2242 let _x = list![1, 2, 3 => &mut a];
2243 let epoch = a.begin_epoch().unwrap();
2244 dbg!(&epoch);
2245 let y = list![4, 5, 6 => &mut a];
2246 dbg!(&a);
2247 a.truncate(epoch).unwrap();
2248 dbg!(&a);
2249 dbg!(a.view(&y).unwrap());
2250 }
2251
2252 #[test]
2253 fn big_term() {
2254 let mut a1 = Arena::new();
2255 let x = a1.atom("Hello, hello, quite long long string, world! X");
2256 let p = a1.func("foo", vec![x; 1_000_000]);
2257 assert!(p.arity() == 1_000_000);
2258 dbg!(a1.stats());
2259 }
2260
2261 #[test]
2262 fn interface() {
2263 let a = &mut Arena::new();
2264 let s = String::from("x");
2265 let x1 = a.func(&s, &vec![Term::date(1000)]);
2266 let x2 = a.func(s.as_str(), vec![Term::date(1000)]);
2267 let x3 = a.func(s, &[Term::date(1000)]);
2268 let _x4 = a.func("x", [Term::date(1000)]);
2269 let _x5 = a.func("x", [x1, x2, x3]);
2270 let _x6 = a.func("x", (5..=6).map(|x| x as f64));
2271 let _x7 = a.func("x", vec![&x1, &x2, &x3]);
2272 let _x8 = a.func("x", &[x1, x2, x3]);
2273 let x9 = func!(
2274 String::from("aaa");
2275 x1, 1u8, 1i8, 2.0,
2276 "x",
2277 "X",
2278 atom!("ATOM"),
2279 var!("var"),
2280 "a string",
2281 b"a binary",
2282 1,
2283 2,
2284 3,
2285 4,
2286 6,
2287 unit!(),
2288 list![1, 2, 3; tuple!()],
2289 list![1, 2, 3; nil!()],
2290 => a
2291 );
2292 dbg!(a.view(&x9).unwrap());
2293 dbg!(a.stats());
2294 }
2295
2296 #[test]
2297 fn into_test() {
2298 let mut arena = Arena::new();
2299 let t1 = arena.term(1);
2301 let t2 = arena.term(2.0);
2302 let t3 = arena.term("x");
2303 let t4 = arena.term(b"bin" as &[u8]);
2304 let point1 = arena.func("point", [t1, t2, t3, t4]);
2305 let t1 = Term::int(1);
2307 let t2 = Term::real(2.0);
2308 let t3 = Term::str(&mut arena, "x");
2309 let t4 = Term::bin(&mut arena, b"bin");
2310 let point2 = arena.func("point", [t1, t2, t3, t4]);
2311 assert_eq!(arena.view(&point1).unwrap(), arena.view(&point2).unwrap());
2312 dbg!(arena.view(&point1).unwrap());
2313
2314 let lazy = Term::func(&mut arena, "lazy", [|arena: &mut Arena| arena.atom("ok")]);
2316 dbg!(arena.view(&lazy).unwrap());
2317
2318 let list = arena.list([1, 2, 3]);
2319 dbg!(arena.view(&list).unwrap());
2320 }
2321
2322 #[test]
2323 fn arena_truncate_test() {
2324 let a = &mut Arena::new();
2325
2326 let t1 = a.str("a".repeat(1000));
2327 let _t5 = atom!("x".repeat(100) => a);
2328 let _t6 = var!("X".repeat(200) => a);
2329 let _t7 = a.bin(b"x".repeat(5000));
2330 let epoch1 = a.begin_epoch().unwrap();
2331 dbg!(a.stats());
2332 dbg!(&epoch1);
2333 let t2 = a.str("b".repeat(2000));
2334 let t3 = a.bin(b"b".repeat(3000));
2335 let _t4 = list![t1, t2, t3];
2336 let _t5 = atom!("z".repeat(4000) => a);
2337 let _t8 = var!("Z".repeat(2000) => a);
2338 let _t7 = a.bin(b"z".repeat(10_000));
2339 let epoch2 = a.begin_epoch().unwrap();
2340 dbg!(a.stats());
2341 dbg!(&epoch2);
2342 a.truncate(epoch2).unwrap();
2343 dbg!(a.stats());
2344 }
2345
2346 #[test]
2347 fn funcv() {
2348 let a = &mut Arena::new();
2349 let xs = [a.atom("foo"), a.atom("x"), a.atom("y")];
2350 let x = a.funcv(xs).unwrap();
2351 let ys = [a.atom("x"), a.atom("y")];
2352 let y = a.func("foo", ys);
2353 assert_eq!(x.arity(), y.arity());
2354 if let Ok(View::Func(_, functor, args)) = x.view(&a) {
2355 assert_eq!(functor, "foo");
2356 assert_eq!(args.len(), 2);
2357 }
2358 if let Ok(View::Func(_, functor, args)) = y.view(&a) {
2359 assert_eq!(functor, "foo");
2360 assert_eq!(args.len(), 2);
2361 }
2362 }
2363
2364 #[test]
2365 fn unpack() {
2366 let a = &mut Arena::new();
2367 let xs = [a.atom("foo"), a.atom("x"), a.atom("y")];
2368 let x = a.funcv(xs).unwrap();
2369
2370 let (foo, [x, y]) = x.unpack_func(a, &["foo", "boo"]).unwrap();
2371 dbg!((foo, x, y));
2372
2373 let z = tuple!(1 => a);
2374 assert_eq!(z.arity(), 1);
2375 }
2376
2377 #[test]
2378 fn arity_primitives_and_lists_are_zero() {
2379 let a = &mut Arena::new();
2380
2381 let t_int = Term::int(42);
2382 let t_real = Term::real(3.14);
2383 let t_atom = Term::atom(a, "ok");
2384 let t_var = Term::var(a, "X");
2385 let t_str = Term::str(a, "hello");
2386 let t_bin = Term::bin(a, &[1, 2, 3, 4]);
2387 let t_list = Term::list(a, &[Term::int(1), Term::int(2), Term::int(3)]);
2388
2389 assert_eq!(t_int.arity(), 0);
2390 assert_eq!(t_real.arity(), 0);
2391 assert_eq!(t_atom.arity(), 0);
2392 assert_eq!(t_var.arity(), 0);
2393 assert_eq!(t_str.arity(), 0);
2394 assert_eq!(t_bin.arity(), 0);
2395 assert_eq!(t_list.arity(), 0); }
2397
2398 #[test]
2399 fn arity_for_tuples_and_funcs() {
2400 let a = &mut Arena::new();
2401
2402 let t2 = Term::tuple(a, &[Term::int(1), Term::int(2)]);
2403 let t3 = Term::tuple(a, &[Term::int(1), Term::int(2), Term::int(3)]);
2404 assert_eq!(t2.arity(), 2);
2405 assert_eq!(t3.arity(), 3);
2406
2407 let f0 = Term::func(a, "nilary", &[] as &[Term]); let f2 = Term::func(a, "pair", &[Term::int(1), Term::int(2)]);
2409 let f3 = Term::func(a, "triple", &[Term::int(1), Term::int(2), Term::int(3)]);
2410
2411 assert_eq!(f0.arity(), 0);
2412 assert_eq!(f2.arity(), 2);
2413 assert_eq!(f3.arity(), 3);
2414 }
2415
2416 #[test]
2417 fn name_and_kind_name() {
2418 let a = &mut Arena::new();
2419
2420 let atom = Term::atom(a, "foo");
2421 let var = Term::var(a, "X");
2422 let fun = Term::func(a, "bar", &[Term::int(1)]);
2423 let tup = Term::tuple(a, &[Term::int(1), Term::int(2)]);
2424 let lst = Term::list(a, &[Term::int(1), Term::int(2), Term::int(3)]);
2425
2426 assert_eq!(atom.name(&a).unwrap(), "foo");
2428 assert_eq!(var.name(&a).unwrap(), "X");
2429 assert_eq!(fun.name(&a).unwrap(), "bar");
2430
2431 assert_eq!(atom.kind_name(), "atom");
2433 assert_eq!(var.kind_name(), "var");
2434 assert_eq!(fun.kind_name(), "func");
2435 assert_eq!(tup.kind_name(), "tuple");
2436 assert_eq!(lst.kind_name(), "list");
2437 assert_eq!(Term::int(7).kind_name(), "int");
2438 assert_eq!(Term::str(a, "s").kind_name(), "str");
2439 }
2440
2441 #[test]
2442 fn is_func_tuple_list() {
2443 let a = &mut Arena::new();
2444
2445 let f2 = Term::func(a, "pair", &[Term::int(1), Term::int(2)]);
2446 let tup = Term::tuple(a, &[Term::int(1), Term::int(2)]);
2447 let lst = Term::list(a, &[Term::int(1), Term::int(2), Term::int(3)]);
2448
2449 assert!(f2.is_func());
2450 assert!(tup.is_tuple());
2451 assert!(lst.is_list());
2452
2453 assert!(!f2.is_tuple());
2454 assert!(!f2.is_list());
2455 assert!(!tup.is_func());
2456 assert!(!lst.is_func());
2457 assert!(!lst.is_tuple());
2458 }
2459
2460 #[test]
2461 fn is_inline_obvious_cases() {
2462 let a = &mut Arena::new();
2463
2464 let i = Term::int(42);
2465 let f0 = Term::func(a, "nilary", &[] as &[Term]);
2466 let tup = Term::tuple(a, &[Term::int(1), Term::int(2)]);
2467 let lst = Term::list(a, &[Term::int(1), Term::int(2)]);
2468
2469 assert!(i.is_inline());
2471 assert!(f0.is_inline()); assert!(!tup.is_inline());
2473 assert!(!lst.is_inline());
2474 }
2475
2476 #[test]
2477 fn is_atom_var_str_bin_number_minimal() {
2478 let a = &mut Arena::new();
2479
2480 let at = Term::atom(a, "foo");
2481 let vr = Term::var(a, "X");
2482 let st = Term::str(a, "hi");
2483 let bi = Term::bin(a, &[1, 2, 3, 4]);
2484 let i = Term::int(7);
2485
2486 assert!(at.is_atom());
2487 assert!(vr.is_var());
2488 assert!(st.is_str());
2489 assert!(bi.is_bin());
2490
2491 assert!(!at.is_var());
2492 assert!(!vr.is_atom());
2493 assert!(!st.is_bin());
2494 assert!(!bi.is_str());
2495
2496 assert!(i.is_number());
2498 assert!(!at.is_number());
2499 assert!(!st.is_number());
2500 assert!(!bi.is_number());
2501 }
2502
2503 #[test]
2504 fn nil_and_tuple_edge_behavior() {
2505 let a = &mut Arena::new();
2506
2507 assert!(Term::NIL.is_list());
2509 assert!(!Term::NIL.is_tuple());
2510 assert!(!Term::NIL.is_func());
2511 assert_eq!(Term::NIL.arity(), 0);
2512
2513 let t = Term::tuple(a, &[Term::int(1), Term::int(2), Term::int(3)]);
2515 assert!(t.is_tuple());
2516 assert_eq!(t.arity(), 3);
2517 assert!(!t.is_list());
2518 assert!(!t.is_func());
2519 }
2520
2521 #[test]
2522 fn arity_consistency_with_predicates() {
2523 let a = &mut Arena::new();
2524
2525 let f3 = Term::func(a, "triple", &[Term::int(1), Term::int(2), Term::int(3)]);
2526 let t2 = Term::tuple(a, &[Term::int(1), Term::int(2)]);
2527 let l2 = Term::list(a, &[Term::int(1), Term::int(2)]);
2528 let v = Term::var(a, "X");
2529
2530 assert!(f3.is_func());
2531 assert_eq!(f3.arity(), 3);
2532
2533 assert!(t2.is_tuple());
2534 assert_eq!(t2.arity(), 2);
2535
2536 assert!(l2.is_list());
2537 assert_eq!(l2.arity(), 0); assert!(v.is_var());
2540 assert_eq!(v.arity(), 0); }
2542
2543 #[test]
2544 fn name_and_kind_name_roundtrip() {
2545 let a = &mut Arena::new();
2546
2547 let atom = Term::atom(a, "foo");
2548 let var = Term::var(a, "X");
2549 let fun = Term::func(a, "bar", &[Term::int(1)]);
2550 let tup = Term::tuple(a, &[Term::int(1), Term::int(2)]);
2551 let lst = Term::list(a, &[Term::int(1), Term::int(2), Term::int(3)]);
2552
2553 assert_eq!(atom.name(&a).unwrap(), "foo");
2555 assert_eq!(var.name(&a).unwrap(), "X");
2556 assert_eq!(fun.name(&a).unwrap(), "bar");
2557
2558 assert_eq!(atom.kind_name(), "atom");
2560 assert_eq!(var.kind_name(), "var");
2561 assert_eq!(fun.kind_name(), "func");
2562 assert_eq!(tup.kind_name(), "tuple");
2563 assert_eq!(lst.kind_name(), "list");
2564 assert_eq!(Term::int(7).kind_name(), "int");
2565 assert_eq!(Term::str(a, "s").kind_name(), "str");
2566 }
2567
2568 #[test]
2569 fn unpack_primitives_ok() {
2570 let a = &mut Arena::new();
2571
2572 let t_int = Term::int(42);
2573 let t_real = Term::real(3.5);
2574 let t_date = Term::date(2);
2575 let t_str = Term::str(a, "hello");
2576 let t_bin = Term::bin(a, &[1u8, 2, 3, 4]);
2577
2578 assert_eq!(t_int.unpack_int(a).unwrap(), 42);
2579 assert!((t_real.unpack_real(a).unwrap() - 3.5).abs() < f64::EPSILON);
2580 assert_eq!(t_date.unpack_date(a).unwrap(), 2);
2581
2582 assert_eq!(t_str.unpack_str(a).unwrap(), "hello");
2583 assert_eq!(t_bin.unpack_bin(a).unwrap(), &[1, 2, 3, 4]);
2584 }
2585
2586 #[test]
2587 fn unpack_primitives_wrong_type_errs() {
2588 let a = &mut Arena::new();
2589
2590 let not_int = Term::str(a, "nope");
2591 let not_real = Term::int(1);
2592 let not_date = Term::str(a, "2024-01-02");
2593
2594 assert!(not_int.unpack_int(a).is_err());
2595 assert!(not_real.unpack_real(a).is_err());
2596 assert!(not_date.unpack_date(a).is_err());
2597
2598 let not_str = Term::int(5);
2599 let not_bin = Term::str(a, "bytes");
2600 assert!(not_str.unpack_str(a).is_err());
2601 assert!(not_bin.unpack_bin(a).is_err());
2602 }
2603
2604 #[test]
2605 fn unpack_atom_and_var_with_allowed_names() {
2606 let a = &mut Arena::new();
2607
2608 let at_ok = Term::atom(a, "foo");
2609 let at_no = Term::atom(a, "bar");
2610 let vr_ok = Term::var(a, "X");
2611 let vr_no = Term::var(a, "Y");
2612
2613 let allowed_atoms = ["foo", "baz"];
2615 let allowed_vars = ["X", "Z"];
2616
2617 assert_eq!(at_ok.unpack_atom(a, &allowed_atoms).unwrap(), "foo");
2618 assert!(at_no.unpack_atom(a, &allowed_atoms).is_err());
2619
2620 assert_eq!(vr_ok.unpack_var(a, &allowed_vars).unwrap(), "X");
2621 assert!(vr_no.unpack_var(a, &allowed_vars).is_err());
2622
2623 assert_eq!(at_no.unpack_atom(a, &[]).unwrap(), "bar");
2625 assert_eq!(vr_no.unpack_var(a, &[]).unwrap(), "Y");
2626 }
2627
2628 #[test]
2629 fn unpack_func_any_and_arity_specific() {
2630 let a = &mut Arena::new();
2631
2632 let f0 = Term::func(a, "nilary", &[] as &[Term]);
2633 let f2 = Term::func(a, "pair", &[Term::int(1), Term::int(2)]);
2634 let f3 = Term::func(a, "triple", &[Term::int(1), Term::int(2), Term::int(3)]);
2635
2636 {
2638 let (name, args) = f2.unpack_func_any(a, &["pair", "other"]).unwrap();
2639 assert_eq!(name, "pair");
2640 assert_eq!(args.len(), 2);
2641 assert_eq!(args[0].unpack_int(a).unwrap(), 1);
2642 assert_eq!(args[1].unpack_int(a).unwrap(), 2);
2643
2644 let (name0, args0) = f0.unpack_func_any(a, &[]).unwrap();
2646 assert_eq!(name0, "nilary");
2647 assert!(args0.is_empty());
2648
2649 assert!(f3.unpack_func_any(a, &["not_triple"]).is_err());
2651 }
2652
2653 {
2655 let (name2, [x, y]) = f2.unpack_func(a, &["pair"]).unwrap();
2656 assert_eq!(name2, "pair");
2657 assert_eq!(x.unpack_int(a).unwrap(), 1);
2658 assert_eq!(y.unpack_int(a).unwrap(), 2);
2659
2660 assert!(f3.unpack_func::<2>(a, &["triple"]).is_err());
2662
2663 assert!(f2.unpack_func::<2>(a, &["other"]).is_err());
2665 }
2666 }
2667
2668 #[test]
2669 fn unpack_list_proper_and_tail() {
2670 let a = &mut Arena::new();
2671
2672 let l0 = Term::list(a, &[] as &[Term]);
2673 let (elems0, tail0) = l0.unpack_list(a).unwrap();
2674 assert!(elems0.is_empty());
2675 assert_eq!(tail0, Term::NIL);
2676
2677 let l3 = Term::list(a, &[Term::int(1), Term::int(2), Term::int(3)]);
2678 let (elems3, tail3) = l3.unpack_list(a).unwrap();
2679 assert_eq!(elems3.len(), 3);
2680 assert_eq!(elems3[0].unpack_int(a).unwrap(), 1);
2681 assert_eq!(elems3[1].unpack_int(a).unwrap(), 2);
2682 assert_eq!(elems3[2].unpack_int(a).unwrap(), 3);
2683 assert_eq!(tail3, Term::NIL);
2684
2685 let not_list = Term::int(9);
2687 assert!(not_list.unpack_list(a).is_err());
2688 }
2689
2690 #[test]
2691 fn unpack_tuple_any_and_fixed() {
2692 let a = &mut Arena::new();
2693
2694 let t0 = Term::tuple(a, &[] as &[Term]);
2695 let t3 = Term::tuple(a, &[Term::int(1), Term::int(2), Term::int(3)]);
2696
2697 let elems0 = t0.unpack_tuple_any(a).unwrap();
2699 assert!(elems0.is_empty());
2700
2701 let elems3 = t3.unpack_tuple_any(a).unwrap();
2702 assert_eq!(elems3.len(), 3);
2703 assert_eq!(elems3[0].unpack_int(a).unwrap(), 1);
2704 assert_eq!(elems3[1].unpack_int(a).unwrap(), 2);
2705 assert_eq!(elems3[2].unpack_int(a).unwrap(), 3);
2706
2707 let arr3 = t3.unpack_tuple::<3>(a).unwrap();
2709 assert_eq!(arr3[0].unpack_int(a).unwrap(), 1);
2710 assert_eq!(arr3[1].unpack_int(a).unwrap(), 2);
2711 assert_eq!(arr3[2].unpack_int(a).unwrap(), 3);
2712
2713 assert!(t3.unpack_tuple::<2>(a).is_err());
2715
2716 assert!(Term::int(1).unpack_tuple_any(a).is_err());
2718 assert!(Term::int(1).unpack_tuple::<0>(a).is_err());
2719 }
2720
2721 #[test]
2722 fn unpack_atom_var_wrong_type_errs() {
2723 let a = &mut Arena::new();
2724
2725 let not_atom = Term::int(1);
2726 let not_var = Term::str(a, "X");
2727 assert!(not_atom.unpack_atom(a, &[]).is_err());
2728 assert!(not_var.unpack_var(a, &[]).is_err());
2729 }
2730
2731 #[test]
2732 fn unpack_func_wrong_type_errs() {
2733 let a = &mut Arena::new();
2734
2735 let tup = Term::tuple(a, &[Term::int(1), Term::int(2)]);
2737 assert!(tup.unpack_func_any(a, &[]).is_err());
2738 assert!(tup.unpack_func::<2>(a, &[]).is_err());
2739
2740 let at = Term::atom(a, "f");
2742 assert!(!at.unpack_func_any(a, &[]).is_err());
2743 assert!(!at.unpack_func::<0>(a, &[]).is_err());
2744 }
2745
2746 #[test]
2747 fn fmt_nil_to_string() {
2748 let arena = Arena::new();
2749 let t = Term::NIL;
2750 assert_eq!(t.display(&arena).to_string(), "nil");
2751 }
2752
2753 #[test]
2754 fn fmt_unit_format_macro() {
2755 let arena = Arena::new();
2756 let t = Term::UNIT;
2757 assert_eq!(format!("{}", t.display(&arena)), "unit");
2758 }
2759
2760 #[test]
2761 fn fmt_int_positive() {
2762 let arena = Arena::new();
2763 let t = Term::int(42);
2764 assert_eq!(format!("{}", t.display(&arena)), "42");
2765 }
2766
2767 #[test]
2768 fn fmt_int_negative_to_string() {
2769 let arena = Arena::new();
2770 let t = Term::int(-9001);
2771 assert_eq!(t.display(&arena).to_string(), "-9001");
2772 }
2773
2774 #[test]
2775 fn fmt_str_quotes() {
2776 let mut arena = Arena::new();
2777 let t = Term::str(&mut arena, "hello");
2778 assert_eq!(format!("{}", t.display(&arena)), r#""hello""#);
2779 }
2780
2781 #[test]
2782 fn fmt_str_with_escape_chars() {
2783 let mut arena = Arena::new();
2784 let t = Term::str(&mut arena, "a\nb\tc");
2785 assert_eq!(t.display(&arena).to_string(), "\"a\\nb\\tc\"");
2786 }
2787
2788 #[test]
2789 fn fmt_date_epoch_zero() {
2790 let arena = Arena::new();
2791 let t = Term::date(0);
2793 assert_eq!(
2794 format!("{}", t.display(&arena)),
2795 "date(1970-01-01T00:00:00+00:00)"
2796 );
2797 }
2798
2799 #[test]
2800 fn fmt_date_epoch_ms_trunc_to_seconds() {
2801 let arena = Arena::new();
2802 let t = Term::date(1_234);
2803 assert_eq!(
2804 t.display(&arena).to_string(),
2805 "date(1970-01-01T00:00:01.234+00:00)"
2806 );
2807 }
2808
2809 #[test]
2810 fn fmt_date_specific_moment() {
2811 let arena = Arena::new();
2812 let t = Term::date(1_727_525_530_123i64);
2813 assert_eq!(
2814 format!("{}", t.display(&arena)),
2815 "date(2024-09-28T12:12:10.123+00:00)"
2816 );
2817 }
2818
2819 #[test]
2820 fn fmt_date_specific_moment_in_the_past() {
2821 let arena = Arena::new();
2822 let t = Term::date(-5_382_698_399_999i64);
2823 assert_eq!(
2824 format!("{}", t.display(&arena)),
2825 "date(1799-06-06T06:00:00.001+00:00)"
2826 );
2827 }
2828
2829 #[test]
2830 fn fmt_write_into_string_buffer() {
2831 let arena = Arena::new();
2832 let t = Term::int(7);
2833 let mut buf = String::new();
2834 write!(&mut buf, "val={}", t.display(&arena)).expect("formatting failed");
2836 assert_eq!(buf, "val=7");
2837 }
2838}