Newer
Older
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE LambdaCase #-}
import Colog
import Control.Applicative
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Trans.Maybe
import Data.Foldable
import Data.Functor
import Data.Maybe
import Options.Applicative
import System.Directory
import System.Exit
import System.FilePath
import Data.Text qualified as Text
import Data.Map.Strict qualified as Map
import GHC.Stack
import Language.C (CInitializer (CInitExpr))
import System.IO
import System.Process.Typed
import Text.Pretty.Simple
import Text.PrettyPrint qualified as P
import Prelude hiding (log)
. fold
$ []
process :: (WithLog env (Msg sev) m, MonadIO m) => sev -> Text.Text -> m a -> m a
process sev p ma = do
msg "Started "
a <- ma
msg "Done "
pure a
s = withFrozenCallStack callStack
msg t = Colog.logMsg (Msg sev s (t <> p))
run :: (HasCallStack) => Parser (IO ())
run = do
file <- strArgument $ fold [metavar "FILE"]
validity <-
flag False True
$ fold
[ long "validity"
, help "check for validity, throw error if command fails"
]
pure
$ usingLoggerT (cmap fmtMessage logTextStdout)
$ do
let
test f = process D ("test " <> Text.pack f) do
log D (Text.pack $ show err)
pure (err == ExitSuccess)
output f c = process D ("writing " <> Text.pack f) do
let x = P.render (C.pretty (c $> C.undefNode))
liftIO $ writeFile f x
when validity do
liftIO $ copyFile file (file <.> "last")
output f c
t <- test f
if t
else do
liftIO $ when validity do
copyFile file (file <.> "fail")
copyFile (file <.> "last") file
exitFailure
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
let bak = file <.> "bak"
process D "backing up file" do
liftIO $ copyFile file bak
process I "check predicate" do
t <- test file
unless t do
liftIO exitFailure
c <- process D "parsing file" do
parseCFile file
output file c
process I "sanity checks" do
c' <- parseCFile file
unless (void c' == void c) do
liftIO do
withFile "error.1.hs" WriteMode (`pHPrint` void c)
withFile "error.2.hs" WriteMode (`pHPrint` void c')
logError "Outputted a different file than i read... Please report original file and error.{1,2}.hs"
liftIO exitFailure
t <- test file
unless t do
liftIO exitFailure
l <-
reduceI
(check' file)
(Map.singleton (C.internalIdent "main") True)
(reduceC c)
where
parseCFile file = do
res <- liftIO $ C.parseCFilePre file
case res of
Right c -> pure c
Left err -> do
logError (Text.pack (show err))
liftIO exitFailure
reduceC :: (MonadReduce Lab m) => C.CTranslUnit -> m C.CTranslUnit
reduceC (C.CTranslUnit es ni) = do
es' <- collect mrCExternalDeclaration es
pure $ C.CTranslUnit es' ni
mrCExternalDeclaration :: (MonadReduce Lab m) => C.CExternalDeclaration C.NodeInfo -> MaybeT m (C.CExternalDeclaration C.NodeInfo)
mrCExternalDeclaration = \case
C.CFDefExt fun -> do
givenWith (funName fun)
C.CFDefExt <$> rCFunctionDef fun
C.CDeclExt decl ->
C.CDeclExt <$> mrCDeclaration decl
a -> error (show a)
where
funName (C.CFunDef _ (C.CDeclr x _ _ _ _) _ _ _) =
x
mrCDeclarationItem :: (MonadReduce Lab m) => C.CDeclarationItem C.NodeInfo -> MaybeT m (C.CDeclarationItem C.NodeInfo)
mrCDeclarationItem = \case
C.CDeclarationItem d@(C.CDeclr x _ _ _ _) i e -> do
givenWith x
i' <- mtry $ munder i mrCInitializer
e' <- mtry $ munder e mrCExpression
pure (C.CDeclarationItem d i' e')
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
a -> error (show a)
mrCInitializer :: (MonadReduce Lab m) => C.CInitializer C.NodeInfo -> MaybeT m (C.CInitializer C.NodeInfo)
mrCInitializer = \case
C.CInitExpr e ni -> mrCExpression e <&> \e' -> CInitExpr e' ni
C.CInitList (C.CInitializerList items) ni -> do
collectNonEmpty' rmCInitializerListItem items <&> \items' ->
C.CInitList (C.CInitializerList items') ni
where
rmCInitializerListItem (pds, is) = do
pds' <- lift $ collect rmCPartDesignator pds
is' <- mrCInitializer is
pure (pds', is')
rmCPartDesignator :: (MonadReduce Lab m) => C.CPartDesignator C.NodeInfo -> m (C.CPartDesignator C.NodeInfo)
rmCPartDesignator = \case
a -> error (show a)
mrCDeclaration :: (MonadReduce Lab m) => C.CDeclaration C.NodeInfo -> MaybeT m (C.CDeclaration C.NodeInfo)
mrCDeclaration = \case
C.CDecl spc decl ni -> do
decl' <- lift $ collect mrCDeclarationItem decl
case decl' of
[] -> empty
decl'' -> pure $ C.CDecl spc decl'' ni
a -> error (show a)
rCFunctionDef :: (MonadReduce Lab m) => C.CFunctionDef C.NodeInfo -> m (C.CFunctionDef C.NodeInfo)
rCFunctionDef (C.CFunDef spc dec cdecls smt ni) = do
rCStatement :: (MonadReduce Lab m) => C.CStatement C.NodeInfo -> m (C.CStatement C.NodeInfo)
C.CCompound is cbi ni -> do
cbi' <- collect mrCCompoundBlockItem cbi
pure $ C.CCompound is cbi' ni
C.CExpr e ni -> do
pure $ C.CExpr e' ni
C.CIf e s els ni -> do
e' <- runMaybeT $ mrCExpression e
s' <- rCStatement s
els' <- case els of
Just els' -> do
pure Nothing <| Just <$> rCStatement els'
Nothing -> pure Nothing
case (e', els') of
(Nothing, Nothing) -> pure s'
(Just e'', Nothing) -> pure $ C.CIf e'' s' Nothing ni
(Nothing, Just x) -> pure $ C.CIf zeroExp s' (Just x) ni
(Just e'', Just x) -> pure $ C.CIf e'' s' (Just x) ni
C.CFor e1 e2 e3 s ni -> do
rCStatement s <| do
e1' <- rCForInit e1
e2' <- runMaybeT $ munder e2 mrCExpression
e3' <- runMaybeT $ munder e3 mrCExpression
s' <- rCStatement s
pure $ C.CFor e1' e2' e3' s' ni
C.CReturn e ni -> do
e' <- case e of
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
Just e' -> Just <$> zrCExpression e'
pure $ C.CReturn e' ni
C.CBreak ni -> pure (C.CBreak ni)
C.CCont ni -> pure (C.CCont ni)
C.CLabel i s [] ni ->
-- todo fix attrs
splitOn i (rCStatement s) do
s' <- rCStatement s
pure $ C.CLabel i s' [] ni
C.CGoto i ni ->
-- todo fix attrs
splitOn i (pure $ C.CExpr Nothing ni) do
pure $ C.CGoto i ni
a -> error (show a)
where
rCForInit = \case
C.CForDecl decl -> do
m <- runMaybeT $ mrCDeclaration decl
pure $ case m of
Nothing -> C.CForInitializing Nothing
Just d' -> C.CForDecl d'
C.CForInitializing n -> do
C.CForInitializing <$> runMaybeT (munder n mrCExpression)
orZero :: Maybe (C.CExpression C.NodeInfo) -> C.CExpression C.NodeInfo
orZero = fromMaybe zeroExp
zeroExp :: C.CExpression C.NodeInfo
zeroExp = C.CConst (C.CIntConst (C.cInteger 0) C.undefNode)
zrCExpression :: (MonadReduce Lab m) => C.CExpression C.NodeInfo -> m (C.CExpression C.NodeInfo)
zrCExpression e = orZero <$> runMaybeT (mrCExpression e)
mrCExpression :: (MonadReduce Lab m) => C.CExpression C.NodeInfo -> MaybeT m (C.CExpression C.NodeInfo)
mrCExpression = \case
C.CCall e es ni -> do
e' <- mrCExpression e
es' <- lift $ traverse zrCExpression es
pure $ C.CCall e' es' ni
C.CCond ec et ef ni -> do
ec' <- mrCExpression ec
ef' <- mrCExpression ef
et' <- mtry $ munder et mrCExpression
pure $ C.CCond ec' et' ef' ni
C.CBinary o elhs erhs ni -> onBothExpr elhs erhs \lhs rhs ->
pure $ C.CBinary o lhs rhs ni
C.CUnary o elhs ni -> do
lhs <- mrCExpression elhs
pure $ C.CUnary o lhs ni
C.CConst c -> do
-- TODO fix
pure $ C.CConst c
C.CCast cd e ni -> do
-- TODO fix
cd' <- mrCDeclaration cd
e' <- mrCExpression e
pure $ C.CCast cd' e' ni
givenThat i
e' <- mrCExpression e
pure $ C.CMember e' i b ni
C.CComma items ni -> do
C.CComma <$> collectNonEmpty' mrCExpression items <*> pure ni
e -> error (show e)
where
onBothExpr elhs erhs = onBoth (mrCExpression elhs) (mrCExpression erhs)
mtry :: (Functor m) => MaybeT m a -> MaybeT m (Maybe a)
mtry (MaybeT mt) = MaybeT (Just <$> mt)
mlift :: (Applicative m) => Maybe a -> MaybeT m a
mlift a = MaybeT (pure a)
munder :: (Monad m) => Maybe a -> (a -> MaybeT m b) -> MaybeT m b
munder a mf = mlift a >>= mf
mrCCompoundBlockItem :: (MonadReduce Lab m) => C.CCompoundBlockItem C.NodeInfo -> MaybeT m (C.CCompoundBlockItem C.NodeInfo)
mrCCompoundBlockItem = \case
C.CBlockStmt s -> empty <| lift (C.CBlockStmt <$> rCStatement s)
C.CBlockDecl d -> C.CBlockDecl <$> mrCDeclaration d
a -> error (show a)