241 lines
8.5 KiB
Racket
241 lines
8.5 KiB
Racket
#lang racket/base
|
|
|
|
(provide (struct-out discard)
|
|
(struct-out capture)
|
|
|
|
(for-syntax analyse-pattern
|
|
instantiate-pattern->pattern
|
|
instantiate-pattern->value
|
|
desc->key
|
|
desc->skeleton-proj
|
|
desc->skeleton-stx
|
|
desc->capture-proj
|
|
desc->capture-names
|
|
desc->assertion-stx)
|
|
|
|
(all-from-out "pattern-expander.rkt"))
|
|
|
|
(require (for-syntax racket/base))
|
|
(require (for-syntax racket/match))
|
|
(require (for-syntax racket/struct-info))
|
|
(require (for-syntax syntax/stx))
|
|
(require "pattern-expander.rkt")
|
|
|
|
(struct discard () #:prefab)
|
|
(struct capture (detail) #:prefab)
|
|
|
|
;;---------------------------------------------------------------------------
|
|
;; ## Analysing patterns
|
|
;;
|
|
;; Patterns generate several pieces, which work together to form
|
|
;; routing tables:
|
|
;;
|
|
;; - the *assertion* allows observers of observers to function;
|
|
;; - the `Skeleton` classifies the shape of the pattern;
|
|
;; - two `SkProj`s select constant and variable pieces from a pattern, respectively; and
|
|
;; - a `SkKey` specifies constant pieces of a pattern, matched against one of the `SkProj`s.
|
|
;;
|
|
;; The other `SkProj` generates a second `SkKey` which is used as the
|
|
;; input to a handler function.
|
|
|
|
(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 (vector-id? stx)
|
|
(and (identifier? stx)
|
|
(free-identifier=? #'vector stx)))
|
|
|
|
(define (analyse-pattern stx)
|
|
(define disarmed-stx (syntax-disarm stx orig-insp))
|
|
(syntax-case disarmed-stx ($ quasiquote unquote quote)
|
|
[(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) (list 'atom stx)]
|
|
[(quote p) (list 'atom stx)]
|
|
|
|
[(ctor piece ...)
|
|
(struct-info? (id-value #'ctor))
|
|
(list* 'compound
|
|
(extract-struct-info (id-value #'ctor))
|
|
(stx-map analyse-pattern #'(piece ...)))]
|
|
[(list piece ...)
|
|
(list-id? #'list)
|
|
(list* 'compound
|
|
'list
|
|
(stx-map analyse-pattern #'(piece ...)))]
|
|
[(vector piece ...)
|
|
(vector-id? #'vector)
|
|
(list* 'compound
|
|
'vector
|
|
(stx-map analyse-pattern #'(piece ...)))]
|
|
[id
|
|
(dollar-id? #'id)
|
|
(list 'capture (undollar #'id) (list 'discard))]
|
|
[($ id p)
|
|
(list 'capture #'id (analyse-pattern #'p))]
|
|
[id
|
|
(discard-id? #'id)
|
|
(list 'discard)]
|
|
[_
|
|
(list 'atom stx)]))
|
|
|
|
(define (instantiate-pattern->pattern stx)
|
|
(define disarmed-stx (syntax-disarm stx orig-insp))
|
|
(syntax-case disarmed-stx ($ quasiquote unquote quote)
|
|
[(expander args ...)
|
|
(pattern-expander-id? #'expander)
|
|
(pattern-expander-transform disarmed-stx
|
|
(lambda (result)
|
|
(instantiate-pattern->pattern (syntax-rearm result stx))))]
|
|
|
|
;; Extremely limited support for quasiquoting and quoting
|
|
[(quasiquote (unquote p)) (instantiate-pattern->pattern #'p)]
|
|
[(quasiquote (p ...)) (instantiate-pattern->pattern #'(list (quasiquote p) ...))]
|
|
[(quasiquote p) stx]
|
|
[(quote p) stx]
|
|
|
|
[(ctor piece ...)
|
|
(struct-info? (id-value #'ctor))
|
|
(quasisyntax/loc stx (ctor #,@(stx-map instantiate-pattern->pattern #'(piece ...))))]
|
|
[(list piece ...)
|
|
(list-id? #'list)
|
|
(quasisyntax/loc stx (list #,@(stx-map instantiate-pattern->pattern #'(piece ...))))]
|
|
[(vector piece ...)
|
|
(vector-id? #'vector)
|
|
(quasisyntax/loc stx (vector #,@(stx-map instantiate-pattern->pattern #'(piece ...))))]
|
|
[id
|
|
(dollar-id? #'id)
|
|
(undollar #'id)]
|
|
[($ id p)
|
|
#'id]
|
|
[id
|
|
(discard-id? #'id)
|
|
#'id]
|
|
[other
|
|
#'other]))
|
|
|
|
(define (instantiate-pattern->value stx)
|
|
(define disarmed-stx (syntax-disarm stx orig-insp))
|
|
(syntax-case disarmed-stx ($ quasiquote unquote quote)
|
|
[(expander args ...)
|
|
(pattern-expander-id? #'expander)
|
|
(pattern-expander-transform disarmed-stx
|
|
(lambda (result)
|
|
(instantiate-pattern->value (syntax-rearm result stx))))]
|
|
|
|
;; Extremely limited support for quasiquoting and quoting
|
|
[(quasiquote (unquote p)) (instantiate-pattern->pattern #'p)]
|
|
[(quasiquote (p ...)) (instantiate-pattern->pattern #'(list (quasiquote p) ...))]
|
|
[(quasiquote p) stx]
|
|
[(quote p) stx]
|
|
|
|
[(ctor piece ...)
|
|
(struct-info? (id-value #'ctor))
|
|
(quasisyntax/loc stx (ctor #,@(stx-map instantiate-pattern->value #'(piece ...))))]
|
|
[(list piece ...)
|
|
(list-id? #'list)
|
|
(quasisyntax/loc stx (list #,@(stx-map instantiate-pattern->value #'(piece ...))))]
|
|
[(vector piece ...)
|
|
(vector-id? #'vector)
|
|
(quasisyntax/loc stx (vector #,@(stx-map instantiate-pattern->value #'(piece ...))))]
|
|
[id
|
|
(dollar-id? #'id)
|
|
(undollar #'id)]
|
|
[($ id p)
|
|
#'id]
|
|
[id
|
|
(discard-id? #'id)
|
|
#'(discard)]
|
|
[other
|
|
#'other])))
|
|
|
|
;;---------------------------------------------------------------------------
|
|
|
|
(begin-for-syntax
|
|
(define (select-pattern-leaves desc capture-fn atom-fn)
|
|
(define (walk-node key-rev desc)
|
|
(match desc
|
|
[`(compound ,_ ,pieces ...) (walk-edge 0 key-rev pieces)]
|
|
[`(capture ,name-stx ,p) (append (capture-fn key-rev name-stx) (walk-node key-rev p))]
|
|
[`(discard) (list)]
|
|
[`(atom ,v) (atom-fn key-rev v)]))
|
|
(define (walk-edge index key-rev pieces)
|
|
(match pieces
|
|
['() '()]
|
|
[(cons p pieces) (append (walk-node (cons index key-rev) p)
|
|
(walk-edge (+ index 1) key-rev pieces))]))
|
|
(walk-node '(0) desc))
|
|
|
|
(define (desc->key desc)
|
|
(select-pattern-leaves desc
|
|
(lambda (key-rev name-stx) (list))
|
|
(lambda (key-rev atom) (list atom))))
|
|
|
|
(define (desc->skeleton-proj desc)
|
|
(select-pattern-leaves desc
|
|
(lambda (key-rev name-stx) (list))
|
|
(lambda (key-rev atom) (list (reverse key-rev)))))
|
|
|
|
(define (desc->skeleton-stx desc)
|
|
(match desc
|
|
[`(compound list ,pieces ...)
|
|
#`(list 'list #,@(map desc->skeleton-stx pieces))]
|
|
[`(compound vector ,pieces ...)
|
|
#`(list 'vector #,@(map desc->skeleton-stx pieces))]
|
|
[`(compound (,struct-type ,_ctor ,_pred ,_getters ,_setters ,_super) ,pieces ...)
|
|
#`(list #,struct-type #,@(map desc->skeleton-stx pieces))]
|
|
[`(capture ,_ ,p) (desc->skeleton-stx p)]
|
|
[`(discard) #'#f]
|
|
[`(atom ,atom-stx) #'#f]))
|
|
|
|
(define (desc->capture-proj desc)
|
|
(select-pattern-leaves desc
|
|
(lambda (key-rev name-stx) (list (reverse key-rev)))
|
|
(lambda (key-rev atom) (list))))
|
|
|
|
(define (desc->capture-names desc)
|
|
(select-pattern-leaves desc
|
|
(lambda (key-rev name-stx) (list name-stx))
|
|
(lambda (key-rev atom) (list))))
|
|
|
|
(define (desc->assertion-stx desc)
|
|
(match desc
|
|
[`(compound list ,pieces ...)
|
|
#`(list #,@(map desc->assertion-stx pieces))]
|
|
[`(compound vector ,pieces ...)
|
|
#`(vector #,@(map desc->assertion-stx pieces))]
|
|
[`(compound (,_struct-type ,ctor ,_pred ,_getters ,_setters ,_super) ,pieces ...)
|
|
#`(#,ctor #,@(map desc->assertion-stx pieces))]
|
|
[`(capture ,_ ,p) #`(capture #,(desc->assertion-stx p))]
|
|
[`(discard) #'(discard)]
|
|
[`(atom ,v) v]))
|
|
)
|