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)
 	($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*