|
Packit |
2cbdf3 |
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
|
|
Packit |
2cbdf3 |
{-# LANGUAGE PatternGuards #-}
|
|
Packit |
2cbdf3 |
module DFAMin (minimizeDFA) where
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
import AbsSyn
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
import Data.Map (Map)
|
|
Packit |
2cbdf3 |
import qualified Data.Map as Map
|
|
Packit |
2cbdf3 |
import Data.IntSet (IntSet)
|
|
Packit |
2cbdf3 |
import qualified Data.IntSet as IS
|
|
Packit |
2cbdf3 |
import Data.IntMap (IntMap)
|
|
Packit |
2cbdf3 |
import qualified Data.IntMap as IM
|
|
Packit |
2cbdf3 |
import Data.List as List
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
-- Hopcroft's Algorithm for DFA minimization (cut/pasted from Wikipedia):
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
-- P := {{all accepting states}, {all nonaccepting states}};
|
|
Packit |
2cbdf3 |
-- Q := {{all accepting states}};
|
|
Packit |
2cbdf3 |
-- while (Q is not empty) do
|
|
Packit |
2cbdf3 |
-- choose and remove a set A from Q
|
|
Packit |
2cbdf3 |
-- for each c in ∑ do
|
|
Packit |
2cbdf3 |
-- let X be the set of states for which a transition on c leads to a state in A
|
|
Packit |
2cbdf3 |
-- for each set Y in P for which X ∩ Y is nonempty do
|
|
Packit |
2cbdf3 |
-- replace Y in P by the two sets X ∩ Y and Y \ X
|
|
Packit |
2cbdf3 |
-- if Y is in Q
|
|
Packit |
2cbdf3 |
-- replace Y in Q by the same two sets
|
|
Packit |
2cbdf3 |
-- else
|
|
Packit |
2cbdf3 |
-- add the smaller of the two sets to Q
|
|
Packit |
2cbdf3 |
-- end;
|
|
Packit |
2cbdf3 |
-- end;
|
|
Packit |
2cbdf3 |
-- end;
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
minimizeDFA :: Ord a => DFA Int a -> DFA Int a
|
|
Packit |
2cbdf3 |
minimizeDFA dfa@ DFA { dfa_start_states = starts,
|
|
Packit |
2cbdf3 |
dfa_states = statemap
|
|
Packit |
2cbdf3 |
}
|
|
Packit |
2cbdf3 |
= DFA { dfa_start_states = starts,
|
|
Packit |
2cbdf3 |
dfa_states = Map.fromList states }
|
|
Packit |
2cbdf3 |
where
|
|
Packit |
2cbdf3 |
equiv_classes = groupEquivStates dfa
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
numbered_states = number (length starts) equiv_classes
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
-- assign each state in the minimized DFA a number, making
|
|
Packit |
2cbdf3 |
-- sure that we assign the numbers [0..] to the start states.
|
|
Packit |
2cbdf3 |
number _ [] = []
|
|
Packit |
2cbdf3 |
number n (ss:sss) =
|
|
Packit |
2cbdf3 |
case filter (`IS.member` ss) starts of
|
|
Packit |
2cbdf3 |
[] -> (n,ss) : number (n+1) sss
|
|
Packit |
2cbdf3 |
starts' -> zip starts' (repeat ss) ++ number n sss
|
|
Packit |
2cbdf3 |
-- if one of the states of the minimized DFA corresponds
|
|
Packit |
2cbdf3 |
-- to multiple starts states, we just have to duplicate
|
|
Packit |
2cbdf3 |
-- that state.
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
states = [
|
|
Packit |
2cbdf3 |
let old_states = map (lookup statemap) (IS.toList equiv)
|
|
Packit |
2cbdf3 |
accs = map fix_acc (state_acc (head old_states))
|
|
Packit |
2cbdf3 |
-- accepts should all be the same
|
|
Packit |
2cbdf3 |
out = IM.fromList [ (b, get_new old)
|
|
Packit |
2cbdf3 |
| State _ out <- old_states,
|
|
Packit |
2cbdf3 |
(b,old) <- IM.toList out ]
|
|
Packit |
2cbdf3 |
in (n, State accs out)
|
|
Packit |
2cbdf3 |
| (n, equiv) <- numbered_states
|
|
Packit |
2cbdf3 |
]
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
fix_acc acc = acc { accRightCtx = fix_rctxt (accRightCtx acc) }
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
fix_rctxt (RightContextRExp s) = RightContextRExp (get_new s)
|
|
Packit |
2cbdf3 |
fix_rctxt other = other
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
lookup m k = Map.findWithDefault (error "minimizeDFA") k m
|
|
Packit |
2cbdf3 |
get_new = lookup old_to_new
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
old_to_new :: Map Int Int
|
|
Packit |
2cbdf3 |
old_to_new = Map.fromList [ (s,n) | (n,ss) <- numbered_states,
|
|
Packit |
2cbdf3 |
s <- IS.toList ss ]
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
groupEquivStates :: (Ord a) => DFA Int a -> [IntSet]
|
|
Packit |
2cbdf3 |
groupEquivStates DFA { dfa_states = statemap }
|
|
Packit |
2cbdf3 |
= go init_p init_q
|
|
Packit |
2cbdf3 |
where
|
|
Packit |
2cbdf3 |
(accepting, nonaccepting) = Map.partition acc statemap
|
|
Packit |
2cbdf3 |
where acc (State as _) = not (List.null as)
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
nonaccepting_states = IS.fromList (Map.keys nonaccepting)
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
-- group the accepting states into equivalence classes
|
|
Packit |
2cbdf3 |
accept_map = {-# SCC "accept_map" #-}
|
|
Packit |
2cbdf3 |
foldl' (\m (n,s) -> Map.insertWith (++) (state_acc s) [n] m)
|
|
Packit |
2cbdf3 |
Map.empty
|
|
Packit |
2cbdf3 |
(Map.toList accepting)
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
-- accept_groups :: Ord s => [Set s]
|
|
Packit |
2cbdf3 |
accept_groups = map IS.fromList (Map.elems accept_map)
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
init_p = nonaccepting_states : accept_groups
|
|
Packit |
2cbdf3 |
init_q = accept_groups
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
-- map token T to
|
|
Packit |
2cbdf3 |
-- a map from state S to the list of states that transition to
|
|
Packit |
2cbdf3 |
-- S on token T
|
|
Packit |
2cbdf3 |
-- This is a cache of the information needed to compute x below
|
|
Packit |
2cbdf3 |
bigmap :: IntMap (IntMap [SNum])
|
|
Packit |
2cbdf3 |
bigmap = IM.fromListWith (IM.unionWith (++))
|
|
Packit |
2cbdf3 |
[ (i, IM.singleton to [from])
|
|
Packit |
2cbdf3 |
| (from, state) <- Map.toList statemap,
|
|
Packit |
2cbdf3 |
(i,to) <- IM.toList (state_out state) ]
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
-- incoming I A = the set of states that transition to a state in
|
|
Packit |
2cbdf3 |
-- A on token I.
|
|
Packit |
2cbdf3 |
incoming :: Int -> IntSet -> IntSet
|
|
Packit |
2cbdf3 |
incoming i a = IS.fromList (concat ss)
|
|
Packit |
2cbdf3 |
where
|
|
Packit |
2cbdf3 |
map1 = IM.findWithDefault IM.empty i bigmap
|
|
Packit |
2cbdf3 |
ss = [ IM.findWithDefault [] s map1
|
|
Packit |
2cbdf3 |
| s <- IS.toList a ]
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
-- The outer loop: recurse on each set in Q
|
|
Packit |
2cbdf3 |
go p [] = p
|
|
Packit |
2cbdf3 |
go p (a:q) = go1 0 p q
|
|
Packit |
2cbdf3 |
where
|
|
Packit |
2cbdf3 |
-- recurse on each token (0..255)
|
|
Packit |
2cbdf3 |
go1 256 p q = go p q
|
|
Packit |
2cbdf3 |
go1 i p q = go1 (i+1) p' q'
|
|
Packit |
2cbdf3 |
where
|
|
Packit |
2cbdf3 |
(p',q') = go2 p [] q
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
x = incoming i a
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
-- recurse on each set in P
|
|
Packit |
2cbdf3 |
go2 [] p' q = (p',q)
|
|
Packit |
2cbdf3 |
go2 (y:p) p' q
|
|
Packit |
2cbdf3 |
| IS.null i || IS.null d = go2 p (y:p') q
|
|
Packit |
2cbdf3 |
| otherwise = go2 p (i:d:p') q1
|
|
Packit |
2cbdf3 |
where
|
|
Packit |
2cbdf3 |
i = IS.intersection x y
|
|
Packit |
2cbdf3 |
d = IS.difference y x
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
q1 = replaceyin q
|
|
Packit |
2cbdf3 |
where
|
|
Packit |
2cbdf3 |
replaceyin [] =
|
|
Packit |
2cbdf3 |
if IS.size i < IS.size d then [i] else [d]
|
|
Packit |
2cbdf3 |
replaceyin (z:zs)
|
|
Packit |
2cbdf3 |
| z == y = i : d : zs
|
|
Packit |
2cbdf3 |
| otherwise = z : replaceyin zs
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
|
|
Packit |
2cbdf3 |
|