#lang racket/base (require rackunit syntax/parse (only-in syntax/parse/private/residual attribute-binding) syntax/parse/private/residual-ct ;; for attr functions syntax/macro-testing (for-syntax racket/base)) (provide tok t3d terx terx* tcerr bound s= a= convert-syntax-error) #| Testing forms ------------- (tok stx-template pattern [#:pre pre-pattern ...] [#:post post-pattern ...]) -- pattern should succeed parsing stx (pre and post patterns should fail) (terx stx-template pattern ErrorPattern ...) (terx* stx-template (pattern ...) ErrorPattern ...) where ErrorPattern is regexp | (not regexp) -- pattern should fail with exn message matching every ErrorPattern (tcerr tc-name-expr expr ErrorPattern ...) -- delays syntax errors in expr until runtime, error msg must every pattern Auxiliaries ----------- (bound (name depth [syntax?]) ...) -- checks that name is an attr w/ proper depth and syntax? (s= stx-template sexpr) -- checks that stx-template produces stx w/ datum equivalent to sexpr (a= attr expr) -- checks that attr has value equal to expr |# ;; tok = test pattern ok (define-syntax (tok stx) (syntax-case stx () [(tok s p expr #:pre [pre-p ...] #:post [post-p ...]) #`(test-case (format "line ~s: ~s match ~s" '#,(syntax-line #'s) 's 'p) (syntax-parse (quote-syntax s) [pre-p (error 'wrong-pattern "~s" 'pre-p)] ... [p expr] [post-p (error 'wrong-pattern "~s" 'post-p)] ...) (void))] [(tok s p expr) #'(tok s p expr #:pre () #:post ())] [(tok s p) #'(tok s p 'ok)])) (define-syntax (t3d stx) (syntax-case stx () [(_ #:pass [pass ...] #:fail [fail ...]) (with-syntax ([(pass-line ...) (map syntax-line (syntax->list #'(pass ...)))] [(fail-line ...) (map syntax-line (syntax->list #'(fail ...)))]) #`(begin (test-case (format "line ~s: ~a should not be 3D syntax" 'pass-line 'pass) (check-not-exn (λ () (syntax-parse (quote-syntax ()) [_ #:with _ pass 'ok])))) ... (test-case (format "line ~s: ~a should be rejected 3D syntax" 'fail-line 'fail) (check-exn #px"implicit conversion to 3D syntax" (λ () (syntax-parse (quote-syntax ()) [_ #:with _ fail 'ok])))) ...))])) (define-syntax-rule (bound b ...) (begin (bound1 b) ...)) (define-syntax bound1 (syntax-rules () [(bound1 (name depth)) (let ([a (attribute-binding name)]) (check-pred attr? a) (when (attr? a) (check-equal? (attr-depth a) 'depth)))] [(bound1 (name depth syntax?)) (let ([a (attribute-binding name)]) (check-pred attr? a) (when (attr? a) (check-equal? (attr-depth a) 'depth) (check-equal? (attr-syntax? a) 'syntax?)))])) (define-syntax-rule (s= t v) (check-equal? (syntax->datum #'t) v)) (define-syntax-rule (a= a v) (check-equal? (attribute a) v)) (define-syntax-rule (terx s p rx ...) (terx* s [p] rx ...)) (define-syntax (terx* stx) (syntax-case stx () [(terx s [p ...] rx ...) #`(test-case (format "line ~s: ~a match ~s for error" '#,(syntax-line #'s) 's '(p ...)) (check-exn (lambda (exn) (erx rx (exn-message exn)) ... #t) (lambda () (syntax-parse (quote-syntax s) [p 'ok] ...))) (void))])) (define-syntax erx (syntax-rules (not) [(erx (not rx) msg) (check (compose not regexp-match?) rx msg)] [(erx rx msg) (check regexp-match? rx msg)])) ;; ==== (define-syntax-rule (tcerr name expr rx ...) (test-case name (check-exn (lambda (exn) (define msg (exn-message exn)) (erx rx msg) ... #t) (lambda () (parameterize ((error-print-source-location #f)) (convert-syntax-error expr)))) (void)))