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.String (IsString)
28 import GHC.Generics (Generic)
29 import System.FilePath (FilePath)
30 import Text.Show (Show)
31 import qualified Data.Char as Char
32 import qualified Data.List as List
33 import qualified Data.Text.Lazy as TL
34 import qualified Data.TreeSeq.Strict as TS
35 import qualified Hjugement as MJ
37 import Hdoc.Utils (Nat(..), Nat1(..), succNat, succNat1)
38 import Hdoc.XML (Ident(..), URL(..))
39 import qualified Hdoc.XML as XML
40 import qualified Hdoc.TCT.Cell as TCT
43 data Document = Document
47 instance Default Document where
56 , judgments :: ![Judgment]
57 -- [(Judgment, [Tree.Tree (Maybe MJ.Share, [Choice])])]
59 instance Default Head where
67 { headers :: ![Header]
70 , authors :: ![Entity]
71 , editor :: !(Maybe Entity)
72 , date :: !(Maybe Date)
76 , includes :: ![Include] -- FIXME: remove?
78 instance Default About where
91 instance Semigroup About where
93 { headers = headers x <> headers y
94 , titles = titles x <> titles y
95 , url = url (x::About) <> url (y::About)
96 , authors = authors x <> authors y
97 , editor = editor x <> editor y
98 , date = date x <> date y
99 , tags = tags x <> tags y
100 , links = links x <> links y
101 , series = series x <> series y
102 , includes = includes x <> includes y
112 type Body = TS.Trees BodyNode
114 -- ** Type 'BodyNode'
116 = BodySection !Section -- ^ node
117 | BodyBlock !Block -- ^ leaf
121 data Section = Section
123 , attrs :: !CommonAttrs
125 , aliases :: ![Alias]
126 , judgments :: ![Judgment]
132 | BlockBreak { attrs :: !CommonAttrs }
133 | BlockToC { xmlPos :: !XML.Pos
134 , attrs :: !CommonAttrs
135 , depth :: !(Maybe Nat)
137 | BlockToF { xmlPos :: !XML.Pos
138 , attrs :: !CommonAttrs
139 , types :: ![TL.Text]
141 | BlockAside { xmlPos :: !XML.Pos
142 , attrs :: !CommonAttrs
145 | BlockFigure { xmlPos :: !XML.Pos
147 , attrs :: !CommonAttrs
148 , mayTitle :: !(Maybe Title)
151 | BlockIndex { xmlPos :: !XML.Pos
152 , attrs :: !CommonAttrs
155 | BlockReferences { xmlPos :: !XML.Pos
156 , attrs :: !CommonAttrs
157 , refs :: ![Reference]
158 } -- FIXME: move to ParaReferences?
159 | BlockJudges { xmlPos :: !XML.Pos
160 , attrs :: !CommonAttrs
163 | BlockGrades { xmlPos :: !XML.Pos
164 , attrs :: !CommonAttrs
170 data Judgment = Judgment
171 { opinionsByChoice :: !(Maybe (MJ.OpinionsByChoice Choice Judge Grade))
174 , importance :: !(Maybe MJ.Share)
175 , question :: !(Maybe Title)
176 , choices :: ![Choice]
178 instance Eq Judgment where
180 judges x == judges y &&
181 grades x == grades y &&
182 question x == question y
183 instance Hashable Judgment where
184 hashWithSalt s Judgment{..} =
185 s`hashWithSalt`judges
187 `hashWithSalt`question
192 , title :: !(Maybe Title)
193 , defaultGrades :: ![(Ident, Name)]
202 , title :: !(Maybe Title)
207 { title :: !(Maybe Title)
208 , opinions :: ![Opinion]
210 instance Eq Choice where
211 (==) = (==)`on`(title::Choice -> Maybe Title)
212 instance Hashable Choice where
213 hashWithSalt s Choice{..} =
217 data Opinion = Opinion
220 , importance :: !(Maybe MJ.Share)
221 , comment :: !(Maybe Title)
226 = ParaItem { item :: !ParaItem }
227 | ParaItems { xmlPos :: !XML.Pos
228 , attrs :: !CommonAttrs
229 , items :: ![ParaItem]
233 -- ** Type 'ParaItem'
236 | ParaComment !TL.Text
239 | ParaQuote { type_ :: !TL.Text
242 | ParaArtwork { type_ :: !TL.Text
245 | ParaJudgment !Judgment
248 -- *** Type 'ListItem'
249 data ListItem = ListItem
255 type Plain = TS.Trees PlainNode
257 -- ** Type 'PlainNode'
261 | PlainCode -- ^ Code (monospaced)
262 | PlainDel -- ^ Deleted (crossed-over)
264 | PlainGroup -- ^ Group subTrees (neutral)
266 | PlainSC -- ^ Small Caps
267 | PlainSub -- ^ Subscript
268 | PlainSup -- ^ Superscript
269 | PlainU -- ^ Underlined
270 | PlainEref { href :: !URL } -- ^ External reference
271 | PlainIref { anchor :: !(Maybe Anchor)
273 } -- ^ Index reference
274 | PlainTag { error :: !(Maybe ErrorTarget)
275 , locTCT :: !TCT.Location
277 | PlainRref { error :: !(Maybe ErrorTarget)
278 , number :: !(Maybe Nat1)
279 , locTCT :: !TCT.Location
281 } -- ^ Reference reference
282 | PlainSpan { attrs :: !CommonAttrs } -- ^ Neutral node
284 | PlainBreak -- ^ Line break (\n)
286 | PlainNote { number :: !(Maybe Nat1)
291 -- * Type 'ErrorTarget'
293 = ErrorTarget_Unknown !Nat1
294 | ErrorTarget_Ambiguous !(Maybe Nat1)
297 -- * Type 'ErrorAnchor'
299 = ErrorAnchor_Ambiguous !Nat1
302 -- * Type 'CommonAttrs'
303 data CommonAttrs = CommonAttrs
304 { id :: !(Maybe Ident)
305 , classes :: ![TL.Text]
306 } deriving (Eq,Ord,Show)
307 instance Default CommonAttrs where
315 { section :: !XML.Pos
317 } deriving (Eq,Ord,Show)
320 newtype Name = Name { unName :: TL.Text }
321 deriving (Eq,Ord,Show,Semigroup,Monoid,Default,IsString,Hashable)
324 newtype Title = Title { unTitle :: Plain }
325 deriving (Show,Semigroup,Monoid,Default)
326 instance Eq Title where
327 (==) = (==) `on` similarPlain . unTitle
328 -- | Return a similar version of a 'Plain' by removing:
330 -- * parsing residues ('PlainGroup'),
331 -- * notes ('PlainNote'),
332 -- * and position specific annotations ('Ident' and 'Anchor').
333 similarPlain :: Plain -> Plain
334 similarPlain = foldMap $ \(TS.Tree n ts) ->
335 let skip = similarPlain ts in
336 let keep = pure $ TS.Tree n $ skip in
340 PlainIref{..} -> pure $ TS.Tree PlainIref{anchor=Nothing, ..} skip
341 PlainRref{..} -> pure $ TS.Tree PlainRref{error=Nothing, number=Nothing, locTCT=def, ..} skip
342 PlainSpan attrs -> pure $ TS.Tree n' skip
343 where n' = PlainSpan{attrs = CommonAttrs{ id = Nothing
344 , classes = List.sort $ classes attrs }}
354 PlainEref _to -> keep
355 PlainTag{..} -> pure $ TS.Tree PlainTag{locTCT=def, ..} skip
358 -- | Return the same hash if 'similarPlain' is applied on the 'Title' before hashing.
360 -- Warning: when using the key of HashMap or HashSet,
361 -- only the data taken into account by this 'Hashable' instance is reliable.
362 instance Hashable Title where
363 hashWithSalt salt (Title ps) = hs salt ps
371 PlainIref{..} -> s`hashWithSalt`(0::Int)`hashWithSalt`term
372 PlainTag{..} -> s`hashWithSalt`(1::Int)
373 PlainSpan{..} -> s`hashWithSalt`(2::Int)`hashWithSalt`List.sort (classes attrs)
374 PlainB -> s`hashWithSalt`(3::Int)
375 PlainCode -> s`hashWithSalt`(4::Int)
376 PlainDel -> s`hashWithSalt`(5::Int)
377 PlainI -> s`hashWithSalt`(6::Int)
378 PlainQ -> s`hashWithSalt`(7::Int)
379 PlainSC -> s`hashWithSalt`(8::Int)
380 PlainSub -> s`hashWithSalt`(9::Int)
381 PlainSup -> s`hashWithSalt`(10::Int)
382 PlainU -> s`hashWithSalt`(11::Int)
383 PlainEref{..} -> s`hashWithSalt`(12::Int)`hashWithSalt`href
384 PlainRref{..} -> s`hashWithSalt`(13::Int)`hashWithSalt`to
385 PlainBreak -> s`hashWithSalt`(14::Int)
386 PlainText t -> s`hashWithSalt`(15::Int)`hashWithSalt`t
392 , zipcode :: !TL.Text
395 , country :: !TL.Text
399 , url :: !(Maybe URL)
400 , org :: !(Maybe Entity)
402 instance Default Entity where
416 instance Semigroup Entity where
420 newtype Include = Include
423 instance Default Include where
428 -- * Type 'Reference'
429 data Reference = Reference
430 { error :: !(Maybe ErrorAnchor)
432 , locTCT :: !TCT.Location
440 , month :: !(Maybe Nat1)
441 , day :: !(Maybe Nat1)
443 instance Default Date where
446 , month = Just (Nat1 01)
447 , day = Just (Nat1 01)
449 instance Semigroup Date where
457 , type_ :: !(Maybe TL.Text)
460 instance Default Link where
470 newtype Alias = Alias
479 instance Default Serie where
485 -- | Builtins 'URL' recognized from |Serie|'s 'name'.
486 urlSerie :: Serie -> Maybe URL
487 urlSerie Serie{id=id_, name} =
489 "RFC" | TL.all Char.isDigit id_ ->
490 Just $ URL $ "https://tools.ietf.org/html/rfc"<>id_
491 "DOI" -> Just $ URL $ "https://dx.doi.org/"<>id_
498 type Words = [WordOrSpace]
500 -- *** Type 'WordOrSpace'
504 deriving (Eq,Ord,Show,Generic)
505 instance Hashable WordOrSpace
508 type Aliases = [Words]
511 type Terms = [Aliases]