-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathmad.lisp
355 lines (295 loc) · 11.3 KB
/
mad.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
;;; Macroexpand dammit -- a portable code walker for Common Lisp
;;; Written by John Fremlin at MSI (http://www.msi.co.jp) Released
;;; into the public domain.
;;; http://john.freml.in/macroexpand-dammit
;;; Transforms code to return a quoted version its macroexpansion
;;; using the host lisp to implicitly augment the lexical environment.
;;; Expands macros, macrolets, symbol-macros, symbol-macrolets, and
;;; compiler-macros. Removes macrolets and symbol-macrolets.
;;; Supports a few non-standard special forms for current (2009) Lisps.
;;; Lightly tested on SBCL 1.0.29, ClozureCL 1.4-pre, Lispworks 5.1,
;;; Allegro 8.1
;;; 20100301
;; -- do not totally discard macrolet bodies (doh), as
;;; reported by mathrick on #lisp
;; 20100701
;; - correct the mistaken loop bindings to remove warnings for CCL.
;;; reported by Daniel Gackle
(cl:in-package #:fn)
(eval-when (:compile-toplevel :load-toplevel :execute)
(defvar *form-handler* (make-hash-table))
(defun force-first (x)
(if (listp x) (first x) x))
(defun force-list (x)
(if (listp x) x (list x))))
(defvar *env*)
(defun binding-to-symbol (binding)
(let ((name (force-first binding)))
(cond ((listp name)
(assert (eq 'setf (first name)))
(check-type (second name) symbol)
(second name))
(t
name))))
(defmacro with-imposed-bindings (&body body)
`(locally ,@body)
#+sbcl
(destructuring-bind ((binder bindings &rest binder-body))
body
`(locally
(declare (sb-ext:disable-package-locks ,@(mapcar 'binding-to-symbol bindings)))
(,binder ,bindings
,@binder-body))))
(defmacro without-package-locking (&body body)
`(
#. (progn 'progn
#+sbcl 'sb-ext:without-package-locks)
,@body))
(defmacro defhandler (symbol lambda-list &body body)
(let ((syms (force-list symbol)))
(let ((func (intern (format nil "~A~A" 'hander- (first syms)))))
`(progn
(defun ,func ,lambda-list
,@body)
(setf
,@(loop for sym in syms
collect `(gethash ',sym *form-handler*)
collect `',func))))))
(defun e-list (list)
(mapcar 'e list))
(defhandler (progn locally) (progn &rest body)
`(list ',progn
,@(e-list body)))
(defhandler let (let bindings &rest body)
(let ((names (loop for binding in bindings
collect
(force-first binding))))
`(list*
',let
(list
,@(loop for binding in bindings
collect
(if (symbolp binding)
`',binding
`(list ',(first binding)
,@(e-list (rest binding))))))
(with-imposed-bindings
(,let ,names
(declare (ignorable ,@names))
(m-list ,@body))))))
(defun dump-fbinding (name lambda-list &rest body)
(let (bound-vars)
(labels (
(binding-vars (&rest body)
`(let ,bound-vars
(declare (ignorable ,@bound-vars))
(m-list ,@body)))
(l (lambda-arg)
(cond ((member lambda-arg lambda-list-keywords)
`',lambda-arg)
(t
(destructuring-bind
(var &optional (val nil val-present-p) present-var)
(force-list lambda-arg)
(prog1
(if (listp lambda-arg)
`(list ',var ,@(when val-present-p `((car ,(binding-vars val))))
,@(when present-var `(',present-var)))
`',var)
(push var bound-vars)
(when present-var (push present-var bound-vars))))))))
`(list* ',name (list ,@(mapcar #'l lambda-list))
,(apply #'binding-vars body)))))
(defun dump-fbindings (bindings)
`(list ,@(mapcar (lambda (f) (apply 'dump-fbinding f)) bindings)))
(defun declare-fbindings-ignorable (bindings)
`(declare (ignorable ,@(mapcar (lambda (f)
`(function ,(force-first f))) bindings))))
(defun declare-lambda-list-ignorable (lambda-list)
`(declare (ignorable
,@(loop for binding in lambda-list
append
(unless (member binding lambda-list-keywords)
(destructuring-bind (var &optional default present-var)
(force-list binding)
(declare (ignore default))
(list* var (when present-var (list present-var)))))))))
(defun maybe-locally (forms)
(flet ((starts-with-declare ()
(and (listp (first forms)) (eq (first (first forms)) 'declare))))
(cond ((or (rest forms) (starts-with-declare))
(list* (if (starts-with-declare) 'locally 'progn) forms))
(t
(first forms)))))
(defhandler declare (declare &rest body)
`(list ',declare
,@(mapcar (lambda (f) `',f) body)))
(defhandler block (block name &rest body)
`(list ',block ',name
,@(e-list body)))
(defhandler return-from (return-from name &optional (value nil value-p))
`(list ',return-from ',name
,@(when value-p
`(,(e value)))))
(defhandler catch (catch tag &rest body)
`(list ',catch ,(e tag) ,@(e-list body)))
(defhandler load-time-value (load-time-value form &optional (read-only-p nil rop-p))
`(list ',load-time-value ,(e form)
,@(when rop-p
`(',read-only-p))))
(defhandler
(macrolet
symbol-macrolet
compiler-let ; mostly for Lispworks
)
(macrolet bindings &rest body)
`(maybe-locally
(with-imposed-bindings
(,macrolet ,bindings
(m-list ,@body)))))
(defun clean-fbindings (bindings)
"Return a set of bindings that always defaults to nil"
(flet ((clean-argument-bindings (bindings)
(loop for binding in bindings
collect
(destructuring-bind (var &optional default present-var)
(force-list binding)
(declare (ignore default))
(if present-var
`(,var nil ,present-var)
var)))))
(loop for (func lambda-list) in bindings
for clean-lambda-list = (clean-argument-bindings lambda-list)
collect `(,func ,clean-lambda-list
,(declare-lambda-list-ignorable clean-lambda-list)))))
(defhandler flet (flet bindings &rest body)
`(list* ',flet
,(dump-fbindings bindings)
(with-imposed-bindings
(,flet ,(clean-fbindings bindings)
,(declare-fbindings-ignorable bindings)
(m-list ,@body)))))
(defhandler labels (labels bindings &rest body)
`(with-imposed-bindings
(,labels ,(clean-fbindings bindings)
,(declare-fbindings-ignorable bindings)
(list* ',labels
,(dump-fbindings bindings)
(m-list ,@body)))))
(defhandler let* (let* bindings &rest body)
(if (not bindings)
(e `(locally ,@body))
(destructuring-bind (first &rest rest)
bindings
(e `(let (,first)
,@(if rest
`((,let* ,rest ,@body))
body))))))
(defhandler eval-when (eval-when situation &rest body)
`(list ',eval-when ',situation
,@(e-list body)))
#+sbcl
(defhandler sb-int:named-lambda (named-lambda name lambda-list &rest body)
`(list* ',named-lambda ,(apply 'dump-fbinding name lambda-list body)))
(defhandler defun (defun name lambda-list &rest body)
`(list* ',defun ,(apply 'dump-fbinding name lambda-list body)))
(defhandler lambda (lambda lambda-list &rest body)
(apply 'dump-fbinding lambda lambda-list body))
(defun tagbody-restore-tags (list)
(loop for f in list
collect
(cond ((or (symbolp f) (integerp f))
`(progn ,f))
((and (listp f) (eq 'tagbody-restore-tag (first f)))
(second f))
(t
f))))
(defhandler tagbody (tagbody &rest tags-and-forms)
`(list* ',tagbody
(tagbody-restore-tags
(list
,@(loop for f in tags-and-forms
collect
(if (or (symbolp f) (integerp f))
`(list 'tagbody-restore-tag ',f)
(e f)))))))
(defhandler setq (setq &rest pairs)
(declare (ignore setq))
(let ((vars (loop for s in pairs by #'cddr collect (macroexpand s *env*))))
(let ((expanded (loop for n in vars for r in (rest pairs) by #'cddr
collect n collect r)))
(if (some 'listp vars)
(e `(setf ,@expanded))
`(list 'setq ,@(e-list expanded))))))
(defun function-name-p (name)
(or (symbolp name)
(and (listp name) (eq (first name) 'setf) (symbolp (second name)) (not (cddr name)))))
(defhandler function (function name)
`(list ',function
,(if (function-name-p name)
`',name
(e name))))
(defhandler the (the value-type form)
`(list ',the ',value-type ,(e form)))
(defhandler go (go tag)
`(list ',go ',tag))
(defhandler unwind-protect (unwind-protect protected-form &rest cleanup)
`(list ',unwind-protect ,(e protected-form) ,@(e-list cleanup)))
(defhandler progv (progv symbols values &rest body)
`(list ',progv
(list ,@(e-list symbols))
(list ,@(e-list values))
,@(e-list body)))
(defhandler quote (quote object)
`(list ',quote ',object))
(defun default-form-handler (first &rest rest)
`(list ,(if (symbolp first)
`',first
(e first)) ,@(e-list rest)))
(defun form-handler (first)
(gethash first *form-handler*
'default-form-handler))
(defun compiler-macroexpand-1 (form &optional *env*)
(let ((cm
(and (listp form) (function-name-p (first form))
(compiler-macro-function (first form) *env*))))
(if cm
(funcall *macroexpand-hook* cm form *env*)
form)))
(defun e (form)
(flet ((handle (form)
(apply (form-handler (first form)) form)))
(cond ((and (listp form) (gethash (first form) *form-handler*))
(handle form))
(t
(multiple-value-bind (form expanded)
(macroexpand-1 form *env*)
(cond (expanded
(e form))
(t
(typecase form
(null nil)
(list
(let ((next (compiler-macroexpand-1 form)))
(if (eq form next)
(handle form)
(e next))))
(t
`',form)))))))))
(defmacro m (form &environment *env*)
(e form))
(defmacro m-list (&body body &environment *env*)
`(list ,@(e-list body)))
(defun macroexpand-dammit (form &optional *env*)
(eval (e form)))
(defmacro macroexpand-dammit-as-macro (form)
`(m ,form))
(defun macroexpand-dammit-expansion (form &optional *env*)
(e form))
;;; Some shenanigans to support running with or without swank
(defun runtime-symbol (name package-name)
(or (find-symbol (symbol-name name)
(or (find-package package-name) (error "No package ~A" package-name)))
(error "No symbol ~A in package ~A" name package-name)))
(defun macroexpand-dammit-string (str)
(funcall (runtime-symbol 'apply-macro-expander 'swank) 'macroexpand-dammit str))