Blame src/DFAMin.hs

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