-- |
-- Module      :  Cryptol.Parser.NoInclude
-- Copyright   :  (c) 2013-2016 Galois, Inc.
-- License     :  BSD3
-- Maintainer  :  cryptol@galois.com
-- Stability   :  provisional
-- Portability :  portable

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE BlockArguments #-}
module Cryptol.Parser.NoInclude
  ( removeIncludesModule
  , IncludeError(..), ppIncludeError
  ) where

import qualified Control.Applicative as A
import Control.DeepSeq
import qualified Control.Exception as X
import qualified Control.Monad.Fail as Fail

import Data.Set(Set)
import qualified Data.Set as Set
import Data.ByteString (ByteString)
import Data.Either (partitionEithers)
import Data.Text(Text)
import qualified Data.Text.Encoding as T (decodeUtf8')
import Data.Text.Encoding.Error (UnicodeException)
import GHC.Generics (Generic)
import MonadLib
import System.Directory (makeAbsolute)
import System.FilePath (takeDirectory,(</>),isAbsolute)

import Cryptol.Utils.PP hiding ((</>))
import Cryptol.Parser (parseProgramWith)
import Cryptol.Parser.AST
import Cryptol.Parser.LexerUtils (Config(..),defaultConfig)
import Cryptol.Parser.ParserUtils
import Cryptol.Parser.Unlit (guessPreProc)

removeIncludesModule ::
  (FilePath -> IO ByteString) ->
  FilePath ->
  Module PName ->
  IO (Either [IncludeError] (Module PName, Set FilePath))
removeIncludesModule :: (FilePath -> IO ByteString)
-> FilePath
-> Module PName
-> IO (Either [IncludeError] (Module PName, Deps))
removeIncludesModule FilePath -> IO ByteString
reader FilePath
modPath Module PName
m =
  forall a.
(FilePath -> IO ByteString)
-> FilePath -> NoIncM a -> IO (Either [IncludeError] (a, Deps))
runNoIncM FilePath -> IO ByteString
reader FilePath
modPath (forall mname. ModuleG mname PName -> NoIncM (ModuleG mname PName)
noIncludeModule Module PName
m)

data IncludeError
  = IncludeFailed (Located FilePath)
  | IncludeDecodeFailed (Located FilePath) UnicodeException
  | IncludeParseError ParseError
  | IncludeCycle [Located FilePath]
    deriving (Int -> IncludeError -> ShowS
[IncludeError] -> ShowS
IncludeError -> FilePath
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [IncludeError] -> ShowS
$cshowList :: [IncludeError] -> ShowS
show :: IncludeError -> FilePath
$cshow :: IncludeError -> FilePath
showsPrec :: Int -> IncludeError -> ShowS
$cshowsPrec :: Int -> IncludeError -> ShowS
Show, forall x. Rep IncludeError x -> IncludeError
forall x. IncludeError -> Rep IncludeError x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep IncludeError x -> IncludeError
$cfrom :: forall x. IncludeError -> Rep IncludeError x
Generic, IncludeError -> ()
forall a. (a -> ()) -> NFData a
rnf :: IncludeError -> ()
$crnf :: IncludeError -> ()
NFData)

ppIncludeError :: IncludeError -> Doc
ppIncludeError :: IncludeError -> Doc
ppIncludeError IncludeError
ie = case IncludeError
ie of

  IncludeFailed Located FilePath
lp -> (Char -> Doc
char Char
'`' Doc -> Doc -> Doc
<.> FilePath -> Doc
text (forall a. Located a -> a
thing Located FilePath
lp) Doc -> Doc -> Doc
<.> Char -> Doc
char Char
'`')
                  Doc -> Doc -> Doc
<+> FilePath -> Doc
text FilePath
"included at"
                  Doc -> Doc -> Doc
<+> forall a. PP a => a -> Doc
pp (forall a. Located a -> Range
srcRange Located FilePath
lp)
                  Doc -> Doc -> Doc
<+> FilePath -> Doc
text FilePath
"was not found"

  IncludeDecodeFailed Located FilePath
lp UnicodeException
err -> (Char -> Doc
char Char
'`' Doc -> Doc -> Doc
<.> FilePath -> Doc
text (forall a. Located a -> a
thing Located FilePath
lp) Doc -> Doc -> Doc
<.> Char -> Doc
char Char
'`')
                            Doc -> Doc -> Doc
<+> FilePath -> Doc
text FilePath
"included at"
                            Doc -> Doc -> Doc
<+> forall a. PP a => a -> Doc
pp (forall a. Located a -> Range
srcRange Located FilePath
lp)
                            Doc -> Doc -> Doc
<+> FilePath -> Doc
text FilePath
"contains invalid UTF-8."
                            Doc -> Doc -> Doc
<+> FilePath -> Doc
text FilePath
"Details:"
                            Doc -> Doc -> Doc
$$  Int -> Doc -> Doc
nest Int
2 ([Doc] -> Doc
vcat (forall a b. (a -> b) -> [a] -> [b]
map FilePath -> Doc
text (FilePath -> [FilePath]
lines (forall e. Exception e => e -> FilePath
X.displayException UnicodeException
err))))

  IncludeParseError ParseError
pe -> ParseError -> Doc
ppError ParseError
pe

  IncludeCycle [Located FilePath]
is -> FilePath -> Doc
text FilePath
"includes form a cycle:"
                  Doc -> Doc -> Doc
$$ Int -> Doc -> Doc
nest Int
2 ([Doc] -> Doc
vcat (forall a b. (a -> b) -> [a] -> [b]
map (forall a. PP a => a -> Doc
pp forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Located a -> Range
srcRange) [Located FilePath]
is))


newtype NoIncM a = M
  { forall a.
NoIncM a
-> ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
unM :: ReaderT Env
         ( ExceptionT [IncludeError]
         ( StateT Deps
           IO
         )) a }

type Deps = Set FilePath

data Env = Env { Env -> [Located FilePath]
envSeen       :: [Located FilePath]
                 -- ^ Files that have been loaded

               , Env -> FilePath
envIncPath    :: FilePath
                 -- ^ The path that includes are relative to

               , Env -> FilePath -> IO ByteString
envFileReader :: FilePath -> IO ByteString
                 -- ^ How to load files
               }


runNoIncM ::
  (FilePath -> IO ByteString) ->
  FilePath ->
  NoIncM a -> IO (Either [IncludeError] (a,Deps))
runNoIncM :: forall a.
(FilePath -> IO ByteString)
-> FilePath -> NoIncM a -> IO (Either [IncludeError] (a, Deps))
runNoIncM FilePath -> IO ByteString
reader FilePath
sourcePath NoIncM a
m =
  do FilePath
incPath <- FilePath -> IO FilePath
getIncPath FilePath
sourcePath
     (Either [IncludeError] a
mb,Deps
s) <- forall (m :: * -> *) a r. RunM m a r => m a -> r
runM (forall a.
NoIncM a
-> ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
unM NoIncM a
m)
                  Env { envSeen :: [Located FilePath]
envSeen = []
                      , envIncPath :: FilePath
envIncPath = FilePath
incPath
                      , envFileReader :: FilePath -> IO ByteString
envFileReader = FilePath -> IO ByteString
reader
                      }
                  forall a. Set a
Set.empty
     forall (f :: * -> *) a. Applicative f => a -> f a
pure
       do a
ok <- Either [IncludeError] a
mb
          forall (f :: * -> *) a. Applicative f => a -> f a
pure (a
ok,Deps
s)

tryNoIncM :: NoIncM a -> NoIncM (Either [IncludeError] a)
tryNoIncM :: forall a. NoIncM a -> NoIncM (Either [IncludeError] a)
tryNoIncM NoIncM a
m = forall a.
ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
-> NoIncM a
M (forall (m :: * -> *) i a.
RunExceptionM m i =>
m a -> m (Either i a)
try (forall a.
NoIncM a
-> ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
unM NoIncM a
m))

-- | Get the absolute directory name of a file that contains cryptol source.
getIncPath :: FilePath -> IO FilePath
getIncPath :: FilePath -> IO FilePath
getIncPath FilePath
file = FilePath -> IO FilePath
makeAbsolute (ShowS
takeDirectory FilePath
file)

-- | Run a 'NoIncM' action with a different include path.  The argument is
-- expected to be the path of a file that contains cryptol source, and will be
-- adjusted with getIncPath.
withIncPath :: FilePath -> NoIncM a -> NoIncM a
withIncPath :: forall a. FilePath -> NoIncM a -> NoIncM a
withIncPath FilePath
path (M ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
body) = forall a.
ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
-> NoIncM a
M forall a b. (a -> b) -> a -> b
$
  do FilePath
incPath <- forall (m :: * -> *) (n :: * -> *) a. BaseM m n => n a -> m a
inBase (FilePath -> IO FilePath
getIncPath FilePath
path)
     Env
env     <- forall (m :: * -> *) i. ReaderM m i => m i
ask
     forall (m :: * -> *) i a. RunReaderM m i => i -> m a -> m a
local Env
env { envIncPath :: FilePath
envIncPath = FilePath
incPath } ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
body

-- | Adjust an included file with the current include path.
fromIncPath :: FilePath -> NoIncM FilePath
fromIncPath :: FilePath -> NoIncM FilePath
fromIncPath FilePath
path
  | FilePath -> Bool
isAbsolute FilePath
path = forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
path
  | Bool
otherwise       = forall a.
ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
-> NoIncM a
M
    do Env { FilePath
[Located FilePath]
FilePath -> IO ByteString
envFileReader :: FilePath -> IO ByteString
envIncPath :: FilePath
envSeen :: [Located FilePath]
envFileReader :: Env -> FilePath -> IO ByteString
envIncPath :: Env -> FilePath
envSeen :: Env -> [Located FilePath]
.. } <- forall (m :: * -> *) i. ReaderM m i => m i
ask
       forall (m :: * -> *) a. Monad m => a -> m a
return (FilePath
envIncPath FilePath -> ShowS
</> FilePath
path)

addDep :: FilePath -> NoIncM ()
addDep :: FilePath -> NoIncM ()
addDep FilePath
path = forall a.
ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
-> NoIncM a
M
  do Deps
s <- forall (m :: * -> *) i. StateM m i => m i
get
     let s1 :: Deps
s1 = forall a. Ord a => a -> Set a -> Set a
Set.insert FilePath
path Deps
s
     Deps
s1 seq :: forall a b. a -> b -> b
`seq` forall (m :: * -> *) i. StateM m i => i -> m ()
set Deps
s1


instance Functor NoIncM where
  fmap :: forall a b. (a -> b) -> NoIncM a -> NoIncM b
fmap = forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
liftM

instance A.Applicative NoIncM where
  pure :: forall a. a -> NoIncM a
pure a
x = forall a.
ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
-> NoIncM a
M (forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x)
  <*> :: forall a b. NoIncM (a -> b) -> NoIncM a -> NoIncM b
(<*>) = forall (m :: * -> *) a b. Monad m => m (a -> b) -> m a -> m b
ap

instance Monad NoIncM where
  return :: forall a. a -> NoIncM a
return   = forall (f :: * -> *) a. Applicative f => a -> f a
pure
  NoIncM a
m >>= :: forall a b. NoIncM a -> (a -> NoIncM b) -> NoIncM b
>>= a -> NoIncM b
f  = forall a.
ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
-> NoIncM a
M (forall a.
NoIncM a
-> ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
unM NoIncM a
m forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall a.
NoIncM a
-> ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
unM forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> NoIncM b
f)

instance Fail.MonadFail NoIncM where
  fail :: forall a. FilePath -> NoIncM a
fail FilePath
x = forall a.
ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
-> NoIncM a
M (forall (m :: * -> *) a. MonadFail m => FilePath -> m a
fail FilePath
x)

-- | Raise an 'IncludeFailed' error.
includeFailed :: Located FilePath -> NoIncM a
includeFailed :: forall a. Located FilePath -> NoIncM a
includeFailed Located FilePath
path = forall a.
ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
-> NoIncM a
M (forall (m :: * -> *) i a. ExceptionM m i => i -> m a
raise [Located FilePath -> IncludeError
IncludeFailed Located FilePath
path])

-- | Push a path on the stack of included files, and run an action.  If the path
-- is already on the stack, an include cycle has happened, and an error is
-- raised.
pushPath :: Located FilePath -> NoIncM a -> NoIncM a
pushPath :: forall a. Located FilePath -> NoIncM a -> NoIncM a
pushPath Located FilePath
path NoIncM a
m = forall a.
ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
-> NoIncM a
M forall a b. (a -> b) -> a -> b
$ do
  Env { FilePath
[Located FilePath]
FilePath -> IO ByteString
envFileReader :: FilePath -> IO ByteString
envIncPath :: FilePath
envSeen :: [Located FilePath]
envFileReader :: Env -> FilePath -> IO ByteString
envIncPath :: Env -> FilePath
envSeen :: Env -> [Located FilePath]
.. } <- forall (m :: * -> *) i. ReaderM m i => m i
ask
  let alreadyIncluded :: Located FilePath -> Bool
alreadyIncluded Located FilePath
l = forall a. Located a -> a
thing Located FilePath
path forall a. Eq a => a -> a -> Bool
== forall a. Located a -> a
thing Located FilePath
l
  forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Located FilePath -> Bool
alreadyIncluded [Located FilePath]
envSeen) (forall (m :: * -> *) i a. ExceptionM m i => i -> m a
raise [[Located FilePath] -> IncludeError
IncludeCycle [Located FilePath]
envSeen])
  forall (m :: * -> *) i a. RunReaderM m i => i -> m a -> m a
