From 8a14aad99e59e02a0f1972352ae365f8afcdfdde Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Sun, 23 Apr 2023 18:54:26 +0200 Subject: [PATCH 1/6] Add Display impls for type, name, variable --- src/types/name.rs | 12 ++++ src/types/type.rs | 39 +++++++++++++ src/types/typed_list.rs | 121 +++++++++++++++++++++++++++++++++++++++- src/types/variable.rs | 18 ++++++ 4 files changed, 189 insertions(+), 1 deletion(-) diff --git a/src/types/name.rs b/src/types/name.rs index f933e84..01c1bda 100644 --- a/src/types/name.rs +++ b/src/types/name.rs @@ -183,6 +183,12 @@ impl PartialEq for Name { } } +impl<'a> Display for Name { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + impl ToTyped for Name { fn to_typed>(self, r#type: I) -> Typed { Typed::new(self, r#type.into()) @@ -260,6 +266,12 @@ mod tests { use super::*; use nom_greedyerror::AsStr; + #[test] + fn test_display() { + let name = Name::new("x"); + assert_eq!(format!("{name}"), "x"); + } + #[test] fn map_to_static_works() { let object = Name::map_to_static("object").expect("mapping works"); diff --git a/src/types/type.rs b/src/types/type.rs index 9af6c3f..6caf59d 100644 --- a/src/types/type.rs +++ b/src/types/type.rs @@ -2,6 +2,7 @@ use crate::types::iterators::FlatteningIntoIterator; use crate::types::Name; +use std::fmt::{Display, Formatter}; use std::ops::Deref; /// The `object` type. @@ -44,6 +45,14 @@ impl Type { /// The predefined type `number`. pub const NUMBER: Type = 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, @@ -133,12 +142,36 @@ impl IntoIterator for Type { } } +impl<'a> Display for PrimitiveType { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl<'a> Display for Type { + 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(" ")) + } + } + } +} + #[cfg(test)] mod tests { use super::*; use crate::parsers::Span; use crate::Parser; + #[test] + fn simple_works() { + let t = Type::new_exactly("location"); + assert_eq!(format!("{t}"), "location"); + } + #[test] fn flatten_with_single_element_works() { let (_, t) = Type::parse(Span::new("object")).unwrap(); @@ -157,4 +190,10 @@ mod tests { assert!(iter.next().is_some()); assert!(iter.next().is_none()); } + + #[test] + fn either_works() { + let t = Type::new_either(["location", "memory"]); + assert_eq!(format!("{t}"), "(either location memory)"); + } } diff --git a/src/types/typed_list.rs b/src/types/typed_list.rs index 5cd4195..d8691a3 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. @@ -84,3 +85,121 @@ impl IntoIterator for TypedList { self.0.into_iter() } } + +impl Display for TypedList +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_().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_()) { + stack.push(format!("{}", item.value())) + } else { + stack.push(format!("{} - {}", item.value(), item.type_())); + last_type = item.type_(); + } + } + + 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::from_str("x").to_typed(Type::OBJECT), + Variable::from_str("y").to_typed(Type::OBJECT), + Variable::from_str("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 8beb511..2049a21 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. @@ -81,3 +82,20 @@ impl Deref for Variable { &self.0 } } + +impl Display for Variable { + 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"); + } +} From e394157d5267bfe0b6411904eb583549edb852e5 Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Sun, 23 Apr 2023 20:35:32 +0200 Subject: [PATCH 2/6] Add initial pretty-printing via pretty crate --- Cargo.toml | 3 ++- src/lib.rs | 4 +++ src/pretty_print/mod.rs | 28 ++++++++++++++++++++ src/pretty_print/name.rs | 47 +++++++++++++++++++++++++++++++++ src/pretty_print/type.rs | 56 ++++++++++++++++++++++++++++++++++++++++ src/types/type.rs | 2 +- 6 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 src/pretty_print/mod.rs create mode 100644 src/pretty_print/name.rs create mode 100644 src/pretty_print/type.rs diff --git a/Cargo.toml b/Cargo.toml index d24552c..a4b0604 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ edition = "2021" rust-version = "1.68.0" [features] -default = ["parser", "interning"] +default = ["parser", "interning", "pretty"] parser = ["dep:nom", "dep:nom-greedyerror", "dep:nom_locate", "dep:thiserror"] interning = ["dep:lazy_static"] @@ -22,6 +22,7 @@ lazy_static = { version = "1.4.0", optional = true } nom = { version = "7.1.3", optional = true } nom-greedyerror = { version = "0.5.0", optional = true } nom_locate = { version = "4.2.0", optional = true } +pretty = { version = "0.12.1", optional = true } thiserror = { version = "1.0.61", optional = true } [package.metadata.docs.rs] diff --git a/src/lib.rs b/src/lib.rs index 1286b4f..eb7bce0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,3 +90,7 @@ pub use parsers::Parser; // re-export types pub use types::*; + +#[cfg_attr(docsrs, doc(cfg(feature = "pretty")))] +#[cfg(feature = "pretty")] +pub(crate) mod pretty_print; diff --git a/src/pretty_print/mod.rs b/src/pretty_print/mod.rs new file mode 100644 index 0000000..e54c85c --- /dev/null +++ b/src/pretty_print/mod.rs @@ -0,0 +1,28 @@ +use pretty::RcDoc; + +mod name; +mod r#type; + +#[derive(Default)] +pub struct PrettyRenderer; + +impl PrettyRenderer { + pub fn to_pretty(&self, doc: RcDoc<'_>, width: usize) -> String { + let mut w = Vec::new(); + doc.render(width, &mut w).unwrap(); + String::from_utf8(w).unwrap() + } +} + +/// Helper macro to quickly prettify an element. +#[cfg(test)] +macro_rules! prettify { + ($x:expr, $n:literal) => {{ + let renderer = PrettyRenderer::default(); + let doc = $x.accept(&renderer); + renderer.to_pretty(doc, $n) + }}; +} + +#[cfg(test)] +pub(crate) use prettify; diff --git a/src/pretty_print/name.rs b/src/pretty_print/name.rs new file mode 100644 index 0000000..cd3b81d --- /dev/null +++ b/src/pretty_print/name.rs @@ -0,0 +1,47 @@ +use crate::pretty_print::PrettyRenderer; +use crate::types::{FunctionSymbol, Name, Variable}; +use crate::visitor::Visitor; +use pretty::RcDoc; + +impl<'a> Visitor, RcDoc<'a>> for PrettyRenderer { + fn visit(&self, value: &Name<'a>) -> RcDoc<'a> { + RcDoc::text(value.as_str()) + } +} + +impl<'a> Visitor, RcDoc<'a>> for PrettyRenderer { + fn visit(&self, value: &Variable<'a>) -> RcDoc<'a> { + RcDoc::text("?").append(value.as_str()) + } +} + +impl<'a> Visitor, RcDoc<'a>> for PrettyRenderer { + fn visit(&self, value: &FunctionSymbol<'a>) -> RcDoc<'a> { + RcDoc::text(value.as_str()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::pretty_print::prettify; + use crate::visitor::Accept; + + #[test] + fn name_works() { + let x = Name::new("name"); + assert_eq!(prettify!(x, 10), "name"); + } + + #[test] + fn variable_works() { + let x = Variable::new("var"); + assert_eq!(prettify!(x, 10), "?var"); + } + + #[test] + fn function_symbol_works() { + let x = FunctionSymbol::new("fun-sym"); + assert_eq!(prettify!(x, 10), "fun-sym"); + } +} diff --git a/src/pretty_print/type.rs b/src/pretty_print/type.rs new file mode 100644 index 0000000..6320416 --- /dev/null +++ b/src/pretty_print/type.rs @@ -0,0 +1,56 @@ +use crate::pretty_print::PrettyRenderer; +use crate::types::{PrimitiveType, Type}; +use crate::visitor::{Accept, Visitor}; +use pretty::RcDoc; + +impl<'a> Visitor, RcDoc<'a>> for PrettyRenderer { + fn visit(&self, value: &PrimitiveType<'a>) -> RcDoc<'a> { + RcDoc::text(value.as_str()) + } +} + +impl<'a> Visitor, RcDoc<'a>> for PrettyRenderer { + fn visit(&self, value: &Type<'a>) -> RcDoc<'a> { + match value { + Type::Exactly(t) => RcDoc::text(t.as_str()), + Type::EitherOf(ts) => RcDoc::text("(either") + .append(RcDoc::softline()) + .group() + .nest(4) + .append(RcDoc::intersperse( + ts.iter().map(|t| t.accept(self)), + RcDoc::softline(), + )) + .nest(4) + .group() + .append(")"), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::pretty_print::prettify; + use crate::visitor::Accept; + + #[test] + fn primitive_type_works() { + let x = PrimitiveType::new("pt"); + assert_eq!(prettify!(x, 10), "pt"); + } + + #[test] + fn simple_type_works() { + let x = Type::from("a"); + assert_eq!(prettify!(x, 10), "a"); + } + + #[test] + fn either_type_works() { + let x = Type::from_iter(["a", "b"]); + assert_eq!(prettify!(x, 12), "(either a b)"); + assert_eq!(prettify!(x, 10), "(either a\n b)"); + assert_eq!(prettify!(x, 8), "(either\n a b)"); + } +} diff --git a/src/types/type.rs b/src/types/type.rs index 6caf59d..c733e18 100644 --- a/src/types/type.rs +++ b/src/types/type.rs @@ -123,7 +123,7 @@ impl AsRef for PrimitiveType { } impl Deref for PrimitiveType { - type Target = str; + type Target = Name; fn deref(&self) -> &Self::Target { &self.0 From a3c7f1f389e301abc1db1547550a0e752f755113 Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Sun, 23 Apr 2023 21:22:11 +0200 Subject: [PATCH 3/6] Suppress unused code warnings --- src/pretty_print/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pretty_print/mod.rs b/src/pretty_print/mod.rs index e54c85c..a5fc766 100644 --- a/src/pretty_print/mod.rs +++ b/src/pretty_print/mod.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use pretty::RcDoc; mod name; From 07cb6b88ca56636fed9976cbbfd357338183f8b8 Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Mon, 24 Apr 2023 23:11:35 +0200 Subject: [PATCH 4/6] Fix compilation errors after rebasing unto main --- src/pretty_print/name.rs | 4 ++-- src/pretty_print/type.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pretty_print/name.rs b/src/pretty_print/name.rs index cd3b81d..51b68aa 100644 --- a/src/pretty_print/name.rs +++ b/src/pretty_print/name.rs @@ -35,13 +35,13 @@ mod tests { #[test] fn variable_works() { - let x = Variable::new("var"); + let x = Variable::from("var"); assert_eq!(prettify!(x, 10), "?var"); } #[test] fn function_symbol_works() { - let x = FunctionSymbol::new("fun-sym"); + let x = FunctionSymbol::from("fun-sym"); assert_eq!(prettify!(x, 10), "fun-sym"); } } diff --git a/src/pretty_print/type.rs b/src/pretty_print/type.rs index 6320416..9cf2ba3 100644 --- a/src/pretty_print/type.rs +++ b/src/pretty_print/type.rs @@ -36,7 +36,7 @@ mod tests { #[test] fn primitive_type_works() { - let x = PrimitiveType::new("pt"); + let x = PrimitiveType::from("pt"); assert_eq!(prettify!(x, 10), "pt"); } From f500c52084022db6458771249f1243a596ae2f6f Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Mon, 24 Apr 2023 23:13:27 +0200 Subject: [PATCH 5/6] Update description in Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a4b0604..f38853e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "pddl" version = "0.0.8-unstable" -description ="A PDDL 3.1 parser" +description = "A PDDL 3.1 parser, strongly typed" license = "EUPL-1.2" documentation = "https://docs.rs/pddl" categories = ["parser-implementations", "science", "simulation"] From 98b36fd29f540235b9ecbb940d6d057831121e99 Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Sun, 19 May 2024 17:11:23 +0200 Subject: [PATCH 6/6] Fix code after rebasing --- src/pretty_print/name.rs | 18 +++++++++--------- src/pretty_print/type.rs | 12 ++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/pretty_print/name.rs b/src/pretty_print/name.rs index 51b68aa..60e5cc9 100644 --- a/src/pretty_print/name.rs +++ b/src/pretty_print/name.rs @@ -3,21 +3,21 @@ use crate::types::{FunctionSymbol, Name, Variable}; use crate::visitor::Visitor; use pretty::RcDoc; -impl<'a> Visitor, RcDoc<'a>> for PrettyRenderer { - fn visit(&self, value: &Name<'a>) -> RcDoc<'a> { - RcDoc::text(value.as_str()) +impl<'a> Visitor> for PrettyRenderer { + fn visit(&self, value: &Name) -> RcDoc<'a> { + RcDoc::text(value.to_string()) } } -impl<'a> Visitor, RcDoc<'a>> for PrettyRenderer { - fn visit(&self, value: &Variable<'a>) -> RcDoc<'a> { - RcDoc::text("?").append(value.as_str()) +impl<'a> Visitor> for PrettyRenderer { + fn visit(&self, value: &Variable) -> RcDoc<'a> { + RcDoc::text(value.to_string()) } } -impl<'a> Visitor, RcDoc<'a>> for PrettyRenderer { - fn visit(&self, value: &FunctionSymbol<'a>) -> RcDoc<'a> { - RcDoc::text(value.as_str()) +impl<'a> Visitor> for PrettyRenderer { + fn visit(&self, value: &FunctionSymbol) -> RcDoc<'a> { + RcDoc::text(value.to_string()) } } diff --git a/src/pretty_print/type.rs b/src/pretty_print/type.rs index 9cf2ba3..236cb2a 100644 --- a/src/pretty_print/type.rs +++ b/src/pretty_print/type.rs @@ -3,16 +3,16 @@ use crate::types::{PrimitiveType, Type}; use crate::visitor::{Accept, Visitor}; use pretty::RcDoc; -impl<'a> Visitor, RcDoc<'a>> for PrettyRenderer { - fn visit(&self, value: &PrimitiveType<'a>) -> RcDoc<'a> { - RcDoc::text(value.as_str()) +impl<'a> Visitor> for PrettyRenderer { + fn visit(&self, value: &PrimitiveType) -> RcDoc<'a> { + RcDoc::text(value.to_string()) } } -impl<'a> Visitor, RcDoc<'a>> for PrettyRenderer { - fn visit(&self, value: &Type<'a>) -> RcDoc<'a> { +impl<'a> Visitor> for PrettyRenderer { + fn visit(&self, value: &Type) -> RcDoc<'a> { match value { - Type::Exactly(t) => RcDoc::text(t.as_str()), + Type::Exactly(t) => RcDoc::text(t.to_string()), Type::EitherOf(ts) => RcDoc::text("(either") .append(RcDoc::softline()) .group()