| 1 | |
(declare (unit match-case-simple))
|
| 2 | |
; A simple linear pattern matcher
|
| 3 | |
; It is efficient (generates code at macro-expansion time) and simple:
|
| 4 | |
; it should work on any R5RS Scheme system.
|
| 5 | |
;
|
| 6 | |
; It was first developed for the leanTAP theorem prover in miniKanren.
|
| 7 | |
; It has been in the miniKanren repository
|
| 8 | |
; http://kanren.sf.net/viewvc/kanren/kanren/mini/leanTAP.scm?view=log
|
| 9 | |
; since August 2005.
|
| 10 | |
;
|
| 11 | |
; See the above code for the example of using match-case-simple:
|
| 12 | |
; transforming a first-order logic formula to the Negation Normal Form.
|
| 13 | |
|
| 14 | |
|
| 15 | |
; (match-case-simple exp <clause> ...[<else-clause>])
|
| 16 | |
; <clause> ::= (<pattern> <guard> exp ...)
|
| 17 | |
; <else-clause> ::= (else exp ...)
|
| 18 | |
; <guard> ::= boolean exp | ()
|
| 19 | |
; <pattern> :: =
|
| 20 | |
; ,var -- matches always and binds the var
|
| 21 | |
; pattern must be linear! No check is done
|
| 22 | |
; __ -- matches always
|
| 23 | |
; 'exp -- comparison with exp (using equal?)
|
| 24 | |
; exp -- comparison with exp (using equal?)
|
| 25 | |
; (<pattern1> <pattern2> ...) -- matches the list of patterns
|
| 26 | |
; (<pattern1> . <pattern2>) -- ditto
|
| 27 | |
; () -- matches the empty list
|
| 28 | |
|
| 29 | |
; In the original version, the always-matching pattern was specified
|
| 30 | |
; as a simple underscore. That does not work in R6RS which reserves
|
| 31 | |
; the underscore. Therefore, the always-matching pattern is changed
|
| 32 | |
; to two underscores.
|
| 33 | |
|
| 34 | |
(define-syntax match-case-simple
|
| 35 | |
(syntax-rules ()
|
| 36 | |
((_ exp clause ...)
|
| 37 | |
(let ((val-to-match exp))
|
| 38 | |
(match-case-simple* val-to-match clause ...)))))
|
| 39 | |
|
| 40 | |
(define (match-failure val)
|
| 41 | |
(error "failed match" val))
|
| 42 | |
|
| 43 | |
(define-syntax match-case-simple*
|
| 44 | |
(syntax-rules (else)
|
| 45 | |
((_ val (else exp ...))
|
| 46 | |
(let () exp ...))
|
| 47 | |
((_ val)
|
| 48 | |
(match-failure val))
|
| 49 | |
((_ val (pattern () exp ...) . clauses)
|
| 50 | |
(let ((fail (lambda () (match-case-simple* val . clauses))))
|
| 51 | |
; note that match-pattern may do binding. Here,
|
| 52 | |
; other clauses are outside of these binding
|
| 53 | |
(match-pattern val pattern (let () exp ...) (fail))))
|
| 54 | |
((_ val (pattern guard exp ...) . clauses)
|
| 55 | |
(let ((fail (lambda () (match-case-simple* val . clauses))))
|
| 56 | |
(match-pattern val pattern
|
| 57 | |
(if guard (let () exp ...) (fail))
|
| 58 | |
(fail))))
|
| 59 | |
))
|
| 60 | |
|
| 61 | |
|
| 62 | |
; (match-pattern val pattern kt kf)
|
| 63 | |
(define-syntax match-pattern
|
| 64 | |
(syntax-rules (__ quote unquote)
|
| 65 | |
((_ val __ kt kf) kt)
|
| 66 | |
((_ val () kt kf)
|
| 67 | |
(if (null? val) kt kf))
|
| 68 | |
((_ val (quote lit) kt kf)
|
| 69 | |
(if (equal? val (quote lit)) kt kf))
|
| 70 | |
((_ val (unquote var) kt kf)
|
| 71 | |
(let ((var val)) kt))
|
| 72 | |
((_ val (x . y) kt kf)
|
| 73 | |
(if (pair? val)
|
| 74 | |
(let ((valx (car val))
|
| 75 | |
(valy (cdr val)))
|
| 76 | |
(match-pattern valx x
|
| 77 | |
(match-pattern valy y kt kf)
|
| 78 | |
kf))
|
| 79 | |
kf))
|
| 80 | |
((_ val lit kt kf)
|
| 81 | |
(if (equal? val (quote lit)) kt kf))))
|
| 82 | |
|
| 83 | |
number?
|
| 84 | |
|
| 85 | |
; A simple example:
|
| 86 | |
'(let ()
|
| 87 | |
(define (test-match x)
|
| 88 | |
(match-case-simple x
|
| 89 | |
(,x (number? x) "number")
|
| 90 | |
(() () "nil")
|
| 91 | |
(#t () "bool")
|
| 92 | |
(#f () "bool")
|
| 93 | |
((,x . ,y) ()
|
| 94 | |
(string-append "pair of " (test-match x) " and " (test-match y)))
|
| 95 | |
(__ () "something else")))
|
| 96 | |
(for-each (lambda (x) (display (test-match x)) (newline))
|
| 97 | |
'(1 #t "str" (1 2 3))))
|
| 98 | |
;; printed result:
|
| 99 | |
|
| 100 | |
;; number
|
| 101 | |
;; bool
|
| 102 | |
;; something else
|
| 103 | |
;; pair of number and pair of number and pair of number and nil
|
| 104 | |
|
| 105 | |
|
| 106 | |
; more complex example: meta-circular interpreter
|
| 107 | |
|
| 108 | |
'(let ()
|
| 109 | |
(define (int code env)
|
| 110 | |
(match-case-simple code
|
| 111 | |
(('quote ,x) () x)
|
| 112 | |
((let ((,x ,e)) ,body) (symbol? x)
|
| 113 | |
(let ((xv (int e env)))
|
| 114 | |
(int body (cons (cons x xv) env))))
|
| 115 | |
((lambda () ,body) () ; thunk
|
| 116 | |
(lambda () (int body env))) ; closed over the env
|
| 117 | |
((lambda ,argl ,body) (symbol? argl) ; arglist
|
| 118 | |
(lambda arglv
|
| 119 | |
(int body (cons (cons argl arglv) env))))
|
| 120 | |
((lambda (,x) ,body) (symbol? x) ; 1-arg function
|
| 121 | |
(lambda (xv)
|
| 122 | |
(int body (cons (cons x xv) env))))
|
| 123 | |
; the general case of lambda is skipped to keep the example small
|
| 124 | |
((,op . ,args) ()
|
| 125 | |
(let* ((opv (int op env))
|
| 126 | |
(argvs (map (lambda (c) (int c env)) args)))
|
| 127 | |
(apply opv argvs)))
|
| 128 | |
(,x (symbol? x) (lookup x env))
|
| 129 | |
(,x () x) ; probably number, string, etc.
|
| 130 | |
))
|
| 131 | |
|
| 132 | |
; Lookup a symbol in the environment
|
| 133 | |
(define (lookup x env)
|
| 134 | |
(cond
|
| 135 | |
((assq x env) => cdr)
|
| 136 | |
(else (error "Can't find " x))))
|
| 137 | |
|
| 138 | |
; Initial environment
|
| 139 | |
(define env0
|
| 140 | |
(map (lambda (x) (cons x (eval x (interaction-environment))))
|
| 141 | |
'(+ - display))) ; add more
|
| 142 | |
|
| 143 | |
; tests
|
| 144 | |
|
| 145 | |
(int 1 env0) ; 1
|
| 146 | |
(int '1 env0) ; '1 is the same as 1
|
| 147 | |
|
| 148 | |
(int '(quote x) env0) ; x
|
| 149 | |
|
| 150 | |
(int '(display 'x) env0) ; x
|
| 151 | |
|
| 152 | |
(int '(display x) env0) ; error: unbound x
|
| 153 | |
|
| 154 | |
(int '(let ((x (+ 1 2 3))) (display x)) env0) ; 6
|
| 155 | |
((int '(lambda () 1) env0)) ; 1
|
| 156 | |
((int '(lambda (x) x) env0) 1) ; 1
|
| 157 | |
(((int '(lambda (x) (lambda (y) (+ x y))) env0) 2) 3) ; 5 (test closure)
|
| 158 | |
|
| 159 | |
((int '(lambda l (display l)) env0) 1 2 3) ; (1 2 3)
|
| 160 | |
)
|
| 161 | |
|