module PP.Builders.Nfa
( combineNfa
) where
import qualified Data.Char as C
import qualified Data.Graph.Inductive.Graph as Gr
import qualified Data.List as L
import PP.Builder
import PP.Grammar
import PP.Grammars.Lexical
instance NfaBuilder RegExpr where
buildNfa re = buildNfa' (stringify re) re
buildNfa' n (RegExpr []) = buildSym n NfaEmpty
buildNfa' n (RegExpr [x]) = buildNfa' n x
buildNfa' n (RegExpr xs) = union n $ map (buildNfa' n) xs
buildNfa' n (Choice []) = buildSym n NfaEmpty
buildNfa' n (Choice [x]) = buildNfa' n x
buildNfa' n (Choice xs) = foldl1 concatenate $ map (buildNfa' n) xs
buildNfa' n (Many0 x) = kleeneStar $ buildNfa' n x
buildNfa' n (Many1 x) = kleenePlus $ buildNfa' n x
buildNfa' n (Option x) = option $ buildNfa' n x
buildNfa' n (Group x) = buildNfa' n x
buildNfa' n (Value c) = buildSym n $ NfaValue c
buildNfa' n classes = buildNfa' n $ buildClasses classes
buildSym :: String -> NfaSymbol -> NfaGraph
buildSym n s = Gr.mkGraph [(0,NfaInitial),(1,NfaFinal n)] [(0,1,s)]
buildClasses :: RegExpr -> RegExpr
buildClasses (Class xs) = RegExpr $ L.nub [ c
| x <- xs
, let (RegExpr cs)= buildClasses x
, c <- cs]
buildClasses (Interval a b) = RegExpr [Value c | c <- [a..b]]
buildClasses Any = RegExpr [ Value c
| c <- [minBound..maxBound]
, C.isAscii c]
buildClasses v@(Value _) = RegExpr [v]
concatenate :: NfaGraph -> NfaGraph -> NfaGraph
concatenate a b = Gr.mkGraph (an2 ++ bn) (ae ++ be)
where
an2 = map (\n@(i, _) -> if i == final then (i, NfaNode) else n) an
bn = map (\(i, n) -> (i + final, n)) $ filter isNotInitial $ Gr.labNodes b
ae = Gr.labEdges a
be = map (\(i, j, e) -> (i + final, j + final, e)) $ Gr.labEdges b
final = ifinal a
an = Gr.labNodes a
union :: String -> [NfaGraph] -> NfaGraph
union n gs = Gr.mkGraph (nodesU ++ nodes3) (edgesU ++ edges2)
where
nodes3 = map (\(i, _) -> (i, NfaNode)) nodes2
nodesU = [(0,NfaInitial),(final,NfaFinal n)]
edgesU = [ (i,j,NfaEmpty)
| n <- nodes2
, isNotNode n
, let (i,j) = getIJ n]
nodes2 = concat $ add $ zip diff nodes
edges2 = concat $ adde $ zip diff edges
nodes = map Gr.labNodes gs
edges = map Gr.labEdges gs
getIJ (j, NfaInitial) = (0, j)
getIJ (i, NfaFinal _) = (i, final)
final = last diff
diff = diff' nodes 1
diff' [] d = [d]
diff' (x:xs) d = d : diff' xs (d + length x)
add = map add'
add' (d, xs) = map (add'' d) xs
add'' d (i, n) = (i + d, n)
adde = map adde'
adde' (d, xs) = map (adde'' d) xs
adde'' d (i, j, n) = (i + d, j + d, n)
kleeneStar :: NfaGraph -> NfaGraph
kleeneStar g = Gr.mkGraph (nodes2 ++ nodesK) (edges2 ++ edgesK)
where
nodesK = [(initial1,NfaInitial),(final+1,NfaFinal finalN)]
edgesK = [(initial1,initial,NfaEmpty),
(final,final+1,NfaEmpty),
(initial1,final+1,NfaEmpty)]
nodes2 = map (\(i, _) -> (i, NfaNode)) nodes
edges2 = (final,initial,NfaEmpty) : edges
final = let [(i, _)] = filter isFinal nodes in i
finalN = let [(_, NfaFinal n)] = filter isFinal nodes in n
initial = let [(i, _)] = filter isInitial nodes in i
nodes = map (\(i, n) -> (i + 1, n)) $ Gr.labNodes g
edges = map (\(i, j, e) -> (i + 1, j + 1, e)) $ Gr.labEdges g
kleenePlus :: NfaGraph -> NfaGraph
kleenePlus g = Gr.delEdge (iinitial g', ifinal g') g'
where
g' = kleeneStar g
option :: NfaGraph -> NfaGraph
option g = Gr.delEdge (ifinal g' 1, iinitial g' + 1) g'
where
g' = kleeneStar g
combineNfa :: [NfaGraph] -> NfaGraph
combineNfa gs = Gr.mkGraph (nodesU ++ nodes3) (edgesU ++ edges2)
where
nodes3 = map (\n@(i, _) -> if isFinal n then n else (i, NfaNode)) nodes2
nodesU = [(0,NfaInitial)]
edgesU = [ (i,j,NfaEmpty)
| n <- nodes2
, isInitial n
, let (i,j) = getIJ n]
nodes2 = concat $ add $ zip diff nodes
edges2 = concat $ adde $ zip diff edges
nodes = map Gr.labNodes gs
edges = map Gr.labEdges gs
getIJ (j, NfaInitial) = (0, j)
diff = diff' nodes 1
diff' [] d = []
diff' (x:xs) d = d : diff' xs (d + length x)
add = map add'
add' (d, xs) = map (add'' d) xs
add'' d (i, n) = (i + d, n)
adde = map adde'
adde' (d, xs) = map (adde'' d) xs
adde'' d (i, j, n) = (i + d, j + d, n)
iinitial g = let [(i, _)] = filter isInitial (Gr.labNodes g) in i
ifinal g = let [(i, _)] = filter isFinal (Gr.labNodes g) in i
isFinal (_, NfaFinal _) = True
isFinal _ = False
isInitial (_, NfaInitial) = True
isInitial _ = False
isNotNode (_, NfaNode) = False
isNotNode _ = True
isNotInitial (_, NfaInitial) = False
isNotInitial _ = True