
(load-relative "loadtest.ss")

(SECTION 'READING)
(define readstr
  (lambda (s)
    (let* ([o (open-input-string s)]
	   [read (lambda () (read o))])
      (let loop ([last eof])
	(let ([v (read)])
	  (if (eof-object? v)
	      last
	      (loop v)))))))

(define readerrtype
  (lambda (x) x))

; Make sure {whitespace} == {delimiter}
(let ([with-censor (load-relative "censor.ss")])
  (with-censor
   (lambda ()
     (let loop ([n 0])
       (unless (= n 256)
	 (let* ([c0 (integer->char n)]
		[c (if (read-case-sensitive)
		       c0
		       (char-downcase c0))])
	   (cond
	    [(char-whitespace? c)
	     (test 'b readstr (string #\a c #\b))]
	    [(char=? #\\ c) (test 'ab readstr (string #\a c #\b))]
	    [(char=? #\; c) (test 'a readstr (string #\a c #\b))]
	    [(char=? #\' c) (test ''b readstr (string #\a c #\b))]
	    [(char=? #\` c) (test '`b readstr (string #\a c #\b))]
	    [(char=? #\, c) (test ',b readstr (string #\a c #\b))]
	    [else
	     (test (string->symbol (string #\a c #\b))
		   'readstr
		   (with-handlers ([void 
				    (lambda (x) 
				      (string->symbol (string #\a c #\b)))])
		     (readstr (string #\a c0 #\b))))]))
	 (loop (add1 n)))))))

(err/rt-test (readstr ")") exn:fail:read?)
(err/rt-test (readstr "[)") exn:fail:read?)
(err/rt-test (readstr "[}") exn:fail:read?)
(err/rt-test (readstr "8 )") exn:fail:read?)
(err/rt-test (readstr "(. )") exn:fail:read?)
(err/rt-test (readstr "(. 8)") exn:fail:read?)
(err/rt-test (readstr "(8 . )") exn:fail:read?)
(err/rt-test (readstr "(8 . ]") exn:fail:read?)
(err/rt-test (readstr "(8 . 9 . )") exn:fail:read?)
(err/rt-test (readstr "(8 . 9 . ]") exn:fail:read?)
(err/rt-test (readstr "(8 . 9 . 1 . )") exn:fail:read?)
(err/rt-test (readstr "(8 . 9 . 1 . 10)") exn:fail:read?)

(test (integer->char 0) readstr "#\\nul")
(test (integer->char 0) readstr "#\\Nul")
(test (integer->char 0) readstr "#\\NuL")
(test (integer->char 0) readstr "#\\null")
(test (integer->char 0) readstr "#\\Null")
(test (integer->char 0) readstr "#\\NulL")
(test (integer->char 8) readstr "#\\Backspace")
(test (integer->char 8) readstr "#\\BacksPace")
(test (integer->char 9) readstr "#\\tab")
(test (integer->char 9) readstr "#\\Tab")
(test (integer->char 9) readstr "#\\TaB")
(test (integer->char 10) readstr "#\\newline")
(test (integer->char 10) readstr "#\\Newline")
(test (integer->char 10) readstr "#\\NewLine")
(test (integer->char 10) readstr "#\\linefeed")
(test (integer->char 10) readstr "#\\Linefeed")
(test (integer->char 10) readstr "#\\LinefEeD")
(test (integer->char 11) readstr "#\\vtab")
(test (integer->char 11) readstr "#\\Vtab")
(test (integer->char 11) readstr "#\\VtAb")
(test (integer->char 12) readstr "#\\page")
(test (integer->char 12) readstr "#\\Page")
(test (integer->char 12) readstr "#\\PaGe")
(test (integer->char 13) readstr "#\\return")
(test (integer->char 13) readstr "#\\Return")
(test (integer->char 13) readstr "#\\retUrn")
(test (integer->char 127) readstr "#\\rubout")
(test (integer->char 127) readstr "#\\Rubout")
(test (integer->char 127) readstr "#\\RubOut")
(test (integer->char #x1) readstr "#\\u1")
(test (integer->char #x10) readstr "#\\u10")
(test (integer->char #x100) readstr "#\\u100")
(test (integer->char #x1000) readstr "#\\u1000")
(test (integer->char #xa) readstr "#\\ua")
(test (integer->char #xa7) readstr "#\\uA7")
(test (integer->char #xa77) readstr "#\\ua77")
(test (integer->char #xa77C) readstr "#\\uA77C")
(test 0 readstr "#\\u10000")
(test 'a readstr "#\\ua000a")
(test 'x readstr "#\\ua000x")
(test (integer->char #xa) readstr "#\\Ua")
(test (integer->char #xa7) readstr "#\\UA7")
(test (integer->char #xa77) readstr "#\\Ua77")
(test (integer->char #xa77C) readstr "#\\UA77C")
(test (integer->char #x10000) readstr "#\\U10000")
(test (integer->char #x100000) readstr "#\\U100000")
(test (integer->char #x10FFFF) readstr "#\\U10FFFF")
(test (integer->char #x10FFFF) readstr "#\\U0010FFFF")
(test (integer->char #x0) readstr "#\\U00000000")
(test 1 readstr "#\\U000000011")

(err/rt-test (readstr "#\\uD800") exn:fail:read?)
(err/rt-test (readstr "#\\uD900") exn:fail:read?)
(err/rt-test (readstr "#\\UDFFF") exn:fail:read?)
(err/rt-test (readstr "#\\UFFFFFF") exn:fail:read?)
(err/rt-test (readstr "#\\U110000") exn:fail:read?)
(err/rt-test (readstr "#\\U1000000") exn:fail:read?)
(err/rt-test (readstr "#\\U10000000") exn:fail:read?)
(err/rt-test (readstr "#\\UFFFFFFFF") exn:fail:read?)

(define (astring n) (string (integer->char n)))

(test (astring 7) readstr "\"\\a\"")
(test (astring 8) readstr "\"\\b\"")
(test (astring 9) readstr "\"\\t\"")
(test (astring 10) readstr "\"\\n\"")
(test (astring 11) readstr "\"\\v\"")
(test (astring 12) readstr "\"\\f\"")
(test (astring 13) readstr "\"\\r\"")
(test #\u1B string-ref (readstr "\"\\e\"") 0)
(test #\u0 string-ref (readstr "\"\\0\"") 0)
(test #\u8 string-ref (readstr "\"\\10\"") 0)
(test #\uC string-ref (readstr "\"\\14\"") 0)
(test (integer->char #o114)  string-ref (readstr "\"\\114\"") 0)
(test (integer->char #o111)  string-ref (readstr "\"\\1111\"") 0)
(test (astring #x24) readstr "\"\\x24\"")
(test (string #\u24 #\space) readstr "\"\\x24 \"")
(test (string #\u24 #\5) readstr "\"\\x245\"")
(test (astring #x1024) readstr "\"\\u1024\"")
(test (astring #x1C24) readstr "\"\\u1C24\"")
(test (string #\u0001) readstr "\"\\u1\"")
(test (string #\u0001 #\space) readstr "\"\\u1 \"")
(test (string #\u0001) readstr "\"\\u01\"")
(test (string #\u0001 #\space) readstr "\"\\u01 \"")
(test (string #\u0001) readstr "\"\\u001\"")
(test (string #\u0001 #\space) readstr "\"\\u001 \"")
(test (string #\u0001) readstr "\"\\u0001\"")
(test (string #\u0001 #\space) readstr "\"\\u0001 \"")
(test (string #\u0001 #\1) readstr "\"\\u00011\"")

(err/rt-test (readstr "\"\\c\"") exn:fail:read?)
(err/rt-test (readstr "\"\\777\"") exn:fail:read?)
(err/rt-test (readstr "\"\\uD800\"") exn:fail:read?)
(err/rt-test (readstr "\"\\UB0000000\"") exn:fail:read?)
(err/rt-test (readstr "\"\\UFFFFFFFF\"") exn:fail:read?)

(test (bytes 7) readstr "#\"\\a\"")
(test (bytes 8) readstr "#\"\\b\"")
(test (bytes 9) readstr "#\"\\t\"")
(test (bytes 10) readstr "#\"\\n\"")
(test (bytes 11) readstr "#\"\\v\"")
(test (bytes 12) readstr "#\"\\f\"")
(test (bytes 13) readstr "#\"\\r\"")
(test #x1B bytes-ref (readstr "#\"\\e\"") 0)
(test #x0 bytes-ref (readstr "#\"\\0\"") 0)
(test #x8 bytes-ref (readstr "#\"\\10\"") 0)
(test #xC bytes-ref (readstr "#\"\\14\"") 0)
(test #o114 bytes-ref (readstr "#\"\\114\"") 0)
(test #o111 bytes-ref (readstr "#\"\\1111\"") 0)
(test (bytes #x24) readstr "#\"\\x24\"")
(test (bytes #x24 32) readstr "#\"\\x24 \"")
(test (bytes #x24 53) readstr "#\"\\x245\"")

(err/rt-test (readstr "#\"\\c\"") exn:fail:read?)
(err/rt-test (readstr "#\"\\777\"") exn:fail:read?)
(err/rt-test (readstr "#\"\\u0040\"") exn:fail:read?)

(load-relative "numstrs.ss")
(let loop ([l number-table])
  (unless (null? l)
	  (let* ([pair (car l)]
		 [v (car pair)]
		 [s (cadr pair)])
	    (cond
	     [(memq v '(X DBZ NOE))
	      (err/rt-test (readstr s) exn:fail:read?)
	      (test #f string->number s)]
	     [v 
	      (test v readstr s)
	      (test (if (symbol? v) #f v) string->number s)]
	     [else 
	      (test (string->symbol s) readstr s)
	      (test #f string->number s)
	      (unless (regexp-match "#" s)
		(err/rt-test (readstr (string-append "#d" s)) exn:fail:read?)
		(test #f string->number (string-append "#d" s)))]))
	  (loop (cdr l))))

(test 5 readstr "#| hi |# 5")
(test 5 readstr "#| #| #| #| hi |# |# |# |# 5")
(test '(5) readstr "(#| #| #| #| hi |# |# |# |# 5)")
(test '(10 1) readstr "(10 #|z#|#f|#z|# 1)")
(test 17 readstr "#|z#|#f|#z|# 17")

(err/rt-test (readstr "#\\silly") exn:fail:read?)
(err/rt-test (readstr "#\\nully") exn:fail:read?)
(err/rt-test (readstr "#\\nu") exn:fail:read?)
(err/rt-test (readstr "#\\733") exn:fail:read?)
(err/rt-test (readstr "#\\433") exn:fail:read?)
(err/rt-test (readstr "#\\longerthanthrityonecharcterswhichisthebufsize") exn:fail:read?)
(err/rt-test (readstr "#\\rcase") exn:fail:read?)
(err/rt-test (readstr "#\\pcase") exn:fail:read?)
(err/rt-test (readstr "#\\tcase") exn:fail:read?)
(err/rt-test (readstr "#\\vcase") exn:fail:read?)
(err/rt-test (readstr "#\\bcase") exn:fail:read?)
(err/rt-test (readstr "#\\lcase") exn:fail:read?)

(err/rt-test (readstr "(hi") exn:fail:read:eof?)
(err/rt-test (readstr "\"hi") exn:fail:read:eof?)
(err/rt-test (readstr "\"hi\\") exn:fail:read:eof?)
(err/rt-test (readstr "#(hi") exn:fail:read:eof?)
(err/rt-test (readstr "#[hi") exn:fail:read:eof?)
(err/rt-test (readstr "#{hi") exn:fail:read:eof?)
(err/rt-test (readstr "#4(hi") exn:fail:read:eof?)
(err/rt-test (readstr "#4[hi") exn:fail:read:eof?)
(err/rt-test (readstr "#4{hi") exn:fail:read:eof?)
(err/rt-test (readstr "|hi") exn:fail:read:eof?)
(err/rt-test (readstr "hi\\") exn:fail:read:eof?)
(err/rt-test (readstr "#\\") exn:fail:read:eof?)
(err/rt-test (readstr "#\\12") exn:fail:read:eof?)
(err/rt-test (readstr "#| hi") exn:fail:read:eof?)
(err/rt-test (readstr "(1 #| hi") exn:fail:read:eof?)
(err/rt-test (readstr "'") exn:fail:read:eof?)
(err/rt-test (readstr "`") exn:fail:read:eof?)
(err/rt-test (readstr ",@") exn:fail:read:eof?)
(err/rt-test (readstr ",") exn:fail:read:eof?)
(err/rt-test (readstr "#'") exn:fail:read:eof?)
(err/rt-test (readstr "#&") exn:fail:read:eof?)

(err/rt-test (readstr ".") exn:fail:read?)
(err/rt-test (readstr "a .") exn:fail:read?)
(err/rt-test (readstr "a . b") exn:fail:read?)
(err/rt-test (readstr "( . )") exn:fail:read?)
(err/rt-test (readstr "(1 .") exn:fail:read:eof?)
(err/rt-test (readstr "(1 .   ") exn:fail:read:eof?)
(err/rt-test (readstr "(1 . 2") exn:fail:read:eof?)
(err/rt-test (readstr "( . 8)") exn:fail:read?)
(err/rt-test (readstr "(0 . 8 9)") exn:fail:read?)
(err/rt-test (readstr "( . 8 9)") exn:fail:read?)
(err/rt-test (readstr "(1 . 2 3 . 4)") exn:fail:read?)
(err/rt-test (readstr "(1 . 2 . 3 . 4)") exn:fail:read?)
(err/rt-test (readstr "(1 . 2 .3)") exn:fail:read?)
(err/rt-test (readstr "(1 . 2 .a)") exn:fail:read?)
(err/rt-test (readstr "#(8 . )") exn:fail:read?)
(err/rt-test (readstr "#( . )") exn:fail:read?)
(err/rt-test (readstr "#( . 8)") exn:fail:read?)
(err/rt-test (readstr "#(0 . 8 9)") exn:fail:read?)
(err/rt-test (readstr "#( . 8 9)") exn:fail:read?)
(err/rt-test (readstr "#( 8 . 9)") exn:fail:read?)
(err/rt-test (readstr "#( 8 . (9))") exn:fail:read?)
(err/rt-test (readstr "#(1 . 2 . 3)") exn:fail:read?)

(err/rt-test (readstr "#Q") exn:fail:read?)
(err/rt-test (readstr "##") exn:fail:read?)
(err/rt-test (readstr "#?") exn:fail:read?)
(err/rt-test (readstr "#-1()") exn:fail:read?)
(err/rt-test (readstr "#<a>") exn:fail:read?)
(err/rt-test (readstr "#") exn:fail:read:eof?)

(test #(1 a c) readstr "#[1 a c]")
(test #(1 a c) readstr "#{1 a c}")
(test #(1 a a) readstr "#3[1 a]")
(test #(1 a a) readstr "#3{1 a}")
(parameterize ([read-square-bracket-as-paren #f]
	       [read-curly-brace-as-paren #f]
	       [read-accept-quasiquote #f])
  (err/rt-test (readstr "[2") exn:fail:read?)
  (err/rt-test (readstr "{2") exn:fail:read?)
  (err/rt-test (readstr "}2") exn:fail:read?)
  (err/rt-test (readstr "]2") exn:fail:read?)
  (err/rt-test (readstr "#{1}") exn:fail:read?)
  (err/rt-test (readstr "#[1]") exn:fail:read?)
  (err/rt-test (readstr "#2{1}") exn:fail:read?)
  (err/rt-test (readstr "#2[1]") exn:fail:read?)
  (err/rt-test (readstr ",2") exn:fail:read?)
  (err/rt-test (readstr ",@2") exn:fail:read?)
  (err/rt-test (readstr "`2") exn:fail:read?))

(test '(1 2 3) readstr "(2 . 1 . 3)")
(test '(1 2 3 4) readstr "(2 . 1 . 3 4)")
(test '(1 2 3 4) readstr "(2 3 . 1 . 4)")
(test '(2 . 0.4) readstr "(2 . .4)")

(err/rt-test (readstr "#ha") exn:fail:read:eof?)
(err/rt-test (readstr "#ham") exn:fail:read?)
(err/rt-test (readstr "#hash") exn:fail:read:eof?)
(err/rt-test (readstr "#hashe") exn:fail:read:eof?)
(err/rt-test (readstr "#hasheq") exn:fail:read:eof?)
(err/rt-test (readstr "#hash(") exn:fail:read:eof?)
(err/rt-test (readstr "#hash((1") exn:fail:read:eof?)
(err/rt-test (readstr "#hash((1 .") exn:fail:read:eof?)
(err/rt-test (readstr "#hash((1 . 2)") exn:fail:read:eof?)
(err/rt-test (readstr "#hash(1)") exn:fail:read?)
(err/rt-test (readstr "#hash(1 2)") exn:fail:read?)
(err/rt-test (readstr "#hash(1 . 2)") exn:fail:read?)
(err/rt-test (readstr "#hash((1))") exn:fail:read?)
(err/rt-test (readstr "#hash((1 2))") exn:fail:read?)
(err/rt-test (readstr "#hash((1. 2))") exn:fail:read?)
(err/rt-test (readstr "#hash((1 .2))") exn:fail:read?)
(err/rt-test (readstr "#hash((1 . 2 3))") exn:fail:read?)
(err/rt-test (readstr "#hash((1 . 2) . ((3 . 4)))") exn:fail:read?)
(err/rt-test (readstr "#hash((1 . 2) . (3 . 4) . (5 . 6))") exn:fail:read?)
(err/rt-test (readstr "#hash((1 . 2 . 3))") exn:fail:read?)
(err/rt-test (readstr "#hash(#0=(1 . 2))") exn:fail:read?)
(err/rt-test (readstr "#hash#0=((1 . 2))") exn:fail:read?)
(err/rt-test (readstr "#hash((1 #0=(2)))") exn:fail:read?)
(err/rt-test (readstr "#0=#hash#0#") exn:fail:read?)
(err/rt-test (readstr "#0=#hash(#0#)") exn:fail:read?)
(err/rt-test (readstr "#hash([1 . 2))") exn:fail:read?)

(define (test-ht t size eq? key val)
  (test #t hash-table? t)
  (test (not eq?) hash-table? t 'equal)
  (test size length (hash-table-map t cons))
  (test 'nope hash-table-get t 'not-there (lambda () 'nope))
  (test val hash-table-get t key (lambda () #f)))
(test-ht (readstr "#hash()") 0 #f 'none #f)
(test-ht (readstr "#hash((1 . 2))") 1 #f 1 2)
(test-ht (readstr "#hash([1 . 2])") 1 #f 1 2)
(test-ht (readstr "#hash[(1 . 2)]") 1 #f 1 2)
(test-ht (readstr "#hash({1 . 2})") 1 #f 1 2)
(test-ht (readstr "#hash{(1 . 2)}") 1 #f 1 2)
(test-ht (readstr "#hash{[1 . 2]}") 1 #f 1 2)
(test-ht (readstr "#hasheq((1 . 2))") 1 #t 1 2)
(test-ht (readstr "#hash((\"apple\" . 1))") 1 #f "apple" 1)
(test-ht (readstr "#hasheq((\"apple\" . 1))") 1 #t "apple" #f)
(test-ht (readstr "#hash((\"apple\" . 1) (\"apple\" . 10))") 1 #f "apple" 10)
(test-ht (readstr "#hasheq((\"apple\" . 1) (\"apple\" . 10))") 2 #t "apple" #f)
(test-ht (readstr "#hash((apple . 1) (apple . 10))") 1 #f 'apple 10)
(test-ht (readstr "#hasheq((apple . 1) (apple . 10))") 1 #t 'apple 10)
(test-ht (readstr "#hasheq((#0=\"apple\" . 1) (#0# . 10))") 1 #t "apple" #f)
(test-ht (readstr "#hash((#0=\"apple\" . 1) (\"banana\" . #0#))") 2 #f "banana" "apple")
(test-ht (readstr "#hash((a . 1) (b . 2) (c . 3) (e . 4) (f . 5) (g . 6) (h . 7) (i . 8))") 8 #f 'f 5)
(let ([t (readstr "#0=#hash((\"apple\" . #0#))")])
  (test-ht t 1 #f "apple" t))
(test-ht (readstr "#hash((#hash((1 . 2)) . 11) (#hash((3 . 4)) . 12))") 2 #f #hash((1 . 2)) 11)
(test-ht (readstr "#hash((#hash((1 . 2)) . 11) (#hash((3 . 4)) . 12))") 2 #f #hash((3 . 4)) 12)
(let ([t (readstr "#0=#hasheq((#0# . 17))")])
  (test-ht t 1 #t t 17))
(let ([t (readstr "#0=#hash((#0# . 17))")])
  ;; Don't look for t, because that's a hash on a circular object!
  (test-ht t 1 #f 'none #f))

(define (test-write-ht writer t . strings)
  (let ([o (open-output-string)])
    (writer t o)
    (test #t (car strings) (and (member (get-output-string o) strings) #t))))
(test-write-ht write #hash((1 . 2)) "#<hash-table>")

(parameterize ([print-hash-table #t])
  (test-write-ht write #hash((1 . 2)) "#hash((1 . 2))")
  (test-write-ht write #hash((1 . 2) (3 . 4)) "#hash((1 . 2) (3 . 4))" "#hash((3 . 4) (1 . 2))")
  (test-write-ht write #hash(("apple" . |coconut !|)) "#hash((\"apple\" . |coconut !|))")
  (test-write-ht display #hash(("apple" . |coconut !|)) "#hash((apple . coconut !))")
  (test-write-ht write #3=#hash((1 . #3#)) "#0=#hash((1 . #0#))")
  (test-write-ht write #hash((#37=(1 2) . #37#)) "#hash(((1 2) . (1 2)))")
  (test-write-ht write #hash((a . #9=(1 2)) (b . #9#)) 
		 "#hash((a . (1 2)) (b . (1 2)))"
		 "#hash((b . (1 2)) (a . (1 2)))")
  (parameterize ([print-graph #t])
    (test-write-ht write #hash((#33=(1 2) . #33#)) "#hash((#0=(1 2) . #0#))")
    (test-write-ht write #hash((a . #7=(1 2)) (b . #7#)) 
		   "#hash((a . #0=(1 2)) (b . #0#))"
		   "#hash((b . #0=(1 2)) (a . #0#))")))


(test #t regexp? (readstr "#rx\".\""))
(test '("abc") regexp-match #rx"a.." "123abcdef")

(err/rt-test (readstr "#r") exn:fail:read:eof?)
(err/rt-test (readstr "#rx") exn:fail:read:eof?)
(err/rt-test (readstr "#rx\"") exn:fail:read:eof?)
(err/rt-test (readstr "#ra") exn:fail:read?)
(err/rt-test (readstr "#rxa") exn:fail:read?)
(err/rt-test (readstr "#rx\"?\"") exn:fail:read?)

(test 2 vector-length (readstr "#2()"))
(test 0 vector-ref (readstr "#2()") 1)
(test 2 vector-length (readstr "#000000000000000000000000000000002()"))

(err/rt-test (readstr "#2(1 2 3)") exn:fail:read?)
(err/rt-test (readstr "#200000000000(1 2 3)") (readerrtype exn:fail:out-of-memory?))
(err/rt-test (readstr "#111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111x1(1 2 3)") exn:fail:read?)

(test #t (lambda (x) (eq? (car x) (cdr x))) (readstr "(#1=(1 2) . #0001#)"))
(test #t (lambda (x) (and (box? x) (eq? x (unbox x)))) (readstr "#0=#&#0#"))
(test #t (lambda (x) (and (vector? x) (eq? x (vector-ref x 0)) (eq? x (vector-ref x 1)))) (readstr "#0=#2(#0#)"))
(test #t (lambda (x) (and (vector? x) (eq? (vector-ref x 1) (vector-ref x 2)))) (readstr "#3(#0=(1 2) #0#)"))
(test '(1 1 1) readstr "(#0=1 #1=#0# #1#)")

;; Check that syntax, expansion, etc. preserve vector sharing
(test #t (lambda (x) (and (vector? x) (eq? (vector-ref x 0) (vector-ref x 1)))) #2((1 2)))

(define (graph-error-tests readstr)
  (err/rt-test (readstr "#0#") exn:fail:read?)
  (err/rt-test (readstr "#0=#0#") exn:fail:read?)
  (err/rt-test (readstr "#0=#0#") exn:fail:read?)
  (err/rt-test (readstr "(#0# #0=7)") exn:fail:read?)
  (err/rt-test (readstr "(#0=7 #1#)") exn:fail:read?)
  (err/rt-test (readstr "(#0=7 #0=7)") exn:fail:read?)
  (err/rt-test (readstr "#0=") exn:fail:read:eof?)
  (err/rt-test (readstr "#0") exn:fail:read:eof?)
  (err/rt-test (readstr "#012345678=7") exn:fail:read?)
  (err/rt-test (readstr "(#12345678=7 #012345678#)") exn:fail:read?)
  (err/rt-test (readstr "#111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111x1=(1 2 3)") exn:fail:read?)
  (parameterize ([read-accept-graph #f])
    (err/rt-test (readstr "#1=1") exn:fail:read?)
    (err/rt-test (readstr "#1#") exn:fail:read?)))
(graph-error-tests readstr)
(graph-error-tests (lambda (s)
		     (read-syntax "string" (open-input-string s))))

;; Long symbol:
(test 'abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg
      readstr "abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg")

(test 3 string-length (readstr (string #\" #\a #\nul #\b #\")))
(test (string->symbol (string #\a #\nul #\b)) 'sym (readstr (string #\a #\nul #\b)))
(test (string->symbol (string #\1 #\nul #\b)) 'sym (readstr (string #\1 #\nul #\b)))

; Test read/write invariance on symbols and use of pipe quotes
(define (test-write-sym with-bar without-bar s)
  (let ([sym (string->symbol s)])
    (parameterize ([read-case-sensitive #t])
      (let ([p (open-output-string)])
	(write sym p)
	(test with-bar 'write-sym-with-bar (get-output-string p))
	(test sym read (open-input-string (get-output-string p))))
      (let ([p (open-output-string)])
	(parameterize ([read-accept-bar-quote #f])
	  (write sym p)
	  (test without-bar 'write-sym-no-bar (get-output-string p))
	  (test sym read (open-input-string (get-output-string p)))))
      (let ([p (open-output-string)])
	(display sym p)
	(test s 'display-sym (get-output-string p))))))

(test-write-sym "a->b" "a->b" "a->b")
(test-write-sym "|a,b|" "a\\,b" "a,b")
(test-write-sym "a\\|b" "a|b" "a|b")
(test-write-sym "|a\\b|" "a\\\\b" "a\\b")

(test 'a 'quote '\a)
(test '|\a| 'quote '\\a)
(test 'a 'quote '||||a||)
#ci(test (string->symbol "aaa") 'quote 'aAa)
#ci(test (string->symbol "aAa") 'quote 'A\AA)
#ci(test (string->symbol "aAa") 'quote '|aAa|)
#ci(test (string->symbol "aAa") 'quote 'A|A|A)

(load-relative "numstrs.ss")
(let loop ([l number-table])
  (cond
   [(null? l) 'done]
   [(or (number? (caar l)) (memq (caar l) '(X DBZ NOE)))
    (test-write-sym (string-append "|" (cadar l) "|") 
		    (string-append "\\" (cadar l)) 
		    (cadar l))
    (loop (cdr l))]
   [else 
    (test-write-sym (cadar l) (cadar l) (cadar l))
    (loop (cdr l))]))

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Test mid-stream EOF

(define (test-mid-stream-eof use-peek?)
  (define chars (map (lambda (x)
		       (if (char? x) (char->integer x) x))
		     (append
		      (string->list "1 2")
		      (list eof)
		      (string->list "\"a\" \"b\"")
		      (list eof)
		      (string->list "(a) (b)")
		      (list eof)
		      (string->list "eof"))))
  (define cp (make-input-port
	      'mid-stream
	      (lambda (b)
		(if (null? chars)
		    eof
		    (let ([c (car chars)])
		      (set! chars (cdr chars))
		      (cond 
		       [(eof-object? c)
			eof]
		       [else
			(bytes-set! b 0 c)
			1]))))
	      (and use-peek?
		   (lambda (b skip progress-evt)
		     (when (positive? skip)
		       (error 'ouch!))
		     (if (null? chars)
			 eof
			 (let ([c (car chars)])
			   (cond 
			    [(eof-object? c)
			     eof]
			    [else
			     (bytes-set! b 0 c)
			     1])))))
	      void))
  (define (f) (read cp))

  (test 1 f)
  (test 2 f)
  (test eof f)
  (test "a" f)
  (test "b" f)
  (test eof f)
  (test '(a) f)
  (test '(b) f)
  (test eof f)
  (test 'eof f)
  (test eof f)
  (test eof f))

(test-mid-stream-eof #f)
(test-mid-stream-eof #t)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Test non-character results for getc

(define-struct special (size))

(define a-special (make-special 1))
(define b-special (make-special 1))
(define special-comment (make-special 1))

(define (make-p stream special-size check-pos)
  ;; The `stream' arg is a list of strings and non-strings;
  ;;  characters from the strings are returned one by one,
  ;;  and the non-strings are returns as "special" literals.
  ;; The `special-size' arg meansures the size (in char
  ;;  positions) of a non-string special literal.
  (let* ([pos 0]
	 [incpos! (lambda () (set! pos (add1 pos)))]
	 [read-one (lambda (str)
		     (let loop ([s stream][p pos])
		       (if (null? s)
			   eof
			   (let ([i (car s)])
			     (if (bytes? i)
				 (if ((bytes-length i) . > . p)
				     (begin
				       (incpos!)
				       (bytes-set! str 0 (bytes-ref i p))
				       1)
				     (loop (cdr s) (- p (bytes-length i))))
				 ;; a special:
				 (cond
				  [(zero? p) (incpos!)
				   (if (and (number? i) 
					    (or (inexact? i)
						(negative? i)))
				       i ; creates an error
				       (lambda (where line col pos)
					 (check-pos where line col pos)
					 (cond
					  [(symbol? i) (i)]
					  [(eq? i special-comment)
					   (make-special-comment i)]
					  [(number? i)
					   (if (inexact? i)
					       (make-special-comment i)
					       'aha)]
					  [else i])))]
				  [else (loop (cdr s) (sub1 p))]))))))])
    (make-input-port
     'specializer
     ;; Non-blocking read string:
     (lambda (str)
       (read-one str))
     ;; Peek char
     (lambda (str skip progress-evt)
       (let ([old-p pos])
	 (let loop ([skip skip])
	   (unless (zero? skip)
	     (read-one str)
	     (loop (sub1 skip))))
	 (begin0
	  (read-one str)
	  (set! pos old-p))))
     ;; Close proc
     (lambda () #t))))

;; Read without specials:
(let* ([p (make-p `(#"(list "
		    #"a"
		    #" "
		    #"b"
		    #"))")
		  special-size
		  (lambda (w l c p)
		    (error "shouldn't get here")))]
       [_ (port-count-lines! p)]
       [v (syntax-e (read-syntax 'ok p))])
  (test 'list syntax-e (car v))
  (test 'a syntax-e (cadr v))
  (test 'b syntax-e (caddr v))
  (test 1 syntax-line (car v))
  (test 1 syntax-column (car v))
  (test 1 syntax-line (cadr v))
  (test 6 syntax-column (cadr v))
  (test 1 syntax-line (caddr v))
  (test 8 syntax-column (caddr v)))

;; Without specials, with newlines:
(let* ([p (make-p `(#"(list\n"
		    #"a"
		    #"\n"
		    #"b"
		    #"))")
		  special-size
		  (lambda (w l c p)
		    (error "shouldn't get here")))]
       [_ (port-count-lines! p)]
       [v (syntax-e (read-syntax 'ok p))])
  (test 'list syntax-e (car v))
  (test 'a syntax-e (cadr v))
  (test 'b syntax-e (caddr v))
  (test 1 syntax-line (car v))
  (test 1 syntax-column (car v))
  (test 2 syntax-line (cadr v))
  (test 0 syntax-column (cadr v))
  (test 3 syntax-line (caddr v))
  (test 0 syntax-column (caddr v)))
  
;; Simple read:
(let* ([p (make-p `(#"(list "
		    ,a-special
		    #" "
		    ,b-special
		    #"))")
		  special-size
		  (lambda (w l c p)
		    (test #f 'no-place1 w)
		    (test 1 'no-place2 l)
		    (test (and p (sub1 p)) 'no-place3 c)
		    (test #f not (memq p '(7 9)))))]
       [_ (port-count-lines! p)]
       [v (read p)])
  (test 'list car v)
  (test a-special cadr v)
  (test b-special caddr v))

;; Read with newlines
(let* ([p (make-p `(#"(list\n"
		    ,a-special
		    #"\n"
		    ,b-special
		    #"))")
		  special-size
		  (lambda (w l c p)
		    (test l 'no-place4 l)
		    (test #f 'no-place5 w)
		    (test 0 'no-place6 c)
		    (test #f not (memq p '(7 9)))
		    (test #f not (memq l '(2 3)))))]
       [_ (port-count-lines! p)]
       [v (read p)])
  (test 'list car v)
  (test a-special cadr v)
  (test b-special caddr v))

(require (rename (lib "port.ss") relocate-input-port relocate-input-port))
(define (shift-port p count-lines? deltas)
  (let ([p (relocate-input-port p 
				(add1 (car deltas))
				(cadr deltas)
				(add1 (caddr deltas)))])
    (when count-lines?
      (port-count-lines! p))
    p))

;; Read with src loc:
(let* ([p (make-p `(#"(list "
		    ,a-special
		    #" "
		    ,b-special
		    #" end))")
		  special-size
		  (lambda (w l c p)
		    (test 'dk 'dk-place w)
		    (test 8 'no-place7 l)
		    (test p + c 631)
		    (test #f not (memq p '(707 709)))))]
       [_ (port-count-lines! p)]
       [v (read-syntax 'dk (shift-port p #t '(7 70 700)))]
       [l (syntax->list v)]
       [v2 (syntax-object->datum v)])
  (test 'list car v2)
  (test a-special cadr v2)
  (test b-special caddr v2)
  (test 'end cadddr v2)
  
  (test 702 syntax-position (car l))
  (test 707 syntax-position (cadr l))
  (test 709 syntax-position (caddr l))
  (test 711 syntax-position (cadddr l))

  ;; Read with specials as syntax syntax already:
  (let* ([stx v]
	 [p (make-p `(#"(list "
		      ,stx
		      #" end))")
		    (lambda (x)
		      ;; it's 1 wide
		      1)
		    (lambda (w l c p)
		      (test 'dk 'dk-place w)
		    (test #f 'no-place8 l)
		    (test #f 'no-place9 c)
		    (test 7 'place p)))]
	 [v (read-syntax 'dk p)]
	 [l (syntax->list v)])
    ;; make sure syntax object is intact:
    (test stx cadr l)
    (test 9 syntax-position (caddr l))

    ;; Check that plain read performs a syntax-object->datum:
    (let* ([p (make-p `(#"(list "
			,stx
			#" end))")
		      (lambda (x) 100)
		    (lambda (w l c p)
		      (test #f 'no-place10 w)
		      (test #f 'no-place11 l)
		      (test #f 'no-place12 c)
		      (test 7 'place p)))]
	   [v (read p)])
      (test `(list (list ,a-special ,b-special end) end) values v))))

;; Check that syntax read with with a list special
;;  syntaxizes the list.
(let* ([p (make-p `(#"(list "
		    ,(list a-special b-special)
		    #" end))")
		  (lambda (x)
		    1)
		  (lambda (w l c p)
		    (test 'dk 'dk-place w)
		    (test #f 'no-place13 l)
		    (test #f 'no-place14 c)
		    (test 7 'place p)))]
       [v (read-syntax 'dk p)]
       [l (syntax->list v)])
  (test #t syntax? (cadr l))
  (test #t list? (syntax-e (cadr l)))
  (test a-special syntax-e (car (syntax-e (cadr l))))
  (test b-special syntax-e (cadr (syntax-e (cadr l))))
  (test 9 syntax-position (caddr l)))

;; Test delimitting and unsupported positions:
(test (list 1 a-special) read (make-p (list #"(1" a-special #")") (lambda (x) 1) void))
(test (list 1) read (make-p (list #"(1" special-comment #")") (lambda (x) 1) void))
(test (list 'a a-special 'b) read (make-p (list #"(a" a-special #"b)") (lambda (x) 1) void))
(test (list #\a a-special) read (make-p (list #"(#\\a" a-special #")") (lambda (x) 1) void))
(test (list #\newline a-special) read (make-p (list #"(#\\newline" a-special #")") (lambda (x) 1) void))
(test (list #\newline) read (make-p (list #"(#\\newline" special-comment #")") (lambda (x) 1) void))
(test a-special read-char-or-special (make-p (list a-special) (lambda (x) 1) void))

;; Type error triggered by symbol 'z --- make sure it's propagated:
(err/rt-test (read (make-p (list #"(a" 'z #")") (lambda (x) 1) void)))
;; Negative number triggers bad special result:
(err/rt-test (read (make-p (list #"(a" -42 #")") (lambda (x) 1) void)))
;; Inexact number triggers bad special-comment result:
(err/rt-test (read (make-p (list #"(a" 42.0 #")") (lambda (x) 1) void)))

(define (run-delim-special a-special)
  (test (list 5) read (make-p (list #"(; \"" a-special #"\n5)") (lambda (x) 1) void))
  (test (list 5) read (make-p (list #"(#| \"" a-special #" |# 5)") (lambda (x) 1) void))
  (test (list 5) read (make-p (list #"(;" a-special #"\n 5)") (lambda (x) 1) void))
  (test 5 read (make-p (list #"#| \"" a-special #" |# 5") (lambda (x) 1) void))
  (test 5 read (make-p (list #";" a-special #"\n 5") (lambda (x) 1) void))
  (err/rt-test (read (make-p (list #"\"a" a-special #"\"") (lambda (x) 1) void)) exn:fail:read:non-char?)
  (err/rt-test (read (make-p (list #"\"" a-special #"\"") (lambda (x) 1) void)) exn:fail:read:non-char?)
  (err/rt-test (read (make-p (list #"\"\\" a-special #"\"") (lambda (x) 1) void)) exn:fail:read:non-char?)
  (err/rt-test (read (make-p (list #"\"\\x" a-special #"\"") (lambda (x) 1) void)) exn:fail:read:non-char?)
  (err/rt-test (read (make-p (list #"\"\\x1" a-special #"\"") (lambda (x) 1) void)) exn:fail:read:non-char?)
  (err/rt-test (read (make-p (list #"#\\" a-special #"") (lambda (x) 1) void)) exn:fail:read:non-char?)
  (err/rt-test (read (make-p (list #"#\\12" a-special #"") (lambda (x) 1) void)) exn:fail:read:non-char?)
  (err/rt-test (read (make-p (list #"#" a-special #"") (lambda (x) 1) void)) exn:fail:read:non-char?)
  (err/rt-test (read (make-p (list #"x\\" a-special #"y") (lambda (x) 1) void)) exn:fail:read:non-char?)
  (err/rt-test (read (make-p (list #"|" a-special #"y|") (lambda (x) 1) void)) exn:fail:read:non-char?)
  (err/rt-test (read (make-p (list #"|x" a-special #"y|") (lambda (x) 1) void)) exn:fail:read:non-char?))
(run-delim-special a-special)
(run-delim-special special-comment)

;; Test read-char-or-special:
(let ([p (make-p (list #"x" a-special #"y") (lambda (x) 5) void)])
  (test #\x peek-char-or-special p)
  (test 0 file-position p)
  (test #\x peek-char-or-special p 0)
  (test a-special peek-char-or-special p 1)
  (test #\y peek-char-or-special p 2)
  (test 0 file-position p)
  (test #\x read-char-or-special p)
  (test 1 file-position p)
  (test a-special peek-char-or-special p)
  (test 1 file-position p)
  (test a-special read-char-or-special p)
  (test 2 file-position p)
  (test #\y peek-char-or-special p)
  (test 2 file-position p)
  (test #\y read-char-or-special p)
  (test 3 file-position p))

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Test read-syntax offsets:

(let ([p (open-input-string " a ")])
  (let ([v (read-syntax 'ok (shift-port p #f (list 70 700 7000)))])
    (test #f syntax-line v)
    (test #f syntax-column v)
    (test 7002 syntax-position v)))

(let ([p (open-input-string " a ")])
  (port-count-lines! p)
  (let ([v (read-syntax 'ok (shift-port p #t (list 70 700 7000)))])
    (test 71 syntax-line v)
    (test 701 syntax-column v)
    (test 7002 syntax-position v)))

(let ([p (open-input-string " \n a ")])
  (port-count-lines! p)
  (let ([v (read-syntax 'ok (shift-port p #t (list 70 700 7000)))])
    (test 72 syntax-line v)
    (test 1 syntax-column v)
    (test 7004 syntax-position v)))

;; Check exception record:
(let ([p (open-input-string " . ")])
  (let ([x (with-handlers ([values values])
	     (read-syntax 'ok (shift-port p #f (list 70 700 7000))))])
    (test 'ok srcloc-source (car (exn:fail:read-srclocs x)))
    (test #f srcloc-line (car (exn:fail:read-srclocs x)))
    (test #f srcloc-column (car (exn:fail:read-srclocs x)))
    (test 7002 srcloc-position (car (exn:fail:read-srclocs x)))))
    
(let ([p (open-input-string " . ")])
  (port-count-lines! p)
  (let ([x (with-handlers ([values values])
	     (read-syntax 'ok (shift-port p #t (list 70 700 7000))))])
    (test 'ok srcloc-source (car (exn:fail:read-srclocs x)))
    (test 71 srcloc-line (car (exn:fail:read-srclocs x)))
    (test 701 srcloc-column (car (exn:fail:read-srclocs x)))
    (test 7002 srcloc-position (car (exn:fail:read-srclocs x)))))

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(let ([p (open-output-bytes)])
  (display void p)
  (test "#<primitive:void>" get-output-string p)
  (let ([try-bad
	 (lambda (x)
	   (test (void) (list x)
		 (parameterize ([print-unreadable #f])
		   (display x p)))
	   (err/rt-test (parameterize ([print-unreadable #f])
			  (write x p))))]
	[try-good
	 (lambda (x)
	   (test (void) (list x)
		 (parameterize ([print-unreadable #f])
		   (write x p))))])
    (try-bad void)
    (try-bad (lambda () 10))
    (try-bad (seconds->date (current-seconds)))
    (try-bad (let ()
	   (define-struct s (x))
	   (make-s 10)))
    (try-bad #'apple)
    
    (try-good 'ex)
    (try-good '(1 ex))
    (try-good '(1 . ex))
    (try-good #(1 2))
    (try-good #&(1))
    (try-good 1)
    (try-good 1.0)
    (try-good "apple")
    (try-good #"apple")
    (try-good #rx"ok")
    (try-good #rx#"ok")
    (try-good #f)))

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Test #reader

(err/rt-test (parameterize ([read-accept-reader #f])
	       (read (open-input-string "#reader mzscheme 10")))
	     exn:fail:read?)
(test 10 'ten (parameterize ([read-accept-reader #t])
	       (read (open-input-string "#reader mzscheme 10"))))

(module reader-test-module mzscheme
  (define (my-read port)
    `(READ ,(read port)))
  (define (my-read-syntax name port)
    `(READ-SYNTAX ,(read-syntax name port)))
  (provide (rename my-read read)
	   (rename my-read-syntax read-syntax)))

(test `(READ 10) 'ten 
      (parameterize ([read-accept-reader #t])
	(read (open-input-string "#reader reader-test-module 10"))))
(test `(READ-SYNTAX 10) 'ten 
      (syntax-object->datum
       (parameterize ([read-accept-reader #t])
	 (read-syntax '??? (open-input-string "#reader reader-test-module 10")))))

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Test 'paren-shape property

(test #f syntax-property (read-syntax 'x (open-input-string "(1 2 3)")) 'paren-shape)
(test #\[ syntax-property (read-syntax 'x (open-input-string "[1 2 3]")) 'paren-shape)
(test #\[ syntax-property (read-syntax 'x (open-input-string "[1 . 3]")) 'paren-shape)
(test #\[ syntax-property (read-syntax 'x (open-input-string "[1 . 3 . 2]")) 'paren-shape)
(test #\[ syntax-property (read-syntax 'x (open-input-string "#[1 2]")) 'paren-shape)
(test #\{ syntax-property (read-syntax 'x (open-input-string "{1 2 3}")) 'paren-shape)
(test #\{ syntax-property (read-syntax 'x (open-input-string "#{1 2}")) 'paren-shape)

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(report-errs)

(load-relative "readtable.ss")
