#lang racket/unit
(require racket/contract
syntax/parse
syntax/readerr
syntax/stx
"read-sig.rkt"
"util.rkt")
(import (prefix old- read^))
(export (rename read^ [modern-read read]
[modern-read-syntax read-syntax]))
(define modern-backwards-compatible #f) (define modern-bracketaccess #f)
(define (ismember? item lyst)
(pair? (member item lyst)))
(define (debug-result marker value)
(newline)
(display "DEBUG: ")
(display marker)
(display " ")
(write value)
(newline)
value)
(define tab #\tab)
(define (skip-whitespace port)
(define c (peek-char port))
(cond
[(and (char? c) (char-whitespace? c))
(read-char port)
(skip-whitespace port)]))
(define modern-delimiters
`(#\space #\newline #\return #\( #\) #\[ #\] #\{ #\} ,tab))
(define (read-until-delim port delims)
(let ((c (peek-char port)))
(cond
((eof-object? c) '())
((ismember? c delims) '())
((char-whitespace? c) '())
(#t (cons (read-char port) (read-until-delim port delims))))))
(define (read-error message)
(display "Error: ")
(display message)
'())
(define (read-number port starting-lyst)
(define-values (ln col pos) (port-next-location port))
(define digits
(append starting-lyst
(read-until-delim port modern-delimiters)))
(define n (string->number (list->string digits)))
(define span (length digits))
(make-stx n ln col pos span))
(define digits '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))
(define (process-period port)
(read-char port) (let ((c (peek-char port)))
(cond
((eof-object? c) '|.|) ((ismember? c digits)
(read-number port (list #\.))) (#t
(string->symbol (list->string (cons #\.
(read-until-delim port modern-delimiters))))))))
(define (underlying-read port)
(skip-whitespace port)
(let ((c (peek-char port)))
(cond
((eof-object? c) c)
((char=? c #\") (old-read-syntax (current-source-name) port))
((ismember? c digits) (read-number port '()))
((char=? c #\#)
(old-read-syntax (current-source-name) port)) ((char=? c #\.) (process-period port))
((or (char=? c #\+) (char=? c #\-)) (if (ismember? (peek-char port 1) digits)
(begin (read-char) (read-number port (list c)))
(read-symbol port)))
((char=? c #\; )
(skip-line port)
(underlying-read port))
((char=? c #\| ) (read-char port) (let ((newsymbol
(string->symbol (list->string
(read-until-delim port '(#\|))))))
(read-char port)
newsymbol))
(#t (read-symbol port)))))
(define (read-symbol port)
(define-values (ln col pos) (port-next-location port))
(define chars (read-until-delim port modern-delimiters))
(define sym (string->symbol (list->string chars)))
(make-stx sym ln col pos (length chars)))
(define (even-and-op-prefix op lyst)
(cond
[(stx-null? lyst) #t]
[(not (stx-pair? lyst)) #f] [(not (free-identifier=? op (stx-car lyst))) #f] [(null? (stx-cdr lyst)) #f] [#t (even-and-op-prefix op (stx-cdr (stx-cdr lyst)))]))
(define (simple-infix-listp stx)
(syntax-parse stx
[(fst snd:id trd rst ...)
(even-and-op-prefix #'snd
#'(rst ...))]
[_ #f]))
(define (alternating-parameters stx)
(syntax-parse stx
[() #'()]
[(e) #'(e)]
[(fst snd rst ...)
#`(fst #,@(alternating-parameters #'(rst ...)))]))
(define (transform-simple-infix stx)
(syntax-parse stx
[(fst snd rst ...)
#`(snd #,@(alternating-parameters #'(fst snd rst ...)))]))
(define (process-curly stx)
(define nfx (datum->syntax stx 'nfx stx))
(if (simple-infix-listp stx)
(transform-simple-infix stx) (syntax-cons nfx stx)))
(define (my-read-delimited-list stop-char port)
(define-values (_1 _2 start) (port-next-location port))
(define (read-accum subs)
(skip-whitespace port)
(define c (peek-char port))
(define-values (ln col pos) (port-next-location port))
(cond
[(eof-object? c)
(raise-read-eof-error "EOF in middle of list" #f ln col pos #f)
c]
[(char=? c stop-char)
(read-char port)
(datum->syntax #f subs (list #f ln col start (- pos start)))] [(ismember? c '(#\) #\] #\}))
(raise-read-error "Bad closing character" #f ln col pos #f)
c]
[else
(define datum (modern-read2 port))
(cond [(eq? datum '|.|) (read-dot-extension)]
[else (read-accum (append subs (list datum)))])]))
(define (read-dot-extension)
(define datum2 (modern-read2 port))
(define-values (ln col pos) (port-next-location port))
(skip-whitespace port)
(cond [(not (eqv? (peek-char port) stop-char))
(raise-read-error "Bad closing character after . datum"
#f ln col pos #f)]
[else (read-char port)
datum2]))
(read-accum '()))
(define (modern-process-tail port stx)
(define prefix (syntax-e stx))
(define c (peek-char port))
(cond [(not (or (symbol? prefix) (pair? prefix)))
stx] [(eof-object? c) stx]
[(char=? c #\( ) (read-char port)
(modern-process-tail port (syntax-cons stx (my-read-delimited-list #\) port)))]
[(char=? c #\[ ) (read-char port)
(modern-process-tail port
(syntax-cons stx (my-read-delimited-list #\] port)))]
[(char=? c #\{ ) (read-char port)
(modern-process-tail port
(syntax-list stx
(process-curly
(my-read-delimited-list #\} port))))]
[else stx]))
(define (skip-line port)
(let ((c (peek-char port)))
(cond
((not (or (eof-object? c) (char=? c #\newline)))
(read-char port)
(skip-line port)))))
(define (modern-read-syntax [source-name #f]
[port (current-input-port)])
(when (not source-name)
(set! source-name (object-name port)))
(parameterize ([current-source-name source-name]
[current-input-port port])
(modern-read2 port)))
(define (modern-read2 port)
(skip-whitespace port)
(define c (peek-char port))
(define-values (ln col pos) (port-next-location port))
(modern-process-tail port
(cond
[(eof-object? c) eof]
[(char=? c #\')
(read-char port)
(define q (make-stx 'quote ln col pos 0))
(syntax-cons q (modern-read2 port))]
[(char=? c #\`)
(read-char port)
(define q (make-stx 'quasiquote ln col pos 0))
(syntax-cons q (modern-read2 port))]
[(char=? c #\,)
(read-char port)
(cond [(char=? #\@ (peek-char port))
(read-char port)
(define u (make-stx 'unquote-splicing ln col pos 0))
(syntax-list u (modern-read2 port))]
[else
(define u (make-stx 'unquote ln col pos 0))
(syntax-list u (modern-read2 port))])]
[(char=? c #\( ) (if modern-backwards-compatible
(underlying-read port)
(begin
(read-char port) (my-read-delimited-list #\) port)))]
[(char=? c #\[ )
(read-char port)
(my-read-delimited-list #\] port)]
[(char=? c #\{ )
(read-char port)
(process-curly
(my-read-delimited-list #\} port))]
[(char=? c #\; ) (skip-line port)
(modern-read2 port)]
[else (define result (underlying-read port))
result])))
(define (modern-read [port (current-input-port)])
(define stx (modern-read-syntax #f port))
(if (eof-object? stx)
eof
(syntax->datum stx)))
(define (modern-filter)
(let ((result (modern-read (current-input-port))))
(if (eof-object? result)
result
(begin (write result) (newline) (modern-filter)))))
(define (modern-load filename)
(define (load port)
(let ((inp (modern-read port)))
(if (eof-object? inp)
#t
(begin
(eval inp)
(load port)))))
(load (open-input-file filename)))