Synch with Oort Gnus.
[elisp/gnus.git-] / lisp / gnus-clfns.el
1 ;;; gnus-clfns.el --- compiler macros for emulating cl functions
2 ;; Copyright (C) 2000, 2001 Free Software Foundation, Inc.
3
4 ;; Author: Kastsumi Yamaoka <yamaoka@jpl.org>
5 ;; Keywords: cl, compile
6
7 ;; This file is part of GNU Emacs.
8
9 ;; GNU Emacs is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
12 ;; any later version.
13
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
21 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
23
24 ;;; Commentary:
25
26 ;; This module is for mainly avoiding cl runtime functions in FSF
27 ;; Emacsen.  Function should also be defined as an ordinary function
28 ;; if it will not be provided in cl.
29
30 ;;; Code:
31
32 (if (featurep 'xemacs)
33     nil
34   (eval-when-compile (require 'cl))
35   (require 'pym)
36
37   (define-compiler-macro butlast (&whole form x &optional n)
38     (if (and (fboundp 'butlast)
39              (subrp (symbol-function 'butlast)))
40         form
41       (if n
42           `(let ((x ,x)
43                  (n ,n))
44              (if (and n (<= n 0))
45                  x
46                (let ((m (length x)))
47                  (or n (setq n 1))
48                  (and (< n m)
49                       (progn
50                         (if (> n 0) (setcdr (nthcdr (- (1- m) n) x) nil))
51                         x)))))
52         `(let* ((x ,x)
53                 (m (length x)))
54            (and (< 1 m)
55                 (progn
56                   (setcdr (nthcdr (- m 2) x) nil)
57                   x))))))
58
59   (define-compiler-macro coerce (&whole form x type)
60     (if (and (fboundp 'coerce)
61              (subrp (symbol-function 'coerce)))
62         form
63       `(let ((x ,x)
64              (type ,type))
65          (cond ((eq type 'list) (if (listp x) x (append x nil)))
66                ((eq type 'vector) (if (vectorp x) x (vconcat x)))
67                ((eq type 'string) (if (stringp x) x (concat x)))
68                ((eq type 'array) (if (arrayp x) x (vconcat x)))
69                ((and (eq type 'character) (stringp x) (= (length x) 1))
70                 (aref x 0))
71                ((and (eq type 'character) (symbolp x)
72                      (= (length (symbol-name x)) 1))
73                 (aref (symbol-name x) 0))
74                ((eq type 'float) (float x))
75                ((typep x type) x)
76                (t (error "Can't coerce %s to type %s" x type))))))
77
78   (define-compiler-macro copy-list (&whole form list)
79     (if (and (fboundp 'copy-list)
80              (subrp (symbol-function 'copy-list)))
81         form
82       `(let ((list ,list))
83          (if (consp list)
84              (let ((res nil))
85                (while (consp list) (push (pop list) res))
86                (prog1 (nreverse res) (setcdr res list)))
87            (car list)))))
88
89   (define-compiler-macro last (&whole form x &optional n)
90     (if (and (fboundp 'last)
91              (subrp (symbol-function 'last)))
92         form
93       (if n
94           `(let* ((x ,x)
95                   (n ,n)
96                   (m 0)
97                   (p x))
98              (while (consp p)
99                (incf m)
100                (pop p))
101              (if (<= n 0)
102                  p
103                (if (< n m)
104                    (nthcdr (- m n) x)
105                  x)))
106         `(let ((x ,x))
107            (while (consp (cdr x))
108              (pop x))
109            x))))
110
111   (define-compiler-macro merge (&whole form type seq1 seq2 pred &rest keys)
112     (if (and (fboundp 'merge)
113              (subrp (symbol-function 'merge)))
114         form
115       `(let ((type ,type)
116              (seq1 ,seq1)
117              (seq2 ,seq2)
118              (pred ,pred))
119          (or (listp seq1) (setq seq1 (append seq1 nil)))
120          (or (listp seq2) (setq seq2 (append seq2 nil)))
121          (let ((res nil))
122            (while (and seq1 seq2)
123              (if (funcall pred (car seq2) (car seq1))
124                  (push (pop seq2) res)
125                (push (pop seq1) res)))
126            (coerce (nconc (nreverse res) seq1 seq2) type)))))
127
128   (define-compiler-macro string (&whole form &rest args)
129     (if (and (fboundp 'string)
130              (subrp (symbol-function 'string)))
131         form
132       (list 'concat (cons 'list args))))
133
134   (defun-maybe string (&rest args)
135     "Concatenate all the argument characters and make the result a string."
136     (concat args))
137
138   (define-compiler-macro subseq (&whole form seq start &optional end)
139     (if (and (fboundp 'subseq)
140              (subrp (symbol-function 'subseq)))
141         form
142       (if end
143           `(let ((seq ,seq)
144                  (start ,start)
145                  (end ,end))
146              (if (stringp seq)
147                  (substring seq start end)
148                (let (len)
149                  (if (< end 0)
150                      (setq end (+ end (setq len (length seq)))))
151                  (if (< start 0)
152                      (setq start (+ start (or len (setq len (length seq))))))
153                  (cond ((listp seq)
154                         (if (> start 0)
155                             (setq seq (nthcdr start seq)))
156                         (let ((res nil))
157                           (while (>= (setq end (1- end)) start)
158                             (push (pop seq) res))
159                           (nreverse res)))
160                        (t
161                         (let ((res (make-vector (max (- end start) 0) nil))
162                               (i 0))
163                           (while (< start end)
164                             (aset res i (aref seq start))
165                             (setq i (1+ i)
166                                   start (1+ start)))
167                           res))))))
168         `(let ((seq ,seq)
169                (start ,start))
170            (if (stringp seq)
171                (substring seq start)
172              (let (len)
173                (if (< start 0)
174                    (setq start (+ start (or len (setq len (length seq))))))
175                (cond ((listp seq)
176                       (if (> start 0)
177                           (setq seq (nthcdr start seq)))
178                       (copy-sequence seq))
179                      (t
180                       (let* ((end (or len (length seq)))
181                              (res (make-vector (max (- end start) 0) nil))
182                              (i 0))
183                         (while (< start end)
184                           (aset res i (aref seq start))
185                           (setq i (1+ i)
186                                 start (1+ start)))
187                         res)))))))))
188   )
189
190 ;; A tool for the developers.
191
192 (defvar cl-run-time-functions
193   '(Values
194     Values-list acons assoc-if assoc-if-not build-klist butlast ceiling*
195     coerce common-lisp-indent-function compiler-macroexpand concatenate
196     copy-list count count-if count-if-not delete* delete-duplicates delete-if
197     delete-if-not duplicate-symbols-p elt-satisfies-test-p equalp evenp every
198     extract-from-klist fill find find-if find-if-not floatp-safe floor* gcd
199     gensym gentemp get-setf-method getf hash-table-count hash-table-p
200     intersection isqrt keyword-argument-supplied-p keyword-of keywordp last
201     lcm ldiff lisp-indent-259 lisp-indent-do lisp-indent-function-lambda-hack
202     lisp-indent-report-bad-format lisp-indent-tagbody list-length
203     make-hash-table make-random-state map mapc mapcan mapcar* mapcon mapl
204     maplist member-if member-if-not merge mismatch mod* nbutlast nintersection
205     notany notevery nreconc nset-difference nset-exclusive-or nsublis nsubst
206     nsubst-if nsubst-if-not nsubstitute nsubstitute-if nsubstitute-if-not
207     nunion oddp pair-with-newsyms pairlis position position-if position-if-not
208     proclaim random* random-state-p rassoc* rassoc-if rassoc-if-not
209     reassemble-argslists reduce rem* remove remove* remove-duplicates
210     remove-if remove-if-not remq replace revappend round* safe-idiv search
211     set-difference set-exclusive-or setelt setnth setnthcdr signum some sort*
212     stable-sort sublis subseq subsetp subst subst-if subst-if-not substitute
213     substitute-if substitute-if-not tailp tree-equal truncate* union
214     unzip-lists zip-lists)
215   "A list of CL run-time functions.  Some functions were built-in, nowadays.")
216
217 ;;;###autoload
218 (defun find-cl-run-time-functions (file-or-directory arg)
219   "Find CL run-time functions in the FILE-OR-DIRECTORY.  You can alter
220 the behavior of this command with the prefix ARG as described below.
221
222 By default, it searches for all the CL run-time functions listed in
223  the variable `cl-run-time-functions'.
224 With 1 or 3 \\[universal-argument]'s, the built-in functions in this Emacs\
225  will not be
226  reported.
227 With 2 or 3 \\[universal-argument]'s, just the symbols will also be reported.
228
229 You can use the `digit-argument' 1, 2 or 3 instead of\
230  \\[universal-argument]'s."
231   (interactive (list (read-file-name "Find CL run-time functions in: "
232                                      nil default-directory t)
233                      current-prefix-arg))
234   (unless (interactive-p)
235     (error "You should invoke `M-x find-cl-run-time-functions' interactively"))
236   (let ((report-symbols (member arg '((16) (64) 2 3)))
237         files clfns working file lines form forms fns fn newform buffer
238         window scroll
239         buffer-file-format format-alist
240         insert-file-contents-post-hook insert-file-contents-pre-hook)
241     (cond ((file-directory-p file-or-directory)
242            (setq files (directory-files file-or-directory t "\\.el$"))
243            (dolist (file files)
244              (unless (file-exists-p file)
245                (setq files (delete file files))))
246            (unless files
247              (message "No files found in: %s" file-or-directory))
248            files)
249           ((file-exists-p file-or-directory)
250            (setq files (list file-or-directory)))
251           (t
252            (message "No such file or directory: %s" file-or-directory)))
253     (when files
254       (if (member arg '((4) (64) 1 3))
255           (dolist (fn cl-run-time-functions)
256             (unless (and (fboundp fn)
257                          (subrp (symbol-function fn)))
258               (push fn clfns)))
259         (setq clfns cl-run-time-functions))
260       (set-buffer (setq working
261                         (get-buffer-create
262                          " *Searching for CL run-time functions*")))
263       (let (emacs-lisp-mode-hook)
264         (emacs-lisp-mode))
265       (while files
266         (setq file (pop files)
267               lines (list nil nil))
268         (message "Searching for CL run-time functions in: %s..."
269                  (file-name-nondirectory file))
270         (insert-file-contents file nil nil nil t)
271         ;; XEmacs moves point to the beginning of the buffer after
272         ;; inserting a file, FSFmacs doesn't so if the fifth argument
273         ;; of `insert-file-contents' is specified.
274         (goto-char (point-min))
275         ;;
276         (while (progn
277                  (while (and (looking-at "[\t\v\f\r ]*\\(;.*\\)?$")
278                              (zerop (forward-line 1))))
279                  (not (eobp)))
280           (setcar lines (if (bolp)
281                             (1+ (count-lines (point-min) (point)))
282                           (count-lines (point-min) (point))))
283           (when (consp;; Ignore stand-alone symbols, strings, etc.
284                  (setq form (condition-case nil
285                                 (read working)
286                               (error nil))))
287             (setcdr lines (list (count-lines (point-min) (point))))
288             (setq forms (list form)
289                   fns nil)
290             (while forms
291               (setq form (pop forms))
292               (when (consp form)
293                 (setq fn (pop form))
294                 (cond ((memq fn '(apply mapatoms mapcar mapconcat
295                                         mapextent symbol-function))
296                        (if (consp (car form))
297                            (when (memq (caar form) '(\` backquote quote))
298                              (setcar form (cdar form)))
299                          (setq form (cdr form))))
300                       ((memq fn '(\` backquote quote))
301                        (if report-symbols
302                            (progn
303                              (setq form (car form)
304                                    newform nil)
305                              (while form
306                                (push (list (or (car-safe form) form))
307                                      newform)
308                                (setq form (cdr-safe form)))
309                              (setq form (nreverse newform)))
310                          (setq form nil)))
311                       ((memq fn '(defadvice
312                                    defmacro defsubst defun
313                                    defmacro-maybe defmacro-maybe-cond
314                                    defsubst-maybe defun-maybe
315                                    defun-maybe-cond))
316                        (setq form (cddr form)))
317                       ((memq fn '(defalias lambda fset))
318                        (setq form (cdr form)))
319                       ((eq fn 'define-compiler-macro)
320                        (setq form nil))
321                       ((eq fn 'dolist)
322                        (setcar form (cadar form)))
323                       ((memq fn '(let let*))
324                        (setq form
325                              (append
326                               (delq nil
327                                     (mapcar
328                                      (lambda (element)
329                                        (when (and (consp element)
330                                                   (consp (cadr element)))
331                                          (cadr element)))
332                                      (car form)))
333                               (cdr form))))
334                       ((eq fn 'sort)
335                        (when (and (consp (cadr form))
336                                   (memq (caadr form) '(\` backquote quote)))
337                          (setcdr form (list (cdadr form)))))
338                       ((and (memq fn clfns)
339                             (listp form))
340                        (push fn fns)))
341                 (setq forms (append form forms))))
342             (when fns
343               (if buffer
344                   (set-buffer buffer)
345                 (display-buffer
346                  (setq buffer (get-buffer-create
347                                (concat "*CL run-time functions in: "
348                                        file-or-directory "*"))))
349                 (set-buffer buffer)
350                 (erase-buffer)
351                 (setq window (get-buffer-window buffer t)
352                       scroll (- 2 (window-height window))
353                       fill-column (max 16 (- (window-width window) 2))
354                       fill-prefix "               "))
355               (when file
356                 (insert file "\n")
357                 (setq file nil))
358               (narrow-to-region
359                (point)
360                (progn
361                  (insert fill-prefix
362                          (mapconcat (lambda (fn) (format "%s" fn))
363                                     (nreverse fns) " "))
364                  (point)))
365               (fill-region (point-min) (point-max))
366               (goto-char (point-min))
367               (widen)
368               (delete-char 14)
369               (insert (format "%5d - %5d:" (car lines) (cadr lines)))
370               (goto-char (point-max))
371               (forward-line scroll)
372               (set-window-start window (point))
373               (goto-char (point-max))
374               (sit-for 0)
375               (set-buffer working)))))
376       (kill-buffer working)
377       (if buffer
378           (message "Done")
379         (message "No CL run-time functions found in: %s"
380                  file-or-directory)))))
381
382 (provide 'gnus-clfns)
383
384 ;;; gnus-clfns.el ends here