#lang racket

;; Testing all exp modules in one test module masks lazy-require-related bugs,
;; since some exp modules currently eagerly require impl modules.
;; So create separate submodules with minimal dependencies.

;; common defs
(module common racket
  (require syntax/parse)
  (provide nat>)
  (define-syntax-class (nat> x)
    #:description (format "natural number greater than ~s" x)
    (pattern n:nat
             #:when (> (syntax-e #'n) x)
             #:with diff (- (syntax-e #'n) x))))


;; Reflection
(module reflect racket
  (require rackunit "setup.rkt" syntax/parse syntax/parse/experimental/reflect
           (submod ".." common))

  (define r-nat> (reify-syntax-class nat>))

  (tok (1 2 -3 -4 5) ((~alt (~reflect yes (r-nat> 1) #:attributes (diff)) no) ...)
       (and (s= (yes ...) '(2 5))
            (s= (yes.diff ...) '(1 4))
            (s= (no ...) '(1 -3 -4))))
  (terx 3 (~reflect pos (r-nat> 5))
        #rx"expected natural number greater than 5")
  (terx whatever (~reflect x (r-nat> 0) #:attributes (wrong nope)))

  (define r-nat>1 (reified-syntax-class-curry r-nat> 1))

  (tok (1 2 -3 -4 5) ((~alt (~reflect yes (r-nat>1) #:attributes (diff)) no) ...)
       (and (s= (yes ...) '(2 5))
            (s= (yes.diff ...) '(1 4))
            (s= (no ...) '(1 -3 -4))))
  (terx 0 (~reflect pos (r-nat>1))
        #rx"expected natural number greater than 1")
  (terx whatever (~reflect x (r-nat>1) #:attributes (wrong nope)))

  (define-splicing-syntax-class opt
    (pattern (~seq #:a a:expr)))
  (define r-opt (reify-syntax-class opt))

  (tok (#:a 1) ((~splicing-reflect s (r-opt) #:attributes (a)))
       (s= s.a '1))
  (tok (#:a 1 #:a 2 #:a 3) ((~splicing-reflect s (r-opt) #:attributes (a)) ...)
       (s= (s.a ...) '(1 2 3)))
  )
(require 'reflect)


;; EH-alternative-sets
(module eh-alts racket
  (require rackunit "setup.rkt" syntax/parse syntax/parse/experimental/eh)

  (define-eh-alternative-set opts
    (pattern (~once (~seq #:a a:expr) #:name "A option"))
    (pattern (~seq #:b b:expr)))

  (tok (#:a 1) ((~eh-var s opts) ...)
       (and (s= s.a 1) (s= (s.b ...) '())))
  (tok (#:a 1 #:b 2 #:b 3) ((~eh-var s opts) ...)
       (and (s= s.a 1) (s= (s.b ...) '(2 3))))

  (terx (#:b 2 #:b 3) ((~eh-var s opts) ...)
        #rx"missing required occurrence of A option")
  (terx (#:a 1 #:a 2) ((~eh-var s opts) ...)
        #rx"too many occurrences of A option")

  (define-eh-alternative-set extopts
    (pattern (~eh-var s opts))
    (pattern (~seq #:c c1:expr c2:expr)))

  (tok (#:a 1 #:c 2 3 #:c 4 5) ((~eh-var x extopts) ...)
       (and (s= x.s.a 1) (s= (x.s.b ...) '())
            (s= ((x.c1 x.c2) ...) '((2 3) (4 5)))))
  (terx (#:c 1 2) ((~eh-var x extopts) ...)
        #rx"missing required occurrence of A option")
  )
(require 'eh-alts)


;; Splicing
(module splicing racket
  (require rackunit "setup.rkt" syntax/parse syntax/parse/experimental/splicing)

  (define-primitive-splicing-syntax-class (foo)
    #:attributes (z x y)
    #:description "foo"
    (lambda (stx fail)
      (syntax-case stx ()
        [(a b c . rest)
         (list 3 #'a #'b #'c)]
        [_ (fail)])))

  (tok (1 2 3 4) (f:foo 4)
       (and (s= f.z 1) (s= f.x 2) (s= f.y 3)))

  (terx (1) (f:foo)
        #rx"expected foo")
  )
(require 'splicing)


;; Specialization
(module specialize racket
  (require rackunit "setup.rkt" syntax/parse syntax/parse/experimental/specialize
           (submod ".." common))

  (define-syntax-class/specialize nat>10 (nat> 10))

  (tok (11 23 45) (n:nat>10 ...))
  (terx (11 10 9) (n:nat>10 ...)
        #rx"expected natural number greater than 10")

  (tcerr "specialize preserves #:no-delimit-cut"
         (let ()
           (define-syntax-class a #:no-delimit-cut (pattern _))
           (define-syntax-class/specialize b a)
           (syntax-parse #'12 [(~not x:b) (void)]))
         #rx"syntax class with #:no-delimit-cut option not allowed within ~not pattern")

  (test-case "specialize preserves lack of #:no-delimit-cut"
    (let ()
      (define-syntax-class a (pattern _:id))
      (define-syntax-class/specialize b a)
      (syntax-parse #'12 [(~not x:b) (void)])))
  )
(require 'specialize)
