add procedure_minimum_arity
[bpt/guile.git] / module / ice-9 / session.scm
CommitLineData
8470b3f4 1;;;; Copyright (C) 1997, 2000, 2001, 2003, 2006, 2009, 2010 Free Software Foundation, Inc.
7bb1bfc2 2;;;;
73be1d9e
MV
3;;;; This library is free software; you can redistribute it and/or
4;;;; modify it under the terms of the GNU Lesser General Public
5;;;; License as published by the Free Software Foundation; either
53befeb7 6;;;; version 3 of the License, or (at your option) any later version.
73be1d9e
MV
7;;;;
8;;;; This library is distributed in the hope that it will be useful,
0e81dabd 9;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
73be1d9e
MV
10;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11;;;; Lesser General Public License for more details.
12;;;;
13;;;; You should have received a copy of the GNU Lesser General Public
14;;;; License along with this library; if not, write to the Free Software
92205699 15;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
a482f2cc 16;;;;
0e81dabd
MD
17\f
18
bbefd480 19(define-module (ice-9 session)
3bdca000 20 :use-module (ice-9 documentation)
3510b484 21 :use-module (ice-9 regex)
1a179b03 22 :use-module (ice-9 rdelim)
4f7a0504
AW
23 :export (help
24 add-value-help-handler! remove-value-help-handler!
25 add-name-help-handler! remove-name-help-handler!
26 apropos apropos-internal apropos-fold apropos-fold-accessible
27 apropos-fold-exported apropos-fold-all source arity
df22662f
AW
28 procedure-arguments
29 module-commentary))
0e81dabd
MD
30
31\f
32
53d81399
AW
33(define *value-help-handlers*
34 `(,(lambda (name value)
35 (object-documentation value))))
4f7a0504
AW
36
37(define (add-value-help-handler! proc)
38 "Adds a handler for performing `help' on a value.
39
40`proc' will be called as (PROC NAME VALUE). `proc' should return #t to
41indicate that it has performed help, a string to override the default
42object documentation, or #f to try the other handlers, potentially
43falling back on the normal behavior for `help'."
44 (set! *value-help-handlers* (cons proc *value-help-handlers*)))
45
46(define (remove-value-help-handler! proc)
53d81399 47 "Removes a handler for performing `help' on a value."
4f7a0504
AW
48 (set! *value-help-handlers* (delete! proc *value-help-handlers*)))
49
50(define (try-value-help name value)
51 (or-map (lambda (proc) (proc name value)) *value-help-handlers*))
52
53
54(define *name-help-handlers* '())
55
56(define (add-name-help-handler! proc)
57 "Adds a handler for performing `help' on a name.
58
59`proc' will be called with the unevaluated name as its argument. That is
60to say, when the user calls `(help FOO)', the name is FOO, exactly as
61the user types it.
62
53d81399
AW
63`proc' should return #t to indicate that it has performed help, a string
64to override the default object documentation, or #f to try the other
65handlers, potentially falling back on the normal behavior for `help'."
4f7a0504
AW
66 (set! *name-help-handlers* (cons proc *name-help-handlers*)))
67
68(define (remove-name-help-handler! proc)
53d81399 69 "Removes a handler for performing `help' on a name."
4f7a0504
AW
70 (set! *name-help-handlers* (delete! proc *name-help-handlers*)))
71
72(define (try-name-help name)
73 (or-map (lambda (proc) (proc name)) *name-help-handlers*))
74
75
13ae9151
MD
76;;; Documentation
77;;;
99b1dd09
AW
78(define-macro (help . exp)
79 "(help [NAME])
13ae9151 80Prints useful information. Try `(help)'."
99b1dd09 81 (cond ((not (= (length exp) 1))
2588eccd
AW
82 (help-usage)
83 '(begin))
99b1dd09
AW
84 ((not (provided? 'regex))
85 (display "`help' depends on the `regex' feature.
2588eccd
AW
86You don't seem to have regular expressions installed.\n")
87 '(begin))
99b1dd09
AW
88 (else
89 (let ((name (car exp))
90 (not-found (lambda (type x)
91 (simple-format #t "No ~A found for ~A\n"
92 type x))))
93 (cond
94
df22662f
AW
95 ;; User-specified
96 ((try-name-help name)
97 => (lambda (x) (if (not (eq? x #t)) (display x))))
98
99b1dd09
AW
99 ;; SYMBOL
100 ((symbol? name)
101 (help-doc name
102 (simple-format
103 #f "^~A$"
104 (regexp-quote (symbol->string name)))))
105
106 ;; "STRING"
107 ((string? name)
108 (help-doc name name))
109
110 ;; (unquote SYMBOL)
111 ((and (list? name)
112 (= (length name) 2)
113 (eq? (car name) 'unquote))
df22662f 114 (let ((doc (try-value-help (cadr name)
84012ef4
LC
115 (module-ref (current-module)
116 (cadr name)))))
df22662f
AW
117 (cond ((not doc) (not-found 'documentation (cadr name)))
118 ((eq? doc #t)) ;; pass
119 (else (write-line doc)))))
99b1dd09
AW
120
121 ;; (quote SYMBOL)
122 ((and (list? name)
123 (= (length name) 2)
124 (eq? (car name) 'quote)
125 (symbol? (cadr name)))
126 (cond ((search-documentation-files (cadr name))
127 => write-line)
128 (else (not-found 'documentation (cadr name)))))
129
130 ;; (SYM1 SYM2 ...)
131 ((and (list? name)
132 (and-map symbol? name)
133 (not (null? name))
134 (not (eq? (car name) 'quote)))
135 (cond ((module-commentary name)
136 => (lambda (doc)
137 (display name) (write-line " commentary:")
138 (write-line doc)))
139 (else (not-found 'commentary name))))
140
141 ;; unrecognized
de25f281 142 (else
99b1dd09
AW
143 (help-usage)))
144 '(begin)))))
13ae9151 145
7bb1bfc2
TTN
146(define (module-filename name) ; fixme: better way? / done elsewhere?
147 (let* ((name (map symbol->string name))
148 (reverse-name (reverse name))
149 (leaf (car reverse-name))
150 (dir-hint-module-name (reverse (cdr reverse-name)))
151 (dir-hint (apply string-append
152 (map (lambda (elt)
153 (string-append elt "/"))
154 dir-hint-module-name))))
155 (%search-load-path (in-vicinity dir-hint leaf))))
156
157(define (module-commentary name)
158 (cond ((module-filename name) => file-commentary)
159 (else #f)))
160
3bdca000
MD
161(define (help-doc term regexp)
162 (let ((entries (apropos-fold (lambda (module name object data)
163 (cons (list module
164 name
53d81399 165 (try-value-help name object)
314b8716 166 (cond ((procedure? object)
db611983 167 "a procedure")
db611983
NJ
168 (else
169 "an object")))
3bdca000
MD
170 data))
171 '()
172 regexp
173 apropos-fold-exported))
174 (module car)
175 (name cadr)
db611983
NJ
176 (doc caddr)
177 (type cadddr))
de25f281
TTN
178 (cond ((not (null? entries))
179 (let ((first? #t)
180 (undocumented-entries '())
181 (documented-entries '())
182 (documentations '()))
183
184 (for-each (lambda (entry)
185 (let ((entry-summary (simple-format
186 #f "~S: ~S\n"
187 (module-name (module entry))
188 (name entry))))
189 (if (doc entry)
190 (begin
191 (set! documented-entries
192 (cons entry-summary documented-entries))
193 ;; *fixme*: Use `describe' when we have GOOPS?
194 (set! documentations
195 (cons (simple-format
196 #f "`~S' is ~A in the ~S module.\n\n~A\n"
197 (name entry)
198 (type entry)
199 (module-name (module entry))
200 (doc entry))
201 documentations)))
202 (set! undocumented-entries
203 (cons entry-summary
204 undocumented-entries)))))
205 entries)
206
207 (if (and (not (null? documented-entries))
208 (or (> (length documented-entries) 1)
209 (not (null? undocumented-entries))))
210 (begin
211 (display "Documentation found for:\n")
212 (for-each (lambda (entry) (display entry))
213 documented-entries)
214 (set! first? #f)))
215
216 (for-each (lambda (entry)
217 (if first?
218 (set! first? #f)
219 (newline))
220 (display entry))
221 documentations)
222
223 (if (not (null? undocumented-entries))
224 (begin
225 (if first?
226 (set! first? #f)
227 (newline))
228 (display "No documentation found for:\n")
229 (for-each (lambda (entry) (display entry))
230 undocumented-entries)))))
231 ((search-documentation-files term)
232 => (lambda (doc)
233 (write-line "Documentation from file:")
234 (write-line doc)))
235 (else
236 ;; no matches
237 (display "Did not find any object ")
238 (simple-format #t
239 (if (symbol? term)
240 "named `~A'\n"
241 "matching regexp \"~A\"\n")
242 term)))))
3bdca000 243
13ae9151 244(define (help-usage)
3bdca000
MD
245 (display "Usage: (help NAME) gives documentation about objects named NAME (a symbol)
246 (help REGEXP) ditto for objects with names matching REGEXP (a string)
1de3b33b 247 (help 'NAME) gives documentation for NAME, even if it is not an object
d1c50f73 248 (help ,EXPR) gives documentation for object returned by EXPR
8bbe4c82 249 (help (my module)) gives module commentary for `(my module)'
13ae9151
MD
250 (help) gives this text
251
3bdca000
MD
252`help' searches among bindings exported from loaded modules, while
253`apropos' searches among bindings visible from the \"current\" module.
254
2f52380c
MD
255Examples: (help help)
256 (help cons)
3bdca000 257 (help \"output-string\")
13ae9151
MD
258
259Other useful sources of helpful information:
260
261(apropos STRING)
262(arity PROCEDURE)
263(name PROCEDURE-OR-MACRO)
264(source PROCEDURE-OR-MACRO)
265
266Tools:
267
2f52380c
MD
268(backtrace) ;show backtrace from last error
269(debug) ;enter the debugger
270(trace [PROCEDURE]) ;trace procedure (no arg => show)
271(untrace [PROCEDURE]) ;untrace (no arg => untrace all)
13ae9151
MD
272
273(OPTIONSET-options 'full) ;display option information
274(OPTIONSET-enable 'OPTION)
275(OPTIONSET-disable 'OPTION)
276(OPTIONSET-set! OPTION VALUE)
277
278where OPTIONSET is one of debug, read, eval, print
279
280"))
281
0e81dabd
MD
282;;; {Apropos}
283;;;
284;;; Author: Roland Orre <orre@nada.kth.se>
285;;;
286
1a179b03 287(define (apropos rgx . options)
0e81dabd
MD
288 "Search for bindings: apropos regexp {options= 'full 'shadow 'value}"
289 (if (zero? (string-length rgx))
290 "Empty string not allowed"
4f161c5c 291 (let* ((match (make-regexp rgx))
3742da68 292 (uses (module-uses (current-module)))
0e81dabd 293 (modules (cons (current-module)
3742da68
MD
294 (if (and (not (null? uses))
295 (eq? (module-name (car uses))
296 'duplicates))
297 (cdr uses)
298 uses)))
0e81dabd
MD
299 (separator #\tab)
300 (shadow (member 'shadow options))
301 (value (member 'value options)))
302 (cond ((member 'full options)
303 (set! shadow #t)
304 (set! value #t)))
305 (for-each
306 (lambda (module)
296ff5e7
MV
307 (let* ((name (module-name module))
308 (obarray (module-obarray module)))
309 ;; XXX - should use hash-fold here
1798b73d
MD
310 (hash-for-each
311 (lambda (symbol variable)
312 (cond ((regexp-exec match (symbol->string symbol))
313 (display name)
314 (display ": ")
315 (display symbol)
316 (cond ((variable-bound? variable)
317 (let ((val (variable-ref variable)))
318 (cond ((or (procedure? val) value)
319 (display separator)
320 (display val)))))
321 (else
322 (display separator)
323 (display "(unbound)")))
324 (if (and shadow
325 (not (eq? (module-ref module symbol)
326 (module-ref (current-module) symbol))))
327 (display " shadowed"))
328 (newline))))
10764e3c 329 obarray)))
0e81dabd 330 modules))))
68aed3ea 331
1a179b03 332(define (apropos-internal rgx)
68aed3ea 333 "Return a list of accessible variable names."
3bdca000
MD
334 (apropos-fold (lambda (module name var data)
335 (cons name data))
336 '()
337 rgx
338 (apropos-fold-accessible (current-module))))
339
1a179b03 340(define (apropos-fold proc init rgx folder)
3bdca000
MD
341 "Folds PROCEDURE over bindings matching third arg REGEXP.
342
343Result is
344
345 (PROCEDURE MODULE1 NAME1 VALUE1
346 (PROCEDURE MODULE2 NAME2 VALUE2
347 ...
348 (PROCEDURE MODULEn NAMEn VALUEn INIT)))
349
350where INIT is the second arg to `apropos-fold'.
351
352Fourth arg FOLDER is one of
353
354 (apropos-fold-accessible MODULE) ;fold over bindings accessible in MODULE
355 apropos-fold-exported ;fold over all exported bindings
356 apropos-fold-all ;fold over all bindings"
357 (let ((match (make-regexp rgx))
358 (recorded (make-vector 61 '())))
359 (let ((fold-module
360 (lambda (module data)
361 (let* ((obarray-filter
362 (lambda (name val data)
4adc3028 363 (if (and (regexp-exec match (symbol->string name))
3bdca000
MD
364 (not (hashq-get-handle recorded name)))
365 (begin
366 (hashq-set! recorded name #t)
367 (proc module name val data))
368 data)))
369 (module-filter
370 (lambda (name var data)
aef9dd65
MV
371 (if (variable-bound? var)
372 (obarray-filter name (variable-ref var) data)
373 data))))
296ff5e7 374 (cond (module (hash-fold module-filter
3bdca000
MD
375 data
376 (module-obarray module)))
377 (else data))))))
378 (folder fold-module init))))
379
380(define (make-fold-modules init-thunk traverse extract)
381 "Return procedure capable of traversing a forest of modules.
382The forest traversed is the image of the forest generated by root
383modules returned by INIT-THUNK and the generator TRAVERSE.
384It is an image under the mapping EXTRACT."
385 (lambda (fold-module init)
9aec4751
MD
386 (let* ((table (make-hash-table 31))
387 (first? (lambda (obj)
8d627248
MD
388 (let* ((handle (hash-create-handle! table obj #t))
389 (first? (cdr handle)))
390 (set-cdr! handle #f)
391 first?))))
9aec4751
MD
392 (let rec ((data init)
393 (modules (init-thunk)))
394 (do ((modules modules (cdr modules))
395 (data data (if (first? (car modules))
396 (rec (fold-module (extract (car modules)) data)
397 (traverse (car modules)))
398 data)))
399 ((null? modules) data))))))
3bdca000 400
1a179b03 401(define (apropos-fold-accessible module)
3bdca000
MD
402 (make-fold-modules (lambda () (list module))
403 module-uses
de25f281 404 identity))
3bdca000
MD
405
406(define (root-modules)
407 (cons the-root-module
408 (submodules (nested-ref the-root-module '(app modules)))))
409
410(define (submodules m)
411 (hash-fold (lambda (name var data)
aef9dd65 412 (let ((obj (and (variable-bound? var) (variable-ref var))))
3bdca000
MD
413 (if (and (module? obj)
414 (eq? (module-kind obj) 'directory))
415 (cons obj data)
416 data)))
417 '()
418 (module-obarray m)))
419
1a179b03 420(define apropos-fold-exported
3bdca000
MD
421 (make-fold-modules root-modules submodules module-public-interface))
422
1a179b03 423(define apropos-fold-all
de25f281 424 (make-fold-modules root-modules submodules identity))
7cfae7e6 425
1a179b03 426(define (source obj)
7cfae7e6
MD
427 (cond ((procedure? obj) (procedure-source obj))
428 ((macro? obj) (procedure-source (macro-transformer obj)))
429 (else #f)))
4a9f464e 430
1a179b03 431(define (arity obj)
c7bb434f
TTN
432 (define (display-arg-list arg-list)
433 (display #\`)
434 (display (car arg-list))
435 (let loop ((ls (cdr arg-list)))
436 (cond ((null? ls)
437 (display #\'))
438 ((not (pair? ls))
439 (display "', the rest in `")
440 (display ls)
441 (display #\'))
442 (else
443 (if (pair? (cdr ls))
444 (display "', `")
445 (display "' and `"))
446 (display (car ls))
447 (loop (cdr ls))))))
448 (define (display-arg-list/summary arg-list type)
449 (let ((len (length arg-list)))
450 (display len)
451 (display " ")
452 (display type)
453 (if (> len 1)
454 (display " arguments: ")
455 (display " argument: "))
456 (display-arg-list arg-list)))
457 (cond
458 ((procedure-property obj 'arglist)
459 => (lambda (arglist)
460 (let ((required-args (car arglist))
461 (optional-args (cadr arglist))
462 (keyword-args (caddr arglist))
463 (allow-other-keys? (cadddr arglist))
464 (rest-arg (car (cddddr arglist)))
465 (need-punctuation #f))
466 (cond ((not (null? required-args))
467 (display-arg-list/summary required-args "required")
468 (set! need-punctuation #t)))
469 (cond ((not (null? optional-args))
470 (if need-punctuation (display ", "))
471 (display-arg-list/summary optional-args "optional")
472 (set! need-punctuation #t)))
473 (cond ((not (null? keyword-args))
474 (if need-punctuation (display ", "))
475 (display-arg-list/summary keyword-args "keyword")
476 (set! need-punctuation #t)))
477 (cond (allow-other-keys?
478 (if need-punctuation (display ", "))
479 (display "other keywords allowed")
480 (set! need-punctuation #t)))
481 (cond (rest-arg
482 (if need-punctuation (display ", "))
483 (display "the rest in `")
484 (display rest-arg)
485 (display "'"))))))
486 (else
487 (let ((arity (procedure-property obj 'arity)))
488 (display (car arity))
489 (cond ((caddr arity)
490 (display " or more"))
491 ((not (zero? (cadr arity)))
492 (display " required and ")
493 (display (cadr arity))
494 (display " optional")))
495 (if (and (not (caddr arity))
496 (= (car arity) 1)
497 (<= (cadr arity) 1))
498 (display " argument")
314b8716 499 (display " arguments")))))
c7bb434f 500 (display ".\n"))
6ae34994 501
0704c813
AW
502
503(define (procedure-arguments proc)
504 "Return an alist describing the arguments that `proc' accepts, or `#f'
505if the information cannot be obtained.
506
507The alist keys that are currently defined are `required', `optional',
508`keyword', and `rest'."
509 (cond
510 ((procedure-property proc 'arglist)
511 => (lambda (arglist)
512 `((required . ,(car arglist))
513 (optional . ,(cadr arglist))
514 (keyword . ,(caddr arglist))
515 (rest . ,(car (cddddr arglist))))))
516 ((procedure-source proc)
517 => cadr)
518 (((@ (system vm program) program?) proc)
8470b3f4 519 ((@ (system vm program) program-arguments-alist) proc))
0704c813
AW
520 (else #f)))
521
522
de25f281 523;;; session.scm ends here