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 hiding (Parser)
36 import Text.Blaze.XML ()
40 -- NOTE: 'XmlNode' are still annotated with 'Cell',
41 -- but nothing is done to preserve any ordering amongst them,
42 -- because 'Node's sometimes need to be reordered
43 -- (eg. about/title may have a title from the section before,
44 -- hence outside of about).
45 -- Still holding: @'cell_begin' < 'cell_end'@ and 'Cell' nesting.
46 writeXML :: Roots -> XMLs
48 -- (`S.evalState` def) $
50 sec@(Tree (unCell -> NodeHeader HeaderSection{}) _body) :< foot ->
51 let (titles, content) = partitionSection sec in
52 case Seq.viewl titles of
53 (unTree -> Cell st _) :< _ ->
61 case Seq.findIndexL isAbout content of
62 Nothing -> Tree (Cell st $ NodeHeader $ HeaderColon "about" "") mempty <| content
65 (unTree -> (unCell -> NodeHeader (HeaderColon "about" _wh))) -> True
74 , inh_para :: [Inh -> Root -> XML]
77 instance Default Inh where
80 , inh_para = List.repeat elementPara
85 elementPara :: Inh -> Root -> XML
86 elementPara inh (Tree c ts) = Tree (XmlElem "para" <$ c) $ xmlify inh ts
88 elementTitle :: Inh -> Root -> XML
89 elementTitle inh (Tree c ts) = Tree (XmlElem "title" <$ c) $ xmlify inh ts
91 elementName :: Inh -> Root -> XML
92 elementName inh (Tree c ts) = Tree (XmlElem "name" <$ c) $ xmlify inh ts
94 attributeName :: Inh -> Root -> XML
95 attributeName _inh (Tree c ts) = tree0 (XmlAttr "name" (Plain.writePlain ts) <$ c)
97 attributeId :: Inh -> Root -> XML
98 attributeId _inh (Tree c ts) = tree0 (XmlAttr "id" (Plain.writePlain ts) <$ c)
102 xmlify :: Inh -> a -> XMLs
103 instance Xmlify Roots where
105 case Seq.viewl roots of
107 r@(Tree cr@(Cell _sr nr) ts) :< rs ->
109 ----------------------
110 -- NOTE: HeaderColon becomes parent
111 -- of any continuous following-sibling HeaderBar or HeaderGreat
112 NodeHeader (HeaderColon n _wh)
113 | (span, rest) <- spanlHeaderColon rs
115 xmlify inh (Tree cr (ts<>span)) <>
118 spanlHeaderColon :: Roots -> (Roots, Roots)
121 Tree (unCell -> NodeHeader (HeaderBar m _)) _ -> m == n
122 Tree (unCell -> NodeHeader (HeaderGreat m _)) _ -> m == n
124 ----------------------
125 -- NOTE: gather HeaderBrackets
126 NodeHeader HeaderBrackets{}
127 | (span,rest) <- spanlBrackets roots
129 (<| xmlify inh rest) $
130 element "references" $
133 spanlBrackets :: Roots -> (Roots, Roots)
136 Tree (unCell -> NodeHeader HeaderBrackets{}) _ -> True
138 ----------------------
139 -- NOTE: merge adjacent NodeText-s, shouldn't be needed, but just in case.
141 | Tree (cy@(unCell -> NodeText y)) ys :< rs' <- Seq.viewl rs ->
142 xmlify inh $ Tree (NodeText <$> (x <$ cr) <> (y <$ cy)) (ts <> ys) <| rs'
143 ----------------------
144 -- NOTE: detect [some text](http://some.url) or [SomeRef]
146 | Tree (Cell sb (NodePair PairBracket)) bracket :< rs' <- Seq.viewl rs ->
147 (<| xmlify inh rs') $
149 (toList -> [unTree -> Cell sl (NodeToken (TokenLink lnk))]) ->
151 xmlAttrs [Cell sl ("to",lnk)] <>
155 xmlAttrs [Cell sb ("to",Plain.writePlain bracket)] <>
157 ----------------------
158 -- NOTE: gather HeaderDash
159 _ | (span, rest) <- spanlItems (==HeaderDash) roots
161 (<| xmlify inh rest) $
163 span >>= xmlify inh{inh_para=List.repeat elementPara}
164 ----------------------
165 -- NOTE: gather HeaderDot
166 | (span,rest) <- spanlItems (\case HeaderDot{} -> True; _ -> False) roots
168 (<| xmlify inh rest) $
170 span >>= xmlify inh{inh_para=List.repeat elementPara}
172 spanlItems :: (Header -> Bool) -> Roots -> (Roots, Roots)
173 spanlItems liHeader =
174 Seq.spanl $ \(unTree -> (unCell -> nod)) ->
176 NodeHeader (HeaderColon "li" _wh) -> True
177 NodeHeader hdr -> liHeader hdr
178 NodePair (PairElem "li" _as) -> True
180 ----------------------
181 NodePara | para:inh_para <- inh_para inh ->
183 xmlify inh{inh_para} rs
184 ----------------------
185 -- NOTE: context-free Root
187 xmlify inh r `unionXml`
190 element :: XmlName -> XMLs -> XML
191 element n = Tree (XmlElem n <$ cr)
192 instance Xmlify Root where
193 xmlify inh tn@(Tree (Cell ss@(sn:|ssn) nod) ts) =
195 ----------------------
199 para:_ -> Seq.singleton $ para inh tn
200 ----------------------
206 element "section" $ head <> xmlify inh' body
208 (titles, content) = partitionSection tn
209 (attrs, body) = partitionAttrs content
211 case Seq.viewl titles of
213 title@(unTree -> ct) :< subtitles ->
214 xmlAttrs (attrs `defaultAttr` (ct $> ("id",getAttrId title))) <>
215 xmlify inh{inh_para=List.repeat elementTitle} title <>
219 subtitles >>= \subtitle@(unTree -> cs) ->
221 Tree (cs $> XmlElem "alias") $
222 xmlAttrs [cs $> ("id",getAttrId subtitle)]
224 { inh_para = List.repeat elementPara
229 let (attrs,body) = partitionAttrs ts in
231 -- NOTE: insert titles into <about>
235 xmlify inh' (inh_titles inh) <>
237 xmlify inh'{inh_figure=False} body
238 -- NOTE: handle judgment
239 _ | n`List.elem`elemsJudgment -> -- FIXME: not a special case so far.
241 element (xmlLocalName n) $
248 "grades" -> List.repeat attributeId
249 "judges" -> List.repeat attributeId
250 _ -> List.repeat elementTitle
252 -- NOTE: in <figure> mode, unreserved elements become <figure>
253 _ | inh_figure inh && n`List.notElem`elems || TL.null n ->
256 -- xmlAttrs (setAttr (Cell en en ("type",n)) attrs) <>
257 xmlAttrs (attrs `defaultAttr` Cell (sn{span_end=span_begin sn}:|ssn) ("type", n)) <>
259 [Tree0{}] -> xmlify inh'{inh_para = List.repeat elementPara} body
260 _ -> xmlify inh'{inh_para = elementTitle : List.repeat elementPara} body
261 -- NOTE: reserved elements
264 element (xmlLocalName n) $
271 "about" -> List.repeat elementTitle
272 "reference" -> elementTitle : List.repeat elementPara
273 "serie" -> List.repeat attributeName
274 "author" -> List.repeat attributeName
275 "editor" -> List.repeat attributeName
276 "org" -> List.repeat attributeName
277 "note" -> List.repeat elementPara
282 if inh_figure inh && n`List.notElem`elems || TL.null n
286 xmlAttrs (Seq.singleton $ Cell (sn{span_end=span_end sn}:|ssn) ("type", n)) <>
287 xmlify inh{inh_para=[]} ts
290 Tree (cell $ NodeHeader $ HeaderColon n wh) ts
294 let (attrs,body) = partitionAttrs ts in
296 xmlAttrs (attrs `defaultAttr` Cell (sn{span_end=span_end sn}:|ssn) ("type", n)) <>
297 xmlify inh{inh_para=List.repeat elementPara} body
301 Tree0 $ cell $ XmlAttr (xmlLocalName n) $
302 Plain.text (Plain.setStart ts def{Plain.state_escape = False}) ts
307 let span_end = (span_begin sn){pos_column=pos_column (span_begin sn) + int (TL.length n)} in
308 xmlAttrs (Seq.singleton $ Cell (sn{span_end}:|ssn) ("name", n)) <>
311 HeaderDash -> Seq.singleton $ element "li" $ xmlify inh ts
314 Seq.singleton $ Tree0 $ cell $
315 XmlComment $ Plain.writePlain ts
317 HeaderBrackets ident ->
318 let (attrs,body) = partitionAttrs ts in
320 element "reference" $
321 xmlAttrs (setAttr (Cell (sn{span_end=span_end sn}:|ssn) ("id",ident)) attrs) <>
322 xmlify inh'{inh_para = elementTitle : elementTitle : List.repeat elementPara} body
324 inh' = inh{inh_figure = False}
326 HeaderDotSlash _file -> xmlify inh ts
327 ----------------------
330 PairBracket | to <- Plain.writePlain ts
331 , TL.all (\c -> Char.isAlphaNum c || Char.isSymbol c) to ->
334 xmlAttrs [cell ("to",to)]
335 PairStar -> Seq.singleton $ element "b" $ xmlify inh ts
336 PairSlash -> Seq.singleton $ element "i" $ xmlify inh ts
337 PairBackquote -> Seq.singleton $ element "code" $ xmlify inh ts
342 (Seq.viewl -> Tree0 (Cell sl (NodeToken (TokenText l))) :< ls) ->
344 m :> Tree0 (Cell sr (NodeToken (TokenText r))) ->
346 Tree0 (Cell sl (NodeToken (TokenText (TL.dropWhile Char.isSpace l))))
347 Seq.<|(m Seq.|>Tree0 (Cell sr (NodeToken (TokenText (TL.dropWhileEnd Char.isSpace r)))))
350 Tree0 (Cell sl (NodeToken (TokenText (TL.dropAround Char.isSpace l)))) Seq.<| ls
351 (Seq.viewr -> rs :> Tree0 (Cell sr (NodeToken (TokenText r)))) ->
353 rs Seq.|> Tree0 (Cell sr (NodeToken (TokenText (TL.dropAround Char.isSpace r))))
358 xmlAttrs [cell ("to",Plain.writePlain ts)]
359 PairElem name attrs ->
361 element (xmlLocalName name) $
362 xmlAttrs (Seq.fromList $ (\(_wh,ElemAttr{..}) ->
363 cell (xmlLocalName elemAttr_name,elemAttr_value)) <$> attrs) <>
366 Seq.singleton (Tree0 $ Cell (sn{span_end=bn'}:|ssn) $ XmlText open) `unionXml`
367 xmlify inh ts `unionXml`
368 Seq.singleton (Tree0 $ Cell (sn{span_begin=en'}:|ssn) $ XmlText close)
370 (open, close) = pairBorders pair ts
371 bn' = (span_begin sn){pos_column=pos_column (span_begin sn) + int (TL.length open)}
372 en' = (span_end sn){pos_column=pos_column (span_end sn) - int (TL.length close)}
373 ----------------------
374 NodeText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
375 ----------------------
378 TokenEscape c -> Seq.singleton $ Tree0 $ cell $ XmlText $ TL.singleton c
379 TokenText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
380 TokenTag t -> Seq.singleton $ element "ref" $ xmlAttrs [cell ("to",t)]
381 TokenLink lnk -> Seq.singleton $ element "eref" $ xmlAttrs [cell ("to",lnk)]
382 ----------------------
390 element :: XmlName -> XMLs -> XML
391 element n = Tree (cell $ XmlElem n)
392 instance Xmlify (Seq (Cell (XmlName,TL.Text))) where
393 xmlify _inh = xmlAttrs
397 -- | Reserved elements' name
493 elemsJudgment :: Set TL.Text
506 -- | Convenient alias, forcing the types
507 xmlAttrs :: Seq (Cell (XmlName,TL.Text)) -> XMLs
508 xmlAttrs = (Tree0 . (uncurry XmlAttr <$>) <$>)
510 -- | Extract section titles
511 partitionSection :: Root -> (Roots, Roots)
512 partitionSection (Tree (unCell -> NodeHeader (HeaderSection lvlPar)) body) =
513 case Seq.viewl body of
515 title@(unTree -> Cell (Span{span_end=et}:|_) NodePara) :< rest ->
516 let (subtitles, content) = spanlSubtitles et rest in
517 (title <| (subtitles >>= subTrees), content)
519 spanlSubtitles ep ts =
521 sub@(unTree -> Cell (Span{..}:|_) (NodeHeader (HeaderSection lvlSub))) :< rs
523 , pos_line span_begin - pos_line ep <= 1 ->
524 let (subs, ts') = spanlSubtitles span_end rs in
528 partitionSection _ = mempty
530 -- | Extract attributes
531 partitionAttrs :: Roots -> (Seq (Cell (XmlName, TL.Text)), Roots)
532 partitionAttrs ts = (attrs,cs)
534 (as,cs) = (`Seq.partition` ts) $ \case
535 Tree (unCell -> NodeHeader (HeaderEqual n _wh)) _cs -> not $ TL.null n
539 Tree (Cell ssn (NodeHeader (HeaderEqual n _wh))) a ->
540 Cell ssn (xmlLocalName n, v)
542 v = Plain.text (Plain.setStart a def{Plain.state_escape = False}) a
545 getAttrId :: Root -> TL.Text
546 getAttrId = Plain.writePlain . Seq.singleton
549 Cell (XmlName, TL.Text) ->
550 Seq (Cell (XmlName, TL.Text)) ->
551 Seq (Cell (XmlName, TL.Text))
552 setAttr a@(unCell -> (k, _v)) as =
553 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
554 Just idx -> Seq.update idx a as
558 Seq (Cell (XmlName, TL.Text)) ->
559 Cell (XmlName, TL.Text) ->
560 Seq (Cell (XmlName, TL.Text))
561 defaultAttr as a@(unCell -> (k, _v)) =
562 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
568 -- | Unify two 'XMLs', merging border 'XmlText's if any.
569 unionXml :: XMLs -> XMLs -> XMLs
571 case (Seq.viewr x, Seq.viewl y) of
572 (xs :> x0, y0 :< ys) ->
574 ( Tree0 (Cell ssx@(Span {span_file=fx}:|_sx) (XmlText tx))
575 , Tree0 (Cell ssy@(Span {span_file=fy}:|_sy) (XmlText ty)) ) | fx == fy ->
577 Seq.singleton (Tree0 $ (XmlText <$>) $ Cell ssx tx <> Cell ssy ty) `unionXml`
583 unionsXml :: Foldable f => f XMLs -> XMLs
584 unionsXml = foldl' unionXml mempty