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.Traversable (Traversable(..))
23 import Data.TreeMap.Strict (TreeMap(..))
24 import Data.TreeSeq.Strict (Tree(..), tree0)
25 import qualified Control.Monad.Trans.State as S
26 import qualified Data.Char as Char
27 import qualified Data.List as List
28 import qualified Data.Map.Strict as Map
29 import qualified Data.Sequence as Seq
30 import qualified Data.Strict.Maybe as Strict
31 import qualified Data.Text.Lazy as TL
32 import qualified Data.TreeMap.Strict as TreeMap
33 -- import qualified Data.TreeSeq.Strict as TreeSeq
35 import Language.DTC.Document
38 type PathWord = TreeMap.Path Word
40 pathFromWords :: Words -> Maybe PathWord
42 case ws >>= unSpace of
43 p:ps | not (TL.null p) -> Just (TreeMap.path p ps)
51 type Irefs = TreeMap Word [Anchor]
53 irefsOfTerms :: Terms -> Irefs
54 irefsOfTerms = TreeMap.fromList const . (>>= f) . concat
57 f ws = maybe [] (\p -> [(p,[])]) $ pathFromWords ws
60 type Rrefs = Map Ident [Anchor]
63 type Notes = Map Pos [(Nat1,Para)]
68 { state_section :: Pos
69 , state_irefs :: Irefs
70 , state_rrefs :: Rrefs
71 , state_notes :: Notes
73 instance Default State where
76 , state_irefs = mempty
77 , state_rrefs = mempty
78 , state_notes = mempty
81 -- * Class 'Anchorify'
82 class Anchorify a where
83 anchorify :: a -> S.State State a
84 instance Anchorify (Tree a) => Anchorify [Tree a] where
85 anchorify = mapM anchorify
86 instance Anchorify Body where
87 anchorify = mapM anchorify
88 instance Anchorify (Tree BodyNode) where
93 before@State{state_section} <- S.get
94 S.put before{state_section = pos}
95 t <- Tree <$> anchorify n <*> anchorify ts
97 S.put after{state_section}
99 _ -> Tree <$> anchorify n <*> anchorify ts
100 instance Anchorify BodyNode where
110 Figure pos attrs type_
111 <$> anchorify mayTitle
117 Block <$> anchorify v
118 instance Anchorify a => Anchorify (Maybe a) where
119 anchorify = mapM anchorify
120 instance Anchorify [Reference] where
121 anchorify = mapM anchorify
122 instance Anchorify [Block] where
123 anchorify = mapM anchorify
124 instance Anchorify [[Block]] where
125 anchorify = mapM (mapM anchorify)
126 instance Anchorify Title where
127 anchorify (Title t) = Title <$> anchorify t
128 instance Anchorify Block where
130 Para{..} -> Para pos attrs <$> anchorify para
131 OL{..} -> OL pos attrs <$> anchorify items
132 UL{..} -> UL pos attrs <$> anchorify items
133 Quote{..} -> Quote pos attrs type_ <$> anchorify blocks
134 d@Artwork{} -> pure d
135 d@Comment{} -> pure d
136 instance Anchorify Para where
142 else join <$> traverse indexifyLines ls
145 go :: Lines -> S.State State Lines
153 let notes = Map.findWithDefault [] state_section state_notes
154 let count | (cnt,_):_ <- notes = succNat1 cnt
156 S.modify $ \s -> s{state_notes=
157 Map.insert state_section ((count,ts):notes) state_notes}
158 return Note{number=Just count}
161 let anchs = Map.findWithDefault [] to state_rrefs
162 let count = case anchs of [] -> def; Anchor{count=c}:_ -> succNat1 c
163 let anch = Anchor{count, section=state_section}
164 S.modify $ \s -> s{state_rrefs=
165 Map.insert to (anch:anchs) state_rrefs}
166 return Rref{anchor=Just anch, to}
169 instance Anchorify Reference where
172 indexifyLines :: Lines -> S.State State Para
173 indexifyLines = \case
175 | Just words <- pathFromWords term -> do
176 State{state_irefs, state_section} <- S.get
177 case TreeMap.lookup words state_irefs of
179 Seq.singleton . Tree n . join
180 <$> traverse indexifyLines ts
181 Strict.Just anchs -> do
182 let count = case anchs of [] -> def; Anchor{count=c}:_ -> succNat1 c
183 let anch = Anchor{count, section=state_section}
184 S.modify $ \s -> s{state_irefs=
185 TreeMap.insert const words (anch:anchs) state_irefs}
186 Seq.singleton . Tree Iref{term, anchor=Just anch} . join
187 <$> traverse indexifyLines ts
188 Tree BR _ -> pure $ Seq.singleton $ tree0 BR
189 Tree (Plain p) _ -> do
191 let (irefs,ts) = indexifyWords state_section state_irefs (wordify p)
192 S.modify $ \s -> s{state_irefs=irefs}
195 Seq.singleton . Tree n . join
196 <$> traverse indexifyLines ts
198 indexifyWords :: Pos -> Irefs -> Words -> (Irefs, Para)
199 indexifyWords section = go mempty
201 go :: Para -> Irefs -> Words -> (Irefs, Para)
206 go (acc |> tree0 (Plain " ")) irefs next
208 case goWords irefs [] inp of
209 Nothing -> go (acc |> tree0 (Plain w)) irefs next
210 Just (anch, ls, ns, rs) ->
211 let term = List.reverse ls in
212 let lines = Seq.fromList $ tree0 . Plain . plainifyWord <$> term in
213 go (acc |> Tree Iref{term, anchor=Just anch} lines) rs ns
217 Maybe (Anchor, Words, Words, Irefs)
218 goWords m@(TreeMap irefsByWord) prev inp =
221 curr@Space : next -> goWords m (curr:prev) next
222 curr@(Word w) : next ->
223 case Map.lookup w irefsByWord of
225 Just nod@TreeMap.Node{..} ->
226 let prev' = curr:prev in
229 | null node_descendants -> Nothing
231 (<$> goWords node_descendants prev' next) $ \(anch, ls, ns, rs) ->
232 (anch, ls, ns, TreeMap $
233 Map.insert w nod{TreeMap.node_descendants = rs} irefsByWord)
235 case goWords node_descendants prev' next of
237 let count = case anchs of [] -> def; Anchor{count=c}:_ -> succNat1 c in
238 let anch = Anchor{count, section} in
239 Just (anch, prev', next, TreeMap $
240 Map.insert w nod{TreeMap.node_value = Strict.Just $ anch:anchs} irefsByWord)
241 Just (anch, ls, ns, rs) ->
242 Just (anch, ls, ns, TreeMap $
243 Map.insert w nod{TreeMap.node_descendants = rs} irefsByWord)
245 wordify :: TL.Text -> Words
246 wordify = List.reverse . go []
248 go :: Words -> TL.Text -> Words
250 case TL.span Char.isAlphaNum t of
252 case TL.span Char.isSpace t of
256 Just (c,r) -> go (Word (TL.singleton c) : acc) r
257 (_s,r) -> go (Space : acc) r
258 (w,r) -> go (Word w : acc) r
260 plainifyWord :: WordOrSpace -> TL.Text
265 plainifyWords :: Words -> TL.Text
266 plainifyWords = TL.concat . (plainifyWord <$>)
268 termsByChar :: Terms -> Map Char Terms
270 foldr (\aliases acc ->
272 (Word w:_):_ | not (TL.null w) ->
274 (Char.toUpper $ TL.index w 0)