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