[racket] Problem with macro
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
problem.
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))))