pddl_parser/
lib.rs

1#![allow(
2    clippy::missing_errors_doc,
3    clippy::use_self,
4    clippy::module_name_repetitions,
5    clippy::must_use_candidate,
6    clippy::return_self_not_must_use,
7    clippy::module_inception
8)]
9#![warn(
10    clippy::unwrap_used,
11    clippy::panic,
12    clippy::todo,
13    clippy::unimplemented,
14    missing_docs,
15    clippy::missing_panics_doc
16)]
17
18//! # PDDL Parser
19
20/// The domain module contains the types used to represent a PDDL domain.
21pub mod domain;
22/// The error module contains the error types used by the library.
23pub mod error;
24/// The lexer module contains the lexer used to tokenize a PDDL file.
25pub mod lexer;
26/// The plan module contains the types used to represent a PDDL plan.
27pub mod plan;
28/// The problem module contains the types used to represent a PDDL problem.
29pub mod problem;
30/// The tokens module contains the functions used to parse tokens.
31pub mod tokens;
32
33#[cfg(test)]
34mod tests {
35    use crate::domain::domain::Domain;
36    use crate::domain::durative_action::DurativeAction;
37    use crate::domain::expression::{BinaryOp, DurationInstant, Expression};
38    use crate::domain::requirement::Requirement;
39    use crate::domain::typed_parameter::TypedParameter;
40    use crate::domain::typed_predicate::TypedPredicate;
41    use crate::domain::typedef::TypeDef;
42    use crate::domain::{self};
43    use crate::plan;
44    use crate::plan::action::Action;
45    use crate::plan::plan::Plan;
46    use crate::plan::simple_action::SimpleAction;
47    use crate::problem::{Object, Problem};
48
49    #[test]
50    fn test_domain_to_pddl() {
51        std::env::set_var("RUST_LOG", "debug");
52        let _ = pretty_env_logger::try_init();
53        let domain_example = include_str!("../tests/domain.pddl");
54        let domain = Domain::parse(domain_example.into()).expect("Failed to parse domain");
55        eprintln!("{}", domain.to_pddl());
56        let redomain = Domain::parse(domain.to_pddl().as_str().into()).expect("Failed to parse domain again");
57        assert_eq!(domain, redomain);
58    }
59
60    #[test]
61    fn test_problem_to_pddl() {
62        std::env::set_var("RUST_LOG", "debug");
63        let _ = pretty_env_logger::try_init();
64        let problem_example = include_str!("../tests/problem.pddl");
65        let problem = Problem::parse(problem_example.into()).expect("Failed to parse problem");
66        eprintln!("{}", problem.to_pddl());
67        let reproblem = Problem::parse(problem.to_pddl().as_str().into()).expect("Failed to parse problem again");
68        assert_eq!(problem, reproblem);
69    }
70
71    #[test]
72    fn test_plan() {
73        std::env::set_var("RUST_LOG", "debug");
74        let _ = pretty_env_logger::try_init();
75        let plan_example = include_str!("../tests/plan.txt");
76        assert_eq!(
77            Plan::parse(plan_example.into()).expect("Failed to parse plan"),
78            Plan(vec![
79                Action::Simple(SimpleAction {
80                    name: "pick-up".into(),
81                    parameters: vec!["arm".into(), "cupcake".into(), "table".into()]
82                }),
83                Action::Simple(SimpleAction {
84                    name: "move".into(),
85                    parameters: vec!["arm".into(), "table".into(), "plate".into()]
86                }),
87                Action::Simple(SimpleAction {
88                    name: "drop".into(),
89                    parameters: vec!["arm".into(), "cupcake".into(), "plate".into()]
90                }),
91            ])
92        );
93    }
94
95    #[test]
96    fn test_problem() {
97        std::env::set_var("RUST_LOG", "debug");
98        let _ = pretty_env_logger::try_init();
99        let problem_example = include_str!("../tests/problem.pddl");
100        assert_eq!(
101            Problem::parse(problem_example.into()).expect("Failed to parse problem"),
102            Problem {
103                name: "letseat-simple".into(),
104                domain: "letseat".into(),
105                objects: vec![
106                    Object {
107                        name: "arm".into(),
108                        type_: "robot".into(),
109                    },
110                    Object {
111                        name: "cupcake".into(),
112                        type_: "cupcake".into(),
113                    },
114                    Object {
115                        name: "table".into(),
116                        type_: "location".into(),
117                    },
118                    Object {
119                        name: "plate".into(),
120                        type_: "location".into(),
121                    },
122                ],
123                init: vec![
124                    Expression::Atom {
125                        name: "on".into(),
126                        parameters: vec!["arm".into(), "table".into(),]
127                    },
128                    Expression::Atom {
129                        name: "on".into(),
130                        parameters: vec!["cupcake".into(), "table".into(),]
131                    },
132                    Expression::Atom {
133                        name: "arm-empty".into(),
134                        parameters: vec![]
135                    },
136                    Expression::Atom {
137                        name: "path".into(),
138                        parameters: vec!["table".into(), "plate".into(),]
139                    },
140                ],
141                goal: Expression::Atom {
142                    name: "on".into(),
143                    parameters: vec!["cupcake".into(), "plate".into()]
144                }
145            }
146        );
147    }
148
149    #[test]
150    #[allow(clippy::too_many_lines)]
151    fn test_domain() {
152        std::env::set_var("RUST_LOG", "debug");
153        let _ = pretty_env_logger::try_init();
154        let domain_example = include_str!("../tests/domain.pddl");
155        assert_eq!(
156            Domain::parse(domain_example.into()).expect("Failed to parse domain"),
157            Domain {
158                name: "letseat".into(),
159                requirements: vec![Requirement::Typing],
160                types: vec![
161                    TypeDef {
162                        name: "location".into(),
163                        parent: Some("object".into()),
164                    },
165                    TypeDef {
166                        name: "locatable".into(),
167                        parent: Some("object".into()),
168                    },
169                    TypeDef {
170                        name: "bot".into(),
171                        parent: Some("locatable".into()),
172                    },
173                    TypeDef {
174                        name: "cupcake".into(),
175                        parent: Some("locatable".into()),
176                    },
177                    TypeDef {
178                        name: "robot".into(),
179                        parent: Some("bot".into()),
180                    },
181                ],
182                constants: vec![],
183                predicates: vec![
184                    TypedPredicate {
185                        name: "on".into(),
186                        parameters: vec![
187                            TypedParameter {
188                                name: "?obj".into(),
189                                type_: "locatable".into(),
190                            },
191                            TypedParameter {
192                                name: "?loc".into(),
193                                type_: "location".into(),
194                            },
195                        ],
196                    },
197                    TypedPredicate {
198                        name: "holding".into(),
199                        parameters: vec![
200                            TypedParameter {
201                                name: "?arm".into(),
202                                type_: "locatable".into(),
203                            },
204                            TypedParameter {
205                                name: "?cupcake".into(),
206                                type_: "locatable".into(),
207                            },
208                        ],
209                    },
210                    TypedPredicate {
211                        name: "arm-empty".into(),
212                        parameters: vec![],
213                    },
214                    TypedPredicate {
215                        name: "path".into(),
216                        parameters: vec![
217                            TypedParameter {
218                                name: "?location1".into(),
219                                type_: "location".into(),
220                            },
221                            TypedParameter {
222                                name: "?location2".into(),
223                                type_: "location".into(),
224                            },
225                        ],
226                    },
227                ],
228                functions: vec![],
229                actions: vec![
230                    domain::action::Action::Simple(domain::simple_action::SimpleAction {
231                        name: "pick-up".into(),
232                        parameters: vec![
233                            TypedParameter {
234                                name: "?arm".into(),
235                                type_: "bot".into(),
236                            },
237                            TypedParameter {
238                                name: "?cupcake".into(),
239                                type_: "locatable".into(),
240                            },
241                            TypedParameter {
242                                name: "?loc".into(),
243                                type_: "location".into(),
244                            },
245                        ],
246                        precondition: Some(Expression::And(vec![
247                            Expression::Atom {
248                                name: "on".into(),
249                                parameters: vec!["?arm".into(), "?loc".into()],
250                            },
251                            Expression::Atom {
252                                name: "on".into(),
253                                parameters: vec!["?cupcake".into(), "?loc".into(),],
254                            },
255                            Expression::Atom {
256                                name: "arm-empty".into(),
257                                parameters: vec![],
258                            },
259                        ])),
260                        effect: Expression::And(vec![
261                            Expression::Not(Box::new(Expression::Atom {
262                                name: "on".into(),
263                                parameters: vec!["?cupcake".into(), "?loc".into()],
264                            })),
265                            Expression::Atom {
266                                name: "holding".into(),
267                                parameters: vec!["?arm".into(), "?cupcake".into()],
268                            },
269                            Expression::Not(Box::new(Expression::Atom {
270                                name: "arm-empty".into(),
271                                parameters: vec![],
272                            })),
273                        ])
274                    }),
275                    domain::action::Action::Simple(domain::simple_action::SimpleAction {
276                        name: "drop".into(),
277                        parameters: vec![
278                            TypedParameter {
279                                name: "?arm".into(),
280                                type_: "bot".into(),
281                            },
282                            TypedParameter {
283                                name: "?cupcake".into(),
284                                type_: "locatable".into(),
285                            },
286                            TypedParameter {
287                                name: "?loc".into(),
288                                type_: "location".into(),
289                            },
290                        ],
291                        precondition: Some(Expression::And(vec![
292                            Expression::Atom {
293                                name: "on".into(),
294                                parameters: vec!["?arm".into(), "?loc".into(),],
295                            },
296                            Expression::Atom {
297                                name: "holding".into(),
298                                parameters: vec!["?arm".into(), "?cupcake".into(),],
299                            },
300                        ])),
301                        effect: Expression::And(vec![
302                            Expression::Atom {
303                                name: "on".into(),
304                                parameters: vec!["?cupcake".into(), "?loc".into(),],
305                            },
306                            Expression::Atom {
307                                name: "arm-empty".into(),
308                                parameters: vec![],
309                            },
310                            Expression::Not(Box::new(Expression::Atom {
311                                name: "holding".into(),
312                                parameters: vec!["?arm".into(), "?cupcake".into(),],
313                            })),
314                        ])
315                    }),
316                    domain::action::Action::Simple(domain::simple_action::SimpleAction {
317                        name: "move".into(),
318                        parameters: vec![
319                            TypedParameter {
320                                name: "?arm".into(),
321                                type_: "bot".into(),
322                            },
323                            TypedParameter {
324                                name: "?from".into(),
325                                type_: "location".into(),
326                            },
327                            TypedParameter {
328                                name: "?to".into(),
329                                type_: "location".into(),
330                            },
331                        ],
332                        precondition: Some(Expression::And(vec![
333                            Expression::Atom {
334                                name: "on".into(),
335                                parameters: vec!["?arm".into(), "?from".into(),],
336                            },
337                            Expression::Atom {
338                                name: "path".into(),
339                                parameters: vec!["?from".into(), "?to".into(),],
340                            },
341                        ])),
342                        effect: Expression::And(vec![
343                            Expression::Not(Box::new(Expression::Atom {
344                                name: "on".into(),
345                                parameters: vec!["?arm".into(), "?from".into(),],
346                            })),
347                            Expression::Atom {
348                                name: "on".into(),
349                                parameters: vec!["?arm".into(), "?to".into(),],
350                            },
351                        ])
352                    })
353                ],
354            }
355        );
356    }
357
358    #[test]
359    #[allow(clippy::too_many_lines)]
360    fn test_durative_domain() {
361        std::env::set_var("RUST_LOG", "debug");
362        let _ = pretty_env_logger::try_init();
363        let durative_actions_domain = include_str!("../tests/durative-actions-domain.pddl");
364        assert_eq!(
365            Domain::parse(durative_actions_domain.into()).expect("Failed to parse domain"),
366            Domain {
367                name: "collaborative-cloth-piling".into(),
368                requirements: vec![
369                    Requirement::Strips,
370                    Requirement::Typing,
371                    Requirement::DurativeActions,
372                    Requirement::NumericFluents,
373                ],
374                types: vec![
375                    TypeDef {
376                        name: "robot".into(),
377                        parent: Some("agent".into()),
378                    },
379                    TypeDef {
380                        name: "human".into(),
381                        parent: Some("agent".into()),
382                    },
383                    TypeDef {
384                        name: "garment".into(),
385                        parent: Some("physical-object".into()),
386                    },
387                    TypeDef {
388                        name: "pile".into(),
389                        parent: Some("physical-object".into()),
390                    },
391                    TypeDef {
392                        name: "agent".into(),
393                        parent: Some("physical-object".into()),
394                    },
395                    TypeDef {
396                        name: "garment-type".into(),
397                        parent: Some("concept".into()),
398                    },
399                    TypeDef {
400                        name: "concept".into(),
401                        parent: Some("social-object".into()),
402                    },
403                    TypeDef {
404                        name: "social-object".into(),
405                        parent: Some("object".into()),
406                    },
407                    TypeDef {
408                        name: "physical-object".into(),
409                        parent: Some("object".into()),
410                    },
411                    TypeDef {
412                        name: "object".into(),
413                        parent: Some("entity".into()),
414                    },
415                    TypeDef {
416                        name: "entity".into(),
417                        parent: None,
418                    },
419                ],
420                predicates: vec![
421                    TypedPredicate {
422                        name: "grasped-by".into(),
423                        parameters: vec![
424                            TypedParameter {
425                                name: "?o".into(),
426                                type_: "object".into(),
427                            },
428                            TypedParameter {
429                                name: "?a".into(),
430                                type_: "agent".into(),
431                            },
432                        ],
433                    },
434                    TypedPredicate {
435                        name: "graspable".into(),
436                        parameters: vec![TypedParameter {
437                            name: "?o".into(),
438                            type_: "object".into(),
439                        },],
440                    },
441                    TypedPredicate {
442                        name: "free-to-manipulate".into(),
443                        parameters: vec![TypedParameter {
444                            name: "?a".into(),
445                            type_: "agent".into(),
446                        },],
447                    },
448                    TypedPredicate {
449                        name: "on-pile".into(),
450                        parameters: vec![
451                            TypedParameter {
452                                name: "?g".into(),
453                                type_: "garment".into(),
454                            },
455                            TypedParameter {
456                                name: "?p".into(),
457                                type_: "pile".into(),
458                            },
459                        ],
460                    },
461                    TypedPredicate {
462                        name: "piled".into(),
463                        parameters: vec![TypedParameter {
464                            name: "?g".into(),
465                            type_: "garment".into(),
466                        },],
467                    },
468                    TypedPredicate {
469                        name: "supported".into(),
470                        parameters: vec![TypedParameter {
471                            name: "?g".into(),
472                            type_: "garment".into(),
473                        },],
474                    },
475                    TypedPredicate {
476                        name: "lifted".into(),
477                        parameters: vec![TypedParameter {
478                            name: "?g".into(),
479                            type_: "garment".into(),
480                        },],
481                    },
482                    TypedPredicate {
483                        name: "folded".into(),
484                        parameters: vec![TypedParameter {
485                            name: "?g".into(),
486                            type_: "garment".into(),
487                        },],
488                    },
489                    TypedPredicate {
490                        name: "unfolded".into(),
491                        parameters: vec![TypedParameter {
492                            name: "?g".into(),
493                            type_: "garment".into(),
494                        },],
495                    },
496                ],
497                constants: vec![],
498                functions: vec![
499                    TypedPredicate {
500                        name: "grasp-time".into(),
501                        parameters: vec![TypedParameter {
502                            name: "?a".into(),
503                            type_: "agent".into(),
504                        },],
505                    },
506                    TypedPredicate {
507                        name: "current-number-of-garments-on-pile".into(),
508                        parameters: vec![TypedParameter {
509                            name: "?p".into(),
510                            type_: "pile".into(),
511                        },],
512                    },
513                    TypedPredicate {
514                        name: "target-number-of-garments-on-pile".into(),
515                        parameters: vec![TypedParameter {
516                            name: "?p".into(),
517                            type_: "pile".into(),
518                        },],
519                    },
520                ],
521                actions: vec![
522                    domain::action::Action::Durative(DurativeAction {
523                        name: "grasp-folded-garment".into(),
524                        parameters: vec![
525                            TypedParameter {
526                                name: "?g".into(),
527                                type_: "garment".into(),
528                            },
529                            TypedParameter {
530                                name: "?a".into(),
531                                type_: "agent".into(),
532                            },
533                        ],
534                        duration: Expression::BinaryOp(
535                            BinaryOp::Equal,
536                            Box::new(Expression::Atom {
537                                name: "?duration".into(),
538                                parameters: vec![]
539                            }),
540                            Box::new(Expression::Atom {
541                                name: "grasp-time".into(),
542                                parameters: vec!["?a".into()],
543                            })
544                        ),
545                        condition: Some(Expression::And(vec![
546                            Expression::Duration(
547                                DurationInstant::Start,
548                                Box::new(Expression::Atom {
549                                    name: "free-to-manipulate".into(),
550                                    parameters: vec!["?a".into()],
551                                })
552                            ),
553                            Expression::Duration(
554                                DurationInstant::Start,
555                                Box::new(Expression::Atom {
556                                    name: "folded".into(),
557                                    parameters: vec!["?g".into()],
558                                })
559                            ),
560                            Expression::Duration(
561                                DurationInstant::Start,
562                                Box::new(Expression::Atom {
563                                    name: "graspable".into(),
564                                    parameters: vec!["?g".into()],
565                                })
566                            ),
567                        ])),
568                        effect: Expression::And(vec![
569                            Expression::Duration(
570                                DurationInstant::Start,
571                                Box::new(Expression::Not(Box::new(Expression::Atom {
572                                    name: "free-to-manipulate".into(),
573                                    parameters: vec!["?a".into()],
574                                })))
575                            ),
576                            Expression::Duration(
577                                DurationInstant::Start,
578                                Box::new(Expression::Not(Box::new(Expression::Atom {
579                                    name: "graspable".into(),
580                                    parameters: vec!["?g".into()],
581                                })))
582                            ),
583                            Expression::Duration(
584                                DurationInstant::End,
585                                Box::new(Expression::Atom {
586                                    name: "grasped-by".into(),
587                                    parameters: vec!["?g".into(), "?a".into()],
588                                })
589                            ),
590                        ])
591                    }),
592                    domain::action::Action::Durative(DurativeAction {
593                        name: "grasp-unfolded-garment".into(),
594                        parameters: vec![
595                            TypedParameter {
596                                name: "?g".into(),
597                                type_: "garment".into(),
598                            },
599                            TypedParameter {
600                                name: "?h".into(),
601                                type_: "human".into(),
602                            },
603                        ],
604                        duration: Expression::BinaryOp(
605                            BinaryOp::Equal,
606                            Box::new(Expression::Atom {
607                                name: "?duration".into(),
608                                parameters: vec![]
609                            }),
610                            Box::new(Expression::Number(100))
611                        ),
612                        condition: Some(Expression::And(vec![
613                            Expression::Duration(
614                                DurationInstant::Start,
615                                Box::new(Expression::Atom {
616                                    name: "free-to-manipulate".into(),
617                                    parameters: vec!["?h".into()],
618                                })
619                            ),
620                            Expression::Duration(
621                                DurationInstant::Start,
622                                Box::new(Expression::Atom {
623                                    name: "unfolded".into(),
624                                    parameters: vec!["?g".into()],
625                                })
626                            ),
627                            Expression::Duration(
628                                DurationInstant::Start,
629                                Box::new(Expression::Atom {
630                                    name: "graspable".into(),
631                                    parameters: vec!["?g".into()],
632                                })
633                            ),
634                        ])),
635                        effect: Expression::And(vec![
636                            Expression::Duration(
637                                DurationInstant::Start,
638                                Box::new(Expression::Not(Box::new(Expression::Atom {
639                                    name: "free-to-manipulate".into(),
640                                    parameters: vec!["?h".into()],
641                                })))
642                            ),
643                            Expression::Duration(
644                                DurationInstant::Start,
645                                Box::new(Expression::Not(Box::new(Expression::Atom {
646                                    name: "graspable".into(),
647                                    parameters: vec!["?g".into()],
648                                })))
649                            ),
650                            Expression::Duration(
651                                DurationInstant::End,
652                                Box::new(Expression::Atom {
653                                    name: "grasped-by".into(),
654                                    parameters: vec!["?g".into(), "?h".into()],
655                                })
656                            ),
657                        ])
658                    }),
659                    domain::action::Action::Durative(DurativeAction {
660                        name: "lift".into(),
661                        parameters: vec![
662                            TypedParameter {
663                                name: "?g".into(),
664                                type_: "garment".into(),
665                            },
666                            TypedParameter {
667                                name: "?a".into(),
668                                type_: "agent".into(),
669                            },
670                        ],
671                        duration: Expression::BinaryOp(
672                            BinaryOp::Equal,
673                            Box::new(Expression::Atom {
674                                name: "?duration".into(),
675                                parameters: vec![]
676                            }),
677                            Box::new(Expression::Number(100))
678                        ),
679                        condition: Some(Expression::And(vec![
680                            Expression::Duration(
681                                DurationInstant::Start,
682                                Box::new(Expression::Atom {
683                                    name: "grasped-by".into(),
684                                    parameters: vec!["?g".into(), "?a".into()],
685                                })
686                            ),
687                            Expression::Duration(
688                                DurationInstant::Start,
689                                Box::new(Expression::Atom {
690                                    name: "supported".into(),
691                                    parameters: vec!["?g".into()],
692                                })
693                            ),
694                        ])),
695                        effect: Expression::And(vec![
696                            Expression::Duration(
697                                DurationInstant::End,
698                                Box::new(Expression::Not(Box::new(Expression::Atom {
699                                    name: "supported".into(),
700                                    parameters: vec!["?g".into()],
701                                })))
702                            ),
703                            Expression::Duration(
704                                DurationInstant::End,
705                                Box::new(Expression::Atom {
706                                    name: "lifted".into(),
707                                    parameters: vec!["?g".into()],
708                                })
709                            ),
710                        ])
711                    }),
712                    domain::action::Action::Durative(DurativeAction {
713                        name: "pile-garment".into(),
714                        parameters: vec![
715                            TypedParameter {
716                                name: "?g".into(),
717                                type_: "garment".into(),
718                            },
719                            TypedParameter {
720                                name: "?p".into(),
721                                type_: "pile".into(),
722                            },
723                            TypedParameter {
724                                name: "?t".into(),
725                                type_: "garment-type".into(),
726                            },
727                            TypedParameter {
728                                name: "?a".into(),
729                                type_: "agent".into(),
730                            },
731                        ],
732                        duration: Expression::BinaryOp(
733                            BinaryOp::Equal,
734                            Box::new(Expression::Atom {
735                                name: "?duration".into(),
736                                parameters: vec![]
737                            }),
738                            Box::new(Expression::Atom {
739                                name: "grasp-time".into(),
740                                parameters: vec!["?a".into()],
741                            })
742                        ),
743                        condition: Some(Expression::And(vec![
744                            Expression::Duration(
745                                DurationInstant::Start,
746                                Box::new(Expression::Atom {
747                                    name: "grasped-by".into(),
748                                    parameters: vec!["?g".into(), "?a".into()],
749                                })
750                            ),
751                            Expression::Duration(
752                                DurationInstant::Start,
753                                Box::new(Expression::Atom {
754                                    name: "lifted".into(),
755                                    parameters: vec!["?g".into()],
756                                })
757                            ),
758                            Expression::Duration(
759                                DurationInstant::Start,
760                                Box::new(Expression::Atom {
761                                    name: "folded".into(),
762                                    parameters: vec!["?g".into()],
763                                })
764                            ),
765                        ])),
766                        effect: Expression::And(vec![
767                            Expression::Duration(
768                                DurationInstant::Start,
769                                Box::new(Expression::Not(Box::new(Expression::Atom {
770                                    name: "grasped-by".into(),
771                                    parameters: vec!["?g".into(), "?a".into()],
772                                })))
773                            ),
774                            Expression::Duration(
775                                DurationInstant::End,
776                                Box::new(Expression::Atom {
777                                    name: "graspable".into(),
778                                    parameters: vec!["?g".into()],
779                                })
780                            ),
781                            Expression::Duration(
782                                DurationInstant::End,
783                                Box::new(Expression::Atom {
784                                    name: "free-to-manipulate".into(),
785                                    parameters: vec!["?a".into()],
786                                })
787                            ),
788                            Expression::Duration(
789                                DurationInstant::End,
790                                Box::new(Expression::Atom {
791                                    name: "piled".into(),
792                                    parameters: vec!["?g".into()],
793                                })
794                            ),
795                            Expression::Duration(
796                                DurationInstant::End,
797                                Box::new(Expression::Atom {
798                                    name: "on-pile".into(),
799                                    parameters: vec!["?g".into(), "?p".into()],
800                                })
801                            ),
802                            Expression::Duration(
803                                DurationInstant::End,
804                                Box::new(Expression::Increase(
805                                    Box::new(Expression::Atom {
806                                        name: "current-number-of-garments-on-pile".into(),
807                                        parameters: vec!["?p".into()],
808                                    }),
809                                    Box::new(Expression::Number(1))
810                                ))
811                            ),
812                        ])
813                    }),
814                    domain::action::Action::Durative(DurativeAction {
815                        name: "fold-garment".into(),
816                        parameters: vec![
817                            TypedParameter {
818                                name: "?g".into(),
819                                type_: "garment".into(),
820                            },
821                            TypedParameter {
822                                name: "?h".into(),
823                                type_: "human".into(),
824                            },
825                        ],
826                        duration: Expression::BinaryOp(
827                            BinaryOp::Equal,
828                            Box::new(Expression::Atom {
829                                name: "?duration".into(),
830                                parameters: vec![]
831                            }),
832                            Box::new(Expression::Number(100))
833                        ),
834                        condition: Some(Expression::And(vec![
835                            Expression::Duration(
836                                DurationInstant::Start,
837                                Box::new(Expression::Atom {
838                                    name: "unfolded".into(),
839                                    parameters: vec!["?g".into()],
840                                })
841                            ),
842                            Expression::Duration(
843                                DurationInstant::Start,
844                                Box::new(Expression::Atom {
845                                    name: "lifted".into(),
846                                    parameters: vec!["?g".into()],
847                                })
848                            ),
849                            Expression::Duration(
850                                DurationInstant::Start,
851                                Box::new(Expression::Atom {
852                                    name: "grasped-by".into(),
853                                    parameters: vec!["?g".into(), "?h".into()],
854                                })
855                            ),
856                        ])),
857                        effect: Expression::And(vec![
858                            Expression::Duration(
859                                DurationInstant::End,
860                                Box::new(Expression::Atom {
861                                    name: "free-to-manipulate".into(),
862                                    parameters: vec!["?h".into()],
863                                })
864                            ),
865                            Expression::Duration(
866                                DurationInstant::End,
867                                Box::new(Expression::Not(Box::new(Expression::Atom {
868                                    name: "unfolded".into(),
869                                    parameters: vec!["?g".into()],
870                                })))
871                            ),
872                            Expression::Duration(
873                                DurationInstant::End,
874                                Box::new(Expression::Not(Box::new(Expression::Atom {
875                                    name: "lifted".into(),
876                                    parameters: vec!["?g".into()],
877                                })))
878                            ),
879                            Expression::Duration(
880                                DurationInstant::End,
881                                Box::new(Expression::Not(Box::new(Expression::Atom {
882                                    name: "grasped-by".into(),
883                                    parameters: vec!["?g".into(), "?h".into()],
884                                })))
885                            ),
886                            Expression::Duration(
887                                DurationInstant::End,
888                                Box::new(Expression::Atom {
889                                    name: "graspable".into(),
890                                    parameters: vec!["?g".into()],
891                                })
892                            ),
893                            Expression::Duration(
894                                DurationInstant::End,
895                                Box::new(Expression::Atom {
896                                    name: "folded".into(),
897                                    parameters: vec!["?g".into()],
898                                })
899                            ),
900                            Expression::Duration(
901                                DurationInstant::End,
902                                Box::new(Expression::Atom {
903                                    name: "supported".into(),
904                                    parameters: vec!["?g".into()],
905                                })
906                            ),
907                        ])
908                    }),
909                    domain::action::Action::Durative(DurativeAction {
910                        name: "grasp-pile-of-garments".into(),
911                        parameters: vec![
912                            TypedParameter {
913                                name: "?p".into(),
914                                type_: "pile".into(),
915                            },
916                            TypedParameter {
917                                name: "?h".into(),
918                                type_: "human".into(),
919                            },
920                        ],
921                        duration: Expression::BinaryOp(
922                            BinaryOp::Equal,
923                            Box::new(Expression::Atom {
924                                name: "?duration".into(),
925                                parameters: vec![]
926                            }),
927                            Box::new(Expression::Number(100))
928                        ),
929                        condition: Some(Expression::And(vec![
930                            Expression::Duration(
931                                DurationInstant::Start,
932                                Box::new(Expression::Atom {
933                                    name: "free-to-manipulate".into(),
934                                    parameters: vec!["?h".into()],
935                                })
936                            ),
937                            Expression::Duration(
938                                DurationInstant::Start,
939                                Box::new(Expression::BinaryOp(
940                                    BinaryOp::Equal,
941                                    Box::new(Expression::Atom {
942                                        name: "current-number-of-garments-on-pile".into(),
943                                        parameters: vec!["?p".into()],
944                                    }),
945                                    Box::new(Expression::Atom {
946                                        name: "target-number-of-garments-on-pile".into(),
947                                        parameters: vec!["?p".into()],
948                                    })
949                                ))
950                            ),
951                            Expression::Duration(
952                                DurationInstant::Start,
953                                Box::new(Expression::Atom {
954                                    name: "graspable".into(),
955                                    parameters: vec!["?p".into()],
956                                })
957                            ),
958                        ])),
959                        effect: Expression::And(vec![
960                            Expression::Duration(
961                                DurationInstant::Start,
962                                Box::new(Expression::Not(Box::new(Expression::Atom {
963                                    name: "free-to-manipulate".into(),
964                                    parameters: vec!["?h".into()],
965                                })))
966                            ),
967                            Expression::Duration(
968                                DurationInstant::Start,
969                                Box::new(Expression::Not(Box::new(Expression::Atom {
970                                    name: "graspable".into(),
971                                    parameters: vec!["?p".into()],
972                                })))
973                            ),
974                            Expression::Duration(
975                                DurationInstant::End,
976                                Box::new(Expression::Atom {
977                                    name: "grasped-by".into(),
978                                    parameters: vec!["?p".into(), "?h".into()],
979                                })
980                            ),
981                        ])
982                    }),
983                ]
984            }
985        );
986    }
987
988    #[test]
989    #[allow(clippy::too_many_lines)]
990    fn test_durative_plan() {
991        std::env::set_var("RUST_LOG", "debug");
992        let _ = pretty_env_logger::try_init();
993        let durative_plan = include_str!("../tests/durative-plan.txt");
994        assert_eq!(
995            Plan::parse(durative_plan.into()).expect("Failed to parse plan"),
996            Plan(vec![
997                Action::Durative(plan::durative_action::DurativeAction {
998                    name: "grasp-folded-garment".into(),
999                    parameters: vec!["towel-01".into(), "robot-01".into()],
1000                    duration: 100.0,
1001                    timestamp: 0.0,
1002                }),
1003                Action::Durative(plan::durative_action::DurativeAction {
1004                    name: "grasp-unfolded-garment".into(),
1005                    parameters: vec!["dish-towel-01".into(), "human-01".into()],
1006                    duration: 100.0,
1007                    timestamp: 0.0,
1008                }),
1009                Action::Durative(plan::durative_action::DurativeAction {
1010                    name: "lift".into(),
1011                    parameters: vec!["dish-towel-01".into(), "human-01".into()],
1012                    duration: 100.0,
1013                    timestamp: 100.001,
1014                }),
1015                Action::Durative(plan::durative_action::DurativeAction {
1016                    name: "lift".into(),
1017                    parameters: vec!["towel-01".into(), "robot-01".into()],
1018                    duration: 100.0,
1019                    timestamp: 100.001,
1020                }),
1021                Action::Durative(plan::durative_action::DurativeAction {
1022                    name: "pile-garment".into(),
1023                    parameters: vec![
1024                        "towel-01".into(),
1025                        "pile-01".into(),
1026                        "dish-towel".into(),
1027                        "robot-01".into()
1028                    ],
1029                    duration: 100.0,
1030                    timestamp: 200.002,
1031                }),
1032                Action::Durative(plan::durative_action::DurativeAction {
1033                    name: "fold-garment".into(),
1034                    parameters: vec!["dish-towel-01".into(), "human-01".into()],
1035                    duration: 100.0,
1036                    timestamp: 200.002,
1037                }),
1038                Action::Durative(plan::durative_action::DurativeAction {
1039                    name: "grasp-folded-garment".into(),
1040                    parameters: vec!["dish-towel-01".into(), "robot-01".into()],
1041                    duration: 100.0,
1042                    timestamp: 300.003,
1043                }),
1044                Action::Durative(plan::durative_action::DurativeAction {
1045                    name: "grasp-unfolded-garment".into(),
1046                    parameters: vec!["towel-02".into(), "human-01".into()],
1047                    duration: 100.0,
1048                    timestamp: 300.003,
1049                }),
1050                Action::Durative(plan::durative_action::DurativeAction {
1051                    name: "lift".into(),
1052                    parameters: vec!["towel-02".into(), "human-01".into()],
1053                    duration: 100.0,
1054                    timestamp: 400.004,
1055                }),
1056                Action::Durative(plan::durative_action::DurativeAction {
1057                    name: "lift".into(),
1058                    parameters: vec!["dish-towel-01".into(), "robot-01".into()],
1059                    duration: 100.0,
1060                    timestamp: 400.004,
1061                }),
1062                Action::Durative(plan::durative_action::DurativeAction {
1063                    name: "pile-garment".into(),
1064                    parameters: vec![
1065                        "dish-towel-01".into(),
1066                        "pile-01".into(),
1067                        "dish-towel".into(),
1068                        "robot-01".into()
1069                    ],
1070                    duration: 100.0,
1071                    timestamp: 500.005,
1072                }),
1073                Action::Durative(plan::durative_action::DurativeAction {
1074                    name: "fold-garment".into(),
1075                    parameters: vec!["towel-02".into(), "human-01".into()],
1076                    duration: 100.0,
1077                    timestamp: 500.005,
1078                }),
1079                Action::Durative(plan::durative_action::DurativeAction {
1080                    name: "grasp-folded-garment".into(),
1081                    parameters: vec!["towel-02".into(), "robot-01".into()],
1082                    duration: 100.0,
1083                    timestamp: 600.006,
1084                }),
1085                Action::Durative(plan::durative_action::DurativeAction {
1086                    name: "lift".into(),
1087                    parameters: vec!["towel-02".into(), "robot-01".into()],
1088                    duration: 100.0,
1089                    timestamp: 700.007,
1090                }),
1091                Action::Durative(plan::durative_action::DurativeAction {
1092                    name: "pile-garment".into(),
1093                    parameters: vec![
1094                        "towel-02".into(),
1095                        "pile-01".into(),
1096                        "dish-towel".into(),
1097                        "robot-01".into()
1098                    ],
1099                    duration: 100.0,
1100                    timestamp: 800.008,
1101                }),
1102            ])
1103        );
1104    }
1105}