-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdka-2-mka.hs
279 lines (231 loc) · 9.72 KB
/
dka-2-mka.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
-- DFA-2-MKA, project for FLP course at FIT BUT
-- Martin Krajnak, [email protected]
import System.Environment
import System.IO
import Data.List
data Transition = Transition {
currState:: Int,
symbol :: String,
nextState :: Int
} deriving (Show, Eq, Ord)
data DFA = DFA { -- Deterministiv Finite State Automata - internal representation
states :: [Int],
startState :: Int,
endStates :: [Int],
transitions :: [Transition],
alphabet :: [String]
} deriving Show
view :: DFA -> IO()
view dfa = do
putStrLn (getStatesString (getStates dfa))
putStrLn (show (getStartState dfa))
putStrLn (getStatesString (getEndStates dfa))
mapM_ putStrLn $ getTransitions dfa
getStates :: DFA -> [Int]
getStates (DFA states _ _ _ _) = states
getStatesString :: [Int] -> String
getStatesString [] = ""
getStatesString (s:[]) = show s
getStatesString (s:xs) = show s ++ "," ++ getStatesString xs
getStartState :: DFA -> Int
getStartState (DFA _ startState _ _ _) = startState
getEndStates :: DFA -> [Int]
getEndStates (DFA _ _ endStates _ _) = endStates
getTrans :: DFA -> [Transition]
getTrans (DFA _ _ _ t _) = t
-- Returns the list of all transitions as a list of Strings (1 transition per line)
getTransitions :: DFA -> [String]
getTransitions (DFA _ _ _ [] _) = []
getTransitions (DFA as st e ((Transition c s n):ts) a) =
(show c++","++s++","++ show n):getTransitions (DFA as st e (ts) a)
getAlphabet :: DFA -> [String]
getAlphabet (DFA _ _ _ _ alphabet) = alphabet
getStringToDelim :: Char -> String -> String
getStringToDelim _ [] = []
getStringToDelim d (x:xs)
| x == d = []
| otherwise = x : getStringToDelim d xs
jumpToDelim :: Char -> String -> [String]
jumpToDelim _ [] = []
jumpToDelim d (x:xs)
| x == d = getSeparatedSubStrings xs
| otherwise = jumpToDelim d xs
getSeparatedSubStrings :: String -> [String]
getSeparatedSubStrings [] = []
getSeparatedSubStrings (x:xs) = getStringToDelim ',' (x:xs) : (jumpToDelim ',' xs)
parseTransition :: [String] -> [Transition]
parseTransition [] = []
parseTransition (x:xs) = if length x > 0
then Transition {
currState = read ((getSeparatedSubStrings x) !! 0) :: Int,
symbol = (getSeparatedSubStrings x) !! 1,
nextState = read ((getSeparatedSubStrings x) !! 2) :: Int
} : parseTransition xs
else error "State cannot be defined by an empty line"
parseAlphabet :: [String] -> [String]
parseAlphabet [] = []
parseAlphabet (x:xs) = getSymbol x:parseAlphabet xs
getSymbol :: String -> String
getSymbol transition = (getSeparatedSubStrings transition) !! 1
parseDFA :: [String] -> DFA
parseDFA (f:s:t:xs) = DFA {
states = map(\x -> read x :: Int)(getSeparatedSubStrings f), -- states are declared on the first line
startState = read s :: Int, -- start state are declared on the second line
endStates = map(\x -> read x :: Int)(getSeparatedSubStrings t), -- end states are declared on the third line
transitions = parseTransition xs, -- the rest of lines are output
alphabet = sort $ nub $ parseAlphabet xs
}
getFileArg :: [String] -> String
getFileArg x = if (length x == 2)
then x !! 1
else ""
checkArgs :: String -> Bool
checkArgs arg = elem arg ["-i", "-t"]
-- Reads DFA either from the specified file or stdin
getAutomata :: String -> IO String
getAutomata fileName = do
if fileName == ""
then getContents
else readFile fileName
sortByLen :: [[a]] -> [[a]]
sortByLen l = sortBy(\a b -> compare(length a) (length b)) l
sortT :: [Transition] -> [Transition]
sortT l = sortBy(\a b -> compare a b) l
start_minimization :: DFA -> IO()
start_minimization dfa = view $ rebuild reduced complete
where
sink = maximum(getStates dfa)+1
complete = makeComplete dfa sink
start = [(getEndStates complete), (getStates complete) \\ (getEndStates complete)]
reduced = zip [1..] $ compareResults [] start complete
rebuild :: [(Int,[Int])] -> DFA -> DFA
rebuild reduced old@(DFA _ start end trans alpha) = DFA {
states = sort $ map(\x -> fst x) reduced,
startState = renameState reduced start,
endStates = sort $nub $ map(\x -> renameState reduced x ) end,
transitions = sortT $ nub $ renameTransitions reduced trans,
alphabet = alpha
}
renameTransitions :: [(Int,[Int])] -> [Transition] -> [Transition]
renameTransitions _ [] = []
renameTransitions r ((Transition c s e):xs) =
(Transition (renameState r c) s (renameState r e) ):renameTransitions r xs
renameState :: [(Int,[Int])] -> Int -> Int
renameState (x:xs) oldState
| elem oldState (snd x) = fst x
| otherwise = renameState xs oldState
-- function executes the minimization procces until it yields the same results
-- in two iterations
compareResults :: [[Int]] -> [[Int]] -> DFA -> [[Int]]
compareResults old new dfa
| old == new = sort new
| otherwise = compareResults new (minimize new dfa) dfa
minimize :: [[Int]] -> DFA -> [[Int]]
minimize _ (DFA _ _ _ _ []) = []
minimize states dfa@(DFA allSt start end trans (a:as)) = stepStates minimizer len
where
len = length allSt
nextDFA = (DFA allSt start end trans as)
new = splitItIfYouCan states trans a
minimizer = (sortByLen $ nub $ sort(map(\x -> sort $ nub x)(new ++ minimize new nextDFA)))
stepStates :: [[Int]] -> Int -> [[Int]]
stepStates [] _ = []
stepStates l@(s:sx) len
| ((length s) <= len && (len > 0)) = s:(stepStates newXs (len - length s))
| otherwise = []
where
newXs = deleteAdded s sx
deleteAdded :: [Int] -> [[Int]] -> [[Int]]
deleteAdded s [] = []
deleteAdded s (x:xs)
| ((intersect s x) /= []) = deleteAdded s xs
| otherwise = x:deleteAdded s xs
-- Test if current allocation of states has to be split and make if required
splitItIfYouCan :: [[Int]] -> [Transition] -> String-> [[Int]]
splitItIfYouCan [] _ _ = []
splitItIfYouCan (state:xs) trans a =
(split state trans a) ++ splitItIfYouCan xs trans a
split :: [Int] -> [Transition] -> String -> [[Int]]
split currentStates trans a
| (length currentStates == 1) = [currentStates]
| otherwise = delete [] [currentClass, currentStates \\ currentClass]
where
endStates = nub $ filter(\x -> elem x currentStates) $ provideEndStates currentStates trans a
currentClass = filter(\x -> (leadToEndStates x a endStates trans)) currentStates
leadToEndStates _ _ _ [] = False
leadToEndStates state a endStates ((Transition c s n):xs)
| ((state == c) && (a == s) && (elem n endStates)) = True
| otherwise = leadToEndStates state a endStates xs
checkClass :: [Int] -> [[Int]] -> Bool
checkClass _ [] = False
checkClass endStates (l:ls)
| intersect l endStates == [] = True
| otherwise = checkClass endStates ls
provideEndStates :: [Int] -> [Transition] -> String -> [Int]
provideEndStates states trans a = makeEndStatesList states trans a
-- Returns list of endStates returned by executing the transition on states from
-- list l with symbol a
makeEndStatesList :: [Int] -> [Transition] -> String -> [Int]
makeEndStatesList [] _ _ = []
makeEndStatesList l@(s:xs) trans a = [makeTransition s a trans] ++ makeEndStatesList xs trans a
makeTransition :: Int -> String -> [Transition] -> Int
makeTransition state symbol ((Transition c s e):xs)
| state == c && symbol == s = e
| otherwise = makeTransition state symbol xs
-- Checks if DFA has a transition for every symbol in Σ, if not add SINK state
makeComplete :: DFA -> Int -> DFA
makeComplete dfa@(DFA states start end trans alpha) sink
| (isComplete dfa) = dfa
| otherwise = DFA {
states = states ++ [sink],
startState = start,
endStates = end,
transitions = sortT(trans ++ addMissingTrans(states ++ [sink]) trans alpha sink),
alphabet = alpha
}
-- Go through every state and adds a transition to the SINK state if missing
addMissingTrans :: [Int] -> [Transition] -> [String] -> Int -> [Transition]
addMissingTrans [] _ _ _ = []
addMissingTrans (s:ss) trans alpha sink
= checkState s trans alpha sink ++ addMissingTrans ss trans alpha sink
-- Checks if given state has transition with symbol "a" from Σ, if not add it
checkState :: Int -> [Transition] -> [String] -> Int -> [Transition]
checkState _ _ [] _ = []
checkState state trans (a:as) sink
| (hasRule state a trans) = checkState state trans as sink
| otherwise = [(Transition state a sink)] ++ (checkState state trans as sink)
-- helper function checks if transition is in list of transitions
hasRule :: Int -> String -> [Transition] -> Bool
hasRule _ _ [] = False
hasRule state symbol (t:ts)
| (hasState state symbol t) = True
| otherwise = hasRule state symbol ts
hasState :: Int -> String -> Transition -> Bool
hasState state symbol (Transition c s n)
| c == state && s == symbol = True
| otherwise = False
isComplete :: DFA -> Bool
isComplete dfa
| length(getTransitions dfa) == length(getStates dfa) * length(getAlphabet dfa) = True
| otherwise = False
-- decite beetween printing or minimization
handleAutomata :: String -> DFA -> IO()
handleAutomata cmd dfa
| cmd == "-i" = view dfa
| otherwise = start_minimization dfa
-- check if list has at least num lines
hasNumLines :: Int -> [String] -> Bool
hasNumLines num list = num > (length list)
getHelp :: String
getHelp = "Usage: ./dfa-2-mka [-i|-t] [file]"
main = do
args <- getArgs
-- check arguments 1. zero args 2. 1st must be in [-i|-t] 3. less than tree
if ((length args == 0) || not (checkArgs $ head args) || (length args) > 2 )
then error getHelp
else do
automata <- getAutomata $ getFileArg args -- break input lineByLine
let lineByLine = lines automata
if hasNumLines 4 lineByLine -- input should have at least 4 lines
then error "Automata description too short"
else handleAutomata (head args) (parseDFA lineByLine)