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 | |
|