mirror of
https://github.com/ashinn/chibi-scheme.git
synced 2025-05-18 21:29:19 +02:00
262 lines
9.3 KiB
Scheme
262 lines
9.3 KiB
Scheme
;; modules.scm -- module introspection utilities
|
|
;; Copyright (c) 2011-2013 Alex Shinn. All rights reserved.
|
|
;; BSD-style license: http://synthcode.com/license.txt
|
|
|
|
;;> Module introspection library.
|
|
|
|
(define (file->sexp-list file)
|
|
(call-with-input-file file
|
|
(lambda (in)
|
|
(port-source?-set! in #t)
|
|
(let lp ((res '()))
|
|
(let ((x (read in)))
|
|
(if (eof-object? x)
|
|
(reverse res)
|
|
(lp (cons x res))))))))
|
|
|
|
(define (module? x)
|
|
(and (vector? x)
|
|
(>= (vector-length x) 4)
|
|
(or (list? (vector-ref x 0)) (not (vector-ref x 0)))))
|
|
|
|
(define (module-ast mod) (vector-ref mod 3))
|
|
(define (module-ast-set! mod x) (vector-set! mod 3 x))
|
|
|
|
(define (module-name mod)
|
|
(if (pair? mod)
|
|
(car mod)
|
|
(let lp ((ls *modules*))
|
|
(and (pair? ls)
|
|
(if (eq? mod (cdar ls))
|
|
(caar ls)
|
|
(lp (cdr ls)))))))
|
|
|
|
(define (module-dir mod)
|
|
(let ((name (module-name mod)))
|
|
(if (member name '((chibi) (meta)))
|
|
""
|
|
(module-name-prefix name))))
|
|
|
|
;; assuming mod-name was found in file, resolves to the containing lib dir
|
|
(define (module-lib-dir file mod-name)
|
|
(let lp ((ls (map (lambda (x)
|
|
(if (number? x) (number->string x) (symbol->string x)))
|
|
(reverse mod-name)))
|
|
(path (reverse (string-split (path-strip-extension file) #\/))))
|
|
(if (and (pair? ls) (pair? path) (equal? (car ls) (car path)))
|
|
(lp (cdr ls) (cdr path))
|
|
(if (null? path)
|
|
"."
|
|
(string-join (reverse path) "/")))))
|
|
|
|
(define (module-metas mod metas . o)
|
|
(let* ((mod (if (module? mod) mod (find-module mod)))
|
|
(dir (if (pair? o) (car o) (module-dir mod))))
|
|
(let lp ((ls (module-meta-data mod)) (res '()))
|
|
(cond
|
|
((not (pair? ls)) (reverse res))
|
|
((and (pair? (car ls)) (memq (caar ls) metas))
|
|
(lp (cdr ls) (append (reverse (cdar ls)) res)))
|
|
(else (lp (cdr ls) res))))))
|
|
|
|
(define (module-extract-declaration-files mod decls)
|
|
(let* ((mod (if (module? mod) mod (find-module mod)))
|
|
(dir (module-dir mod)))
|
|
(define (module-file f)
|
|
(find-module-file (string-append dir f)))
|
|
(map module-file (reverse (module-metas mod decls dir)))))
|
|
|
|
(define (module-includes mod)
|
|
(module-extract-declaration-files mod '(include)))
|
|
|
|
(define (module-include-library-declarations mod)
|
|
(module-extract-declaration-files mod '(include-library-declarations)))
|
|
|
|
(define (module-shared-includes mod)
|
|
(let* ((mod (if (module? mod) mod (find-module mod)))
|
|
(dir (module-dir mod)))
|
|
(define (module-file f)
|
|
(find-module-file (string-append dir f ".stub")))
|
|
(let lp ((ls (reverse (module-metas mod '(include-shared)))) (res '()))
|
|
(cond ((null? ls) (reverse res))
|
|
((module-file (car ls)) => (lambda (x) (lp (cdr ls) (cons x res))))
|
|
(else (lp (cdr ls) res))))))
|
|
|
|
(define (analyze-module-source name mod recursive?)
|
|
(let ((env (make-environment))
|
|
(dir (module-dir mod)))
|
|
(define (resolve-file file)
|
|
(find-module-file (string-append dir file)))
|
|
(define (include-source file)
|
|
(cond ((resolve-file file)
|
|
=> (lambda (x) (cons 'begin (file->sexp-list x))))
|
|
(else (error "couldn't find include" file))))
|
|
(cond
|
|
((equal? '(chibi) name)
|
|
(env-define! env '*features* *features*)
|
|
(env-define! env '*shared-object-extension* *shared-object-extension*)
|
|
(%import env (primitive-environment 7) #f #t))
|
|
(else
|
|
(resolve-module-imports env (module-meta-data mod))))
|
|
(let lp ((ls (module-meta-data mod)) (res '()))
|
|
(cond
|
|
((not (pair? ls))
|
|
(reverse res))
|
|
(else
|
|
(case (and (pair? (car ls)) (caar ls))
|
|
((import import-immutable)
|
|
(for-each
|
|
(lambda (m)
|
|
(let* ((mod2-name+imports (resolve-import m))
|
|
(mod2-name (car mod2-name+imports)))
|
|
(if recursive?
|
|
(analyze-module mod2-name #t))))
|
|
(cdar ls))
|
|
(lp (cdr ls) res))
|
|
((include include-ci)
|
|
(lp (append (map include-source (cdar ls)) (cdr ls)) res))
|
|
((include-library-declarations)
|
|
(lp (append (append-map file->sexp-list (map resolve-file (cdar ls))) (cdr ls)) res))
|
|
((include-shared include-shared-optionally)
|
|
(for-each
|
|
(lambda (file)
|
|
(let ((f (string-append file *shared-object-extension*)))
|
|
(cond ((find-module-file f) => (lambda (path) (load path env))))))
|
|
(cdar ls)))
|
|
((begin body)
|
|
(let lp2 ((ls2 (cdar ls)) (res res))
|
|
(cond
|
|
((pair? ls2)
|
|
(let ((x (analyze (car ls2) env)))
|
|
(eval (car ls2) env)
|
|
(lp2 (cdr ls2) (cons x res))))
|
|
(else
|
|
(lp (cdr ls) res)))))
|
|
(else
|
|
(lp (cdr ls) res))))))))
|
|
|
|
(define (analyze-module name . o)
|
|
(let ((recursive? (and (pair? o) (car o)))
|
|
(mod (load-module name)))
|
|
(cond
|
|
((not (module-ast mod))
|
|
(module-ast-set! mod '()) ; break cycles, just in case
|
|
(module-ast-set! mod (analyze-module-source name mod recursive?))))
|
|
mod))
|
|
|
|
(define (module-ref mod var-name . o)
|
|
(let ((cell (env-cell (module-env (if (module? mod) mod (load-module mod)))
|
|
var-name)))
|
|
(if cell
|
|
(cdr cell)
|
|
(if (pair? o) (car o) (error "no binding in module" mod var-name)))))
|
|
|
|
(define (module-contains? mod var-name)
|
|
(and (env-cell (module-env (if (module? mod) mod (load-module mod))) var-name)
|
|
#t))
|
|
|
|
(define (module-defines? name mod var-name)
|
|
(let lp ((ls (module-ast (analyze-module name))))
|
|
(cond
|
|
((null? ls) #f)
|
|
((and (set? (car ls))
|
|
(eq? var-name (ref-name (set-var (car ls))))))
|
|
((seq? (car ls)) (lp (append (seq-ls (car ls)) (cdr ls))))
|
|
(else (lp (cdr ls))))))
|
|
|
|
(define (containing-module x)
|
|
(let lp1 ((ls (reverse *modules*)))
|
|
(and (pair? ls)
|
|
(let ((env (module-env (cdar ls))))
|
|
(let lp2 ((e-ls (if (environment? env) (env-exports env) '())))
|
|
(if (null? e-ls)
|
|
(lp1 (cdr ls))
|
|
(let ((cell (env-cell env (car e-ls))))
|
|
(if (and (eq? x (cdr cell))
|
|
(or (opcode? x)
|
|
(module-defines? (caar ls) (cdar ls) (car cell))))
|
|
(car ls)
|
|
(lp2 (cdr e-ls))))))))))
|
|
|
|
(define (procedure-analysis x . o)
|
|
(cond
|
|
((opcode? x)
|
|
#f)
|
|
(else
|
|
(let ((name (if (procedure? x) (procedure-name x) x))
|
|
(mod (or (and (pair? o) (car o))
|
|
(containing-module x))))
|
|
(and mod
|
|
(let lp ((ls (module-ast (analyze-module (module-name mod)))))
|
|
(and (pair? ls)
|
|
(cond
|
|
((and (set? (car ls))
|
|
(eq? name (ref-name (set-var (car ls)))))
|
|
(set-value (car ls)))
|
|
((seq? (car ls))
|
|
(lp (append (seq-ls (car ls)) (cdr ls))))
|
|
(else
|
|
(lp (cdr ls)))))))))))
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
;; finding all available modules
|
|
|
|
(define (module-file? file)
|
|
(let ((len (string-length file)))
|
|
(and (> len 4) (equal? ".sld" (substring file (- len 4))))))
|
|
|
|
(define (read-static-modules file)
|
|
(protect (exn (else '()))
|
|
(call-with-input-file file
|
|
(lambda (in)
|
|
(let lp ((res '()))
|
|
(let ((expr (read in)))
|
|
(cond
|
|
((eof-object? expr)
|
|
res)
|
|
((and (pair? expr) (eq? 'define-library (car expr)))
|
|
(let ((name (cadr expr))
|
|
(exports (cond ((assq 'export (cddr expr)) => cdr)
|
|
(else '()))))
|
|
(lp (cons (cons name (make-module exports #f #f)) res))))
|
|
(else
|
|
(lp res)))))))))
|
|
|
|
(define no-module-depth-limit 2)
|
|
|
|
(define (available-modules-in-directory dir depth res)
|
|
(call-with-values
|
|
(lambda ()
|
|
(partition file-directory?
|
|
(map (lambda (f) (string-append dir "/" f))
|
|
(remove (lambda (f) (member f '("." "..")))
|
|
(directory-files dir)))))
|
|
(lambda (dirs files)
|
|
(let ((mods (append-map read-static-modules
|
|
(filter module-file? files))))
|
|
(if (and (null? mods) (>= depth no-module-depth-limit))
|
|
res
|
|
(let ((depth (if (pair? mods) 0 (+ 1 depth))))
|
|
(let lp ((ls dirs) (res (append mods res)))
|
|
(if (null? ls)
|
|
res
|
|
(lp (cdr ls)
|
|
(available-modules-in-directory (car ls) depth res)
|
|
)))))))))
|
|
|
|
(define (available-modules)
|
|
(let lp ((ls (current-module-path)) (res *modules*))
|
|
(if (null? ls)
|
|
res
|
|
(lp (cdr ls)
|
|
(available-modules-in-directory (car ls) 0 res)))))
|
|
|
|
(define (modules-exporting-identifier name)
|
|
(let lp ((ls (available-modules))
|
|
(res '()))
|
|
(cond
|
|
((null? ls) (reverse res))
|
|
((and (memq name (module-exports (cdar ls)))
|
|
(not (assoc (caar ls) res)))
|
|
(lp (cdr ls) (cons (car ls) res)))
|
|
(else (lp (cdr ls) res)))))
|