ikarus/scheme/ikarus.reader.ss

1595 lines
61 KiB
Scheme

;;; Ikarus Scheme -- A compiler for R6RS Scheme.
;;; Copyright (C) 2006,2007,2008 Abdulaziz Ghuloum
;;;
;;; This program is free software: you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License version 3 as
;;; published by the Free Software Foundation.
;;;
;;; This program is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;;; General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
(library (ikarus.reader)
(export read read-initial read-token comment-handler get-datum
read-annotated read-script-annotated annotation?
annotation-expression annotation-source
annotation-stripped)
(import
(only (ikarus.string-to-number) define-string->number-parser)
(ikarus system $chars)
(ikarus system $fx)
(ikarus system $pairs)
(ikarus system $bytevectors)
(only (ikarus.io) input-port-byte-position
input-port-column-number)
(except (ikarus) read-char read read-token comment-handler get-datum
read-annotated read-script-annotated annotation?
annotation-expression annotation-source annotation-stripped
input-port-column-number))
(define (die/lex id pos who msg arg*)
(raise
(condition
(make-lexical-violation)
(make-message-condition msg)
(if (null? arg*)
(condition)
(make-irritants-condition arg*))
(make-source-position-condition
id pos))))
(define (die/pos p off who msg arg*)
(die/lex (port-id p)
(let ([pos (input-port-byte-position p)])
(and pos (+ pos off)))
who msg arg*))
(define (die/p p who msg . arg*)
(die/pos p 0 who msg arg*))
(define (die/p-1 p who msg . arg*)
(die/pos p -1 who msg arg*))
(define (die/ann ann who msg . arg*)
(let ([src (annotation-source ann)])
(die/lex (car src) (cdr src) who msg arg*)))
(define (checked-integer->char n ac p)
(define (valid-integer-char? n)
(cond
[(<= n #xD7FF) #t]
[(< n #xE000) #f]
[(<= n #x10FFFF) #t]
[else #f]))
(if (valid-integer-char? n)
($fixnum->char n)
(die/p p 'tokenize
"invalid numeric value for character"
(list->string (reverse ac)))))
(define-syntax read-char
(syntax-rules ()
[(_ p) (get-char p)]))
(define delimiter?
(lambda (c)
(or (char-whitespace? c)
(memq c '(#\( #\) #\[ #\] #\" #\# #\; #\{ #\} #\|)))))
(define digit?
(lambda (c)
(and ($char<= #\0 c) ($char<= c #\9))))
(define char->num
(lambda (c)
(fx- ($char->fixnum c) ($char->fixnum #\0))))
(define initial?
(lambda (c)
(cond
[($char<= c ($fixnum->char 127))
(or (letter? c) (special-initial? c))]
[else (unicode-printable-char? c)])))
(define letter?
(lambda (c)
(or (and ($char<= #\a c) ($char<= c #\z))
(and ($char<= #\A c) ($char<= c #\Z)))))
(define special-initial?
(lambda (c)
(memq c '(#\! #\$ #\% #\& #\* #\/ #\: #\< #\= #\> #\? #\^ #\_ #\~))))
(define special-subsequent?
(lambda (c)
(memq c '(#\+ #\- #\. #\@))))
(define subsequent?
(lambda (c)
(cond
[($char<= c ($fixnum->char 127))
(or (letter? c)
(digit? c)
(special-initial? c)
(special-subsequent? c))]
[else
(or (unicode-printable-char? c)
(memq (char-general-category c) '(Nd Mc Me)))])))
(define tokenize-identifier
(lambda (ls p)
(let ([c (peek-char p)])
(cond
[(eof-object? c) ls]
[(subsequent? c)
(read-char p)
(tokenize-identifier (cons c ls) p)]
[(delimiter? c)
ls]
[(char=? c #\\)
(read-char p)
(tokenize-backslash ls p)]
[(eq? (port-mode p) 'r6rs-mode)
(die/p p 'tokenize "invalid identifier syntax"
(list->string (reverse (cons c ls))))]
[else ls]))))
(define (tokenize-string ls p)
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "invalid eof inside string")]
[else (tokenize-string-char ls p c)])))
(define LF1 '(#\xA #\x85 #\x2028)) ;;; these are considered newlines
(define LF2 '(#\xA #\x85)) ;;; these are not newlines if they
;;; appear after CR
(define (tokenize-string-char ls p c)
(define (intraline-whitespace? c)
(or (eqv? c #\x9)
(eq? (char-general-category c) 'Zs)))
(define (tokenize-string-continue ls p c)
(cond
[(eof-object? c)
(die/p p 'tokenize "invalid eof inside string")]
[(intraline-whitespace? c)
(let f ()
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "invalid eof inside string")]
[(intraline-whitespace? c) (f)]
[else (tokenize-string-char ls p c)])))]
[else (tokenize-string-char ls p c)]))
(cond
[($char= #\" c) ls]
[($char= #\\ c)
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "invalid eof after string escape")]
[($char= #\a c) (tokenize-string (cons #\x7 ls) p)]
[($char= #\b c) (tokenize-string (cons #\x8 ls) p)]
[($char= #\t c) (tokenize-string (cons #\x9 ls) p)]
[($char= #\n c) (tokenize-string (cons #\xA ls) p)]
[($char= #\v c) (tokenize-string (cons #\xB ls) p)]
[($char= #\f c) (tokenize-string (cons #\xC ls) p)]
[($char= #\r c) (tokenize-string (cons #\xD ls) p)]
[($char= #\" c) (tokenize-string (cons #\x22 ls) p)]
[($char= #\\ c) (tokenize-string (cons #\x5C ls) p)]
[($char= #\x c) ;;; unicode escape \xXXX;
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "invalid eof inside string")]
[(hex c) =>
(lambda (n)
(let f ([n n] [ac (cons c '(#\x))])
(let ([c (read-char p)])
(cond
[(eof-object? n)
(die/p p 'tokenize "invalid eof inside string")]
[(hex c) =>
(lambda (v) (f (+ (* n 16) v) (cons c ac)))]
[($char= c #\;)
(tokenize-string
(cons (checked-integer->char n ac p) ls) p)]
[else
(die/p-1 p 'tokenize
"invalid char in escape sequence"
(list->string (reverse (cons c ac))))]))))]
[else
(die/p-1 p 'tokenize
"invalid char in escape sequence" c)]))]
[(intraline-whitespace? c)
(let f ()
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "invalid eof inside string")]
[(intraline-whitespace? c) (f)]
[(memv c LF1)
(tokenize-string-continue ls p (read-char p))]
[(eqv? c #\return)
(let ([c (read-char p)])
(cond
[(memv c LF2)
(tokenize-string-continue ls p (read-char p))]
[else
(tokenize-string-continue ls p c)]))]
[else
(die/p-1 p 'tokenize
"non-whitespace character after escape")])))]
[(memv c LF1)
(tokenize-string-continue ls p (read-char p))]
[(eqv? c #\return)
(let ([c (read-char p)])
(cond
[(memv c LF2)
(tokenize-string-continue ls p (read-char p))]
[else
(tokenize-string-continue ls p c)]))]
[else (die/p-1 p 'tokenize "invalid string escape" c)]))]
[(memv c LF1)
(tokenize-string (cons #\linefeed ls) p)]
[(eqv? c #\return)
(let ([c (peek-char p)])
(when (memv c LF2) (read-char p))
(tokenize-string (cons #\linefeed ls) p))]
[else
(tokenize-string (cons c ls) p)]))
(define skip-comment
(lambda (p)
(let ([c (read-char p)])
(unless (or (eof-object? c) (memv c LF1) (eqv? c #\return))
(skip-comment p)))))
(define tokenize-dot
(lambda (p)
(let ([c (peek-char p)])
(cond
[(eof-object? c) 'dot]
[(delimiter? c) 'dot]
[($char= c #\.) ; this is second dot
(read-char p)
(let ([c (peek-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "invalid syntax .. near end of file")]
[($char= c #\.) ; this is the third
(read-char p)
(let ([c (peek-char p)])
(cond
[(eof-object? c) '(datum . ...)]
[(delimiter? c) '(datum . ...)]
[else
(die/p p 'tokenize "invalid syntax"
(string-append "..." (string c)))]))]
[else
(die/p p 'tokenize "invalid syntax"
(string-append ".." (string c)))]))]
[else
(cons 'datum
(u:dot p '(#\.) 10 #f #f +1))]))))
(define tokenize-char*
(lambda (i str p d)
(cond
[(fx= i (string-length str))
(let ([c (peek-char p)])
(cond
[(eof-object? c) d]
[(delimiter? c) d]
[else
(die/p p 'tokenize "invalid character after sequence"
(string-append str (string c)))]))]
[else
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize
"invalid eof in the middle of expected sequence" str)]
[($char= c (string-ref str i))
(tokenize-char* (fxadd1 i) str p d)]
[else
(die/p-1 p 'tokenize
"invalid char while scanning string"
c str)]))])))
(define tokenize-char-seq
(lambda (p str d)
(let ([c (peek-char p)])
(cond
[(eof-object? c) (cons 'datum (string-ref str 0))]
[(delimiter? c) (cons 'datum (string-ref str 0))]
[($char= (string-ref str 1) c)
(read-char p)
(tokenize-char* 2 str p d)]
[else
(die/p p 'tokenize "invalid syntax"
(string-ref str 0) c)]))))
(define tokenize-char
(lambda (p)
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "invalid #\\ near end of file")]
[(eqv? #\n c)
(let ([c (peek-char p)])
(cond
[(eof-object? c)
(read-char p)
'(datum . #\n)]
[(eqv? #\u c)
(read-char p)
(tokenize-char-seq p "ul" '(datum . #\x0))]
[(eqv? #\e c)
(read-char p)
(tokenize-char-seq p "ewline" '(datum . #\xA))]
[(delimiter? c)
'(datum . #\n)]
[else
(die/p p 'tokenize "invalid syntax"
(string #\# #\\ #\n c))]))]
[(eqv? #\a c)
(tokenize-char-seq p "alarm" '(datum . #\x7))]
[(eqv? #\b c)
(tokenize-char-seq p "backspace" '(datum . #\x8))]
[(eqv? #\t c)
(tokenize-char-seq p "tab" '(datum . #\x9))]
[(eqv? #\l c)
(tokenize-char-seq p "linefeed" '(datum . #\xA))]
[(eqv? #\v c)
(tokenize-char-seq p "vtab" '(datum . #\xB))]
[(eqv? #\p c)
(tokenize-char-seq p "page" '(datum . #\xC))]
[(eqv? #\r c)
(tokenize-char-seq p "return" '(datum . #\xD))]
[(eqv? #\e c)
(tokenize-char-seq p "esc" '(datum . #\x1B))]
[(eqv? #\s c)
(tokenize-char-seq p "space" '(datum . #\x20))]
[(eqv? #\d c)
(tokenize-char-seq p "delete" '(datum . #\x7F))]
[(eqv? #\x c)
(let ([n (peek-char p)])
(cond
[(or (eof-object? n) (delimiter? n))
'(datum . #\x)]
[(hex n) =>
(lambda (v)
(read-char p)
(let f ([v v] [ac (cons n '(#\x))])
(let ([c (peek-char p)])
(cond
[(eof-object? c)
(cons 'datum (checked-integer->char v ac p))]
[(delimiter? c)
(cons 'datum (checked-integer->char v ac p))]
[(hex c) =>
(lambda (v0)
(read-char p)
(f (+ (* v 16) v0) (cons c ac)))]
[else
(die/p p 'tokenize
"invalid character sequence"
(list->string (reverse (cons c ac))))]))))]
[else
(die/p p 'tokenize "invalid character sequence"
(string-append "#\\" (string n)))]))]
[else
(let ([n (peek-char p)])
(cond
[(eof-object? n) (cons 'datum c)]
[(delimiter? n) (cons 'datum c)]
[else
(die/p p 'tokenize "invalid syntax"
(string-append "#\\" (string c n)))]))]))))
(define (hex x)
(cond
[(and ($char<= #\0 x) ($char<= x #\9))
($fx- ($char->fixnum x) ($char->fixnum #\0))]
[(and ($char<= #\a x) ($char<= x #\f))
($fx- ($char->fixnum x)
($fx- ($char->fixnum #\a) 10))]
[(and ($char<= #\A x) ($char<= x #\F))
($fx- ($char->fixnum x)
($fx- ($char->fixnum #\A) 10))]
[else #f]))
(define multiline-error
(lambda (p)
(die/p p 'tokenize
"end of file encountered while inside a #|-style comment")))
(define apprev
(lambda (str i ac)
(cond
[(fx= i (string-length str)) ac]
[else
(apprev str (fx+ i 1) (cons (string-ref str i) ac))])))
(define multiline-comment
(lambda (p)
(define f
(lambda (p ac)
(let ([c (read-char p)])
(cond
[(eof-object? c) (multiline-error p)]
[($char= #\| c)
(let g ([c (read-char p)] [ac ac])
(cond
[(eof-object? c) (multiline-error p)]
[($char= #\# c) ac]
[($char= #\| c)
(g (read-char p) (cons c ac))]
[else (f p (cons c ac))]))]
[($char= #\# c)
(let ([c (read-char p)])
(cond
[(eof-object? c) (multiline-error p)]
[($char= #\| c)
(let ([v (multiline-comment p)])
(if (string? v)
(f p (apprev v 0 ac))
(f p ac)))]
[else
(f p (cons c (cons #\# ac)))]))]
[else (f p (cons c ac))]))))
(let ([ac (f p '())])
((comment-handler)
(list->string (reverse ac))))))
(define tokenize-hash
(lambda (p)
(tokenize-hash/c (read-char p) p)))
(define (skip-whitespace p caller)
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "invalid eof inside" caller)]
[(char-whitespace? c)
(skip-whitespace p caller)]
[else c])))
(define tokenize-hash/c
(lambda (c p)
(cond
[(eof-object? c) (die/p p 'tokenize "invalid # near end of file")]
[(memq c '(#\t #\T))
(let ([c1 (peek-char p)])
(cond
[(eof-object? c1) '(datum . #t)]
[(delimiter? c1) '(datum . #t)]
[else (die/p p 'tokenize
(format "invalid syntax near #~a~a" c c1))]))]
[(memq c '(#\f #\F))
(let ([c1 (peek-char p)])
(cond
[(eof-object? c1) '(datum . #f)]
[(delimiter? c1) '(datum . #f)]
[else (die/p p 'tokenize
(format "invalid syntax near #~a~a" c c1))]))]
[($char= #\\ c) (tokenize-char p)]
[($char= #\( c) 'vparen]
[($char= #\' c) '(macro . syntax)]
[($char= #\` c) '(macro . quasisyntax)]
[($char= #\, c)
(let ([c (peek-char p)])
(cond
[(eqv? c #\@) (read-char p)
'(macro . unsyntax-splicing)]
[else '(macro . unsyntax)]))]
[($char= #\! c)
(let ([e (read-char p)])
(when (eof-object? e)
(die/p p 'tokenize "invalid eof near #!"))
(case e
[(#\e)
(when (eq? (port-mode p) 'r6rs-mode)
(die/p-1 p 'tokenize "invalid syntax: #!e"))
(read-char* p '(#\e) "of" "eof sequence" #f #f)
(cons 'datum (eof-object))]
[(#\r)
(read-char* p '(#\r) "6rs" "#!r6rs comment" #f #f)
(set-port-mode! p 'r6rs-mode)
(tokenize/1 p)]
[(#\i)
(read-char* p '(#\i) "karus" "#!ikarus comment" #f #f)
(set-port-mode! p 'ikarus-mode)
(tokenize/1 p)]
[else
(die/p-1 p 'tokenize
(format "invalid syntax near #!~a" e))]))]
[(digit? c)
(when (eq? (port-mode p) 'r6rs-mode)
(die/p-1 p 'tokenize "graph syntax is invalid in #!r6rs mode"
(format "#~a" c)))
(tokenize-hashnum p (char->num c))]
[($char= #\: c)
(when (eq? (port-mode p) 'r6rs-mode)
(die/p-1 p 'tokenize "gensym syntax is invalid in #!r6rs mode"
(format "#~a" c)))
(let* ([c (skip-whitespace p "gensym")]
[id0
(cond
[(initial? c)
(list->string
(reverse (tokenize-identifier (cons c '()) p)))]
[($char= #\| c)
(list->string
(reverse (tokenize-bar p '())))]
[else
(die/p-1 p 'tokenize
"invalid char inside gensym" c)])])
(cons 'datum (gensym id0)))]
[($char= #\{ c)
(when (eq? (port-mode p) 'r6rs-mode)
(die/p-1 p 'tokenize "gensym syntax is invalid in #!r6rs mode"
(format "#~a" c)))
(let* ([c (skip-whitespace p "gensym")]
[id0
(cond
[(initial? c)
(list->string
(reverse (tokenize-identifier (cons c '()) p)))]
[($char= #\| c)
(list->string
(reverse (tokenize-bar p '())))]
[else
(die/p-1 p 'tokenize
"invalid char inside gensym" c)])]
[c (skip-whitespace p "gensym")])
(cond
[($char= #\} c)
(cons 'datum
(foreign-call "ikrt_strings_to_gensym" #f id0))]
[else
(let ([id1
(cond
[(initial? c)
(list->string
(reverse
(tokenize-identifier
(cons c '()) p)))]
[($char= #\| c)
(list->string
(reverse (tokenize-bar p '())))]
[else
(die/p-1 p 'tokenize
"invalid char inside gensym" c)])])
(let ([c (skip-whitespace p "gensym")])
(cond
[($char= #\} c)
(cons 'datum
(foreign-call "ikrt_strings_to_gensym"
id0 id1))]
[else
(die/p-1 p 'tokenize
"invalid char inside gensym" c)])))]))]
[($char= #\v c)
(let ([c (read-char p)])
(cond
[($char= #\u c)
(let ([c (read-char p)])
(cond
[($char= c #\8)
(let ([c (read-char p)])
(cond
[($char= c #\() 'vu8]
[(eof-object? c)
(die/p p 'tokenize "invalid eof object after #vu8")]
[else (die/p-1 p 'tokenize
(format "invalid sequence #vu8~a" c))]))]
[(eof-object? c)
(die/p p 'tokenize "invalid eof object after #vu")]
[else (die/p-1 p 'tokenize
(format "invalid sequence #vu~a" c))]))]
[(eof-object? c)
(die/p p 'tokenize "invalid eof object after #v")]
[else (die/p p 'tokenize
(format "invalid sequence #v~a" c))]))]
[(memq c '(#\e #\E))
(cons 'datum (parse-string p (list c #\#) 10 #f 'e))]
[(memq c '(#\i #\I))
(cons 'datum (parse-string p (list c #\#) 10 #f 'i))]
[(memq c '(#\b #\B))
(cons 'datum (parse-string p (list c #\#) 2 2 #f))]
[(memq c '(#\x #\X))
(cons 'datum (parse-string p (list c #\#) 16 16 #f))]
[(memq c '(#\o #\O))
(cons 'datum (parse-string p (list c #\#) 8 8 #f))]
[(memq c '(#\d #\D))
(cons 'datum (parse-string p (list c #\#) 10 10 #f))]
;[($char= #\@ c) DEAD: Unfixable due to port encoding
; that does not allow mixing binary and
; textual data in the same port.
; Left here for historical value
; (when (eq? (port-mode p) 'r6rs-mode)
; (die/p-1 p 'tokenize "fasl syntax is invalid in #!r6rs mode"
; (format "#~a" c)))
; (die/p-1 p 'read "FIXME: fasl read disabled")
; '(cons 'datum ($fasl-read p))]
[else
(die/p-1 p 'tokenize
(format "invalid syntax #~a" c))])))
(define (num-error p str ls)
(die/p-1 p 'read str
(list->string (reverse ls))))
(define-syntax port-config
(syntax-rules (GEN-TEST GEN-ARGS FAIL EOF-ERROR GEN-DELIM-TEST)
[(_ GEN-ARGS k . rest) (k (p ac) . rest)]
[(_ FAIL (p ac))
(num-error p "invalid numeric sequence" ac)]
[(_ FAIL (p ac) c)
(num-error p "invalid numeric sequence" (cons c ac))]
[(_ EOF-ERROR (p ac))
(num-error p "invalid eof while reading number" ac)]
[(_ GEN-DELIM-TEST c sk fk)
(if (delimiter? c) sk fk)]
[(_ GEN-TEST var next fail (p ac) eof-case char-case)
(let ([c (peek-char p)])
(if (eof-object? c)
(let ()
(define-syntax fail
(syntax-rules ()
[(_) (num-error p "invalid numeric sequence" ac)]))
eof-case)
(let ([var c])
(define-syntax fail
(syntax-rules ()
[(_)
(num-error p "invalid numeric sequence"
(cons var ac))]))
(define-syntax next
(syntax-rules ()
[(_ who args (... ...))
(who p (cons (get-char p) ac) args (... ...))]))
char-case)))]))
(define-string->number-parser port-config
(parse-string u:digit+ u:sign u:dot))
(define (read-char* p ls str who ci? delimited?)
(let f ([i 0] [ls ls])
(cond
[(fx= i (string-length str))
(when delimited?
(let ([c (peek-char p)])
(when (and (not (eof-object? c)) (not (delimiter? c)))
(die/p p 'tokenize
(format "invalid ~a: ~s" who
(list->string (reverse (cons c ls))))))))]
[else
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize
(format "invalid eof inside ~a" who))]
[(or (and (not ci?) (char=? c (string-ref str i)))
(and ci? (char=? (char-downcase c) (string-ref str i))))
(f (add1 i) (cons c ls))]
[else
(die/p-1 p 'tokenize
(format "invalid ~a: ~s" who
(list->string (reverse (cons c ls)))))]))])))
(define (tokenize-hashnum p n)
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "invalid eof inside #n mark/ref")]
[($char= #\= c) (cons 'mark n)]
[($char= #\# c) (cons 'ref n)]
[(digit? c)
(tokenize-hashnum p (fx+ (fx* n 10) (char->num c)))]
[else
(die/p-1 p 'tokenize "invalid char while inside a #n mark/ref" c)])))
(define tokenize-bar
(lambda (p ac)
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "unexpected eof while reading symbol")]
[($char= #\\ c)
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "unexpected eof while reading symbol")]
[else (tokenize-bar p (cons c ac))]))]
[($char= #\| c) ac]
[else (tokenize-bar p (cons c ac))]))))
(define (tokenize-backslash main-ac p)
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "invalid eof after symbol escape")]
[($char= #\x c)
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "invalid eof after \\x")]
[(hex c) =>
(lambda (v)
(let f ([v v] [ac `(,c #\x #\\)])
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize
(format "invalid eof after ~a"
(list->string (reverse ac))))]
[($char= #\; c)
(tokenize-identifier
(cons (checked-integer->char v ac p) main-ac)
p)]
[(hex c) =>
(lambda (v0)
(f (+ (* v 16) v0) (cons c ac)))]
[else
(die/p-1 p 'tokenize "invalid sequence"
(list->string (cons c (reverse ac))))]))))]
[else
(die/p-1 p 'tokenize
(format "invalid sequence \\x~a" c))]))]
[else
(die/p-1 p 'tokenize
(format "invalid sequence \\~a" c))])))
(define tokenize/c
(lambda (c p)
(cond
[(eof-object? c)
(error 'tokenize/c "hmmmm eof")
(eof-object)]
[($char= #\( c) 'lparen]
[($char= #\) c) 'rparen]
[($char= #\[ c) 'lbrack]
[($char= #\] c) 'rbrack]
[($char= #\' c) '(macro . quote)]
[($char= #\` c) '(macro . quasiquote)]
[($char= #\, c)
(let ([c (peek-char p)])
(cond
[(eof-object? c) '(macro . unquote)]
[($char= c #\@)
(read-char p)
'(macro . unquote-splicing)]
[else '(macro . unquote)]))]
[($char= #\# c) (tokenize-hash p)]
[(char<=? #\0 c #\9)
(let ([d (fx- (char->integer c) (char->integer #\0))])
(cons 'datum
(u:digit+ p (list c) 10 #f #f +1 d)))]
[(initial? c)
(let ([ls (reverse (tokenize-identifier (cons c '()) p))])
(cons 'datum (string->symbol (list->string ls))))]
[($char= #\" c)
(let ([ls (tokenize-string '() p)])
(cons 'datum (list->string (reverse ls))))]
[(memq c '(#\+))
(let ([c (peek-char p)])
(cond
[(eof-object? c) '(datum . +)]
[(delimiter? c) '(datum . +)]
[else
(cons 'datum
(u:sign p '(#\+) 10 #f #f +1))]))]
[(memq c '(#\-))
(let ([c (peek-char p)])
(cond
[(eof-object? c) '(datum . -)]
[(delimiter? c) '(datum . -)]
[($char= c #\>)
(read-char p)
(let ([ls (tokenize-identifier '() p)])
(let ([str (list->string (cons* #\- #\> (reverse ls)))])
(cons 'datum (string->symbol str))))]
[else
(cons 'datum
(u:sign p '(#\-) 10 #f #f -1))]))]
[($char= #\. c)
(tokenize-dot p)]
[($char= #\| c)
(when (eq? (port-mode p) 'r6rs-mode)
(die/p p 'tokenize "|symbol| syntax is invalid in #!r6rs mode"))
(let ([ls (reverse (tokenize-bar p '()))])
(cons 'datum (string->symbol (list->string ls))))]
[($char= #\\ c)
(cons 'datum
(string->symbol
(list->string
(reverse (tokenize-backslash '() p)))))]
;[($char= #\{ c) 'lbrace]
[($char= #\@ c)
(when (eq? (port-mode p) 'r6rs-mode)
(die/p p 'tokenize "@-expr syntax is invalid in #!r6rs mode"))
'at-expr]
[else
(die/p-1 p 'tokenize "invalid syntax" c)])))
(define tokenize/1
(lambda (p)
(let ([c (read-char p)])
(cond
[(eof-object? c) (eof-object)]
[(eqv? c #\;)
(skip-comment p)
(tokenize/1 p)]
[(eqv? c #\#)
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "invalid eof after #")]
[(eqv? c #\;)
(read-as-comment p)
(tokenize/1 p)]
[(eqv? c #\|)
(multiline-comment p)
(tokenize/1 p)]
[else
(tokenize-hash/c c p)]))]
[(char-whitespace? c) (tokenize/1 p)]
[else (tokenize/c c p)]))))
(define tokenize/1+pos
(lambda (p)
(let ([pos (input-port-byte-position p)])
(let ([c (read-char p)])
(cond
[(eof-object? c) (values (eof-object) pos)]
[(eqv? c #\;)
(skip-comment p)
(tokenize/1+pos p)]
[(eqv? c #\#)
(let ([pos (input-port-byte-position p)])
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "invalid eof after #")]
[(eqv? c #\;)
(read-as-comment p)
(tokenize/1+pos p)]
[(eqv? c #\|)
(multiline-comment p)
(tokenize/1+pos p)]
[else
(values (tokenize-hash/c c p) pos)])))]
[(char-whitespace? c) (tokenize/1+pos p)]
[else
(values (tokenize/c c p) pos)])))))
(define tokenize-script-initial
(lambda (p)
(let ([c (read-char p)])
(cond
[(eof-object? c) c]
[(eqv? c #\;)
(skip-comment p)
(tokenize/1 p)]
[(eqv? c #\#)
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "invalid eof after #")]
[(eqv? c #\!)
(skip-comment p)
(tokenize/1 p)]
[(eqv? c #\;)
(read-as-comment p)
(tokenize/1 p)]
[(eqv? c #\|)
(multiline-comment p)
(tokenize/1 p)]
[else
(tokenize-hash/c c p)]))]
[(char-whitespace? c) (tokenize/1 p)]
[else (tokenize/c c p)]))))
(define tokenize-script-initial+pos
(lambda (p)
(let ([pos (input-port-byte-position p)])
(let ([c (read-char p)])
(cond
[(eof-object? c) (values (eof-object) pos)]
[(eqv? c #\;)
(skip-comment p)
(tokenize/1+pos p)]
[(eqv? c #\#)
(let ([pos (input-port-byte-position p)])
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'tokenize "invalid eof after #")]
[(eqv? c #\!)
(skip-comment p)
(tokenize/1+pos p)]
[(eqv? c #\;)
(read-as-comment p)
(tokenize/1+pos p)]
[(eqv? c #\|)
(multiline-comment p)
(tokenize/1+pos p)]
[else
(values (tokenize-hash/c c p) pos)])))]
[(char-whitespace? c) (tokenize/1+pos p)]
[else (values (tokenize/c c p) pos)])))))
(define-struct loc (value value^ set?))
;;; this is reverse engineered from psyntax.ss
(define-struct annotation (expression source stripped))
;;; - source is a pair of file-name x char-position
;;; - stripped is an s-expression with no annotations
;;; - expression is a list/vector/id/whathaveyou that
;;; may contain further annotations.
(module (read-expr read-expr-script-initial)
(define-syntax tokenize/1 syntax-error)
(define (annotate-simple datum pos p)
(make-annotation datum (cons (port-id p) pos) datum))
(define (annotate stripped expression pos p)
(make-annotation expression (cons (port-id p) pos) stripped))
(define read-list
(lambda (p locs k end mis init?)
(let-values ([(t pos) (tokenize/1+pos p)])
(cond
[(eof-object? t)
(die/p p 'read "end of file encountered while reading list")]
[(eq? t end) (values '() '() locs k)]
[(eq? t mis)
(die/p-1 p 'read "paren mismatch")]
[(eq? t 'dot)
(when init?
(die/p-1 p 'read "invalid dot while reading list"))
(let-values ([(d d^ locs k) (read-expr p locs k)])
(let-values ([(t pos^) (tokenize/1+pos p)])
(cond
[(eq? t end) (values d d^ locs k)]
[(eq? t mis)
(die/p-1 p 'read "paren mismatch")]
[(eq? t 'dot)
(die/p-1 p 'read "cannot have two dots in a list")]
[else
(die/p-1 p 'read
(format "expecting ~a, got ~a" end t))])))]
[else
(let-values ([(a a^ locs k) (parse-token p locs k t pos)])
(let-values ([(d d^ locs k) (read-list p locs k end mis #f)])
(let ([x (cons a d)] [x^ (cons a^ d^)])
(values x x^ locs (extend-k-pair x x^ a d k)))))]))))
(define extend-k-pair
(lambda (x x^ a d k)
(cond
[(or (loc? a) (loc? d))
(lambda ()
(let ([a (car x)])
(when (loc? a)
(set-car! x (loc-value a))
(set-car! x^ (loc-value^ a))))
(let ([d (cdr x)])
(when (loc? d)
(set-cdr! x (loc-value d))
(set-cdr! x^ (loc-value^ d))))
(k))]
[else k])))
(define vector-put
(lambda (v v^ k i ls ls^)
(cond
[(null? ls) k]
[else
(let ([a (car ls)])
(vector-set! v i a)
(vector-set! v^ i (car ls^))
(vector-put v v^
(if (loc? a)
(lambda ()
(vector-set! v i (loc-value a))
(vector-set! v^ i (loc-value^ a))
(k))
k)
(fxsub1 i)
(cdr ls)
(cdr ls^)))])))
(define read-vector
(lambda (p locs k count ls ls^)
(let-values ([(t pos) (tokenize/1+pos p)])
(cond
[(eof-object? t)
(die/p p 'read "end of file encountered while reading a vector")]
[(eq? t 'rparen)
(let ([v (make-vector count)] [v^ (make-vector count)])
(let ([k (vector-put v v^ k (fxsub1 count) ls ls^)])
(values v v^ locs k)))]
[(eq? t 'rbrack)
(die/p-1 p 'read "unexpected ] while reading a vector")]
[(eq? t 'dot)
(die/p-1 p 'read "unexpected . while reading a vector")]
[else
(let-values ([(a a^ locs k) (parse-token p locs k t pos)])
(read-vector p locs k (fxadd1 count)
(cons a ls) (cons a^ ls^)))]))))
(define read-bytevector
(lambda (p locs k count ls)
(let-values ([(t pos) (tokenize/1+pos p)])
(cond
[(eof-object? t)
(die/p p 'read "end of file encountered while reading a bytevector")]
[(eq? t 'rparen)
(let ([v (u8-list->bytevector (reverse ls))])
(values v v locs k))]
[(eq? t 'rbrack)
(die/p-1 p 'read "unexpected ] while reading a bytevector")]
[(eq? t 'dot)
(die/p-1 p 'read "unexpected . while reading a bytevector")]
[else
(let-values ([(a a^ locs k) (parse-token p locs k t pos)])
(unless (and (fixnum? a) (fx<= 0 a) (fx<= a 255))
(die/ann a^ 'read
"invalid value in a bytevector" a))
(read-bytevector p locs k (fxadd1 count)
(cons a ls)))]))))
(define read-at-expr
(lambda (p locs k at-pos)
(define-struct nested (a a^))
(define-struct nested* (a* a*^))
(define (get-chars chars pos p a* a*^)
(if (null? chars)
(values a* a*^)
(let ([str (list->string chars)])
(let ([str^ (annotate-simple str pos p)])
(values (cons str a*) (cons str^ a*^))))))
(define (return start-pos start-col c*** p)
(let ([indent
(apply min start-col
(map
(lambda (c**)
(define (st00 c* c** n)
(if (null? c*)
(st0 c** n)
(if (char=? (car c*) #\space)
(st00 (cdr c*) c** (+ n 1))
n)))
(define (st0 c** n)
(if (null? c**)
start-col
(let ([c* (car c**)])
(if (or (nested? c*) (nested*? c*))
start-col
(st00 (car c*) (cdr c**) n)))))
(st0 c** 0))
(cdr c***)))])
(define (convert c*)
(if (or (nested? c*) (nested*? c*))
c*
(let ([str (list->string (car c*))])
(let ([str^ (annotate-simple str (cdr c*) p)])
(make-nested str str^)))))
(define (trim/convert c**)
(define (mk n pos)
(let ([str (make-string (- n indent) #\space)])
(let ([str^ (annotate-simple str pos p)])
(make-nested str str^))))
(define (s1 c* pos c** n)
(if (null? c*)
(let ([c* (car c**)])
(if (or (nested? c*) (nested*? c*))
(cons (mk n pos) (map convert c**))
(s1 c* pos (cdr c**) n)))
(if (char=? (car c*) #\space)
(s1 (cdr c*) pos c** (+ n 1))
(cons*
(mk n pos)
(map convert (cons (cons c* pos) c**))))))
(define (s00 c* pos c** n)
(if (null? c*)
(s0 c** n)
(if (char=? #\space (car c*))
(if (< n indent)
(s00 (cdr c*) pos c** (+ n 1))
(s1 (cdr c*) pos c** (+ n 1)))
(map convert (cons (cons c* pos) c**)))))
(define (s0 c** n)
(if (null? c**)
'()
(let ([c* (car c**)])
(if (or (nested? c*) (nested*? c*))
(map convert c**)
(s00 (car c*) (cdr c*) (cdr c**) n)))))
(s0 c** 0))
(define (cons-initial c** c***)
(define (all-white? c**)
(andmap (lambda (c*)
(and (not (nested? c*))
(not (nested*? c*))
(andmap
(lambda (c) (char=? c #\space))
(car c*))))
c**))
(define (nl)
(let ([str "\n"])
(list (make-nested str str))))
(define (S1 c*** n)
(if (null? c***)
(make-list n (nl))
(let ([c** (car c***)] [c*** (cdr c***)])
(if (all-white? c**)
(S1 c*** (+ n 1))
(append
(make-list n (nl))
(cons (trim/convert c**)
(S2 c*** 0 0)))))))
(define (S2 c*** n m)
(if (null? c***)
(make-list (+ n m) (nl))
(let ([c** (car c***)] [c*** (cdr c***)])
(if (all-white? c**)
(S2 c*** (+ n 1) -1)
(append
(make-list (+ n 1) (nl))
(cons (trim/convert c**)
(S2 c*** 0 0)))))))
(define (S0 c** c***)
(if (all-white? c**)
(S1 c*** 0)
(cons
(map convert c**)
(S2 c*** 0 0))))
(S0 c** c***))
(let ([c** (cons-initial (car c***) (cdr c***))])
(let ([n* (apply append c**)])
(define (extract p p* ls)
(let f ([ls ls])
(cond
[(null? ls) '()]
[(nested? (car ls)) (cons (p (car ls)) (f (cdr ls)))]
[else (append (p* (car ls)) (f (cdr ls)))])))
(let ([c* (extract nested-a nested*-a* n*)]
[c*^ (extract nested-a^ nested*-a*^ n*)])
(values c* (annotate c* c*^ start-pos p) locs k))))))
(define (read-text p locs k pref*)
(let ([start-pos (port-position p)]
[start-col (input-port-column-number p)])
(let f ([c* '()] [pos start-pos]
[c** '()] [c*** '()]
[depth 0] [locs locs] [k k])
(define (match-prefix c* pref*)
(cond
[(and (pair? c*) (pair? pref*))
(and (char=? (car c*) (car pref*))
(match-prefix (cdr c*) (cdr pref*)))]
[else (and (null? pref*) c*)]))
(let ([c (read-char p)])
(cond
[(eof-object? c)
(die/p p 'read "end of file while reading @-expr text")]
[(char=? c #\})
(let g ([x* (cons #\} c*)] [p* pref*])
(if (null? p*)
(if (= depth 0)
(let ([c**
(reverse
(if (null? c*)
c**
(cons (cons (reverse c*) pos) c**)))])
(let ([c*** (reverse (cons c** c***))])
(return start-pos start-col c*** p)))
(f x* pos c** c*** (- depth 1) locs k))
(let ([c (peek-char p)])
(cond
[(eof-object? c)
(die/p p 'read "invalid eof inside @-expression")]
[(char=? c (rev-punc (car p*)))
(read-char p)
(g (cons c x*) (cdr p*))]
[else
(f x* pos c** c*** depth locs k)]))))]
[(char=? c #\{)
(f (cons c c*) pos c** c***
(if (match-prefix c* pref*) (+ depth 1) depth)
locs k)]
[(char=? c #\newline)
(f '()
(port-position p)
'()
(cons (reverse
(if (null? c*)
c**
(cons (cons (reverse c*) pos) c**)))
c***)
depth locs k)]
[(and (char=? c #\@) (match-prefix c* pref*)) =>
(lambda (c*)
(let ([c (peek-char p)])
(cond
[(eof-object? c)
(die/p p 'read "invalid eof inside nested @-expr")]
[(char=? c #\")
(read-char p)
(let ([c* (tokenize-string c* p)])
(f c* pos c** c*** depth locs k))]
[else
(let-values ([(a* a*^ locs k)
(read-at-text-mode p locs k)])
(f '()
(port-position p)
(cons (make-nested* a* a*^)
(if (null? c*)
c**
(cons (cons (reverse c*) pos) c**)))
c*** depth locs k))])))]
[else
(f (cons c c*) pos c** c*** depth locs k)])))))
(define (read-brackets p locs k)
(let-values ([(a* a*^ locs k)
(read-list p locs k 'rbrack 'rparen #t)])
(unless (list? a*)
(die/ann a*^ 'read "not a proper list"))
(let ([c (peek-char p)])
(cond
[(eof-object? c) ;;; @<cmd>[...]
(values a* a*^ locs k)]
[(char=? c #\{)
(read-char p)
(let-values ([(b* b*^ locs k)
(read-text p locs k '())])
(values (append a* b*)
(append a*^ b*^)
locs k))]
[(char=? c #\|)
(read-char p)
(let-values ([(b* b*^ locs k)
(read-at-bar p locs k #t)])
(values (append a* b*)
(append a*^ b*^)
locs k))]
[else (values a* a*^ locs k)]))))
(define (left-punc? c)
(define chars "([<!?~$%^&*-_+=:")
(let f ([i 0])
(cond
[(= i (string-length chars)) #f]
[(char=? c (string-ref chars i)) #t]
[else (f (+ i 1))])))
(define (rev-punc c)
(cond
[(char=? c #\() #\)]
[(char=? c #\[) #\]]
[(char=? c #\<) #\>]
[else c]))
(define (read-at-bar p locs k text-mode?)
(let ([c (peek-char p)])
(cond
[(eof-object? c)
(die/p p 'read "eof inside @|-expression")]
[(and (char=? c #\|) text-mode?) ;;; @||
(read-char p)
(values '() '() locs k)]
[(char=? c #\{) ;;; @|{
(read-char p)
(read-text p locs k '(#\|))]
[(left-punc? c) ;;; @|<({
(read-char p)
(let ([pos (port-position p)])
(let f ([ls (list c)])
(let ([c (peek-char p)])
(cond
[(eof-object? c)
(die/p p 'read "eof inside @|< mode")]
[(left-punc? c)
(read-char p)
(f (cons c ls))]
[(char=? c #\{)
(read-char p)
(read-text p locs k (append ls '(#\|)))]
[else
(read-at-bar-others ls p locs k)]))))]
[text-mode? ;;; @|5 6 7|
(read-at-bar-datum p locs k)]
[else
(die/p p 'read "invalid char in @| mode" c)])))
(define (read-at-bar-others ls p locs k)
(define (split ls)
(cond
[(null? ls) (values '() '())]
[(initial? (car ls))
(let-values ([(a d) (split (cdr ls))])
(values (cons (car ls) a) d))]
[else
(values '() ls)]))
(define (mksymbol ls)
(let ([s (string->symbol
(list->string
(reverse ls)))])
(values s s)))
(let-values ([(inits rest) (split ls)])
(let ([ls (tokenize-identifier inits p)])
(let-values ([(s s^) (mksymbol ls)])
(let g ([rest rest]
[a* (list s)]
[a*^ (list s^)]
[locs locs]
[k k])
(if (null? rest)
(let-values ([(b* b*^ locs k)
(read-at-bar-datum p locs k)])
(values (append a* b*) (append a*^ b*^) locs k))
(let ([x (car rest)])
(case x
[(#\() #\) ;;; vim paren-matching sucks
(let-values ([(b* b*^ locs k)
(read-list p locs k 'rparen 'rbrack #t)])
(g (cdr rest)
(list (append a* b*))
(list (append a*^ b*^))
locs k))]
[(#\[) #\] ;;; vim paren-matching sucks
(let-values ([(b* b*^ locs k)
(read-list p locs k 'rbrack 'rparen #t)])
(g (cdr rest)
(list (append a* b*))
(list (append a*^ b*^))
locs k))]
[else
(let-values ([(inits rest) (split rest)])
(let-values ([(s s^) (mksymbol inits)])
(g rest
(cons s a*)
(cons s^ a*^)
locs k)))]))))))))
(define (read-at-bar-datum p locs k)
(let ([c (peek-char p)])
(cond
[(eof-object? c) (die/p p 'read "eof inside @|datum mode")]
[(char-whitespace? c)
(read-char p)
(read-at-bar-datum p locs k)]
[(char=? c #\|)
(read-char p)
(values '() '() locs k)]
[else
(let-values ([(a a^ locs k) (read-expr p locs k)])
(let-values ([(a* a*^ locs k) (read-at-bar-datum p locs k)])
(values (cons a a*) (cons a^ a*^) locs k)))])))
(define (read-at-text-mode p locs k)
(let ([c (peek-char p)])
(cond
[(eof-object? c)
(die/p p 'read "eof encountered inside @-expression")]
[(char=? c #\|)
(read-char p)
(read-at-bar p locs k #t)]
[else
(let-values ([(a a^ locs k)
(read-at-sexpr-mode p locs k)])
(values (list a) (list a^) locs k))])))
(define (read-at-sexpr-mode p locs k)
(let ([c (peek-char p)])
(cond
[(eof-object? c)
(die/p p 'read "eof encountered inside @-expression")]
[(eqv? c '#\[) ;;; @[ ...
(read-char p)
(read-brackets p locs k)]
[(eqv? c #\{) ;;; @{ ...
(read-char p)
(read-text p locs k '())]
[(char=? c #\|)
(read-char p)
(read-at-bar p locs k #f)]
[else ;;; @<cmd> ...
(let-values ([(a a^ locs k) (read-expr p locs k)])
(let ([c (peek-char p)])
(cond
[(eof-object? c) ;;; @<cmd><eof>
(values a a^ locs k)]
[(eqv? c #\[)
(read-char p)
(let-values ([(a* a*^ locs k)
(read-brackets p locs k)])
(let ([v (cons a a*)] [v^ (cons a^ a*^)])
(values v (annotate v v^ at-pos p) locs k)))]
[(eqv? c #\{) ;;; @<cmd>{ ...
(read-char p)
(let-values ([(a* a*^ locs k)
(read-text p locs k '())])
(let ([v (cons a a*)] [v^ (cons a^ a*^)])
(values v (annotate v v^ at-pos p) locs k)))]
[(eqv? c #\|) ;;; @<cmd>| ...
(read-char p)
(let-values ([(a* a*^ locs k)
(read-at-bar p locs k #f)])
(let ([v (cons a a*)] [v^ (cons a^ a*^)])
(values v (annotate v v^ at-pos p) locs k)))]
[else
(values a a^ locs k)])))])))
(read-at-sexpr-mode p locs k)))
(define parse-token
(lambda (p locs k t pos)
(cond
[(eof-object? t)
(values (eof-object)
(annotate-simple (eof-object) pos p) locs k)]
[(eq? t 'lparen)
(let-values ([(ls ls^ locs k)
(read-list p locs k 'rparen 'rbrack #t)])
(values ls (annotate ls ls^ pos p) locs k))]
[(eq? t 'lbrack)
(let-values ([(ls ls^ locs k)
(read-list p locs k 'rbrack 'rparen #t)])
(values ls (annotate ls ls^ pos p) locs k))]
[(eq? t 'vparen)
(let-values ([(v v^ locs k)
(read-vector p locs k 0 '() '())])
(values v (annotate v v^ pos p) locs k))]
[(eq? t 'vu8)
(let-values ([(v v^ locs k)
(read-bytevector p locs k 0 '())])
(values v (annotate v v^ pos p) locs k))]
[(eq? t 'at-expr)
(read-at-expr p locs k pos)]
[(pair? t)
(cond
[(eq? (car t) 'datum)
(values (cdr t)
(annotate-simple (cdr t) pos p) locs k)]
[(eq? (car t) 'macro)
(let ([macro (cdr t)])
(define (read-macro)
(let-values ([(t pos) (tokenize/1+pos p)])
(cond
[(eof-object? t)
(die/p p 'read
(format "invalid eof after ~a read macro"
macro))]
[else (parse-token p locs k t pos)])))
(let-values ([(expr expr^ locs k) (read-macro)])
(let ([d (list expr)] [d^ (list expr^)])
(let ([x (cons macro d)]
[x^ (cons (annotate-simple macro pos p) d^)])
(values x (annotate x x^ pos p) locs
(extend-k-pair d d^ expr '() k))))))]
[(eq? (car t) 'mark)
(let ([n (cdr t)])
(let-values ([(expr expr^ locs k)
(read-expr p locs k)])
(cond
[(assq n locs) =>
(lambda (x)
(let ([loc (cdr x)])
(when (loc-set? loc) ;;; FIXME: pos
(die/p p 'read "duplicate mark" n))
(set-loc-value! loc expr)
(set-loc-value^! loc expr^)
(set-loc-set?! loc #t)
(values expr expr^ locs k)))]
[else
(let ([loc (make-loc expr 'unused #t)])
(let ([locs (cons (cons n loc) locs)])
(values expr expr^ locs k)))])))]
[(eq? (car t) 'ref)
(let ([n (cdr t)])
(cond
[(assq n locs) =>
(lambda (x)
(values (cdr x) 'unused locs k))]
[else
(let ([loc (make-loc #f 'unused #f)])
(let ([locs (cons (cons n loc) locs)])
(values loc 'unused locs k)))]))]
[else (die/p p 'read "invalid token" t)])]
[else
(die/p-1 p 'read
(format "unexpected ~s found" t))])))
(define read-expr
(lambda (p locs k)
(let-values ([(t pos) (tokenize/1+pos p)])
(parse-token p locs k t pos))))
(define read-expr-script-initial
(lambda (p locs k)
(let-values ([(t pos) (tokenize-script-initial+pos p)])
(parse-token p locs k t pos)))))
(define (reduce-loc! p)
(lambda (x)
(let ([loc (cdr x)])
(unless (loc-set? loc)
(die/p p 'read "referenced mark is not set" (car x)))
(when (loc? (loc-value loc))
(let f ([h loc] [t loc])
(if (loc? h)
(let ([h1 (loc-value h)])
(if (loc? h1)
(begin
(when (eq? h1 t)
(die/p p 'read "circular marks"))
(let ([v (f (loc-value h1) (loc-value t))])
(set-loc-value! h1 v)
(set-loc-value! h v)
v))
(begin
(set-loc-value! h h1)
h1)))
h))))))
(define (read-as-comment p)
(begin (read-expr p '() void) (void)))
(define (return-annotated x)
(cond
[(and (annotation? x) (eof-object? (annotation-expression x)))
(eof-object)]
[else x]))
(define my-read
(lambda (p)
(let-values ([(expr expr^ locs k) (read-expr p '() void)])
(cond
[(null? locs) expr]
[else
(for-each (reduce-loc! p) locs)
(k)
(if (loc? expr)
(loc-value expr)
expr)]))))
(define read-initial
(lambda (p)
(let-values ([(expr expr^ locs k) (read-expr-script-initial p '() void)])
(cond
[(null? locs) expr]
[else
(for-each (reduce-loc! p) locs)
(k)
(if (loc? expr)
(loc-value expr)
expr)]))))
(define read-annotated
(case-lambda
[(p)
(unless (input-port? p)
(error 'read-annotated "not an input port" p))
(let-values ([(expr expr^ locs k) (read-expr p '() void)])
(cond
[(null? locs) (return-annotated expr^)]
[else
(for-each (reduce-loc! p) locs)
(k)
(if (loc? expr)
(loc-value^ expr)
(return-annotated expr^))]))]
[() (read-annotated (current-input-port))]))
(define read-script-annotated
(lambda (p)
(let-values ([(expr expr^ locs k) (read-expr-script-initial p '() void)])
(cond
[(null? locs) (return-annotated expr^)]
[else
(for-each (reduce-loc! p) locs)
(k)
(if (loc? expr)
(loc-value^ expr)
(return-annotated expr^))]))))
(define read-token
(case-lambda
[() (tokenize/1 (current-input-port))]
[(p)
(if (input-port? p)
(tokenize/1 p)
(die 'read-token "not an input port" p))]))
(define read
(case-lambda
[() (my-read (current-input-port))]
[(p)
(if (input-port? p)
(my-read p)
(die 'read "not an input port" p))]))
(define (get-datum p)
(unless (input-port? p)
(die 'get-datum "not an input port"))
(my-read p))
(define comment-handler
;;; this is stale, maybe delete
(make-parameter
(lambda (x) (void))
(lambda (x)
(unless (procedure? x)
(die 'comment-handler "not a procedure" x))
x)))
)