bcond.rkt
#lang racket

(require syntax/parse
         (for-syntax syntax/parse))

(provide bcond)

(define-syntax (bcond stx)
  (define-syntax-class distinct-identifiers
    #:description "sequence of distinct identifiers"
    (pattern (x:identifier ...)
             #:fail-when (check-duplicate-identifier
                          (syntax->list #'(x ...)))
                         "duplicate variable name"
             #:with (var ...) #'(x ...)))
  (syntax-parse stx #:literals (else => let let-values)
    [(bcond)
     #'(void)]
    [(bcond [let ~! x:identifier e:expr] clause ...)
     #'(let ([x e]) (bcond clause ...))]
    [(bcond [let-values ~! xs:distinct-identifiers e:expr] clause ...)
     #'(let-values ([(xs.var ...) e]) (bcond clause ...))]
    ;; deliberately do *NOT* force else to be last clause in bcond --
    ;; for benefit of those writing macros that expand into bcond.
    [(bcond [else ~! body:expr ...+] . _)
     #'(begin body ...)]
    [(bcond [e1:expr => ~! e2:expr] clause ...)
     #'(let ([tmp e1])
         (if tmp (e2 tmp) (bcond clause ...)))]
    [(bcond [e:expr] clause ...)
     #'(let ([tmp e])
         (if tmp tmp (bcond clause ...)))]
    ;; ...+ instead of ... in next pattern is redundant but clear
    [(bcond [e1:expr body:expr ...+] clause ...)
     #'(if e1 (begin body ...) (bcond clause ...))]))