packages feed

hoq-0.3: src/File/Load.hs

module File.Load
    ( loadFile
    ) where

import System.IO
import System.FilePath
import Control.Monad
import Control.Monad.Trans
import Control.Monad.State
import Control.Exception
import qualified Data.ByteString as B

import Syntax
import Syntax.Parser
import Syntax.ErrorDoc
import Syntax.PrettyPrinter()
import TypeChecking.Expressions.Utils
import TypeChecking.Definitions
import TypeChecking.Monad

loadFile :: MonadIO m => String -> ScopeT m [(Name,Fixity)]
loadFile filename = do
    ((errs, _), (_, tab)) <- runStateT (runWarnT $ loadFile' [] filename) ([],[])
    liftIO $ mapM_ (\(fn, err) -> hPutStrLn stderr $ erenderWithFilename fn $ errorMsg err) errs
    return tab

loadFile' :: MonadIO m => [String] -> String
    -> WarnT [(String,Error)] (StateT ([String],[(Name,Fixity)]) (ScopeT m)) ()
loadFile' checking filename = do
    mcnt <- liftIO $ fmap Right (B.readFile filename)
                    `catch` \e -> return $ Left $ show (e :: SomeException)
    case mcnt of
        Right cnt -> parseDefs filename checking cnt
        Left err  -> warn [(filename, Error Other $ emsg err enull)]

parseDefs :: MonadIO m => String -> [String] -> B.ByteString
    -> WarnT [(String,Error)] (StateT ([String],[(Name,Fixity)]) (ScopeT m)) ()
parseDefs cur checking s = do
    defs <- mapWarnT (map $ \e -> (cur,e)) (pDefs s)
    defs' <- forM defs $ \def -> case def of
        DefImport moduleName -> do
            let filename = foldr1 combine moduleName <.> "hoq"
            if filename `elem` checking
                then warn [(cur, Error Other $ emsg ("Modules " ++ cur ++ " and " ++ filename ++ " form a cycle") enull)]
                else do
                    (checked,_) <- lift get
                    if filename `elem` checked then return () else loadFile' (cur:checking) filename
            return def
        DefFixity pos inf pr ops -> do
            forM_ ops $ \op -> do
                (fns,tab) <- lift get
                case lookup op tab of
                    Just ft ->
                        let msg = "Multiple fixity declarations for " ++ nameToInfix op ++ " [" ++ show ft ++ "]"
                        in warn [(cur, Error Other $ emsgLC pos msg enull)]
                    Nothing -> return ()
                lift $ put (fns, (op, Infix inf pr):tab)
            return def
        _ -> lift get >>= \(_,tab) -> mapWarnT (map $ \e -> (cur,e)) (fixFixityDef tab def)
    lift $ modify $ \(fns,tab) -> (cur:fns,tab)
    (es,mr) <- lift $ lift $ runWarnT (typeCheckDefs defs')
    let es' = map (\e -> (cur,e)) es
    case mr of
        Nothing -> throwError es'
        Just r -> warn es' >> return r