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 | PlainPageRef { pageRef_locTCT :: !TCT.Location
326 , pageRef_posXML :: !XML.Pos
327 , pageRef_path :: !PathPage
328 , pageRef_at :: !(Maybe Ident)
329 } -- ^ Page reference
330 | PlainSpan { attrs :: !CommonAttrs } -- ^ Neutral node
332 | PlainBreak -- ^ Line break (\n)
334 | PlainNote { note_paras :: ![Para]
336 | PlainTag { tag_locTCT :: !TCT.Location
337 , tag_posXML :: !XML.Pos
338 , tag_ident :: !Ident
341 | PlainAt { at_locTCT :: !TCT.Location
342 , at_posXML :: !XML.Pos
356 newtype Tag = Tag TL.Text
357 deriving (Eq,Ord,Show,Semigroup,Monoid,Default,IsString,Hashable)
361 -- * Type 'ErrorTarget'
363 = ErrorTarget_Unknown !Nat1
364 | ErrorTarget_Ambiguous !(Maybe Nat1)
367 -- * Type 'ErrorAnchor'
369 = ErrorAnchor_Ambiguous !Nat1
373 -- * Type 'CommonAttrs'
374 data CommonAttrs = CommonAttrs
375 { attrs_id :: !(Maybe Ident)
376 , attrs_classes :: ![TL.Text]
377 } deriving (Eq,Ord,Show)
378 instance Default CommonAttrs where
381 , attrs_classes = def
386 { anchor_section :: !XML.Pos
387 , anchor_count :: !Nat1
388 } deriving (Eq,Ord,Show)
391 type PathPage = TL.Text
394 newtype Name = Name { unName :: TL.Text }
395 deriving (Eq,Ord,Show,Semigroup,Monoid,Default,IsString,Hashable)
398 newtype Title = Title { unTitle :: Plain }
399 deriving (Show,Semigroup,Monoid,Default)
400 instance Eq Title where
401 (==) = (==) `on` similarPlain . unTitle
402 -- | Return a similar version of a 'Plain' by removing:
404 -- * parsing residues ('PlainGroup'),
405 -- * notes ('PlainNote'),
406 -- * and position specific annotations ('Ident' and 'Anchor').
407 similarPlain :: Plain -> Plain
408 similarPlain = foldMap $ \(TS.Tree n ts) ->
409 let skip = similarPlain ts in
410 let keep = pure $ TS.Tree n $ skip in
414 PlainIref{..} -> keep
415 PlainRef{..} -> pure $ TS.Tree PlainRef{ ref_locTCT = def
418 PlainPageRef{..} -> pure $ TS.Tree PlainPageRef{ pageRef_locTCT = def
419 , pageRef_posXML = def
421 PlainSpan attrs -> pure $ TS.Tree n' skip
422 where n' = PlainSpan{attrs = CommonAttrs{ attrs_id = Nothing
423 , attrs_classes = List.sort $ attrs_classes attrs }}
433 PlainEref{..} -> keep
434 PlainAt{..} -> pure $ TS.Tree PlainAt{at_locTCT=def, at_posXML=def, ..} skip
435 PlainTag{..} -> pure $ TS.Tree PlainTag{tag_locTCT=def, tag_posXML=def, ..} skip
438 -- | Return the same hash if 'similarPlain' is applied on the 'Title' before hashing.
440 -- Warning: when using the key of HashMap or HashSet,
441 -- only the data taken into account by this 'Hashable' instance is reliable.
442 instance Hashable Title where
443 hashWithSalt salt (Title ps) = hs salt ps
451 PlainIref{..} -> s`hashWithSalt`(0::Int)`hashWithSalt`iref_term
452 PlainTag{..} -> s`hashWithSalt`(1::Int)`hashWithSalt`tag_ident`hashWithSalt`tag_back
453 PlainAt{..} -> s`hashWithSalt`(2::Int)`hashWithSalt`at_ident`hashWithSalt`at_back
454 PlainSpan{..} -> s`hashWithSalt`(3::Int)`hashWithSalt`List.sort (attrs_classes attrs)
455 PlainB -> s`hashWithSalt`(4::Int)
456 PlainCode -> s`hashWithSalt`(5::Int)
457 PlainDel -> s`hashWithSalt`(6::Int)
458 PlainI -> s`hashWithSalt`(7::Int)
459 PlainQ -> s`hashWithSalt`(8::Int)
460 PlainSC -> s`hashWithSalt`(9::Int)
461 PlainSub -> s`hashWithSalt`(10::Int)
462 PlainSup -> s`hashWithSalt`(11::Int)
463 PlainU -> s`hashWithSalt`(12::Int)
464 PlainEref{..} -> s`hashWithSalt`(13::Int)`hashWithSalt`eref_href
465 PlainRef{..} -> s`hashWithSalt`(14::Int)`hashWithSalt`ref_ident
466 PlainPageRef{..} -> s`hashWithSalt`(15::Int)`hashWithSalt`pageRef_at`hashWithSalt`pageRef_path
467 PlainBreak -> s`hashWithSalt`(16::Int)
468 PlainText t -> s`hashWithSalt`(17::Int)`hashWithSalt`t
472 { entity_rel :: !Name
473 , entity_role :: !Name
474 , entity_name :: !TL.Text
475 , entity_street :: !TL.Text
476 , entity_zipcode :: !TL.Text
477 , entity_city :: !TL.Text
478 , entity_region :: !TL.Text
479 , entity_country :: !TL.Text
480 , entity_email :: !TL.Text
481 , entity_tel :: !TL.Text
482 , entity_fax :: !TL.Text
483 , entity_url :: !(Maybe URL)
484 , entity_org :: ![Entity]
486 instance Default Entity where
491 , entity_street = def
492 , entity_zipcode = def
494 , entity_region = def
495 , entity_country = def
504 newtype Include = Include
505 { include_href :: FilePath
507 instance Default Include where
512 -- * Type 'Reference'
513 data Reference = Reference
514 { reference_posXML :: !XML.Pos
515 , reference_locTCT :: !TCT.Location
516 , reference_id :: !Ident
517 , reference_about :: !About
525 , date_month :: !(Maybe Nat1)
526 , date_day :: !(Maybe Nat1)
528 instance Default Date where
533 , date_month = Just (Nat1 01)
534 , date_day = Just (Nat1 01)
536 instance Semigroup Date where
544 , link_plain :: !Plain
545 -- , link_type :: !(Maybe TL.Text)
547 instance Default Link where
558 { alias_attrs :: !CommonAttrs
559 , alias_title :: !Title
564 { serie_name :: !Name
565 , serie_id :: !TL.Text
567 instance Default Serie where
573 -- | Builtins 'URL' recognized from |Serie|'s 'name'.
574 urlSerie :: Serie -> Maybe URL
577 "RFC" | TL.all Char.isDigit serie_id ->
578 Just $ URL $ "https://tools.ietf.org/html/rfc"<>serie_id
579 "DOI" -> Just $ URL $ "https://dx.doi.org/"<>serie_id
586 type Words = [WordOrSpace]
588 -- *** Type 'WordOrSpace'
592 deriving (Eq,Ord,Show,Generic)
593 instance Hashable WordOrSpace
596 type Terms = [Aliases]
598 -- *** Type 'Aliases'
599 type Aliases = [Words]
601 -- ** Type 'PathWord'
602 type PathWord = TM.Path Word
604 pathFromWords :: Words -> Maybe PathWord
606 case ws >>= unSpace of
607 p:ps | not (TL.null p) -> Just (TM.path p ps)
615 type Location = (TCT.Location, XML.Pos)
618 type Pos = Seq Section