Skip to content

Commit

Permalink
Merge pull request #49 from AlecTroemel/fsm-fix
Browse files Browse the repository at this point in the history
track state transition methods, remove them when leave state
  • Loading branch information
AlecTroemel authored Dec 29, 2023
2 parents 8517c1b + d23087a commit 0121328
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 30 deletions.
63 changes: 35 additions & 28 deletions junk-drawer/fsm.janet
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,23 @@ Check out the docs of that fn for more!
(defn- current-node-call [self fn-name & args]
""
(when-let [current-node (:get-current-state self)
node-fn (get-in current-node [:data fn-name] nil)
leave-exists (not (nil? node-fn))]
node-fn (get-in current-node [:data fn-name] nil)
leave-exists (not (nil? node-fn))]
(node-fn self ;args)))

(defn- apply-edges-functions [self]
"Create functions on self for each edge in the current node"
# clear out old transition methods
(each key (get self :current-transition-methods [])
(put self key nil))
(put self :current-transition-methods @[])

(when-let [current-node (:get-current-state self)
edges (current-node :edges)]
edges (current-node :edges)]
(each (edge-name edge) (pairs edges)
(array/push (self :current-transition-methods) edge-name)
(put self edge-name
(fn [self & args] (:goto self (get edge :to) ;args)))))
(fn [self & args] (:goto self (get edge :to) ;args)))))

self)

Expand All @@ -38,7 +44,7 @@ Check out the docs of that fn for more!

# apply data to root of fsm
(let [current-node (:get-current-state self)
{:data data} current-node]
{:data data} current-node]
(each (key val) (pairs data)
(array/push (self :current-data-keys) key)
(put self key val)))
Expand All @@ -64,20 +70,21 @@ Check out the docs of that fn for more!

(def FSM
(merge digraph/Graph
@{:current @{}
:current-data-keys @[]
:visited @{}
:get-current-state get-current-state
:current-node-call current-node-call
:apply-edges-functions apply-edges-functions
:apply-data-to-root apply-data-to-root
:goto goto
:add-state (get digraph/Graph :add-node)}))
@{:current @{}
:current-data-keys @[]
:current-transition-methods @[]
:visited @{}
:get-current-state get-current-state
:current-node-call current-node-call
:apply-edges-functions apply-edges-functions
:apply-data-to-root apply-data-to-root
:goto goto
:add-state (get digraph/Graph :add-node)}))

(defn create [& states]
"Create a new FSM from the given states."
(table/setproto (digraph/create ;states)
FSM))
FSM))

(def state digraph/node)
(def transition digraph/edge)
Expand All @@ -99,15 +106,15 @@ Check out the docs of that fn for more!
time the state is visited.
(fsm/define colors-fsm
(state :green
:enter (fn [self] (print "entering green"))
:leave (fn [self] (print "entering leaving")))
(transition :next :green :yellow)
(fsm/state :green
:enter (fn [self from] (print "entering green"))
:leave (fn [self to] (print "entering leaving")))
(fsm/transition :next :green :yellow)
(state :yellow
:init (fn [self] (print "visiting yellow for the first time"))
:enter (fn [self] (print "entering yellow")))
(transition :prev :yellow :green))
(fsm/state :yellow
:init (fn [self] (print "visiting yellow for the first time"))
:enter (fn [self from] (print "entering yellow")))
(fsm/transition :prev :yellow :green))
(def *colors* (colors-fsm :green))
Expand All @@ -127,8 +134,8 @@ Check out the docs of that fn for more!

~(defn ,name [&opt initial-state]
(-> (,create ,;states)
(put :current initial-state)
(put :__validate__ (fn [& args] true))
(put :__id__ ,(keyword name))
(:apply-edges-functions)
(:apply-data-to-root))))
(put :current initial-state)
(put :__validate__ (fn [& args] true))
(put :__id__ ,(keyword name))
(:apply-edges-functions)
(:apply-data-to-root))))
8 changes: 6 additions & 2 deletions test/unit/fsm-test.janet
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,23 @@
(fsm/state :c
:enter (fn [self from] (set enter-c-called true))
:leave (fn [self to] (set leave-c-called true)))
(fsm/transition :goto-a :b :a))
(fsm/transition :goto-a :c :a))

(let [*state* (a2b :a)]
(test/assert (= :a (*state* :current)) "Start at state A.")
(:goto-b *state*)
(test/assert (not (has-key? *state* :goto-b)) "State A transition methods removed when state left")
(test/assert (not (has-key? *state* :goto-c)) "State A transition methods removed when state left")

(test/assert (= :b (*state* :current)) "In state B after moving to it.")
(test/assert (= "value" (*state* :field)) "Copies state data to root of FSM.")

(:goto-a *state* "arg data")
(test/assert (= :a (*state* :current)) "Move back to state A, with arg passed in.")
(test/assert (= (*state* :field) nil) "no more data from state B.")

(test/assert-error "transition fn does not exist"
(:goto-dne *state*))
(:goto-dne *state*))

(:goto-c *state*)
(test/assert enter-c-called "Enter fn for state C called.")
Expand Down

0 comments on commit 0121328

Please sign in to comment.