{-# language BangPatterns #-}
{-# language DeriveFunctor #-}
{-# language DerivingStrategies #-}
{-# language MagicHash #-}
{-# language RankNTypes #-}
{-# language ScopedTypeVariables #-}
{-# language UnboxedTuples #-}
module Automata.Dfsa
(
Dfsa
, evaluate
, union
, intersection
, acceptance
, rejection
, Builder
, State
, build
, state
, transition
, accept
) where
import Automata.Internal (Dfsa(..),State(..),union,intersection,acceptance,rejection,minimize)
import Data.Foldable (foldl',for_)
import Data.Primitive (Array)
import Data.Semigroup (Last(..))
import Control.Monad.ST (runST)
import qualified Data.Primitive.Contiguous as C
import qualified Data.Map.Interval.DBTSLL as DM
import qualified Data.Set.Unboxed as SU
evaluate :: (Foldable f, Ord t) => Dfsa t -> f t -> Bool
evaluate (Dfsa transitions finals) tokens = SU.member
(foldl' (\(active :: Int) token -> DM.lookup token (C.index transitions active)) 0 tokens)
finals
newtype Builder t s a = Builder (Int -> [Edge t] -> [Int] -> Result t a)
deriving stock (Functor)
instance Applicative (Builder t s) where
pure a = Builder (\i es fs -> Result i es fs a)
Builder f <*> Builder g = Builder $ \i es fs -> case f i es fs of
Result i' es' fs' x -> case g i' es' fs' of
Result i'' es'' fs'' y -> Result i'' es'' fs'' (x y)
instance Monad (Builder t s) where
Builder f >>= g = Builder $ \i es fs -> case f i es fs of
Result i' es' fs' a -> case g a of
Builder g' -> g' i' es' fs'
data Result t a = Result !Int ![Edge t] ![Int] a
deriving stock (Functor)
data Edge t = Edge !Int !Int !t !t
data EdgeDest t = EdgeDest !Int t t
build :: forall t a. (Bounded t, Ord t, Enum t) => (forall s. State s -> Builder t s a) -> Dfsa t
build fromStartState =
case state >>= fromStartState of
Builder f -> case f 0 [] [] of
Result totalStates edges final _ ->
let ts = runST $ do
transitions <- C.replicateM totalStates (DM.pure Nothing)
outbounds <- C.replicateM totalStates []
for_ edges $ \(Edge source destination lo hi) -> do
edgeDests0 <- C.read outbounds source
let !edgeDests1 = EdgeDest destination lo hi : edgeDests0
C.write outbounds source edgeDests1
(outbounds' :: Array [EdgeDest t]) <- C.unsafeFreeze outbounds
flip C.imapMutable' transitions $ \i _ ->
let dests = C.index outbounds' i
in mconcat
( map
(\(EdgeDest dest lo hi) -> DM.singleton Nothing lo hi (Just (Last dest)))
dests
)
C.unsafeFreeze transitions
in minimize (fmap (DM.map (maybe 0 getLast)) ts) (SU.fromList final)
state :: Builder t s (State s)
state = Builder $ \i edges final ->
Result (i + 1) edges final (State i)
accept :: State s -> Builder t s ()
accept (State n) = Builder $ \i edges final -> Result i edges (n : final) ()
transition ::
t
-> t
-> State s
-> State s
-> Builder t s ()
transition lo hi (State source) (State dest) =
Builder $ \i edges final -> Result i (Edge source dest lo hi : edges) final ()