#lang plait
(define-type Exp
[numE (val : Number)]
[plusE (l : Exp) (r : Exp)]
[varE (name : Symbol)]
[let1E (id : Symbol) (named-expr : Exp) (bound-body : Exp)]
[lamE (param : Symbol) (body : Exp)]
[appE (fun : Exp) (val : Exp)])
(define (bound name env)
(if (none? (hash-ref env name)) #f #t))
(define (extend name env)
(hash-set env name #t))
(define empty-env (hash empty))
(test (bound 'x empty-env) #f)
(test (bound 'x (extend 'y (extend 'x empty-env))) #t)
(define (union the-set other-set)
(hash (map (lambda (x) (pair x #t))
(append (hash-keys the-set) (hash-keys other-set)))))
(define (free-vars expr env)
(type-case Exp expr
[(numE n) empty-env]
[(plusE l r) (union (free-vars l env) (free-vars r env))]
[(let1E bound-id named-expr bound-body) ....]
[(varE name) (if (bound name env) empty-env (extend name empty-env))]
[(lamE bound-id bound-body) ....]
[(appE fun-expr arg-expr) ....]))
(test (free-vars (numE 3) empty-env) empty-env)
(test (free-vars (plusE (varE 'x) (numE 3)) empty-env)
(extend 'x empty-env))
(test (free-vars (plusE (varE 'x) (numE 3))
(extend 'x empty-env)) empty-env)
(test (free-vars (let1E 'x (numE 3) (plusE (varE 'x) (numE 3)))
empty-env) empty-env)
(test (free-vars (let1E 'x (numE 3) (plusE (varE 'x) (numE 3)))
(extend 'x empty-env)) empty-env)
(test (free-vars (let1E 'x (varE 'y) (plusE (varE 'x) (numE 3)))
(extend 'x empty-env)) (extend 'y empty-env))
(test (free-vars (lamE 'x (plusE (varE 'x) (numE 3)))
(extend 'x empty-env)) empty-env)
(test (free-vars (appE (varE 'x) (varE 'x)) empty-env)
(extend 'x empty-env))
(define-syntax nand
(syntax-rules () [(_ arg ...) ....]))
(test (nand #f) #t)
(test (nand #t (begin (/ 1 0) #t)) #f)
(test (nand #f #t (eq? (/ 1 0) 0)) #f)