git.fiddlerwoaroof.com
src/ops-io.lisp
5092d8a5
 ;;; ****************************************************************
 ;;; 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.
 
6c4d2158
 (defvar *write-file* nil)
 (defvar *trace-file* nil)
 (defvar *accept-file* nil)
 (defvar *ppline* nil)
 (defvar *filters* nil)
5092d8a5
 
 
 
 ;;; 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)
983cb74c
          ($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))))))))
5092d8a5
 
 
 (defun ops-openfile (z)
   (prog (file mode id)
983cb74c
      ($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)))
5092d8a5
 
 
 (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))
983cb74c
            (%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*)))
5092d8a5
     nil))
 
 (defun ops-default (z)
   (prog (file use)
983cb74c
      ($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)))
5092d8a5
 
 
 (defun ops-accept (z)
   (prog (port arg)
983cb74c
      (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)))) 
5092d8a5
 
 
 
 ;;; 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*)
983cb74c
         (def z))
5092d8a5
     (cond (*accept-file*
983cb74c
            (setq port ($ifile *accept-file*))
            (cond ((null port) 
                   (%warn '|acceptline: file has been closed| 
                          *accept-file*)
                   (return-from ops-acceptline nil)))))
5092d8a5
     (cond ((> (length def) 0)
983cb74c
            (let ((arg ($varbind (car def))))
              (cond ((and (symbolp arg) ($ifile arg))
                     (setq port ($ifile arg))
                     (setq def (cdr def)))))))
5092d8a5
     (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 "")
983cb74c
         (mapc (function $change) def)
         (return-from ops-acceptline nil))
5092d8a5
       (setq line (concatenate 'simple-string "(" line ")"))
       ;; Read all items from the line
       (flat-value (read-from-string line)))))
 
 (defun ops-rjust (z)
   (prog (val)
983cb74c
      (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)))
5092d8a5
 
 
 (defun ops-crlf (z)
   (cond  (z (%warn '|crlf: does not take arguments| z))
983cb74c
          (t ($value '|=== C R L F ===|))))
5092d8a5
 
 
 (defun ops-tabto (z)
   (prog (val)
983cb74c
      (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)))
5092d8a5
 
 (defun do-rjust (width value port)
   (prog (size)
983cb74c
      (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)))
5092d8a5
 
 (defun do-tabto (col port)
   (prog (pos)
983cb74c
      ;;### 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)))
5092d8a5
 
 
 (defun flat-value (x)
   (cond ((atom x) ($value x))
983cb74c
         (t (mapc #'flat-value x)))) 
5092d8a5
 
 
 
 ;;; Printing WM
 
 (defun ops-ppwm (avlist)
   (prog (next a)
983cb74c
      (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))) 
5092d8a5
 
 
 (defun default-write-file ()
   (let ((port *standard-output*))
     (when *write-file*
       (setq port ($ofile *write-file*))
       (when (null port) 
983cb74c
         (%warn '|write: file has been closed| *write-file*)
         (setq port *standard-output*)))
5092d8a5
     port))
 
 (defun trace-file ()
   (let ((port *standard-output*))
     (when *trace-file*
       (setq port ($ofile *trace-file*))
       (when (null port)
983cb74c
         (%warn '|trace: file has been closed| *trace-file*)
         (setq port *standard-output*)))
5092d8a5
     port))
 
 (defun ppwm2 (elm-tag)
   (cond ((filter (car elm-tag))
983cb74c
          (terpri) (ppelm (car elm-tag) (default-write-file))))) 
5092d8a5
 
 (defun filter (elm)
   (prog (fl indx val)
983cb74c
      (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))) 
5092d8a5
 
 (defun ident (x y)
   (cond ((eq x y) t)
983cb74c
         ((not (numberp x)) nil)
         ((not (numberp y)) nil)
         ((=alg x y) t)
         (t nil))) 
5092d8a5
 
983cb74c
 ;; the new ppelm is designed especially to handle literalize format
 ;; however, it will do as well as the old ppelm on other formats
5092d8a5
 
 (defun ppelm (elm port)
   (prog (ppdat sep val att mode lastpos)
983cb74c
      (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)))
5092d8a5
 
 (defun ppval (val att lastpos port)
983cb74c
   ;;  (break "in ppval")		
5092d8a5
   (cond ((not (equal att (1+ lastpos)))		; ok, if we got an att 
983cb74c
          (princ '^ port)
          (princ att port)
          (princ '| | port)))
5092d8a5
   (princ val port))
 
 
 
 ;;; Printing production memory
 
 (defun ops-pm (z) (mapc #'pprule z) (terpri) nil)
 
 (defun pprule (name)
   (prog (matrix next lab)
983cb74c
      (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))))))
5092d8a5
 
 ;;; *EOF*