281 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Scheme
		
	
	
	
			
		
		
	
	
			281 lines
		
	
	
		
			9.8 KiB
		
	
	
	
		
			Scheme
		
	
	
	
| ;;; Copyright (c) 2006, 2007 Abdulaziz Ghuloum and Kent Dybvig
 | |
| ;;; 
 | |
| ;;; Permission is hereby granted, free of charge, to any person obtaining a
 | |
| ;;; copy of this software and associated documentation files (the "Software"),
 | |
| ;;; to deal in the Software without restriction, including without limitation
 | |
| ;;; the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | |
| ;;; and/or sell copies of the Software, and to permit persons to whom the
 | |
| ;;; Software is furnished to do so, subject to the following conditions:
 | |
| ;;; 
 | |
| ;;; The above copyright notice and this permission notice shall be included in
 | |
| ;;; all copies or substantial portions of the Software.
 | |
| ;;; 
 | |
| ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| ;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| ;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | |
| ;;; THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
| ;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | |
| ;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | |
| ;;; DEALINGS IN THE SOFTWARE. 
 | |
| 
 | |
| (library (psyntax library-manager)
 | |
|   (export imported-label->binding library-subst installed-libraries
 | |
|     visit-library library-name library-version library-exists?
 | |
|     find-library-by-name install-library library-spec invoke-library 
 | |
|     extend-library-subst! extend-library-env! current-library-expander
 | |
|     current-library-collection)
 | |
|   (import (rnrs) (psyntax compat) (rnrs r5rs))
 | |
| 
 | |
|   (define (make-collection)
 | |
|     (let ((set '()))
 | |
|       (define (set-cons x ls)
 | |
|         (cond
 | |
|           ((memq x ls) ls)
 | |
|           (else (cons x ls))))
 | |
|       (case-lambda
 | |
|         (() set)
 | |
|         ((x) (set! set (set-cons x set))))))
 | |
| 
 | |
|   (define current-library-collection
 | |
|     ;;; this works now because make-collection is a lambda
 | |
|     ;;; binding and this turns into a complex binding as far
 | |
|     ;;; as letrec is concerned.  It will be more ok once we do
 | |
|     ;;; letrec*.
 | |
|     (make-parameter (make-collection)
 | |
|       (lambda (x)
 | |
|         (unless (procedure? x)
 | |
|           (error 'current-library-collection "not a procedure" x))
 | |
|         x)))
 | |
| 
 | |
|   (define-record library 
 | |
|     (id name version imp* vis* inv* subst env visit-state invoke-state visible?)
 | |
|     (lambda (x p)
 | |
|       (unless (library? x)
 | |
|         (error 'record-type-printer "not a library"))
 | |
|       (display 
 | |
|         (format "#<library ~s>" 
 | |
|           (if (null? (library-version x))
 | |
|               (library-name x)
 | |
|               (append (library-name x) (list (library-version x)))))
 | |
|         p)))
 | |
| 
 | |
|   (define (find-dependencies ls)
 | |
|     (cond
 | |
|       ((null? ls) '())
 | |
|       (else (error 'find-dependencies "cannot handle deps yet"))))
 | |
| 
 | |
|   (define (find-library-by pred)
 | |
|     (let f ((ls ((current-library-collection))))
 | |
|       (cond
 | |
|         ((null? ls) #f)
 | |
|         ((pred (car ls)) (car ls))
 | |
|         (else (f (cdr ls))))))
 | |
| 
 | |
|   (define library-path
 | |
|     (make-parameter
 | |
|       '(".")
 | |
|       (lambda (x)
 | |
|         (if (and (list? x) (for-all string? x))
 | |
|             ;(map values x)
 | |
|             (map (lambda (x) x) x)
 | |
|             (error 'library-path "not a list of strings" x)))))
 | |
|   
 | |
|   (define (library-name->file-name x) 
 | |
|     (let-values (((p extract) (open-string-output-port)))
 | |
|       (define (display-hex n)
 | |
|         (cond
 | |
|           ((<= 0 n 9) (display n p))
 | |
|           (else (display 
 | |
|                   (integer->char 
 | |
|                     (+ (char->integer #\A)
 | |
|                        (- n 10)))
 | |
|                   p))))
 | |
|       (let f ((ls x))
 | |
|         (cond
 | |
|           ((null? ls) (display ".ss" p))
 | |
|           (else
 | |
|            (display "/" p)
 | |
|            (for-each
 | |
|              (lambda (c)
 | |
|                (cond
 | |
|                  ((or (char<=? #\a c #\z)
 | |
|                       (char<=? #\A c #\Z)
 | |
|                       (char<=? #\0 c #\9)
 | |
|                       (memv c '(#\- #\. #\_ #\~)))
 | |
|                   (display c p))
 | |
|                  (else
 | |
|                   (display "%" p)
 | |
|                   (let ((n (char->integer c)))
 | |
|                     (display-hex (quotient n 16))
 | |
|                     (display-hex (remainder n 16))))))
 | |
|              (string->list 
 | |
|                (symbol->string (car ls))))
 | |
|            (f (cdr ls)))))
 | |
|       (extract)))
 | |
| 
 | |
|   (define file-locator
 | |
|     (make-parameter
 | |
|       (lambda (x)
 | |
|         (let ((str (library-name->file-name x)))
 | |
|           (let f ((ls (library-path)))
 | |
|             (and (pair? ls)
 | |
|                  (let ((name (string-append (car ls) str)))
 | |
|                    (if (file-exists? name)
 | |
|                        name
 | |
|                        (f (cdr ls))))))))
 | |
|       (lambda (f)
 | |
|         (if (procedure? f)
 | |
|             f
 | |
|             (error 'file-locator "not a procedure" f)))))
 | |
| 
 | |
|   (define library-locator
 | |
|     (make-parameter
 | |
|       (lambda (x)
 | |
|         (let ((file-name ((file-locator) x)))
 | |
|           (and (string? file-name)
 | |
|                (with-input-from-file file-name read))))
 | |
|       (lambda (f)
 | |
|         (if (procedure? f)
 | |
|             f
 | |
|             (error 'library-locator 
 | |
|                    "not a procedure" f)))))
 | |
| 
 | |
|   (define current-library-expander
 | |
|     (make-parameter
 | |
|       (lambda (x)
 | |
|         (error 'library-expander "not initialized"))
 | |
|       (lambda (f)
 | |
|         (if (procedure? f)
 | |
|             f
 | |
|             (error 'library-expander 
 | |
|                    "not a procedure" f)))))
 | |
| 
 | |
|   (define external-pending-libraries 
 | |
|     (make-parameter '()))
 | |
| 
 | |
|   (define (find-external-library name)
 | |
|     (when (member name (external-pending-libraries))
 | |
|       (error #f "circular attempt to import library was detected"
 | |
|              name))
 | |
|     (parameterize ((external-pending-libraries
 | |
|                     (cons name (external-pending-libraries))))
 | |
|       (let ((lib-expr ((library-locator) name)))
 | |
|         (unless lib-expr 
 | |
|           (error #f "cannot find library" name))
 | |
|         ((current-library-expander) lib-expr)
 | |
|         (or (find-library-by
 | |
|               (lambda (x) (equal? (library-name x) name)))
 | |
|             (error #f
 | |
|               "handling external library did not yield the currect library"
 | |
|                name)))))
 | |
|           
 | |
|   (define (find-library-by-name name)
 | |
|     (or (find-library-by
 | |
|           (lambda (x) (equal? (library-name x) name)))
 | |
|         (find-external-library name)))
 | |
| 
 | |
|   (define (library-exists? name)
 | |
|     (and (find-library-by
 | |
|            (lambda (x) (equal? (library-name x) name)))
 | |
|          #t))
 | |
| 
 | |
|   (define (find-library-by-spec/die spec)
 | |
|     (let ((id (car spec)))
 | |
|       (or (find-library-by
 | |
|             (lambda (x) (eq? id (library-id x))))
 | |
|           (error #f "cannot find library with required spec" spec))))
 | |
| 
 | |
|   (define label->binding-table (make-eq-hashtable))
 | |
| 
 | |
|   (define (install-library-record lib)
 | |
|     (let ((exp-env (library-env lib)))
 | |
|       (for-each 
 | |
|         (lambda (x) 
 | |
|           (let ((label (car x)) (binding (cdr x)))
 | |
|             (let ((binding 
 | |
|                    (case (car binding)
 | |
|                      ((global) 
 | |
|                       (cons 'global (cons lib (cdr binding))))
 | |
|                      ((global-macro)
 | |
|                       (cons 'global-macro (cons lib (cdr binding))))
 | |
|                      ((global-macro!)
 | |
|                       (cons 'global-macro! (cons lib (cdr binding))))
 | |
|                      (else binding))))
 | |
|               (hashtable-set! label->binding-table label binding))))
 | |
|         exp-env))
 | |
|     ((current-library-collection) lib))
 | |
| 
 | |
|   (define (install-library id name ver imp* vis* inv* 
 | |
|             exp-subst exp-env visit-code invoke-code visible?)
 | |
|     (let ((imp-lib* (map find-library-by-spec/die imp*))
 | |
|           (vis-lib* (map find-library-by-spec/die vis*))
 | |
|           (inv-lib* (map find-library-by-spec/die inv*)))
 | |
|       (unless (and (symbol? id) (list? name) (list? ver))
 | |
|         (error 'install-library "invalid spec with id/name/ver" id name ver))
 | |
|       (when (library-exists? name)
 | |
|         (error 'install-library "library is already installed" name))
 | |
|       (let ((lib (make-library id name ver imp-lib* vis-lib* inv-lib* 
 | |
|                     exp-subst exp-env visit-code invoke-code 
 | |
|                     visible?)))
 | |
|         (install-library-record lib))))
 | |
| 
 | |
|   (define extend-library-subst!
 | |
|     (lambda (lib sym label)
 | |
|       (set-library-subst! lib 
 | |
|         (cons (cons sym label) (library-subst lib)))))
 | |
| 
 | |
|   (define extend-library-env!
 | |
|     (lambda (lib label binding)
 | |
|       (set-library-env! lib
 | |
|         (cons (cons label binding) (library-env lib)))
 | |
|       (hashtable-set! label->binding-table label binding)))
 | |
| 
 | |
|   (define (imported-label->binding lab)
 | |
|     (hashtable-ref label->binding-table lab #f))
 | |
| 
 | |
|   (define (invoke-library lib)
 | |
|     (let ((invoke (library-invoke-state lib)))
 | |
|       (when (procedure? invoke)
 | |
|         (set-library-invoke-state! lib 
 | |
|           (lambda () (error 'invoke "circularity detected" lib)))
 | |
|         (for-each invoke-library (library-inv* lib))
 | |
|         (set-library-invoke-state! lib 
 | |
|           (lambda () (error 'invoke "first invoke did not return" lib)))
 | |
|         (invoke)
 | |
|         (set-library-invoke-state! lib #t))))
 | |
| 
 | |
| 
 | |
|   (define (visit-library lib)
 | |
|     (let ((visit (library-visit-state lib)))
 | |
|       (when (procedure? visit)
 | |
|         (set-library-visit-state! lib 
 | |
|           (lambda () (error 'visit "circularity detected" lib)))
 | |
|         (for-each invoke-library (library-vis* lib))
 | |
|         (set-library-visit-state! lib 
 | |
|           (lambda () (error 'invoke "first visit did not return" lib)))
 | |
|         (visit)
 | |
|         (set-library-visit-state! lib #t))))
 | |
| 
 | |
| 
 | |
|   (define (invoke-library-by-spec spec)
 | |
|     (invoke-library (find-library-by-spec/die spec)))
 | |
| 
 | |
|   (define installed-libraries 
 | |
|     (case-lambda
 | |
|       ((all?)
 | |
|        (let f ((ls ((current-library-collection))))
 | |
|          (cond
 | |
|            ((null? ls) '())
 | |
|            ((or all? (library-visible? (car ls)))
 | |
|             (cons (car ls) (f (cdr ls))))
 | |
|            (else (f (cdr ls))))))
 | |
|       (() (installed-libraries #f))))
 | |
| 
 | |
|   (define library-spec       
 | |
|     (lambda (x) 
 | |
|       (unless (library? x)
 | |
|         (error 'library-spec "not a library" x))
 | |
|       (list (library-id x) (library-name x) (library-version x)))) 
 | |
|   )
 | |
| 
 |