Added a Warn monad for warnings, and incorporated a WriterT monad into the PassM stack to support the Warn monad, then changed all the rest of the code accordingly, including adding a Warn instance for the GenParser parser that hides it in the state

This commit is contained in:
Neil Brown 2008-02-08 13:22:23 +00:00
parent 95cdb39789
commit f17ff5071c
8 changed files with 81 additions and 48 deletions

13
Main.hs
View File

@ -22,6 +22,7 @@ module Main (main) where
import Control.Monad.Error import Control.Monad.Error
import Control.Monad.Identity import Control.Monad.Identity
import Control.Monad.State import Control.Monad.State
import Control.Monad.Writer
import Data.Either import Data.Either
import Data.Generics import Data.Generics
import Data.Maybe import Data.Maybe
@ -145,10 +146,10 @@ main = do
ModeFull -> evalStateT (compileFull fn) [] ModeFull -> evalStateT (compileFull fn) []
-- Run the compiler. -- Run the compiler.
v <- evalStateT (runErrorT operation) initState v <- runWriterT $ evalStateT (runErrorT operation) initState
case v of case v of
Left e -> dieIO e (Left e, ws) -> showWarnings ws >> dieIO e
Right r -> return () (Right r, ws) -> showWarnings ws
removeFiles :: [FilePath] -> IO () removeFiles :: [FilePath] -> IO ()
removeFiles = mapM_ (\file -> catch (removeFile file) doNothing) removeFiles = mapM_ (\file -> catch (removeFile file) doNothing)
@ -252,8 +253,6 @@ compile mode fn outHandle
debugAST ast1 debugAST ast1
debug "}}}" debug "}}}"
showWarnings
output <- output <-
case mode of case mode of
ModeParse -> return $ pshow ast1 ModeParse -> return $ pshow ast1
@ -290,8 +289,6 @@ compile mode fn outHandle
return code return code
showWarnings
liftIO $ hPutStr outHandle output liftIO $ hPutStr outHandle output
progress "Done" progress "Done"
@ -304,7 +301,5 @@ postCAnalyse fn outHandle
progress "Analysing assembly" progress "Analysing assembly"
output <- analyseAsm asm output <- analyseAsm asm
showWarnings
liftIO $ hPutStr outHandle output liftIO $ hPutStr outHandle output

View File

