352 lines
13 KiB
Common Lisp
352 lines
13 KiB
Common Lisp
;; 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)
|
|
;; Note that (p1 v1) may be of the form p1 in which case the variable
|
|
;; is bound to nil.
|
|
|
|
(defun let macro(l)
|
|
`((lambda ,(mapcar '_lvar (cadr l))
|
|
,@(cddr l)
|
|
)
|
|
,@(mapcar '_lval (cadr l]
|
|
|
|
(defun _lvar (l)(cond ((atom l) l) (t (car l]
|
|
|
|
(defun _lval (l)(cond ((atom l) nil) (t (cadr l]
|
|
|
|
;; 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.
|
|
|
|
(defun defmacro fexpr(l)
|
|
(putd (car l)
|
|
(cons 'macro
|
|
(list '(defmacroarg)
|
|
`((lambda ,(__dmlats (cadr l))
|
|
,@(cddr l))
|
|
,@(__dmal (cadr l))]
|
|
|
|
(defun defcmacro fexpr(l) ; no such thing as compiler yet but
|
|
(putd (car l) ; keeps interpreter happy
|
|
(cons 'macro
|
|
(list '(defmacroarg)
|
|
`((lambda ,(__dmlats (cadr l))
|
|
,@(cddr l))
|
|
,@(__dmal (cadr l))]
|
|
|
|
(defun __dma(l a)
|
|
(cond ((null l) nil)
|
|
((atom l) (setq __dmalhold (cons a __dmalhold)))
|
|
(t (__dma (car l) (cons 'car (list a)))
|
|
(__dma (cdr l) (cons 'cdr (list a)))]
|
|
|
|
(defun __dmal(l &aux __dmalhold)
|
|
(__dma l '(cdr defmacroarg))
|
|
(reverse __dmalhold ]
|
|
|
|
(defun __dmlats(l)
|
|
(cond ((null l) nil)
|
|
((atom l) (list l))
|
|
( t (append (__dmlats (car l)) (__dmlats (cdr l)))]
|
|
|
|
;; (do "symbol" "exp1" "exp2" "test" -"exps"-) ; case 1
|
|
;; (do -"(symbol [exp1 [exp2]])"- "test" -"exps"-) ; case 2
|
|
;;
|
|
(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))))
|
|
|
|
;; This macro allow the following forms:
|
|
;; (if a then b) ==> (cond (a b))
|
|
;; (if a thenret) ==> (cond (a))
|
|
;; (if a then b else c) ==> (cond (a b) (t c))
|
|
;; (if a then b b2 ==> (cond (a b b2) (c d d2) (t e))
|
|
;; elseif c then d d2
|
|
;; else e)
|
|
;;
|
|
;; I stole this from the SLANG package and changed its name to 'if from
|
|
;; 'If.
|
|
;;
|
|
(defun if macro (lis)
|
|
(prog (majlis minlis revl)
|
|
(do ((revl (reverse lis) (cdr revl)))
|
|
((null revl))
|
|
(cond ((eq (car revl) 'else)
|
|
(setq majlis `((t ,@minlis) ,@majlis)
|
|
minlis nil))
|
|
((or (eq (car revl) 'then) (eq (car revl) 'thenret))
|
|
(setq revl (cdr revl)
|
|
majlis `((,(car revl) ,@minlis) ,@majlis)
|
|
minlis nil))
|
|
((eq (car revl) 'elseif))
|
|
((eq (car revl) 'if)
|
|
(setq majlis `(cond ,@majlis)))
|
|
(t (setq minlis `( ,(car revl) ,@minlis)))))
|
|
; we displace the previous macro, that is we actually replace
|
|
; the if list structure with the corresponding cond, meaning
|
|
; that the expansion is done only once
|
|
(rplaca lis (car majlis))
|
|
(rplacd lis (cdr majlis))
|
|
(return majlis)))
|
|
|
|
;; A couple of rareley used definitions but just to complete the chapter
|
|
;; on mapping functions, here they are:
|
|
;;
|
|
(defun mapcan macro(l) `(apply 'nconc (mapcar ,@(cdr l))))
|
|
(defun mapcon macro(l) `(apply 'nconc (maplist ,@(cdr l))))
|
|
|
|
;; The progS functions again to fill in some gaps
|
|
;;
|
|
(defun progn macro(l) `(prog nil ,@(cdr l)))
|
|
(defmacro prog1(a . b) `(prog (__p1ret) (setq __p1ret ,a) ,@b __p1ret))
|
|
(defmacro prog2(a b . c) `(prog (__p2ret) ,a (setq __p2ret ,b) ,@c __p2ret))
|
|
|