1 {-# LANGUAGE DuplicateRecordFields #-}
2 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE FlexibleContexts #-}
4 {-# LANGUAGE OverloadedStrings #-}
5 {-# OPTIONS_GHC -fno-warn-orphans #-}
6 module Language.DTC.Anchor where
8 import Control.Applicative (Applicative(..))
9 import Control.Category
10 import Control.Monad (Monad(..), mapM, join)
12 import Data.Char (Char)
13 import Data.Default.Class (Default(..))
14 import Data.Foldable (Foldable(..), concat)
15 import Data.Function (($), const)
16 import Data.Functor ((<$>))
17 import Data.Map.Strict (Map)
18 import Data.Maybe (Maybe(..), maybe)
19 import Data.Monoid (Monoid(..))
20 import Data.Semigroup (Semigroup(..))
21 import Data.Sequence ((|>))
22 import Data.Text (Text)
23 import Data.Traversable (Traversable(..))
24 import Data.TreeMap.Strict (TreeMap(..))
25 import Data.TreeSeq.Strict (Tree(..))
26 import qualified Control.Monad.Trans.State as S
27 import qualified Data.Char as Char
28 import qualified Data.List as List
29 import qualified Data.Map.Strict as Map
30 import qualified Data.Sequence as Seq
31 import qualified Data.Strict.Maybe as Strict
32 import qualified Data.Text as Text
33 import qualified Data.TreeMap.Strict as TreeMap
34 -- import qualified Data.TreeSeq.Strict as Tree
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
61 type Rrefs = Map Ident [Anchor]
64 type Notes = Map Pos [(Nat1,Para)]
69 { state_section :: Pos
70 , state_irefs :: Irefs
71 , state_rrefs :: Rrefs
72 , state_notes :: Notes
74 instance Default State where
77 , state_irefs = mempty
78 , state_rrefs = mempty
79 , state_notes = mempty
82 -- * Class 'Anchorify'
83 class Anchorify a where
84 anchorify :: a -> S.State State a
85 instance Anchorify (Tree k a) => Anchorify [Tree k a] where
86 anchorify = mapM anchorify
87 instance Anchorify (Tree BodyKey BodyValue) where
89 Tree0 v -> Tree0 <$> anchorify v
93 before@State{state_section} <- S.get
94 S.put before{state_section = pos}
95 t <- TreeN <$> anchorify k <*> anchorify v
97 S.put after{state_section}
99 instance Anchorify Body where
100 anchorify = mapM anchorify
101 instance Anchorify BodyKey where
107 instance Anchorify BodyValue where
113 Figure pos attrs type_
120 Block <$> anchorify v
121 instance Anchorify a => Anchorify (Maybe a) where
122 anchorify = mapM anchorify
123 instance Anchorify [Reference] where
124 anchorify = mapM anchorify
125 instance Anchorify [Block] where
126 anchorify = mapM anchorify
127 instance Anchorify [[Block]] where
128 anchorify = mapM (mapM anchorify)
129 instance Anchorify Title where
130 anchorify (Title t) = Title <$> anchorify t
131 instance Anchorify Block where
133 Para{..} -> Para pos attrs <$> anchorify para
134 OL{..} -> OL pos attrs <$> anchorify items
135 UL{..} -> UL pos attrs <$> anchorify items
136 Artwork{..} -> Artwork pos attrs <$> anchorify art
137 d@Comment{} -> pure d
138 instance Anchorify Para where
144 else join <$> traverse indexifyLines ls
147 go :: Lines -> S.State State Lines
156 let notes = Map.findWithDefault [] state_section state_notes
157 let count | (cnt,_):_ <- notes = succNat1 cnt
159 S.modify $ \s -> s{state_notes=
160 Map.insert state_section ((count,ts):notes) state_notes}
161 return Note{number=Just count}
164 let anchs = Map.findWithDefault [] to state_rrefs
165 let count = case anchs of [] -> def; Anchor{count=c}:_ -> succNat1 c
166 let anch = Anchor{count, section=state_section}
167 S.modify $ \s -> s{state_rrefs=
168 Map.insert to (anch:anchs) state_rrefs}
169 return Rref{anchor=Just anch, to}
172 instance Anchorify Reference where
174 instance Anchorify Artwork where
177 indexifyLines :: Lines -> S.State State Para
178 indexifyLines = \case
179 Tree0 a -> indexifyPlain a
180 TreeN k@Iref{term} ts
181 | Just words <- pathFromWords term -> do
182 State{state_irefs, state_section} <- S.get
183 case TreeMap.lookup words state_irefs of
185 Seq.singleton . TreeN k . join
186 <$> traverse indexifyLines ts
187 Strict.Just anchs -> do
188 let count = case anchs of [] -> def; Anchor{count=c}:_ -> succNat1 c
189 let anch = Anchor{count, section=state_section}
190 S.modify $ \s -> s{state_irefs=
191 TreeMap.insert const words (anch:anchs) state_irefs}
192 Seq.singleton . TreeN Iref{term, anchor=Just anch} . join
193 <$> traverse indexifyLines ts
195 Seq.singleton . TreeN k . join
196 <$> traverse indexifyLines ts
198 indexifyPlain :: LineValue -> S.State State Para
199 indexifyPlain = \case
200 BR -> pure $ Seq.singleton $ Tree0 BR
203 let (irefs,ts) = indexifyWords state_section state_irefs (wordify p)
204 S.modify $ \s -> s{state_irefs=irefs}
207 indexifyWords :: Pos -> Irefs -> Words -> (Irefs, Para)
208 indexifyWords section = go mempty
210 go :: Para -> Irefs -> Words -> (Irefs, Para)
215 go (acc |> Tree0 (Plain " ")) irefs next
217 case goWords irefs [] inp of
218 Nothing -> go (acc |> Tree0 (Plain w)) irefs next
219 Just (anch, ls, ns, rs) ->
220 let term = List.reverse ls in
221 let lines = Seq.fromList $ Tree0 . Plain . plainifyWord <$> term in
222 go (acc |> TreeN Iref{term, anchor=Just anch} lines) rs ns
226 Maybe (Anchor, Words, Words, Irefs)
227 goWords m@(TreeMap irefsByWord) prev inp =
230 curr@Space : next -> goWords m (curr:prev) next
231 curr@(Word w) : next ->
232 case Map.lookup w irefsByWord of
234 Just nod@TreeMap.Node{..} ->
235 let prev' = curr:prev in
238 | null node_descendants -> Nothing
240 (<$> goWords node_descendants prev' next) $ \(anch, ls, ns, rs) ->
241 (anch, ls, ns, TreeMap $
242 Map.insert w nod{TreeMap.node_descendants = rs} irefsByWord)
244 case goWords node_descendants prev' next of
246 let count = case anchs of [] -> def; Anchor{count=c}:_ -> succNat1 c in
247 let anch = Anchor{count, section} in
248 Just (anch, prev', next, TreeMap $
249 Map.insert w nod{TreeMap.node_value = Strict.Just $ anch:anchs} irefsByWord)
250 Just (anch, ls, ns, rs) ->
251 Just (anch, ls, ns, TreeMap $
252 Map.insert w nod{TreeMap.node_descendants = rs} irefsByWord)
254 wordify :: Text -> Words
255 wordify = List.reverse . go []
257 go :: Words -> Text -> Words
259 case Text.span Char.isAlphaNum t of
261 case Text.span Char.isSpace t of
263 case Text.uncons t of
265 Just (c,r) -> go (Word (Text.singleton c) : acc) r
266 (_s,r) -> go (Space : acc) r
267 (w,r) -> go (Word w : acc) r
269 plainifyWord :: WordOrSpace -> Text
274 plainifyWords :: Words -> Text
275 plainifyWords = Text.concat . (plainifyWord <$>)
277 termsByChar :: Terms -> Map Char Terms
279 foldr (\aliases acc ->
281 (Word w:_):_ | not (Text.null w) ->
283 (Char.toUpper $ Text.index w 0)