) where
import Control.Applicative (Applicative(..))
+import Control.Arrow (left)
import Control.Monad (Monad(..), join)
+import Data.Bool
import Data.Either (Either(..))
-import Data.Foldable (Foldable(..))
+import Data.Eq (Eq)
import Data.Function (($), (.))
import Data.Functor ((<$>))
+import Data.Ord (Ord(..))
+import Data.Set (Set)
import Data.Traversable (Traversable(..))
import Data.TreeSeq.Strict (Tree(..), Trees)
import Data.Void (Void)
-import System.IO (FilePath)
+import System.FilePath ((</>))
+import System.IO (FilePath, IO)
+import Text.Show (Show(..), showParen, showString, showChar)
+import qualified Data.ByteString.Lazy as BSL
+import qualified Data.Set as Set
import qualified Data.Text.Lazy as TL
+import qualified Data.Text.Lazy.Encoding as TL
+import qualified System.FilePath as FilePath
import qualified Text.Megaparsec as P
import Language.TCT.Debug
FilePath -> TL.Text ->
Either ErrorRead (Trees (Cell Node))
readTCT inp txt = do
- trs <- P.runParser (p_Trees <* P.eof) inp txt
+ trs <- left ErrorReadParser $ P.runParser (p_Trees <* P.eof) inp txt
join <$> traverse (go $ NodeHeader HeaderDash)
(debug0 "readTCT" trs)
where
NodeHeader HeaderBar{} -> Right $ pure t
NodeHeader HeaderEqual{} -> Right $ pure t
NodeHeader HeaderDashDash{} -> Right $ pure t
- _ -> do
- toks <- parseTokens <$> parseLexemes inp (Cell bn en n)
- Right $ toks
+ _ -> left ErrorReadParser $ parseTokens <$> parseLexemes inp (Cell bn en n)
_ -> pure . Tree c . join <$> traverse (go nod') ts
where
-- NOTE: skip parent 'Node's whose semantic does not change 'NodeText'
NodePara -> parent
_ -> nod
+readFile :: FilePath -> IO TL.Text
+readFile fp = TL.decodeUtf8 <$> BSL.readFile fp
+
+readTCTrec :: FilePath -> IO (Either ErrorRead (Trees (Cell Node)))
+readTCTrec = goFile Set.empty (NodeHeader HeaderDash)
+ where
+ goFile :: Set FilePath -> Node -> FilePath -> IO (Either ErrorRead (Trees (Cell Node)))
+ goFile inputFiles parentNode inpFile
+ | Set.member inpFileNorm inputFiles =
+ return $ Left $ ErrorReadIncludeLoop inpFileNorm
+ | otherwise = do
+ inpText <- readFile inpFileNorm
+ case P.runParser (p_Trees <* P.eof) inpFileNorm inpText of
+ Left err -> return $ Left $ ErrorReadParser err
+ Right trees ->
+ (join <$>) . sequence <$>
+ traverse
+ (goTree (Set.insert inpFileNorm inputFiles) parentNode)
+ (debug0 "readTCTrec" trees)
+ where
+ inpFileNorm = FilePath.normalise inpFile
+ goTree :: Set FilePath -> Node -> Tree (Cell Node) -> IO (Either ErrorRead (Trees (Cell Node)))
+ goTree inpFiles parNode t@(Tree c@(Cell bn en nod) ts) =
+ case nod of
+ NodeLower{} -> return $ Right $ pure t
+ -- NOTE: preserve NodeText ""
+ NodeText n | TL.null n -> return $ Right $ pure t
+ NodeText n ->
+ case parNode of
+ NodeHeader HeaderBar{} -> return $ Right $ pure t
+ NodeHeader HeaderEqual{} -> return $ Right $ pure t
+ NodeHeader HeaderDashDash{} -> return $ Right $ pure t
+ _ ->
+ return $ left ErrorReadParser $
+ parseTokens <$> parseLexemes inpFileNorm (Cell bn en n)
+ NodeHeader (HeaderDotSlash incFile) ->
+ goFile inpFiles parNode $
+ FilePath.takeDirectory inpFileNorm </> incFile
+ _ ->
+ (pure . Tree c . join <$>) .
+ sequence <$> traverse (goTree inpFiles nod') ts
+ where
+ -- NOTE: skip parent 'Node's whose semantic does not change 'NodeText'
+ nod' = case nod of
+ NodePara -> parNode
+ _ -> nod
+
-- * Type 'ErrorRead'
-type ErrorRead = P.ParseError (P.Token TL.Text) (P.ErrorFancy Void)
+data ErrorRead
+ = ErrorReadParser (P.ParseError (P.Token TL.Text) (P.ErrorFancy Void))
+ | ErrorReadIncludeLoop FilePath
+ deriving (Eq)
+instance Show ErrorRead where
+ showsPrec p = \case
+ ErrorReadParser e ->
+ showParen (p >= 10) $
+ showString (P.parseErrorPretty e)
+ ErrorReadIncludeLoop file ->
+ showString "ErrorReadIncludeLoop" .
+ showChar ' ' . showsPrec 10 file
-- ** Type 'State'
data State
= State
- { state_pos :: Pos
- , state_indent :: Html5
- , state_italic :: Bool
+ { state_pos :: Pos
+ , state_indent :: Html5
+ , state_italic :: Bool
+ , state_ext_html :: String
} -- deriving (Eq, Show)
instance Default State where
def = State
- { state_pos = pos1
- , state_indent = ""
- , state_italic = False
+ { state_pos = pos1
+ , state_indent = ""
+ , state_italic = False
+ , state_ext_html = ".html"
}
-- instance Pretty State
HeaderColon n wh -> html5Header "" "" n wh ":" "" "colon"
HeaderEqual n wh -> html5Header "" "" n wh "=" "" "equal"
HeaderDot n -> html5Header "" "" n "" "." "" "dot"
- HeaderDotSlash n -> html5Header "./" "" (fromString n) "" "" "" "dotslash"
HeaderDash -> html5Header "" "" "" "" "-" " " "dash"
HeaderDashDash -> html5Header "" "" "" "" "--" " " "dashdash"
HeaderBrackets n -> html5Header "[" "" n "" "]" "" "dashdash"
h 6 = H.h6
h n | n > 6 = H.span ! HA.class_ ("h h"<>attrify n)
h _ = undefined
+ HeaderDotSlash n -> do
+ ext <- liftStateMarkup $ S.gets state_ext_html
+ if null ext
+ then html5ify file
+ else
+ H.a ! HA.class_ "header-dotslash"
+ ! HA.href (attrify $ file<>ext) $$
+ html5ify file
+ where file = "./"<>n
where
html5Head :: Name -> White -> Name -> White -> TL.Text -> White -> H.AttributeValue -> Html5
html5Head markBegin whmb name whn markEnd whme cl = do