1

This seems to work, it's a macro that expands to successive integers depending on how many times it has been expanded.

;; Library (test macro-state)
(library
 (test macro-state)
 (export get-count incr-count)
 (import (rnrs))

 (define *count* 0)
 (define (get-count) *count*)
 (define (incr-count) (set! *count* (+ *count* 1)))

 )

;; Program
(import (rnrs) (for (test macro-state) expand))

(define-syntax m
  (lambda (x)
    (syntax-case x ()
      ((m) (begin (incr-count) (datum->syntax #'m (get-count)))))))

(write (list (m) (m) (m)))
(newline)
;; prints (1 2 3)

But it's clumsy to me because the macro state *count* and the macro m itself are in different modules. Is there a better way to do this in r6rs, preferably one that doesn't split the implementation over two modules?

EDIT

I should make it clear that although this example is just a single macro, in reality I'm looking for a method that works when multiple macros need to share state.

john
  • 85,011
  • 4
  • 57
  • 81

1 Answers1

5

You can make the state local to the macro transformer:

(define-syntax m
  (let ()
    (define *count* 0)
    (define (get-count) *count*)
    (define (incr-count) (set! *count* (+ *count* 1)))
    (lambda (x)
      (syntax-case x ()
        ((m) (begin (incr-count) (datum->syntax #'m (get-count))))))))

Edited to add: In Racket, you can also do this:

(begin-for-syntax
  (define *count* 0)
  (define (get-count) *count*)
  (define (incr-count) (set! *count* (+ *count* 1))))
(define-syntax m
  (lambda (x)
    (syntax-case x ()
      ((m) (begin (incr-count) (datum->syntax #'m (get-count)))))))

But I don't think R6RS has anything that corresponds to begin-for-syntax.

Ryan Culpepper
  • 10,495
  • 4
  • 31
  • 30
  • Yes I thought of that after I'd posted. But does that method generalise if you want multiple macros to share state? – john Aug 09 '11 at 17:24
  • @john, no, and I don't know of anything else in R6RS that would help. See also my edited answer. – Ryan Culpepper Aug 09 '11 at 18:21