; 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)))))
OyBmc20gZ2VuZXJhdG9yCgooZGVmbWFjcm8gbWVhbHkgKGlucHV0IGFjdGlvbnMpCiAgKGxldCAoKGMgKGdlbnN5bSkpICAgOzsgQXZvaWQgdmFyaWFibGUgY2FwdHVyZQogICAgICAgIChzdGF0ZSAoZ2Vuc3ltKSkKICAgICAgICAob3V0IChnZW5zeW0pKSkgICA7OyBQdXQgb3V0cHV0IGhlcmUKICAgIGAobG9vcCBmb3IgLGMgaW4gKGNvZXJjZSAsaW5wdXQgJ2xpc3QpIDs7IENvbnZlcnQgaW5wdXQgaW50byBhIGxpc3QKICAgICAgICAgICB3aXRoICxzdGF0ZSA9IDAKICAgICAgICAgICB3aGlsZSAoPj0gLHN0YXRlIDApCiAgICAgICAgICAgYXBwZW5kCiAgICAgICAgICAgKGNvbmQgCiAgICAgICAgICAgICxAKG1hcGNhcgogICAgICAgICAgICAgICAjJyhsYW1iZGEgKHN0YXRlc3BlYykKICAgICAgICAgICAgICAgICAgIChsZXQgKCh0aGlzc3RhdGUgKGNhciBzdGF0ZXNwZWMpKSkKICAgICAgICAgICAgICAgICAgICAgYCgoPSAsc3RhdGUgLHRoaXNzdGF0ZSkKICAgICAgICAgICAgICAgICAgICAgICAoY29uZAogICAgICAgICAgICAgICAgICAgICAgICAsQChtYXBjYXIgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICMnKGxhbWJkYSAoYWN0aW9uc3BlYykKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChsZXQgKChtYXRjaCAoY2FyIGFjdGlvbnNwZWMpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKG5ld3N0YXRlIChjYWRyIGFjdGlvbnNwZWMpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKG91dHB1dCAoY2RkciBhY3Rpb25zcGVjKSkpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGAoLChpZiAoZXEgbWF0Y2ggbmlsKSB0IGAoZXEgLGMgLG1hdGNoKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA7OyBSZXNldCB0aGUgc3RhdGUgaWYgbmVjZXNzYXJ5CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEAoaWYgKG5vdCAoPSB0aGlzc3RhdGUgbmV3c3RhdGUpKSBgKChzZXRxICxzdGF0ZSAsbmV3c3RhdGUpKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAobGlzdAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsQChtYXBjYXIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgOzsgSWYgb3V0cHV0IGNoYXIgaXMgbmlsLCBqdXN0IGVjaG8gdGhlIGlucHV0LgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjJyhsYW1iZGEob3V0Y2hhcikgKGlmIChlcSBvdXRjaGFyIG5pbCkgYyBvdXRjaGFyKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKGNkZHIgYWN0aW9uc3BlYykpKSkpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAoY2RyIHN0YXRlc3BlYykpKSkpKQogICAgICAgICAgICAgICBhY3Rpb25zKSkgaW50byAsb3V0CiAgICAgICAgICAgZmluYWxseSAocmV0dXJuIChjb2VyY2UgLG91dCAnc3RyaW5nKSkpKSkKCihkZWZ1biBzaW5nbGV0b24xIChzKQogIChtZWFseSBzCiAgICAgICAgICgoMCAKICAgICAgICAgICAoI1xYIDEpIAogICAgICAgICAgIChuaWwgMCBuaWwpKQogICAgICAgICAgKDEgCiAgICAgICAgICAgKCNcWCAxICNcWCAjXFgpCiAgICAgICAgICAgKG5pbCAwIG5pbCkpKSkpCiAgICAgICAgIAoocHJpbnQgKHNpbmdsZXRvbjEgIlhiYlhYYlgiKSkKCihkZWZ1biBzaW5nbGV0b24yIChzIGMpIAogIChtZWFseSBzCiAgICAgICAgICgoMCAKICAgICAgICAgICAoYyAxKSAKICAgICAgICAgICAobmlsIDAgbmlsKSkKICAgICAgICAgICgxIAogICAgICAgICAgIChjIDEgYyBjKQogICAgICAgICAgIChuaWwgMCBuaWwpKSkpKQoKKHByaW50IChzaW5nbGV0b24yICJYYmJYWGJYIiAjXFgpKQoKKHByaW50IChtZWFseSAiYWJjYWNiIiAoKDAgKCNcYSAwIG5pbCkgKG5pbCAwIG5pbCBuaWwpKSkpKQ==