repos
/
cl-unification
/ annotate_shade
summary
|
shortlog
|
log
|
tree
|
commit
|
commitdiff
|
headdiff
|
annotate
|
headblob
|
headfilediff
|
filehistory
normal
|
plain
|
shade
|
zebra
Rather than a new UNIFY** function, make UNIFY* work that way
Annotate for file match-block.lisp
2007-11-09 mantoniotti
1
;;;; -*- Mode: Lisp -*-
13:43:20 '
2
'
3
;;;; match-block.lisp --
'
4
;;;; Various macros built on top of the unifier: MATCH, MATCHING and MATCH-CASE.
2004-11-17 mantoniotti
5
22:19:54 '
6
(in-package "UNIFY")
'
7
'
8
(defun clean-unify-var-name (v)
'
9
(assert (variablep v))
'
10
(intern (subseq (symbol-name v) 1)
'
11
(symbol-package v)))
'
12
2010-02-04 pix
13
(defun %template-for-match (template)
07:32:18 '
14
(if (variablep template)
'
15
`',template ; Logical variables are special-cased.
'
16
template))
2004-11-17 mantoniotti
17
2010-02-04 pix
18
(defun %wrap-var-bindings (template environment-var forms)
07:32:18 '
19
(let* ((template-vars (collect-template-vars template))
'
20
(bindings (loop for v in template-vars
'
21
nconc (list `(,v (find-variable-value ',v
'
22
,environment-var))
'
23
`(,(clean-unify-var-name v) ,v)))))
'
24
`(let* ,bindings
'
25
(declare (ignorable ,@(mapcar #'first bindings)))
'
26
,@forms)))
2010-01-12 pix
27
2004-11-17 mantoniotti
28
(defmacro match ((template object
22:19:54 '
29
&key
2010-02-04 pix
30
(match-named nil)
07:32:18 '
31
(substitution '(make-empty-environment))
2004-11-17 mantoniotti
32
(errorp t)
22:19:54 '
33
(error-value nil))
'
34
&body forms)
2010-02-05 pix
35
"Sets up a lexical environment to evaluate FORMS after an unification.
03:21:40 '
36
'
37
MATCH unifies a TEMPLATE and an OBJECT and then sets up a lexical
'
38
environment where the variables present in the template are bound
'
39
lexically. Note that both variable names '?FOO' and 'FOO' are bound
'
40
for convenience.
'
41
'
42
The MATCH form returns the values returned by the evaluation of the
'
43
last of the FORMS.
'
44
'
45
If ERRORP is non-NIL (the default) then the form raises a
'
46
UNIFICATION-FAILURE, otherwise the result of evaluating ERROR-VALUE,
'
47
whose default is NIL is returned. (Note that UNIFICATION-FAILUREs
'
48
raising from the evaluation of FORMS will also be caught and handled
'
49
according to ERRORP settings.)
'
50
'
51
If MATCH-NAMED is not NIL, then a surrounding BLOCK named MATCH-NAMED
'
52
is set up around the matching code.
'
53
"
2010-02-04 pix
54
(let ((env-var (gensym "UNIFICATION-ENV-"))
07:32:18 '
55
(template (%template-for-match template))
'
56
)
'
57
`(block ,match-named
'
58
(handler-case
'
59
(let* ((,env-var (unify ,template ,object ,substitution))
'
60
)
'
61
,(%wrap-var-bindings template env-var forms))
'
62
'
63
;; Yes. The above is sligthly wasteful.
'
64
'
65
(unification-failure (uf)
'
66
(if ,errorp
'
67
(error uf)
'
68
,error-value))
'
69
))))
'
70
2009-04-15 mantoniotti
71
10:16:24 '
72
(defmacro matchf ((template object
'
73
&key
2010-02-04 pix
74
(match-named nil)
07:32:18 '
75
(substitution '(make-empty-environment))
2009-04-15 mantoniotti
76
(errorp t)
10:16:24 '
77
(error-value nil))
'
78
&body forms)
2010-02-05 pix
79
"Sets up a lexical environment to evaluate FORMS after an unification.
2009-04-15 mantoniotti
80
2010-02-05 pix
81
MATCHF unifies a TEMPLATE and an OBJECT and then sets up a lexical
2009-04-15 mantoniotti
82
environment where the variables present in the template are bound
10:16:24 '
83
lexically. Note that both variable names '?FOO' and 'FOO' are bound
'
84
for convenience.
'
85
'
86
MATCHF does not 'evaluate' TEMPLATE (note that using the #T syntax will
'
87
generate a template at read-time).
'
88
2010-02-05 pix
89
The MATCHF form returns the values returned by the evaluation of the
2009-04-15 mantoniotti
90
last of the FORMS.
10:16:24 '
91
'
92
If ERRORP is non-NIL (the default) then the form raises a
'
93
UNIFICATION-FAILURE, otherwise the result of evaluating ERROR-VALUE,
'
94
whose default is NIL is returned. (Note that UNIFICATION-FAILUREs
2010-02-05 pix
95
raising from the evaluation of FORMS will also be caught and handled
2009-04-15 mantoniotti
96
according to ERRORP settings.)
10:16:24 '
97
2010-02-05 pix
98
If MATCH-NAMED is not NIL, then a surrounding BLOCK named MATCH-NAMED
03:21:40 '
99
is set up around the matching code.
'
100
"
2010-02-04 pix
101
(let ((env-var (gensym "UNIFICATION-ENV-"))
07:32:18 '
102
(template (cond ((variablep template)
'
103
`',template) ; Logical variables are special-cased.
'
104
((listp template) ; Same for lists.
'
105
(make-instance 'list-template
'
106
:spec (cons 'list template)))
'
107
;`',template)
'
108
(t
'
109
template)))
'
110
)
'
111
;; Logical variables and lists are special cased for convenience.
'
112
;; Lists are especially inteded as abbreviation for destructuring.
'
113
`(block ,match-named
'
114
(handler-case
'
115
(let* ((,env-var (unify ,template ,object ,substitution))
'
116
)
'
117
,(%wrap-var-bindings template env-var forms))
'
118
'
119
;; Yes. The above is sligthly wasteful.
'
120
'
121
(unification-failure (uf)
'
122
(if ,errorp
'
123
(error uf)
'
124
,error-value))
'
125
))))
2004-11-17 mantoniotti
126
22:19:54 '
127
'
128
'
129
(define-condition unification-non-exhaustive (unification-failure)
2010-02-04 pix
130
())
2004-11-17 mantoniotti
131
22:19:54 '
132
'
133
(defmacro matching ((&key errorp
2010-02-04 pix
134
(default-substitution
07:32:18 '
135
(make-empty-environment))
'
136
(matching-named nil))
2010-01-15 pix
137
&body match-clauses)
2010-02-05 pix
138
"MATCHING sets up a COND-like environment for multiple template matching clauses.
03:21:40 '
139
'
140
The syntax of MATCHING comprises a number of clauses of the form
'
141
'
142
<clause> ::= <regular-clause> | <default-clause>
'
143
<regular-clause> ::= ((<template> <form>) &body <forms>)
'
144
<default-clause> ::= (t &body <forms>)
'
145
| (otherwise &body <forms>)
'
146
<form> and <forms> are regular Common Lisp forms.
'
147
<template> is a unification template.
'
148
'
149
The full syntax of MATCHING is
'
150
'
151
matching (&key errorp default-substitution) <clauses>
'
152
'
153
Each clause evaluates its forms in an environment where the variables
'
154
present in the template are bound lexically. Note that both variable
'
155
names '?FOO' and 'FOO' are bound for convenience.
'
156
'
157
The values returned by the MATCHING form are those of the last form in
'
158
the first clause that satisfies the match test.
'
159
'
160
If ERRORP is non-NIL then if none of the regular clauses matches, then
'
161
an error of type UNIFICATION-NON-EXAUSTIVE is signalled, regardless of
'
162
any default clause. Otherwise, the default clause behaves as a
'
163
standard COND default clause. The default value of ERRORP is NIL.
'
164
"
2010-02-04 pix
165
(declare (ignore default-substitution)) ; For the time being.
07:32:18 '
166
(labels ((%%match%% (clause-var template object forms substitution)
'
167
(let ((template (%template-for-match template))
'
168
)
'
169
`((setf ,clause-var
2010-01-21 pix
170
(unify* ,template ,object ,substitution))
2010-02-04 pix
171
,(%wrap-var-bindings template clause-var forms))
07:32:18 '
172
))
'
173
'
174
(build-match-clause (match-clause match-env-var)
'
175
(destructuring-bind ((template object) &body forms)
'
176
match-clause
'
177
(%%match%% match-env-var
'
178
template
'
179
object
'
180
forms
'
181
'(make-empty-environment))))
'
182
)
'
183
(when (or (and (find t match-clauses :key #'first)
'
184
(find 'otherwise match-clauses :key #'first))
'
185
(> (count t match-clauses :key #'first) 1)
'
186
(> (count 'otherwise match-clauses :key #'first) 1))
'
187
(error 'program-error))
'
188
(let* ((default-clause (or (find t match-clauses
'
189
:key #'first)
'
190
(find 'otherwise match-clauses
'
191
:key #'first)))
'
192
(match-clauses (delete default-clause match-clauses)) ; EQL
'
193
; test
'
194
; suffices.
'
195
(env-var (gensym "UNIFICATION-ENV-"))
'
196
)
2004-11-17 mantoniotti
197
2010-02-04 pix
198
`(block ,matching-named
07:32:18 '
199
(let (,env-var)
'
200
(declare (dynamic-extent ,env-var))
'
201
(cond ,@(mapcar (lambda (match-clause)
'
202
(build-match-clause match-clause
'
203
env-var))
'
204
match-clauses)
'
205
(,errorp
'
206
(error 'unification-non-exhaustive
'
207
:format-control "Non exhaustive matching."))
2010-01-25 pix
208
,(when default-clause (cons t (cdr default-clause)))))))
2010-02-04 pix
209
))
07:32:18 '
210
'
211
'
212
(defmacro match-case ((object &key errorp default-substitution match-case-named)
2010-02-05 pix
213
&body clauses)
03:21:40 '
214
"MATCH-CASE sets up a CASE-like environment for multiple template matching clauses.
'
215
'
216
The syntax of MATCH-CASE comprises a number of clauses of the form
2005-04-27 mantoniotti
217
20:41:56 '
218
<clause> ::= <regular-clause> | <default-clause>
2010-02-05 pix
219
<regular-clause> ::= (<template> &body <forms>)
2005-04-27 mantoniotti
220
<default-clause> ::= (t &body <forms>)
20:41:56 '
221
| (otherwise &body <forms>)
'
222
<form> and <forms> are regular Common Lisp forms.
'
223
<template> is a unification template.
'
224
2010-02-05 pix
225
The full syntax of MATCH-CASE is
03:21:40 '
226
'
227
match-case <object> (&key errorp default-substitution) <clauses>
2005-04-27 mantoniotti
228
20:41:56 '
229
Each clause evaluates its forms in an environment where the variables
'
230
present in the template are bound lexically. Note that both variable
'
231
names '?FOO' and 'FOO' are bound for convenience.
'
232
2010-02-05 pix
233
The values returned by the MATCH-CASE form are those of the last form in
2005-04-27 mantoniotti
234
the first clause that satisfies the match test.
20:41:56 '
235
'
236
If ERRORP is non-NIL then if none of the regular clauses matches, then
'
237
an error of type UNIFICATION-NON-EXAUSTIVE is signalled, regardless of
'
238
any default clause. Otherwise, the default clause behaves as a
2010-02-05 pix
239
standard CASE default clause. The default value of ERRORP is NIL.
03:21:40 '
240
"
2010-02-04 pix
241
(let ((object-var (gensym "OBJECT-VAR-")))
07:32:18 '
242
`(let ((,object-var ,object))
'
243
(matching (:errorp ,errorp :default-substitution ,default-substitution :matching-named ,match-case-named)
'
244
,@(mapcar
'
245
(lambda (clause)
'
246
`(,(if (member (first clause) '(t otherwise))
'
247
(first clause)
'
248
(list (first clause) object-var))
'
249
,@(rest clause)))
'
250
clauses)))))
2005-04-27 mantoniotti
251
2009-12-17 mantoniotti
252
2010-02-04 pix
253
(defmacro matchf-case ((object &key errorp default-substitution match-case-named)
07:32:18 '
254
&body clauses)
2010-02-05 pix
255
"MATCHF-CASE sets up a CASE-like environment for multiple template matching clauses.
03:21:40 '
256
'
257
The syntax of MATCHF-CASE comprises a number of clauses of the form
'
258
'
259
<clause> ::= <regular-clause> | <default-clause>
'
260
<regular-clause> ::= (<template> &body <forms>)
'
261
<default-clause> ::= (t &body <forms>)
'
262
| (otherwise &body <forms>)
'
263
<form> and <forms> are regular Common Lisp forms.
'
264
<template> is a unification template.
'
265
'
266
The full syntax of MATCHF-CASE is
'
267
'
268
matchf-case <object> (&key errorp default-substitution) <clauses>
'
269
'
270
Each clause evaluates its forms in an environment where the variables
'
271
present in the template are bound lexically. Note that both variable
'
272
names '?FOO' and 'FOO' are bound for convenience.
'
273
'
274
The values returned by the MATCH-CASE form are those of the last form in
'
275
the first clause that satisfies the match test.
'
276
'
277
If ERRORP is non-NIL then if none of the regular clauses matches, then
'
278
an error of type UNIFICATION-NON-EXAUSTIVE is signalled, regardless of
'
279
any default clause. Otherwise, the default clause behaves as a
'
280
standard CASE default clause. The default value of ERRORP is NIL.
'
281
'
282
MATCHF-CASE behaves like MATCH-CASE, but the patterns are not
'
283
evaluated (i.e., it relies on MATCHF instead of MATCH to construct the
'
284
macro expansion.
'
285
"
2010-02-04 pix
286
(declare (ignore default-substitution)) ; For the time being.
07:32:18 '
287
(let* ((object-var (gensym "OBJECT-VAR-"))
'
288
(otherwise-clause-present-p
'
289
(member (caar (last clauses)) '(t otherwise)))
'
290
(non-otherwise-clauses
'
291
(if otherwise-clause-present-p
'
292
(butlast clauses)
'
293
clauses))
'
294
(otherwise-clause
'
295
(if otherwise-clause-present-p
'
296
(first (last clauses))
'
297
(when errorp
'
298
`(t (error 'unification-non-exhaustive
'
299
:format-control "Non exhaustive matching.")))))
'
300
)
'
301
(labels ((generate-matchers (clauses)
'
302
(if (null clauses)
'
303
`(progn ,@(rest otherwise-clause))
'
304
(destructuring-bind (pattern &rest body)
'
305
(car clauses)
'
306
`(handler-case (matchf (,pattern ,object-var)
'
307
,@body)
'
308
(unification-failure ()
'
309
,(generate-matchers (cdr clauses))))))))
'
310
`(block ,match-case-named
'
311
(let ((,object-var ,object))
'
312
,(generate-matchers non-otherwise-clauses))))))
2009-12-17 mantoniotti
313
2007-11-09 mantoniotti
314
;;;;---------------------------------------------------------------------------
13:43:20 '
315
;;;; Testing.
'
316
'
317
#| Tests
'
318
'
319
(let ((n 42))
'
320
(matching ()
'
321
((0 n) 1)
'
322
((?x n) (* x (1- x)))))
'
323
'
324
'
325
(let ((n 42))
'
326
(match-case (n)
'
327
(0 1)
'
328
(?x (* x (1- x)))))
'
329
'
330
'
331
(let ((n 42))
'
332
(match-case (n)
'
333
(0 1)
'
334
(otherwise (* n (1- n)))))
'
335
'
336
(defun fatt (x)
'
337
(match-case (x :errorp t)
'
338
(0 1)
'
339
(#T(number ?n) (* ?n (fatt (1- n))))
'
340
))
'
341
'
342
|#
2005-04-27 mantoniotti
343
2007-11-09 mantoniotti
344
;;;; end of file -- math-blocks.lisp --