From 936997995c637222d58584ce26a204e2394ee9ab Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Sun, 23 Apr 2023 18:54:26 +0200 Subject: [PATCH] Add Display impls for type, name, variable --- src/types/name.rs | 18 ++++++ src/types/type.rs | 44 ++++++++++++++ src/types/typed.rs | 6 +- src/types/typed_list.rs | 125 +++++++++++++++++++++++++++++++++++++++- src/types/variable.rs | 23 ++++++++ 5 files changed, 210 insertions(+), 6 deletions(-) diff --git a/src/types/name.rs b/src/types/name.rs index 917f15e..3888f34 100644 --- a/src/types/name.rs +++ b/src/types/name.rs @@ -1,6 +1,7 @@ //! Contains names. use crate::types::{PrimitiveType, ToTyped, Type, Typed}; +use std::fmt::{Display, Formatter}; use std::ops::Deref; /// A name. @@ -56,3 +57,20 @@ impl<'a> Deref for Name<'a> { &self.0 } } + +impl<'a> Display for Name<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let name = Name::new("x"); + assert_eq!(format!("{name}"), "x"); + } +} diff --git a/src/types/type.rs b/src/types/type.rs index 9304fef..7a8cbf6 100644 --- a/src/types/type.rs +++ b/src/types/type.rs @@ -1,6 +1,7 @@ //! Contains types. use crate::types::Name; +use std::fmt::{Display, Formatter}; use std::ops::Deref; /// The `object` type. @@ -30,6 +31,14 @@ impl<'a> Type<'a> { /// The predefined type `number`. pub const NUMBER: Type<'a> = Type::Exactly(TYPE_NUMBER); + pub fn new_exactly>>(t: S) -> Self { + Self::Exactly(t.into()) + } + + pub fn new_either, P: Into>>(iter: T) -> Self { + Self::EitherOf(iter.into_iter().map(|x| x.into()).collect()) + } + pub fn len(&self) -> usize { match self { Type::Exactly(_) => 1, @@ -102,3 +111,38 @@ impl<'a> Deref for PrimitiveType<'a> { &self.0 } } + +impl<'a> Display for Type<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Type::Exactly(x) => write!(f, "{}", x), + Type::EitherOf(xs) => { + let xs: Vec<_> = xs.iter().map(|p| p.to_string()).collect(); + write!(f, "(either {})", xs.join(" ")) + } + } + } +} + +impl<'a> Display for PrimitiveType<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn simple_works() { + let t = Type::new_exactly("location"); + assert_eq!(format!("{t}"), "location"); + } + + #[test] + fn either_works() { + let t = Type::new_either(["location", "memory"]); + assert_eq!(format!("{t}"), "(either location memory)"); + } +} diff --git a/src/types/typed.rs b/src/types/typed.rs index 8b4f570..d0c7e58 100644 --- a/src/types/typed.rs +++ b/src/types/typed.rs @@ -16,11 +16,11 @@ impl<'a, O> Typed<'a, O> { Self::new(value, Type::OBJECT) } - pub const fn value_ref(&self) -> &O { + pub const fn value(&self) -> &O { &self.0 } - pub const fn type_ref(&self) -> &Type<'a> { + pub const fn type_name(&self) -> &Type<'a> { &self.1 } } @@ -69,6 +69,6 @@ impl<'a, O> Deref for Typed<'a, O> { type Target = O; fn deref(&self) -> &Self::Target { - self.value_ref() + self.value() } } diff --git a/src/types/typed_list.rs b/src/types/typed_list.rs index b79fc57..138fcc8 100644 --- a/src/types/typed_list.rs +++ b/src/types/typed_list.rs @@ -1,4 +1,5 @@ -use crate::types::Typed; +use crate::types::{Type, Typed}; +use std::fmt::{Display, Formatter}; use std::ops::Deref; /// A list of typed elements. @@ -12,8 +13,8 @@ use std::ops::Deref; /// ]); /// /// assert_eq!(tl.len(), 2); -/// assert_eq!(tl[0].value_ref(), &Name::from("location")); -/// assert_eq!(tl[1].value_ref(), &Name::from("physob")); +/// assert_eq!(tl[0].value(), &Name::from("location")); +/// assert_eq!(tl[1].value(), &Name::from("physob")); /// ``` #[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct TypedList<'a, T>(Vec>); @@ -70,3 +71,121 @@ impl<'a, T> IntoIterator for TypedList<'a, T> { self.0.into_iter() } } + +impl<'a, T> Display for TypedList<'a, T> +where + T: Display, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if self.is_empty() { + return Ok(()); + } + + let mut stack = Vec::with_capacity(self.len()); + let mut type_required = false; + let mut last_type = &Type::OBJECT; + + for item in self.iter().rev() { + // The `object` type is the default (for non-fluents) but can only + // be omitted if it is the last type displayed. + if !type_required && item.type_name().eq(&Type::OBJECT) { + stack.push(format!("{}", item.value())); + continue; + } + + // The moment a non-`object` type is noticed, we are required to list names. + type_required = true; + + // If the type is identical to the one of the previous iteration, + // it was already stated (since we are iterating backwards). + // Therefore, we can skip it. + if last_type.eq(item.type_name()) { + stack.push(format!("{}", item.value())) + } else { + stack.push(format!("{} - {}", item.value(), item.type_name())); + last_type = item.type_name(); + } + } + + stack = stack.into_iter().rev().collect(); + write!(f, "{}", stack.join(" ")) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::types::{Name, ToTyped, Type, Variable}; + + #[test] + fn object_is_default_works() { + let name = TypedList::from_iter([Name::new("x").to_typed(Type::OBJECT)]); + assert_eq!(format!("{name}"), "x"); + } + + #[test] + fn multiple_object_works() { + let name = TypedList::from_iter([ + Name::new("x").to_typed(Type::OBJECT), + Name::new("y").to_typed(Type::OBJECT), + Name::new("z").to_typed(Type::OBJECT), + ]); + assert_eq!(format!("{name}"), "x y z"); + } + + #[test] + fn simple_works() { + let name = TypedList::from_iter([Name::new("x").to_typed(Type::new_exactly("letter"))]); + assert_eq!(format!("{name}"), "x - letter"); + } + + #[test] + fn multiple_same_works() { + let name = TypedList::from_iter([ + Name::new("x").to_typed(Type::new_exactly("letter")), + Name::new("y").to_typed(Type::new_exactly("letter")), + Name::new("z").to_typed(Type::new_exactly("letter")), + ]); + assert_eq!(format!("{name}"), "x y z - letter"); + } + + #[test] + fn interleaved_at_end() { + let name = TypedList::from_iter([ + Name::new("x").to_typed(Type::new_exactly("letter")), + Name::new("y").to_typed(Type::new_exactly("letter")), + Name::new("z").to_typed(Type::new_exactly("car")), + ]); + assert_eq!(format!("{name}"), "x y - letter z - car"); + } + + #[test] + fn interleaved() { + let name = TypedList::from_iter([ + Name::new("x").to_typed(Type::new_exactly("letter")), + Name::new("y").to_typed(Type::new_exactly("car")), + Name::new("z").to_typed(Type::new_exactly("letter")), + ]); + assert_eq!(format!("{name}"), "x - letter y - car z - letter"); + } + + #[test] + fn object_at_end() { + let name = TypedList::from_iter([ + Name::new("x").to_typed(Type::new_exactly("letter")), + Name::new("y").to_typed(Type::OBJECT), + Name::new("z").to_typed(Type::OBJECT), + ]); + assert_eq!(format!("{name}"), "x - letter y z"); + } + + #[test] + fn object_at_start() { + let name = TypedList::from_iter([ + Variable::new("x").to_typed(Type::OBJECT), + Variable::new("y").to_typed(Type::OBJECT), + Variable::new("z").to_typed(Type::new_exactly("letter")), + ]); + assert_eq!(format!("{name}"), "?x ?y - object ?z - letter"); + } +} diff --git a/src/types/variable.rs b/src/types/variable.rs index 5d1e1f4..bc06f56 100644 --- a/src/types/variable.rs +++ b/src/types/variable.rs @@ -1,6 +1,7 @@ //! Contains variables. use crate::types::{Name, PrimitiveType, ToTyped, Type, Typed}; +use std::fmt::{Display, Formatter}; use std::ops::Deref; /// A variable name. @@ -8,6 +9,11 @@ use std::ops::Deref; pub struct Variable<'a>(Name<'a>); impl<'a> Variable<'a> { + #[inline(always)] + pub fn new>>(name: N) -> Self { + Self(name.into()) + } + #[inline(always)] pub const fn from_str(name: &'a str) -> Self { Self(Name::new(name)) @@ -67,3 +73,20 @@ impl<'a> Deref for Variable<'a> { &self.0 } } + +impl<'a> Display for Variable<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "?{}", self.0) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let var = Variable::from_str("x"); + assert_eq!(format!("{var}"), "?x"); + } +}