local Env { envSeen :: [Located FilePath]
envSeen = Located FilePath
pathforall a. a -> [a] -> [a]
:[Located FilePath]
envSeen, FilePath
FilePath -> IO ByteString
envFileReader :: FilePath -> IO ByteString
envIncPath :: FilePath
envFileReader :: FilePath -> IO ByteString
envIncPath :: FilePath
.. } (forall a.
NoIncM a
-> ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
unM NoIncM a
m)

-- | Lift an IO operation, with a way to handle the exception that it might
-- throw.
failsWith :: X.Exception e => IO a -> (e -> NoIncM a) -> NoIncM a
failsWith :: forall e a. Exception e => IO a -> (e -> NoIncM a) -> NoIncM a
failsWith IO a
m e -> NoIncM a
k = forall a.
ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
-> NoIncM a
M forall a b. (a -> b) -> a -> b
$ do
  Either e a
e <- forall (m :: * -> *) (n :: * -> *) a. BaseM m n => n a -> m a
inBase (forall e a. Exception e => IO a -> IO (Either e a)
X.try IO a
m)
  case Either e a
e of
    Right a
a  -> forall (m :: * -> *) a. Monad m => a -> m a
return a
a
    Left e
exn -> forall a.
NoIncM a
-> ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
unM (e -> NoIncM a
k e
exn)

