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 xmlify inh{inh_para=List.repeat elementTitle} subtitle
227 { inh_para = List.repeat elementPara
232 let (attrs,body) = partitionAttrs ts in
234 -- NOTE: insert titles into <about>
238 xmlify inh' (inh_titles inh) <>
240 xmlify inh'{inh_figure=False} body
241 -- NOTE: handle judgment
242 _ | n`List.elem`elemsJudgment -> -- FIXME: not a special case so far.
244 element (XML.localName n) $
251 "grades" -> List.repeat attributeId
252 "judges" -> List.repeat attributeId
253 _ -> List.repeat elementTitle
255 -- NOTE: in <figure> mode, unreserved elements become <figure>
256 _ | inh_figure inh && n`List.notElem`elems || TL.null n ->
259 -- xmlAttrs (setAttr (Cell en en ("type",n)) attrs) <>
260 xmlAttrs (attrs `defaultAttr` Cell (sn{span_end=span_begin sn}:|ssn) ("type", n)) <>
262 [Tree0{}] -> xmlify inh'{inh_para = List.repeat elementPara} body
263 _ -> xmlify inh'{inh_para = elementTitle : List.repeat elementPara} body
264 -- NOTE: reserved elements
267 element (XML.localName n) $
274 "about" -> List.repeat elementTitle
275 "reference" -> elementTitle : List.repeat elementPara
276 "serie" -> List.repeat attributeName
277 "author" -> List.repeat attributeName
278 "editor" -> List.repeat attributeName
279 "org" -> List.repeat attributeName
280 "note" -> List.repeat elementPara
285 if inh_figure inh && n`List.notElem`elems || TL.null n
289 xmlAttrs (Seq.singleton $ Cell (sn{span_end=span_end sn}:|ssn) ("type", n)) <>
290 xmlify inh{inh_para=[]} ts
293 Tree (cell $ NodeHeader $ HeaderColon n wh) ts
297 let (attrs,body) = partitionAttrs ts in
299 xmlAttrs (attrs `defaultAttr` Cell (sn{span_end=span_end sn}:|ssn) ("type", n)) <>
300 xmlify inh{inh_para=List.repeat elementPara} body
304 Tree0 $ cell $ XML.NodeAttr (XML.localName n) $
305 Plain.text (Plain.setStart ts def{Plain.state_escape = False}) ts
310 let span_end = (span_begin sn){pos_column=pos_column (span_begin sn) + int (TL.length n)} in
311 xmlAttrs (Seq.singleton $ Cell (sn{span_end}:|ssn) ("name", n)) <>
314 HeaderDash -> Seq.singleton $ element "li" $ xmlify inh ts
317 Seq.singleton $ Tree0 $ cell $
318 XML.NodeComment $ Plain.writePlain ts
320 HeaderBrackets ident ->
321 let (attrs,body) = partitionAttrs ts in
323 element "reference" $
324 xmlAttrs (setAttr (Cell (sn{span_end=span_end sn}:|ssn) ("id",ident)) attrs) <>
325 xmlify inh'{inh_para = elementTitle : elementTitle : List.repeat elementPara} body
327 inh' = inh{inh_figure = False}
329 HeaderDotSlash _file -> xmlify inh ts
330 ----------------------
333 PairBracket | to <- Plain.writePlain ts
334 , TL.all (\c -> Char.isAlphaNum c || Char.isSymbol c) to ->
337 xmlAttrs [cell ("to",to)]
338 PairStar -> Seq.singleton $ element "b" $ xmlify inh ts
339 PairSlash -> Seq.singleton $ element "i" $ xmlify inh ts
340 PairBackquote -> Seq.singleton $ element "code" $ xmlify inh ts
345 (Seq.viewl -> Tree0 (Cell sl (NodeToken (TokenText l))) :< ls) ->
347 m :> Tree0 (Cell sr (NodeToken (TokenText r))) ->
349 Tree0 (Cell sl (NodeToken (TokenText (TL.dropWhile Char.isSpace l))))
350 Seq.<|(m Seq.|>Tree0 (Cell sr (NodeToken (TokenText (TL.dropWhileEnd Char.isSpace r)))))
353 Tree0 (Cell sl (NodeToken (TokenText (TL.dropAround Char.isSpace l)))) Seq.<| ls
354 (Seq.viewr -> rs :> Tree0 (Cell sr (NodeToken (TokenText r)))) ->
356 rs Seq.|> Tree0 (Cell sr (NodeToken (TokenText (TL.dropAround Char.isSpace r))))
361 xmlify inh{inh_para=[]} ts
362 -- xmlAttrs [cell ("to",Plain.writePlain ts)]
363 PairElem name attrs ->
365 element (XML.localName name) $
366 xmlAttrs (Seq.fromList $ (\(_wh,ElemAttr{..}) ->
367 cell (XML.localName elemAttr_name,elemAttr_value)) <$> attrs) <>
370 Seq.singleton (Tree0 $ Cell (sn{span_end=bn'}:|ssn) $ XML.NodeText open) `unionXml`
371 xmlify inh ts `unionXml`
372 Seq.singleton (Tree0 $ Cell (sn{span_begin=en'}:|ssn) $ XML.NodeText close)
374 (open, close) = pairBorders pair ts
375 bn' = (span_begin sn){pos_column=pos_column (span_begin sn) + int (TL.length open)}
376 en' = (span_end sn){pos_column=pos_column (span_end sn) - int (TL.length close)}
377 ----------------------
378 NodeText t -> Seq.singleton $ Tree0 $ cell $ XML.NodeText t
379 ----------------------
382 TokenEscape c -> Seq.singleton $ Tree0 $ cell $ XML.NodeText $ TL.singleton c
383 TokenText t -> Seq.singleton $ Tree0 $ cell $ XML.NodeText t
384 TokenTag t -> Seq.singleton $ element "tag" $ Seq.singleton $ Tree0 $ cell $ XML.NodeText t
385 TokenLink lnk -> Seq.singleton $ element "eref" $ xmlAttrs [cell ("to",lnk)]
386 ----------------------
394 element :: XML.Name -> XMLs -> XML
395 element n = Tree (cell $ XML.NodeElem n)
396 instance Xmlify (Seq (Cell (XML.Name,TL.Text))) where
397 xmlify _inh = xmlAttrs
401 -- | Reserved elements' name
497 elemsJudgment :: Set TL.Text
510 -- | Convenient alias, forcing the types
511 xmlAttrs :: Seq (Cell (XML.Name,TL.Text)) -> XMLs
512 xmlAttrs = (Tree0 . (uncurry XML.NodeAttr <$>) <$>)
514 -- | Extract section titles
515 partitionSection :: Root -> (Roots, Roots)
516 partitionSection (Tree (unCell -> NodeHeader (HeaderSection lvlPar)) body) =
517 case Seq.viewl body of
519 title@(unTree -> Cell (Span{span_end=et}:|_) NodePara) :< rest ->
520 let (subtitles, content) = spanlSubtitles et rest in
521 (title <| (subtitles >>= subTrees), content)
523 spanlSubtitles ep ts =
525 sub@(unTree -> Cell (Span{..}:|_) (NodeHeader (HeaderSection lvlSub))) :< rs
527 , pos_line span_begin - pos_line ep <= 1 ->
528 let (subs, ts') = spanlSubtitles span_end rs in
532 partitionSection _ = mempty
534 -- | Extract attributes
535 partitionAttrs :: Roots -> (Seq (Cell (XML.Name, TL.Text)), Roots)
536 partitionAttrs ts = (attrs,cs)
538 (as,cs) = (`Seq.partition` ts) $ \case
539 Tree (unCell -> NodeHeader (HeaderEqual n _wh)) _cs -> not $ TL.null n
543 Tree (Cell loc (NodeHeader (HeaderEqual n _wh))) a ->
544 Cell loc (XML.localName n, v)
546 v = Plain.text (Plain.setStart a def{Plain.state_escape = False}) a
549 getAttrId :: Root -> TL.Text
550 getAttrId = Plain.writePlain . Seq.singleton
553 Cell (XML.Name, TL.Text) ->
554 Seq (Cell (XML.Name, TL.Text)) ->
555 Seq (Cell (XML.Name, TL.Text))
556 setAttr a@(unCell -> (k, _v)) as =
557 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
558 Just idx -> Seq.update idx a as
562 Seq (Cell (XML.Name, TL.Text)) ->
563 Cell (XML.Name, TL.Text) ->
564 Seq (Cell (XML.Name, TL.Text))
565 defaultAttr as a@(unCell -> (k, _v)) =
566 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
572 -- | Unify two 'XMLs', merging border 'XML.NodeText's if any.
573 unionXml :: XMLs -> XMLs -> XMLs
575 case (Seq.viewr x, Seq.viewl y) of
576 (xs :> x0, y0 :< ys) ->
578 ( Tree0 (Cell ssx@(Span {span_file=fx}:|_sx) (XML.NodeText tx))
579 , Tree0 (Cell ssy@(Span {span_file=fy}:|_sy) (XML.NodeText ty)) ) | fx == fy ->
581 Seq.singleton (Tree0 $ (XML.NodeText <$>) $ Cell ssx tx <> Cell ssy ty) `unionXml`
587 unionsXml :: Foldable f => f XMLs -> XMLs
588 unionsXml = foldl' unionXml mempty