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))))
364 xmlify inh{inh_para=[]} ts
365 -- xmlAttrs [cell ("to",Plain.writePlain ts)]
366 PairElem name attrs ->
368 element (XML.localName name) $
369 xmlAttrs (Seq.fromList $ (\(_wh,ElemAttr{..}) ->
370 cell (XML.localName elemAttr_name,elemAttr_value)) <$> attrs) <>
373 Seq.singleton (Tree0 $ Cell (sn{span_end=bn'}:|ssn) $ XML.NodeText open) `unionXml`
374 xmlify inh ts `unionXml`
375 Seq.singleton (Tree0 $ Cell (sn{span_begin=en'}:|ssn) $ XML.NodeText close)
377 (open, close) = pairBorders pair ts
378 bn' = (span_begin sn){pos_column=pos_column (span_begin sn) + int (TL.length open)}
379 en' = (span_end sn){pos_column=pos_column (span_end sn) - int (TL.length close)}
380 ----------------------
381 NodeText t -> Seq.singleton $ Tree0 $ cell $ XML.NodeText t
382 ----------------------
385 TokenEscape c -> Seq.singleton $ Tree0 $ cell $ XML.NodeText $ TL.singleton c
386 TokenText t -> Seq.singleton $ Tree0 $ cell $ XML.NodeText t
387 TokenTag t -> Seq.singleton $ element "tag" $ Seq.singleton $ Tree0 $ cell $ XML.NodeText t
388 TokenLink lnk -> Seq.singleton $ element "eref" $ xmlAttrs [cell ("to",lnk)]
389 ----------------------
397 element :: XML.Name -> XMLs -> XML
398 element n = Tree (cell $ XML.NodeElem n)
399 instance Xmlify (Seq (Cell (XML.Name,TL.Text))) where
400 xmlify _inh = xmlAttrs
404 -- | Reserved elements' name
500 elemsJudgment :: Set TL.Text
513 -- | Convenient alias, forcing the types
514 xmlAttrs :: Seq (Cell (XML.Name,TL.Text)) -> XMLs
515 xmlAttrs = (Tree0 . (uncurry XML.NodeAttr <$>) <$>)
517 -- | Extract section titles
518 partitionSection :: Root -> (Roots, Roots)
519 partitionSection (Tree (unCell -> NodeHeader (HeaderSection lvlPar)) body) =
520 case Seq.viewl body of
522 title@(unTree -> Cell (Span{span_end=et}:|_) NodePara) :< rest ->
523 let (subtitles, content) = spanlSubtitles et rest in
524 (title <| (subtitles >>= subTrees), content)
526 spanlSubtitles ep ts =
528 sub@(unTree -> Cell (Span{..}:|_) (NodeHeader (HeaderSection lvlSub))) :< rs
530 , pos_line span_begin - pos_line ep <= 1 ->
531 let (subs, ts') = spanlSubtitles span_end rs in
535 partitionSection _ = mempty
537 -- | Extract attributes
538 partitionAttrs :: Roots -> (Seq (Cell (XML.Name, TL.Text)), Roots)
539 partitionAttrs ts = (attrs,cs)
541 (as,cs) = (`Seq.partition` ts) $ \case
542 Tree (unCell -> NodeHeader (HeaderEqual n _wh)) _cs -> not $ TL.null n
546 Tree (Cell loc (NodeHeader (HeaderEqual n _wh))) a ->
547 Cell loc (XML.localName n, v)
549 v = Plain.text (Plain.setStart a def{Plain.state_escape = False}) a
552 getAttrId :: Root -> TL.Text
553 getAttrId = Plain.writePlain . Seq.singleton
556 Cell (XML.Name, TL.Text) ->
557 Seq (Cell (XML.Name, TL.Text)) ->
558 Seq (Cell (XML.Name, TL.Text))
559 setAttr a@(unCell -> (k, _v)) as =
560 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
561 Just idx -> Seq.update idx a as
565 Seq (Cell (XML.Name, TL.Text)) ->
566 Cell (XML.Name, TL.Text) ->
567 Seq (Cell (XML.Name, TL.Text))
568 defaultAttr as a@(unCell -> (k, _v)) =
569 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
575 -- | Unify two 'XMLs', merging border 'XML.NodeText's if any.
576 unionXml :: XMLs -> XMLs -> XMLs
578 case (Seq.viewr x, Seq.viewl y) of
579 (xs :> x0, y0 :< ys) ->
581 ( Tree0 (Cell ssx@(Span {span_file=fx}:|_sx) (XML.NodeText tx))
582 , Tree0 (Cell ssy@(Span {span_file=fy}:|_sy) (XML.NodeText ty)) ) | fx == fy ->
584 Seq.singleton (Tree0 $ (XML.NodeText <$>) $ Cell ssx tx <> Cell ssy ty) `unionXml`
590 unionsXml :: Foldable f => f XMLs -> XMLs
591 unionsXml = foldl' unionXml mempty