forked from dbetz/xlisp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
macros.lsp
executable file
·334 lines (287 loc) · 9.52 KB
/
macros.lsp
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
; for XLisp 3.0
(define %compile compile)
(define (%expand-macros expr)
(if (pair? expr)
(if (symbol? (car expr))
(let ((expander (get (car expr) '%syntax)))
(if expander
(expander expr)
(let ((expander (get (car expr) '%macro)))
(if expander
(%expand-macros (expander expr))
(cons (car expr) (%expand-list (cdr expr)))))))
(%expand-list expr))
expr))
(define (%expand-list lyst)
(if (pair? lyst)
(cons (%expand-macros (car lyst)) (%expand-list (cdr lyst)))
lyst))
(define (compile expr #!optional env)
(if (default-object? env)
(%compile (%expand-macros expr))
(%compile (%expand-macros expr) env)))
(put 'macro '%macro
(lambda (form)
(list 'put
(list 'quote (cadr form))
(list 'quote '%macro)
(caddr form))))
(macro syntax
(lambda (form)
#f))
(macro compiler-syntax
(lambda (form)
(list 'begin
(list 'put
(list 'quote (cadr form))
(list 'quote '%syntax)
(caddr form))
(list 'quote (cadr form)))))
(compiler-syntax quote
(lambda (form) form))
(compiler-syntax quasiquote
(lambda (x)
(qq-process (cadr x))))
(define (parse-higher-order-function-definition lambda-list body)
(let loop ((lambda-list lambda-list) (body body))
(let ((var (car lambda-list))
(formals (cdr lambda-list)))
(if (symbol? var)
(values var `(named-lambda ,var ,formals ,@body))
(loop var `((lambda ,formals ,@body)))))))
(define (convert-internal-definitions body)
(let loop ((body body) (bindings '()))
(if (and body (pair? (car body)) (eq? (caar body) 'define))
(let* ((expr (car body))
(var (second expr)))
(if (pair? var)
(multiple-value-bind (var val)
(parse-higher-order-function-definition var (cddr expr))
(loop (cdr body) (cons `(,var ,val) bindings)))
(let ((val (third expr)))
(loop (cdr body) (cons `(,var ,val) bindings)))))
(if bindings
`((letrec ,(reverse bindings) ,@body))
body))))
(compiler-syntax lambda
(lambda (form)
`(lambda ,(second form)
,@(%expand-list (convert-internal-definitions (cddr form))))))
(compiler-syntax named-lambda
(lambda (form)
`(named-lambda ,(second form) ,(third form)
,@(%expand-list (convert-internal-definitions (cdddr form))))))
(compiler-syntax define
(lambda (form)
(let ((var (second form)))
(if (pair? var)
(let ((body (%expand-list (convert-internal-definitions (cddr form)))))
(multiple-value-bind (var val)
(parse-higher-order-function-definition var body)
`(define ,var ,val)))
(let ((val (%expand-macros (third form))))
(if (and (pair? val) (eq? (car val) 'lambda))
(let ((val `(named-lambda ,var ,@(cdr val))))
`(define ,var ,val))
`(define ,var ,val)))))))
(compiler-syntax multiple-value-bind
(lambda (form)
`(multiple-value-bind ,(second form)
,(%expand-macros (third form))
,@(%expand-list (convert-internal-definitions (cdddr form))))))
(compiler-syntax set!
(lambda (form)
`(set!
,(second form)
,@(%expand-list (cddr form)))))
(define (%cond-expander lyst)
(cond
((pair? lyst)
(cons
(if (pair? (car lyst))
(%expand-list (car lyst))
(car lyst))
(%cond-expander (cdr lyst))))
(else lyst)))
(compiler-syntax cond
(lambda (form)
`(cond ,@(%cond-expander (cdr form)))))
; The following code for expanding let/let*/letrec was donated by:
;
; Harald Hanche-Olsen
; The University of Trondheim
; The Norwegian Institute of Technology
; Division of Mathematics
; N-7034 Trondheim NTH
; Norway
(define (%expand-let-assignment pair)
(if (pair? pair)
(cons
(car pair)
(%expand-macros (cdr pair)))
pair))
(define (%expand-let-form form)
(cons
(car form)
(cons
(let ((lyst (cadr form)))
(if (pair? lyst)
(map %expand-let-assignment lyst)
lyst))
(%expand-list (convert-internal-definitions (cddr form))))))
(compiler-syntax let %expand-let-form)
(compiler-syntax let* %expand-let-form)
(compiler-syntax letrec %expand-let-form)
(macro define-integrable
(lambda (form)
`(define ,@(cdr form))))
(macro declare
(lambda (form) #f))
(define (macro-expand x)
(let ((expander (get (car x) '%macro)))
(expander x)))
(define (subst new old tree)
(define (subst1 tree)
(cond ((pair? tree) (cons (subst1 (car tree))
(subst1 (cdr tree))))
((eqv? tree old) new)
(else tree)))
(subst1 tree))
(macro define-macro
(lambda (form)
(let ((name (caadr form))
(args (subst '&rest '&body (cdadr form)))
(body (cddr form)))
`(macro ,name (named-lambda ,name (form)
(apply (lambda ,args ,@body) (cdr form)))))))
(define-macro (fluid-let bindings &body body)
(let ((vars (map (lambda (binding) (if (pair? binding) (car binding) binding)) bindings))
(inits (map (lambda (binding) (if (pair? binding) (cadr binding) binding)) bindings))
(init-vars (map (lambda (binding) (gensym)) bindings))
(save-vars (map (lambda (binding) (gensym)) bindings))
(make-set (lambda (v i) `(set! ,v ,i))))
`(let ,(append (map (lambda (sv v) (list sv v)) save-vars vars)
(map (lambda (iv i) (list iv i)) init-vars inits))
(unwind-protect
(begin ,@(append (map make-set vars init-vars) body))
,@(map make-set vars save-vars)))))
(define-macro (when test &body body)
`(if ,test (begin ,@body)))
(define-macro (unless test &body body)
`(if (not ,test) (begin ,@body)))
(define-macro (case test &body cases)
(let* ((sym (gensym))
(clauses (map (lambda (x)
(cond ((eq? (car x) 'else)
x)
((atom? (car x))
`((eqv? ,sym ',(car x)) ,@(cdr x)))
(else
`((memv ,sym ',(car x)) ,@(cdr x)))))
cases)) )
`(let ((,sym ,test))
(cond ,@clauses))))
(define-macro (multiple-value-list expr)
`(multiple-value-call list ,expr))
(define-macro (multiple-value-set! vars expr)
(let* ((tmps (map (lambda (x)
(gensym))
vars))
(set-forms (map (lambda (v tv)
`(set! ,v ,tv))
vars tmps)))
`(multiple-value-bind ,tmps
,expr
,@set-forms)))
;;; Contributed by Matthew Halfant
(define-macro (push! ob lst)
`(begin
(set! ,lst (cons ,ob ,lst))
,lst))
(define-macro (pop! lst)
(let ((var (gensym)))
`(let ((,var (car ,lst)))
(set! ,lst (cdr ,lst))
,var)))
(define-macro (inc! x &optional (inc 1))
`(set! ,x (+ ,x ,inc)))
(define-macro (dec! x &optional (dec 1))
`(set! ,x (- ,x ,dec)))
;;; (dotimes (i 10 [result]) (print i)) prints integers from 0 to 9
;;; This version doesn't support embedded RETURN.
;;; Contributed by Matthew Halfant
(define-macro (dotimes range &body body)
(let ((incvar (car range))
(maxvar (cadr range))
(result (caddr range))
(loop (gensym)))
`(let ,loop ((,incvar 0))
(if (>= ,incvar ,maxvar)
,result
(begin
,@body
(,loop (+ ,incvar 1)))))))
(define-macro (dotimes2 range &body body)
(let ((var (car range))
(maximum (cadr range))
(result (caddr range)))
`(let ((,var 0))
(while (< ,var ,maximum)
,@body
(set! ,var (1+ ,var)))
,result)))
;;; (dolist (x '(a b) [result]) (print i)) prints a and b
;;; This version doesn't support embedded RETURN.
;;; Modified from dotimes contributed by Matthew Halfant
(define-macro (dolist range &body body)
(let ((var (car range))
(value-list (cadr range))
(result (caddr range))
(loop (gensym))
(list-var (gensym)))
`(let ,loop ((,list-var ,value-list))
(if ,list-var
(begin
(let ((,var (car ,list-var)))
,@body)
(,loop (cdr ,list-var)))
,result))))
(define-macro (dolist2 range &body body)
(let ((var (car range))
(value-list (cadr range))
(result (caddr range))
(list-var (gensym)))
`(let ((,list-var ,value-list))
(while ,list-var
(let ((,var (car ,list-var)))
,@body)
(set! ,list-var (cdr ,list-var)))
,result)))
(define-macro (do bindings test-result &body body)
(let ((loop (gensym))
(let-bindings nil)
(step-exprs nil)
(test (car test-result))
(result (cdr test-result)))
(let loop ((bindings bindings))
(if bindings
(let* ((binding (car bindings))
(var (first binding))
(init (second binding))
(step (if (cddr binding) (third binding) var)))
(push! (list var init) let-bindings)
(push! step step-exprs)
(loop (cdr bindings)))))
(set! let-bindings (reverse let-bindings))
(set! step-exprs (reverse step-exprs))
`(let ,loop ,let-bindings
(if ,test
(begin ,@result)
(begin ,@(append body (list (cons loop step-exprs))))))))
(define-macro (time &body body)
(let ((time (gensym)))
`(let ((,time (get-time)))
(begin ,@body)
(set! ,time (- (get-time) ,time))
(format t "~%Elapsed time: ~A seconds" ,time)
,time)))