The manual says to use pp as a macro expander:
(define-macro (push var #!optional val)
`(set! ,var (cons ,val ,var)))
(pp (lambda () (push stack 1) (push stack) (push stack 3)))
(lambda () (set! stack (cons 1 stack)) (set! stack (cons #f stack)) (set! stack (cons 3 stack)))
Since I got tired of the extra writing and the lambda in the output, I wrote my own macro-expand macro based on the pp example from the manual:
;; Expand one a gambit-c macro form. (define-macro (macro-expand mac) (let ((port (gensym)) (form (gensym))) `(let ((,port (open-string))) (pp (lambda () ,mac) ,port) (let ((,form (read ,port))) (caddr ,form)))))
It works as I expect:
(macro-expand (macro-expand 0))
(let ((#:g2 (open-string))) (pp (lambda () 0) #:g2) (let ((#:g3 (read #:g2))) (caddr #:g3)))
but it strikes me as a bit inelegant.
Is there a better solution? Is there a way to read from pp without creating a port first? I played around with with-output-to-string first but that just gave me a headache.
/Joel
Afficher les réponses par date
Joel Borggrén-Franck wrote:
Is there a way to read from pp without creating a port first?
(##decompile (lambda (y) (* y y)))
(lambda (y) (* y y))
(caddr (##decompile (lambda (y) (* y y))))
(* y y)
BTW be careful: macros using (begin ..) to output multiple forms will not give what you want since the current interpreter strips the begin during parsing time:
(caddr (##decompile (lambda () (begin (form1) (* y y)))))
(form1)
So you want something like:
(define (macro-expand-decompile-thunk thunk) (let ((code (cddr
(##decompile thunk)))) (if (null? (cdr code)) (car code) `(begin ,@code))))
(macro-expand-decompile-thunk (lambda () (* y y)))
(* y y)
(macro-expand-decompile-thunk (lambda () (begin (form1) (* y y))))
(begin (form1) (* y y))
I played around with with-output-to-string first but that just gave me a headache.
pp doesn't print to current-output-port by default.
(with-output-to-string "" (lambda () (pp "fun")))
"fun" ""
(with-output-to-string "" (lambda () (pp "fun" (current-output-port))))
""fun"\n"
Christian.
I wrote:
BTW be careful: macros using (begin ..) to output multiple forms will not give what you want since the current interpreter strips the begin during parsing time:
I forgot that the macro could also have output nothing, i.e. (begin). This should be correct in all(tm) circumstances:
(define (macro-expand-decompile-thunk thunk+) (let* ((code (cddr (##decompile thunk+))) (len (length code)) ;; strip last faked element: (code (take code (- len 1)))) (cond ((null? code) '(begin)) ((null? (cdr code)) (car code)) (else `(begin ,@code)))))
(define-macro (macro-expand syntax) `(cj-macro-util#macro-expand-decompile-thunk (lambda () ,syntax (fake-form))))
It requires the srfi-1 take function and expects that this code is put into the cj-macro-util# namespace.
Christian.
On Sun, Mar 9, 2008 at 12:37 AM, Christian Jaeger christian@pflanze.mine.nu wrote:
I wrote:
BTW be careful: macros using (begin ..) to output multiple forms will not give what you want since the current interpreter strips the begin during parsing time:
I forgot that the macro could also have output nothing, i.e. (begin). This should be correct in all(tm) circumstances:
Is this possible? '(begin)' is as far as I can tell not a valid form in r5rs? Both 'lambda' and 'begin' requires a '<sequence>' which requires at least one '<expression>' which at a glance can't be empty.
Joel Borggrén-Franck wrote:
Is this possible? '(begin)' is as far as I can tell not a valid form in r5rs? Both 'lambda' and 'begin' requires a '<sequence>' which requires at least one '<expression>' which at a glance can't be empty.
I'm too lazy to check any *r*rs right now, but I think it would only be natural / consistent to allow empty begin. That's not 'logically' the same as an empty lambda, i.e. it's reasonable that it depends on the environment where you put an empty begin form, and not on the begin form itself, whether it is valid. (Note that I think "begin" is rather just meant as a list of forms to be merged into the environment. Calling it a sequencing form could be called wrong, since it's the lambda around it which does the sequencing, not the begin itself. Anyway that's how I see it being implemented in Gambit (4.0 beta 21 anyway) and it does make sense to me.)
In fact Gambit doesn't allow empty lambdas, which is why I put the fake form there.
Christian.
On Sun, Mar 9, 2008 at 12:10 AM, Christian Jaeger christian@pflanze.mine.nu wrote:
Joel Borggrén-Franck wrote:
Is there a way to read from pp without creating a port first?
(##decompile (lambda (y) (* y y)))
(lambda (y) (* y y))
(caddr (##decompile (lambda (y) (* y y))))
(* y y)
Neat. But since ##decompile segfaults on me I think I'll stick to my macro-expand. jbf@deep:~$ gsi Gambit v4.2.2
(macro-expand (car '(a b)))
(car '(a b))
(##decompile (car '(a b)))
Segmentation fault (core dumped) jbf@deep:~$
I'll look in to this and make a bugzilla report RSN.
BTW be careful: macros using (begin ..) to output multiple forms will not give what you want since the current interpreter strips the begin during parsing time:
Thx for the heads up.
On Mon, Mar 10, 2008 at 3:03 PM, Joel Borggrén-Franck joel.borggren.franck@gmail.com wrote:
On Sun, Mar 9, 2008 at 12:10 AM, Christian Jaeger christian@pflanze.mine.nu wrote:
BTW be careful: macros using (begin ..) to output multiple forms will not give what you want since the current interpreter strips the begin during parsing time:
Thx for the heads up.
First i tried:
(define-macro (macro-expand mac) (let ((port (gensym)) (form (gensym)) (code (gensym))) `(let ((,port (open-string))) (pp (lambda () ,mac) ,port) (let* ((,form (read ,port)) (,code (cddr ,form))) (cond ((null? ,code) '(begin)) ((null? (cdr ,code)) (car ,code)) (else `(begin ,@,code)))))))
It works well. Then I decided to put it all in a let* since it was only a single expression between the two lets. Wasteful (one unnecessary (gensym), one extra nested let) or good looking?
;; Expand gambit-c macro form. (define-macro (macro-expand-dummy mac) (let ((port (gensym)) (dummy (gensym)) (form (gensym)) (code (gensym))) `(let* ((,port (open-string)) (,dummy (pp (lambda () ,mac) ,port)) (,form (read ,port)) (,code (cddr ,form))) (cond ((null? ,code) '(begin)) ((null? (cdr ,code)) (car ,code)) (else `(begin ,@,code))))))
/Joel