tags:

views:

141

answers:

2

I'm just playing with an NFA for string recognition. I have a macro that creates a function which consumes input and passes on the rest to some other functions. Because there might be loops in my NFA graph, I'm using letrec to put the whole thing together. Here is some code (been testing in PLT-Scheme):

(define-syntax-rule (match chars next accepting)
  ; a function that consumes a list of chars from a list l. 
  ; on success (if there's more to do) invokes each of next on the remainder of l.
  (lambda (l) 
    (let loop ((c chars) (s l))
      (cond
        ((empty? c)
         (cond 
           ((and (empty? s) accepting) #t)
           (else 
            (ormap (lambda (x) (x s)) next))))
        ((empty? s) #f)
        ((eq? (car c) (car s)) 
         (loop (cdr c) (cdr s)))
        (else #f)))))

; matches (a|b)*ac. e .g. '(a a b b a c)
(define (matches? l)
  (letrec
      ([s4 (match '( ) '()        #t)]
       [s3 (match '(c) `(,s4)     #f)]
       [s2 (match '(a) `(,s3)     #f)]
       [s1 (match '( ) `(,s2 ,s5) #f)]
       [s5 (match '( ) `(,s6 ,s7) #f)]
       [s6 (match '(a) `(,s8)     #f)]
       [s7 (match '(b) `(,s8)     #f)]
       [s8 (match '( ) `(,s1)     #f)])
    (s1 l)))


(matches? '(a c))
(matches? '(a b b b a c))
(matches? '(z a b b b a c))

Now, what if I had a simple data-structure to represent my NFA, like a list of lists. e.g.

'((s4 () () #t)
  (s3 (c) (s4) #f) 
  ...)

My question is: How would I turn that list into the former letrec statement? I'm not too good with Macros and my understanding is that I probably shouldn't be using eval.

+4  A: 

If the list is known at compile time (what I mean is, before your program starts running) then you can use a macro. Otherwise you must use eval.

It's ok. This is one of the good uses for eval. :)

Jason Orendorff
Hearing you say this reaffirms what I thought to be true. You get the check-mark until someone proves otherwise.
z5h
+3  A: 

I came up with this macro which seems to do the job (I'm not an expert either):

(define-syntax nfa
  (syntax-rules (let-bindings)
    ; All the let bindings have been expanded
    [(nfa start (let-bindings . bindings))
     (lambda (l) (letrec bindings (start l)))]
    ; Otherwise, expand the next binding
    [(nfa start (let-bindings . bindings) (s c n a) . rest)
     (nfa start (let-bindings (s (match 'c (list . n) a)) . bindings) . rest)]
    ; Insert the expanded bindings list
    [(nfa start states)
     (nfa start (let-bindings) . states)]))

; matches (a|b)*ac. e .g. '(a a b b a c)
(define matches?
  (nfa s1 ([s4 ( ) ()      #t]
           [s3 (c) (s4)    #f]
           [s2 (a) (s3)    #f]
           [s1 ( ) (s2 s5) #f]
           [s5 ( ) (s6 s7) #f]
           [s6 (a) (s8)    #f]
           [s7 (b) (s8)    #f]
           [s8 ( ) (s1)    #f])))

The trick is to use intermediate forms to create "subtitution loops", and reserve identifiers (cf. let-bindings) to distinguish these intermediate forms from direct usage of the macro.

Jérémie Koenig
+1. I appreciate your hard work and example, but my problem is that my list is generated programatically. Say, for the sake of example it's stored as `nfa-rules` and the start is `s1`. Then I want to be able to do `(nfa s1 nfa-rules)`. Getting the macro to "peer inside" nfa-rules and iterate over the contents is the thing I have problems with.
z5h
I think what Jason said could be correct. If I know the list at compile time, I can use your macro to simplify the coding. However, if the list is generated programatically, I may have to use `eval` to convert it into the appropriate code at runtime.
z5h
If you're going to manipulate your NFA's as data, it might be more straightforward to implement them as such, and use a macro only for easier entry when you have to hardcode some of them in the code. Your letrec/macro trick is neat, though :-)
Jérémie Koenig
Also, no macro is ever going to "peer inside" any variable : they manipulate the code before there's any evaluation done, so as far as they're concerned, `nfa-rules` is just a word.
Jérémie Koenig