-- | Like 'mapM', but tries to collect as many errors as possible before
-- failing.
collectErrors :: (a -> NoIncM b) -> [a] -> NoIncM [b]
collectErrors :: forall a b. (a -> NoIncM b) -> [a] -> NoIncM [b]
collectErrors a -> NoIncM b
f [a]
ts = do
  [Either [IncludeError] b]
es <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (forall a. NoIncM a -> NoIncM (Either [IncludeError] a)
tryNoIncM forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> NoIncM b
f) [a]
ts
  let ([[IncludeError]]
ls,[b]
rs) = forall a b. [Either a b] -> ([a], [b])
partitionEithers [Either [IncludeError] b]
es
      errs :: [IncludeError]
errs    = forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[IncludeError]]
ls
  forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (forall (t :: * -> *) a. Foldable t => t a -> Bool
null [IncludeError]
errs) (forall a.
ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
-> NoIncM a
M (forall (m :: * -> *) i a. ExceptionM m i => i -> m a
raise [IncludeError]
errs))
  forall (m :: * -> *) a. Monad m => a -> m a
return [b]
rs

-- | Remove includes from a module.
noIncludeModule :: ModuleG mname PName -> NoIncM (ModuleG mname PName)
noIncludeModule :: forall mname. ModuleG mname PName -> NoIncM (ModuleG mname PName)
noIncludeModule ModuleG mname PName
m =
  do ModuleDefinition PName
