pddl 0.2.0

A PDDL 3.1 parser, strongly typed
Documentation
use crate::pretty_print::{sealed, PrettyRenderer};
use crate::types::{
    DurationConstraint, DurationValue, DurativeActionEffect, DurativeActionGoalDefinition,
    PreferenceTimedGoalDefinition, SimpleDurationConstraint, TimedEffect, TimedGoalDefinition,
};
use crate::visitor::{Accept, Visitor};
use pretty::RcDoc;

impl sealed::Sealed for DurationValue {}
impl sealed::Sealed for SimpleDurationConstraint {}
impl sealed::Sealed for DurationConstraint {}
impl sealed::Sealed for TimedGoalDefinition {}
impl sealed::Sealed for PreferenceTimedGoalDefinition {}
impl sealed::Sealed for TimedEffect {}
impl sealed::Sealed for DurativeActionEffect {}
impl sealed::Sealed for DurativeActionGoalDefinition {}

impl Visitor<DurationValue, RcDoc<'static>> for PrettyRenderer {
    fn visit(&self, value: &DurationValue) -> RcDoc<'static> {
        match value {
            DurationValue::Number(n) => n.accept(self),
            DurationValue::FluentExpression(e) => e.accept(self),
        }
    }
}

impl Visitor<SimpleDurationConstraint, RcDoc<'static>> for PrettyRenderer {
    fn visit(&self, value: &SimpleDurationConstraint) -> RcDoc<'static> {
        match value {
            SimpleDurationConstraint::Op(op, dv) => RcDoc::text("(")
                .append(op.accept(self))
                .append(" ?duration ")
                .append(dv.accept(self))
                .append(")"),
            SimpleDurationConstraint::At(ts, inner) => {
                let inner_doc = self.visit(inner.as_ref());
                RcDoc::text("(at ")
                    .append(ts.accept(self))
                    .append(" ")
                    .append(inner_doc)
                    .append(")")
            }
        }
    }
}

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

impl Visitor<TimedGoalDefinition, RcDoc<'static>> for PrettyRenderer {
    fn visit(&self, value: &TimedGoalDefinition) -> RcDoc<'static> {
        match value {
            TimedGoalDefinition::At(ts, gd) => RcDoc::text("(at ")
                .append(ts.accept(self))
                .append(" ")
                .append(gd.accept(self))
                .append(")"),
            TimedGoalDefinition::Over(interval, gd) => RcDoc::text("(over ")
                .append(interval.accept(self))
                .append(" ")
                .append(gd.accept(self))
                .append(")"),
        }
    }
}

impl Visitor<PreferenceTimedGoalDefinition, RcDoc<'static>> for PrettyRenderer {
    fn visit(&self, value: &PreferenceTimedGoalDefinition) -> RcDoc<'static> {
        match value {
            PreferenceTimedGoalDefinition::Required(tgd) => tgd.accept(self),
            PreferenceTimedGoalDefinition::Preference(name, tgd) => {
                let children: Vec<RcDoc<'static>> = match name {
                    Some(n) => vec![n.accept(self), self.visit(tgd)],
                    None => vec![self.visit(tgd)],
                };
                self.sexpr("preference", children)
            }
        }
    }
}

impl Visitor<TimedEffect, RcDoc<'static>> for PrettyRenderer {
    fn visit(&self, value: &TimedEffect) -> RcDoc<'static> {
        match value {
            TimedEffect::Conditional(ts, ce) => RcDoc::text("(at ")
                .append(ts.accept(self))
                .append(" ")
                .append(ce.accept(self))
                .append(")"),
            TimedEffect::NumericFluent(ts, fassign) => RcDoc::text("(at ")
                .append(ts.accept(self))
                .append(" ")
                .append(fassign.accept(self))
                .append(")"),
            TimedEffect::ContinuousEffect(op, head, exp) => RcDoc::text("(")
                .append(op.accept(self))
                .append(" ")
                .append(head.accept(self))
                .append(" ")
                .append(exp.accept(self))
                .append(")"),
        }
    }
}

impl Visitor<DurativeActionGoalDefinition, RcDoc<'static>> for PrettyRenderer {
    fn visit(&self, value: &DurativeActionGoalDefinition) -> RcDoc<'static> {
        match value {
            DurativeActionGoalDefinition::Timed(ptgd) => ptgd.accept(self),
            DurativeActionGoalDefinition::And(items) => {
                if items.is_empty() {
                    RcDoc::text("(and)")
                } else {
                    self.sexpr("and", items.iter().map(|dagd| self.visit(dagd)))
                }
            }
            DurativeActionGoalDefinition::Forall(vars, body) => self.sexpr(
                "forall",
                [
                    RcDoc::text("(").append(vars.accept(self)).append(")"),
                    self.visit(body.as_ref()),
                ],
            ),
        }
    }
}

