#lang racket/base (require (for-syntax racket/base) racket/match syntax/stx "context.rkt" "stx-util.rkt") (provide (all-defined-out)) (module base racket/base (require racket/match) (provide (struct-out rep) parse-pattern pattern-vars) ;; A Pattern is one of ;; - Symbol ;; - (cons Pattern Pattern) ;; - '() ;; - (rep Pattern VarList Pattern) ;; A VarList is (Listof Symbol) (struct rep (ph varsh pt) #:prefab) ;; parse-pattern : Sexpr [Boolean] -> Pattern (define (parse-pattern p0 [template? #f]) (let loop ([p p0]) (match p ['() '()] [(? symbol? p) p] [(list* ph '... pt) (unless (or (null? pt) template?) (error 'parse-pattern "ellipsis with tail: ~e in ~e" p p0)) (let ([ph (loop ph)] [pt (loop pt)]) (rep ph (pattern-vars ph) pt))] [(cons p1 p2) (cons (loop p1) (loop p2))] [_ (error 'parse-pattern "bad pattern: ~e in ~e" p p0)]))) ;; pattern-vars : Pattern -> VarList (define (pattern-vars p0) (let loop ([p p0]) (match p [(? symbol? p) (list p)] [(cons p1 p2) (append (loop p1) (loop p2))] ['() null] [(rep ph varsh '()) varsh] [(rep ph varsh pt) (append varsh (pattern-vars pt))])))) (require (for-syntax 'base) 'base) (define-syntax (quote-pattern stx) (syntax-case stx () [(_ p) #`(quote #,(parse-pattern (syntax->datum #'p)))])) (define-syntax (quote-template-pattern stx) (syntax-case stx () [(_ p) #`(quote #,(parse-pattern (syntax->datum #'p) #t))])) ;; A Match is (match-result VarList MatchEnv) ;; where MatchEnv = (Listof MatchValue) ;; MatchValue = Stx | (Listof MatchValue) (struct match-result (vars vals) #:prefab) (define empty-match-result (match-result null null)) ;; pattern-match : Pattern Stx -> Match/#f (define (pattern-match p0 t0) (define menv (let loop ([p p0] [t t0]) (match p [(? symbol? p) (list t)] ['() (and (stx-null? t) null)] [(cons p1 p2) (cond [(stx-pair? t) (let ([m1 (loop p1 (stx-car t))] [m2 (loop p2 (stx-cdr t))]) (and m1 m2 (append m1 m2)))] [else #f])] [(rep p* vars* '()) (cond [(stx->list t) => (lambda (ts) (define ms (map (lambda (t) (loop p* t)) ts)) (and (andmap values ms) (foldr (lambda (row acc) (map cons row acc)) (map (lambda (var) null) vars*) ms)))] [else #f])]))) (and menv (match-result (pattern-vars p0) menv))) ;; pattern-match-update : Match Match [Nat/#f] -> Match ;; Updates first result with second. If index is given, then m2's vars ;; must occur in m1's pattern in ellipsis, and m2's values replace the ;; index-th elements rather than the whole lists. (define (pattern-match-update m1 m2 [index #f]) (match-define (match-result vars1 vals1) m1) (match-define (match-result vars2 vals2) m2) (define (m2-var-index v) (for/first ([var (in-list vars2)] [k (in-naturals)] #:when (eq? v var)) k)) (define (list-replace xs k y) (cond [(not (pair? xs)) (error 'pattern-match-update "index out of range: ~s for ~e" index m1)] [(zero? k) (cons y (cdr xs))] [else (cons (car xs) (list-replace (cdr xs) (sub1 k) y))])) (match-result vars1 (for/list ([var (in-list vars1)] [val1 (in-list vals1)]) (cond [(m2-var-index var) => (lambda (var-index2) (define val2 (list-ref vals2 var-index2)) (cond [index (list-replace val1 index val2)] [else val2]))] [else val1])))) ;; pattern-template : Pattern Match -> Stx (define (pattern-template p0 mv) (match-define (match-result vars m) mv) (let outerloop ([p p0] [vars vars] [m m]) (define (var-index v) (or (for/first ([var (in-list vars)] [k (in-naturals)] #:when (eq? v var)) k) (error 'pattern-template "unknown var: ~e in ~e" v p))) (define (get-var v) (list-ref m (var-index v))) (let loop ([p p]) (match p [(? symbol? p) (get-var p)] ['() null] [(cons p1 p2) (cons (loop p1) (loop p2))] [(rep (? symbol? p) _ '()) (get-var p)] [(rep p* vars* pt) (define m* (map (lambda (v) (get-var v)) vars*)) (let reploop ([m* m*]) (cond [(andmap pair? m*) (cons (outerloop p* vars* (map car m*)) (reploop (map cdr m*)))] [else (loop pt)]))])))) ;; pattern-resyntax : Pattern Stx Stx -> Stx (define (pattern-resyntax p0 orig t0) (let loop ([p p0] [orig orig] [t t0]) (if (or (syntax? t) (eq? t orig)) t (match p [(cons p1 p2) (restx (cons (loop p1 (stx-car orig) (car t)) (loop p2 (stx-cdr orig) (cdr t))) orig)] [(rep p* _ '()) (let reploop ([orig orig] [t t]) (cond [(syntax? t) t] [(stx-pair? t) (restx (cons (loop p* (stx-car orig) (stx-car t)) (reploop (stx-cdr orig) (stx-cdr t))) orig)] [else (restx t orig)]))] [_ (restx t orig)])))) ;; pattern-replace : Pattern Stx Pattern Stx -> Stx ;; Like (with-syntax ([p1 stx1]) (with-syntax ([p2 stx2]) (syntax p1))). (define (pattern-replace p1 stx1 p2 stx2 #:resyntax? resyntax?) (define m1 (pattern-match p1 stx1)) (define m2 (pattern-match p2 stx2)) (define m-out (pattern-match-update m1 m2)) (define stx-out (pattern-template p1 m-out)) (if resyntax? (pattern-resyntax p1 stx1 stx-out) stx-out)) ;; subpattern-path : Pattern Symbol [Boolean] -> (U Path (vector Path Path)) (define (subpattern-path p0 hole [rep? #f]) (define (outerloop p repb) (let loop ([p p]) (match p [(cons p1 p2) (cond [(loop p1) => path-add-car] [(loop p2) => path-add-cdr] [else #f])] [(rep p* _ '()) (cond [(outerloop p* #f) => (lambda (subpath) (unless repb (error 'subpattern->path "hole has ellipses: ~s, ~s" hole p0)) (set-box! repb subpath) (empty-path))] [else #f])] [(== hole) (when repb (error 'subpattern->path "hole does not have ellipses: ~s, ~s" hole p0)) (empty-path)] [else #f]))) (let ([repb (and rep? (box #f))]) (cond [(outerloop p0 repb) => (lambda (path) (if repb (vector path (unbox repb)) path))] [(error 'subpattern->path "hole not found: ~s, ~s" hole p0)])))