Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions copilot-libraries/CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
2026-05-07
* Add stream analysis module. (#726)
* Add state machine module. (#728)

2026-03-07
* Version bump (4.7). (#714)
Expand Down
1 change: 1 addition & 0 deletions copilot-libraries/copilot-libraries.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ library
, Copilot.Library.Clocks
, Copilot.Library.LTL
, Copilot.Library.PTLTL
, Copilot.Library.StateMachines
, Copilot.Library.Statistics
, Copilot.Library.RegExp
, Copilot.Library.Utils
Expand Down
45 changes: 45 additions & 0 deletions copilot-libraries/src/Copilot/Library/StateMachines.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{-# LANGUAGE ScopedTypeVariables #-}
-- | Simulate state machines using streams.
module Copilot.Library.StateMachines where

import Copilot.Language (Stream, Typed, constant, ifThenElse, (&&), (++), (==))
import Prelude hiding ((&&), (++), (==))

-- | A definition of a state machine where some elements are defined
-- as streams.
--
-- A state machine is defined by an initial state, a final state, a no
-- transition stream (true when tere is no input coming in), a list of
-- transitions, and a bad state.
type StateMachine a = (a, a, Stream Bool, [(a, Stream Bool, a)], a)

-- | Produce a stream that, at any given time, contains the current state of
-- the state machine.
stateMachine :: forall a . (Eq a, Typed a) => StateMachine a -> Stream a
stateMachine (initial, final, noInputData, transitions, bad) = state
where
state = ifThenElses transitions
previousState = [initial] ++ state

ifThenElses :: [(a, Stream Bool, a)] -> Stream a
ifThenElses [] =
ifThenElse (previousState == constant final && noInputData)
(constant final)
(constant bad)

ifThenElses ((s1, i, s2):ss) =
ifThenElse
(previousState == constant s1 && i)
(constant s2)
(ifThenElses ss)

-- | Produce a stream that, at any given time, contains the current state of
-- the state machine as the numeric representation of an enum.
stateMachineEnum :: (Eq b, Typed b, Num b, Enum a)
=> StateMachine a
-> Stream b
stateMachineEnum (initial, final, noInputData, transitions, bad) =
stateMachine (fe initial, fe final, noInputData, transitionsE, fe bad)
where
transitionsE = map (\(s1, t, s2) -> (fe s1, t, fe s2)) transitions
fe = fromIntegral . fromEnum