[racket] Problem with macro

From: Casey Klein (clklein at eecs.northwestern.edu)
Date: Fri Aug 12 11:11:42 EDT 2011

On Fri, Aug 12, 2011 at 9:22 AM, Neil Van Dyke <neil at neilvandyke.org> wrote:
> 1. Without trying it, I'm pretty sure this particular syntax transformation
> can be done in "syntax-rules", but I'm not certain it can be done using only
> the power of "..." in "syntax-rules", without a helper macro.  Your life
> might be easier if you do this particular transformation in "syntax-case"
> rather than "syntax-rules".

Below is an example of how to do it without the syntax-rules
acrobatics. Besides abandoning syntax-rules, I made two other changes:

1. The `transition-function' macro takes the `curr-state' variable as
an input. The macro could make up a variable with the same name, but
it wouldn't be bound by the letrec generated by the `automaton' macro
-- it's out of scope. The original `process-state' macro had this

2. The `END' transition is defined at the top. It would again be
possible to pass the letrec-bound variable into `transition-function'
macro, but lifting is easier.


(define-for-syntax (make-transition-case curr-state-stx transition-spec)
  (with-syntax ([curr-state curr-state-stx])
    (syntax-case transition-spec (-> END)
      [(label -> END)
       #'[(label) (set! curr-state END) 'endstate]]
      [(label -> new-state)
       #'[(label) (set! curr-state new-state) 'ok]])))

(define-syntax (transition-function stx)
  (syntax-case stx ()
    [(_ curr-state transition-spec ...)
     (with-syntax ([(transition-case ...)
                    (for/list ([spec (syntax->list #'(transition-spec ...))])
                      (make-transition-case #'curr-state spec))])
       #'(λ (symbol)
           (case symbol
             transition-case ...)))]))

(define-syntax automaton
  (syntax-rules (: -> END)
    [(_ init-state
        (state : transition ...)
     (letrec ([curr-state empty]
              [state (transition-function curr-state transition ...)]
       (set! curr-state init-state)
       (lambda (c)
         (curr-state c)))]))

(define (END symbol) 'reading-past-end-error)

(define (make-selector-automaton)
  (automaton init
             (init : (c -> more))
             (more : (a -> more)
                   (d -> more)
                   (r -> END))))

(require rackunit)

(let ([a (make-selector-automaton)])
  (check-equal? (a 'c) 'ok)
  (check-equal? (a 'a) 'ok)
  (check-equal? (a 'd) 'ok)
  (check-equal? (a 'r) 'endstate)
  (check-equal? (a 'q) (END 'whatever)))

(let ([a (make-selector-automaton)])
  (check-equal? (a 'c) 'ok)
  ; falls off end of the `case'
  (check-true (void? (a 'c))))

Posted on the users mailing list.