229 lines
7.8 KiB
Racket
229 lines
7.8 KiB
Racket
#lang racket/base
|
|
|
|
(provide (for-syntax preserves-pattern-registry
|
|
register-preserves-pattern!
|
|
analyse-pattern
|
|
analyse-pattern-bindings)
|
|
define-preserves-pattern
|
|
:pattern
|
|
:template)
|
|
|
|
(require (for-syntax racket/base))
|
|
(require (for-syntax racket/match))
|
|
(require (for-syntax racket/list))
|
|
(require (for-syntax racket/struct-info))
|
|
(require (for-syntax racket/syntax))
|
|
(require (for-syntax syntax/id-table))
|
|
(require (for-syntax syntax/stx))
|
|
|
|
(require "pattern-expander.rkt")
|
|
(require "schemas/gen/dataspace-patterns.rkt")
|
|
|
|
;;---------------------------------------------------------------------------
|
|
|
|
(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 ()
|
|
[(_ (ctor-stx field-stxs ...) pattern-stx bindings-stx)
|
|
#`(begin (begin-for-syntax
|
|
(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")])]
|
|
['template
|
|
(syntax-case s ()
|
|
[(_ field-stxs ...)
|
|
(syntax (#,(format-id #'ctor-stx "~a->preserves" (syntax-e #'ctor-stx))
|
|
(ctor-stx field-stxs ...)))])]
|
|
['bindings
|
|
(syntax-case s ()
|
|
[(_ field-stxs ...) bindings-stx]
|
|
[_ (raise-syntax-error 'ctor-stx "Invalid binding-pattern")])]))))
|
|
(void))]))
|
|
|
|
;;---------------------------------------------------------------------------
|
|
|
|
(define-for-syntax orig-insp
|
|
(variable-reference->module-declaration-inspector (#%variable-reference)))
|
|
|
|
(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)
|
|
(free-identifier=? #'_ stx)))
|
|
|
|
(define (id-value stx)
|
|
(and (identifier? stx)
|
|
(syntax-local-value stx (lambda () #f))))
|
|
|
|
(define (list-id? stx)
|
|
(and (identifier? stx)
|
|
(free-identifier=? #'list stx)))
|
|
|
|
(define (constructor-registered? stx)
|
|
(free-id-table-ref preserves-pattern-registry stx #f))
|
|
|
|
(define (member-entry key-stx pat-stx)
|
|
(define analysed (analyse-pattern pat-stx))
|
|
(syntax-case analysed (DDiscard)
|
|
[(DDiscard) (list)]
|
|
[_ (list key-stx analysed)]))
|
|
|
|
(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))
|
|
|
|
(define (analyse-pattern 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) 'pattern disarmed-stx)]
|
|
|
|
[(expander args ...)
|
|
(pattern-expander-id? #'expander)
|
|
(pattern-expander-transform disarmed-stx
|
|
(lambda (result)
|
|
(analyse-pattern (syntax-rearm result stx))))]
|
|
|
|
;; Extremely limited support for quasiquoting and quoting
|
|
[(quasiquote (unquote p)) (analyse-pattern #'p)]
|
|
[(quasiquote (p ...)) (analyse-pattern #'(list (quasiquote p) ...))]
|
|
[(quasiquote p) #`(DLit 'p)]
|
|
[(quote p) #`(DLit 'p)]
|
|
|
|
[(ctor piece ...)
|
|
(struct-info? (id-value #'ctor))
|
|
(let-values (((label arity) (struct-label-and-arity #'ctor)))
|
|
#`(DCompound-rec '#,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)
|
|
#`(DCompound-arr #,(length (syntax->list #'(piece ...)))
|
|
(hasheqv #,@(append*
|
|
(for/list [(n (in-naturals))
|
|
(piece (in-list (syntax->list #'(piece ...))))]
|
|
(member-entry n piece)))))]
|
|
|
|
[id
|
|
(dollar-id? #'id)
|
|
#`(DBind '#,(undollar #'id) (DDiscard))]
|
|
|
|
[($ id p)
|
|
#`(DBind 'id #,(analyse-pattern #'p))]
|
|
|
|
[id
|
|
(discard-id? #'id)
|
|
#`(DDiscard)]
|
|
|
|
[other
|
|
#`(DLit other)]))
|
|
|
|
(define (analyse-pattern-bindings 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)
|
|
(analyse-pattern-bindings (syntax-rearm result stx))))]
|
|
|
|
;; Extremely limited support for quasiquoting and quoting
|
|
[(quasiquote (unquote p)) (analyse-pattern-bindings #'p)]
|
|
[(quasiquote (p ...)) (analyse-pattern-bindings #'(list (quasiquote p) ...))]
|
|
[(quasiquote _p) '()]
|
|
[(quote _p) '()]
|
|
|
|
[(ctor piece ...)
|
|
(struct-info? (id-value #'ctor))
|
|
(append-map analyse-pattern-bindings (syntax->list #'(piece ...)))]
|
|
|
|
[(list-stx piece ...)
|
|
(list-id? #'list-stx)
|
|
(append-map analyse-pattern-bindings (syntax->list #'(piece ...)))]
|
|
|
|
[id
|
|
(dollar-id? #'id)
|
|
(list (undollar #'id))]
|
|
|
|
[($ id p)
|
|
(cons #'id (analyse-pattern-bindings #'p))]
|
|
|
|
[id
|
|
(discard-id? #'id)
|
|
'()]
|
|
|
|
[other
|
|
'()]))
|
|
|
|
(define (analyse-template stx)
|
|
(syntax-case stx ($ quasiquote unquote quote)
|
|
[(ctor args ...)
|
|
(constructor-registered? #'ctor)
|
|
((free-id-table-ref preserves-pattern-registry #'ctor) 'template stx)]
|
|
|
|
;; Extremely limited support for quasiquoting and quoting
|
|
[(quasiquote (unquote p)) #'p]
|
|
[(quasiquote (p ...)) (analyse-template #'(list (quasiquote p) ...))]
|
|
[(quasiquote p) #''p]
|
|
[(quote p) #''p]
|
|
|
|
[(ctor piece ...)
|
|
(struct-info? (id-value #'ctor))
|
|
#`(ctor #,@(map analyse-template (syntax->list #'(piece ...))))]
|
|
|
|
[(list-stx piece ...)
|
|
(list-id? #'list-stx)
|
|
#`(list-stx #,@(map analyse-template (syntax->list #'(piece ...))))]
|
|
|
|
[other #'other])))
|
|
|
|
(define-syntax (:pattern stx)
|
|
(syntax-case stx ()
|
|
[(_ pat-stx)
|
|
(analyse-pattern #'pat-stx)]))
|
|
|
|
;; (provide :bindings)
|
|
;; (define-syntax (:bindings stx)
|
|
;; (syntax-case stx ()
|
|
;; [(_ pat-stx)
|
|
;; #`(quote #,(analyse-pattern-bindings #'pat-stx))]))
|
|
|
|
(define-syntax (:template stx)
|
|
(syntax-case stx ()
|
|
[(_ template-stx)
|
|
(analyse-template #'template-stx)]))
|