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 Text.Blaze.XML ()
33 import Language.TCT hiding (Parser)
34 import Language.TCT.Debug
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 xmlDocument :: 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
88 type Xmls = S.State State XMLs
89 type Xml = S.State State XML
90 instance Semigroup Xmls where
92 instance Monoid Xmls where
93 mempty = return mempty
101 instance Default State where
111 , inh_para :: [Cell () -> XMLs -> XML]
112 , inh_titles :: Roots
114 instance Default Inh where
117 , inh_para = List.repeat xmlPara
118 , inh_titles = mempty
122 xmlPara :: Cell a -> XMLs -> XML
123 xmlPara c = Tree (XmlElem "para" <$ c)
125 xmlTitle :: Cell a -> XMLs -> XML
126 xmlTitle c = Tree (XmlElem "title" <$ c)
128 xmlName :: Cell a -> XMLs -> XML
129 xmlName c = Tree (XmlElem "name" <$ c)
133 xmlify :: Inh -> a -> XMLs
134 instance Xmlify Roots where
136 case Seq.viewl roots of
138 r@(Tree cr@(Cell _br _er nr) ts) :< rs ->
140 ----------------------
141 -- NOTE: HeaderColon becomes parent
142 -- of any continuous following-sibling HeaderBar or HeaderGreat
143 NodeHeader (HeaderColon n _wh)
144 | (span, rest) <- spanlHeaderColon rs
146 xmlify inh $ Tree cr (ts<>span) <| rest
148 spanlHeaderColon :: Roots -> (Roots, Roots)
151 Tree (unCell -> NodeHeader (HeaderBar m _)) _ -> m == n
152 Tree (unCell -> NodeHeader (HeaderGreat m _)) _ -> m == n
154 ----------------------
155 -- NOTE: gather HeaderBrackets
156 NodeHeader HeaderBrackets{}
157 | (span,rest) <- spanlBrackets roots
159 (<| xmlify inh rest) $
160 element "references" $
163 spanlBrackets :: Roots -> (Roots, Roots)
166 Tree (unCell -> NodeHeader HeaderBrackets{}) _ -> True
168 ----------------------
169 -- NOTE: merge adjacent NodeText-s, shouldn't be needed, but just in case.
171 | Tree (cy@(unCell -> NodeText y)) ys :< rs' <- Seq.viewl rs ->
172 xmlify inh $ Tree (NodeText <$> (x <$ cr) <> (y <$ cy)) (ts <> ys) <| rs'
173 ----------------------
174 -- NOTE: detect [some text](http://some.url) or [SomeRef]
176 | Tree (Cell bb eb (NodePair PairBracket)) bracket :< rs' <- Seq.viewl rs ->
177 (<| xmlify inh rs') $
179 (toList -> [unTree -> Cell bl el (NodeToken (TokenLink lnk))]) ->
181 xmlAttrs [Cell bl el ("to",lnk)] <>
185 xmlAttrs [Cell bb eb ("to",Plain.plainDocument bracket)] <>
187 ----------------------
188 -- NOTE: gather HeaderDash
189 _ | (span, rest) <- spanlItems (==HeaderDash) roots
191 (<| xmlify inh rest) $
193 span >>= xmlify inh{inh_para=List.repeat xmlPara}
194 ----------------------
195 -- NOTE: gather HeaderDot
196 | (span,rest) <- spanlItems (\case HeaderDot{} -> True; _ -> False) roots
198 (<| xmlify inh rest) $
200 span >>= xmlify inh{inh_para=List.repeat xmlPara}
202 spanlItems :: (Header -> Bool) -> Roots -> (Roots, Roots)
203 spanlItems liHeader =
204 Seq.spanl $ \(unTree -> unCell -> nod) ->
206 NodeHeader (HeaderColon "li" _wh) -> True
207 NodeHeader hdr -> liHeader hdr
208 NodePair (PairElem "li" _as) -> True
210 ----------------------
211 -- NOTE: context-free Root
216 element :: XmlName -> XMLs -> XML
217 element n = Tree (XmlElem n <$ cr)
219 t@(Tree (NodePair (PairElem))) :< ts ->
221 [] -> xmlify inh t <> go inh ts
222 _ | isTokenElem toks -> xmlify inh t <> go inh ts
224 (case Seq.viewl toks of
226 (unTree -> cell_begin -> bp) :< _ -> (tree0 bp (xmlify inh t) <|)) $
229 instance Xmlify Root where
230 xmlify inh tr@(Tree cel@(Cell bp ep nod) ts) =
232 NodeGroup -> xmlify inh ts
233 ----------------------
240 xmlify inh{inh_para} ts
241 ----------------------
247 element "section" $ head <> xmlify inh' body
249 (titles, content) = partitionSection tr
250 (attrs, body) = partitionAttrs content
252 case Seq.viewl titles of
254 title@(unTree -> ct) :< subtitles ->
255 xmlAttrs (attrs `defaultAttr` (ct $> ("id",getAttrId title))) <>
259 subtitles >>= \subtitle@(unTree -> cs) ->
261 Tree (cs $> XmlElem "alias") $
262 xmlAttrs (return $ cs $> ("id",getAttrId subtitle))
264 { inh_para = xmlTitle : List.repeat xmlPara
269 let (attrs,body) = partitionAttrs ts in
271 -- NOTE: insert titles into <about>.
275 (inh_titles inh >>= xmlify inh') <>
278 -- NOTE: in <figure> mode, unreserved nodes become <figure>
279 _ | inh_figure inh && not (n`List.elem`elems) ->
282 -- xmlAttrs (setAttr (Cell ep ep ("type",n)) attrs) <>
283 xmlAttrs (attrs `defaultAttr` Cell bp bp ("type", n)) <>
285 [Tree0{}] -> xmlify inh'{inh_para = List.repeat xmlPara} body
286 _ -> xmlify inh'{inh_para = xmlTitle : List.repeat xmlPara} body
287 -- NOTE: reserved nodes
290 element (xmlLocalName n) $
297 "about" -> xmlTitle : List.repeat xmlPara
298 "reference" -> xmlTitle : List.repeat xmlPara
299 "serie" -> List.repeat xmlName
300 "author" -> List.repeat xmlName
301 "editor" -> List.repeat xmlName
302 "org" -> List.repeat xmlName
305 ----------------------
309 xmlAttrs (Seq.singleton $ Cell bp bp ("type", if TL.null n then "txt" else n)) <>
310 xmlify inh{inh_para=[]} ts
311 ----------------------
314 let (attrs,body) = partitionAttrs ts in
316 xmlAttrs (attrs `defaultAttr` Cell bp bp ("type", if TL.null n then "quote" else n)) <>
317 xmlify inh{inh_para=[]} body
319 HeaderEqual n _wh -> Seq.singleton $ element (xmlLocalName n) $ xmlify inh ts
321 HeaderDot _n -> Seq.singleton $ element "li" $ xmlify inh ts
323 HeaderDash -> Seq.singleton $ element "li" $ xmlify inh ts
325 HeaderDashDash -> Seq.singleton $ Tree0 $ cell $ XmlComment $
326 -- debug1_ ("TS", ts) $
327 -- debug1_ ("RS", (S.evalState (Plain.rackUpLeft ts) Nothing)) $
328 Plain.plainDocument ts
329 -- Plain.text def $ S.evalState (Plain.rackUpLeft ts) Nothing
336 (\_k' -> cell1 . unCell)) <$> ts
339 HeaderBrackets ident ->
340 let (attrs,body) = partitionAttrs ts in
342 element "reference" $
343 xmlAttrs (setAttr (Cell ep ep ("id",ident)) attrs) <>
344 xmlify inh'{inh_para = xmlTitle : xmlTitle : List.repeat xmlPara} body
346 inh' = inh{inh_figure = False}
351 xmlAttrs [cell ("href",TL.pack $ FP.replaceExtension p "dtc")] <>
353 ----------------------
356 PairBracket | to <- Plain.plainDocument ts
357 , TL.all (\c -> Char.isAlphaNum c || Char.isSymbol c) to ->
360 xmlAttrs [cell ("to",to)]
361 PairStar -> Seq.singleton $ element "b" $ xmlify inh ts
362 PairSlash -> Seq.singleton $ element "i" $ xmlify inh ts
363 PairBackquote -> Seq.singleton $ element "code" $ xmlify inh ts
370 (Seq.viewl -> Tree0 (Cell bl el (TokenPlain l)) :< ls) ->
372 m :> Tree0 (Cell br er (TokenPlain r)) ->
374 Tree0 (Cell bl el (TokenPlain (Text.dropWhile Char.isSpace l)))
375 <|(m|>Tree0 (Cell br er (TokenPlain (Text.dropWhileEnd Char.isSpace r))))
378 Tree0 (Cell bl el (TokenPlain (Text.dropAround Char.isSpace l))) <| ls
379 (Seq.viewr -> rs :> Tree0 (Cell br er (TokenPlain r))) ->
381 rs |> Tree0 (Cell br er (TokenPlain (Text.dropAround Char.isSpace r)))
387 xmlAttrs [cell ("to",Plain.plainDocument ts)]
388 PairElem name attrs ->
390 element (xmlLocalName name) $
391 xmlAttrs (Seq.fromList $ (\(_wh,ElemAttr{..}) ->
392 cell (xmlLocalName elemAttr_name,elemAttr_value)) <$> attrs) <>
395 let (open, close) = pairBorders pair ts in
396 Seq.singleton (Tree0 $ Cell bp bp $ XmlText open) `unionXml`
397 xmlify inh ts `unionXml`
398 Seq.singleton (Tree0 $ Cell ep ep $ XmlText close)
399 ----------------------
400 NodeText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
401 ----------------------
404 TokenEscape c -> Seq.singleton $ Tree0 $ cell $ XmlText $ TL.singleton c
405 TokenText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
406 TokenTag t -> Seq.singleton $ element "ref" $ xmlAttrs [cell ("to",t)]
407 TokenLink lnk -> Seq.singleton $ element "eref" $ xmlAttrs [cell ("to",lnk)]
408 ----------------------
416 element :: XmlName -> XMLs -> XML
417 element n = Tree (cell $ XmlElem n)
418 instance Xmlify (Seq (Cell (XmlName,TL.Text))) where
419 xmlify _inh = xmlAttrs
423 -- | Reserved elements' name
519 -- | Convenient alias, forcing the types
520 xmlAttrs :: Seq (Cell (XmlName,TL.Text)) -> XMLs
521 xmlAttrs = (Tree0 . (uncurry XmlAttr <$>) <$>)
523 -- | Extract attributes
524 partitionAttrs :: Roots -> (Seq (Cell (XmlName, TL.Text)), Roots)
525 partitionAttrs ts = (attrs,cs)
527 (as,cs) = (`Seq.partition` ts) $ \case
528 Tree (unCell -> NodeHeader (HeaderEqual (TL.null -> False) _wh)) _cs -> True
532 Tree (Cell bp ep (NodeHeader (HeaderEqual n _wh))) a ->
533 Cell bp ep (xmlLocalName n, v)
535 v = Plain.text (Plain.setStart a def{Plain.state_escape = False}) a
538 getAttrId :: Root -> TL.Text
539 getAttrId = Plain.plainDocument . Seq.singleton
542 Cell (XmlName, TL.Text) ->
543 Seq (Cell (XmlName, TL.Text)) ->
544 Seq (Cell (XmlName, TL.Text))
545 setAttr a@(unCell -> (k, _v)) as =
546 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
547 Just idx -> Seq.update idx a as
551 Seq (Cell (XmlName, TL.Text)) ->
552 Cell (XmlName, TL.Text) ->
553 Seq (Cell (XmlName, TL.Text))
554 defaultAttr as a@(unCell -> (k, _v)) =
555 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
561 -- | Unify two 'XMLs', merging border 'XmlText's if any.
562 unionXml :: XMLs -> XMLs -> XMLs
564 case (Seq.viewr x, Seq.viewl y) of
565 (xs :> x0, y0 :< ys) ->
567 ( Tree0 (Cell bx ex (XmlText tx))
568 , Tree0 (Cell by ey (XmlText ty)) ) ->
570 Seq.singleton (Tree0 $ (XmlText <$>) $ Cell bx ex tx <> Cell by ey ty) `unionXml`
576 unionsXml :: Foldable f => f XMLs -> XMLs
577 unionsXml = foldl' unionXml mempty