newDef <- case forall mname name. ModuleG mname name -> ModuleDefinition name
mDef ModuleG mname PName
m of
                 NormalModule [TopDecl PName]
ds         -> forall name. [TopDecl name] -> ModuleDefinition name
NormalModule forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [TopDecl PName] -> NoIncM [TopDecl PName]
doDecls [TopDecl PName]
ds
                 FunctorInstance Located (ImpName PName)
f ModuleInstanceArgs PName
as ModuleInstance PName
is -> forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall name.
Located (ImpName name)
-> ModuleInstanceArgs name
-> ModuleInstance name
-> ModuleDefinition name
FunctorInstance Located (ImpName PName)
f ModuleInstanceArgs PName
as ModuleInstance PName
is)
                 InterfaceModule Signature PName
s       -> forall (f :: * -> *) a. Applicative f => a -> f a
pure (forall name. Signature name -> ModuleDefinition name
InterfaceModule Signature PName
s)
     forall (f :: * -> *) a. Applicative f => a -> f a
pure ModuleG mname PName
m { mDef :: ModuleDefinition PName
mDef = ModuleDefinition PName
newDef }
  where
  doDecls :: [TopDecl PName] -> NoIncM [TopDecl PName]
doDecls    = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> NoIncM b) -> [a] -> NoIncM [b]
collectErrors TopDecl PName -> NoIncM [TopDecl PName]
noIncTopDecl

