preserves/implementations/racket/preserves/preserves/read-text.rkt

82 lines
3.4 KiB
Racket

#lang racket/base
(provide read-preserve/text
string->preserve)
(require racket/match)
(require racket/set)
(require "record.rkt")
(require "read-text-generic.rkt")
(require syntax/readerr)
(define *reader-name* 'read-preserve/text)
(define text-reader
(make-preserve-text-reader
#:reader-name *reader-name*
#:read-annotated-value (lambda (in-port source next parse-error*) next)
#:on-hash (lambda (in-port source next parse-error* default)
(match-lambda
[#\{ (sequence-fold in-port
source
next
skip-whitespace/commas
(set)
(lambda (s e)
(when (set-member? s e)
(parse-error* "Duplicate set element: ~v" e))
(set-add s e))
values
#\})]
[c (default c)]))
#:on-char (lambda (in-port source next parse-error* default)
(match-lambda
[#\< (match (read-sequence in-port source next skip-whitespace #\>)
['() (parse-error* "Missing record label")]
[(cons label fields) (record label fields)])]
[#\[ (read-sequence in-port source next skip-whitespace/commas #\])]
[#\{ (read-dictionary in-port source next parse-error*)]
[#\; (parse-error* "Semicolon is reserved syntax")]
[#\: (parse-error* "Unexpected key/value separator between items")]
[#\> (parse-error* "Unexpected >")]
[#\] (parse-error* "Unexpected ]")]
[#\} (parse-error* "Unexpected }")]
[#\, (parse-error* "Unexpected ,")]
[c (default c)]))))
(define string->preserve (make-preserve-string-reader *reader-name* text-reader))
(define (sequence-fold in-port source next skip-ws acc accumulate-one finish terminator-char)
(let loop ((acc acc))
(skip-ws in-port)
(match (eof-guard *reader-name* in-port source (peek-char in-port))
[(== terminator-char) (read-char in-port) (finish acc)]
[_ (loop (accumulate-one acc (next)))])))
(define (read-sequence in-port source next skip-ws terminator)
(sequence-fold in-port source next skip-ws '() (lambda (acc v) (cons v acc)) reverse terminator))
(define (read-dictionary in-port source next parse-error*)
(sequence-fold in-port
source
next
skip-whitespace/commas
(hash)
(lambda (acc k)
(skip-whitespace in-port)
(match (peek-char in-port)
[#\: (read-char in-port)
(when (hash-has-key? acc k) (parse-error* "Duplicate key: ~v" k))
(hash-set acc k (next))]
[_ (parse-error* "Missing expected key/value separator")]))
values
#\}))
(define (read-preserve/text [in-port (current-input-port)]
#:read-syntax? [read-syntax? #f]
#:decode-embedded [decode-embedded #f]
#:source [source (object-name in-port)])
(text-reader in-port source read-syntax? decode-embedded))