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
9 , Ident(..), URL(..), Nat(..), Nat1(..)
14 import Control.Applicative (Applicative(..))
16 import Data.Default.Class (Default(..))
17 import Data.Default.Instances.Containers ()
18 import Data.Eq (Eq(..))
19 import Data.Foldable (Foldable(..))
20 import Data.Function (on, ($), (.))
21 import Data.Hashable (Hashable(..))
23 import Data.Maybe (Maybe(..))
24 import Data.Monoid (Monoid(..))
25 import Data.Ord (Ord(..))
26 import Data.Semigroup (Semigroup(..))
27 import Data.Sequence (Seq(..))
28 import Data.String (IsString)
29 import GHC.Generics (Generic)
30 import System.FilePath (FilePath)
31 import Text.Show (Show)
32 import qualified Data.Char as Char
33 import qualified Data.HashMap.Strict as HM
34 import qualified Data.List as List
35 import qualified Data.Text.Lazy as TL
36 import qualified Data.TreeSeq.Strict as TS
37 import qualified Hjugement as MJ
39 import Hdoc.Utils (Nat(..), Nat1(..), succNat, succNat1)
40 import Hdoc.XML (Ident(..), URL(..))
41 import qualified Hdoc.XML as XML
42 import qualified Hdoc.TCT.Cell as TCT
45 data Document = Document
49 instance Default Document where
58 , judgments :: ![Judgment]
59 -- [(Judgment, [Tree.Tree (Maybe MJ.Share, [Choice])])]
61 instance Default Head where
69 { headers :: ![Header]
72 , authors :: ![Entity]
73 , editor :: !(Maybe Entity)
74 , date :: !(Maybe Date)
78 , includes :: ![Include] -- FIXME: remove?
80 instance Default About where
93 instance Semigroup About where
95 { headers = headers x <> headers y
96 , titles = titles x <> titles y
97 , url = url (x::About) <> url (y::About)
98 , authors = authors x <> authors y
99 , editor = editor x <> editor y
100 , date = date x <> date y
101 , tags = tags x <> tags y
102 , links = links x <> links y
103 , series = series x <> series y
104 , includes = includes x <> includes y
114 type Body = TS.Trees BodyNode
116 -- ** Type 'BodyNode'
118 = BodySection !Section -- ^ node
119 | BodyBlock !Block -- ^ leaf
123 data Section = Section
124 { section_posXML :: !XML.Pos
125 , section_attrs :: !CommonAttrs
126 , section_title :: !Title
127 , section_aliases :: ![Alias]
128 , section_judgments :: ![Judgment]
134 | BlockBreak { attrs :: !CommonAttrs }
135 | BlockToC { posXML :: !XML.Pos
136 , attrs :: !CommonAttrs
137 , depth :: !(Maybe Nat)
139 | BlockToF { posXML :: !XML.Pos
140 , attrs :: !CommonAttrs
141 , types :: ![TL.Text]
143 | BlockAside { posXML :: !XML.Pos
144 , attrs :: !CommonAttrs
147 | BlockFigure { posXML :: !XML.Pos
149 , attrs :: !CommonAttrs
150 , mayTitle :: !(Maybe Title)
153 | BlockIndex { posXML :: !XML.Pos
154 , attrs :: !CommonAttrs
157 | BlockReferences { posXML :: !XML.Pos
158 , attrs :: !CommonAttrs
159 , refs :: ![Reference]
160 } -- FIXME: move to ParaReferences?
161 | BlockJudges !Judges
162 | BlockGrades { posXML :: !XML.Pos
163 , attrs :: !CommonAttrs
169 data Judgment = Judgment
170 { judgment_opinionsByChoice :: !(Maybe (MJ.OpinionsByChoice Choice Name (MJ.Ranked Grade)))
171 , judgment_judges :: !(Maybe Judges)
172 , judgment_grades :: !(Maybe (MJ.Grades (MJ.Ranked Grade)))
173 , judgment_posXML :: !XML.Pos
174 , judgment_locTCT :: !TCT.Location
175 , judgment_judgesId :: !Ident
176 , judgment_gradesId :: !Ident
177 , judgment_importance :: !(Maybe MJ.Share)
178 , judgment_question :: !(Maybe Title)
179 , judgment_choices :: ![Choice]
181 instance Eq Judgment where
183 judgment_judgesId x == judgment_judgesId y &&
184 judgment_gradesId x == judgment_gradesId y &&
185 judgment_question x == judgment_question y
186 instance Hashable Judgment where
187 hashWithSalt s Judgment{..} =
188 s`hashWithSalt`judgment_judgesId
189 `hashWithSalt`judgment_gradesId
190 `hashWithSalt`judgment_question
192 -- ** Type 'ErrorJudgment'
194 = ErrorJudgment_Judges
195 | ErrorJudgment_Grades
200 { judges_locTCT :: !TCT.Location
201 , judges_posXML :: !XML.Pos
202 , judges_attrs :: !CommonAttrs
203 , judges_byName :: !(HM.HashMap Name [Judge])
208 { judge_locTCT :: !TCT.Location
209 , judge_posXML :: !XML.Pos
210 , judge_name :: !Name
211 , judge_title :: !(Maybe Title)
212 , judge_defaultGrades :: !(HM.HashMap Ident [Name])
217 { grade_posXML :: !XML.Pos
218 , grade_name :: !Name
219 , grade_color :: !TL.Text
220 , grade_isDefault :: !Bool
221 , grade_title :: !(Maybe Title)
226 { choice_locTCT :: TCT.Location
227 , choice_posXML :: XML.Pos
228 , choice_title :: !(Maybe Title)
229 , choice_opinions :: ![Opinion]
231 instance Eq Choice where
232 (==) = (==)`on`choice_title
233 instance Hashable Choice where
234 hashWithSalt s Choice{..} =
235 hashWithSalt s choice_title
238 data Opinion = Opinion
239 { opinion_locTCT :: !TCT.Location
240 , opinion_posXML :: !XML.Pos
241 , opinion_judge :: !Name
242 , opinion_grade :: !Name
243 , opinion_importance :: !(Maybe MJ.Share)
244 , opinion_comment :: !(Maybe Title)
249 = ParaItem { item :: !ParaItem }
250 | ParaItems { posXML :: !XML.Pos
251 , attrs :: !CommonAttrs
252 , items :: ![ParaItem]
256 -- ** Type 'ParaItem'
259 | ParaComment !TL.Text
262 | ParaQuote { type_ :: !TL.Text
265 | ParaArtwork { type_ :: !TL.Text
268 | ParaJudgment !Judgment
271 -- *** Type 'ListItem'
272 data ListItem = ListItem
278 type Plain = TS.Trees PlainNode
280 -- ** Type 'PlainNode'
284 | PlainCode -- ^ Code (monospaced)
285 | PlainDel -- ^ Deleted (crossed-over)
287 | PlainGroup -- ^ Group subTrees (neutral)
289 | PlainSC -- ^ Small Caps
290 | PlainSub -- ^ Subscript
291 | PlainSup -- ^ Superscript
292 | PlainU -- ^ Underlined
293 | PlainEref { eref_href :: !URL } -- ^ External reference
294 | PlainIref { iref_anchor :: !(Maybe Anchor)
295 , iref_term :: !Words
296 } -- ^ Index reference
297 | PlainTag { tag_error :: !(Maybe ErrorTarget)
298 , tag_locTCT :: !TCT.Location
300 | PlainRref { rref_error :: !(Maybe ErrorTarget)
301 , rref_number :: !(Maybe Nat1)
302 , rref_locTCT :: !TCT.Location
304 } -- ^ Reference reference
305 | PlainSpan { attrs :: !CommonAttrs } -- ^ Neutral node
307 | PlainBreak -- ^ Line break (\n)
309 | PlainNote { note_number :: !(Maybe Nat1)
310 , note_paras :: ![Para]
314 -- * Type 'ErrorTarget'
316 = ErrorTarget_Unknown !Nat1
317 | ErrorTarget_Ambiguous !(Maybe Nat1)
320 -- * Type 'ErrorAnchor'
322 = ErrorAnchor_Ambiguous !Nat1
325 -- * Type 'CommonAttrs'
326 data CommonAttrs = CommonAttrs
327 { id :: !(Maybe Ident)
328 , classes :: ![TL.Text]
329 } deriving (Eq,Ord,Show)
330 instance Default CommonAttrs where
338 { anchor_section :: !XML.Pos
339 , anchor_count :: !Nat1
340 } deriving (Eq,Ord,Show)
343 newtype Name = Name { unName :: TL.Text }
344 deriving (Eq,Ord,Show,Semigroup,Monoid,Default,IsString,Hashable)
347 newtype Title = Title { unTitle :: Plain }
348 deriving (Show,Semigroup,Monoid,Default)
349 instance Eq Title where
350 (==) = (==) `on` similarPlain . unTitle
351 -- | Return a similar version of a 'Plain' by removing:
353 -- * parsing residues ('PlainGroup'),
354 -- * notes ('PlainNote'),
355 -- * and position specific annotations ('Ident' and 'Anchor').
356 similarPlain :: Plain -> Plain
357 similarPlain = foldMap $ \(TS.Tree n ts) ->
358 let skip = similarPlain ts in
359 let keep = pure $ TS.Tree n $ skip in
363 PlainIref{..} -> pure $ TS.Tree PlainIref{ iref_anchor = Nothing, ..} skip
364 PlainRref{..} -> pure $ TS.Tree PlainRref{ rref_error = Nothing
365 , rref_number = Nothing
368 PlainSpan attrs -> pure $ TS.Tree n' skip
369 where n' = PlainSpan{attrs = CommonAttrs{ id = Nothing
370 , classes = List.sort $ classes attrs }}
380 PlainEref{..} -> keep
381 PlainTag{..} -> pure $ TS.Tree PlainTag{tag_locTCT=def, ..} skip
384 -- | Return the same hash if 'similarPlain' is applied on the 'Title' before hashing.
386 -- Warning: when using the key of HashMap or HashSet,
387 -- only the data taken into account by this 'Hashable' instance is reliable.
388 instance Hashable Title where
389 hashWithSalt salt (Title ps) = hs salt ps
397 PlainIref{..} -> s`hashWithSalt`(0::Int)`hashWithSalt`iref_term
398 PlainTag{..} -> s`hashWithSalt`(1::Int)
399 PlainSpan{..} -> s`hashWithSalt`(2::Int)`hashWithSalt`List.sort (classes attrs)
400 PlainB -> s`hashWithSalt`(3::Int)
401 PlainCode -> s`hashWithSalt`(4::Int)
402 PlainDel -> s`hashWithSalt`(5::Int)
403 PlainI -> s`hashWithSalt`(6::Int)
404 PlainQ -> s`hashWithSalt`(7::Int)
405 PlainSC -> s`hashWithSalt`(8::Int)
406 PlainSub -> s`hashWithSalt`(9::Int)
407 PlainSup -> s`hashWithSalt`(10::Int)
408 PlainU -> s`hashWithSalt`(11::Int)
409 PlainEref{..} -> s`hashWithSalt`(12::Int)`hashWithSalt`eref_href
410 PlainRref{..} -> s`hashWithSalt`(13::Int)`hashWithSalt`rref_to
411 PlainBreak -> s`hashWithSalt`(14::Int)
412 PlainText t -> s`hashWithSalt`(15::Int)`hashWithSalt`t
418 , zipcode :: !TL.Text
421 , country :: !TL.Text
425 , url :: !(Maybe URL)
426 , org :: !(Maybe Entity)
428 instance Default Entity where
442 instance Semigroup Entity where
446 newtype Include = Include
449 instance Default Include where
454 -- * Type 'Reference'
455 data Reference = Reference
456 { reference_error :: !(Maybe ErrorAnchor)
457 , reference_posXML :: !XML.Pos
458 , reference_locTCT :: !TCT.Location
459 , reference_id :: !Ident
460 , reference_about :: !About
466 , month :: !(Maybe Nat1)
467 , day :: !(Maybe Nat1)
469 instance Default Date where
472 , month = Just (Nat1 01)
473 , day = Just (Nat1 01)
475 instance Semigroup Date where
483 , type_ :: !(Maybe TL.Text)
486 instance Default Link where
496 newtype Alias = Alias
505 instance Default Serie where
511 -- | Builtins 'URL' recognized from |Serie|'s 'name'.
512 urlSerie :: Serie -> Maybe URL
513 urlSerie Serie{id=id_, name} =
515 "RFC" | TL.all Char.isDigit id_ ->
516 Just $ URL $ "https://tools.ietf.org/html/rfc"<>id_
517 "DOI" -> Just $ URL $ "https://dx.doi.org/"<>id_
524 type Words = [WordOrSpace]
526 -- *** Type 'WordOrSpace'
530 deriving (Eq,Ord,Show,Generic)
531 instance Hashable WordOrSpace
534 type Aliases = [Words]
537 type Terms = [Aliases]