reader.test 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. ;;;; reader.test --- Reader test. -*- coding: iso-8859-1; mode: scheme -*-
  2. ;;;;
  3. ;;;; Copyright (C) 1999,2001-2003,2007-2011,2013-2015,2020,2021
  4. ;;;; Free Software Foundation, Inc.
  5. ;;;;
  6. ;;;; Jim Blandy <jimb@red-bean.com>
  7. ;;;;
  8. ;;;; This library is free software; you can redistribute it and/or
  9. ;;;; modify it under the terms of the GNU Lesser General Public
  10. ;;;; License as published by the Free Software Foundation; either
  11. ;;;; version 3 of the License, or (at your option) any later version.
  12. ;;;;
  13. ;;;; This library is distributed in the hope that it will be useful,
  14. ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. ;;;; Lesser General Public License for more details.
  17. ;;;;
  18. ;;;; You should have received a copy of the GNU Lesser General Public
  19. ;;;; License along with this library; if not, write to the Free Software
  20. ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21. (define-module (test-suite reader)
  22. :use-module (srfi srfi-1)
  23. :use-module (test-suite lib))
  24. (define exception:eof
  25. (cons 'read-error "unexpected end of input"))
  26. (define exception:unexpected-rparen
  27. (cons 'read-error "unexpected \")\"$"))
  28. (define exception:unexpected-rsqbracket
  29. (cons 'read-error "unexpected \"]\"$"))
  30. (define exception:unterminated-block-comment
  31. (cons 'read-error "unterminated `#. \\.\\.\\. .#' comment$"))
  32. (define exception:unknown-character-name
  33. (cons 'read-error "unknown character name .*$"))
  34. (define exception:unknown-sharp-object
  35. (cons 'read-error "Unknown # object: .*$"))
  36. (define exception:eof-in-string
  37. (cons 'read-error "end of input while reading string$"))
  38. (define exception:eof-in-symbol
  39. (cons 'read-error "end of input while reading symbol$"))
  40. (define exception:invalid-escape
  41. (cons 'read-error "invalid character in escape sequence: .*$"))
  42. (define exception:missing-expression
  43. (cons 'read-error "no expression after #;"))
  44. (define exception:mismatched-paren
  45. (cons 'read-error "mismatched close paren"))
  46. (define (read-string s)
  47. (with-input-from-string s (lambda () (read))))
  48. (define (with-read-options opts thunk)
  49. (let ((saved-options (read-options)))
  50. (dynamic-wind
  51. (lambda ()
  52. (read-options opts))
  53. thunk
  54. (lambda ()
  55. (read-options saved-options)))))
  56. (define (read-string-as-list s)
  57. (with-input-from-string s
  58. (lambda ()
  59. (unfold eof-object? values (lambda (x) (read)) (read)))))
  60. (with-test-prefix "reading"
  61. (pass-if "0"
  62. (equal? (read-string "0") 0))
  63. (pass-if "1++i"
  64. (equal? (read-string "1++i") '1++i))
  65. (pass-if "1+i+i"
  66. (equal? (read-string "1+i+i") '1+i+i))
  67. (pass-if "1+e10000i"
  68. (equal? (read-string "1+e10000i") '1+e10000i))
  69. (pass-if "-nan.0-1i"
  70. (not (equal? (imag-part (read-string "-nan.0-1i"))
  71. (imag-part (read-string "-nan.0+1i")))))
  72. (pass-if-equal "'\|' in string literals"
  73. "a|b"
  74. (read-string "\"a\\|b\""))
  75. (pass-if-equal "'(' in string literals"
  76. "a(b"
  77. (read-string "\"a\\(b\""))
  78. (pass-if-equal "#\\escape"
  79. '(a #\esc b)
  80. (read-string "(a #\\escape b)"))
  81. (pass-if-equal "#true"
  82. '(a #t b)
  83. (read-string "(a #true b)"))
  84. (pass-if-equal "#false"
  85. '(a #f b)
  86. (read-string "(a #false b)"))
  87. ;; At one time the arg list for "Unknown # object: ~S" didn't make it out
  88. ;; of read.c. Check that `format' can be applied to this error.
  89. (pass-if "error message on bad #"
  90. (catch #t
  91. (lambda ()
  92. (read-string "#ZZZ")
  93. ;; oops, this # is supposed to be unrecognised
  94. #f)
  95. (lambda (key subr message args rest)
  96. (apply format #f message args)
  97. ;; message and args are ok
  98. #t)))
  99. (pass-if "block comment"
  100. (equal? '(+ 1 2 3)
  101. (read-string "(+ 1 #! this is a\ncomment !# 2 3)")))
  102. (pass-if "block comment finishing s-exp"
  103. (equal? '(+ 2)
  104. (read-string "(+ 2 #! a comment\n!#\n) ")))
  105. (pass-if "R6RS lexeme comment"
  106. (equal? '(+ 1 2 3)
  107. (read-string "(+ 1 #!r6rs 2 3)")))
  108. (pass-if "partial R6RS lexeme comment"
  109. (equal? '(+ 1 2 3)
  110. (read-string "(+ 1 #!r6r !# 2 3)")))
  111. (pass-if "R6RS/SRFI-30 block comment"
  112. (equal? '(+ 1 2 3)
  113. (read-string "(+ 1 #| this is a\ncomment |# 2 3)")))
  114. (pass-if "R6RS/SRFI-30 nested block comment"
  115. (equal? '(a b c)
  116. (read-string "(a b c #| d #| e |# f |#)")))
  117. (pass-if "R6RS/SRFI-30 nested block comment (2)"
  118. (equal? '(a b c)
  119. (read-string "(a b c #|||||||#)")))
  120. (pass-if "R6RS/SRFI-30 nested block comment (3)"
  121. (equal? '(a b c)
  122. (read-string "(a b c #||||||||#)")))
  123. (pass-if "R6RS/SRFI-30 block comment syntax overridden"
  124. ;; To be compatible with 1.8 and earlier, we should be able to override
  125. ;; this syntax.
  126. (with-fluids ((%read-hash-procedures (fluid-ref %read-hash-procedures)))
  127. (read-hash-extend #\| (lambda args 'not))
  128. (fold (lambda (x y result)
  129. (and result (eq? x y)))
  130. #t
  131. (read-string "(this is #| a comment)")
  132. `(this is not a comment))))
  133. (pass-if "unprintable symbol"
  134. ;; The reader tolerates unprintable characters for symbols.
  135. (equal? (string->symbol "\x01\x02\x03")
  136. (read-string "\x01\x02\x03")))
  137. (pass-if "CR recognized as a token delimiter"
  138. ;; In 1.8.3, character 0x0d was not recognized as a delimiter.
  139. (equal? (read-string "one\x0dtwo") 'one))
  140. (pass-if "returned strings are mutable"
  141. ;; Per R5RS Section 3.4, "Storage Model", `read' is supposed to return
  142. ;; mutable objects.
  143. (let ((str (with-input-from-string "\"hello, world\"" read)))
  144. (string-set! str 0 #\H)
  145. (string=? str "Hello, world")))
  146. (pass-if "square brackets are parens"
  147. (equal? '() (read-string "[]")))
  148. (pass-if-exception "paren mismatch" exception:mismatched-paren
  149. (read-string "'[)"))
  150. (pass-if-exception "paren mismatch (2)" exception:mismatched-paren
  151. (read-string "'(]"))
  152. (pass-if-exception "paren mismatch (3)" exception:mismatched-paren
  153. (read-string "'(foo bar]"))
  154. (pass-if-exception "paren mismatch (4)" exception:mismatched-paren
  155. (read-string "'[foo bar)"))
  156. (pass-if-equal '(#f 1) (read-string "(#f1)"))
  157. (pass-if-equal '(#f a) (read-string "(#fa)"))
  158. (pass-if-equal '(#f a) (read-string "(#Fa)"))
  159. (pass-if-equal '(#t 1) (read-string "(#t1)"))
  160. (pass-if-equal '(#t r) (read-string "(#tr)"))
  161. (pass-if-equal '(#t r) (read-string "(#Tr)"))
  162. (pass-if-equal '(#t) (read-string "(#TrUe)"))
  163. (pass-if-equal '(#t) (read-string "(#TRUE)"))
  164. (pass-if-equal '(#t) (read-string "(#true)"))
  165. (pass-if-equal '(#f) (read-string "(#false)"))
  166. (pass-if-equal '(#f) (read-string "(#FALSE)"))
  167. (pass-if-equal '(#f) (read-string "(#FaLsE)"))
  168. (pass-if (eof-object? (read-string "#!!#"))))
  169. (pass-if-exception "radix passed to number->string can't be zero"
  170. exception:out-of-range
  171. (number->string 10 0))
  172. (pass-if-exception "radix passed to number->string can't be one either"
  173. exception:out-of-range
  174. (number->string 10 1))
  175. (with-test-prefix "mismatching parentheses"
  176. (pass-if-exception "opening parenthesis"
  177. exception:eof
  178. (read-string "("))
  179. (pass-if-exception "closing parenthesis following mismatched opening"
  180. exception:unexpected-rparen
  181. (read-string ")"))
  182. (pass-if-exception "closing square bracket following mismatched opening"
  183. exception:unexpected-rsqbracket
  184. (read-string "]"))
  185. (pass-if-exception "opening vector parenthesis"
  186. exception:eof
  187. (read-string "#("))
  188. (pass-if-exception "closing parenthesis following mismatched vector opening"
  189. exception:unexpected-rparen
  190. (read-string ")")))
  191. (with-test-prefix "exceptions"
  192. ;; Reader exceptions: although they are not documented, they may be relied
  193. ;; on by some programs, hence these tests.
  194. (pass-if-exception "unterminated block comment"
  195. exception:unterminated-block-comment
  196. (read-string "(+ 1 #! comment\n..."))
  197. (pass-if-exception "R6RS/SRFI-30 unterminated nested block comment"
  198. exception:unterminated-block-comment
  199. (read-string "(foo #| bar #| |#)"))
  200. (pass-if-exception "unknown character name"
  201. exception:unknown-character-name
  202. (read-string "#\\theunknowncharacter"))
  203. (pass-if-exception "unknown sharp object"
  204. exception:unknown-sharp-object
  205. (read-string "#?"))
  206. (pass-if-exception "eof in string"
  207. exception:eof-in-string
  208. (read-string "\"the string that never ends"))
  209. (pass-if-exception "invalid escape in string"
  210. exception:invalid-escape
  211. (read-string "\"some string \\???\"")))
  212. (with-test-prefix "read-options"
  213. (pass-if "case-sensitive"
  214. (not (eq? 'guile 'GuiLe)))
  215. (pass-if "case-insensitive"
  216. (eq? 'guile
  217. (with-read-options '(case-insensitive)
  218. (lambda ()
  219. (read-string "GuiLe")))))
  220. (pass-if-equal "r7rs-symbols"
  221. (list 'a (string->symbol "Hello, this is | a \"test\"") 'b)
  222. (with-read-options '(r7rs-symbols)
  223. (lambda ()
  224. (read-string "(a |H\\x65;llo, this is \\| a \"test\"| b)"))))
  225. (pass-if "prefix keywords"
  226. (eq? #:keyword
  227. (with-read-options '(keywords prefix case-insensitive)
  228. (lambda ()
  229. (read-string ":KeyWord")))))
  230. (pass-if "prefix non-keywords"
  231. (symbol? (with-read-options '(keywords prefix)
  232. (lambda ()
  233. (read-string "srfi88-keyword:")))))
  234. (pass-if "postfix keywords"
  235. (eq? #:keyword
  236. (with-read-options '(keywords postfix)
  237. (lambda ()
  238. (read-string "keyword:")))))
  239. (pass-if "long postfix keywords"
  240. (eq? #:keyword0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
  241. (with-read-options '(keywords postfix)
  242. (lambda ()
  243. (read-string "keyword0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789:")))))
  244. (pass-if "`:' is not a postfix keyword (per SRFI-88)"
  245. (eq? ':
  246. (with-read-options '(keywords postfix)
  247. (lambda ()
  248. (read-string ":")))))
  249. (pass-if "no positions"
  250. (let ((sexp (with-read-options '()
  251. (lambda ()
  252. (read-string "(+ 1 2 3)")))))
  253. (and (not (source-property sexp 'line))
  254. (not (source-property sexp 'column)))))
  255. (pass-if "positions"
  256. (let ((sexp (with-read-options '(positions)
  257. (lambda ()
  258. (read-string "(+ 1 2 3)")))))
  259. (and (equal? (source-property sexp 'line) 0)
  260. (equal? (source-property sexp 'column) 0))))
  261. (pass-if "positions on quote"
  262. (let ((sexp (with-read-options '(positions)
  263. (lambda ()
  264. (read-string "'abcde")))))
  265. (and (equal? (source-property sexp 'line) 0)
  266. (equal? (source-property sexp 'column) 0))))
  267. (pass-if "position of SCSH block comment"
  268. ;; In Guile 2.0.0 the reader would not update the port's position
  269. ;; when reading an SCSH block comment.
  270. (let ((sexp (with-read-options '(positions)
  271. (lambda ()
  272. (read-string "#!foo\nbar\nbaz\n!#\n(hello world)\n")))))
  273. (= 4 (source-property sexp 'line))))
  274. (with-test-prefix "r6rs-hex-escapes"
  275. (pass-if-exception "non-hex char in two-digit hex-escape"
  276. exception:invalid-escape
  277. (with-read-options '(r6rs-hex-escapes)
  278. (lambda ()
  279. (with-input-from-string "\"\\x0g;\"" read))))
  280. (pass-if-exception "non-hex char in four-digit hex-escape"
  281. exception:invalid-escape
  282. (with-read-options '(r6rs-hex-escapes)
  283. (lambda ()
  284. (with-input-from-string "\"\\x000g;\"" read))))
  285. (pass-if-exception "non-hex char in six-digit hex-escape"
  286. exception:invalid-escape
  287. (with-read-options '(r6rs-hex-escapes)
  288. (lambda ()
  289. (with-input-from-string "\"\\x00000g;\"" read))))
  290. (pass-if-exception "no semicolon at termination of one-digit hex-escape"
  291. exception:invalid-escape
  292. (with-read-options '(r6rs-hex-escapes)
  293. (lambda ()
  294. (with-input-from-string "\"\\x0\"" read))))
  295. (pass-if-exception "no semicolon at termination of three-digit hex-escape"
  296. exception:invalid-escape
  297. (with-read-options '(r6rs-hex-escapes)
  298. (lambda ()
  299. (with-input-from-string "\"\\x000\"" read))))
  300. (pass-if "two-digit hex escape"
  301. (eqv?
  302. (with-read-options '(r6rs-hex-escapes)
  303. (lambda ()
  304. (string-ref (with-input-from-string "\"--\\xff;--\"" read) 2)))
  305. (integer->char #xff)))
  306. (pass-if "four-digit hex escape"
  307. (eqv?
  308. (with-read-options '(r6rs-hex-escapes)
  309. (lambda ()
  310. (string-ref (with-input-from-string "\"--\\x0100;--\"" read) 2)))
  311. (integer->char #x0100)))
  312. (pass-if "six-digit hex escape"
  313. (eqv?
  314. (with-read-options '(r6rs-hex-escapes)
  315. (lambda ()
  316. (string-ref (with-input-from-string "\"--\\x010300;--\"" read) 2)))
  317. (integer->char #x010300)))
  318. (pass-if "escaped characters match non-escaped ASCII characters"
  319. (string=?
  320. (with-read-options '(r6rs-hex-escapes)
  321. (lambda ()
  322. (with-input-from-string "\"\\x41;\\x0042;\\x000043;\"" read)))
  323. "ABC"))
  324. (pass-if "write R6RS string escapes"
  325. (let* ((s1 (apply string
  326. (map integer->char '(#x8 ; backspace
  327. #x18 ; cancel
  328. #x20 ; space
  329. #x30 ; zero
  330. #x40 ; at sign
  331. ))))
  332. (s2 (with-read-options '(r6rs-hex-escapes)
  333. (lambda ()
  334. (with-output-to-string
  335. (lambda () (write s1)))))))
  336. (lset= eqv?
  337. (string->list s2)
  338. (list #\" #\\ #\b #\\ #\x #\1 #\8 #\; #\space #\0 #\@ #\"))))
  339. (pass-if "display R6RS string escapes"
  340. (string=?
  341. (with-read-options '(r6rs-hex-escapes)
  342. (lambda ()
  343. (let ((pt (open-output-string))
  344. (s1 (apply string (map integer->char
  345. '(#xFF #x100 #xFFF #x1000 #xFFFF #x10000)))))
  346. (set-port-encoding! pt "ASCII")
  347. (set-port-conversion-strategy! pt 'escape)
  348. (display s1 pt)
  349. (get-output-string pt))))
  350. "\\xff;\\x100;\\xfff;\\x1000;\\xffff;\\x10000;"))
  351. (pass-if "one-digit hex escape"
  352. (eqv? (with-input-from-string "#\\xA" read)
  353. (integer->char #x0A)))
  354. (pass-if "two-digit hex escape"
  355. (eqv? (with-input-from-string "#\\xFF" read)
  356. (integer->char #xFF)))
  357. (pass-if "four-digit hex escape"
  358. (eqv? (with-input-from-string "#\\x00FF" read)
  359. (integer->char #xFF)))
  360. (pass-if "eight-digit hex escape"
  361. (eqv? (with-input-from-string "#\\x00006587" read)
  362. (integer->char #x6587)))
  363. (pass-if "write R6RS escapes"
  364. (string=?
  365. (with-read-options '(r6rs-hex-escapes)
  366. (lambda ()
  367. (with-output-to-string
  368. (lambda ()
  369. (write (integer->char #x80))))))
  370. "#\\x80")))
  371. (with-test-prefix "hungry escapes"
  372. (pass-if "default not hungry"
  373. ;; Assume default setting of not hungry.
  374. (equal? (with-input-from-string "\"foo\\\n bar\""
  375. read)
  376. "foo bar"))
  377. (pass-if "hungry"
  378. (dynamic-wind
  379. (lambda ()
  380. (read-enable 'hungry-eol-escapes))
  381. (lambda ()
  382. (equal? (with-input-from-string "\"foo\\\n bar\""
  383. read)
  384. "foobar"))
  385. (lambda ()
  386. (read-disable 'hungry-eol-escapes))))))
  387. (with-test-prefix "per-port-read-options"
  388. (pass-if "case-sensitive"
  389. (equal? '(guile GuiLe gUIle)
  390. (with-read-options '(case-insensitive)
  391. (lambda ()
  392. (read-string-as-list "GUIle #!no-fold-case GuiLe gUIle")))))
  393. (pass-if "case-insensitive"
  394. (equal? '(GUIle guile guile)
  395. (read-string-as-list "GUIle #!fold-case GuiLe gUIle")))
  396. (with-test-prefix "r6rs"
  397. (pass-if-equal "case sensitive"
  398. '(guile GuiLe gUIle)
  399. (with-read-options '(case-insensitive)
  400. (lambda ()
  401. (read-string-as-list "GUIle #!r6rs GuiLe gUIle"))))
  402. (pass-if-equal "square brackets"
  403. '((a b c) (foo 42 bar) (x . y))
  404. (read-string-as-list "(a b c) #!r6rs [foo 42 bar] [x . y]"))
  405. (pass-if-equal "hex string escapes"
  406. '("native\x7fsyntax"
  407. "\0"
  408. "ascii\x7fcontrol"
  409. "U\u0100BMP"
  410. "U\U010402SMP")
  411. (read-string-as-list (string-append "\"native\\x7fsyntax\" "
  412. "#!r6rs "
  413. "\"\\x0;\" "
  414. "\"ascii\\x7f;control\" "
  415. "\"U\\x100;BMP\" "
  416. "\"U\\x10402;SMP\"")))
  417. (with-test-prefix "keyword style"
  418. (pass-if-equal "postfix disabled"
  419. '(#:regular #:postfix postfix: #:regular2)
  420. (with-read-options '(keywords postfix)
  421. (lambda ()
  422. (read-string-as-list "#:regular postfix: #!r6rs postfix: #:regular2"))))
  423. (pass-if-equal "prefix disabled"
  424. '(#:regular #:prefix :prefix #:regular2)
  425. (with-read-options '(keywords prefix)
  426. (lambda ()
  427. (read-string-as-list "#:regular :prefix #!r6rs :prefix #:regular2")))))))
  428. (with-test-prefix "#;"
  429. (for-each
  430. (lambda (pair)
  431. (pass-if (car pair)
  432. (equal? (with-input-from-string (car pair) read) (cdr pair))))
  433. '(("#;foo 10". 10)
  434. ("#;(10 20 30) foo" . foo)
  435. ("#; (10 20 30) foo" . foo)
  436. ("#;\n10\n20" . 20)))
  437. (pass-if "#;foo"
  438. (eof-object? (with-input-from-string "#;foo" read)))
  439. (pass-if-exception "#;"
  440. exception:eof
  441. (with-input-from-string "#;" read))
  442. (pass-if-exception "#;("
  443. exception:eof
  444. (with-input-from-string "#;(" read)))
  445. (with-test-prefix "#'"
  446. (for-each
  447. (lambda (pair)
  448. (pass-if (car pair)
  449. (equal? (with-input-from-string (car pair) read) (cdr pair))))
  450. '(("#'foo". (syntax foo))
  451. ("#`foo" . (quasisyntax foo))
  452. ("#,foo" . (unsyntax foo))
  453. ("#,@foo" . (unsyntax-splicing foo)))))
  454. (with-test-prefix "#{}#"
  455. (pass-if (equal? (read-string "#{}#") '#{}#))
  456. (pass-if (not (equal? (read-string "(a #{.}# b)") '(a . b))))
  457. (pass-if (equal? (read-string "#{a}#") 'a))
  458. (pass-if (equal? (read-string "#{a b}#") '#{a b}#))
  459. (pass-if-exception "#{" exception:eof-in-symbol
  460. (read-string "#{"))
  461. (pass-if (equal? (read-string "#{a\\x20;b}#") '#{a b}#)))
  462. (begin-deprecated
  463. (with-test-prefix "deprecated #{}# escapes"
  464. (pass-if (equal? (read-string "#{a\\ b}#") '#{a b}#))))
  465. ;;; Local Variables:
  466. ;;; eval: (put 'with-read-options 'scheme-indent-function 1)
  467. ;;; End: