1use core::fmt;
64use smartstring::alias::String;
65use std::borrow::Cow;
66use std::cmp::Ordering;
67
68#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
77struct TinyArray {
78 bytes: [u8; 14],
79 len: u8,
80}
81
82#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
83struct Slice {
84 epoch_id: EpochID,
85 index: u32,
86 len: u32,
87}
88
89#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
96#[repr(u8)]
97enum Handle {
98 Int(i64),
99 Real(f64),
100 Date(i64),
101 Var(TinyArray),
102 VarRef(Slice),
103 Atom(TinyArray),
104 AtomRef(Slice),
105 Str(TinyArray),
106 StrRef(Slice),
107 Bin(TinyArray),
108 BinRef(Slice),
109 FuncRef(Slice),
110 ListRef(Slice),
111 ListCRef(Slice),
112 TupleRef(Slice),
113}
114
115#[derive(Copy, Clone, PartialEq, PartialOrd)]
130pub struct Term(Handle);
131
132impl AsRef<Term> for Term {
133 fn as_ref(&self) -> &Self {
134 self
135 }
136}
137
138macro_rules! impl_from_integers_for_term {
139 ($($t:ty),* $(,)?) => {$(
140 impl From<$t> for Term {
141 #[inline]
142 fn from(v: $t) -> Self { Term::int(v as i64) }
143 }
144 )*};
145}
146impl_from_integers_for_term!(i8, i16, i32, i64, u8, u16, u32);
147
148macro_rules! impl_from_floats_for_term {
149 ($($t:ty),* $(,)?) => {$(
150 impl From<$t> for Term {
151 #[inline]
152 fn from(v: $t) -> Self { Term::real(v as f64) }
153 }
154 )*};
155}
156impl_from_floats_for_term!(f32, f64);
157
158pub trait IntoTerm {
159 fn into_term(self, arena: &mut Arena) -> Term;
160}
161
162macro_rules! impl_intoterm_for_integers {
163 ($($t:ty),* $(,)?) => {$(
164 impl IntoTerm for $t {
165 #[inline]
166 fn into_term(self, _arena: &mut Arena) -> Term { Term::int(self as i64) }
167 }
168 )*};
169}
170impl_intoterm_for_integers!(i8, i16, i32, i64, u8, u16, u32);
171
172macro_rules! impl_intoterm_for_floats {
173 ($($t:ty),* $(,)?) => {$(
174 impl IntoTerm for $t {
175 #[inline]
176 fn into_term(self, _arena: &mut Arena) -> Term { Term::real(self as f64) }
177 }
178 )*};
179}
180impl_intoterm_for_floats!(f32, f64);
181
182impl<'a> IntoTerm for &'a str {
183 #[inline]
184 fn into_term(self, arena: &mut Arena) -> Term {
185 Term::str(arena, self)
186 }
187}
188
189impl<'a> IntoTerm for &'a [u8] {
190 #[inline]
191 fn into_term(self, arena: &mut Arena) -> Term {
192 Term::bin(arena, self)
193 }
194}
195
196impl<'a> IntoTerm for Cow<'a, str> {
197 #[inline]
198 fn into_term(self, arena: &mut Arena) -> Term {
199 match self {
200 Cow::Borrowed(s) => Term::str(arena, s),
201 Cow::Owned(s) => Term::str(arena, s),
202 }
203 }
204}
205
206impl<'a> IntoTerm for Cow<'a, [u8]> {
207 #[inline]
208 fn into_term(self, arena: &mut Arena) -> Term {
209 match self {
210 Cow::Borrowed(s) => Term::bin(arena, s),
211 Cow::Owned(s) => Term::bin(arena, s),
212 }
213 }
214}
215
216impl IntoTerm for String {
217 #[inline]
218 fn into_term(self, arena: &mut Arena) -> Term {
219 Term::str(arena, &self)
220 }
221}
222
223impl IntoTerm for std::string::String {
224 #[inline]
225 fn into_term(self, arena: &mut Arena) -> Term {
226 Term::str(arena, &self)
227 }
228}
229
230impl IntoTerm for Vec<u8> {
231 #[inline]
232 fn into_term(self, arena: &mut Arena) -> Term {
233 Term::bin(arena, &self)
234 }
235}
236
237impl IntoTerm for Term {
238 #[inline]
239 fn into_term(self, _arena: &mut Arena) -> Term {
240 self
241 }
242}
243
244impl IntoTerm for &Term {
245 #[inline]
246 fn into_term(self, _arena: &mut Arena) -> Term {
247 *self
248 }
249}
250
251impl<F> IntoTerm for F
252where
253 F: FnOnce(&mut Arena) -> Term,
254{
255 #[inline]
256 fn into_term(self, arena: &mut Arena) -> Term {
257 self(arena)
258 }
259}
260
261impl Term {
262 #[inline]
266 pub fn int(i: impl Into<i64>) -> Self {
267 Self(Handle::Int(i.into()))
268 }
269
270 #[inline]
273 pub fn real(f: impl Into<f64>) -> Self {
274 Self(Handle::Real(f.into()))
275 }
276
277 #[inline]
282 pub fn date(ms: impl Into<i64>) -> Self {
283 Self(Handle::Date(ms.into()))
284 }
285
286 #[inline]
291 pub fn atom(arena: &mut Arena, name: impl AsRef<str>) -> Self {
292 let name = name.as_ref();
293 let bytes = name.as_bytes();
294 if bytes.len() <= 14 {
295 let mut buf = [0u8; 14];
296 buf[..bytes.len()].copy_from_slice(bytes);
297 Self(Handle::Atom(TinyArray {
298 bytes: buf,
299 len: bytes.len() as u8,
300 }))
301 } else {
302 Self(Handle::AtomRef(arena.intern_str(name)))
303 }
304 }
305
306 #[inline]
311 pub fn var(arena: &mut Arena, name: impl AsRef<str>) -> Self {
312 let name = name.as_ref();
313 let bytes = name.as_bytes();
314 if bytes.len() <= 14 {
315 let mut buf = [0u8; 14];
316 buf[..bytes.len()].copy_from_slice(bytes);
317 Self(Handle::Var(TinyArray {
318 bytes: buf,
319 len: bytes.len() as u8,
320 }))
321 } else {
322 Self(Handle::VarRef(arena.intern_str(name)))
323 }
324 }
325
326 #[inline]
331 pub fn str(arena: &mut Arena, s: impl AsRef<str>) -> Self {
332 let s = s.as_ref();
333 let bytes = s.as_bytes();
334 if bytes.len() <= 14 {
335 let mut buf = [0u8; 14];
336 buf[..bytes.len()].copy_from_slice(bytes);
337 Self(Handle::Str(TinyArray {
338 bytes: buf,
339 len: bytes.len() as u8,
340 }))
341 } else {
342 Self(Handle::StrRef(arena.intern_str(s)))
343 }
344 }
345
346 #[inline]
350 pub fn bin(arena: &mut Arena, bytes: impl AsRef<[u8]>) -> Self {
351 let bytes = bytes.as_ref();
352 if bytes.len() <= 14 {
353 let mut buf = [0u8; 14];
354 buf[..bytes.len()].copy_from_slice(bytes);
355 Self(Handle::Bin(TinyArray {
356 bytes: buf,
357 len: bytes.len() as u8,
358 }))
359 } else {
360 Self(Handle::BinRef(arena.intern_bytes(bytes)))
361 }
362 }
363
364 #[inline]
370 pub fn func(
371 arena: &mut Arena,
372 functor: impl AsRef<str>,
373 args: impl IntoIterator<Item = impl IntoTerm>,
374 ) -> Self {
375 let functor_atom = Self::atom(arena, functor);
376 let mut args = args.into_iter();
377 let Some(first) = args.next() else {
378 return functor_atom;
379 };
380 Self(Handle::FuncRef(arena.intern_func(
381 functor_atom,
382 std::iter::once(first).chain(args),
383 )))
384 }
385
386 #[inline]
391 pub fn funcv(
392 arena: &mut Arena,
393 terms: impl IntoIterator<Item = impl IntoTerm>,
394 ) -> Result<Self, TermError> {
395 let mut terms = terms.into_iter();
396 let Some(functor_atom) = terms.next() else {
397 return Err(TermError::MissingFunctor);
398 };
399 let functor_atom = functor_atom.into_term(arena);
400 if !functor_atom.is_atom() {
401 return Err(TermError::InvalidFunctor(functor_atom));
402 }
403 let Some(first) = terms.next() else {
404 return Ok(functor_atom);
405 };
406 Ok(Self(Handle::FuncRef(arena.intern_func(
407 functor_atom,
408 std::iter::once(first).chain(terms),
409 ))))
410 }
411
412 #[inline]
415 pub fn list(arena: &mut Arena, terms: impl IntoIterator<Item = impl IntoTerm>) -> Self {
416 let mut terms = terms.into_iter();
417 let Some(first) = terms.next() else {
418 return Self::NIL;
419 };
420 Self(Handle::ListRef(
421 arena.intern_seq(std::iter::once(first).chain(terms)),
422 ))
423 }
424
425 #[inline]
428 pub fn listc(
429 arena: &mut Arena,
430 terms: impl IntoIterator<Item = impl IntoTerm>,
431 tail: impl IntoTerm,
432 ) -> Self {
433 let mut terms = terms.into_iter();
434 let Some(first) = terms.next() else {
435 return Self::NIL;
436 };
437 let tail = tail.into_term(arena);
438 if tail != Term::NIL {
439 Self(Handle::ListCRef(arena.intern_seq_plus_one(
440 std::iter::once(first).chain(terms),
441 tail,
442 )))
443 } else {
444 Self(Handle::ListRef(
445 arena.intern_seq(std::iter::once(first).chain(terms)),
446 ))
447 }
448 }
449
450 #[inline]
453 pub fn tuple(arena: &mut Arena, terms: impl IntoIterator<Item = impl IntoTerm>) -> Self {
454 let mut terms = terms.into_iter();
455 let Some(first) = terms.next() else {
456 return Self::UNIT;
457 };
458 Self(Handle::TupleRef(
459 arena.intern_seq(std::iter::once(first).chain(terms)),
460 ))
461 }
462
463 pub const UNIT: Self = {
467 let buf: [u8; 14] = [b'u', b'n', b'i', b't', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
468 Self(Handle::Atom(TinyArray { bytes: buf, len: 4 }))
469 };
470
471 pub const NIL: Self = {
475 let buf: [u8; 14] = [b'n', b'i', b'l', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
476 Self(Handle::Atom(TinyArray { bytes: buf, len: 3 }))
477 };
478
479 #[inline]
481 pub fn unpack_int(&self, arena: &Arena) -> Result<i64, TermError> {
482 arena.unpack_int(self)
483 }
484
485 #[inline]
487 pub fn unpack_real(&self, arena: &Arena) -> Result<f64, TermError> {
488 arena.unpack_real(self)
489 }
490
491 #[inline]
493 pub fn unpack_date(&self, arena: &Arena) -> Result<i64, TermError> {
494 arena.unpack_date(self)
495 }
496
497 #[inline]
499 pub fn unpack_str<'a>(&'a self, arena: &'a Arena) -> Result<&'a str, TermError> {
500 arena.unpack_str(self)
501 }
502
503 #[inline]
505 pub fn unpack_bin<'a>(&'a self, arena: &'a Arena) -> Result<&'a [u8], TermError> {
506 arena.unpack_bin(self)
507 }
508
509 #[inline]
511 pub fn unpack_atom<'a>(
512 &'a self,
513 arena: &'a Arena,
514 allowed_names: &[&str],
515 ) -> Result<&'a str, TermError> {
516 arena.unpack_atom(self, allowed_names)
517 }
518
519 #[inline]
521 pub fn unpack_var<'a>(
522 &'a self,
523 arena: &'a Arena,
524 allowed_names: &[&str],
525 ) -> Result<&'a str, TermError> {
526 arena.unpack_var(self, allowed_names)
527 }
528
529 #[inline]
533 pub fn unpack_func_any<'a>(
534 &'a self,
535 arena: &'a Arena,
536 allowed_names: &[&str],
537 ) -> Result<(&'a Term, &'a [Term]), TermError> {
538 arena.unpack_func_any(self, allowed_names)
539 }
540
541 #[inline]
545 pub fn unpack_func<'a, const ARITY: usize>(
546 &'a self,
547 arena: &'a Arena,
548 allowed_names: &[&str],
549 ) -> Result<(&'a Term, [Term; ARITY]), TermError> {
550 arena.unpack_func(self, allowed_names)
551 }
552
553 #[inline]
556 pub fn unpack_list<'a>(
557 &'a self,
558 arena: &'a Arena,
559 ) -> Result<(&'a [Term], &'a Term), TermError> {
560 arena.unpack_list(self)
561 }
562
563 #[inline]
566 pub fn unpack_tuple_any<'a>(&'a self, arena: &'a Arena) -> Result<&'a [Term], TermError> {
567 arena.unpack_tuple_any(self)
568 }
569
570 #[inline]
573 pub fn unpack_tuple<const ARITY: usize>(
574 &self,
575 arena: &Arena,
576 ) -> Result<[Term; ARITY], TermError> {
577 arena.unpack_tuple(self)
578 }
579
580 #[inline]
585 pub fn view<'a>(&'a self, arena: &'a Arena) -> Result<View<'a>, TermError> {
586 match &self.0 {
587 Handle::Int(i) => Ok(View::Int(*i)),
588 Handle::Real(f) => Ok(View::Real(*f)),
589 Handle::Date(d) => Ok(View::Date(*d)),
590 Handle::Var(vs) => {
591 let s_bytes = &vs.bytes[..vs.len as usize];
592 let s = unsafe { core::str::from_utf8_unchecked(s_bytes) };
593 Ok(View::Var(s))
594 }
595 Handle::VarRef(vr) => Ok(View::Var(unsafe {
596 core::str::from_utf8_unchecked(
597 arena
598 .byte_slice(vr)
599 .map_err(|_| TermError::InvalidTerm(*self))?,
600 )
601 })),
602 Handle::Atom(a) => {
603 let s_bytes = &a.bytes[..a.len as usize];
604 let s = unsafe { core::str::from_utf8_unchecked(s_bytes) };
605 Ok(View::Atom(s))
606 }
607 Handle::AtomRef(ar) => Ok(View::Atom(unsafe {
608 core::str::from_utf8_unchecked(
609 arena
610 .byte_slice(ar)
611 .map_err(|_| TermError::InvalidTerm(*self))?,
612 )
613 })),
614 Handle::Str(ss) => {
615 let s_bytes = &ss.bytes[..ss.len as usize];
616 let s = unsafe { core::str::from_utf8_unchecked(s_bytes) };
617 Ok(View::Str(s))
618 }
619 Handle::StrRef(sr) => Ok(View::Str(unsafe {
620 core::str::from_utf8_unchecked(
621 arena
622 .byte_slice(sr)
623 .map_err(|_| TermError::InvalidTerm(*self))?,
624 )
625 })),
626 Handle::Bin(bs) => {
627 let b = &bs.bytes[..bs.len as usize];
628 Ok(View::Bin(b))
629 }
630 Handle::BinRef(br) => Ok(View::Bin(
631 arena
632 .byte_slice(br)
633 .map_err(|_| TermError::InvalidTerm(*self))?,
634 )),
635 Handle::FuncRef(fr) => {
636 let slice = arena
637 .term_slice(fr)
638 .map_err(|_| TermError::InvalidTerm(*self))?;
639 let functor = &slice[0];
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]
778 pub fn name<'a>(&'a self, arena: &'a Arena) -> Result<&'a str, TermError> {
779 arena.name(self)
780 }
781
782 #[inline]
784 pub fn atom_name<'a>(&'a self, arena: &'a Arena) -> Result<&'a str, TermError> {
785 arena.unpack_atom(self, &[])
786 }
787
788 #[inline]
790 pub fn var_name<'a>(&'a self, arena: &'a Arena) -> Result<&'a str, TermError> {
791 arena.unpack_var(self, &[])
792 }
793
794 #[inline]
796 pub fn func_name<'a>(&'a self, arena: &'a Arena) -> Result<&'a str, TermError> {
797 let (functor, _) = arena.unpack_func_any(self, &[])?;
798 arena.atom_name(functor)
799 }
800
801 #[inline]
803 pub fn kind_name(&self) -> &'static str {
804 match &self.0 {
805 Handle::Int(_) => "int",
806 Handle::Real(_) => "real",
807 Handle::Date(_) => "date",
808 Handle::Var(_) | Handle::VarRef(_) => "var",
809 Handle::Atom(_) | Handle::AtomRef(_) => "atom",
810 Handle::Str(_) | Handle::StrRef(_) => "str",
811 Handle::Bin(_) | Handle::BinRef(_) => "bin",
812 Handle::FuncRef(_) => "func",
813 Handle::ListRef(_) | Handle::ListCRef(_) => "list",
814 Handle::TupleRef(_) => "tuple",
815 }
816 }
817}
818
819impl fmt::Debug for Term {
836 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
837 match &self.0 {
838 Handle::Int(i) => f.debug_tuple("Int").field(i).finish(),
839 Handle::Real(r) => f.debug_tuple("Real").field(r).finish(),
840 Handle::Date(d) => f.debug_tuple("Date").field(d).finish(),
841 Handle::Var(v) => {
842 let name =
843 core::str::from_utf8(&v.bytes[..v.len as usize]).unwrap_or("<invalid utf8>");
844 f.debug_struct("Var").field("name", &name).finish()
845 }
846 Handle::VarRef(v) => f
847 .debug_struct("VarRef")
848 .field("epoch_id", &v.epoch_id)
849 .field("index", &v.index)
850 .field("len", &v.len)
851 .finish(),
852 Handle::Atom(a) => {
853 let name =
854 core::str::from_utf8(&a.bytes[..a.len as usize]).unwrap_or("<invalid utf8>");
855 f.debug_struct("Atom").field("name", &name).finish()
856 }
857 Handle::AtomRef(v) => f
858 .debug_struct("AtomRef")
859 .field("epoch_id", &v.epoch_id)
860 .field("index", &v.index)
861 .field("len", &v.len)
862 .finish(),
863 Handle::Str(s) => {
864 let value =
865 core::str::from_utf8(&s.bytes[..s.len as usize]).unwrap_or("<invalid utf8>");
866 f.debug_struct("Str").field("value", &value).finish()
867 }
868 Handle::StrRef(v) => f
869 .debug_struct("StrRef")
870 .field("epoch_id", &v.epoch_id)
871 .field("index", &v.index)
872 .field("len", &v.len)
873 .finish(),
874 Handle::Bin(b) => {
875 let slice = &b.bytes[..b.len as usize];
876 f.debug_struct("Bin").field("bytes", &slice).finish()
877 }
878 Handle::BinRef(v) => f
879 .debug_struct("BinRef")
880 .field("epoch_id", &v.epoch_id)
881 .field("index", &v.index)
882 .field("len", &v.len)
883 .finish(),
884 Handle::FuncRef(v) => f
885 .debug_struct("Func")
886 .field("epoch_id", &v.epoch_id)
887 .field("index", &v.index)
888 .field("len", &v.len)
889 .finish(),
890 Handle::ListRef(v) => f
891 .debug_struct("List")
892 .field("epoch_id", &v.epoch_id)
893 .field("index", &v.index)
894 .field("len", &v.len)
895 .finish(),
896 Handle::ListCRef(v) => f
897 .debug_struct("ListC")
898 .field("epoch_id", &v.epoch_id)
899 .field("index", &v.index)
900 .field("len", &v.len)
901 .finish(),
902 Handle::TupleRef(v) => f
903 .debug_struct("Tuple")
904 .field("epoch_id", &v.epoch_id)
905 .field("index", &v.index)
906 .field("len", &v.len)
907 .finish(),
908 }
909 }
910}
911
912impl fmt::Debug for View<'_> {
913 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
914 match &self {
915 View::Int(i) => f.debug_tuple("Int").field(&i).finish(),
916 View::Real(r) => f.debug_tuple("Real").field(&r).finish(),
917 View::Date(d) => f.debug_tuple("Date").field(&d).finish(),
918 View::Var(v) => f.debug_tuple("Var").field(&v).finish(),
919 View::Atom(a) => f.debug_tuple("Atom").field(&a).finish(),
920 View::Str(s) => f.debug_tuple("Str").field(&s).finish(),
921 View::Bin(b) => f.debug_tuple("Bin").field(&b).finish(),
922 View::Func(a, fr, ts) => f
923 .debug_tuple("Func")
924 .field(&a.arena_id)
925 .field(&fr)
926 .field(&ts.iter().map(|t| t.view(a)).collect::<Vec<_>>())
927 .finish(),
928 View::List(a, ts, tail) => f
929 .debug_tuple("List")
930 .field(&a.arena_id)
931 .field(&ts.iter().map(|t| t.view(a)).collect::<Vec<_>>())
932 .field(&tail.view(a))
933 .finish(),
934 View::Tuple(a, ts) => f
935 .debug_tuple("Tuple")
936 .field(&a.arena_id)
937 .field(&ts.iter().map(|t| t.view(a)).collect::<Vec<_>>())
938 .finish(),
939 }
940 }
941}
942
943#[derive(Clone, Copy)]
953pub enum View<'a> {
954 Int(i64),
956 Real(f64),
958 Date(i64),
960 Var(&'a str),
962 Atom(&'a str),
964 Str(&'a str),
966 Bin(&'a [u8]),
968 Func(&'a Arena, &'a Term, &'a [Term]),
973 List(&'a Arena, &'a [Term], &'a Term),
978 Tuple(&'a Arena, &'a [Term]),
982}
983
984#[derive(Default, Clone, Debug)]
1027pub struct Arena {
1028 arena_id: ArenaID,
1030
1031 current_epoch: usize,
1034
1035 epoch_ids: [EpochID; MAX_LIVE_EPOCHS],
1041
1042 bytes: Vec<u8>,
1045
1046 byte_start_by_epoch: [usize; MAX_LIVE_EPOCHS],
1049
1050 terms: Vec<Term>,
1056
1057 term_start_by_epoch: [usize; MAX_LIVE_EPOCHS],
1060}
1061
1062pub const MAX_LIVE_EPOCHS: usize = 8;
1063
1064#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
1065pub struct EpochID(u32); #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
1068struct ArenaID(u32); #[derive(Debug, Clone, Copy)]
1071pub struct ArenaStats {
1072 pub current_epoch: EpochID,
1073 pub bytes_len: usize,
1074 pub terms_len: usize,
1075}
1076
1077impl Arena {
1078 pub fn with_capacity(bytes_capacity: usize, terms_capacity: usize) -> Self {
1080 let mut epoch_ids = [EpochID(0); MAX_LIVE_EPOCHS];
1081 epoch_ids[0] = EpochID(rand::random());
1082
1083 Self {
1084 arena_id: ArenaID(rand::random()),
1085 current_epoch: 0,
1086 epoch_ids,
1087 bytes: Vec::with_capacity(bytes_capacity),
1088 byte_start_by_epoch: [0; MAX_LIVE_EPOCHS],
1089 terms: Vec::with_capacity(terms_capacity),
1090 term_start_by_epoch: [0; MAX_LIVE_EPOCHS],
1091 }
1092 }
1093
1094 pub fn new() -> Self {
1096 Self::with_capacity(4096, 1024)
1097 }
1098
1099 pub fn stats(&self) -> ArenaStats {
1101 ArenaStats {
1102 current_epoch: self.epoch_ids[self.current_epoch],
1103 bytes_len: self.bytes.len(),
1104 terms_len: self.terms.len(),
1105 }
1106 }
1107
1108 pub fn current_epoch(&self) -> EpochID {
1110 self.epoch_ids[self.current_epoch]
1111 }
1112
1113 pub fn begin_epoch(&mut self) -> Result<EpochID, TermError> {
1115 let new_epoch = self.current_epoch + 1;
1116 if new_epoch >= MAX_LIVE_EPOCHS {
1117 return Err(TermError::LiveEpochsExceeded);
1118 }
1119 self.epoch_ids[new_epoch] = EpochID(rand::random());
1120 self.byte_start_by_epoch[new_epoch] = self.bytes.len();
1121 self.term_start_by_epoch[new_epoch] = self.terms.len();
1122 self.current_epoch = new_epoch;
1123 Ok(self.epoch_ids[new_epoch])
1124 }
1125
1126 pub fn clear(&mut self) -> Result<(), TermError> {
1129 self.truncate(self.epoch_ids[0])
1130 }
1131
1132 pub fn truncate_current(&mut self) -> Result<(), TermError> {
1135 self.truncate(self.epoch_ids[self.current_epoch])
1136 }
1137
1138 pub fn truncate(&mut self, epoch_id: EpochID) -> Result<(), TermError> {
1141 let epoch = self
1142 .epoch_index(epoch_id)
1143 .map_err(|_| TermError::InvalidEpoch(epoch_id))?;
1144 self.bytes.truncate(self.byte_start_by_epoch[epoch]);
1145 self.terms.truncate(self.term_start_by_epoch[epoch]);
1146 self.current_epoch = epoch;
1147 Ok(())
1148 }
1149
1150 #[inline]
1152 fn epoch_index(&self, epoch_id: EpochID) -> Result<usize, InternalTermError> {
1153 let Some(epoch) = self.epoch_ids[..=self.current_epoch]
1154 .iter()
1155 .position(|&id| id == epoch_id)
1156 else {
1157 return Err(InternalTermError::InvalidEpoch(epoch_id));
1158 };
1159 Ok(epoch)
1160 }
1161
1162 #[inline]
1165 fn verify_byte_slice(&self, slice: &Slice) -> Result<(), InternalTermError> {
1166 let epoch = self.epoch_index(slice.epoch_id)?;
1167 let epoch_start = self.byte_start_by_epoch[epoch];
1168 let epoch_end = if epoch == self.current_epoch {
1169 self.bytes.len()
1170 } else {
1171 self.byte_start_by_epoch[epoch + 1]
1172 };
1173 if (slice.index as usize) < epoch_start
1174 || (slice.index as usize) + (slice.len as usize) > epoch_end
1175 {
1176 return Err(InternalTermError::InvalidSlice(*slice));
1177 }
1178 Ok(())
1179 }
1180
1181 #[inline]
1184 fn verify_term_slice(&self, slice: &Slice) -> Result<(), InternalTermError> {
1185 let epoch = self.epoch_index(slice.epoch_id)?;
1186 let epoch_start = self.term_start_by_epoch[epoch];
1187 let epoch_end = if epoch == self.current_epoch {
1188 self.terms.len()
1189 } else {
1190 self.term_start_by_epoch[epoch + 1]
1191 };
1192 if (slice.index as usize) < epoch_start
1193 || (slice.index as usize) + (slice.len as usize) > epoch_end
1194 {
1195 return Err(InternalTermError::InvalidSlice(*slice));
1196 }
1197 Ok(())
1198 }
1199
1200 #[inline]
1205 pub fn view<'a>(&'a self, term: &'a Term) -> Result<View<'a>, TermError> {
1206 term.view(self)
1207 }
1208
1209 #[inline]
1211 pub fn term<'a, T: IntoTerm>(&'a mut self, value: T) -> Term {
1212 value.into_term(self)
1213 }
1214
1215 #[inline]
1219 pub fn int(&mut self, i: impl Into<i64>) -> Term {
1220 Term::int(i)
1221 }
1222
1223 #[inline]
1226 pub fn real(&mut self, r: impl Into<f64>) -> Term {
1227 Term::real(r)
1228 }
1229
1230 #[inline]
1233 pub fn date(&mut self, ms: impl Into<i64>) -> Term {
1234 Term::date(ms)
1235 }
1236
1237 #[inline]
1242 pub fn atom(&mut self, name: impl AsRef<str>) -> Term {
1243 Term::atom(self, name)
1244 }
1245
1246 #[inline]
1251 pub fn var(&mut self, name: impl AsRef<str>) -> Term {
1252 Term::var(self, name)
1253 }
1254
1255 #[inline]
1260 pub fn str(&mut self, s: impl AsRef<str>) -> Term {
1261 Term::str(self, s)
1262 }
1263
1264 #[inline]
1268 pub fn bin(&mut self, bytes: impl AsRef<[u8]>) -> Term {
1269 Term::bin(self, bytes)
1270 }
1271
1272 #[inline]
1278 pub fn func(
1279 &mut self,
1280 functor: impl AsRef<str>,
1281 args: impl IntoIterator<Item = impl IntoTerm>,
1282 ) -> Term {
1283 Term::func(self, functor, args)
1284 }
1285
1286 #[inline]
1291 pub fn funcv(
1292 &mut self,
1293 terms: impl IntoIterator<Item = impl IntoTerm>,
1294 ) -> Result<Term, TermError> {
1295 Term::funcv(self, terms)
1296 }
1297
1298 #[inline]
1301 pub fn list(&mut self, terms: impl IntoIterator<Item = impl IntoTerm>) -> Term {
1302 Term::list(self, terms)
1303 }
1304
1305 #[inline]
1308 pub fn listc(
1309 &mut self,
1310 terms: impl IntoIterator<Item = impl IntoTerm>,
1311 tail: impl IntoTerm,
1312 ) -> Term {
1313 Term::listc(self, terms, tail)
1314 }
1315
1316 #[inline]
1319 pub fn tuple(&mut self, terms: impl IntoIterator<Item = impl IntoTerm>) -> Term {
1320 Term::tuple(self, terms)
1321 }
1322
1323 pub const UNIT: Term = Term::UNIT;
1327
1328 pub const NIL: Term = Term::NIL;
1332
1333 #[inline]
1337 pub fn name<'a>(&'a self, term: &'a Term) -> Result<&'a str, TermError> {
1338 match self.view(term)? {
1339 View::Var(name) | View::Atom(name) => Ok(name),
1340 View::Func(ar, functor, _) => Ok(functor.atom_name(ar)?),
1341 _ => Err(TermError::UnexpectedKind {
1342 expected: "var, atom, func",
1343 found: term.kind_name(),
1344 }),
1345 }
1346 }
1347
1348 #[inline]
1350 pub fn atom_name<'a>(&'a self, term: &'a Term) -> Result<&'a str, TermError> {
1351 self.unpack_atom(term, &[])
1352 }
1353
1354 #[inline]
1356 pub fn var_name<'a>(&'a self, term: &'a Term) -> Result<&'a str, TermError> {
1357 self.unpack_var(term, &[])
1358 }
1359
1360 #[inline]
1362 pub fn func_name<'a>(&'a self, term: &'a Term) -> Result<&'a str, TermError> {
1363 let (functor, _) = self.unpack_func_any(term, &[])?;
1364 self.atom_name(functor)
1365 }
1366
1367 #[inline]
1369 pub fn unpack_int(&self, term: &Term) -> Result<i64, TermError> {
1370 match self.view(term)? {
1371 View::Int(v) => Ok(v),
1372 _ => Err(TermError::UnexpectedKind {
1373 expected: "int",
1374 found: term.kind_name(),
1375 }),
1376 }
1377 }
1378
1379 #[inline]
1381 pub fn unpack_real(&self, term: &Term) -> Result<f64, TermError> {
1382 match self.view(term)? {
1383 View::Real(v) => Ok(v),
1384 _ => Err(TermError::UnexpectedKind {
1385 expected: "real",
1386 found: term.kind_name(),
1387 }),
1388 }
1389 }
1390
1391 #[inline]
1393 pub fn unpack_date(&self, term: &Term) -> Result<i64, TermError> {
1394 match self.view(term)? {
1395 View::Date(v) => Ok(v),
1396 _ => Err(TermError::UnexpectedKind {
1397 expected: "date",
1398 found: term.kind_name(),
1399 }),
1400 }
1401 }
1402
1403 #[inline]
1405 pub fn unpack_str<'a>(&'a self, term: &'a Term) -> Result<&'a str, TermError> {
1406 match self.view(term)? {
1407 View::Str(v) => Ok(v),
1408 _ => Err(TermError::UnexpectedKind {
1409 expected: "str",
1410 found: term.kind_name(),
1411 }),
1412 }
1413 }
1414
1415 #[inline]
1417 pub fn unpack_bin<'a>(&'a self, term: &'a Term) -> Result<&'a [u8], TermError> {
1418 match self.view(term)? {
1419 View::Bin(v) => Ok(v),
1420 _ => Err(TermError::UnexpectedKind {
1421 expected: "bin",
1422 found: term.kind_name(),
1423 }),
1424 }
1425 }
1426
1427 #[inline]
1429 pub fn unpack_atom<'a>(
1430 &'a self,
1431 term: &'a Term,
1432 allowed_names: &[&str],
1433 ) -> Result<&'a str, TermError> {
1434 match self.view(term)? {
1435 View::Atom(name) => {
1436 if !allowed_names.is_empty() && !allowed_names.contains(&name) {
1437 return Err(TermError::UnexpectedName(*term));
1438 }
1439 Ok(name)
1440 }
1441 _ => Err(TermError::UnexpectedKind {
1442 expected: "atom",
1443 found: term.kind_name(),
1444 }),
1445 }
1446 }
1447
1448 #[inline]
1450 pub fn unpack_var<'a>(
1451 &'a self,
1452 term: &'a Term,
1453 allowed_names: &[&str],
1454 ) -> Result<&'a str, TermError> {
1455 match self.view(term)? {
1456 View::Var(name) => {
1457 if !allowed_names.is_empty() && !allowed_names.contains(&name) {
1458 return Err(TermError::UnexpectedName(*term));
1459 }
1460 Ok(name)
1461 }
1462 _ => Err(TermError::UnexpectedKind {
1463 expected: "var",
1464 found: term.kind_name(),
1465 }),
1466 }
1467 }
1468
1469 #[inline]
1473 pub fn unpack_func_any<'a>(
1474 &'a self,
1475 term: &'a Term,
1476 allowed_names: &[&str],
1477 ) -> Result<(&'a Term, &'a [Term]), TermError> {
1478 match self.view(term)? {
1479 View::Atom(name) => {
1480 if !allowed_names.is_empty() && !allowed_names.contains(&name) {
1481 return Err(TermError::UnexpectedName(*term));
1482 }
1483 Ok((term, &[] as &[Term]))
1484 }
1485 View::Func(_, functor, args) => {
1486 if args.is_empty() {
1487 return Err(TermError::InvalidTerm(*term));
1488 }
1489 if !allowed_names.is_empty() {
1490 let name = self.atom_name(functor)?;
1491 if !allowed_names.contains(&name) {
1492 return Err(TermError::UnexpectedName(*term));
1493 }
1494 }
1495 Ok((functor, args))
1496 }
1497 _ => Err(TermError::UnexpectedKind {
1498 expected: "func",
1499 found: term.kind_name(),
1500 }),
1501 }
1502 }
1503
1504 #[inline]
1508 pub fn unpack_func<'a, const ARITY: usize>(
1509 &'a self,
1510 term: &'a Term,
1511 allowed_names: &[&str],
1512 ) -> Result<(&'a Term, [Term; ARITY]), TermError> {
1513 let (functor, args) = self.unpack_func_any(term, allowed_names)?;
1514 if args.len() != ARITY {
1515 return Err(TermError::UnexpectedArity {
1516 expected: ARITY,
1517 found: args.len(),
1518 });
1519 }
1520 let arr: [_; ARITY] = args.try_into().unwrap();
1521 return Ok((functor, arr));
1522 }
1523
1524 #[inline]
1527 pub fn unpack_list<'a>(&'a self, term: &'a Term) -> Result<(&'a [Term], &'a Term), TermError> {
1528 match self.view(term)? {
1529 View::Atom(_) if term == &Term::NIL => Ok((&[], &Term::NIL)),
1530 View::List(_, terms, tail) => Ok((terms, tail)),
1531 _ => Err(TermError::UnexpectedKind {
1532 expected: "list",
1533 found: term.kind_name(),
1534 }),
1535 }
1536 }
1537
1538 #[inline]
1541 pub fn unpack_tuple_any<'a>(&'a self, term: &'a Term) -> Result<&'a [Term], TermError> {
1542 match self.view(term)? {
1543 View::Atom(_) if *term == Term::UNIT => Ok(&[]),
1544 View::Tuple(_, terms) => Ok(terms),
1545 _ => Err(TermError::UnexpectedKind {
1546 expected: "tuple",
1547 found: term.kind_name(),
1548 }),
1549 }
1550 }
1551
1552 #[inline]
1555 pub fn unpack_tuple<const ARITY: usize>(
1556 &self,
1557 term: &Term,
1558 ) -> Result<[Term; ARITY], TermError> {
1559 let terms = self.unpack_tuple_any(term)?;
1560 if terms.len() != ARITY {
1561 return Err(TermError::UnexpectedArity {
1562 expected: ARITY,
1563 found: terms.len(),
1564 });
1565 }
1566 let arr: [_; ARITY] = terms.try_into().unwrap();
1567 return Ok(arr);
1568 }
1569
1570 #[inline]
1573 fn intern_str(&mut self, s: &str) -> Slice {
1574 let index = self.bytes.len();
1575 self.bytes.extend_from_slice(s.as_bytes());
1576 let len = s.len();
1577 Slice {
1578 epoch_id: self.epoch_ids[self.current_epoch],
1579 index: index as u32,
1580 len: len as u32,
1581 }
1582 }
1583
1584 #[inline]
1586 fn intern_bytes(&mut self, bytes: &[u8]) -> Slice {
1587 let index = self.bytes.len();
1588 self.bytes.extend_from_slice(bytes);
1589 let len = bytes.len();
1590 Slice {
1591 epoch_id: self.epoch_ids[self.current_epoch],
1592 index: index as u32,
1593 len: len as u32,
1594 }
1595 }
1596
1597 #[inline]
1599 fn intern_func(
1600 &mut self,
1601 functor: Term,
1602 args: impl IntoIterator<Item = impl IntoTerm>,
1603 ) -> Slice {
1604 let index = self.terms.len();
1605 self.terms.push(functor);
1606 for x in args {
1607 let t = x.into_term(self);
1608 self.terms.push(t);
1609 }
1610 let len = self.terms.len() - index;
1611 Slice {
1612 epoch_id: self.epoch_ids[self.current_epoch],
1613 index: index as u32,
1614 len: len as u32,
1615 }
1616 }
1617
1618 #[inline]
1620 fn intern_seq(&mut self, terms: impl IntoIterator<Item = impl IntoTerm>) -> Slice {
1621 let index = self.terms.len();
1622 for x in terms {
1623 let t = x.into_term(self);
1624 self.terms.push(t);
1625 }
1626 let len = self.terms.len() - index;
1627 Slice {
1628 epoch_id: self.epoch_ids[self.current_epoch],
1629 index: index as u32,
1630 len: len as u32,
1631 }
1632 }
1633
1634 #[inline]
1636 fn intern_seq_plus_one(
1637 &mut self,
1638 terms: impl IntoIterator<Item = impl IntoTerm>,
1639 tail: impl IntoTerm,
1640 ) -> Slice {
1641 let index = self.terms.len();
1642 for x in terms {
1643 let t = x.into_term(self);
1644 self.terms.push(t);
1645 }
1646 let t = tail.into_term(self);
1647 self.terms.push(t);
1648 let len = self.terms.len() - index;
1649 Slice {
1650 epoch_id: self.epoch_ids[self.current_epoch],
1651 index: index as u32,
1652 len: len as u32,
1653 }
1654 }
1655
1656 #[inline]
1660 fn byte_slice<'a>(&'a self, slice: &Slice) -> Result<&'a [u8], InternalTermError> {
1661 self.verify_byte_slice(slice)?;
1662 Ok(&self.bytes[(slice.index as usize)..((slice.index + slice.len) as usize)])
1663 }
1664
1665 #[inline]
1667 fn term_slice<'a>(&'a self, slice: &Slice) -> Result<&'a [Term], InternalTermError> {
1668 self.verify_term_slice(slice)?;
1669 Ok(&self.terms[(slice.index as usize)..((slice.index + slice.len) as usize)])
1670 }
1671}
1672
1673#[derive(Debug, Clone)]
1675pub enum TermError {
1676 InvalidTerm(Term),
1677 LiveEpochsExceeded,
1678 InvalidEpoch(EpochID),
1679 MissingFunctor,
1680 InvalidFunctor(Term),
1681 UnexpectedKind {
1682 expected: &'static str,
1683 found: &'static str,
1684 },
1685 UnexpectedArity {
1686 expected: usize,
1687 found: usize,
1688 },
1689 UnexpectedName(Term),
1690}
1691
1692impl fmt::Display for TermError {
1693 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1694 match self {
1695 TermError::InvalidTerm(term) => {
1696 write!(f, "Invalid term {:?}", term)
1697 }
1698 TermError::LiveEpochsExceeded => {
1699 write!(f, "Epoch overflow")
1700 }
1701 TermError::InvalidEpoch(epoch_id) => {
1702 write!(f, "Invalid epoch {:?}", epoch_id)
1703 }
1704 TermError::MissingFunctor => {
1705 write!(f, "Missing functor")
1706 }
1707 TermError::InvalidFunctor(term) => {
1708 write!(f, "Invalid functor {:?}", term)
1709 }
1710 TermError::UnexpectedKind { expected, found } => {
1711 write!(f, "Type mismatch: expected {}, found {}", expected, found)
1712 }
1713 TermError::UnexpectedArity { expected, found } => {
1714 write!(f, "Arity mismatch: expected {}, found {}", expected, found)
1715 }
1716 TermError::UnexpectedName(term) => {
1717 write!(f, "Unexpected name in {:?}", term)
1718 }
1719 }
1720 }
1721}
1722
1723#[derive(Debug, Clone)]
1725enum InternalTermError {
1726 InvalidEpoch(EpochID),
1727 InvalidSlice(Slice),
1728}
1729
1730impl fmt::Display for InternalTermError {
1731 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1732 match self {
1733 InternalTermError::InvalidEpoch(epoch_id) => {
1734 write!(f, "Invalid epoch {:?}", epoch_id)
1735 }
1736 InternalTermError::InvalidSlice(slice) => {
1737 write!(f, "Invalid slice {:?}", slice)
1738 }
1739 }
1740 }
1741}
1742
1743impl std::error::Error for TermError {}
1744
1745impl<'a> PartialEq for View<'a> {
1746 fn eq(&self, other: &Self) -> bool {
1747 let order_a = kind_order(self);
1748 let order_b = kind_order(other);
1749 if order_a != order_b {
1750 return false;
1751 }
1752 match (self, other) {
1753 (
1755 View::Int(_) | View::Real(_) | View::Date(_),
1756 View::Int(_) | View::Real(_) | View::Date(_),
1757 ) => {
1758 let a = numeric_value(self);
1759 let b = numeric_value(other);
1760 a == b
1761 }
1762 (View::Var(a), View::Var(b)) => a == b,
1764 (View::Atom(a), View::Atom(b)) => a == b,
1766 (View::Str(a), View::Str(b)) => a == b,
1768 (View::Bin(a), View::Bin(b)) => a == b,
1770 (View::Func(arena_a, functor_a, args_a), View::Func(arena_b, functor_b, args_b)) => {
1772 if args_a.len() != args_b.len() {
1773 return false;
1774 }
1775 if functor_a != functor_b {
1776 return false;
1777 }
1778 args_a.iter().zip(args_b.iter()).all(|(a, b)| {
1779 a.view(arena_a).expect("arena mismatch")
1780 == b.view(arena_b).expect("arena mismatch")
1781 })
1782 }
1783 (View::List(arena_a, args_a, tail_a), View::List(arena_b, args_b, tail_b)) => {
1784 if args_a.len() != args_b.len() {
1785 return false;
1786 }
1787 args_a.iter().zip(args_b.iter()).all(|(a, b)| {
1788 a.view(arena_a).expect("arena mismatch")
1789 == b.view(arena_b).expect("arena mismatch")
1790 }) && tail_a.view(arena_a).expect("arena mismatch")
1791 == tail_b.view(arena_b).expect("arena mismatch")
1792 }
1793 (View::Tuple(arena_a, args_a), View::Tuple(arena_b, args_b)) => {
1794 if args_a.len() != args_b.len() {
1795 return false;
1796 }
1797 args_a.iter().zip(args_b.iter()).all(|(a, b)| {
1798 a.view(arena_a).expect("arena mismatch")
1799 == b.view(arena_b).expect("arena mismatch")
1800 })
1801 }
1802 _ => unreachable!(),
1803 }
1804 }
1805}
1806
1807impl<'a> Eq for View<'a> {}
1808
1809impl core::cmp::PartialOrd for View<'_> {
1810 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1811 Some(self.cmp(other))
1812 }
1813}
1814
1815impl core::cmp::Ord for View<'_> {
1816 fn cmp(&self, other: &Self) -> Ordering {
1817 let order_a = kind_order(self);
1818 let order_b = kind_order(other);
1819 if order_a != order_b {
1820 return order_a.cmp(&order_b);
1821 }
1822 match (self, other) {
1823 (
1825 View::Int(_) | View::Real(_) | View::Date(_),
1826 View::Int(_) | View::Real(_) | View::Date(_),
1827 ) => {
1828 let a = numeric_value(self);
1829 let b = numeric_value(other);
1830 a.total_cmp(&b)
1831 }
1832 (View::Var(a), View::Var(b)) => a.cmp(b),
1834 (View::Atom(a), View::Atom(b)) => a.cmp(b),
1836 (View::Str(a), View::Str(b)) => a.cmp(b),
1838 (View::Bin(a), View::Bin(b)) => a.cmp(b),
1840 (View::Func(arena_a, functor_a, args_a), View::Func(arena_b, functor_b, args_b)) => {
1842 let ord = args_a.len().cmp(&args_b.len());
1843 if ord != Ordering::Equal {
1844 return ord;
1845 }
1846 let ord = functor_a
1847 .view(arena_a)
1848 .expect("arena mismatch")
1849 .cmp(&functor_b.view(arena_b).expect("arena mismatch"));
1850 if ord != Ordering::Equal {
1851 return ord;
1852 }
1853 for (arg_a, arg_b) in args_a.iter().zip(args_b.iter()).map(|(a, b)| {
1854 (
1855 a.view(arena_a).expect("arena mismatch"),
1856 b.view(arena_b).expect("arena mismatch"),
1857 )
1858 }) {
1859 let ord = arg_a.cmp(&arg_b);
1860 if ord != Ordering::Equal {
1861 return ord;
1862 }
1863 }
1864 Ordering::Equal
1865 }
1866 (View::List(arena_a, args_a, tail_a), View::List(arena_b, args_b, tail_b)) => {
1867 let ord = args_a.len().cmp(&args_b.len());
1868 if ord != Ordering::Equal {
1869 return ord;
1870 }
1871 for (arg_a, arg_b) in args_a.iter().zip(args_b.iter()).map(|(a, b)| {
1872 (
1873 a.view(arena_a).expect("arena mismatch"),
1874 b.view(arena_b).expect("arena mismatch"),
1875 )
1876 }) {
1877 let ord = arg_a.cmp(&arg_b);
1878 if ord != Ordering::Equal {
1879 return ord;
1880 }
1881 }
1882 tail_a
1883 .view(arena_a)
1884 .expect("arena mismatch")
1885 .cmp(&tail_b.view(arena_b).expect("arena mismatch"))
1886 }
1887 (View::Tuple(arena_a, args_a), View::Tuple(arena_b, args_b)) => {
1888 let ord = args_a.len().cmp(&args_b.len());
1889 if ord != Ordering::Equal {
1890 return ord;
1891 }
1892 for (arg_a, arg_b) in args_a.iter().zip(args_b.iter()).map(|(a, b)| {
1893 (
1894 a.view(arena_a).expect("arena mismatch"),
1895 b.view(arena_b).expect("arena mismatch"),
1896 )
1897 }) {
1898 let ord = arg_a.cmp(&arg_b);
1899 if ord != Ordering::Equal {
1900 return ord;
1901 }
1902 }
1903 Ordering::Equal
1904 }
1905
1906 _ => unreachable!(),
1907 }
1908 }
1909}
1910
1911fn kind_order(t: &View) -> u8 {
1915 match t {
1916 View::Var(_) => 0,
1917 View::Int(_) => 1,
1918 View::Date(_) => 2,
1919 View::Real(_) => 3,
1920 View::Atom(_) => 4,
1921 View::Str(_) => 5,
1922 View::Func(_, _, _) => 6,
1923 View::Tuple(_, _) => 7,
1924 View::List(_, _, _) => 8,
1925 View::Bin(_) => 9,
1926 }
1927}
1928
1929fn numeric_value(t: &View) -> f64 {
1934 match t {
1935 View::Int(i) => *i as f64,
1936 View::Real(f) => *f,
1937 View::Date(d) => *d as f64,
1938 _ => unreachable!(),
1939 }
1940}
1941
1942#[macro_export]
1944macro_rules! list {
1945 ($($arg:expr),* $(,)?; $tail:expr => $arena:expr) => {
1947 $crate::list!($($arg),* ; $tail)($arena)
1948 };
1949 ($($arg:expr),* $(,)? => $arena:expr) => {
1951 $crate::list!($($arg),*)($arena)
1952 };
1953 ($($arg:expr),* $(,)?; $tail:expr) => { (|__arena: &mut $crate::Arena| {
1955 let __args: &[$crate::Term] = &[$($arg.into_term(__arena)),*];
1956 let __tail: Term = $tail.into_term(__arena);
1957 __arena.listc(__args, __tail)
1958 })};
1959 ($($arg:expr),* $(,)?) => { (|__arena: &mut $crate::Arena| {
1961 let __args: &[$crate::Term] = &[$($arg.into_term(__arena)),*];
1962 __arena.list(__args)
1963 })};
1964}
1965
1966#[macro_export]
1967macro_rules! tuple {
1968 ($($arg:expr),* $(,)? => $arena:expr) => {
1970 $crate::tuple!($($arg),*)($arena)
1971 };
1972 ($($arg:expr),* $(,)?) => { (|__arena: &mut $crate::Arena| {
1974 let __args: &[$crate::Term] = &[$($arg.into_term(__arena)),*];
1975 __arena.tuple(__args)
1976 })};
1977}
1978
1979#[macro_export]
1980macro_rules! func {
1981 ($functor:expr; $($arg:expr),+ $(,)? => $arena:expr) => {
1983 $crate::func!($functor; $($arg),+)($arena)
1984 };
1985 ($functor:expr; $($arg:expr),+ $(,)?) => { (|__arena: &mut $crate::Arena| {
1987 let __args: &[$crate::Term] = &[$($arg.into_term(__arena)),+];
1988 __arena.func($functor, __args)
1989 })};
1990}
1991
1992#[macro_export]
1993macro_rules! atom {
1994 ($functor:expr => $arena:expr) => {
1996 $crate::atom!($functor)($arena)
1997 };
1998 ($functor:expr) => {
2000 (|__arena: &mut $crate::Arena| __arena.atom($functor))
2001 };
2002}
2003
2004#[macro_export]
2005macro_rules! var {
2006 ($name:expr => $arena:expr) => {
2008 $crate::var!($name)($arena)
2009 };
2010 ($name:expr) => {
2012 (|__arena: &mut $crate::Arena| __arena.var($name))
2013 };
2014}
2015
2016#[macro_export]
2017macro_rules! date {
2018 ($value:expr) => {
2019 $crate::Term::date($value)
2020 };
2021}
2022
2023#[macro_export]
2024macro_rules! unit {
2025 () => {
2026 $crate::Term::UNIT
2027 };
2028}
2029
2030#[macro_export]
2031macro_rules! nil {
2032 () => {
2033 $crate::Term::NIL
2034 };
2035}
2036
2037pub struct TermDisplay<'a> {
2057 term: &'a Term,
2059 arena: &'a Arena,
2061}
2062
2063impl Term {
2064 #[inline]
2072 pub fn display<'a>(&'a self, arena: &'a Arena) -> TermDisplay<'a> {
2073 TermDisplay { term: self, arena }
2074 }
2075}
2076
2077impl<'a> fmt::Display for TermDisplay<'a> {
2080 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2081 fn is_unquoted_atom(s: &str) -> bool {
2082 let mut chars = s.chars();
2083 match chars.next() {
2084 Some(c) if c.is_ascii_lowercase() => {}
2085 _ => return false,
2086 }
2087 chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
2088 }
2089
2090 fn write_atom(f: &mut fmt::Formatter<'_>, arena: &Arena, functor: &Term) -> fmt::Result {
2091 let s = arena.atom_name(functor).map_err(|_e| fmt::Error)?;
2092 write_atom_str(f, s)
2093 }
2094
2095 fn write_atom_str(f: &mut fmt::Formatter<'_>, s: &str) -> fmt::Result {
2096 if s.is_empty() || !is_unquoted_atom(s) {
2097 let escaped = s.replace('\'', "\\'");
2098 write!(f, "'{}'", escaped)
2099 } else {
2100 write!(f, "{}", s)
2101 }
2102 }
2103
2104 fn write_str_quoted(f: &mut fmt::Formatter<'_>, s: &str) -> fmt::Result {
2105 let mut out = String::new();
2106 out.push('"');
2107 for ch in s.chars() {
2108 match ch {
2109 '\\' => out.push_str("\\\\"),
2110 '"' => out.push_str("\\\""),
2111 '\n' => out.push_str("\\n"),
2112 '\r' => out.push_str("\\r"),
2113 '\t' => out.push_str("\\t"),
2114 c if c.is_control() => out.push_str(&format!("\\x{:02X}\\", c as u32)),
2115 c => out.push(c),
2116 }
2117 }
2118 out.push('"');
2119 f.write_str(&out)
2120 }
2121
2122 fn epoch_to_date_string(epoch_ms: i64, fmt: Option<&str>) -> String {
2123 use chrono::{DateTime, Utc};
2124
2125 let secs = epoch_ms.div_euclid(1000);
2126 let nsecs = (epoch_ms.rem_euclid(1000) * 1_000_000) as u32;
2127
2128 let dt_utc = DateTime::<Utc>::from_timestamp(secs, nsecs).unwrap();
2129
2130 String::from(match fmt {
2131 None => dt_utc.to_rfc3339(),
2132 Some(layout) => dt_utc.format(layout).to_string(),
2133 })
2134 }
2135
2136 fn write_args(f: &mut fmt::Formatter<'_>, arena: &Arena, args: &[Term]) -> fmt::Result {
2137 for (i, t) in args.iter().enumerate() {
2138 if i > 0 {
2139 f.write_str(", ")?;
2140 }
2141 write!(f, "{}", t.display(arena))?;
2142 }
2143 Ok(())
2144 }
2145
2146 match self.term.view(self.arena).map_err(|_e| fmt::Error)? {
2147 View::Int(i) => write!(f, "{i}"),
2148 View::Real(r) => {
2149 if r.fract() == 0.0 {
2150 write!(f, "{:.1}", r)
2151 } else {
2152 write!(f, "{}", r)
2153 }
2154 }
2155 View::Date(epoch) => write!(f, "date{{{}}}", epoch_to_date_string(epoch, None)),
2156 View::Str(s) => write_str_quoted(f, s),
2157 View::Bin(bytes) => {
2158 write!(f, "hex{{")?;
2159 for b in bytes {
2160 write!(f, "{:02X}", b)?;
2161 }
2162 write!(f, "}}")
2163 }
2164 View::Atom(a) => write_atom_str(f, a),
2165 View::Var(v) => write!(f, "{}", v),
2166
2167 View::Func(ar, functor, args) => {
2168 if args.is_empty() {
2169 return write!(f, "/* invalid Func */");
2170 }
2171 write_atom(f, ar, functor)?;
2172 write!(f, "(")?;
2173 write_args(f, ar, args)?;
2174 write!(f, ")")
2175 }
2176
2177 View::Tuple(ar, items) => {
2178 if items.is_empty() {
2179 write!(f, "()")
2180 } else {
2181 write!(f, "(")?;
2182 write_args(f, ar, items)?;
2183 write!(f, ")")
2184 }
2185 }
2186
2187 View::List(ar, items, tail) => {
2188 if items.is_empty() {
2189 write!(f, "[]")
2190 } else {
2191 write!(f, "[")?;
2192 write_args(f, ar, items)?;
2193 if *tail != Term::NIL {
2194 f.write_str(" | ")?;
2195 write!(f, "{}", tail.display(ar))?;
2196 }
2197 write!(f, "]")
2198 }
2199 }
2200 }
2201 }
2202}
2203
2204#[cfg(test)]
2205mod tests {
2206 use super::*;
2207 use std::fmt::Write;
2208
2209 #[test]
2210 fn term_size_is_16_bytes() {
2211 assert_eq!(core::mem::size_of::<Term>(), 16);
2212 }
2213
2214 #[test]
2215 fn option_term_size_is_16_bytes() {
2216 assert_eq!(core::mem::size_of::<Option<Term>>(), 16);
2217 }
2218
2219 #[test]
2220 fn view_size_is_40_bytes() {
2221 assert_eq!(core::mem::size_of::<View>(), 40);
2222 }
2223
2224 #[test]
2225 fn option_view_size_is_40_bytes() {
2226 assert_eq!(core::mem::size_of::<Option<View>>(), 40);
2227 }
2228
2229 #[test]
2230 fn small_atom_interning() {
2231 let mut arena = Arena::new();
2232 let a1 = Term::atom(&mut arena, "foo");
2233 let a2 = Term::atom(&mut arena, "foo");
2234 assert_eq!(a1, a2);
2235 if let Ok(View::Atom(name)) = a1.view(&arena) {
2236 assert_eq!(name, "foo");
2237 } else {
2238 panic!("wrong view");
2239 }
2240 }
2241
2242 #[test]
2243 fn compound_construction_and_formatting() {
2244 let mut arena = Arena::new();
2245 let a = Term::int(1);
2246 let b = Term::real(2.0);
2247 let c = Term::date(1000);
2248 let d = Term::atom(&mut arena, "hello");
2249 let e = Term::var(&mut arena, "Hello");
2250 let f = Term::str(&mut arena, "A str\ning. Longer string.");
2251 let g = list![d, e, f => &mut arena];
2252 let h = tuple!(f, f => &mut arena);
2253 let p = Term::func(&mut arena, "point", &[a, b, c, d, e, f, g, h]);
2254 let p = func![
2255 "foo";
2256 Term::NIL,
2257 Term::UNIT,
2258 p,
2259 p,
2260 list![],
2261 list![a, b; c],
2262 => &mut arena
2263 ];
2264 dbg!(&p);
2265 dbg!(p.view(&arena).unwrap());
2266 dbg!(arena.stats());
2267 assert!(p.is_func());
2268 if let Ok(View::Func(_, functor, args)) = p.view(&arena) {
2269 assert_eq!(functor.atom_name(&arena).unwrap(), "foo");
2270 assert_eq!(p.arity(), 6);
2271 assert_eq!(args.len(), 6);
2272 } else {
2273 panic!("unexpected view");
2274 }
2275
2276 let s = format!("{}", p.display(&arena));
2277 assert_eq!(
2278 s,
2279 "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}])"
2280 );
2281 }
2282
2283 #[test]
2284 fn view_construction() {
2285 let mut a1 = Arena::new();
2286 let x = a1.atom("Hello, hello, quite long long string, world! X");
2287 dbg!(a1.view(&x).unwrap());
2288 dbg!(a1.stats());
2289 let p = list![x, x => &mut a1];
2290 dbg!(p);
2291 let v = a1.view(&p).unwrap();
2292 dbg!(v);
2293 }
2294
2295 #[test]
2296 #[should_panic]
2297 fn arena_mismatch() {
2298 let a1 = Arena::new();
2299 let mut a2 = Arena::new();
2300 let y = a2.str("Hello, hello, quite long long string, world! Y");
2301 dbg!(a1.view(&y).unwrap());
2302 }
2303
2304 #[test]
2305 #[should_panic]
2306 fn stale_term_str() {
2307 let mut a = Arena::new();
2308 let x = a.str("Hello, hello, quite long long string, world! Y");
2309 dbg!(&a);
2310 a.truncate(a.current_epoch()).unwrap();
2311 dbg!(a.view(&x).unwrap());
2312 }
2313
2314 #[test]
2315 #[should_panic]
2316 fn stale_term_list() {
2317 let mut a = Arena::new();
2318 let _x = list![1, 2, 3 => &mut a];
2319 let epoch = a.begin_epoch().unwrap();
2320 dbg!(&epoch);
2321 let y = list![4, 5, 6 => &mut a];
2322 dbg!(&a);
2323 a.truncate(epoch).unwrap();
2324 dbg!(&a);
2325 dbg!(a.view(&y).unwrap());
2326 }
2327
2328 #[test]
2329 fn big_term() {
2330 let mut a1 = Arena::new();
2331 let x = a1.atom("Hello, hello, quite long long string, world! X");
2332 let p = a1.func("foo", vec![x; 1_000_000]);
2333 assert!(p.arity() == 1_000_000);
2334 dbg!(a1.stats());
2335 }
2336
2337 #[test]
2338 fn interface() {
2339 let a = &mut Arena::new();
2340 let s = String::from("x");
2341 let x1 = a.func(&s, &vec![Term::date(1000)]);
2342 let x2 = a.func(s.as_str(), vec![Term::date(1000)]);
2343 let x3 = a.func(s, &[Term::date(1000)]);
2344 let _x4 = a.func("x", [Term::date(1000)]);
2345 let _x5 = a.func("x", [x1, x2, x3]);
2346 let _x6 = a.func("x", (5..=6).map(|x| x as f64));
2347 let _x7 = a.func("x", vec![&x1, &x2, &x3]);
2348 let _x8 = a.func("x", &[x1, x2, x3]);
2349 let x9 = func!(
2350 String::from("aaa");
2351 x1, 1u8, 1i8, 2.0,
2352 "x",
2353 "X",
2354 atom!("ATOM"),
2355 var!("var"),
2356 "a string",
2357 b"a binary",
2358 1,
2359 2,
2360 3,
2361 4,
2362 6,
2363 unit!(),
2364 list![1, 2, 3; tuple!()],
2365 list![1, 2, 3; nil!()],
2366 => a
2367 );
2368 dbg!(a.view(&x9).unwrap());
2369 dbg!(a.stats());
2370 }
2371
2372 #[test]
2373 fn into_test() {
2374 let mut arena = Arena::new();
2375 let t1 = arena.term(1);
2377 let t2 = arena.term(2.0);
2378 let t3 = arena.term("x");
2379 let t4 = arena.term(b"bin" as &[u8]);
2380 let point1 = arena.func("point", [t1, t2, t3, t4]);
2381 let t1 = Term::int(1);
2383 let t2 = Term::real(2.0);
2384 let t3 = Term::str(&mut arena, "x");
2385 let t4 = Term::bin(&mut arena, b"bin");
2386 let point2 = arena.func("point", [t1, t2, t3, t4]);
2387 assert_eq!(arena.view(&point1).unwrap(), arena.view(&point2).unwrap());
2388 dbg!(arena.view(&point1).unwrap());
2389
2390 let lazy = Term::func(&mut arena, "lazy", [|arena: &mut Arena| arena.atom("ok")]);
2392 dbg!(arena.view(&lazy).unwrap());
2393
2394 let list = arena.list([1, 2, 3]);
2395 dbg!(arena.view(&list).unwrap());
2396 }
2397
2398 #[test]
2399 fn arena_truncate_test() {
2400 let a = &mut Arena::new();
2401
2402 let t1 = a.str("a".repeat(1000));
2403 let _t5 = atom!("x".repeat(100) => a);
2404 let _t6 = var!("X".repeat(200) => a);
2405 let _t7 = a.bin(b"x".repeat(5000));
2406 let epoch1 = a.begin_epoch().unwrap();
2407 dbg!(a.stats());
2408 dbg!(&epoch1);
2409 let t2 = a.str("b".repeat(2000));
2410 let t3 = a.bin(b"b".repeat(3000));
2411 let _t4 = list![t1, t2, t3];
2412 let _t5 = atom!("z".repeat(4000) => a);
2413 let _t8 = var!("Z".repeat(2000) => a);
2414 let _t7 = a.bin(b"z".repeat(10_000));
2415 let epoch2 = a.begin_epoch().unwrap();
2416 dbg!(a.stats());
2417 dbg!(&epoch2);
2418 a.truncate(epoch2).unwrap();
2419 dbg!(a.stats());
2420 }
2421
2422 #[test]
2423 fn funcv() {
2424 let a = &mut Arena::new();
2425 let xs = [a.atom("foo"), a.atom("x"), a.atom("y")];
2426 let x = a.funcv(xs).unwrap();
2427 let ys = [a.atom("x"), a.atom("y")];
2428 let y = a.func("foo", ys);
2429 assert_eq!(x.arity(), y.arity());
2430 if let Ok(View::Func(_, functor, args)) = x.view(&a) {
2431 assert_eq!(functor.name(a).unwrap(), "foo");
2432 assert_eq!(args.len(), 2);
2433 }
2434 if let Ok(View::Func(_, functor, args)) = y.view(&a) {
2435 assert_eq!(functor.name(a).unwrap(), "foo");
2436 assert_eq!(args.len(), 2);
2437 }
2438 }
2439
2440 #[test]
2441 fn unpack() {
2442 let a = &mut Arena::new();
2443 let xs = [a.atom("foo"), a.atom("x"), a.atom("y")];
2444 let x = a.funcv(xs).unwrap();
2445
2446 let (foo, [x, y]) = x.unpack_func(a, &["foo", "boo"]).unwrap();
2447 dbg!((foo, x, y));
2448
2449 let z = tuple!(1 => a);
2450 assert_eq!(z.arity(), 1);
2451 }
2452
2453 #[test]
2454 fn arity_primitives_and_lists_are_zero() {
2455 let a = &mut Arena::new();
2456
2457 let t_int = Term::int(42);
2458 let t_real = Term::real(3.14);
2459 let t_atom = Term::atom(a, "ok");
2460 let t_var = Term::var(a, "X");
2461 let t_str = Term::str(a, "hello");
2462 let t_bin = Term::bin(a, &[1, 2, 3, 4]);
2463 let t_list = Term::list(a, &[Term::int(1), Term::int(2), Term::int(3)]);
2464
2465 assert_eq!(t_int.arity(), 0);
2466 assert_eq!(t_real.arity(), 0);
2467 assert_eq!(t_atom.arity(), 0);
2468 assert_eq!(t_var.arity(), 0);
2469 assert_eq!(t_str.arity(), 0);
2470 assert_eq!(t_bin.arity(), 0);
2471 assert_eq!(t_list.arity(), 0); }
2473
2474 #[test]
2475 fn arity_for_tuples_and_funcs() {
2476 let a = &mut Arena::new();
2477
2478 let t2 = Term::tuple(a, &[Term::int(1), Term::int(2)]);
2479 let t3 = Term::tuple(a, &[Term::int(1), Term::int(2), Term::int(3)]);
2480 assert_eq!(t2.arity(), 2);
2481 assert_eq!(t3.arity(), 3);
2482
2483 let f0 = Term::func(a, "nilary", &[] as &[Term]); let f2 = Term::func(a, "pair", &[Term::int(1), Term::int(2)]);
2485 let f3 = Term::func(a, "triple", &[Term::int(1), Term::int(2), Term::int(3)]);
2486
2487 assert_eq!(f0.arity(), 0);
2488 assert_eq!(f2.arity(), 2);
2489 assert_eq!(f3.arity(), 3);
2490 }
2491
2492 #[test]
2493 fn name_and_kind_name() {
2494 let a = &mut Arena::new();
2495
2496 let atom = Term::atom(a, "foo");
2497 let var = Term::var(a, "X");
2498 let fun = Term::func(a, "bar", &[Term::int(1)]);
2499 let tup = Term::tuple(a, &[Term::int(1), Term::int(2)]);
2500 let lst = Term::list(a, &[Term::int(1), Term::int(2), Term::int(3)]);
2501
2502 assert_eq!(atom.name(&a).unwrap(), "foo");
2504 assert_eq!(var.name(&a).unwrap(), "X");
2505 assert_eq!(fun.name(&a).unwrap(), "bar");
2506
2507 assert_eq!(atom.kind_name(), "atom");
2509 assert_eq!(var.kind_name(), "var");
2510 assert_eq!(fun.kind_name(), "func");
2511 assert_eq!(tup.kind_name(), "tuple");
2512 assert_eq!(lst.kind_name(), "list");
2513 assert_eq!(Term::int(7).kind_name(), "int");
2514 assert_eq!(Term::str(a, "s").kind_name(), "str");
2515 }
2516
2517 #[test]
2518 fn is_func_tuple_list() {
2519 let a = &mut Arena::new();
2520
2521 let f2 = Term::func(a, "pair", &[Term::int(1), Term::int(2)]);
2522 let tup = Term::tuple(a, &[Term::int(1), Term::int(2)]);
2523 let lst = Term::list(a, &[Term::int(1), Term::int(2), Term::int(3)]);
2524
2525 assert!(f2.is_func());
2526 assert!(tup.is_tuple());
2527 assert!(lst.is_list());
2528
2529 assert!(!f2.is_tuple());
2530 assert!(!f2.is_list());
2531 assert!(!tup.is_func());
2532 assert!(!lst.is_func());
2533 assert!(!lst.is_tuple());
2534 }
2535
2536 #[test]
2537 fn is_inline_obvious_cases() {
2538 let a = &mut Arena::new();
2539
2540 let i = Term::int(42);
2541 let f0 = Term::func(a, "nilary", &[] as &[Term]);
2542 let tup = Term::tuple(a, &[Term::int(1), Term::int(2)]);
2543 let lst = Term::list(a, &[Term::int(1), Term::int(2)]);
2544
2545 assert!(i.is_inline());
2547 assert!(f0.is_inline()); assert!(!tup.is_inline());
2549 assert!(!lst.is_inline());
2550 }
2551
2552 #[test]
2553 fn is_atom_var_str_bin_number_minimal() {
2554 let a = &mut Arena::new();
2555
2556 let at = Term::atom(a, "foo");
2557 let vr = Term::var(a, "X");
2558 let st = Term::str(a, "hi");
2559 let bi = Term::bin(a, &[1, 2, 3, 4]);
2560 let i = Term::int(7);
2561
2562 assert!(at.is_atom());
2563 assert!(vr.is_var());
2564 assert!(st.is_str());
2565 assert!(bi.is_bin());
2566
2567 assert!(!at.is_var());
2568 assert!(!vr.is_atom());
2569 assert!(!st.is_bin());
2570 assert!(!bi.is_str());
2571
2572 assert!(i.is_number());
2574 assert!(!at.is_number());
2575 assert!(!st.is_number());
2576 assert!(!bi.is_number());
2577 }
2578
2579 #[test]
2580 fn nil_and_tuple_edge_behavior() {
2581 let a = &mut Arena::new();
2582
2583 assert!(Term::NIL.is_list());
2585 assert!(!Term::NIL.is_tuple());
2586 assert!(!Term::NIL.is_func());
2587 assert_eq!(Term::NIL.arity(), 0);
2588
2589 let t = Term::tuple(a, &[Term::int(1), Term::int(2), Term::int(3)]);
2591 assert!(t.is_tuple());
2592 assert_eq!(t.arity(), 3);
2593 assert!(!t.is_list());
2594 assert!(!t.is_func());
2595 }
2596
2597 #[test]
2598 fn arity_consistency_with_predicates() {
2599 let a = &mut Arena::new();
2600
2601 let f3 = Term::func(a, "triple", &[Term::int(1), Term::int(2), Term::int(3)]);
2602 let t2 = Term::tuple(a, &[Term::int(1), Term::int(2)]);
2603 let l2 = Term::list(a, &[Term::int(1), Term::int(2)]);
2604 let v = Term::var(a, "X");
2605
2606 assert!(f3.is_func());
2607 assert_eq!(f3.arity(), 3);
2608
2609 assert!(t2.is_tuple());
2610 assert_eq!(t2.arity(), 2);
2611
2612 assert!(l2.is_list());
2613 assert_eq!(l2.arity(), 0); assert!(v.is_var());
2616 assert_eq!(v.arity(), 0); }
2618
2619 #[test]
2620 fn name_and_kind_name_roundtrip() {
2621 let a = &mut Arena::new();
2622
2623 let atom = Term::atom(a, "foo");
2624 let var = Term::var(a, "X");
2625 let fun = Term::func(a, "bar", &[Term::int(1)]);
2626 let tup = Term::tuple(a, &[Term::int(1), Term::int(2)]);
2627 let lst = Term::list(a, &[Term::int(1), Term::int(2), Term::int(3)]);
2628
2629 assert_eq!(atom.name(&a).unwrap(), "foo");
2631 assert_eq!(var.name(&a).unwrap(), "X");
2632 assert_eq!(fun.name(&a).unwrap(), "bar");
2633
2634 assert_eq!(atom.kind_name(), "atom");
2636 assert_eq!(var.kind_name(), "var");
2637 assert_eq!(fun.kind_name(), "func");
2638 assert_eq!(tup.kind_name(), "tuple");
2639 assert_eq!(lst.kind_name(), "list");
2640 assert_eq!(Term::int(7).kind_name(), "int");
2641 assert_eq!(Term::str(a, "s").kind_name(), "str");
2642 }
2643
2644 #[test]
2645 fn unpack_primitives_ok() {
2646 let a = &mut Arena::new();
2647
2648 let t_int = Term::int(42);
2649 let t_real = Term::real(3.5);
2650 let t_date = Term::date(2);
2651 let t_str = Term::str(a, "hello");
2652 let t_bin = Term::bin(a, &[1u8, 2, 3, 4]);
2653
2654 assert_eq!(t_int.unpack_int(a).unwrap(), 42);
2655 assert!((t_real.unpack_real(a).unwrap() - 3.5).abs() < f64::EPSILON);
2656 assert_eq!(t_date.unpack_date(a).unwrap(), 2);
2657
2658 assert_eq!(t_str.unpack_str(a).unwrap(), "hello");
2659 assert_eq!(t_bin.unpack_bin(a).unwrap(), &[1, 2, 3, 4]);
2660 }
2661
2662 #[test]
2663 fn unpack_primitives_wrong_type_errs() {
2664 let a = &mut Arena::new();
2665
2666 let not_int = Term::str(a, "nope");
2667 let not_real = Term::int(1);
2668 let not_date = Term::str(a, "2024-01-02");
2669
2670 assert!(not_int.unpack_int(a).is_err());
2671 assert!(not_real.unpack_real(a).is_err());
2672 assert!(not_date.unpack_date(a).is_err());
2673
2674 let not_str = Term::int(5);
2675 let not_bin = Term::str(a, "bytes");
2676 assert!(not_str.unpack_str(a).is_err());
2677 assert!(not_bin.unpack_bin(a).is_err());
2678 }
2679
2680 #[test]
2681 fn unpack_atom_and_var_with_allowed_names() {
2682 let a = &mut Arena::new();
2683
2684 let at_ok = Term::atom(a, "foo");
2685 let at_no = Term::atom(a, "bar");
2686 let vr_ok = Term::var(a, "X");
2687 let vr_no = Term::var(a, "Y");
2688
2689 let allowed_atoms = ["foo", "baz"];
2691 let allowed_vars = ["X", "Z"];
2692
2693 assert_eq!(at_ok.unpack_atom(a, &allowed_atoms).unwrap(), "foo");
2694 assert!(at_no.unpack_atom(a, &allowed_atoms).is_err());
2695
2696 assert_eq!(vr_ok.unpack_var(a, &allowed_vars).unwrap(), "X");
2697 assert!(vr_no.unpack_var(a, &allowed_vars).is_err());
2698
2699 assert_eq!(at_no.name(a).unwrap(), "bar");
2701 assert_eq!(vr_no.var_name(a).unwrap(), "Y");
2702 }
2703
2704 #[test]
2705 fn unpack_func_any_and_arity_specific() {
2706 let a = &mut Arena::new();
2707
2708 let f0 = Term::func(a, "nilary", &[] as &[Term]);
2709 let f2 = Term::func(a, "pair", &[Term::int(1), Term::int(2)]);
2710 let f3 = Term::func(a, "triple", &[Term::int(1), Term::int(2), Term::int(3)]);
2711
2712 {
2714 let (name, args) = f2.unpack_func_any(a, &["pair", "other"]).unwrap();
2715 assert_eq!(name.name(a).unwrap(), "pair");
2716 assert_eq!(args.len(), 2);
2717 assert_eq!(args[0].unpack_int(a).unwrap(), 1);
2718 assert_eq!(args[1].unpack_int(a).unwrap(), 2);
2719
2720 let (name0, args0) = f0.unpack_func_any(a, &[]).unwrap();
2722 assert_eq!(name0.name(a).unwrap(), "nilary");
2723 assert!(args0.is_empty());
2724
2725 assert!(f3.unpack_func_any(a, &["not_triple"]).is_err());
2727 }
2728
2729 {
2731 let (name2, [x, y]) = f2.unpack_func(a, &["pair"]).unwrap();
2732 assert_eq!(name2.name(a).unwrap(), "pair");
2733 assert_eq!(x.unpack_int(a).unwrap(), 1);
2734 assert_eq!(y.unpack_int(a).unwrap(), 2);
2735
2736 assert!(f3.unpack_func::<2>(a, &["triple"]).is_err());
2738
2739 assert!(f2.unpack_func::<2>(a, &["other"]).is_err());
2741 }
2742 }
2743
2744 #[test]
2745 fn unpack_list_proper_and_tail() {
2746 let a = &mut Arena::new();
2747
2748 let l0 = Term::list(a, &[] as &[Term]);
2749 let (elems0, tail0) = l0.unpack_list(a).unwrap();
2750 assert!(elems0.is_empty());
2751 assert_eq!(*tail0, Term::NIL);
2752
2753 let l3 = Term::list(a, &[Term::int(1), Term::int(2), Term::int(3)]);
2754 let (elems3, tail3) = l3.unpack_list(a).unwrap();
2755 assert_eq!(elems3.len(), 3);
2756 assert_eq!(elems3[0].unpack_int(a).unwrap(), 1);
2757 assert_eq!(elems3[1].unpack_int(a).unwrap(), 2);
2758 assert_eq!(elems3[2].unpack_int(a).unwrap(), 3);
2759 assert_eq!(tail3, &Term::NIL);
2760
2761 let not_list = Term::int(9);
2763 assert!(not_list.unpack_list(a).is_err());
2764 }
2765
2766 #[test]
2767 fn unpack_tuple_any_and_fixed() {
2768 let a = &mut Arena::new();
2769
2770 let t0 = Term::tuple(a, &[] as &[Term]);
2771 let t3 = Term::tuple(a, &[Term::int(1), Term::int(2), Term::int(3)]);
2772
2773 let elems0 = t0.unpack_tuple_any(a).unwrap();
2775 assert!(elems0.is_empty());
2776
2777 let elems3 = t3.unpack_tuple_any(a).unwrap();
2778 assert_eq!(elems3.len(), 3);
2779 assert_eq!(elems3[0].unpack_int(a).unwrap(), 1);
2780 assert_eq!(elems3[1].unpack_int(a).unwrap(), 2);
2781 assert_eq!(elems3[2].unpack_int(a).unwrap(), 3);
2782
2783 let arr3 = t3.unpack_tuple::<3>(a).unwrap();
2785 assert_eq!(arr3[0].unpack_int(a).unwrap(), 1);
2786 assert_eq!(arr3[1].unpack_int(a).unwrap(), 2);
2787 assert_eq!(arr3[2].unpack_int(a).unwrap(), 3);
2788
2789 assert!(t3.unpack_tuple::<2>(a).is_err());
2791
2792 assert!(Term::int(1).unpack_tuple_any(a).is_err());
2794 assert!(Term::int(1).unpack_tuple::<0>(a).is_err());
2795 }
2796
2797 #[test]
2798 fn unpack_atom_var_wrong_type_errs() {
2799 let a = &mut Arena::new();
2800
2801 let not_atom = Term::int(1);
2802 let not_var = Term::str(a, "X");
2803 assert!(not_atom.atom_name(a).is_err());
2804 assert!(not_var.var_name(a).is_err());
2805 }
2806
2807 #[test]
2808 fn unpack_func_wrong_type_errs() {
2809 let a = &mut Arena::new();
2810
2811 let tup = Term::tuple(a, &[Term::int(1), Term::int(2)]);
2813 assert!(tup.func_name(a).is_err());
2814 assert!(tup.unpack_func::<2>(a, &[]).is_err());
2815
2816 let at = Term::atom(a, "f");
2818 assert!(!at.unpack_func_any(a, &[]).is_err());
2819 assert!(!at.unpack_func::<0>(a, &[]).is_err());
2820 }
2821
2822 #[test]
2823 fn fmt_nil_to_string() {
2824 let arena = Arena::new();
2825 let t = Term::NIL;
2826 assert_eq!(t.display(&arena).to_string(), "nil");
2827 }
2828
2829 #[test]
2830 fn fmt_unit_format_macro() {
2831 let arena = Arena::new();
2832 let t = Term::UNIT;
2833 assert_eq!(format!("{}", t.display(&arena)), "unit");
2834 }
2835
2836 #[test]
2837 fn fmt_int_positive() {
2838 let arena = Arena::new();
2839 let t = Term::int(42);
2840 assert_eq!(format!("{}", t.display(&arena)), "42");
2841 }
2842
2843 #[test]
2844 fn fmt_int_negative_to_string() {
2845 let arena = Arena::new();
2846 let t = Term::int(-9001);
2847 assert_eq!(t.display(&arena).to_string(), "-9001");
2848 }
2849
2850 #[test]
2851 fn fmt_str_quotes() {
2852 let mut arena = Arena::new();
2853 let t = Term::str(&mut arena, "hello");
2854 assert_eq!(format!("{}", t.display(&arena)), r#""hello""#);
2855 }
2856
2857 #[test]
2858 fn fmt_str_with_escape_chars() {
2859 let mut arena = Arena::new();
2860 let t = Term::str(&mut arena, "a\nb\tc");
2861 assert_eq!(t.display(&arena).to_string(), "\"a\\nb\\tc\"");
2862 }
2863
2864 #[test]
2865 fn fmt_date_epoch_zero() {
2866 let arena = Arena::new();
2867 let t = Term::date(0);
2869 assert_eq!(
2870 format!("{}", t.display(&arena)),
2871 "date{1970-01-01T00:00:00+00:00}"
2872 );
2873 }
2874
2875 #[test]
2876 fn fmt_date_epoch_ms_trunc_to_seconds() {
2877 let arena = Arena::new();
2878 let t = Term::date(1_234);
2879 assert_eq!(
2880 t.display(&arena).to_string(),
2881 "date{1970-01-01T00:00:01.234+00:00}"
2882 );
2883 }
2884
2885 #[test]
2886 fn fmt_date_specific_moment() {
2887 let arena = Arena::new();
2888 let t = Term::date(1_727_525_530_123i64);
2889 assert_eq!(
2890 format!("{}", t.display(&arena)),
2891 "date{2024-09-28T12:12:10.123+00:00}"
2892 );
2893 }
2894
2895 #[test]
2896 fn fmt_date_specific_moment_in_the_past() {
2897 let arena = Arena::new();
2898 let t = Term::date(-5_382_698_399_999i64);
2899 assert_eq!(
2900 format!("{}", t.display(&arena)),
2901 "date{1799-06-06T06:00:00.001+00:00}"
2902 );
2903 }
2904
2905 #[test]
2906 fn fmt_write_into_string_buffer() {
2907 let arena = Arena::new();
2908 let t = Term::int(7);
2909 let mut buf = String::new();
2910 write!(&mut buf, "val={}", t.display(&arena)).expect("formatting failed");
2912 assert_eq!(buf, "val=7");
2913 }
2914}