-- | Remove includes from a program.
noIncludeProgram :: Program PName -> NoIncM (Program PName)
noIncludeProgram :: Program PName -> NoIncM (Program PName)
noIncludeProgram (Program [TopDecl PName]
tds) =
  (forall name. [TopDecl name] -> Program name
Program forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` forall a b. (a -> NoIncM b) -> [a] -> NoIncM [b]
collectErrors TopDecl PName -> NoIncM [TopDecl PName]
noIncTopDecl [TopDecl PName]
tds

-- | Substitute top-level includes with the declarations from the files they
-- reference.
noIncTopDecl :: TopDecl PName -> NoIncM [TopDecl PName]
noIncTopDecl :: TopDecl PName -> NoIncM [TopDecl PName]
noIncTopDecl TopDecl PName
td = case TopDecl PName
td of
  Decl TopLevel (Decl PName)
_     -> forall (f :: * -> *) a. Applicative f => a -> f a
pure [TopDecl PName
td]
  DPrimType {} -> forall (f :: * -> *) a. Applicative f => a -> f a
pure [TopDecl PName
td]
  TDNewtype TopLevel (Newtype PName)
_-> forall (f :: * -> *) a. Applicative f => a -> f a
pure [TopDecl PName
td]
  DParamDecl {} -> forall (f :: * -> *) a. Applicative f => a -> f a
pure [TopDecl PName
td]
  DInterfaceConstraint {} -> forall (f :: * -> *) a. Applicative f => a -> f a
pure [TopDecl PName
td]
  Include Located FilePath
lf -> Located FilePath -> NoIncM [TopDecl PName]
resolveInclude Located FilePath
lf
  DModule TopLevel (NestedModule PName)
tl ->
    case forall a. TopLevel a -> a
tlValue TopLevel (NestedModule PName)
tl of
      NestedModule ModuleG PName PName
m ->
        do ModuleG PName PName
m1 <- forall mname. ModuleG mname PName -> NoIncM (ModuleG mname PName)
noIncludeModule ModuleG PName PName
m
           forall (f :: * -> *) a. Applicative f => a -> f a
pure [ forall name. TopLevel (NestedModule name) -> TopDecl name
DModule TopLevel (NestedModule PName)
tl { tlValue :: NestedModule PName
tlValue = forall name. ModuleG name name -> NestedModule name
NestedModule ModuleG PName PName
m1 } ]
  DImport {} -> forall (f :: * -> *) a. Applicative f => a -> f a
pure [TopDecl PName
td]
  DModParam {} -> forall (f :: * -> *) a. Applicative f => a -> f a
pure [TopDecl PName
td]

-- | Resolve the file referenced by a include into a list of top-level
-- declarations.
resolveInclude :: Located FilePath -> NoIncM [TopDecl PName]
resolveInclude :: Located FilePath -> NoIncM [TopDecl PName]
resolveInclude Located FilePath
lf = forall a. Located FilePath -> NoIncM a -> NoIncM a
pushPath Located FilePath
lf forall a b. (a -> b) -> a -> b
$ do
  Text
source <- Located FilePath -> NoIncM Text
readInclude Located FilePath
lf
  let cfg :: Config
cfg = Config
defaultConfig { cfgSource :: FilePath
cfgSource = forall a. Located a -> a
thing Located FilePath
lf
                          , cfgPreProc :: PreProc
cfgPreProc = FilePath -> PreProc
guessPreProc (forall a. Located a -> a
thing Located FilePath
lf)
                          }
  case Config -> Text -> Either ParseError (Program PName)
parseProgramWith Config
cfg Text
source of

    Right Program PName
prog -> do
      Program [TopDecl PName]
ds <-
        do FilePath
path <- FilePath -> NoIncM FilePath
fromIncPath (forall a. Located a -> a
thing Located FilePath
lf)
           forall a. FilePath -> NoIncM a -> NoIncM a
withIncPath FilePath
path (Program PName -> NoIncM (Program PName)
noIncludeProgram Program PName
prog)
      forall (m :: * -> *) a. Monad m => a -> m a
return [TopDecl PName]
ds

    Left ParseError
err -> forall a.
ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
-> NoIncM a
M (forall (m :: * -> *) i a. ExceptionM m i => i -> m a
raise [ParseError -> IncludeError
IncludeParseError ParseError
err])

-- | Read a file referenced by an include.
readInclude :: Located FilePath -> NoIncM Text
readInclude :: Located FilePath -> NoIncM Text
readInclude Located FilePath
path = do
  FilePath -> IO ByteString
readBytes   <- Env -> FilePath -> IO ByteString
envFileReader forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a.
ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
-> NoIncM a
M forall (m :: * -> *) i. ReaderM m i => m i
ask
  FilePath
file        <- FilePath -> NoIncM FilePath
fromIncPath (forall a. Located a -> a
thing Located FilePath
path)
  FilePath -> NoIncM ()
addDep FilePath
file
  ByteString
sourceBytes <- FilePath -> IO ByteString
readBytes FilePath
file forall e a. Exception e => IO a -> (e -> NoIncM a) -> NoIncM a
`failsWith` forall a. IOException -> NoIncM a
handler
  Either UnicodeException Text
sourceText  <- forall a. a -> IO a
X.evaluate (ByteString -> Either UnicodeException Text
T.decodeUtf8' ByteString
sourceBytes) forall e a. Exception e => IO a -> (e -> NoIncM a) -> NoIncM a
`failsWith` forall a. IOException -> NoIncM a
handler
  case Either UnicodeException Text
sourceText of
    Left UnicodeException
encodingErr -> forall a.
ReaderT Env (ExceptionT [IncludeError] (StateT Deps IO)) a
-> NoIncM a
M (forall (m :: * -> *) i a. ExceptionM m i => i -> m a
raise [Located FilePath -> UnicodeException -> IncludeError
IncludeDecodeFailed Located FilePath
path UnicodeException
encodingErr])
    Right Text
txt -> forall (m :: * -> *) a. Monad m => a -> m a
return Text
txt
  where
  handler :: X.IOException -> NoIncM a
  handler :: forall a. IOException -> NoIncM a
handler IOException
_ = forall a. Located FilePath -> NoIncM a
includeFailed Located FilePath
path