Implement Dfa using Fa
This commit is contained in:
parent
33ed11abc4
commit
e273c8bc75
3 changed files with 62 additions and 111 deletions
|
|
@ -1,21 +1,12 @@
|
||||||
module Rextra.Dfa (
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
-- * Deterministic Finite Automaton
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
Dfa
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
, State(..)
|
|
||||||
, StateMap
|
module Rextra.Dfa
|
||||||
-- ** Constructing
|
( Dfa
|
||||||
, dfa
|
, dfa
|
||||||
, dfa'
|
, State(..)
|
||||||
-- ** Properties
|
|
||||||
, stateMap
|
|
||||||
, entryState
|
|
||||||
, exitStates
|
|
||||||
, transitionsByState
|
, transitionsByState
|
||||||
-- ** Executing
|
|
||||||
, transition
|
|
||||||
, execute
|
|
||||||
-- ** Renaming
|
|
||||||
, rename
|
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.List
|
import Data.List
|
||||||
|
|
@ -23,105 +14,40 @@ import qualified Data.Map.Strict as Map
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
import Data.Tuple
|
import Data.Tuple
|
||||||
|
|
||||||
|
import Rextra.Fa
|
||||||
import Rextra.Util
|
import Rextra.Util
|
||||||
|
|
||||||
{-
|
-- State stuff
|
||||||
- Types
|
|
||||||
-}
|
|
||||||
|
|
||||||
data Dfa s t = Dfa
|
|
||||||
{ stateMap :: StateMap s t
|
|
||||||
, entryState :: s
|
|
||||||
} deriving (Show)
|
|
||||||
|
|
||||||
getState :: (Ord s) => Dfa s t -> s -> State s t
|
|
||||||
getState dfa s = stateMap dfa Map.! s
|
|
||||||
|
|
||||||
exitStates :: (Ord s) => Dfa s t -> Set.Set s
|
|
||||||
exitStates dfa = Set.fromList
|
|
||||||
. map fst
|
|
||||||
. filter (accepting . snd)
|
|
||||||
. Map.assocs
|
|
||||||
$ stateMap dfa
|
|
||||||
|
|
||||||
data State s t = State
|
data State s t = State
|
||||||
{ transitions :: Map.Map t s
|
{ transitions :: Map.Map t s
|
||||||
, defaultTransition :: s
|
, defaultTransition :: s
|
||||||
, accepting :: Bool
|
|
||||||
} deriving (Show)
|
} deriving (Show)
|
||||||
|
|
||||||
type StateMap s t = Map.Map s (State s t)
|
instance FaState State where
|
||||||
|
canReach State{transitions, defaultTransition} =
|
||||||
|
Set.fromList $ defaultTransition : Map.elems transitions
|
||||||
|
|
||||||
fromMonoidalList :: (Monoid m, Ord k) => [(k, m)] -> Map.Map k m
|
transitionsByState :: (Ord s, Ord t) => State s t -> Map.Map s (Set.Set t)
|
||||||
fromMonoidalList = foldl' insertMonoidal Map.empty
|
transitionsByState = groupByFirst . map swap . Map.assocs . transitions
|
||||||
where
|
|
||||||
insertMonoidal :: (Monoid m, Ord k) => Map.Map k m -> (k, m) -> Map.Map k m
|
|
||||||
insertMonoidal map (k, m) = Map.insertWith mappend k m map
|
|
||||||
|
|
||||||
groupByFirst :: (Ord a, Ord b) => [(a, b)] -> Map.Map a (Set.Set b)
|
-- Dfa stuff
|
||||||
groupByFirst pairs =
|
|
||||||
let prepared = map (\(a, b) -> (a, Set.singleton b)) pairs
|
|
||||||
in fromMonoidalList prepared
|
|
||||||
|
|
||||||
transitionsByState :: (Ord s, Ord t) => Map.Map t s -> Map.Map s (Set.Set t)
|
type Dfa s t = Fa State s t
|
||||||
transitionsByState = groupByFirst . map swap . Map.assocs
|
|
||||||
|
|
||||||
{-
|
instance (Ord s) => Executable (Fa State s) s where
|
||||||
- Constructing
|
startState = entryState
|
||||||
-}
|
transition = dfaTransition
|
||||||
|
accepts a s = s `Set.member` exitStates a
|
||||||
|
|
||||||
integrityCheck :: (Ord s) => Dfa s t -> Bool
|
dfaTransition :: (Ord s, Ord t) => Dfa s t -> s -> t -> s
|
||||||
integrityCheck dfa =
|
dfaTransition a s t =
|
||||||
let states = Map.elems $ stateMap dfa
|
let state = getState a s
|
||||||
transitionStates = concatMap (Map.elems . transitions) states
|
|
||||||
defaultTransitionStates = map defaultTransition states
|
|
||||||
referencedStates = Set.fromList $ concat [[entryState dfa], transitionStates, defaultTransitionStates]
|
|
||||||
in referencedStates `Set.isSubsetOf` Map.keysSet (stateMap dfa)
|
|
||||||
|
|
||||||
dfa :: (Ord s) => StateMap s t -> s -> Maybe (Dfa s t)
|
|
||||||
dfa stateMap entryState =
|
|
||||||
let myDfa = Dfa{stateMap=stateMap, entryState=entryState}
|
|
||||||
in if integrityCheck myDfa then Just myDfa else Nothing
|
|
||||||
|
|
||||||
dfa' :: (Ord s, Ord t) => [(s, [(t, s)], s, Bool)] -> s -> Maybe (Dfa s t)
|
|
||||||
dfa' states entryState =
|
|
||||||
let stateList = map (\(s, ts, dt, a) -> (s, State (Map.fromList ts) dt a)) states
|
|
||||||
in dfa (Map.fromList stateList) entryState
|
|
||||||
|
|
||||||
{-
|
|
||||||
- Executing
|
|
||||||
-}
|
|
||||||
|
|
||||||
transition :: (Ord s, Ord t) => Dfa s t -> s -> t -> s
|
|
||||||
transition dfa s t =
|
|
||||||
let state = getState dfa s
|
|
||||||
in case transitions state Map.!? t of
|
in case transitions state Map.!? t of
|
||||||
(Just nextState) -> nextState
|
(Just nextState) -> nextState
|
||||||
Nothing -> defaultTransition state
|
Nothing -> defaultTransition state
|
||||||
|
|
||||||
execute :: (Ord s, Ord t) => Dfa s t -> [t] -> Bool
|
dfa :: (Ord s, Ord t) => [(s, [(t, s)], s)] -> s -> [s] -> Maybe (Dfa s t)
|
||||||
execute dfa tokens =
|
dfa states entryState exitStates =
|
||||||
let finalState = foldl' (transition dfa) (entryState dfa) tokens
|
let stateList = map (\(s, ts, dt) -> (s, State (Map.fromList ts) dt)) states
|
||||||
in accepting $ getState dfa finalState
|
in fa (Map.fromList stateList) entryState (Set.fromList exitStates)
|
||||||
|
|
||||||
{-
|
|
||||||
- Renaming
|
|
||||||
-}
|
|
||||||
|
|
||||||
renameState :: (Ord s, Ord t) => State s t -> Rename s (State Int t)
|
|
||||||
renameState state = do
|
|
||||||
newTransitions <- renameValues getName $ transitions state
|
|
||||||
newDefaultTransition <- getName $ defaultTransition state
|
|
||||||
pure $ State { transitions = newTransitions
|
|
||||||
, defaultTransition = newDefaultTransition
|
|
||||||
, accepting = accepting state
|
|
||||||
}
|
|
||||||
|
|
||||||
renameAssoc :: (Ord s, Ord t) => (s, State s t) -> Rename s (Int, State Int t)
|
|
||||||
renameAssoc (name, state) = (,) <$> getName name <*> renameState state
|
|
||||||
|
|
||||||
rename :: (Ord s, Ord t) => Dfa s t -> Dfa Int t
|
|
||||||
rename dfa = doRename $ do
|
|
||||||
newStateMap <- renameMap renameAssoc $ stateMap dfa
|
|
||||||
newEntryState <- getName $ entryState dfa
|
|
||||||
pure $ Dfa { stateMap = newStateMap, entryState = newEntryState }
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@
|
||||||
-- | This module contains ways to represent finite automata and their
|
-- | This module contains ways to represent finite automata and their
|
||||||
-- execution/evaluation.
|
-- execution/evaluation.
|
||||||
|
|
||||||
module Rextra.Fa
|
module Rextra.Fa (
|
||||||
( FaState(..)
|
-- * Finite automata
|
||||||
|
FaState(..)
|
||||||
, Fa
|
, Fa
|
||||||
, fa
|
, fa
|
||||||
, stateMap
|
, stateMap
|
||||||
|
|
@ -13,6 +14,7 @@ module Rextra.Fa
|
||||||
, exitStates
|
, exitStates
|
||||||
, states
|
, states
|
||||||
, getState
|
, getState
|
||||||
|
-- * Executing automata
|
||||||
, Executable(..)
|
, Executable(..)
|
||||||
, transitionAll
|
, transitionAll
|
||||||
, execute
|
, execute
|
||||||
|
|
@ -22,11 +24,15 @@ import Data.List
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
|
|
||||||
|
{-
|
||||||
|
- Finite automata
|
||||||
|
-}
|
||||||
|
|
||||||
-- | The state of a finite automaton.
|
-- | The state of a finite automaton.
|
||||||
class FaState state where
|
class FaState state where
|
||||||
-- | All the states that can be reached (by any sort of transition)
|
-- | All the states that can be reached (by any sort of transition)
|
||||||
-- from this state.
|
-- from this state.
|
||||||
canReach :: state s t -> Set.Set s
|
canReach :: (Ord s) => state s t -> Set.Set s
|
||||||
|
|
||||||
-- | A finite automaton.
|
-- | A finite automaton.
|
||||||
data Fa state s t = Fa
|
data Fa state s t = Fa
|
||||||
|
|
@ -38,7 +44,7 @@ data Fa state s t = Fa
|
||||||
, exitStates :: Set.Set s
|
, exitStates :: Set.Set s
|
||||||
-- ^ The automaton's accepting states, i. e. the states that
|
-- ^ The automaton's accepting states, i. e. the states that
|
||||||
-- determine whether the automaton accepts a certain word.
|
-- determine whether the automaton accepts a certain word.
|
||||||
}
|
} deriving (Show)
|
||||||
|
|
||||||
-- | @'states' fa@ are the identifiers of all states contained in @fa@.
|
-- | @'states' fa@ are the identifiers of all states contained in @fa@.
|
||||||
states :: Fa state s t -> Set.Set s
|
states :: Fa state s t -> Set.Set s
|
||||||
|
|
@ -67,19 +73,23 @@ fa stateMap entryState exitStates =
|
||||||
let potentialFa = Fa{stateMap, entryState, exitStates}
|
let potentialFa = Fa{stateMap, entryState, exitStates}
|
||||||
in if integrityCheck potentialFa then Just potentialFa else Nothing
|
in if integrityCheck potentialFa then Just potentialFa else Nothing
|
||||||
|
|
||||||
|
{-
|
||||||
|
- Executing
|
||||||
|
-}
|
||||||
|
|
||||||
-- | A special type class for automata that can be executed. These
|
-- | A special type class for automata that can be executed. These
|
||||||
-- automata must not necessarily be finite.
|
-- automata must not necessarily be finite.
|
||||||
class Executable a execState where
|
class Executable a execState where
|
||||||
-- | The state at which execution of the automaton begins.
|
-- | The state at which execution of the automaton begins.
|
||||||
startState :: a s t -> execState
|
startState :: a t -> execState
|
||||||
-- | A function that determines the automaton's next state based on
|
-- | A function that determines the automaton's next state based on
|
||||||
-- a token.
|
-- a token.
|
||||||
transition :: a s t -> execState -> t -> execState
|
transition :: (Ord t) => a t -> execState -> t -> execState
|
||||||
-- | Whether the automaton acceps
|
-- | Whether the automaton accepts the execution state.
|
||||||
accepts :: a s t -> execState -> Bool
|
accepts :: a t -> execState -> Bool
|
||||||
|
|
||||||
-- | Perform all transitions corresponding to a word (or list) of tokens, in order.
|
-- | Perform all transitions corresponding to a word (or list) of tokens, in order.
|
||||||
transitionAll :: (Executable a execState) => a s t -> execState -> [t] -> execState
|
transitionAll :: (Executable a execState, Ord t) => a t -> execState -> [t] -> execState
|
||||||
transitionAll a = foldl' (transition a)
|
transitionAll a = foldl' (transition a)
|
||||||
|
|
||||||
-- | Like 'transitionAll', starting with the automaton's 'startState'.
|
-- | Like 'transitionAll', starting with the automaton's 'startState'.
|
||||||
|
|
@ -88,5 +98,5 @@ transitionAll a = foldl' (transition a)
|
||||||
-- 'accepts' like this:
|
-- 'accepts' like this:
|
||||||
--
|
--
|
||||||
-- > a `accepts` execute a w
|
-- > a `accepts` execute a w
|
||||||
execute :: (Executable a execState) => a s t -> [t] -> execState
|
execute :: (Executable a execState, Ord t) => a t -> [t] -> execState
|
||||||
execute a = transitionAll a (startState a)
|
execute a = transitionAll a (startState a)
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,14 @@ module Rextra.Util
|
||||||
, renameMap
|
, renameMap
|
||||||
, renameKeys
|
, renameKeys
|
||||||
, renameValues
|
, renameValues
|
||||||
|
-- * Grouping
|
||||||
|
, fromMonoidalList
|
||||||
|
, groupByFirst
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Control.Monad.Trans.State
|
import Control.Monad.Trans.State
|
||||||
|
import Data.List
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
|
|
||||||
|
|
@ -54,3 +58,14 @@ renameKeys f = renameMap (\(k, v) -> (,v) <$> f k)
|
||||||
|
|
||||||
renameValues :: (Ord k) => (v1 -> Rename n v2) -> Map.Map k v1 -> Rename n (Map.Map k v2)
|
renameValues :: (Ord k) => (v1 -> Rename n v2) -> Map.Map k v1 -> Rename n (Map.Map k v2)
|
||||||
renameValues f = renameMap (\(k, v) -> (k,) <$> f v)
|
renameValues f = renameMap (\(k, v) -> (k,) <$> f v)
|
||||||
|
|
||||||
|
fromMonoidalList :: (Monoid m, Ord k) => [(k, m)] -> Map.Map k m
|
||||||
|
fromMonoidalList = foldl' insertMonoidal Map.empty
|
||||||
|
where
|
||||||
|
insertMonoidal :: (Monoid v, Ord k) => Map.Map k v -> (k, v) -> Map.Map k v
|
||||||
|
insertMonoidal m (k, v) = Map.insertWith mappend k v m
|
||||||
|
|
||||||
|
groupByFirst :: (Ord a, Ord b) => [(a, b)] -> Map.Map a (Set.Set b)
|
||||||
|
groupByFirst pairs =
|
||||||
|
let prepared = map (\(a, b) -> (a, Set.singleton b)) pairs
|
||||||
|
in fromMonoidalList prepared
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue