pddl 0.2.0

A PDDL 3.1 parser, strongly typed
Documentation
use crate::pretty_print::{sealed, PrettyRenderer};
use crate::types::{
    ConditionalEffect, EffectCondition, Effects, ForallConditionalEffect, PrimitiveEffect,
    WhenConditionalEffect,
};
use crate::visitor::{Accept, Visitor};
use pretty::RcDoc;

impl sealed::Sealed for PrimitiveEffect {}
impl sealed::Sealed for ConditionalEffect {}
impl sealed::Sealed for EffectCondition {}
impl sealed::Sealed for ForallConditionalEffect {}
impl sealed::Sealed for WhenConditionalEffect {}
impl sealed::Sealed for Effects {}

impl Visitor<PrimitiveEffect, RcDoc<'static>> for PrettyRenderer {
    fn visit(&self, value: &PrimitiveEffect) -> RcDoc<'static> {
        match value {
            PrimitiveEffect::AtomicFormula(af) => af.accept(self),
            PrimitiveEffect::NotAtomicFormula(af) => {
                RcDoc::text("(not ").append(af.accept(self)).append(")")
            }
            PrimitiveEffect::AssignNumericFluent(op, head, exp) => RcDoc::text("(")
                .append(op.accept(self))
                .append(" ")
                .append(head.accept(self))
                .append(" ")
                .append(exp.accept(self))
                .append(")"),
            PrimitiveEffect::AssignObjectFluent(ft, term) => match term {
                Some(t) => RcDoc::text("(assign ")
                    .append(ft.accept(self))
                    .append(" ")
                    .append(t.accept(self))
                    .append(")"),
                None => RcDoc::text("(assign ").append(ft.accept(self)).append(")"),
            },
        }
    }
}

impl Visitor<ConditionalEffect, RcDoc<'static>> for PrettyRenderer {
    fn visit(&self, value: &ConditionalEffect) -> RcDoc<'static> {
        match value {
            ConditionalEffect::Effect(pe) => pe.accept(self),
            ConditionalEffect::Forall(fc) => fc.accept(self),
            ConditionalEffect::When(wc) => wc.accept(self),
        }
    }
}

impl Visitor<ForallConditionalEffect, RcDoc<'static>> for PrettyRenderer {
    fn visit(&self, value: &ForallConditionalEffect) -> RcDoc<'static> {
        RcDoc::text("(forall (")
            .append(value.variables.accept(self))
            .append(") ")
            .append(value.effects.accept(self))
            .append(")")
    }
}

impl Visitor<WhenConditionalEffect, RcDoc<'static>> for PrettyRenderer {
    fn visit(&self, value: &WhenConditionalEffect) -> RcDoc<'static> {
        RcDoc::text("(when ")
            .append(value.condition.accept(self))
            .append(" ")
            .append(value.effect.accept(self))
            .append(")")
    }
}

impl Visitor<EffectCondition, RcDoc<'static>> for PrettyRenderer {
    fn visit(&self, value: &EffectCondition) -> RcDoc<'static> {
        match value {
            EffectCondition::Single(pe) => pe.accept(self),
            EffectCondition::All(pes) => {
                if pes.is_empty() {
                    RcDoc::text("(and)")
                } else {
                    RcDoc::text("(and")
                        .append(RcDoc::softline())
                        .append(RcDoc::intersperse(
                            pes.iter().map(|pe| pe.accept(self)),
                            RcDoc::softline(),
                        ))
                        .nest(4)
                        .group()
                        .append(")")
                }
            }
        }
    }
}

