haskintex 0.4.1.0 → 0.5.0.0
raw patch · 2 files changed
+351/−166 lines, 2 filesdep +binarydep +bytestringdep +containersdep ~HaTeXdep ~basedep ~directoryPVP ok
version bump matches the API change (PVP)
Dependencies added: binary, bytestring, containers, haskell-src-exts
Dependency ranges changed: HaTeX, base, directory, filepath, hint, parsec, process
API changes (from Hackage documentation)
Files
- Haskintex.hs +338/−158
- haskintex.cabal +13/−8
Haskintex.hs view
@@ -12,27 +12,40 @@ import Data.Text (pack,unpack) import qualified Data.Text as T import qualified Data.Text.IO as T+import Data.Text.Encoding -- Parser--- import Data.Attoparsec.Text import Text.Parsec hiding (many,(<|>))-import Text.Parsec.Text+import Text.Parsec.Text () -- Transformers-import Control.Monad (when,unless)+import Control.Monad (when,unless,replicateM) import Control.Monad.Trans.Class-import Control.Monad.Trans.Reader+import Control.Monad.Trans.State -- LaTeX import Text.LaTeX hiding (version)+import qualified Text.LaTeX as Hatex import Text.LaTeX.Base.Syntax -- Utils import Control.Applicative import Data.Foldable (foldMap)+import Numeric (showFFloat) -- Paths-import Paths_haskintex+import Paths_haskintex (version) import Data.Version (showVersion) -- Lists import Data.List (intersperse) -- GHC-import Language.Haskell.Interpreter+import Language.Haskell.Interpreter hiding (get)+import Data.Typeable+import qualified Language.Haskell.Exts.Pretty as H+import qualified Language.Haskell.Exts.Parser as H+-- Map+import qualified Data.Map as M+-- Binary+import Data.Binary.Put+import Data.Binary.Get hiding (lookAhead)+import Data.ByteString.Lazy (ByteString)+import qualified Data.ByteString.Lazy as LB+import qualified Data.ByteString as SB -- Syntax @@ -56,58 +69,148 @@ -- data Syntax = WriteLaTeX Text- | WriteHaskell Bool Bool Text -- First Bool: False for Hidden, True for Visible.- -- Second Bool: True for Header, False for Body.- | InsertHaTeX Text- | InsertHaTeXIO Text- | EvalHaskell Bool Text -- False for Command, True for Environment+ | WriteHaskell Bool -- Visibility: False for Hidden, True for Visible+ Bool -- Location: True for Header, False for Body+ Text+ | InsertHaTeX Bool -- Memorized expression?+ Text+ | InsertHaTeXIO Bool -- Memorized expression?+ Text+ | EvalHaskell Bool -- Type: False for Command, True for Environment+ Bool -- Memorized expression?+ Text | Sequence [Syntax] deriving Show -- Show instance for debugging. +-- Configuration++data Conf = Conf+ { keepFlag :: Bool+ , visibleFlag :: Bool+ , verboseFlag :: Bool+ , manualFlag :: Bool+ , helpFlag :: Bool+ , lhs2texFlag :: Bool+ , stdoutFlag :: Bool+ , overwriteFlag :: Bool+ , debugFlag :: Bool+ , memoFlag :: Bool+ , memocleanFlag :: Bool+ , unknownFlags :: [String]+ , inputs :: [FilePath]+ , memoTree :: MemoTree+ }++supportedFlags :: [(String,Conf -> Bool)]+supportedFlags =+ [ ("keep" , keepFlag)+ , ("visible" , visibleFlag)+ , ("verbose" , verboseFlag)+ , ("manual" , manualFlag)+ , ("help" , helpFlag)+ , ("lhs2tex" , lhs2texFlag)+ , ("stdout" , stdoutFlag)+ , ("overwrite" , overwriteFlag)+ , ("debug" , debugFlag)+ , ("memo" , memoFlag)+ , ("memoclean" , memocleanFlag)+ ]++readConf :: [String] -> Conf+readConf = go $ Conf False False False False False False False False False False False [] [] M.empty+ where+ go c [] = c+ go c (x:xs) =+ case x of+ -- Arguments starting with '-' are considered a flag.+ ('-':flag) ->+ case flag of+ "keep" -> go (c {keepFlag = True}) xs+ "visible" -> go (c {visibleFlag = True}) xs+ "verbose" -> go (c {verboseFlag = True}) xs+ "manual" -> go (c {manualFlag = True}) xs+ "help" -> go (c {helpFlag = True}) xs+ "lhs2tex" -> go (c {lhs2texFlag = True}) xs+ "stdout" -> go (c {stdoutFlag = True}) xs+ "overwrite" -> go (c {overwriteFlag = True}) xs+ "debug" -> go (c {debugFlag = True}) xs+ "memo" -> go (c {memoFlag = True}) xs+ "memoclean" -> go (c {memocleanFlag = True}) xs+ _ -> go (c {unknownFlags = unknownFlags c ++ [flag]}) xs+ -- Otherwise, an input file.+ _ -> go (c {inputs = inputs c ++ [x]}) xs++-- Haskintex monad++type Haskintex = StateT Conf IO++outputStr :: String -> Haskintex ()+outputStr str = do+ b <- verboseFlag <$> get+ when b $ lift $ putStrLn str+ -- PARSING -parseSyntax :: Bool -> Parser Syntax-parseSyntax v = do- s <- fmap Sequence $ many $ choice $ fmap try [ p_writehaskell v, p_inserthatex, p_inserthatexio , p_evalhaskell, p_writelatex ]+type Parser = ParsecT Text () Haskintex++parseSyntax :: Parser Syntax+parseSyntax = do+ s <- fmap Sequence $ many $ choice [ p_writehaskell, p_inserthatex False , p_inserthatex True , p_evalhaskell, p_writelatex ] eof return s -p_writehaskell :: Bool -> Parser Syntax-p_writehaskell v = do+p_writehaskell :: Parser Syntax+p_writehaskell = do isH <- (try $ string "\\begin{writehaskell}" >> return False)- <|> (string "\\begin{haskellpragmas}" >> return True)+ <|> (try $ string "\\begin{haskellpragmas}" >> return True) b <- choice $ fmap try [ string "[hidden]" >> return False , string "[visible]" >> return True- , return v ] -- When no option is given, take the default.+ , lift $ visibleFlag <$> get ] -- When no option is given, take the default. h <- manyTill anyChar $ try $ string $ if isH then "\\end{haskellpragmas}" else "\\end{writehaskell}" return $ WriteHaskell b isH $ pack h -p_inserthatex :: Parser Syntax-p_inserthatex = do- _ <- string "\\hatex{"- h <- p_haskell 0- return $ InsertHaTeX $ pack h+readMemo :: Parser Bool+readMemo = (char '[' *> choice xs <* char ']') <|> lift (memoFlag <$> get)+ where+ xs = [ string "memo" >> return True+ , string "notmemo" >> return False ] -p_inserthatexio :: Parser Syntax-p_inserthatexio = do- _ <- string "\\iohatex{"+processExp :: Text -> Text+processExp t =+ case H.parseExp (unpack t) of+ H.ParseOk e -> pack $ H.prettyPrint e + _ -> t++p_inserthatex :: Bool -- False for pure, True for IO+ -> Parser Syntax+p_inserthatex isIO = do+ --+ let iden = if isIO then "iohatex" else "hatex"+ cons = if isIO then InsertHaTeXIO else InsertHaTeX+ --+ _ <- try $ string $ '\\' : iden+ b <- readMemo+ _ <- char '{' h <- p_haskell 0- return $ InsertHaTeXIO $ pack h+ return $ cons b $ processExp $ pack h p_evalhaskell :: Parser Syntax-p_evalhaskell = choice $ fmap try [ p_evalhaskellenv, p_evalhaskellcomm ]+p_evalhaskell = choice [ p_evalhaskellenv, p_evalhaskellcomm ] p_evalhaskellenv :: Parser Syntax p_evalhaskellenv = do- _ <- string "\\begin{evalhaskell}"+ _ <- try $ string "\\begin{evalhaskell}"+ b <- readMemo h <- manyTill anyChar $ try $ string "\\end{evalhaskell}"- return $ EvalHaskell True $ pack h+ return $ EvalHaskell True b $ processExp $ pack h p_evalhaskellcomm :: Parser Syntax p_evalhaskellcomm = do- _ <- string "\\evalhaskell{"+ _ <- try $ string "\\evalhaskell"+ b <- readMemo+ _ <- char '{' h <- p_haskell 0- return $ EvalHaskell False $ pack h+ return $ EvalHaskell False b $ processExp $ pack h p_haskell :: Int -> Parser String p_haskell n = choice [@@ -146,6 +249,146 @@ , return True ] +----------------------------------------------------------+----------------------------------------------------------+-- MEMO TREE++-- | A 'MemoTree' maps each expression to its reduced form.+type MemoTree = M.Map Text Text++memoreduce :: Typeable t+ => String -- ^ Auxiliar module name+ -> Bool -- ^ Is this expression memorized?+ -> Text -- ^ Input+ -> t -- ^ Type+ -> (t -> Haskintex Text) -- ^ Rendering function+ -> Haskintex Text+memoreduce modName isMemo t ty f = do+ let e = unpack t+ outputStr $ "Evaluation (" ++ showsTypeRep (typeRep $ Just t) "" ++ "): " ++ e+ memt <- memoTree <$> get+ let p = if isMemo then M.lookup t memt else Nothing+ case p of+ Nothing -> do+ let int = do+ loadModules [modName]+ setTopLevelModules [modName]+ setImports ["Prelude"]+ interpret e ty+ r <- runInterpreter int+ case r of+ Left err -> do+ outputStr $ "Warning: Error while evaluating the expression.\n"+ ++ errorString err+ return mempty+ Right x -> do+ -- Render result+ t' <- f x+ -- If the expression is marked to be memorized, store it in the 'MemoTree'.+ when isMemo $ do+ modify $ \st -> st { memoTree = M.insert t t' $ memoTree st }+ outputStr $ "-> Result has been memorized."+ -- Return result+ return t'+ Just o -> do+ outputStr "-> Result of the evaluation recovered from memo tree."+ return o++{- Memo Tree Format++A memo tree is stored as a list of (key,value) in key ascending order.+Keys and values are encoded in UTF-8.++| offset | description | size (in bytes) |+---------------------------------------------------+| 00 | Number of blocks | 2 |+| 02 | Zero or more blocks | variable |++Each block has the following structure:++| offset | description | size (in bytes) |+---------------------------------------------------+| 00 | Length of key (k) | 2 |+| 02 | Length of value (v) | 2 |+| 04 | Key | k |+| 04+k | Value | v |++-}++memoTreeToBinary :: MemoTree -> ByteString+memoTreeToBinary memt = runPut $ do+ putWord16le $ fromIntegral $ M.size memt+ mapM_ (\(t,t') -> do+ let b = encodeUtf8 t+ b' = encodeUtf8 t'+ putWord16le $ fromIntegral $ SB.length b+ putWord16le $ fromIntegral $ SB.length b'+ putByteString b+ putByteString b'+ ) $ M.toAscList memt++memoTreeFromBinary :: ByteString -> Either String MemoTree+memoTreeFromBinary b =+ case runGetOrFail getMemoTree b of+ Left (_,_,err) -> Left err+ Right (_,_,memt) -> Right memt++getMemoTree :: Get MemoTree+getMemoTree = do+ n <- fromIntegral <$> getWord16le+ fmap M.fromAscList $ replicateM n $ do+ l <- getWord16le+ l' <- getWord16le+ b <- getByteString $ fromIntegral l+ b' <- getByteString $ fromIntegral l'+ return (decodeUtf8 b, decodeUtf8 b')++memoTreeOpen :: Haskintex ()+memoTreeOpen = do+ d <- liftIO $ getAppUserDataDirectory "haskintex"+ let fp = d </> "memotree"+ b <- liftIO $ doesFileExist fp+ if b then do t <- liftIO $ LB.readFile fp+ case memoTreeFromBinary t of+ Left err -> do+ outputStr $ "Error: memotree failed to read: " ++ err+ outputStr "-> Using empty memotree."+ modify $ \st -> st { memoTree = M.empty }+ Right memt -> do+ modify $ \st -> st { memoTree = memt }+ let n = LB.length t+ kbs :: Double+ kbs = fromIntegral n / 1024+ s = if kbs < 1 then show n ++ " Bs"+ else showFFloat (Just 2) kbs " KBs"+ outputStr $ "Info: memotree loaded (" ++ s ++ ")."+ else do outputStr "Info: memotree does not exist."+ outputStr "-> Using empty memotree."+ modify $ \st -> st { memoTree = M.empty }++memoTreeSave :: Haskintex ()+memoTreeSave = do+ memt <- memoTree <$> get+ unless (M.null memt) $ do+ outputStr "Saving memotree..."+ liftIO $ do+ d <- getAppUserDataDirectory "haskintex"+ createDirectoryIfMissing True d+ let fp = d </> "memotree"+ LB.writeFile fp $ memoTreeToBinary memt+ outputStr "Info: memotree saved."++memoTreeClean :: Haskintex ()+memoTreeClean = do+ d <- liftIO $ getAppUserDataDirectory "haskintex"+ let fp = d </> "memotree"+ b <- liftIO $ doesFileExist fp+ when b $ do + liftIO $ removeFile fp+ outputStr "Info: memotree removed."++----------------------------------------------------------+ -- PASS 1: Extract code from processed Syntax. extractCode :: Syntax -> (Text,Text)@@ -156,70 +399,57 @@ -- PASS 2: Evaluate Haskell expressions from processed Syntax. evalCode :: String -- ^ Auxiliary module name- -> Bool -- ^ Is manual flag on?- -> Bool -- ^ Is lhs2tex flag on? -> Syntax -> Haskintex Text-evalCode modName mFlag lhsFlag = go+evalCode modName = go where go (WriteLaTeX t) = return t- go (WriteHaskell b _ t) =+ go (WriteHaskell b _ t) = do+ mFlag <- manualFlag <$> get+ lhsFlag <- lhs2texFlag <$> get let f :: Text -> LaTeX f x | not b = mempty | mFlag = raw x | lhsFlag = TeXEnv "code" [] $ raw x | otherwise = verbatim x- in return $ render $ f t- go (InsertHaTeX t) = do- let e = unpack $ T.strip t- int = do- loadModules [modName]- setTopLevelModules [modName]- setImports ["Prelude"]- interpret e (as :: LaTeX)- outputStr $ "Evaluation (LaTeX): " ++ e- r <- runInterpreter int- case r of- Left err -> do- outputStr $ "Warning: Error while evaluating the expression.\n"- ++ errorString err- return mempty- Right l -> return $ render l- go (InsertHaTeXIO t) = do- let e = unpack $ T.strip t- int = do- loadModules [modName]- setTopLevelModules [modName]- setImports ["Prelude"]- interpret e (as :: IO LaTeX)- outputStr $ "Evaluation (IO LaTeX): " ++ e- r <- runInterpreter int- case r of- Left err -> do- outputStr $ "Warning: Error while evaluating the expression.\n"- ++ errorString err- return mempty- Right l -> liftIO $ render <$> l- go (EvalHaskell env t) =+ return $ render $ f t+ go (InsertHaTeX isMemo t) = memoreduce modName isMemo t (as :: LaTeX) (return . render)+ go (InsertHaTeXIO isMemo t) = memoreduce modName isMemo t (as :: IO LaTeX) (liftIO . fmap render)+ go (EvalHaskell env isMemo t) = do+ mFlag <- manualFlag <$> get+ lhsFlag <- lhs2texFlag <$> get let f :: Text -> LaTeX f x | mFlag = raw x -- Manual flag overrides lhs2tex flag behavior | env && lhsFlag = TeXEnv "code" [] $ raw x | lhsFlag = raw $ "|" <> x <> "|" | env = verbatim $ layout x | otherwise = verb x- in (render . f . pack) <$> ghc modName t+ (render . f) <$> ghc modName isMemo t go (Sequence xs) = mconcat <$> mapM go xs -ghc :: String -> Text -> Haskintex String-ghc modName e = do- let e' = unpack $ T.strip e- outputStr $ "Evaluation: " ++ e'- lift $ init <$> readProcess "ghc" - -- Disable reading of .ghci files.- [ "-ignore-dot-ghci"- -- Evaluation loading the temporal module.- -- The expression is stripped.- , "-e", e', modName ++ ".hs"- ] []+ghc :: String -> Bool -> Text -> Haskintex Text+ghc modName isMemo t = do+ let e = unpack t+ outputStr $ "Evaluation: " ++ e+ memt <- memoTree <$> get+ let p = if isMemo then M.lookup t memt else Nothing+ case p of+ Nothing -> do+ -- Run GHC externally and read the result.+ r <- lift $ pack . init <$> readProcess "ghc" + -- Disable reading of .ghci files.+ [ "-ignore-dot-ghci"+ -- Evaluation loading the temporal module.+ , "-e", e, modName ++ ".hs"+ ] []+ -- If the expression is marked to be memorized, we do so.+ when isMemo $ do+ modify $ \st -> st { memoTree = M.insert t r $ memoTree st }+ outputStr "-> Result has been memorized."+ -- Return result+ return r+ Just o -> do+ outputStr "-> Result of the evaluation recovered from memo tree."+ return o maxLineLength :: Int maxLineLength = 60@@ -243,78 +473,20 @@ errorString (NotAllowed e) = "Not allowed:" ++ e errorString (GhcException e) = "GHC exception: " ++ e --- Configuration--data Conf = Conf- { keepFlag :: Bool- , visibleFlag :: Bool- , verboseFlag :: Bool- , manualFlag :: Bool- , helpFlag :: Bool- , lhs2texFlag :: Bool- , stdoutFlag :: Bool- , overwriteFlag :: Bool- , debugFlag :: Bool- , unknownFlags :: [String]- , inputs :: [FilePath]- }--supportedFlags :: [(String,Conf -> Bool)]-supportedFlags =- [ ("keep" , keepFlag)- , ("visible" , visibleFlag)- , ("verbose" , verboseFlag)- , ("manual" , manualFlag)- , ("help" , helpFlag)- , ("lhs2tex" , lhs2texFlag)- , ("stdout" , stdoutFlag)- , ("overwrite" , overwriteFlag)- , ("debug" , debugFlag)- ]--readConf :: [String] -> Conf-readConf = go $ Conf False False False False False False False False False [] []- where- go c [] = c- go c (x:xs) =- case x of- -- Arguments starting with '-' are considered a flag.- ('-':flag) ->- case flag of- "keep" -> go (c {keepFlag = True}) xs- "visible" -> go (c {visibleFlag = True}) xs- "verbose" -> go (c {verboseFlag = True}) xs- "manual" -> go (c {manualFlag = True}) xs- "help" -> go (c {helpFlag = True}) xs- "lhs2tex" -> go (c {lhs2texFlag = True}) xs- "stdout" -> go (c {stdoutFlag = True}) xs- "overwrite" -> go (c {overwriteFlag = True}) xs- "debug" -> go (c {debugFlag = True}) xs- _ -> go (c {unknownFlags = unknownFlags c ++ [flag]}) xs- -- Otherwise, an input file.- _ -> go (c {inputs = inputs c ++ [x]}) xs---- Haskintex--type Haskintex = ReaderT Conf IO--outputStr :: String -> Haskintex ()-outputStr str = do- b <- verboseFlag <$> ask- when b $ lift $ putStrLn str+-- Haskintex main function -- | Run haskintex with the given arguments. For example: -- -- > haskintex ["-visible","-overwrite","foo.htex"] -- -- Useful if you want to call /haskintex/ from another program.--- This function does /not/ do any system call.+-- haskintex :: [String] -> IO ()-haskintex = runReaderT haskintexmain . readConf+haskintex = evalStateT haskintexmain . readConf haskintexmain :: Haskintex () haskintexmain = do- flags <- ask+ flags <- get if -- If the help flag is passed, ignore everything else -- and just print the help. helpFlag flags@@ -322,14 +494,17 @@ else let xs = inputs flags in if null xs then lift $ putStr noFiles- else mapM_ haskintexFile xs+ else do memoTreeOpen+ mapM_ haskintexFile xs+ willClean <- memocleanFlag <$> get+ if willClean then memoTreeClean else memoTreeSave commas :: [String] -> String commas = concat . intersperse ", " showEnabledFlags :: Haskintex () showEnabledFlags = do- c <- ask+ c <- get outputStr $ "Enabled flags: " ++ commas (foldr (\(str,f) xs -> if f c then str : xs else xs) [] supportedFlags) ++ "."@@ -337,8 +512,8 @@ reportWarnings :: Haskintex () reportWarnings = do -- Combination of manual and lhs2tex flags.- manFlag <- manualFlag <$> ask- lhsFlag <- lhs2texFlag <$> ask+ manFlag <- manualFlag <$> get+ lhsFlag <- lhs2texFlag <$> get when (manFlag && lhsFlag) $ outputStr "Warning: lhs2tex flag is useless in presence of manual flag." @@ -352,20 +527,18 @@ -- Warnings reportWarnings -- Other unknown flags passed.- uFlags <- unknownFlags <$> ask+ uFlags <- unknownFlags <$> get unless (null uFlags) $ outputStr $ "Unsupported flags: " ++ commas uFlags ++ "." -- File parsing. outputStr $ "Reading " ++ fp ++ "..."- vFlag <- visibleFlag <$> ask t <- lift $ T.readFile fp- -- case parseOnly (parseSyntax vFlag) t of- -- Left err -> outputStr $ "Reading of " ++ fp ++ " failed: " ++ err- case parse (parseSyntax vFlag) fp t of+ pres <- runParserT parseSyntax () fp t+ case pres of Left err -> outputStr $ "Reading of " ++ fp ++ " failed:\n" ++ show err Right s -> do -- Zero pass: In case of debugging, write down the parsed AST.- dbugFlag <- debugFlag <$> ask+ dbugFlag <- debugFlag <$> get when dbugFlag $ do let debugfp = dropExtension (takeFileName fp) ++ ".debughtex" outputStr $ "Writing file " ++ debugfp ++ " with debugging output..."@@ -378,15 +551,13 @@ lift $ T.writeFile (modName ++ ".hs") $ hsH <> moduleHeader <> hs -- Second pass: Evaluate expressions using 'evalCode'. outputStr $ "Evaluating expressions in " ++ fp ++ "..."- mFlag <- manualFlag <$> ask- lhsFlag <- lhs2texFlag <$> ask- l <- evalCode modName mFlag lhsFlag s+ l <- evalCode modName s -- Write final output. let fp' = dropExtension (takeFileName fp) ++ ".tex" writeit = do outputStr $ "Writing final file at " ++ fp' ++ "..." lift $ T.writeFile fp' l- outFlag <- stdoutFlag <$> ask- overFlag <- overwriteFlag <$> ask+ outFlag <- stdoutFlag <$> get+ overFlag <- overwriteFlag <$> get nonew <- lift $ doesFileExist fp' let finalOutput | outFlag = do outputStr "Sending final output to stdout..."@@ -402,7 +573,7 @@ | otherwise = writeit finalOutput -- If the keep flag is not set, remove the haskell source file.- kFlag <- keepFlag <$> ask+ kFlag <- keepFlag <$> get unless kFlag $ do outputStr $ "Removing Haskell source file " ++ modName ++ ".hs " ++ "(use -keep to avoid this)..."@@ -417,6 +588,8 @@ "You are using haskintex version " ++ showVersion version ++ "." , "http://daniel-diaz.github.io/projects/haskintex" , ""+ , "The underlying HaTeX version is " ++ showVersion Hatex.version ++ "."+ , "" , "Usage and flags:" , "Any argument passed to haskintex that starts with '-' will be considered" , "a flag. Otherwise, it will be considered an input file. Every input file"@@ -456,6 +629,13 @@ , " -debug Only for debugging purposes. It writes a file with extension" , " .debughtex with the AST of the internal representation of the" , " input file haskintex uses."+ , ""+ , " -memo Unless otherwise specified, every evalhaskell, hatex or iohatex"+ , " command (or environment) will be called with the memo option."+ , ""+ , " -memoclean Cleans the memo tree after the execution of haskintex. If "+ , " several files are processed, the memo tree will be cleaned"+ , " after processing all of them." , "" , "Any unsupported flag will be ignored." ]
haskintex.cabal view
@@ -1,5 +1,5 @@ name: haskintex-version: 0.4.1.0+version: 0.5.0.0 synopsis: Haskell Evaluation inside of LaTeX code. description: The /haskintex/ (Haskell in LaTeX) program is a tool that reads a LaTeX file and evaluates Haskell expressions contained@@ -37,14 +37,19 @@ library default-language: Haskell2010- build-depends: base == 4.*+ build-depends: base >= 4.7 && < 5 , transformers == 0.3.* , text >= 0.11.2.3 && < 2- , directory >= 1.2.0.0 && < 1.3- , filepath >= 1.1.0.0 && < 1.4- , process- , HaTeX >= 3.9.0.0 && < 4- , parsec >= 3.1.2 && < 3.2- , hint >= 0.3.3.5 && < 0.5+ , bytestring >= 0.10.4+ , directory >= 1.2.0 && < 1.3+ , filepath >= 1.1.0 && < 1.4+ , process >= 1.2.0+ , HaTeX >= 3.9.0.0+ , parsec >= 3.1.2+ , hint >= 0.3.3 && < 0.5+ , containers >= 0.5.5+ , binary >= 0.7.1+ , haskell-src-exts >= 1.15.0 exposed-modules: Haskintex other-modules: Paths_haskintex+ ghc-options: -Wall