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 Control.Monad (Monad(..))
20 import Data.Foldable (Foldable(..))
21 import Data.Function (on, ($), (.))
22 import Data.Hashable (Hashable(..))
24 import Data.Maybe (Maybe(..))
25 import Data.Monoid (Monoid(..))
26 import Data.Ord (Ord(..))
27 import Data.Semigroup (Semigroup(..))
28 import Data.Sequence (Seq(..))
29 import Data.String (IsString)
30 import GHC.Generics (Generic)
31 import System.FilePath (FilePath)
32 import Text.Show (Show)
33 import qualified Data.Char as Char
34 import qualified Data.HashMap.Strict as HM
35 import qualified Data.List as List
36 import qualified Data.Text.Lazy as TL
37 import qualified Data.TreeMap.Strict as TM
38 import qualified Data.TreeSeq.Strict as TS
39 import qualified Majority.Judgment as MJ
41 import Hdoc.Utils (Nat(..), Nat1(..), succNat, succNat1)
42 import Hdoc.XML (Ident(..), URL(..))
43 import qualified Hdoc.XML as XML
44 import qualified Hdoc.TCT.Cell as TCT
47 data Document = Document
48 { document_head :: !(Maybe Head)
49 , document_body :: !Body
54 { head_section :: !Section
57 instance Default Head where
62 instance Ord Head where
63 compare = compare `on` head_section
68 { header_name :: !TL.Text
69 , header_value :: !Plain
74 type Body = TS.Trees BodyNode
78 = BodySection !Section -- ^ node
79 | BodyBlock !Block -- ^ leaf
83 data Section = Section
84 { section_posXML :: !XML.Pos
85 , section_locTCT :: !TCT.Location
86 , section_attrs :: !CommonAttrs
87 , section_about :: !About
89 instance Ord Section where
90 compare = compare `on` section_posXML
91 instance Default Section where
93 { section_posXML = def
94 , section_locTCT = def
101 { about_titles :: ![Title]
102 , about_aliases :: ![Alias]
103 , about_authors :: ![Entity]
104 , about_dates :: ![Date]
105 , about_tags :: ![TL.Text]
106 , about_links :: ![Link]
107 , about_series :: ![Serie]
108 , about_description :: ![Para]
109 , about_judgments :: ![Judgment]
111 instance Default About where
114 , about_aliases = def
116 , about_authors = def
120 , about_description = def
121 , about_judgments = def
123 instance Semigroup About where
125 { about_titles = about_titles x <> about_titles y
126 , about_aliases = about_aliases x <> about_aliases y
127 , about_dates = about_dates x <> about_dates y
128 , about_authors = about_authors x <> about_authors y
129 , about_tags = about_tags x <> about_tags y
130 , about_links = about_links x <> about_links y
131 , about_series = about_series x <> about_series y
132 , about_description = about_description x <> about_description y
133 , about_judgments = about_judgments x <> about_judgments y
135 instance Monoid About where
142 | BlockBreak { attrs :: !CommonAttrs }
143 | BlockToC { posXML :: !XML.Pos
144 , attrs :: !CommonAttrs
145 , depth :: !(Maybe Nat)
147 | BlockToF { posXML :: !XML.Pos
148 , attrs :: !CommonAttrs
149 , types :: ![TL.Text]
151 | BlockAside { posXML :: !XML.Pos
152 , attrs :: !CommonAttrs
155 | BlockFigure { posXML :: !XML.Pos
157 , attrs :: !CommonAttrs
158 , mayTitle :: !(Maybe Title)
161 | BlockIndex { posXML :: !XML.Pos
162 , attrs :: !CommonAttrs
165 | BlockReferences { posXML :: !XML.Pos
166 , attrs :: !CommonAttrs
167 , refs :: ![Reference]
168 } -- FIXME: move to ParaReferences?
169 | BlockJudges !Judges
170 | BlockGrades { posXML :: !XML.Pos
171 , attrs :: !CommonAttrs
177 type Index = TM.TreeMap Word Pos
180 data Judgment = Judgment
181 { judgment_opinionsByChoice :: !(Maybe (MJ.OpinionsByChoice Choice Name (MJ.Ranked Grade)))
182 -- , judgment_judges :: !(Maybe Judges)
183 -- , judgment_grades :: !(Maybe (MJ.Grades (MJ.Ranked Grade)))
184 , judgment_posXML :: !XML.Pos
185 , judgment_locTCT :: !TCT.Location
186 , judgment_judgesId :: !Ident
187 , judgment_gradesId :: !Ident
188 , judgment_importance :: !(Maybe MJ.Share)
189 , judgment_hide :: !(Maybe Bool)
190 , judgment_question :: !(Maybe Title)
191 , judgment_choices :: ![Choice]
193 instance Default Judgment where
195 { judgment_opinionsByChoice = def
196 -- , judgment_judges = def
197 -- , judgment_grades = def
198 , judgment_posXML = def
199 , judgment_locTCT = def
200 , judgment_judgesId = def
201 , judgment_gradesId = def
202 , judgment_importance = def
203 , judgment_hide = def
204 , judgment_question = def
205 , judgment_choices = def
208 -- ** Type 'JudgmentKey'
209 data JudgmentKey = JudgmentKey
210 { judgmentKey_judgesId :: !Ident
211 , judgmentKey_gradesId :: !Ident
212 , judgmentKey_question :: !(Maybe Title)
213 } deriving (Eq,Show,Generic)
214 instance Hashable JudgmentKey
216 -- ** Type 'ErrorJudgment'
218 = ErrorJudgment_Judges
219 | ErrorJudgment_Grades
224 { judges_locTCT :: !TCT.Location
225 , judges_posXML :: !XML.Pos
226 , judges_attrs :: !CommonAttrs
227 , judges_byName :: !(HM.HashMap Name [Judge])
232 { judge_locTCT :: !TCT.Location
233 , judge_posXML :: !XML.Pos
234 , judge_name :: !Name
235 , judge_title :: !(Maybe Title)
236 , judge_defaultGrades :: !(HM.HashMap Ident [Name])
241 { grade_posXML :: !XML.Pos
242 , grade_name :: !Name
243 , grade_color :: !TL.Text
244 , grade_isDefault :: !Bool
245 , grade_title :: !(Maybe Title)
250 { choice_locTCT :: TCT.Location
251 , choice_posXML :: XML.Pos
252 , choice_title :: !(Maybe Title)
253 , choice_opinions :: ![Opinion]
255 instance Eq Choice where
256 (==) = (==)`on`choice_title
257 instance Hashable Choice where
258 hashWithSalt s Choice{..} =
259 hashWithSalt s choice_title
262 data Opinion = Opinion
263 { opinion_locTCT :: !TCT.Location
264 , opinion_posXML :: !XML.Pos
265 , opinion_judge :: !Name
266 , opinion_grade :: !Name
267 , opinion_default :: !(Maybe Name)
268 , opinion_importance :: !(Maybe MJ.Share)
269 , opinion_comment :: !(Maybe Title)
274 = ParaItem { item :: !ParaItem }
275 | ParaItems { posXML :: !XML.Pos
276 , attrs :: !CommonAttrs
277 , items :: ![ParaItem]
281 -- ** Type 'ParaItem'
284 | ParaComment !TL.Text
287 | ParaQuote { type_ :: !TL.Text
290 | ParaArtwork { type_ :: !TL.Text
293 | ParaJudgment !Judgment
296 -- *** Type 'ListItem'
297 data ListItem = ListItem
303 type Plain = TS.Trees PlainNode
305 -- ** Type 'PlainNode'
309 | PlainCode -- ^ Code (monospaced)
310 | PlainDel -- ^ Deleted (crossed-over)
312 | PlainGroup -- ^ Group subTrees (neutral)
314 | PlainSC -- ^ Small Caps
315 | PlainSub -- ^ Subscript
316 | PlainSup -- ^ Superscript
317 | PlainU -- ^ Underlined
318 | PlainEref { eref_href :: !URL } -- ^ External reference
319 | PlainIref { iref_term :: !Words
320 } -- ^ Index reference
321 | PlainRef { ref_locTCT :: !TCT.Location
322 , ref_posXML :: !XML.Pos
323 , ref_ident :: !Ident
324 } -- ^ Reference reference
325 | PlainSpan { attrs :: !CommonAttrs } -- ^ Neutral node
327 | PlainBreak -- ^ Line break (\n)
329 | PlainNote { note_paras :: ![Para]
331 | PlainTag { tag_locTCT :: !TCT.Location
332 , tag_posXML :: !XML.Pos
333 , tag_ident :: !Ident
336 | PlainAt { at_locTCT :: !TCT.Location
337 , at_posXML :: !XML.Pos
351 newtype Tag = Tag TL.Text
352 deriving (Eq,Ord,Show,Semigroup,Monoid,Default,IsString,Hashable)
356 -- * Type 'ErrorTarget'
358 = ErrorTarget_Unknown !Nat1
359 | ErrorTarget_Ambiguous !(Maybe Nat1)
362 -- * Type 'ErrorAnchor'
364 = ErrorAnchor_Ambiguous !Nat1
368 -- * Type 'CommonAttrs'
369 data CommonAttrs = CommonAttrs
370 { attrs_id :: !(Maybe Ident)
371 , attrs_classes :: ![TL.Text]
372 } deriving (Eq,Ord,Show)
373 instance Default CommonAttrs where
376 , attrs_classes = def
381 { anchor_section :: !XML.Pos
382 , anchor_count :: !Nat1
383 } deriving (Eq,Ord,Show)
386 newtype Name = Name { unName :: TL.Text }
387 deriving (Eq,Ord,Show,Semigroup,Monoid,Default,IsString,Hashable)
390 newtype Title = Title { unTitle :: Plain }
391 deriving (Show,Semigroup,Monoid,Default)
392 instance Eq Title where
393 (==) = (==) `on` similarPlain . unTitle
394 -- | Return a similar version of a 'Plain' by removing:
396 -- * parsing residues ('PlainGroup'),
397 -- * notes ('PlainNote'),
398 -- * and position specific annotations ('Ident' and 'Anchor').
399 similarPlain :: Plain -> Plain
400 similarPlain = foldMap $ \(TS.Tree n ts) ->
401 let skip = similarPlain ts in
402 let keep = pure $ TS.Tree n $ skip in
406 PlainIref{..} -> keep
407 PlainRef{..} -> pure $ TS.Tree PlainRef{ ref_locTCT = def
410 PlainSpan attrs -> pure $ TS.Tree n' skip
411 where n' = PlainSpan{attrs = CommonAttrs{ attrs_id = Nothing
412 , attrs_classes = List.sort $ attrs_classes attrs }}
422 PlainEref{..} -> keep
423 PlainAt{..} -> pure $ TS.Tree PlainAt{at_locTCT=def, at_posXML=def, ..} skip
424 PlainTag{..} -> pure $ TS.Tree PlainTag{tag_locTCT=def, tag_posXML=def, ..} skip
427 -- | Return the same hash if 'similarPlain' is applied on the 'Title' before hashing.
429 -- Warning: when using the key of HashMap or HashSet,
430 -- only the data taken into account by this 'Hashable' instance is reliable.
431 instance Hashable Title where
432 hashWithSalt salt (Title ps) = hs salt ps
440 PlainIref{..} -> s`hashWithSalt`(0::Int)`hashWithSalt`iref_term
441 PlainTag{..} -> s`hashWithSalt`(1::Int)`hashWithSalt`tag_ident`hashWithSalt`tag_back
442 PlainAt{..} -> s`hashWithSalt`(2::Int)`hashWithSalt`at_ident`hashWithSalt`at_back
443 PlainSpan{..} -> s`hashWithSalt`(3::Int)`hashWithSalt`List.sort (attrs_classes attrs)
444 PlainB -> s`hashWithSalt`(4::Int)
445 PlainCode -> s`hashWithSalt`(5::Int)
446 PlainDel -> s`hashWithSalt`(6::Int)
447 PlainI -> s`hashWithSalt`(7::Int)
448 PlainQ -> s`hashWithSalt`(8::Int)
449 PlainSC -> s`hashWithSalt`(9::Int)
450 PlainSub -> s`hashWithSalt`(10::Int)
451 PlainSup -> s`hashWithSalt`(11::Int)
452 PlainU -> s`hashWithSalt`(12::Int)
453 PlainEref{..} -> s`hashWithSalt`(13::Int)`hashWithSalt`eref_href
454 PlainRef{..} -> s`hashWithSalt`(14::Int)`hashWithSalt`ref_ident
455 PlainBreak -> s`hashWithSalt`(15::Int)
456 PlainText t -> s`hashWithSalt`(16::Int)`hashWithSalt`t
460 { entity_rel :: !Name
461 , entity_role :: !Name
462 , entity_name :: !TL.Text
463 , entity_street :: !TL.Text
464 , entity_zipcode :: !TL.Text
465 , entity_city :: !TL.Text
466 , entity_region :: !TL.Text
467 , entity_country :: !TL.Text
468 , entity_email :: !TL.Text
469 , entity_tel :: !TL.Text
470 , entity_fax :: !TL.Text
471 , entity_url :: !(Maybe URL)
472 , entity_org :: ![Entity]
474 instance Default Entity where
479 , entity_street = def
480 , entity_zipcode = def
482 , entity_region = def
483 , entity_country = def
492 newtype Include = Include
493 { include_href :: FilePath
495 instance Default Include where
500 -- * Type 'Reference'
501 data Reference = Reference
502 { reference_posXML :: !XML.Pos
503 , reference_locTCT :: !TCT.Location
504 , reference_id :: !Ident
505 , reference_about :: !About
513 , date_month :: !(Maybe Nat1)
514 , date_day :: !(Maybe Nat1)
516 instance Default Date where
521 , date_month = Just (Nat1 01)
522 , date_day = Just (Nat1 01)
524 instance Semigroup Date where
532 , link_plain :: !Plain
533 -- , link_type :: !(Maybe TL.Text)
535 instance Default Link where
546 { alias_attrs :: !CommonAttrs
547 , alias_title :: !Title
552 { serie_name :: !Name
553 , serie_id :: !TL.Text
555 instance Default Serie where
561 -- | Builtins 'URL' recognized from |Serie|'s 'name'.
562 urlSerie :: Serie -> Maybe URL
565 "RFC" | TL.all Char.isDigit serie_id ->
566 Just $ URL $ "https://tools.ietf.org/html/rfc"<>serie_id
567 "DOI" -> Just $ URL $ "https://dx.doi.org/"<>serie_id
574 type Words = [WordOrSpace]
576 -- *** Type 'WordOrSpace'
580 deriving (Eq,Ord,Show,Generic)
581 instance Hashable WordOrSpace
584 type Terms = [Aliases]
586 -- *** Type 'Aliases'
587 type Aliases = [Words]
589 -- ** Type 'PathWord'
590 type PathWord = TM.Path Word
592 pathFromWords :: Words -> Maybe PathWord
594 case ws >>= unSpace of
595 p:ps | not (TL.null p) -> Just (TM.path p ps)
603 type Location = (TCT.Location, XML.Pos)
606 type Pos = Seq Section