toktok-0.5: Toktok/Transducer.hs
-- | This is the module defining the transducer.
-- a transducer is build from a Trie with added sandhis.
module Toktok.Transducer where
import Data.Map (Map)
import Data.Monoid
import qualified Data.Map as Map
import Data.List(isPrefixOf)
import Data.Char(isUpper, toLower)
import Toktok.Lattice ((<:), Lattice)
import Toktok.Sandhi
import Toktok.Stack
import Toktok.Trie
import qualified Toktok.Stack as Stack
data Transducer
= Trans Bool -- Acceptance
(Map Char Transducer) -- Deterministic part
[Sandhi] -- Non deterministic part
-- | Build a transducer from a trie and a list of sandhis...
mkTransducer :: Trie -> [Sandhi] -> Transducer
mkTransducer t sandhis = case mkTransducer' t sandhis "" of
(t,Stack []) -> t
_ -> error "Non empty stack"
mkTransducer' :: Trie -> [Sandhi] -> String -> (Transducer, Stack Sandhi)
mkTransducer' (Trie b trieMap) sandhis pref
= (Trans b newMap (Stack.head oldStack), newStack)
where intermediateMap
= Map.mapWithKey (\ k t -> mkTransducer' t sandhis (k:pref)) trieMap
newMap = fmap fst intermediateMap
oldStack = mconcat $ map snd $ Map.elems intermediateMap
newStack = merge (Stack.pop oldStack) createStack
-- Sandhi are applied only if we are at the end of a word
goodSandhis = if b
then filter (\(Sandhi u _ _) -> u `isPrefixOf` pref) sandhis
else []
sandhiToStack s@(Sandhi u _ _) = Stack.singleton (length u) s
createStack = mconcat $ map sandhiToStack goodSandhis
-- | In this algorithm, we segment the input in the folowing way
-- input = ¬pref ++ suf
-- or input = ¬pref ++ c ++ suf
-- where pref is the already processed part.
applyTransducer :: Transducer -> String -> Lattice String
applyTransducer rootT = apply' rootT []
where -- first, if we have consumed all the input
apply' (Trans True _ _) pref [] = [[reverse pref]]
apply' (Trans False _ _) pref [] = []
-- then, if the state is final
apply' (Trans True trie sandhis) pref suf
= (reverse pref <: apply' rootT "" suf)
++ continueWithTrie trie pref suf
++ continueWithSandhis sandhis pref suf
-- then if the state is not final...
apply' (Trans False trie sandhis) pref suf
= continueWithTrie trie pref suf
++ continueWithSandhis sandhis pref suf
-- continue processing using (deterministic) trie
continueWithTrie trieMap pref (c:suf)
= case Map.lookup c trieMap of
Nothing -> []
Just t' -> apply' t' (c:pref) suf
++ if isUpper c
then case Map.lookup (toLower c) trieMap of
Nothing -> []
Just t' -> apply' t' (toLower c:pref) suf
else []
-- continue processing using sandhis
continueWithSandhis [] _ _ = [] -- processed all sandhis
continueWithSandhis sandhis pref suf
= concatMap (\ s -> applySandhi s pref suf) sandhis
applySandhi (Sandhi u v w) pref suf | w `isPrefixOf` suf
= (reverse $ u ++ pref) <: (apply' (access v rootT) (reverse v) suf')
where suf' = drop (length w) suf
-- | Access a state in a transducter, ignoring sandhis and intermediate
-- final states.
access :: String -> Transducer -> Transducer
access [] t = t
access (c:cs) (Trans _ map _)
= case Map.lookup c map of
Nothing -> undefined
Just t' -> access cs t'