impl Visitor<DurativeActionEffect, RcDoc<'static>> for PrettyRenderer {
    fn visit(&self, value: &DurativeActionEffect) -> RcDoc<'static> {
        match value {
            DurativeActionEffect::Timed(te) => te.accept(self),
            DurativeActionEffect::All(effects) => RcDoc::text("(and")
                .append(RcDoc::softline())
                .append(RcDoc::intersperse(
                    effects.iter().map(|e| self.visit(e)),
                    RcDoc::softline(),
                ))
                .nest(4)
                .group()
                .append(")"),
            DurativeActionEffect::Forall(vars, body) => self.sexpr(
                "forall",
                [
                    RcDoc::text("(").append(vars.accept(self)).append(")"),
                    self.visit(body.as_ref()),
                ],
            ),
            DurativeActionEffect::When(condition, effect) => RcDoc::text("(at ")
                .append(self.visit(condition))
                .append(" ")
                .append(self.visit(effect))
                .append(")"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::pretty_print::prettify;
    use crate::visitor::Accept;
    use crate::{DurationOperator, PreferenceTimedGoalDefinition};

    #[test]
    fn duration_value_number() {
        let dv = DurationValue::number(10);
        assert_eq!(prettify!(dv, 20), "10");
    }

    #[test]
    fn simple_duration_constraint_op() {
        let sdc = SimpleDurationConstraint::op(DurationOperator::Equal, DurationValue::number(10));
        assert_eq!(prettify!(sdc, 30), "(= ?duration 10)");
    }

    #[test]
    fn duration_value_fexp() {
        let exp = crate::FluentExpression::number(crate::Number::from(5));
        let dv = DurationValue::fluent_expression(exp);
        assert_eq!(prettify!(dv, 20), "5");
    }

    #[test]
    fn simple_duration_constraint_at() {
        let inner =
            SimpleDurationConstraint::op(DurationOperator::Equal, DurationValue::number(10));
        let sdc = SimpleDurationConstraint::at(crate::TimeSpecifier::Start, inner);
        assert_eq!(prettify!(sdc, 30), "(at start (= ?duration 10))");
    }

    #[test]
    fn duration_constraint_single() {
        let sdc = SimpleDurationConstraint::op(DurationOperator::Equal, DurationValue::number(10));
        let dc = DurationConstraint::new(sdc);
        assert_eq!(prettify!(dc, 30), "(= ?duration 10)");
    }

    #[test]
    fn duration_constraint_all() {
        let sdc1 = SimpleDurationConstraint::op(DurationOperator::Equal, DurationValue::number(10));
        let sdc2 = SimpleDurationConstraint::op(
            DurationOperator::GreaterOrEqual,
            DurationValue::number(5),
        );
        let dc = DurationConstraint::all([sdc1, sdc2]);
        let out = prettify!(dc, 30);
        assert!(out.starts_with("(and"));
        assert!(out.contains("(= ?duration 10)"));
        assert!(out.contains("(>= ?duration 5)"));
    }

    #[test]
    fn timed_gd_at() {
        let gd = crate::GoalDefinition::and(Vec::<crate::GoalDefinition>::new());
        let tgd = TimedGoalDefinition::at(crate::TimeSpecifier::Start, gd);
        assert_eq!(prettify!(tgd, 30), "(at start (and))");
    }

    #[test]
    fn timed_gd_over() {
        use crate::Interval;
        let gd = crate::GoalDefinition::and(Vec::<crate::GoalDefinition>::new());
        let tgd = TimedGoalDefinition::over(Interval::All, gd);
        assert_eq!(prettify!(tgd, 30), "(over all (and))");
    }

    #[test]
    fn pref_timed_gd_required() {
        let gd = crate::GoalDefinition::and(Vec::<crate::GoalDefinition>::new());
        let tgd = TimedGoalDefinition::at(crate::TimeSpecifier::Start, gd);
        let ptgd = PreferenceTimedGoalDefinition::required(tgd);
        assert_eq!(prettify!(ptgd, 30), "(at start (and))");
    }

    #[test]
    fn pref_timed_gd_preference_with_name() {
        use crate::PreferenceName;
        let gd = crate::GoalDefinition::and(Vec::<crate::GoalDefinition>::new());
        let tgd = TimedGoalDefinition::at(crate::TimeSpecifier::Start, gd);
        let ptgd =
            PreferenceTimedGoalDefinition::preference(Some(PreferenceName::string("p1")), tgd);
        assert_eq!(prettify!(ptgd, 40), "(preference p1 (at start (and)))");
    }

    #[test]
    fn pref_timed_gd_preference_no_name() {
        let gd = crate::GoalDefinition::and(Vec::<crate::GoalDefinition>::new());
        let tgd = TimedGoalDefinition::at(crate::TimeSpecifier::Start, gd);
        let ptgd = PreferenceTimedGoalDefinition::preference(None, tgd);
        assert_eq!(prettify!(ptgd, 40), "(preference (at start (and)))");
    }

    #[test]
    fn timed_effect_conditional() {
        use crate::EffectCondition;
        let ce = EffectCondition::all(Vec::new());
        let te = TimedEffect::conditional(crate::TimeSpecifier::Start, ce);
        assert_eq!(prettify!(te, 30), "(at start (and))");
    }

    #[test]
    fn timed_effect_numeric_fluent() {
        use crate::{
            AssignOp, DurativeActionFluentExpression, DurativeActionFunctionAssignment,
            FluentExpression, FunctionHead, FunctionSymbol, Number,
        };
        let head = FunctionHead::Simple(FunctionSymbol::from("x"));
        let expr = DurativeActionFluentExpression::fluent_expression(FluentExpression::number(
            Number::from(5),
        ));
        let fassign = DurativeActionFunctionAssignment::new(AssignOp::Increase, head, expr);
        let te = TimedEffect::fluent(crate::TimeSpecifier::End, fassign);
        assert_eq!(prettify!(te, 30), "(at end (increase x 5))");
    }

    #[test]
    fn timed_effect_continuous() {
        use crate::{
            FluentExpression, FunctionHead, FunctionSymbol, Number, TimedAssignOperator,
            TimedFluentExpression,
        };
        let head = FunctionHead::Simple(FunctionSymbol::from("x"));
        let exp = FluentExpression::number(Number::from(1));
        let te = TimedEffect::continuous(
            TimedAssignOperator::Increase,
            head,
            TimedFluentExpression::Scaled(exp),
        );
        assert_eq!(prettify!(te, 30), "(increase x 1)");
    }

    #[test]
    fn durative_action_goal_timed() {
        let gd = crate::GoalDefinition::and(Vec::<crate::GoalDefinition>::new());
        let tgd = TimedGoalDefinition::at(crate::TimeSpecifier::Start, gd);
        let ptgd = PreferenceTimedGoalDefinition::required(tgd);
        let dagd = DurativeActionGoalDefinition::timed(ptgd);
        assert_eq!(prettify!(dagd, 30), "(at start (and))");
    }

    #[test]
    fn durative_action_goal_and_empty() {
        let dagd = DurativeActionGoalDefinition::and(Vec::new());
        assert_eq!(prettify!(dagd, 30), "(and)");
    }

    #[test]
    fn durative_action_goal_and_non_empty() {
        let gd = crate::GoalDefinition::and(Vec::<crate::GoalDefinition>::new());
        let tgd = TimedGoalDefinition::at(crate::TimeSpecifier::Start, gd);
        let ptgd = PreferenceTimedGoalDefinition::required(tgd);
        let dagd = DurativeActionGoalDefinition::and([DurativeActionGoalDefinition::timed(ptgd)]);
        assert_eq!(prettify!(dagd, 40), "(and (at start (and)))");
    }

    #[test]
    fn durative_action_goal_forall() {
        use crate::{ToTyped, Type, TypedVariables, Variable};
        let vars: TypedVariables = vec![Variable::string("x").to_typed(Type::OBJECT)].into();
        let inner = DurativeActionGoalDefinition::and(Vec::new());
        let dagd = DurativeActionGoalDefinition::forall(vars, inner);
        assert_eq!(prettify!(dagd, 40), "(forall (?x) (and))");
    }

    #[test]
    fn durative_action_effect_timed() {
        use crate::EffectCondition;
        let ce = EffectCondition::all(Vec::new());
        let te = TimedEffect::conditional(crate::TimeSpecifier::Start, ce);
        let dae = DurativeActionEffect::timed(te);
        assert_eq!(prettify!(dae, 30), "(at start (and))");
    }

    #[test]
    fn durative_action_effect_all() {
        use crate::EffectCondition;
        let ce = EffectCondition::all(Vec::new());
        let te = TimedEffect::conditional(crate::TimeSpecifier::Start, ce);
        let dae = DurativeActionEffect::and([DurativeActionEffect::timed(te)]);
        assert_eq!(prettify!(dae, 40), "(and (at start (and)))");
    }

    #[test]
    fn durative_action_effect_forall() {
        use crate::{EffectCondition, ToTyped, Type, TypedVariables, Variable};
        let vars: TypedVariables = vec![Variable::string("x").to_typed(Type::OBJECT)].into();
        let ce = EffectCondition::all(Vec::new());
        let inner =
            DurativeActionEffect::timed(TimedEffect::conditional(crate::TimeSpecifier::Start, ce));
        let dae = DurativeActionEffect::forall(vars, inner);
        assert_eq!(prettify!(dae, 40), "(forall (?x) (at start (and)))");
    }

    #[test]
    fn durative_action_effect_when() {
        use crate::{EffectCondition, GoalDefinition};
        let gd = GoalDefinition::and(Vec::<GoalDefinition>::new());
        let ce = EffectCondition::all(Vec::new());
        let te = TimedEffect::conditional(crate::TimeSpecifier::Start, ce);
        let dae = DurativeActionEffect::when(
            DurativeActionGoalDefinition::timed(PreferenceTimedGoalDefinition::required(
                TimedGoalDefinition::at(crate::TimeSpecifier::Start, gd),
            )),
            te,
        );
        assert_eq!(prettify!(dae, 40), "(at (at start (and)) (at start (and)))");
    }
}