1 {-# LANGUAGE FlexibleContexts #-}
2 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE OverloadedLists #-}
4 {-# LANGUAGE OverloadedStrings #-}
5 {-# LANGUAGE ViewPatterns #-}
6 {-# OPTIONS_GHC -fno-warn-orphans #-}
7 module Hdoc.TCT.Write.XML where
9 import Control.Monad (Monad(..))
11 import Data.Default.Class (Default(..))
12 import Data.Eq (Eq(..))
13 import Data.Foldable (Foldable(..))
14 import Data.Function (($), (.))
15 import Data.Functor ((<$>), (<$), ($>))
16 import Data.List.NonEmpty (NonEmpty(..))
17 import Data.Maybe (Maybe(..))
18 import Data.Monoid (Monoid(..))
19 import Data.Ord (Ord(..))
20 import Data.Semigroup (Semigroup(..))
21 import Data.Sequence (Seq, ViewL(..), ViewR(..), (<|))
23 import Data.TreeSeq.Strict (Tree(..), tree0)
24 import Data.Tuple (uncurry)
25 import Prelude (Num(..), undefined)
26 import qualified Data.Char as Char
27 import qualified Data.List as List
28 import qualified Data.Sequence as Seq
29 import qualified Data.Text.Lazy as TL
30 import qualified Hdoc.TCT.Write.Plain as Plain
32 -- import Hdoc.TCT.Debug
34 import Hdoc.TCT as TCT hiding (Parser)
35 import Hdoc.XML (XML, XMLs)
36 import qualified Hdoc.XML as XML
37 import Text.Blaze.XML ()
41 -- NOTE: 'XmlNode' are still annotated with 'Cell',
42 -- but nothing is done to preserve any ordering amongst them,
43 -- because 'Node's sometimes need to be reordered
44 -- (eg. about/title may have a title from the section before,
45 -- hence outside of about).
46 -- Still holding: @'cell_begin' < 'cell_end'@ and 'Cell' nesting.
47 writeXML :: Roots -> XMLs
49 -- (`S.evalState` def) $
51 sec@(Tree (unCell -> NodeHeader HeaderSection{}) _body) :< foot ->
52 let (titles, content) = partitionSection sec in
53 case Seq.viewl titles of
54 (unTree -> Cell st _) :< _ ->
62 case Seq.findIndexL isAbout content of
63 Nothing -> Tree (Cell st $ NodeHeader $ HeaderColon "about" "") mempty <| content
66 (unTree -> (unCell -> NodeHeader (HeaderColon "about" _wh))) -> True
75 , inh_para :: [Inh -> Root -> XML]
78 instance Default Inh where
81 , inh_para = List.repeat elementPara
86 elementPara :: Inh -> Root -> XML
87 elementPara inh (Tree c ts) = Tree (XML.NodeElem "para" <$ c) $ xmlify inh ts
89 elementTitle :: Inh -> Root -> XML
90 elementTitle inh (Tree c ts) = Tree (XML.NodeElem "title" <$ c) $ xmlify inh ts
92 elementName :: Inh -> Root -> XML
93 elementName inh (Tree c ts) = Tree (XML.NodeElem "name" <$ c) $ xmlify inh ts
95 attributeName :: Inh -> Root -> XML
96 attributeName _inh (Tree c ts) = tree0 (XML.NodeAttr "name" (Plain.writePlain ts) <$ c)
98 attributeId :: Inh -> Root -> XML
99 attributeId _inh (Tree c ts) = tree0 (XML.NodeAttr "id" (Plain.writePlain ts) <$ c)
103 xmlify :: Inh -> a -> XMLs
104 instance Xmlify Roots where
106 case Seq.viewl roots of
108 r@(Tree cr@(Cell _sr nr) ts) :< rs ->
110 ----------------------
111 -- NOTE: HeaderColon becomes parent
112 -- of any continuous following-sibling HeaderBar or HeaderGreat
113 NodeHeader (HeaderColon n _wh)
114 | (span, rest) <- spanlHeaderColon rs
116 xmlify inh (Tree cr (ts<>span)) <>
119 spanlHeaderColon :: Roots -> (Roots, Roots)
122 Tree (unCell -> NodeHeader (HeaderBar m _)) _ -> m == n
123 Tree (unCell -> NodeHeader (HeaderGreat m _)) _ -> m == n
125 ----------------------
126 -- NOTE: gather HeaderBrackets
127 NodeHeader HeaderBrackets{}
128 | (span,rest) <- spanlBrackets roots
130 (<| xmlify inh rest) $
131 element "references" $
134 spanlBrackets :: Roots -> (Roots, Roots)
137 Tree (unCell -> NodeHeader HeaderBrackets{}) _ -> True
139 ----------------------
140 -- NOTE: merge adjacent NodeText-s, shouldn't be needed, but just in case.
142 | Tree (cy@(unCell -> NodeText y)) ys :< rs' <- Seq.viewl rs ->
143 xmlify inh $ Tree (NodeText <$> (x <$ cr) <> (y <$ cy)) (ts <> ys) <| rs'
144 ----------------------
145 -- NOTE: detect (some text)[http://some.url] or (some text)[SomeRef]
147 | Tree (Cell sb (NodePair PairBracket)) bracket :< rs' <- Seq.viewl rs ->
148 (<| xmlify inh rs') $
150 (toList -> [unTree -> Cell sl (NodeToken (TokenLink lnk))]) ->
152 xmlAttrs [Cell sl ("to",lnk)] <>
156 xmlAttrs [Cell sb ("to",Plain.writePlain bracket)] <>
157 if null ts -- NOTE: preserve empty parens
158 then Seq.singleton $ tree0 (XML.NodeText "" <$ cr)
160 ----------------------
161 -- NOTE: gather HeaderDash
162 _ | (span, rest) <- spanlItems (==HeaderDash) roots
164 (<| xmlify inh rest) $
166 span >>= xmlify inh{inh_para=List.repeat elementPara}
167 ----------------------
168 -- NOTE: gather HeaderDot
169 | (span,rest) <- spanlItems (\case HeaderDot{} -> True; _ -> False) roots
171 (<| xmlify inh rest) $
173 span >>= xmlify inh{inh_para=List.repeat elementPara}
175 spanlItems :: (Header -> Bool) -> Roots -> (Roots, Roots)
176 spanlItems liHeader =
177 Seq.spanl $ \(unTree -> (unCell -> nod)) ->
179 NodeHeader (HeaderColon "li" _wh) -> True
180 NodeHeader hdr -> liHeader hdr
181 NodePair (PairElem "li" _as) -> True
183 ----------------------
184 NodePara | para:inh_para <- inh_para inh ->
186 xmlify inh{inh_para} rs
187 ----------------------
188 -- NOTE: context-free Root
190 xmlify inh r `unionXml`
193 element :: XML.Name -> XMLs -> XML
194 element n = Tree (XML.NodeElem n <$ cr)
195 instance Xmlify Root where
196 xmlify inh tn@(Tree (Cell ss@(sn:|ssn) nod) ts) =
198 ----------------------
202 para:_ -> Seq.singleton $ para inh tn
203 ----------------------
209 element "section" $ head <> xmlify inh' body
211 (titles, content) = partitionSection tn
212 (attrs, body) = partitionAttrs content
214 case Seq.viewl titles of
216 title@(unTree -> ct) :< subtitles ->
217 xmlAttrs (attrs `defaultAttr` (ct $> ("id",getAttrId title))) <>
218 xmlify inh{inh_para=List.repeat elementTitle} title <>
222 subtitles >>= \subtitle@(unTree -> cs) ->
224 Tree (cs $> XML.NodeElem "alias") $
225 xmlAttrs (Seq.singleton $ ct $> ("id",getAttrId subtitle)) <>
226 xmlify inh{inh_para=List.repeat elementTitle} subtitle
228 { inh_para = List.repeat elementPara
233 let (attrs,body) = partitionAttrs ts in
235 -- NOTE: insert titles into <about>
239 xmlify inh' (inh_titles inh) <>
241 xmlify inh'{inh_figure=False} body
242 -- NOTE: handle judgment
243 _ | n`List.elem`elemsJudgment -> -- FIXME: not a special case so far.
245 element (XML.localName n) $
252 "grades" -> List.repeat attributeId
253 "judges" -> List.repeat attributeId
254 _ -> List.repeat elementTitle
256 -- NOTE: in <figure> mode, unreserved elements become <figure>
257 _ | inh_figure inh && n`List.notElem`elems || TL.null n ->
260 -- xmlAttrs (setAttr (Cell en en ("type",n)) attrs) <>
261 xmlAttrs (attrs `defaultAttr` Cell (sn{span_end=span_begin sn}:|ssn) ("type", n)) <>
263 [Tree0{}] -> xmlify inh'{inh_para = List.repeat elementPara} body
264 _ -> xmlify inh'{inh_para = elementTitle : List.repeat elementPara} body
265 -- NOTE: reserved elements
268 element (XML.localName n) $
275 "about" -> List.repeat elementTitle
276 "reference" -> elementTitle : List.repeat elementPara
277 "serie" -> List.repeat attributeName
278 "author" -> List.repeat attributeName
279 "editor" -> List.repeat attributeName
280 "org" -> List.repeat attributeName
281 "note" -> List.repeat elementPara
286 if inh_figure inh && n`List.notElem`elems || TL.null n
290 xmlAttrs (Seq.singleton $ Cell (sn{span_end=span_end sn}:|ssn) ("type", n)) <>
291 xmlify inh{inh_para=[]} ts
294 Tree (cell $ NodeHeader $ HeaderColon n wh) ts
298 let (attrs,body) = partitionAttrs ts in
300 xmlAttrs (attrs `defaultAttr` Cell (sn{span_end=span_end sn}:|ssn) ("type", n)) <>
301 xmlify inh{inh_para=List.repeat elementPara} body
305 Tree0 $ cell $ XML.NodeAttr (XML.localName n) $
306 Plain.text (Plain.setStart ts def{Plain.state_escape = False}) ts
311 let span_end = (span_begin sn){pos_column=pos_column (span_begin sn) + int (TL.length n)} in
312 xmlAttrs (Seq.singleton $ Cell (sn{span_end}:|ssn) ("name", n)) <>
315 HeaderDash -> Seq.singleton $ element "li" $ xmlify inh ts
318 Seq.singleton $ Tree0 $ cell $
319 XML.NodeComment $ Plain.writePlain ts
321 HeaderBrackets ident ->
322 let (attrs,body) = partitionAttrs ts in
324 element "reference" $
325 xmlAttrs (setAttr (Cell (sn{span_end=span_end sn}:|ssn) ("id",ident)) attrs) <>
326 xmlify inh'{inh_para = elementTitle : elementTitle : List.repeat elementPara} body
328 inh' = inh{inh_figure = False}
330 HeaderDotSlash _file -> xmlify inh ts
331 ----------------------
334 PairBracket | to <- Plain.writePlain ts
335 , TL.all (\c -> c/='[' && c/=']' && Char.isPrint c && not (Char.isSpace c)) to ->
338 xmlAttrs [cell ("to",to)]
339 PairStar -> Seq.singleton $ element "b" $ xmlify inh ts
340 PairDash -> Seq.singleton $ element "del" $ xmlify inh ts
341 PairUnderscore -> Seq.singleton $ element "u" $ xmlify inh ts
342 PairSlash -> Seq.singleton $ element "i" $ xmlify inh ts
343 PairBackquote -> Seq.singleton $ element "code" $ xmlify inh ts
348 (Seq.viewl -> Tree0 (Cell sl (NodeToken (TokenText l))) :< ls) ->
350 m :> Tree0 (Cell sr (NodeToken (TokenText r))) ->
352 Tree0 (Cell sl (NodeToken (TokenText (TL.dropWhile Char.isSpace l))))
353 Seq.<|(m Seq.|>Tree0 (Cell sr (NodeToken (TokenText (TL.dropWhileEnd Char.isSpace r)))))
356 Tree0 (Cell sl (NodeToken (TokenText (TL.dropAround Char.isSpace l)))) Seq.<| ls
357 (Seq.viewr -> rs :> Tree0 (Cell sr (NodeToken (TokenText r)))) ->
359 rs Seq.|> Tree0 (Cell sr (NodeToken (TokenText (TL.dropAround Char.isSpace r))))
363 element (if isBackref then "tag-back" else "tag") $
364 xmlAttrs [cell ("to",Plain.writePlain ts)]
365 -- xmlAttrs [cell ("to",to)]
366 -- xmlify inh{inh_para=[]} ts
367 -- xmlAttrs [cell ("to",Plain.writePlain ts)]
370 element (if isBackref then "at-back" else "at") $
371 xmlAttrs [cell ("to",Plain.writePlain ts)]
372 PairElem name attrs ->
374 element (XML.localName name) $
375 xmlAttrs (Seq.fromList $ (\(_wh,ElemAttr{..}) ->
376 cell (XML.localName elemAttr_name,elemAttr_value)) <$> attrs) <>
379 Seq.singleton (Tree0 $ Cell (sn{span_end=bn'}:|ssn) $ XML.NodeText open) `unionXml`
380 xmlify inh ts `unionXml`
381 Seq.singleton (Tree0 $ Cell (sn{span_begin=en'}:|ssn) $ XML.NodeText close)
383 (open, close) = pairBorders pair ts
384 bn' = (span_begin sn){pos_column=pos_column (span_begin sn) + int (TL.length open)}
385 en' = (span_end sn){pos_column=pos_column (span_end sn) - int (TL.length close)}
386 ----------------------
387 NodeText t -> Seq.singleton $ Tree0 $ cell $ XML.NodeText t
388 ----------------------
391 TokenEscape c -> Seq.singleton $ Tree0 $ cell $ XML.NodeText $ TL.singleton c
392 TokenText t -> Seq.singleton $ Tree0 $ cell $ XML.NodeText t
393 TokenAt b to -> Seq.singleton $ element (if b then "at-back" else "at") $
394 xmlAttrs [cell ("to",to)] -- Seq.singleton $ Tree0 $ cell $ XML.NodeText to
395 TokenTag b to -> Seq.singleton $ element (if b then "tag-back" else "tag") $
396 xmlAttrs [cell ("to",to)] -- Seq.singleton $ Tree0 $ cell $ XML.NodeText to
397 TokenLink lnk -> Seq.singleton $ element "eref" $ xmlAttrs [cell ("to",lnk)]
398 ----------------------
406 element :: XML.Name -> XMLs -> XML
407 element n = Tree (cell $ XML.NodeElem n)
408 instance Xmlify (Seq (Cell (XML.Name,TL.Text))) where
409 xmlify _inh = xmlAttrs
413 -- | Reserved elements' name
513 elemsJudgment :: Set TL.Text
526 -- | Convenient alias, forcing the types
527 xmlAttrs :: Seq (Cell (XML.Name,TL.Text)) -> XMLs
528 xmlAttrs = (Tree0 . (uncurry XML.NodeAttr <$>) <$>)
530 -- | Extract section titles
531 partitionSection :: Root -> (Roots, Roots)
532 partitionSection (Tree (unCell -> NodeHeader (HeaderSection lvlPar)) body) =
533 case Seq.viewl body of
535 title@(unTree -> Cell (Span{span_end=et}:|_) NodePara) :< rest ->
536 let (subtitles, content) = spanlSubtitles et rest in
537 (title <| (subtitles >>= subTrees), content)
539 spanlSubtitles ep ts =
541 sub@(unTree -> Cell (Span{..}:|_) (NodeHeader (HeaderSection lvlSub))) :< rs
543 , pos_line span_begin - pos_line ep <= 1 ->
544 let (subs, ts') = spanlSubtitles span_end rs in
548 partitionSection _ = mempty
550 -- | Extract attributes
551 partitionAttrs :: Roots -> (Seq (Cell (XML.Name, TL.Text)), Roots)
552 partitionAttrs ts = (attrs,cs)
554 (as,cs) = (`Seq.partition` ts) $ \case
555 Tree (unCell -> NodeHeader (HeaderEqual n _wh)) _cs -> not $ TL.null n
559 Tree (Cell loc (NodeHeader (HeaderEqual n _wh))) a ->
560 Cell loc (XML.localName n, v)
562 v = Plain.text (Plain.setStart a def{Plain.state_escape = False}) a
565 getAttrId :: Root -> TL.Text
566 getAttrId = Plain.writePlain . Seq.singleton
569 Cell (XML.Name, TL.Text) ->
570 Seq (Cell (XML.Name, TL.Text)) ->
571 Seq (Cell (XML.Name, TL.Text))
572 setAttr a@(unCell -> (k, _v)) as =
573 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
574 Just idx -> Seq.update idx a as
578 Seq (Cell (XML.Name, TL.Text)) ->
579 Cell (XML.Name, TL.Text) ->
580 Seq (Cell (XML.Name, TL.Text))
581 defaultAttr as a@(unCell -> (k, _v)) =
582 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
588 -- | Unify two 'XMLs', merging border 'XML.NodeText's if any.
589 unionXml :: XMLs -> XMLs -> XMLs
591 case (Seq.viewr x, Seq.viewl y) of
592 (xs :> x0, y0 :< ys) ->
594 ( Tree0 (Cell ssx@(Span {span_file=fx}:|_sx) (XML.NodeText tx))
595 , Tree0 (Cell ssy@(Span {span_file=fy}:|_sy) (XML.NodeText ty)) ) | fx == fy ->
597 Seq.singleton (Tree0 $ (XML.NodeText <$>) $ Cell ssx tx <> Cell ssy ty) `unionXml`
603 unionsXml :: Foldable f => f XMLs -> XMLs
604 unionsXml = foldl' unionXml mempty