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(..), tree0)
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
86 partitionSection _ = mempty
92 , inh_para :: [Inh -> Root -> XML]
95 instance Default Inh where
98 , inh_para = List.repeat elementPara
103 elementPara :: Inh -> Root -> XML
104 elementPara inh (Tree c ts) = Tree (XmlElem "para" <$ c) $ xmlify inh ts
106 elementTitle :: Inh -> Root -> XML
107 elementTitle inh (Tree c ts) = Tree (XmlElem "title" <$ c) $ xmlify inh ts
109 elementName :: Inh -> Root -> XML
110 elementName inh (Tree c ts) = Tree (XmlElem "name" <$ c) $ xmlify inh ts
112 attributeName :: Inh -> Root -> XML
113 attributeName _inh (Tree c ts) = tree0 (XmlAttr "name" (Plain.document ts) <$ c)
117 xmlify :: Inh -> a -> XMLs
118 instance Xmlify Roots where
120 case Seq.viewl roots of
122 r@(Tree cr@(Cell _br _er nr) ts) :< rs ->
124 ----------------------
125 -- NOTE: HeaderColon becomes parent
126 -- of any continuous following-sibling HeaderBar or HeaderGreat
127 NodeHeader (HeaderColon n _wh)
128 | (span, rest) <- spanlHeaderColon rs
130 xmlify inh (Tree cr (ts<>span)) <>
133 spanlHeaderColon :: Roots -> (Roots, Roots)
136 Tree (unCell -> NodeHeader (HeaderBar m _)) _ -> m == n
137 Tree (unCell -> NodeHeader (HeaderGreat m _)) _ -> m == n
139 ----------------------
140 -- NOTE: gather HeaderBrackets
141 NodeHeader HeaderBrackets{}
142 | (span,rest) <- spanlBrackets roots
144 (<| xmlify inh rest) $
145 element "references" $
148 spanlBrackets :: Roots -> (Roots, Roots)
151 Tree (unCell -> NodeHeader HeaderBrackets{}) _ -> True
153 ----------------------
154 -- NOTE: merge adjacent NodeText-s, shouldn't be needed, but just in case.
156 | Tree (cy@(unCell -> NodeText y)) ys :< rs' <- Seq.viewl rs ->
157 xmlify inh $ Tree (NodeText <$> (x <$ cr) <> (y <$ cy)) (ts <> ys) <| rs'
158 ----------------------
159 -- NOTE: detect [some text](http://some.url) or [SomeRef]
161 | Tree (Cell bb eb (NodePair PairBracket)) bracket :< rs' <- Seq.viewl rs ->
162 (<| xmlify inh rs') $
164 (toList -> [unTree -> Cell bl el (NodeToken (TokenLink lnk))]) ->
166 xmlAttrs [Cell bl el ("to",lnk)] <>
170 xmlAttrs [Cell bb eb ("to",Plain.document bracket)] <>
172 ----------------------
173 -- NOTE: gather HeaderDash
174 _ | (span, rest) <- spanlItems (==HeaderDash) roots
176 (<| xmlify inh rest) $
178 span >>= xmlify inh{inh_para=List.repeat elementPara}
179 ----------------------
180 -- NOTE: gather HeaderDot
181 | (span,rest) <- spanlItems (\case HeaderDot{} -> True; _ -> False) roots
183 (<| xmlify inh rest) $
185 span >>= xmlify inh{inh_para=List.repeat elementPara}
187 spanlItems :: (Header -> Bool) -> Roots -> (Roots, Roots)
188 spanlItems liHeader =
189 Seq.spanl $ \(unTree -> (unCell -> nod)) ->
191 NodeHeader (HeaderColon "li" _wh) -> True
192 NodeHeader hdr -> liHeader hdr
193 NodePair (PairElem "li" _as) -> True
195 ----------------------
196 NodePara | para:inh_para <- inh_para inh ->
198 -- para (() <$ cr) (xmlify inh ts) <|
199 xmlify inh{inh_para} rs
200 ----------------------
201 -- NOTE: context-free Root
206 element :: XmlName -> XMLs -> XML
207 element n = Tree (XmlElem n <$ cr)
208 instance Xmlify Root where
209 xmlify inh tn@(Tree (Cell bn en nod) ts) =
211 NodeGroup -> xmlify inh ts
212 ----------------------
216 para:_ -> Seq.singleton $ para inh tn -- para (() <$ cn) $ xmlify inh ts
217 ----------------------
223 element "section" $ head <> xmlify inh' body
225 (titles, content) = partitionSection tn
226 (attrs, body) = partitionAttrs content
228 case Seq.viewl titles of
230 title@(unTree -> ct) :< subtitles ->
231 xmlAttrs (attrs `defaultAttr` (ct $> ("id",getAttrId title))) <>
232 xmlify inh{inh_para=List.repeat elementTitle} title <>
236 subtitles >>= \subtitle@(unTree -> cs) ->
238 Tree (cs $> XmlElem "alias") $
239 xmlAttrs [cs $> ("id",getAttrId subtitle)]
241 { inh_para = List.repeat elementPara
246 let (attrs,body) = partitionAttrs ts in
248 -- NOTE: insert titles into <about>.
252 xmlify inh' (inh_titles inh) <>
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 bn bn ("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 (xmlLocalName 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
287 xmlAttrs (Seq.singleton $ Cell bn bn ("type", n)) <>
288 xmlify inh{inh_para=[]} ts
292 let (attrs,body) = partitionAttrs ts in
294 xmlAttrs (attrs `defaultAttr` Cell bn bn ("type", n)) <>
295 xmlify inh{inh_para=List.repeat elementPara} body
299 Tree0 $ cell $ XmlAttr (xmlLocalName n) $
300 Plain.text (Plain.setStart ts def{Plain.state_escape = False}) ts
302 HeaderDot _n -> Seq.singleton $ element "li" $ xmlify inh ts
304 HeaderDash -> Seq.singleton $ element "li" $ xmlify inh ts
306 HeaderDashDash -> Seq.singleton $ Tree0 $ cell $ XmlComment $
307 -- debug1_ ("TS", ts) $
308 -- debug1_ ("RS", (S.evalState (Plain.rackUpLeft ts) Nothing)) $
310 -- Plain.text def $ S.evalState (Plain.rackUpLeft ts) Nothing
317 (\_k' -> cell1 . unCell)) <$> ts
320 HeaderBrackets ident ->
321 let (attrs,body) = partitionAttrs ts in
323 element "reference" $
324 xmlAttrs (setAttr (Cell en en ("id",ident)) attrs) <>
325 xmlify inh'{inh_para = elementTitle : elementTitle : List.repeat elementPara} body
327 inh' = inh{inh_figure = False}
332 xmlAttrs [cell ("href",TL.pack $ FP.replaceExtension p "dtc")] <>
334 ----------------------
337 PairBracket | to <- Plain.document ts
338 , TL.all (\c -> Char.isAlphaNum c || Char.isSymbol c) to ->
341 xmlAttrs [cell ("to",to)]
342 PairStar -> Seq.singleton $ element "b" $ xmlify inh ts
343 PairSlash -> Seq.singleton $ element "i" $ xmlify inh ts
344 PairBackquote -> Seq.singleton $ element "code" $ xmlify inh ts
351 (Seq.viewl -> Tree0 (Cell bl el (TokenPlain l)) :< ls) ->
353 m :> Tree0 (Cell br er (TokenPlain r)) ->
355 Tree0 (Cell bl el (TokenPlain (Text.dropWhile Char.isSpace l)))
356 <|(m|>Tree0 (Cell br er (TokenPlain (Text.dropWhileEnd Char.isSpace r))))
359 Tree0 (Cell bl el (TokenPlain (Text.dropAround Char.isSpace l))) <| ls
360 (Seq.viewr -> rs :> Tree0 (Cell br er (TokenPlain r))) ->
362 rs |> Tree0 (Cell br er (TokenPlain (Text.dropAround Char.isSpace r)))
368 xmlAttrs [cell ("to",Plain.document ts)]
369 PairElem name attrs ->
371 element (xmlLocalName name) $
372 xmlAttrs (Seq.fromList $ (\(_wh,ElemAttr{..}) ->
373 cell (xmlLocalName elemAttr_name,elemAttr_value)) <$> attrs) <>
376 let (open, close) = pairBorders pair ts in
377 Seq.singleton (Tree0 $ Cell bn bn $ XmlText open) `unionXml`
378 xmlify inh ts `unionXml`
379 Seq.singleton (Tree0 $ Cell en en $ XmlText close)
380 ----------------------
381 NodeText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
382 ----------------------
385 TokenEscape c -> Seq.singleton $ Tree0 $ cell $ XmlText $ TL.singleton c
386 TokenText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
387 TokenTag t -> Seq.singleton $ element "ref" $ xmlAttrs [cell ("to",t)]
388 TokenLink lnk -> Seq.singleton $ element "eref" $ xmlAttrs [cell ("to",lnk)]
389 ----------------------
397 element :: XmlName -> XMLs -> XML
398 element n = Tree (cell $ XmlElem n)
399 instance Xmlify (Seq (Cell (XmlName,TL.Text))) where
400 xmlify _inh = xmlAttrs
404 -- | Reserved elements' name
500 -- | Convenient alias, forcing the types
501 xmlAttrs :: Seq (Cell (XmlName,TL.Text)) -> XMLs
502 xmlAttrs = (Tree0 . (uncurry XmlAttr <$>) <$>)
504 -- | Extract attributes
505 partitionAttrs :: Roots -> (Seq (Cell (XmlName, TL.Text)), Roots)
506 partitionAttrs ts = (attrs,cs)
508 (as,cs) = (`Seq.partition` ts) $ \case
509 Tree (unCell -> NodeHeader (HeaderEqual n _wh)) _cs -> not $ TL.null n
513 Tree (Cell bp ep (NodeHeader (HeaderEqual n _wh))) a ->
514 Cell bp ep (xmlLocalName n, v)
516 v = Plain.text (Plain.setStart a def{Plain.state_escape = False}) a
519 getAttrId :: Root -> TL.Text
520 getAttrId = Plain.document . Seq.singleton
523 Cell (XmlName, TL.Text) ->
524 Seq (Cell (XmlName, TL.Text)) ->
525 Seq (Cell (XmlName, TL.Text))
526 setAttr a@(unCell -> (k, _v)) as =
527 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
528 Just idx -> Seq.update idx a as
532 Seq (Cell (XmlName, TL.Text)) ->
533 Cell (XmlName, TL.Text) ->
534 Seq (Cell (XmlName, TL.Text))
535 defaultAttr as a@(unCell -> (k, _v)) =
536 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
542 -- | Unify two 'XMLs', merging border 'XmlText's if any.
543 unionXml :: XMLs -> XMLs -> XMLs
545 case (Seq.viewr x, Seq.viewl y) of
546 (xs :> x0, y0 :< ys) ->
548 ( Tree0 (Cell bx ex (XmlText tx))
549 , Tree0 (Cell by ey (XmlText ty)) ) ->
551 Seq.singleton (Tree0 $ (XmlText <$>) $ Cell bx ex tx <> Cell by ey ty) `unionXml`
557 unionsXml :: Foldable f => f XMLs -> XMLs
558 unionsXml = foldl' unionXml mempty