Skip to content

Commit

Permalink
Add Display impls for type, name, variable
Browse files Browse the repository at this point in the history
  • Loading branch information
sunsided committed Apr 23, 2023
1 parent 7269aa3 commit 9369979
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 6 deletions.
18 changes: 18 additions & 0 deletions src/types/name.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Contains names.
use crate::types::{PrimitiveType, ToTyped, Type, Typed};
use std::fmt::{Display, Formatter};
use std::ops::Deref;

/// A name.
Expand Down Expand Up @@ -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");
}
}
44 changes: 44 additions & 0 deletions src/types/type.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Contains types.
use crate::types::Name;
use std::fmt::{Display, Formatter};
use std::ops::Deref;

/// The `object` type.
Expand Down Expand Up @@ -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<S: Into<PrimitiveType<'a>>>(t: S) -> Self {
Self::Exactly(t.into())
}

pub fn new_either<T: IntoIterator<Item = P>, P: Into<PrimitiveType<'a>>>(iter: T) -> Self {
Self::EitherOf(iter.into_iter().map(|x| x.into()).collect())
}

pub fn len(&self) -> usize {
match self {
Type::Exactly(_) => 1,
Expand Down Expand Up @@ -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)");
}
}
6 changes: 3 additions & 3 deletions src/types/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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()
}
}
125 changes: 122 additions & 3 deletions src/types/typed_list.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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<Typed<'a, T>>);
Expand Down Expand Up @@ -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");
}
}
23 changes: 23 additions & 0 deletions src/types/variable.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
//! Contains variables.
use crate::types::{Name, PrimitiveType, ToTyped, Type, Typed};
use std::fmt::{Display, Formatter};
use std::ops::Deref;

/// A variable name.
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
pub struct Variable<'a>(Name<'a>);

impl<'a> Variable<'a> {
#[inline(always)]
pub fn new<N: Into<Name<'a>>>(name: N) -> Self {
Self(name.into())
}

#[inline(always)]
pub const fn from_str(name: &'a str) -> Self {
Self(Name::new(name))
Expand Down Expand Up @@ -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");
}
}

0 comments on commit 9369979

Please sign in to comment.