1
1
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
2
- {-# LANGUAGE PatternGuards #-}
2
+
3
+ {-# LANGUAGE PatternGuards #-}
4
+ {-# LANGUAGE ScopedTypeVariables #-}
5
+ {-# LANGUAGE TupleSections #-}
6
+
3
7
module DFAMin (minimizeDFA ) where
4
8
5
9
import AbsSyn
@@ -10,7 +14,7 @@ import Data.IntSet (IntSet)
10
14
import qualified Data.IntSet as IS
11
15
import Data.IntMap (IntMap )
12
16
import qualified Data.IntMap as IM
13
- import Data.List as List
17
+ import qualified Data.List as List
14
18
15
19
16
20
-- Hopcroft's Algorithm for DFA minimization (cut/pasted from Wikipedia):
@@ -31,28 +35,32 @@ import Data.List as List
31
35
-- end;
32
36
-- end;
33
37
34
- minimizeDFA :: Ord a => DFA Int a -> DFA Int a
38
+ minimizeDFA :: forall a . Ord a => DFA Int a -> DFA Int a
35
39
minimizeDFA dfa@ DFA { dfa_start_states = starts,
36
40
dfa_states = statemap
37
41
}
38
42
= DFA { dfa_start_states = starts,
39
43
dfa_states = Map. fromList states }
40
44
where
45
+ equiv_classes :: [EquivalenceClass ]
41
46
equiv_classes = groupEquivStates dfa
42
47
48
+ numbered_states :: [(Int , EquivalenceClass )]
43
49
numbered_states = number (length starts) equiv_classes
44
50
45
51
-- assign each state in the minimized DFA a number, making
46
52
-- sure that we assign the numbers [0..] to the start states.
53
+ number :: Int -> [EquivalenceClass ] -> [(Int , EquivalenceClass )]
47
54
number _ [] = []
48
55
number n (ss: sss) =
49
56
case filter (`IS.member` ss) starts of
50
57
[] -> (n,ss) : number (n+ 1 ) sss
51
- starts' -> zip starts' ( repeat ss) ++ number n sss
58
+ starts' -> map (, ss) starts' ++ number n sss
52
59
-- if one of the states of the minimized DFA corresponds
53
60
-- to multiple starts states, we just have to duplicate
54
61
-- that state.
55
62
63
+ states :: [(Int , State Int a )]
56
64
states = [
57
65
let old_states = map (lookup statemap) (IS. toList equiv)
58
66
accs = map fix_acc (state_acc (head old_states))
@@ -64,38 +72,50 @@ minimizeDFA dfa@DFA { dfa_start_states = starts,
64
72
| (n, equiv) <- numbered_states
65
73
]
66
74
75
+ fix_acc :: Accept a -> Accept a
67
76
fix_acc acc = acc { accRightCtx = fix_rctxt (accRightCtx acc) }
68
77
78
+ fix_rctxt :: RightContext SNum -> RightContext SNum
69
79
fix_rctxt (RightContextRExp s) = RightContextRExp (get_new s)
70
80
fix_rctxt other = other
71
81
82
+ lookup :: Ord k => Map k v -> k -> v
72
83
lookup m k = Map. findWithDefault (error " minimizeDFA" ) k m
84
+
85
+ get_new :: Int -> Int
73
86
get_new = lookup old_to_new
74
87
75
88
old_to_new :: Map Int Int
76
89
old_to_new = Map. fromList [ (s,n) | (n,ss) <- numbered_states,
77
90
s <- IS. toList ss ]
78
91
92
+ type EquivalenceClass = IntSet
79
93
80
- groupEquivStates :: ( Ord a ) => DFA Int a -> [IntSet ]
94
+ groupEquivStates :: forall a . Ord a => DFA Int a -> [EquivalenceClass ]
81
95
groupEquivStates DFA { dfa_states = statemap }
82
96
= go init_p init_q
83
97
where
98
+ accepting , nonaccepting :: Map Int (State Int a )
84
99
(accepting, nonaccepting) = Map. partition acc statemap
85
100
where acc (State as _) = not (List. null as)
86
101
102
+ nonaccepting_states :: EquivalenceClass
87
103
nonaccepting_states = IS. fromList (Map. keys nonaccepting)
88
104
89
105
-- group the accepting states into equivalence classes
106
+ accept_map :: Map [Accept a ] [Int ]
90
107
accept_map = {-# SCC "accept_map" #-}
91
- foldl' (\ m (n,s) -> Map. insertWith (++) (state_acc s) [n] m)
108
+ List. foldl' (\ m (n,s) -> Map. insertWith (++) (state_acc s) [n] m)
92
109
Map. empty
93
110
(Map. toList accepting)
94
111
95
- -- accept_groups :: Ord s => [Set s ]
112
+ accept_groups :: [ EquivalenceClass ]
96
113
accept_groups = map IS. fromList (Map. elems accept_map)
97
114
98
- init_p = nonaccepting_states : accept_groups
115
+ init_p , init_q :: [EquivalenceClass ]
116
+ init_p -- Issue #71: each EquivalenceClass needs to be a non-empty set
117
+ | IS. null nonaccepting_states = accept_groups
118
+ | otherwise = nonaccepting_states : accept_groups
99
119
init_q = accept_groups
100
120
101
121
-- map token T to
@@ -118,6 +138,7 @@ groupEquivStates DFA { dfa_states = statemap }
118
138
| s <- IS. toList a ]
119
139
120
140
-- The outer loop: recurse on each set in Q
141
+ go :: [EquivalenceClass ] -> [EquivalenceClass ] -> [EquivalenceClass ]
121
142
go p [] = p
122
143
go p (a: q) = go1 0 p q
123
144
where
@@ -145,6 +166,3 @@ groupEquivStates DFA { dfa_states = statemap }
145
166
replaceyin (z: zs)
146
167
| z == y = i : d : zs
147
168
| otherwise = z : replaceyin zs
148
-
149
-
150
-
0 commit comments