free-vars: add mode that also reports module-bound variables
As a separate mode, for backwards compatibility.
This commit is contained in:
parent
22caaad944
commit
5353dd1076
|
@ -5,7 +5,9 @@
|
|||
|
||||
@defmodule[syntax/free-vars]
|
||||
|
||||
@defproc[(free-vars [expr-stx syntax?] [insp inspector? _mod-decl-insp])
|
||||
@defproc[(free-vars [expr-stx syntax?]
|
||||
[insp inspector? _mod-decl-insp]
|
||||
[#:module-bound? module-bound? any/c #f])
|
||||
(listof identifier?)]{
|
||||
|
||||
Returns a list of free @racket[lambda]- and @racket[let]-bound
|
||||
|
@ -18,3 +20,6 @@ The inspector @racket[insp] is used to disarm @racket[expr-stx] and
|
|||
sub-expressions before extracting identifiers. The default
|
||||
@racket[insp] is the declaration-time inspector of the
|
||||
@racketmodname[syntax/free-vars] module.}
|
||||
|
||||
If @racket[module-bound?] is non-false, the list of free variables also
|
||||
includes free module-bound identifiers.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#lang racket
|
||||
(require syntax/free-vars)
|
||||
(require syntax/free-vars
|
||||
rackunit)
|
||||
|
||||
(parameterize ([current-namespace (make-base-namespace)])
|
||||
(define (check stx)
|
||||
|
@ -40,3 +41,9 @@
|
|||
'(x)
|
||||
(let ([y 3])
|
||||
(list x y)))))
|
||||
|
||||
(check-equal? (free-vars (expand #'(+ 1 2)))
|
||||
'())
|
||||
(check-pred (lambda (x) (free-identifier=? x #'+))
|
||||
(first (free-vars (expand #'(+ 1 2))
|
||||
#:module-bound? #t)))
|
||||
|
|
|
@ -43,12 +43,15 @@
|
|||
[(null? f) null]
|
||||
[(syntax? f) (loop (syntax-e f))])))
|
||||
|
||||
;; free-vars : expr-stx -> (listof id)
|
||||
;; free-vars : expr-stx [inspector?] [#:module-bound? any/c] -> (listof id)
|
||||
;; Returns a list of free lambda- and let-bound identifiers in a
|
||||
;; given epression. The expression must be fully expanded.
|
||||
(define (free-vars e [code-insp
|
||||
(variable-reference->module-declaration-inspector
|
||||
(#%variable-reference))])
|
||||
;; If `module-bound?` is true, also return module-bound variables.
|
||||
(define (free-vars e
|
||||
[code-insp
|
||||
(variable-reference->module-declaration-inspector
|
||||
(#%variable-reference))]
|
||||
#:module-bound? [module-bound? #f])
|
||||
(define (submodule-error e)
|
||||
(error 'free-vars "submodules not supported: ~a" e))
|
||||
;; It would be nicer to have a functional mapping:
|
||||
|
@ -56,12 +59,19 @@
|
|||
(merge
|
||||
(let free-vars ([e e])
|
||||
(kernel-syntax-case (syntax-disarm e code-insp) #f
|
||||
[id
|
||||
(identifier? #'id)
|
||||
(if (and (eq? 'lexical (identifier-binding #'id))
|
||||
(not (bound-identifier-mapping-get bindings #'id (lambda () #f))))
|
||||
(list #'id)
|
||||
null)]
|
||||
[id
|
||||
(identifier? #'id)
|
||||
(let ([b (identifier-binding #'id)])
|
||||
(cond [(and (eq? 'lexical b)
|
||||
(not (bound-identifier-mapping-get bindings #'id (lambda () #f))))
|
||||
(list #'id)]
|
||||
[(and module-bound? ; do we count module-bound vars too?
|
||||
;; we're in an expression context, so any module-bound
|
||||
;; variable is free
|
||||
(list? b))
|
||||
(list #'id)]
|
||||
[else
|
||||
null]))]
|
||||
[(#%top . id) null]
|
||||
[(quote q) null]
|
||||
[(quote-syntax . _) null]
|
||||
|
|
Loading…
Reference in New Issue
Block a user