pexprs.rkt

This commit is contained in:
Tony Garnock-Jones 2023-11-04 16:10:08 +01:00
parent cd4f8e410f
commit 6e3950cbc5
4 changed files with 283 additions and 22 deletions

View File

@ -7,6 +7,7 @@
(all-from-out "order.rkt")
(all-from-out "embedded.rkt")
(all-from-out "merge.rkt")
(all-from-out "pexprs.rkt")
(all-from-out "read-binary.rkt")
(all-from-out "read-text.rkt")
@ -18,7 +19,9 @@
detect-preserve-syntax
read-preserve
port->preserves
file->preserves)
file->preserves
port->pexprs
file->pexprs)
(require racket/dict)
(require racket/match)
@ -32,6 +35,7 @@
(require "order.rkt")
(require "embedded.rkt")
(require "merge.rkt")
(require "pexprs.rkt")
(require "read-binary.rkt")
(require "read-text.rkt")
@ -79,7 +83,7 @@
#:decode-embedded decode-embedded
#:source path))))
(define (port->preserves in-port
(define (port->preserves [in-port (current-input-port)]
#:read-syntax? [read-syntax? #f]
#:decode-embedded [decode-embedded #f]
#:source [source (object-name in-port)])
@ -89,3 +93,25 @@
#:decode-embedded decode-embedded
#:source source))
in-port))
(define (file->pexprs path
#:read-syntax? [read-syntax? #f]
#:decode-embedded [decode-embedded #f])
(call-with-input-file path (lambda (p) (port->pexprs p
#:read-syntax? read-syntax?
#:decode-embedded decode-embedded))))
(define (port->pexprs [in-port (current-input-port)]
#:read-syntax? [read-syntax? #f]
#:decode-embedded [decode-embedded #f]
#:source [source (object-name in-port)])
(define vs
(port->list (lambda (in-port)
(read-pexpr in-port
#:read-syntax? read-syntax?
#:decode-embedded decode-embedded
#:source source))
in-port))
(if read-syntax?
vs
(remove-trailer vs)))

View File

@ -0,0 +1,229 @@
#lang racket/base
(provide read-pexpr
string->pexpr
pexpr->preserve
preserve->pexpr
COMMA
SEMICOLON
TRAILER-ANCHOR
COLONS
*COMMA*
*SEMICOLON*
*TRAILER-ANCHOR*
RECORD
BLOCK
GROUP
SET
remove-trailer)
(require (for-syntax racket/base))
(require racket/match)
(require racket/set)
(require "record.rkt")
(require "annotation.rkt")
(require "embedded.rkt")
(require "read-text-generic.rkt")
(require (only-in racket/list append-map))
(define *reader-name* 'read-pexpr)
(define *COMMA* (record 'p '(|,|)))
(define *SEMICOLON* (record 'p '(|;|)))
(define *TRAILER-ANCHOR* (record 'a '()))
(define-match-expander COMMA
(syntax-rules () [(_) (strip-annotations (== *COMMA*) #:depth 3)])
(lambda (stx)
(syntax-case stx ()
[(_) #'*COMMA*]
[_ #'(lambda () *COMMA*)])))
(define-match-expander SEMICOLON
(syntax-rules () [(_) (strip-annotations (== *SEMICOLON*) #:depth 3)])
(lambda (stx)
(syntax-case stx ()
[(_) #'*SEMICOLON*]
[_ #'(lambda () *SEMICOLON*)])))
(define-match-expander TRAILER-ANCHOR
(syntax-rules () [(_) (strip-annotations (== *TRAILER-ANCHOR*) #:depth 2)])
(lambda (stx)
(syntax-case stx ()
[(_) #'*TRAILER-ANCHOR*]
[_ #'(lambda () *TRAILER-ANCHOR*)])))
(define-match-expander RECORD
(syntax-rules () [(_ ps ...) (strip-annotations (record 'r (list ps ...)) #:depth 2)])
(lambda (stx)
(syntax-case stx ()
[(_ v ...) #'(record 'r (list v ...))]
[_ #'(lambda vs (record 'r vs))])))
(define-match-expander BLOCK
(syntax-rules () [(_ ps ...) (strip-annotations (record 'b (list ps ...)) #:depth 2)])
(lambda (stx)
(syntax-case stx ()
[(_ v ...) #'(record 'b (list v ...))]
[_ #'(lambda vs (record 'b vs))])))
(define-match-expander GROUP
(syntax-rules () [(_ ps ...) (strip-annotations (record 'g (list ps ...)) #:depth 2)])
(lambda (stx)
(syntax-case stx ()
[(_ v ...) #'(record 'g (list v ...))]
[_ #'(lambda vs (record 'g vs))])))
(define-match-expander SET
(syntax-rules () [(_ ps ...) (strip-annotations (record 's (list ps ...)) #:depth 2)])
(lambda (stx)
(syntax-case stx ()
[(_ v ...) #'(record 's (list v ...))]
[_ #'(lambda vs (record 's vs))])))
(define (colon-sym-length s)
(and (symbol? s)
(let ((s (string->list (symbol->string s))))
(and (andmap (lambda (c) (eqv? c #\:)) s)
(length s)))))
(define (colon-sym n) (string->symbol (make-string n #\:)))
(define (make-colons n) (record 'p (list (colon-sym n))))
(define-match-expander COLONS
(syntax-rules () [(_ n) (strip-annotations (record 'p (list (app colon-sym-length n))) #:depth 3)])
(lambda (stx)
(syntax-case stx ()
[(_ n) (let ((s (string->symbol (make-string (syntax->datum #'n) #\:)))) #`(record 'p '(#,s)))]
[_ #'make-colons])))
(define (remove-trailer ps)
(filter (match-lambda [(TRAILER-ANCHOR) #f] [_ #t]) ps))
(define pexpr-reader
(make-preserve-text-reader
#:reader-name *reader-name*
#:read-annotated-value (lambda (in-port source next parse-error*)
(lambda ()
(skip-whitespace in-port)
(match (peek-char in-port)
[(or (? eof-object?) #\] #\> #\} #\)) *TRAILER-ANCHOR*]
[_ (next)])))
#:on-hash (lambda (in-port source next parse-error* default)
(match-lambda
[#\{ (read-sequence 's in-port source next #\})]
[c (default c)]))
#:on-char (lambda (in-port source next parse-error* default)
(match-lambda
[#\< (read-sequence 'r in-port source next #\>)]
[#\[ (read-sequence #f in-port source next #\])]
[#\{ (read-sequence 'b in-port source next #\})]
[#\( (read-sequence 'g in-port source next #\))]
[#\, *COMMA*]
[#\; *SEMICOLON*]
[#\: (let loop ((acc '(#\:)))
(match (peek-char in-port)
[#\: (loop (cons (read-char in-port) acc))]
[_ (record 'p (list (string->symbol (list->string (reverse acc)))))]))]
[c (default c)]))))
(define string->pexpr (make-preserve-string-reader *reader-name* pexpr-reader))
(define (read-sequence record-label in-port source next terminator-char)
(let loop ((acc '()))
(skip-whitespace in-port)
(match (eof-guard *reader-name* in-port source (peek-char in-port))
[(== terminator-char)
(read-char in-port)
(if record-label
(record record-label (reverse acc))
(reverse acc))]
[_
(match (next)
[(record 'a '()) ;; skip bare annotation-anchors
;; NB. Not matching (TRAILER-ANCHOR)! That would skip *non-bare* anchors too!
(loop acc)]
[v (loop (cons v acc))])])))
(define (read-pexpr [in-port (current-input-port)]
#:read-syntax? [read-syntax? #f]
#:decode-embedded [decode-embedded #f]
#:source [source (object-name in-port)])
(pexpr-reader in-port source read-syntax? decode-embedded))
;;---------------------------------------------------------------------------
(define (uncomma p
#:map-embedded [map-embedded (lambda (walk v) (walk v))])
(define (walk-seq ps)
(map walk (filter (match-lambda [(COMMA) #f] [_ #t]) ps)))
(define (walk p)
(match p
[(? list? ps) (walk-seq ps)]
[(RECORD ps ...) (apply RECORD (walk-seq ps))]
[(GROUP ps ...) (apply GROUP (walk-seq ps))]
[(BLOCK ps ...) (apply BLOCK (walk-seq ps))]
[(SET ps ...) (apply SET (walk-seq ps))]
[(embedded v) (map-embedded walk v)]
[(annotated as loc v) (annotated (map walk as) loc (walk v))]
[(COMMA) (error 'uncomma "Cannot remove commas from term with comma outside container")]
[v v]))
(walk p))
(define (pexpr->preserve p
#:discard-trailers? [discard-trailers? #f]
#:map-embedded [map-embedded (lambda (walk v) (walk v))])
(define untrailer (if discard-trailers? remove-trailer values))
(let walk ((p (uncomma p #:map-embedded map-embedded)))
(match p
[(list ps ...)
(map walk (untrailer ps))]
[(RECORD l ps ...) (record (walk l) (map walk (untrailer ps)))]
[(GROUP _ ...) (error 'pexpr->preserve "Cannot convert uninterpreted grouping")]
[(BLOCK ps ...)
(let loop ((acc (hash)) (ps (untrailer ps)))
(match ps
[(list)
acc]
[(list k0 (COLONS 1) v more ...)
(define k (walk k0))
(if (hash-has-key? acc k)
(error 'pexpr->preserve "Duplicate key in dictionary: ~v" k)
(loop (hash-set acc k (walk v)) more))]
[_
(error 'pexpr->preserve "Cannot convert invalid dictionary")]))]
[(SET ps ...)
(let loop ((acc (set)) (ps (untrailer ps)))
(match ps
['() acc]
[(cons v0 more)
(define v (walk v0))
(if (set-member? acc v)
(error 'pexpr->preserve "Duplicate item in set: ~v" v)
(loop (set-add acc v) more))]))]
[(SEMICOLON) (error 'pexpr->preserve "Cannot convert semicolon")]
[(COLONS _) (error 'pexpr->preserve "Cannot convert colons")]
[(TRAILER-ANCHOR) (error 'pexpr->preserve "Cannot convert trailer")]
[(embedded v) (map-embedded walk v)]
[(annotated as loc v) (annotated (map walk as) loc (walk v))]
[v v])))
(define (preserve->pexpr v #:map-embedded [map-embedded (lambda (walk v) (walk v))])
(let walk ((v v))
(match v
[(list vs ...) (map walk vs)]
[(? set?) (record 's (map walk (set->list v)))]
[(hash-table (kk vv) ...)
(record 'b (append-map (lambda (kk vv) (list (walk kk) (COLONS 1) (walk vv)))
kk vv))]
[(record l fs) (record 'r (map walk (cons l fs)))]
[(embedded v) (map-embedded walk v)]
[(annotated as loc v) (annotated (map walk as) loc (walk v))]
[v v])))

View File

@ -1,6 +1,7 @@
#lang racket/base
(provide make-preserve-text-reader
make-preserve-string-reader
parse-error
skip-whitespace
@ -18,6 +19,21 @@
(define PIPE #\|)
(define (make-preserve-string-reader reader-name reader)
(lambda (s
#:read-syntax? [read-syntax? #f]
#:decode-embedded [decode-embedded #f]
#:source [source "<string>"])
(define p (open-input-string s))
(when read-syntax? (port-count-lines! p))
(define v (reader p source read-syntax? decode-embedded))
(when (eof-object? v)
(parse-error #:raise-proc raise-read-eof-error reader-name p source "Unexpected end of input"))
(skip-whitespace p)
(when (not (eof-object? (peek-char p)))
(parse-error reader-name p source "Unexpected following text"))
v))
(define (parse-error #:raise-proc [raise-proc raise-read-error] reader-name i source fmt . args)
(define-values [line column pos] (port-next-location i))
(raise-proc (format "~a: ~a" reader-name (apply format fmt args))
@ -48,7 +64,8 @@
(define ((make-preserve-text-reader #:reader-name reader-name
#:on-char on-char0
#:on-hash on-hash0)
#:on-hash on-hash0
#:read-annotated-value read-annotated-value0)
in-port source read-syntax? decode-embedded0)
(define read-annotations? read-syntax?)
(define decode-embedded (or decode-embedded0
@ -104,10 +121,13 @@
(on-hash0 in-port source next parse-error*
(lambda (c) (parse-error* "Invalid # syntax: ~v" c))))
(define read-annotated-value
(read-annotated-value0 in-port source next parse-error*))
(define (annotate-next-with a)
(if read-annotations?
(annotate (next) a)
(next)))
(annotate (read-annotated-value) a)
(read-annotated-value)))
;;---------------------------------------------------------------------------
;; Source location tracking

View File

@ -11,26 +11,10 @@
(define *reader-name* 'read-preserve/text)
(define (string->preserve s
#:read-syntax? [read-syntax? #f]
#:decode-embedded [decode-embedded #f]
#:source [source "<string>"])
(define p (open-input-string s))
(when read-syntax? (port-count-lines! p))
(define v (read-preserve/text p
#:read-syntax? read-syntax?
#:decode-embedded decode-embedded
#:source source))
(when (eof-object? v)
(parse-error #:raise-proc raise-read-eof-error *reader-name* p source "Unexpected end of input"))
(skip-whitespace p)
(when (not (eof-object? (peek-char p)))
(parse-error *reader-name* p source "Unexpected text following preserve"))
v)
(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
@ -62,6 +46,8 @@
[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)