racket/collects/tests/stxparse/stxclass.rkt
2010-05-17 12:07:32 -04:00

236 lines
7.1 KiB
Racket

#lang scheme/base
(require rackunit
syntax/parse
(for-syntax scheme/base syntax/parse))
;; Testing stuff
(define-namespace-anchor anchor)
(define tns (namespace-anchor->namespace anchor))
(define (teval expr) (eval expr tns))
(define-syntax-rule (stx-like? expr template)
(equal? (stx->datum expr) 'template))
(define (stx->datum expr)
(syntax->datum (datum->syntax #f expr)))
;; Syntax classes
(define-syntax-class one
(pattern a))
(define-syntax-class two
(pattern (a b)))
(define-syntax-class three
(pattern (a b c)))
(define-syntax-class two-or-three/flat
(pattern (a b))
(pattern (a b c)))
(define-syntax-class two-or-three/tag
#:attributes (a a.a a.b)
(pattern a:two)
(pattern a:three))
(define-syntax-class two-to-four/untagged
#:attributes (a b)
(pattern :two)
(pattern :three)
(pattern (a b c d)))
(define-syntax-class xs
(pattern (x ...)))
(define-syntax-class pairs
(pattern ((x y) ...)))
(define-syntax-class id-num
(pattern (x n)
#:declare x id
#:declare n number))
(define-syntax-class id-string
(pattern (x:id label:str)))
;; Test macros
(define-syntax-rule (test-sc-attrs name ([attr depth] ...))
(test-case (format "~s" 'name)
(let* ([r-attrs (syntax-class-attributes name)]
[r-names (map car r-attrs)]
[expected '((attr depth) ...)])
(for ([ra r-names])
(check-memq ra '(attr ...) "Unexpected attr returned"))
(for ([a '(attr ...)])
(check-memq a r-names "Expected attr not returned"))
(for ([rec r-attrs])
(let ([ex (assq (car rec) expected)])
(check-equal? (cadr rec) (cadr ex) "Wrong depth returned"))))))
(define-simple-check (check-memq item items)
(memq item items))
(define-syntax-rule (test-parse-sc sc stx ([attr depth form] ...))
(test-case (format "~s" 'sc)
(let* ([r (syntax-class-parse sc stx)]
[r-attrs (for/list ([record r]) (vector-ref record 0))]
[expected '([attr depth form] ...)])
(for ([ra r-attrs])
(check-memq ra '(attr ...) "Unexpected attr returned"))
(for ([a '(attr ...)])
(check-memq a r-attrs "Expected attr not returned"))
(for ([rec r])
(let ([ex (assq (vector-ref rec 0) expected)])
(check-equal? (vector-ref rec 1) (cadr ex))
(check-equal? (stx->datum (vector-ref rec 2)) (caddr ex)))))))
(define-syntax-rule (test-patterns pattern stx . body)
(test-case (format "~s" 'pattern)
(syntax-parse stx [pattern . body])))
;; Tests
(begin ;; define tests
(begin ;; test-suite "Syntax grammars"
(begin ;; test-suite "sc attrs"
(test-sc-attrs one ([a 0]))
(test-sc-attrs two ([a 0] [b 0]))
(test-sc-attrs three ([a 0] [b 0] [c 0]))
(test-sc-attrs two-or-three/tag ([a 0] [a.a 0] [a.b 0]))
(test-sc-attrs id-num ([x 0] [n 0])))
(begin ;; test-suite "parse-sc"
(test-parse-sc one #'1 ([a 0 1]))
(test-parse-sc two #'(1 2) ([a 0 1] [b 0 2]))
(test-parse-sc three #'(1 2 3) ([a 0 1] [b 0 2] [c 0 3]))
(test-parse-sc two-or-three/tag #'(1 2 3)
([a 0 (1 2 3)] [a.a 0 1] [a.b 0 2]))
(test-parse-sc id-num #'(this 12)
([x 0 this] [n 0 12]))
(test-parse-sc id-string #'(that "here")
([x 0 that] [label 0 "here"])))
(begin ;; test-suite "with-patterns"
(test-patterns (t:two-to-four/untagged ...) #'((1 2 3) (4 5) (6 7 8))
(check-equal? (syntax->datum #'(t.a ...)) '(1 4 6)))
(test-patterns (t:two-to-four/untagged ...) #'((1 2 3) (4 5) (6 7 8))
(check-equal? (syntax->datum #'(t.b ...)) '(2 5 7)))
(test-patterns ({~or {~seq x:id v:nat} s:str} ...) #'(x 1 y 2 "whee" x 3)
(check-equal? (stx->datum #'((x v) ...)) '((x 1) (y 2) (x 3)))
(check-equal? (stx->datum #'(s ...)) '("whee")))
(test-patterns ({~or {~seq x:id v:nat} s:str} ...) #'(x 1 y 2 "whee" x 3)
(check-equal? (stx->datum #'((x v) ...)) '((x 1) (y 2) (x 3)))
(check-equal? (stx->datum #'(s ...)) '("whee")))
(test-patterns ({~or (~once 1)
(~once 2)
(~once 3)} ...)
#'(1 2 3)
'ok)
(test-patterns ({~or a:id b:nat c:str} ...) #'("one" 2 three)
(check-equal? (stx->datum #'(a ...)) '(three))
(check-equal? (stx->datum #'(b ...)) '(2))
(check-equal? (stx->datum #'(c ...)) '("one")))
(test-patterns ({~or (~once 1)
(~once 2)
(~once 3)
(~once x)
(~once y)
(~once w)} ...)
#'(1 2 3 x y z)
(for ([s (syntax->list #'(x y w))]) (check-pred identifier? s))
(check-equal? (sort
(map symbol->string (stx->datum #'(x y w)))
string<?)
'("x" "y" "z")))
(test-patterns ({~or x
(~once 1)
(~once 2)
(~once 3)} ...)
#'(1 2 3 x y z)
(check-equal? (stx->datum #'(x ...)) '(x y z)))
)))
(define-syntax-class bindings
(pattern ((var:id e) ...)
#:with vars #'(var ...)))
(define-syntax-class sorted
(pattern (n:nat ...)
#:fail-unless (sorted? (syntax->datum #'(n ...))) "not sorted"))
(define (sorted? ns)
(define (loop ns min)
(cond [(pair? ns)
(and (<= min (car ns))
(loop (cdr ns) (car ns)))]
[(null? ns) #t]))
(loop ns -inf.0))
(define-syntax-class Opaque
(pattern (a:id n:nat)))
(define-syntax-class Transparent
#:transparent
(pattern (a:id n:nat)))
(with-handlers ([exn? exn-message])
(syntax-parse #'(0 1) [_:Opaque 'ok]))
(with-handlers ([exn? exn-message])
(syntax-parse #'(0 1) [_:Transparent 'ok]))
(syntax-parse #'(+) #:literals ([plus +])
[(plus) (void)])
(define-syntax-class (Nat> n)
#:description (format "Nat > ~s" n)
(pattern x:nat #:fail-unless (> (syntax-e #'x) n) #f))
(syntax-parse #'(1 2 3)
[(a:nat b0:nat c0:nat)
#:with b #'b0
#:declare b (Nat> (syntax-e #'a))
#:with c #'c0
#:declare c (Nat> (syntax-e #'b0))
(void)])
(define-syntax-class (nat> bound)
#:opaque
#:description (format "natural number greater than ~s" bound)
(pattern n:nat
#:when (> (syntax-e #'n) bound)))
(define-conventions nat-convs
[N (nat> 0)])
(syntax-parse #'(5 4) #:conventions (nat-convs)
[(N ...) (void)])
(let/ec escape
(with-handlers ([exn? (compose escape void)])
(syntax-parse #'(4 -1) #:conventions (nat-convs)
[(N ...) (void)]))
(error 'test-conv1 "didn't work"))
;; Local conventions
(define-syntax-class (nats> bound)
#:local-conventions ([N (nat> bound)])
(pattern (N ...)))
(define (p1 bound x)
(syntax-parse x
#:local-conventions ([ns (nats> bound)])
[ns 'yes]
[_ 'no]))
(eq? (p1 0 #'(1 2 3)) 'yes)
(eq? (p1 2 #'(1 2 3)) 'no)
;; Regression (2/2/2010)
(define-splicing-syntax-class twoseq
(pattern (~seq a b)))
(syntax-parse #'(1 2 3 4)
[(x:twoseq ...) 'ok])