123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- ;;; r6rs-libraries.scm --- Support for the R6RS `library' and `import' forms
- ;; Copyright (C) 2010, 2019, 2023 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 3 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
- ;; This file is included from boot-9.scm and assumes the existence of (and
- ;; expands into) procedures and syntactic forms defined therein.
- ;; Note that we can't use top-level define for helpers here as it will
- ;; pollute the (guile) module.
- (define (resolve-r6rs-interface import-spec)
- (define (sym? stx)
- (symbol? (syntax->datum stx)))
- (define (n? stx)
- (let ((n (syntax->datum stx)))
- (and (exact-integer? n)
- (not (negative? n)))))
- (define (colon-n? x)
- (let ((sym (syntax->datum x)))
- (and (symbol? sym)
- (let ((str (symbol->string sym)))
- (and (string-prefix? ":" str)
- (let ((num (string->number (substring str 1))))
- (and (exact-integer? num)
- (not (negative? num)))))))))
- (define (srfi-name? stx)
- (syntax-case stx (srfi)
- ((srfi n rest ...)
- (cond ((n? #'n) 'r7rs)
- ((colon-n? #'n) 'r6rs)
- (else #f)))
- (_ #f)))
- (define (module-name? stx)
- (or (srfi-name? stx)
- (syntax-case stx ()
- ((name name* ...)
- (and-map sym? #'(name name* ...)))
- (_ #f))))
- (define (make-srfi-n context n)
- (datum->syntax
- context
- (string->symbol
- (string-append
- "srfi-"
- (let ((n (syntax->datum n)))
- (if (symbol? n)
- (substring (symbol->string n) 1)
- (number->string n)))))))
- (define (make-custom-interface mod)
- (let ((iface (make-module)))
- (set-module-kind! iface 'custom-interface)
- (set-module-name! iface (module-name mod))
- iface))
- (define (module-for-each/nonlocal f mod)
- (define (module-and-uses mod)
- (let lp ((in (list mod)) (out '()))
- (cond
- ((null? in) (reverse out))
- ((memq (car in) out) (lp (cdr in) out))
- (else (lp (append (module-uses (car in)) (cdr in))
- (cons (car in) out))))))
- (for-each (lambda (mod)
- (module-for-each f mod))
- (module-and-uses mod)))
- (syntax-case import-spec (library only except prefix rename srfi)
- ;; XXX: This is R7RS-specific, but it's here since we want the
- ;; `import' procedure below to accept (srfi 64) as well as
- ;; (srfi :64).
- ;;
- ;; (srfi n ...) -> (srfi srfi-n ...)
- ((library (srfi n rest ... (version ...)))
- (eq? 'r7rs (srfi-name? #'(srfi n rest ...)))
- (let ((srfi-n (make-srfi-n #'srfi #'n)))
- (resolve-r6rs-interface
- #`(library (srfi #,srfi-n rest ... (version ...))))))
- ;; (srfi :n ...) -> (srfi srfi-n ...)
- ((library (srfi n rest ... (version ...)))
- (eq? 'r6rs (srfi-name? #'(srfi n rest ...)))
- (let ((srfi-n (make-srfi-n #'srfi #'n)))
- (resolve-r6rs-interface
- (syntax-case #'(rest ...) ()
- (()
- #`(library (srfi #,srfi-n (version ...))))
- ((name rest ...)
- ;; SRFI 97 says that the first identifier after the `n'
- ;; is used for the libraries name, so it must be ignored.
- #`(library (srfi #,srfi-n rest ... (version ...))))))))
- ((library (name name* ... (version ...)))
- (and-map sym? #'(name name* ...))
- (resolve-interface (syntax->datum #'(name name* ...))
- #:version (syntax->datum #'(version ...))))
- ((library (name name* ...))
- (and-map sym? #'(name name* ...))
- (resolve-r6rs-interface #'(library (name name* ... ()))))
- ((only import-set identifier ...)
- (and-map sym? #'(identifier ...))
- (let* ((mod (resolve-r6rs-interface #'import-set))
- (iface (make-custom-interface mod)))
- (for-each (lambda (sym)
- (module-add! iface sym
- (or (module-variable mod sym)
- (error "no binding `~A' in module ~A"
- sym mod)))
- (when (hashq-ref (module-replacements mod) sym)
- (hashq-set! (module-replacements iface) sym #t)))
- (syntax->datum #'(identifier ...)))
- iface))
- ((except import-set identifier ...)
- (and-map sym? #'(identifier ...))
- (let* ((mod (resolve-r6rs-interface #'import-set))
- (iface (make-custom-interface mod)))
- (module-for-each/nonlocal (lambda (sym var)
- (module-add! iface sym var))
- mod)
- (for-each (lambda (sym)
- (unless (module-local-variable iface sym)
- (error "no binding `~A' in module ~A" sym mod))
- (module-remove! iface sym))
- (syntax->datum #'(identifier ...)))
- iface))
- ((prefix import-set identifier)
- (sym? #'identifier)
- (let* ((mod (resolve-r6rs-interface #'import-set))
- (iface (make-custom-interface mod))
- (pre (syntax->datum #'identifier)))
- (module-for-each/nonlocal
- (lambda (sym var)
- (let ((sym* (symbol-append pre sym)))
- (module-add! iface sym* var)
- (when (hashq-ref (module-replacements mod) sym)
- (hashq-set! (module-replacements iface) sym* #t))))
- mod)
- iface))
- ((rename import-set (from to) ...)
- (and (and-map sym? #'(from ...)) (and-map sym? #'(to ...)))
- (let* ((mod (resolve-r6rs-interface #'import-set))
- (replacements (module-replacements mod))
- (iface (make-custom-interface mod)))
- (module-for-each/nonlocal
- (lambda (sym var) (module-add! iface sym var))
- mod)
- (let lp ((in (syntax->datum #'((from . to) ...))) (out '()))
- (cond
- ((null? in)
- (for-each
- (lambda (v)
- (let ((to (vector-ref v 0))
- (replace? (vector-ref v 1))
- (var (vector-ref v 2)))
- (when (module-local-variable iface to)
- (error "duplicate binding for `~A' in module ~A" to mod))
- (module-add! iface to var)
- (when replace?
- (hashq-set! replacements to #t))))
- out)
- iface)
- (else
- (let* ((from (caar in))
- (to (cdar in))
- (var (module-variable mod from))
- (replace? (hashq-ref replacements from)))
- (unless var (error "no binding `~A' in module ~A" from mod))
- (module-remove! iface from)
- (hashq-remove! replacements from)
- (lp (cdr in) (cons (vector to replace? var) out))))))))
- ((name name* ... (version ...))
- (module-name? #'(name name* ...))
- (resolve-r6rs-interface #'(library (name name* ... (version ...)))))
- ((name name* ...)
- (module-name? #'(name name* ...))
- (resolve-r6rs-interface #'(library (name name* ... ()))))))
- (define-syntax library
- (lambda (stx)
- (define (sym? stx)
- (symbol? (syntax->datum stx)))
- (define (module-name? stx)
- (syntax-case stx ()
- ((name name* ...)
- (and-map sym? #'(name name* ...)))
- (_ #f)))
- (define (compute-exports ifaces specs)
- (define (re-export? sym)
- (or-map (lambda (iface) (module-variable iface sym)) ifaces))
- (define (replace? sym)
- (module-variable the-scm-module sym))
-
- (let lp ((specs specs) (e '()) (r '()) (x '()))
- (syntax-case specs (rename)
- (() (values e r x))
- (((rename (from to) ...) . rest)
- (and (and-map identifier? #'(from ...))
- (and-map identifier? #'(to ...)))
- (let lp2 ((in #'((from . to) ...)) (e e) (r r) (x x))
- (syntax-case in ()
- (() (lp #'rest e r x))
- (((from . to) . in)
- (cond
- ((re-export? (syntax->datum #'from))
- (lp2 #'in e (cons #'(from . to) r) x))
- ((replace? (syntax->datum #'from))
- (lp2 #'in e r (cons #'(from . to) x)))
- (else
- (lp2 #'in (cons #'(from . to) e) r x)))))))
- ((id . rest)
- (identifier? #'id)
- (let ((sym (syntax->datum #'id)))
- (cond
- ((re-export? sym)
- (lp #'rest e (cons #'id r) x))
- ((replace? sym)
- (lp #'rest e r (cons #'id x)))
- (else
- (lp #'rest (cons #'id e) r x))))))))
- (syntax-case stx (export import srfi)
- ((_ (name name* ...)
- (export espec ...)
- (import ispec ...)
- body ...)
- (module-name? #'(name name* ...))
- ;; Add () as the version.
- #'(library (name name* ... ())
- (export espec ...)
- (import ispec ...)
- body ...))
- ((_ (name name* ... (version ...))
- (export espec ...)
- (import ispec ...)
- body ...)
- (module-name? #'(name name* ...))
- (call-with-values
- (lambda ()
- (compute-exports
- (map (lambda (im)
- (syntax-case im (for)
- ((for import-set import-level ...)
- (resolve-r6rs-interface #'import-set))
- (import-set (resolve-r6rs-interface #'import-set))))
- #'(ispec ...))
- #'(espec ...)))
- (lambda (exports re-exports replacements)
- (with-syntax (((e ...) exports)
- ((r ...) re-exports)
- ((x ...) replacements))
- ;; It would be nice to push the module that was current before the
- ;; definition, and pop it after the library definition, but I
- ;; actually can't see a way to do that. Helper procedures perhaps,
- ;; around a fluid that is rebound in save-module-excursion? Patches
- ;; welcome!
- #'(begin
- (define-module (name name* ...)
- #:pure
- #:version (version ...))
- (import ispec)
- ...
- (export e ...)
- (re-export r ...)
- (export! x ...)
- (@@ @@ (name name* ...) body)
- ...))))))))
- (define-syntax import
- (lambda (stx)
- (define (strip-for import-set)
- (syntax-case import-set (for)
- ((for import-set import-level ...)
- #'import-set)
- (import-set
- #'import-set)))
- (syntax-case stx ()
- ((_ import-set ...)
- (with-syntax (((library-reference ...) (map strip-for #'(import-set ...))))
- #'(eval-when (expand load eval)
- (let ((iface (resolve-r6rs-interface 'library-reference)))
- (call-with-deferred-observers
- (lambda ()
- (module-use-interfaces! (current-module) (list iface)))))
- ...
- (if #f #f)))))))
|