123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 |
- ;;;; Copyright (C) 1997, 2000, 2001, 2003, 2006 Free Software Foundation, Inc.
- ;;;;
- ;;;; This library is free software; you can redistribute it and/or
- ;;;; modify it under the terms of the GNU Lesser General Public
- ;;;; License as published by the Free Software Foundation; either
- ;;;; version 2.1 of the License, or (at your option) any later version.
- ;;;;
- ;;;; This library is distributed in the hope that it will be useful,
- ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- ;;;; Lesser General Public License for more details.
- ;;;;
- ;;;; You should have received a copy of the GNU Lesser General Public
- ;;;; License along with this library; if not, write to the Free Software
- ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- ;;;;
- (define-module (ice-9 session)
- :use-module (ice-9 documentation)
- :use-module (ice-9 regex)
- :use-module (ice-9 rdelim)
- :export (help
- add-value-help-handler! remove-value-help-handler!
- add-name-help-handler! remove-name-help-handler!
- apropos apropos-internal apropos-fold apropos-fold-accessible
- apropos-fold-exported apropos-fold-all source arity
- system-module module-commentary))
- (define *value-help-handlers*
- `(,(lambda (name value)
- (object-documentation value))))
- (define (add-value-help-handler! proc)
- "Adds a handler for performing `help' on a value.
- `proc' will be called as (PROC NAME VALUE). `proc' should return #t to
- indicate that it has performed help, a string to override the default
- object documentation, or #f to try the other handlers, potentially
- falling back on the normal behavior for `help'."
- (set! *value-help-handlers* (cons proc *value-help-handlers*)))
- (define (remove-value-help-handler! proc)
- "Removes a handler for performing `help' on a value."
- (set! *value-help-handlers* (delete! proc *value-help-handlers*)))
- (define (try-value-help name value)
- (or-map (lambda (proc) (proc name value)) *value-help-handlers*))
- (define *name-help-handlers* '())
- (define (add-name-help-handler! proc)
- "Adds a handler for performing `help' on a name.
- `proc' will be called with the unevaluated name as its argument. That is
- to say, when the user calls `(help FOO)', the name is FOO, exactly as
- the user types it.
- `proc' should return #t to indicate that it has performed help, a string
- to override the default object documentation, or #f to try the other
- handlers, potentially falling back on the normal behavior for `help'."
- (set! *name-help-handlers* (cons proc *name-help-handlers*)))
- (define (remove-name-help-handler! proc)
- "Removes a handler for performing `help' on a name."
- (set! *name-help-handlers* (delete! proc *name-help-handlers*)))
- (define (try-name-help name)
- (or-map (lambda (proc) (proc name)) *name-help-handlers*))
- ;;; Documentation
- ;;;
- (define help
- (procedure->syntax
- (lambda (exp env)
- "(help [NAME])
- Prints useful information. Try `(help)'."
- (cond ((not (= (length exp) 2))
- (help-usage))
- ((not (provided? 'regex))
- (display "`help' depends on the `regex' feature.
- You don't seem to have regular expressions installed.\n"))
- (else
- (let ((name (cadr exp))
- (not-found (lambda (type x)
- (simple-format #t "No ~A found for ~A\n"
- type x))))
- (cond
- ;; User-specified
- ((try-name-help name)
- => (lambda (x) (if (not (eq? x #t)) (display x))))
- ;; SYMBOL
- ((symbol? name)
- (help-doc name
- (simple-format
- #f "^~A$"
- (regexp-quote (symbol->string name)))))
- ;; "STRING"
- ((string? name)
- (help-doc name name))
- ;; (unquote SYMBOL)
- ((and (list? name)
- (= (length name) 2)
- (eq? (car name) 'unquote))
- (let ((doc (try-value-help (cadr name)
- (local-eval (cadr name) env))))
- (cond ((not doc) (not-found 'documentation (cadr name)))
- ((eq? doc #t)) ;; pass
- (else (write-line doc)))))
- ;; (quote SYMBOL)
- ((and (list? name)
- (= (length name) 2)
- (eq? (car name) 'quote)
- (symbol? (cadr name)))
- (cond ((search-documentation-files (cadr name))
- => write-line)
- (else (not-found 'documentation (cadr name)))))
- ;; (SYM1 SYM2 ...)
- ((and (list? name)
- (and-map symbol? name)
- (not (null? name))
- (not (eq? (car name) 'quote)))
- (cond ((module-commentary name)
- => (lambda (doc)
- (display name) (write-line " commentary:")
- (write-line doc)))
- (else (not-found 'commentary name))))
- ;; unrecognized
- (else
- (help-usage)))
- *unspecified*))))))
- (define (module-filename name) ; fixme: better way? / done elsewhere?
- (let* ((name (map symbol->string name))
- (reverse-name (reverse name))
- (leaf (car reverse-name))
- (dir-hint-module-name (reverse (cdr reverse-name)))
- (dir-hint (apply string-append
- (map (lambda (elt)
- (string-append elt "/"))
- dir-hint-module-name))))
- (%search-load-path (in-vicinity dir-hint leaf))))
- (define (module-commentary name)
- (cond ((module-filename name) => file-commentary)
- (else #f)))
- (define (help-doc term regexp)
- (let ((entries (apropos-fold (lambda (module name object data)
- (cons (list module
- name
- (try-value-help name object)
- (cond ((closure? object)
- "a procedure")
- ((procedure? object)
- "a primitive procedure")
- (else
- "an object")))
- data))
- '()
- regexp
- apropos-fold-exported))
- (module car)
- (name cadr)
- (doc caddr)
- (type cadddr))
- (cond ((not (null? entries))
- (let ((first? #t)
- (undocumented-entries '())
- (documented-entries '())
- (documentations '()))
- (for-each (lambda (entry)
- (let ((entry-summary (simple-format
- #f "~S: ~S\n"
- (module-name (module entry))
- (name entry))))
- (if (doc entry)
- (begin
- (set! documented-entries
- (cons entry-summary documented-entries))
- ;; *fixme*: Use `describe' when we have GOOPS?
- (set! documentations
- (cons (simple-format
- #f "`~S' is ~A in the ~S module.\n\n~A\n"
- (name entry)
- (type entry)
- (module-name (module entry))
- (doc entry))
- documentations)))
- (set! undocumented-entries
- (cons entry-summary
- undocumented-entries)))))
- entries)
- (if (and (not (null? documented-entries))
- (or (> (length documented-entries) 1)
- (not (null? undocumented-entries))))
- (begin
- (display "Documentation found for:\n")
- (for-each (lambda (entry) (display entry))
- documented-entries)
- (set! first? #f)))
- (for-each (lambda (entry)
- (if first?
- (set! first? #f)
- (newline))
- (display entry))
- documentations)
- (if (not (null? undocumented-entries))
- (begin
- (if first?
- (set! first? #f)
- (newline))
- (display "No documentation found for:\n")
- (for-each (lambda (entry) (display entry))
- undocumented-entries)))))
- ((search-documentation-files term)
- => (lambda (doc)
- (write-line "Documentation from file:")
- (write-line doc)))
- (else
- ;; no matches
- (display "Did not find any object ")
- (simple-format #t
- (if (symbol? term)
- "named `~A'\n"
- "matching regexp \"~A\"\n")
- term)))))
- (define (help-usage)
- (display "Usage: (help NAME) gives documentation about objects named NAME (a symbol)
- (help REGEXP) ditto for objects with names matching REGEXP (a string)
- (help 'NAME) gives documentation for NAME, even if it is not an object
- (help ,EXPR) gives documentation for object returned by EXPR
- (help (my module)) gives module commentary for `(my module)'
- (help) gives this text
- `help' searches among bindings exported from loaded modules, while
- `apropos' searches among bindings visible from the \"current\" module.
- Examples: (help help)
- (help cons)
- (help \"output-string\")
- Other useful sources of helpful information:
- (apropos STRING)
- (arity PROCEDURE)
- (name PROCEDURE-OR-MACRO)
- (source PROCEDURE-OR-MACRO)
- Tools:
- (backtrace) ;show backtrace from last error
- (debug) ;enter the debugger
- (trace [PROCEDURE]) ;trace procedure (no arg => show)
- (untrace [PROCEDURE]) ;untrace (no arg => untrace all)
- (OPTIONSET-options 'full) ;display option information
- (OPTIONSET-enable 'OPTION)
- (OPTIONSET-disable 'OPTION)
- (OPTIONSET-set! OPTION VALUE)
- where OPTIONSET is one of debug, read, eval, print
- "))
- ;;; {Apropos}
- ;;;
- ;;; Author: Roland Orre <orre@nada.kth.se>
- ;;;
- (define (apropos rgx . options)
- "Search for bindings: apropos regexp {options= 'full 'shadow 'value}"
- (if (zero? (string-length rgx))
- "Empty string not allowed"
- (let* ((match (make-regexp rgx))
- (uses (module-uses (current-module)))
- (modules (cons (current-module)
- (if (and (not (null? uses))
- (eq? (module-name (car uses))
- 'duplicates))
- (cdr uses)
- uses)))
- (separator #\tab)
- (shadow (member 'shadow options))
- (value (member 'value options)))
- (cond ((member 'full options)
- (set! shadow #t)
- (set! value #t)))
- (for-each
- (lambda (module)
- (let* ((name (module-name module))
- (obarray (module-obarray module)))
- ;; XXX - should use hash-fold here
- (hash-for-each
- (lambda (symbol variable)
- (cond ((regexp-exec match (symbol->string symbol))
- (display name)
- (display ": ")
- (display symbol)
- (cond ((variable-bound? variable)
- (let ((val (variable-ref variable)))
- (cond ((or (procedure? val) value)
- (display separator)
- (display val)))))
- (else
- (display separator)
- (display "(unbound)")))
- (if (and shadow
- (not (eq? (module-ref module symbol)
- (module-ref (current-module) symbol))))
- (display " shadowed"))
- (newline))))
- obarray)))
- modules))))
- (define (apropos-internal rgx)
- "Return a list of accessible variable names."
- (apropos-fold (lambda (module name var data)
- (cons name data))
- '()
- rgx
- (apropos-fold-accessible (current-module))))
- (define (apropos-fold proc init rgx folder)
- "Folds PROCEDURE over bindings matching third arg REGEXP.
- Result is
- (PROCEDURE MODULE1 NAME1 VALUE1
- (PROCEDURE MODULE2 NAME2 VALUE2
- ...
- (PROCEDURE MODULEn NAMEn VALUEn INIT)))
- where INIT is the second arg to `apropos-fold'.
- Fourth arg FOLDER is one of
- (apropos-fold-accessible MODULE) ;fold over bindings accessible in MODULE
- apropos-fold-exported ;fold over all exported bindings
- apropos-fold-all ;fold over all bindings"
- (let ((match (make-regexp rgx))
- (recorded (make-vector 61 '())))
- (let ((fold-module
- (lambda (module data)
- (let* ((obarray-filter
- (lambda (name val data)
- (if (and (regexp-exec match (symbol->string name))
- (not (hashq-get-handle recorded name)))
- (begin
- (hashq-set! recorded name #t)
- (proc module name val data))
- data)))
- (module-filter
- (lambda (name var data)
- (if (variable-bound? var)
- (obarray-filter name (variable-ref var) data)
- data))))
- (cond (module (hash-fold module-filter
- data
- (module-obarray module)))
- (else data))))))
- (folder fold-module init))))
- (define (make-fold-modules init-thunk traverse extract)
- "Return procedure capable of traversing a forest of modules.
- The forest traversed is the image of the forest generated by root
- modules returned by INIT-THUNK and the generator TRAVERSE.
- It is an image under the mapping EXTRACT."
- (lambda (fold-module init)
- (let* ((table (make-hash-table 31))
- (first? (lambda (obj)
- (let* ((handle (hash-create-handle! table obj #t))
- (first? (cdr handle)))
- (set-cdr! handle #f)
- first?))))
- (let rec ((data init)
- (modules (init-thunk)))
- (do ((modules modules (cdr modules))
- (data data (if (first? (car modules))
- (rec (fold-module (extract (car modules)) data)
- (traverse (car modules)))
- data)))
- ((null? modules) data))))))
- (define (apropos-fold-accessible module)
- (make-fold-modules (lambda () (list module))
- module-uses
- identity))
- (define (root-modules)
- (cons the-root-module
- (submodules (nested-ref the-root-module '(app modules)))))
- (define (submodules m)
- (hash-fold (lambda (name var data)
- (let ((obj (and (variable-bound? var) (variable-ref var))))
- (if (and (module? obj)
- (eq? (module-kind obj) 'directory))
- (cons obj data)
- data)))
- '()
- (module-obarray m)))
- (define apropos-fold-exported
- (make-fold-modules root-modules submodules module-public-interface))
- (define apropos-fold-all
- (make-fold-modules root-modules submodules identity))
- (define (source obj)
- (cond ((procedure? obj) (procedure-source obj))
- ((macro? obj) (procedure-source (macro-transformer obj)))
- (else #f)))
- (define (arity obj)
- (define (display-arg-list arg-list)
- (display #\`)
- (display (car arg-list))
- (let loop ((ls (cdr arg-list)))
- (cond ((null? ls)
- (display #\'))
- ((not (pair? ls))
- (display "', the rest in `")
- (display ls)
- (display #\'))
- (else
- (if (pair? (cdr ls))
- (display "', `")
- (display "' and `"))
- (display (car ls))
- (loop (cdr ls))))))
- (define (display-arg-list/summary arg-list type)
- (let ((len (length arg-list)))
- (display len)
- (display " ")
- (display type)
- (if (> len 1)
- (display " arguments: ")
- (display " argument: "))
- (display-arg-list arg-list)))
- (cond
- ((procedure-property obj 'arglist)
- => (lambda (arglist)
- (let ((required-args (car arglist))
- (optional-args (cadr arglist))
- (keyword-args (caddr arglist))
- (allow-other-keys? (cadddr arglist))
- (rest-arg (car (cddddr arglist)))
- (need-punctuation #f))
- (cond ((not (null? required-args))
- (display-arg-list/summary required-args "required")
- (set! need-punctuation #t)))
- (cond ((not (null? optional-args))
- (if need-punctuation (display ", "))
- (display-arg-list/summary optional-args "optional")
- (set! need-punctuation #t)))
- (cond ((not (null? keyword-args))
- (if need-punctuation (display ", "))
- (display-arg-list/summary keyword-args "keyword")
- (set! need-punctuation #t)))
- (cond (allow-other-keys?
- (if need-punctuation (display ", "))
- (display "other keywords allowed")
- (set! need-punctuation #t)))
- (cond (rest-arg
- (if need-punctuation (display ", "))
- (display "the rest in `")
- (display rest-arg)
- (display "'"))))))
- (else
- (let ((arity (procedure-property obj 'arity)))
- (display (car arity))
- (cond ((caddr arity)
- (display " or more"))
- ((not (zero? (cadr arity)))
- (display " required and ")
- (display (cadr arity))
- (display " optional")))
- (if (and (not (caddr arity))
- (= (car arity) 1)
- (<= (cadr arity) 1))
- (display " argument")
- (display " arguments"))
- (if (closure? obj)
- (let ((formals (cadr (procedure-source obj))))
- (cond
- ((pair? formals)
- (display ": ")
- (display-arg-list formals))
- (else
- (display " in `")
- (display formals)
- (display #\'))))))))
- (display ".\n"))
- (define system-module
- (procedure->syntax
- (lambda (exp env)
- (let* ((m (nested-ref the-root-module
- (append '(app modules) (cadr exp)))))
- (if (not m)
- (error "Couldn't find any module named" (cadr exp)))
- (let ((s (not (procedure-property (module-eval-closure m)
- 'system-module))))
- (set-system-module! m s)
- (string-append "Module " (symbol->string (module-name m))
- " is now a " (if s "system" "user") " module."))))))
- ;;; session.scm ends here
|