Changeset 30129 in project


Ignore:
Timestamp:
11/27/13 13:38:25 (6 years ago)
Author:
juergen
Message:

low-level-version 2.0

File:
1 edited

Legend:

Unmodified
Added
Removed
  • wiki/eggref/4/low-level-macros

    r25447 r30129  
    44== Low-level macros made easy
    55
    6 This module contains some macros to make the use of low-level macros easier. It replaces the now obsolete modules er-macros and ir-macros.
    7 
    8 Recall that those macros are implemented as transformer routines, which are three-parameter procedures enclosed in er-macro-transformer or ir-macro-transformer respectively
     6This module contains some macros to make the use of low-level macros
     7easier. It replaces the now obsolete modules er-macros and ir-macros.
     8
     9Recall that low-level macros are implemented as transformer routines,
     10which are three-parameter procedures enclosed in er-macro-transformer or
     11ir-macro-transformer respectively
    912
    1013<enscript highlight=scheme>
     
    1518</enscript>
    1619
    17 The programmer's job is to destructure the macro-code, form, and to do the renaming of all symbols which should appear in the macro-expansion by hand in the explicit-renaming case or to inject those symbols, which should not be renamed in the implicit-renaming case. In any case, symbols which are not renamed are unhygienic.
    18 
    19 The job of destructuring the macro-code is tedious and error prone, it can be done by a tool, bind from the contracts module, for example. It is reproduced here but not exported to make the module self-contained.  Based on that we will implement macros er-macro-rules and ir-macro-rules here, which mimic syntax-rules by design.
    20 
    21 The other job, compare? literal symbols supplied by the macro's client with renamed ones, something which is needed to use symbols like else and => in the cond macro, remains to be done. Hence er-macro-rules and ir-macros are unhygienic by design.  They export compare?  to their local scopes. Moreover, ir-macro-rules has to export inject to its local scope in case it is to produce new symbols - think about structure definitions, for example - which must not be renamed, off course.
    22 
    23 == Important note
    24 
    25 All macros in this module except with-aliases are unhygienic by design.  They pollute the local (!) namespace with the symbol compare? (and inject in the implicit renaming case). But the macros implemented with those macros can be - and are in most cases - hygienic.
     20The programmer's job is to destructure the macro-code, form, and to do
     21the renaming of all symbols which should appear in the macro-expansion
     22by hand in the explicit-renaming case or to inject those symbols, which
     23should not be renamed in the implicit-renaming case. In any case,
     24symbols which are not renamed are unhygienic. The third parameter allows
     25to handle additional keywords.
     26
     27Each of these transformer arguments does a special job, each of which is
     28tedious and error-prone. In this module, we'll automate each of these
     29jobs.
     30
     31Let's start with destructuring the macro-code. It can be done by a tool,
     32bind from the bindings  module, for example. Renaming or injecting can
     33be done by supplying a special prefix, % in most cases. Symbols with
     34this prefix can be extracted from the macro-body and transformed into a
     35let which defines the necessary symbols. Last but not least, additional
     36keywords can be extracted from the macro body as well and transformed
     37into a where clause of the bind macro. This way, the transformer
     38disappears completely on the surface, but ,of course, happens to do the
     39job behind the scene.
     40
     41There is a helper module, symbols, which provides some routines which
     42are used in low-level-macros, but might be of separate interest.
    2643
    2744== Programming interface
    2845
    29 === low-level-macros
    30 
    31 <procedure>(low-level-macros sym)</procedure>
    32 
    33 where sym is optional.
    34 
    35 This is the documentation dispatcher. Without a symbol, it lists all exported symbols of the module, with one of these symbols it prints the documentation of that symbol.
    36 
    37 === er-macro-rules
    38 
    39 <syntax>(er-macro-rules (%sym ...) (code0 xpr0) (code1 xpr1) ...)</syntax>
    40 
    41 references a renamed version of sym ... under the name %sym ... and pairs the differnt macro-codes code0 code1 ... with expressions xpr0 xpr1 ..., which usually evalute to backquoted templates.
    42 
    43 This macro is unhygienic by design, it introduces the symbol compare?  into its scope.
    44 
    45 === ir-macro-rules
    46 
    47 <syntax>(ir-macro-rules (sym ...) (code0 xpr0) (code1 xpr1) ...)</syntax>
    48 
    49 pairs the differnt macro-codes code0 code1 ... with expressions xpr0 xpr1 ..., which usually evalute to backquoted templates in the scope of injected symbols sym ....
    50 
    51 This macro is unhygienic by design, it introduces the two symbols inject and compare? into its scope.
    52 
    53 Unhygienic macros can not be implemented with syntax-rules, so we must use one of the low-level transformers for the implementation.
    54 
    55 Based on er-macro-rules and ir-macro-rules, it's easy to implement three macros, define-macro, macro-let and macro-letrec, which facilitate the implementation of low-level macros even more: We simply match one pattern, the macro code, against a list of the form
    56 
    57   (with-renamed (%sym ...) xpr . xprs)
    58 
    59 where %sym ... are aliases of sym ... in the explicit-renaming case, or
    60 
    61   (with-injected (sym ...) xpr . xprs)
    62 
    63 where sym ... are unhygienic symbols in the implicit-renaming case  and the sequence xpr . xprs produce the macro-expansion. Since the list of injected symbols is empty in most cases, the wrapper (with-injected () ...) kann be omitted. If that is the case, implicit-renaming is used as default.
    64 
    65 
    66 === define-macro
    67 
    68 <syntax>(define-macro code (with-renamed (%sym ...) xpr . xprs))</syntax>
    69 
    70 <syntax>(define-macro code (with-injected (sym ...) xpr . xprs))</syntax>
    71 
    72 <syntax>(define-macro code xpr . xprs))</syntax>
    73 
    74 where code is the complete macro-code (name . args), i.e. the pattern of a macro call, and the rest is explained above.
    75 
    76 macro-let and macro-letrec are local versions of define-macro, where the local macros are evaluated in parallel or recursively.
    77 
    78 === macro-let
    79 
    80 <syntax>(macro-let ((code0 clause0) (code1 clause1)...) xpr . xprs)</syntax>
    81 
    82 where code0, code1  are as in define-macro and each clause is one of
    83 
    84 <enscript highlight=scheme>
    85   (with-renamed (%sym ...) xpr . xprs)
    86 
    87   (with-injected (sym ...) xpr . xprs)
    88 
    89   xpr . xprs
    90 </enscript>
    91 
    92 This is a local version of define-macro, allowing a list of (code clause) lists to be processed in xpr . xprs in parallel.
    93 
    94 === macro-letrec
    95 
    96 <syntax>(macro-letrec ((code0 clause0) (code1 clause1)...) xpr . xprs)</syntax>
    97 
    98 where code0, code1  are as in define-macro and each clause is one of
    99 
    100 <enscript highlight=scheme>
    101   (with-renamed (%sym ...) xpr . xprs)
    102 
    103   (with-injected (sym ...) xpr . xprs)
    104 
    105   xpr . xprs
    106 </enscript>
    107 
    108 This is a local version of define-macro, allowing a list of (code clause) lists to be processed in xpr . xprs recursively.
    109 
    110 === with-aliases
    111 
    112 <syntax>(with-aliases (op %sym0 %sym1 ...) xpr . xprs)</syntax>
    113 
    114 binds each %sym0 %sym1 ... to (op sym0) (op sym1) ... and evaluates xpr . xprs in this context. Usually used in explicit-renaming macros where op is rename.
     46=== The symbols module
     47
     48==== symbols
     49
     50<procedure>(symbols)</procedure>
     51
     52shows which symbols are exported.
     53
     54==== prefixed-with?
     55
     56<procedure>(prefixed-with? pre)</procedure>
     57
     58returns a predicate, which checks, if pre is a prefix of its argument.
     59
     60==== strip-prefix
     61
     62<procedure>(strip-prefix pre id)</procedure>
     63
     64strips the prefix pre from the identifier id.
     65
     66==== strip-suffix
     67
     68<procedure>(strip-suffix suf id)</procedure>
     69
     70strips the suffix suf from the identifier id.
     71
     72==== extract
     73
     74<procedure>(extract ok? tree)</procedure>
     75
     76returns a flat list of all the symbols in a tree which pass the ok?
     77test.
     78
     79==== remove-duplicates
     80
     81<procedure>(remove-duplicates lst)</procedure>
     82
     83returns a sublist of lst with dups removed.
     84
     85==== adjoin
     86
     87<procedure>(adjoin obj lst)</procedure>
     88
     89conses obj to lst provided it isn't already there.
     90
     91==== memp
     92
     93<procedure>(memp ok? lst)</procedure>
     94
     95returns the tail of lst, whose car passes ok?, or #f otherwise.
     96
     97==== filter
     98
     99<procedure>(filter ok? lst)</procedure>
     100
     101returns a sublist of lst where each item passes ok?
     102
     103==== flatten
     104
     105<procedure>(flatten tree)</procedure>
     106
     107returns a flat list with all the items of tree.
     108
     109=== The module low-level-macros
     110
     111==== low-level-macros
     112
     113<procedure>(low-level-macros)</procedure>
     114
     115returns a list of all the exported symbols of the module.
     116
     117==== define-macro
     118
     119<macro>(define-macro (name . args) xpr . xprs))</macro>
     120<macro>(define-macro (name . args) (with-rename-prefix % xpr . xprs))</macro>
     121<macro>(define-macro (name . args) (with-inject-prefix % xpr . xprs))</macro>
     122<macro>(define-macro (name . args) (with-keywords (x . xs) xpr . xprs))</macro>
     123<macro>(define-macro (name . args) (with-rename-prefix % (with-keywords (x . xs) xpr . xprs)))</macro>
     124<macro>(define-macro (name . args) (with-inject-prefix % (with-keywords (x . xs) xpr . xprs)))</macro>
     125
     126generates a macro name, which is of type explicit-renaming if
     127with-rename-prefix is supplied or implicit-renaming otherwise. Keywords
     128and prefixed symbols are extracted from the macro body transformed into
     129appropriate subexpressions of the macro-transformer.
     130
     131
     132let-macro and letrec-macro are local versions of define-macro, where the local macros
     133are evaluated in parallel or recursively.
     134
     135==== let-macro
     136
     137<macro>(let-macro ((code0 clause0) (code1 clause1)...) xpr . xprs)</macro>
     138
     139where (code0 clause0), (code1 clause0), ...  are as in define-macro.
     140
     141This is a local version of define-macro, allowing a list of (code clause)
     142lists to be processed in xpr . xprs in parallel.
     143
     144==== letrec-macro
     145
     146<macro>(letre-macro ((code0 clause0) (code1 clause1)...) xpr . xprs)</macro>
     147
     148where (code0 clause0), (code1 clause0), ...  are as in define-macro.
     149
     150This is a local version of define-macro, allowing a list of (code clause)
     151lists to be processed in xpr . xprs recursively.
     152
     153==== macro-rules
     154
     155<macro>(macro-rules sym ... (keyword ...) (pat0 tpl0) (pat1 tpl1) ...)</macro>
     156
     157like syntax-rules, but the templates are usually quasiquote-expressions.
     158Moreover, the symbols sym ... are injected, if there are any.
     159
     160==== once-only
     161
     162<macro>(once-only (x ...) . body)</macro>
     163
     164to be used in a macro-body to avoid side-effects.
     165The arguments x ... are only evaluated once.
     166
     167
     168==== with-gensyms
     169
     170<macro>(with-gensyms (x ...) . body)</macro>
     171
     172to be used in a macro body. Generates a list of gensyms x ...
     173
     174==== define-syntax-rule
     175
     176<macro>(define-syntax-rule (name . args) tpl)</macro>
     177
     178the only high-level macro. To be used instead of syntax-rules in case
     179there is only one rule and no additional keywords
    115180
    116181== Requires
    117182
    118 Nothing
     183bindings
    119184
    120185== Usage
     
    122187<enscript highlight=scheme>
    123188(use low-level-macros)
    124 (import-for-syntax (only low-level-macros ir-macro-rules er-macro-rules))
     189(import-for-syntax (only low-level-macros macro-rules))
    125190</enscript>
    126191
     
    129194<enscript highlight=scheme>
    130195(use low-level-macros)
    131 (import-for-syntax (only low-level-macros ir-macro-rules er-macro-rules))
    132 
     196(import-for-syntax (only low-level-macros macro-rules))
     197
     198;; two anaphoric macros
    133199(define-syntax aif
    134   (er-macro-rules (%let %if)
    135     ((_ test then)
    136      `(,%let ((it ,test))
    137         (,%if it ,then)))
    138     ((_ test then else)
    139      `(,%let ((it ,test))
    140         (,%if it ,then ,else)))))
    141 
    142 (define-syntax acond
    143   (ir-macro-rules (it)
    144     ((_ (test . xprs))
    145      (if (compare? test 'else)
    146        `(begin ,@xprs)
    147        `(let ((,it ,test))
    148           (if ,it
    149             (begin ,@xprs)
    150             (error 'acond "no test succeeds")))))
    151     ((_ (test . xprs) (test1 . xprs1) . clauses)
    152      `(let ((,it ,test))
    153         (if ,it
    154           (begin ,@xprs)
    155           (acond (,test1 ,@xprs1) ,@clauses))))))
    156 
    157 ;; a variant of or
    158 (define-macro (my-or . args)
    159   (with-renamed (%if %my-or)
    160     (if (null? args)
    161       #f
    162       (let ((tmp (car args)))
    163         `(,%if ,tmp ,tmp (,%my-or ,@(cdr args)))))))
    164 
    165 ;; anaphoric when, can refer to it
    166 (define-macro (awhen test xpr . xprs)
    167   (with-injected (it)
    168     `(let ((,it ,test))
    169        (if ,it (begin ,xpr ,@xprs)))))
    170 
    171 ;; anaphoric and, each arg can refer to its precursor via it
    172 (define-macro (aand . args)
    173   (with-injected (it)
    174    (let loop ((args args))
    175      (cond
    176        ((null? args) #t)
    177        ((null? (cdr args)) (car args))
    178        (else
    179          `(let ((,it ,(car args)))
    180             (if ,it
    181               ,(loop (cdr args)))))))))
    182 
    183 
    184 ;; anaphoric while, which can reference the result of each ok? with it
    185 (define-macro (awhile ok? xpr . xprs)
    186   (with-renamed (%let %loop)
    187    `(,%let ,%loop ((it ,ok?))
    188       (when it
    189         ,xpr ,@xprs
    190         (,%loop ,ok?)))))
    191 
    192 ;; anaphoric lambda, can refer to itself via self
     200        (macro-rules it ()
     201                ((_ test consequent)
     202                 `(let ((,it ,test))
     203                                (if ,it ,consequent)))
     204                ((_ test consequent alternative)
     205                 `(let ((,it ,test))
     206                                (if ,it ,consequent ,alternative)))))
     207
    193208(define-macro (alambda args xpr . xprs)
    194   (with-injected (self)
    195    `(letrec ((,self (lambda ,args ,xpr ,@xprs)))
    196       ,self)))
    197 
    198 (let ((f (lambda (n) (+ n 10))))
    199         (macro-let (
    200                 ((f n) (with-renamed ()  n))
    201                 ((g n) (with-renamed (%f) `(,%f ,n)))
    202                 )
    203                 (list (f 1) (g 1)))) ; -> (1 11)
    204 
    205 (let ((f (lambda (n) (+ n 10))))
    206         (macro-letrec (
    207                 ((f n) n)
    208                 ((g n) `(f ,n)))
    209                 )
    210                 (list (f 1) (g 1)))) ; -> (1 1)
    211 
    212 (macro-letrec (
    213         ((aif test then)
    214          (with-renamed (%let %if) `(,%let ((it ,test)) (,%if it ,then))))
    215         )
    216         (aif (memv 2 '(1 2 3)) it)) ; -> '(2 3)
    217 
    218 (macro-let (
    219         ((aif test then) `(let ((it ,test)) (if it ,then)))
    220         )
    221         (aif (memv 2 '(1 2 3)) it)) ; -> '(2 3)
    222 
    223 (map (alambda (n) (if (zero? n) 1 (* n (self (- n 1)))))
    224                  '(1 2 3 4 5)) ; -> '(1 2 6 24 120)
    225 
    226 (let ((lst '(0 1 2 3))) (aand lst (cdr it) (cdr it))) ; -> '(2 3)
    227 
    228 (let ((lst '(0 1 2 3)))
    229         (acond ((memv 5 lst) it) ((memv 2 lst) it) (else it))) ; -> '(2 3)
    230 
    231 (let ((lst '(0 1 2 3))) (aif (memv 2 lst) it #f)) ; -> '(2 3)
    232 
    233 (let ((lst '(0 1 2 3))) (awhen (memv 2 lst) (reverse it))) ; -> '(3 2)
    234 
    235 (let ((lst '(0 1 2 3)) (acc '()))
    236         (awhile lst
    237                 (if (null? lst)
    238                         (set! lst #f)
    239                         (begin
    240                                 (set!  acc (cons (car lst) acc))
    241                                 (set! lst (cdr lst)))))
    242         acc) ; -> '(3 2 1 0)
     209        (with-inject-prefix %
     210                `(letrec ((,%self (lambda ,args ,xpr ,@xprs)))
     211                         ,%self)))
     212
     213;; two versions of a verbose if
     214(define-macro (verbose-if test (then . xprs) (else . yprs))
     215        (with-rename-prefix %
     216                (with-keywords (then else)
     217                        `(,%if ,test
     218                                 (,%begin ,@xprs)
     219                                 (,%begin ,@yprs)))))
     220
     221(define-syntax vif
     222        (macro-rules (then else)
     223                ((_ test (then xpr . xprs))
     224                 `(if ,test
     225                                (begin ,xpr ,@xprs)))
     226                ((_ test (else xpr . xprs))
     227                 `(if ,(not test)
     228                                (begin ,xpr ,@xprs)))
     229                ((_ test (then xpr . xprs) (else ypr . yprs))
     230                 `(if ,test
     231                                (begin ,xpr ,@xprs)
     232                                (begin ,ypr ,@yprs)))))
     233
     234;; low-level version of cond
     235(define-syntax my-cond
     236        (macro-rules (else =>)
     237                ((_ (else xpr . xprs))
     238                 `(begin ,xpr ,@xprs))
     239                ((_ (test => xpr))
     240                 (let ((temp test))
     241                         `(if ,temp (,xpr ,temp))))
     242                ((_ (test => xpr) . clauses)
     243                 (let ((temp test))
     244                         `(if ,temp
     245                                        (,xpr ,temp)
     246                                        (my-cond ,@clauses))))
     247                ((_ (test)) test)
     248                ((_ (test) . clauses)
     249                 (let ((temp test))
     250                         `(if ,temp
     251                                        ,temp
     252                                        (my-cond ,@clauses))))
     253                ((_ (test xpr . xprs))
     254                 `(if ,test (begin ,xpr ,@xprs)))
     255                ((_ (test xpr . xprs) . clauses)
     256                 `(if ,test
     257                                (begin ,xpr ,@xprs)
     258                                (my-cond ,@clauses)))))
    243259
    244260</enscript>
     
    250266== License
    251267
    252  Copyright (c) 2011, Juergen Lorenz
     268 Copyright (c) 2011-2013, Juergen Lorenz
    253269 All rights reserved.
    254270
     
    261277== Last update
    262278
    263 Sep 13, 2011
     279Nov 27, 2013
    264280
    265281== Version History
    266282
     283; 2.0 : complete rewrite
    267284; 1.2 : renamed macro-define to define-macro
    268285; 1.1 : fixed low-level-macros.meta and low-level-macros.setup
Note: See TracChangeset for help on using the changeset viewer.