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