Remove xetla pkg
[packages] / xemacs-packages / efs / efs-defun.el
1 ;; -*-Emacs-Lisp-*-
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;
4 ;; File:         efs-defun.el
5 ;; Release:      $efs release: 1.24 $
6 ;; Version:      #Revision: 1.1 $
7 ;; RCS:          
8 ;; Description:  efs-defun allows for OS-dependent coding of functions
9 ;; Author:       Sandy Rutherford <sandy@ibm550.sissa.it>
10 ;; Created:      Thu Oct 22 17:58:14 1992
11 ;; Modified:     Sun Nov 27 12:18:35 1994 by sandy on gandalf
12 ;; Language:     Emacs-Lisp
13 ;;
14 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
15
16 ;;; This file is part of efs. See efs.el for copyright
17 ;;; (it's copylefted) and warranty (there isn't one) information.
18
19 ;;; efs-defun allows object-oriented emacs lisp definitions.
20 ;;; In efs, this feature is used to support multiple host types.
21 ;;; 
22 ;;; The first arg after the function name is a key which determines
23 ;;; which version of the function is being defined. Normally, when the function
24 ;;; is called this key is given as the first argument to the function.
25 ;;;
26 ;;; For example:
27 ;;; 
28 ;;; (efs-defun foobar vms (x y)
29 ;;;   (message "hello vms world")
30 ;;;   (+ x y))
31 ;;;   => foobar
32 ;;;
33 ;;; (foobar 'vms 1 2)
34 ;;;   => 3
35
36 ;;; The key nil plays a special role: 
37 ;;;
38 ;;; First, it defines a default action. If there is no function
39 ;;; definition associated with a given OS-key, then the function
40 ;;; definition associated with nil is used.  If further there is no
41 ;;; function definition associated with nil, then an error is
42 ;;; signaled.  
43 ;;;
44 ;;; Second, the documentation string for the function is the one given
45 ;;; with the nil definition. You can supply doc-strings with other
46 ;;; definitions of the function, but they are not accessible with
47 ;;; 'describe-function. In fact, when the function is either loaded or
48 ;;; byte-compiled, they are just thrown away.
49
50 ;;; There is another way to define the default action of an efs-function.
51 ;;; This is with the use flag. If you give as the key (&use foobar),
52 ;;; then when the function is called the variable foobar will be used to
53 ;;; determine which OS version of the function to use. As well as
54 ;;; allowing you to define the doc string, if the use flag is used,
55 ;;; then you can specify an interactive specification with the function.
56 ;;; Although a function is only interactive, if the default definition
57 ;;; has an interactive spec, it is still necessary to give interactive
58 ;;; specs for the other definitions of the function as well. It is possible
59 ;;; for these interactive specs to differ.
60 ;;; 
61 ;;; For example:
62 ;;; 
63 ;;; (efs-defun fizzle (&use foobar)
64 ;;;   "Fizzle's doc string."
65 ;;;   (interactive)
66 ;;;   (message "fizz wizz"))
67 ;;; 
68 ;;; (efs-defun fizzle vms
69 ;;;   (interactive)
70 ;;;   (message "VMS is fizzled."))
71 ;;; 
72 ;;; (setq foobar 'unix)
73 ;;; => unix
74 ;;; 
75 ;;; (fizzle)
76 ;;; => "fizz wizz"
77 ;;; 
78 ;;; (setq foobar 'vms)
79 ;;; => vms
80 ;;; 
81 ;;; (fizzle)
82 ;;; => "VMS is fizzled."
83 ;;; 
84 ;;; M-x f i z z l e <return>
85 ;;; => "VMS is fizzled."
86 ;;;
87 ;;; Actually, when you use the &use spec, whatever follows it is simply
88 ;;; evaluated at call time.
89
90 ;;; Note that when the function is defined, the key is implicitly
91 ;;; quoted, whereas when the function is called, the key is
92 ;;; evaluated.  If this seems strange, think about how efs-defuns
93 ;;; are used in practice.
94
95 ;;; There are no restrictions on the order in which the different OS-type
96 ;;; definitions are done.
97
98 ;;; There are no restrictions on the keys that can be used, nor on the
99 ;;; symbols that can be used as arguments to an efs-defun.  We go
100 ;;; to some lengths to avoid potential conflicts. In particular, when
101 ;;; the OS-keys are looked up in the symbol's property list, we
102 ;;; actually look for a symbol with the same name in the special
103 ;;; obarray, efs-key-obarray. This avoids possible conflicts with
104 ;;; other entries in the property list, that are usually accessed with
105 ;;; symbols in the standard obarray.
106
107 ;;; The V19 byte-compiler will byte-compile efs-defun's.
108 ;;; The standard emacs V18 compiler will not, however they will still
109 ;;; work, just not at byte-compiled speed.
110
111 ;;; efs-autoload works much like the standard autoload, except it
112 ;;; defines the efs function cell for a given host type as an autoload.
113 ;;; The from-kbd arg only makes sense if the default action of the autoload
114 ;;; has been defined with a &use.
115
116 ;;; To do:
117 ;;;
118 ;;; 1. Set an edebug-form-hook for efs-defun
119
120 ;;; Known Bugs:
121 ;;;
122 ;;; 1. efs-autoload will correctly NOT overload an existing function
123 ;;;    definition with an autoload definition. However, it will also
124 ;;;    not overload a previous autoload with a new one. It should. An
125 ;;;    overload can be forced for the KEY def of function FUN by doing
126 ;;;    (put 'FUN (intern "KEY" efs-key-obarray) nil) first.
127 ;;;
128
129 ;;; Provisions and requirements
130
131 (provide 'efs-defun)
132 (require 'backquote)
133
134 ;;; Variables
135
136 (defconst efs-defun-version
137   (concat (substring "$efs release: 1.24 $" 14 -2)
138           "/"
139           (substring "#Revision: 1.1 $" 11 -2)))
140
141 (defconst efs-key-obarray (make-vector 7 0))
142
143 ;; Unfortunately, we need to track this in bytecomp.el.
144 ;; It's not much to keep track of, although.
145 (defconst efs-defun-bytecomp-buffer "*Compile-Log*")
146
147 (defvar efs-key nil
148   "Inside an efs function, this is set to the key that was used to
149 call the function. You can test this inside the default definition, to
150 determine which key was actually used.")
151 (defvar efs-args nil
152   "Inside an efs function, this is set to a list of the calling args
153 of the function.")
154
155 ;;; Utility Functions
156
157 ;;; These functions are called when the macros efs-defun and efs-autoload
158 ;;; are expanded. Their purpose is to help in producing the expanded code.
159
160 (defun efs-defun-arg-count (list)
161   ;; Takes a list of arguments, and returns a list of three
162   ;; integers giving the number of normal args, the number
163   ;; of &optional args, and the number of &rest args (this should
164   ;; only be 0 or 1, but we don't check this).
165   (let ((o-leng (length (memq '&optional list)))
166         (r-leng (length (memq '&rest list)))
167         (leng (length list)))
168     (list (- leng (max o-leng r-leng))
169           (max 0 (- o-leng r-leng 1))
170           (max 0 (1- r-leng)))))
171
172 ;; For each efs-function the property efs-function-arg-structure
173 ;; is either a list of three integers to indicate the number of normal,
174 ;; optional, and rest args, or it can be the symbol 'autoload to indicate
175 ;; that all definitions of the function are autoloads, and we have no
176 ;; idea of its arg structure.
177
178 (defun efs-defun-arg-check (fun key list)
179   ;; Checks that the LIST of args is consistent for the KEY def
180   ;; of function FUN.
181   (let ((prop (get fun 'efs-function-arg-structure))
182         count)
183     (if (eq list 'autoload)
184         (or prop (put fun 'efs-function-arg-structure 'autoload))
185       (setq count (efs-defun-arg-count list))
186       (if (and prop (not (eq prop 'autoload)) (not (equal prop count)))
187           (let ((warning
188                  (format
189                   "args. for the %s def. of %s don't agree with previous defs."
190                   key fun)))
191             (message (concat "Warning: " warning))
192             ;; We are compiling, I suppose...
193             (if (get-buffer efs-defun-bytecomp-buffer)
194                 (save-excursion
195                   (set-buffer efs-defun-bytecomp-buffer)
196                   (goto-char (point-max))
197                   (insert "efs warning:\n  " warning "\n")))))
198       (put fun 'efs-function-arg-structure count))))
199
200 (defun efs-def-generic (fun use doc-string interactive-p)
201   ;; Generates a generic function def using USE.
202   ;; If use is nil, the first arg of the function
203   ;; is the key.
204   (let ((def-args '(&rest efs-args))
205         result)
206     (or use
207         (setq def-args (cons 'efs-key def-args)))
208     (setq result
209           (` (or (get (quote (, fun))
210                       (, (if use
211                              (list 'intern
212                                    (list 'symbol-name use)
213                                    'efs-key-obarray)
214                            '(intern
215                              (symbol-name efs-key)
216                              efs-key-obarray))))
217                  (get (quote (, fun))
218                       (intern "nil" efs-key-obarray)))))
219     ;; Make the gen fun interactive, if nec.
220     (setq result
221           (if interactive-p
222               (` ((interactive)
223                   (if (interactive-p)
224                       (let ((prefix-arg current-prefix-arg))
225                         (call-interactively
226                          (, result)))
227                     (, (cons 'apply (list result 'efs-args))))))
228             (list (cons 'apply (list result 'efs-args)))))
229     (if doc-string (setq result (cons doc-string result)))
230     (cons 'defun (cons fun (cons def-args result)))))
231
232 (defun efs-def-autoload (fun key file from-kbd)
233   ;; Returns the autoload lambda for FUN and FILE.
234   ;; I really should have some notion of efs-autoload
235   ;; objects, and not just plain lambda's.
236   (let ((result
237          (if from-kbd
238              (`
239               (lambda (&rest args)
240                 (interactive)
241                 (let ((qkey (intern (symbol-name (quote (, key)))
242                                     efs-key-obarray))
243                       (tmp1 (intern "tmp1" efs-key-obarray))
244                       (tmp2 (intern "tmp2" efs-key-obarray)))
245                   ;; Need to store the a-f-function, to see if it has been
246                   ;; re-defined by the load. This is avoid to an infinite loop.
247                   (set tmp1 (get (quote (, fun)) qkey))
248                   ;; Need to store the prefix arg in case it's interactive.
249                   ;; These values are stored in variables interned in the
250                   ;; efs-key-obarray, because who knows what loading a
251                   ;; file might do.
252                   (set tmp2 current-prefix-arg)
253                   (load (, file))
254                   ;; check for re-def
255                   (if (equal (symbol-value tmp1)
256                              (get (quote (, fun)) qkey))
257                       (error "%s definition of %s is not defined by loading %s"
258                              qkey (quote (, fun)) (, file)))
259                   ;; call function
260                   (if (interactive-p)
261                       (let ((prefix-arg (symbol-value tmp2)))
262                         (call-interactively
263                          (get (quote (, fun)) qkey)))
264                     (apply (get (quote (, fun)) qkey) args)))))
265            (` (lambda (&rest args)
266                 (let ((qkey (intern (symbol-name (quote (, key)))
267                                     efs-key-obarray))
268                       (tmp1 (intern "tmp1" efs-key-obarray)))
269                   ;; Need to store the a-f-function, to see if it has been
270                   ;; re-defined by the load. This is avoid to an infinite loop.
271                   (set tmp1 (get (quote (, fun)) qkey))
272                   (load (, file))
273                   ;; check for re-def
274                   (if (equal (symbol-value tmp1)
275                              (get (quote (, fun)) qkey))
276                       (error "%s definition of %s is not defined by loading %s"
277                              qkey (quote (, fun)) (, file)))
278                   ;; call function
279                   (apply (get (quote (, fun)) qkey) args)))))))
280     (list 'put (list 'quote fun)
281           (list 'intern
282                 (list 'symbol-name (list 'quote key))
283                 'efs-key-obarray)
284           (list 'function result))))
285  
286 ;;; User level macros -- efs-defun and efs-autoload.
287
288 (defmacro efs-defun (funame key args &rest body)
289   (let* ((use (and (eq (car-safe key) '&use)
290                    (nth 1 key)))
291          (key (and (null use) key))
292          result doc-string interactive-p)
293     ;; check args
294     (efs-defun-arg-check funame key args)
295     ;; extract doc-string
296     (if (stringp (car body))
297         (setq doc-string  (car body)
298               body (cdr body)))
299     ;; If the default fun is interactive, and it's a use construct,
300     ;; then we allow the gen fun to be interactive.
301     (if use
302         (setq interactive-p (eq (car-safe (car-safe body)) 'interactive)))
303     (setq result
304           (` ((put (quote (, funame))
305                    (intern (symbol-name (quote (, key)))
306                            efs-key-obarray)
307                    (function
308                     (, (cons 'lambda
309                              (cons args body)))))
310               (quote (, funame)))))
311     ;; if the key is null, make a generic def
312     (if (null key)
313         (setq result
314               (cons (efs-def-generic
315                      funame use doc-string interactive-p)
316                     result)))
317     ;; return
318     (cons 'progn result)))
319
320 ;;; For lisp-mode
321
322 (put 'efs-defun 'lisp-indent-hook 'defun)
323
324 ;; efs-autoload
325 ;; Allows efs function cells to be defined as autoloads.
326 ;; If efs-autoload inserted autoload objects in the property list,
327 ;; and the funcall mechanism in efs-defun checked for such
328 ;; auto-load objects, we could reduce the size of the code
329 ;; resulting from expanding efs-autoload. However, the expansion
330 ;; of efs-defun would be larger. What is the best thing to do?
331
332 (defmacro efs-autoload (fun key file &optional docstring from-kbd)
333   (let* ((use (and (eq (car-safe key) '&use)
334                    (nth 1 key)))
335          (key (and (null use) key)))
336     (efs-defun-arg-check (eval fun) key 'autoload)
337     ;; has the function been previously defined?
338     (`
339      (if (null (get (, fun)
340                     (intern (symbol-name (quote (, key)))
341                             efs-key-obarray)))
342          (,
343           (if (null key)
344               (list 'progn
345                     ;; need to eval fun, since autoload wants an explicit
346                     ;; quote built into the fun arg.
347                     (efs-def-generic
348                      (eval fun) use docstring from-kbd )
349                     (efs-def-autoload (eval fun) key file from-kbd)
350                     (list 'quote
351                           (list
352                            'efs-autoload
353                            key file docstring from-kbd)))
354             (list 'progn
355                   (efs-def-autoload (eval fun) key file from-kbd)
356                   (list 'quote
357                         (list 
358                          'efs-autoload
359                          key file docstring from-kbd)))))))))
360
361 (defun efs-fset (sym key fun)
362   ;; Like fset but sets KEY's definition of SYM.
363   (put sym (intern (symbol-name key) efs-key-obarray) fun))
364
365 (defun efs-fboundp (key fun)
366   ;; Like fboundp, but checks for KEY's def.
367   (null (null (get fun (intern (symbol-name key) efs-key-obarray)))))
368
369 ;; If we are going to use autoload objects, the following two functions
370 ;; will be useful.
371 ;;
372 ;; (defun efs-defun-do-autoload (fun file key interactive-p args)
373 ;;   ;; Loads FILE and runs the KEY def of FUN.
374 ;;   (let (fun file key interactive-p args)
375 ;;     (load file))
376 ;;   (let ((new-def (get fun key)))
377 ;;     (if (eq (car-safe new-def) 'autoload)
378 ;;      (error "%s definition of %s is not defined by loading %s"
379 ;;             key fun file)
380 ;;       (if interactive-p
381 ;;        (let ((prefix-arg current-predix-arg))
382 ;;          (call-interactively fun))
383 ;;      (apply new-def args)))))
384 ;; 
385 ;; (defun efs-defun-autoload (fun key file doc-string from-kbd)
386 ;;   ;; Sets the KEY def of FUN to an autoload object.
387 ;;   (let* ((key (intern (symbol-name key) efs-key-obarray))
388 ;;       (def (get fun key)))
389 ;;     (if (or (null def)
390 ;;          (eq (car-safe def) 'autoload))
391 ;;      (put fun key (list 'autoload file doc-string from-kbd)))))
392
393 ;;; end of efs-defun.el