1 {-# LANGUAGE FlexibleContexts #-}
2 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE OverloadedLists #-}
4 {-# LANGUAGE OverloadedStrings #-}
5 {-# LANGUAGE ViewPatterns #-}
6 {-# OPTIONS_GHC -fno-warn-orphans #-}
7 module Language.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.Maybe (Maybe(..))
17 import Data.Monoid (Monoid(..))
18 import Data.Semigroup (Semigroup(..))
19 import Data.Sequence (Seq, ViewL(..), ViewR(..), (<|))
21 import Data.TreeSeq.Strict (Tree(..))
22 import Data.Tuple (uncurry)
23 import Prelude (undefined)
24 import qualified Data.Char as Char
25 import qualified Data.List as List
26 import qualified Data.Sequence as Seq
27 import qualified Data.Text.Lazy as TL
28 import qualified Language.TCT.Write.Plain as Plain
29 import qualified System.FilePath as FP
31 import Text.Blaze.XML ()
32 import Language.TCT hiding (Parser)
33 -- import Language.TCT.Debug
38 -- NOTE: 'XmlNode' are still annotated with 'Cell',
39 -- but nothing is done to preserve any ordering amongst them,
40 -- because 'Node's sometimes need to be reordered
41 -- (eg. about/title may have a title from the section before,
42 -- hence outside of about).
43 -- Still holding: @'cell_begin' < 'cell_end'@ and 'Cell' nesting.
44 xmlDocument :: Roots -> XMLs
46 -- (`S.evalState` def) $
48 Tree (unCell -> NodeHeader HeaderSection{}) body :< foot ->
49 case Seq.viewl body of
50 title@(unTree -> Cell bt et NodePara{}) :< content ->
52 { inh_titles = return title
58 case Seq.findIndexL isAbout content of
59 Nothing -> Tree (Cell bt et $ NodeHeader $ HeaderColon "about" "") mempty <| content
62 (unTree -> unCell -> NodeHeader (HeaderColon "about" _wh)) -> True
69 type Xmls = S.State State XMLs
70 type Xml = S.State State XML
71 instance Semigroup Xmls where
73 instance Monoid Xmls where
74 mempty = return mempty
82 instance Default State where
92 , inh_para :: [Cell () -> XMLs -> XML]
95 instance Default Inh where
98 , inh_para = List.repeat xmlPara
103 xmlPara :: Cell a -> XMLs -> XML
104 xmlPara c = Tree (XmlElem "para" <$ c)
106 xmlTitle :: Cell a -> XMLs -> XML
107 xmlTitle c = Tree (XmlElem "title" <$ c)
109 xmlName :: Cell a -> XMLs -> XML
110 xmlName c = Tree (XmlElem "name" <$ c)
114 xmlify :: Inh -> a -> XMLs
115 instance Xmlify Roots where
117 case Seq.viewl roots of
119 r@(Tree cr@(Cell _br _er nr) ts) :< rs ->
121 ----------------------
122 -- NOTE: HeaderColon becomes parent
123 -- of any continuous following-sibling HeaderBar or HeaderGreat
124 NodeHeader (HeaderColon n _wh)
125 | (span, rest) <- spanlHeaderColon rs
127 xmlify inh $ Tree cr (ts<>span) <| rest
129 spanlHeaderColon :: Roots -> (Roots, Roots)
132 Tree (unCell -> NodeHeader (HeaderBar m _)) _ -> m == n
133 Tree (unCell -> NodeHeader (HeaderGreat m _)) _ -> m == n
135 ----------------------
136 -- NOTE: gather HeaderBrackets
137 NodeHeader HeaderBrackets{}
138 | (span,rest) <- spanlBrackets roots
140 (<| xmlify inh rest) $
141 element "references" $
144 spanlBrackets :: Roots -> (Roots, Roots)
147 Tree (unCell -> NodeHeader HeaderBrackets{}) _ -> True
149 ----------------------
150 -- NOTE: merge adjacent NodeText-s, shouldn't be needed, but just in case.
152 | Tree (cy@(unCell -> NodeText y)) ys :< rs' <- Seq.viewl rs ->
153 xmlify inh $ Tree (NodeText <$> (x <$ cr) <> (y <$ cy)) (ts <> ys) <| rs'
154 ----------------------
155 -- NOTE: detect [some text](http://some.url) or [SomeRef]
157 | Tree (Cell bb eb (NodePair PairBracket)) bracket :< rs' <- Seq.viewl rs ->
158 (<| xmlify inh rs') $
160 (toList -> [unTree -> Cell bl el (NodeToken (TokenLink lnk))]) ->
162 xmlAttrs [Cell bl el ("to",lnk)] <>
166 xmlAttrs [Cell bb eb ("to",Plain.plainDocument bracket)] <>
168 ----------------------
169 -- NOTE: gather HeaderDash
170 _ | (span, rest) <- spanlItems (==HeaderDash) roots
172 (<| xmlify inh rest) $
174 span >>= xmlify inh{inh_para=List.repeat xmlPara}
175 ----------------------
176 -- NOTE: gather HeaderDot
177 | (span,rest) <- spanlItems (\case HeaderDot{} -> True; _ -> False) roots
179 (<| xmlify inh rest) $
181 span >>= xmlify inh{inh_para=List.repeat xmlPara}
183 spanlItems :: (Header -> Bool) -> Roots -> (Roots, Roots)
184 spanlItems liHeader =
185 Seq.spanl $ \(unTree -> unCell -> nod) ->
187 NodeHeader (HeaderColon "li" _wh) -> True
188 NodeHeader hdr -> liHeader hdr
189 NodePair (PairElem "li" _as) -> True
191 ----------------------
192 -- NOTE: context-free Root
197 element :: XmlName -> XMLs -> XML
198 element n = Tree (XmlElem n <$ cr)
200 t@(Tree (NodePair (PairElem))) :< ts ->
202 [] -> xmlify inh t <> go inh ts
203 _ | isTokenElem toks -> xmlify inh t <> go inh ts
205 (case Seq.viewl toks of
207 (unTree -> cell_begin -> bp) :< _ -> (tree0 bp (xmlify inh t) <|)) $
210 instance Xmlify Root where
211 xmlify inh (Tree cel@(Cell bp ep nod) ts) =
213 NodeGroup -> xmlify inh ts
214 ----------------------
221 xmlify inh{inh_para} ts
222 ----------------------
227 let (attrs,body) = partitionAttrs ts in
230 xmlAttrs (attrs `defaultAttr` Cell ep ep ("id",getAttrId body)) <>
234 { inh_para = xmlTitle : List.repeat xmlPara
239 let (attrs,body) = partitionAttrs ts in
241 -- NOTE: insert titles into <about>.
245 (inh_titles inh >>= xmlify inh') <>
248 -- NOTE: in <figure> mode, unreserved nodes become <figure>
249 _ | inh_figure inh && not (n`List.elem`elems) ->
252 xmlAttrs (setAttr (Cell ep ep ("type",n)) attrs) <>
254 [Tree0{}] -> xmlify inh'{inh_para = List.repeat xmlPara} body
255 _ -> xmlify inh'{inh_para = xmlTitle : List.repeat xmlPara} body
256 -- NOTE: reserved nodes
259 element (xmlLocalName n) $
266 "about" -> xmlTitle : List.repeat xmlPara
267 "reference" -> xmlTitle : List.repeat xmlPara
268 "serie" -> List.repeat xmlName
269 "author" -> List.repeat xmlName
270 "editor" -> List.repeat xmlName
271 "org" -> List.repeat xmlName
274 ----------------------
278 xmlAttrs (Seq.singleton $ Cell bp bp ("type", if TL.null n then "txt" else n)) <>
279 xmlify inh{inh_para=[]} ts
280 ----------------------
283 let (attrs,body) = partitionAttrs ts in
285 xmlAttrs (attrs `defaultAttr` Cell bp bp ("type", if TL.null n then "quote" else n)) <>
286 xmlify inh{inh_para=[]} body
288 HeaderEqual n _wh -> Seq.singleton $ element (xmlLocalName n) $ xmlify inh ts
290 HeaderDot _n -> Seq.singleton $ element "li" $ xmlify inh ts
292 HeaderDash -> Seq.singleton $ element "li" $ xmlify inh ts
294 HeaderDashDash -> Seq.singleton $ Tree0 $ cell $ XmlComment $
295 -- debug1_ ("TS", ts) $
296 -- debug1_ ("RS", (S.evalState (Plain.rackUpLeft ts) Nothing)) $
297 Plain.plainDocument ts
298 -- Plain.text def $ S.evalState (Plain.rackUpLeft ts) Nothing
305 (\_k' -> cell1 . unCell)) <$> ts
308 HeaderBrackets ident ->
309 let (attrs,body) = partitionAttrs ts in
311 element "reference" $
312 xmlAttrs (setAttr (Cell ep ep ("id",ident)) attrs) <>
313 xmlify inh'{inh_para = xmlTitle : xmlTitle : List.repeat xmlPara} body
315 inh' = inh{inh_figure = False}
320 xmlAttrs [cell ("href",TL.pack $ FP.replaceExtension p "dtc")] <>
322 ----------------------
325 PairBracket | to <- Plain.plainDocument ts
326 , TL.all (\c -> Char.isAlphaNum c || Char.isSymbol c) to ->
329 xmlAttrs [cell ("to",to)]
330 PairStar -> Seq.singleton $ element "b" $ xmlify inh ts
331 PairSlash -> Seq.singleton $ element "i" $ xmlify inh ts
332 PairBackquote -> Seq.singleton $ element "code" $ xmlify inh ts
339 (Seq.viewl -> Tree0 (Cell bl el (TokenPlain l)) :< ls) ->
341 m :> Tree0 (Cell br er (TokenPlain r)) ->
343 Tree0 (Cell bl el (TokenPlain (Text.dropWhile Char.isSpace l)))
344 <|(m|>Tree0 (Cell br er (TokenPlain (Text.dropWhileEnd Char.isSpace r))))
347 Tree0 (Cell bl el (TokenPlain (Text.dropAround Char.isSpace l))) <| ls
348 (Seq.viewr -> rs :> Tree0 (Cell br er (TokenPlain r))) ->
350 rs |> Tree0 (Cell br er (TokenPlain (Text.dropAround Char.isSpace r)))
356 xmlAttrs [cell ("to",Plain.plainDocument ts)]
357 PairElem name attrs ->
359 element (xmlLocalName name) $
360 xmlAttrs (Seq.fromList $ (\(_wh,ElemAttr{..}) ->
361 cell (xmlLocalName elemAttr_name,elemAttr_value)) <$> attrs) <>
364 let (open, close) = pairBorders pair ts in
365 Seq.singleton (Tree0 $ Cell bp bp $ XmlText open) `unionXml`
366 xmlify inh ts `unionXml`
367 Seq.singleton (Tree0 $ Cell ep ep $ XmlText close)
368 ----------------------
369 NodeText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
370 ----------------------
373 TokenEscape c -> Seq.singleton $ Tree0 $ cell $ XmlText $ TL.singleton c
374 TokenText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
375 TokenTag t -> Seq.singleton $ element "ref" $ xmlAttrs [cell ("to",t)]
376 TokenLink lnk -> Seq.singleton $ element "eref" $ xmlAttrs [cell ("to",lnk)]
377 ----------------------
385 element :: XmlName -> XMLs -> XML
386 element n = Tree (cell $ XmlElem n)
387 instance Xmlify (Seq (Cell (XmlName,TL.Text))) where
388 xmlify _inh = xmlAttrs
392 -- | Reserved elements' name
488 -- | Convenient alias, forcing the types
489 xmlAttrs :: Seq (Cell (XmlName,TL.Text)) -> XMLs
490 xmlAttrs = (Tree0 . (uncurry XmlAttr <$>) <$>)
492 -- | Extract attributes
493 partitionAttrs :: Roots -> (Seq (Cell (XmlName, TL.Text)), Roots)
494 partitionAttrs ts = (attrs,cs)
496 (as,cs) = (`Seq.partition` ts) $ \case
497 Tree (unCell -> NodeHeader (HeaderEqual (TL.null -> False) _wh)) _cs -> True
501 Tree (Cell bp ep (NodeHeader (HeaderEqual n _wh))) a ->
502 Cell bp ep (xmlLocalName n, v)
504 v = Plain.text (Plain.setStart a def{Plain.state_escape = False}) a
507 getAttrId :: Roots -> TL.Text
511 t :< _ -> Plain.plainDocument $ Seq.singleton t
514 Cell (XmlName, TL.Text) ->
515 Seq (Cell (XmlName, TL.Text)) ->
516 Seq (Cell (XmlName, TL.Text))
517 setAttr a@(unCell -> (k, _v)) as =
518 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
519 Just idx -> Seq.update idx a as
523 Seq (Cell (XmlName, TL.Text)) ->
524 Cell (XmlName, TL.Text) ->
525 Seq (Cell (XmlName, TL.Text))
526 defaultAttr as a@(unCell -> (k, _v)) =
527 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
533 -- | Unify two 'XMLs', merging border 'XmlText's if any.
534 unionXml :: XMLs -> XMLs -> XMLs
536 case (Seq.viewr x, Seq.viewl y) of
537 (xs :> x0, y0 :< ys) ->
539 ( Tree0 (Cell bx ex (XmlText tx))
540 , Tree0 (Cell by ey (XmlText ty)) ) ->
542 Seq.singleton (Tree0 $ (XmlText <$>) $ Cell bx ex tx <> Cell by ey ty) `unionXml`
548 unionsXml :: Foldable f => f XMLs -> XMLs
549 unionsXml = foldl' unionXml mempty