diff --git a/hddl-utils.asd b/hddl-utils.asd index 43d2f8c..b997fcd 100644 --- a/hddl-utils.asd +++ b/hddl-utils.asd @@ -18,9 +18,7 @@ :components ((:file "package") ; Package definition. (:file "decls" :depends-on ("package")) (:file "commons" :depends-on ("package" "decls")) - #+nil(:file "merger" :depends-on ("package")) - #+nil(:file "problem-macros" :depends-on ("package")) -; (:file "object-methods" :depends-on ("package" "decls")) + (:file "hddl-checker" :depends-on ("commons")) )) diff --git a/hddl-utils/commons.lisp b/hddl-utils/commons.lisp index 24bf376..e628f5f 100644 --- a/hddl-utils/commons.lisp +++ b/hddl-utils/commons.lisp @@ -41,17 +41,30 @@ (constants (when (has-element-p old-domain :constants) (domain-constants old-domain))) - (methods (domain-methods old-domain)) + (methods (mapcar #'canonicalize-method (domain-methods old-domain))) (tasks (domain-tasks old-domain)) (actions (domain-actions old-domain))) (make-domain (domain-name old-domain) - :requirements requirements - :types types - :constants constants - :predicates predicates - :tasks tasks - :methods methods - :actions actions))) + :requirements requirements + :types types + :constants constants + :predicates predicates + :tasks tasks + :methods methods + :actions actions))) + +(defun canonicalize-method (original) + (unless (or (find :ordered-subtasks original) + (find :ordered-tasks original)) + (error "CANONICALIZE-METHOD does not yet support partially-ordered methods.")) + (make-ordered-method (method-name original) + (method-task original) + (method-parameters original) + :precond (method-precondition original) + :tasks (let ((tasks (method-subtasks original))) + (if (eq (first tasks) 'and) + (rest tasks) + tasks)))) (defun make-problem (name &key requirements domain objects init goal htn @@ -248,6 +261,9 @@ arguments. Unless COMPLETE-P is NIL, will check for mandatory components." (finally (return nil))) (error "No ordered subtasks found in HTN: ~s" htn))) +(deftype hddl-variable () + 'pddl-variable) + ;;; helper function ;;; allows us to match a tag against x as the first component, diff --git a/hddl-utils/hddl-checker.lisp b/hddl-utils/hddl-checker.lisp new file mode 100644 index 0000000..63d4b0e --- /dev/null +++ b/hddl-utils/hddl-checker.lisp @@ -0,0 +1,402 @@ +(in-package :hddl-utils) + +(deftype only-values (&rest value-spec) + `(values ,@value-spec &optional)) + +(deftype only-value (value-spec) + `(values ,value-spec &optional)) + +(defclass domain-info () + ((name-types + :initarg :name-types + :reader name-types + :type hash-table + :initform (make-hash-table :test 'eq) + :documentation "A table mapping names in the domain to types of entity, +including :task, :method, :action, :type, :predicate, :object" + ) + (name-arities + :initarg :name-arities + :reader name-arities + :type hash-table + :initform (make-hash-table :test 'eq) + )) + ) + +;;; FIXME: rename to HDDL-flaw +(define-condition domain-flaw () + ((context + :initarg :context + :reader context + :initform nil + :type (or null string) + ))) + + +(define-condition inconsistent-name-type (domain-flaw) + ((name + :initarg :name + :reader name + ) + (prev-type + :initarg :prev-type + :reader prev-type + ) + (new-type + :initarg :new-type + :reader new-type + )) + (:report (lambda (c s) + (format s "Inconsistent name type: ~s first defined as ~s is redefined as ~s~@[ in ~a~]." + (name c) (prev-type c) (new-type c) (context-string (context c)))))) + +(define-condition inconsistent-name-arity (domain-flaw) + ((name + :initarg :name + :reader name + ) + (prev-arity + :initarg :prev-arity + :reader prev-arity + ) + (new-arity + :initarg :new-arity + :reader new-arity + )) + (:report (lambda (c s) + (format s "Inconsistent name arity: ~s first defined as arity ~d is redefined as ~d~@[ in ~a~]." + (name c) (prev-arity c) (new-arity c) (context-string (context c)))))) + +(define-condition incorrect-arity (domain-flaw) + ((name + :initarg :name + ) + (arity + :initarg :arity + ) + (expr + :initarg :expr + )) + (:report (lambda (c s) + (with-slots (name arity expr context) c + (format s "Predicate ~a used with incorrect arity ~a, should be ~d~@[ in ~a~]." + name expr arity (context-string context)))))) + +(define-condition undefined-task (domain-flaw) + ((task-name + :initarg :task-name + ) + (type + :initform "task" + :initarg :type)) + (:report (lambda (c s) + (with-slots (task-name type context) c + (format s "Undefined ~a name ~s~@[ in ~a~]." + type task-name (context-string context)))))) + +(define-condition undefined-subtask (undefined-task) + () + (:default-initargs :type "subtask (task or action)")) + +(define-condition undefined-type (domain-flaw) + ((type-name + :initarg :type-name + )) + (:report (lambda (c s) + (with-slots (type-name context) c + (format s "Undefined type name ~s~@[ in ~a~]." type-name + (context-string context)))))) + +(define-condition bad-variable-name (domain-flaw) + ((name + :initarg :name + )) + (:report (lambda (c s) + (with-slots (name context) c + (format s "Incorrect variable name ~a~@[ in ~a~]." name + (context-string context)))))) + +;;;--------------------------------------------------------------------------- +;;; Setter macros +;;;--------------------------------------------------------------------------- + + +(defmacro set-name-arity ((domain-info name &optional context) arity) + `(set-name-arity-fun ,domain-info ,name ,context ,arity)) + +(defmacro set-name-type ((domain-info name &optional context) type) + `(set-name-type-fun ,domain-info ,name ,context ,type)) + +;;; FIXME: for later refinement +(defun context-string (context) + (when context + (format nil "~a" context))) + +(defun print-domain-info (domain-info &optional (stream t)) + (format stream "~&Name types:~%") + (let* ((names (sort (alexandria:hash-table-keys (name-types domain-info)) #'string-lessp)) + (max-name-length + (reduce #'max (mapcar #'symbol-name names) :key 'length :initial-value 0)) + (format-string (format nil "~~&~~~da ~~a~~%" max-name-length))) + (iter (for sym in names) + (format stream format-string sym (name-type domain-info sym)))) + (format stream "~&Name arities:~%") + (let* ((names (alexandria:hash-table-keys (name-arities domain-info))) + (max-name-length + (reduce #'max (mapcar #'symbol-name names) :key 'length :initial-value 0)) + (max-arity (reduce #'max (alexandria:hash-table-values (name-arities domain-info)) :initial-value 0)) + (arity-width (1+ (floor max-arity 10))) + (format-string (format nil "~~&~~~da ~~~da~~%" max-name-length arity-width))) + (iter (for sym in names) + (format stream format-string sym (name-arity domain-info sym))))) + +(defun check-hddl-domain-file (domain-file) + (let ((domain (read-hddl-file domain-file))) + (check-domain domain))) + +(defun check-hddl-domain-and-problem-files (domain-file problem-file) + (let ((problem (read-hddl-file problem-file))) + (multiple-value-bind (success domain-info) + (check-hddl-domain-file domain-file) + (if success + (check-problem problem domain-info) + (progn + (format t "~&Stopped before checking problem because of failure checking domain.~%") + (values nil domain-info)))))) + + +(defun check-domain (domain &optional verbose) + (let ((domain-info (make-instance 'domain-info)) + domain-flaws) + (set-name-type (domain-info 'hddl::object) :type) + (handler-bind ((domain-flaw #'(lambda (c) + (push c domain-flaws) + (continue c)))) + (check-types domain-info (canonicalize-types (domain-types domain))) + ;; at this point all types should be defined... + (check-constants domain-info (canonicalize-types (domain-constants domain))) + (check-predicates domain-info (domain-predicates domain)) + (check-tasks domain-info (domain-tasks domain)) + (check-actions domain-info (domain-actions domain)) + (check-methods domain-info (domain-methods domain))) + (when verbose (print-domain-info domain-info)) + (values + (if domain-flaws + (progn + (print-domain-flaws domain-flaws) + nil) + t) + domain-info))) + +(defun check-problem (problem domain-info &optional verbose) + (let (problem-flaws) + (handler-bind ((domain-flaw #'(lambda (c) + (push c problem-flaws) + (continue c)))) + ;; at this point all types should be defined... + (check-objects domain-info (canonicalize-types (problem-objects problem))) + (check-initial-state domain-info (problem-state problem)) + ;(check-htn (problem-htn problem)) + ) + (when verbose (print-domain-info domain-info)) + (values + (if problem-flaws + (progn + (print-domain-flaws problem-flaws) + nil) + t) + domain-info)) + ) + +(defun print-domain-flaws (flaw-list) + (iter (for flaw in flaw-list) + (princ flaw *error-output*) + (terpri *error-output*))) + +;;; PRECONDITION: must be called *after* CHECK-TYPES +(defun check-constants (domain-info constants) + (iter (for (name hyphen type . rest) on constants by 'cdddr) + (declare (ignorable rest)) + (unless (eq hyphen '-) + (error "Ill-formed constant definition: ~a" `(,name ,hyphen ,type))) + (set-name-type (domain-info name "constant definitions") :object) + (verify-type-name domain-info type "constant definitions"))) + +(defun check-objects (domain-info objects) + (iter (for (name hyphen type . rest) on objects by 'cdddr) + (declare (ignorable rest)) + (unless (eq hyphen '-) + (error "Ill-formed constant definition: ~a" `(,name ,hyphen ,type))) + (set-name-type (domain-info name "problem object definitions") :object) + (verify-type-name domain-info type "problem object definitions"))) + +(defun check-initial-state (domain-info facts) + (iter (for fact in facts) + (as pred = (first fact)) + (verify-predicate-name domain-info pred) + (let ((arity (name-arity domain-info pred))) + (unless (= arity (length (rest fact))) + (signal 'incorrect-arity :arity arity :name pred :expr fact))))) + +(defun check-predicates (domain-info predicates) + (iter (for (name . parameters) in predicates) + (as arity = (check-parameter-list domain-info parameters + (format nil "parameter spec for definition of predicate ~a" name))) + (set-name-type (domain-info name "predicate definitions") :predicate) + (set-name-arity (domain-info name "predicate definitions") arity))) + +(defun check-parameter-list (domain-info parameters &optional context) + "Check that PARAMETERS is a well-formed parameter list, signaling flaws as appropriate. +Return the arity of the parameter list." + (iter (for (var hyphen type . rest) on (canonicalize-types parameters) by 'cdddr) + (with arity = 0) + (declare (ignorable rest)) + (unless (eq hyphen '-) + (error "Ill-formed type definition: ~a" `(,var ,hyphen ,type))) + (unless (typep var 'hddl-variable) + (signal 'bad-variable-name :name var :context context)) + (verify-type-name domain-info type context) + (incf arity) + (finally (return arity)))) + +(defun check-tasks (domain-info tasks) + (iter (for task in tasks) + (as name = (second task)) + (as context = (format nil "parameter spec for definition of task ~a" name)) + (as arity = (check-parameter-list domain-info (task-parameters task) context)) + (assert (eq (first task) :task)) + (set-name-type (domain-info name "task definitions") :task) +(set-name-arity (domain-info name context) arity))) + +(defun check-methods (domain-info methods) + (iter (for method in methods) + (as keyword = (first method)) + (as name = (second method)) + (as method-task = (method-task method)) + (as task-name = (first method-task)) + (assert (eq keyword :method)) + (verify-task-name domain-info task-name (format nil "method task for definition of method ~a" name)) + (set-name-type (domain-info name (format nil "method definition of method named ~a" name)) :method) + (check-method-subtasks domain-info + ;; this way puts subtasks in flattened-conj form and then takes the list of subtasks + (rest (flatten-conjunction (method-subtasks method) nil)) + (format nil "Subtasks of method ~a" name)))) + +(defun check-method-subtasks (domain-info subtasks &optional context) + (iter (for subtask in subtasks) + (as task-name = (first subtask)) + (as valid-task = (verify-subtask-name domain-info task-name)) + (when valid-task + (let ((arity (name-arity domain-info task-name))) + (unless (= arity (length (rest subtask))) + (signal 'incorrect-arity :arity arity :name task-name :expr subtask :context context)))))) + +(defun verify-type-name (domain-info type-name &optional context) + (let ((name-type (name-type domain-info type-name))) + (cond ((null name-type) + (signal 'undefined-type :type-name type-name :context context)) + ((not (eq name-type :type)) + (signal 'inconsistent-name-type :new-type :type :prev-type name-type + :name type-name :context context))))) + +(defun verify-task-name (domain-info task-name &optional context) + (let ((name-type (name-type domain-info task-name))) + (cond ((null name-type) + (signal 'undefined-task :task-name task-name :context context) + nil) + ((not (eq name-type :task)) + (signal 'inconsistent-name-type :new-type :task :prev-type name-type + :name task-name :context context) + nil) + (t t)))) + +(defun verify-subtask-name (domain-info task-name &optional context) + (let ((name-type (name-type domain-info task-name))) + (cond ((null name-type) + (signal 'undefined-subtask :task-name task-name :context context) + nil) + ((or (eq name-type :action) (eq name-type :task)) + t) + (t + (signal 'inconsistent-name-type :new-type :subtask :prev-type name-type + :name task-name :context context) + nil)))) + +(defun verify-predicate-name (domain-info predicate-name &optional context) + (let ((name-type (name-type domain-info predicate-name))) + (cond ((null name-type) + (signal 'undefined-predicate :predicate-name predicate-name :context context)) + ((not (eq name-type :predicate)) + (signal 'inconsistent-name-type :new-type :type :prev-type name-type + :name predicate-name :context context))))) + + +(defun check-actions (domain-info actions) + (iter (for action in actions) + (as keyword = (first action)) + (as name = (second action)) + (as context = (format nil "definition of action ~a" name)) + (assert (eq keyword :action)) + (set-name-type (domain-info name context) :action) + (let ((arity (check-parameter-list domain-info (action-params action) + context))) + (set-name-arity (domain-info name context) arity)))) + +(defun check-types (domain-info types) + (let ((context "type definition")) + (iter (for (type hyphen super-type . rest) on types by 'cdddr) + (with super-types) + (declare (ignorable rest)) + (unless (eq hyphen '-) + (error "Ill-formed type definition: ~a" `(,type ,hyphen ,super-type))) + (set-name-type (domain-info type context) :type) + (pushnew super-type super-types) + (finally (mapc #'(lambda (super-type) (verify-type-name domain-info super-type "type definition supertypes")) super-types))))) + + +;;; Allegro doesn't seem to like the use of a defined type in a return type declaration. +#-allegro +(declaim (ftype (function (domain-info symbol) + (only-value symbol)) + name-type)) +(defun name-type (domain-info name) + (nth-value 0 #-allegro(the (only-values symbol boolean) (gethash name (name-types domain-info))) + #+allegro (gethash name (name-types domain-info)))) + +(defun set-name-type-fun (domain-info name context type) + (with-slots (name-types) domain-info + (if-let ((prev-type (the symbol (name-type domain-info name)))) + (unless (eq type prev-type) + (restart-case + (signal 'inconsistent-name-type + :name name :prev-type prev-type :new-type type) + (continue () prev-type) + (abort (&optional c) + (error "~a" (if c c + (make-instance 'inconsistent-name-type + :context context + :name name :prev-type prev-type :new-type type)))))) + (setf (gethash name (name-types domain-info)) type)))) + +#-allegro +(declaim (ftype (function (domain-info symbol) + (only-value (integer 0))) + name-arity)) +(defun name-arity (domain-info name) + (the (or null (integer 0)) (nth-value 0 (gethash name (name-arities domain-info))))) + +(defun set-name-arity-fun (domain-info name context arity) + (with-slots (name-types) domain-info + (if-let ((prev-arity (name-arity domain-info name))) + (unless (eql arity prev-arity) + (restart-case + (signal 'inconsistent-name-arity + :name name :prev-arity prev-arity :new-arity arity + :context context) + (continue () prev-arity) + (abort (&optional c) + (error "~a" (if c c + (make-instance 'inconsistent-name-arity + :context context + :name name :prev-arity prev-arity :new-arity arity)))))) + (setf (gethash name (name-arities domain-info)) arity)))) diff --git a/hddl-utils/json.lisp b/hddl-utils/json.lisp index e720cf4..92167c6 100644 --- a/hddl-utils/json.lisp +++ b/hddl-utils/json.lisp @@ -14,8 +14,9 @@ (defpackage hddl-json (:use common-lisp hddl-utils cl-json iterate) (:nicknames #:hddl-to-json) - (:import-from hddl + (:import-from #:hddl #:forall #:exists #:imply) + (:import-from #:pddl-utils #:flatten-conjunction) (:export #:hddl-to-json #:json-dump-domain #:json-dump-problem)) @@ -356,12 +357,12 @@ with \"taskName\" and \"args\" (array) components." (flet ((dump-subtasks () (as-object-member (:ordered-subtasks) (with-array () - (let ((flattened (pddl-utils::flatten-conjunction subtask-conj))) + (let ((flattened (flatten-conjunction subtask-conj nil))) (when (eq (first flattened) 'and) (setf flattened (rest flattened))) (dolist (x flattened) (as-array-member () - (json-dump-task x stream)))))))) + (json-dump-task x stream)))))))) (if as-object (with-object () (dump-subtasks)) diff --git a/hddl-utils/package.lisp b/hddl-utils/package.lisp index 4e25e2f..97f81a9 100644 --- a/hddl-utils/package.lisp +++ b/hddl-utils/package.lisp @@ -47,7 +47,8 @@ (:import-from :pddl-utils #:action-sexp-p #:has-element-p - #:durative-action-sexp-p) + #:durative-action-sexp-p + #:flatten-conjunction) #+nil (:import-from #:pddl-pprinter #:complex-task-sexp-p #:*pddl-package* @@ -94,5 +95,6 @@ #:hddl-domain-to-pddl-domain #:hddl-problem-to-pddl-problem #:canonicalize-problem + #:hddl-variable ) ) diff --git a/hddl-utils/tests/json-tests.lisp b/hddl-utils/tests/json-tests.lisp index 0fb7036..359608d 100644 --- a/hddl-utils/tests/json-tests.lisp +++ b/hddl-utils/tests/json-tests.lisp @@ -89,7 +89,7 @@ (with-output-to-string (str) (json-dump-goal prop str)))) (is (equalp - "{\"name\":\"hunt_done\",\"parameters\":[],\"task\":{\"taskName\":\"hunt\",\"args\":[]},\"precondition\":{\"op\":\"forall\",\"boundVars\":[{\"name\":\"?pos\",\"type\":\"location\"}],\"operand\":{\"op\":\"not\",\"operand\":{\"predicate\":\"mouse-at\",\"args\":[\"?pos\"]}}},\"taskNetwork\":{\"orderedSubtasks\":[{\"taskName\":\"nil\",\"args\":[]}]}}" + "{\"name\":\"hunt_done\",\"parameters\":[],\"task\":{\"taskName\":\"hunt\",\"args\":[]},\"precondition\":{\"op\":\"forall\",\"boundVars\":[{\"name\":\"?pos\",\"type\":\"location\"}],\"operand\":{\"op\":\"not\",\"operand\":{\"predicate\":\"mouse-at\",\"args\":[\"?pos\"]}}},\"taskNetwork\":{\"orderedSubtasks\":[]}}" (with-output-to-string (str) (hddl-to-json::json-dump-method (hddlify-tree '(:method hunt_done :parameters () @@ -231,5 +231,3 @@ (is (equalp "{\"parameters\":[],\"orderedSubtasks\":[{\"taskName\":\"move-short\",\"args\":[\"?snake\",\"?pos2\",\"?snakepos\"]},{\"taskName\":\"move\",\"args\":[\"?snake\",\"?pos2\",\"?goalpos\"]}]}" (with-output-to-string (str) (json-dump-htn (member :ordered-subtasks method-def) str)))))) - - diff --git a/hddl/hddl-pprint.lisp b/hddl/hddl-pprint.lisp index 642ce3c..515ff3c 100644 --- a/hddl/hddl-pprint.lisp +++ b/hddl/hddl-pprint.lisp @@ -296,4 +296,3 @@ in the form of a list of actions." (print-object obj str)))) 0 *hddl-pprint-dispatch*) - diff --git a/pddl-utils.asd b/pddl-utils.asd index 490227e..cec545b 100644 --- a/pddl-utils.asd +++ b/pddl-utils.asd @@ -30,12 +30,7 @@ :version (:read-file-form "version.lisp-expr") :serial t :class :fiveam-tester-system - :test-names ((#:problem-acc . :pddl-utils-tests) - (#:domain-acc . :pddl-utils-tests) - (#:domain-well-defined-p . :pddl-utils-tests) - (#:predicate-definitions-correct-p . :pddl-utils-tests) - (#:predicate-DONE-member-p . :pddl-utils-tests) - (#:types-correct-p . :pddl-utils-tests) + :test-names ((#:pddl-utils-tests . :pddl-utils-tests) ) :pathname "utils/tests/" :components ((:file "package") diff --git a/utils/commons.lisp b/utils/commons.lisp index f3244f5..f5d66b8 100644 --- a/utils/commons.lisp +++ b/utils/commons.lisp @@ -14,6 +14,12 @@ (deftype action () `(and list (satisfies action-sexp-p))) +(deftype pddl-variable () + `(and symbol (satisfies question-mark-prefix-p))) + +(defun question-mark-prefix-p (sym) + (eql #\? (aref (symbol-name sym) 0))) + (defun domain-p (x) (typep x 'domain)) @@ -605,9 +611,14 @@ Translates to (constant . type) alist." (member (first sexp) predicates) t))) -(defun flatten-conjunction (conj) - "Take an s-expression and, if it is a multilayer conjunction, -make it a single-layer conjunction (intermediate AND's removed)." +(defun flatten-conjunction (conj &optional (strict t)) + "Take an s-expression and, if it is a multilayer conjunction. +Returns a single-layer conjunction (intermediate AND's, if any, +removed). + Special cases: + 1. NIL should yield (AND) + 2. Simple proposition ( *) should be wrapped in AND as + (AND ( *))" (labels ((flatten-conj-list (cl) (alexandria:mappend #'flatten-1 cl)) (flatten-1 (conj) @@ -622,6 +633,15 @@ make it a single-layer conjunction (intermediate AND's removed)." (error "Cannot handle negations other than negated literals in flatten-conjunction: ~s" conj))) (otherwise (list conj))))) - (if (eq (first conj) 'and) - `(and ,@ (flatten-conj-list (rest conj))) - (flatten-1 conj)))) + (cond ((eq (first conj) 'and) + `(and ,@(flatten-conj-list (rest conj)))) + ;; just a single proposition/conjunct + ((and (first conj) (symbolp (first conj))) + (if strict + (error "FLATTEN-CONJUNCTION expects a conjunction, but got a proposition: ~s" conj) + `(and ,conj))) + (strict (error "FLATTEN-CONJUNCTION expects a conjunction as input, not an implicit conjunction.")) + (t + ;; in this case we have an implicit conjunction with no initial 'and + ;; supply one. + `(and ,@(flatten-conj-list conj)))))) diff --git a/utils/package.lisp b/utils/package.lisp index 3a29ada..6e9efdf 100644 --- a/utils/package.lisp +++ b/utils/package.lisp @@ -119,6 +119,7 @@ #:action #:domain #:problem + #:pddl-variable ;; constructor macros #:with-problem @@ -130,5 +131,3 @@ #:make-inst #:add-fact )) - - diff --git a/utils/tests/domain-test.lisp b/utils/tests/domain-test.lisp index b08b891..3c6e35b 100644 --- a/utils/tests/domain-test.lisp +++ b/utils/tests/domain-test.lisp @@ -1,5 +1,7 @@ (in-package :pddl-utils-tests) +(def-suite* pddl-utils-tests) + (defparameter *tests-dir* (namestring (translate-logical-pathname @@ -16,10 +18,10 @@ (if (and (not (= pos 0)) (eql (nth (1- pos) typed-list) '-)) ;; then the TYPE-EL is a parent type itself. - ;; Skip this occurrence of the symbol in the TYPED-LIST. + ;; Skip this occurrence of the symbol in the TYPED-LIST. (problem-free-p type-el (subseq typed-list (1+ pos))) ;; Otherwise, TYPE-EL is a subtype so look for if we have its - ;; parent. + ;; parent. (let ((next-parent-type (position '- (subseq typed-list (1+ pos))))) (if (null next-parent-type) @@ -32,7 +34,7 @@ (return-from problem-free-p nil)) ;; Else there is a parent type. That's ok, but check if this ;; TYPE-EL belongs to another parent now... - (problem-free-p type-el + (problem-free-p type-el (subseq typed-list (1+ next-parent-type))))))) t)) @@ -79,14 +81,14 @@ (read-planning-input (merge-pathnames "airport-nontemporal-adl-domain.pddl" *tests-dir*)))) - (setf (domain-predicates domain) + (setf (domain-predicates domain) (append (domain-predicates domain) (list '(DONE)))) (&body))) (test types-correct-p - (with-fixture + (with-fixture well-defined-pddl-objects () ;; Check for simple subtyping -- every ;; type should have a parent type, except the type @@ -95,13 +97,13 @@ (problem-free-p element (copy-tree (domain-types domain)))) (domain-types domain))))) - + (test predicate-definitions-correct-p (with-fixture well-defined-pddl-objects () ;; Check for duplicates... (is (every #'(lambda (element) - (let ((element-trail + (let ((element-trail (member element (domain-predicates domain) :test #'equal))) @@ -116,10 +118,10 @@ :test #'equal)))) (test domain-well-defined-p - (with-fixture + (with-fixture well-defined-pddl-objects () (is (every #'keywordp (domain-reqs domain))) - (is (every #'(lambda (el) + (is (every #'(lambda (el) (member el *pddl-keywords* :test #'eql)) (domain-reqs domain))))) @@ -219,9 +221,18 @@ (test flatten-conjunction - (is - (equalp *conjunction* - (flatten-conjunction *conjunction*))) - (is - (equalp *flattened-nested-conjunction* - (flatten-conjunction *nested-conjunction*)))) + (is + (equalp *conjunction* + (flatten-conjunction *conjunction*))) + (is + (equalp *flattened-nested-conjunction* + (flatten-conjunction *nested-conjunction*))) + (is (equalp *conjunction* + (flatten-conjunction (rest *conjunction*) nil))) + (is (equalp '(and) (flatten-conjunction nil nil))) + (is (equalp '(and (hunt snark)) + (flatten-conjunction '(hunt snark) nil))) + ;; check strict mode + (signals error (flatten-conjunction '(hunt snark) t)) + (signals error (flatten-conjunction (rest *conjunction*) t)) + ) diff --git a/utils/tests/package.lisp b/utils/tests/package.lisp index 921ebe8..404771e 100644 --- a/utils/tests/package.lisp +++ b/utils/tests/package.lisp @@ -4,9 +4,11 @@ (:use common-lisp pddl-utils) (:import-from :pddl-utils #:flatten-conjunction) (:import-from fiveam + #:def-suite* #:def-fixture #:with-fixture #:is #:test + #:signals #:run! #:*on-error*)) diff --git a/version.lisp-expr b/version.lisp-expr index b96657b..e6c3369 100644 --- a/version.lisp-expr +++ b/version.lisp-expr @@ -1 +1 @@ -"3.0.0" +"3.1.0"