free-vars: add mode that also reports module-bound variables

As a separate mode, for backwards compatibility.
This commit is contained in:
Vincent St-Amour 2015-12-03 15:39:34 -06:00
parent 22caaad944
commit 5353dd1076
3 changed files with 34 additions and 12 deletions

View File

@ -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.

View File

@ -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)))

View File

@ -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]