module Trie
( buildTrieWithTokens
, isTerminal
, findNodeFromTrie
, findNodeFromChildren
, Trie
, Node
) where
import qualified Data.HashMap.Strict as Map
import qualified Data.Text as T
type NodeMap = Map.HashMap Char Node
newtype Trie =
Root NodeMap
deriving (Show, Eq)
data Node
= Terminal !NodeMap
| NonTerminal !NodeMap
deriving (Show, Eq)
buildTrieWithTokens :: [T.Text] -> Trie
buildTrieWithTokens = foldl (flip add) buildTrie
findNodeFromTrie :: Trie -> Char -> Maybe Node
findNodeFromTrie (Root nodes) char = Map.lookup char nodes
findNodeFromChildren :: Node -> Char -> Maybe Node
findNodeFromChildren node char = Map.lookup char $ nodeChildren node
mergeSameNodes :: Node -> Node -> Node
mergeSameNodes (NonTerminal xs) (NonTerminal ys) =
NonTerminal $ mergeNodeMaps xs ys
mergeSameNodes x y = Terminal $ mergeNodeMaps (nodeChildren x) (nodeChildren y)
mergeNodeMaps :: NodeMap -> NodeMap -> NodeMap
mergeNodeMaps = Map.unionWith mergeSameNodes
isTerminal :: Node -> Bool
isTerminal (Terminal _) = True
isTerminal (NonTerminal _) = False
nodeChildren :: Node -> NodeMap
nodeChildren (Terminal xs) = xs
nodeChildren (NonTerminal xs) = xs
addNode :: Trie -> Char -> Node -> Trie
addNode (Root nodes) char node =
Root $ Map.insertWith mergeSameNodes char node nodes
buildTrie :: Trie
buildTrie = Root Map.empty
add :: T.Text -> Trie -> Trie
add input trie =
case T.uncons input of
Nothing -> trie
Just (x, xs) -> addNode trie x $ createNode xs
createNode :: T.Text -> Node
createNode input =
case T.uncons input of
Nothing -> Terminal Map.empty
Just (x, xs) -> NonTerminal $ Map.singleton x $ createNode xs