;; PC-LISP.L  for PC-LISP.EXE V2.15                                
;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                                                     
;;     A small library of functions to help fill in the gap between PC and      
;; Franz Lisp. These functions are not documented in the PC-LISP.DOC file but 
;; any Franz manual will cover them in detail. Especially the backquote 
;; and other macro definitions towards the end of the file. These functions
;; were written pretty hastily so there could be bugs. Check them out for
;; yourself to make sure they behave in the way you are used to with Franz.
;;
;;    This file is automatically loaded by PC-LISP.EXE. It should either    
;; be located in the current working directory, or in a library directory
;; whose path is set in the LISP_LIB environment variable. All load files
;; should be put in one of your LISP_LIB directories. You could also strip 
;; comments and white space from this file to make it load faster. This
;; is important if you load this file every time you run PC-LISP. 
;;      
;;              Peter Ashwood-Smith
;;                November 1986
;;
;; Pretty Print: (pp [(F file) (E expr) (P port)] symbol)
;; ~~~~~~~~~~~~
;;    Print in a readable way the function associated with 'symbol'. If
;; the parameter (F file) is specified the output goes to file 'file. If
;; the parameter (P port) is specified the output goes to the open port
;; 'port'. If the parameter (E expr) is specified the expression 'expr'
;; is evaluated before the function is pretty printed. Makes use of the
;; predefined symbol poport whose binding is 'stdout'.

(setq displace-macros t)        ; override Franz default (faster do loops)