@ -49,6 +49,7 @@ import GenerateCBased
import GenerateCPPCSP import GenerateCPPCSP
import Metadata import Metadata
import TestUtils import TestUtils
import Utils
-- | A few helper functions for writing certain characters (that won't appear in our generated C/C++ source) -- | A few helper functions for writing certain characters (that won't appear in our generated C/C++ source)
-- to the WriterT monad. Useful as simple placeholders/special values during testers. -- to the WriterT monad. Useful as simple placeholders/special values during testers.
@ -105,7 +106,10 @@ assertGenFail n act
else assertFailure $ n ++ " pass succeeded when expected to fail, output: " ++ (subRegex (mkRegex "/\\*\\*/") (concat ss) "") else assertFailure $ n ++ " pass succeeded when expected to fail, output: " ++ (subRegex (mkRegex "/\\*\\*/") (concat ss) "")
evalCGen :: CGen () -> GenOps -> CompState -> IO (Either Errors.ErrorReport [String]) evalCGen :: CGen () -> GenOps -> CompState -> IO (Either Errors.ErrorReport [String])
evalCGen act ops state = evalStateT (runErrorT $ execWriterT $ runReaderT act ops) state evalCGen act ops state = evalCGen' (runReaderT act ops) state
evalCGen' :: CGen' () -> CompState -> IO (Either Errors.ErrorReport [String])
evalCGen' act state = runWriterT (evalStateT (runErrorT $ execWriterT act) state) >>* fst
-- | Checks that running the test for the C and C++ backends produces the right output for each. -- | Checks that running the test for the C and C++ backends produces the right output for each.
testBothS :: testBothS ::
@ -132,7 +136,7 @@ testBothFailS testName act startState = TestList
-- | Checks that the given output of a backend satisfies the given regex, and returns the matched groups. -- | Checks that the given output of a backend satisfies the given regex, and returns the matched groups.
testRS :: String -> String -> CGen' () -> State CompState () -> IO [String] testRS :: String -> String -> CGen' () -> State CompState () -> IO [String]
testRS testName exp act startState = assertGenR testName exp (evalStateT (runErrorT (execWriterT act)) state) testRS testName exp act startState = assertGenR testName exp (evalCGen' act state)
where where
state = execState startState emptyState state = execState startState emptyState
@ -1096,7 +1100,7 @@ testMobile :: Test
testMobile = TestList testMobile = TestList
[ [
testBoth "testMobile 0" "malloc(#(Int Left False))" "new Int" (local over (tcall3 genAllocMobile emptyMeta (A.Mobile A.Int) Nothing)) testBoth "testMobile 0" "malloc(#(Int Left False))" "new Int" (local over (tcall3 genAllocMobile emptyMeta (A.Mobile A.Int) Nothing))
,TestCase $ assertGen "testMobile 1/C++" "new Int($)" $ (evalStateT (runErrorT (execWriterT $ flip runReaderT (over cppgenOps) $ call genAllocMobile emptyMeta (A.Mobile A.Int) (Just undefined))) emptyState) ,TestCase $ assertGen "testMobile 1/C++" "new Int($)" $ (evalCGen (call genAllocMobile emptyMeta (A.Mobile A.Int) (Just undefined)) (over cppgenOps) emptyState)
,testBoth "testMobile 100" "if(@!=NULL){free(@);@=NULL;}" "if(@!=NULL){delete @;@=NULL;}" ,testBoth "testMobile 100" "if(@!=NULL){free(@);@=NULL;}" "if(@!=NULL){delete @;@=NULL;}"
(local over (tcall2 genClearMobile emptyMeta undefined)) (local over (tcall2 genClearMobile emptyMeta undefined))

View File

@ -72,7 +72,6 @@ data CompState = CompState {
csUnscopedNames :: Map String String, csUnscopedNames :: Map String String,
csNameCounter :: Int, csNameCounter :: Int,
csTypeContext :: [Maybe A.Type], csTypeContext :: [Maybe A.Type],
csWarnings :: [String],
-- Set by passes -- Set by passes
csNonceCounter :: Int, csNonceCounter :: Int,
@ -103,7 +102,6 @@ emptyState = CompState {
csUnscopedNames = Map.empty, csUnscopedNames = Map.empty,
csNameCounter = 0, csNameCounter = 0,
csTypeContext = [], csTypeContext = [],
csWarnings = [],
csNonceCounter = 0, csNonceCounter = 0,
csFunctionReturns = Map.empty, csFunctionReturns = Map.empty,
@ -146,6 +144,14 @@ instance (CSMR m, Error e) => CSMR (ErrorT e m) where
instance (CSMR m, Monoid w) => CSMR (WriterT w m) where instance (CSMR m, Monoid w) => CSMR (WriterT w m) where
getCompState = lift getCompState getCompState = lift getCompState
type WarningReport = (Maybe Meta, String)
class Monad m => Warn m where
warnReport :: WarningReport -> m ()
--instance (MonadWriter [WarningReport] m) => Warn m where
-- warnReport r = tell [r]
--{{{ name definitions --{{{ name definitions
-- | Add the definition of a name. -- | Add the definition of a name.
defineName :: CSM m => A.Name -> A.NameDef -> m () defineName :: CSM m => A.Name -> A.NameDef -> m ()
@ -167,12 +173,14 @@ lookupNameOrError n err
--{{{ warnings --{{{ warnings
-- | Add a warning with no source position. -- | Add a warning with no source position.
addPlainWarning :: CSM m => String -> m () addPlainWarning :: Warn m => String -> m ()
addPlainWarning msg = modify (\ps -> ps { csWarnings = msg : csWarnings ps }) addPlainWarning msg = warnReport (Nothing, msg)
-- modify (\ps -> ps { csWarnings = msg : csWarnings ps })
-- | Add a warning. -- | Add a warning.
addWarning :: CSM m => Meta -> String -> m () addWarning :: Warn m => Meta -> String -> m ()
addWarning m s = addPlainWarning $ "Warning: " ++ show m ++ ": " ++ s addWarning m s = warnReport (Just m, s)
-- addPlainWarning $ "Warning: " ++ show m ++ ": " ++ s
--}}} --}}}
--{{{ pulled items --{{{ pulled items

View File

@ -22,6 +22,7 @@ module Pass where
import Control.Monad.Error import Control.Monad.Error
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.State import Control.Monad.State
import Control.Monad.Writer
import Data.Generics import Data.Generics
import Data.List import Data.List
import System.IO import System.IO
@ -34,8 +35,8 @@ import PrettyShow
import TreeUtils import TreeUtils
-- | The monad in which AST-mangling passes operate. -- | The monad in which AST-mangling passes operate.
type PassM = ErrorT ErrorReport (StateT CompState IO) type PassM = ErrorT ErrorReport (StateT CompState (WriterT [WarningReport] IO))
type PassMR = ErrorT ErrorReport (ReaderT CompState IO) type PassMR = ErrorT ErrorReport (ReaderT CompState (WriterT [WarningReport] IO))
instance Die PassM where instance Die PassM where
dieReport = throwError dieReport = throwError
@ -43,15 +44,21 @@ instance Die PassM where
instance Die PassMR where instance Die PassMR where
dieReport = throwError dieReport = throwError
instance Warn PassM where
warnReport w = tell [w]
instance Warn PassMR where
warnReport w = tell [w]
-- | The type of an AST-mangling pass. -- | The type of an AST-mangling pass.
type Pass = A.AST -> PassM A.AST type Pass = A.AST -> PassM A.AST
runPassR :: PassMR a -> PassM a runPassR :: PassMR a -> PassM a
runPassR p = do st <- get runPassR p = do st <- get
r <- liftIO $ runReaderT (runErrorT p) st (r,w) <- liftIO $ runWriterT $ runReaderT (runErrorT p) st
case r of case r of
Left err -> throwError err Left err -> throwError err
Right result -> return result Right result -> tell w >> return result
-- | Compose a list of passes into a single pass. -- | Compose a list of passes into a single pass.
runPasses :: [(String, Pass)] -> Pass runPasses :: [(String, Pass)] -> Pass
@ -71,16 +78,17 @@ verboseMessage n s
when (csVerboseLevel ps >= n) $ when (csVerboseLevel ps >= n) $
liftIO $ hPutStrLn stderr s liftIO $ hPutStrLn stderr s
{-
-- | Print a warning message. -- | Print a warning message.
warn :: (CSM m, MonadIO m) => String -> m () warn :: (CSM m, MonadIO m) => String -> m ()
warn = verboseMessage 0 warn = verboseMessage 0
-}
-- | Print out any warnings stored. -- | Print out a list of warnings
showWarnings :: (CSM m, MonadIO m) => m () showWarnings :: MonadIO m => [WarningReport] -> m ()
showWarnings showWarnings = mapM_ printWarning
= do ps <- get where
sequence_ $ map warn (reverse $ csWarnings ps) printWarning (Just m, s) = liftIO $ hPutStrLn stderr $ show m ++ " " ++ s
put $ ps { csWarnings = [] } printWarning (Nothing, s) = liftIO $ hPutStrLn stderr s
-- | Print a progress message. -- | Print a progress message.
progress :: (CSM m, MonadIO m) => String -> m () progress :: (CSM m, MonadIO m) => String -> m ()

View File

@ -35,6 +35,7 @@ module TestHarness (automaticTest, automaticTimeTest) where
import Control.Monad.Error import Control.Monad.Error
import Control.Monad.State import Control.Monad.State
import Control.Monad.Writer
import Data.List import Data.List
import Data.Maybe import Data.Maybe
import System.IO import System.IO
@ -62,7 +63,7 @@ defaultState = emptyState {csUsageChecking = True}
-- | Tests if compiling the given source gives any errors. -- | Tests if compiling the given source gives any errors.
-- If there are errors, they are returned. Upon success, Nothing is returned -- If there are errors, they are returned. Upon success, Nothing is returned
testOccam :: String -> IO (Maybe String) testOccam :: String -> IO (Maybe String)
testOccam source = do result <- evalStateT (runErrorT compilation) defaultState testOccam source = do (result,_) <- runWriterT $ evalStateT (runErrorT compilation) defaultState
return $ case result of return $ case result of
Left (_,err) -> Just err Left (_,err) -> Just err
Right _ -> Nothing Right _ -> Nothing

View File

@ -40,6 +40,7 @@ module TestUtils where
import Control.Monad.Error import Control.Monad.Error
import Control.Monad.State import Control.Monad.State
import Control.Monad.Writer
import Data.Generics import Data.Generics
import qualified Data.Map as Map import qualified Data.Map as Map
import System.Time import System.Time
@ -322,7 +323,7 @@ runPass ::
PassM b -- ^ The actual pass. PassM b -- ^ The actual pass.
-> CompState -- ^ The state to use to run the pass. -> CompState -- ^ The state to use to run the pass.
-> IO (CompState, Either ErrorReport b) -- ^ The resultant state, and either an error or the successful outcome of the pass. -> IO (CompState, Either ErrorReport b) -- ^ The resultant state, and either an error or the successful outcome of the pass.
runPass actualPass startState = (liftM (\(x,y) -> (y,x))) (runStateT (runErrorT actualPass) startState) runPass actualPass startState = (liftM (\((x,y),_) -> (y,x))) (runWriterT $ runStateT (runErrorT actualPass) startState)
-- | A test that runs a given AST pass and checks that it succeeds. -- | A test that runs a given AST pass and checks that it succeeds.
testPass :: testPass ::

View File

@ -21,6 +21,7 @@ module ParseOccam (parseOccamProgram) where
import Control.Monad (liftM, when) import Control.Monad (liftM, when)
import Control.Monad.State (MonadState, modify, get, put) import Control.Monad.State (MonadState, modify, get, put)
import Control.Monad.Writer (tell)
import Data.List import Data.List
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Maybe import Data.Maybe
@ -41,17 +42,29 @@ import Types
import Utils import Utils
--{{{ the parser monad --{{{ the parser monad
type OccParser = GenParser Token CompState type OccParser = GenParser Token ([WarningReport], CompState)
-- | Make MonadState functions work in the parser monad. -- | Make MonadState functions work in the parser monad.
-- This came from <http://hackage.haskell.org/trac/ghc/ticket/1274> -- which means -- This came from <http://hackage.haskell.org/trac/ghc/ticket/1274> -- which means
-- it'll probably be in a future GHC release anyway. -- it'll probably be in a future GHC release anyway.
{-
instance MonadState st (GenParser tok st) where instance MonadState st (GenParser tok st) where
get = getState get = getState
put = setState put = setState
-}
instance CSMR (GenParser tok (a,CompState)) where
getCompState = getState >>* snd
instance CSMR (GenParser tok CompState) where -- We can expose only part of the state to make it look like we are only using
getCompState = getState -- CompState:
instance MonadState CompState (GenParser tok (a,CompState)) where
get = getState >>* snd
put st = do (other, _) <- getState
setState (other, st)
instance Warn (GenParser tok ([WarningReport], b)) where
warnReport w = do (ws, other) <- getState
setState (ws ++ [w], other)
instance Die (GenParser tok st) where instance Die (GenParser tok st) where
dieReport (Just m, err) = fail $ packMeta m err dieReport (Just m, err) = fail $ packMeta m err
@ -410,7 +423,7 @@ noTypeContext = inTypeContext Nothing
--{{{ name scoping --{{{ name scoping
findName :: A.Name -> OccParser A.Name findName :: A.Name -> OccParser A.Name
findName thisN findName thisN
= do st <- getState = do st <- get
origN <- case lookup (A.nameName thisN) (csLocalNames st) of origN <- case lookup (A.nameName thisN) (csLocalNames st) of
Nothing -> dieP (A.nameMeta thisN) $ "name " ++ A.nameName thisN ++ " not defined" Nothing -> dieP (A.nameMeta thisN) $ "name " ++ A.nameName thisN ++ " not defined"
Just n -> return n Just n -> return n
@ -420,13 +433,13 @@ findName thisN
makeUniqueName :: String -> OccParser String makeUniqueName :: String -> OccParser String
makeUniqueName s makeUniqueName s
= do st <- getState = do st <- get
setState $ st { csNameCounter = csNameCounter st + 1 } put $ st { csNameCounter = csNameCounter st + 1 }
return $ s ++ "_u" ++ show (csNameCounter st) return $ s ++ "_u" ++ show (csNameCounter st)
findUnscopedName :: A.Name -> OccParser A.Name findUnscopedName :: A.Name -> OccParser A.Name
findUnscopedName n@(A.Name m nt s) findUnscopedName n@(A.Name m nt s)
= do st <- getState = do st <- get
case Map.lookup s (csUnscopedNames st) of case Map.lookup s (csUnscopedNames st) of
Just s' -> return $ A.Name m nt s' Just s' -> return $ A.Name m nt s'
Nothing -> Nothing ->
@ -456,11 +469,11 @@ scopeIn n@(A.Name m nt s) t am
scopeOut :: A.Name -> OccParser () scopeOut :: A.Name -> OccParser ()
scopeOut n@(A.Name m nt s) scopeOut n@(A.Name m nt s)
= do st <- getState = do st <- get
let lns' = case csLocalNames st of let lns' = case csLocalNames st of
(s, _):ns -> ns (s, _):ns -> ns
otherwise -> dieInternal (Just m, "scopeOut trying to scope out the wrong name") otherwise -> dieInternal (Just m, "scopeOut trying to scope out the wrong name")
setState $ st { csLocalNames = lns' } put $ st { csLocalNames = lns' }
-- FIXME: Do these with generics? (going carefully to avoid nested code blocks) -- FIXME: Do these with generics? (going carefully to avoid nested code blocks)
scopeInRep :: A.Replicator -> OccParser A.Replicator scopeInRep :: A.Replicator -> OccParser A.Replicator
@ -1979,7 +1992,7 @@ topLevelItem = handleSpecs (allocation <|> specification) topLevelItem
-- Stash the current locals so that we can either restore them -- Stash the current locals so that we can either restore them
-- when we get back to the file we included this one from, or -- when we get back to the file we included this one from, or
-- pull the TLP name from them at the end. -- pull the TLP name from them at the end.
updateState $ (\ps -> ps { csMainLocals = csLocalNames ps }) modify $ (\ps -> ps { csMainLocals = csLocalNames ps })
return $ A.Several m [] return $ A.Several m []
@ -1987,11 +2000,11 @@ topLevelItem = handleSpecs (allocation <|> specification) topLevelItem
-- A source file is really a series of specifications, but the later ones need to -- A source file is really a series of specifications, but the later ones need to
-- have the earlier ones in scope, so we can't parse them separately. -- have the earlier ones in scope, so we can't parse them separately.
-- Instead, we nest the specifications -- Instead, we nest the specifications
sourceFile :: OccParser (A.AST, CompState) sourceFile :: OccParser (A.AST, [WarningReport], CompState)
sourceFile sourceFile
= do p <- topLevelItem = do p <- topLevelItem
s <- getState (w, s) <- getState
return (p, s) return (p, w, s)
--}}} --}}}
--}}} --}}}
@ -1999,7 +2012,7 @@ sourceFile
-- | Parse a token stream with the given production. -- | Parse a token stream with the given production.
runTockParser :: [Token] -> OccParser t -> CompState -> PassM t runTockParser :: [Token] -> OccParser t -> CompState -> PassM t
runTockParser toks prod cs runTockParser toks prod cs
= do case runParser prod cs "" toks of = do case runParser prod ([], cs) "" toks of
Left err -> dieReport (Nothing, "Parse error: " ++ show err) Left err -> dieReport (Nothing, "Parse error: " ++ show err)
Right r -> return r Right r -> return r
@ -2007,8 +2020,9 @@ runTockParser toks prod cs
parseOccamProgram :: [Token] -> PassM A.AST parseOccamProgram :: [Token] -> PassM A.AST
parseOccamProgram toks parseOccamProgram toks
= do cs <- get = do cs <- get
(p, cs') <- runTockParser toks sourceFile cs (p, ws, cs') <- runTockParser toks sourceFile cs
put cs' put cs'
tell ws
return p return p
--}}} --}}}

View File

@ -21,6 +21,7 @@ module RainTypesTest where
import Control.Monad.State import Control.Monad.State
import Control.Monad.Error import Control.Monad.Error
import Control.Monad.Writer
import Data.Generics import Data.Generics
import Test.HUnit hiding (State) import Test.HUnit hiding (State)
@ -34,6 +35,7 @@ import TagAST
import TestUtils import TestUtils
import TreeUtils import TreeUtils
import Types import Types
import Utils
-- | Tests that constants in expressions are folded properly. TODO these tests could do with a lot of expanding. -- | Tests that constants in expressions are folded properly. TODO these tests could do with a lot of expanding.
-- It may even be easiest to use QuickCheck for the testing. -- It may even be easiest to use QuickCheck for the testing.
@ -379,7 +381,7 @@ checkExpressionTest = TestList
if (e /= act) then pass' (10000 + n) t (mkPattern e) e else return () if (e /= act) then pass' (10000 + n) t (mkPattern e) e else return ()
where where
errorOrType :: IO (Either ErrorReport A.Type) errorOrType :: IO (Either ErrorReport A.Type)
errorOrType = evalStateT (runErrorT $ typeOfExpression e) (execState state emptyState) errorOrType = ((runWriterT (evalStateT (runErrorT $ typeOfExpression e) (execState state emptyState))) :: IO (Either ErrorReport A.Type, [WarningReport])) >>* fst
fail :: Int -> ExprHelper -> Test fail :: Int -> ExprHelper -> Test