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