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.TreeSeq.Strict as TS
38 import qualified Data.TreeMap.Strict as TM
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
51 instance Default Document where
59 { head_about :: !About
60 , head_judgments :: ![Judgment]
61 -- [(Judgment, [Tree.Tree (Maybe MJ.Share, [Choice])])]
63 instance Default Head where
66 , head_judgments = def
71 { about_headers :: ![Header]
72 , about_titles :: ![Title]
73 , about_url :: !(Maybe URL)
74 , about_authors :: ![Entity]
75 , about_editor :: !(Maybe Entity)
76 , about_date :: !(Maybe Date)
77 , about_tags :: ![TL.Text]
78 , about_links :: ![Link]
79 , about_series :: ![Serie]
80 , about_includes :: ![Include] -- FIXME: remove?
82 instance Default About where
85 , about_includes = def
95 instance Semigroup About where
97 { about_headers = about_headers x <> about_headers y
98 , about_titles = about_titles x <> about_titles y
99 , about_url = about_url x <> about_url y
100 , about_authors = about_authors x <> about_authors y
101 , about_editor = about_editor x <> about_editor y
102 , about_date = about_date x <> about_date y
103 , about_tags = about_tags x <> about_tags y
104 , about_links = about_links x <> about_links y
105 , about_series = about_series x <> about_series y
106 , about_includes = about_includes x <> about_includes y
111 { header_name :: !TL.Text
112 , header_value :: !Plain
116 type Body = TS.Trees BodyNode
118 -- ** Type 'BodyNode'
120 = BodySection !Section -- ^ node
121 | BodyBlock !Block -- ^ leaf
125 data Section = Section
126 { section_posXML :: !XML.Pos
127 , section_attrs :: !CommonAttrs
128 , section_title :: !Title
129 , section_aliases :: ![Alias]
130 , section_judgments :: ![Judgment]
136 | BlockBreak { attrs :: !CommonAttrs }
137 | BlockToC { posXML :: !XML.Pos
138 , attrs :: !CommonAttrs
139 , depth :: !(Maybe Nat)
141 | BlockToF { posXML :: !XML.Pos
142 , attrs :: !CommonAttrs
143 , types :: ![TL.Text]
145 | BlockAside { posXML :: !XML.Pos
146 , attrs :: !CommonAttrs
149 | BlockFigure { posXML :: !XML.Pos
151 , attrs :: !CommonAttrs
152 , mayTitle :: !(Maybe Title)
155 | BlockIndex { posXML :: !XML.Pos
156 , attrs :: !CommonAttrs
159 | BlockReferences { posXML :: !XML.Pos
160 , attrs :: !CommonAttrs
161 , refs :: ![Reference]
162 } -- FIXME: move to ParaReferences?
163 | BlockJudges !Judges
164 | BlockGrades { posXML :: !XML.Pos
165 , attrs :: !CommonAttrs
171 data Judgment = Judgment
172 { judgment_opinionsByChoice :: !(Maybe (MJ.OpinionsByChoice Choice Name (MJ.Ranked Grade)))
173 , judgment_judges :: !(Maybe Judges)
174 , judgment_grades :: !(Maybe (MJ.Grades (MJ.Ranked Grade)))
175 , judgment_posXML :: !XML.Pos
176 , judgment_locTCT :: !TCT.Location
177 , judgment_judgesId :: !Ident
178 , judgment_gradesId :: !Ident
179 , judgment_importance :: !(Maybe MJ.Share)
180 , judgment_question :: !(Maybe Title)
181 , judgment_choices :: ![Choice]
183 instance Eq Judgment where
185 judgment_judgesId x == judgment_judgesId y &&
186 judgment_gradesId x == judgment_gradesId y &&
187 judgment_question x == judgment_question y
188 instance Hashable Judgment where
189 hashWithSalt s Judgment{..} =
190 s`hashWithSalt`judgment_judgesId
191 `hashWithSalt`judgment_gradesId
192 `hashWithSalt`judgment_question
194 -- ** Type 'ErrorJudgment'
196 = ErrorJudgment_Judges
197 | ErrorJudgment_Grades
202 { judges_locTCT :: !TCT.Location
203 , judges_posXML :: !XML.Pos
204 , judges_attrs :: !CommonAttrs
205 , judges_byName :: !(HM.HashMap Name [Judge])
210 { judge_locTCT :: !TCT.Location
211 , judge_posXML :: !XML.Pos
212 , judge_name :: !Name
213 , judge_title :: !(Maybe Title)
214 , judge_defaultGrades :: !(HM.HashMap Ident [Name])
219 { grade_posXML :: !XML.Pos
220 , grade_name :: !Name
221 , grade_color :: !TL.Text
222 , grade_isDefault :: !Bool
223 , grade_title :: !(Maybe Title)
228 { choice_locTCT :: TCT.Location
229 , choice_posXML :: XML.Pos
230 , choice_title :: !(Maybe Title)
231 , choice_opinions :: ![Opinion]
233 instance Eq Choice where
234 (==) = (==)`on`choice_title
235 instance Hashable Choice where
236 hashWithSalt s Choice{..} =
237 hashWithSalt s choice_title
240 data Opinion = Opinion
241 { opinion_locTCT :: !TCT.Location
242 , opinion_posXML :: !XML.Pos
243 , opinion_judge :: !Name
244 , opinion_grade :: !Name
245 , opinion_importance :: !(Maybe MJ.Share)
246 , opinion_comment :: !(Maybe Title)
251 = ParaItem { item :: !ParaItem }
252 | ParaItems { posXML :: !XML.Pos
253 , attrs :: !CommonAttrs
254 , items :: ![ParaItem]
258 -- ** Type 'ParaItem'
261 | ParaComment !TL.Text
264 | ParaQuote { type_ :: !TL.Text
267 | ParaArtwork { type_ :: !TL.Text
270 | ParaJudgment !Judgment
273 -- *** Type 'ListItem'
274 data ListItem = ListItem
280 type Plain = TS.Trees PlainNode
282 -- ** Type 'PlainNode'
286 | PlainCode -- ^ Code (monospaced)
287 | PlainDel -- ^ Deleted (crossed-over)
289 | PlainGroup -- ^ Group subTrees (neutral)
291 | PlainSC -- ^ Small Caps
292 | PlainSub -- ^ Subscript
293 | PlainSup -- ^ Superscript
294 | PlainU -- ^ Underlined
295 | PlainEref { eref_href :: !URL } -- ^ External reference
296 | PlainIref { iref_term :: !Words
297 } -- ^ Index reference
298 | PlainTag { tag_locTCT :: !TCT.Location
299 , tag_posXML :: !XML.Pos
301 | PlainRref { rref_locTCT :: !TCT.Location
302 , rref_posXML :: !XML.Pos
304 } -- ^ Reference reference
305 | PlainSpan { attrs :: !CommonAttrs } -- ^ Neutral node
307 | PlainBreak -- ^ Line break (\n)
309 | PlainNote { note_paras :: ![Para]
314 -- * Type 'ErrorTarget'
316 = ErrorTarget_Unknown !Nat1
317 | ErrorTarget_Ambiguous !(Maybe Nat1)
320 -- * Type 'ErrorAnchor'
322 = ErrorAnchor_Ambiguous !Nat1
326 -- * Type 'CommonAttrs'
327 data CommonAttrs = CommonAttrs
328 { id :: !(Maybe Ident)
329 , classes :: ![TL.Text]
330 } deriving (Eq,Ord,Show)
331 instance Default CommonAttrs where
339 { anchor_section :: !XML.Pos
340 , anchor_count :: !Nat1
341 } deriving (Eq,Ord,Show)
344 newtype Name = Name { unName :: TL.Text }
345 deriving (Eq,Ord,Show,Semigroup,Monoid,Default,IsString,Hashable)
348 newtype Title = Title { unTitle :: Plain }
349 deriving (Show,Semigroup,Monoid,Default)
350 instance Eq Title where
351 (==) = (==) `on` similarPlain . unTitle
352 -- | Return a similar version of a 'Plain' by removing:
354 -- * parsing residues ('PlainGroup'),
355 -- * notes ('PlainNote'),
356 -- * and position specific annotations ('Ident' and 'Anchor').
357 similarPlain :: Plain -> Plain
358 similarPlain = foldMap $ \(TS.Tree n ts) ->
359 let skip = similarPlain ts in
360 let keep = pure $ TS.Tree n $ skip in
364 PlainIref{..} -> keep
365 PlainRref{..} -> pure $ TS.Tree PlainRref{ rref_locTCT = def
367 PlainSpan attrs -> pure $ TS.Tree n' skip
368 where n' = PlainSpan{attrs = CommonAttrs{ id = Nothing
369 , classes = List.sort $ classes attrs }}
379 PlainEref{..} -> keep
380 PlainTag{..} -> pure $ TS.Tree PlainTag{tag_locTCT=def, ..} skip
383 -- | Return the same hash if 'similarPlain' is applied on the 'Title' before hashing.
385 -- Warning: when using the key of HashMap or HashSet,
386 -- only the data taken into account by this 'Hashable' instance is reliable.
387 instance Hashable Title where
388 hashWithSalt salt (Title ps) = hs salt ps
396 PlainIref{..} -> s`hashWithSalt`(0::Int)`hashWithSalt`iref_term
397 PlainTag{..} -> s`hashWithSalt`(1::Int)
398 PlainSpan{..} -> s`hashWithSalt`(2::Int)`hashWithSalt`List.sort (classes attrs)
399 PlainB -> s`hashWithSalt`(3::Int)
400 PlainCode -> s`hashWithSalt`(4::Int)
401 PlainDel -> s`hashWithSalt`(5::Int)
402 PlainI -> s`hashWithSalt`(6::Int)
403 PlainQ -> s`hashWithSalt`(7::Int)
404 PlainSC -> s`hashWithSalt`(8::Int)
405 PlainSub -> s`hashWithSalt`(9::Int)
406 PlainSup -> s`hashWithSalt`(10::Int)
407 PlainU -> s`hashWithSalt`(11::Int)
408 PlainEref{..} -> s`hashWithSalt`(12::Int)`hashWithSalt`eref_href
409 PlainRref{..} -> s`hashWithSalt`(13::Int)`hashWithSalt`rref_to
410 PlainBreak -> s`hashWithSalt`(14::Int)
411 PlainText t -> s`hashWithSalt`(15::Int)`hashWithSalt`t
417 , zipcode :: !TL.Text
420 , country :: !TL.Text
424 , url :: !(Maybe URL)
425 , org :: !(Maybe Entity)
427 instance Default Entity where
441 instance Semigroup Entity where
445 newtype Include = Include
448 instance Default Include where
453 -- * Type 'Reference'
454 data Reference = Reference
455 { {-reference_error :: !(Maybe ErrorAnchor)
456 ,-} reference_posXML :: !XML.Pos
457 , reference_locTCT :: !TCT.Location
458 , reference_id :: !Ident
459 , reference_about :: !About
465 , month :: !(Maybe Nat1)
466 , day :: !(Maybe Nat1)
468 instance Default Date where
471 , month = Just (Nat1 01)
472 , day = Just (Nat1 01)
474 instance Semigroup Date where
482 , type_ :: !(Maybe TL.Text)
485 instance Default Link where
495 newtype Alias = Alias
504 instance Default Serie where
510 -- | Builtins 'URL' recognized from |Serie|'s 'name'.
511 urlSerie :: Serie -> Maybe URL
512 urlSerie Serie{id=id_, name} =
514 "RFC" | TL.all Char.isDigit id_ ->
515 Just $ URL $ "https://tools.ietf.org/html/rfc"<>id_
516 "DOI" -> Just $ URL $ "https://dx.doi.org/"<>id_
523 type Words = [WordOrSpace]
525 -- *** Type 'WordOrSpace'
529 deriving (Eq,Ord,Show,Generic)
530 instance Hashable WordOrSpace
533 type Terms = [Aliases]
535 -- *** Type 'Aliases'
536 type Aliases = [Words]
538 -- ** Type 'PathWord'
539 type PathWord = TM.Path Word
541 pathFromWords :: Words -> Maybe PathWord
543 case ws >>= unSpace of
544 p:ps | not (TL.null p) -> Just (TM.path p ps)
552 type Location = (TCT.Location, XML.Pos)