mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-11-25 07:28:20 +00:00
3163 lines
121 KiB
EmacsLisp
3163 lines
121 KiB
EmacsLisp
;;; cl.el --- Common-Lisp extensions for GNU Emacs Lisp.
|
||
|
||
;; Copyright (C) 1987, 1988, 1989, 1992 Free Software Foundation, Inc.
|
||
|
||
;; Author: Cesar Quiroz <quiroz@cs.rochester.edu>
|
||
;; Keywords: extensions
|
||
|
||
(defvar cl-version "3.0 07-February-1993")
|
||
|
||
;; This file is part of GNU Emacs.
|
||
|
||
;; GNU Emacs is distributed in the hope that it will be useful,
|
||
;; but WITHOUT ANY WARRANTY. No author or distributor
|
||
;; accepts responsibility to anyone for the consequences of using it
|
||
;; or for whether it serves any particular purpose or works at all,
|
||
;; unless he says so in writing. Refer to the GNU Emacs General Public
|
||
;; License for full details.
|
||
|
||
;; Everyone is granted permission to copy, modify and redistribute
|
||
;; GNU Emacs, but only under the conditions described in the
|
||
;; GNU Emacs General Public License. A copy of this license is
|
||
;; supposed to have been given to you along with GNU Emacs so you
|
||
;; can know your rights and responsibilities. It should be in a
|
||
;; file named COPYING. Among other things, the copyright notice
|
||
;; and this notice must be preserved on all copies.
|
||
|
||
;;; Commentary:
|
||
|
||
;;; Notes from Rob Austein on his mods
|
||
;; yaya:/usr/u/sra/cl/cl.el, 5-May-1991 16:01:34, sra
|
||
;;
|
||
;; Slightly hacked copy of cl.el 2.0 beta 27.
|
||
;;
|
||
;; Various minor performance improvements:
|
||
;; a) Don't use MAPCAR when we're going to discard its results.
|
||
;; b) Make various macros a little more clever about optimizing
|
||
;; generated code in common cases.
|
||
;; c) Fix DEFSETF to expand to the right code at compile-time.
|
||
;; d) Make various macros cleverer about generating reasonable
|
||
;; code when compiled, particularly forms like DEFSTRUCT which
|
||
;; are usually used at top-level and thus are only compiled if
|
||
;; you use Hallvard Furuseth's hacked bytecomp.el.
|
||
;;
|
||
;; New features: GETF, REMF, and REMPROP.
|
||
;;
|
||
;; Notes:
|
||
;; 1) I'm sceptical about the FBOUNDP checks in SETF. Why should
|
||
;; the SETF expansion fail because the SETF method isn't defined
|
||
;; at compile time? Lisp is going to check for a binding at run-time
|
||
;; anyway, so maybe we should just assume the user's right here.
|
||
|
||
;;;; These are extensions to Emacs Lisp that provide some form of
|
||
;;;; Common Lisp compatibility, beyond what is already built-in
|
||
;;;; in Emacs Lisp.
|
||
;;;;
|
||
;;;; When developing them, I had the code spread among several files.
|
||
;;;; This file 'cl.el' is a concatenation of those original files,
|
||
;;;; minus some declarations that became redundant. The marks between
|
||
;;;; the original files can be found easily, as they are lines that
|
||
;;;; begin with four semicolons (as this does). The names of the
|
||
;;;; original parts follow the four semicolons in uppercase, those
|
||
;;;; names are GLOBAL, SYMBOLS, LISTS, SEQUENCES, CONDITIONALS,
|
||
;;;; ITERATIONS, MULTIPLE VALUES, ARITH, SETF and DEFSTRUCT. If you
|
||
;;;; add functions to this file, you might want to put them in a place
|
||
;;;; that is compatible with the division above (or invent your own
|
||
;;;; categories).
|
||
;;;;
|
||
;;;; To compile this file, make sure you load it first. This is
|
||
;;;; because many things are implemented as macros and now that all
|
||
;;;; the files are concatenated together one cannot ensure that
|
||
;;;; declaration always precedes use.
|
||
;;;;
|
||
;;;; Bug reports, suggestions and comments,
|
||
;;;; to quiroz@cs.rochester.edu
|
||
|
||
|
||
;;;; GLOBAL
|
||
;;;; This file provides utilities and declarations that are global
|
||
;;;; to Common Lisp and so might be used by more than one of the
|
||
;;;; other libraries. Especially, I intend to keep here some
|
||
;;;; utilities that help parsing/destructuring some difficult calls.
|
||
;;;;
|
||
;;;;
|
||
;;;; Cesar Quiroz @ UofR DofCSc - Dec. 1986
|
||
;;;; (quiroz@cs.rochester.edu)
|
||
|
||
;;; Too many pieces of the rest of this package use psetq. So it is unwise to
|
||
;;; use here anything but plain Emacs Lisp! There is a neater recursive form
|
||
;;; for the algorithm that deals with the bodies.
|
||
|
||
;;; Code:
|
||
|
||
;;; This version is due to Hallvard Furuseth (hallvard@ifi.uio.no, 6 Jul 91)
|
||
(defmacro psetq (&rest args)
|
||
"(psetq {VARIABLE VALUE}...): In parallel, set each VARIABLE to its VALUE.
|
||
All the VALUEs are evaluated, and then all the VARIABLEs are set.
|
||
Aside from order of evaluation, this is the same as `setq'."
|
||
;; check there is a reasonable number of forms
|
||
(if (/= (% (length args) 2) 0)
|
||
(error "Odd number of arguments to `psetq'"))
|
||
(setq args (copy-sequence args)) ;for safety below
|
||
(prog1 (cons 'setq args)
|
||
(while (progn (if (not (symbolp (car args)))
|
||
(error "`psetq' expected a symbol, found '%s'."
|
||
(prin1-to-string (car args))))
|
||
(cdr (cdr args)))
|
||
(setcdr args (list (list 'prog1 (nth 1 args)
|
||
(cons 'setq
|
||
(setq args (cdr (cdr args))))))))))
|
||
|
||
;;; utilities
|
||
;;;
|
||
;;; pair-with-newsyms takes a list and returns a list of lists of the
|
||
;;; form (newsym form), such that a let* can then bind the evaluation
|
||
;;; of the forms to the newsyms. The idea is to guarantee correct
|
||
;;; order of evaluation of the subforms of a setf. It also returns a
|
||
;;; list of the newsyms generated, in the corresponding order.
|
||
|
||
(defun pair-with-newsyms (oldforms)
|
||
"PAIR-WITH-NEWSYMS OLDFORMS
|
||
The top-level components of the list oldforms are paired with fresh
|
||
symbols, the pairings list and the newsyms list are returned."
|
||
(do ((ptr oldforms (cdr ptr))
|
||
(bindings '())
|
||
(newsyms '()))
|
||
((endp ptr) (values (nreverse bindings) (nreverse newsyms)))
|
||
(let ((newsym (gentemp)))
|
||
(setq bindings (cons (list newsym (car ptr)) bindings))
|
||
(setq newsyms (cons newsym newsyms)))))
|
||
|
||
(defun zip-lists (evens odds)
|
||
"Merge two lists EVENS and ODDS, taking elts from each list alternatingly.
|
||
EVENS and ODDS are two lists. ZIP-LISTS constructs a new list, whose
|
||
even numbered elements (0,2,...) come from EVENS and whose odd
|
||
numbered elements (1,3,...) come from ODDS.
|
||
The construction stops when the shorter list is exhausted."
|
||
(do* ((p0 evens (cdr p0))
|
||
(p1 odds (cdr p1))
|
||
(even (car p0) (car p0))
|
||
(odd (car p1) (car p1))
|
||
(result '()))
|
||
((or (endp p0) (endp p1))
|
||
(nreverse result))
|
||
(setq result
|
||
(cons odd (cons even result)))))
|
||
|
||
(defun unzip-list (list)
|
||
"Extract even and odd elements of LIST into two separate lists.
|
||
The argument LIST is separated in two strands, the even and the odd
|
||
numbered elements. Numbering starts with 0, so the first element
|
||
belongs in EVENS. No check is made that there is an even number of
|
||
elements to start with."
|
||
(do* ((ptr list (cddr ptr))
|
||
(this (car ptr) (car ptr))
|
||
(next (cadr ptr) (cadr ptr))
|
||
(evens '())
|
||
(odds '()))
|
||
((endp ptr)
|
||
(values (nreverse evens) (nreverse odds)))
|
||
(setq evens (cons this evens))
|
||
(setq odds (cons next odds))))
|
||
|
||
(defun reassemble-argslists (argslists)
|
||
"(reassemble-argslists ARGSLISTS) => a list of lists
|
||
ARGSLISTS is a list of sequences. Return a list of lists, the first
|
||
sublist being all the entries coming from ELT 0 of the original
|
||
sublists, the next those coming from ELT 1 and so on, until the
|
||
shortest list is exhausted."
|
||
(let* ((minlen (apply 'min (mapcar 'length argslists)))
|
||
(result '()))
|
||
(dotimes (i minlen (nreverse result))
|
||
;; capture all the elements at index i
|
||
(setq result
|
||
(cons (mapcar (function (lambda (sublist) (elt sublist i)))
|
||
argslists)
|
||
result)))))
|
||
|
||
|
||
;;; Checking that a list of symbols contains no duplicates is a common
|
||
;;; task when checking the legality of some macros. The check for 'eq
|
||
;;; pairs can be too expensive, as it is quadratic on the length of
|
||
;;; the list. I use a 4-pass, linear, counting approach. It surely
|
||
;;; loses on small lists (less than 5 elements?), but should win for
|
||
;;; larger lists. The fourth pass could be eliminated.
|
||
;;; 10 dec 1986. Emacs Lisp has no REMPROP, so I just eliminated the
|
||
;;; 4th pass.
|
||
;;;
|
||
;;; [22 April 1991, sra] REMPROP now in library, so restored 4th pass.
|
||
(defun duplicate-symbols-p (list)
|
||
"Find all symbols appearing more than once in LIST.
|
||
Return a list of all such duplicates; `nil' if there are no duplicates."
|
||
(let ((duplicates '()) ;result built here
|
||
(propname (gensym)) ;we use a fresh property
|
||
)
|
||
;; check validity
|
||
(unless (and (listp list)
|
||
(every 'symbolp list))
|
||
(error "a list of symbols is needed"))
|
||
;; pass 1: mark
|
||
(dolist (x list)
|
||
(put x propname 0))
|
||
;; pass 2: count
|
||
(dolist (x list)
|
||
(put x propname (1+ (get x propname))))
|
||
;; pass 3: collect
|
||
(dolist (x list)
|
||
(if (> (get x propname) 1)
|
||
(setq duplicates (cons x duplicates))))
|
||
;; pass 4: unmark.
|
||
(dolist (x list)
|
||
(remprop x propname))
|
||
;; return result
|
||
duplicates))
|
||
|
||
;;;; end of cl-global.el
|
||
|
||
;;;; SYMBOLS
|
||
;;;; This file provides the gentemp function, which generates fresh
|
||
;;;; symbols, plus some other minor Common Lisp symbol tools.
|
||
;;;;
|
||
;;;; Cesar Quiroz @ UofR DofCSc - Dec. 1986
|
||
;;;; (quiroz@cs.rochester.edu)
|
||
|
||
;;; Keywords. There are no packages in Emacs Lisp, so this is only a
|
||
;;; kludge around to let things be "as if" a keyword package was around.
|
||
|
||
(defmacro defkeyword (x &optional docstring)
|
||
"Make symbol X a keyword (symbol whose value is itself).
|
||
Optional second argument is a documentation string for it."
|
||
(cond ((symbolp x)
|
||
(list 'defconst x (list 'quote x) docstring))
|
||
(t
|
||
(error "`%s' is not a symbol" (prin1-to-string x)))))
|
||
|
||
(defun keywordp (sym)
|
||
"t if SYM is a keyword."
|
||
(if (and (symbolp sym) (char-equal (aref (symbol-name sym) 0) ?\:))
|
||
;; looks like one, make sure value is right
|
||
(set sym sym)
|
||
nil))
|
||
|
||
(defun keyword-of (sym)
|
||
"Return a keyword that is naturally associated with symbol SYM.
|
||
If SYM is keyword, the value is SYM.
|
||
Otherwise it is a keyword whose name is `:' followed by SYM's name."
|
||
(cond ((keywordp sym)
|
||
sym)
|
||
((symbolp sym)
|
||
(let ((newsym (intern (concat ":" (symbol-name sym)))))
|
||
(set newsym newsym)))
|
||
(t
|
||
(error "expected a symbol, not `%s'" (prin1-to-string sym)))))
|
||
|
||
;;; Temporary symbols.
|
||
;;;
|
||
|
||
(defvar *gentemp-index* 0
|
||
"Integer used by gentemp to produce new names.")
|
||
|
||
(defvar *gentemp-prefix* "T$$_"
|
||
"Names generated by gentemp begin with this string by default.")
|
||
|
||
(defun gentemp (&optional prefix oblist)
|
||
"Generate a fresh interned symbol.
|
||
There are 2 optional arguments, PREFIX and OBLIST. PREFIX is the
|
||
string that begins the new name, OBLIST is the obarray used to search for
|
||
old names. The defaults are just right, YOU SHOULD NEVER NEED THESE
|
||
ARGUMENTS IN YOUR OWN CODE."
|
||
(if (null prefix)
|
||
(setq prefix *gentemp-prefix*))
|
||
(if (null oblist)
|
||
(setq oblist obarray)) ;default for the intern functions
|
||
(let ((newsymbol nil)
|
||
(newname))
|
||
(while (not newsymbol)
|
||
(setq newname (concat prefix *gentemp-index*))
|
||
(setq *gentemp-index* (+ *gentemp-index* 1))
|
||
(if (not (intern-soft newname oblist))
|
||
(setq newsymbol (intern newname oblist))))
|
||
newsymbol))
|
||
|
||
(defvar *gensym-index* 0
|
||
"Integer used by gensym to produce new names.")
|
||
|
||
(defvar *gensym-prefix* "G$$_"
|
||
"Names generated by gensym begin with this string by default.")
|
||
|
||
(defun gensym (&optional prefix)
|
||
"Generate a fresh uninterned symbol.
|
||
There is an optional argument, PREFIX. PREFIX is the
|
||
string that begins the new name. Most people take just the default,
|
||
except when debugging needs suggest otherwise."
|
||
(if (null prefix)
|
||
(setq prefix *gensym-prefix*))
|
||
(let ((newsymbol nil)
|
||
(newname ""))
|
||
(while (not newsymbol)
|
||
(setq newname (concat prefix *gensym-index*))
|
||
(setq *gensym-index* (+ *gensym-index* 1))
|
||
(if (not (intern-soft newname))
|
||
(setq newsymbol (make-symbol newname))))
|
||
newsymbol))
|
||
|
||
;;;; end of cl-symbols.el
|
||
|
||
;;;; CONDITIONALS
|
||
;;;; This file provides some of the conditional constructs of
|
||
;;;; Common Lisp. Total compatibility is again impossible, as the
|
||
;;;; 'if' form is different in both languages, so only a good
|
||
;;;; approximation is desired.
|
||
;;;;
|
||
;;;; Cesar Quiroz @ UofR DofCSc - Dec. 1986
|
||
;;;; (quiroz@cs.rochester.edu)
|
||
|
||
;;; indentation info
|
||
(put 'case 'lisp-indent-hook 1)
|
||
(put 'ecase 'lisp-indent-hook 1)
|
||
(put 'when 'lisp-indent-hook 1)
|
||
(put 'unless 'lisp-indent-hook 1)
|
||
|
||
;;; WHEN and UNLESS
|
||
;;; These two forms are simplified ifs, with a single branch.
|
||
|
||
(defmacro when (condition &rest body)
|
||
"(when CONDITION . BODY) => evaluate BODY if CONDITION is true."
|
||
(list* 'if (list 'not condition) '() body))
|
||
|
||
(defmacro unless (condition &rest body)
|
||
"(unless CONDITION . BODY) => evaluate BODY if CONDITION is false."
|
||
(list* 'if condition '() body))
|
||
|
||
;;; CASE and ECASE
|
||
;;; CASE selects among several clauses, based on the value (evaluated)
|
||
;;; of a expression and a list of (unevaluated) key values. ECASE is
|
||
;;; the same, but signals an error if no clause is activated.
|
||
|
||
(defmacro case (expr &rest cases)
|
||
"(case EXPR . CASES) => evals EXPR, chooses from CASES on that value.
|
||
EXPR -> any form
|
||
CASES -> list of clauses, non empty
|
||
CLAUSE -> HEAD . BODY
|
||
HEAD -> t = catch all, must be last clause
|
||
-> otherwise = same as t
|
||
-> nil = illegal
|
||
-> atom = activated if (eql EXPR HEAD)
|
||
-> list of atoms = activated if (memq EXPR HEAD)
|
||
BODY -> list of forms, implicit PROGN is built around it.
|
||
EXPR is evaluated only once."
|
||
(let* ((newsym (gentemp))
|
||
(clauses (case-clausify cases newsym)))
|
||
;; convert case into a cond inside a let
|
||
(list 'let
|
||
(list (list newsym expr))
|
||
(list* 'cond (nreverse clauses)))))
|
||
|
||
(defmacro ecase (expr &rest cases)
|
||
"(ecase EXPR . CASES) => like `case', but error if no case fits.
|
||
`t'-clauses are not allowed."
|
||
(let* ((newsym (gentemp))
|
||
(clauses (case-clausify cases newsym)))
|
||
;; check that no 't clause is present.
|
||
;; case-clausify would put one such at the beginning of clauses
|
||
(if (eq (caar clauses) t)
|
||
(error "no clause-head should be `t' or `otherwise' for `ecase'"))
|
||
;; insert error-catching clause
|
||
(setq clauses
|
||
(cons
|
||
(list 't (list 'error
|
||
"ecase on %s = %s failed to take any branch"
|
||
(list 'quote expr)
|
||
(list 'prin1-to-string newsym)))
|
||
clauses))
|
||
;; generate code as usual
|
||
(list 'let
|
||
(list (list newsym expr))
|
||
(list* 'cond (nreverse clauses)))))
|
||
|
||
|
||
(defun case-clausify (cases newsym)
|
||
"CASE-CLAUSIFY CASES NEWSYM => clauses for a 'cond'
|
||
Converts the CASES of a [e]case macro into cond clauses to be
|
||
evaluated inside a let that binds NEWSYM. Returns the clauses in
|
||
reverse order."
|
||
(do* ((currentpos cases (cdr currentpos))
|
||
(nextpos (cdr cases) (cdr nextpos))
|
||
(curclause (car cases) (car currentpos))
|
||
(result '()))
|
||
((endp currentpos) result)
|
||
(let ((head (car curclause))
|
||
(body (cdr curclause)))
|
||
;; construct a cond-clause according to the head
|
||
(cond ((null head)
|
||
(error "case clauses cannot have null heads: `%s'"
|
||
(prin1-to-string curclause)))
|
||
((or (eq head 't)
|
||
(eq head 'otherwise))
|
||
;; check it is the last clause
|
||
(if (not (endp nextpos))
|
||
(error "clause with `t' or `otherwise' head must be last"))
|
||
;; accept this clause as a 't' for cond
|
||
(setq result (cons (cons 't body) result)))
|
||
((atom head)
|
||
(setq result
|
||
(cons (cons (list 'eql newsym (list 'quote head)) body)
|
||
result)))
|
||
((listp head)
|
||
(setq result
|
||
(cons (cons (list 'memq newsym (list 'quote head)) body)
|
||
result)))
|
||
(t
|
||
;; catch-all for this parser
|
||
(error "don't know how to parse case clause `%s'"
|
||
(prin1-to-string head)))))))
|
||
|
||
;;;; end of cl-conditionals.el
|
||
|
||
;;;; ITERATIONS
|
||
;;;; This file provides simple iterative macros (a la Common Lisp)
|
||
;;;; constructed on the basis of let, let* and while, which are the
|
||
;;;; primitive binding/iteration constructs of Emacs Lisp
|
||
;;;;
|
||
;;;; The Common Lisp iterations use to have a block named nil
|
||
;;;; wrapped around them, and allow declarations at the beginning
|
||
;;;; of their bodies and you can return a value using (return ...).
|
||
;;;; Nothing of the sort exists in Emacs Lisp, so I haven't tried
|
||
;;;; to imitate these behaviors.
|
||
;;;;
|
||
;;;; Other than the above, the semantics of Common Lisp are
|
||
;;;; correctly reproduced to the extent this was reasonable.
|
||
;;;;
|
||
;;;; Cesar Quiroz @ UofR DofCSc - Dec. 1986
|
||
;;;; (quiroz@cs.rochester.edu)
|
||
|
||
;;; some lisp-indentation information
|
||
(put 'do 'lisp-indent-hook 2)
|
||
(put 'do* 'lisp-indent-hook 2)
|
||
(put 'dolist 'lisp-indent-hook 1)
|
||
(put 'dotimes 'lisp-indent-hook 1)
|
||
(put 'do-symbols 'lisp-indent-hook 1)
|
||
(put 'do-all-symbols 'lisp-indent-hook 1)
|
||
|
||
|
||
(defmacro do (stepforms endforms &rest body)
|
||
"(do STEPFORMS ENDFORMS . BODY): Iterate BODY, stepping some local variables.
|
||
STEPFORMS must be a list of symbols or lists. In the second case, the
|
||
lists must start with a symbol and contain up to two more forms. In
|
||
the STEPFORMS, a symbol is the same as a (symbol). The other 2 forms
|
||
are the initial value (def. NIL) and the form to step (def. itself).
|
||
The values used by initialization and stepping are computed in parallel.
|
||
The ENDFORMS are a list (CONDITION . ENDBODY). If the CONDITION
|
||
evaluates to true in any iteration, ENDBODY is evaluated and the last
|
||
form in it is returned.
|
||
The BODY (which may be empty) is evaluated at every iteration, with
|
||
the symbols of the STEPFORMS bound to the initial or stepped values."
|
||
;; check the syntax of the macro
|
||
(and (check-do-stepforms stepforms)
|
||
(check-do-endforms endforms))
|
||
;; construct emacs-lisp equivalent
|
||
(let ((initlist (extract-do-inits stepforms))
|
||
(steplist (extract-do-steps stepforms))
|
||
(endcond (car endforms))
|
||
(endbody (cdr endforms)))
|
||
(cons 'let (cons initlist
|
||
(cons (cons 'while (cons (list 'not endcond)
|
||
(append body steplist)))
|
||
(append endbody))))))
|
||
|
||
|
||
(defmacro do* (stepforms endforms &rest body)
|
||
"`do*' is to `do' as `let*' is to `let'.
|
||
STEPFORMS must be a list of symbols or lists. In the second case, the
|
||
lists must start with a symbol and contain up to two more forms. In
|
||
the STEPFORMS, a symbol is the same as a (symbol). The other 2 forms
|
||
are the initial value (def. NIL) and the form to step (def. itself).
|
||
Initializations and steppings are done in the sequence they are written.
|
||
The ENDFORMS are a list (CONDITION . ENDBODY). If the CONDITION
|
||
evaluates to true in any iteration, ENDBODY is evaluated and the last
|
||
form in it is returned.
|
||
The BODY (which may be empty) is evaluated at every iteration, with
|
||
the symbols of the STEPFORMS bound to the initial or stepped values."
|
||
;; check the syntax of the macro
|
||
(and (check-do-stepforms stepforms)
|
||
(check-do-endforms endforms))
|
||
;; construct emacs-lisp equivalent
|
||
(let ((initlist (extract-do-inits stepforms))
|
||
(steplist (extract-do*-steps stepforms))
|
||
(endcond (car endforms))
|
||
(endbody (cdr endforms)))
|
||
(cons 'let* (cons initlist
|
||
(cons (cons 'while (cons (list 'not endcond)
|
||
(append body steplist)))
|
||
(append endbody))))))
|
||
|
||
|
||
;;; DO and DO* share the syntax checking functions that follow.
|
||
|
||
(defun check-do-stepforms (forms)
|
||
"True if FORMS is a valid stepforms for the do[*] macro (q.v.)"
|
||
(if (nlistp forms)
|
||
(error "init/step form for do[*] should be a list, not `%s'"
|
||
(prin1-to-string forms))
|
||
(mapcar
|
||
(function
|
||
(lambda (entry)
|
||
(if (not (or (symbolp entry)
|
||
(and (listp entry)
|
||
(symbolp (car entry))
|
||
(< (length entry) 4))))
|
||
(error "init/step must be %s, not `%s'"
|
||
"symbol or (symbol [init [step]])"
|
||
(prin1-to-string entry)))))
|
||
forms)))
|
||
|
||
(defun check-do-endforms (forms)
|
||
"True if FORMS is a valid endforms for the do[*] macro (q.v.)"
|
||
(if (nlistp forms)
|
||
(error "termination form for do macro should be a list, not `%s'"
|
||
(prin1-to-string forms))))
|
||
|
||
(defun extract-do-inits (forms)
|
||
"Returns a list of the initializations (for do) in FORMS
|
||
--a stepforms, see the do macro--. FORMS is assumed syntactically valid."
|
||
(mapcar
|
||
(function
|
||
(lambda (entry)
|
||
(cond ((symbolp entry)
|
||
(list entry nil))
|
||
((listp entry)
|
||
(list (car entry) (cadr entry))))))
|
||
forms))
|
||
|
||
;;; There used to be a reason to deal with DO differently than with
|
||
;;; DO*. The writing of PSETQ has made it largely unnecessary.
|
||
|
||
(defun extract-do-steps (forms)
|
||
"EXTRACT-DO-STEPS FORMS => an s-expr
|
||
FORMS is the stepforms part of a DO macro (q.v.). This function
|
||
constructs an s-expression that does the stepping at the end of an
|
||
iteration."
|
||
(list (cons 'psetq (select-stepping-forms forms))))
|
||
|
||
(defun extract-do*-steps (forms)
|
||
"EXTRACT-DO*-STEPS FORMS => an s-expr
|
||
FORMS is the stepforms part of a DO* macro (q.v.). This function
|
||
constructs an s-expression that does the stepping at the end of an
|
||
iteration."
|
||
(list (cons 'setq (select-stepping-forms forms))))
|
||
|
||
(defun select-stepping-forms (forms)
|
||
"Separate only the forms that cause stepping."
|
||
(let ((result '()) ;ends up being (... var form ...)
|
||
(ptr forms) ;to traverse the forms
|
||
entry ;to explore each form in turn
|
||
)
|
||
(while ptr ;(not (endp entry)) might be safer
|
||
(setq entry (car ptr))
|
||
(cond ((and (listp entry) (= (length entry) 3))
|
||
(setq result (append ;append in reverse order!
|
||
(list (caddr entry) (car entry))
|
||
result))))
|
||
(setq ptr (cdr ptr))) ;step in the list of forms
|
||
(nreverse result)))
|
||
|
||
;;; Other iterative constructs
|
||
|
||
(defmacro dolist (stepform &rest body)
|
||
"(dolist (VAR LIST [RESULTFORM]) . BODY): do BODY for each elt of LIST.
|
||
The RESULTFORM defaults to nil. The VAR is bound to successive
|
||
elements of the value of LIST and remains bound (to the nil value) when the
|
||
RESULTFORM is evaluated."
|
||
;; check sanity
|
||
(cond
|
||
((nlistp stepform)
|
||
(error "stepform for `dolist' should be (VAR LIST [RESULT]), not `%s'"
|
||
(prin1-to-string stepform)))
|
||
((not (symbolp (car stepform)))
|
||
(error "first component of stepform should be a symbol, not `%s'"
|
||
(prin1-to-string (car stepform))))
|
||
((> (length stepform) 3)
|
||
(error "too many components in stepform `%s'"
|
||
(prin1-to-string stepform))))
|
||
;; generate code
|
||
(let* ((var (car stepform))
|
||
(listform (cadr stepform))
|
||
(resultform (caddr stepform))
|
||
(listsym (gentemp)))
|
||
(nconc
|
||
(list 'let (list var (list listsym listform))
|
||
(nconc
|
||
(list 'while listsym
|
||
(list 'setq
|
||
var (list 'car listsym)
|
||
listsym (list 'cdr listsym)))
|
||
body))
|
||
(and resultform
|
||
(cons (list 'setq var nil)
|
||
(list resultform))))))
|
||
|
||
(defmacro dotimes (stepform &rest body)
|
||
"(dotimes (VAR COUNTFORM [RESULTFORM]) . BODY): Repeat BODY, counting in VAR.
|
||
The COUNTFORM should return a positive integer. The VAR is bound to
|
||
successive integers from 0 to COUNTFORM-1 and the BODY is repeated for
|
||
each of them. At the end, the RESULTFORM is evaluated and its value
|
||
returned. During this last evaluation, the VAR is still bound, and its
|
||
value is the number of times the iteration occurred. An omitted RESULTFORM
|
||
defaults to nil."
|
||
;; check sanity
|
||
(cond
|
||
((nlistp stepform)
|
||
(error "stepform for `dotimes' should be (VAR COUNT [RESULT]), not `%s'"
|
||
(prin1-to-string stepform)))
|
||
((not (symbolp (car stepform)))
|
||
(error "first component of stepform should be a symbol, not `%s'"
|
||
(prin1-to-string (car stepform))))
|
||
((> (length stepform) 3)
|
||
(error "too many components in stepform `%s'"
|
||
(prin1-to-string stepform))))
|
||
;; generate code
|
||
(let* ((var (car stepform))
|
||
(countform (cadr stepform))
|
||
(resultform (caddr stepform))
|
||
(testsym (if (consp countform) (gentemp) countform)))
|
||
(nconc
|
||
(list
|
||
'let (cons (list var -1)
|
||
(and (not (eq countform testsym))
|
||
(list (list testsym countform))))
|
||
(nconc
|
||
(list 'while (list '< (list 'setq var (list '1+ var)) testsym))
|
||
body))
|
||
(and resultform (list resultform)))))
|
||
|
||
(defmacro do-symbols (stepform &rest body)
|
||
"(do_symbols (VAR [OBARRAY [RESULTFORM]]) . BODY)
|
||
The VAR is bound to each of the symbols in OBARRAY (def. obarray) and
|
||
the BODY is repeatedly performed for each of those bindings. At the
|
||
end, RESULTFORM (def. nil) is evaluated and its value returned.
|
||
During this last evaluation, the VAR is still bound and its value is nil.
|
||
See also the function `mapatoms'."
|
||
;; check sanity
|
||
(cond
|
||
((nlistp stepform)
|
||
(error "stepform for `do-symbols' should be (VAR OBARRAY [RESULT]), not `%s'"
|
||
(prin1-to-string stepform)))
|
||
((not (symbolp (car stepform)))
|
||
(error "first component of stepform should be a symbol, not `%s'"
|
||
(prin1-to-string (car stepform))))
|
||
((> (length stepform) 3)
|
||
(error "too many components in stepform `%s'"
|
||
(prin1-to-string stepform))))
|
||
;; generate code
|
||
(let* ((var (car stepform))
|
||
(oblist (cadr stepform))
|
||
(resultform (caddr stepform)))
|
||
(list 'progn
|
||
(list 'mapatoms
|
||
(list 'function
|
||
(cons 'lambda (cons (list var) body)))
|
||
oblist)
|
||
(list 'let
|
||
(list (list var nil))
|
||
resultform))))
|
||
|
||
|
||
(defmacro do-all-symbols (stepform &rest body)
|
||
"(do-all-symbols (VAR [RESULTFORM]) . BODY)
|
||
Is the same as (do-symbols (VAR obarray RESULTFORM) . BODY)."
|
||
(list*
|
||
'do-symbols
|
||
(list (car stepform) 'obarray (cadr stepform))
|
||
body))
|
||
|
||
(defmacro loop (&rest body)
|
||
"(loop . BODY) repeats BODY indefinitely and does not return.
|
||
Normally BODY uses `throw' or `signal' to cause an exit.
|
||
The forms in BODY should be lists, as non-lists are reserved for new features."
|
||
;; check that the body doesn't have atomic forms
|
||
(if (nlistp body)
|
||
(error "body of `loop' should be a list of lists or nil")
|
||
;; ok, it is a list, check for atomic components
|
||
(mapcar
|
||
(function (lambda (component)
|
||
(if (nlistp component)
|
||
(error "components of `loop' should be lists"))))
|
||
body)
|
||
;; build the infinite loop
|
||
(cons 'while (cons 't body))))
|
||
|
||
;;;; end of cl-iterations.el
|
||
|
||
;;;; LISTS
|
||
;;;; This file provides some of the lists machinery of Common-Lisp
|
||
;;;; in a way compatible with Emacs Lisp. Especially, see the the
|
||
;;;; typical c[ad]*r functions.
|
||
;;;;
|
||
;;;; Cesar Quiroz @ UofR DofCSc - Dec. 1986
|
||
;;;; (quiroz@cs.rochester.edu)
|
||
|
||
;;; Synonyms for list functions
|
||
(defsubst first (x)
|
||
"Synonym for `car'"
|
||
(car x))
|
||
|
||
(defsubst second (x)
|
||
"Return the second element of the list LIST."
|
||
(nth 1 x))
|
||
|
||
(defsubst third (x)
|
||
"Return the third element of the list LIST."
|
||
(nth 2 x))
|
||
|
||
(defsubst fourth (x)
|
||
"Return the fourth element of the list LIST."
|
||
(nth 3 x))
|
||
|
||
(defsubst fifth (x)
|
||
"Return the fifth element of the list LIST."
|
||
(nth 4 x))
|
||
|
||
(defsubst sixth (x)
|
||
"Return the sixth element of the list LIST."
|
||
(nth 5 x))
|
||
|
||
(defsubst seventh (x)
|
||
"Return the seventh element of the list LIST."
|
||
(nth 6 x))
|
||
|
||
(defsubst eighth (x)
|
||
"Return the eighth element of the list LIST."
|
||
(nth 7 x))
|
||
|
||
(defsubst ninth (x)
|
||
"Return the ninth element of the list LIST."
|
||
(nth 8 x))
|
||
|
||
(defsubst tenth (x)
|
||
"Return the tenth element of the list LIST."
|
||
(nth 9 x))
|
||
|
||
(defsubst rest (x)
|
||
"Synonym for `cdr'"
|
||
(cdr x))
|
||
|
||
(defsubst endp (x)
|
||
"t if X is nil, nil if X is a cons; error otherwise."
|
||
(if (listp x)
|
||
(null x)
|
||
(error "endp received a non-cons, non-null argument `%s'"
|
||
(prin1-to-string x))))
|
||
|
||
(defun last (x)
|
||
"Returns the last link in the list LIST."
|
||
(if (nlistp x)
|
||
(error "arg to `last' must be a list"))
|
||
(do ((current-cons x (cdr current-cons))
|
||
(next-cons (cdr x) (cdr next-cons)))
|
||
((endp next-cons) current-cons)))
|
||
|
||
(defun list-length (x) ;taken from CLtL sect. 15.2
|
||
"Returns the length of a non-circular list, or `nil' for a circular one."
|
||
(do ((n 0) ;counter
|
||
(fast x (cddr fast)) ;fast pointer, leaps by 2
|
||
(slow x (cdr slow)) ;slow pointer, leaps by 1
|
||
(ready nil)) ;indicates termination
|
||
(ready n)
|
||
(cond ((endp fast)
|
||
(setq ready t)) ;return n
|
||
((endp (cdr fast))
|
||
(setq n (+ n 1))
|
||
(setq ready t)) ;return n+1
|
||
((and (eq fast slow) (> n 0))
|
||
(setq n nil)
|
||
(setq ready t)) ;return nil
|
||
(t
|
||
(setq n (+ n 2)))))) ;just advance counter
|
||
|
||
(defun butlast (list &optional n)
|
||
"Return a new list like LIST but sans the last N elements.
|
||
N defaults to 1. If the list doesn't have N elements, nil is returned."
|
||
(if (null n) (setq n 1))
|
||
(nreverse (nthcdr n (reverse list)))) ;optim. due to macrakis@osf.org
|
||
|
||
;;; This version due to Aaron Larson (alarson@src.honeywell.com, 26 Jul 91)
|
||
(defun list* (arg &rest others)
|
||
"Return a new list containing the first arguments consed onto the last arg.
|
||
Thus, (list* 1 2 3 '(a b)) returns (1 2 3 a b)."
|
||
(if (null others)
|
||
arg
|
||
(let* ((others (cons arg (copy-sequence others)))
|
||
(a others))
|
||
(while (cdr (cdr a))
|
||
(setq a (cdr a)))
|
||
(setcdr a (car (cdr a)))
|
||
others)))
|
||
|
||
(defun adjoin (item list)
|
||
"Return a list which contains ITEM but is otherwise like LIST.
|
||
If ITEM occurs in LIST, the value is LIST. Otherwise it is (cons ITEM LIST).
|
||
When comparing ITEM against elements, `eql' is used."
|
||
(if (memq item list)
|
||
list
|
||
(cons item list)))
|
||
|
||
(defun ldiff (list sublist)
|
||
"Return a new list like LIST but sans SUBLIST.
|
||
SUBLIST must be one of the links in LIST; otherwise the value is LIST itself."
|
||
(do ((result '())
|
||
(curcons list (cdr curcons)))
|
||
((or (endp curcons) (eq curcons sublist))
|
||
(reverse result))
|
||
(setq result (cons (car curcons) result))))
|
||
|
||
;;; The popular c[ad]*r functions and other list accessors.
|
||
|
||
;;; To implement this efficiently, a new byte compile handler is used to
|
||
;;; generate the minimal code, saving one function call.
|
||
|
||
(defsubst caar (X)
|
||
"Return the car of the car of X."
|
||
(car (car X)))
|
||
|
||
(defsubst cadr (X)
|
||
"Return the car of the cdr of X."
|
||
(car (cdr X)))
|
||
|
||
(defsubst cdar (X)
|
||
"Return the cdr of the car of X."
|
||
(cdr (car X)))
|
||
|
||
(defsubst cddr (X)
|
||
"Return the cdr of the cdr of X."
|
||
(cdr (cdr X)))
|
||
|
||
(defsubst caaar (X)
|
||
"Return the car of the car of the car of X."
|
||
(car (car (car X))))
|
||
|
||
(defsubst caadr (X)
|
||
"Return the car of the car of the cdr of X."
|
||
(car (car (cdr X))))
|
||
|
||
(defsubst cadar (X)
|
||
"Return the car of the cdr of the car of X."
|
||
(car (cdr (car X))))
|
||
|
||
(defsubst cdaar (X)
|
||
"Return the cdr of the car of the car of X."
|
||
(cdr (car (car X))))
|
||
|
||
(defsubst caddr (X)
|
||
"Return the car of the cdr of the cdr of X."
|
||
(car (cdr (cdr X))))
|
||
|
||
(defsubst cdadr (X)
|
||
"Return the cdr of the car of the cdr of X."
|
||
(cdr (car (cdr X))))
|
||
|
||
(defsubst cddar (X)
|
||
"Return the cdr of the cdr of the car of X."
|
||
(cdr (cdr (car X))))
|
||
|
||
(defsubst cdddr (X)
|
||
"Return the cdr of the cdr of the cdr of X."
|
||
(cdr (cdr (cdr X))))
|
||
|
||
(defsubst caaaar (X)
|
||
"Return the car of the car of the car of the car of X."
|
||
(car (car (car (car X)))))
|
||
|
||
(defsubst caaadr (X)
|
||
"Return the car of the car of the car of the cdr of X."
|
||
(car (car (car (cdr X)))))
|
||
|
||
(defsubst caadar (X)
|
||
"Return the car of the car of the cdr of the car of X."
|
||
(car (car (cdr (car X)))))
|
||
|
||
(defsubst cadaar (X)
|
||
"Return the car of the cdr of the car of the car of X."
|
||
(car (cdr (car (car X)))))
|
||
|
||
(defsubst cdaaar (X)
|
||
"Return the cdr of the car of the car of the car of X."
|
||
(cdr (car (car (car X)))))
|
||
|
||
(defsubst caaddr (X)
|
||
"Return the car of the car of the cdr of the cdr of X."
|
||
(car (car (cdr (cdr X)))))
|
||
|
||
(defsubst cadadr (X)
|
||
"Return the car of the cdr of the car of the cdr of X."
|
||
(car (cdr (car (cdr X)))))
|
||
|
||
(defsubst cdaadr (X)
|
||
"Return the cdr of the car of the car of the cdr of X."
|
||
(cdr (car (car (cdr X)))))
|
||
|
||
(defsubst caddar (X)
|
||
"Return the car of the cdr of the cdr of the car of X."
|
||
(car (cdr (cdr (car X)))))
|
||
|
||
(defsubst cdadar (X)
|
||
"Return the cdr of the car of the cdr of the car of X."
|
||
(cdr (car (cdr (car X)))))
|
||
|
||
(defsubst cddaar (X)
|
||
"Return the cdr of the cdr of the car of the car of X."
|
||
(cdr (cdr (car (car X)))))
|
||
|
||
(defsubst cadddr (X)
|
||
"Return the car of the cdr of the cdr of the cdr of X."
|
||
(car (cdr (cdr (cdr X)))))
|
||
|
||
(defsubst cddadr (X)
|
||
"Return the cdr of the cdr of the car of the cdr of X."
|
||
(cdr (cdr (car (cdr X)))))
|
||
|
||
(defsubst cdaddr (X)
|
||
"Return the cdr of the car of the cdr of the cdr of X."
|
||
(cdr (car (cdr (cdr X)))))
|
||
|
||
(defsubst cdddar (X)
|
||
"Return the cdr of the cdr of the cdr of the car of X."
|
||
(cdr (cdr (cdr (car X)))))
|
||
|
||
(defsubst cddddr (X)
|
||
"Return the cdr of the cdr of the cdr of the cdr of X."
|
||
(cdr (cdr (cdr (cdr X)))))
|
||
|
||
;;; some inverses of the accessors are needed for setf purposes
|
||
|
||
(defsubst setnth (n list newval)
|
||
"Set (nth N LIST) to NEWVAL. Returns NEWVAL."
|
||
(rplaca (nthcdr n list) newval))
|
||
|
||
(defun setnthcdr (n list newval)
|
||
"(setnthcdr N LIST NEWVAL) => NEWVAL
|
||
As a side effect, sets the Nth cdr of LIST to NEWVAL."
|
||
(when (< n 0)
|
||
(error "N must be 0 or greater, not %d" n))
|
||
(while (> n 0)
|
||
(setq list (cdr list)
|
||
n (- n 1)))
|
||
;; here only if (zerop n)
|
||
(rplaca list (car newval))
|
||
(rplacd list (cdr newval))
|
||
newval)
|
||
|
||
;;; A-lists machinery
|
||
|
||
(defsubst acons (key item alist)
|
||
"Return a new alist with KEY paired with ITEM; otherwise like ALIST.
|
||
Does not copy ALIST."
|
||
(cons (cons key item) alist))
|
||
|
||
(defun pairlis (keys data &optional alist)
|
||
"Return a new alist with each elt of KEYS paired with an elt of DATA;
|
||
optional 3rd arg ALIST is nconc'd at the end. KEYS and DATA must
|
||
have the same length."
|
||
(unless (= (length keys) (length data))
|
||
(error "keys and data should be the same length"))
|
||
(do* ;;collect keys and data in front of alist
|
||
((kptr keys (cdr kptr)) ;traverses the keys
|
||
(dptr data (cdr dptr)) ;traverses the data
|
||
(key (car kptr) (car kptr)) ;current key
|
||
(item (car dptr) (car dptr)) ;current data item
|
||
(result alist))
|
||
((endp kptr) result)
|
||
(setq result (acons key item result))))
|
||
|
||
;;;; end of cl-lists.el
|
||
|
||
;;;; SEQUENCES
|
||
;;;; Emacs Lisp provides many of the 'sequences' functionality of
|
||
;;;; Common Lisp. This file provides a few things that were left out.
|
||
;;;;
|
||
|
||
|
||
(defkeyword :test "Used to designate positive (selection) tests.")
|
||
(defkeyword :test-not "Used to designate negative (rejection) tests.")
|
||
(defkeyword :key "Used to designate component extractions.")
|
||
(defkeyword :predicate "Used to define matching of sequence components.")
|
||
(defkeyword :start "Inclusive low index in sequence")
|
||
(defkeyword :end "Exclusive high index in sequence")
|
||
(defkeyword :start1 "Inclusive low index in first of two sequences.")
|
||
(defkeyword :start2 "Inclusive low index in second of two sequences.")
|
||
(defkeyword :end1 "Exclusive high index in first of two sequences.")
|
||
(defkeyword :end2 "Exclusive high index in second of two sequences.")
|
||
(defkeyword :count "Number of elements to affect.")
|
||
(defkeyword :from-end "T when counting backwards.")
|
||
(defkeyword :initial-value "For the syntax of #'reduce")
|
||
|
||
(defun some (pred seq &rest moreseqs)
|
||
"Test PREDICATE on each element of SEQUENCE; is it ever non-nil?
|
||
Extra args are additional sequences; PREDICATE gets one arg from each
|
||
sequence and we advance down all the sequences together in lock-step.
|
||
A sequence means either a list or a vector."
|
||
(let ((args (reassemble-argslists (list* seq moreseqs))))
|
||
(do* ((ready nil) ;flag: return when t
|
||
(result nil) ;resulting value
|
||
(applyval nil) ;result of applying pred once
|
||
(remaining args
|
||
(cdr remaining)) ;remaining argument sets
|
||
(current (car remaining) ;current argument set
|
||
(car remaining)))
|
||
((or ready (endp remaining)) result)
|
||
(setq applyval (apply pred current))
|
||
(when applyval
|
||
(setq ready t)
|
||
(setq result applyval)))))
|
||
|
||
(defun every (pred seq &rest moreseqs)
|
||
"Test PREDICATE on each element of SEQUENCE; is it always non-nil?
|
||
Extra args are additional sequences; PREDICATE gets one arg from each
|
||
sequence and we advance down all the sequences together in lock-step.
|
||
A sequence means either a list or a vector."
|
||
(let ((args (reassemble-argslists (list* seq moreseqs))))
|
||
(do* ((ready nil) ;flag: return when t
|
||
(result t) ;resulting value
|
||
(applyval nil) ;result of applying pred once
|
||
(remaining args
|
||
(cdr remaining)) ;remaining argument sets
|
||
(current (car remaining) ;current argument set
|
||
(car remaining)))
|
||
((or ready (endp remaining)) result)
|
||
(setq applyval (apply pred current))
|
||
(unless applyval
|
||
(setq ready t)
|
||
(setq result nil)))))
|
||
|
||
(defun notany (pred seq &rest moreseqs)
|
||
"Test PREDICATE on each element of SEQUENCE; is it always nil?
|
||
Extra args are additional sequences; PREDICATE gets one arg from each
|
||
sequence and we advance down all the sequences together in lock-step.
|
||
A sequence means either a list or a vector."
|
||
(let ((args (reassemble-argslists (list* seq moreseqs))))
|
||
(do* ((ready nil) ;flag: return when t
|
||
(result t) ;resulting value
|
||
(applyval nil) ;result of applying pred once
|
||
(remaining args
|
||
(cdr remaining)) ;remaining argument sets
|
||
(current (car remaining) ;current argument set
|
||
(car remaining)))
|
||
((or ready (endp remaining)) result)
|
||
(setq applyval (apply pred current))
|
||
(when applyval
|
||
(setq ready t)
|
||
(setq result nil)))))
|
||
|
||
(defun notevery (pred seq &rest moreseqs)
|
||
"Test PREDICATE on each element of SEQUENCE; is it sometimes nil?
|
||
Extra args are additional sequences; PREDICATE gets one arg from each
|
||
sequence and we advance down all the sequences together in lock-step.
|
||
A sequence means either a list or a vector."
|
||
(let ((args (reassemble-argslists (list* seq moreseqs))))
|
||
(do* ((ready nil) ;flag: return when t
|
||
(result nil) ;resulting value
|
||
(applyval nil) ;result of applying pred once
|
||
(remaining args
|
||
(cdr remaining)) ;remaining argument sets
|
||
(current (car remaining) ;current argument set
|
||
(car remaining)))
|
||
((or ready (endp remaining)) result)
|
||
(setq applyval (apply pred current))
|
||
(unless applyval
|
||
(setq ready t)
|
||
(setq result t)))))
|
||
|
||
;;; More sequence functions that don't need keyword arguments
|
||
|
||
(defun concatenate (type &rest sequences)
|
||
"(concatenate TYPE &rest SEQUENCES) => a sequence
|
||
The sequence returned is of type TYPE (must be 'list, 'string, or 'vector) and
|
||
contains the concatenation of the elements of all the arguments, in the order
|
||
given."
|
||
(let ((sequences (append sequences '(()))))
|
||
(case type
|
||
(list
|
||
(apply (function append) sequences))
|
||
(string
|
||
(apply (function concat) sequences))
|
||
(vector
|
||
(apply (function vector) (apply (function append) sequences)))
|
||
(t
|
||
(error "type for concatenate `%s' not 'list, 'string or 'vector"
|
||
(prin1-to-string type))))))
|
||
|
||
(defun map (type function &rest sequences)
|
||
"(map TYPE FUNCTION &rest SEQUENCES) => a sequence
|
||
The FUNCTION is called on each set of elements from the SEQUENCES \(stopping
|
||
when the shortest sequence is terminated\) and the results are possibly
|
||
returned in a sequence of type TYPE \(one of 'list, 'vector, 'string, or nil\)
|
||
giving NIL for TYPE gets rid of the values."
|
||
(if (not (memq type (list 'list 'string 'vector nil)))
|
||
(error "type for map `%s' not 'list, 'string, 'vector or nil"
|
||
(prin1-to-string type)))
|
||
(let ((argslists (reassemble-argslists sequences))
|
||
results)
|
||
(if (null type)
|
||
(while argslists ;don't bother accumulating
|
||
(apply function (car argslists))
|
||
(setq argslists (cdr argslists)))
|
||
(setq results (mapcar (function (lambda (args) (apply function args)))
|
||
argslists))
|
||
(case type
|
||
(list
|
||
results)
|
||
(string
|
||
(funcall (function concat) results))
|
||
(vector
|
||
(apply (function vector) results))))))
|
||
|
||
;;; an inverse of elt is needed for setf purposes
|
||
|
||
(defun setelt (seq n newval)
|
||
"In SEQUENCE, set the Nth element to NEWVAL. Returns NEWVAL.
|
||
A sequence means either a list or a vector."
|
||
(let ((l (length seq)))
|
||
(if (or (< n 0) (>= n l))
|
||
(error "N(%d) should be between 0 and %d" n l)
|
||
;; only two cases need be considered valid, as strings are arrays
|
||
(cond ((listp seq)
|
||
(setnth n seq newval))
|
||
((arrayp seq)
|
||
(aset seq n newval))
|
||
(t
|
||
(error "SEQ should be a sequence, not `%s'"
|
||
(prin1-to-string seq)))))))
|
||
|
||
;;; Testing with keyword arguments.
|
||
;;;
|
||
;;; Many of the sequence functions use keywords to denote some stylized
|
||
;;; form of selecting entries in a sequence. The involved arguments
|
||
;;; are collected with a &rest marker (as Emacs Lisp doesn't have a &key
|
||
;;; marker), then they are passed to build-klist, who
|
||
;;; constructs an association list. That association list is used to
|
||
;;; test for satisfaction and matching.
|
||
|
||
;;; DON'T USE MEMBER, NOR ANY FUNCTION THAT COULD TAKE KEYWORDS HERE!!!
|
||
|
||
(defun build-klist (argslist acceptable &optional allow-other-keys)
|
||
"Decode a keyword argument list ARGSLIST for keywords in ACCEPTABLE.
|
||
ARGSLIST is a list, presumably the &rest argument of a call, whose
|
||
even numbered elements must be keywords.
|
||
ACCEPTABLE is a list of keywords, the only ones that are truly acceptable.
|
||
The result is an alist containing the arguments named by the keywords
|
||
in ACCEPTABLE, or an error is signalled, if something failed.
|
||
If the third argument (an optional) is non-nil, other keys are acceptable."
|
||
;; check legality of the arguments, then destructure them
|
||
(unless (and (listp argslist)
|
||
(evenp (length argslist)))
|
||
(error "build-klist: odd number of keyword-args"))
|
||
(unless (and (listp acceptable)
|
||
(every 'keywordp acceptable))
|
||
(error "build-klist: second arg should be a list of keywords"))
|
||
(multiple-value-bind
|
||
(keywords forms)
|
||
(unzip-list argslist)
|
||
(unless (every 'keywordp keywords)
|
||
(error "build-klist: expected keywords, found `%s'"
|
||
(prin1-to-string keywords)))
|
||
(unless (or allow-other-keys
|
||
(every (function (lambda (keyword)
|
||
(memq keyword acceptable)))
|
||
keywords))
|
||
(error "bad keyword[s]: %s not in %s"
|
||
(prin1-to-string (mapcan (function (lambda (keyword)
|
||
(if (memq keyword acceptable)
|
||
nil
|
||
(list keyword))))
|
||
keywords))
|
||
(prin1-to-string acceptable)))
|
||
(do* ;;pick up the pieces
|
||
((auxlist ;auxiliary a-list, may
|
||
(pairlis keywords forms)) ;contain repetitions and junk
|
||
(ptr acceptable (cdr ptr)) ;pointer in acceptable
|
||
(this (car ptr) (car ptr)) ;current acceptable keyword
|
||
(auxval nil) ;used to move values around
|
||
(alist '())) ;used to build the result
|
||
((endp ptr) alist)
|
||
;; if THIS appears in auxlist, use its value
|
||
(when (setq auxval (assq this auxlist))
|
||
(setq alist (cons auxval alist))))))
|
||
|
||
|
||
(defun extract-from-klist (klist key &optional default)
|
||
"(extract-from-klist KLIST KEY [DEFAULT]) => value of KEY or DEFAULT
|
||
Extract value associated with KEY in KLIST (return DEFAULT if nil)."
|
||
(let ((retrieved (cdr (assq key klist))))
|
||
(or retrieved default)))
|
||
|
||
(defun keyword-argument-supplied-p (klist key)
|
||
"(keyword-argument-supplied-p KLIST KEY) => nil or something
|
||
NIL if KEY (a keyword) does not appear in the KLIST."
|
||
(assq key klist))
|
||
|
||
(defun add-to-klist (key item klist)
|
||
"(ADD-TO-KLIST KEY ITEM KLIST) => new KLIST
|
||
Add association (KEY . ITEM) to KLIST."
|
||
(setq klist (acons key item klist)))
|
||
|
||
(defun elt-satisfies-test-p (item elt klist)
|
||
"(elt-satisfies-test-p ITEM ELT KLIST) => t or nil
|
||
KLIST encodes a keyword-arguments test, as in CH. 14 of CLtL.
|
||
True if the given ITEM and ELT satisfy the test."
|
||
(let ((test (extract-from-klist klist :test))
|
||
(test-not (extract-from-klist klist :test-not))
|
||
(keyfn (extract-from-klist klist :key 'identity)))
|
||
(cond (test
|
||
(funcall test item (funcall keyfn elt)))
|
||
(test-not
|
||
(not (funcall test-not item (funcall keyfn elt))))
|
||
(t ;should never happen
|
||
(error "neither :test nor :test-not in `%s'"
|
||
(prin1-to-string klist))))))
|
||
|
||
(defun elt-satisfies-if-p (item klist)
|
||
"(elt-satisfies-if-p ITEM KLIST) => t or nil
|
||
True if an -if style function was called and ITEM satisfies the
|
||
predicate under :predicate in KLIST."
|
||
(let ((predicate (extract-from-klist klist :predicate))
|
||
(keyfn (extract-from-klist klist :key 'identity)))
|
||
(funcall predicate (funcall keyfn item))))
|
||
|
||
(defun elt-satisfies-if-not-p (item klist)
|
||
"(elt-satisfies-if-not-p ITEM KLIST) => t or nil
|
||
KLIST encodes a keyword-arguments test, as in CH. 14 of CLtL.
|
||
True if an -if-not style function was called and ITEM does not satisfy
|
||
the predicate under :predicate in KLIST."
|
||
(let ((predicate (extract-from-klist klist :predicate))
|
||
(keyfn (extract-from-klist klist :key 'identity)))
|
||
(not (funcall predicate (funcall keyfn item)))))
|
||
|
||
(defun elts-match-under-klist-p (e1 e2 klist)
|
||
"(elts-match-under-klist-p E1 E2 KLIST) => t or nil
|
||
KLIST encodes a keyword-arguments test, as in CH. 14 of CLtL.
|
||
True if elements E1 and E2 match under the tests encoded in KLIST."
|
||
(let ((test (extract-from-klist klist :test))
|
||
(test-not (extract-from-klist klist :test-not))
|
||
(keyfn (extract-from-klist klist :key 'identity)))
|
||
(if (and test test-not)
|
||
(error "both :test and :test-not in `%s'"
|
||
(prin1-to-string klist)))
|
||
(cond (test
|
||
(funcall test (funcall keyfn e1) (funcall keyfn e2)))
|
||
(test-not
|
||
(not (funcall test-not (funcall keyfn e1) (funcall keyfn e2))))
|
||
(t ;should never happen
|
||
(error "neither :test nor :test-not in `%s'"
|
||
(prin1-to-string klist))))))
|
||
|
||
;;; This macro simplifies using keyword args. It is less clumsy than using
|
||
;;; the primitives build-klist, etc... For instance, member could be written
|
||
;;; this way:
|
||
|
||
;;; (defun member (item list &rest kargs)
|
||
;;; (with-keyword-args kargs (test test-not (key 'identity))
|
||
;;; ...))
|
||
|
||
;;; Suggested by Robert Potter (potter@cs.rochester.edu, 15 Nov 1989)
|
||
|
||
(defmacro with-keyword-args (keyargslist vardefs &rest body)
|
||
"(WITH-KEYWORD-ARGS KEYARGSLIST VARDEFS . BODY)
|
||
KEYARGSLIST can be either a symbol or a list of one or two symbols.
|
||
In the second case, the second symbol is either T or NIL, indicating whether
|
||
keywords other than the mentioned ones are tolerable.
|
||
|
||
VARDEFS is a list. Each entry is either a VAR (symbol) or matches
|
||
\(VAR [DEFAULT [KEYWORD]]). Just giving VAR is the same as giving
|
||
\(VAR nil :VAR).
|
||
|
||
The BODY is executed in an environment where each VAR (a symbol) is bound to
|
||
the value present in the KEYARGSLIST provided, or to the DEFAULT. The value
|
||
is searched by using the keyword form of VAR (i.e., :VAR) or the optional
|
||
keyword if provided.
|
||
|
||
Notice that this macro doesn't distinguish between a default value given
|
||
explicitly by the user and one provided by default. See also the more
|
||
primitive functions build-klist, add-to-klist, extract-from-klist,
|
||
keyword-argument-supplied-p, elt-satisfies-test-p, elt-satisfies-if-p,
|
||
elt-satisfies-if-not-p, elts-match-under-klist-p. They provide more complete,
|
||
if clumsier, control over this feature."
|
||
(let (allow-other-keys)
|
||
(if (listp keyargslist)
|
||
(if (> (length keyargslist) 2)
|
||
(error
|
||
"`%s' should be SYMBOL, (SYMBOL), or (SYMBOL t-OR-nil)"
|
||
(prin1-to-string keyargslist))
|
||
(setq allow-other-keys (cadr keyargslist)
|
||
keyargslist (car keyargslist))
|
||
(if (not (and
|
||
(symbolp keyargslist)
|
||
(memq allow-other-keys '(t nil))))
|
||
(error
|
||
"first subform should be SYMBOL, (SYMBOL), or (SYMBOL t-OR-nil)"
|
||
)))
|
||
(if (symbolp keyargslist)
|
||
(setq allow-other-keys nil)
|
||
(error
|
||
"first subform should be SYMBOL, (SYMBOL), or (SYMBOL t-OR-nil)")))
|
||
(let (vars defaults keywords forms
|
||
(klistname (gensym "KLIST_")))
|
||
(mapcar (function (lambda (entry)
|
||
(if (symbolp entry) ;defaulty case
|
||
(setq entry (list entry nil (keyword-of entry))))
|
||
(let* ((l (length entry))
|
||
(v (car entry))
|
||
(d (cadr entry))
|
||
(k (caddr entry)))
|
||
(if (or (< l 1) (> l 3))
|
||
(error
|
||
"`%s' must match (VAR [DEFAULT [KEYWORD]])"
|
||
(prin1-to-string entry)))
|
||
(if (or (null v) (not (symbolp v)))
|
||
(error
|
||
"bad variable `%s': must be non-null symbol"
|
||
(prin1-to-string v)))
|
||
(setq vars (cons v vars))
|
||
(setq defaults (cons d defaults))
|
||
(if (< l 3)
|
||
(setq k (keyword-of v)))
|
||
(if (and (= l 3)
|
||
(or (null k)
|
||
(not (keywordp k))))
|
||
(error
|
||
"bad keyword `%s'" (prin1-to-string k)))
|
||
(setq keywords (cons k keywords))
|
||
(setq forms (cons (list v (list 'extract-from-klist
|
||
klistname
|
||
k
|
||
d))
|
||
forms)))))
|
||
vardefs)
|
||
(append
|
||
(list 'let* (nconc (list (list klistname
|
||
(list 'build-klist keyargslist
|
||
(list 'quote keywords)
|
||
allow-other-keys)))
|
||
(nreverse forms)))
|
||
body))))
|
||
(put 'with-keyword-args 'lisp-indent-hook 1)
|
||
|
||
|
||
;;; REDUCE
|
||
;;; It is here mostly as an example of how to use KLISTs.
|
||
;;;
|
||
;;; First of all, you need to declare the keywords (done elsewhere in this
|
||
;;; file):
|
||
;;; (defkeyword :from-end "syntax of sequence functions")
|
||
;;; (defkeyword :start "syntax of sequence functions")
|
||
;;; etc...
|
||
;;;
|
||
;;; Then, you capture all the possible keyword arguments with a &rest
|
||
;;; argument. You can pass that list downward again, of course, but
|
||
;;; internally you need to parse it into a KLIST (an alist, really). One uses
|
||
;;; (build-klist REST-ARGS ACCEPTABLE-KEYWORDS [ALLOW-OTHER]). You can then
|
||
;;; test for presence by using (keyword-argument-supplied-p KLIST KEY) and
|
||
;;; extract a value with (extract-from-klist KLIST KEY [DEFAULT]).
|
||
|
||
(defun reduce (function sequence &rest kargs)
|
||
"Apply FUNCTION (a function of two arguments) to successive pairs of elements
|
||
from SEQUENCE. Some keyword arguments are valid after FUNCTION and SEQUENCE:
|
||
:from-end If non-nil, process the values backwards
|
||
:initial-value If given, prefix it to the SEQUENCE. Suffix, if :from-end
|
||
:start Restrict reduction to the subsequence from this index
|
||
:end Restrict reduction to the subsequence BEFORE this index.
|
||
If the sequence is empty and no :initial-value is given, the FUNCTION is
|
||
called on zero (not two) arguments. Otherwise, if there is exactly one
|
||
element in the combination of SEQUENCE and the initial value, that element is
|
||
returned."
|
||
(let* ((klist (build-klist kargs '(:from-end :start :end :initial-value)))
|
||
(length (length sequence))
|
||
(from-end (extract-from-klist klist :from-end))
|
||
(initial-value-given (keyword-argument-supplied-p
|
||
klist :initial-value))
|
||
(start (extract-from-klist kargs :start 0))
|
||
(end (extract-from-klist kargs :end length)))
|
||
(setq sequence (cl$subseq-as-list sequence start end))
|
||
(if from-end
|
||
(setq sequence (reverse sequence)))
|
||
(if initial-value-given
|
||
(setq sequence (cons (extract-from-klist klist :initial-value)
|
||
sequence)))
|
||
(if (null sequence)
|
||
(funcall function) ;only use of 0 arguments
|
||
(let* ((result (car sequence))
|
||
(sequence (cdr sequence)))
|
||
(while sequence
|
||
(setq result (if from-end
|
||
(funcall function (car sequence) result)
|
||
(funcall function result (car sequence)))
|
||
sequence (cdr sequence)))
|
||
result))))
|
||
|
||
(defun cl$subseq-as-list (sequence start end)
|
||
"(cl$subseq-as-list SEQUENCE START END) => a list"
|
||
(let ((list (append sequence nil))
|
||
(length (length sequence))
|
||
result)
|
||
(if (< start 0)
|
||
(error "start should be >= 0, not %d" start))
|
||
(if (> end length)
|
||
(error "end should be <= %d, not %d" length end))
|
||
(if (and (zerop start) (= end length))
|
||
list
|
||
(let ((i start)
|
||
(vector (apply 'vector list)))
|
||
(while (/= i end)
|
||
(setq result (cons (elt vector i) result))
|
||
(setq i (+ i 1)))
|
||
(nreverse result)))))
|
||
|
||
;;;; end of cl-sequences.el
|
||
|
||
;;;; Some functions with keyword arguments
|
||
;;;;
|
||
;;;; Both list and sequence functions are considered here together. This
|
||
;;;; doesn't fit any more with the original split of functions in files.
|
||
|
||
(defun cl-member (item list &rest kargs)
|
||
"Look for ITEM in LIST; return first tail of LIST the car of whose first
|
||
cons cell tests the same as ITEM. Admits arguments :key, :test, and
|
||
:test-not."
|
||
(if (null kargs) ;treat this fast for efficiency
|
||
(memq item list)
|
||
(let* ((klist (build-klist kargs '(:test :test-not :key)))
|
||
(test (extract-from-klist klist :test))
|
||
(testnot (extract-from-klist klist :test-not))
|
||
(key (extract-from-klist klist :key 'identity)))
|
||
;; another workaround allegedly for speed, BLAH
|
||
(if (and (or (eq test 'eq) (eq test 'eql)
|
||
(eq test (symbol-function 'eq))
|
||
(eq test (symbol-function 'eql)))
|
||
(null testnot)
|
||
(or (eq key 'identity) ;either by default or so given
|
||
(eq key (function identity)) ;could this happen?
|
||
(eq key (symbol-function 'identity)) ;sheer paranoia
|
||
))
|
||
(memq item list)
|
||
(if (and test testnot)
|
||
(error ":test and :test-not both specified for member"))
|
||
(if (not (or test testnot))
|
||
(setq test 'eql))
|
||
;; final hack: remove the indirection through the function names
|
||
(if testnot
|
||
(if (symbolp testnot)
|
||
(setq testnot (symbol-function testnot)))
|
||
(if (symbolp test)
|
||
(setq test (symbol-function test))))
|
||
(if (symbolp key)
|
||
(setq key (symbol-function key)))
|
||
;; ok, go for it
|
||
(let ((ptr list)
|
||
(done nil)
|
||
(result '()))
|
||
(if testnot
|
||
(while (not (or done (endp ptr)))
|
||
(cond ((not (funcall testnot item (funcall key (car ptr))))
|
||
(setq done t)
|
||
(setq result ptr)))
|
||
(setq ptr (cdr ptr)))
|
||
(while (not (or done (endp ptr)))
|
||
(cond ((funcall test item (funcall key (car ptr)))
|
||
(setq done t)
|
||
(setq result ptr)))
|
||
(setq ptr (cdr ptr))))
|
||
result)))))
|
||
|
||
;;;; MULTIPLE VALUES
|
||
;;;; This package approximates the behavior of the multiple-values
|
||
;;;; forms of Common Lisp.
|
||
;;;;
|
||
;;;; Cesar Quiroz @ UofR DofCSc - Dec. 1986
|
||
;;;; (quiroz@cs.rochester.edu)
|
||
|
||
;;; Lisp indentation information
|
||
(put 'multiple-value-bind 'lisp-indent-hook 2)
|
||
(put 'multiple-value-setq 'lisp-indent-hook 2)
|
||
(put 'multiple-value-list 'lisp-indent-hook nil)
|
||
(put 'multiple-value-call 'lisp-indent-hook 1)
|
||
(put 'multiple-value-prog1 'lisp-indent-hook 1)
|
||
|
||
;;; Global state of the package is kept here
|
||
(defvar *mvalues-values* nil
|
||
"Most recently returned multiple-values")
|
||
(defvar *mvalues-count* nil
|
||
"Count of multiple-values returned, or nil if the mechanism was not used")
|
||
|
||
;;; values is the standard multiple-value-return form. Must be the
|
||
;;; last thing evaluated inside a function. If the caller is not
|
||
;;; expecting multiple values, only the first one is passed. (values)
|
||
;;; is the same as no-values returned (unaware callers see nil). The
|
||
;;; alternative (values-list <list>) is just a convenient shorthand
|
||
;;; and complements multiple-value-list.
|
||
|
||
(defun values (&rest val-forms)
|
||
"Produce multiple values (zero or more). Each arg is one value.
|
||
See also `multiple-value-bind', which is one way to examine the
|
||
multiple values produced by a form. If the containing form or caller
|
||
does not check specially to see multiple values, it will see only
|
||
the first value."
|
||
(setq *mvalues-values* val-forms)
|
||
(setq *mvalues-count* (length *mvalues-values*))
|
||
(car *mvalues-values*))
|
||
|
||
(defun values-list (&optional val-forms)
|
||
"Produce multiple values (zero or more). Each element of LIST is one value.
|
||
This is equivalent to (apply 'values LIST)."
|
||
(cond ((nlistp val-forms)
|
||
(error "Argument to values-list must be a list, not `%s'"
|
||
(prin1-to-string val-forms))))
|
||
(setq *mvalues-values* val-forms)
|
||
(setq *mvalues-count* (length *mvalues-values*))
|
||
(car *mvalues-values*))
|
||
|
||
;;; Callers that want to see the multiple values use these macros.
|
||
|
||
(defmacro multiple-value-list (form)
|
||
"Execute FORM and return a list of all the (multiple) values FORM produces.
|
||
See `values' and `multiple-value-bind'."
|
||
(list 'progn
|
||
(list 'setq '*mvalues-count* nil)
|
||
(list 'let (list (list 'it '(gensym)))
|
||
(list 'set 'it form)
|
||
(list 'if '*mvalues-count*
|
||
(list 'copy-sequence '*mvalues-values*)
|
||
(list 'progn
|
||
(list 'setq '*mvalues-count* 1)
|
||
(list 'setq '*mvalues-values*
|
||
(list 'list (list 'symbol-value 'it)))
|
||
(list 'copy-sequence '*mvalues-values*))))))
|
||
|
||
(defmacro multiple-value-call (function &rest args)
|
||
"Call FUNCTION on all the values produced by the remaining arguments.
|
||
(multiple-value-call '+ (values 1 2) (values 3 4)) is 10."
|
||
(let* ((result (gentemp))
|
||
(arg (gentemp)))
|
||
(list 'apply (list 'function (eval function))
|
||
(list 'let* (list (list result '()))
|
||
(list 'dolist (list arg (list 'quote args) result)
|
||
(list 'setq result
|
||
(list 'append
|
||
result
|
||
(list 'multiple-value-list
|
||
(list 'eval arg)))))))))
|
||
|
||
(defmacro multiple-value-bind (vars form &rest body)
|
||
"Bind VARS to the (multiple) values produced by FORM, then do BODY.
|
||
VARS is a list of variables; each is bound to one of FORM's values.
|
||
If FORM doesn't make enough values, the extra variables are bound to nil.
|
||
(Ordinary forms produce only one value; to produce more, use `values'.)
|
||
Extra values are ignored.
|
||
BODY (zero or more forms) is executed with the variables bound,
|
||
then the bindings are unwound."
|
||
(let* ((vals (gentemp)) ;name for intermediate values
|
||
(clauses (mv-bind-clausify ;convert into clauses usable
|
||
vars vals))) ; in a let form
|
||
(list* 'let*
|
||
(cons (list vals (list 'multiple-value-list form))
|
||
clauses)
|
||
body)))
|
||
|
||
(defmacro multiple-value-setq (vars form)
|
||
"Set VARS to the (multiple) values produced by FORM.
|
||
VARS is a list of variables; each is set to one of FORM's values.
|
||
If FORM doesn't make enough values, the extra variables are set to nil.
|
||
(Ordinary forms produce only one value; to produce more, use `values'.)
|
||
Extra values are ignored."
|
||
(let* ((vals (gentemp)) ;name for intermediate values
|
||
(clauses (mv-bind-clausify ;convert into clauses usable
|
||
vars vals))) ; in a setq (after append).
|
||
(list 'let*
|
||
(list (list vals (list 'multiple-value-list form)))
|
||
(cons 'setq (apply (function append) clauses)))))
|
||
|
||
(defmacro multiple-value-prog1 (form &rest body)
|
||
"Evaluate FORM, then BODY, then produce the same values FORM produced.
|
||
Thus, (multiple-value-prog1 (values 1 2) (foobar)) produces values 1 and 2.
|
||
This is like `prog1' except that `prog1' would produce only one value,
|
||
which would be the first of FORM's values."
|
||
(let* ((heldvalues (gentemp)))
|
||
(cons 'let*
|
||
(cons (list (list heldvalues (list 'multiple-value-list form)))
|
||
(append body (list (list 'values-list heldvalues)))))))
|
||
|
||
;;; utility functions
|
||
;;;
|
||
;;; mv-bind-clausify makes the pairs needed to have the variables in
|
||
;;; the variable list correspond with the values returned by the form.
|
||
;;; vals is a fresh symbol that intervenes in all the bindings.
|
||
|
||
(defun mv-bind-clausify (vars vals)
|
||
"MV-BIND-CLAUSIFY VARS VALS => Auxiliary list
|
||
Forms a list of pairs `(,(nth i vars) (nth i vals)) for i from 0 to
|
||
the length of VARS (a list of symbols). VALS is just a fresh symbol."
|
||
(if (or (nlistp vars)
|
||
(notevery 'symbolp vars))
|
||
(error "expected a list of symbols, not `%s'"
|
||
(prin1-to-string vars)))
|
||
(let* ((nvars (length vars))
|
||
(clauses '()))
|
||
(dotimes (n nvars clauses)
|
||
(setq clauses (cons (list (nth n vars)
|
||
(list 'nth n vals)) clauses)))))
|
||
|
||
;;;; end of cl-multiple-values.el
|
||
|
||
;;;; ARITH
|
||
;;;; This file provides integer arithmetic extensions. Although
|
||
;;;; Emacs Lisp doesn't really support anything but integers, that
|
||
;;;; has still to be made to look more or less standard.
|
||
;;;;
|
||
;;;;
|
||
;;;; Cesar Quiroz @ UofR DofCSc - Dec. 1986
|
||
;;;; (quiroz@cs.rochester.edu)
|
||
|
||
|
||
(defsubst plusp (number)
|
||
"True if NUMBER is strictly greater than zero."
|
||
(> number 0))
|
||
|
||
(defsubst minusp (number)
|
||
"True if NUMBER is strictly less than zero."
|
||
(< number 0))
|
||
|
||
(defsubst oddp (number)
|
||
"True if INTEGER is not divisible by 2."
|
||
(/= (% number 2) 0))
|
||
|
||
(defsubst evenp (number)
|
||
"True if INTEGER is divisible by 2."
|
||
(= (% number 2) 0))
|
||
|
||
(defsubst abs (number)
|
||
"Return the absolute value of NUMBER."
|
||
(if (< number 0)
|
||
(- number)
|
||
number))
|
||
|
||
(defsubst signum (number)
|
||
"Return -1, 0 or 1 according to the sign of NUMBER."
|
||
(cond ((< number 0)
|
||
-1)
|
||
((> number 0)
|
||
1)
|
||
(t ;exactly zero
|
||
0)))
|
||
|
||
(defun gcd (&rest integers)
|
||
"Return the greatest common divisor of all the arguments.
|
||
The arguments must be integers. With no arguments, value is zero."
|
||
(let ((howmany (length integers)))
|
||
(cond ((= howmany 0)
|
||
0)
|
||
((= howmany 1)
|
||
(abs (car integers)))
|
||
((> howmany 2)
|
||
(apply (function gcd)
|
||
(cons (gcd (nth 0 integers) (nth 1 integers))
|
||
(nthcdr 2 integers))))
|
||
(t ;howmany=2
|
||
;; essentially the euclidean algorithm
|
||
(when (zerop (* (nth 0 integers) (nth 1 integers)))
|
||
(error "a zero argument is invalid for `gcd'"))
|
||
(do* ((absa (abs (nth 0 integers))) ; better to operate only
|
||
(absb (abs (nth 1 integers))) ;on positives.
|
||
(dd (max absa absb)) ; setup correct order for the
|
||
(ds (min absa absb)) ;successive divisions.
|
||
;; intermediate results
|
||
(q 0)
|
||
(r 0)
|
||
;; final results
|
||
(done nil) ; flag: end of iterations
|
||
(result 0)) ; final value
|
||
(done result)
|
||
(setq q (/ dd ds))
|
||
(setq r (% dd ds))
|
||
(cond ((zerop r) (setq done t) (setq result ds))
|
||
(t (setq dd ds) (setq ds r))))))))
|
||
|
||
(defun lcm (integer &rest more)
|
||
"Return the least common multiple of all the arguments.
|
||
The arguments must be integers and there must be at least one of them."
|
||
(let ((howmany (length more))
|
||
(a integer)
|
||
(b (nth 0 more))
|
||
prod ; intermediate product
|
||
(yetmore (nthcdr 1 more)))
|
||
(cond ((zerop howmany)
|
||
(abs a))
|
||
((> howmany 1) ; recursive case
|
||
(apply (function lcm)
|
||
(cons (lcm a b) yetmore)))
|
||
(t ; base case, just 2 args
|
||
(setq prod (* a b))
|
||
(cond
|
||
((zerop prod)
|
||
0)
|
||
(t
|
||
(/ (abs prod) (gcd a b))))))))
|
||
|
||
(defun isqrt (number)
|
||
"Return the integer square root of NUMBER.
|
||
NUMBER must not be negative. Result is largest integer less than or
|
||
equal to the real square root of the argument."
|
||
;; The method used here is essentially the Newtonian iteration
|
||
;; x[n+1] <- (x[n] + Number/x[n]) / 2
|
||
;; suitably adapted to integer arithmetic.
|
||
;; Thanks to Philippe Schnoebelen <phs@lifia.imag.fr> for suggesting the
|
||
;; termination condition.
|
||
(cond ((minusp number)
|
||
(error "argument to `isqrt' (%d) must not be negative"
|
||
number))
|
||
((zerop number)
|
||
0)
|
||
(t ;so (>= number 0)
|
||
(do* ((approx 1) ;any positive integer will do
|
||
(new 0) ;init value irrelevant
|
||
(done nil))
|
||
(done (if (> (* approx approx) number)
|
||
(- approx 1)
|
||
approx))
|
||
(setq new (/ (+ approx (/ number approx)) 2)
|
||
done (or (= new approx) (= new (+ approx 1)))
|
||
approx new)))))
|
||
|
||
(defun cl-floor (number &optional divisor)
|
||
"Divide DIVIDEND by DIVISOR, rounding toward minus infinity.
|
||
DIVISOR defaults to 1. The remainder is produced as a second value."
|
||
(cond ((and (null divisor) ; trivial case
|
||
(numberp number))
|
||
(values number 0))
|
||
(t ; do the division
|
||
(multiple-value-bind
|
||
(q r s)
|
||
(safe-idiv number divisor)
|
||
(cond ((zerop s)
|
||
(values 0 0))
|
||
((plusp s)
|
||
(values q r))
|
||
(t ;opposite-signs case
|
||
(if (zerop r)
|
||
(values (- q) 0)
|
||
(let ((q (- (+ q 1))))
|
||
(values q (- number (* q divisor)))))))))))
|
||
|
||
(defun cl-ceiling (number &optional divisor)
|
||
"Divide DIVIDEND by DIVISOR, rounding toward plus infinity.
|
||
DIVISOR defaults to 1. The remainder is produced as a second value."
|
||
(cond ((and (null divisor) ; trivial case
|
||
(numberp number))
|
||
(values number 0))
|
||
(t ; do the division
|
||
(multiple-value-bind
|
||
(q r s)
|
||
(safe-idiv number divisor)
|
||
(cond ((zerop s)
|
||
(values 0 0))
|
||
((plusp s)
|
||
(values (+ q 1) (- r divisor)))
|
||
(t
|
||
(values (- q) (+ number (* q divisor)))))))))
|
||
|
||
(defun cl-truncate (number &optional divisor)
|
||
"Divide DIVIDEND by DIVISOR, rounding toward zero.
|
||
DIVISOR defaults to 1. The remainder is produced as a second value."
|
||
(cond ((and (null divisor) ; trivial case
|
||
(numberp number))
|
||
(values number 0))
|
||
(t ; do the division
|
||
(multiple-value-bind
|
||
(q r s)
|
||
(safe-idiv number divisor)
|
||
(cond ((zerop s)
|
||
(values 0 0))
|
||
((plusp s) ;same as floor
|
||
(values q r))
|
||
(t ;same as ceiling
|
||
(values (- q) (+ number (* q divisor)))))))))
|
||
|
||
(defun cl-round (number &optional divisor)
|
||
"Divide DIVIDEND by DIVISOR, rounding to nearest integer.
|
||
DIVISOR defaults to 1. The remainder is produced as a second value."
|
||
(cond ((and (null divisor) ; trivial case
|
||
(numberp number))
|
||
(values number 0))
|
||
(t ; do the division
|
||
(multiple-value-bind
|
||
(q r s)
|
||
(safe-idiv number divisor)
|
||
(setq r (abs r))
|
||
;; adjust magnitudes first, and then signs
|
||
(let ((other-r (- (abs divisor) r)))
|
||
(cond ((> r other-r)
|
||
(setq q (+ q 1)))
|
||
((and (= r other-r)
|
||
(oddp q))
|
||
;; round to even is mandatory
|
||
(setq q (+ q 1))))
|
||
(setq q (* s q))
|
||
(setq r (- number (* q divisor)))
|
||
(values q r))))))
|
||
|
||
;;; These two functions access the implementation-dependent representation of
|
||
;;; the multiple value returns.
|
||
|
||
(defun cl-mod (number divisor)
|
||
"Return remainder of X by Y (rounding quotient toward minus infinity).
|
||
That is, the remainder goes with the quotient produced by `cl-floor'.
|
||
Emacs Lisp hint:
|
||
If you know that both arguments are positive, use `%' instead for speed."
|
||
(cl-floor number divisor)
|
||
(cadr *mvalues-values*))
|
||
|
||
(defun rem (number divisor)
|
||
"Return remainder of X by Y (rounding quotient toward zero).
|
||
That is, the remainder goes with the quotient produced by `cl-truncate'.
|
||
Emacs Lisp hint:
|
||
If you know that both arguments are positive, use `%' instead for speed."
|
||
(cl-truncate number divisor)
|
||
(cadr *mvalues-values*))
|
||
|
||
;;; internal utilities
|
||
;;;
|
||
;;; safe-idiv performs an integer division with positive numbers only.
|
||
;;; It is known that some machines/compilers implement weird remainder
|
||
;;; computations when working with negatives, so the idea here is to
|
||
;;; make sure we know what is coming back to the caller in all cases.
|
||
|
||
;;; Signum computation fixed by mad@math.keio.JUNET (MAEDA Atusi)
|
||
|
||
(defun safe-idiv (a b)
|
||
"SAFE-IDIV A B => Q R S
|
||
Q=|A|/|B|, S is the sign of A/B, R is the rest A - S*Q*B."
|
||
;; (unless (and (numberp a) (numberp b))
|
||
;; (error "arguments to `safe-idiv' must be numbers"))
|
||
;; (when (zerop b)
|
||
;; (error "cannot divide %d by zero" a))
|
||
(let* ((q (/ (abs a) (abs b)))
|
||
(s (* (signum a) (signum b)))
|
||
(r (- a (* s q b))))
|
||
(values q r s)))
|
||
|
||
;;;; end of cl-arith.el
|
||
|
||
;;;; SETF
|
||
;;;; This file provides the setf macro and friends. The purpose has
|
||
;;;; been modest, only the simplest defsetf forms are accepted.
|
||
;;;; Use it and enjoy.
|
||
;;;;
|
||
;;;; Cesar Quiroz @ UofR DofCSc - Dec. 1986
|
||
;;;; (quiroz@cs.rochester.edu)
|
||
|
||
|
||
(defkeyword :setf-update-fn
|
||
"Property, its value is the function setf must invoke to update a
|
||
generalized variable whose access form is a function call of the
|
||
symbol that has this property.")
|
||
|
||
(defkeyword :setf-update-doc
|
||
"Property of symbols that have a `defsetf' update function on them,
|
||
installed by the `defsetf' from its optional third argument.")
|
||
|
||
(defmacro setf (&rest pairs)
|
||
"Generalized `setq' that can set things other than variable values.
|
||
A use of `setf' looks like (setf {PLACE VALUE}...).
|
||
The behavior of (setf PLACE VALUE) is to access the generalized variable
|
||
at PLACE and store VALUE there. It returns VALUE. If there is more
|
||
than one PLACE and VALUE, each PLACE is set from its VALUE before
|
||
the next PLACE is evaluated."
|
||
(let ((nforms (length pairs)))
|
||
;; check the number of subforms
|
||
(cond ((/= (% nforms 2) 0)
|
||
(error "odd number of arguments to `setf'"))
|
||
((= nforms 0)
|
||
nil)
|
||
((> nforms 2)
|
||
;; this is the recursive case
|
||
(cons 'progn
|
||
(do* ;collect the place-value pairs
|
||
((args pairs (cddr args))
|
||
(place (car args) (car args))
|
||
(value (cadr args) (cadr args))
|
||
(result '()))
|
||
((endp args) (nreverse result))
|
||
(setq result
|
||
(cons (list 'setf place value)
|
||
result)))))
|
||
(t ;i.e., nforms=2
|
||
;; this is the base case (SETF PLACE VALUE)
|
||
(let* ((place (car pairs))
|
||
(value (cadr pairs))
|
||
(head nil)
|
||
(updatefn nil))
|
||
;; dispatch on the type of the PLACE
|
||
(cond ((symbolp place)
|
||
(list 'setq place value))
|
||
((and (listp place)
|
||
(setq head (car place))
|
||
(symbolp head)
|
||
(setq updatefn (get head :setf-update-fn)))
|
||
;; dispatch on the type of update function
|
||
(cond ((and (consp updatefn) (eq (car updatefn) 'lambda))
|
||
(cons 'funcall
|
||
(cons (list 'function updatefn)
|
||
(append (cdr place) (list value)))))
|
||
((and (symbolp updatefn)
|
||
(fboundp updatefn)
|
||
(let ((defn (symbol-function updatefn)))
|
||
(or (subrp defn)
|
||
(and (consp defn)
|
||
(or (eq (car defn) 'lambda)
|
||
(eq (car defn) 'macro))))))
|
||
(cons updatefn (append (cdr place) (list value))))
|
||
(t
|
||
(multiple-value-bind
|
||
(bindings newsyms)
|
||
(pair-with-newsyms
|
||
(append (cdr place) (list value)))
|
||
;; this let gets new symbols to ensure adequate
|
||
;; order of evaluation of the subforms.
|
||
(list 'let
|
||
bindings
|
||
(cons updatefn newsyms))))))
|
||
(t
|
||
(error "no `setf' update-function for `%s'"
|
||
(prin1-to-string place)))))))))
|
||
|
||
(defmacro defsetf (accessfn updatefn &optional docstring)
|
||
"Define how `setf' works on a certain kind of generalized variable.
|
||
A use of `defsetf' looks like (defsetf ACCESSFN UPDATEFN [DOCSTRING]).
|
||
ACCESSFN is a symbol. UPDATEFN is a function or macro which takes
|
||
one more argument than ACCESSFN does. DEFSETF defines the translation
|
||
of (SETF (ACCESFN . ARGS) NEWVAL) to be a form like (UPDATEFN ARGS... NEWVAL).
|
||
The function UPDATEFN must return its last arg, after performing the
|
||
updating called for."
|
||
;; reject ill-formed requests. too bad one can't test for functionp
|
||
;; or macrop.
|
||
(when (not (symbolp accessfn))
|
||
(error "first argument of `defsetf' must be a symbol, not `%s'"
|
||
(prin1-to-string accessfn)))
|
||
;; update properties
|
||
(list 'progn
|
||
(list 'eval-and-compile
|
||
(list 'put (list 'quote accessfn)
|
||
:setf-update-fn (list 'function updatefn)))
|
||
(list 'put (list 'quote accessfn) :setf-update-doc docstring)
|
||
;; any better thing to return?
|
||
(list 'quote accessfn)))
|
||
|
||
;;; This section provides the "default" setfs for Common-Emacs-Lisp
|
||
;;; The user will not normally add anything to this, although
|
||
;;; defstruct will introduce new ones as a matter of fact.
|
||
;;;
|
||
;;; Apply is a special case. The Common Lisp
|
||
;;; standard makes the case of apply be useful when the user writes
|
||
;;; something like (apply #'name ...), Emacs Lisp doesn't have the #
|
||
;;; stuff, but it has (function ...). Notice that V18 includes a new
|
||
;;; apply: this file is compatible with V18 and pre-V18 Emacses.
|
||
|
||
;;; INCOMPATIBILITY: the SETF macro evaluates its arguments in the
|
||
;;; (correct) left to right sequence *before* checking for apply
|
||
;;; methods (which should really be an special case inside setf). Due
|
||
;;; to this, the lambda expression defsetf'd to apply will succeed in
|
||
;;; applying the right function even if the name was not quoted, but
|
||
;;; computed! That extension is not Common Lisp (nor is particularly
|
||
;;; useful, I think).
|
||
|
||
(defsetf apply
|
||
(lambda (&rest args)
|
||
;; disassemble the calling form
|
||
;; "(((quote fn) x1 x2 ... xn) val)" (function instead of quote, too)
|
||
(let* ((fnform (car args)) ;functional form
|
||
(applyargs (append ;arguments "to apply fnform"
|
||
(apply 'list* (butlast (cdr args)))
|
||
(last args)))
|
||
(newupdater nil)) ; its update-fn, if any
|
||
(if (and (symbolp fnform)
|
||
(setq newupdater (get fnform :setf-update-fn)))
|
||
(apply newupdater applyargs)
|
||
(error "can't `setf' to `%s'"
|
||
(prin1-to-string fnform)))))
|
||
"`apply' is a special case for `setf'")
|
||
|
||
|
||
(defsetf aref
|
||
aset
|
||
"`setf' inversion for `aref'")
|
||
|
||
(defsetf nth
|
||
setnth
|
||
"`setf' inversion for `nth'")
|
||
|
||
(defsetf nthcdr
|
||
setnthcdr
|
||
"`setf' inversion for `nthcdr'")
|
||
|
||
(defsetf elt
|
||
setelt
|
||
"`setf' inversion for `elt'")
|
||
|
||
(defsetf first
|
||
(lambda (list val) (setnth 0 list val))
|
||
"`setf' inversion for `first'")
|
||
|
||
(defsetf second
|
||
(lambda (list val) (setnth 1 list val))
|
||
"`setf' inversion for `second'")
|
||
|
||
(defsetf third
|
||
(lambda (list val) (setnth 2 list val))
|
||
"`setf' inversion for `third'")
|
||
|
||
(defsetf fourth
|
||
(lambda (list val) (setnth 3 list val))
|
||
"`setf' inversion for `fourth'")
|
||
|
||
(defsetf fifth
|
||
(lambda (list val) (setnth 4 list val))
|
||
"`setf' inversion for `fifth'")
|
||
|
||
(defsetf sixth
|
||
(lambda (list val) (setnth 5 list val))
|
||
"`setf' inversion for `sixth'")
|
||
|
||
(defsetf seventh
|
||
(lambda (list val) (setnth 6 list val))
|
||
"`setf' inversion for `seventh'")
|
||
|
||
(defsetf eighth
|
||
(lambda (list val) (setnth 7 list val))
|
||
"`setf' inversion for `eighth'")
|
||
|
||
(defsetf ninth
|
||
(lambda (list val) (setnth 8 list val))
|
||
"`setf' inversion for `ninth'")
|
||
|
||
(defsetf tenth
|
||
(lambda (list val) (setnth 9 list val))
|
||
"`setf' inversion for `tenth'")
|
||
|
||
(defsetf rest
|
||
(lambda (list val) (setcdr list val))
|
||
"`setf' inversion for `rest'")
|
||
|
||
(defsetf car setcar "Replace the car of a cons")
|
||
|
||
(defsetf cdr setcdr "Replace the cdr of a cons")
|
||
|
||
(defsetf caar
|
||
(lambda (list val) (setcar (nth 0 list) val))
|
||
"`setf' inversion for `caar'")
|
||
|
||
(defsetf cadr
|
||
(lambda (list val) (setcar (cdr list) val))
|
||
"`setf' inversion for `cadr'")
|
||
|
||
(defsetf cdar
|
||
(lambda (list val) (setcdr (car list) val))
|
||
"`setf' inversion for `cdar'")
|
||
|
||
(defsetf cddr
|
||
(lambda (list val) (setcdr (cdr list) val))
|
||
"`setf' inversion for `cddr'")
|
||
|
||
(defsetf caaar
|
||
(lambda (list val) (setcar (caar list) val))
|
||
"`setf' inversion for `caaar'")
|
||
|
||
(defsetf caadr
|
||
(lambda (list val) (setcar (cadr list) val))
|
||
"`setf' inversion for `caadr'")
|
||
|
||
(defsetf cadar
|
||
(lambda (list val) (setcar (cdar list) val))
|
||
"`setf' inversion for `cadar'")
|
||
|
||
(defsetf cdaar
|
||
(lambda (list val) (setcdr (caar list) val))
|
||
"`setf' inversion for `cdaar'")
|
||
|
||
(defsetf caddr
|
||
(lambda (list val) (setcar (cddr list) val))
|
||
"`setf' inversion for `caddr'")
|
||
|
||
(defsetf cdadr
|
||
(lambda (list val) (setcdr (cadr list) val))
|
||
"`setf' inversion for `cdadr'")
|
||
|
||
(defsetf cddar
|
||
(lambda (list val) (setcdr (cdar list) val))
|
||
"`setf' inversion for `cddar'")
|
||
|
||
(defsetf cdddr
|
||
(lambda (list val) (setcdr (cddr list) val))
|
||
"`setf' inversion for `cdddr'")
|
||
|
||
(defsetf caaaar
|
||
(lambda (list val) (setcar (caaar list) val))
|
||
"`setf' inversion for `caaaar'")
|
||
|
||
(defsetf caaadr
|
||
(lambda (list val) (setcar (caadr list) val))
|
||
"`setf' inversion for `caaadr'")
|
||
|
||
(defsetf caadar
|
||
(lambda (list val) (setcar (cadar list) val))
|
||
"`setf' inversion for `caadar'")
|
||
|
||
(defsetf cadaar
|
||
(lambda (list val) (setcar (cdaar list) val))
|
||
"`setf' inversion for `cadaar'")
|
||
|
||
(defsetf cdaaar
|
||
(lambda (list val) (setcdr (caar list) val))
|
||
"`setf' inversion for `cdaaar'")
|
||
|
||
(defsetf caaddr
|
||
(lambda (list val) (setcar (caddr list) val))
|
||
"`setf' inversion for `caaddr'")
|
||
|
||
(defsetf cadadr
|
||
(lambda (list val) (setcar (cdadr list) val))
|
||
"`setf' inversion for `cadadr'")
|
||
|
||
(defsetf cdaadr
|
||
(lambda (list val) (setcdr (caadr list) val))
|
||
"`setf' inversion for `cdaadr'")
|
||
|
||
(defsetf caddar
|
||
(lambda (list val) (setcar (cddar list) val))
|
||
"`setf' inversion for `caddar'")
|
||
|
||
(defsetf cdadar
|
||
(lambda (list val) (setcdr (cadar list) val))
|
||
"`setf' inversion for `cdadar'")
|
||
|
||
(defsetf cddaar
|
||
(lambda (list val) (setcdr (cdaar list) val))
|
||
"`setf' inversion for `cddaar'")
|
||
|
||
(defsetf cadddr
|
||
(lambda (list val) (setcar (cdddr list) val))
|
||
"`setf' inversion for `cadddr'")
|
||
|
||
(defsetf cddadr
|
||
(lambda (list val) (setcdr (cdadr list) val))
|
||
"`setf' inversion for `cddadr'")
|
||
|
||
(defsetf cdaddr
|
||
(lambda (list val) (setcdr (caddr list) val))
|
||
"`setf' inversion for `cdaddr'")
|
||
|
||
(defsetf cdddar
|
||
(lambda (list val) (setcdr (cddar list) val))
|
||
"`setf' inversion for `cdddar'")
|
||
|
||
(defsetf cddddr
|
||
(lambda (list val) (setcdr (cddr list) val))
|
||
"`setf' inversion for `cddddr'")
|
||
|
||
(defsetf get put "`setf' inversion for `get' is `put'")
|
||
|
||
(defsetf symbol-function fset
|
||
"`setf' inversion for `symbol-function' is `fset'")
|
||
|
||
(defsetf symbol-plist setplist
|
||
"`setf' inversion for `symbol-plist' is `setplist'")
|
||
|
||
(defsetf symbol-value set
|
||
"`setf' inversion for `symbol-value' is `set'")
|
||
|
||
(defsetf point goto-char
|
||
"To set (point) to N, use (goto-char N)")
|
||
|
||
;; how about defsetfing other Emacs forms?
|
||
|
||
;;; Modify macros
|
||
;;;
|
||
;;; It could be nice to implement define-modify-macro, but I don't
|
||
;;; think it really pays.
|
||
|
||
(defmacro incf (ref &optional delta)
|
||
"(incf REF [DELTA]) -> increment the g.v. REF by DELTA (default 1)"
|
||
(if (null delta)
|
||
(setq delta 1))
|
||
(list 'setf ref (list '+ ref delta)))
|
||
|
||
(defmacro decf (ref &optional delta)
|
||
"(decf REF [DELTA]) -> decrement the g.v. REF by DELTA (default 1)"
|
||
(if (null delta)
|
||
(setq delta 1))
|
||
(list 'setf ref (list '- ref delta)))
|
||
|
||
(defmacro push (item ref)
|
||
"(push ITEM REF) -> cons ITEM at the head of the g.v. REF (a list)"
|
||
(list 'setf ref (list 'cons item ref)))
|
||
|
||
(defmacro pushnew (item ref)
|
||
"(pushnew ITEM REF): adjoin ITEM at the head of the g.v. REF (a list)"
|
||
(list 'setf ref (list 'adjoin item ref)))
|
||
|
||
(defmacro pop (ref)
|
||
"(pop REF) -> (prog1 (car REF) (setf REF (cdr REF)))"
|
||
(let ((listname (gensym)))
|
||
(list 'let (list (list listname ref))
|
||
(list 'prog1
|
||
(list 'car listname)
|
||
(list 'setf ref (list 'cdr listname))))))
|
||
|
||
;;; PSETF
|
||
;;;
|
||
;;; Psetf is the generalized variable equivalent of psetq. The right
|
||
;;; hand sides are evaluated and assigned (via setf) to the left hand
|
||
;;; sides. The evaluations are done in an environment where they
|
||
;;; appear to occur in parallel.
|
||
|
||
(defmacro psetf (&rest body)
|
||
"(psetf {var value }...) => nil
|
||
Like setf, but all the values are computed before any assignment is made."
|
||
(let ((length (length body)))
|
||
(cond ((/= (% length 2) 0)
|
||
(error "psetf needs an even number of arguments, %d given"
|
||
length))
|
||
((null body)
|
||
'())
|
||
(t
|
||
(list 'prog1 nil
|
||
(let ((setfs '())
|
||
(bodyforms (reverse body)))
|
||
(while bodyforms
|
||
(let* ((value (car bodyforms))
|
||
(place (cadr bodyforms)))
|
||
(setq bodyforms (cddr bodyforms))
|
||
(if (null setfs)
|
||
(setq setfs (list 'setf place value))
|
||
(setq setfs (list 'setf place
|
||
(list 'prog1 value
|
||
setfs))))))
|
||
setfs))))))
|
||
|
||
;;; SHIFTF and ROTATEF
|
||
;;;
|
||
|
||
(defmacro shiftf (&rest forms)
|
||
"(shiftf PLACE1 PLACE2... NEWVALUE)
|
||
Set PLACE1 to PLACE2, PLACE2 to PLACE3...
|
||
Each PLACE is set to the old value of the following PLACE,
|
||
and the last PLACE is set to the value NEWVALUE.
|
||
Returns the old value of PLACE1."
|
||
(unless (> (length forms) 1)
|
||
(error "`shiftf' needs more than one argument"))
|
||
(let ((places (butlast forms))
|
||
(newvalue (car (last forms))))
|
||
;; the places are accessed to fresh symbols
|
||
(multiple-value-bind
|
||
(bindings newsyms)
|
||
(pair-with-newsyms places)
|
||
(list 'let bindings
|
||
(cons 'setf
|
||
(zip-lists places
|
||
(append (cdr newsyms) (list newvalue))))
|
||
(car newsyms)))))
|
||
|
||
(defmacro rotatef (&rest places)
|
||
"(rotatef PLACE...) sets each PLACE to the old value of the following PLACE.
|
||
The last PLACE is set to the old value of the first PLACE.
|
||
Thus, the values rotate through the PLACEs. Returns nil."
|
||
(if (null places)
|
||
nil
|
||
(multiple-value-bind
|
||
(bindings newsyms)
|
||
(pair-with-newsyms places)
|
||
(list
|
||
'let bindings
|
||
(cons 'setf
|
||
(zip-lists places
|
||
(append (cdr newsyms) (list (car newsyms)))))
|
||
nil))))
|
||
|
||
;;; GETF, REMF, and REMPROP
|
||
;;;
|
||
|
||
(defun getf (place indicator &optional default)
|
||
"Return PLACE's PROPNAME property, or DEFAULT if not present."
|
||
(while (and place (not (eq (car place) indicator)))
|
||
(setq place (cdr (cdr place))))
|
||
(if place
|
||
(car (cdr place))
|
||
default))
|
||
|
||
(defmacro getf$setf$method (place indicator default &rest newval)
|
||
"SETF method for GETF. Not for public use."
|
||
(case (length newval)
|
||
(0 (setq newval default default nil))
|
||
(1 (setq newval (car newval)))
|
||
(t (error "Wrong number of arguments to (setf (getf ...)) form")))
|
||
(let ((psym (gentemp)) (isym (gentemp)) (vsym (gentemp)))
|
||
(list 'let (list (list psym place)
|
||
(list isym indicator)
|
||
(list vsym newval))
|
||
(list 'while
|
||
(list 'and psym
|
||
(list 'not
|
||
(list 'eq (list 'car psym) isym)))
|
||
(list 'setq psym (list 'cdr (list 'cdr psym))))
|
||
(list 'if psym
|
||
(list 'setcar (list 'cdr psym) vsym)
|
||
(list 'setf place
|
||
(list 'nconc place (list 'list isym newval))))
|
||
vsym)))
|
||
|
||
(defsetf getf
|
||
getf$setf$method)
|
||
|
||
(defmacro remf (place indicator)
|
||
"Remove from the property list at PLACE its PROPNAME property.
|
||
Returns non-nil if and only if the property existed."
|
||
(let ((psym (gentemp)) (isym (gentemp)))
|
||
(list 'let (list (list psym place) (list isym indicator))
|
||
(list 'cond
|
||
(list (list 'eq isym (list 'car psym))
|
||
(list 'setf place (list 'cdr (list 'cdr psym)))
|
||
t)
|
||
(list t
|
||
(list 'setq psym (list 'cdr psym))
|
||
(list 'while
|
||
(list 'and (list 'cdr psym)
|
||
(list 'not
|
||
(list 'eq (list 'car (list 'cdr psym))
|
||
isym)))
|
||
(list 'setq psym (list 'cdr (list 'cdr psym))))
|
||
(list 'cond
|
||
(list (list 'cdr psym)
|
||
(list 'setcdr psym
|
||
(list 'cdr
|
||
(list 'cdr (list 'cdr psym))))
|
||
t)))))))
|
||
|
||
(defun remprop (symbol indicator)
|
||
"Remove SYMBOL's PROPNAME property, returning non-nil if it was present."
|
||
(remf (symbol-plist symbol) indicator))
|
||
|
||
|
||
;;;; STRUCTS
|
||
;;;; This file provides the structures mechanism. See the
|
||
;;;; documentation for Common-Lisp's defstruct. Mine doesn't
|
||
;;;; implement all the functionality of the standard, although some
|
||
;;;; more could be grafted if so desired. More details along with
|
||
;;;; the code.
|
||
;;;;
|
||
;;;;
|
||
;;;; Cesar Quiroz @ UofR DofCSc - Dec. 1986
|
||
;;;; (quiroz@cs.rochester.edu)
|
||
|
||
|
||
(defkeyword :include "Syntax of `defstruct'")
|
||
(defkeyword :named "Syntax of `defstruct'")
|
||
(defkeyword :conc-name "Syntax of `defstruct'")
|
||
(defkeyword :copier "Syntax of `defstruct'")
|
||
(defkeyword :predicate "Syntax of `defstruct'")
|
||
(defkeyword :print-function "Syntax of `defstruct'")
|
||
(defkeyword :type "Syntax of `defstruct'")
|
||
(defkeyword :initial-offset "Syntax of `defstruct'")
|
||
|
||
(defkeyword :structure-doc "Documentation string for a structure.")
|
||
(defkeyword :structure-slotsn "Number of slots in structure")
|
||
(defkeyword :structure-slots "List of the slot's names")
|
||
(defkeyword :structure-indices "List of (KEYWORD-NAME . INDEX)")
|
||
(defkeyword :structure-initforms "List of (KEYWORD-NAME . INITFORM)")
|
||
(defkeyword :structure-includes
|
||
"() or list of a symbol, that this struct includes")
|
||
(defkeyword :structure-included-in
|
||
"List of the structs that include this")
|
||
|
||
|
||
(defmacro defstruct (&rest args)
|
||
"(defstruct NAME [DOC-STRING] . SLOTS) define NAME as structure type.
|
||
NAME must be a symbol, the name of the new structure. It could also
|
||
be a list (NAME . OPTIONS).
|
||
|
||
Each option is either a symbol, or a list of a keyword symbol taken from the
|
||
list \{:conc-name, :copier, :constructor, :predicate, :include,
|
||
:print-function, :type, :initial-offset\}. The meanings of these are as in
|
||
CLtL, except that no BOA-constructors are provided, and the options
|
||
\{:print-function, :type, :initial-offset\} are ignored quietly. All these
|
||
structs are named, in the sense that their names can be used for type
|
||
discrimination.
|
||
|
||
The DOC-STRING is established as the `structure-doc' property of NAME.
|
||
|
||
The SLOTS are one or more of the following:
|
||
SYMBOL -- meaning the SYMBOL is the name of a SLOT of NAME
|
||
list of SYMBOL and VALUE -- meaning that VALUE is the initial value of
|
||
the slot.
|
||
`defstruct' defines functions `make-NAME', `NAME-p', `copy-NAME' for the
|
||
structure, and functions with the same name as the slots to access
|
||
them. `setf' of the accessors sets their values."
|
||
(multiple-value-bind
|
||
(name options docstring slotsn slots initlist)
|
||
(parse$defstruct$args args)
|
||
;; Names for the member functions come from the options. The
|
||
;; slots* stuff collects info about the slots declared explicitly.
|
||
(multiple-value-bind
|
||
(conc-name constructor copier predicate
|
||
moreslotsn moreslots moreinits included)
|
||
(parse$defstruct$options name options slots)
|
||
;; The moreslots* stuff refers to slots gained as a consequence
|
||
;; of (:include clauses). -- Oct 89: Only one :include tolerated
|
||
(when (and (numberp moreslotsn)
|
||
(> moreslotsn 0))
|
||
(setf slotsn (+ slotsn moreslotsn))
|
||
(setf slots (append moreslots slots))
|
||
(setf initlist (append moreinits initlist)))
|
||
(unless (> slotsn 0)
|
||
(error "%s needs at least one slot"
|
||
(prin1-to-string name)))
|
||
(let ((dups (duplicate-symbols-p slots)))
|
||
(when dups
|
||
(error "`%s' are duplicates"
|
||
(prin1-to-string dups))))
|
||
(setq initlist (simplify$inits slots initlist))
|
||
(let (properties functions keywords accessors alterators returned)
|
||
;; compute properties of NAME
|
||
(setq properties
|
||
(append
|
||
(list
|
||
(list 'put (list 'quote name) :structure-doc
|
||
docstring)
|
||
(list 'put (list 'quote name) :structure-slotsn
|
||
slotsn)
|
||
(list 'put (list 'quote name) :structure-slots
|
||
(list 'quote slots))
|
||
(list 'put (list 'quote name) :structure-initforms
|
||
(list 'quote initlist))
|
||
(list 'put (list 'quote name) :structure-indices
|
||
(list 'quote (extract$indices initlist))))
|
||
;; If this definition :includes another defstruct,
|
||
;; modify both property lists.
|
||
(cond (included
|
||
(list
|
||
(list 'put
|
||
(list 'quote name)
|
||
:structure-includes
|
||
(list 'quote included))
|
||
(list 'pushnew
|
||
(list 'quote name)
|
||
(list 'get (list 'quote (car included))
|
||
:structure-included-in))))
|
||
(t
|
||
(list
|
||
(let ((old (gensym)))
|
||
(list 'let
|
||
(list (list old
|
||
(list 'car
|
||
(list 'get
|
||
(list 'quote name)
|
||
:structure-includes))))
|
||
(list 'when old
|
||
(list 'put
|
||
old
|
||
:structure-included-in
|
||
(list 'delq
|
||
(list 'quote name)
|
||
;; careful with destructive
|
||
;;manipulation!
|
||
(list
|
||
'append
|
||
(list
|
||
'get
|
||
old
|
||
:structure-included-in)
|
||
'())
|
||
)))))
|
||
(list 'put
|
||
(list 'quote name)
|
||
:structure-includes
|
||
'()))))
|
||
;; If this definition used to be :included in another, warn
|
||
;; that things make break. On the other hand, the redefinition
|
||
;; may be trivial, so don't call it an error.
|
||
(let ((old (gensym)))
|
||
(list
|
||
(list 'let
|
||
(list (list old (list 'get
|
||
(list 'quote name)
|
||
:structure-included-in)))
|
||
(list 'when old
|
||
(list 'message
|
||
"`%s' redefined. Should redefine `%s'?"
|
||
(list 'quote name)
|
||
(list 'prin1-to-string old))))))))
|
||
|
||
;; Compute functions associated with NAME. This is not
|
||
;; handling BOA constructors yet, but here would be the place.
|
||
(setq functions
|
||
(list
|
||
(list 'fset (list 'quote constructor)
|
||
(list 'function
|
||
(list 'lambda (list '&rest 'args)
|
||
(list 'make$structure$instance
|
||
(list 'quote name)
|
||
'args))))
|
||
(list 'fset (list 'quote copier)
|
||
(list 'function 'copy-sequence))
|
||
(let ((typetag (gensym)))
|
||
(list 'fset (list 'quote predicate)
|
||
(list
|
||
'function
|
||
(list
|
||
'lambda (list 'thing)
|
||
(list 'and
|
||
(list 'vectorp 'thing)
|
||
(list 'let
|
||
(list (list typetag
|
||
(list 'elt 'thing 0)))
|
||
(list 'or
|
||
(list
|
||
'and
|
||
(list 'eq
|
||
typetag
|
||
(list 'quote name))
|
||
(list '=
|
||
(list 'length 'thing)
|
||
(1+ slotsn)))
|
||
(list
|
||
'memq
|
||
typetag
|
||
(list 'get
|
||
(list 'quote name)
|
||
:structure-included-in))))))
|
||
)))))
|
||
;; compute accessors for NAME's slots
|
||
(multiple-value-setq
|
||
(accessors alterators keywords)
|
||
(build$accessors$for name conc-name predicate slots slotsn))
|
||
;; generate returned value -- not defined by the standard
|
||
(setq returned
|
||
(list
|
||
(cons 'vector
|
||
(mapcar
|
||
(function (lambda (x) (list 'quote x)))
|
||
(cons name slots)))))
|
||
;; generate code
|
||
(cons 'progn
|
||
(nconc properties functions keywords
|
||
accessors alterators returned))))))
|
||
|
||
(defun parse$defstruct$args (args)
|
||
"(parse$defstruct$args ARGS) => NAME OPTIONS DOCSTRING SLOTSN SLOTS INITLIST
|
||
NAME=symbol, OPTIONS=list of, DOCSTRING=string, SLOTSN=count of slots,
|
||
SLOTS=list of their names, INITLIST=alist (keyword . initform)."
|
||
(let (name ;args=(symbol...) or ((symbol...)...)
|
||
options ;args=((symbol . options) ...)
|
||
(docstring "") ;args=(head docstring . slotargs)
|
||
slotargs ;second or third cdr of args
|
||
(slotsn 0) ;number of slots
|
||
(slots '()) ;list of slot names
|
||
(initlist '())) ;list of (slot keyword . initform)
|
||
;; extract name and options
|
||
(cond ((symbolp (car args)) ;simple name
|
||
(setq name (car args)
|
||
options '()))
|
||
((and (listp (car args)) ;(name . options)
|
||
(symbolp (caar args)))
|
||
(setq name (caar args)
|
||
options (cdar args)))
|
||
(t
|
||
(error "first arg to `defstruct' must be symbol or (symbol ...)")))
|
||
(setq slotargs (cdr args))
|
||
;; is there a docstring?
|
||
(when (stringp (car slotargs))
|
||
(setq docstring (car slotargs)
|
||
slotargs (cdr slotargs)))
|
||
;; now for the slots
|
||
(multiple-value-bind
|
||
(slotsn slots initlist)
|
||
(process$slots slotargs)
|
||
(values name options docstring slotsn slots initlist))))
|
||
|
||
(defun process$slots (slots)
|
||
"(process$slots SLOTS) => SLOTSN SLOTSLIST INITLIST
|
||
Converts a list of symbols or lists of symbol and form into the last 3
|
||
values returned by PARSE$DEFSTRUCT$ARGS."
|
||
(let ((slotsn (length slots)) ;number of slots
|
||
slotslist ;(slot1 slot2 ...)
|
||
initlist) ;((:slot1 . init1) ...)
|
||
(do*
|
||
((ptr slots (cdr ptr))
|
||
(this (car ptr) (car ptr)))
|
||
((endp ptr))
|
||
(cond ((symbolp this)
|
||
(setq slotslist (cons this slotslist))
|
||
(setq initlist (acons (keyword-of this) nil initlist)))
|
||
((and (listp this)
|
||
(symbolp (car this)))
|
||
(let ((name (car this))
|
||
(form (cadr this)))
|
||
;; this silently ignores any slot options. bad...
|
||
(setq slotslist (cons name slotslist))
|
||
(setq initlist (acons (keyword-of name) form initlist))))
|
||
(t
|
||
(error "slot should be symbol or (symbol ...), not `%s'"
|
||
(prin1-to-string this)))))
|
||
(values slotsn (nreverse slotslist) (nreverse initlist))))
|
||
|
||
(defun parse$defstruct$options (name options slots)
|
||
"(parse$defstruct$options name OPTIONS SLOTS) => many values
|
||
A defstruct named NAME, with options list OPTIONS, has already slots SLOTS.
|
||
Parse the OPTIONS and return the updated form of the struct's slots and other
|
||
information. The values returned are:
|
||
|
||
CONC-NAME is the string to use as prefix/suffix in the methods,
|
||
CONST is the name of the official constructor,
|
||
COPIER is the name of the structure copier,
|
||
PRED is the name of the type predicate,
|
||
MORESLOTSN is the number of slots added by :include,
|
||
MORESLOTS is the list of slots added by :include,
|
||
MOREINITS is the list of initialization forms added by :include,
|
||
INCLUDED is nil, or the list of the symbol added by :include"
|
||
(let* ((namestring (symbol-name name))
|
||
;; to build the return values
|
||
(conc-name (concat namestring "-"))
|
||
(const (intern (concat "make-" namestring)))
|
||
(copier (intern (concat "copy-" namestring)))
|
||
(pred (intern (concat namestring "-p")))
|
||
(moreslotsn 0)
|
||
(moreslots '())
|
||
(moreinits '())
|
||
;; auxiliaries
|
||
option-head ;When an option is not a plain
|
||
option-second ; keyword, it must be a list of
|
||
option-rest ; the form (head second . rest)
|
||
these-slotsn ;When :include is found, the
|
||
these-slots ; info about the included
|
||
these-inits ; structure is added here.
|
||
included ;NIL or (list INCLUDED)
|
||
)
|
||
;; Values above are the defaults. Now we read the options themselves
|
||
(dolist (option options)
|
||
;; 2 cases arise, as options must be a keyword or a list
|
||
(cond
|
||
((keywordp option)
|
||
(case option
|
||
(:named
|
||
) ;ignore silently
|
||
(t
|
||
(error "can't recognize option `%s'"
|
||
(prin1-to-string option)))))
|
||
((and (listp option)
|
||
(keywordp (setq option-head (car option))))
|
||
(setq option-second (second option))
|
||
(setq option-rest (nthcdr 2 option))
|
||
(case option-head
|
||
(:conc-name
|
||
(setq conc-name
|
||
(cond
|
||
((stringp option-second)
|
||
option-second)
|
||
((null option-second)
|
||
"")
|
||
(t
|
||
(error "`%s' is invalid as `conc-name'"
|
||
(prin1-to-string option-second))))))
|
||
(:copier
|
||
(setq copier
|
||
(cond
|
||
((and (symbolp option-second)
|
||
(null option-rest))
|
||
option-second)
|
||
(t
|
||
(error "can't recognize option `%s'"
|
||
(prin1-to-string option))))))
|
||
|
||
(:constructor ;no BOA-constructors allowed
|
||
(setq const
|
||
(cond
|
||
((and (symbolp option-second)
|
||
(null option-rest))
|
||
option-second)
|
||
(t
|
||
(error "can't recognize option `%s'"
|
||
(prin1-to-string option))))))
|
||
(:predicate
|
||
(setq pred
|
||
(cond
|
||
((and (symbolp option-second)
|
||
(null option-rest))
|
||
option-second)
|
||
(t
|
||
(error "can't recognize option `%s'"
|
||
(prin1-to-string option))))))
|
||
(:include
|
||
(unless (symbolp option-second)
|
||
(error "arg to `:include' should be a symbol, not `%s'"
|
||
(prin1-to-string option-second)))
|
||
(setq these-slotsn (get option-second :structure-slotsn)
|
||
these-slots (get option-second :structure-slots)
|
||
these-inits (get option-second :structure-initforms))
|
||
(unless (and (numberp these-slotsn)
|
||
(> these-slotsn 0))
|
||
(error "`%s' is not a valid structure"
|
||
(prin1-to-string option-second)))
|
||
(if included
|
||
(error "`%s' already includes `%s', can't include `%s' too"
|
||
name (car included) option-second)
|
||
(push option-second included))
|
||
(multiple-value-bind
|
||
(xtra-slotsn xtra-slots xtra-inits)
|
||
(process$slots option-rest)
|
||
(when (> xtra-slotsn 0)
|
||
(dolist (xslot xtra-slots)
|
||
(unless (memq xslot these-slots)
|
||
(error "`%s' is not a slot of `%s'"
|
||
(prin1-to-string xslot)
|
||
(prin1-to-string option-second))))
|
||
(setq these-inits (append xtra-inits these-inits)))
|
||
(setq moreslotsn (+ moreslotsn these-slotsn))
|
||
(setq moreslots (append these-slots moreslots))
|
||
(setq moreinits (append these-inits moreinits))))
|
||
((:print-function :type :initial-offset)
|
||
) ;ignore silently
|
||
(t
|
||
(error "can't recognize option `%s'"
|
||
(prin1-to-string option)))))
|
||
(t
|
||
(error "can't recognize option `%s'"
|
||
(prin1-to-string option)))))
|
||
;; Return values found
|
||
(values conc-name const copier pred
|
||
moreslotsn moreslots moreinits
|
||
included)))
|
||
|
||
(defun simplify$inits (slots initlist)
|
||
"(simplify$inits SLOTS INITLIST) => new INITLIST
|
||
Removes from INITLIST - an ALIST - any shadowed bindings."
|
||
(let ((result '()) ;built here
|
||
key ;from the slot
|
||
)
|
||
(dolist (slot slots)
|
||
(setq key (keyword-of slot))
|
||
(setq result (acons key (cdr (assoc key initlist)) result)))
|
||
(nreverse result)))
|
||
|
||
(defun extract$indices (initlist)
|
||
"(extract$indices INITLIST) => indices list
|
||
Kludge. From a list of pairs (keyword . form) build a list of pairs
|
||
of the form (keyword . position in list from 0). Useful to precompute
|
||
some of the work of MAKE$STRUCTURE$INSTANCE."
|
||
(let ((result '())
|
||
(index 0))
|
||
(dolist (entry initlist (nreverse result))
|
||
(setq result (acons (car entry) index result)
|
||
index (+ index 1)))))
|
||
|
||
(defun build$accessors$for (name conc-name predicate slots slotsn)
|
||
"(build$accessors$for NAME PREDICATE SLOTS SLOTSN) => FSETS DEFSETFS KWDS
|
||
Generate the code for accesors and defsetfs of a structure called
|
||
NAME, whose slots are SLOTS. Also, establishes the keywords for the
|
||
slots names."
|
||
(do ((i 0 (1+ i))
|
||
(accessors '())
|
||
(alterators '())
|
||
(keywords '())
|
||
(canonic "")) ;slot name with conc-name prepended
|
||
((>= i slotsn)
|
||
(values
|
||
(nreverse accessors) (nreverse alterators) (nreverse keywords)))
|
||
(setq canonic (intern (concat conc-name (symbol-name (nth i slots)))))
|
||
(setq accessors
|
||
(cons
|
||
(list 'fset (list 'quote canonic)
|
||
(list 'function
|
||
(list 'lambda (list 'object)
|
||
(list 'cond
|
||
(list (list predicate 'object)
|
||
(list 'aref 'object (1+ i)))
|
||
(list 't
|
||
(list 'error
|
||
"`%s' is not a struct %s"
|
||
(list 'prin1-to-string
|
||
'object)
|
||
(list 'prin1-to-string
|
||
(list 'quote
|
||
name))))))))
|
||
accessors))
|
||
(setq alterators
|
||
(cons
|
||
(list 'defsetf canonic
|
||
(list 'lambda (list 'object 'newval)
|
||
(list 'cond
|
||
(list (list predicate 'object)
|
||
(list 'aset 'object (1+ i) 'newval))
|
||
(list 't
|
||
(list 'error
|
||
"`%s' not a `%s'"
|
||
(list 'prin1-to-string
|
||
'object)
|
||
(list 'prin1-to-string
|
||
(list 'quote
|
||
name)))))))
|
||
alterators))
|
||
(setq keywords
|
||
(cons (list 'defkeyword (keyword-of (nth i slots)))
|
||
keywords))))
|
||
|
||
(defun make$structure$instance (name args)
|
||
"(make$structure$instance NAME ARGS) => new struct NAME
|
||
A struct of type NAME is created, some slots might be initialized
|
||
according to ARGS (the &rest argument of MAKE-name)."
|
||
(unless (symbolp name)
|
||
(error "`%s' is not a possible name for a structure"
|
||
(prin1-to-string name)))
|
||
(let ((initforms (get name :structure-initforms))
|
||
(slotsn (get name :structure-slotsn))
|
||
(indices (get name :structure-indices))
|
||
initalist ;pairlis'd on initforms
|
||
initializers ;definitive initializers
|
||
)
|
||
;; check sanity of the request
|
||
(unless (and (numberp slotsn)
|
||
(> slotsn 0))
|
||
(error "`%s' is not a defined structure"
|
||
(prin1-to-string name)))
|
||
(unless (evenp (length args))
|
||
(error "slot initializers `%s' not of even length"
|
||
(prin1-to-string args)))
|
||
;; analyze the initializers provided by the call
|
||
(multiple-value-bind
|
||
(speckwds specvals) ;keywords and values given
|
||
(unzip-list args) ; by the user
|
||
;; check that all the arguments are introduced by keywords
|
||
(unless (every (function keywordp) speckwds)
|
||
(error "all of the names in `%s' should be keywords"
|
||
(prin1-to-string speckwds)))
|
||
;; check that all the keywords are known
|
||
(dolist (kwd speckwds)
|
||
(unless (numberp (cdr (assoc kwd indices)))
|
||
(error "`%s' is not a valid slot name for %s"
|
||
(prin1-to-string kwd) (prin1-to-string name))))
|
||
;; update initforms
|
||
(setq initalist
|
||
(pairlis speckwds
|
||
(do* ;;protect values from further evaluation
|
||
((ptr specvals (cdr ptr))
|
||
(val (car ptr) (car ptr))
|
||
(result '()))
|
||
((endp ptr) (nreverse result))
|
||
(setq result
|
||
(cons (list 'quote val)
|
||
result)))
|
||
(copy-sequence initforms)))
|
||
;; compute definitive initializers
|
||
(setq initializers
|
||
(do* ;;gather the values of the most definitive forms
|
||
((ptr indices (cdr ptr))
|
||
(key (caar ptr) (caar ptr))
|
||
(result '()))
|
||
((endp ptr) (nreverse result))
|
||
(setq result
|
||
(cons (eval (cdr (assoc key initalist))) result))))
|
||
;; do real initialization
|
||
(apply (function vector)
|
||
(cons name initializers)))))
|
||
|
||
;;;; end of cl-structs.el
|
||
|
||
;;; For lisp-interaction mode, so that multiple values can be seen when passed
|
||
;;; back. Lies every now and then...
|
||
|
||
(defvar - nil "form currently under evaluation")
|
||
(defvar + nil "previous -")
|
||
(defvar ++ nil "previous +")
|
||
(defvar +++ nil "previous ++")
|
||
(defvar / nil "list of values returned by +")
|
||
(defvar // nil "list of values returned by ++")
|
||
(defvar /// nil "list of values returned by +++")
|
||
(defvar * nil "(first) value of +")
|
||
(defvar ** nil "(first) value of ++")
|
||
(defvar *** nil "(first) value of +++")
|
||
|
||
(defun cl-eval-print-last-sexp ()
|
||
"Evaluate sexp before point; print value\(s\) into current buffer.
|
||
If the evaled form returns multiple values, they are shown one to a line.
|
||
The variables -, +, ++, +++, *, **, ***, /, //, /// have their usual meaning.
|
||
|
||
It clears the multiple-value passing mechanism, and does not pass back
|
||
multiple values. Use this only if you are debugging cl.el and understand well
|
||
how the multiple-value stuff works, because it can be fooled into believing
|
||
that multiple values have been returned when they actually haven't, for
|
||
instance
|
||
\(identity \(values nil 1\)\)
|
||
However, even when this fails, you can trust the first printed value to be
|
||
\(one of\) the returned value\(s\)."
|
||
(interactive)
|
||
;; top level call, can reset mvalues
|
||
(setq *mvalues-count* nil
|
||
*mvalues-values* nil)
|
||
(setq - (car (read-from-string
|
||
(buffer-substring
|
||
(let ((stab (syntax-table)))
|
||
(unwind-protect
|
||
(save-excursion
|
||
(set-syntax-table emacs-lisp-mode-syntax-table)
|
||
(forward-sexp -1)
|
||
(point))
|
||
(set-syntax-table stab)))
|
||
(point)))))
|
||
(setq *** **
|
||
** *
|
||
* (eval -))
|
||
(setq /// //
|
||
// /
|
||
/ *mvalues-values*)
|
||
(setq +++ ++
|
||
++ +
|
||
+ -)
|
||
(cond ((or (null *mvalues-count*) ;mvalues mechanism not used
|
||
(not (eq * (car *mvalues-values*))))
|
||
(print * (current-buffer)))
|
||
((null /) ;no values returned
|
||
(terpri (current-buffer)))
|
||
(t ;more than zero mvalues
|
||
(terpri (current-buffer))
|
||
(mapcar (function (lambda (value)
|
||
(prin1 value (current-buffer))
|
||
(terpri (current-buffer))))
|
||
/)))
|
||
(setq *mvalues-count* nil ;make sure
|
||
*mvalues-values* nil))
|
||
|
||
;;;; More LISTS functions
|
||
;;;;
|
||
|
||
;;; Some mapping functions on lists, commonly useful.
|
||
;;; They take no extra sequences, to go along with Emacs Lisp's MAPCAR.
|
||
|
||
(defun mapc (function list)
|
||
"(MAPC FUNCTION LIST) => LIST
|
||
Apply FUNCTION to each element of LIST, return LIST.
|
||
Like mapcar, but called only for effect."
|
||
(let ((args list))
|
||
(while args
|
||
(funcall function (car args))
|
||
(setq args (cdr args))))
|
||
list)
|
||
|
||
(defun maplist (function list)
|
||
"(MAPLIST FUNCTION LIST) => list'ed results of FUNCTION on cdrs of LIST
|
||
Apply FUNCTION to successive sublists of LIST, return the list of the results"
|
||
(let ((args list)
|
||
results '())
|
||
(while args
|
||
(setq results (cons (funcall function args) results)
|
||
args (cdr args)))
|
||
(nreverse results)))
|
||
|
||
(defun mapl (function list)
|
||
"(MAPL FUNCTION LIST) => LIST
|
||
Apply FUNCTION to successive cdrs of LIST, return LIST.
|
||
Like maplist, but called only for effect."
|
||
(let ((args list))
|
||
(while args
|
||
(funcall function args)
|
||
(setq args (cdr args)))
|
||
list))
|
||
|
||
(defun mapcan (function list)
|
||
"(MAPCAN FUNCTION LIST) => nconc'd results of FUNCTION on LIST
|
||
Apply FUNCTION to each element of LIST, nconc the results.
|
||
Beware: nconc destroys its first argument! See copy-list."
|
||
(let ((args list)
|
||
(results '()))
|
||
(while args
|
||
(setq results (nconc (funcall function (car args)) results)
|
||
args (cdr args)))
|
||
(nreverse results)))
|
||
|
||
(defun mapcon (function list)
|
||
"(MAPCON FUNCTION LIST) => nconc'd results of FUNCTION on cdrs of LIST
|
||
Apply FUNCTION to successive sublists of LIST, nconc the results.
|
||
Beware: nconc destroys its first argument! See copy-list."
|
||
(let ((args list)
|
||
(results '()))
|
||
(while args
|
||
(setq results (nconc (funcall function args) results)
|
||
args (cdr args)))
|
||
(nreverse results)))
|
||
|
||
;;; Copiers
|
||
|
||
(defsubst copy-list (list)
|
||
"Build a copy of LIST"
|
||
(append list '()))
|
||
|
||
(defun copy-tree (tree)
|
||
"Build a copy of the tree of conses TREE
|
||
The argument is a tree of conses, it is recursively copied down to
|
||
non conses. Circularity and sharing of substructure are not
|
||
necessarily preserved."
|
||
(if (consp tree)
|
||
(cons (copy-tree (car tree))
|
||
(copy-tree (cdr tree)))
|
||
tree))
|
||
|
||
;;; reversals, and destructive manipulations of a list's spine
|
||
|
||
(defun revappend (x y)
|
||
"does what (append (reverse X) Y) would, only faster"
|
||
(if (endp x)
|
||
y
|
||
(revappend (cdr x) (cons (car x) y))))
|
||
|
||
(defun nreconc (x y)
|
||
"does (nconc (nreverse X) Y) would, only faster
|
||
Destructive on X, be careful."
|
||
(if (endp x)
|
||
y
|
||
;; reuse the first cons of x, making it point to y
|
||
(nreconc (cdr x) (prog1 x (rplacd x y)))))
|
||
|
||
(defun nbutlast (list &optional n)
|
||
"Side-effected LIST truncated N+1 conses from the end.
|
||
This is the destructive version of BUTLAST. Returns () and does not
|
||
modify the LIST argument if the length of the list is not at least N."
|
||
(when (null n) (setf n 1))
|
||
(let ((length (list-length list)))
|
||
(cond ((null length)
|
||
list)
|
||
((< length n)
|
||
'())
|
||
(t
|
||
(setnthcdr (- length n) list nil)
|
||
list))))
|
||
|
||
;;; Substitutions
|
||
|
||
(defun subst (new old tree)
|
||
"NEW replaces OLD in a copy of TREE
|
||
Uses eql for the test."
|
||
(subst-if new (function (lambda (x) (eql x old))) tree))
|
||
|
||
(defun subst-if-not (new test tree)
|
||
"NEW replaces any subtree or leaf that fails TEST in a copy of TREE"
|
||
;; (subst-if new (function (lambda (x) (not (funcall test x)))) tree)
|
||
(cond ((not (funcall test tree))
|
||
new)
|
||
((atom tree)
|
||
tree)
|
||
(t ;no match so far
|
||
(let ((head (subst-if-not new test (car tree)))
|
||
(tail (subst-if-not new test (cdr tree))))
|
||
;; If nothing changed, return originals. Else use the new
|
||
;; components to assemble a new tree.
|
||
(if (and (eql head (car tree))
|
||
(eql tail (cdr tree)))
|
||
tree
|
||
(cons head tail))))))
|
||
|
||
(defun subst-if (new test tree)
|
||
"NEW replaces any subtree or leaf that satisfies TEST in a copy of TREE"
|
||
(cond ((funcall test tree)
|
||
new)
|
||
((atom tree)
|
||
tree)
|
||
(t ;no match so far
|
||
(let ((head (subst-if new test (car tree)))
|
||
(tail (subst-if new test (cdr tree))))
|
||
;; If nothing changed, return originals. Else use the new
|
||
;; components to assemble a new tree.
|
||
(if (and (eql head (car tree))
|
||
(eql tail (cdr tree)))
|
||
tree
|
||
(cons head tail))))))
|
||
|
||
(defun sublis (alist tree)
|
||
"Use association list ALIST to modify a copy of TREE
|
||
If a subtree or leaf of TREE is a key in ALIST, it is replaced by the
|
||
associated value. Not exactly Common Lisp, but close in spirit and
|
||
compatible with the native Emacs Lisp ASSOC, which uses EQUAL."
|
||
(let ((toplevel (assoc tree alist)))
|
||
(cond (toplevel ;Bingo at top
|
||
(cdr toplevel))
|
||
((atom tree) ;Give up on this
|
||
tree)
|
||
(t
|
||
(let ((head (sublis alist (car tree)))
|
||
(tail (sublis alist (cdr tree))))
|
||
(if (and (eql head (car tree))
|
||
(eql tail (cdr tree)))
|
||
tree
|
||
(cons head tail)))))))
|
||
|
||
(defun member-if (predicate list)
|
||
"PREDICATE is applied to the members of LIST. As soon as one of them
|
||
returns true, that tail of the list if returned. Else NIL."
|
||
(catch 'found-member-if
|
||
(while (not (endp list))
|
||
(if (funcall predicate (car list))
|
||
(throw 'found-member-if list)
|
||
(setq list (cdr list))))
|
||
nil))
|
||
|
||
(defun member-if-not (predicate list)
|
||
"PREDICATE is applied to the members of LIST. As soon as one of them
|
||
returns false, that tail of the list if returned. Else NIL."
|
||
(catch 'found-member-if-not
|
||
(while (not (endp list))
|
||
(if (funcall predicate (car list))
|
||
(setq list (cdr list))
|
||
(throw 'found-member-if-not list)))
|
||
nil))
|
||
|
||
(defun tailp (sublist list)
|
||
"(tailp SUBLIST LIST) => True if SUBLIST is a sublist of LIST."
|
||
(catch 'tailp-found
|
||
(while (not (endp list))
|
||
(if (eq sublist list)
|
||
(throw 'tailp-found t)
|
||
(setq list (cdr list))))
|
||
nil))
|
||
|
||
;;; Suggestion of phr%widow.Berkeley.EDU@lilac.berkeley.edu
|
||
|
||
(defmacro declare (&rest decls)
|
||
"Ignore a Common-Lisp declaration."
|
||
"declarations are ignored in this implementation")
|
||
|
||
(defun proclaim (&rest decls)
|
||
"Ignore a Common-Lisp proclamation."
|
||
"declarations are ignored in this implementation")
|
||
|
||
(defmacro the (type form)
|
||
"(the TYPE FORM) macroexpands to FORM
|
||
No checking is even attempted. This is just for compatibility with
|
||
Common-Lisp codes."
|
||
form)
|
||
|
||
;;; Due to Aaron Larson (alarson@src.honeywell.com, 26 Jul 91)
|
||
(put 'progv 'common-lisp-indent-hook '(4 4 &body))
|
||
(defmacro progv (vars vals &rest body)
|
||
"progv vars vals &body forms
|
||
bind vars to vals then execute forms.
|
||
If there are more vars than vals, the extra vars are unbound, if
|
||
there are more vals than vars, the extra vals are just ignored."
|
||
(` (progv$runtime (, vars) (, vals) (function (lambda () (,@ body))))))
|
||
|
||
;;; To do this efficiently, it really needs to be a special form...
|
||
(defun progv$runtime (vars vals body)
|
||
(eval (let ((vars-n-vals nil)
|
||
(unbind-forms nil))
|
||
(do ((r vars (cdr r))
|
||
(l vals (cdr l)))
|
||
((endp r))
|
||
(push (list (car r) (list 'quote (car l))) vars-n-vals)
|
||
(if (null l)
|
||
(push (` (makunbound '(, (car r)))) unbind-forms)))
|
||
(` (let (, vars-n-vals) (,@ unbind-forms) (funcall '(, body)))))))
|
||
|
||
(provide 'cl)
|
||
|
||
;;;; end of cl.el
|