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 (some text)[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)] <>
156 if null ts -- NOTE: preserve empty parens
157 then Seq.singleton $ tree0 (XmlText "" <$ cr)
159 ----------------------
160 -- NOTE: gather HeaderDash
161 _ | (span, rest) <- spanlItems (==HeaderDash) roots
163 (<| xmlify inh rest) $
165 span >>= xmlify inh{inh_para=List.repeat elementPara}
166 ----------------------
167 -- NOTE: gather HeaderDot
168 | (span,rest) <- spanlItems (\case HeaderDot{} -> True; _ -> False) roots
170 (<| xmlify inh rest) $
172 span >>= xmlify inh{inh_para=List.repeat elementPara}
174 spanlItems :: (Header -> Bool) -> Roots -> (Roots, Roots)
175 spanlItems liHeader =
176 Seq.spanl $ \(unTree -> (unCell -> nod)) ->
178 NodeHeader (HeaderColon "li" _wh) -> True
179 NodeHeader hdr -> liHeader hdr
180 NodePair (PairElem "li" _as) -> True
182 ----------------------
183 NodePara | para:inh_para <- inh_para inh ->
185 xmlify inh{inh_para} rs
186 ----------------------
187 -- NOTE: context-free Root
189 xmlify inh r `unionXml`
192 element :: XmlName -> XMLs -> XML
193 element n = Tree (XmlElem n <$ cr)
194 instance Xmlify Root where
195 xmlify inh tn@(Tree (Cell ss@(sn:|ssn) nod) ts) =
197 ----------------------
201 para:_ -> Seq.singleton $ para inh tn
202 ----------------------
208 element "section" $ head <> xmlify inh' body
210 (titles, content) = partitionSection tn
211 (attrs, body) = partitionAttrs content
213 case Seq.viewl titles of
215 title@(unTree -> ct) :< subtitles ->
216 xmlAttrs (attrs `defaultAttr` (ct $> ("id",getAttrId title))) <>
217 xmlify inh{inh_para=List.repeat elementTitle} title <>
221 subtitles >>= \subtitle@(unTree -> cs) ->
223 Tree (cs $> XmlElem "alias") $
224 xmlify inh{inh_para=List.repeat elementTitle} subtitle
226 { inh_para = List.repeat elementPara
231 let (attrs,body) = partitionAttrs ts in
233 -- NOTE: insert titles into <about>
237 xmlify inh' (inh_titles inh) <>
239 xmlify inh'{inh_figure=False} body
240 -- NOTE: handle judgment
241 _ | n`List.elem`elemsJudgment -> -- FIXME: not a special case so far.
243 element (xmlLocalName n) $
250 "grades" -> List.repeat attributeId
251 "judges" -> List.repeat attributeId
252 _ -> List.repeat elementTitle
254 -- NOTE: in <figure> mode, unreserved elements become <figure>
255 _ | inh_figure inh && n`List.notElem`elems || TL.null n ->
258 -- xmlAttrs (setAttr (Cell en en ("type",n)) attrs) <>
259 xmlAttrs (attrs `defaultAttr` Cell (sn{span_end=span_begin sn}:|ssn) ("type", n)) <>
261 [Tree0{}] -> xmlify inh'{inh_para = List.repeat elementPara} body
262 _ -> xmlify inh'{inh_para = elementTitle : List.repeat elementPara} body
263 -- NOTE: reserved elements
266 element (xmlLocalName n) $
273 "about" -> List.repeat elementTitle
274 "reference" -> elementTitle : List.repeat elementPara
275 "serie" -> List.repeat attributeName
276 "author" -> List.repeat attributeName
277 "editor" -> List.repeat attributeName
278 "org" -> List.repeat attributeName
279 "note" -> List.repeat elementPara
284 if inh_figure inh && n`List.notElem`elems || TL.null n
288 xmlAttrs (Seq.singleton $ Cell (sn{span_end=span_end sn}:|ssn) ("type", n)) <>
289 xmlify inh{inh_para=[]} ts
292 Tree (cell $ NodeHeader $ HeaderColon n wh) ts
296 let (attrs,body) = partitionAttrs ts in
298 xmlAttrs (attrs `defaultAttr` Cell (sn{span_end=span_end sn}:|ssn) ("type", n)) <>
299 xmlify inh{inh_para=List.repeat elementPara} body
303 Tree0 $ cell $ XmlAttr (xmlLocalName n) $
304 Plain.text (Plain.setStart ts def{Plain.state_escape = False}) ts
309 let span_end = (span_begin sn){pos_column=pos_column (span_begin sn) + int (TL.length n)} in
310 xmlAttrs (Seq.singleton $ Cell (sn{span_end}:|ssn) ("name", n)) <>
313 HeaderDash -> Seq.singleton $ element "li" $ xmlify inh ts
316 Seq.singleton $ Tree0 $ cell $
317 XmlComment $ Plain.writePlain ts
319 HeaderBrackets ident ->
320 let (attrs,body) = partitionAttrs ts in
322 element "reference" $
323 xmlAttrs (setAttr (Cell (sn{span_end=span_end sn}:|ssn) ("id",ident)) attrs) <>
324 xmlify inh'{inh_para = elementTitle : elementTitle : List.repeat elementPara} body
326 inh' = inh{inh_figure = False}
328 HeaderDotSlash _file -> xmlify inh ts
329 ----------------------
332 PairBracket | to <- Plain.writePlain ts
333 , TL.all (\c -> Char.isAlphaNum c || Char.isSymbol c) to ->
336 xmlAttrs [cell ("to",to)]
337 PairStar -> Seq.singleton $ element "b" $ xmlify inh ts
338 PairSlash -> Seq.singleton $ element "i" $ xmlify inh ts
339 PairBackquote -> Seq.singleton $ element "code" $ xmlify inh ts
344 (Seq.viewl -> Tree0 (Cell sl (NodeToken (TokenText l))) :< ls) ->
346 m :> Tree0 (Cell sr (NodeToken (TokenText r))) ->
348 Tree0 (Cell sl (NodeToken (TokenText (TL.dropWhile Char.isSpace l))))
349 Seq.<|(m Seq.|>Tree0 (Cell sr (NodeToken (TokenText (TL.dropWhileEnd Char.isSpace r)))))
352 Tree0 (Cell sl (NodeToken (TokenText (TL.dropAround Char.isSpace l)))) Seq.<| ls
353 (Seq.viewr -> rs :> Tree0 (Cell sr (NodeToken (TokenText r)))) ->
355 rs Seq.|> Tree0 (Cell sr (NodeToken (TokenText (TL.dropAround Char.isSpace r))))
360 xmlify inh{inh_para=[]} ts
361 -- xmlAttrs [cell ("to",Plain.writePlain ts)]
362 PairElem name attrs ->
364 element (xmlLocalName name) $
365 xmlAttrs (Seq.fromList $ (\(_wh,ElemAttr{..}) ->
366 cell (xmlLocalName elemAttr_name,elemAttr_value)) <$> attrs) <>
369 Seq.singleton (Tree0 $ Cell (sn{span_end=bn'}:|ssn) $ XmlText open) `unionXml`
370 xmlify inh ts `unionXml`
371 Seq.singleton (Tree0 $ Cell (sn{span_begin=en'}:|ssn) $ XmlText close)
373 (open, close) = pairBorders pair ts
374 bn' = (span_begin sn){pos_column=pos_column (span_begin sn) + int (TL.length open)}
375 en' = (span_end sn){pos_column=pos_column (span_end sn) - int (TL.length close)}
376 ----------------------
377 NodeText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
378 ----------------------
381 TokenEscape c -> Seq.singleton $ Tree0 $ cell $ XmlText $ TL.singleton c
382 TokenText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
383 TokenTag t -> Seq.singleton $ element "tag" $ Seq.singleton $ Tree0 $ cell $ XmlText t
384 TokenLink lnk -> Seq.singleton $ element "eref" $ xmlAttrs [cell ("to",lnk)]
385 ----------------------
393 element :: XmlName -> XMLs -> XML
394 element n = Tree (cell $ XmlElem n)
395 instance Xmlify (Seq (Cell (XmlName,TL.Text))) where
396 xmlify _inh = xmlAttrs
400 -- | Reserved elements' name
496 elemsJudgment :: Set TL.Text
509 -- | Convenient alias, forcing the types
510 xmlAttrs :: Seq (Cell (XmlName,TL.Text)) -> XMLs
511 xmlAttrs = (Tree0 . (uncurry XmlAttr <$>) <$>)
513 -- | Extract section titles
514 partitionSection :: Root -> (Roots, Roots)
515 partitionSection (Tree (unCell -> NodeHeader (HeaderSection lvlPar)) body) =
516 case Seq.viewl body of
518 title@(unTree -> Cell (Span{span_end=et}:|_) NodePara) :< rest ->
519 let (subtitles, content) = spanlSubtitles et rest in
520 (title <| (subtitles >>= subTrees), content)
522 spanlSubtitles ep ts =
524 sub@(unTree -> Cell (Span{..}:|_) (NodeHeader (HeaderSection lvlSub))) :< rs
526 , pos_line span_begin - pos_line ep <= 1 ->
527 let (subs, ts') = spanlSubtitles span_end rs in
531 partitionSection _ = mempty
533 -- | Extract attributes
534 partitionAttrs :: Roots -> (Seq (Cell (XmlName, TL.Text)), Roots)
535 partitionAttrs ts = (attrs,cs)
537 (as,cs) = (`Seq.partition` ts) $ \case
538 Tree (unCell -> NodeHeader (HeaderEqual n _wh)) _cs -> not $ TL.null n
542 Tree (Cell ssn (NodeHeader (HeaderEqual n _wh))) a ->
543 Cell ssn (xmlLocalName n, v)
545 v = Plain.text (Plain.setStart a def{Plain.state_escape = False}) a
548 getAttrId :: Root -> TL.Text
549 getAttrId = Plain.writePlain . Seq.singleton
552 Cell (XmlName, TL.Text) ->
553 Seq (Cell (XmlName, TL.Text)) ->
554 Seq (Cell (XmlName, TL.Text))
555 setAttr a@(unCell -> (k, _v)) as =
556 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
557 Just idx -> Seq.update idx a as
561 Seq (Cell (XmlName, TL.Text)) ->
562 Cell (XmlName, TL.Text) ->
563 Seq (Cell (XmlName, TL.Text))
564 defaultAttr as a@(unCell -> (k, _v)) =
565 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
571 -- | Unify two 'XMLs', merging border 'XmlText's if any.
572 unionXml :: XMLs -> XMLs -> XMLs
574 case (Seq.viewr x, Seq.viewl y) of
575 (xs :> x0, y0 :< ys) ->
577 ( Tree0 (Cell ssx@(Span {span_file=fx}:|_sx) (XmlText tx))
578 , Tree0 (Cell ssy@(Span {span_file=fy}:|_sy) (XmlText ty)) ) | fx == fy ->
580 Seq.singleton (Tree0 $ (XmlText <$>) $ Cell ssx tx <> Cell ssy ty) `unionXml`
586 unionsXml :: Foldable f => f XMLs -> XMLs
587 unionsXml = foldl' unionXml mempty