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(..), (<$>))
18 import Data.Map.Strict (Map)
19 import Data.Maybe (Maybe(..), maybe)
20 import Data.Monoid (Monoid(..))
21 import Data.Ord (Ord(..))
22 import Data.Semigroup (Semigroup(..))
23 import Data.Sequence (Seq, ViewL(..), ViewR(..), (<|), (|>))
25 import Data.String (IsString(..))
26 import Data.Text (Text)
27 import Data.TreeSeq.Strict (Tree(..))
28 import GHC.Exts (toList)
29 import Prelude (error, undefined)
30 import Text.Show (Show(..), showChar, showString)
31 import qualified Data.Char as Char
32 import qualified Data.List as List
33 import qualified Data.Sequence as Seq
34 import qualified Data.Text as Text
35 import qualified Data.Text.Lazy as TL
36 import qualified Language.TCT.Write.Text as Write
37 import qualified System.FilePath as FP
39 import Language.TCT hiding (Parser)
40 import qualified Data.TreeSeq.Strict as TreeSeq
43 type XML = Tree (Cell XmlName) (Cell XmlLeaf)
49 { xmlNamePrefix :: Text
50 , xmlNameSpace :: Text
51 , xmlNameLocal :: Text
53 instance Show XmlName where
54 showsPrec _p XmlName{xmlNameSpace="", ..} =
55 showString (Text.unpack xmlNameLocal)
56 showsPrec _p XmlName{..} =
57 if Text.null xmlNameSpace
58 then showString (Text.unpack xmlNameLocal)
61 showString (Text.unpack xmlNameSpace) .
63 showString (Text.unpack xmlNameLocal)
64 instance Eq XmlName where
65 XmlName _ sx lx == XmlName _ sy ly = sx == sy && lx == ly
66 instance Ord XmlName where
67 XmlName _ sx lx `compare` XmlName _ sy ly = compare sx sy <> compare lx ly
68 instance IsString XmlName where
69 fromString "" = XmlName "" "" ""
70 fromString full@('{':rest) =
71 case List.break (== '}') rest of
72 (_, "") -> error ("Invalid Clark notation: " <> show full)
73 (ns, local) -> XmlName "" (Text.pack ns) (Text.pack $ List.drop 1 local)
74 fromString local = XmlName "" "" (Text.pack local)
76 xmlLocalName :: Text -> XmlName
77 xmlLocalName = XmlName "" ""
81 = XmlAttr XmlName Text
84 deriving (Eq,Ord,Show)
89 { xmlPosAncestors :: [(XmlName,Count)]
90 , xmlPosPrecedingsSiblings :: Map XmlName Count
97 { inhXml_figure :: Bool
98 , inhXml_tree0 :: [Pos -> XMLs -> XML]
99 , inhXml_titles :: Seq Tokens
103 { inhXml_figure = False
105 , inhXml_titles = mempty
108 mimetype :: Text -> Maybe Text
109 mimetype "hs" = Just "text/x-haskell"
110 mimetype "sh" = Just "text/x-shellscript"
111 mimetype "shell" = Just "text/x-shellscript"
112 mimetype "shellscript" = Just "text/x-shellscript"
115 xmlPhantom :: XmlName -> Pos -> XMLs -> XML
116 xmlPhantom n bp = TreeN (Cell bp bp n)
117 xmlPara :: Pos -> XMLs -> XML
118 xmlPara = xmlPhantom "para"
119 xmlTitle :: Pos -> XMLs -> XML
120 xmlTitle = xmlPhantom "title"
121 xmlName :: Pos -> XMLs -> XML
122 xmlName bp (toList -> [Tree0 (unCell -> XmlText t)]) = Tree0 (Cell bp bp $ XmlAttr "name" t)
123 xmlName bp ts = xmlPhantom "name" bp ts
125 xmlDocument :: TCTs -> XMLs
127 case Seq.viewl trees of
128 TreeN (unCell -> KeySection{}) vs :< ts ->
129 case spanlTokens vs of
130 (titles@(Seq.viewl -> (Seq.viewl -> Cell bp _ep _ :< _) :< _), vs') ->
134 TreeN (unCell -> KeyColon "about" _) _ -> True
137 Nothing -> TreeN (Cell bp bp $ KeyColon "about" "") mempty <| vs'
140 { inhXml_titles = titles
141 , inhXml_figure = True
142 , inhXml_tree0 = List.repeat xmlPara
145 _ -> xmlTCTs inhXml trees
146 _ -> xmlTCTs inhXml trees
148 xmlTCTs :: InhXml -> TCTs -> XMLs
149 xmlTCTs inh_orig = go inh_orig
151 go :: InhXml -> TCTs -> XMLs
153 case Seq.viewl trees of
154 TreeN (Cell bp ep (KeyBar n _)) _ :< _
155 | (body,ts) <- spanlBar n trees
158 TreeN (Cell bp ep "artwork") $
159 maybe id (\v -> (Tree0 (Cell bp ep (XmlAttr "type" v)) <|)) (mimetype n) $
160 body >>= xmlTCT inh{inhXml_tree0=[]}
162 TreeN key@(unCell -> KeyColon n _) cs :< ts
163 | (cs',ts') <- spanlKeyColon n ts
165 go inh $ TreeN key (cs<>cs') <| ts'
167 TreeN (Cell bp ep KeyBrackets{}) _ :< _
168 | (rl,ts) <- spanlBrackets trees
171 TreeN (Cell bp ep "rl") $
172 rl >>= xmlTCT inh_orig
174 _ | (ul,ts) <- spanlItems (==KeyDash) trees
175 , TreeN (Cell bp ep _) _ :< _ <- Seq.viewl ul ->
177 TreeN (Cell bp ep "ul") $
178 ul >>= xmlTCT inh{inhXml_tree0=List.repeat xmlPara}
180 _ | (ol,ts) <- spanlItems (\case KeyDot{} -> True; _ -> False) trees
181 , TreeN (Cell bp ep _) _ :< _ <- Seq.viewl ol ->
183 TreeN (Cell bp ep "ol") $
184 ol >>= xmlTCT inh{inhXml_tree0=List.repeat xmlPara}
186 t@(Tree0 toks) :< ts | isTokenElem toks ->
190 t@(Tree0 toks) :< ts ->
191 case inhXml_tree0 inh of
194 go inh{inhXml_tree0=[]} ts
196 case Seq.viewl toks of
197 EmptyL -> go inh{inhXml_tree0=xs} ts
198 Cell bp _ep _ :< _ ->
199 (<| go inh{inhXml_tree0=xs} ts) $
209 xmlTCT :: InhXml -> TCT -> XMLs
212 TreeN (Cell bp ep KeySection{}) ts ->
213 let (attrs,body) = partitionAttributesChildren ts in
215 { inhXml_tree0 = xmlTitle : List.repeat xmlPara
216 , inhXml_figure = True
219 TreeN (Cell bp ep "section") $
220 xmlAttrs (defXmlAttr (Cell ep ep ("id", getAttrId body)) attrs) <>
223 TreeN key@(Cell bp ep (KeyColon kn _)) ts ->
224 let (attrs,body) = partitionAttributesChildren ts in
225 let inh' = inh { inhXml_tree0 =
227 "about" -> xmlTitle : xmlTitle : List.repeat xmlPara
228 "reference" -> xmlTitle : xmlTitle : List.repeat xmlPara
229 "author" -> List.repeat xmlName
233 _ | kn == "about" -> xmlAbout inh' key attrs body
235 _ | inhXml_figure inh && not (kn`List.elem`elems) ->
237 TreeN (Cell bp ep "figure") $
238 xmlAttrs (setXmlAttr (Cell ep ep ("type", kn)) attrs) <>
240 [Tree0{}] -> xmlTCTs inh'{inhXml_tree0 = List.repeat xmlPara} body
241 _ -> xmlTCTs inh'{inhXml_tree0 = xmlTitle : List.repeat xmlPara} body
243 _ -> Seq.singleton $ xmlKey inh' key attrs body
245 TreeN key ts -> Seq.singleton $ xmlKey inh key mempty ts
247 Tree0 ts -> xmlTokens ts
251 Cell Key -> Seq (Cell (XmlName, Text)) ->
253 xmlAbout inh key attrs body =
255 xmlKey inh key attrs $
256 case Seq.viewl (inhXml_titles inh) of
257 (Seq.viewl -> Cell bt _et _ :< _) :< _ ->
258 ((<$> inhXml_titles inh) $ \title ->
259 TreeN (Cell bt bt $ KeyColon "title" "") $
260 Seq.singleton $ Tree0 title)
264 xmlKey :: InhXml -> Cell Key -> Seq (Cell (XmlName, Text)) -> TCTs -> XML
265 xmlKey inh (Cell bp ep key) attrs ts =
267 KeyColon n _wh -> d_key n
268 KeyGreat n _wh -> d_key n
269 KeyEqual n _wh -> d_key n
270 KeyBar n _wh -> d_key n
271 KeyDot _n -> TreeN (cell "li") $ xmlTCTs inh ts
272 KeyDash -> TreeN (cell "li") $ xmlTCTs inh ts
273 KeyDashDash -> Tree0 $ cell $ XmlComment $ TL.toStrict com
277 Write.text Write.config_text $
280 (\_path -> fmap $ cell1 . unCell) <$> ts
281 KeyLower n as -> TreeN (cell "artwork") $ xmlTCTs inh ts
283 let inh' = inh{inhXml_figure = False} in
284 TreeN (cell "reference") $
285 xmlAttrs (setXmlAttr (Cell ep ep ("id", ident)) attrs) <>
288 TreeN (cell "include") $
289 xmlAttrs [cell ("href", Text.pack $ FP.replaceExtension p "dtc")] <>
296 TreeN (cell $ xmlLocalName n) $
300 xmlTokens :: Tokens -> XMLs
301 xmlTokens tok = goTokens tok
303 go :: Cell Token -> XMLs
306 TokenPlain t -> Seq.singleton $ Tree0 $ cell $ XmlText t
307 TokenTag t -> Seq.singleton $ TreeN (cell "ref") $ xmlAttrs [cell ("to",t)]
308 TokenEscape c -> Seq.singleton $ Tree0 $ cell $ XmlText $ Text.singleton c
309 TokenLink lnk -> Seq.singleton $
310 TreeN (cell "eref") $
311 xmlAttrs [cell ("to",lnk)] |>
312 Tree0 (cell $ XmlText lnk)
313 TokenPair PairBracket ts | to <- Write.textTokens ts
314 , TL.all (\c -> Char.isAlphaNum c || Char.isSymbol c) to ->
316 TreeN (cell "rref") $
317 xmlAttrs [cell ("to",TL.toStrict to)]
318 TokenPair PairStar ts -> Seq.singleton $ TreeN (cell "b") $ goTokens ts
319 TokenPair PairSlash ts -> Seq.singleton $ TreeN (cell "i") $ goTokens ts
320 TokenPair PairBackquote ts -> Seq.singleton $ TreeN (cell "code") $ goTokens ts
321 TokenPair PairFrenchquote toks@ts ->
325 (Seq.viewl -> Cell bl el (TokenPlain l) :< ls) ->
327 m :> Cell br er (TokenPlain r) ->
329 Cell bl el (TokenPlain (Text.dropWhile Char.isSpace l))
330 <|(m|>Cell br er (TokenPlain (Text.dropWhileEnd Char.isSpace r)))
333 Cell bl el (TokenPlain (Text.dropAround Char.isSpace l)) <| ls
334 (Seq.viewr -> rs :> Cell br er (TokenPlain r)) ->
336 rs |> Cell br er (TokenPlain (Text.dropAround Char.isSpace r))
338 TokenPair PairHash to ->
341 xmlAttrs [cell ("to",TL.toStrict $ Write.textTokens to)]
342 TokenPair (PairElem name attrs) ts ->
344 TreeN (cell $ xmlLocalName name) $
345 xmlAttrs (Seq.fromList $ (\(_wh,Attr{..}) -> cell (xmlLocalName attr_name,attr_value)) <$> attrs) <>
348 let (o,c) = pairBorders p ts in
349 Seq.singleton (Tree0 $ Cell bp bp $ XmlText o) `unionXml`
350 goTokens ts `unionXml`
351 Seq.singleton (Tree0 $ Cell ep ep $ XmlText c)
356 goTokens :: Tokens -> XMLs
358 case Seq.viewl toks of
359 Cell bp _ep (TokenPair PairParen paren)
360 :< (Seq.viewl -> Cell bb eb (TokenPair PairBracket bracket)
364 (toList -> [Cell bl el (TokenLink lnk)]) ->
365 TreeN (Cell bp eb "eref") $
366 xmlAttrs [Cell bl el ("to",lnk)] <>
369 TreeN (Cell bp eb "rref") $
370 xmlAttrs [Cell bb eb ("to",TL.toStrict $ Write.textTokens bracket)] <>
372 t :< ts -> go t `unionXml` goTokens ts
375 -- | Unify two 'XMLs', merging border 'XmlText's if any.
376 unionXml :: XMLs -> XMLs -> XMLs
378 case (Seq.viewr x, Seq.viewl y) of
379 (xs :> x0, y0 :< ys) ->
381 (Tree0 (Cell bx _ex (XmlText tx)), Tree0 (Cell _by ey (XmlText ty))) ->
383 Seq.singleton (Tree0 $ Cell bx ey $ XmlText $ tx <> ty) `unionXml`
390 spanlBar :: Name -> TCTs -> (TCTs, TCTs)
391 spanlBar name = first unKeyBar . spanBar
393 unKeyBar :: TCTs -> TCTs
394 unKeyBar = (=<<) $ \case
395 TreeN (unCell -> KeyBar{}) ts -> ts
399 TreeN (unCell -> KeyBar n _) _ | n == name -> True
402 spanlItems :: (Key -> Bool) -> TCTs -> (TCTs, TCTs)
403 spanlItems liKey ts =
404 let (lis, ts') = spanLIs ts in
405 foldl' accumLIs (mempty,ts') lis
407 spanLIs = Seq.spanl $ \case
408 TreeN (unCell -> liKey -> True) _ -> True
411 (unCell -> TokenPair (PairElem "li" _) _) -> True
414 case toList $ Seq.dropWhileR (isTokenWhite . unCell) toks of
415 [unCell -> TokenPair (PairElem "li" _) _] -> True
419 accumLIs acc@(oks,kos) t =
421 TreeN (unCell -> liKey -> True) _ -> (oks|>t,kos)
424 (`Seq.spanl` toks) $ \tok ->
426 TokenPair (PairElem "li" _) _ -> True
427 TokenPlain txt -> Char.isSpace`Text.all`txt
429 ( if null ok then oks else oks|>Tree0 (rmTokenPlain ok)
430 , if null ko then kos else Tree0 ko<|kos )
434 (unCell -> TokenPlain{}) -> False
437 spanlKeyColon :: Name -> TCTs -> (TCTs, TCTs)
440 TreeN (unCell -> KeyBar n _) _ -> n == name
441 TreeN (unCell -> KeyGreat n _) _ -> n == name
444 spanlBrackets :: TCTs -> (TCTs, TCTs)
447 TreeN (unCell -> KeyBrackets{}) _ -> True
450 spanlTokens :: TCTs -> (Seq Tokens, TCTs)
454 _ -> undefined) <$>) .
459 getAttrId :: TCTs -> Text
461 case Seq.index ts <$> Seq.findIndexL TreeSeq.isTree0 ts of
462 Just (Tree0 toks) -> TL.toStrict $ Write.textTokens toks
465 setXmlAttr :: Cell (XmlName, Text) -> Seq (Cell (XmlName, Text)) -> Seq (Cell (XmlName, Text))
466 setXmlAttr a@(unCell -> (k, _v)) as =
467 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
468 Just idx -> Seq.update idx a as
471 defXmlAttr :: Cell (XmlName, Text) -> Seq (Cell (XmlName, Text)) -> Seq (Cell (XmlName, Text))
472 defXmlAttr a@(unCell -> (k, _v)) as =
473 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
477 xmlAttrs :: Seq (Cell (XmlName,Text)) -> XMLs
478 xmlAttrs = ((\(Cell bp ep (n,v)) -> Tree0 (Cell bp ep $ XmlAttr n v)) <$>)
481 xmlAttr :: XmlAttrs -> (Text,Attr) -> XmlAttrs
482 xmlAttr acc (_,Attr{..}) = Map.insert (xmlLocalName attr_name) attr_value acc
487 d_Attributes :: XmlAttrs -> DTC -> DTC
488 d_Attributes = flip $ Map.foldrWithKey $ \n v ->
489 B.AddCustomAttribute (B.Text n) (B.Text v)
492 partitionAttributesChildren :: TCTs -> (Seq (Cell (XmlName, Text)), TCTs)
493 partitionAttributesChildren ts = (attrs,cs)
495 (as,cs) = (`Seq.partition` ts) $ \case
496 TreeN (unCell -> KeyEqual{}) _cs -> True
498 attrs = foldl' (\acc a -> acc |> attr a) Seq.empty as
500 TreeN (Cell bp ep (KeyEqual n _wh)) a ->
501 Cell bp ep (xmlLocalName n, v)
504 Write.text Write.config_text{Write.config_text_escape = False} $
505 TreeSeq.mapAlsoKey (cell1 . unCell) (\_path -> fmap $ cell1 . unCell) <$> a