diff --git a/crates/any-intern/src/any.rs b/crates/any-intern/src/any.rs index b6f00ab..261a6f0 100644 --- a/crates/any-intern/src/any.rs +++ b/crates/any-intern/src/any.rs @@ -49,6 +49,16 @@ pub struct AnyInterner { impl AnyInterner { /// Creates an interner for values of type `K`. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyInterner; + /// + /// let interner = AnyInterner::of::(); + /// assert!(interner.is_type_of::()); + /// assert!(interner.is_empty()); + /// ``` pub fn of() -> Self { // Safety: Only one instance exists. let inner = unsafe { UnsafeLock::new(AnyInternSet::of::()) }; @@ -58,6 +68,17 @@ impl AnyInterner { impl AnyInterner { /// Creates an interner for values of type `K` with a custom hasher. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyInterner; + /// use std::hash::RandomState; + /// + /// let interner = AnyInterner::with_hasher::(RandomState::new()); + /// assert!(interner.is_type_of::()); + /// assert!(interner.is_empty()); + /// ``` pub fn with_hasher(hash_builder: S) -> Self { // Safety: Only one instance exists. let inner = unsafe { UnsafeLock::new(AnyInternSet::with_hasher::(hash_builder)) }; @@ -65,11 +86,39 @@ impl AnyInterner { } /// Returns the number of values the interner contains. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyInterner; + /// + /// let interner = AnyInterner::of::(); + /// assert_eq!(interner.len(), 0); + /// + /// unsafe { + /// interner.intern(1_u32); + /// } + /// assert_eq!(interner.len(), 1); + /// ``` pub fn len(&self) -> usize { self.with_inner(|set| set.len()) } /// Returns `true` if the interner is empty. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyInterner; + /// + /// let interner = AnyInterner::of::(); + /// assert!(interner.is_empty()); + /// + /// unsafe { + /// interner.intern(1_u32); + /// } + /// assert!(!interner.is_empty()); + /// ``` pub fn is_empty(&self) -> bool { self.with_inner(|set| set.is_empty()) } @@ -191,6 +240,16 @@ impl AnyInterner { } /// Returns `true` if the interner contains values of the given type. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyInterner; + /// + /// let interner = AnyInterner::of::(); + /// assert!(interner.is_type_of::()); + /// assert!(!interner.is_type_of::()); + /// ``` pub fn is_type_of(&self) -> bool { self.with_inner(|set| set.is_type_of::()) } @@ -199,6 +258,20 @@ impl AnyInterner { /// /// Although the interner supports interior mutability, `clear` requires mutable access to /// invalidate all [`Interned`] values referencing the interner. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyInterner; + /// + /// let mut interner = AnyInterner::of::(); + /// unsafe { + /// interner.intern(1_u32); + /// } + /// + /// interner.clear(); + /// assert!(interner.is_empty()); + /// ``` pub fn clear(&mut self) { self.with_inner(|set| set.clear()) } @@ -254,6 +327,16 @@ pub struct AnyInternSet { impl AnyInternSet { /// Creates an intern set for values of type `K`. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyInternSet; + /// + /// let set = AnyInternSet::of::(); + /// assert!(set.is_type_of::()); + /// assert!(set.is_empty()); + /// ``` pub fn of() -> Self { Self { arena: AnyArena::of::(), @@ -265,6 +348,17 @@ impl AnyInternSet { impl AnyInternSet { /// Creates an intern set for values of type `K` with the default hasher. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyInternSet; + /// use std::hash::RandomState; + /// + /// let set = AnyInternSet::::default_of::(); + /// assert!(set.is_type_of::()); + /// assert!(set.is_empty()); + /// ``` pub fn default_of() -> Self { Self { arena: AnyArena::of::(), @@ -276,6 +370,17 @@ impl AnyInternSet { impl AnyInternSet { /// Creates an intern set for values of type `K` with a custom hasher. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyInternSet; + /// use std::hash::RandomState; + /// + /// let set = AnyInternSet::with_hasher::(RandomState::new()); + /// assert!(set.is_type_of::()); + /// assert!(set.is_empty()); + /// ``` pub fn with_hasher(hash_builder: S) -> Self { Self { arena: AnyArena::of::(), @@ -285,11 +390,39 @@ impl AnyInternSet { } /// Returns the number of values in the set. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyInternSet; + /// + /// let mut set = AnyInternSet::of::(); + /// assert_eq!(set.len(), 0); + /// + /// unsafe { + /// set.intern(1_u32); + /// } + /// assert_eq!(set.len(), 1); + /// ``` pub fn len(&self) -> usize { self.arena.len() } /// Returns `true` if the set is empty. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyInternSet; + /// + /// let mut set = AnyInternSet::of::(); + /// assert!(set.is_empty()); + /// + /// unsafe { + /// set.intern(1_u32); + /// } + /// assert!(!set.is_empty()); + /// ``` pub fn is_empty(&self) -> bool { self.len() == 0 } @@ -451,11 +584,35 @@ impl AnyInternSet { } /// Returns `true` if the set contains values of the given type. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyInternSet; + /// + /// let set = AnyInternSet::of::(); + /// assert!(set.is_type_of::()); + /// assert!(!set.is_type_of::()); + /// ``` pub fn is_type_of(&self) -> bool { self.arena.is_type_of::() } /// Removes all items in the set. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyInternSet; + /// + /// let mut set = AnyInternSet::of::(); + /// unsafe { + /// set.intern(1_u32); + /// } + /// + /// set.clear(); + /// assert!(set.is_empty()); + /// ``` pub fn clear(&mut self) { self.arena.clear(); self.set.clear(); @@ -511,6 +668,16 @@ pub struct AnyArena { impl AnyArena { /// Creates an arena for values of type `T`. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyArena; + /// + /// let arena = AnyArena::of::(); + /// assert!(arena.is_type_of::()); + /// assert!(arena.is_empty()); + /// ``` pub fn of() -> Self { Self { bump: Bump::new(), @@ -526,21 +693,67 @@ impl AnyArena { } /// Returns `true` if this arena stores values of type `T`. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyArena; + /// + /// let arena = AnyArena::of::(); + /// assert!(arena.is_type_of::()); + /// assert!(!arena.is_type_of::()); + /// ``` pub fn is_type_of(&self) -> bool { TypeId::of::() == self.ty } /// Returns the number of elements in this arena. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyArena; + /// + /// let arena = AnyArena::of::(); + /// assert_eq!(arena.len(), 0); + /// + /// arena.alloc(1_u32); + /// assert_eq!(arena.len(), 1); + /// ``` pub fn len(&self) -> usize { self.len.get() } /// Returns `true` if this arena is empty. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyArena; + /// + /// let arena = AnyArena::of::(); + /// assert!(arena.is_empty()); + /// + /// arena.alloc(1_u32); + /// assert!(!arena.is_empty()); + /// ``` pub fn is_empty(&self) -> bool { self.len() == 0 } /// Allocates `value` in the arena. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyArena; + /// + /// let arena = AnyArena::of::(); + /// let value = arena.alloc(String::from("hello")); + /// + /// assert_eq!(value, "hello"); + /// assert_eq!(arena.len(), 1); + /// ``` pub fn alloc(&self, value: T) -> &mut T { debug_assert!(self.is_type_of::()); @@ -549,6 +762,18 @@ impl AnyArena { } /// Drops all stored values and clears the arena. + /// + /// # Examples + /// + /// ``` + /// use any_intern::AnyArena; + /// + /// let mut arena = AnyArena::of::(); + /// arena.alloc(1_u32); + /// + /// arena.clear(); + /// assert!(arena.is_empty()); + /// ``` pub fn clear(&mut self) { self.drop_all(); self.bump.reset(); diff --git a/crates/any-intern/src/common.rs b/crates/any-intern/src/common.rs index c824a14..04e5b87 100644 --- a/crates/any-intern/src/common.rs +++ b/crates/any-intern/src/common.rs @@ -16,6 +16,18 @@ pub struct Interned<'a, T: ?Sized>(pub &'a T, Prv); impl<'a, T: ?Sized> Interned<'a, T> { /// Returns the typed raw pointer for this interned value. + /// + /// # Examples + /// + /// ``` + /// use any_intern::Interner; + /// + /// let interner = Interner::new(); + /// let value = interner.intern_static(42_u32); + /// let raw = value.raw(); + /// + /// assert_eq!(raw.as_ptr(), (&*value as *const u32).cast_mut()); + /// ``` pub fn raw(&self) -> RawInterned { // Safety: A reference is non-null. let ptr = unsafe { NonNull::new_unchecked(self.0 as *const T as *mut T) }; @@ -23,6 +35,18 @@ impl<'a, T: ?Sized> Interned<'a, T> { } /// Returns the type-erased raw pointer for this interned value. + /// + /// # Examples + /// + /// ``` + /// use any_intern::Interner; + /// + /// let interner = Interner::new(); + /// let value = interner.intern_static(42_u32); + /// let erased = value.erased_raw(); + /// + /// assert_eq!(erased, value.raw().erase()); + /// ``` pub fn erased_raw(&self) -> RawInterned { let ptr = unsafe { NonNull::new_unchecked(self.0 as *const T as *mut T) }; RawInterned(ptr.cast::()) @@ -38,6 +62,18 @@ impl<'a, T: ?Sized> Interned<'a, T> { /// # Safety /// /// Value pointed by the given `raw` must be alive in an interner. + /// + /// # Examples + /// + /// ``` + /// use any_intern::{Interned, Interner}; + /// + /// let interner = Interner::new(); + /// let value = interner.intern_static(42_u32); + /// let restored = unsafe { Interned::from_raw(value.raw()) }; + /// + /// assert_eq!(value, restored); + /// ``` pub unsafe fn from_raw(raw: RawInterned) -> Self { let ref_ = unsafe { raw.0.as_ref() }; Self(ref_, Prv) @@ -49,6 +85,18 @@ impl<'a, T> Interned<'a, T> { /// /// * Value pointed by the given `raw` must be alive in an interner. /// * Type must be correct. + /// + /// # Examples + /// + /// ``` + /// use any_intern::{Interned, Interner}; + /// + /// let interner = Interner::new(); + /// let value = interner.intern_static(42_u32); + /// let restored = unsafe { Interned::::from_erased_raw(value.erased_raw()) }; + /// + /// assert_eq!(value, restored); + /// ``` pub unsafe fn from_erased_raw(raw: RawInterned) -> Self { let ref_ = unsafe { raw.0.cast::().as_ref() }; Self(ref_, Prv) @@ -141,12 +189,35 @@ pub struct RawInterned(pub(crate) NonNull); impl RawInterned { #[inline] /// Casts this raw interned pointer to another type. + /// + /// # Examples + /// + /// ``` + /// use any_intern::Interner; + /// + /// let interner = Interner::new(); + /// let value = interner.intern_static(42_u32); + /// let typed = value.erased_raw().cast::(); + /// + /// assert_eq!(typed, value.raw()); + /// ``` pub fn cast(self) -> RawInterned { RawInterned(self.0.cast()) } - #[inline] /// Erases the pointee type from this raw interned pointer. + /// + /// # Examples + /// + /// ``` + /// use any_intern::Interner; + /// + /// let interner = Interner::new(); + /// let value = interner.intern_static(42_u32); + /// + /// assert_eq!(value.raw().erase(), value.erased_raw()); + /// ``` + #[inline] pub fn erase(self) -> RawInterned { RawInterned(self.0.cast()) } @@ -249,6 +320,19 @@ impl UnsafeLock { /// There must be no copies of the value. See [`Send implementation`]. /// /// [`Send implementation`]: UnsafeLock#impl-Send-for-UnsafeLock + /// + /// # Examples + /// + /// ``` + /// use any_intern::UnsafeLock; + /// + /// let lock = unsafe { UnsafeLock::new(1_u32) }; + /// let ptr = unsafe { lock.lock() }; + /// unsafe { + /// assert_eq!(*ptr.as_ref(), 1); + /// lock.unlock(); + /// } + /// ``` pub unsafe fn new(value: T) -> Self { Self { inner: Arc::new(ManualMutex { @@ -266,6 +350,25 @@ impl UnsafeLock { /// * Do not make copies of `T` from the returned pointer. See [`Send implementation`]. /// /// [`Send implementation`]: UnsafeLock#impl-Send-for-UnsafeLock + /// + /// # Examples + /// + /// ``` + /// use any_intern::UnsafeLock; + /// + /// let lock = unsafe { UnsafeLock::new(1_u32) }; + /// let ptr = unsafe { lock.lock() }; + /// unsafe { + /// *ptr.as_ptr() = 2; + /// lock.unlock(); + /// } + /// + /// let ptr = unsafe { lock.lock() }; + /// unsafe { + /// assert_eq!(*ptr.as_ref(), 2); + /// lock.unlock(); + /// } + /// ``` pub unsafe fn lock(&self) -> NonNull { self.inner.mutex.lock(); unsafe { NonNull::new_unchecked(self.inner.data.get()) } @@ -274,6 +377,19 @@ impl UnsafeLock { /// # Safety /// /// Must follow [`lock`](Self::lock). + /// + /// # Examples + /// + /// ``` + /// use any_intern::UnsafeLock; + /// + /// let lock = unsafe { UnsafeLock::new(1_u32) }; + /// let ptr = unsafe { lock.lock() }; + /// unsafe { + /// assert_eq!(*ptr.as_ref(), 1); + /// lock.unlock(); + /// } + /// ``` pub unsafe fn unlock(&self) { self.inner.mutex.unlock(); } diff --git a/crates/any-intern/src/dropless.rs b/crates/any-intern/src/dropless.rs index 14b063e..347c32e 100644 --- a/crates/any-intern/src/dropless.rs +++ b/crates/any-intern/src/dropless.rs @@ -64,12 +64,33 @@ use std::{ /// ``` pub trait Dropless { /// Returns pointer to the instance. + /// + /// # Examples + /// + /// ``` + /// use any_intern::Dropless; + /// + /// let value = 42_u32; + /// let ptr = value.as_byte_ptr(); + /// + /// assert_eq!(ptr.as_ptr(), (&value as *const u32).cast_mut().cast::()); + /// ``` fn as_byte_ptr(&self) -> NonNull { // Safety: A reference is non-null unsafe { NonNull::new_unchecked(self as *const Self as *mut Self).cast::() } } /// Returns layout of the type. + /// + /// # Examples + /// + /// ``` + /// use any_intern::Dropless; + /// use std::alloc::Layout; + /// + /// let value = 42_u32; + /// assert_eq!(value.layout(), Layout::new::()); + /// ``` fn layout(&self) -> Layout { Layout::for_value(self) } @@ -83,6 +104,20 @@ pub trait Dropless { /// Undefined behavior if any conditions below are not met. /// * Implementation should interpret the byte slice into the type correctly. /// * Caller should give well aligned data for the type. + /// + /// # Examples + /// + /// ``` + /// use any_intern::Dropless; + /// + /// let value = 42_u32; + /// let bytes = unsafe { + /// std::slice::from_raw_parts((&value as *const u32).cast::(), std::mem::size_of::()) + /// }; + /// + /// let decoded = unsafe { u32::from_bytes(&bytes) }; + /// assert_eq!(*decoded, 42); + /// ``` unsafe fn from_bytes(bytes: &[u8]) -> &Self; /// Computes a hash value for the type using the provided byte slice. @@ -94,6 +129,22 @@ pub trait Dropless { /// Undefined behavior if any conditions below are not met. /// * Implementation should interpret the byte slice into the type correctly. /// * Caller should give well aligned data for the type. + /// + /// # Examples + /// + /// ``` + /// use any_intern::Dropless; + /// use std::hash::RandomState; + /// + /// let value = 42_u32; + /// let bytes = unsafe { + /// std::slice::from_raw_parts((&value as *const u32).cast::(), std::mem::size_of::()) + /// }; + /// let state = RandomState::new(); + /// let hash = unsafe { u32::hash(&state, bytes) }; + /// + /// assert_eq!(hash, unsafe { u32::hash(&state, bytes) }); + /// ``` unsafe fn hash(hash_builder: &S, bytes: &[u8]) -> u64; /// Compares two byte slices for equality as instances of the type. @@ -105,6 +156,28 @@ pub trait Dropless { /// Undefined behavior if any conditions below are not met. /// * Implementation should interpret the byte slice into the type correctly. /// * Caller should give well aligned data for the type. + /// + /// # Examples + /// + /// ``` + /// use any_intern::Dropless; + /// + /// let a = 42_u32; + /// let b = 42_u32; + /// let c = 7_u32; + /// let a_bytes = unsafe { + /// std::slice::from_raw_parts((&a as *const u32).cast::(), std::mem::size_of::()) + /// }; + /// let b_bytes = unsafe { + /// std::slice::from_raw_parts((&b as *const u32).cast::(), std::mem::size_of::()) + /// }; + /// let c_bytes = unsafe { + /// std::slice::from_raw_parts((&c as *const u32).cast::(), std::mem::size_of::()) + /// }; + /// + /// assert!(unsafe { ::eq(a_bytes, b_bytes) }); + /// assert!(!unsafe { ::eq(a_bytes, c_bytes) }); + /// ``` unsafe fn eq(a: &[u8], b: &[u8]) -> bool; } @@ -224,6 +297,15 @@ pub struct DroplessInterner { impl DroplessInterner { /// Creates an empty dropless interner. + /// + /// # Examples + /// + /// ``` + /// use any_intern::DroplessInterner; + /// + /// let interner = DroplessInterner::new(); + /// assert!(interner.is_empty()); + /// ``` pub fn new() -> Self { Self::default() } @@ -231,6 +313,16 @@ impl DroplessInterner { impl DroplessInterner { /// Creates an empty dropless interner with a custom hasher. + /// + /// # Examples + /// + /// ``` + /// use any_intern::DroplessInterner; + /// use std::hash::RandomState; + /// + /// let interner = DroplessInterner::with_hasher(RandomState::new()); + /// assert!(interner.is_empty()); + /// ``` pub fn with_hasher(hash_builder: S) -> Self { // Safety: Only one instance exists. let inner = unsafe { UnsafeLock::new(DroplessInternSet::with_hasher(hash_builder)) }; @@ -238,11 +330,35 @@ impl DroplessInterner { } /// Returns the number of values the interner contains. + /// + /// # Examples + /// + /// ``` + /// use any_intern::DroplessInterner; + /// + /// let interner = DroplessInterner::new(); + /// assert_eq!(interner.len(), 0); + /// + /// interner.intern("hello"); + /// assert_eq!(interner.len(), 1); + /// ``` pub fn len(&self) -> usize { self.with_inner(|set| set.len()) } /// Returns `true` if the interner is empty. + /// + /// # Examples + /// + /// ``` + /// use any_intern::DroplessInterner; + /// + /// let interner = DroplessInterner::new(); + /// assert!(interner.is_empty()); + /// + /// interner.intern("hello"); + /// assert!(!interner.is_empty()); + /// ``` pub fn is_empty(&self) -> bool { self.with_inner(|set| set.is_empty()) } @@ -339,6 +455,19 @@ impl DroplessInterner { /// /// Although the interner supports interior mutability, `clear` requires mutable access to /// invalidate all [`Interned`] values referencing the interner. + /// + /// # Examples + /// + /// ``` + /// use any_intern::DroplessInterner; + /// + /// let mut interner = DroplessInterner::new(); + /// interner.intern("hello"); + /// assert!(!interner.is_empty()); + /// + /// interner.clear(); + /// assert!(interner.is_empty()); + /// ``` pub fn clear(&mut self) { self.with_inner(|set| set.clear()) } @@ -379,6 +508,15 @@ pub struct DroplessInternSet { impl DroplessInternSet { /// Creates an empty dropless intern set. + /// + /// # Examples + /// + /// ``` + /// use any_intern::DroplessInternSet; + /// + /// let set = DroplessInternSet::new(); + /// assert!(set.is_empty()); + /// ``` pub fn new() -> Self { Self::default() } @@ -386,6 +524,16 @@ impl DroplessInternSet { impl DroplessInternSet { /// Creates an empty dropless intern set with a custom hasher. + /// + /// # Examples + /// + /// ``` + /// use any_intern::DroplessInternSet; + /// use std::hash::RandomState; + /// + /// let set = DroplessInternSet::with_hasher(RandomState::new()); + /// assert!(set.is_empty()); + /// ``` pub fn with_hasher(hash_builder: S) -> Self { Self { bump: Bump::new(), @@ -396,6 +544,19 @@ impl DroplessInternSet { } /// Stores a dropless value, returning a reference to the interned value. + /// + /// # Examples + /// + /// ``` + /// use any_intern::DroplessInternSet; + /// + /// let mut set = DroplessInternSet::new(); + /// let a = set.intern("hello").raw(); + /// let b = set.intern("hello").raw(); + /// + /// assert_eq!(a, b); + /// assert_eq!(set.len(), 1); + /// ``` pub fn intern(&mut self, value: &K) -> Interned<'_, K> { let src = value.as_byte_ptr(); let layout = value.layout(); @@ -438,6 +599,17 @@ impl DroplessInternSet { } /// Stores a value formatted through [`Display`] as an interned string. + /// + /// # Examples + /// + /// ``` + /// use any_intern::DroplessInternSet; + /// + /// let mut set = DroplessInternSet::new(); + /// let value = set.intern_formatted_str(&42, 2).unwrap(); + /// + /// assert_eq!(&*value, "42"); + /// ``` pub fn intern_formatted_str( &mut self, value: &K, @@ -519,16 +691,53 @@ impl DroplessInternSet { } /// Returns the number of values in the set. + /// + /// # Examples + /// + /// ``` + /// use any_intern::DroplessInternSet; + /// + /// let mut set = DroplessInternSet::new(); + /// assert_eq!(set.len(), 0); + /// + /// set.intern("hello"); + /// assert_eq!(set.len(), 1); + /// ``` pub fn len(&self) -> usize { self.set.len() } /// Returns `true` if the set is empty. + /// + /// # Examples + /// + /// ``` + /// use any_intern::DroplessInternSet; + /// + /// let mut set = DroplessInternSet::new(); + /// assert!(set.is_empty()); + /// + /// set.intern("hello"); + /// assert!(!set.is_empty()); + /// ``` pub fn is_empty(&self) -> bool { self.len() == 0 } /// Removes all items in the set. + /// + /// # Examples + /// + /// ``` + /// use any_intern::DroplessInternSet; + /// + /// let mut set = DroplessInternSet::new(); + /// set.intern("hello"); + /// assert!(!set.is_empty()); + /// + /// set.clear(); + /// assert!(set.is_empty()); + /// ``` pub fn clear(&mut self) { self.bump.reset(); self.set.clear(); diff --git a/crates/any-intern/src/lib.rs b/crates/any-intern/src/lib.rs index 74df70f..61933d9 100644 --- a/crates/any-intern/src/lib.rs +++ b/crates/any-intern/src/lib.rs @@ -68,6 +68,15 @@ pub struct Interner { impl Interner { /// Creates an empty generic interner. + /// + /// # Examples + /// + /// ``` + /// use any_intern::Interner; + /// + /// let interner = Interner::new(); + /// assert!(interner.is_empty()); + /// ``` pub fn new() -> Self { Self::default() } @@ -272,12 +281,37 @@ impl Interner { } /// Returns the number of values the interner contains. + /// + /// # Examples + /// + /// ``` + /// use any_intern::Interner; + /// + /// let interner = Interner::new(); + /// assert_eq!(interner.len(), 0); + /// + /// interner.intern_static(1_u32); + /// interner.intern_dropless("one"); + /// assert_eq!(interner.len(), 2); + /// ``` pub fn len(&self) -> usize { self.with_any_sets(|sets| sets.values().map(AnyInternSet::len).sum::()) + self.dropless.len() } /// Returns `true` if the interner is empty. + /// + /// # Examples + /// + /// ``` + /// use any_intern::Interner; + /// + /// let interner = Interner::new(); + /// assert!(interner.is_empty()); + /// + /// interner.intern_static(1_u32); + /// assert!(!interner.is_empty()); + /// ``` pub fn is_empty(&self) -> bool { self.len() == 0 } @@ -286,6 +320,20 @@ impl Interner { /// /// Although the interner supports interior mutability, `clear` requires mutable access to /// invalidate all [`Interned`] values referencing the interner. + /// + /// # Examples + /// + /// ``` + /// use any_intern::Interner; + /// + /// let mut interner = Interner::new(); + /// interner.intern_static(1_u32); + /// interner.intern_dropless("one"); + /// assert!(!interner.is_empty()); + /// + /// interner.clear(); + /// assert!(interner.is_empty()); + /// ``` pub fn clear(&mut self) { self.with_any_sets(|sets| { for set in sets.values_mut() { diff --git a/crates/any-intern/src/typed.rs b/crates/any-intern/src/typed.rs index 0d459b7..656febb 100644 --- a/crates/any-intern/src/typed.rs +++ b/crates/any-intern/src/typed.rs @@ -17,22 +17,71 @@ pub struct TypedArena { impl TypedArena { /// Returns the number of elements in this arena. + /// + /// # Examples + /// + /// ``` + /// use any_intern::TypedArena; + /// + /// let arena = TypedArena::default(); + /// assert_eq!(arena.len(), 0); + /// + /// arena.alloc(1_u32); + /// assert_eq!(arena.len(), 1); + /// ``` pub fn len(&self) -> usize { self.len.get() } /// Returns `true` if this arena is empty. + /// + /// # Examples + /// + /// ``` + /// use any_intern::TypedArena; + /// + /// let arena = TypedArena::default(); + /// assert!(arena.is_empty()); + /// + /// arena.alloc(1_u32); + /// assert!(!arena.is_empty()); + /// ``` pub fn is_empty(&self) -> bool { self.len() == 0 } /// Allocates `value` in the arena. + /// + /// # Examples + /// + /// ``` + /// use any_intern::TypedArena; + /// + /// let arena = TypedArena::default(); + /// let value = arena.alloc(String::from("hello")); + /// + /// assert_eq!(value, "hello"); + /// assert_eq!(arena.len(), 1); + /// ``` pub fn alloc(&self, value: T) -> &mut T { self.len.set(self.len() + 1); self.bump.alloc(value) } /// Drops all stored values and clears the arena. + /// + /// # Examples + /// + /// ``` + /// use any_intern::TypedArena; + /// + /// let mut arena = TypedArena::default(); + /// arena.alloc(1_u32); + /// assert!(!arena.is_empty()); + /// + /// arena.clear(); + /// assert!(arena.is_empty()); + /// ``` pub fn clear(&mut self) { self.drop_all(); self.bump.reset(); diff --git a/crates/logic-eval-util/src/reference.rs b/crates/logic-eval-util/src/reference.rs index e38a4c4..f3ab6c8 100644 --- a/crates/logic-eval-util/src/reference.rs +++ b/crates/logic-eval-util/src/reference.rs @@ -16,6 +16,17 @@ pub struct Ref<'a, T: 'a + ?Sized> { impl<'a, T: 'a + ?Sized> Ref<'a, T> { /// Creates a pointer wrapper from a shared reference. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::reference::Ref; + /// + /// let value = 42; + /// let ptr = Ref::from_ref(&value); + /// + /// assert_eq!(*ptr.as_ref(), 42); + /// ``` pub const fn from_ref(r: &'a T) -> Self { // Safety: A reference is non-null. let ptr = unsafe { NonNull::new_unchecked(r as *const T as *mut T) }; @@ -26,6 +37,17 @@ impl<'a, T: 'a + ?Sized> Ref<'a, T> { } /// Creates a pointer wrapper from a mutable reference. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::reference::Ref; + /// + /// let mut value = 42; + /// let ptr = Ref::from_mut(&mut value); + /// + /// assert_eq!(*ptr.as_ref(), 42); + /// ``` pub fn from_mut(r: &'a mut T) -> Self { // Safety: A reference is non-null. let ptr = unsafe { NonNull::new_unchecked(r as *mut T) }; @@ -35,9 +57,20 @@ impl<'a, T: 'a + ?Sized> Ref<'a, T> { } } - // Output lifetime is 'a, so we need this method rather than AsRef::as_ref or something like - // that. /// Returns the original shared reference. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::reference::Ref; + /// + /// let value = String::from("hello"); + /// let ptr = Ref::from_ref(&value); + /// + /// assert_eq!(ptr.as_ref(), "hello"); + /// ``` + // + // Output lifetime is 'a, which is the difference from AsRef::as_ref. #[allow(clippy::should_implement_trait)] pub fn as_ref(&self) -> &'a T { // Safety: The type actually has the `&'a T`. @@ -45,11 +78,33 @@ impl<'a, T: 'a + ?Sized> Ref<'a, T> { } /// Returns this wrapper as a non-null pointer. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::reference::Ref; + /// + /// let value = 42; + /// let ptr = Ref::from_ref(&value).as_nonnull(); + /// + /// assert_eq!(ptr.as_ptr(), (&value as *const i32).cast_mut()); + /// ``` pub const fn as_nonnull(self) -> NonNull { self.ptr } /// Returns this wrapper as a raw mutable pointer. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::reference::Ref; + /// + /// let value = 42; + /// let ptr = Ref::from_ref(&value).as_ptr(); + /// + /// assert_eq!(ptr, (&value as *const i32).cast_mut()); + /// ``` pub const fn as_ptr(self) -> *mut T { self.ptr.as_ptr() } diff --git a/crates/logic-eval-util/src/str.rs b/crates/logic-eval-util/src/str.rs index 9555af1..20df8a6 100644 --- a/crates/logic-eval-util/src/str.rs +++ b/crates/logic-eval-util/src/str.rs @@ -11,6 +11,17 @@ pub struct StrPath<'a> { impl<'a> StrPath<'a> { /// Creates an absolute string path. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::str::StrPath; + /// + /// let path = StrPath::absolute("/root/module"); + /// + /// assert!(path.is_absolute); + /// assert_eq!(&*path, "/root/module"); + /// ``` pub const fn absolute(path: &'a str) -> Self { Self { is_absolute: true, @@ -19,6 +30,17 @@ impl<'a> StrPath<'a> { } /// Creates a relative string path. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::str::StrPath; + /// + /// let path = StrPath::relative("local"); + /// + /// assert!(!path.is_absolute); + /// assert_eq!(path.path, "local"); + /// ``` pub const fn relative(path: &'a str) -> Self { Self { is_absolute: false, diff --git a/crates/logic-eval-util/src/symbol.rs b/crates/logic-eval-util/src/symbol.rs index b76dec4..7cba3b7 100644 --- a/crates/logic-eval-util/src/symbol.rs +++ b/crates/logic-eval-util/src/symbol.rs @@ -9,16 +9,53 @@ pub struct SymbolTable { impl SymbolTable { /// Removes every symbol and block marker. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::symbol::SymbolTable; + /// + /// let mut table = SymbolTable::default(); + /// table.push("x", 1); + /// assert!(!table.is_empty()); + /// + /// table.clear(); + /// assert!(table.is_empty()); + /// ``` pub fn clear(&mut self) { self.map.clear(); } /// Returns `true` if the table contains no symbols or block markers. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::symbol::SymbolTable; + /// + /// let mut table = SymbolTable::default(); + /// assert!(table.is_empty()); + /// + /// table.push("x", 1); + /// assert!(!table.is_empty()); + /// ``` pub fn is_empty(&self) -> bool { self.map.is_empty() } /// Pushes a block boundary that allows lookup to continue into outer blocks. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::symbol::SymbolTable; + /// + /// let mut table = SymbolTable::default(); + /// table.push("x", 1); + /// table.push_transparent_block(); + /// + /// assert_eq!(table.get("x"), Some(&1)); + /// ``` pub fn push_transparent_block(&mut self) { for v in self.map.values_mut() { v.push(Symbol::Transparent); @@ -26,6 +63,18 @@ impl SymbolTable { } /// Pushes a block boundary that hides symbols in outer blocks. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::symbol::SymbolTable; + /// + /// let mut table = SymbolTable::default(); + /// table.push("x", 1); + /// table.push_opaque_block(); + /// + /// assert_eq!(table.get("x"), None); + /// ``` pub fn push_opaque_block(&mut self) { for v in self.map.values_mut() { v.push(Symbol::Opaque); @@ -33,6 +82,21 @@ impl SymbolTable { } /// Pops symbols from the current block. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::symbol::SymbolTable; + /// + /// let mut table = SymbolTable::default(); + /// table.push("x", 1); + /// table.push_transparent_block(); + /// table.push("x", 2); + /// + /// assert_eq!(table.get("x"), Some(&2)); + /// table.pop_block(); + /// assert_eq!(table.get("x"), Some(&1)); + /// ``` pub fn pop_block(&mut self) { for v in self.map.values_mut() { while let Some(Symbol::Data(_)) = v.pop() {} @@ -43,6 +107,17 @@ impl SymbolTable { impl SymbolTable { /// Pushes a symbol binding for `name`. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::symbol::SymbolTable; + /// + /// let mut table = SymbolTable::default(); + /// table.push("x", 1); + /// + /// assert_eq!(table.get("x"), Some(&1)); + /// ``` pub fn push(&mut self, name: K, symbol: V) { if let Some(v) = self.map.get_mut(&name) { v.push(Symbol::Data(symbol)); @@ -52,6 +127,19 @@ impl SymbolTable { } /// Pops the most recent symbol binding for `name`. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::symbol::SymbolTable; + /// + /// let mut table = SymbolTable::default(); + /// table.push("x", 1); + /// table.push("x", 2); + /// + /// assert_eq!(table.pop("x"), Some(2)); + /// assert_eq!(table.get("x"), Some(&1)); + /// ``` pub fn pop(&mut self, name: &Q) -> Option where K: Borrow, @@ -64,6 +152,20 @@ impl SymbolTable { } /// Returns the visible symbol binding for `name`. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::symbol::SymbolTable; + /// + /// let mut table = SymbolTable::default(); + /// table.push("x", 1); + /// table.push_transparent_block(); + /// table.push("x", 2); + /// + /// assert_eq!(table.get("x"), Some(&2)); + /// assert_eq!(table.get("y"), None); + /// ``` pub fn get(&self, name: &Q) -> Option<&V> where K: Borrow, @@ -77,6 +179,18 @@ impl SymbolTable { } /// Returns the visible mutable symbol binding for `name`. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::symbol::SymbolTable; + /// + /// let mut table = SymbolTable::default(); + /// table.push("x", 1); + /// + /// *table.get_mut("x").unwrap() = 2; + /// assert_eq!(table.get("x"), Some(&2)); + /// ``` pub fn get_mut(&mut self, name: &Q) -> Option<&mut V> where K: Borrow, diff --git a/crates/logic-eval-util/src/unique.rs b/crates/logic-eval-util/src/unique.rs index a9f366a..96d3978 100644 --- a/crates/logic-eval-util/src/unique.rs +++ b/crates/logic-eval-util/src/unique.rs @@ -21,6 +21,15 @@ pub struct UniqueContainer { impl UniqueContainer { /// Creates an empty container. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::unique::UniqueContainer; + /// + /// let container = UniqueContainer::::new(); + /// assert!(container.is_empty()); + /// ``` pub fn new() -> Self { Self { values: Values::new(), @@ -29,31 +38,107 @@ impl UniqueContainer { } /// Returns the number of indices in the container. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::unique::UniqueContainer; + /// + /// let mut container = UniqueContainer::new(); + /// assert_eq!(container.len(), 0); + /// + /// container.insert("a"); + /// container.insert("a"); + /// assert_eq!(container.len(), 1); + /// ``` pub fn len(&self) -> usize { self.values.len() } /// Returns `true` if the container is empty. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::unique::UniqueContainer; + /// + /// let mut container = UniqueContainer::new(); + /// assert!(container.is_empty()); + /// + /// container.insert("a"); + /// assert!(!container.is_empty()); + /// ``` pub fn is_empty(&self) -> bool { self.len() == 0 } /// Returns the value at `index`, following indirect entries. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::unique::UniqueContainer; + /// + /// let mut container = UniqueContainer::new(); + /// let index = container.insert("a"); + /// + /// assert_eq!(container.get(index), Some(&"a")); + /// assert_eq!(container.get(99), None); + /// ``` pub fn get(&self, index: usize) -> Option<&T> { self.values.get(index) } /// Iterates over `(index, value)` pairs for real data entries. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::unique::UniqueContainer; + /// + /// let mut container = UniqueContainer::new(); + /// container.insert("a"); + /// container.insert("b"); + /// + /// let pairs = container.iter().collect::>(); + /// assert_eq!(pairs, vec![(0, &"a"), (1, &"b")]); + /// ``` pub fn iter(&self) -> PairIter<'_, T> { self.values.iter() } /// Iterates over unique values. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::unique::UniqueContainer; + /// + /// let mut container = UniqueContainer::new(); + /// container.insert("a"); + /// container.insert("b"); + /// + /// let values = container.values().copied().collect::>(); + /// assert_eq!(values, vec!["a", "b"]); + /// ``` pub fn values(&self) -> ValueIter<'_, T> { self.values.values() } /// All indices will be invalidated. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::unique::UniqueContainer; + /// + /// let mut container = UniqueContainer::new(); + /// container.insert("a"); + /// assert!(!container.is_empty()); + /// + /// container.clear(); + /// assert!(container.is_empty()); + /// ``` pub fn clear(&mut self) { self.values.clear(); self.map.clear(); @@ -67,6 +152,20 @@ where /// Shortens the container to the given length. /// /// All indices beyond the length will be invalidated. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::unique::UniqueContainer; + /// + /// let mut container = UniqueContainer::new(); + /// container.insert(1); + /// container.insert(2); + /// + /// container.truncate(1); + /// assert_eq!(container.len(), 1); + /// assert_eq!(container.find(&2), None); + /// ``` pub fn truncate(&mut self, len: usize) { for index in len..self.values.len() { let hash = Self::hash(&self.values[index]); @@ -79,6 +178,19 @@ where /// /// If the same value was found by [`PartialEq`], then the old value is replaced with the given /// new value. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::unique::UniqueContainer; + /// + /// let mut container = UniqueContainer::new(); + /// let first = container.insert("a"); + /// let second = container.insert("a"); + /// + /// assert_eq!(first, second); + /// assert_eq!(container.len(), 1); + /// ``` pub fn insert(&mut self, value: T) -> usize { let hash = Self::hash(&value); if let Some(index) = self._find(hash, &value) { @@ -94,15 +206,21 @@ where index } - /// Returns the existing index for `value`, or the next insertion index. - pub fn next_index(&mut self, value: &Q) -> usize - where - Q: Hash + PartialEq + ?Sized, - { - self.find(value).unwrap_or(self.values.len()) - } - /// Replaces `old` with `new`, returning whether `old` was found. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::unique::UniqueContainer; + /// + /// let mut container = UniqueContainer::new(); + /// container.insert(1); + /// + /// assert!(container.replace(&1, 2)); + /// assert_eq!(container.find(&1), None); + /// assert_eq!(container.find(&2), Some(0)); + /// assert!(!container.replace(&99, 3)); + /// ``` pub fn replace(&mut self, old: &Q, new: T) -> bool where Q: Hash + PartialEq + ?Sized, @@ -151,6 +269,18 @@ where } /// Finds the index of `value`. + /// + /// # Examples + /// + /// ``` + /// use logic_eval_util::unique::UniqueContainer; + /// + /// let mut container = UniqueContainer::new(); + /// let index = container.insert(1); + /// + /// assert_eq!(container.find(&1), Some(index)); + /// assert_eq!(container.find(&2), None); + /// ``` pub fn find(&self, value: &Q) -> Option where Q: Hash + PartialEq + ?Sized, diff --git a/crates/logic-eval/src/parse/common.rs b/crates/logic-eval/src/parse/common.rs index dbc2b25..24fe0ff 100644 --- a/crates/logic-eval/src/parse/common.rs +++ b/crates/logic-eval/src/parse/common.rs @@ -9,9 +9,31 @@ pub trait Intern { Self: 'a; /// Interns a string slice. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{Intern, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let name = interner.intern_str("sunny"); + /// + /// assert_eq!(&*name, "sunny"); + /// ``` fn intern_str(&self, s: &str) -> Self::Interned<'_>; /// Formats a value into an interned string using at most `upper_size` bytes. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{Intern, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let name = interner.intern_formatted_str(&42, 2).unwrap(); + /// + /// assert_eq!(&*name, "42"); + /// ``` fn intern_formatted_str( &self, value: &T, diff --git a/crates/logic-eval/src/parse/inner.rs b/crates/logic-eval/src/parse/inner.rs index 8985b5f..3c200c1 100644 --- a/crates/logic-eval/src/parse/inner.rs +++ b/crates/logic-eval/src/parse/inner.rs @@ -1,6 +1,17 @@ use crate::{Error, Intern, Result}; /// Parses `text` as `T` using the provided interner. +/// +/// # Examples +/// +/// ``` +/// use logic_eval::{parse_str, Clause, StrInterner}; +/// +/// let interner = StrInterner::new(); +/// let clause: Clause<_> = parse_str("parent(alice, bob).", &interner).unwrap(); +/// +/// assert_eq!(clause.to_string(), "parent(alice, bob)."); +/// ``` pub fn parse_str<'int, Int, T>(text: &str, interner: &'int Int) -> Result where Int: Intern, @@ -28,7 +39,7 @@ pub struct ParseBuffer<'a> { impl<'a> ParseBuffer<'a> { /// Creates a buffer that spans the whole input string. - pub const fn new(text: &'a str) -> Self { + pub(crate) const fn new(text: &'a str) -> Self { Self { text, start: 0, diff --git a/crates/logic-eval/src/parse/repr.rs b/crates/logic-eval/src/parse/repr.rs index 30c085e..2221344 100644 --- a/crates/logic-eval/src/parse/repr.rs +++ b/crates/logic-eval/src/parse/repr.rs @@ -40,11 +40,35 @@ pub struct Clause { impl Clause { /// Creates a fact clause. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{Clause, Term}; + /// + /// let clause = Clause::fact(Term::atom("sunny")); + /// + /// assert_eq!(clause.to_string(), "sunny."); + /// assert!(clause.body.is_none()); + /// ``` pub fn fact(head: Term) -> Self { Self { head, body: None } } /// Creates a rule clause with a head and body. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{Clause, Expr, Term}; + /// + /// let clause = Clause::rule( + /// Term::atom("outdoors"), + /// Expr::term(Term::atom("sunny")), + /// ); + /// + /// assert_eq!(clause.to_string(), "outdoors :- sunny."); + /// ``` pub fn rule(head: Term, body: Expr) -> Self { Self { head, @@ -53,6 +77,17 @@ impl Clause { } /// Maps every atom in the clause to another type. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{Clause, Term}; + /// + /// let clause = Clause::fact(Term::atom("sunny")); + /// let mapped = clause.map(&mut |name| name.len()); + /// + /// assert_eq!(mapped.head.functor, 5); + /// ``` pub fn map U>(self, f: &mut F) -> Clause { Clause { head: self.head.map(f), @@ -61,6 +96,23 @@ impl Clause { } /// Replaces every matching term in the clause. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{Clause, Expr, Term}; + /// + /// let mut clause = Clause::rule( + /// Term::atom("outdoors"), + /// Expr::term(Term::atom("sunny")), + /// ); + /// + /// clause.replace_term(&mut |term| { + /// (term.functor == "sunny").then(|| Term::atom("clear")) + /// }); + /// + /// assert_eq!(clause.to_string(), "outdoors :- clear."); + /// ``` pub fn replace_term(&mut self, f: &mut F) where F: FnMut(&Term) -> Option>, @@ -132,6 +184,17 @@ pub struct Term { impl Term { /// Creates an atom term with no arguments. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::Term; + /// + /// let term = Term::atom("alice"); + /// + /// assert_eq!(term.to_string(), "alice"); + /// assert!(term.args.is_empty()); + /// ``` pub fn atom(functor: T) -> Self { Term { functor, @@ -140,6 +203,16 @@ impl Term { } /// Creates a compound term from a functor and arguments. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::Term; + /// + /// let term = Term::compound("parent", [Term::atom("alice"), Term::atom("bob")]); + /// + /// assert_eq!(term.to_string(), "parent(alice, bob)"); + /// ``` pub fn compound>>(functor: T, args: I) -> Self { Term { functor, @@ -148,6 +221,18 @@ impl Term { } /// Maps every atom in the term to another type. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::Term; + /// + /// let term = Term::compound("parent", [Term::atom("alice")]); + /// let mapped = term.map(&mut |name| name.len()); + /// + /// assert_eq!(mapped.functor, 6); + /// assert_eq!(mapped.args[0].functor, 5); + /// ``` pub fn map U>(self, f: &mut F) -> Term { Term { functor: f(self.functor), @@ -156,6 +241,20 @@ impl Term { } /// Replaces this term and all nested terms that match `f`. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::Term; + /// + /// let mut term = Term::compound("parent", [Term::atom("alice")]); + /// let replaced = term.replace_all(&mut |term| { + /// (term.functor == "alice").then(|| Term::atom("carol")) + /// }); + /// + /// assert!(replaced); + /// assert_eq!(term.to_string(), "parent(carol)"); + /// ``` pub fn replace_all(&mut self, f: &mut F) -> bool where F: FnMut(&Term) -> Option>, @@ -175,6 +274,17 @@ impl Term { impl Term { /// Returns this term's predicate. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::Term; + /// + /// let predicate = Term::compound("parent", [Term::atom("alice")]).predicate(); + /// + /// assert_eq!(predicate.functor, "parent"); + /// assert_eq!(predicate.arity, 1); + /// ``` pub fn predicate(&self) -> Predicate { Predicate { functor: self.functor.clone(), @@ -185,6 +295,17 @@ impl Term { impl Term { /// Returns `true` if this term is a variable. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{Name, StrInterner, Term}; + /// + /// let interner = StrInterner::new(); + /// let term = Term::atom(Name::with_intern("$X", &interner)); + /// + /// assert!(term.is_variable()); + /// ``` pub fn is_variable(&self) -> bool { let is_variable = self.functor.is_variable(); @@ -197,6 +318,20 @@ impl Term { } /// Returns `true` if this term contains a variable. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{Name, StrInterner, Term}; + /// + /// let interner = StrInterner::new(); + /// let term = Term::compound(Name::with_intern("pair", &interner), [ + /// Term::atom(Name::with_intern("alice", &interner)), + /// Term::atom(Name::with_intern("$X", &interner)), + /// ]); + /// + /// assert!(term.contains_variable()); + /// ``` pub fn contains_variable(&self) -> bool { if self.is_variable() { return true; @@ -206,6 +341,18 @@ impl Term { } /// Applies `f` to every variable functor in the term. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{Name, StrInterner, Term}; + /// + /// let interner = StrInterner::new(); + /// let mut term = Term::atom(Name::with_intern("$X", &interner)); + /// + /// term.replace_variables(|name| *name = Name::with_intern("$Y", &interner)); + /// assert_eq!(term.to_string(), "$Y"); + /// ``` pub fn replace_variables(&mut self, mut f: F) { fn helper(term: &mut Term, f: &mut F) where @@ -256,36 +403,107 @@ pub enum Expr { impl Expr { /// Creates an expression from a term. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{Expr, Term}; + /// + /// let expr = Expr::term(Term::atom("sunny")); + /// + /// assert_eq!(expr.to_string(), "sunny"); + /// ``` pub fn term(term: Term) -> Self { Self::Term(term) } /// Creates an atom term expression. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::Expr; + /// + /// let expr = Expr::term_atom("sunny"); + /// + /// assert_eq!(expr.to_string(), "sunny"); + /// ``` pub fn term_atom(functor: T) -> Self { Self::Term(Term::atom(functor)) } /// Creates a compound term expression. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{Expr, Term}; + /// + /// let expr = Expr::term_compound("parent", [Term::atom("alice"), Term::atom("bob")]); + /// + /// assert_eq!(expr.to_string(), "parent(alice, bob)"); + /// ``` pub fn term_compound>>(functor: T, args: I) -> Self { Self::Term(Term::compound(functor, args)) } /// Creates a negated expression. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::Expr; + /// + /// let expr = Expr::expr_not(Expr::term_atom("rainy")); + /// + /// assert_eq!(expr.to_string(), "\\+ rainy"); + /// ``` pub fn expr_not(expr: Expr) -> Self { Self::Not(Box::new(expr)) } /// Creates a conjunction expression. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::Expr; + /// + /// let expr = Expr::expr_and([Expr::term_atom("sunny"), Expr::term_atom("warm")]); + /// + /// assert_eq!(expr.to_string(), "sunny, warm"); + /// ``` pub fn expr_and>>(args: I) -> Self { Self::And(args.into_iter().collect()) } /// Creates a disjunction expression. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::Expr; + /// + /// let expr = Expr::expr_or([Expr::term_atom("tea"), Expr::term_atom("coffee")]); + /// + /// assert_eq!(expr.to_string(), "tea; coffee"); + /// ``` pub fn expr_or>>(args: I) -> Self { Self::Or(args.into_iter().collect()) } /// Maps every atom in the expression to another type. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::Expr; + /// + /// let expr = Expr::expr_and([Expr::term_atom("sunny"), Expr::term_atom("warm")]); + /// let mapped = expr.map(&mut |name| name.len()); + /// + /// assert_eq!(mapped.to_string(), "5, 4"); + /// ``` pub fn map U>(self, f: &mut F) -> Expr { match self { Self::Term(term) => Expr::Term(term.map(f)), @@ -296,6 +514,19 @@ impl Expr { } /// Replaces every matching term in the expression. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{Expr, Term}; + /// + /// let mut expr = Expr::expr_and([Expr::term_atom("sunny"), Expr::term_atom("warm")]); + /// expr.replace_term(&mut |term| { + /// (term.functor == "warm").then(|| Term::atom("dry")) + /// }); + /// + /// assert_eq!(expr.to_string(), "sunny, dry"); + /// ``` pub fn replace_term(&mut self, f: &mut F) where F: FnMut(&Term) -> Option>, @@ -316,6 +547,17 @@ impl Expr { impl Expr { /// Returns `true` if this expression contains `term`. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{Expr, Term}; + /// + /// let expr = Expr::expr_and([Expr::term_atom("sunny"), Expr::term_atom("warm")]); + /// + /// assert!(expr.contains_term(&Term::atom("warm"))); + /// assert!(!expr.contains_term(&Term::atom("rainy"))); + /// ``` pub fn contains_term(&self, term: &Term) -> bool { match self { Self::Term(t) => t == term, @@ -325,6 +567,19 @@ impl Expr { } /// e.g. ¬(A ∧ (B ∨ C)) -> ¬A ∨ (¬B ∧ ¬C) + /// + /// # Examples + /// + /// ``` + /// use logic_eval::Expr; + /// + /// let expr = Expr::expr_not(Expr::expr_and([ + /// Expr::term_atom("a"), + /// Expr::term_atom("b"), + /// ])); + /// + /// assert_eq!(expr.distribute_not().to_string(), "\\+ a; \\+ b"); + /// ``` pub fn distribute_not(self) -> Self { match self { Self::Term(term) => Self::Term(term), diff --git a/crates/logic-eval/src/parse/text.rs b/crates/logic-eval/src/parse/text.rs index 8355235..1fef56b 100644 --- a/crates/logic-eval/src/parse/text.rs +++ b/crates/logic-eval/src/parse/text.rs @@ -215,6 +215,16 @@ pub struct Name(T); impl> Name { /// Creates a non-empty name. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::Name; + /// + /// let name = Name::new("sunny"); + /// + /// assert_eq!(name.as_ref(), "sunny"); + /// ``` pub fn new(s: T) -> Self { assert!(!s.as_ref().is_empty()); Self(s) @@ -223,6 +233,17 @@ impl> Name { impl Name<()> { /// Interns `s` and returns a name backed by the interner. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{Name, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let name = Name::with_intern("sunny", &interner); + /// + /// assert_eq!(name.as_ref(), "sunny"); + /// ``` pub fn with_intern<'int, Int: Intern>(s: &str, interner: &'int Int) -> NameIn<'int, Int> { assert!(!s.is_empty()); let interned = interner.intern_str(s); diff --git a/crates/logic-eval/src/prove/common.rs b/crates/logic-eval/src/prove/common.rs index 5c21bbd..eca3065 100644 --- a/crates/logic-eval/src/prove/common.rs +++ b/crates/logic-eval/src/prove/common.rs @@ -4,6 +4,19 @@ use core::hash::Hash; /// Atom type used in parsed and proved terms. pub trait Atom: Clone + Eq + Hash { /// Returns `true` when this atom represents a variable. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{Atom, Name, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let variable = Name::with_intern("$X", &interner); + /// let constant = Name::with_intern("alice", &interner); + /// + /// assert!(variable.is_variable()); + /// assert!(!constant.is_variable()); + /// ``` fn is_variable(&self) -> bool; } diff --git a/crates/logic-eval/src/prove/db.rs b/crates/logic-eval/src/prove/db.rs index 54d2105..bb089c7 100644 --- a/crates/logic-eval/src/prove/db.rs +++ b/crates/logic-eval/src/prove/db.rs @@ -49,6 +49,17 @@ pub struct Database { impl Database { /// Creates an empty database. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{Database, InternedStr, Name}; + /// + /// type Db<'a> = Database>>; + /// + /// let db: Db<'_> = Database::new(); + /// assert_eq!(db.clauses().count(), 0); + /// ``` pub fn new() -> Self { Self { clauses: IndexMap::default(), @@ -62,6 +73,21 @@ impl Database { } /// Iterates over all terms stored in the database. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, Clause, Database, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let clause: Clause<_> = parse_str("sunny.", &interner).unwrap(); + /// let mut db = Database::new(); + /// db.insert_clause(clause); + /// db.commit(); + /// + /// let terms = db.terms().map(|term| term.to_string()).collect::>(); + /// assert_eq!(terms, vec!["sunny"]); + /// ``` pub fn terms(&self) -> NamedTermViewIter<'_, T> { NamedTermViewIter { term_iter: self.stor.terms.terms(), @@ -70,6 +96,21 @@ impl Database { } /// Iterates over all clauses stored in the database. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, Clause, Database, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let clause: Clause<_> = parse_str("sunny.", &interner).unwrap(); + /// let mut db = Database::new(); + /// db.insert_clause(clause); + /// db.commit(); + /// + /// let clauses = db.clauses().map(|clause| clause.to_string()).collect::>(); + /// assert_eq!(clauses, vec!["sunny."]); + /// ``` pub fn clauses(&self) -> ClauseIter<'_, T> { ClauseIter { clauses: &self.clauses, @@ -81,6 +122,21 @@ impl Database { } /// Inserts every clause from `dataset`. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, ClauseDataset, Database, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let dataset: ClauseDataset<_> = parse_str("sunny.\nwarm.", &interner).unwrap(); + /// let mut db = Database::new(); + /// + /// db.insert_dataset(dataset); + /// db.commit(); + /// + /// assert_eq!(db.clauses().count(), 2); + /// ``` pub fn insert_dataset(&mut self, dataset: ClauseDataset) { for clause in dataset { self.insert_clause(clause); @@ -88,6 +144,22 @@ impl Database { } /// Inserts the given clause into the database. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, Clause, Database, Expr, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let clause: Clause<_> = parse_str("sunny.", &interner).unwrap(); + /// let query: Expr<_> = parse_str("sunny", &interner).unwrap(); + /// let mut db = Database::new(); + /// + /// db.insert_clause(clause); + /// db.commit(); + /// + /// assert!(db.query(query).is_true()); + /// ``` pub fn insert_clause(&mut self, clause: Clause) { // Saves current state. We will revert DB when the change is not committed. if self.revert_point.is_none() { @@ -123,6 +195,25 @@ impl Database { } /// Starts a query against the committed database. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, ClauseDataset, Database, Expr, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let dataset: ClauseDataset<_> = parse_str("parent(alice, bob).", &interner).unwrap(); + /// let query: Expr<_> = parse_str("parent(alice, $Who)", &interner).unwrap(); + /// let mut db = Database::new(); + /// db.insert_dataset(dataset); + /// db.commit(); + /// + /// let mut cx = db.query(query); + /// let answer = cx.prove_next().unwrap().next().unwrap(); + /// + /// assert_eq!(answer.get_lhs_variable().as_ref(), "$Who"); + /// assert_eq!(answer.rhs().to_string(), "bob"); + /// ``` pub fn query(&mut self, expr: Expr) -> ProveCx<'_, T> { // Discards uncommitted changes. if let Some(revert_point) = self.revert_point.take() { @@ -139,6 +230,22 @@ impl Database { } /// Commits pending clause insertions. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, Clause, Database, Expr, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let clause: Clause<_> = parse_str("sunny.", &interner).unwrap(); + /// let query: Expr<_> = parse_str("sunny", &interner).unwrap(); + /// let mut db = Database::new(); + /// + /// db.insert_clause(clause); + /// db.commit(); + /// + /// assert!(db.query(query).is_true()); + /// ``` pub fn commit(&mut self) { self.revert_point.take(); } @@ -147,6 +254,21 @@ impl Database { /// /// Requires T to implement [`AsRef`] so that functor names can be serialized into Prolog /// syntax. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, ClauseDataset, Database, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let dataset: ClauseDataset<_> = parse_str("parent(Alice, Bob).", &interner).unwrap(); + /// let mut db = Database::new(); + /// db.insert_dataset(dataset); + /// db.commit(); + /// + /// let prolog = db.to_prolog(|name| name); + /// assert_eq!(prolog, "parent(alice, bob).\n"); + /// ``` pub fn to_prolog &str>(&self, sanitize: F) -> String where T: AsRef, @@ -515,12 +637,42 @@ pub struct ClauseRef<'a, T> { impl<'a, T: Atom> ClauseRef<'a, T> { /// Returns the clause head. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, Clause, Database, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let clause: Clause<_> = parse_str("outdoors :- sunny.", &interner).unwrap(); + /// let mut db = Database::new(); + /// db.insert_clause(clause); + /// db.commit(); + /// + /// let clause = db.clauses().next().unwrap(); + /// assert_eq!(clause.head().to_string(), "outdoors"); + /// ``` pub fn head(&self) -> NamedTermView<'a, T> { let head = self.stor.get_term(self.id.head); NamedTermView::new(head, self.nimap) } /// Returns the clause body, if this clause is a rule. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, Clause, Database, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let clause: Clause<_> = parse_str("outdoors :- sunny.", &interner).unwrap(); + /// let mut db = Database::new(); + /// db.insert_clause(clause); + /// db.commit(); + /// + /// let clause = db.clauses().next().unwrap(); + /// assert_eq!(clause.body().unwrap().to_string(), "sunny"); + /// ``` pub fn body(&self) -> Option> { self.id.body.map(|id| { let body = self.stor.get_expr(id); diff --git a/crates/logic-eval/src/prove/prover.rs b/crates/logic-eval/src/prove/prover.rs index 1222eac..8346c27 100644 --- a/crates/logic-eval/src/prove/prover.rs +++ b/crates/logic-eval/src/prove/prover.rs @@ -764,6 +764,30 @@ pub struct ProveCx<'a, T: Atom> { impl<'a, T: Atom> ProveCx<'a, T> { /// Returns the next proof result, if one is available. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, ClauseDataset, Database, Expr, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let dataset: ClauseDataset<_> = + /// parse_str("parent(alice, bob). parent(alice, carol).", &interner).unwrap(); + /// let query: Expr<_> = parse_str("parent(alice, $Who)", &interner).unwrap(); + /// let mut db = Database::new(); + /// db.insert_dataset(dataset); + /// db.commit(); + /// + /// let mut cx = db.query(query); + /// let mut answers = Vec::new(); + /// while let Some(answer) = cx.prove_next() { + /// for assignment in answer { + /// answers.push(assignment.rhs().to_string()); + /// } + /// } + /// answers.sort_unstable(); + /// assert_eq!(answers, vec!["bob", "carol"]); + /// ``` pub fn prove_next(&mut self) -> Option> { while let Some(node_index) = self.prover.queue.pop() { if let Some(proof_result) = @@ -788,6 +812,21 @@ impl<'a, T: Atom> ProveCx<'a, T> { } /// Returns `true` if the query has at least one proof. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, ClauseDataset, Database, Expr, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let dataset: ClauseDataset<_> = parse_str("sunny.", &interner).unwrap(); + /// let query: Expr<_> = parse_str("sunny", &interner).unwrap(); + /// let mut db = Database::new(); + /// db.insert_dataset(dataset); + /// db.commit(); + /// + /// assert!(db.query(query).is_true()); + /// ``` pub fn is_true(mut self) -> bool { self.prove_next().is_some() } @@ -863,6 +902,24 @@ impl<'a, T: 'a> Assignment<'a, T> { /// Returns the left-hand-side variable name of the assignment. /// /// Note that the assignment's left-hand side is always a variable. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, ClauseDataset, Database, Expr, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let dataset: ClauseDataset<_> = parse_str("parent(alice, bob).", &interner).unwrap(); + /// let query: Expr<_> = parse_str("parent(alice, $Who)", &interner).unwrap(); + /// let mut db = Database::new(); + /// db.insert_dataset(dataset); + /// db.commit(); + /// + /// let mut cx = db.query(query); + /// let assignment = cx.prove_next().unwrap().next().unwrap(); + /// + /// assert_eq!(assignment.get_lhs_variable().as_ref(), "$Who"); + /// ``` pub fn get_lhs_variable(&self) -> &T { let int = self.lhs_view().find_variable().unwrap(); self.nimap.get_name(&int).unwrap() @@ -888,6 +945,24 @@ impl<'a, T: Atom + 'a> Assignment<'a, T> { /// Creates the left-hand-side term of the assignment. /// /// Creating a term may allocate memory. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, ClauseDataset, Database, Expr, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let dataset: ClauseDataset<_> = parse_str("parent(alice, bob).", &interner).unwrap(); + /// let query: Expr<_> = parse_str("parent(alice, $Who)", &interner).unwrap(); + /// let mut db = Database::new(); + /// db.insert_dataset(dataset); + /// db.commit(); + /// + /// let mut cx = db.query(query); + /// let assignment = cx.prove_next().unwrap().next().unwrap(); + /// + /// assert_eq!(assignment.lhs().to_string(), "$Who"); + /// ``` pub fn lhs(&self) -> Term { Self::term_view_to_term(self.lhs_view(), self.nimap) } @@ -895,6 +970,24 @@ impl<'a, T: Atom + 'a> Assignment<'a, T> { /// Creates the right-hand-side term of the assignment. /// /// Creating a term may allocate memory. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, ClauseDataset, Database, Expr, StrInterner}; + /// + /// let interner = StrInterner::new(); + /// let dataset: ClauseDataset<_> = parse_str("parent(alice, bob).", &interner).unwrap(); + /// let query: Expr<_> = parse_str("parent(alice, $Who)", &interner).unwrap(); + /// let mut db = Database::new(); + /// db.insert_dataset(dataset); + /// db.commit(); + /// + /// let mut cx = db.query(query); + /// let assignment = cx.prove_next().unwrap().next().unwrap(); + /// + /// assert_eq!(assignment.rhs().to_string(), "bob"); + /// ``` pub fn rhs(&self) -> Term { Self::term_deep_view_to_term(self.rhs_view(), self.nimap) } @@ -1185,6 +1278,23 @@ pub(crate) mod format { } impl<'a, T: Atom> NamedTermView<'a, T> { + /// Returns `true` if this view is equal to `term`. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, Clause, Database, StrInterner, Term}; + /// + /// let interner = StrInterner::new(); + /// let clause: Clause<_> = parse_str("parent(alice, bob).", &interner).unwrap(); + /// let expected: Term<_> = parse_str("parent(alice, bob)", &interner).unwrap(); + /// let mut db = Database::new(); + /// db.insert_clause(clause); + /// db.commit(); + /// + /// let term = db.terms().next().unwrap(); + /// assert!(term.is(&expected)); + /// ``` pub fn is(&self, term: &Term) -> bool { let functor = self.view.functor(); let Some(functor) = self.nimap.get_name(functor) else { @@ -1198,6 +1308,23 @@ pub(crate) mod format { self.args().zip(&term.args).all(|(l, r)| l.is(r)) } + /// Returns `true` if this view contains `term` as itself or a nested argument. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, Clause, Database, StrInterner, Term}; + /// + /// let interner = StrInterner::new(); + /// let clause: Clause<_> = parse_str("parent(alice, bob).", &interner).unwrap(); + /// let expected: Term<_> = parse_str("bob", &interner).unwrap(); + /// let mut db = Database::new(); + /// db.insert_clause(clause); + /// db.commit(); + /// + /// let term = db.terms().next().unwrap(); + /// assert!(term.contains(&expected)); + /// ``` pub fn contains(&self, term: &Term) -> bool { if self.is(term) { return true; @@ -1338,6 +1465,23 @@ pub(crate) mod format { } impl<'a, T: Atom> NamedExprView<'a, T> { + /// Returns `true` if this expression view contains `term`. + /// + /// # Examples + /// + /// ``` + /// use logic_eval::{parse_str, Clause, Database, StrInterner, Term}; + /// + /// let interner = StrInterner::new(); + /// let clause: Clause<_> = parse_str("outdoors :- sunny, warm.", &interner).unwrap(); + /// let expected: Term<_> = parse_str("warm", &interner).unwrap(); + /// let mut db = Database::new(); + /// db.insert_clause(clause); + /// db.commit(); + /// + /// let body = db.clauses().next().unwrap().body().unwrap(); + /// assert!(body.contains_term(&expected)); + /// ``` pub fn contains_term(&self, term: &Term) -> bool { match self.view.as_kind() { ExprKind::Term(view) => NamedTermView {