[
  {
    "path": ".gitignore",
    "content": "autodoc/**\nlib/**\npom.xml\npom.xml.asc\n.#*\nmulti-lib/**\n*.jar\n*.class\n/target\n/bin\n/classes\n/.classpath\n/.project\n/checkouts\n/.lein-*\n/.nrepl-port\n"
  },
  {
    "path": "CHANGES.md",
    "content": "*Version 0.1.4*\n  - Fix Issue #9 - invalid excpetion call breaks Clojure 1.8 compatability.\n  - Bumped dependencies for clojure -> 1.7.0 & core.match -> 0.2.2\n\n*Version 0.1.3*\n  - support for specifying the initial state of the fsm at runtime\n  \n*Version 0.1.0*\n\n- Added support for incremental finite state machines. These allow you to provide events via a function call instead of a sequence.\n  Useful for when events are provided by callbacks\n\n- updated dependencies:\n  - clojure 1.5.1\n  - core.match 0.2.0-rc3\n  - dorothy 0.0.3\n    \n- Fixed a bug that would cause the state machines to overflow the stack on large input sequences\n\n"
  },
  {
    "path": "README.md",
    "content": "# reduce-fsm\n\n## Features\nreduce-fsm provides a simple way to specify clojure [finite state machines](http://en.wikipedia.org/wiki/Finite-state_machine), it allows you to:\n\n- Define define state machines that accumulate values (in the same was that reduce does)\n- Create lazy sequences from state machines\n- Perform stateful filtering with clojures filter/remove functions\n- Visualize the resulting state machines with graphviz\n\nAll generated state machines are plain clojure functions and read events from clojure sequences.\nEvents are dispatched with core.match and allow the use of all match features (guards, destructuring, regex matching, etc.)\n\n## Documentation and Source\n\n- This documentation is available at http://cdorrat.github.com/reduce-fsm/\n- The API documentation is available on github at http://cdorrat.github.com/reduce-fsm/api\n- The source is available on GitHub at https://github.com/cdorrat/reduce-fsm\n\n\n## Usage\nThe fastest way to use this library is with Leiningen. Add the following to your project.clj dependencies:\n\n```clojure\n[reduce-fsm \"0.1.4\"]\n```\n\nUse via:\n\n```clojure\n(require '[reduce-fsm :as fsm])\n```\n\n## Examples\n\n#### Basic FSM \nThe following example counts the number of times \"ab\" occurs in a sequence. \n\n```clojure\n(defn inc-val [val & _] (inc val))\n\n(fsm/defsm count-ab\n  [[:start\n    \\a -> :found-a]\n   [:found-a\n    \\a ->  :found-a\n    \\b -> {:action inc-val} :start\n    _ -> :start]])\n\n;; We can use the generated fsm like any function\n(map (partial count-ab 0) [\"abaaabc\" \"aaacb\" \"bbbcab\"])\n;; returns => (2 0 1)\n\n(fsm/show-fsm count-ab)\n;; displays the fsm diagram below\n\n```\n\n![show-fsm output](http://cdorrat.github.com/reduce-fsm/images/fsm-count-ab.png)\n\n### Incremental FSM\nThe following example repeats the state machine from the Basic FSM example but uses function calls to provide\nevents instead of clojure sequences. This can be useful when you have multiple event sources or events are\ngenerated by callbacks.\n\n```clojure\n(defn inc-val [val & _] (inc val))\n\n(fsm/defsm-inc count-ab\n  [[:start\n    \\a -> :found-a]\n   [:found-a\n    \\a ->  :found-a\n    \\b -> {:action inc-val} :start\n    _ -> :start]])\n\n;; create an instance of the fsm with an initial value of 0\n(def fsm-state (atom (count-ab 0)))\n\n;; update the state with a few events\n(swap! fsm-state fsm/fsm-event \\a)\n(swap! fsm-state fsm/fsm-event \\a)\n(swap! fsm-state fsm/fsm-event \\b)\n\n(:value @fsm-state)\n;; returns the current accumulated value => 1 \n\n(:state @fsm-state)\n;; the current state of the fsm => :start\n\n;; count the number of ab occurences in a string\n(:value (reduce fsm/fsm-event (count-ab 0) \"abaaabc\"))\n;; => 2\n\n````\n\n#### Generating Lazy Sequences\n\nThe fsm-seq functions return lazy sequences of values created by the emit function when a state change occurs. \nThis example looks for log lines where the sequence of events was (a,c) instead of the expected (a,b,c) and\nadds the unexpected event to the output sequence. \n\n\n```clojure\n(defn emit-evt [val evt] evt)\n\n(defsm-seq log-search\n  [[:start\n    #\".*event a\" -> :found-a]\n   [:found-a\n    #\".*event b\" -> :found-b\n    #\".*event c\" -> {:emit emit-evt} :start]\n   [:found-b\n    #\".*event c\" -> :start]])\n\n;; The resulting function accepts a sequence of events \n;; and returns a lazy sequence of emitted values\n(take 2 (log-search (cycle [\"1 event a\"\n                            \"2 event b\"\n                            \"3 event c\"\n                            \"another event\"\n                            \"4 event a\"\n                            \"event x\"\n                            \"5 event c\"])))\n\n;; returns => (\"5 event c\" \"5 event c\")\n\n(fsm/show-fsm log-search)\n;; displays the image below\n\n```\n\n![show-fsm output](http://cdorrat.github.com/reduce-fsm/images/fsm-log-search.png)\n\n\n#### Stateful Filtering\nStates in filters are defined as passing values (default) or suppressing them {:pass false}.\nFor each event the filter will return the pass value of the state it is in after processing the event (input sequence element).\n\nThe following example suppresses values from the time a 3 is encountered until we see a 6.\n\n```clojure\n(defsm-filter sample-filter\n  [[:initial\n    3 -> :suppressing]\n   [:suppressing {:pass false}\n    6 -> :initial]])\n\n;; The resulting fsm is used with the clojure.core/filter and remove functions like this.\n(filter (sample-filter) [1 2 3 4 5 1 2 6 1 2])\n;; returns => (1 2 6 1 2)\n\n(fsm/show-fsm sample-filter)\n;; displays the diagram below\n```\n\n![show-fsm output](http://cdorrat.github.com/reduce-fsm/images/fsm-sample-filter.png)\n\n#### Different dispatch types\n\nWhen defining a state machine the matching rules for a transition only use the current event by default, by adding the :dispatch option you can make transitions conditional on the state as well as the current event.\nThe following dispatch types are supported:\n\n- :event-only (default) - just the current event is available for matches (equivalent to (clojure.core.match/match evt ...))\n- :event-and-acc        - both the current accumulated state and the event are passed (equivalent to (clojure.core.match/match [state evt] ...))\n- :event-acc-vec        - the state and event are passed in a single vector (equivalent to (clojure.core.match/match [ [state evt] ] ...))\n\nThe following example demonstrates :event-acc-vec dispatch.\n\n```clojure\n\n(defn should-transition? [[state event]]\n  (= (* state 2) event))\n\n(defn event-is-even? [[state event]]\n  (even? event))\n\n(defn inc-count [cnt & _ ]\n  (inc cnt))\n\n(defn reset-count [& _]\n  0)\n\n;; transition to the next state when we get a value thats twice the number of even events we've seen\n(fsm/defsm even-example \n\t   [[:start\n\t     [_ :guard should-transition?] -> {:action reset-count} :next-state\n\t     [_ :guard event-is-even?] -> {:action inc-count} :start]\n\t    [:next-state ,,,]]\n\t   :default-acc  0\n\t   :dispatch :event-acc-vec)\n\n(even-example [1 1 2])   ;; => 1 (the number of even events)\n(even-example [1 2 2 4]) ;; => 0 (we transitioned to next state)\n```\n\n#### Other examples\n\nThere are additional exmaples on [github](https://github.com/cdorrat/reduce-fsm/tree/master)\nin the examples and test directories  including:\n\n- a simple tcp server\n- matching repeating groups\n- using the :event-and-acc match syntax\n- using guards on events\n\n\n## License\n\nCopyright (C) 2011 Cameron Dorrat\n\nDistributed under the Eclipse Public License, the same as Clojure.\n"
  },
  {
    "path": "examples/fsm_dispatch_examples.clj",
    "content": "(ns fsm-dispatch-examples\n  \"Examples illustrating the supported event dispatch styles\"\n  (:require [reduce-fsm :as fsm]))\n\n\n\n;; ============================================================\n;; match-1 (default) event dispatch\n;; Events are dispatched using the core.match/match-1 syntax.\n;; See https://github.com/clojure/core.match for details\n\n(defn found-lisp [num-instances & _]\n  (println \"Found lisp\")\n  (inc num-instances))\n\n;; count the number of instances of 'lisp' in a string\n(fsm/defsm find-lisp\n  [[:start\n    \\l -> :found-l]\n   [:found-l\n    \\i -> :found-i\n    \\l -> :found-l\n     _ -> :start]\n   [:found-i\n    \\s -> :found-s\n    \\l -> :found-l\n     _ -> :start]\n   [:found-s\n    \\p -> {:action found-lisp} :start\n    \\l -> :found-l\n    _ -> :start]]\n  :default-acc 0)\n\n\n(find-lisp \"ablilispghlihilisp\")\n;; => Found lisp\n;;    Found lisp\n;;    2\n;;\n\n\n;; ============================================================\n;; match dispatch\n;; Events are dispatched using the core.match/match syntax\n;; with a vector of [acc event] where acc is the current\n;; accumulator value.\n;; See https://github.com/clojure/core.match for details\n\n\n;; we'll create an fsm to match the equvalent of the \"ab{2,3}c\" regex\n(defn inc-b-count [acc & _]\n  (update-in acc [:repeats] inc))\n\n(defn reset-b-count [acc & _]\n  (assoc acc :repeats 0))\n\n(defn count-satisfied? [{:keys [repeats]}]\n  (and (>= repeats 2)\n       (<= repeats 3)))\n\n(defn matched-event [acc & _]\n  (assoc acc :matched true))\n\n(defn done-state [& _]\n  true)\n\n(fsm/defsm sample-regex\n  [[:start\n    [_ \\a] -> :found-a]\n   [:found-a\n    [_ \\a] -> :found-a\n    [_ \\b] -> {:action inc-b-count} :found-b\n    [_ _]  -> :start]   \n   [:found-b\n    [(n :when count-satisfied?) \\c] -> {:action matched-event} done-state\n    [_ \\b] -> {:action inc-b-count} :found-b\n    [_ _]  -> {:action reset-b-count} :start]\n   [done-state]]\n  :default-acc {:matched false :repeats 0}\n  :dispatch :event-and-acc)\n\n;; test a series of strings\n(map #(-> % sample-regex  :matched)\n     [\"abc\" \"abbbbbc\" \"abbbc\"])\n;;=>  (false false true)\n\n"
  },
  {
    "path": "examples/simple_server.clj",
    "content": "(ns simple-server\n  \"An example fsm used for a simple TCP service to that lets users list the contents of directories after authenticating\"  \n  (:require [reduce-fsm :as fsm]\n\t    [clojure [string :as str]]\n\t    [clojure.java [io :as io]]\n\t    [server [socket :as ss]])\n  (:import java.io.File\n\t   java.io.PrintWriter))\n\n(defn parse-command\n  \"Parse a single line into a command for our server\"\n  [line]\n  (let [[cmd & args] (str/split line #\"\\s+\")]\n    {:cmd cmd\n     :args args}))\n\n(defn password-valid?\n  \"return true if the user/password identifies a valid user\"\n  [[user password]]\n  (and (= \"user\" user)\n       (= \"password\" password)))\n\n;; ================================= actions  ================================= \n;; These actions will be called in response to commands\n;; with the following parameters [accumulated-state event from-state to-state]\n;; they should return the new accumulated state after the transition \n\n(defn- chdir\n  \"set the current directory from a command\"\n  [acc {[new-dir] :args} & _]\n  (if (.. (File. new-dir) isDirectory)\n    (assoc acc :curr-dir new-dir)\n    acc))\n\n(defn- list-dir\n  \"list the contents of the current directory.\n   may be tested with: (list-dir {:writer println :curr-dir \\\"/tmp\\\"})\"\n  [{:keys [writer curr-dir] :as acc} & _]\n  (writer (format \"Contents of %s is:\" curr-dir))\n  (doseq [f (.. (File. curr-dir) list)]\n    (writer (str \"\\t\" f)))\n  acc)\n\n(defn- invalid-command [{:keys [writer] :as acc} evt & _]  \n  (writer (str \"unrecognised command: \" evt))\n  acc)\n\n;; a state function - returning true will cause the fsm to exit\n(defn quit [& _]\n  true)\n\n;; ================================= actions  ================================= \n;; define our fsm\n;; list-session will be a function with the following arities:\n;;   [events]     - a sequence of events\n;;   [acc events] - the initial state and a sequence of events\n;;\n;; It will return when one of the following occurs:\n;;   a). it reaches a terminal state\n;;   b). there are no more events\n;;   c). a state function returns true\n\n(fsm/defsm list-session [[:connected\n\t\t\t  {:cmd \"login\" :args (a :when password-valid?)} -> :authorised\n\t\t\t  {:cmd _} -> {:action invalid-command} :connected]\n\t\t\t [:authorised\n\t\t\t  {:cmd \"cd\"} -> {:action chdir} :authorised\n\t\t\t  {:cmd \"ls\"} -> {:action list-dir} :authorised\n\t\t\t  {:cmd \"quit\"} -> quit\n\t\t\t  {:cmd _} -> {:action invalid-command} :authorised]\n\t\t\t [quit]])\n\n(comment\n  ;; we can show our fsm as a diagram with\n  (fsm/show-fsm list-session)\n  \n  ;; the fsm is just an ordinary function\n  ;; we can test our server at the repl like so\n  (list-session {:writer println :curr-dir \".\"}\n\t\t(map parse-command\n\t\t     [\"login user password\"\n\t\t      \"cd /tmp\"\n\t\t      \"ls\"\n\t\t      \"quit\"]))\n  )\n\n;; ================================= actions  ================================= \n;; create the tcp server\n\n(defn handle-connection\n  \"Handle a single client connection to our service\"\n  [input-stream output-stream]\n  (with-open [w (PrintWriter. (io/writer output-stream) true)\n\t      rdr (io/reader input-stream)]\n    (list-session {:writer #(doto w (.println %) .flush)  :curr-dir \".\"}\n\t\t  (map parse-command (line-seq rdr)))))\n\t\t\t     \n(defn start-server\n  \"Start a server listening on a specified port\"\n  [port]\n  (println (str \"directory listing server listening on port \" port))\n  (ss/create-server port handle-connection))\n\n;; that's it, run (start-server 5000) from the repl\n;; telnet to your port (5000) to test the server\n;; there's no mutable state in the whole implementation\n"
  },
  {
    "path": "project.clj",
    "content": "(defproject reduce-fsm \"0.1.4\"\n  :description \"Clojure finite state machines\"\n  :dependencies [[org.clojure/clojure \"1.12.0\"]\n\t\t [dorothy \"0.0.6\"]\n                 [org.clojure/core.match \"1.1.0\"]]\n  :dev-dependencies [[server-socket \"1.0.0\"] ;; used for examples/simple_server.clj\t\t     \n\t\t     [org.clojars.weavejester/autodoc \"0.9.0\"]]\n  :autodoc {:web-src-dir \"https://github.com/cdorrat/reduce-fsm/\"\n\t    :web-home \"http://cdorrat.github.com/reduce-fsm/\"\n\t    :output-path \"./autodoc/api\"})\n"
  },
  {
    "path": "src/reduce_fsm.clj",
    "content": "(ns reduce-fsm\n  \"Generate and display functional finite state machines that accumulate state\nin the same way as reduce.\nThis package allows you to:\n - Create basic fsm's (see fsm)\n - Create lazy sequences from state machines (see fsm-seq)\n - Create stateful filter functions for use with filter/remove (see fsm-filter)\n - Visualise state machines as\"\n  (:use [clojure.core [match :only [match]]])\n  (:require\n   [clojure [set :as set]]\n   clojure.core.match.regex\n   [dorothy [core :as d]]\n   [clojure [string :as str]]))\n\n(defn- fsm-fn?\n  \"return true if the symbol will be treated as a function in fsm state definitions.\"\n  [sym]\n  (not (keyword? sym)))\n\n(defn- report-compile-error\n  \"Report fatal errors during fsm compilation\"\n  [& args]\n  (let [msg (apply format args)]\n    (println (str \"error: \" msg))\n    (throw (Exception. (str \"FSM compilation exception: \" msg)))))\n\n(defn- report-compile-warning\n  \"Report warnings during fsm compilation\"\n  [& args]\n  (let [msg (apply format args)]\n    (println (str \"warning: \" msg))))\n\n(defn- sanity-check-fsm\n  \"Be nice to our users and check for problems in the fsm definition at compile time\"\n  [state-maps]\n  (let [state-names (set (map :from-state state-maps))\n\ttransitions (mapcat (fn [s] (map #(assoc % :from-state (:from-state s)) (:transitions s))) state-maps)]\n\n    ;; all targets of a transition must exist\n    (doseq [{:keys [from-state to-state]} transitions]\n      (when-not (state-names to-state)\n\t(report-compile-error \"The state %s was referenced in a transition from %s but does not exist\" to-state from-state)))\n    \n    ;; all states except for the first should be reachable by a transition\n    (let [state-has-incoming-trans (set (map :to-state transitions))]\n      (doseq [state (rest (map :from-state state-maps))]\n\t(when-not (state-has-incoming-trans state)\n\t  (report-compile-warning \"The state %s is not the initial state and is unreachable by any transitions\" state))))\n    \n    ;; check for unexpected keys in transition properties\n    (let [user-keys #{:emit :action}\n\t  expected-keys (into #{:from-state :to-state :evt} user-keys)]\n      (doseq [t transitions]\n\t(let [xtra-keys (set/difference (-> t keys set) expected-keys)]\n\t  (when (seq xtra-keys)\n\t    (report-compile-warning \"The key(s) %s was/were used in a transition from state %s, we only one expected or more of %s\"\n\t\t\t\t    xtra-keys (:from-state t) user-keys)))))\n    \n    ;; check for unexpected keys in state params\n    (let [expected-keys #{:pass :is-terminal}]\n      (doseq [s state-maps]\n\t(let [xtra-keys (set/difference (-> s :state-params keys set) expected-keys)]\n\t  (when (seq xtra-keys)\n\t    (report-compile-warning \"The key(s) %s was used in the state parameters for %s, we only expected one or more of %s\"\n\t\t\t\t    xtra-keys (:from-state s) expected-keys)))))))\n\n(defn- create-transition\n  \"create a single transition make with default params if none specified.\n  the expected input is like: [[#\\\".*event c\\\"] [{:emit emit-event :action inc-matches}? :waiting-for-a\\\"]]\"\n  [[from to]]\n  (let [has-params? (map? (first to))\n\tparams (if has-params? (first to) {})\n\tto-state (if has-params? (second to) (first to))]\n    (assoc params\n      :evt (last from)\n      :to-state to-state)))\n\n(defn- create-state-map\n  \"Create an entry for a single '[:state evt1 -> :state1 :evt2 -> :state2 ...]\"\n  [forms]\n  (let [from-state (first forms)\n\ttransitions (partition 2 1\n\t\t\t       (remove #(= '-> (first %))\n\t\t\t\t       (partition-by #(= '-> %) (rest forms))))]\n    {:from-state from-state\n     :state-params (when (-> forms second map?) (second forms))\n     :transitions (vec (map create-transition transitions))}))\n\n(defn- create-state-maps\n  \"Create the sequence of maps that defines the fsm from vector representation.\n  returns a list of maps similar to {:from-state :a-state :state-params {...} :transitions [{:to-state :another-state :evt 1}]}\"\n  [states]\n  (let [state-maps  (map create-state-map states)]\n    (sanity-check-fsm state-maps)\n    state-maps))\n  \n\n(defn- state-fn-name\n  \"Create a name for the internal function that will represent this state.\n   We want it to be recognisable to the user so stack traces are more intelligible\"\n  [sym]\n  (cond\n   (fn? sym) (let [fn-name (-> sym meta :name str)]\n               (if (empty? fn-name)\n                 (str (gensym \"fn-\"))\n                 fn-name))\n   (keyword? sym) (name sym)\n   :else (str sym)))\n\n(defn- state-fn-symbol\n  \"Create a name for the internal function that will represent this state.\n   We want it to be recognisable to the user so stack traces are more intelligible\"\n  [sym]\n  (gensym (str \"state\" \"-\" (state-fn-name sym) \"-\")))\n\n(defn- state-for-action [state]\n  (cond\n   (fn? state) (-> state meta :name keyword)\n   (keyword? state) state\n   :else (keyword state)))\n\n\n(defn- expand-evt-dispatch\n  \"Expand the dispatch of a single event, this corresponds to a single case in the match expression.\nParameters:\n  state-fn-map - a map of state-symbol -> name of implementing function\n  state-params - maps of state-symbol -> {:param1 ..}\n  from-state   - the state we're transitioning from\n  evt          - the name of the event parameter in the match statement\n  acc          - the name of the accumulator parameter in the match statement\n  events       - the sequence of events\n  evt-map      - the map representing this transition, eg. {:to-state x :action .... }\"\n  [state-fn-map state-params from-state evt acc events evt-map]\n  (let [target-state-fn (state-fn-map (:to-state evt-map))\n\tnew-acc (gensym \"new-acc\")]\n    `[~(:evt evt-map)\n      (let [~new-acc ~(if (:action evt-map)\n\t\t\t`(~(:action evt-map) ~acc ~evt ~(state-for-action from-state) ~(state-for-action (:to-state evt-map)))\n\t\t\tacc)]\n\t~(cond\n\t  (-> evt-map :to-state state-params :is-terminal) `~new-acc ;; terminal states return the accumulated val\n\t  (fsm-fn? (:to-state evt-map))           ;; if the target state is a function we need to check for early conditional termination\n\t  `(if (~(:to-state evt-map) ~new-acc)    ;; truthy return from a state function causes the fsm to exit\n\t     ~new-acc\n\t     (partial ~target-state-fn ~new-acc (rest ~events)))\n\t  :else `(partial ~target-state-fn ~new-acc (rest ~events))))])) ;; normal (keyword/non-terminal) states\n\n(defn- expand-dispatch [dispatch-type evt acc]\n  (case dispatch-type\n\t:event-only [`match evt]\n\t:event-and-acc   [`match [acc evt]]\n        :event-acc-vec [`match [[acc evt]]]\n\t(throw (RuntimeException. \"unknown fsm dispatch type, expected one of [:event-only :event-and-acc :event-acc-vec]\"))))\n  \n(defn- state-fn-impl\n  \"define the function used to represent a single state internally\"\n  [dispatch-type state-fn-map state-params state]\n  (let [this-state-fn  (state-fn-map (:from-state state))\n\tevents (gensym \"events\")\n\tacc (gensym \"acc\")\n\tevt (gensym \"evt\")]\n    `(~this-state-fn\n      [~acc ~events]\n      (if-let [~evt (first ~events)]\n\t#(~@(expand-dispatch dispatch-type evt acc)\n\t\t~@(mapcat (partial expand-evt-dispatch state-fn-map state-params (:from-state state)  evt acc events) (:transitions state))\n\t\t:else (partial ~this-state-fn ~acc (rest ~events))\n\t\t)\n\t~acc))))\n  \n\n(defn- transitions-metadata\n  \"create the metadata representation of all transitions for a single state\"\n  [state]\n   (let [from-state (keyword (:from-state state))]\n     (map (fn [t]\n \t   (let [trans (dissoc t :from-state :to-state)] ;; convert all non-state params into strings\n \t     (assoc (zipmap (keys trans) (map (fn [x] `'~x) (vals trans)))\n \t       :from-state from-state\n \t       :to-state (keyword (:to-state t)))))\n\t  (:transitions state))))\n\n(defn- state-metadata\n  \"create the metadata representation for a single state\"\n  [state]\n  {:state  (keyword (:from-state state))\n   :name (if (fsm-fn? (:from-state state))\n\t\t  (str \"(\" (:from-state state) \")\")\n\t\t  (str (:from-state state)))\n   :params (:state-params state)\n   :transitions  (vec (transitions-metadata state))})\n\n(defn- fsm-metadata\n  \"Create the metadata for the fsm. We'll use this at runtime to draw diagrams of the fsm\"\n  [fsm-type state-maps]\n  {::fsm-type fsm-type\n   ::states (vec (map state-metadata state-maps))})\n\n(defn lookup-state [state-fn-map the-state]\n  (if-let [a-state-fn (get state-fn-map the-state)]\n    a-state-fn\n    (throw (RuntimeException. (str \"Could not find the state \\\"\" the-state \"\\\"\")))))\n\n;;===================================================================================================\n;; We want to turn an fsm definition looking like this:\n;;\n;; (fsm [[:waiting-for-a\n;;        #\".*event a\" -> :waiting-for-b]\n;;       [:waiting-for-b\n;;        #\".*event d\" -> :waiting-for-a\n;;        #\".*event c\" -> {:action (fn [acc evt & _] (conj acc evt))} :waiting-for-a]])\n;;\n;; into the following trampoline based implementation:\n;;\n;; (fn the-fsm\n;;   ([events]\n;;    (the-fsm nil events))\n;;   ([acc events]\n;;      (letfn [(state-waiting-for-a  [acc events]\n;; \t      (if-let [evt (first events)]\n;; \t\t#(match evt\n;; \t\t\t  #\".*event a\" (partial state-waiting-for-b evt (rest events))\n;; \t\t\t  :else        (partial state-waiting-for-a evt (rest events)))\n;; \t\tacc))\n;; \t     (state-waiting-for-b  [acc events]\n;; \t      (if-let [evt (first events)]\n;; \t\t#(match evt\n;; \t\t\t  #\".*event d\" (partial state-waiting-for-a acc (rest events))\n;; \t\t\t  #\".*event c\" (let [new-acc ((fn [acc evt & _] (conj acc evt)) acc evt :waiting-for-b :waiting-for-a)]\n;; \t\t\t\t\t (partial state-waiting-for-a new-acc (rest events)))\n;; \t\t\t  :else (partial state-waiting-for-b acc (rest events)))\n;; \t\tacc))]\n;;        (trampoline state-waiting-for-a acc events))))\n(defmacro fsm\n  \"Returns an fsm function that reads a sequence of events and returns\nan accumulated value (like reduce). The returned function will have the following 2 arities:\n [events]                   - accepts a sequence of events\n [val events]               - accepts an initial value for the accumulator and a sequence of events.\n [initial-state val events] - start the fsm in the specified state with an accumulator value and a sequence of events.\n\nThe generated function will return when one of the following is true:\n - There are no more events in the event sequence\n - The fsm reaches a terminal state\n - The fsm reaches a state defined by a function and it returns a truthy value\n\nParameters:\n fsm      - the fsm definition (see below for syntax)\n fsm-opts - the following options are recognised:\n  :default-acc val - sets the initial value for the accumulator in the single arity version of the function\n  :dispatch - changes the way events are matched, the follow options are accepted:\n    - :event-only (default) - events are matched using the  core.match/match syntax against the event only\n    - :event-and-acc        - events use the default match syntax and are matched against [acc-value event]\n    - :event-acc-vec        - events are matches against a single vector of [[acc-value event]]\n\nFSM definitions:\n fsm's are defined as follows:\n [[state {:is-terminal true/false}?\n   event -> {:action a-fn}? target-state\n   event2 -> ...]\n  [target-state ...]]\n\nWhere\n state  - is a keyword or function\n state  - options (:is-terminal) are optional\n event  - is any legal core.match pattern (see https://github.com/clojure/core.match)\n action - is optional but must be a function if specified and the return value\n          will be used as the new accumulated state.\n\nState and Event Functions:\n State functions are called like so (state-fn acc) where acc is the current accumulated state.\n Event functions are called with (event-fn acc event from-state to-state) where\n   acc        - is the current accumulated state\n   event      - is the event that fired the transition\n   from-state - the state we're transitioning from\n   to-state   - the state we're transitioning to\n\nSee https://github.com/cdorrat/reduce-fsm for examples and documentation\"\n\n  [states & fsm-opts]\n  (let [{:keys [dispatch default-acc] :or {dispatch :event-only}} fsm-opts\n\tstate-maps  (create-state-maps states)\n\tstate-params (zipmap (map :from-state state-maps) (map :state-params state-maps))\n\tstate-fn-names (map state-fn-symbol (map :from-state state-maps))\n\tstate-fn-map (zipmap (map :from-state state-maps) state-fn-names)] ;; map of state -> letfn function name\n    `(letfn [~@(map #(state-fn-impl dispatch state-fn-map state-params %) state-maps)]\t    \n      (with-meta\n        (fn the-fsm#\n          ([events#] (the-fsm# ~default-acc events#))\n          ([acc# events#]\n             (the-fsm# ~(-> state-maps first :from-state) acc# events#))\n          ([initial-state# acc# events#]\n             (trampoline (lookup-state ~state-fn-map initial-state#) acc# events#)))\n        ~(fsm-metadata :fsm state-maps)))))\n\n(defmacro defsm\n  \"A convenience macro to define a fsm, equivalent to (def fsm-name (fsm states opts)\n   see reduce-fsm/fsm for details\"\n  [fsm-name states & opts]\n  `(def ~fsm-name (fsm ~states ~@opts)))\n\n;;===================================================================================================\n;; support for incremental fsms\n(defn- state-disp-name [sym]\n  (keyword (state-fn-name sym)))\n\n(defn- expand-inc-evt-dispatch\n  \"Expand the dispatch of a single event for incremental fsms, this corresponds to a single case in the match expression.\nParameters:\n  state-fn-map - a map of state-symbol -> name of implementing function\n  state-params - maps of state-symbol -> {:param1 ..}\n  from-state   - the state we're transitioning from\n  evt          - the name of the event parameter in the match statement\n  acc          - the name of the accumulator parameter in the match statement\n  events       - the sequence of events\n  evt-map      - the map representing this transition, eg. {:to-state x :action .... }\"\n  [state-fn-map state-params from-state evt acc events evt-map]\n  (let [new-acc (gensym \"new-acc\")]\n    `[~(:evt evt-map)\n      (let [~new-acc ~(if (:action evt-map)\n                        `(~(:action evt-map) ~acc ~evt ~(state-for-action from-state) ~(state-for-action (:to-state evt-map)))\n                        acc)]\n        {:state ~(state-disp-name (:to-state evt-map))\n         :value ~new-acc\n         :fsm ~(state-fn-map (:to-state evt-map))\n         :is-terminated? ~(if (fsm-fn? (:to-state evt-map)) ;; if the target state is a fn exit on truthy value\n                               `(~(:to-state evt-map) ~new-acc)\n                               (if (-> evt-map :to-state state-params :is-terminal)\n                                 true\n                                 false))})]))\n\n(defn- inc-state-fn-impl\n  \"define the function used to represent a single state internally for incremental fsms\"\n  [dispatch-type state-fn-map state-params state]\n  (let [this-state-fn  (state-fn-map (:from-state state))\n\tevents (gensym \"events\")\n\tacc (gensym \"acc\")\n\tevt (gensym \"evt\")]\n    `(~this-state-fn  [~acc ~evt]\n\t(~@(expand-dispatch dispatch-type evt acc)\n         ~@(mapcat (partial expand-inc-evt-dispatch state-fn-map state-params (:from-state state)  evt acc events) (:transitions state))\n         :else  {:state ~(state-disp-name (:from-state state))\n                 :is-terminated? false\n                 :value ~acc\n                 :fsm ~ this-state-fn}))))\n  \n(defmacro fsm-inc\n  \"Define an incremental finite state machine.\nState definitions and capabilities are the same as reduce-fsm/fsm but events\n are provided by calls to (fsm-event inc-fsm event) instead of a sequence.\nReturns a function that takes the intial accumulator value (or none for the default nil) and returns an incremental fsm.\nSubsequent chained calls to  fsm-event will move the fsm thought it's states.\n (reduce fsm-event ((inc-fsm [... fsm def ..])) events)\n is equivalent to\n (fsm [... fsm def ..] events)\"\n  [states & fsm-opts]\n  (let [{:keys [dispatch default-acc] :or {dispatch :event-only}} fsm-opts\n\tstate-maps  (create-state-maps states)\n\tstate-params (zipmap (map :from-state state-maps) (map :state-params state-maps))\n\tstate-fn-names (map state-fn-symbol (map :from-state state-maps))\n\tstate-fn-map (zipmap (map :from-state state-maps) state-fn-names)] ;; map of state -> letfn function name\n    `(letfn [~@(map #(inc-state-fn-impl dispatch state-fn-map state-params %) state-maps)]\n       (with-meta\n         (fn the-fsm#\n           ([] (the-fsm# ~default-acc))\n           ([acc#] (the-fsm# ~(-> state-maps first :from-state) acc#))\n           ([initial-state# acc#]\n              {:state (~state-disp-name initial-state#)\n               :is-terminated? false\n               :value acc#\n               :fsm (lookup-state ~state-fn-map initial-state#)}))\n         ~(fsm-metadata :inc-fsm state-maps)))))\n\n\n(defn fsm-event\n  \"process a single event with an incremental finite state machine (those created with fsm-inc or defsm-inc)\nReturns a map with the following keys:\n  :state          - the current state of the fsm after processing the event\n  :value          - the current accumulator value\n  :is-terminated? - true when the fsm is in a terminal state and no more events can be processed\"\n  [fsm event]\n  {:pre [(map? fsm) (contains? fsm :fsm)]} ;; only valid for incremental fsms\n  (if (:is-terminated? fsm)\n    fsm\n    ((:fsm fsm) (:value fsm) event)))\n\n(defmacro defsm-inc\n\"A convenience macro to define an incremental fsm, equivalent to (def fsm-name (fsm-inc states opts)\n   see reduce-fsm/fsm-inc for details\"\n[fsm-name states & opts]\n`(def ~fsm-name (fsm-inc ~states ~@opts)))\n\n;;===================================================================================================\n;; fsm-filter impl\n\n(defn- expand-filter-evt-dispatch\n  \"Expand the dispatch line for a single fsm-filter dispatch line.\n   The return value corresponds to a single case in a match clause\"\n  [state-fn-map state-params from-state evt acc evt-map]\n  (let [target-state-fn (state-fn-map (:to-state evt-map))\n\ttarget-pass-val (-> evt-map :to-state state-params (get :pass true))\n\tnew-acc (gensym \"new-acc\")]\n    `[~(:evt evt-map)\n      (let [~new-acc ~(if (:action evt-map)\n\t\t\t`(~(:action evt-map) ~acc ~evt ~(state-for-action from-state) ~(state-for-action (:to-state evt-map)))\n\t\t\tacc)]\n\t[~target-pass-val (~target-state-fn ~new-acc)])]))\n\n\n(defn- state-filter-fn-impl\n  \"Expand the definition of a function to handle a single filter state\"\n  [dispatch-type state-fn-map state-params state]\n  (let [this-state-fn  (state-fn-map (:from-state state))\n\tacc (gensym \"acc\")\n\tevt (gensym \"evt\")]\n    `(~this-state-fn\n      [~acc]\n      (fn [~evt]\n\t(~@(expand-dispatch dispatch-type evt acc)\n\t ~@(mapcat (partial expand-filter-evt-dispatch state-fn-map state-params (:from-state state) evt acc) (:transitions state))\n\t :else [~(get (:state-params state) :pass true) (~this-state-fn ~acc)])))))\n\n;;===================================================================================================\n;; We want to turn the following filter definition:\n;; (fsm-filter [[:initial\n;; \t      3 -> :suppressing]\n;; \t     [:suppressing {:pass false}\n;; \t      6 -> :initial]])\n;;\n;; into this implementation:\n;;\n;; (letfn [(state-initial [acc]\n;; \t (fn [evt]\n;; \t   (match evt\n;; \t\t    3 [false (state-suppressing acc)]\n;; \t\t    :else [true (state-initial acc)])))\n;; \t(state-suppressing [acc]\n;; \t (fn  [evt]\n;; \t   (match evt\n;; \t\t    6 [true (state-initial acc)]\n;; \t\t    :else  [false (state-suppressing acc)])))]\n;;   (fn filter-builder\n;;    ([] (filter-builder nil))\n;;    ([acc]\n;;     (let [curr-state (atom (state-initial acc))]\n;;      (fn [evt]\n;;       (let [[pass next-state] (@curr-state evt)]\n;;        (reset! curr-state next-state)\n;;        pass))))))\n;;\n(defmacro fsm-filter\n\"Returns a function that returns fsm filters suitable for use with clojure.core/filter and remove.\nEach state in the fsm definition has a :pass attribute that will be returned from the generated function\nwhen it is in that state.\n\nThe returned function will have the following 2 arities:\n []    - creates a filter with the default accumulator state (nil or the value from :default-acc)\n [val] - accepts an initial value for the accumulator\n [initial-state val] - start the fsm in the given state with the specified accumulator value\n\nParameters:\nfsm      - the fsm definition (see below for syntax)\nfsm-opts - the following options are recognised:\n  :default-acc val - sets the initial value for the accumulator in the single arity version function\n  :dispatch - changes the way events are matched, the follow options are accepted:\n    - :event-only (default) - events are matched using the  core.match/match syntax against the event only\n    - :event-and-acc        - events use the default match syntax and are matched against [acc-value event]\n\nFSM definitions:\nfilters are defined as follows:\n [[state {:pass true/false}?\n   event -> {:action a-fn}? target-state]\n  [target-state ...]]\n\nWhere\n  state is a keyword\n  state option (:pass) is optional, it defaults to true\n  event is any legal core.match pattern (see https://github.com/clojure/core.match)\n  action is optional but must be a function if specified and their return value\n         will be used as the new accumulated state\n\nEvent functions are called with (event-fn acc event from-state to-state) where\n  acc        - is the current accumulated state\n  event      - is the event that fired the transition\n  from-state - the state we're transitioning from\n  to-state   - the state we're transitioning to\n\nExample:\n  Suppress numbers after seeing a 3 until we see a 6.\n\n  (def f (fsm-filter [[:initial\n \t               3 -> :suppressing]\n \t              [:suppressing {:pass false}\n \t               6 -> :initial]]))\n\n  (= [1 2 6 1 2] (filter (f) [1 2 3 4 5 1 2 6 1 2]))\"\n  [states & fsm-opts]\n  (let [{:keys [dispatch default-acc] :or {dispatch :event-only}} fsm-opts\n\tstate-maps  (create-state-maps states)\n\tstate-fn-names (map state-fn-symbol (map :from-state state-maps))\n\tstate-params (zipmap (map :from-state state-maps) (map :state-params state-maps))\n\tstate-fn-map (zipmap (map :from-state state-maps) state-fn-names)] ;; map of state -> letfn function name\n    `(letfn [~@(map #(state-filter-fn-impl dispatch state-fn-map state-params %) state-maps)]\n       (with-meta\n\t (fn filter-builder#\n\t   ([] (filter-builder# ~default-acc))\n           ([acc#]\n              (filter-builder# ~(-> state-maps first :from-state) acc#))\n\t   ([initial-state# acc#]\n\t      (let [curr-state# (atom ((lookup-state ~state-fn-map initial-state#) acc#))]\n\t\t(fn [evt#]\n\t\t  (let [[pass# next-state#] (@curr-state# evt#)]\n\t\t    (reset! curr-state# next-state#)\n\t\t    pass#)))))\n\t ~(fsm-metadata :fsm-filter state-maps)))))\n          \n(defmacro defsm-filter\n  \"A convenience macro to define an fsm filter, equivalent to (def fsm-name (fsm-filter states opts)\n   see reduce-fsm/fsm-filter for details\"\n    [name states & fsm-opts]\n  `(def ~name (fsm-filter ~states ~@fsm-opts)))\n  \n\n\n;;===================================================================================================\n;; fsm-seq impl\n\n(defn- next-emitted\n  \"Process events with the fsm-seq function f until we emit a new value for the sequence\"\n  [f]\n  (when f\n    (loop [[emitted next-step] (f)]\n      (if next-step\n\t(if (not= ::no-event emitted)\n\t  [emitted next-step]\n\t  (recur (next-step)))\n\t[emitted nil]))))\n\n(defn ^{:skip-wiki true} fsm-seq-impl*\n  \"Create a lazy sequence from a fsm-seq state function\"\n  [f]\n  (let [[emitted next-step] (next-emitted f)]\n    (lazy-seq\n     (if next-step\n       (cons emitted (fsm-seq-impl* next-step))\n       (when (not= ::no-event emitted)\n\t (cons emitted nil))))))\n\n\n(defn- expand-seq-evt-dispatch\n  \"Expand the dispatch line for a single fsm-seq dispatch line.\n   The return value corresponds to a single case in a match clause\"\n  [state-fn-map state-params from-state evt acc evt-map]\n  (let [target-state-fn (state-fn-map (:to-state evt-map))\n\ttarget-pass-val (-> evt-map :to-state state-params :pass)\n\tnew-acc (gensym \"new-acc\")]\n\n    `[~(:evt evt-map)\n      (let [emitted# ~(if (:emit evt-map)\n\t\t\t`(~(:emit evt-map) ~acc (first ~evt))\n\t\t\t`::no-event)\n\t    ~new-acc ~(if (:action evt-map)\n\t\t\t`(~(:action evt-map) ~acc (first ~evt) ~(state-for-action from-state) ~(state-for-action (:to-state evt-map)))\n\t\t\tacc)]\n\t[emitted# (~target-state-fn ~new-acc (rest ~evt))])]))\n\n(defn- state-seq-fn-impl\n  \"Expand the definition of a function to handle a single state\"\n  [dispatch-type state-fn-map state-params state]\n  (let [this-state-fn  (state-fn-map (:from-state state))\n\tacc (gensym \"acc\")\n\tevt (gensym \"evt\")]\n    `(~this-state-fn\n      [~acc ~evt]\n      (when (seq ~evt)\n\t#(~@(expand-dispatch dispatch-type `(first ~evt) acc)\n\t ~@(mapcat (partial expand-seq-evt-dispatch state-fn-map state-params (:from-state state) evt acc) (:transitions state))\n\t :else [::no-event (~this-state-fn ~acc (rest ~evt))])))))\n\n;;===================================================================================================\n;; We want to turn an fsm-seq definition looking like this:\n;;\n;;  (fsm-seq\n;;   [[:waiting-for-a\n;;     #\".*event a\" -> :waiting-for-b]\n;;    [:waiting-for-b\n;;     #\".*event b\" -> :waiting-for-c\n;;     #\".*event c\" -> {:emit emit-evt} :waiting-for-a]\n;;    [:waiting-for-c\n;;     #\".*event c\" -> :waiting-for-a]])\n;;\n;; into this implementation:\n;;\n;;  (letfn [(state-waiting-for-a\n;; \t  [acc events]\n;; \t  (when (seq events)\n;; \t    #(match (first events)\n;; \t\t      #\".*event a\"  [::no-event  (state-waiting-for-b acc (rest events))]\n;; \t\t      :else [::no-event (state-waiting-for-a acc (rest events))])))\n;; \t (state-waiting-for-b\n;; \t  [acc events]\n;; \t  (when (seq events)\n;; \t    #(match (first events)\n;; \t\t      #\".*event b\" [::no-event (state-waiting-for-c acc (rest events))]\n;; \t\t      #\".*event c\" [(emit-evt acc (first events)) (state-waiting-for-a acc (rest events))]\n;; \t\t      :else [::no-event (state-waiting-for-b acc (rest events))])))\n;; \t (state-waiting-for-c\n;; \t  [acc events]\n;; \t  (when (seq events)\n;; \t    #(match  (first events)\n;; \t\t       #\".*event c\" [::no-event (state-waiting-for-a acc (rest events))]\n;; \t\t       :else [::no-event (state-waiting-for-c acc (rest events))])))]\n;;   (fn fsm-seq-fn\n;;    ([events] (fsm-seq-fn nil events))\n;;    ([acc events]\n;;     (when (seq events)\n;;       (reduce-fsm/fsm-seq-impl*\n;;        (state-waiting-for-a acc events))))))\n;;\n(defmacro fsm-seq\n\"Returns an fsm function that produces lazy sequences from a finite state machine.\n The state machines can optionally add new values to the lazy sequence on transitions\n (with the :emit option) and may accumulate state in the same way as reduce.\n\nThe returned function will have the following 2 arities:\n [events]                    - accepts a sequence of events\n [val events]                - accepts an initial value for the accumulator and a sequence of events.\n [initial-state vals events] - start the seq fsm with a given state and accumulator value \n\nThe generated function will produce a lazy sequence that ends when one of the following is true:\n - There are no more events in the event sequence\n - The fsm reaches a terminal state\n - The fsm reaches a state defined by a function and it returns a truthy value\n\nParameters:\n fsm      - the fsm definition (see below for syntax)\n fsm-opts - the following options are recognised:\n  :default-acc val - sets the initial value for the accumulator in the single arity version of the function\n  :dispatch - changes the way events are matched, the follow options are accepted:\n    - :event-only (default) - events are matched using the  core.match/match syntax against the event only\n    - :event-and-acc        - events use the default match syntax and are matched against [acc-value event]\n\nFSM definitions:\n fsm's are defined as follows:\n [[state {:is-terminal true/false}?\n   event -> {:emit emit-fn :action action-fn}? target-state\n   event2 -> ...]\n  [target-state ...]]\n\nWhere\n state  is a keyword or function\n state options (:is-terminal) are optional\n event  is any legal core.match pattern (see https://github.com/clojure/core.match)\n emit   is optional but its value must be a function, the return value will be added to the lazy sequence\n action is optional but its value must be a function if specified and their return value\n        will be used as the new accumulated state\n\nState and Event Functions:\n State functions are called with the current accumulated statelike so (state-fn acc).\n Emit functions are called with  (emit-fn acc event),\n Action functions are called with (action-fn acc event from-state to-state) where\n   acc        - is the current accumulated state\n   event      - is the event that fired the transition\n   from-state - the state we're transitioning from\n   to-state   - the state we're transitioning to\n\nSee https://github.com/cdorrat/reduce-fsm for examples and documentation\"  \n  [states & fsm-opts]\n  (let [{:keys [dispatch default-acc] :or {dispatch :event-only}} fsm-opts\n\tstate-maps  (create-state-maps states)\n\tstate-fn-names (map state-fn-symbol (map :from-state state-maps))\n\tstate-params (zipmap (map :from-state state-maps) (map :state-params state-maps))\n\tstate-fn-map (zipmap (map :from-state state-maps) state-fn-names)] ;; map of state -> letfn function name\n    `(letfn [~@(map #(state-seq-fn-impl dispatch state-fn-map state-params %) state-maps)]\n       (with-meta\n\t (fn fsm-seq-fn#\n\t   ([events#] (fsm-seq-fn# ~default-acc events#))\n           ([acc# events#]\n              (fsm-seq-fn# ~(-> state-maps first :from-state) acc# events#))\n\t   ([initial-state# acc# events#]\n\t      (when (seq events#)\n\t\t(fsm-seq-impl* ((lookup-state ~state-fn-map initial-state#)  acc# events#)))))\n\t ~(fsm-metadata :fsm-seq state-maps)))))\n\n(defmacro defsm-seq\n  \"A convenience macro to define an fsm sequence, equivalent to (def fsm-name (fsm-seq states opts)\n   see reduce-fsm/fsm-seq for details\"\n  [name states & fsm-opts]\n  `(def ~name (fsm-seq ~states ~@fsm-opts)))\n\n\n;; ===================================================================================================\n;; methods to display fsm\n\n(defn- dot-exists\n  \"return true if the dot executable from graphviz is available on the path\"\n  [& _ ]\n  (try\n    (->> \"dot -V\"\n\t (.exec (Runtime/getRuntime))\n\t (.waitFor)\n\t (= 0))\n    (catch Exception e false)))\n\n(defn- no-graphviz-message []\n  (println \"The dot executable from graphviz was not found on the path, unable to draw fsm diagrams\")\n  (println \"Download a copy from http://www.graphviz.org/\"))\n\n(defn- graphviz-installed? []\n  (if (dot-exists)\n    true\n    (do\n      (no-graphviz-message)\n      false)))\n\n\n(defn- dorothy-edge\n  \"Create a single edge (transition) in a dorothy graph\"\n  [from-state trans]\n  (let [label (str  \" \" (:evt trans)\n\t\t    (when (:action trans)\n\t\t      (str \"\\\\n(\" (-> trans :action meta :name str) \")\") ))]\n    (vector from-state (:to-state trans) {:label label} )))\n\n(defn- dorothy-state\n  \"Create a single dorothy state\"\n  [fsm-type {:keys [params name state]}]\n  (let [is-terminal? (if (= :fsm-filter fsm-type)\n                       (not (:pass params true))\n                       (or (:is-terminal params)\n                           (= \\( (first name))))]\n    [state\n     (merge {:label name} \n            (when is-terminal?\n              {:style \"filled,setlinewidth(2)\"\n               :fillcolor \"grey88\"}))]))\n\n(defn- transitions-for-state\n  \"return a sequence of dorothy transitions for a single state\"\n  [state]\n  (letfn [(transition-label [trans idx]\n\t\t\t    (str\n\t\t\t     (format \"<TABLE BORDER=\\\"0\\\"><TR><TD TITLE=\\\"priority = %d\\\">%s</TD></TR>\" idx (:evt trans))\n\t\t\t     (when (:action trans)\n\t\t\t       (format \"<TR><TD>(%s)</TD></TR>\" (:action trans)))\n\t\t\t     (when (:emit trans)\n\t\t\t       (format \"<TR><TD>(%s) -&gt;</TD></TR>\" (:emit trans)))\n\t\t\t     \"</TABLE>\"))\n\t  (format-trans [trans idx]\n\t\t\t[(:from-state trans) (:to-state trans) {:label (transition-label trans idx)} ])]\n    (map format-trans (:transitions state) (range (count (:transitions state))))))\n\n(defn fsm-dorothy\n  \"Create a dorothy digraph definition for an fsm\"\n  [fsm]\n  (let [start-state (keyword (gensym \"start-state\"))\n        state-map (->> fsm meta :reduce-fsm/states)\n        fsm-type (->> fsm meta :reduce-fsm/fsm-type)]\n    (d/digraph\n      (concat\n        [[start-state {:label \"start\" :style :filled :color :black :shape \"point\" :width \"0.2\" :height \"0.2\"}]]\n        (map (partial dorothy-state fsm-type) state-map)\n        [[start-state (-> state-map first :state)]]\n        (mapcat transitions-for-state state-map)))))\n\n(defn fsm-dot\n  \"Create the graphviz dot output for an fsm\"\n  [fsm]\n    (d/dot (fsm-dorothy fsm)))\n\n(defn- show-dorothy-fsm [fsm]\n  (d/show! (fsm-dot fsm)))\n\n(defn show-fsm\n  \"Display the fsm as a diagram using graphviz (see http://www.graphviz.org/)\"\n  [fsm]\n  (when (graphviz-installed?)\n    (show-dorothy-fsm fsm)))\n  \n\n(defn save-fsm-image\n    \"Save the state transition diagram for an fsm as a png.\nExpects the following parameters:\n  - fsm      - the fsm to render\n  - filename - the output file for the png.\"\n  [fsm filename]\n  (when (graphviz-installed?)\n    (d/save! (fsm-dot fsm) filename {:format :png}))\n  nil)\n\n"
  },
  {
    "path": "test/reduce_fsm_test.clj",
    "content": "(ns reduce-fsm-test  \n  (:use [clojure.test])\n  (:use [reduce-fsm])\n  (:import [java.awt.Frame]))\n\n(defn- test-save-line [state evt from-state to-state]\n  (conj state evt))\n\n(defsm log-search-fsm-test\n  [[:waiting-for-a\n    #\".*event a\" -> :waiting-for-b]\n   [:waiting-for-b\n    #\".*event b\" -> :waiting-for-c\n    #\".*event c\" -> {:action test-save-line} :waiting-for-a]\n   [:waiting-for-c\n    #\".*event c\" -> :waiting-for-a]])\n\n\n(deftest simple-fsm-behaviour\n  \n  (are [res acc events] (= res (log-search-fsm-test acc events))       \n       [\"5 event c\"]  []  [\"1 event a\" \"event x\" \"2 event b\" \"3 event c\" \"4 event a\" \"event x\" \"5 event c\" \"6 event a\"]\n       nil nil [\"0 event c\" \"1 event a\" \"2 event b\" \"3 event c\"]\n       [] [] [\"0 event c\" \"1 event a\" \"2 event b\" \"3 event c\" ])\n\n  (testing \"can specify initial states\"\n    (is  (= [] (log-search-fsm-test :waiting-for-c []  [\"4 event a\" \"event x\" \"5 event c\" \"6 event a\"]))))\n  \n  (testing \"invalid initial states throw\"\n    (is (thrown? RuntimeException (log-search-fsm-test :i-dont-exist []  [\"4 event a\" \"event x\" \"5 event c\" \"6 event a\"])))))\n   \n\n(deftest dispatch-with-acc\n  (let [an-fsm (fsm\n\t\t[[:waiting-for-a\n\t\t  [_ #\".*event a\"] -> :waiting-for-b]\n\t\t [:waiting-for-b\n\t\t  [_ #\".*event b\"] -> :waiting-for-c\n\t\t  [_ #\".*event c\"] -> {:action test-save-line} :waiting-for-a]\n\t\t [:waiting-for-c\n\t\t  [_ #\".*event c\"] -> :waiting-for-a]] :dispatch :event-and-acc)]\n\n  (are [res acc events] (= res (an-fsm acc events))       \n       [\"5 event c\"]  []  [\"1 event a\" \"event x\" \"2 event b\" \"3 event c\" \"4 event a\" \"event x\" \"5 event c\" \"6 event a\"]\n       nil nil [\"0 event c\" \"1 event a\" \"2 event b\" \"3 event c\"]\n       [] [] [\"0 event c\" \"1 event a\" \"2 event b\" \"3 event c\" ])\n  \n  ))\n\n(deftest single-dispatch-with-when\n  (let [save-to-state (fn [acc evt from to] (conj acc to))\n\tan-fsm (fsm\n\t\t[[:initial \n\t\t  (n :guard #(< % 5)) -> {:action save-to-state} :small\n\t\t  (n :guard even?) -> {:action save-to-state} :even]  ;; match guards take the last value if multiple match?\n\t\t [:small\n\t\t  1 -> {:action save-to-state} :initial]\n\t\t [:even\n\t\t  (n :guard odd?) -> {:action save-to-state} :initial]])]\n    (is (= [:even :initial :small :initial] (an-fsm [] [8 2 4 3 1 2 2 1])))))\n\n\n(deftest display-dorothy-fsm-test\n  (let [frame (#'reduce-fsm/show-dorothy-fsm log-search-fsm-test)]\n    (is (not (nil? frame)))\n    (when frame\n      (.dispose ^java.awt.Frame frame))\n    ))\n \n  \n(deftest exit-with-state-fn\n  (let [inc-val (fn [val & _] (inc val))\n\tpong (fn [val] (>= val 3))\n\tping-pong (fsm [[:ping  ;; we oscillate between 2 states, pong exits when the number of transitions >= 3\n\t\t\t _ -> {:action inc-val} pong]\n\t\t\t[pong\n\t\t\t _ -> {:action inc-val} :ping]])]\n    (is (= 3 (ping-pong 0 (range 100))))))\n    \n\n(deftest simple-fsm-seq\n  (let [emit-evt (fn [acc evt] evt)\n\tlog-seq (fsm-seq \n\t\t [[:waiting-for-a\n\t\t   #\".*event a\" -> :waiting-for-b]\n\t\t  [:waiting-for-b\n\t\t   #\".*event b\" -> :waiting-for-c\n\t\t   #\".*event c\" -> {:emit emit-evt} :waiting-for-a]\n\t\t  [:waiting-for-c\n\t\t   #\".*event c\" -> :waiting-for-a]])]\n\n    \n    (are [res events] (= res (doall (log-seq [] events)))\n\t [\"5 event c\"]  [\"1 event a\" \"event x\" \"2 event b\" \"3 event c\" \"4 event a\" \"event x\" \"5 event c\" \"6 event a\"]\n\t []  [\"1 event a\" \"2 event b\" \"3 event c\"]\n\t [\"2 event c\" \"4 event c\"]  [\"x na-event\" \"1 event a\" \"2 event c\" \"3 event a\" \"4 event c\"])))\n\n\n(deftest simple-fsm-seq-with-initial-state\n  (let [emit-evt (fn [acc evt] evt)\n\tlog-seq (fsm-seq \n\t\t [[:waiting-for-a\n\t\t   #\".*event a\" -> :waiting-for-b]\n\t\t  [:waiting-for-b\n\t\t   #\".*event b\" -> :waiting-for-c\n\t\t   #\".*event c\" -> {:emit emit-evt} :waiting-for-a]\n\t\t  [:waiting-for-c\n\t\t   #\".*event c\" -> :waiting-for-a]])]\n\n    (is (= [] (doall (log-seq :waiting-for-c  [] [\"4 event a\" \"event x\" \"5 event c\" \"6 event a\"]))))))\n  \n;;===================================================================================================\n;; fsm-filter tests\n\n(deftest simple-fsm-filter\n  (let [a-filter (fsm-filter [[:initial {:pass true}\n\t\t\t       3 -> :suppressing]\n\t\t\t      [:suppressing {:pass false}\n\t\t\t       6 -> :initial]])]\n    (is (= [1 2 6 1 2] (filter (a-filter) [1 2 3 4 5 1 2 6 1 2])))))\n\n(deftest simple-fsm-filter-with-inital-state\n  (let [a-filter (fsm-filter [[:initial {:pass true}\n\t\t\t       3 -> :suppressing]\n\t\t\t      [:suppressing {:pass false}\n\t\t\t       6 -> :initial]])]\n    (is (= [6 1 2] (filter (a-filter :suppressing nil) [1 2 3 4 5 1 2 6 1 2])))))\n\n(deftest fsm-filter-with-default-pass\n  (let [a-filter (fsm-filter [[:initial\n\t\t\t       3 -> :suppressing]\n\t\t\t      [:suppressing {:pass false}\n\t\t\t       6 -> :initial]])]\n    (is (= [1 2 6 1 2] (filter (a-filter) [1 2 3 4 5 1 2 6 1 2])))))\n       \n\n(deftest using-is-terminal\n  (let [inc-val (fn [val & _] (inc val))\n\tthe-fsm (fsm [[:start\n\t\t       \\a -> {:action inc-val} :found-a]\n\t\t      [:found-a\n\t\t       \\b -> {:action inc-val} :found-b\n\t\t       \\a -> {:action inc-val} :found-a\n\t\t       _ -> {:action inc-val} :start]\n\t\t      [:found-b {:is-terminal true}\n\t\t       _ -> {:action inc-val} :start]]\n\t\t     :default-acc 0)]\n    (is (= 2 (the-fsm \"..ababab\")))))\n\n\n(deftest doc-examples\n  ;; make sure all the examples in the documentation actually work\n  (let [inc-val (fn [val & _] (inc val))\n\tcount-ab (fsm [[:start\n\t\t\t\\a -> :found-a]\n\t\t       [:found-a\n\t\t\t\\a ->  :found-a\n\t\t\t\\b -> {:action inc-val} :start\n\t\t\t_ -> :start]])]\n    (is (= [2 0 1] (map (partial count-ab 0) [\"abaaabc\" \"aaacb\" \"bbbcab\"]))))\n\n  \n  (let [emit-evt (fn [acc evt] evt)\n\tlog-search (fsm-seq \n\t\t    [[:start\n\t\t      #\".*event a\" -> :found-a]\n\t\t     [:found-a\n\t\t      #\".*event b\" -> :found-b\n\t\t      #\".*event c\" -> {:emit emit-evt} :start]\n\t\t     [:found-b\n\t\t      #\".*event c\" -> :start]])]\n  \n    \n    (is (= [\"5 event c\" \"5 event c\"]\n\t     (take 2 (log-search (cycle [\"1 event a\"\n\t\t\t\t\t \"2 event b\"\n\t\t\t\t\t \"3 event c\"\n\t\t\t\t\t \"another event\"\n\t\t\t\t\t \"4 event a\"\n\t\t\t\t\t \"event x\"\n\t\t\t\t\t \"5 event c\"]))))))\n\t\t\t         \n    )  \n\n\n;;===================================================================================================\n;; fsm-inc tests\n\n(deftest simple-incremental-fsm\n  (let [an-fsm (fsm-inc [[:waiting-for-a\n                       \\a -> :waiting-for-b]\n                      [:waiting-for-b\n                       \\b -> :done\n                       _ -> :waiting-for-a]\n                      [:done {:is-terminal true}]])\n        fsm-state (atom (an-fsm))]\n\n    (is (= :waiting-for-a (:state @fsm-state)))\n\n    (swap! fsm-state fsm-event \\a)\n    (is (= :waiting-for-b (:state @fsm-state)))\n\n    (swap! fsm-state fsm-event \\b)\n    (is (= :done (:state @fsm-state)))))\n\n(deftest incremental-fsm-with-initial-state\n  (let [an-fsm (fsm-inc [[:waiting-for-a\n                       \\a -> :waiting-for-b]\n                      [:waiting-for-b\n                       \\b -> :done\n                       _ -> :waiting-for-a]\n                      [:done {:is-terminal true}]])\n        fsm-state (atom (an-fsm :waiting-for-b nil))]\n\n    (is (= :waiting-for-b (:state @fsm-state)))\n\n    (swap! fsm-state fsm-event \\b)\n    (is (= :done (:state @fsm-state)))))\n\n(defsm-inc inc-log-fsm\n  [[:waiting-for-a\n    #\".*event a\" -> :waiting-for-b]\n   [:waiting-for-b\n    #\".*event b\" -> :waiting-for-c\n    #\".*event c\" -> {:action test-save-line} :waiting-for-a]\n   [:waiting-for-c\n    #\".*event c\" -> :waiting-for-a]])\n\n\n(deftest simple-fsm-inc-behaviour \n  (are [res acc events] (= res (:value (reduce fsm-event (inc-log-fsm acc) events)))\n       [\"5 event c\"]  []  [\"1 event a\" \"event x\" \"2 event b\" \"3 event c\" \"4 event a\" \"event x\" \"5 event c\" \"6 event a\"]\n       nil nil [\"0 event c\" \"1 event a\" \"2 event b\" \"3 event c\"]\n       [] [] [\"0 event c\" \"1 event a\" \"2 event b\" \"3 event c\" ]))\n   \n(deftest dispatch-inc-with-acc\n  (let [an-fsm (fsm-inc\n\t\t[[:waiting-for-a\n\t\t  [_ #\".*event a\"] -> :waiting-for-b]\n\t\t [:waiting-for-b\n\t\t  [_ #\".*event b\"] -> :waiting-for-c\n\t\t  [_ #\".*event c\"] -> {:action test-save-line} :waiting-for-a]\n\t\t [:waiting-for-c\n\t\t  [_ #\".*event c\"] -> :waiting-for-a]] :dispatch :event-and-acc)]\n\n  (are [res acc events] (= res (:value (reduce fsm-event (an-fsm acc) events)))\n       [\"5 event c\"]  []  [\"1 event a\" \"event x\" \"2 event b\" \"3 event c\" \"4 event a\" \"event x\" \"5 event c\" \"6 event a\"]\n       nil nil [\"0 event c\" \"1 event a\" \"2 event b\" \"3 event c\"]\n       [] [] [\"0 event c\" \"1 event a\" \"2 event b\" \"3 event c\" ])))\n\n(deftest single-inc-dispatch-with-when\n  (let [an-fsm (fsm-inc\n\t\t[[:initial \n\t\t  (n :guard #(< % 5)) -> :small\n\t\t  (n :guard even?) -> :even]  ;; match guards take the last value if multiple match?\n\t\t [:small\n\t\t  1 -> :initial]\n\t\t [:even\n\t\t  (n :guard odd?) -> :initial]])]\n\n    (is (= [:initial :even :even :even :initial :small :small :small :initial] \n           (map :state (reductions fsm-event (an-fsm) [8 2 4 3 1 2 2 1]))))))\n\n  \n(deftest inc-exit-with-state-fn\n  (let [inc-val (fn [val & _] (inc val))\n\tpong (fn [val] (>= val 3))\n\tping-pong (fsm-inc [[:ping  ;; we oscillate between 2 states, pong exits when the number of transitions >= 3\n\t\t\t _ -> {:action inc-val} pong]\n\t\t\t[pong\n\t\t\t _ -> {:action inc-val} :ping]])]\n    (is (= 3\n           (:value\n            (first \n             (drop-while (complement :is-terminated?) \n                         (reductions fsm-event (ping-pong 0) (range 100)))))))))\n\n\n(deftest event-acc-vec-dispatch \n  (let [should-transition? (fn [[state event]] (= (* state 2) event))\n        event-is-even? (fn [[state event]] (even? event))\n        inc-count (fn [cnt & _ ] (inc cnt))\n        reset-count (fn [& _]  100)\n        even-example (fsm  \n\t   [[:start\n\t     [_ :guard should-transition?] -> {:action reset-count} :next-state\n\t     [_ :guard event-is-even?] -> {:action inc-count} :start]\n\t    [:next-state ,,,]]\n\t   :default-acc  0\n\t   :dispatch :event-acc-vec)]\n\n    (are [events res] (= res (even-example events))\n         [1 1 2] 1        ;;  (the number of even events)\n         [1 2 2 4] 100)))  ;; 0 (we transitioned to next state)\n  \n"
  }
]