1 {-# LANGUAGE DeriveGeneric #-}
2 {-# LANGUAGE DisambiguateRecordFields #-}
3 {-# LANGUAGE DuplicateRecordFields #-}
4 {-# LANGUAGE FlexibleInstances #-}
5 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
6 {-# LANGUAGE OverloadedStrings #-}
7 module Hdoc.DTC.Document
8 ( module Hdoc.DTC.Document
12 import Control.Applicative (Applicative(..))
14 import Data.Default.Class (Default(..))
15 import Data.Default.Instances.Containers ()
16 import Data.Eq (Eq(..))
17 import Data.Foldable (Foldable(..))
18 import Data.Function (on, ($), (.))
19 import Data.Hashable (Hashable(..))
21 import Data.Maybe (Maybe(..))
22 import Data.Monoid (Monoid(..))
23 import Data.Ord (Ord(..))
24 import Data.Semigroup (Semigroup(..))
25 import Data.String (IsString)
26 import GHC.Generics (Generic)
27 import Text.Show (Show)
28 import qualified Data.Char as Char
29 -- import qualified Data.HashMap.Strict as HM
30 -- import qualified Data.HashSet as HS
31 import qualified Data.List as List
32 import qualified Data.Text.Lazy as TL
33 -- import qualified Data.Tree as Tree
34 import qualified Data.TreeSeq.Strict as TS
35 import qualified Hjugement as MJ
39 import qualified Hdoc.TCT.Cell as TCT
42 data Document = Document
46 instance Default Document where
55 , judgments :: ![Judgment]
56 -- [(Judgment, [Tree.Tree (Maybe MJ.Share, [Choice])])]
58 instance Default Head where
66 { headers :: ![Header]
69 , authors :: ![Entity]
70 , editor :: !(Maybe Entity)
71 , date :: !(Maybe Date)
75 , includes :: ![Include] -- FIXME: remove?
77 instance Default About where
90 instance Semigroup About where
92 { headers = headers x <> headers y
93 , titles = titles x <> titles y
94 , url = url (x::About) <> url (y::About)
95 , authors = authors x <> authors y
96 , editor = editor x <> editor y
97 , date = date x <> date y
98 , tags = tags x <> tags y
99 , links = links x <> links y
100 , series = series x <> series y
101 , includes = includes x <> includes y
111 type Body = TS.Trees BodyNode
113 -- ** Type 'BodyNode'
115 = BodySection !Section -- ^ node
116 | BodyBlock !Block -- ^ leaf
120 data Section = Section
122 , attrs :: !CommonAttrs
124 , aliases :: ![Alias]
125 , judgments :: ![Judgment]
131 | BlockBreak { attrs :: !CommonAttrs }
132 | BlockToC { xmlPos :: !XmlPos
133 , attrs :: !CommonAttrs
134 , depth :: !(Maybe Nat)
136 | BlockToF { xmlPos :: !XmlPos
137 , attrs :: !CommonAttrs
138 , types :: ![TL.Text]
140 | BlockAside { xmlPos :: !XmlPos
141 , attrs :: !CommonAttrs
144 | BlockFigure { xmlPos :: !XmlPos
146 , attrs :: !CommonAttrs
147 , mayTitle :: !(Maybe Title)
150 | BlockIndex { xmlPos :: !XmlPos
151 , attrs :: !CommonAttrs
154 | BlockReferences { xmlPos :: !XmlPos
155 , attrs :: !CommonAttrs
156 , refs :: ![Reference]
157 } -- FIXME: move to ParaReferences?
158 | BlockJudges { xmlPos :: !XmlPos
159 , attrs :: !CommonAttrs
162 | BlockGrades { xmlPos :: !XmlPos
163 , attrs :: !CommonAttrs
169 data Judgment = Judgment
170 { opinionsByChoice :: !(Maybe (MJ.OpinionsByChoice Choice Judge Grade))
173 , importance :: !(Maybe MJ.Share)
174 , question :: !(Maybe Title)
175 , choices :: ![Choice]
177 instance Eq Judgment where
179 judges x == judges y &&
180 grades x == grades y &&
181 question x == question y
182 instance Hashable Judgment where
183 hashWithSalt s Judgment{..} =
184 s`hashWithSalt`judges
186 `hashWithSalt`question
191 , title :: !(Maybe Title)
192 , defaultGrades :: ![(Ident, Name)]
201 , title :: !(Maybe Title)
206 { title :: !(Maybe Title)
207 , opinions :: ![Opinion]
209 instance Eq Choice where
210 (==) = (==)`on`(title::Choice -> Maybe Title)
211 instance Hashable Choice where
212 hashWithSalt s Choice{..} =
216 data Opinion = Opinion
219 , importance :: !(Maybe MJ.Share)
220 , comment :: !(Maybe Title)
225 = ParaItem { item :: !ParaItem }
226 | ParaItems { xmlPos :: !XmlPos
227 , attrs :: !CommonAttrs
228 , items :: ![ParaItem]
232 -- ** Type 'ParaItem'
235 | ParaComment !TL.Text
238 | ParaQuote { type_ :: !TL.Text
241 | ParaArtwork { type_ :: !TL.Text
244 | ParaJudgment !Judgment
247 -- *** Type 'ListItem'
248 data ListItem = ListItem
254 type Plain = TS.Trees PlainNode
256 -- ** Type 'PlainNode'
260 | PlainCode -- ^ Code (monospaced)
261 | PlainDel -- ^ Deleted (crossed-over)
263 | PlainGroup -- ^ Group subTrees (neutral)
265 | PlainSC -- ^ Small Caps
266 | PlainSub -- ^ Subscript
267 | PlainSup -- ^ Superscript
268 | PlainU -- ^ Underlined
269 | PlainEref { href :: !URL } -- ^ External reference
270 | PlainIref { anchor :: !(Maybe Anchor)
272 } -- ^ Index reference
273 | PlainTag { error :: !(Maybe ErrorTarget)
274 , tctPos :: !TCT.Spans
276 | PlainRref { error :: !(Maybe ErrorTarget)
277 , number :: !(Maybe Nat1)
278 , tctPos :: !TCT.Spans
280 } -- ^ Reference reference
281 | PlainSpan { attrs :: !CommonAttrs } -- ^ Neutral node
283 | PlainBreak -- ^ Line break (\n)
285 | PlainNote { number :: !(Maybe Nat1)
290 -- * Type 'ErrorTarget'
292 = ErrorTarget_Unknown !Nat1
293 | ErrorTarget_Ambiguous !(Maybe Nat1)
296 -- * Type 'ErrorAnchor'
298 = ErrorAnchor_Ambiguous !Nat1
301 -- * Type 'CommonAttrs'
302 data CommonAttrs = CommonAttrs
303 { id :: !(Maybe Ident)
304 , classes :: ![TL.Text]
305 } deriving (Eq,Ord,Show)
306 instance Default CommonAttrs where
316 } deriving (Eq,Ord,Show)
319 newtype Name = Name { unName :: TL.Text }
320 deriving (Eq,Ord,Show,Semigroup,Monoid,Default,IsString,Hashable)
323 newtype Title = Title { unTitle :: Plain }
324 deriving (Show,Semigroup,Monoid,Default)
325 instance Eq Title where
326 (==) = (==) `on` similarPlain . unTitle
327 -- | Return a similar version of a 'Plain' by removing:
329 -- * parsing residues ('PlainGroup'),
330 -- * notes ('PlainNote'),
331 -- * and position specific annotations ('Ident' and 'Anchor').
332 similarPlain :: Plain -> Plain
333 similarPlain = foldMap $ \(TS.Tree n ts) ->
334 let skip = similarPlain ts in
335 let keep = pure $ TS.Tree n $ skip in
339 PlainIref{..} -> pure $ TS.Tree PlainIref{anchor=Nothing, ..} skip
340 PlainRref{..} -> pure $ TS.Tree PlainRref{error=Nothing, number=Nothing, tctPos=def, ..} skip
341 PlainSpan attrs -> pure $ TS.Tree n' skip
342 where n' = PlainSpan{attrs = CommonAttrs{ id = Nothing
343 , classes = List.sort $ classes attrs }}
353 PlainEref _to -> keep
354 PlainTag{..} -> pure $ TS.Tree PlainTag{tctPos=def, ..} skip
357 -- | Return the same hash if 'similarPlain' is applied on the 'Title' before hashing.
359 -- Warning: when using the key of HashMap or HashSet,
360 -- only the data taken into account by this 'Hashable' instance is reliable.
361 instance Hashable Title where
362 hashWithSalt salt (Title ps) = hs salt ps
370 PlainIref{..} -> s`hashWithSalt`(0::Int)`hashWithSalt`term
371 PlainTag{..} -> s`hashWithSalt`(1::Int)
372 PlainSpan{..} -> s`hashWithSalt`(2::Int)`hashWithSalt`List.sort (classes attrs)
373 PlainB -> s`hashWithSalt`(3::Int)
374 PlainCode -> s`hashWithSalt`(4::Int)
375 PlainDel -> s`hashWithSalt`(5::Int)
376 PlainI -> s`hashWithSalt`(6::Int)
377 PlainQ -> s`hashWithSalt`(7::Int)
378 PlainSC -> s`hashWithSalt`(8::Int)
379 PlainSub -> s`hashWithSalt`(9::Int)
380 PlainSup -> s`hashWithSalt`(10::Int)
381 PlainU -> s`hashWithSalt`(11::Int)
382 PlainEref{..} -> s`hashWithSalt`(12::Int)`hashWithSalt`href
383 PlainRref{..} -> s`hashWithSalt`(13::Int)`hashWithSalt`to
384 PlainBreak -> s`hashWithSalt`(14::Int)
385 PlainText t -> s`hashWithSalt`(15::Int)`hashWithSalt`t
391 , zipcode :: !TL.Text
394 , country :: !TL.Text
398 , url :: !(Maybe URL)
399 , org :: !(Maybe Entity)
401 instance Default Entity where
415 instance Semigroup Entity where
419 newtype Include = Include
422 instance Default Include where
427 -- * Type 'Reference'
428 data Reference = Reference
429 { error :: !(Maybe ErrorAnchor)
431 , tctPos :: !TCT.Spans
439 , month :: !(Maybe Nat1)
440 , day :: !(Maybe Nat1)
442 instance Default Date where
445 , month = Just (Nat1 01)
446 , day = Just (Nat1 01)
448 instance Semigroup Date where
456 , type_ :: !(Maybe TL.Text)
459 instance Default Link where
469 newtype Alias = Alias
478 instance Default Serie where
484 -- | Builtins 'URL' recognized from |Serie|'s 'name'.
485 urlSerie :: Serie -> Maybe URL
486 urlSerie Serie{id=id_, name} =
488 "RFC" | TL.all Char.isDigit id_ ->
489 Just $ URL $ "https://tools.ietf.org/html/rfc"<>id_
490 "DOI" -> Just $ URL $ "https://dx.doi.org/"<>id_
497 type Words = [WordOrSpace]
499 -- *** Type 'WordOrSpace'
503 deriving (Eq,Ord,Show,Generic)
504 instance Hashable WordOrSpace
507 type Aliases = [Words]
510 type Terms = [Aliases]