My knowledgebase is not yet making the conclusions I'm hoping for.  In
hopes of determining what additional facts or inference rules I need,
I hacked together a CommonLisp program that reads my '.clp' files and
produces an html file which shows how the rules and fact templates
relate to one another.

I expect that there's probably already similar tools that would
provide the same information (or more) working directly from the Java
Rete instance but I don't know what they are.


Here's my hack, in case anyone's interested.  It was develooped using
LispWorks but I expect it should work in any CommonLisp.


;;; -*- Mode:Lisp -*-

(defun index-clp-file (file)
  (let ((jess-info (process-jess-clp-file file)))
    (html-write jess-info
                (make-pathname :type "html" :defaults file))))


(defclass jess-info ()
  ((fact-names :initform nil)
   (rule-names :initform nil)
   (facts-to-rules
    ;; ALIST of (FACT RULE-IT-TRIGGERS)
    :initform nil)
   (rules-to-facts
    ;; ALIST of (RULE FACT-IT-ASSERTS)
    :initform nil)))

(defmethod note-rule-requires-fact ((ji jess-info) 
                                    rule-name fact-name)
  (with-slots (fact-names rule-names facts-to-rules) ji
    (pushnew fact-name fact-names)
    (pushnew rule-name rule-names)
    (pushnew (list fact-name rule-name)
             facts-to-rules
             :test 'equal)))

(defmethod note-rule-asserts-fact ((ji jess-info) rule-name fact-name)
  (with-slots (fact-names rule-names rules-to-facts) ji
    (pushnew fact-name fact-names)
    (pushnew rule-name rule-names)
    (pushnew (list rule-name fact-name)
             rules-to-facts
             :test 'equal)))


(defvar *html-indent* 0)
(defparameter +html-indent-step+ 2)

(defmacro with-tag ((stream tag &rest attributes) &body body)
  (let ((tag-var '#:tag)
        (stream-var '#:stream))
    `(let ((,tag-var ,tag)
           (,stream-var ,stream))
       (format ,stream-var "~&~vT<~a~{ ~a='~a'~}>" 
               (* *html-indent* +html-indent-step+)
               ,tag-var
               (list ,@attributes))
       (let ((*html-indent* (1+ *html-indent*)))
         ,@body)
       (format , stream-var "~&~vT</~a>"
               (* *html-indent* +html-indent-step+)
               ,tag-var))))

(defmethod html-write ((jess-info jess-info) to)
  (html-write jess-info (pathname to)))

(defmethod html-write ((jess-info jess-info) (to pathname))
  (with-open-file (stream to
                          :direction :output
                          :if-exists :supersede)
    (format *trace-output* "~&Outputting to ~a" (pathname stream))
    (html-write jess-info stream)))

(defmethod html-write ((jess-info jess-info) (stream stream))
  (with-tag (stream "html")
    (with-tag (stream "body")
      (labels ((make-a-name (what thing)
                            (ecase what
                              (:fact (format nil "fact-~a" thing))
                              (:rule (format nil "rule-~a" thing))))
               (do-em (list match-key match-value relationship other-key other-type)
                      (dolist (item list)
                        (when (eq match-value (funcall match-key item))
                          (let ((other (funcall other-key item)))
                            (with-tag (stream "li")
                              (format stream "~a " relationship)
                              (with-tag (stream
                                         "a" "href"
                                         (string-append "#"
                                                        (make-a-name other-type
                                                                     other)))
                                (format stream "~a" other))))))))
        (with-slots (fact-names rule-names facts-to-rules rules-to-facts) jess-info
          (setq fact-names (sort fact-names 'string-lessp :key 'symbol-name))
          (setq rule-names (sort rule-names 'string-lessp :key 'symbol-name))
          (with-tag (stream "dl")
            (with-tag (stream "h2")
              (write-string "Facts" stream))
            (dolist (fact fact-names)
              (with-tag (stream "dt")
                (with-tag (stream "a" "name" (make-a-name :fact fact))
                  (format stream "fact ")
                  (with-tag (stream "b")
                    (format stream "~a" fact))))
              (with-tag (stream "dd")
                (do-em facts-to-rules 'first  fact "triggers"    'second :rule)
                (do-em rules-to-facts 'second fact "asserted by" 'first  :rule))) 
            (with-tag (stream "h2")
              (write-string "Rules" stream))
            (dolist (rule rule-names)
              (with-tag (stream "dt")
                (with-tag (stream "a" "name" (make-a-name :rule rule))
                  (format stream "rule ")
                  (with-tag (stream "b")
                    (format stream "~a" rule))))
              (with-tag (stream "dd")
                (do-em facts-to-rules 'second rule "triggered by" 'first   :fact)
                (do-em rules-to-facts 'first  rule "asserts"  'second :fact)))))))))


(defun process-jess-clp-file (pathname 
                              &optional
                              (jess-info (make-instance 'jess-info)))
  (with-open-file (stream pathname
                          :direction :input)
    (format t "~&Processing ~a" (pathname stream))
    (handler-case
        (loop 
          (let ((form (read stream)))
            (process-jess-top-level-form jess-info form)))
      (end-of-file () )))
  jess-info)

(defmethod process-jess-top-level-form ((jess-info jess-info)
                                        (form list))
  (process-jess-top-level-expression jess-info (car form) (cdr form)))

(defmethod process-jess-top-level-expression ((jess-info jess-info) 
                                              (op symbol) args)
  )

(defmethod process-jess-top-level-expression ((jess-info jess-info)
                                               (op (eql 'batch)) args)
  (process-jess-clp-file (car args) jess-info))

(defmethod process-jess-top-level-expression ((jess-info jess-info)
                                               (op (eql 'deffacts)) args)
  )

(defmethod process-jess-top-level-expression ((jess-info jess-info)
                                               (op (eql 'do-backward-chaining))
                                               args)
  (format t "~&Backward chaining: ~a" (car args)))

(defmethod process-jess-top-level-expression ((jess-info jess-info)
                                               (op (eql 'defrule)) args)
  (let ((rule-name nil)
        (consequent-p :if))
    (dolist (f args)
      (multiple-value-setq (consequent-p rule-name)
        (process-jess-rule-clause jess-info f consequent-p rule-name)))))

(defgeneric process-jess-rule-clause (jess-info clause consequent-p rule-name)
  (declare (values new-consequent-p rule-name)))

(defmethod process-jess-rule-clause ((jess-info jess-info)
                                     (clause (eql '<-))
                                     (consequent-p (eql :if))
                                     (rule-name symbol))
  ;; no-op
  (values consequent-p rule-name))

(defmethod process-jess-rule-clause ((jess-info jess-info)
                                     (clause (eql '=>))
                                     (consequent-p (eql :if))
                                     (rule-name symbol))
  ;; enter consequent mode
  (values :then rule-name))

(defmethod process-jess-rule-clause ((jess-info jess-info)
                                     (clause string)
                                     (consequent-p (eql :if))
                                     rule-name)
  ;; ignore rule doc string
  (values consequent-p rule-name))

(defmethod process-jess-rule-clause ((jess-info jess-info)
                                     (clause symbol)
                                     (consequent-p (eql :if))
                                     (rule-name null))
  ;; got rule name
  (values consequent-p clause))

(defmethod process-jess-rule-clause ((jess-info jess-info)
                                     (clause symbol)
                                     consequent-p
                                     (rule-name symbol))
  ;; no-op
  (values consequent-p rule-name))

(defmethod process-jess-rule-clause ((jess-info jess-info)
                                     (clause list)
                                     (consequent-p (eql :if))
                                     (rule-name symbol))
  (unless (eq (car clause) 'test)
    (note-rule-requires-fact jess-info rule-name (car clause)))
  (values consequent-p rule-name))

(defmethod process-jess-rule-clause ((jess-info jess-info)
                                     (clause list)
                                     (consequent-p (eql :then))
                                     (rule-name symbol))
  (when (eq (car clause) 'assert)
    (dolist (asserted (cdr clause))
      (note-rule-asserts-fact jess-info rule-name (car asserted))))
  (values consequent-p rule-name))




--------------------------------------------------------------------
To unsubscribe, send the words 'unsubscribe jess-users [EMAIL PROTECTED]'
in the BODY of a message to [EMAIL PROTECTED], NOT to the list
(use your own address!) List problems? Notify [EMAIL PROTECTED]
--------------------------------------------------------------------

Reply via email to