188 lines
11 KiB
Racket
188 lines
11 KiB
Racket
(module search racket/base
|
|
(require "struct.rkt"
|
|
"basic.rkt"
|
|
syntax/modcode)
|
|
|
|
(provide find-racket-tag
|
|
(rename-out [find-racket-tag find-scheme-tag]))
|
|
|
|
(define module-info-cache (make-hasheq))
|
|
|
|
(define (module-path-index-rejoin mpi rel-to)
|
|
(let-values ([(name base) (module-path-index-split mpi)])
|
|
(cond
|
|
[(not name) rel-to]
|
|
[(not base) mpi]
|
|
[else
|
|
(module-path-index-join name
|
|
(module-path-index-rejoin base rel-to))])))
|
|
|
|
(define (try thunk fail-thunk)
|
|
(with-handlers* ([exn:fail? (lambda (exn) (fail-thunk))])
|
|
(thunk)))
|
|
|
|
(define (find-racket-tag part ri stx/binding phase-level)
|
|
;; The phase-level argument is used only when `stx/binding'
|
|
;; is an identifier.
|
|
;;
|
|
;; Note: documentation keys currently don't distinguish different
|
|
;; phase definitions of an identifier from a source module.
|
|
;; That is, there's no way to document (define x ....) differently
|
|
;; from (define-for-syntax x ...). This isn't a problem in practice,
|
|
;; because no one uses the same name for different-phase exported
|
|
;; bindings.
|
|
;;
|
|
;; Formerly, we assumed that bindings are defined as originating from some
|
|
;; module at phase 0. [Maybe it's defined at phase 1 and re-exported
|
|
;; later for phase 0 (after a require-for-template), in which case the
|
|
;; re-exporting module is the one we find.] That assumption has been
|
|
;; lifted, however; search for "GONE" below.
|
|
(let ([b (cond
|
|
[(identifier? stx/binding)
|
|
(identifier-binding stx/binding phase-level)]
|
|
[(and (list? stx/binding)
|
|
(= 7 (length stx/binding)))
|
|
stx/binding]
|
|
[else
|
|
(and (not (symbol? (car stx/binding)))
|
|
(list #f
|
|
(cadr stx/binding)
|
|
(car stx/binding)
|
|
(cadr stx/binding)
|
|
(if (= 2 (length stx/binding))
|
|
0
|
|
(caddr stx/binding))
|
|
(if (= 2 (length stx/binding))
|
|
0
|
|
(cadddr stx/binding))
|
|
(if (= 2 (length stx/binding))
|
|
0
|
|
(cadddr (cdr stx/binding)))))])])
|
|
(and
|
|
(pair? b)
|
|
(let ([seen (make-hash)]
|
|
[search-key #f])
|
|
(let loop ([queue (list (list (caddr b) (cadddr b) (list-ref b 4) (list-ref b 5) (list-ref b 6)))]
|
|
[rqueue null]
|
|
[need-result? #t])
|
|
(cond
|
|
[(null? queue)
|
|
(if (null? rqueue)
|
|
;; Not documented
|
|
#f
|
|
(loop (reverse rqueue) null need-result?))]
|
|
[else
|
|
(let ([mod (list-ref (car queue) 0)]
|
|
[id (list-ref (car queue) 1)]
|
|
[defn-phase (list-ref (car queue) 2)]
|
|
[import-phase (list-ref (car queue) 3)]
|
|
[export-phase (list-ref (car queue) 4)]
|
|
[queue (cdr queue)])
|
|
(let* ([rmp (module-path-index-resolve mod)]
|
|
[eb (and ;; GONE: (equal? 0 export-phase) ;; look for the phase-0 export; good idea?
|
|
(list (module-path-index->taglet mod)
|
|
id))])
|
|
(when (and eb
|
|
(not search-key))
|
|
(set! search-key eb))
|
|
(let ([v (and eb (resolve-search search-key part ri `(dep ,eb)))])
|
|
(let* ([here-result
|
|
(and need-result?
|
|
v
|
|
(let ([v (resolve-get/tentative part ri `(form ,eb))])
|
|
(or (and v `(form ,eb))
|
|
`(def ,eb))))]
|
|
[need-result? (and need-result? (not here-result))]
|
|
[rmp-name (resolved-module-path-name rmp)])
|
|
;; Even if we've found `here-result', look deeper so that we have
|
|
;; consistent `dep' results.
|
|
(let ([nest-result
|
|
;; Maybe it's re-exported from this module...
|
|
;; Try a shortcut:
|
|
(if (eq? rmp (and (car b) (module-path-index-resolve (car b))))
|
|
;; Not defined through this path, so keep looking
|
|
(loop queue rqueue need-result?)
|
|
;; Check parents, if we can get the source:
|
|
(if (and (or (path? rmp-name)
|
|
(and (list? rmp-name)
|
|
(path? (car rmp-name))))
|
|
(not (hash-ref seen (cons export-phase rmp) #f)))
|
|
(let ([exports
|
|
(hash-ref
|
|
module-info-cache
|
|
rmp
|
|
(lambda ()
|
|
(let-values ([(valss stxess)
|
|
(try
|
|
(lambda ()
|
|
;; First, try using bytecode:
|
|
(module-compiled-exports
|
|
(get-module-code (if (list? rmp-name)
|
|
(car rmp-name)
|
|
rmp-name)
|
|
#:submodule-path (if (list? rmp-name)
|
|
(cdr rmp-name)
|
|
'())
|
|
#:choose (lambda (src zo so) 'zo))))
|
|
(lambda ()
|
|
(try
|
|
(lambda ()
|
|
;; Bytecode not available. Declaration in the
|
|
;; current namespace?
|
|
(module->exports rmp))
|
|
(lambda ()
|
|
(values null null)))))])
|
|
(let ([t
|
|
;; Merge the two association lists:
|
|
(let loop ([base valss]
|
|
[stxess stxess])
|
|
(cond
|
|
[(null? stxess) base]
|
|
[(assoc (caar stxess) base)
|
|
=> (lambda (l)
|
|
(loop (cons (cons (car l)
|
|
(append (cdar stxess)
|
|
(cdr l)))
|
|
(remq l base))
|
|
(cdr stxess)))]
|
|
[else (loop (cons (car stxess)
|
|
base)
|
|
(cdr stxess))]))])
|
|
(hash-set! module-info-cache rmp t)
|
|
t))))])
|
|
(hash-set! seen (cons export-phase rmp) #t)
|
|
(let ([a (assq id (let ([a (assoc export-phase exports)])
|
|
(if a
|
|
(cdr a)
|
|
null)))])
|
|
(if a
|
|
(loop queue
|
|
(append (map (lambda (m)
|
|
(if (pair? m)
|
|
(list (module-path-index-rejoin (car m) mod)
|
|
(list-ref m 2)
|
|
defn-phase
|
|
(list-ref m 1)
|
|
(list-ref m 3))
|
|
(list (module-path-index-rejoin m mod)
|
|
id
|
|
defn-phase
|
|
import-phase
|
|
export-phase)))
|
|
(reverse (cadr a)))
|
|
rqueue)
|
|
need-result?)
|
|
(begin
|
|
;; A dead end may not be our fault: the files could
|
|
;; have changed in inconsistent ways. So just say #f
|
|
;; for now.
|
|
#;
|
|
(error 'find-racket-tag
|
|
"dead end when looking for binding source: ~e"
|
|
id)
|
|
(loop queue rqueue need-result?)))))
|
|
;; Can't get the module source, so continue with queue:
|
|
(loop queue rqueue need-result?)))])
|
|
(or here-result
|
|
nest-result))))))])))))))
|