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