(defun pp fexpr(l)
       (prog (expr name port alt)
	     (setq port poport)
	     (cond ((= (length l) 1) (setq name (car l)))
		   ((= (length l) 2) (setq name (cadr l) alt (car l)))
		   (t (return nil))
	     )
	     (cond ((null (getd name)) (return nil)))
	     (setq expr (cons 'def (cons name (list (getd name)))))
	     (cond ((null alt) (go SKIP)))   
	     (cond ((eq (car alt) 'F) (setq port (fileopen (cadr alt) 'w)))
		   ((eq (car alt) 'P) (setq port (cadr alt)))
		   ((eq (car alt) 'E) (eval (cadr alt)))
		   (t (return nil)))
	     (cond ((null port) (patom "cannot open port\n") (return nil)))
       SKIP  (pp-form expr port 0)
	     (cond ((not (equal port poport)) (close port)))
	     (return t)
       )
)

;; _SCL_ Spit Character Loop 
;; ~~~~~~~~~~~~~~~~~~~~~~~~~
;; Prints 'char' 'n' times on port 'port', used by msg for (N) and (B)
  
(defun _SCL_(port char n)
       (prog nil
	 nxt: (cond ((zerop n) (return)))
	      (patom char port)
	      (setq n (1- n))
	      (go nxt:)
       )
)     

;; (msg [B|N|D] [ (P pt) (B n) | (N n)] s*)
;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;; Prints a message on standard output port 'poport'. Where N is a new
;; line, (N n) means n new lines. B is a blank, (B n) means n blanks 
;; D means (Dran port) unsupported in PC-LISP. (P pt) means switch output
;; to port pt rather than poport. s, is any s expression which will be 
;; evaluated and then printed on current output port.

(defun msg fexpr(l)
       (prog (s op)
	     (setq op poport)   
	nxt: (cond ((null l)(return)))
	     (setq s (car l) l (cdr l))
	     (cond ((eq s 'N) (patom "\n" op))
		   ((eq s 'B) (patom " " op))
		   ((eq s 'D) (patom "msg : Drain unsupported\n"))
		   ((listp s)
		       (cond ((eq (car s) 'P)(setq op (cadr s)))
			     ((eq (car s) 'B)(_SCL_ op " " (cadr s)))
			     ((eq (car s) 'N)(_SCL_ op "\n" (cadr s)))
			     (t (patom (eval s) op)) 
		       )
		   )
		   (t (patom (eval s) op))
	     )
	     (go nxt:)
       )
) 

;; (lineread [port])
;; ~~~~~~~~~~~~~~~~~
;; Very simple line read function. Takes atoms from the piport or port until
;; a new line is encountered. It returns these atoms or S-expressions as a
;; list 'ret. 

(defun lineread fexpr(l)
       (prog (port ret)
	     (setq port piport)   
	     (cond ((not (null l)) (setq port (eval (car l)))))
	     (setq ret (list (read port)))
	nxt: (cond ((eq (readc port) '|\n|)(return ret)))
	     (setq ret (append ret (list (read port))))
	     (go nxt:)
       )
)

;; ----------- ASSORTED SMALL FUNCTIONS ------------

(defun tailp(l1 l2)(cond ((null l2) nil)((eq l1 l2) l1)(t(tailp l1(cdr l2]  
(defun bcdp(x) nil)             
(defun bigp(x) nil)             
(defun dtpr(x) (and (listp x) (not (null x))))  
(defun consp(x) (and (listp x) (not (null x))))
(defun litatom(n) (and(atom n)(not(floatp n]   
(defun purep(n) nil)
(defun symbolp(n) (litatom n))                  
(defun valuep(n) nil)
(defun vectorp(n) nil)
(defun typep(n)(type n))
(defun eqstr(a b)(equal a b))
(defun neq(a b)(not(eq a b)))
(defun nequal(a b)(not(equal a b)))
(defun append1(a b)(append a (list b)))
(defun ncons(a)(cons a nil))
(defun xcons(a b)(cons b a))
(defun nthelem(n l) (nth (1- n) l))
(defun minus(n)(- 0 n))
(defun onep(n)(= 1 n))
(defun infile(f)(fileopen f 'r)) 
(defun pntlen(a) (flatsize a))
(defun probef(f &aux tmp)(setq tmp (fileopen f 'r))(and tmp (close tmp)))
(defun shell()(exec "COMMAND.COM"))     ; must have a COMMAND.COM on PATH!

(defun error n
       (cond ((= n 1) (patom (arg 1)) (terpri) (err nil))
	     ((= n 2) (patom (arg 1)) (patom (arg 2)) (terpri) (err nil))
	     (t (error "error bad args"))]

(defun signp(test exp)
       (cond ((eq test 'ge) (or (zerop exp)(plusp exp)))
	     ((eq test 'g ) (plusp exp))
	     ((eq test 'n ) (not (zerop exp)))
	     ((eq test 'e ) (zerop exp))
	     ((eq test 'le) (or (zerop exp)(minusp exp)))
	     ((eq test 'l)  (minusp exp))
	     (t (princ "-- error signp bad test ---\n"))))

;; ----------- ASSORTED SMALL MACROS --------------

(defun >&  macro(l) (cons '> (cdr l)))                
(defun >=  macro(l) (cons 'not (list (cons '< (cdr l))))) 
(defun >=& macro(l) (cons 'not (list (cons '< (cdr l)))))
(defun <&  macro(l) (cons '< (cdr l)))
(defun <=  macro(l) (cons 'not (list (cons '> (cdr l)))))
(defun <=& macro(l) (cons 'not (list (cons '> (cdr l)))))
(defun =&  macro(l) (cons '= (cdr l)))

(defun terpri macro(l)                      ; makes (terpri [port])
      (append (list 'princ "\n")(cdr l)))   ; into  (princ "\n" [port])
(defun tyo macro(l)                         ; makes (princ (asci f) [port])
      (cons 'princ (cons (cons 'ascii (list (cadr l))) (cddr l))))
(defun store macro(l)                       ; makes (store (x -dims-) exp)
      (cons (caadr l)                       ; into  (x exp -dims-)
	    (append (cddr l) (cdadr l))))
(defun arraycall macro(l)                   ; makes (arraycall f a -n-)
      (cddr l))                             ; into  (a -n-)

;; BACKQUOTE READ MACRO AND PARTS  
;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;;      This  file  describes  the back quote macro for PC-LISP. It works in
;; exactly the same way as the  FRANZ  backquote  macro works. Basically the
;; backquote macro ` is supposed to  work  together  with the comma , and at
;; @ macros. As follows: Backquote  has the same effect as ' except that any
;; elements or  sub  elements  that  are preceeded by , are evaluated. If an
;; element  is  preceeded  by  ,@  then  the element is evaluated and should 
;; evaluate to  a  list.  This  list  is  spliced into the built list. I use 
;; cons  to  do  list  building  and append to do list splicing. For example
;; the  input:  `(a ,b c)  will  be  read in  as  (a (*unquote* b) c) by the
;; back quote read macro because the  comma  macro  will have read the b and
;; built up the list (*unquote* b). Next the back quote macro passes control
;; to the _BQB_ function (Back Quote  Builder). This will construct the list
;; (cons 'a (cons b (cons 'c nil)))  which  when evaluated gives the desired    
;; result. If the , were  followed  by  an @ then the @ would build the form
;; (*splice* b). Then the  , would get this form and the function _CB_ comma
;; builder would then make  then pass the form unchanged. Next the backquote
;; builder  _BQB_  would  get the form (a (*splice* b) c) and build the form
;; (cons 'a (append b (cons 'c nil)))  which will cause the value of b to be
;; spliced into the list rather than forming a sublist element as desired.

(defun _BQB_(Sexp) 
       (cond ((null Sexp) Sexp)
	     ((atom Sexp) (list 'quote Sexp))
	     ((eq (car Sexp) '*unquote*)
		  (cadr Sexp))
	     ((and(listp (car Sexp)) (eq (caar Sexp) '*splice*))
		  (list 'append (cadar Sexp)
			     (_BQB_ (cdr Sexp))))
	     ( t (list 'cons (_BQB_ (car Sexp))
			     (_BQB_ (cdr Sexp))))
       )
)

(defun _CB_(Sexp)
       (cond ((null Sexp) Sexp)
	     ((atom Sexp) (list '*unquote* Sexp))
	     ((eq (car Sexp) '*splice*) Sexp)
	     (t (list '*unquote* Sexp))
       )
)

(setsyntax '|`| 'vmacro '(lambda()(_BQB_ (read))))
(setsyntax '|,| 'vmacro '(lambda()(_CB_  (read))))
(setsyntax '|@| 'vmacro '(lambda()(list '*splice* (read))))


;; macro  : (let ((p1 v1)(p2 v2)...(pn vn)) e1 e2 ... en)
;; ~~~~~  
;;      Let macro introduces local variables. Much used in Franz code it
;; basically creates a lambda expression of the form:
;;
;;          ((lambda(p1 p2 ... pn) e1 e2 ... en) v1 v2 ...vn)
;;

(defun let macro(x)
       (cons (append (cons 'lambda                       ; ((lambda ..rest..
			(list (mapcar 'car (cadr x))))   ; ((p1 p2...pn))
		     (cddr x))                           ; (e1 e1...en)
	     (mapcar 'cadr (cadr x))                     ; (v1 v2...vn)
       )
)

;; macro defmacro
;; ~~~~~~~~~~~~~~
;;    Like defun except that it declares a macro. This is more convenient
;; than using the defun name macro(l) because access to variables can be
;; named. It produces almost the same intermediate form as Franz except that
;; it uses the (nth N xxx) form rather than the (cadddr xxx) form for access
;; to the macros parameters.     
;;

(defun defmacro fexpr(plist) 
  (putd (car plist)
	(cons 'macro
	   (list '(defmacroarg)
		  (cons (cons 'lambda (cdr plist))
			(prog (n res)
			      (setq n (length (cadr plist))) 
			 DML: (cond ((zerop n) (return res)))
			      (setq res `((nth ,n defmacroarg) ,@res) n (1- n))
			      (go DML:)))))))


; ### (do "symbol" "exp1" "exp2" "test" -"exps"-)        ; case 1
; ### (do -"(symbol [exp1 [exp2]])"- "test" -"exps"-)    ; case 2
; ###
; ###     Is a macro that generates the appropriate prog form. Where the 
; ### first do loop case is just a prog with a single local "symbol" that
; ### is setto "exp1" the first time  through the loop and set to "exp2"
; ### at the bottom of each loop. "test" is checked at the top of the loop
; ### and if non-nil we return nil.  The second case is more complex, the
; ### symbols are set to nil or exp1 before the loop starts and to exp2 each
; ### time through the loop. "test" is of the form (condition action1..actionN)
; ### and condition is checked at the top of the loop, if it is non nil then
; ### actions 1..N are evaluated and we return the result of eval(actionN).

(defun _do2a_(l) (cond ((cdr l)(cons (car l) (list(cadr l))))(t nil]  
(defun _do2b_(l) (cond ((cddr l)(cons (car l) (list(caddr l))))(t nil]  

(defun _do2_(l)                            ; complex do loop case, many locals
     `(prog ,(mapcar 'car (cadr l))
	    (PAR-setq ,@(apply 'append (mapcar '_do2a_ (cadr l))))
     _dlab_ 
	    (cond (,(caaddr l) (return ,@(cdaddr l)) )) 
	    ,@(cdddr l)
	    (PAR-setq ,@(apply 'append (mapcar '_do2b_ (cadr l))))
	    (go _dlab_)
      )
)

(defun _do1_(l)                            ; simple do loop case, one local
     `(prog (,(nth 1 l))
	    (setq ,(nth 1 l) ,(nth 2 l))
     _dlab_ (cond (,(nth 4 l) (return))) 
	    ,@(cdddddr l)
	    (setq ,(nth 1 l) ,(nth 3 l))
	    (go _dlab_)
      )
)

(defun do macro(l)                        ; select simple/complex case.
       (cond ((atom (cadr l)) (_do1_ l))
	     (t (_do2_ l))))

(defun mapcan macro(l) `(apply 'nconc (mapcar ,@(cdr l))))
(defun mapcon macro(l) `(apply 'nconc (maplist ,@(cdr l))))
