;; (documentation (name plt-match)) ;;
Pattern Matching Syntactic Extensions for Scheme ;; ;; All bugs or questions concerning this software should be directed to ;; Bruce Hauman. The latest version of this software ;; can be obtained from http://sol.cs.wcu.edu/~bhauman/scheme/pattern.php. ;; ;; Special thanks go out to: ;; Robert Bruce Findler for support and bug detection. ;; Doug Orleans for pointing out that pairs should be reused while ;; matching lists. ;; ;; ;; Originally written by Andrew K. Wright, 1993 (wright@research.nj.nec.com) ;; which in turn was adapted from code written by Bruce F. Duba, 1991. ;; ;; This software is in the public domain. Feel free to copy, ;; distribute, and modify this software as desired. No warranties ;; nor guarantees of any kind apply. Please return any improvements ;; or bug fixes to bhauman@cs.wcu.edu so that they may be included ;; in future releases. ;; ;; This macro package extends Scheme with several new expression forms. ;; Following is a brief summary of the new forms. See the associated ;; LaTeX documentation for a full description of their functionality. ;; ;; ;; match expressions: ;; ;; exp ::= ... ;; | (match exp clause ...) ;; | (match-lambda clause ...) ;; | (match-lambda* clause ...) ;; | (match-let ((pat exp) ...) body ...) ;; | (match-let var ((pat exp) ...) body ...) ;; | (match-let* ((pat exp) ...) body ...) ;; | (match-letrec ((pat exp) ...) body ...) ;; | (match-define pat exp) ;; ;; clause ::= (pat body) | (pat (=> identifier) exp) ;; ;; patterns: matches: ;; ;; pat ::= ;; identifier this binds an identifier if it ;; doesn't conflict with ..k, __k or _ ;; | _ anything ;; | #t #t ;; | #f #f ;; | string a string ;; | number a number ;; | character a character ;; | 'sexp an s-expression ;; | 'symbol a symbol (special case of s-expr) ;; | (var id) allows one to use ..k or _ as ;; identifiers ;; | (list lvp_1 ... lvp_n) list of n elements ;; | (list-rest lvp_1 ... lvp_n pat) an improper list of n elements ;; plus a last element which represents ;; the last cdr of the list ;; | (vector lvp_1 ... lvp_n) vector of n elements ;; | (box pat) box ;; | (struct struct-name (pat_1 ... pat_n)) a structure ;; | (regexp exp) if regular expression exp matches ;; | (regexp exp pat) if result of regexp-match matches pat ;; | (pregexp exp) if pregexp.ss regular expression exp matches ;; | (pregexp exp pat) if result of pregexp-match matches pat ;; | (list-no-order pat ...) matches a list with no regard for ;; the order of the ;; items in the list ;; | (list-no-order pat ... pat_n ooo) pat_n matches the remaining ;; unmatched items ;; | (hash-table (pat_k pat_v) ...) matches the elements of a hash table ;; | (hash-table (pat_k pat_v) ... (pat_kn pat_vn) ooo) ;; pat_kn must match the remaining ;; unmatched key elements ;; pat_vn must match the remaining ;; unmatched value elements ;; | (app field pat) a field of a structure (field is ;; an accessor) ;; Actually field can be any function ;; which can be ;; applied to the data being matched. ;; Ex: (match 5 ((= add1 b) b)) => 6 ;; ;; | (and pat_1 ... pat_n) if all of pat_1 thru pat_n match ;; | (or pat_1 ... pat_n) if any of pat_1 thru pat_n match ;; | (not pat_1 ... pat_n) if all pat_1 thru pat_n don't match ;; | (? predicate pat_1 ... pat_n) if predicate true and all of ;; pat_1 thru pat_n match ;; | (set! identifier) anything, and binds setter ;; | (get! identifier) anything, and binds getter ;; | `qp a quasi-pattern ;; ;; lvp ::= pat ooo greedily matches n or more of pat, ;; each element must match pat ;; | pat matches pat ;; ;; ooo ::= ... zero or more ;; | ___ zero or more ;; | ..k k or more ;; | __k k or more ;; ;; quasi-patterns: matches: ;; ;; qp ::= () the empty list ;; | #t #t ;; | #f #f ;; | string a string ;; | number a number ;; | character a character ;; | identifier a symbol ;; | (qp_1 ... qp_n) list of n elements ;; | (qp_1 ... qp_n . qp_{n+1}) list of n or more ;; | (qp_1 ... qp_n qp_n+1 ooo) list of n or more, each element ;; of remainder must match qp_n+1 ;; | #(qp_1 ... qp_n) vector of n elements ;; | #(qp_1 ... qp_n qp_n+1 ooo) vector of n or more, each element ;; of remainder must match qp_n+1 ;; | #&qp box ;; | ,pat a pattern ;; | ,@(list lvp . . . lvp-n) ;; | ,@(list-rest lvp-1 . . . lvp-n pat) ;; | ,@`qp qp must evaluate to a list as ;; so that this rule resembles the ;; above two rules ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; (module plt-match mzscheme (provide match match-lambda match-lambda* match-let match-let* match-letrec match-define match-test match:test-no-order pregexp-match-with-error exn:misc:match? exn:misc:match-value) (require-for-syntax (lib "stx.ss" "syntax") (lib "etc.ss") (lib "list.ss") (lib "include.ss") (lib "struct.ss" "syntax") (lib "pretty.ss")) (require (lib "pregexp.ss") (lib "etc.ss") (lib "list.ss")) ;; this makes pregexp errors a little more friendly (define (pregexp-match-with-error regex str) (if (or (string? regex) (and (pair? regex) (equal? ':sub (car regex)))) (pregexp-match regex str) (error 'match:pregex (string-append "this pattern expects either a S-regexp or a U-regexp," " given " (format "~s" regex) "; " "other argument was " (format "~s" str))))) (define-struct (exn:misc:match exn:fail) (value)) (define match:error (case-lambda ((val) (raise (make-exn:misc:match (format "match: no matching clause for ~e" val) (current-continuation-marks) val))) ((val expr) (raise (make-exn:misc:match (format "match: no matching clause for ~e: ~s" val expr) (current-continuation-marks) val))))) (define-syntax-set (match match-lambda match-lambda* match-let match-let* match-letrec match-define match-test ) (include (build-path "private" "plt-match" "match-inc.scm")) (define node-count 0) ;;!(syntax match-test) ;; This macro only returns a list of two numbers. ;; The first number represents the ;; number of nodes generated in the process of compiling the match ;; expression. This gives one and idea as to the size of the ;; compiled expression. ;; The second number is the amount of "real" time in milliseconds ;; it took to compile the patterns. (define match-test/proc (lambda (stx) (syntax-case stx (=>) ((_ clause ...) (begin (set! node-count 0) (let-values (((stx t rt gc) (time-apply gen-match (list (syntax x) '() (syntax (clause ...)) stx)))) #`(list #,(add1 node-count) #,rt))))))) (define (match-func stx stx-orig) (syntax-case stx (=>) ((_ exp clause ...) (quasisyntax/loc stx (let ((x exp)) #,(gen-match (syntax x) '() (syntax (clause ...)) stx-orig)))))) (define match-lambda-func (lambda (stx stx-orig) (syntax-case stx () [(k clause ...) (quasisyntax/loc stx (lambda (exp) #,(match-func (syntax/loc stx (match exp clause ...)) stx-orig)))]))) (define match-lambda*-func (lambda (stx stx-orig) (syntax-case stx () [(k clause ...) (quasisyntax/loc stx (lambda exp #,(match-func (syntax/loc stx (match exp clause ...)) stx-orig)))]))) (define match-let-func (lambda (stx stx-orig) (syntax-case stx () [(_ name () body1 body ...) (syntax/loc stx (let name () body1 body ...))] [(_ name ([pat1 exp1] [pat exp]...) body1 body ...) (identifier? (syntax name)) (let ((pat-list (syntax-object->datum (syntax (pat1 pat ...)))) (real-name (syntax-object->datum (syntax name)))) (if (andmap pattern-var? pat-list) (syntax/loc stx (let name ([pat1 exp1] [pat exp] ...) body1 body ...)) (quasisyntax/loc stx (letrec ([name #,(match-lambda*-func (syntax/loc stx (match-lambda* ((list pat1 pat ...) body1 body ...))) stx-orig) ]) (name exp1 exp ...)))))] [(_ () body1 body ...) (syntax/loc stx (begin body1 body...))] [(_ ([pat1 exp1] [pat exp]...) body1 body ...) (quasisyntax/loc stx ( #,(match-lambda*-func (syntax/loc stx (match-lambda* ((list pat1 pat ...) body1 body ...))) stx-orig) exp1 exp ...))]))) (define match-let*-func (lambda (stx stx-orig) (syntax-case stx () ((_ () body body1 ...) (syntax/loc stx (let* () body body1 ...))) ((_ ([pat exp] rest ...) body body1 ...) (if (pattern-var? (syntax-object->datum (syntax pat))) (quasisyntax/loc stx (let ([pat exp]) #,(match-let*-func (syntax (match-let* (rest ...) body body1 ...)) stx-orig) ) ) (match-func (quasisyntax/loc stx (match exp [pat #,(match-let*-func (syntax (match-let* (rest ...) body body1 ...)) stx-orig)])) stx-orig)))))) (define match-letrec-func (lambda (stx stx-orig) (syntax-case stx () ((_ () body body1 ...) (syntax/loc stx (let () body body1 ...))) ((_ ([pat exp] ...) body body1 ...) (andmap pattern-var? (syntax-object->datum (syntax (pat ...)))) (syntax/loc stx (letrec ([pat exp] ...) body body1 ...))) ((_ ([pat exp] ...) body body1 ...) (let* ((**match-bound-vars** '()) (compiled-match (gen-match (syntax the-exp);(syntax (list exp ...)) '() (syntax (((list pat ...) never-used))) stx-orig (lambda (sf bv) (set! **match-bound-vars** bv) (quasisyntax/loc stx (begin #,@(map (lambda (x) (quasisyntax/loc stx (set! #,(car x) #,(cdr x)))) (reverse bv)) body body1 ...)))))) (quasisyntax/loc stx (letrec (#,@(map (lambda (x) (quasisyntax/loc stx (#,(car x) #f))) (reverse **match-bound-vars**)) (the-exp (list exp ...))) #,compiled-match))))))) (define match-define-func (lambda (stx stx-orig) (syntax-case stx () [(_ pat exp) (identifier? (syntax pat)) (syntax/loc stx (begin (define pat exp)))] [(_ pat exp) (let* ((**match-bound-vars** '()) (compiled-match (gen-match (syntax the-exp) '() (syntax/loc (syntax pat) ((pat never-used))) stx-orig (lambda (sf bv) (set! **match-bound-vars** bv) (quasisyntax/loc stx (begin #,@(map (lambda (x) (quasisyntax/loc stx (set! #,(car x) #,(cdr x)))) (reverse bv)))))))) (quasisyntax/loc stx (begin #,@(map (lambda (x) (quasisyntax/loc stx (define #,(car x) #f))) (reverse **match-bound-vars**)) (let ((the-exp exp)) #,compiled-match))))]))) (define match/proc (lambda (stx) (match-func stx stx))) (define match-lambda/proc (lambda (stx) (match-lambda-func stx stx))) (define match-lambda*/proc (lambda (stx) (match-lambda*-func stx stx))) (define match-let/proc (lambda (stx) (match-let-func stx stx))) (define match-let*/proc (lambda (stx) (match-let*-func stx stx))) (define match-letrec/proc (lambda (stx) (match-letrec-func stx stx))) (define match-define/proc (lambda (stx) (match-define-func stx stx))) ) ;;!(function match:test-no-order ;; (form (match:test-no-order tests l last-test ddk-num) ;; -> ;; bool) ;; (contract (list list test integer) -> bool)) ;; This is a recursive depth first search for a sequence of ;; items in list l which will satisfy all of the tests in list ;; tests. This is used for list-no-order and hash-table patterns. ;; This function also handles ddk patterns by passing it the last ;; test before the ddk and the value of k. (define (match:test-no-order tests l last-test ddk-num) (define (handle-last-test test l) (and (>= (length l) ddk-num) (andmap test l))) (define (dep-first-test head rest tests) (cond ((null? tests) (if last-test (handle-last-test last-test (cons head rest)) #f)) ((null? rest) (if last-test (and (= 0 ddk-num) (= 1 (length tests)) ((car tests) head)) (and (= 1 (length tests)) ((car tests) head)))) (else (and (pair? tests) ((car tests) head) (match:test-no-order (cdr tests) rest last-test ddk-num))))) (let loop ((lst l)) (if (null? lst) #f (or (dep-first-test (car lst) (remove (car lst) l) tests) (loop (cdr lst)))))) )