1 {-# LANGUAGE FlexibleContexts #-}
2 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE OverloadedLists #-}
4 {-# LANGUAGE OverloadedStrings #-}
5 {-# LANGUAGE RecordWildCards #-}
6 {-# LANGUAGE ViewPatterns #-}
7 {-# OPTIONS_GHC -fno-warn-orphans #-}
8 module Language.TCT.Write.XML where
10 import Control.Arrow (first)
11 import Control.Monad (Monad(..), (=<<))
13 import Data.Eq (Eq(..))
14 import Data.Foldable (null, foldl', any)
15 import Data.Function (($), (.), id)
16 import Data.Functor (Functor(..), (<$>))
17 import Data.Maybe (Maybe(..), maybe)
18 import Data.Monoid (Monoid(..))
19 import Data.Ord (Ord(..))
20 import Data.Semigroup (Semigroup(..))
21 import Data.Sequence (Seq, ViewL(..), ViewR(..), (<|), (|>))
23 import Data.String (IsString(..))
24 import Data.Text (Text)
25 import Data.TreeSeq.Strict (Tree(..))
26 import GHC.Exts (toList)
27 import Prelude (error, undefined)
28 import Text.Show (Show(..), showChar, showString)
29 import qualified Data.Char as Char
30 import qualified Data.List as List
31 import qualified Data.Sequence as Seq
32 import qualified Data.Text as Text
33 import qualified Data.Text.Lazy as TL
34 import qualified Language.TCT.Write.Text as Write
35 import qualified System.FilePath as FP
37 import Language.TCT hiding (Parser)
38 import qualified Data.TreeSeq.Strict as TreeSeq
41 type XML = Tree (Cell XmlName) (Cell XmlLeaf)
47 { xmlNamePrefix :: Text
48 , xmlNameSpace :: Text
49 , xmlNameLocal :: Text
51 instance Show XmlName where
52 showsPrec _p XmlName{xmlNameSpace="", ..} =
53 showString (Text.unpack xmlNameLocal)
54 showsPrec _p XmlName{..} =
55 if Text.null xmlNameSpace
56 then showString (Text.unpack xmlNameLocal)
59 showString (Text.unpack xmlNameSpace) .
61 showString (Text.unpack xmlNameLocal)
62 instance Eq XmlName where
63 XmlName _ sx lx == XmlName _ sy ly = sx == sy && lx == ly
64 instance Ord XmlName where
65 XmlName _ sx lx `compare` XmlName _ sy ly = compare sx sy <> compare lx ly
66 instance IsString XmlName where
67 fromString "" = XmlName "" "" ""
68 fromString full@('{':rest) =
69 case List.break (== '}') rest of
70 (_, "") -> error ("Invalid Clark notation: " <> show full)
71 (ns, local) -> XmlName "" (Text.pack ns) (Text.pack $ List.drop 1 local)
72 fromString local = XmlName "" "" (Text.pack local)
74 xmlLocalName :: Text -> XmlName
75 xmlLocalName = XmlName "" ""
79 = XmlAttr XmlName Text
82 deriving (Eq,Ord,Show)
87 { inhXml_figure :: Bool
88 , inhXml_tree0 :: [Pos -> XMLs -> XML]
89 , inhXml_titles :: Seq Tokens
93 { inhXml_figure = False
95 , inhXml_titles = mempty
98 mimetype :: Text -> Maybe Text
99 mimetype "hs" = Just "text/x-haskell"
100 mimetype "sh" = Just "text/x-shellscript"
101 mimetype "shell" = Just "text/x-shellscript"
102 mimetype "shellscript" = Just "text/x-shellscript"
105 xmlPhantom :: XmlName -> Pos -> XMLs -> XML
106 xmlPhantom n bp = TreeN (Cell bp bp n)
107 xmlPara :: Pos -> XMLs -> XML
108 xmlPara = xmlPhantom "para"
109 xmlTitle :: Pos -> XMLs -> XML
110 xmlTitle = xmlPhantom "title"
111 xmlName :: Pos -> XMLs -> XML
112 xmlName bp (toList -> [Tree0 (unCell -> XmlText t)]) = Tree0 (Cell bp bp $ XmlAttr "name" t)
113 xmlName bp ts = xmlPhantom "name" bp ts
115 xmlDocument :: TCTs -> XMLs
117 case Seq.viewl trees of
118 TreeN (unCell -> KeySection{}) vs :< ts ->
119 case spanlTokens vs of
120 (titles@(Seq.viewl -> (Seq.viewl -> Cell bp _ep _ :< _) :< _), vs') ->
124 TreeN (unCell -> KeyColon "about" _) _ -> True
127 Nothing -> TreeN (Cell bp bp $ KeyColon "about" "") mempty <| vs'
130 { inhXml_titles = titles
131 , inhXml_figure = True
132 , inhXml_tree0 = List.repeat xmlPara
135 _ -> xmlTCTs inhXml trees
136 _ -> xmlTCTs inhXml trees
138 xmlTCTs :: InhXml -> TCTs -> XMLs
139 xmlTCTs inh_orig = go inh_orig
141 go :: InhXml -> TCTs -> XMLs
143 case Seq.viewl trees of
144 TreeN (Cell bp ep (KeyBar n _)) _ :< _
145 | (body,ts) <- spanlBar n trees
148 TreeN (Cell bp ep "artwork") $
149 maybe id (\v -> (Tree0 (Cell bp ep (XmlAttr "type" v)) <|)) (mimetype n) $
150 body >>= xmlTCT inh{inhXml_tree0=[]}
152 TreeN key@(unCell -> KeyColon n _) cs :< ts
153 | (cs',ts') <- spanlKeyColon n ts
155 go inh $ TreeN key (cs<>cs') <| ts'
157 TreeN (Cell bp ep KeyBrackets{}) _ :< _
158 | (rl,ts) <- spanlBrackets trees
161 TreeN (Cell bp ep "rl") $
162 rl >>= xmlTCT inh_orig
164 _ | (ul,ts) <- spanlItems (==KeyDash) trees
165 , TreeN (Cell bp ep _) _ :< _ <- Seq.viewl ul ->
167 TreeN (Cell bp ep "ul") $
168 ul >>= xmlTCT inh{inhXml_tree0=List.repeat xmlPara}
170 _ | (ol,ts) <- spanlItems (\case KeyDot{} -> True; _ -> False) trees
171 , TreeN (Cell bp ep _) _ :< _ <- Seq.viewl ol ->
173 TreeN (Cell bp ep "ol") $
174 ol >>= xmlTCT inh{inhXml_tree0=List.repeat xmlPara}
176 t@(Tree0 toks) :< ts | isTokenElem toks ->
180 t@(Tree0 toks) :< ts ->
181 case inhXml_tree0 inh of
184 go inh{inhXml_tree0=[]} ts
186 case Seq.viewl toks of
187 EmptyL -> go inh{inhXml_tree0=xs} ts
188 Cell bp _ep _ :< _ ->
189 (<| go inh{inhXml_tree0=xs} ts) $
199 xmlTCT :: InhXml -> TCT -> XMLs
202 TreeN (Cell bp ep KeySection{}) ts ->
203 let (attrs,body) = partitionAttributesChildren ts in
205 { inhXml_tree0 = xmlTitle : List.repeat xmlPara
206 , inhXml_figure = True
209 TreeN (Cell bp ep "section") $
210 xmlAttrs (defXmlAttr (Cell ep ep ("id", getAttrId body)) attrs) <>
213 TreeN key@(Cell bp ep (KeyColon kn _)) ts ->
214 let (attrs,body) = partitionAttributesChildren ts in
215 let inh' = inh { inhXml_tree0 =
217 "about" -> xmlTitle : xmlTitle : List.repeat xmlPara
218 "reference" -> xmlTitle : xmlTitle : List.repeat xmlPara
219 "author" -> List.repeat xmlName
223 _ | kn == "about" -> xmlAbout inh' key attrs body
225 _ | inhXml_figure inh && not (kn`List.elem`elems) ->
227 TreeN (Cell bp ep "figure") $
228 xmlAttrs (setXmlAttr (Cell ep ep ("type", kn)) attrs) <>
230 [Tree0{}] -> xmlTCTs inh'{inhXml_tree0 = List.repeat xmlPara} body
231 _ -> xmlTCTs inh'{inhXml_tree0 = xmlTitle : List.repeat xmlPara} body
233 _ -> Seq.singleton $ xmlKey inh' key attrs body
235 TreeN key ts -> Seq.singleton $ xmlKey inh key mempty ts
237 Tree0 ts -> xmlTokens ts
241 Cell Key -> Seq (Cell (XmlName, Text)) ->
243 xmlAbout inh key attrs body =
245 xmlKey inh key attrs $
246 case Seq.viewl (inhXml_titles inh) of
247 (Seq.viewl -> Cell bt _et _ :< _) :< _ ->
248 ((<$> inhXml_titles inh) $ \title ->
249 TreeN (Cell bt bt $ KeyColon "title" "") $
250 Seq.singleton $ Tree0 title)
254 xmlKey :: InhXml -> Cell Key -> Seq (Cell (XmlName, Text)) -> TCTs -> XML
255 xmlKey inh (Cell bp ep key) attrs ts =
257 KeyColon n _wh -> d_key n
258 KeyGreat n _wh -> d_key n
259 KeyEqual n _wh -> d_key n
260 KeyBar n _wh -> d_key n
261 KeyDot _n -> TreeN (cell "li") $ xmlTCTs inh ts
262 KeyDash -> TreeN (cell "li") $ xmlTCTs inh ts
263 KeyDashDash -> Tree0 $ cell $ XmlComment $ TL.toStrict com
267 Write.text Write.config_text $
270 (\_path -> fmap $ cell1 . unCell) <$> ts
271 KeyLower n as -> TreeN (cell "artwork") $ xmlTCTs inh ts
273 let inh' = inh{inhXml_figure = False} in
274 TreeN (cell "reference") $
275 xmlAttrs (setXmlAttr (Cell ep ep ("id", ident)) attrs) <>
278 TreeN (cell "include") $
279 xmlAttrs [cell ("href", Text.pack $ FP.replaceExtension p "dtc")] <>
286 TreeN (cell $ xmlLocalName n) $
290 xmlTokens :: Tokens -> XMLs
291 xmlTokens tok = goTokens tok
293 go :: Cell Token -> XMLs
296 TokenPlain t -> Seq.singleton $ Tree0 $ cell $ XmlText t
297 TokenTag t -> Seq.singleton $ TreeN (cell "ref") $ xmlAttrs [cell ("to",t)]
298 TokenEscape c -> Seq.singleton $ Tree0 $ cell $ XmlText $ Text.singleton c
299 TokenLink lnk -> Seq.singleton $
300 TreeN (cell "eref") $
301 xmlAttrs [cell ("to",lnk)] |>
302 Tree0 (cell $ XmlText lnk)
303 TokenPair PairBracket ts | to <- Write.textTokens ts
304 , TL.all (\c -> Char.isAlphaNum c || Char.isSymbol c) to ->
306 TreeN (cell "rref") $
307 xmlAttrs [cell ("to",TL.toStrict to)]
308 TokenPair PairStar ts -> Seq.singleton $ TreeN (cell "b") $ goTokens ts
309 TokenPair PairSlash ts -> Seq.singleton $ TreeN (cell "i") $ goTokens ts
310 TokenPair PairBackquote ts -> Seq.singleton $ TreeN (cell "code") $ goTokens ts
311 TokenPair PairFrenchquote toks@ts ->
315 (Seq.viewl -> Cell bl el (TokenPlain l) :< ls) ->
317 m :> Cell br er (TokenPlain r) ->
319 Cell bl el (TokenPlain (Text.dropWhile Char.isSpace l))
320 <|(m|>Cell br er (TokenPlain (Text.dropWhileEnd Char.isSpace r)))
323 Cell bl el (TokenPlain (Text.dropAround Char.isSpace l)) <| ls
324 (Seq.viewr -> rs :> Cell br er (TokenPlain r)) ->
326 rs |> Cell br er (TokenPlain (Text.dropAround Char.isSpace r))
328 TokenPair PairHash (toList -> [unCell -> TokenPlain t]) ->
331 xmlAttrs [cell ("to",t)]
332 TokenPair (PairElem name attrs) ts ->
334 TreeN (cell $ xmlLocalName name) $
335 xmlAttrs (Seq.fromList $ (\(_wh,Attr{..}) -> cell (xmlLocalName attr_name,attr_value)) <$> attrs) <>
338 let (o,c) = pairBorders p ts in
339 Seq.singleton (Tree0 $ Cell bp bp $ XmlText o) `unionXml`
340 goTokens ts `unionXml`
341 Seq.singleton (Tree0 $ Cell ep ep $ XmlText c)
346 goTokens :: Tokens -> XMLs
348 case Seq.viewl toks of
349 Cell bp _ep (TokenPair PairParen paren)
350 :< (Seq.viewl -> Cell bb eb (TokenPair PairBracket bracket)
354 (toList -> [Cell bl el (TokenLink lnk)]) ->
355 TreeN (Cell bp eb "eref") $
356 xmlAttrs [Cell bl el ("to",lnk)] <>
359 TreeN (Cell bp eb "rref") $
360 xmlAttrs [Cell bb eb ("to",TL.toStrict $ Write.textTokens bracket)] <>
362 t :< ts -> go t `unionXml` goTokens ts
365 -- | Unify two 'XMLs', merging border 'XmlText's if any.
366 unionXml :: XMLs -> XMLs -> XMLs
368 case (Seq.viewr x, Seq.viewl y) of
369 (xs :> x0, y0 :< ys) ->
371 (Tree0 (Cell bx _ex (XmlText tx)), Tree0 (Cell _by ey (XmlText ty))) ->
373 Seq.singleton (Tree0 $ Cell bx ey $ XmlText $ tx <> ty) `unionXml`
380 spanlBar :: Name -> TCTs -> (TCTs, TCTs)
381 spanlBar name = first unKeyBar . spanBar
383 unKeyBar :: TCTs -> TCTs
384 unKeyBar = (=<<) $ \case
385 TreeN (unCell -> KeyBar{}) ts -> ts
389 TreeN (unCell -> KeyBar n _) _ | n == name -> True
392 spanlItems :: (Key -> Bool) -> TCTs -> (TCTs, TCTs)
393 spanlItems liKey ts =
394 let (lis, ts') = spanLIs ts in
395 foldl' accumLIs (mempty,ts') lis
397 spanLIs = Seq.spanl $ \case
398 TreeN (unCell -> liKey -> True) _ -> True
401 (unCell -> TokenPair (PairElem "li" _) _) -> True
404 case toList $ Seq.dropWhileR (isTokenWhite . unCell) toks of
405 [unCell -> TokenPair (PairElem "li" _) _] -> True
409 accumLIs acc@(oks,kos) t =
411 TreeN (unCell -> liKey -> True) _ -> (oks|>t,kos)
414 (`Seq.spanl` toks) $ \tok ->
416 TokenPair (PairElem "li" _) _ -> True
417 TokenPlain txt -> Char.isSpace`Text.all`txt
419 ( if null ok then oks else oks|>Tree0 (rmTokenPlain ok)
420 , if null ko then kos else Tree0 ko<|kos )
424 (unCell -> TokenPlain{}) -> False
427 spanlKeyColon :: Name -> TCTs -> (TCTs, TCTs)
430 TreeN (unCell -> KeyBar n _) _ -> n == name
431 TreeN (unCell -> KeyGreat n _) _ -> n == name
434 spanlBrackets :: TCTs -> (TCTs, TCTs)
437 TreeN (unCell -> KeyBrackets{}) _ -> True
440 spanlTokens :: TCTs -> (Seq Tokens, TCTs)
444 _ -> undefined) <$>) .
449 getAttrId :: TCTs -> Text
451 case Seq.index ts <$> Seq.findIndexL TreeSeq.isTree0 ts of
452 Just (Tree0 toks) -> TL.toStrict $ Write.textTokens toks
455 setXmlAttr :: Cell (XmlName, Text) -> Seq (Cell (XmlName, Text)) -> Seq (Cell (XmlName, Text))
456 setXmlAttr a@(unCell -> (k, _v)) as =
457 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
458 Just idx -> Seq.update idx a as
461 defXmlAttr :: Cell (XmlName, Text) -> Seq (Cell (XmlName, Text)) -> Seq (Cell (XmlName, Text))
462 defXmlAttr a@(unCell -> (k, _v)) as =
463 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
467 xmlAttrs :: Seq (Cell (XmlName,Text)) -> XMLs
468 xmlAttrs = ((\(Cell bp ep (n,v)) -> Tree0 (Cell bp ep $ XmlAttr n v)) <$>)
471 xmlAttr :: XmlAttrs -> (Text,Attr) -> XmlAttrs
472 xmlAttr acc (_,Attr{..}) = Map.insert (xmlLocalName attr_name) attr_value acc
477 d_Attributes :: XmlAttrs -> DTC -> DTC
478 d_Attributes = flip $ Map.foldrWithKey $ \n v ->
479 B.AddCustomAttribute (B.Text n) (B.Text v)
482 partitionAttributesChildren :: TCTs -> (Seq (Cell (XmlName, Text)), TCTs)
483 partitionAttributesChildren ts = (attrs,cs)
485 (as,cs) = (`Seq.partition` ts) $ \case
486 TreeN (unCell -> KeyEqual{}) _cs -> True
488 attrs = foldl' (\acc a -> acc |> attr a) Seq.empty as
490 TreeN (Cell bp ep (KeyEqual n _wh)) a ->
491 Cell bp ep (xmlLocalName n, v)
494 Write.text Write.config_text{Write.config_text_escape = False} $
495 TreeSeq.mapAlsoKey (cell1 . unCell) (\_path -> fmap $ cell1 . unCell) <$> a