impl Visitor<Effects, RcDoc<'static>> for PrettyRenderer {
    fn visit(&self, value: &Effects) -> RcDoc<'static> {
        RcDoc::text("(and")
            .append(RcDoc::softline())
            .append(RcDoc::intersperse(
                value.iter().map(|ce| ce.accept(self)),
                RcDoc::softline(),
            ))
            .nest(4)
            .group()
            .append(")")
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::pretty_print::prettify;
    use crate::visitor::Accept;
    use crate::{
        AtomicFormula, ConditionalEffect, EffectCondition, FunctionSymbol, GoalDefinition, Name,
        Predicate, PrimitiveEffect, Term,
    };

    #[test]
    fn p_effect_atomic() {
        let af = AtomicFormula::predicate(
            Predicate::string("at"),
            vec![
                Term::new_name(Name::new("B")),
                Term::new_name(Name::new("home")),
            ],
        );
        let pe = PrimitiveEffect::new(af);
        assert_eq!(prettify!(pe, 30), "(at B home)");
    }

    #[test]
    fn p_effect_not() {
        let af = AtomicFormula::predicate(
            Predicate::string("at"),
            vec![Term::new_name(Name::new("B"))],
        );
        let pe = PrimitiveEffect::not(af);
        assert_eq!(prettify!(pe, 30), "(not (at B))");
    }

    #[test]
    fn p_effect_assign() {
        let pe = PrimitiveEffect::numeric_fluent(
            crate::AssignOp::Assign,
            crate::FunctionHead::new(FunctionSymbol::from("battery")),
            crate::FluentExpression::number(10),
        );
        assert_eq!(prettify!(pe, 30), "(assign battery 10)");
    }

    #[test]
    fn effects_single() {
        let af = AtomicFormula::predicate(
            Predicate::string("at"),
            vec![Term::new_name(Name::new("B"))],
        );
        let pe = PrimitiveEffect::new(af);
        let ce = ConditionalEffect::new_primitive_effect(pe);
        let effects = Effects::new(ce);
        assert_eq!(prettify!(effects, 30), "(and (at B))");
    }

    #[test]
    fn p_effect_assign_object_fluent_with_term() {
        use crate::{FunctionSymbol, FunctionTerm};
        let ft = FunctionTerm::new(
            FunctionSymbol::from("loc"),
            vec![Term::new_name(Name::new("x"))],
        );
        let pe = PrimitiveEffect::object_fluent(ft, Some(Term::new_name(Name::new("y"))));
        assert_eq!(prettify!(pe, 30), "(assign (loc x) y)");
    }

    #[test]
    fn p_effect_assign_object_fluent_without_term() {
        use crate::{FunctionSymbol, FunctionTerm};
        let ft = FunctionTerm::new(FunctionSymbol::from("loc"), vec![]);
        let pe = PrimitiveEffect::object_fluent(ft, None);
        assert_eq!(prettify!(pe, 30), "(assign loc)");
    }

    #[test]
    fn c_effect_forall() {
        use crate::{ToTyped, Type, TypedVariables, Variable};
        let vars: TypedVariables = vec![Variable::string("x").to_typed(Type::OBJECT)].into();
        let af = AtomicFormula::predicate(
            Predicate::string("p"),
            vec![Term::new_variable(Variable::from("x"))],
        );
        let pe = PrimitiveEffect::new(af);
        let effects = Effects::new(ConditionalEffect::new_primitive_effect(pe));
        let ce = ConditionalEffect::forall(vars, effects);
        assert_eq!(prettify!(ce, 40), "(forall (?x) (and (p ?x)))");
    }

    #[test]
    fn c_effect_when() {
        let cond = GoalDefinition::and(Vec::<GoalDefinition>::new());
        let ce_inner = EffectCondition::all(Vec::new());
        let ce = ConditionalEffect::when(cond, ce_inner);
        assert_eq!(prettify!(ce, 30), "(when (and) (and))");
    }

    #[test]
    fn conditional_effect_all_empty() {
        let ce = EffectCondition::all(Vec::new());
        assert_eq!(prettify!(ce, 20), "(and)");
    }

    #[test]
    fn conditional_effect_all_non_empty() {
        let af = AtomicFormula::predicate(
            Predicate::string("at"),
            vec![Term::new_name(Name::new("x"))],
        );
        let pe1 = PrimitiveEffect::new(af.clone());
        let pe2 = PrimitiveEffect::new(af);
        let ce = EffectCondition::all(vec![pe1, pe2]);
        assert_eq!(prettify!(ce, 30), "(and (at x) (at x))");
    }

    #[test]
    fn effects_multi_effect() {
        let af1 = AtomicFormula::predicate(
            Predicate::string("at"),
            vec![Term::new_name(Name::new("x"))],
        );
        let af2 = AtomicFormula::predicate(
            Predicate::string("at"),
            vec![Term::new_name(Name::new("y"))],
        );
        let ce1 = ConditionalEffect::new_primitive_effect(PrimitiveEffect::new(af1));
        let ce2 = ConditionalEffect::new_primitive_effect(PrimitiveEffect::new(af2));
        let effects = Effects::and(vec![ce1, ce2]);
        assert_eq!(prettify!(effects, 40), "(and (at x) (at y))");
    }
}