1 {-# LANGUAGE DuplicateRecordFields #-}
2 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE FlexibleContexts #-}
4 {-# LANGUAGE OverloadedStrings #-}
5 {-# OPTIONS_GHC -fno-warn-orphans #-}
6 -- | Compute an Index for a DTC.
7 module Language.DTC.Anchor where
9 import Control.Applicative (Applicative(..))
10 import Control.Category
11 import Control.Monad (Monad(..), mapM, join)
13 import Data.Char (Char)
14 import Data.Default.Class (Default(..))
15 import Data.Foldable (Foldable(..), concat)
16 import Data.Function (($), const)
17 import Data.Functor ((<$>))
18 import Data.Map.Strict (Map)
19 import Data.Maybe (Maybe(..), maybe)
20 import Data.Monoid (Monoid(..))
21 import Data.Semigroup (Semigroup(..))
22 import Data.Sequence ((|>))
23 import Data.Text (Text)
24 import Data.Traversable (Traversable(..))
25 import Data.TreeMap.Strict (TreeMap(..))
26 import Data.TreeSeq.Strict (Tree(..))
27 import qualified Control.Monad.Trans.State as S
28 import qualified Data.Char as Char
29 import qualified Data.List as List
30 import qualified Data.Map.Strict as Map
31 import qualified Data.Sequence as Seq
32 import qualified Data.Strict.Maybe as Strict
33 import qualified Data.Text as Text
34 import qualified Data.TreeMap.Strict as TreeMap
36 import Language.DTC.Document
39 type PathWord = TreeMap.Path Word
41 pathFromWords :: Words -> Maybe PathWord
43 case ws >>= unSpace of
44 p:ps | not (Text.null p) -> Just (TreeMap.path p ps)
52 type Irefs = TreeMap Word [Anchor]
54 irefsOfTerms :: Terms -> Irefs
55 irefsOfTerms = TreeMap.fromList const . (>>= f) . concat
58 f ws = maybe [] (\p -> [(p,[])]) $ pathFromWords ws
63 { state_irefs :: Irefs
64 , state_section :: Pos
68 { state_irefs = mempty
72 -- * Class 'Anchorify'
73 class Anchorify a where
74 anchorify :: a -> S.State State a
75 instance Anchorify (Tree k a) => Anchorify [Tree k a] where
76 anchorify = mapM anchorify
77 instance Anchorify (Tree BodyKey BodyValue) where
79 Tree0 v -> Tree0 <$> anchorify v
83 before@State{state_section} <- S.get
84 S.put before{state_section = pos}
85 t <- TreeN <$> anchorify k <*> anchorify v
87 S.put after{state_section}
89 instance Anchorify Body where
93 () | null state_irefs -> return b
95 instance Anchorify BodyKey where
101 instance Anchorify BodyValue where
107 Figure pos attrs type_
111 Block <$> anchorify v
112 instance Anchorify [Reference] where
113 anchorify = mapM anchorify
114 instance Anchorify [Block] where
115 anchorify = mapM anchorify
116 instance Anchorify [[Block]] where
117 anchorify = mapM (mapM anchorify)
118 instance Anchorify Title where
119 anchorify (Title t) = Title <$> anchorify t
120 instance Anchorify Block where
122 Para{..} -> Para pos attrs <$> anchorify para
123 OL{..} -> OL pos attrs <$> anchorify items
124 UL{..} -> UL pos attrs <$> anchorify items
125 RL{..} -> RL pos attrs <$> anchorify refs
126 Artwork{..} -> Artwork pos attrs <$> anchorify art
127 d@Comment{} -> pure d
128 instance Anchorify Para where
130 join <$> traverse indexifyLines ls
131 instance Anchorify Reference where
133 instance Anchorify Artwork where
136 indexifyLines :: Lines -> S.State State Para
137 indexifyLines = \case
138 Tree0 a -> indexifyPlain a
139 TreeN k@Iref{term} ts
140 | Just words <- pathFromWords term -> do
141 State{state_irefs, state_section} <- S.get
142 case TreeMap.lookup words state_irefs of
144 Seq.singleton . TreeN k . join
145 <$> traverse indexifyLines ts
146 Strict.Just irefs -> do
147 let count = case irefs of [] -> def; Anchor{count=c}:_ -> succNat1 c
148 let anch = Anchor{count, section=state_section}
149 S.modify $ \s -> s{state_irefs=
150 TreeMap.insert const words (anch:irefs) state_irefs}
151 Seq.singleton . TreeN Iref{term, anchor=Just anch} . join
152 <$> traverse indexifyLines ts
154 Seq.singleton . TreeN k . join
155 <$> traverse indexifyLines ts
157 indexifyPlain :: LineValue -> S.State State Para
158 indexifyPlain = \case
159 BR -> pure $ Seq.singleton $ Tree0 BR
162 let (irefs,ts) = indexifyWords state_section state_irefs (wordify p)
163 S.modify $ \s -> s{state_irefs=irefs}
166 indexifyWords :: Pos -> Irefs -> Words -> (Irefs, Para)
167 indexifyWords section = go mempty
169 go :: Para -> Irefs -> Words -> (Irefs, Para)
174 go (acc |> Tree0 (Plain " ")) irefs next
176 case goWords irefs [] inp of
177 Nothing -> go (acc |> Tree0 (Plain w)) irefs next
178 Just (anch, ls, ns, rs) ->
179 let term = List.reverse ls in
180 let lines = Seq.fromList $ Tree0 . Plain . plainifyWord <$> term in
181 go (acc |> TreeN Iref{term, anchor=Just anch} lines) rs ns
185 Maybe (Anchor, Words, Words, Irefs)
186 goWords m@(TreeMap irefsByWord) prev inp =
189 curr@Space : next -> goWords m (curr:prev) next
190 curr@(Word w) : next ->
191 case Map.lookup w irefsByWord of
193 Just nod@TreeMap.Node{..} ->
194 let prev' = curr:prev in
197 | null node_descendants -> Nothing
199 (<$> goWords node_descendants prev' next) $ \(anch, ls, ns, rs) ->
200 (anch, ls, ns, TreeMap $
201 Map.insert w nod{TreeMap.node_descendants = rs} irefsByWord)
203 case goWords node_descendants prev' next of
205 let count = case irefs of [] -> def; Anchor{count=c}:_ -> succNat1 c in
206 let anch = Anchor{count, section} in
207 Just (anch, prev', next, TreeMap $
208 Map.insert w nod{TreeMap.node_value = Strict.Just $ anch:irefs} irefsByWord)
209 Just (anch, ls, ns, rs) ->
210 Just (anch, ls, ns, TreeMap $
211 Map.insert w nod{TreeMap.node_descendants = rs} irefsByWord)
213 wordify :: Text -> Words
214 wordify = List.reverse . go []
216 go :: Words -> Text -> Words
218 case Text.span Char.isAlphaNum t of
220 case Text.span Char.isSpace t of
222 case Text.uncons t of
224 Just (c,r) -> go (Word (Text.singleton c) : acc) r
225 (_s,r) -> go (Space : acc) r
226 (w,r) -> go (Word w : acc) r
228 plainifyWord :: WordOrSpace -> Text
233 plainifyWords :: Words -> Text
234 plainifyWords = Text.concat . (plainifyWord <$>)
236 termsByChar :: Terms -> Map Char Terms
238 foldr (\aliases acc ->
240 (Word w:_):_ | not (Text.null w) ->
242 (Char.toUpper $ Text.index w 0)