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.Utils
34 import Language.TCT hiding (Parser)
36 import Text.Blaze.XML ()
40 -- NOTE: 'XmlNode' are still annotated with 'Cell',
41 -- but nothing is done to preserve any ordering amongst them,
42 -- because 'Node's sometimes need to be reordered
43 -- (eg. about/title may have a title from the section before,
44 -- hence outside of about).
45 -- Still holding: @'cell_begin' < 'cell_end'@ and 'Cell' nesting.
46 document :: Roots -> XMLs
48 -- (`S.evalState` def) $
50 sec@(Tree (unCell -> NodeHeader HeaderSection{}) _body) :< foot ->
51 let (titles, content) = partitionSection sec in
52 case Seq.viewl titles of
53 (unTree -> Cell bt et _) :< _ ->
61 case Seq.findIndexL isAbout content of
62 Nothing -> Tree (Cell bt et $ NodeHeader $ HeaderColon "about" "") mempty <| content
65 (unTree -> (unCell -> NodeHeader (HeaderColon "about" _wh))) -> True
70 partitionSection :: Root -> (Roots, Roots)
71 partitionSection (Tree (unCell -> NodeHeader (HeaderSection lvlPar)) body) =
72 case Seq.viewl body of
74 title@(unTree -> Cell _bt et NodePara) :< rest ->
75 let (subtitles, content) = spanlSubtitles et rest in
76 (title <| (subtitles >>= subTrees), content)
78 spanlSubtitles ep ts =
80 sub@(unTree -> Cell bs es (NodeHeader (HeaderSection lvlSub))) :< rs
82 , pos_line bs - pos_line ep <= 1 ->
83 let (subs, ts') = spanlSubtitles es rs in
87 partitionSection _ = mempty
93 , inh_para :: [Inh -> Root -> XML]
96 instance Default Inh where
99 , inh_para = List.repeat elementPara
100 , inh_titles = mempty
104 elementPara :: Inh -> Root -> XML
105 elementPara inh (Tree c ts) = Tree (XmlElem "para" <$ c) $ xmlify inh ts
107 elementTitle :: Inh -> Root -> XML
108 elementTitle inh (Tree c ts) = Tree (XmlElem "title" <$ c) $ xmlify inh ts
110 elementName :: Inh -> Root -> XML
111 elementName inh (Tree c ts) = Tree (XmlElem "name" <$ c) $ xmlify inh ts
113 attributeName :: Inh -> Root -> XML
114 attributeName _inh (Tree c ts) = tree0 (XmlAttr "name" (Plain.document ts) <$ c)
118 xmlify :: Inh -> a -> XMLs
119 instance Xmlify Roots where
121 case Seq.viewl roots of
123 r@(Tree cr@(Cell _br _er nr) ts) :< rs ->
125 ----------------------
126 -- NOTE: HeaderColon becomes parent
127 -- of any continuous following-sibling HeaderBar or HeaderGreat
128 NodeHeader (HeaderColon n _wh)
129 | (span, rest) <- spanlHeaderColon rs
131 xmlify inh (Tree cr (ts<>span)) <>
134 spanlHeaderColon :: Roots -> (Roots, Roots)
137 Tree (unCell -> NodeHeader (HeaderBar m _)) _ -> m == n
138 Tree (unCell -> NodeHeader (HeaderGreat m _)) _ -> m == n
140 ----------------------
141 -- NOTE: gather HeaderBrackets
142 NodeHeader HeaderBrackets{}
143 | (span,rest) <- spanlBrackets roots
145 (<| xmlify inh rest) $
146 element "references" $
149 spanlBrackets :: Roots -> (Roots, Roots)
152 Tree (unCell -> NodeHeader HeaderBrackets{}) _ -> True
154 ----------------------
155 -- NOTE: merge adjacent NodeText-s, shouldn't be needed, but just in case.
157 | Tree (cy@(unCell -> NodeText y)) ys :< rs' <- Seq.viewl rs ->
158 xmlify inh $ Tree (NodeText <$> (x <$ cr) <> (y <$ cy)) (ts <> ys) <| rs'
159 ----------------------
160 -- NOTE: detect [some text](http://some.url) or [SomeRef]
162 | Tree (Cell bb eb (NodePair PairBracket)) bracket :< rs' <- Seq.viewl rs ->
163 (<| xmlify inh rs') $
165 (toList -> [unTree -> Cell bl el (NodeToken (TokenLink lnk))]) ->
167 xmlAttrs [Cell bl el ("to",lnk)] <>
171 xmlAttrs [Cell bb eb ("to",Plain.document bracket)] <>
173 ----------------------
174 -- NOTE: gather HeaderDash
175 _ | (span, rest) <- spanlItems (==HeaderDash) roots
177 (<| xmlify inh rest) $
179 span >>= xmlify inh{inh_para=List.repeat elementPara}
180 ----------------------
181 -- NOTE: gather HeaderDot
182 | (span,rest) <- spanlItems (\case HeaderDot{} -> True; _ -> False) roots
184 (<| xmlify inh rest) $
186 span >>= xmlify inh{inh_para=List.repeat elementPara}
188 spanlItems :: (Header -> Bool) -> Roots -> (Roots, Roots)
189 spanlItems liHeader =
190 Seq.spanl $ \(unTree -> (unCell -> nod)) ->
192 NodeHeader (HeaderColon "li" _wh) -> True
193 NodeHeader hdr -> liHeader hdr
194 NodePair (PairElem "li" _as) -> True
196 ----------------------
197 NodePara | para:inh_para <- inh_para inh ->
199 -- para (() <$ cr) (xmlify inh ts) <|
200 xmlify inh{inh_para} rs
201 ----------------------
202 -- NOTE: context-free Root
207 element :: XmlName -> XMLs -> XML
208 element n = Tree (XmlElem n <$ cr)
209 instance Xmlify Root where
210 xmlify inh tn@(Tree (Cell bn en nod) ts) =
212 NodeGroup -> xmlify inh ts
213 ----------------------
217 para:_ -> Seq.singleton $ para inh tn -- para (() <$ cn) $ xmlify inh ts
218 ----------------------
224 element "section" $ head <> xmlify inh' body
226 (titles, content) = partitionSection tn
227 (attrs, body) = partitionAttrs content
229 case Seq.viewl titles of
231 title@(unTree -> ct) :< subtitles ->
232 xmlAttrs (attrs `defaultAttr` (ct $> ("id",getAttrId title))) <>
233 xmlify inh{inh_para=List.repeat elementTitle} title <>
237 subtitles >>= \subtitle@(unTree -> cs) ->
239 Tree (cs $> XmlElem "alias") $
240 xmlAttrs [cs $> ("id",getAttrId subtitle)]
242 { inh_para = List.repeat elementPara
247 let (attrs,body) = partitionAttrs ts in
249 -- NOTE: insert titles into <about>.
253 xmlify inh' (inh_titles inh) <>
256 -- NOTE: in <figure> mode, unreserved elements become <figure>
257 _ | inh_figure inh && n`List.notElem`elems || TL.null n ->
260 -- xmlAttrs (setAttr (Cell en en ("type",n)) attrs) <>
261 xmlAttrs (attrs `defaultAttr` Cell bn bn ("type", n)) <>
263 [Tree0{}] -> xmlify inh'{inh_para = List.repeat elementPara} body
264 _ -> xmlify inh'{inh_para = elementTitle : List.repeat elementPara} body
265 -- NOTE: reserved elements
268 element (xmlLocalName n) $
275 "about" -> List.repeat elementTitle
276 "reference" -> elementTitle : List.repeat elementPara
277 "serie" -> List.repeat attributeName
278 "author" -> List.repeat attributeName
279 "editor" -> List.repeat attributeName
280 "org" -> List.repeat attributeName
281 "note" -> List.repeat elementPara
288 xmlAttrs (Seq.singleton $ Cell bn bn ("type", n)) <>
289 xmlify inh{inh_para=[]} ts
293 let (attrs,body) = partitionAttrs ts in
295 xmlAttrs (attrs `defaultAttr` Cell bn bn ("type", n)) <>
296 xmlify inh{inh_para=List.repeat elementPara} body
300 Tree0 $ cell $ XmlAttr (xmlLocalName n) $
301 Plain.text (Plain.setStart ts def{Plain.state_escape = False}) ts
306 xmlAttrs (Seq.singleton $ Cell bn bn{pos_column=pos_column bn + int (TL.length n)} ("name", n)) <>
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 = elementTitle : elementTitle : List.repeat elementPara} 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 Seq.singleton (Tree0 $ Cell bn bn' $ XmlText open) `unionXml`
382 xmlify inh ts `unionXml`
383 Seq.singleton (Tree0 $ Cell en' en $ XmlText close)
385 (open, close) = pairBorders pair ts
386 bn' = bn{pos_column=pos_column bn + int (TL.length open)}
387 en' = en{pos_column=pos_column bn - int (TL.length close)}
388 ----------------------
389 NodeText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
390 ----------------------
393 TokenEscape c -> Seq.singleton $ Tree0 $ cell $ XmlText $ TL.singleton c
394 TokenText t -> Seq.singleton $ Tree0 $ cell $ XmlText t
395 TokenTag t -> Seq.singleton $ element "ref" $ xmlAttrs [cell ("to",t)]
396 TokenLink lnk -> Seq.singleton $ element "eref" $ xmlAttrs [cell ("to",lnk)]
397 ----------------------
405 element :: XmlName -> XMLs -> XML
406 element n = Tree (cell $ XmlElem n)
407 instance Xmlify (Seq (Cell (XmlName,TL.Text))) where
408 xmlify _inh = xmlAttrs
412 -- | Reserved elements' name
508 -- | Convenient alias, forcing the types
509 xmlAttrs :: Seq (Cell (XmlName,TL.Text)) -> XMLs
510 xmlAttrs = (Tree0 . (uncurry XmlAttr <$>) <$>)
512 -- | Extract attributes
513 partitionAttrs :: Roots -> (Seq (Cell (XmlName, TL.Text)), Roots)
514 partitionAttrs ts = (attrs,cs)
516 (as,cs) = (`Seq.partition` ts) $ \case
517 Tree (unCell -> NodeHeader (HeaderEqual n _wh)) _cs -> not $ TL.null n
521 Tree (Cell bp ep (NodeHeader (HeaderEqual n _wh))) a ->
522 Cell bp ep (xmlLocalName n, v)
524 v = Plain.text (Plain.setStart a def{Plain.state_escape = False}) a
527 getAttrId :: Root -> TL.Text
528 getAttrId = Plain.document . Seq.singleton
531 Cell (XmlName, TL.Text) ->
532 Seq (Cell (XmlName, TL.Text)) ->
533 Seq (Cell (XmlName, TL.Text))
534 setAttr a@(unCell -> (k, _v)) as =
535 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
536 Just idx -> Seq.update idx a as
540 Seq (Cell (XmlName, TL.Text)) ->
541 Cell (XmlName, TL.Text) ->
542 Seq (Cell (XmlName, TL.Text))
543 defaultAttr as a@(unCell -> (k, _v)) =
544 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
550 -- | Unify two 'XMLs', merging border 'XmlText's if any.
551 unionXml :: XMLs -> XMLs -> XMLs
553 case (Seq.viewr x, Seq.viewl y) of
554 (xs :> x0, y0 :< ys) ->
556 ( Tree0 (Cell bx ex (XmlText tx))
557 , Tree0 (Cell by ey (XmlText ty)) ) ->
559 Seq.singleton (Tree0 $ (XmlText <$>) $ Cell bx ex tx <> Cell by ey ty) `unionXml`
565 unionsXml :: Foldable f => f XMLs -> XMLs
566 unionsXml = foldl' unionXml mempty