#lang br (require racket/struct (for-syntax br/datum)) (provide define-datatype cases occurs-free?) (define-macro (define-datatype BASE-TYPE BASE-TYPE-PREDICATE? (SUBTYPE [FIELD FIELD-PREDICATE?] ...) ...) #'(begin (struct BASE-TYPE () #:transparent #:mutable) (struct SUBTYPE BASE-TYPE (FIELD ...) #:transparent #:mutable #:guard (λ(FIELD ... name) (unless (FIELD-PREDICATE? FIELD) (error name (format "arg ~a is not ~a" FIELD 'FIELD-PREDICATE?))) ... (values FIELD ...))) ...)) (define-datatype lc-exp lc-exp? (var-exp [var symbol?]) (lambda-exp [bound-var symbol?] [body lc-exp?]) (app-exp [rator lc-exp?] [rand lc-exp?])) #;(define-syntax (cases stx) (syntax-case stx (else) [(_ _base-type INPUT-VAR [SUBTYPE (POSITIONAL-VAR ...) . _body] ... [else . _else-body]) (inject-syntax ([#'(_subtype? ...) (suffix-id #'(SUBTYPE ...) "?")]) #'(cond [(_subtype? INPUT-VAR) (match-let ([(list POSITIONAL-VAR ...) (struct->list INPUT-VAR)]) . _body)] ... [else . _else-body]))] [(_ _base-type INPUT-VAR SUBTYPE-CASE ...) #'(cases _base-type INPUT-VAR SUBTYPE-CASE ... [else (void)])])) (define-macro-cases cases [(_ BASE-TYPE INPUT-VAR [SUBTYPE (POSITIONAL-VAR ...) . BODY] ... [else . ELSE-BODY]) (with-syntax ([(SUBTYPE? ...) (suffix-id #'(SUBTYPE ...) "?")]) #'(cond [(SUBTYPE? INPUT-VAR) (match-let ([(list POSITIONAL-VAR ...) (struct->list INPUT-VAR)]) . BODY)] ... [else . ELSE-BODY]))] [(_ BASE-TYPE INPUT-VAR SUBTYPE-CASE ...) #'(cases BASE-TYPE INPUT-VAR SUBTYPE-CASE ... [else (void)])]) (define (occurs-free? search-var exp) (cases lc-exp exp [var-exp (var) (eqv? var search-var)] [lambda-exp (bound-var body) (and (not (eqv? search-var bound-var)) (occurs-free? search-var body))] [app-exp (rator rand) (or (occurs-free? search-var rator) (occurs-free? search-var rand))])) (module+ test (require rackunit) (check-true (occurs-free? 'foo (var-exp 'foo))) (check-false (occurs-free? 'foo (var-exp 'bar))) (check-false (occurs-free? 'foo (lambda-exp 'foo (var-exp 'bar)))) (check-true (occurs-free? 'foo (lambda-exp 'bar (var-exp 'foo)))) (check-true (occurs-free? 'foo (lambda-exp 'bar (lambda-exp 'zim (lambda-exp 'zam (var-exp 'foo)))))))