; fsm generator

(defmacro mealy (input actions)
  (let ((c (gensym))   ;; Avoid variable capture
        (state (gensym))
        (out (gensym)))   ;; Put output here
    `(loop for ,c in (coerce ,input 'list) ;; Convert input into a list
           with ,state = 0
           while (>= ,state 0)
           append
           (cond 
            ,@(mapcar
               #'(lambda (statespec)
                   (let ((thisstate (car statespec)))
                     `((= ,state ,thisstate)
                       (cond
                        ,@(mapcar 
                           #'(lambda (actionspec)
                               (let ((match (car actionspec))
                                     (newstate (cadr actionspec))
                                     (output (cddr actionspec)))
                                 `(,(if (eq match nil) t `(eq ,c ,match))
                                   ;; Reset the state if necessary
                                   ,@(if (not (= thisstate newstate)) `((setq ,state ,newstate)))
                                   (list
                                    ,@(mapcar
                                       ;; If output char is nil, just echo the input.
                                       #'(lambda(outchar) (if (eq outchar nil) c outchar))
                                       (cddr actionspec))))))
                           (cdr statespec))))))
               actions)) into ,out
           finally (return (coerce ,out 'string)))))

(defun singleton1 (s)
  (mealy s
         ((0 
           (#\X 1) 
           (nil 0 nil))
          (1 
           (#\X 1 #\X #\X)
           (nil 0 nil)))))
         
(print (singleton1 "XbbXXbX"))

(defun singleton2 (s c) 
  (mealy s
         ((0 
           (c 1) 
           (nil 0 nil))
          (1 
           (c 1 c c)
           (nil 0 nil)))))

(print (singleton2 "XbbXXbX" #\X))

(print (mealy "abcacb" ((0 (#\a 0 nil) (nil 0 nil nil)))))