Hi Mark,

This looks like a very interesting and useful utility you've put together here.

I could certainly use this output to do some introspection of my own rulebase.

Problem is, I haven't looked at lisp code for over a decade, and can barely follow it.

Three things might get me started, though:

1) How do I envoke this? (process-jess-clp-file ?)
    Can it be run with multiple .clp files (if not, I could just merge them).
2) Have you tried running this under emacs (the only lisp environment I have at my
disposal)
3) Any other pointers welcome!

Thanks,

Jack

Mark Nahabedian wrote:

> 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]
> --------------------------------------------------------------------


--------------------------------------------------------------------
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