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
42 type XML = Tree (Cell XmlName) (Cell XmlLeaf)
48 { xmlNamePrefix :: Text
49 , xmlNameSpace :: Text
50 , xmlNameLocal :: Text
52 instance Show XmlName where
53 showsPrec _p XmlName{xmlNameSpace="", ..} =
54 showString (Text.unpack xmlNameLocal)
55 showsPrec _p XmlName{..} =
56 if Text.null xmlNameSpace
57 then showString (Text.unpack xmlNameLocal)
60 showString (Text.unpack xmlNameSpace) .
62 showString (Text.unpack xmlNameLocal)
63 instance Eq XmlName where
64 XmlName _ sx lx == XmlName _ sy ly = sx == sy && lx == ly
65 instance Ord XmlName where
66 XmlName _ sx lx `compare` XmlName _ sy ly = compare sx sy <> compare lx ly
67 instance IsString XmlName where
68 fromString "" = XmlName "" "" ""
69 fromString full@('{':rest) =
70 case List.break (== '}') rest of
71 (_, "") -> error ("Invalid Clark notation: " <> show full)
72 (ns, local) -> XmlName "" (Text.pack ns) (Text.pack $ List.drop 1 local)
73 fromString local = XmlName "" "" (Text.pack local)
75 xmlLocalName :: Text -> XmlName
76 xmlLocalName = XmlName "" ""
80 = XmlAttr XmlName Text
83 deriving (Eq,Ord,Show)
88 { inhXml_figure :: Bool
89 , inhXml_tree0 :: [Pos -> XMLs -> XML]
90 , inhXml_titles :: Seq Tokens
94 { inhXml_figure = False
96 , inhXml_titles = mempty
99 mimetype :: Text -> Maybe Text
100 mimetype "hs" = Just "text/x-haskell"
101 mimetype "sh" = Just "text/x-shellscript"
102 mimetype "shell" = Just "text/x-shellscript"
103 mimetype "shellscript" = Just "text/x-shellscript"
106 xmlPhantom :: XmlName -> Pos -> XMLs -> XML
107 xmlPhantom n bp = TreeN (Cell bp bp n)
108 xmlPara :: Pos -> XMLs -> XML
109 xmlPara = xmlPhantom "para"
110 xmlTitle :: Pos -> XMLs -> XML
111 xmlTitle = xmlPhantom "title"
112 xmlName :: Pos -> XMLs -> XML
113 xmlName bp (toList -> [Tree0 (unCell -> XmlText t)]) = Tree0 (Cell bp bp $ XmlAttr "name" t)
114 xmlName bp ts = xmlPhantom "name" bp ts
116 xmlDocument :: TCTs -> XMLs
118 case Seq.viewl trees of
119 TreeN (unCell -> KeySection{}) vs :< ts ->
120 case spanlTokens vs of
121 (titles@(Seq.viewl -> (Seq.viewl -> Cell bp _ep _ :< _) :< _), vs') ->
125 TreeN (unCell -> KeyColon "about" _) _ -> True
128 Nothing -> TreeN (Cell bp bp $ KeyColon "about" "") mempty <| vs'
131 { inhXml_titles = titles
132 , inhXml_figure = True
133 , inhXml_tree0 = List.repeat xmlPara
136 _ -> xmlTCTs inhXml trees
137 _ -> xmlTCTs inhXml trees
139 xmlTCTs :: InhXml -> TCTs -> XMLs
140 xmlTCTs inh_orig = go inh_orig
142 go :: InhXml -> TCTs -> XMLs
144 case Seq.viewl trees of
145 TreeN (Cell bp ep (KeyBar n _)) _ :< _
146 | (body,ts) <- spanlBar n trees
149 TreeN (Cell bp ep "artwork") $
150 maybe id (\v -> (Tree0 (Cell bp ep (XmlAttr "type" v)) <|)) (mimetype n) $
151 body >>= xmlTCT inh{inhXml_tree0=[]}
153 TreeN key@(unCell -> KeyColon n _) cs :< ts
154 | (cs',ts') <- spanlKeyColon n ts
156 go inh $ TreeN key (cs<>cs') <| ts'
158 TreeN (Cell bp ep KeyBrackets{}) _ :< _
159 | (rl,ts) <- spanlBrackets trees
162 TreeN (Cell bp ep "rl") $
163 rl >>= xmlTCT inh_orig
165 _ | (ul,ts) <- spanlItems (==KeyDash) trees
166 , TreeN (Cell bp ep _) _ :< _ <- Seq.viewl ul ->
168 TreeN (Cell bp ep "ul") $
169 ul >>= xmlTCT inh{inhXml_tree0=List.repeat xmlPara}
171 _ | (ol,ts) <- spanlItems (\case KeyDot{} -> True; _ -> False) trees
172 , TreeN (Cell bp ep _) _ :< _ <- Seq.viewl ol ->
174 TreeN (Cell bp ep "ol") $
175 ol >>= xmlTCT inh{inhXml_tree0=List.repeat xmlPara}
177 t@(Tree0 toks) :< ts | isTokenElem toks ->
181 t@(Tree0 toks) :< ts ->
182 case inhXml_tree0 inh of
185 go inh{inhXml_tree0=[]} ts
187 case Seq.viewl toks of
188 EmptyL -> go inh{inhXml_tree0=xs} ts
189 Cell bp _ep _ :< _ ->
190 (<| go inh{inhXml_tree0=xs} ts) $
200 xmlTCT :: InhXml -> TCT -> XMLs
203 TreeN (Cell bp ep KeySection{}) ts ->
204 let (attrs,body) = partitionAttributesChildren ts in
206 { inhXml_tree0 = xmlTitle : List.repeat xmlPara
207 , inhXml_figure = True
210 TreeN (Cell bp ep "section") $
211 xmlAttrs (defXmlAttr (Cell ep ep ("id", getAttrId body)) attrs) <>
214 TreeN key@(Cell bp ep (KeyColon kn _)) ts ->
215 let (attrs,body) = partitionAttributesChildren ts in
216 let inh' = inh { inhXml_tree0 =
218 "about" -> xmlTitle : xmlTitle : List.repeat xmlPara
219 "reference" -> xmlTitle : xmlTitle : List.repeat xmlPara
220 "author" -> List.repeat xmlName
224 _ | kn == "about" -> xmlAbout inh' key attrs body
226 _ | inhXml_figure inh && not (kn`List.elem`elems) ->
228 TreeN (Cell bp ep "figure") $
229 xmlAttrs (setXmlAttr (Cell ep ep ("type", kn)) attrs) <>
231 [Tree0{}] -> xmlTCTs inh'{inhXml_tree0 = List.repeat xmlPara} body
232 _ -> xmlTCTs inh'{inhXml_tree0 = xmlTitle : List.repeat xmlPara} body
234 _ -> Seq.singleton $ xmlKey inh' key attrs body
236 TreeN key ts -> Seq.singleton $ xmlKey inh key mempty ts
238 Tree0 ts -> xmlTokens ts
242 Cell Key -> Seq (Cell (XmlName, Text)) ->
244 xmlAbout inh key attrs body =
246 xmlKey inh key attrs $
247 case Seq.viewl (inhXml_titles inh) of
248 (Seq.viewl -> Cell bt _et _ :< _) :< _ ->
249 ((<$> inhXml_titles inh) $ \title ->
250 TreeN (Cell bt bt $ KeyColon "title" "") $
251 Seq.singleton $ Tree0 title)
255 xmlKey :: InhXml -> Cell Key -> Seq (Cell (XmlName, Text)) -> TCTs -> XML
256 xmlKey inh (Cell bp ep key) attrs ts =
258 KeyColon n _wh -> d_key n
259 KeyGreat n _wh -> d_key n
260 KeyEqual n _wh -> d_key n
261 KeyBar n _wh -> d_key n
262 KeyDot _n -> TreeN (cell "li") $ xmlTCTs inh ts
263 KeyDash -> TreeN (cell "li") $ xmlTCTs inh ts
264 KeyDashDash -> Tree0 $ cell $ XmlComment $ TL.toStrict com
268 Write.text Write.config_text $
271 (\_path -> fmap $ cell1 . unCell) <$> ts
272 KeyLower n as -> TreeN (cell "artwork") $ xmlTCTs inh ts
274 let inh' = inh{inhXml_figure = False} in
275 TreeN (cell "reference") $
276 xmlAttrs (setXmlAttr (Cell ep ep ("id", ident)) attrs) <>
279 TreeN (cell "include") $
280 xmlAttrs [cell ("href", Text.pack $ FP.replaceExtension p "dtc")] <>
287 TreeN (cell $ xmlLocalName n) $
291 xmlTokens :: Tokens -> XMLs
292 xmlTokens tok = goTokens tok
294 go :: Cell Token -> XMLs
297 TokenPlain t -> Seq.singleton $ Tree0 $ cell $ XmlText t
298 TokenTag t -> Seq.singleton $ TreeN (cell "ref") $ xmlAttrs [cell ("to",t)]
299 TokenEscape c -> Seq.singleton $ Tree0 $ cell $ XmlText $ Text.singleton c
300 TokenLink lnk -> Seq.singleton $
301 TreeN (cell "eref") $
302 xmlAttrs [cell ("to",lnk)] |>
303 Tree0 (cell $ XmlText lnk)
304 TokenPair PairBracket ts | to <- Write.t_Tokens ts
305 , TL.all (\c -> Char.isAlphaNum c || Char.isSymbol c) to ->
307 TreeN (cell "rref") $
308 xmlAttrs [cell ("to",TL.toStrict to)]
309 TokenPair PairStar ts -> Seq.singleton $ TreeN (cell "b") $ goTokens ts
310 TokenPair PairSlash ts -> Seq.singleton $ TreeN (cell "i") $ goTokens ts
311 TokenPair PairBackquote ts -> Seq.singleton $ TreeN (cell "code") $ goTokens ts
312 TokenPair PairFrenchquote toks@ts ->
316 (Seq.viewl -> Cell bl el (TokenPlain l) :< ls) ->
318 m :> Cell br er (TokenPlain r) ->
320 Cell bl el (TokenPlain (Text.dropWhile Char.isSpace l))
321 <|(m|>Cell br er (TokenPlain (Text.dropWhileEnd Char.isSpace r)))
324 Cell bl el (TokenPlain (Text.dropAround Char.isSpace l)) <| ls
325 (Seq.viewr -> rs :> Cell br er (TokenPlain r)) ->
327 rs |> Cell br er (TokenPlain (Text.dropAround Char.isSpace r))
329 TokenPair PairHash (toList -> [unCell -> TokenPlain t]) ->
332 xmlAttrs [cell ("to",t)]
333 TokenPair (PairElem name attrs) ts ->
335 TreeN (cell $ xmlLocalName name) $
336 xmlAttrs (Seq.fromList $ (\(_wh,Attr{..}) -> cell (xmlLocalName attr_name,attr_value)) <$> attrs) <>
339 let (o,c) = pairBorders p ts in
340 Seq.singleton (Tree0 $ Cell bp bp $ XmlText o) `unionXml`
341 goTokens ts `unionXml`
342 Seq.singleton (Tree0 $ Cell ep ep $ XmlText c)
347 goTokens :: Tokens -> XMLs
349 case Seq.viewl toks of
350 Cell bp _ep (TokenPair PairParen paren)
351 :< (Seq.viewl -> Cell bb eb (TokenPair PairBracket bracket)
355 (toList -> [Cell bl el (TokenLink lnk)]) ->
356 TreeN (Cell bp eb "eref") $
357 xmlAttrs [Cell bl el ("to",lnk)] <>
360 TreeN (Cell bp eb "rref") $
361 xmlAttrs [Cell bb eb ("to",TL.toStrict $ Write.t_Tokens bracket)] <>
363 t :< ts -> go t `unionXml` goTokens ts
366 -- | Unify two 'XMLs', merging border 'XmlText's if any.
367 unionXml :: XMLs -> XMLs -> XMLs
369 case (Seq.viewr x, Seq.viewl y) of
370 (xs :> x0, y0 :< ys) ->
372 (Tree0 (Cell bx _ex (XmlText tx)), Tree0 (Cell _by ey (XmlText ty))) ->
374 Seq.singleton (Tree0 $ Cell bx ey $ XmlText $ tx <> ty) `unionXml`
381 spanlBar :: Name -> TCTs -> (TCTs, TCTs)
382 spanlBar name = first unKeyBar . spanBar
384 unKeyBar :: TCTs -> TCTs
385 unKeyBar = (=<<) $ \case
386 TreeN (unCell -> KeyBar{}) ts -> ts
390 TreeN (unCell -> KeyBar n _) _ | n == name -> True
393 spanlItems :: (Key -> Bool) -> TCTs -> (TCTs, TCTs)
394 spanlItems liKey ts =
395 let (lis, ts') = spanLIs ts in
396 foldl' accumLIs (mempty,ts') lis
398 spanLIs = Seq.spanl $ \case
399 TreeN (unCell -> liKey -> True) _ -> True
402 (unCell -> TokenPair (PairElem "li" _) _) -> True
405 case toList $ Seq.dropWhileR (isTokenWhite . unCell) toks of
406 [unCell -> TokenPair (PairElem "li" _) _] -> True
410 accumLIs acc@(oks,kos) t =
412 TreeN (unCell -> liKey -> True) _ -> (oks|>t,kos)
415 (`Seq.spanl` toks) $ \tok ->
417 TokenPair (PairElem "li" _) _ -> True
418 TokenPlain txt -> Char.isSpace`Text.all`txt
420 ( if null ok then oks else oks|>Tree0 (rmTokenPlain ok)
421 , if null ko then kos else Tree0 ko<|kos )
425 (unCell -> TokenPlain{}) -> False
428 spanlKeyColon :: Name -> TCTs -> (TCTs, TCTs)
431 TreeN (unCell -> KeyBar n _) _ -> n == name
432 TreeN (unCell -> KeyGreat n _) _ -> n == name
435 spanlBrackets :: TCTs -> (TCTs, TCTs)
438 TreeN (unCell -> KeyBrackets{}) _ -> True
441 spanlTokens :: TCTs -> (Seq Tokens, TCTs)
445 _ -> undefined) <$>) .
450 getAttrId :: TCTs -> Text
452 case Seq.index ts <$> Seq.findIndexL TreeSeq.isTree0 ts of
453 Just (Tree0 toks) -> TL.toStrict $ Write.t_Tokens toks
456 setXmlAttr :: Cell (XmlName, Text) -> Seq (Cell (XmlName, Text)) -> Seq (Cell (XmlName, Text))
457 setXmlAttr a@(unCell -> (k, _v)) as =
458 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
459 Just idx -> Seq.update idx a as
462 defXmlAttr :: Cell (XmlName, Text) -> Seq (Cell (XmlName, Text)) -> Seq (Cell (XmlName, Text))
463 defXmlAttr a@(unCell -> (k, _v)) as =
464 case Seq.findIndexL (\(unCell -> (n,_v)) -> n == k) as of
468 xmlAttrs :: Seq (Cell (XmlName,Text)) -> XMLs
469 xmlAttrs = ((\(Cell bp ep (n,v)) -> Tree0 (Cell bp ep $ XmlAttr n v)) <$>)
472 xmlAttr :: XmlAttrs -> (Text,Attr) -> XmlAttrs
473 xmlAttr acc (_,Attr{..}) = Map.insert (xmlLocalName attr_name) attr_value acc
478 d_Attributes :: XmlAttrs -> DTC -> DTC
479 d_Attributes = flip $ Map.foldrWithKey $ \n v ->
480 B.AddCustomAttribute (B.Text n) (B.Text v)
483 partitionAttributesChildren :: TCTs -> (Seq (Cell (XmlName, Text)), TCTs)
484 partitionAttributesChildren ts = (attrs,cs)
486 (as,cs) = (`Seq.partition` ts) $ \case
487 TreeN (unCell -> KeyEqual{}) _cs -> True
489 attrs = foldl' (\acc a -> acc |> attr a) Seq.empty as
491 TreeN (Cell bp ep (KeyEqual n _wh)) a ->
492 Cell bp ep (xmlLocalName n, v)
495 Write.text Write.config_text{Write.config_text_escape = False} $
496 TreeSeq.mapAlsoKey (cell1 . unCell) (\_path -> fmap $ cell1 . unCell) <$> a