1 {-# LANGUAGE DataKinds #-}
2 {-# LANGUAGE DuplicateRecordFields #-}
3 {-# LANGUAGE OverloadedLists #-}
4 {-# LANGUAGE TypeApplications #-}
5 {-# LANGUAGE TypeFamilies #-}
6 {-# LANGUAGE UndecidableInstances #-}
7 {-# OPTIONS_GHC -fno-warn-orphans #-}
10 import Control.Monad (Monad(..), forM_, when)
11 import Control.Monad.IO.Class (MonadIO(..))
13 import Data.Default.Class (Default(..))
14 import Data.Either (Either(..))
15 import Data.Eq (Eq(..))
16 import Data.Function (($), (.))
17 import Data.Functor ((<$>))
19 import Data.Map.Strict (Map)
20 import Data.Maybe (Maybe(..), fromMaybe)
21 import Data.Monoid (Monoid(..))
22 import Data.Semigroup (Semigroup(..))
23 import Data.String (String)
24 import GHC.Exts (IsList(..))
25 import Options.Applicative as Opt
26 import Prelude (error)
27 import System.IO (IO, FilePath, hPrint, hPutStrLn, stderr, stdout)
28 import Text.Show (Show(..))
29 import qualified Data.ByteString as BS
30 import qualified Data.ByteString.Lazy as BSL
31 import qualified Data.Char as Char
32 import qualified Data.List as List
33 import qualified Data.Map.Strict as Map
34 import qualified Data.Text as Text
35 import qualified Data.Text.IO as Text
36 import qualified Data.Text.Lazy as TL
37 import qualified Data.Text.Lazy.Encoding as TL
38 import qualified Data.Text.Lazy.IO as TL
39 import qualified Data.TreeSeq.Strict as Tree
40 import qualified System.Environment as Env
41 import qualified Text.Blaze.Renderer.Utf8 as Blaze
42 import qualified Text.Blaze.Utils as Blaze
43 import qualified Text.Megaparsec as P
46 import qualified Language.TCT as TCT
47 import qualified Language.TCT.Write.HTML5 as TCT.Write.HTML5
48 import qualified Language.TCT.Write.Plain as TCT.Write.Plain
49 import qualified Language.TCT.Write.XML as TCT.Write.XML
52 import qualified Language.DTC.Read.TCT as DTC.Read.TCT
53 import qualified Language.DTC.Sym as DTC
54 import qualified Language.DTC.Write.HTML5 as DTC.Write.HTML5
55 import qualified Language.DTC.Write.XML as DTC.Write.XML
56 import qualified Language.RNC.Write as RNC
57 import qualified Text.Blaze.DTC as Blaze.DTC
58 import qualified Text.Blaze.HTML5 as Blaze.HTML5
60 type Langs = '[FR, EN]
61 type Lang = LocaleIn Langs
66 (\v -> Map.findWithDefault
67 (LocaleIn @Langs en_US)
68 (Text.pack $ List.takeWhile (\c -> Char.isAlphaNum c || c == '_') v)
71 <$> Env.lookupEnv "LANG"
72 cmd <- execParser $ pArgv lang
76 info (pCommand lang <**> helper) $ mconcat
78 , progDesc "document tool"
79 , header "hdoc - command line tool for TCT and DTC technical documents"
82 mainWithCommand :: Command -> IO ()
83 mainWithCommand (CommandTCT ArgsTCT{..}) = do
84 TCT.readFile input >>= \case
85 Left err -> hPrint stderr err
87 case TCT.readTCTWithoutIncludes input txt of
88 Left err -> error $ show err
90 when (trace_TCT trace) $ do
91 hPutStrLn stderr "### TCT ###"
92 hPutStrLn stderr $ Tree.prettyTrees tct
93 when (trace_XML trace) $ do
94 hPutStrLn stderr "### XML ###"
95 let xml = TCT.Write.XML.document tct
96 hPutStrLn stderr $ Tree.prettyTrees xml
100 TCT.Write.Plain.document tct
102 Blaze.renderMarkupToByteStringIO BS.putStr $
103 TCT.Write.HTML5.document tct
104 mainWithCommand (CommandDTC ArgsDTC{..}) =
105 TCT.readTCT input >>= \case
106 Left err -> error $ show err
108 when (trace_TCT trace) $ do
109 hPutStrLn stderr "### TCT ###"
110 hPutStrLn stderr $ Tree.prettyTrees tct
111 let xml = TCT.Write.XML.document tct
112 when (trace_XML trace) $ do
113 hPutStrLn stderr "### XML ###"
114 hPutStrLn stderr $ Tree.prettyTrees xml
115 case DTC.Read.TCT.readDTC xml of
116 Left err -> error $ P.parseErrorPretty err
118 when (trace_DTC trace) $ do
119 hPutStrLn stderr "### DTC ###"
123 Blaze.prettyMarkupIO Blaze.DTC.isInlinedElement BS.putStr $
124 DTC.Write.XML.document locale dtc
126 Blaze.prettyMarkupIO Blaze.HTML5.isInlinedElement BS.putStr $
127 DTC.Write.HTML5.document locale dtc
128 mainWithCommand (CommandRNC ArgsRNC{}) =
129 forM_ DTC.schema $ \rule ->
130 Text.hPutStrLn stdout $ RNC.renderWriter rule
132 -- * Filesystem utilities
133 readFile :: MonadIO m => FilePath -> (FilePath -> TL.Text -> m a) -> m a
135 content <- liftIO $ BSL.readFile fp
136 f fp $ TL.decodeUtf8 content
138 -- * Options utilities
140 instance IsList (Opt.Mod f a) where
141 type Item (Opt.Mod f a) = Opt.Mod f a
145 readMap :: Map String a -> ReadM a
148 case Map.lookup s m of
149 Nothing -> Left $ "cannot parse value \"" <> s
150 <> "\"\nexpecting one of: "
151 <> (List.intercalate ", " $ Map.keys m)
160 pCommand :: Lang -> Parser Command
165 info (CommandTCT <$> pArgsTCT) $
166 progDesc "TCT (Texte Convivial Technique) rendition."
171 info (CommandDTC <$> pArgsDTC lang) $
172 progDesc "DTC (Document Technique Convivial) rendition."
177 info (CommandRNC <$> pArgsRNC) $
178 progDesc "RNC (RelaxNG Compact) schema."
188 instance Default Trace where
194 instance Semigroup Trace where
197 { trace_TCT = trace_TCT x || trace_TCT y
198 , trace_XML = trace_XML x || trace_XML y
199 , trace_DTC = trace_DTC x || trace_DTC y
201 instance Monoid Trace where
205 pTrace :: Parser Trace
212 , help $ "Print trace. (choices: "
213 <> (List.intercalate ", " $ Map.keys m) <> ")"
217 [ ("tct", def{trace_TCT=True})
218 , ("xml", def{trace_XML=True})
219 , ("dtc", def{trace_DTC=True})
226 , format :: TctFormat
230 pArgsTCT :: Parser ArgsTCT
233 <$> argument str (metavar "FILE")
237 -- *** Type 'TctFormat'
242 pTctFormat :: Parser TctFormat
244 flag TctFormatPlain TctFormatPlain
246 , help "Render as plain text."
248 flag TctFormatHTML5 TctFormatHTML5
250 , help "Render as HTML5."
257 , format :: DtcFormat
261 pArgsDTC :: Lang -> Parser ArgsDTC
264 <$> argument str (metavar "FILE")
269 pLocale :: Lang -> Parser (LocaleIn Langs)
272 (maybeReader $ \s -> Map.lookup (Text.pack s) $ locales @Langs)
280 -- *** Type 'DtcFormat'
285 pDtcFormat :: Parser DtcFormat
287 flag DtcFormatHTML5 DtcFormatHTML5
289 , help "Render as HTML5."
291 flag DtcFormatHTML5 DtcFormatXML
293 , help "Render as XML."
300 pArgsRNC :: Parser ArgsRNC
301 pArgsRNC = pure ArgsRNC