#lang racket

;; x,y ∈ Vars
;; LExp ::= y | (λ (x) LExp) | (LExp LExp)

;; Save this somewhere, you'll want it.
;; (define ...
;;   (lambda (exp ...)
;;     (match exp
;;       [`,y #:when (symbol? y) ...]
;;       [`(λ (,x) ,body) ...]
;;       [`(,rator ,rand) ...])))

;; (define v-o?
;;   (lambda (v e)
;;     (match e
;;       [`,y #:when (symbol? y) <think!>]
;;       [`(λ (,x) ,body) (<think!> (v-o? v body))]
;;       [`(,rator ,rand)
;;        (<think!>
;;         (v-o? v rator)
;;         (v-o? v rand))])))

;; A variable /occurs-free?/ in an expression if the variable /occurs/
;; in the expression and one of the occurrences of that variable is
;; not within the /scope/ of an enclosing lambda /binding/ that same
;; variable

(define v-o-f?
  (lambda (v e acc)
    (match e
      [`,y #:when (symbol? y)
        (and (not (memv v acc)) (eqv? v y))]
      [`(λ (,x) ,body)
       (v-o-f? v body (cons x acc))]
      [`(,rator ,rand)
       (or
        (v-o-f? v rator acc)
        (v-o-f? v rand acc))])))

;; A variable /occurs-bound?/ in an expression if the variable
;; /occurs/ in the expression and one of the occurrences of that
;; variable is within the /scope/ of an enclosing lambda /binding/
;; that same variable

(define v-o-b?
  (lambda (v e acc)
    (match e
      [`,y #:when (symbol? y)
       (and (memv v acc) (eqv? v y))]
      [`(λ (,x) ,body)
       (v-o-b? v body (cons x acc))]
      [`(,rator ,rand)
       (or
        (v-o-b? v rator acc)
        (v-o-b? v rand acc))])))

;; | Expression        | xo? | xf? | xb? | yo? | yf? | yb? |
;; | x                 | y   | y   | n   | n   | n   | n   |
;; | z                 | n   | n   | n   | n   | n   | n   |
;; | (λ (x) y)         | n   | n   | n   | y   | y   | n   |
;; | (λ (x) x)         | y   | n   | y   | n   | n   | n   |
;; | (λ (y) z)         | n   | n   | n   | n   | n   | n   |
;; | (λ (x) (λ (x) x)) | y   | n   | y   | n   | n   | n   |
;; | (λ (x) (λ (y) x)) | y   | n   | y   | n   | n   | n   |
;; | (x y)             | y   | y   | n   | y   | y   | n   |
;; | ((λ (x) x) x)     | y   | y   | y   | n   | n   | n   |
;; |                   |     |     |     |     |     |     |

;; The /lexical address/ of a variable occurrence is the number of
;; enclosing lambda abstractions before reaching the binding of that
;; variable occurrence. (i.e. the number of abstractions you have to
;; "jump over" before reaching the one you want.)

;; Also known as "de Bruijn index" of the variables.

;; (λ (a) 
;;   (λ (b) 
;;     (λ (c)
;;       (λ (a)
;;         ((a b) (c d))))))

;; lexes to 

;; ;;   ;;     ;;       ;;         ((0 2) (1 d))))))
;; d is a /free variable/.
;; Don't have those.

;; 'As Will Rogers might have said, "There's no such thing as a free
;; variable."'
;; – Alan J. Perlis