[comp.sources.unix] v12i020: OPS5 in Common Lisp, Part05/05

rsalz@uunet.UU.NET (Rich Salz) (10/14/87)

Submitted-by: eric@dlcdev.UUCP (eric van tassell)
Posting-number: Volume 12, Issue 20
Archive-name: ops5/part05

;;; printing production memory

(defmacro pm (&rest z)  `(progn (mapc #'pprule ',z) (terpri) nil)) 

;Major modification here, because Common Lisp doesn't have a standard method
;for determining the column position of the cursor.  So we have to keep count.
;So colprinc records the current column number and prints the symbol.

(proclaim '(special *current-col*))
(setq *current-col* 0)

(defun nflatc(x)
   (length (princ-to-string x)))

(defun colprinc(x)
    (setq *current-col* (+ *current-col* (nflatc x)))
    (princ x))

(defun pprule (name)
  (prog (matrix next lab)
        (terpri)
        (setq *current-col* 0)
        (and (not (symbolp name)) (return nil))
        (setq matrix (get name 'production))
	(and (null matrix) (return nil))
	(terpri)
	(colprinc '|(p |)
	(colprinc name)
   top	(and (atom matrix) (go fin))
        (setq next (car matrix))
	(setq matrix (cdr matrix))
	(setq lab nil)
	(terpri)
	(cond ((eq next '-)
	       (colprinc '|  - |)
	       (setq next (car matrix))
	       (setq matrix (cdr matrix)))
	      ((eq next '-->)
	       (colprinc '|  |))
	      ((and (eq next '{) (atom (car matrix)))
	       (colprinc '|   {|)
	       (setq lab (car matrix))
	       (setq next (cadr matrix))
	       (setq matrix (cdddr matrix)))
	      ((eq next '{)
	       (colprinc '|   {|)
	       (setq lab (cadr matrix))
	       (setq next (car matrix))
	       (setq matrix (cdddr matrix)))
	      (t (colprinc '|    |)))
        (ppline next)
	(cond (lab (colprinc '| |) (colprinc lab) (colprinc '})))
	(go top)
    fin	(colprinc '|)|)))

(defun ppline (line)
  (prog ()
	(cond ((atom line) (colprinc line))
              ((equalp (symbol-name (car line)) "DISPLACED") ;don't print expanded macros
               (ppline (cadr line)))
	      (t
	       (colprinc '|(|)
	       (setq *ppline* line)
	       (ppline2)
	       (colprinc '|)|)))
        (return nil)))

(defun ppline2 ()
  (prog (needspace)
        (setq needspace nil)
   top  (and (atom *ppline*) (return nil))
        (and needspace (colprinc '| |))
        (cond ((eq (car *ppline*) #\^) (ppattval))
	      (t (pponlyval)))
        (setq needspace t)
        (go top)))

;NWRITN, sort of. 
(defun nwritn(&optional port)
   (- 76 *current-col*))

(defun ppattval ()
  (prog (att val)
        (setq att (cadr *ppline*))
	(setq *ppline* (cddr *ppline*))
	(setq val (getval))
	(cond ((> (+ (nwritn) (nflatc att) (nflatc val)) 76.)
	       (terpri)
	       (colprinc '|        |)))
        (colprinc '^)
	(colprinc att)
	(mapc (function (lambda (z) (colprinc '| |) (colprinc z))) val)))

(defun pponlyval ()
  (prog (val needspace)
	(setq val (getval))
	(setq needspace nil)
	(cond ((> (+ (nwritn) (nflatc val)) 76.)
	       (setq needspace nil)
	       (terpri)
	       (colprinc '|        |)))
    top	(and (atom val) (return nil))
        (and needspace (colprinc '| |))
	(setq needspace t)
	(colprinc (car val))
	(setq val (cdr val))
	(go top)))

(defun getval ()
  (prog (res v1)
        (setq v1 (car *ppline*))
	(setq *ppline* (cdr *ppline*))
	(cond ((member v1 '(= <> < <= => > <=>) :test #'eq)
	       (setq res (cons v1 (getval))))
	      ((eq v1 '{)
	       (setq res (cons v1 (getupto '}))))
	      ((eq v1 '<<)
	       (setq res (cons v1 (getupto '>>))))
	      ((eq v1 '//)
	       (setq res (list v1 (car *ppline*)))
	       (setq *ppline* (cdr *ppline*)))
	      (t (setq res (list v1))))
        (return res)))

(defun getupto (end)
  (prog (v)
        (and (atom *ppline*) (return nil))
	(setq v (car *ppline*))
	(setq *ppline* (cdr *ppline*))
	(cond ((eq v end) (return (list v)))
	      (t (return (cons v (getupto end))))))) 






;;; backing up



(defun record-index-plus (k)
  (setq *record-index* (+ k *record-index*))
  (cond ((< *record-index* 0.)
         (setq *record-index* *max-record-index*))
        ((> *record-index* *max-record-index*)
         (setq *record-index* 0.)))) 

; the following routine initializes the record.  putting nil in the
; first slot indicates that that the record does not go back further
; than that.  (when the system backs up, it writes nil over the used
; records so that it will recognize which records it has used.  thus
; the system is set up anyway never to back over a nil.)

(defun initialize-record nil
  (setq *record-index* 0.)
  (setq *recording* nil)
  (setq *max-record-index* 31.)
  (putvector *record-array* 0. nil)) 

; *max-record-index* holds the maximum legal index for record-array
; so it and the following must be changed at the same time

(defun begin-record (p data)
  (setq *recording* t)
  (setq *record* (list '=>refract p data))) 

(defun end-record nil
  (cond (*recording*
         (setq *record*
               (cons *cycle-count* (cons *p-name* *record*)))
         (record-index-plus 1.)
         (putvector *record-array* *record-index* *record*)
         (setq *record* nil)
         (setq *recording* nil)))) 

(defun record-change (direct time elm)
  (cond (*recording*
         (setq *record*
               (cons direct (cons time (cons elm *record*))))))) 

; to maintain refraction information, need keep only one piece of information:
; need to record all unsuccessful attempts to delete things from the conflict
; set.  unsuccessful deletes are caused by attempting to delete refracted
; instantiations.  when backing up, have to avoid putting things back into the
; conflict set if they were not deleted when running forward

(defun record-refract (rule data)
  (and *recording*
       (setq *record* (cons '<=refract (cons rule (cons data *record*))))))

(defun refracted (rule data)
  (prog (z)
        (and (null *refracts*) (return nil))
	(setq z (cons rule data))
	(return (member z *refracts*))))

(defun back (k)
  (prog (r)
   l   (and (< k 1.) (return nil))
        (setq r (getvector *record-array* *record-index*))
        (and (null r) (return '|nothing more stored|))
        (putvector *record-array* *record-index* nil)
        (record-index-plus -1.)
        (undo-record r)
        (setq k (1- k))
        (go l))) 

(defun undo-record (r)
  (prog (save act a b rate)
        ;*recording* must be off during back up
        (setq save *recording*)
        (setq *refracts* nil)
        (setq *recording* nil)
        (and *ptrace* (back-print (list 'undo (car r) (cadr r))))
        (setq r (cddr r))
   top  (and (atom r) (go fin))
        (setq act (car r))
        (setq a (cadr r))
        (setq b (caddr r))
        (setq r (cdddr r))
        (and *wtrace* (back-print (list 'undo act a)))
        (cond ((eq act '<=wm) (add-to-wm b a))
              ((eq act '=>wm) (remove-from-wm b))
              ((eq act '<=refract)
               (setq *refracts* (cons (cons a b) *refracts*)))
              ((and (eq act '=>refract) (still-present b))
	       (setq *refracts* (delete (cons a b) *refracts*))
               (setq rate (rating-part (get a 'topnode)))
               (removecs a b)
               (insertcs a b rate))
              (t (%warn '|back: cannot undo action| (list act a))))
        (go top)
   fin  (setq *recording* save)
        (setq *refracts* nil)
        (return nil))) 

; still-present makes sure that the user has not deleted something
; from wm which occurs in the instantiation about to be restored; it
; makes the check by determining whether each wme still has a time tag.

(defun still-present (data)
  (prog nil
   l   (cond ((atom data) (return t))
              ((creation-time (car data))
               (setq data (cdr data))
               (go l))
              (t (return nil))))) 


(defun back-print (x) 
  (prog (port)
        (setq port (trace-file))
        (terpri port)
	(print x port)))




;;; Functions to show how close rules are to firing

(defmacro matches (&rest rule-list)
  `(progn 
    (mapc (function matches2) ',rule-list)
    (terpri)) )

(defun matches2 (p)
  (cond ((atom p)
         (terpri)
         (terpri)
         (princ p)
         (matches3 (get p 'backpointers) 2. (cons 1. nil))))) 

(defun matches3 (nodes ce part)
  (cond ((not (null nodes))
         (terpri)
         (princ '| ** matches for |)
         (princ part)
         (princ '| ** |)
         (mapc (function write-elms) (find-left-mem (car nodes)))
         (terpri)
         (princ '| ** matches for |)
         (princ (cons ce nil))
         (princ '| ** |)
         (mapc (function write-elms) (find-right-mem (car nodes)))
         (matches3 (cdr nodes) (1+ ce) (cons ce part))))) 

(defun write-elms (wme-or-count)
  (cond ((listp wme-or-count)
	 (terpri)
	 (mapc (function write-elms2) wme-or-count)))) 

(defun write-elms2 (x)
  (princ '|  |)
  (princ (creation-time x)))

(defun find-left-mem (node)
  (cond ((eq (car node) '&and) (memory-part (caddr node)))
        (t (car (caddr node))))) 

(defun find-right-mem (node) (memory-part (cadddr node))) 


;;; Check the RHSs of productions 


(defun check-rhs (rhs) (mapc (function check-action) rhs))

(defun check-action (x)
  (prog (a)
    (cond ((atom x)
           (%warn '|atomic action| x)
	   (return nil)))
    (setq a  (car x))
    (cond ((eq a 'bind) (check-bind x))
          ((eq a 'cbind) (check-cbind x))
          ((eq a 'make) (check-make x))
          ((eq a 'modify) (check-modify x))
          ((eq a 'oremove) (check-remove x))
          ((eq a 'owrite) (check-write x))
          ((eq a 'ocall) (check-call x))
          ((eq a 'halt) (check-halt x))
          ((eq a 'openfile) (check-openfile x))
          ((eq a 'closefile) (check-closefile x))
          ((eq a 'default) (check-default x))
          ((eq a 'build) (check-build x))
          ;;the following section is responsible for replacing standard ops RHS actions
          ;;with actions which don't conflict with existing CL functions.  The RPLACA function
          ;;is used so that the change will be reflected in the production body.
          ((eq a 'remove) (rplaca x 'oremove) 
                          (check-remove x))
          ((eq a 'write)   (rplaca x 'owrite)
                          (check-write x)) 
          ((eq a 'call)   (rplaca x 'ocall)
                          (check-call x))
          (t (%warn '|undefined rhs action| a))))) 

(defun check-build (z)
  (and (null (cdr z)) (%warn '|needs arguments| z))
  (check-build-collect (cdr z)))

(defun check-build-collect (args)
  (prog (r)
    top	(and (null args) (return nil))
	(setq r (car args))
	(setq args (cdr args))
	(cond ((listp r) (check-build-collect r))
	      ((eq r '\\)
	       (and (null args) (%warn '|nothing to evaluate| r))
	       (check-rhs-value (car args))
	       (setq args (cdr args))))
	(go top)))

(defun check-remove (z) 
  (and (null (cdr z)) (%warn '|needs arguments| z))
  (mapc (function check-rhs-ce-var) (cdr z))) 

(defun check-make (z)
  (and (null (cdr z)) (%warn '|needs arguments| z))
  (check-change& (cdr z))) 

(defun check-openfile (z)
  (and (null (cdr z)) (%warn '|needs arguments| z))
  (check-change& (cdr z))) 

(defun check-closefile (z)
  (and (null (cdr z)) (%warn '|needs arguments| z))
  (check-change& (cdr z))) 

(defun check-default (z)
  (and (null (cdr z)) (%warn '|needs arguments| z))
  (check-change& (cdr z))) 

(defun check-modify (z)
  (and (null (cdr z)) (%warn '|needs arguments| z))
  (check-rhs-ce-var (cadr z))
  (and (null (cddr z)) (%warn '|no changes to make| z))
  (check-change& (cddr z))) 

(defun check-write (z)
  (and (null (cdr z)) (%warn '|needs arguments| z))
  (check-change& (cdr z))) 

(defun check-call (z)
  (prog (f)
    (and (null (cdr z)) (%warn '|needs arguments| z))
    (setq f (cadr z))
    (and (variablep f)
         (%warn '|function name must be a constant| z))
    (or (symbolp f)
        (%warn '|function name must be a symbolic atom| f))
    (or (externalp f)
        (%warn '|function name not declared external| f))
    (check-change& (cddr z)))) 

(defun check-halt (z)
  (or (null (cdr z)) (%warn '|does not take arguments| z))) 

(defun check-cbind (z)
  (prog (v)
    (or (= (length z) 2.) (%warn '|takes only one argument| z))
    (setq v (cadr z))
    (or (variablep v) (%warn '|takes variable as argument| z))
    (note-ce-variable v))) 

(defun check-bind (z)
  (prog (v)
    (or (> (length z) 1.) (%warn '|needs arguments| z))
    (setq v (cadr z))
    (or (variablep v) (%warn '|takes variable as argument| z))
    (note-variable v)
    (check-change& (cddr z)))) 


(defun check-change& (z)
  (prog (r tab-flag)
        (setq tab-flag nil)
   la   (and (atom z) (return nil))
        (setq r (car z))
        (setq z (cdr z))
        (cond ((eq r #\^)
               (and tab-flag
                    (%warn '|no value before this tab| (car z)))
               (setq tab-flag t)
               (check-tab-index (car z))
               (setq z (cdr z)))
              ((eq r '//) (setq tab-flag nil) (setq z (cdr z)))
              (t (setq tab-flag nil) (check-rhs-value r)))
        (go la))) 

(defun check-rhs-ce-var (v)
  (cond ((and (not (numberp v)) (not (ce-bound? v)))
         (%warn '|unbound element variable| v))
        ((and (numberp v) (or (< v 1.) (> v *ce-count*)))
         (%warn '|numeric element designator out of bounds| v)))) 

(defun check-rhs-value (x)
  (cond ((and x (listp x)) (check-rhs-function x))
        (t (check-rhs-atomic x)))) 

(defun check-rhs-atomic (x)
  (and (variablep x) 
       (not (bound? x)) 
       (%warn '|unbound variable| x)))

(defun check-rhs-function (x)
  (prog (a)
    (setq a (car x))
    (cond ((eq a 'compute) (check-compute x))
          ((eq a 'arith) (check-compute x))
          ((eq a 'substr) (check-substr x))
          ((eq a 'accept) (check-accept x))
          ((eq a 'acceptline) (check-acceptline x))
          ((eq a 'crlf) (check-crlf x))
          ((eq a 'genatom) (check-genatom x))
	  ((eq a 'litval) (check-litval x))
          ((eq a 'tabto) (check-tabto x))
	  ((eq a 'rjust) (check-rjust x))
	  ((not (externalp a))
	   (%warn '"rhs function not declared external" a)))))

(defun check-litval (x) 
  (or (= (length x) 2) (%warn '|wrong number of arguments| x))
  (check-rhs-atomic (cadr x)))

(defun check-accept (x)
  (cond ((= (length x) 1) nil)
        ((= (length x) 2) (check-rhs-atomic (cadr x)))
	(t (%warn '|too many arguments| x))))

(defun check-acceptline (x)
  (mapc (function check-rhs-atomic) (cdr x)))

(defun check-crlf (x) 
  (check-0-args x)) 

(defun check-genatom (x) (check-0-args x)) 

(defun check-tabto (x)
  (or (= (length x) 2) (%warn '|wrong number of arguments| x))
  (check-print-control (cadr x)))

(defun check-rjust (x)
  (or (= (length x) 2) (%warn '|wrong number of arguments| x))
  (check-print-control (cadr x)))

(defun check-0-args (x)
  (or (= (length x) 1.) (%warn '|should not have arguments| x))) 

(defun check-substr (x)
  (or (= (length x) 4.) (%warn '|wrong number of arguments| x))
  (check-rhs-ce-var (cadr x))
  (check-substr-index (caddr x))
  (check-last-substr-index (cadddr x))) 

(defun check-compute (x) (check-arithmetic (cdr x))) 

(defun check-arithmetic (l)
  (cond ((atom l)
         (%warn '|syntax error in arithmetic expression| l))
        ((atom (cdr l)) (check-term (car l)))
        ((not (member (cadr l) '(+ - * // \\) :test #'eq))
         (%warn '|unknown operator| l))
        (t (check-term (car l)) (check-arithmetic (cddr l))))) 

(defun check-term (x)
  (cond ((listp x) (check-arithmetic x))
        (t (check-rhs-atomic x)))) 

(defun check-last-substr-index (x)
  (or (eq x 'inf) (check-substr-index x))) 

(defun check-substr-index (x)
  (prog (v)
    (cond ((bound? x) (return x)))
    (setq v ($litbind x))
    (cond ((not (numberp v))
           (%warn '|unbound symbol used as index in substr| x))
          ((or (< v 1.) (> v 127.))
           (%warn '|index out of bounds in tab| x))))) 

(defun check-print-control (x)
  (prog ()
    (cond ((bound? x) (return x)))
    (cond ((or (not (numberp x)) (< x 1.) (> x 127.))
           (%warn '|illegal value for printer control| x))))) 

(defun check-tab-index (x)
  (prog (v)
    (cond ((bound? x) (return x)))
    (setq v ($litbind x))
    (cond ((not (numberp v))
           (%warn '|unbound symbol occurs after ^| x))
          ((or (< v 1.) (> v 127.))
           (%warn '|index out of bounds after ^| x))))) 

(defun note-variable (var)
  (setq *rhs-bound-vars* (cons var *rhs-bound-vars*)))

(defun bound? (var)
  (or (member var *rhs-bound-vars* :test #'eq)
      (var-dope var)))

(defun note-ce-variable (ce-var)
  (setq *rhs-bound-ce-vars* (cons ce-var *rhs-bound-ce-vars*)))

(defun ce-bound? (ce-var)
  (or (member ce-var *rhs-bound-ce-vars* :test #'eq)
      (ce-var-dope ce-var)))

;;; Top level routines

(defun process-changes (adds dels)
  (prog (x)
   process-deletes (and (atom dels) (go process-adds))
        (setq x (car dels))
        (setq dels (cdr dels))
        (remove-from-wm x)
        (go process-deletes)
   process-adds (and (atom adds) (return nil))
        (setq x (car adds))
        (setq adds (cdr adds))
        (add-to-wm x nil)
        (go process-adds))) 

(defun main nil
  (prog (instance r)
        (setq *halt-flag* nil)
        (setq *break-flag* nil)
        (setq instance nil)
   dil  (setq *phase* 'conflict-resolution)
        (cond (*halt-flag*
               (setq r '|end -- explicit halt|)
               (go finis))
	      ((zerop *remaining-cycles*)
	       (setq r '***break***)
	       (setq *break-flag* t)
	       (go finis))
              (*break-flag* (setq r '***break***) (go finis)))
	(setq *remaining-cycles* (1- *remaining-cycles*))
        (setq instance (conflict-resolution))
        (cond ((not instance)
               (setq r '|end -- no production true|)
               (go finis)))
        (setq *phase* (car instance))
        (accum-stats)
        (eval-rhs (car instance) (cdr instance))
        (check-limits)
	(and (broken (car instance)) (setq *break-flag* t))
        (go dil)
  finis (setq *p-name* nil)
        (return r))) 

(defun do-continue (wmi)
    (cond (*critical*
           (terpri)
           (princ '|warning: network may be inconsistent|)))
    (process-changes wmi nil)
    (print-times (main))) 

(defun accum-stats nil
  (setq *cycle-count* (1+ *cycle-count*))
  (setq *total-token* (+ *total-token* *current-token*))
  (cond ((> *current-token* *max-token*)
         (setq *max-token* *current-token*)))
  (setq *total-wm* (+ *total-wm* *current-wm*))
  (cond ((> *current-wm* *max-wm*) (setq *max-wm* *current-wm*)))) 


(defun print-times (mess)
  (prog (cc ac)
    	(cond (*break-flag* (terpri) (return mess)))
        (setq cc (+ (float *cycle-count*) 1.0e-20))
        (setq ac (+ (float *action-count*) 1.0e-20))
        (terpri)
        (princ mess)
        (pm-size)
        (printlinec (list *cycle-count*
                          'firings
                          (list *action-count* 'rhs 'actions)))
        (terpri)
        (printlinec (list (round (/ (float *total-wm*) cc))
                          'mean 'working 'memory 'size
                          (list *max-wm* 'maximum)))
        (terpri)
        (printlinec (list (round (/ (float *total-cs*) cc))
                          'mean 'conflict 'set 'size
                          (list *max-cs* 'maximum)))
        (terpri)
        (printlinec (list (round (/ (float *total-token*) cc))
                          'mean 'token 'memory 'size
                          (list *max-token* 'maximum)))
        (terpri))) 

(defun pm-size nil
  (terpri)
  (printlinec (list *pcount*
                    'productions
                    (list *real-cnt* '// *virtual-cnt* 'nodes)))
  (terpri)) 

(defun check-limits nil
  (cond ((> (length *conflict-set*) *limit-cs*)
         (terpri)
         (terpri)
         (printlinec (list '|conflict set size exceeded the limit of|
                           *limit-cs*
                           '|after|
                           *p-name*))
         (setq *halt-flag* t)))
  (cond ((> *current-token* *limit-token*)
         (terpri)
         (terpri)
         (printlinec (list '|token memory size exceeded the limit of|
                           *limit-token*
                           '|after|
                           *p-name*))
         (setq *halt-flag* t)))) 


(defun top-level-remove (z)
  (cond ((equal z '(*)) (process-changes nil (get-wm nil)))
        (t (process-changes nil (get-wm z))))) 

(defmacro excise (&rest z) `(mapc (function excise-p) ',z))

(defmacro run (&rest z)
  `(cond ((null ',z) (setq *remaining-cycles* 1000000.) (do-continue nil))
        ((and (atom (cdr ',z)) (numberp (car ',z)) (> (car ',z) 0.))
         (setq *remaining-cycles* (car ',z))
         (do-continue nil))
        (t 'what\?))) 

(defmacro strategy (&rest z)
  `(cond ((atom ',z) *strategy*)
        ((equal ',z '(lex)) (setq *strategy* 'lex))
        ((equal ',z '(mea)) (setq *strategy* 'mea))
        (t 'what\?))) 

(defmacro cs (&optional z)
  `(cond ((null ',z) (conflict-set))
        (t 'what?))) 

(defmacro watch (&rest z)
  `(cond ((equal ',z '(0.))
         (setq *wtrace* nil)
         (setq *ptrace* nil)
         0.)
        ((equal ',z '(1.)) (setq *wtrace* nil) (setq *ptrace* t) 1.)
        ((equal ',z '(2.)) (setq *wtrace* t) (setq *ptrace* t) 2.)
        ((equal ',z '(3.))
         (setq *wtrace* t)
         (setq *ptrace* t)
         '(2. -- conflict set trace not supported))
        ((and (atom ',z) (null *ptrace*)) 0.)
        ((and (atom ',z) (null *wtrace*)) 1.)
        ((atom ',z) 2.)
        (t 'what\?))) 

(defmacro external  (&rest z) `(catch (external2 ',z) '!error!))

(defun external2 (z) (mapc (function external3) z))

(defun external3 (x) 
  (cond ((symbolp x) (putprop x t 'external-routine)
		     (setq *externals* (enter x *externals*)))
	(t (%error '|not a legal function name| x))))

(defun externalp (x)
  (cond ((symbolp x) (get x 'external-routine))
	(t (%warn '|not a legal function name| x) nil)))

(defmacro pbreak (&rest z)
  `(cond ((atom ',z) (terpri) *brkpts*)
	(t (mapc (function pbreak2) ',z) nil)))

(defun pbreak2 (rule)
  (cond ((not (symbolp rule)) (%warn '|illegal name| rule))
	((not (get rule 'topnode)) (%warn '|not a production| rule))
	((member rule *brkpts* :test #'eq) (setq *brkpts* (rematm rule *brkpts*)))
	(t (setq *brkpts* (cons rule *brkpts*)))))

(defun rematm (atm list)
  (cond ((atom list) list)
	((eq atm (car list)) (rematm atm (cdr list)))
	(t (cons (car list) (rematm atm (cdr list))))))

(defun broken (rule) (member rule *brkpts* :test #'eq))