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