git.fiddlerwoaroof.com
Raw Blame History
;;; ****************************************************************
;;; OPS5 Interpreter ***********************************************
;;; ****************************************************************
;;; This Common Lisp version of OPS5 is in the public domain.  It is based
;;; in part on based on a Franz Lisp implementation done by Charles L. Forgy
;;; at Carnegie-Mellon University, which was placed in the public domain by
;;; the author in accordance with CMU policies.  Ported to Common Lisp by 
;;; George Wood and Jim Kowalski. CMU Common Lisp modifications by
;;; Dario Guise, Skef Wholey, Michael Parzen, and Dan Kuokka. 
;;; Modified to work in CLtL1, CLtL2 and X3J13 compatible lisps by 
;;; Mark Kantrowitz on 14-OCT-92.
;;; 
;;; This code is made available is, and without warranty of any kind by the
;;; authors or by Carnegie-Mellon University.
;;;

;;;; This file contains all the functions pertaining to I/O.

(in-package "OPS")
;; (shadow '(write))    ; Should get this by requiring ops-rhs


;;; Internal global variables.

(defvar *write-file* nil)
(defvar *trace-file* nil)
(defvar *accept-file* nil)
(defvar *ppline* nil)
(defvar *filters* nil)



;;; Initialization

(defun io-init ()
  (setq *write-file* nil)
  (setq *trace-file* nil)
  (setq *accept-file* nil))



;;; User I/O commands
;;; Dario Giuse - rewrote the (write) function to follow OPS-5 specifications.
;;; Michael Huhns fixed a few bugs in this rewrttien functions some years later.


;;; used only in this file.
(defmacro append-string (x)
  `(setq wrstring (concatenate 'simple-string wrstring ,x)))


(defun ops-write (z)
  (if (not *in-rhs*)
      (%warn '|cannot be called at top level| 'write)
      (prog (port max k x)
         ($reset)
         (eval-args z)
         (setq max ($parametercount))
         (when (< max 1)
           (%warn '|write: nothing to print| z)
           (return nil))
         (setq x ($parameter 1))
         (cond ((and (symbolp x) ($ofile x)) 
                (setq port ($ofile x))
                (setq k 2))
               (t
                (setq port (default-write-file))
                (setq k 1)))
         ;; Analyze and output all the parameters (write) was passed.
         (do* ((wrstring "")
               (x ($parameter k) ($parameter k))
               (field-width))
              ((> k max)
               (format port wrstring)
               (force-output))		; Dario Giuse - added to force output
           (incf k)
           (case x
             (|=== C R L F ===|
              (format port "~A~%" wrstring) ; Flush the previous line
              (setq wrstring ""))
             (|=== R J U S T ===|
              (setq field-width ($parameter k)) ; Number following (tabto)
              (incf k)
              (setq x (format nil "~A" ($parameter k))) ; Next field to print
              (when (<= (length x) field-width)
                ;; Right-justify field
                (append-string (format nil "~V@A" field-width x))
                (incf k)))		; Skip next field, since we printed it already
             (|=== T A B T O ===|
              (setq x ($parameter k))	; Position to tab to
              (incf k)
              (when (< x (length wrstring))
                ;; Flush line, start a new one
                (format port "~A~%" wrstring)
                (setq wrstring ""))
              (append-string (format nil "~V,1@T" (- x (length wrstring) 1))))
             (t
              (append-string (format nil "~A " x))))))))


(defun ops-openfile (z)
  (prog (file mode id)
     ($reset)
     (eval-args z)
     (cond ((not (equal ($parametercount) 3.))
            (%warn '|openfile: wrong number of arguments| z)
            (return nil)))
     (setq id ($parameter 1))
     (setq file ($parameter 2))
     (setq mode ($parameter 3))
     (cond ((not (symbolp id))
            (%warn '|openfile: file id must be a symbolic atom| id)
            (return nil))
           ((null id)
            (%warn '|openfile: 'nil' is reserved for the terminal| nil)
            (return nil))
           ((or ($ifile id)($ofile id))
            (%warn '|openfile: name already in use| id)
            (return nil)))
     ;;@@@	(cond ((eq mode 'in) (setf (gethash id *inputfile-table*) (infile file)))
     ;;@@@	      ((eq mode 'out) (setf (gethash id *outputfile-table*) (outfile file)))
     ;; dec 7 83 gdw added setq : is putprop needed ? )
     (cond ((eq mode 'in) (setf (gethash id *inputfile-table*)
                                (setq id (infile file))))
           ((eq mode 'out) (setf (gethash id *outputfile-table*)
                                 (setq id (outfile file))))
           (t (%warn '|openfile: illegal mode| mode)
              (return nil)))
     (return nil)))


(defun infile (f_name)
  (open f_name :direction :input))

(defun outfile (f_name)
  (open f_name :direction :output :if-exists :new-version))

(defun ops-closefile (z)
  ($reset)
  (eval-args z)
  (mapc #'closefile2 (use-result-array)))

(defun closefile2 (file)
  (let (port)
    (cond ((not (symbolp file))
           (%warn '|closefile: illegal file identifier| file))
          ((setq port ($ifile file))
           (close port)
           (remhash file *inputfile-table*))
          ((setq port ($ofile file))
           (close port)
           (remhash file *outputfile-table*)))
    nil))

(defun ops-default (z)
  (prog (file use)
     ($reset)
     (eval-args z)
     (cond ((not (equal ($parametercount) 2.))
            (%warn '|default: wrong number of arguments| z)
            (return nil)))
     (setq file ($parameter 1))
     (setq use ($parameter 2))
     (cond ((not (symbolp file))
            (%warn '|default: illegal file identifier| file)
            (return nil))
           ((not (member use '(write accept trace) :test #'equal))
            (%warn '|default: illegal use for a file| use)
            (return nil))
           ((and (member use '(write trace) :test #'equal)
                 (not (null file))
                 (not ($ofile file)))
            (%warn '|default: file has not been opened for output| file)
            (return nil))
           ((and (equal use 'accept) 
                 (not (null file))
                 (not ($ifile file)))
            (%warn '|default: file has not been opened for input| file)
            (return nil))
           ((equal use 'write) (setq *write-file* file))
           ((equal use 'accept) (setq *accept-file* file))
           ((equal use 'trace) (setq *trace-file* file)))
     (return nil)))


(defun ops-accept (z)
  (prog (port arg)
     (cond ((> (length z) 1.)
            (%warn '|accept: wrong number of arguments| z)
            (return nil)))
     (setq port *standard-input*)
     (cond (*accept-file*
            (setq port ($ifile *accept-file*))
            (cond ((null port) 
                   (%warn '|accept: file has been closed| *accept-file*)
                   (return nil)))))
     (cond ((= (length z) 1)
            (setq arg ($varbind (car z)))
            (cond ((not (symbolp arg))
                   (%warn '|accept: illegal file name| arg)
                   (return nil)))
            (setq port ($ifile arg))
            (cond ((null port) 
                   (%warn '|accept: file not open for input| arg)
                   (return nil)))))
     (cond ((equal (peek-char t port nil "eof" ) "eof" )
            ($value 'end-of-file)
            (return nil)))
     (flat-value (read port)))) 



;;; Dario Giuse - completely changed the algorithm. It now uses one read-line
;;; and the read-from-string.
;;;
(defun ops-acceptline (z)
  (let ((port *standard-input*)
        (def z))
    (cond (*accept-file*
           (setq port ($ifile *accept-file*))
           (cond ((null port) 
                  (%warn '|acceptline: file has been closed| 
                         *accept-file*)
                  (return-from ops-acceptline nil)))))
    (cond ((> (length def) 0)
           (let ((arg ($varbind (car def))))
             (cond ((and (symbolp arg) ($ifile arg))
                    (setq port ($ifile arg))
                    (setq def (cdr def)))))))
    (let ((line (read-line port nil 'eof)))
      (declare (simple-string line))
      ;; Strip meaningless characters from start and end of string.
      (setq line (string-trim '(#\( #\) #\, #\tab #\space) line))
      (when (equal line "")
        (mapc (function $change) def)
        (return-from ops-acceptline nil))
      (setq line (concatenate 'simple-string "(" line ")"))
      ;; Read all items from the line
      (flat-value (read-from-string line)))))

(defun ops-rjust (z)
  (prog (val)
     (when (not (= (length z) 1.))
       (%warn '|rjust: wrong number of arguments| z)
       (return nil))
     (setq val ($varbind (car z)))
     (cond ((or (not (numberp val)) (< val 1.) (> val 127.))
            (%warn '|rjust: illegal value for field width| val)
            (return nil)))
     ($value '|=== R J U S T ===|)
     ($value val)))


(defun ops-crlf (z)
  (cond  (z (%warn '|crlf: does not take arguments| z))
         (t ($value '|=== C R L F ===|))))


(defun ops-tabto (z)
  (prog (val)
     (when (not (= (length z) 1.))
       (%warn '|tabto: wrong number of arguments| z)
       (return nil))
     (setq val ($varbind (car z)))
     (cond ((or (not (numberp val)) (< val 1.) (> val 127.))
            (%warn '|tabto: illegal column number| z)
            (return nil)))
     ($value '|=== T A B T O ===|)
     ($value val)))

(defun do-rjust (width value port)
  (prog (size)
     (cond ((eq value '|=== T A B T O ===|)
            (%warn '|rjust cannot precede this function| 'tabto)
            (return nil))
           ((eq value '|=== C R L F ===|)
            (%warn '|rjust cannot precede this function| 'crlf)
            (return nil))
           ((eq value '|=== R J U S T ===|)
            (%warn '|rjust cannot precede this function| 'rjust)
            (return nil)))
     ;;original->        (setq size (flatc value (1+ width)))
     (setq size (min value (1+ width)))  ;### KLUGE
     (cond ((> size width)
            (princ '| | port)
            (princ value port)
            (return nil)))
     ;;###        (do k (- width size) (1- k) (not (> k 0)) (princ '| | port))
     ;;^^^KLUGE @@@do
     (princ value port)))

(defun do-tabto (col port)
  (prog (pos)
     ;;### KLUGE: FLUSHES STREAM & SETS POS TO 0
     ;;OIRGINAL->	(setq pos (1+ (nwritn port)))	;hmm-takes 1 arg @@@ port
     (finish-output port);kluge
     (setq pos 0);kluge
     (cond ((> pos col)
            (terpri port)
            (setq pos 1)))
     ;;###(do k (- col pos) (1- k) (not (> k 0)) (princ '| | port))
     ;;^^^KLUGE @@@do
     (return nil)))


(defun flat-value (x)
  (cond ((atom x) ($value x))
        (t (mapc #'flat-value x)))) 



;;; Printing WM

(defun ops-ppwm (avlist)
  (prog (next a)
     (setq *filters* nil)
     (setq next 1.)
   loop   (and (atom avlist) (go print))
     (setq a (car avlist))
     (setq avlist (cdr avlist))
     ;;this must be expecting (ppwm class ^ attr ^ attr2 ...) not ^attr
     (cond ((eq a '^)
            (setq next (car avlist))
            (setq avlist (cdr avlist))
            (setq next ($litbind next))
            (and (floatp next) (setq next (floor next)))
            (cond ((or (not (numberp next))
                       (> next *size-result-array*)
                       (> 1. next))
                   (%warn '|illegal index after ^| next)
                   (return nil))))
           ((variablep a)
            (%warn '|ppwm does not take variables| a)
            (return nil))
           (t (setq *filters* (cons next (cons a *filters*)))
              (setq next (1+ next))))
     (go loop)
   print (mapwm #'ppwm2)
     (terpri)
     (return nil))) 


(defun default-write-file ()
  (let ((port *standard-output*))
    (when *write-file*
      (setq port ($ofile *write-file*))
      (when (null port) 
        (%warn '|write: file has been closed| *write-file*)
        (setq port *standard-output*)))
    port))

(defun trace-file ()
  (let ((port *standard-output*))
    (when *trace-file*
      (setq port ($ofile *trace-file*))
      (when (null port)
        (%warn '|trace: file has been closed| *trace-file*)
        (setq port *standard-output*)))
    port))

(defun ppwm2 (elm-tag)
  (cond ((filter (car elm-tag))
         (terpri) (ppelm (car elm-tag) (default-write-file))))) 

(defun filter (elm)
  (prog (fl indx val)
     (setq fl *filters*)
   top  (and (atom fl) (return t))
     (setq indx (car fl))
     (setq val (cadr fl))
     (setq fl (cddr fl))
     (and (ident (nth (1- indx) elm) val) (go top))
     (return nil))) 

(defun ident (x y)
  (cond ((eq x y) t)
        ((not (numberp x)) nil)
        ((not (numberp y)) nil)
        ((=alg x y) t)
        (t nil))) 

;; the new ppelm is designed especially to handle literalize format
;; however, it will do as well as the old ppelm on other formats

(defun ppelm (elm port)
  (prog (ppdat sep val att mode lastpos)
     (princ (creation-time elm) port)
     (princ '|:  | port)
     (setq mode 'vector)
     (setq ppdat (gethash (car elm) *ppdat-table*))
     (and ppdat (setq mode 'a-v))
     (setq sep "(")				; ")" 
     (setq lastpos 0)
     (do ((curpos 1 (1+ curpos)) (vlist elm (cdr vlist)))
         ((atom vlist) nil)					; terminate
       (setq val (car vlist))				; tagbody begin
       (setq att (assoc curpos ppdat))	;should ret (curpos attr-name) 
       (cond (att (setq att (cdr att)))	; att = (attr-name) ??
             (t (setq att curpos)))
       (and (symbolp att) (is-vector-attribute att) (setq mode 'vector))
       (cond ((or (not (null val)) (eq mode 'vector))
              (princ sep port)
              (ppval val att lastpos port)
              (setq sep '|    |)
              (setq lastpos curpos))))
     (princ '|)| port)))

(defun ppval (val att lastpos port)
  ;;  (break "in ppval")		
  (cond ((not (equal att (1+ lastpos)))		; ok, if we got an att 
         (princ '^ port)
         (princ att port)
         (princ '| | port)))
  (princ val port))



;;; Printing production memory

(defun ops-pm (z) (mapc #'pprule z) (terpri) nil)

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

  (defun ppline (line)
    (cond ((atom line) (princ line))
          (t
           (princ '|(|)			;)
           (setq *ppline* line)
           (ppline2)
           ;;(
           (princ '|)|)))
    nil)

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

  (defun ppattval ()
    (prog (att val)
       (setq att (cadr *ppline*))
       (setq *ppline* (cddr *ppline*))
       (setq val (getval))
       ;;###	(cond ((> (+ (nwritn) (flatc att) (flatc val)) 76.)))
       ;;@@@ nwritn no arg
       ;;						;"plus" changed to "+" by gdw
       ;;	       (terpri)
       ;;	       (princ '|        |)
       (princ '^)
       (princ att)
       (mapc (function (lambda (z) (princ '| |) (princ z))) val)))

  (defun pponlyval ()
    (prog (val needspace)
       (setq val (getval))
       (setq needspace nil)
       ;;###	(cond ((> (+ (nwritn) (flatc val)) 76.)))
       ;;"plus" changed to "+" by gdw
       ;;	       (setq needspace nil)		;^nwritn no arg @@@
       ;;	       (terpri)
       ;;	       (princ '|        |)
     top	(and (atom val) (return nil))
       (and needspace (princ '| |))
       (setq needspace t)
       (princ (car val))
       (setq val (cdr val))
       (go top)))

  (defun getval ()
    (let ((v1 (pop *ppline*))
          res)
      (cond ((member v1 '(= <> < <= => > <=>))
             (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))))
      res))

  (defun getupto (end)
    (if (atom *ppline*) nil
        (let ((v (pop *ppline*)))
          (if (eq v end) 
              (list v)
              (cons v (getupto end))))))

;;; *EOF*