/pkgs/racket-test/tests/stxparse/select.rkt

http://github.com/plt/racket · Racket · 343 lines · 252 code · 59 blank · 32 comment · 8 complexity · ab2a44acba415d38f72b9f3f6fef6b7d MD5 · raw file

  1. #lang scheme
  2. (require rackunit
  3. syntax/parse)
  4. (require (for-syntax syntax/parse))
  5. (provide (all-defined-out))
  6. ;; Error selection tests
  7. (error-print-source-location #f)
  8. (define-syntax-rule (terx s p stuff ...)
  9. (terx* s [p] stuff ...))
  10. (define-syntax-rule (terx* s [p ...] stuff ...)
  11. (terx** s [[p] ...] stuff ...))
  12. (define-syntax terx**
  13. (syntax-parser
  14. [(terx s [[p c ...] ...] (~optional (~seq #:term term) #:defaults ([term #'#f])) rx ...)
  15. #`(test-case (format "line ~s: ~a match ~s for error"
  16. '#,(syntax-line #'s)
  17. 's '(p ...))
  18. (let ([exn (let/ec escape
  19. (check-exn (lambda (exn)
  20. (escape exn))
  21. (lambda ()
  22. (syntax-parse (quote-syntax s)
  23. [p c ... (void)] ...))))])
  24. (let ([msg (exn-message exn)]
  25. [stxs (and (exn:fail:syntax? exn)
  26. (exn:fail:syntax-exprs exn))])
  27. (when 'term
  28. (check-equal? (and (pair? stxs) (syntax->datum (car stxs))) 'term))
  29. (erx rx (exn-message exn)) ... #t))
  30. (void))]))
  31. (define-syntax erx
  32. (syntax-rules (not)
  33. [(erx (not rx) msg)
  34. (check (compose not regexp-match?) rx msg)]
  35. [(erx rx msg)
  36. (check regexp-match? rx msg)]))
  37. ;; ----
  38. (terx (a b c 7) (x:id ...)
  39. #:term 7
  40. #rx"expected identifier")
  41. ;; ----
  42. (terx* (1 2) [x:nat (y:id z:id)]
  43. #:term 1
  44. #rx"expected identifier")
  45. ;; --
  46. (define-syntax-class bindings
  47. (pattern ((var:id rhs:expr) ...)))
  48. (terx* ((x 1 2)) [x:id bs:bindings]
  49. #:term 2
  50. #rx"unexpected term")
  51. ;; --
  52. (terx ((a 1) (a 2))
  53. ((~or (~once ((~datum a) x) #:name "A clause")
  54. (~optional ((~datum b) y) #:name "B clause"))
  55. ...)
  56. ;; #:term (a 2)
  57. #rx"too many occurrences of A clause")
  58. ;; --
  59. (define-syntax-class A
  60. (pattern ((~datum a) x)))
  61. (define-syntax-class B
  62. (pattern ((~datum b) y)))
  63. (terx ((a 1) (a 2))
  64. ((~or (~once a:A #:name "A clause")
  65. (~optional b:B #:name "B clause"))
  66. ...)
  67. #rx"too many occurrences of A clause")
  68. (terx ((a 1 2) _)
  69. ((~or (~once a:A #:name "A clause")
  70. (~optional b:B #:name "B clause"))
  71. ...)
  72. #rx"unexpected term")
  73. (terx ((b 1 2) _)
  74. ((~or (~once a:A #:name "A clause")
  75. (~optional b:B #:name "B clause"))
  76. ...)
  77. #rx"unexpected term")
  78. ;; Ellipses
  79. (terx (a b c 4)
  80. (x:id ...)
  81. #rx"expected identifier")
  82. ;; Repetition constraints
  83. (terx (1 2)
  84. ((~or (~once x:id #:name "identifier") n:nat) ...)
  85. #rx"missing required occurrence of identifier")
  86. (terx (1 a 2 b)
  87. ((~or (~once x:id #:name "identifier") n:nat) ...)
  88. #rx"too many occurrences of identifier")
  89. ;; Roles
  90. (terx 1
  91. (~var x id #:role "var")
  92. #rx"expected identifier for var")
  93. (terx 1
  94. (~describe #:opaque #:role "R" "D" (_))
  95. #rx"expected D for R")
  96. (terx 1
  97. (~describe #:role "R" "D" (_))
  98. #rx"expected D for R")
  99. (test-case "#:describe #:role"
  100. (check-exn #rx"expected identifier for var"
  101. (lambda ()
  102. (syntax-parse #'1
  103. [x
  104. #:declare x id #:role "var"
  105. 'ok]))))
  106. (test-case "role coalescing"
  107. (check-exn #rx"^m: expected identifier for thing$" ;; not repeated
  108. (lambda ()
  109. (syntax-parse #'(m 0 b)
  110. [(_ x y:nat)
  111. #:declare x id #:role "thing"
  112. 'a]
  113. [(_ x y:id)
  114. #:declare x id #:role "thing"
  115. 'b]))))
  116. ;; Expected more terms
  117. (terx (1)
  118. (a b)
  119. #rx"expected more terms starting with any term$")
  120. (terx (1)
  121. (a b:id)
  122. #rx"expected more terms starting with identifier$")
  123. (terx (1)
  124. (a (~describe "thing" b))
  125. #rx"expected more terms starting with thing$")
  126. (let ()
  127. (define-syntax-class B1 #:description "B1" (pattern _:id))
  128. (define-syntax-class B2 (pattern _:id))
  129. (terx (1)
  130. (a b:B1)
  131. #rx"expected more terms starting with B1")
  132. (terx (1)
  133. (a b:B2)
  134. #rx"expected more terms starting with B2"))
  135. ;; Post:
  136. (terx "hello"
  137. (~or a:nat (~post a:id))
  138. #rx"expected identifier"
  139. (not #rx"exact-nonnegative-integer"))
  140. (terx "hello"
  141. (~or a:nat (~and (~post (~fail "xyz")) _))
  142. #rx"xyz"
  143. (not #rx"exact-nonnegative-integer"))
  144. (terx ("x")
  145. (~or (a:nat) (~post (a:id)))
  146. #rx"expected identifier"
  147. (not #rx"exact-nonnegative-integer"))
  148. ;; sequential ~and
  149. (terx 1
  150. (~and (~or x:nat x:id) (~fail "never happy"))
  151. #rx"never happy"
  152. (not #rx"expected identifier"))
  153. (terx** 1
  154. ([(~post (~or x:nat x:id)) #:fail-when #t "never happy"])
  155. #rx"never happy"
  156. (not #rx"expected identifier"))
  157. ;; indexes only compared within same ~and pattern
  158. (terx** 1
  159. ([(~and (~fail "banana") _)]
  160. [(~and x:nat (~fail "apple"))]
  161. [(~and x:nat y:nat (~fail "orange"))])
  162. #rx"apple"
  163. #rx"orange"
  164. #rx"banana")
  165. ;; default for min rep constraint
  166. (terx ()
  167. (x:id ...+)
  168. #rx"expected more terms starting with identifier")
  169. (let ()
  170. (define-syntax-class thing (pattern _))
  171. (terx ()
  172. (x:thing ...+)
  173. #rx"expected more terms starting with thing"))
  174. ;; ----------------------------------------
  175. ;; See "Simplification" from syntax/parse/private/runtime-report
  176. (define-syntax-class X #:opaque (pattern 1))
  177. (define-syntax-class Y #:opaque (pattern 2))
  178. (let ()
  179. ;; Case 1: [A B X], [A B Y]
  180. (define-syntax-class A (pattern (b:B _)))
  181. (define-syntax-class B (pattern (x:X _)) (pattern (y:Y _)))
  182. (terx ((3 _) _)
  183. a:A
  184. #:term 3
  185. #rx"expected X or expected Y"
  186. #rx"while parsing B.*while parsing A"))
  187. (let ()
  188. ;; Case 2: [A X], [A]
  189. (terx 1
  190. (~describe "A" (x:id ...))
  191. #rx"expected A"))
  192. (let ()
  193. ;; Case 3: [t1:A t2:B t3:X], [t1:A t2:C t3:Y]
  194. (define-syntax-class A (pattern (b:B _)) (pattern (c:C _)))
  195. (define-syntax-class B (pattern (x:X _)))
  196. (define-syntax-class C (pattern (y:Y _)))
  197. (terx ((3 _) _)
  198. a:A
  199. #:term 3
  200. #rx"expected X or expected Y"
  201. (not #rx"while parsing [BC]")
  202. #rx"while parsing A"))
  203. (let ()
  204. ;; Case 4: [t1:A t2:B t4:X], [t1:A t3:C t4:Y]
  205. (define-syntax-class A (pattern (b:B _)) (pattern (c:outerC _)))
  206. (define-syntax-class B (pattern (b:innerB _)))
  207. (define-syntax-class innerB #:description #f (pattern (x:X _)))
  208. (define-syntax-class outerC #:description #f (pattern (c:C _)))
  209. (define-syntax-class C (pattern (y:Y _)))
  210. (terx (((3 _) _) _)
  211. a:A
  212. #:term 3
  213. #rx"expected X or expected Y"
  214. (not #rx"while parsing (B|C|innerB|outerC|X|Y)")
  215. #rx"while parsing A"))
  216. (let ()
  217. ;; Case 5: [t1:A t2:B t3:X], [t1:A t4:C t5:Y]
  218. ;; Need to use ~parse to get t3 != t5
  219. (define-syntax-class A (pattern (b:B)) (pattern (c:outerC)))
  220. (define-syntax-class B (pattern (b:innerB)))
  221. (define-syntax-class innerB #:description #f (pattern _ #:with x:X #'4))
  222. (define-syntax-class outerC #:description #f (pattern (c:C)))
  223. (define-syntax-class C (pattern _ #:with y:Y #'5))
  224. (terx (((3)))
  225. a:A
  226. #:term (((3)))
  227. #rx"expected A"
  228. (not #rx"while parsing (A|B|C|innerB|outerC|X|Y)")))
  229. (let ()
  230. ;; Case 7: [_ t2:B t3:C _], [_ t3:C t2:B _]
  231. ;; Need to use ~parse; not sure if there's a realistic way for this to happen.
  232. ;; We will find the common frame, either B or C
  233. (define stxB #'4)
  234. (define stxC #'5)
  235. (define-syntax-class A
  236. (pattern (~and _ (~parse (~describe "B" (~and _ (~parse (~describe "C" 1) stxC))) stxB)))
  237. (pattern (~and _ (~parse (~describe "C" (~and _ (~parse (~describe "B" 2) stxB))) stxC))))
  238. (terx 3
  239. a:A
  240. ;; #:term {4 or 5}
  241. #rx"expected (B|C)"
  242. #rx"while parsing A"
  243. (not #rx"while parsing (B|C)")))
  244. ;; ------------------------------------------------------------
  245. ;; Regression tests
  246. ;; 4/16/2016, distilled from report by stchang
  247. ;; Want error message in second side clause to take precedence over
  248. ;; ellipsis-matching failures in first side clause.
  249. (test-case "side-clauses order 1"
  250. (check-exn #rx"unhappy about last number"
  251. (lambda ()
  252. (syntax-parse #'(1 2 3 4)
  253. [(x:nat ...)
  254. #:with (y ... z) #'(x ...)
  255. #:fail-unless (>= (syntax->datum #'z) 10)
  256. "unhappy about last number"
  257. 'ok]))))
  258. (test-case "side-clauses order 2"
  259. (check-exn (lambda (exn)
  260. (and (regexp-match? #rx"unhappy about last number" (exn-message exn))
  261. (exn:fail:syntax? exn)
  262. (let* ([terms (exn:fail:syntax-exprs exn)]
  263. [term (and (pair? terms) (syntax->datum (car terms)))])
  264. (check-equal? term '4))))
  265. (lambda ()
  266. (syntax-parse #'(1 2 3 4)
  267. [(x:nat ...)
  268. #:with (y ... z) #'(x ...)
  269. #:fail-when (and (< (syntax->datum #'z) 10) #'z)
  270. "unhappy about last number"
  271. 'ok]))))
  272. (test-case "side-clauses in different stxclasses don't compare"
  273. (check-exn #rx"message1 or message2"
  274. (lambda ()
  275. (syntax-parse #'(1 2 3 4)
  276. [(x:nat ...)
  277. #:with (y ... z) #'(x ...)
  278. #:fail-unless #f "message1" ;; (post 'g1 2)
  279. 'ok]
  280. [(x:nat ...)
  281. #:with (y ... z) #'(x ...)
  282. #:with w #'whatever
  283. #:fail-unless #f "message2" ;; (post 'g2 3), incomp w/ above
  284. 'ok]))))