1 ;;;; guile-ilisp.scm --- ILISP support functions for GUILE
2 ;;;; Matthias Koeppe <mkoeppe@mail.math.uni-magdeburg.de>
4 ;;; Copyright (C) 2000 Matthias Koeppe
6 ;;; This file is part of ILISP.
7 ;;; Please refer to the file COPYING for copyrights and licensing
9 ;;; Please refer to the file ACKNOWLEGDEMENTS for an (incomplete) list
10 ;;; of present and past contributors.
12 ;;; $Id: guile-ilisp.scm,v 1.2 2002-06-03 23:36:58 wbd Exp $
15 (define-module (guile-ilisp)
16 :use-module (ice-9 debug)
17 :use-module (ice-9 session)
18 :use-module (ice-9 documentation)
19 :use-module (ice-9 regex))
21 (define-module (guile-user)
22 :use-module (guile-ilisp))
24 (define-module (guile-ilisp))
26 (define (read-from-string str)
27 (call-with-input-string str read))
29 (define (read-from-string/source str filename line column)
30 "Read from string STR, pretending the source is the given FILENAME, LINE, COLUMN."
31 (call-with-input-string
34 (set-port-filename! port filename)
35 (set-port-line! port (- line 1))
36 (set-port-column! port (- column 1))
39 (define (string->module str)
40 (let ((v (call-with-input-string str read)))
42 ((eq? 'nil v) (current-module))
43 ((pair? v) (resolve-module v))
44 (else (resolve-module (list v))))))
46 (define (first-line s)
47 (let ((i (string-index s #\newline)))
52 (define (doc->arglist doc with-procedure?)
53 "Parse DOC to find the arglist and return it as a string. If
54 WITH-PROCEDURE?, include the procedure symbol."
55 (let ((pattern " - primitive: "))
57 ((string=? (substring doc 0 (string-length pattern))
59 ;; Guile 1.4.1 primitive procedure documentation, passed through
62 ;; - primitive: assoc key alist
63 ;; Behaves like `assq' but uses `equal?' for key comparison.
65 ;; Continuation lines of arglists have an indentation of 10 chars.
68 (string-length pattern)
69 (min (1+ (or (string-index doc #\space
70 (string-length pattern))
71 (string-length pattern)))
72 (or (string-index doc #\newline
73 (string-length pattern))
74 (string-length pattern))))))
75 (let ((eol-index (or (string-index doc #\newline start-index)
76 (string-length doc))))
79 (let loop ((bol-index (+ 1 eol-index))
80 (arglist (substring doc start-index eol-index)))
82 ((and bol-index (>= bol-index (string-length doc)))
84 ((and (>= (string-length doc) (+ bol-index 10))
85 (string=? (substring doc bol-index (+ bol-index 10))
87 (let ((eol-index (string-index doc #\newline bol-index)))
88 (loop (and eol-index (+ 1 eol-index))
89 (string-append arglist " "
90 (substring doc (+ bol-index 10)
95 ((string=? (substring doc 0 1) "(")
96 ;; Guile <= 1.4 primitive procedure documentation and other
100 ;; Prints useful information. Try `(help)'.
104 (let* ((f-l (first-line doc))
105 (index (string-index f-l #\space)))
111 (else (string-append "CAN'T PARSE THIS DOCUMENTATION:\n"
114 (define (info-message sym obj expensive? arglist-only?)
115 "Return an informational message about OBJ, which is the value of SYM.
116 For procedures, return procedure symbol and arglist, or
117 fall back to a message on the arity; if ARGLIST-ONLY?, return the
118 arglist only. If EXPENSIVE?, take some more effort."
119 ;; The code here is so lengthy because we want to return a
120 ;; meaningful result even if we aren't allowed to read the
121 ;; documentation files (EXPENSIVE? = #f).
123 ((and (procedure? obj)
124 (procedure-property obj 'arglist))
126 (let ((required-args (car arglist))
127 (optional-args (cadr arglist))
128 (keyword-args (caddr arglist))
129 (allow-other-keys? (cadddr arglist))
130 (rest-arg (car (cddddr arglist))))
131 (with-output-to-string
133 (define (arg-only arg/default)
134 (if (pair? arg/default) (car arg/default) arg/default))
141 (if (not (null? optional-args))
142 (cons #:optional (map arg-only optional-args))
144 (if (not (null? keyword-args))
145 (cons #:key (map arg-only keyword-args))
147 (if allow-other-keys?
148 (list #:allow-other-keys)
150 (if rest-arg rest-arg '()))))))))
152 (let ((formals (cadr (procedure-source obj))))
153 (if arglist-only? formals (cons sym formals))))
157 ;; object-documentation was introduced in Guile 1.4,
158 ;; There is no documentation for primitives in earlier
160 (object-documentation obj)))
161 (and (procedure? obj)
162 (procedure-property obj 'documentation)
163 ;; The documentation property is attached to a primitive
164 ;; procedure when it was read from the documentation file
168 (doc->arglist doc (not arglist-only?))))
170 (macro-transformer obj)
171 (closure? (macro-transformer obj))
172 (procedure-documentation (macro-transformer obj)))
173 ;; Documentation may be in the doc string of the transformer, as
174 ;; is in session.scm (help).
176 (doc->arglist doc (not arglist-only?))))
178 ;; Return a message about the arity of the procedure.
179 (with-output-to-string
180 (lambda () (arity obj))))
183 (define-public (ilisp-print-info-message sym package)
184 "Evaluate SYM in PACKAGE and print an informational message about
185 the value. For procedures, the arglist is printed.
186 This procedure is invoked by the electric space key."
190 (string->module package)))
195 (info-message sym obj #f #f))
200 (define (if-defined symbol package
201 defined-procedure undefined-procedure)
204 (list (eval-in-package symbol
205 (string->module package))))
208 (defined-procedure (car obj))
209 (undefined-procedure))))
211 (define (strip-parens s)
212 (if (and (string=? (substring s 0 1) "(")
213 (string=? (substring s (- (string-length s) 1)) ")"))
214 (substring s 1 (- (string-length s) 1))
217 (define (symbol-not-present symbol package)
220 (display "' not present in ")
222 ((string=? "nil" package)
223 (display "the current module `")
224 (for-each display (module-name (current-module)))
228 (display (strip-parens package))
232 (define-public (ilisp-arglist symbol package)
233 "Evaluate SYMBOL in PACKAGE and print the arglist if we have a
234 procedure. This procedure is invoked by `arglist-lisp'."
235 (if-defined symbol package
238 ((info-message symbol obj #t #t)
243 (display "Can't get arglist.")
246 (symbol-not-present symbol package))))
248 (define-public (ilisp-help symbol package)
249 "Evaluate SYMBOL in PACKAGE and print help for it."
250 (if-defined symbol package
252 (let ((doc (object-documentation obj)))
255 (display "No documentation."))
258 (symbol-not-present symbol package))))
260 (define (word-separator? ch)
266 (define (string-pred-rindex str pred)
267 (let loop ((index (- (string-length str) 1)))
269 ((negative? index) #f)
270 ((pred (string-ref str index)) index)
271 (else (loop (- index 1))))))
273 (define (separate-fields-before-predicate pred str ret)
274 (let loop ((fields '())
277 ((string-pred-rindex str pred)
278 => (lambda (w) (loop (cons (make-shared-substring str w) fields)
279 (make-shared-substring str 0 w))))
280 (else (apply ret str fields)))))
282 (define (make-word-regexp str)
286 (string-append (regexp-quote word) "[^-:/_]*"))
287 (separate-fields-before-predicate word-separator?
290 (define-public (ilisp-matching-symbols string package function? external? prefix?)
291 (write (map (lambda (sym) (list (symbol->string sym)))
292 (let ((regexp (if (eq? prefix? 't)
293 (string-append "^" (regexp-quote string))
294 (make-word-regexp string)))
295 (a-i apropos-internal))
296 (save-module-excursion
298 (set-current-module (string->module package))
303 (cond ((and (pair? l) (not (null? (cdr l))))
307 (define eval-in-package
308 ;; A two-argument version of `eval'
309 (if (= (car (procedure-property eval 'arity)) 2)
310 (lambda (expression environment) ; we have a R5RS eval
311 (save-module-excursion
313 (eval expression environment))))
314 (lambda (expression environment) ; we have a one-arg eval (Guile <= 1.4)
315 (save-module-excursion
317 (set-current-module environment)
318 (eval expression))))))
320 (define-public (ilisp-get-package sequence-of-defines)
321 "Get the last module name defined in the sequence of define-module forms."
322 ;; First eval the sequence-of-defines. This will register the
323 ;; module with the Guile interpreter if it isn't there already.
324 ;; Otherwise `resolve-module' will give us a bad environment later,
325 ;; which just makes trouble.
328 (append sequence-of-defines
329 '((module-name (current-module))))
330 (string->module "(guile-user)"))))
333 ;; This version of Guile has a module-name procedure that
334 ;; returns the full module name. Good.
337 ;; Now we have the name of the module -- but only the last
338 ;; component. We need to "parse" the sequence-of-defines
340 (let ((last-form (last sequence-of-defines)))
341 (cond ((and (pair? last-form)
342 (eq? (car last-form) 'define-module))
343 (write (cadr last-form)))
344 (else (write '(guile-user))))))))
347 (define-public (ilisp-in-package package)
348 (set-current-module (string->module package))
349 (process-use-modules '(((guile-ilisp))))
352 (define-public (ilisp-eval form package filename line)
353 "Evaluate FORM in PACKAGE recording FILENAME as the source file
354 and LINE as the source code line there."
356 (read-from-string/source form filename line 1)
357 (string->module package)))
359 (define-public (ilisp-trace symbol package breakp)
360 (trace (eval-in-package symbol (string->module package)))
363 (define-public (ilisp-untrace symbol package)
364 (untrace (eval-in-package symbol (string->module package)))
367 (define (or-map* f list)
368 "Apply f to successive elements of l until exhaustion or improper end
369 or while f returns #f. If returning early, return the return value of f."
370 (let loop ((result #f)
374 (loop (f (car l)) (cdr l))))))
376 (define-public (ilisp-source-file symbol package)
377 "Find the source file of SYMBOL's definition in PACKAGE."
380 (let ((value (eval-in-package (read-from-string symbol)
381 (string->module package))))
383 ((and (procedure? value)
384 (procedure-source value))
389 (source-property s 'filename)))
391 (lambda (filename) (throw 'result filename))))))
394 (if (eq? key 'result)
395 (begin (write (car args)) (newline) (write #t))
396 (begin (write 'nil)))))
399 (define-public (ilisp-macroexpand-1 expression package)
400 (write (save-module-excursion
402 (set-current-module (string->module package))
403 (macroexpand-1 (read-from-string expression)))))
406 (define-public (ilisp-macroexpand expression package)
407 (write (save-module-excursion
409 (set-current-module (string->module package))
410 (macroexpand (read-from-string expression)))))