2021-06-04 13:56:03 +00:00
|
|
|
;;; SPDX-License-Identifier: LGPL-3.0-or-later
|
2021-06-04 14:20:14 +00:00
|
|
|
;;; SPDX-FileCopyrightText: Copyright © 2015-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
2021-06-01 15:19:24 +00:00
|
|
|
|
2018-03-21 08:16:54 +00:00
|
|
|
#lang racket/base
|
|
|
|
|
2021-06-02 10:37:36 +00:00
|
|
|
(provide (for-syntax preserves-pattern-registry
|
|
|
|
register-preserves-pattern!
|
|
|
|
analyse-pattern
|
2021-06-10 14:21:30 +00:00
|
|
|
analyse-pattern-bindings
|
|
|
|
analyse-match-pattern)
|
2021-06-02 10:37:36 +00:00
|
|
|
define-preserves-pattern
|
2021-06-02 10:50:21 +00:00
|
|
|
:pattern
|
2021-06-10 14:21:30 +00:00
|
|
|
:parse
|
2021-06-03 13:58:48 +00:00
|
|
|
|
|
|
|
pattern->constant-values
|
|
|
|
pattern->constant-paths
|
|
|
|
pattern->capture-paths
|
|
|
|
pattern->capture-names
|
|
|
|
|
2021-06-03 21:23:48 +00:00
|
|
|
!dump-registered-preserves-patterns!
|
|
|
|
|
2021-06-03 13:58:48 +00:00
|
|
|
(all-from-out "schemas/gen/dataspace-patterns.rkt"))
|
2018-04-08 10:44:32 +00:00
|
|
|
|
|
|
|
(require (for-syntax racket/base))
|
|
|
|
(require (for-syntax racket/match))
|
2021-06-02 10:37:36 +00:00
|
|
|
(require (for-syntax racket/list))
|
2018-04-08 10:44:32 +00:00
|
|
|
(require (for-syntax racket/struct-info))
|
2021-06-02 10:50:21 +00:00
|
|
|
(require (for-syntax racket/syntax))
|
2021-06-02 10:37:36 +00:00
|
|
|
(require (for-syntax syntax/id-table))
|
2018-04-08 10:44:32 +00:00
|
|
|
(require (for-syntax syntax/stx))
|
2021-06-02 10:37:36 +00:00
|
|
|
|
2021-06-08 13:38:24 +00:00
|
|
|
(require preserves-schema)
|
2021-06-03 13:58:48 +00:00
|
|
|
(require racket/match)
|
|
|
|
(require racket/list)
|
2018-05-04 16:52:16 +00:00
|
|
|
(require "pattern-expander.rkt")
|
2021-06-02 10:37:36 +00:00
|
|
|
(require "schemas/gen/dataspace-patterns.rkt")
|
|
|
|
|
|
|
|
;;---------------------------------------------------------------------------
|
2018-03-21 08:16:54 +00:00
|
|
|
|
2021-06-02 10:37:36 +00:00
|
|
|
(define-for-syntax preserves-pattern-registry (make-free-id-table))
|
|
|
|
|
|
|
|
(define-for-syntax (register-preserves-pattern! id-stx transformer)
|
|
|
|
(free-id-table-set! preserves-pattern-registry id-stx transformer))
|
|
|
|
|
|
|
|
(define-syntax (define-preserves-pattern stx)
|
|
|
|
(syntax-case stx ()
|
2021-06-10 14:21:30 +00:00
|
|
|
[(_ top-type-name parser-name (ctor-stx field-stxs ...) pattern-stx bindings-stx)
|
2021-06-02 10:50:21 +00:00
|
|
|
#`(begin (begin-for-syntax
|
2021-06-02 10:37:36 +00:00
|
|
|
(register-preserves-pattern!
|
|
|
|
#'ctor-stx
|
|
|
|
(lambda (mode s)
|
|
|
|
(match mode
|
|
|
|
['pattern
|
|
|
|
(syntax-case s ()
|
|
|
|
[(_ field-stxs ...) pattern-stx]
|
|
|
|
[_ (raise-syntax-error 'ctor-stx "Invalid pattern")])]
|
2021-06-10 14:21:30 +00:00
|
|
|
['match-pattern
|
|
|
|
(syntax-case s ()
|
|
|
|
[(_ field-stxs ...) #`(app parser-name (ctor-stx field-stxs ...))]
|
|
|
|
[_ (raise-syntax-error 'ctor-stx "Invalid match-pattern")])]
|
2021-06-02 10:37:36 +00:00
|
|
|
['bindings
|
|
|
|
(syntax-case s ()
|
|
|
|
[(_ field-stxs ...) bindings-stx]
|
|
|
|
[_ (raise-syntax-error 'ctor-stx "Invalid binding-pattern")])]))))
|
|
|
|
(void))]))
|
2018-03-21 08:16:54 +00:00
|
|
|
|
|
|
|
;;---------------------------------------------------------------------------
|
|
|
|
|
2018-05-04 16:52:16 +00:00
|
|
|
(define-for-syntax orig-insp
|
|
|
|
(variable-reference->module-declaration-inspector (#%variable-reference)))
|
|
|
|
|
2018-04-08 10:44:32 +00:00
|
|
|
(begin-for-syntax
|
|
|
|
(define (dollar-id? stx)
|
|
|
|
(and (identifier? stx)
|
|
|
|
(char=? (string-ref (symbol->string (syntax-e stx)) 0) #\$)))
|
|
|
|
|
|
|
|
(define (undollar stx)
|
|
|
|
(and (dollar-id? stx)
|
|
|
|
(datum->syntax stx (string->symbol (substring (symbol->string (syntax-e stx)) 1)))))
|
|
|
|
|
|
|
|
(define (discard-id? stx)
|
|
|
|
(and (identifier? stx)
|
2018-04-29 10:55:32 +00:00
|
|
|
(free-identifier=? #'_ stx)))
|
2018-04-08 10:44:32 +00:00
|
|
|
|
|
|
|
(define (id-value stx)
|
|
|
|
(and (identifier? stx)
|
|
|
|
(syntax-local-value stx (lambda () #f))))
|
|
|
|
|
|
|
|
(define (list-id? stx)
|
|
|
|
(and (identifier? stx)
|
2018-04-29 10:55:32 +00:00
|
|
|
(free-identifier=? #'list stx)))
|
2018-04-08 10:44:32 +00:00
|
|
|
|
2021-06-03 20:42:42 +00:00
|
|
|
(define (hash-or-hasheqv-id? stx)
|
|
|
|
(and (identifier? stx)
|
|
|
|
(or (free-identifier=? #'hash stx)
|
|
|
|
(free-identifier=? #'hasheqv stx))))
|
|
|
|
|
2021-06-02 10:37:36 +00:00
|
|
|
(define (constructor-registered? stx)
|
|
|
|
(free-id-table-ref preserves-pattern-registry stx #f))
|
|
|
|
|
|
|
|
(define (struct-label-and-arity id-stx actual-count)
|
|
|
|
(match-define (list _type-stx ctor-stx pred-stx accessor-stxs _mutator-stxs _super)
|
|
|
|
(extract-struct-info (id-value id-stx)))
|
|
|
|
(define expected-count (length accessor-stxs))
|
|
|
|
(when (not (= expected-count actual-count))
|
|
|
|
(raise-syntax-error ':pattern
|
|
|
|
(format "Expected ~a arguments to ~v, but got ~a"
|
|
|
|
expected-count
|
|
|
|
ctor-stx
|
|
|
|
actual-count)))
|
|
|
|
(values (syntax-e ctor-stx)
|
|
|
|
expected-count))
|
2018-05-01 19:57:22 +00:00
|
|
|
|
2021-06-03 20:42:42 +00:00
|
|
|
(define (append-map-pairs f xs)
|
|
|
|
(match xs
|
|
|
|
['() '()]
|
|
|
|
[(list _) (raise-syntax-error #f "Odd number of elements in hash-like pattern")]
|
|
|
|
[(list* k v more) (append (f k v) (append-map-pairs f more))]))
|
|
|
|
|
2021-06-10 11:32:39 +00:00
|
|
|
(define (analyse-pattern stx
|
|
|
|
[check-destructuring (lambda (stx) stx)]
|
|
|
|
[wrap-literal (lambda (stx) stx)])
|
|
|
|
(define (member-entry key-stx pat-stx)
|
|
|
|
(define analysed (analyse-pattern pat-stx check-destructuring wrap-literal))
|
|
|
|
(syntax-case analysed (Pattern-DDiscard DDiscard)
|
|
|
|
[(Pattern-DDiscard (DDiscard)) (list)]
|
|
|
|
[_ (list key-stx analysed)]))
|
|
|
|
(let walk ((stx stx))
|
|
|
|
(define disarmed-stx (syntax-disarm stx orig-insp))
|
|
|
|
(syntax-case disarmed-stx ($ quasiquote unquote quote)
|
|
|
|
[(ctor args ...)
|
|
|
|
(constructor-registered? #'ctor)
|
|
|
|
(check-destructuring
|
|
|
|
((free-id-table-ref preserves-pattern-registry #'ctor) 'pattern disarmed-stx))]
|
|
|
|
|
|
|
|
[(expander args ...)
|
|
|
|
(pattern-expander-id? #'expander)
|
|
|
|
(pattern-expander-transform disarmed-stx
|
2021-06-10 14:21:30 +00:00
|
|
|
(lambda (result) (walk (syntax-rearm result stx))))]
|
2021-06-10 11:32:39 +00:00
|
|
|
|
|
|
|
;; Extremely limited support for quasiquoting and quoting
|
|
|
|
[(quasiquote (unquote p)) (walk #'p)]
|
|
|
|
[(quasiquote (p ...)) (walk #'(list (quasiquote p) ...))]
|
|
|
|
[(quasiquote p) #`(Pattern-DLit (DLit #,(wrap-literal #''p)))]
|
|
|
|
[(quote p) #`(Pattern-DLit (DLit #,(wrap-literal #''p)))]
|
|
|
|
|
|
|
|
[(unquote p) #'p]
|
|
|
|
|
|
|
|
[(ctor piece ...)
|
|
|
|
(struct-info? (id-value #'ctor))
|
|
|
|
(let-values (((label arity) (struct-label-and-arity #'ctor (length (syntax->list #'(piece ...))))))
|
|
|
|
(check-destructuring
|
|
|
|
#`(Pattern-DCompound
|
|
|
|
(DCompound-rec (CRec '#,label #,arity)
|
|
|
|
(hasheqv #,@(append*
|
|
|
|
(for/list [(n (in-naturals))
|
|
|
|
(piece (in-list (syntax->list #'(piece ...))))]
|
|
|
|
(member-entry n piece))))))))]
|
|
|
|
|
|
|
|
[(list-stx piece ...)
|
|
|
|
(list-id? #'list-stx)
|
|
|
|
(check-destructuring
|
|
|
|
#`(Pattern-DCompound
|
|
|
|
(DCompound-arr (CArr #,(length (syntax->list #'(piece ...))))
|
|
|
|
(hasheqv #,@(append*
|
|
|
|
(for/list [(n (in-naturals))
|
|
|
|
(piece (in-list (syntax->list #'(piece ...))))]
|
|
|
|
(member-entry n piece)))))))]
|
|
|
|
|
|
|
|
[(hash-stx piece ...)
|
|
|
|
(hash-or-hasheqv-id? #'hash-stx)
|
|
|
|
(check-destructuring
|
|
|
|
#`(Pattern-DCompound
|
|
|
|
(DCompound-dict (CDict)
|
|
|
|
(hash #,@(append-map-pairs member-entry (syntax->list #'(piece ...)))))))]
|
|
|
|
|
|
|
|
[id
|
|
|
|
(dollar-id? #'id)
|
|
|
|
#`(Pattern-DBind (DBind '#,(undollar #'id) (Pattern-DDiscard (DDiscard))))]
|
|
|
|
|
|
|
|
[($ (unquote bp) p)
|
|
|
|
#`(Pattern-DBind (DBind bp #,(walk #'p)))]
|
|
|
|
|
|
|
|
[($ id p)
|
|
|
|
#`(Pattern-DBind (DBind 'id #,(walk #'p)))]
|
|
|
|
|
|
|
|
[id
|
|
|
|
(discard-id? #'id)
|
|
|
|
#`(Pattern-DDiscard (DDiscard))]
|
|
|
|
|
|
|
|
[other
|
|
|
|
#`(Pattern-DLit (DLit #,(wrap-literal #'other)))])))
|
2018-04-22 20:07:35 +00:00
|
|
|
|
2021-06-02 10:37:36 +00:00
|
|
|
(define (analyse-pattern-bindings stx)
|
2021-06-08 07:30:29 +00:00
|
|
|
(let walk ((stx stx))
|
|
|
|
(define disarmed-stx (syntax-disarm stx orig-insp))
|
|
|
|
(syntax-case disarmed-stx ($ quasiquote unquote quote)
|
|
|
|
[(ctor args ...)
|
|
|
|
(constructor-registered? #'ctor)
|
|
|
|
((free-id-table-ref preserves-pattern-registry #'ctor) 'bindings disarmed-stx)]
|
|
|
|
|
|
|
|
[(expander args ...)
|
|
|
|
(pattern-expander-id? #'expander)
|
|
|
|
(pattern-expander-transform disarmed-stx
|
|
|
|
(lambda (result) (walk (syntax-rearm result stx))))]
|
|
|
|
|
|
|
|
;; Extremely limited support for quasiquoting and quoting
|
|
|
|
[(quasiquote (unquote p)) (walk #'p)]
|
|
|
|
[(quasiquote (p ...)) (walk #'(list (quasiquote p) ...))]
|
|
|
|
[(quasiquote _p) '()]
|
|
|
|
[(quote _p) '()]
|
|
|
|
|
|
|
|
[(ctor piece ...)
|
|
|
|
(struct-info? (id-value #'ctor))
|
|
|
|
(append-map walk (syntax->list #'(piece ...)))]
|
|
|
|
|
|
|
|
[(list-stx piece ...)
|
|
|
|
(list-id? #'list-stx)
|
|
|
|
(append-map walk (syntax->list #'(piece ...)))]
|
|
|
|
|
|
|
|
[(hash-stx piece ...)
|
|
|
|
(hash-or-hasheqv-id? #'hash-stx)
|
|
|
|
(append-map-pairs (lambda (_k v) (walk v)) (syntax->list #'(piece ...)))]
|
|
|
|
|
|
|
|
[id
|
|
|
|
(dollar-id? #'id)
|
|
|
|
(list (undollar #'id))]
|
|
|
|
|
|
|
|
[($ id p)
|
|
|
|
(cons #'id (walk #'p))]
|
|
|
|
|
|
|
|
[id
|
|
|
|
(discard-id? #'id)
|
|
|
|
'()]
|
|
|
|
|
|
|
|
[other
|
2021-06-10 11:32:39 +00:00
|
|
|
'()])))
|
2021-06-02 10:37:36 +00:00
|
|
|
|
2021-06-10 14:21:30 +00:00
|
|
|
(define (analyse-match-pattern stx)
|
|
|
|
(let walk ((stx stx))
|
|
|
|
(define disarmed-stx (syntax-disarm stx orig-insp))
|
|
|
|
(syntax-case disarmed-stx ($ quasiquote unquote quote)
|
|
|
|
[(ctor args ...)
|
|
|
|
(constructor-registered? #'ctor)
|
|
|
|
((free-id-table-ref preserves-pattern-registry #'ctor) 'match-pattern disarmed-stx)]
|
|
|
|
|
|
|
|
[(expander args ...)
|
|
|
|
(pattern-expander-id? #'expander)
|
|
|
|
(pattern-expander-transform disarmed-stx
|
|
|
|
(lambda (result) (walk (syntax-rearm result stx))))]
|
|
|
|
|
|
|
|
;; Extremely limited support for quasiquoting and quoting
|
|
|
|
[(quasiquote (unquote p)) (walk #'p)]
|
|
|
|
[(quasiquote (p ...)) (walk #'(list (quasiquote p) ...))]
|
|
|
|
[(quasiquote p) #''p]
|
|
|
|
[(quote p) #''p]
|
|
|
|
|
|
|
|
[(unquote p) (raise-syntax-error #f "Out-of-place unquote in match-pattern")]
|
|
|
|
|
|
|
|
[(ctor piece ...)
|
|
|
|
(struct-info? (id-value #'ctor))
|
|
|
|
#`(ctor (:parse piece) ...)]
|
|
|
|
|
|
|
|
[(list-stx piece ...)
|
|
|
|
(list-id? #'list-stx)
|
|
|
|
#`(list-stx (:parse piece) ...)]
|
|
|
|
|
|
|
|
[(hash-stx piece ...)
|
|
|
|
(hash-or-hasheqv-id? #'hash-stx)
|
|
|
|
#`(hash-table #,@(let loop ((pieces (syntax->list #'(piece ...))))
|
|
|
|
(match pieces
|
|
|
|
['() '()]
|
|
|
|
[(list* k v more) (list* k #`(:parse #,v) (loop more))]))
|
|
|
|
[_ _] ___)]
|
|
|
|
|
|
|
|
[(or-stx piece ...)
|
|
|
|
(and (identifier? #'or-stx) (free-identifier=? #'or #'or-stx))
|
|
|
|
#`(or (:parse piece) ...)]
|
|
|
|
|
|
|
|
[(and-stx piece ...)
|
|
|
|
(and (identifier? #'and-stx) (free-identifier=? #'and #'and-stx))
|
|
|
|
#`(and (:parse piece) ...)]
|
|
|
|
|
|
|
|
[other
|
|
|
|
#`other])))
|
|
|
|
|
2021-06-10 11:32:39 +00:00
|
|
|
(define (expand-:pattern stx)
|
2021-06-03 20:44:18 +00:00
|
|
|
(syntax-case stx ()
|
2021-06-10 11:32:39 +00:00
|
|
|
[(_ pat-stx atomic-literal-transformer)
|
|
|
|
(analyse-pattern #'pat-stx
|
|
|
|
(lambda (stx) (raise-syntax-error #f "Attempt to destructure known-atomic"))
|
|
|
|
(lambda (stx) #`(atomic-literal-transformer #,stx)))]
|
2021-06-03 20:44:18 +00:00
|
|
|
[(_ pat-stx)
|
|
|
|
(analyse-pattern #'pat-stx)])))
|
2021-06-02 10:37:36 +00:00
|
|
|
|
2021-06-10 11:32:39 +00:00
|
|
|
(define-pattern-expander :pattern
|
|
|
|
(lambda (stx)
|
|
|
|
;; This effectively quasiquotes the pattern; a naked unquote in
|
|
|
|
;; the pattern returns to the "outer" context. This is useful for
|
|
|
|
;; observing observers:
|
|
|
|
;;
|
|
|
|
;; (Observe (:pattern (some-pattern ...)) _)
|
|
|
|
;;
|
|
|
|
(expand-:pattern stx))
|
|
|
|
(lambda (stx)
|
|
|
|
(expand-:pattern stx)))
|
|
|
|
|
2021-06-10 14:21:30 +00:00
|
|
|
(define-match-expander :parse
|
|
|
|
(lambda (stx)
|
|
|
|
(syntax-case stx ()
|
|
|
|
[(_ pat-stx) (analyse-match-pattern #'pat-stx)])))
|
|
|
|
|
2021-06-03 13:58:48 +00:00
|
|
|
;;---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
(define (select-pattern-leaves desc capture-fn lit-fn)
|
|
|
|
(let walk-node ((key-rev '()) (desc desc))
|
|
|
|
(match desc
|
|
|
|
[(Pattern-DCompound (or (DCompound-rec _ members)
|
|
|
|
(DCompound-arr _ members)
|
|
|
|
(DCompound-dict _ members)))
|
|
|
|
(append* (for/list [((key subdesc) (in-hash members))]
|
|
|
|
(walk-node (cons key key-rev) subdesc)))]
|
|
|
|
[(Pattern-DBind (DBind name subdesc))
|
|
|
|
(append (capture-fn key-rev name) (walk-node key-rev subdesc))]
|
|
|
|
[(Pattern-DDiscard (DDiscard))
|
|
|
|
'()]
|
|
|
|
[(Pattern-DLit (DLit value))
|
|
|
|
(lit-fn key-rev value)])))
|
|
|
|
|
|
|
|
(define (pattern->constant-values desc)
|
|
|
|
(select-pattern-leaves desc
|
|
|
|
(lambda (key-rev name-stx) (list))
|
|
|
|
(lambda (key-rev value) (list value))))
|
|
|
|
|
|
|
|
(define (pattern->constant-paths desc)
|
|
|
|
(select-pattern-leaves desc
|
|
|
|
(lambda (key-rev name-stx) (list))
|
|
|
|
(lambda (key-rev value) (list (reverse key-rev)))))
|
|
|
|
|
|
|
|
(define (pattern->capture-paths desc)
|
|
|
|
(select-pattern-leaves desc
|
|
|
|
(lambda (key-rev name-stx) (list (reverse key-rev)))
|
|
|
|
(lambda (key-rev value) (list))))
|
|
|
|
|
|
|
|
(define (pattern->capture-names desc)
|
|
|
|
(select-pattern-leaves desc
|
|
|
|
(lambda (key-rev name-stx) (list name-stx))
|
|
|
|
(lambda (key-rev value) (list))))
|
2021-06-03 21:23:48 +00:00
|
|
|
|
|
|
|
(define-syntax (!dump-registered-preserves-patterns! stx)
|
|
|
|
(syntax-case stx ()
|
|
|
|
[(_)
|
|
|
|
(let ()
|
|
|
|
(local-require racket/pretty)
|
|
|
|
(for [(k (in-list (free-id-table-keys preserves-pattern-registry)))]
|
|
|
|
(printf " - ~v\n" k))
|
|
|
|
#'(void))]))
|