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
86 partitionSection _ = mempty
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.document 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 NodePara | para:inh_para <- inh_para inh ->
193 para (() <$ cr) (xmlify inh ts) <|
194 xmlify inh{inh_para} rs
195 ----------------------
196 -- NOTE: context-free Root
201 element :: XmlName -> XMLs -> XML
202 element n = Tree (XmlElem n <$ cr)
204 t@(Tree (NodePair (PairElem))) :< ts ->
206 [] -> xmlify inh t <> go inh ts
207 _ | isTokenElem toks -> xmlify inh t <> go inh ts
209 (case Seq.viewl toks of
211 (unTree -> cell_begin -> bp) :< _ -> (tree0 bp (xmlify inh t) <|)) $
214 instance Xmlify Root where
215 xmlify inh tn@(Tree cn@(Cell bn en nod) ts) =
217 NodeGroup -> xmlify inh ts
218 ----------------------
222 para:_ -> Seq.singleton $ para (() <$ cn) $ xmlify inh ts
223 ----------------------
229 element "section" $ head <> xmlify inh' body
231 (titles, content) = partitionSection tn
232 (attrs, body) = partitionAttrs content
234 case Seq.viewl titles of
236 title@(unTree -> ct) :< subtitles ->
237 xmlAttrs (attrs `defaultAttr` (ct $> ("id",getAttrId title))) <>
238 xmlify inh{inh_para=List.repeat xmlTitle} title <>
242 subtitles >>= \subtitle@(unTree -> cs) ->
244 Tree (cs $> XmlElem "alias") $
245 xmlAttrs [cs $> ("id",getAttrId subtitle)]
247 { inh_para = List.repeat xmlPara
252 let (attrs,body) = partitionAttrs ts in
254 -- NOTE: insert titles into <about>.
258 xmlify inh' (inh_titles inh) <>
261 -- NOTE: in <figure> mode, unreserved nodes become <figure>
262 _ | inh_figure inh && n`List.notElem`elems || TL.null n ->
265 -- xmlAttrs (setAttr (Cell en en ("type",n)) attrs) <>
266 xmlAttrs (attrs `defaultAttr` Cell bn bn ("type", n)) <>
268 [Tree0{}] -> xmlify inh'{inh_para = List.repeat xmlPara} body
269 _ -> xmlify inh'{inh_para = xmlTitle : List.repeat xmlPara} body
270 -- NOTE: reserved nodes
273 element (xmlLocalName n) $
280 "about" -> List.repeat xmlTitle
281 "reference" -> xmlTitle : List.repeat xmlPara
282 "serie" -> List.repeat xmlName
283 "author" -> List.repeat xmlName
284 "editor" -> List.repeat xmlName
285 "org" -> List.repeat xmlName
292 xmlAttrs (Seq.singleton $ Cell bn bn ("type", n)) <>
293 xmlify inh{inh_para=[]} ts
297 let (attrs,body) = partitionAttrs ts in
299 xmlAttrs (attrs `defaultAttr` Cell bn bn ("type", n)) <>
300 xmlify inh{inh_para=List.repeat xmlPara} body
304 Tree0 $ cell $ XmlAttr (xmlLocalName n) $
305 Plain.text (Plain.setStart ts def{Plain.state_escape = False}) ts
307 HeaderDot _n -> Seq.singleton $ element "li" $ xmlify inh ts
309 HeaderDash -> Seq.singleton $ element "li" $ xmlify inh ts
311 HeaderDashDash -> Seq.singleton $ Tree0 $ cell $ XmlComment $
312 -- debug1_ ("TS", ts) $
313 -- debug1_ ("RS", (S.evalState (Plain.rackUpLeft ts) Nothing)) $
315 -- Plain.text def $ S.evalState (Plain.rackUpLeft ts) Nothing
322 (\_k' -> cell1 . unCell)) <$> ts
325 HeaderBrackets ident ->
326 let (attrs,body) = partitionAttrs ts in
328 element "reference" $
329 xmlAttrs (setAttr (Cell en en ("id",ident)) attrs) <>
330 xmlify inh'{inh_para = xmlTitle : xmlTitle : List.repeat xmlPara} body
332 inh' = inh{inh_figure = False}
337 xmlAttrs [cell ("href",TL.pack $ FP.replaceExtension p "dtc")] <>
339 ----------------------
342 PairBracket | to <- Plain.document ts
343 , TL.all (\c -> Char.isAlphaNum c || Char.isSymbol c) to ->
346 xmlAttrs [cell ("to",to)]
347 PairStar -> Seq.singleton $ element "b" $ xmlify inh ts
348 PairSlash -> Seq.singleton $ element "i" $ xmlify inh ts
349 PairBackquote -> Seq.singleton $ element "code" $ xmlify inh ts
356 (Seq.viewl -> Tree0 (Cell bl el (TokenPlain l)) :< ls) ->
358 m :> Tree0 (Cell br er (TokenPlain r)) ->
360 Tree0 (Cell bl el (TokenPlain (Text.dropWhile Char.isSpace l)))
361 <|(m|>Tree0 (Cell br er (TokenPlain (Text.dropWhileEnd Char.isSpace r))))
364 Tree0 (Cell bl el (TokenPlain (Text.dropAround Char.isSpace l))) <| ls
365 (Seq.viewr -> rs :> Tree0 (Cell br er (TokenPlain r))) ->
367 rs |> Tree0 (Cell br er (TokenPlain (Text.dropAround Char.isSpace r)))
373 xmlAttrs [cell ("to",Plain.document ts)]
374 PairElem name attrs ->
376 element (xmlLocalName name) $
377 xmlAttrs (Seq.fromList $ (\(_wh,ElemAttr{..}) ->
378 cell (xmlLocalName elemAttr_name,elemAttr_value)) <$> attrs) <>
381 let (open, close) = pairBorders pair ts in
382 Seq.singleton (Tree0 $ Cell bn bn $ XmlText open) `unionXml`
383 xmlify inh ts `unionXml`
384 Seq.singleton (Tree0 $ Cell en en $ XmlText close)
385 ----------------------
386 NodeText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
387 ----------------------
390 TokenEscape c -> Seq.singleton $ Tree0 $ cell $ XmlText $ TL.singleton c
391 TokenText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
392 TokenTag t -> Seq.singleton $ element "ref" $ xmlAttrs [cell ("to",t)]
393 TokenLink lnk -> Seq.singleton $ element "eref" $ xmlAttrs [cell ("to",lnk)]
394 ----------------------
402 element :: XmlName -> XMLs -> XML
403 element n = Tree (cell $ XmlElem n)
404 instance Xmlify (Seq (Cell (XmlName,TL.Text))) where
405 xmlify _inh = xmlAttrs
409 -- | Reserved elements' name
505 -- | Convenient alias, forcing the types
506 xmlAttrs :: Seq (Cell (XmlName,TL.Text)) -> XMLs
507 xmlAttrs = (Tree0 . (uncurry XmlAttr <$>) <$>)
509 -- | Extract attributes
510 partitionAttrs :: Roots -> (Seq (Cell (XmlName, TL.Text)), Roots)
511 partitionAttrs ts = (attrs,cs)
513 (as,cs) = (`Seq.partition` ts) $ \case
514 Tree (unCell -> NodeHeader (HeaderEqual n _wh)) _cs -> not $ TL.null n
518 Tree (Cell bp ep (NodeHeader (HeaderEqual n _wh))) a ->
519 Cell bp ep (xmlLocalName n, v)
521 v = Plain.text (Plain.setStart a def{Plain.state_escape = False}) a
524 getAttrId :: Root -> TL.Text
525 getAttrId = Plain.document . Seq.singleton
528 Cell (XmlName, TL.Text) ->
529 Seq (Cell (XmlName, TL.Text)) ->
530 Seq (Cell (XmlName, TL.Text))
531 setAttr a@(unCell -> (k, _v)) as =
532 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
533 Just idx -> Seq.update idx a as
537 Seq (Cell (XmlName, TL.Text)) ->
538 Cell (XmlName, TL.Text) ->
539 Seq (Cell (XmlName, TL.Text))
540 defaultAttr as a@(unCell -> (k, _v)) =
541 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
547 -- | Unify two 'XMLs', merging border 'XmlText's if any.
548 unionXml :: XMLs -> XMLs -> XMLs
550 case (Seq.viewr x, Seq.viewl y) of
551 (xs :> x0, y0 :< ys) ->
553 ( Tree0 (Cell bx ex (XmlText tx))
554 , Tree0 (Cell by ey (XmlText ty)) ) ->
556 Seq.singleton (Tree0 $ (XmlText <$>) $ Cell bx ex tx <> Cell by ey ty) `unionXml`
562 unionsXml :: Foldable f => f XMLs -> XMLs
563 unionsXml = foldl' unionXml mempty