#lang racket/base
(require (for-syntax racket/base
                     syntax/parse
                     racket/syntax
                     "../private/minimatch.rkt"
                     "../private/rep.rkt"
                     "../private/rep-data.rkt"
                     "../private/rep-patterns.rkt")
         "../private/keywords.rkt"
         "../private/sc.rkt")

(provide ~eh-var
         define-eh-alternative-set)

(define-syntax (define-eh-alternative-set stx)
  (define-syntax-class alt
    #:description "eh-alternate-set alternative"
    #:literals (pattern)
    (pattern (pattern alt)))
  (syntax-parse stx
    #:literals (pattern)
    [(_ name:id a:alt ...)
     (parameterize ((current-syntax-context stx))
       (let* ([decls (new-declenv null #:conventions null)]
              [ehpat+hstx-list
               (apply append
                      (for/list ([alt (in-list (syntax->list #'(a.alt ...)))])
                        (parse*-ellipsis-head-pattern alt decls #t #:context stx)))]
              [eh-alt+defs-list
               (for/list ([ehpat+hstx (in-list ehpat+hstx-list)])
                 (let ([ehpat (car ehpat+hstx)]
                       [hstx (cadr ehpat+hstx)])
                   (cond [(syntax? hstx)
                          (with-syntax ([(parser) (generate-temporaries '(eh-alt-parser))])
                            (let ([attrs (iattrs->sattrs (pattern-attrs (ehpat-head ehpat)))])
                              (list (eh-alternative (ehpat-repc ehpat) attrs #'parser)
                                    (list #`(define parser
                                              (parser/rhs parser () #,attrs
                                                          [#:description #f (pattern #,hstx)]
                                                          #t
                                                          #,stx))))))]
                         [(eh-alternative? hstx)
                          (list hstx null)]
                         [else
                          (error 'define-eh-alternative-set "internal error: unexpected ~e"
                                 hstx)])))]
              [eh-alts (map car eh-alt+defs-list)]
              [defs (apply append (map cadr eh-alt+defs-list))])
         (with-syntax ([(def ...) defs]
                       [(alt-expr ...)
                        (for/list ([alt (in-list eh-alts)])
                          (with-syntax ([repc-expr
                                         (match (eh-alternative-repc alt)
                                           ['#f
                                            #'(quote #f)]
                                           [(rep:once n u o)
                                            #`(rep:once (quote-syntax #,n)
                                                        (quote-syntax #,u)
                                                        (quote-syntax #,o))]
                                           [(rep:optional n o d)
                                            #`(rep:optional (quote-syntax #,n)
                                                            (quote-syntax #,o)
                                                            (quote-syntax #,d))]
                                           [(rep:bounds min max n u o)
                                            #`(rep:bounds (quote #,min)
                                                          (quote #,max)
                                                          (quote-syntax #,n)
                                                          (quote-syntax #,u)
                                                          (quote-syntax #,o))])]
                                        [attrs-expr
                                         #`(quote #,(eh-alternative-attrs alt))]
                                        [parser-expr
                                         #`(quote-syntax #,(eh-alternative-parser alt))])
                            #'(eh-alternative repc-expr attrs-expr parser-expr)))])
           #'(begin def ...
                    (define-syntax name
                      (eh-alternative-set (list alt-expr ...)))))))]))
