- To be able to write macros within a module which I can use while compiling code in that module or other modules which import the macro-module.
- To be able to write macros which are imported into an repl which incorporates the module in which they are written.
The solution is to write modules with macros like this:
(module with-gensyms
(eval (export-exports))
(export (with-gensyms-expander x e)))
(define (with-gensyms-expander x e)
(match-case x
((with-gensyms (?sym) . ?body)
(e `(let ((,sym (gensym)))
,@body) e))
((with-gensyms (?sym1 . ?rest) . ?body)
(e `(let ((,sym1 (gensym)))
(with-gensyms ,rest ,@body)) e))))
(eval '(define-expander with-gensyms with-gensyms-expander))
And use them in other modules like this:
(module f64vector
(eval (export-all))
(import (with-gensyms "with-gensyms.scm")
(do-macros "do-macros.scm"))
(load (with-gensyms "with-gensyms.scm")
(do-macros "do-macros.scm"))
(type (tvector f64vector (double)))
(export (f64vector::f64vector . inits)
(with-f64vectors-expander x e)))
(define (f64vector::f64vector . inits)
(let* ((n (length inits))
(v (make-f64vector n 0.0)))
(let loop ((i 0) (list inits))
(if (null? list)
v
(begin
(f64vector-set! v i (car list))
(loop (+fx i 1) (cdr list)))))))
(define (with-f64vectors-expander x e)
(match-case x
((with-f64vectors ?vecs (<- . ?body))
(with-gensyms (result n i)
(e `(let* ((,n (f64vector-length ,(car vecs)))
(,result (make-f64vector ,n 0.0)))
(do-times (,i ,n)
(let ,(map (lambda (sym)
`(,sym (f64vector-ref ,sym ,i)))
vecs)
(f64vector-set! ,result ,i
(begin ,@body))))
,result) e)))
((with-f64vectors ?vecs . ?body)
(with-gensyms (i n)
(let* ((vector-syms (map (lambda (sym) (cons sym (gensym))) vecs))
(process-body-term
(lambda (term)
(match-case term
((?result <- . ?body)
`(f64vector-set! ,(cdr (assoc result vector-syms))
,i
(begin ,@body)))
(?- term)))))
(e `(let ((,n (f64vector-length ,(car vecs))))
(let ,(map (lambda (sym)
`(,(cdr (assoc sym vector-syms)) ,sym))
vecs)
(do-times (,i ,n)
(let ,(map (lambda (sym)
`(,sym (f64vector-ref ,sym ,i)))
vecs)
,@(map process-body-term body))))) e))))))
(eval '(define-expander with-f64vectors with-f64vectors-expander))
(Note that this module uses two macro-modules---one of which is not listed above---to define a third. I'm sorry for the confusing example, but it's what I have handy.)
How does this work? Here's my understanding:
- The compiler begins processing the with-gensyms.scm file. The compiler compiles it into with-gensyms.o which contains a module initialization routine that evaluates all the top-level commands in the module whenever the module is initialized---this ensures that the (eval '(define-expander with-gensyms ...)) runs whenever the module is initialized in compiled code (i.e. when running a custom repl).
- The compiler processes f64vector.scm. It sees the (load (with-gensyms ...)) command in the module header, and interprets the file with-gensyms.scm, installing the with-gensyms macro before processing the code in f64vector.scm. The f64vector.scm file compiles into f64vector.o which contains a module initialization routine which will initialize the with-gensyms module first (because bigloo sees that it is required by a (import (with-gensyms ...)) in the f64vector module header) whenever the f64vector module is required from compiled code (i.e. within the main routine which implements the repl).
(module repl
(import (with-gensyms "with-gensyms.scm")
(do-macros "do-macros.scm")
(f64vector "f64vector.scm"))
(main main))
(define (main argv)
(repl))
It took me a long enough time to figure this out (and I wouldn't have figured it out without some suggestions from jg malecki (see this message and its antecedents---thanks jg) that I thought I should post the explanation here so that other people don't have to go through the same difficulties that I have.
No comments:
Post a Comment