source: project/wiki/eggref/5/procedural-macros @ 37429

Last change on this file since 37429 was 37429, checked in by juergen, 21 months ago

procedural-macros docu

File size: 15.7 KB
Line 
1[[tags: egg]]
2[[toc:]]
3
4== Procedural macros made easy
5
6The Scheme standard, R5RS, only provides declarative macros based on
7syntax-rules. They are easy to use, but rather limited. For example, you
8can only create hygienic macros, you have no control over the expansion
9process, in particular, you can't use local procedures to be evaluated at
10compile time. To overcome this limitations, R6RS offers syntax-case
11macros, but that's a mouthfull ...
12
13Fortunately, Chicken offers two versions of procedural macros, explicit
14and implicit renaming macros. They offer full flexibility without any
15limitations but are tedious to use.
16
17First, you must care to avoid variable capture with renaming, if you
18want hygienic macros, or you must decide which variables should be
19captured on purpose. Implicit renaming here helps a lot: You simply
20inject names which you want to be captured, the others are renamed
21automatically by the runtime system.
22
23Second, you must do the destructuring of the macro code by hand.
24Wouldn't it be nice, if this could be done automatically behind the
25scene as well?
26
27This library provides the means for this to happen in the form of two
28modules, basic-macros and procedural-macros. The latter exports the
29symbols of the former as well, so that in general only the latter is
30needed.
31
32=== Module basic-macros
33
34Usually an ambituous explicit renaming macro contains a long let
35defining the renamed symbols -- usually prefixed with some fixed symbol
36constant like % -- which is then executed in the macro's body by
37unquoting it. Our two macros create the let automatically. The only
38thing you have to do is providing a prefix and using it to prefix all
39symbols you want renamed resp injected.
40
41Here is a simple example, the numeric if.
42
43<enscript highlight=scheme>
44  (define-er-macro (nif form % compare?)
45    (bind (_ xpr pos zer neg) form
46      `(,%let ((,%result ,xpr))
47         (,%cond
48           ((,%positive? ,%result) ,pos)
49           ((,%negative? ,%result) ,neg)
50           (,%else ,zer)))))
51</enscript>
52
53Note, that one of the standard arguments of an er-macro-transformer,
54rename, is replaced by the rename-prefix %, which characterize the
55symbols in the body to be renamed.
56
57The macro searches its body for symbols starting with this prefix,
58collects them in a list, removes duplicates and adds the necesary let
59with pairs of the form
60
61  (%name (rename 'name)
62
63to the front of the body. In other words it does what you usually do by
64hand.
65
66For implicit renaming macros the list of injected symbols is usually,
67but not allways, short, even empty for nif. Of course, the generated let
68replaces rename with inject in this case.
69For example, here is a version of alambda, an anaphoric version of
70lambda, which injects the name self:
71
72<enscript highlight=scheme>
73  (define-ir-macro (alambda form % compare?)
74    (bind (_ args xpr . xprs) form
75      `(letrec ((,%self (lambda ,args ,xpr ,@xprs)))
76         ,%self)))
77</enscript>
78
79==== basic-macros
80
81<procedure>(basic-macros sym ..)</procedure>
82
83documentation procedure
84
85==== define-syntax-rule
86
87<macro>(define-syntax-rule (name . args) xpr . xprs)</macro>
88<macro>(define-syntax-rule (name . args) (keywords . keys) xpr .  xprs)</macro>
89
90simplyfied version of syntax-rules if there is only one rule.
91
92==== define-ir-macro-transformer
93
94<macro>(define-er-macro-transformer (name form inject compare?)</macro>
95
96wrapper around ir-macro-transformer.
97
98==== define-er-macro-transformer
99
100<macro>(define-er-macro-transformer (name form rename compare?)</macro>
101
102wrapper around er-macro-transformer.
103
104==== define-er-macro
105
106<macro>(define-er-macro (name form rename-symbol compare?) xpr . xprs)</macro>
107
108defines an explicit-renaming-macro name with macro-code form renaming
109each symbol in the body xpr . xprs starting with rename-symbol
110automatically.
111
112==== define-ir-macro
113
114<macro>(define-ir-macro (name form inject-symbol compare?) xpr . xprs)</macro>
115
116defines an implicit-renaming-macro name with macro-code form injecting
117each symbol in the body xpr . xprs starting with inject-symbol
118automatically.
119
120==== once-only
121
122<macro>(once-only (x . xs) xpr . xprs)</macro>
123
124to be used in a macro-body to avoid side-effects.
125The arguments x . xs are only evaluated once.
126once-only must be used for-syntax in explicit or implicit renaming
127macros.
128
129==== with-mapped-symbols
130
131<macro>(with-mapped-symbols mapper prefix- (prefix-x ...) xpr . xprs)</macro>
132
133binds a series of prefixed names, prefix-x ...
134to the images of the original names, x ..., under mapper
135and evaluates xpr . xprs in this context.
136To be used for-synax in ir- or er-macro-transformers, where mapper is
137either inject or rename.
138
139==== with-gensyms
140
141<macro>(with-gensyms (x ...) xpr ....)</macro>
142
143to be used in a macro body and hence to be imported for-syntax.
144Generates a list of gensyms x ... which can be used in xpr .....
145
146
147=== Module procedural-macros
148
149Combining implicit renaming with destructuring, some macro-writing
150macros are defined, in particular, a (mostly) hygienic procedural
151define-macro and a procedural version of syntax-rules, named
152macro-rules. The latter is almost as easy to use as syntax-rules, but
153much more powerfull. Here is its syntax
154
155<macro>(macro-rules sym ... (key ...) (pat (where fender ...) .. tpl) ....)</macro>
156
157Note the special use of dots here and below: Three dots are ellipses, as
158usual, i.e. the pattern on the left is repeated zero or more times, two
159dots, zero or one time, 4 dots one ore several times.
160
161This form can be used instead of syntax-rules in define-syntax,
162let-sytax and letrec-syntax, provided, you use it for-syntax.
163sym ... denote the injected symbols to break hygiene (if there is none,
164the constructed macro is hygienic). key ... and pat .... symbols are
165as in syntax-rules, fender ... are pairs of pattern variables and
166predicates, the latter applied to the former must be true for the
167pattern to match, and tpl .... are usually quasiquoted expressions.
168
169And here is the syntax of define-macro
170
171<macro>(define-macro (name . args) (where (x . xs) ...) .. xpr ....)</macro>
172
173The implementation of these macros depends on the bind-case macro of the
174basic-macros package which does the pattern matching of macro-rules. Since
175the former can handle wildcards, non-symbol literals and fenders, so
176does the latter.
177
178==== procedural-macros
179
180<procedure>(procedural-macros sym ..)</procedure>
181
182documentation procedure. Shows the exported symbols and the syntax of
183such an exported symbol, respectively.
184
185==== macro-rules
186
187<macro>(macro-rules sym ... (keyword ...) (pat (where fender ...) .. tpl) ....)</macro>
188
189like syntax-rules, but the templates are usually quasiquote-expressions.
190Moreover, the symbols sym ... are injected, if there are any.
191Here and in the sequel, fender is an expresseion of the form
192 (var ok?  ...)
193checking a pattern variable, var, against a sequence of predicates.
194
195Note, that non-symbol literals are accepted in each pat and considered a
196match if they are equal to the corresponding expression in the
197macro-code. The same applies to fenders: If they are not passed, the
198pattern is not matched.
199
200macro-rules must be used for-syntax if used in the preprocessing
201phase of a macro evaluation.
202
203==== define-macro
204
205<macro>(define-macro (name . args) (where (x . xs) ...) .. xpr ....))</macro>
206
207where xs is either a list of predicates, providing fenders,
208or a singleton with one of the symbols keyword or injection,
209providing keyword parameters or unhygienic macros.
210Generates a hygienic implicit-renaming macro, name, if no injection
211parameter is given.
212
213==== macro-let
214
215<macro>(macro-let (((name . args) (where fender ...) .. xpr ...) ...) body ....)</macro>
216
217evaluates body ... in the context of parallel hygienic macros name ....
218
219==== macro-letrec
220
221<macro>(macro-letrec (((name . args) (where fender ...) .. xpr ...) ...) body ....)</macro>
222
223evaluates body ... in the context of recursive hygienic macros name ....
224
225=== Reexports from basic-macros
226
227==== once-only
228
229<macro>(once-only (x ...)  body ....)</macro>
230
231to be used in a macro-body to avoid side-effects.
232The arguments x ... are only evaluated once.
233once-only must be imported for-syntax.
234
235==== define-ir-macro-transformer
236
237<macro>(define-er-macro-transformer (name form inject compare?)</macro>
238
239wrapper around ir-macro-transformer.
240
241==== define-er-macro-transformer
242
243<macro>(define-er-macro-transformer (name form rename compare?)</macro>
244
245wrapper around er-macro-transformer.
246
247==== define-er-macro
248
249<macro>(define-er-macro (name form rename-symbol compare?) xpr . xprs)</macro>
250
251defines an explicit-renaming-macro name with macro-code form renaming
252each symbol in the body xpr . xprs starting with rename-symbol
253automatically.
254
255==== define-ir-macro
256
257<macro>(define-ir-macro (name form inject-symbol compare?) xpr . xprs)</macro>
258
259defines an implicit-renaming-macro name with macro-code form injecting
260each symbol in the body xpr . xprs starting with inject-symbol
261automatically.
262
263==== with-mapped-symbols
264
265<macro>(with-mapped-symbols mapper prefix- (prefix-x ....) xpr ....)</macro>
266
267binds a series of prefixed names, prefix-x ....
268to the images of the original names, x ...., under mapper
269and evaluates xpr .... in this context
270
271==== with-gensyms
272
273<macro>(with-gensyms (x ...) xpr ....)</macro>
274
275to be used in a macro body and hence to be imported for-syntax.
276Generates a list of gensyms x ... which can be used in xpr .....
277
278
279=== Requirements
280
281bindings
282
283=== Usage
284
285<enscript highlight=scheme>
286
287(import procedural-macros)
288
289(import-for-syntax
290 (only procedural-macros macro-rules once-only
291                         with-mapped-symbols with-gensyms)
292
293</enscript>
294
295=== Examples
296
297<enscript highlight=scheme>
298
299(import procedural-macros)
300(import-for-syntax
301  (only bindings bind bind-case)
302  (only procedural-macros macro-rules with-mapped-symbols once-only)
303  (only (chicken base) list-of?))
304
305(define-er-macro (Square form % compare?)
306  (let ((x (cadr form)))
307    (once-only (x)
308      `(* ,x ,x))))
309
310(define-er-macro-transformer (Swap! form rename compare?)
311  (let ((x (cadr form)) (y (caddr form)))
312    (with-mapped-symbols rename % (%tmp %let %set!)
313      `(,%let ((,%tmp ,x))
314         (,%set! ,x ,y)
315         (,%set! ,y ,%tmp)))))
316
317(define-er-macro (Nif form % compare?)
318  (bind (_ xpr pos zer neg)
319    form
320    `(,%let ((,%result ,xpr))
321            (,%cond
322              ((,%positive? ,%result) ,pos)
323              ((,%negative? ,%result) ,neg)
324              (,%else ,zer)))))
325
326(define-ir-macro (Vif form % compare?)
327  (bind-case form
328    ((_ test (key xpr . xprs))
329     (cond
330       ((compare? key %then)
331        `(if ,test (begin ,xpr ,@xprs)))
332       ((compare? key %else)
333        `(if ,(not test) (begin ,xpr ,@xprs)))
334       (else
335         `(error 'Vif "syntax-error"))))
336    ((_ test (key1 xpr . xprs) (key2 ypr . yprs))
337     (cond
338       ((and (compare? key1 %then)
339             (compare? key2 %else))
340       `(if ,test
341          (begin ,xpr ,@xprs)
342          (begin ,ypr ,@yprs)))
343       ((and (compare? key1 %else)
344             (compare? key2 %then))
345       `(if ,test
346          (begin ,ypr ,@yprs)
347          (begin ,xpr ,@xprs)))
348       (else
349         `(error 'Vif "syntax-error"))))
350    ))
351
352;; two anaphoric macros
353(define-syntax aif
354  (macro-rules it ()
355    ((_ test consequent)
356     `(let ((,it ,test))
357        (if ,it ,consequent)))
358    ((_ test consequent alternative)
359     `(let ((,it ,test))
360        (if ,it ,consequent ,alternative)))))
361
362(define-macro (alambda args xpr . xprs)
363  (self injection)
364  `(letrec ((,self (lambda ,args ,xpr ,@xprs)))
365     ,self))
366
367;; effective membership testing
368(define-macro (in? what equ? . choices)
369  (let ((insym 'in))
370    `(let ((,insym ,what))
371       (or ,@(map (lambda (choice) `(,equ? ,insym ,choice))
372                  choices)))))
373
374;; verbose if
375(define-syntax vif
376  (macro-rules (then else)
377    ((_ test (then xpr . xprs))
378     `(if ,test
379        (begin ,xpr ,@xprs)))
380    ((_ test (else xpr . xprs))
381     `(if ,(not test)
382        (begin ,xpr ,@xprs)))
383    ((_ test (then xpr . xprs) (else ypr . yprs))
384     `(if ,test
385        (begin ,xpr ,@xprs)
386        (begin ,ypr ,@yprs)))))
387
388;; procedural version of cond
389(define-syntax my-cond
390  (macro-rules (else =>)
391    ((_ (else xpr . xprs))
392     `(begin ,xpr ,@xprs))
393    ((_ (test => xpr))
394     (let ((temp test))
395       `(if ,temp (,xpr ,temp))))
396    ((_ (test => xpr) . clauses)
397     (let ((temp test))
398       `(if ,temp
399          (,xpr ,temp)
400          (my-cond ,@clauses))))
401    ((_ (test)) `(if #f #f))
402    ((_ (test) . clauses)
403     (let ((temp test))
404       `(if ,temp
405          ,temp
406          (my-cond ,@clauses))))
407    ((_ (test xpr . xprs))
408     `(if ,test (begin ,xpr ,@xprs)))
409    ((_ (test xpr . xprs) . clauses)
410     `(if ,test
411        (begin ,xpr ,@xprs)
412        (my-cond ,@clauses)))))
413
414;; procedural version of letrec
415(define-macro (my-letrec var-val-pairs . body)
416  (where (var-val-pairs (list-of? pair?)))
417  (let ((vars (map car var-val-pairs))
418        (vals (map cadr var-val-pairs))
419        (aux (map (lambda (x) (gensym)) var-val-pairs)))
420    `(let ,(map (lambda (var) `(,var #f)) vars)
421       (let ,(map (lambda (a v) `(,a ,v)) aux vals)
422         ,@(map (lambda (v e) `(set! ,v ,e)) vars vals)
423         ,@body))))
424
425(my-letrec ((o? (lambda (m) (if (zero? m) #f (e? (- m 1)))))
426            (e? (lambda (n) (if (zero? n) #t (o? (- n 1))))))
427           (list (o? 95) (e? 95)))
428
429;; local macros
430(letrec-syntax (
431     (sec (macro-rules ()
432               ((_ lst) `(car (res ,lst)))))
433     (res (macro-rules ()
434             ((_ lst) `(cdr ,lst))))
435     )
436     (sec '(1 2 3)))
437;-> 2
438
439(macro-letrec (
440     ((sec lst) `(car (res ,lst)))
441     ((res lst) `(cdr ,lst))
442     )
443     (sec '(1 2 3)))
444;-> 2
445
446(macro-let (
447     ((fir lst) (where (lst list?)) `(car ,lst))
448     ((res lst) (where (lst list?)) `(cdr ,lst))
449     )
450     (fir (res '(1 2 3))))
451;-> 2
452
453;; non-symbolic literals
454(define-syntax foo
455  (macro-rules ()
456    ((_ "foo" x) x)
457    ((_ #f x) x)
458    ((_ a b) (where (a string?)) `(list ,a ,b))
459    ((_ a b) (where (a odd?)) `(list ,a ,b))
460    ((_ a b) a)))
461(foo "foo" 1)
462; -> 1
463(foo "bar" 2)
464; -> '("bar" 2)
465(foo #f 'blabla)
466; -> 'blabla
467(foo 1 2)
468; -> '(1 2)
469(foo 2 3)
470; -> 2
471
472(define-syntax add
473  (macro-rules ()
474    ((_ x y) (where (x string?) (y string?))
475     `(string-append ,x ,y))
476    (( _ x y) (where (x integer?) (y integer?))
477     `(+ ,x ,y))))
478(add 1 2)
479;-> 3
480(add "x" "y")
481;-> "xy"
482</enscript>
483
484== Last update
485
486Mar 20, 2019
487
488== Author
489
490[[/users/juergen-lorenz|Juergen Lorenz]]
491
492== License
493
494 Copyright (c) 2015-2019, Juergen Lorenz
495 All rights reserved.
496
497 Redistribution and use in source and binary forms, with or without
498 modification, are permitted provided that the following conditions are
499 met:
500 
501 Redistributions of source code must retain the above copyright
502 notice, this list of conditions and the following disclaimer.
503 
504 Redistributions in binary form must reproduce the above copyright
505 notice, this list of conditions and the following disclaimer in the
506 documentation and/or other materials provided with the distribution.
507 Neither the name of the author nor the names of its contributors may be
508 used to endorse or promote products derived from this software without
509 specific prior written permission.
510   
511 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
512 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
513 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
514 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
515 HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
516 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
517 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
518 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
519 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
520 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
521 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
522
523== Version History
524
525; 1.0 : port from chicken-4 procedural- and basic-macros
Note: See TracBrowser for help on using the repository browser.