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.Ord (Ord(..))
19 import Data.Semigroup (Semigroup(..))
20 import Data.Sequence (Seq, ViewL(..), ViewR(..), (<|))
22 import Data.TreeSeq.Strict (Tree(..))
23 import Data.Tuple (uncurry)
24 import Prelude (Num(..), undefined)
25 import qualified Data.Char as Char
26 import qualified Data.List as List
27 import qualified Data.Sequence as Seq
28 import qualified Data.Text.Lazy as TL
29 import qualified Language.TCT.Write.Plain as Plain
30 import qualified System.FilePath as FP
32 -- import Language.TCT.Debug
33 import Language.TCT hiding (Parser)
35 import Text.Blaze.XML ()
39 -- NOTE: 'XmlNode' are still annotated with 'Cell',
40 -- but nothing is done to preserve any ordering amongst them,
41 -- because 'Node's sometimes need to be reordered
42 -- (eg. about/title may have a title from the section before,
43 -- hence outside of about).
44 -- Still holding: @'cell_begin' < 'cell_end'@ and 'Cell' nesting.
45 document :: Roots -> XMLs
47 -- (`S.evalState` def) $
49 sec@(Tree (unCell -> NodeHeader HeaderSection{}) _body) :< foot ->
50 let (titles, content) = partitionSection sec in
51 case Seq.viewl titles of
52 (unTree -> Cell bt et _) :< _ ->
60 case Seq.findIndexL isAbout content of
61 Nothing -> Tree (Cell bt et $ NodeHeader $ HeaderColon "about" "") mempty <| content
64 (unTree -> (unCell -> NodeHeader (HeaderColon "about" _wh))) -> True
69 partitionSection :: Root -> (Roots, Roots)
70 partitionSection (Tree (unCell -> NodeHeader (HeaderSection lvlPar)) body) =
71 case Seq.viewl body of
73 title@(unTree -> Cell _bt et NodePara) :< rest ->
74 let (subtitles, content) = spanlSubtitles et rest in
75 (title <| (subtitles >>= subTrees), content)
77 spanlSubtitles ep ts =
79 sub@(unTree -> Cell bs es (NodeHeader (HeaderSection lvlSub))) :< rs
81 , pos_line bs - pos_line ep <= 1 ->
82 let (subs, ts') = spanlSubtitles es rs in
85 partitionSection _ = mempty
91 , inh_para :: [Cell () -> XMLs -> XML]
94 instance Default Inh where
97 , inh_para = List.repeat xmlPara
102 xmlPara :: Cell a -> XMLs -> XML
103 xmlPara c = Tree (XmlElem "para" <$ c)
105 xmlTitle :: Cell a -> XMLs -> XML
106 xmlTitle c = Tree (XmlElem "title" <$ c)
108 xmlName :: Cell a -> XMLs -> XML
109 xmlName c = Tree (XmlElem "name" <$ c)
113 xmlify :: Inh -> a -> XMLs
114 instance Xmlify Roots where
116 case Seq.viewl roots of
118 r@(Tree cr@(Cell _br _er nr) ts) :< rs ->
120 ----------------------
121 -- NOTE: HeaderColon becomes parent
122 -- of any continuous following-sibling HeaderBar or HeaderGreat
123 NodeHeader (HeaderColon n _wh)
124 | (span, rest) <- spanlHeaderColon rs
126 xmlify inh $ Tree cr (ts<>span) <| rest
128 spanlHeaderColon :: Roots -> (Roots, Roots)
131 Tree (unCell -> NodeHeader (HeaderBar m _)) _ -> m == n
132 Tree (unCell -> NodeHeader (HeaderGreat m _)) _ -> m == n
134 ----------------------
135 -- NOTE: gather HeaderBrackets
136 NodeHeader HeaderBrackets{}
137 | (span,rest) <- spanlBrackets roots
139 (<| xmlify inh rest) $
140 element "references" $
143 spanlBrackets :: Roots -> (Roots, Roots)
146 Tree (unCell -> NodeHeader HeaderBrackets{}) _ -> True
148 ----------------------
149 -- NOTE: merge adjacent NodeText-s, shouldn't be needed, but just in case.
151 | Tree (cy@(unCell -> NodeText y)) ys :< rs' <- Seq.viewl rs ->
152 xmlify inh $ Tree (NodeText <$> (x <$ cr) <> (y <$ cy)) (ts <> ys) <| rs'
153 ----------------------
154 -- NOTE: detect [some text](http://some.url) or [SomeRef]
156 | Tree (Cell bb eb (NodePair PairBracket)) bracket :< rs' <- Seq.viewl rs ->
157 (<| xmlify inh rs') $
159 (toList -> [unTree -> Cell bl el (NodeToken (TokenLink lnk))]) ->
161 xmlAttrs [Cell bl el ("to",lnk)] <>
165 xmlAttrs [Cell bb eb ("to",Plain.document bracket)] <>
167 ----------------------
168 -- NOTE: gather HeaderDash
169 _ | (span, rest) <- spanlItems (==HeaderDash) roots
171 (<| xmlify inh rest) $
173 span >>= xmlify inh{inh_para=List.repeat xmlPara}
174 ----------------------
175 -- NOTE: gather HeaderDot
176 | (span,rest) <- spanlItems (\case HeaderDot{} -> True; _ -> False) roots
178 (<| xmlify inh rest) $
180 span >>= xmlify inh{inh_para=List.repeat xmlPara}
182 spanlItems :: (Header -> Bool) -> Roots -> (Roots, Roots)
183 spanlItems liHeader =
184 Seq.spanl $ \(unTree -> (unCell -> nod)) ->
186 NodeHeader (HeaderColon "li" _wh) -> True
187 NodeHeader hdr -> liHeader hdr
188 NodePair (PairElem "li" _as) -> True
190 ----------------------
191 -- NOTE: context-free Root
196 element :: XmlName -> XMLs -> XML
197 element n = Tree (XmlElem n <$ cr)
199 t@(Tree (NodePair (PairElem))) :< ts ->
201 [] -> xmlify inh t <> go inh ts
202 _ | isTokenElem toks -> xmlify inh t <> go inh ts
204 (case Seq.viewl toks of
206 (unTree -> cell_begin -> bp) :< _ -> (tree0 bp (xmlify inh t) <|)) $
209 instance Xmlify Root where
210 xmlify inh tr@(Tree cel@(Cell bp ep nod) ts) =
212 NodeGroup -> xmlify inh ts
213 ----------------------
220 xmlify inh{inh_para} ts
221 ----------------------
227 element "section" $ head <> xmlify inh' body
229 (titles, content) = partitionSection tr
230 (attrs, body) = partitionAttrs content
232 case Seq.viewl titles of
234 title@(unTree -> ct) :< subtitles ->
235 xmlAttrs (attrs `defaultAttr` (ct $> ("id",getAttrId title))) <>
239 subtitles >>= \subtitle@(unTree -> cs) ->
241 Tree (cs $> XmlElem "alias") $
242 xmlAttrs (return $ cs $> ("id",getAttrId subtitle))
244 { inh_para = xmlTitle : List.repeat xmlPara
249 let (attrs,body) = partitionAttrs ts in
251 -- NOTE: insert titles into <about>.
255 (inh_titles inh >>= xmlify inh') <>
258 -- NOTE: in <figure> mode, unreserved nodes become <figure>
259 _ | inh_figure inh && n`List.notElem`elems ->
262 -- xmlAttrs (setAttr (Cell ep ep ("type",n)) attrs) <>
263 xmlAttrs (attrs `defaultAttr` Cell bp bp ("type", n)) <>
265 [Tree0{}] -> xmlify inh'{inh_para = List.repeat xmlPara} body
266 _ -> xmlify inh'{inh_para = xmlTitle : List.repeat xmlPara} body
267 -- NOTE: reserved nodes
270 element (xmlLocalName n) $
277 "about" -> xmlTitle : List.repeat xmlPara
278 "reference" -> xmlTitle : List.repeat xmlPara
279 "serie" -> List.repeat xmlName
280 "author" -> List.repeat xmlName
281 "editor" -> List.repeat xmlName
282 "org" -> List.repeat xmlName
285 ----------------------
289 xmlAttrs (Seq.singleton $ Cell bp bp ("type", if TL.null n then "txt" else n)) <>
290 xmlify inh{inh_para=[]} ts
291 ----------------------
294 let (attrs,body) = partitionAttrs ts in
296 xmlAttrs (attrs `defaultAttr` Cell bp bp ("type", if TL.null n then "quote" else n)) <>
297 xmlify inh{inh_para=[]} body
299 HeaderEqual n _wh -> Seq.singleton $ element (xmlLocalName n) $ xmlify inh ts
301 HeaderDot _n -> Seq.singleton $ element "li" $ xmlify inh ts
303 HeaderDash -> Seq.singleton $ element "li" $ xmlify inh ts
305 HeaderDashDash -> Seq.singleton $ Tree0 $ cell $ XmlComment $
306 -- debug1_ ("TS", ts) $
307 -- debug1_ ("RS", (S.evalState (Plain.rackUpLeft ts) Nothing)) $
309 -- Plain.text def $ S.evalState (Plain.rackUpLeft ts) Nothing
316 (\_k' -> cell1 . unCell)) <$> ts
319 HeaderBrackets ident ->
320 let (attrs,body) = partitionAttrs ts in
322 element "reference" $
323 xmlAttrs (setAttr (Cell ep ep ("id",ident)) attrs) <>
324 xmlify inh'{inh_para = xmlTitle : xmlTitle : List.repeat xmlPara} body
326 inh' = inh{inh_figure = False}
331 xmlAttrs [cell ("href",TL.pack $ FP.replaceExtension p "dtc")] <>
333 ----------------------
336 PairBracket | to <- Plain.document ts
337 , TL.all (\c -> Char.isAlphaNum c || Char.isSymbol c) to ->
340 xmlAttrs [cell ("to",to)]
341 PairStar -> Seq.singleton $ element "b" $ xmlify inh ts
342 PairSlash -> Seq.singleton $ element "i" $ xmlify inh ts
343 PairBackquote -> Seq.singleton $ element "code" $ xmlify inh ts
350 (Seq.viewl -> Tree0 (Cell bl el (TokenPlain l)) :< ls) ->
352 m :> Tree0 (Cell br er (TokenPlain r)) ->
354 Tree0 (Cell bl el (TokenPlain (Text.dropWhile Char.isSpace l)))
355 <|(m|>Tree0 (Cell br er (TokenPlain (Text.dropWhileEnd Char.isSpace r))))
358 Tree0 (Cell bl el (TokenPlain (Text.dropAround Char.isSpace l))) <| ls
359 (Seq.viewr -> rs :> Tree0 (Cell br er (TokenPlain r))) ->
361 rs |> Tree0 (Cell br er (TokenPlain (Text.dropAround Char.isSpace r)))
367 xmlAttrs [cell ("to",Plain.document ts)]
368 PairElem name attrs ->
370 element (xmlLocalName name) $
371 xmlAttrs (Seq.fromList $ (\(_wh,ElemAttr{..}) ->
372 cell (xmlLocalName elemAttr_name,elemAttr_value)) <$> attrs) <>
375 let (open, close) = pairBorders pair ts in
376 Seq.singleton (Tree0 $ Cell bp bp $ XmlText open) `unionXml`
377 xmlify inh ts `unionXml`
378 Seq.singleton (Tree0 $ Cell ep ep $ XmlText close)
379 ----------------------
380 NodeText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
381 ----------------------
384 TokenEscape c -> Seq.singleton $ Tree0 $ cell $ XmlText $ TL.singleton c
385 TokenText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
386 TokenTag t -> Seq.singleton $ element "ref" $ xmlAttrs [cell ("to",t)]
387 TokenLink lnk -> Seq.singleton $ element "eref" $ xmlAttrs [cell ("to",lnk)]
388 ----------------------
396 element :: XmlName -> XMLs -> XML
397 element n = Tree (cell $ XmlElem n)
398 instance Xmlify (Seq (Cell (XmlName,TL.Text))) where
399 xmlify _inh = xmlAttrs
403 -- | Reserved elements' name
499 -- | Convenient alias, forcing the types
500 xmlAttrs :: Seq (Cell (XmlName,TL.Text)) -> XMLs
501 xmlAttrs = (Tree0 . (uncurry XmlAttr <$>) <$>)
503 -- | Extract attributes
504 partitionAttrs :: Roots -> (Seq (Cell (XmlName, TL.Text)), Roots)
505 partitionAttrs ts = (attrs,cs)
507 (as,cs) = (`Seq.partition` ts) $ \case
508 Tree (unCell -> NodeHeader (HeaderEqual (TL.null -> False) _wh)) _cs -> True
512 Tree (Cell bp ep (NodeHeader (HeaderEqual n _wh))) a ->
513 Cell bp ep (xmlLocalName n, v)
515 v = Plain.text (Plain.setStart a def{Plain.state_escape = False}) a
518 getAttrId :: Root -> TL.Text
519 getAttrId = Plain.document . Seq.singleton
522 Cell (XmlName, TL.Text) ->
523 Seq (Cell (XmlName, TL.Text)) ->
524 Seq (Cell (XmlName, TL.Text))
525 setAttr a@(unCell -> (k, _v)) as =
526 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
527 Just idx -> Seq.update idx a as
531 Seq (Cell (XmlName, TL.Text)) ->
532 Cell (XmlName, TL.Text) ->
533 Seq (Cell (XmlName, TL.Text))
534 defaultAttr as a@(unCell -> (k, _v)) =
535 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
541 -- | Unify two 'XMLs', merging border 'XmlText's if any.
542 unionXml :: XMLs -> XMLs -> XMLs
544 case (Seq.viewr x, Seq.viewl y) of
545 (xs :> x0, y0 :< ys) ->
547 ( Tree0 (Cell bx ex (XmlText tx))
548 , Tree0 (Cell by ey (XmlText ty)) ) ->
550 Seq.singleton (Tree0 $ (XmlText <$>) $ Cell bx ex tx <> Cell by ey ty) `unionXml`
556 unionsXml :: Foldable f => f XMLs -> XMLs
557 unionsXml = foldl' unionXml mempty