From ad637237edac75aaea6db3d6a800b5db5b6bb4a9 Mon Sep 17 00:00:00 2001 From: Christian Gram Kalhauge <chrg@dtu.dk> Date: Thu, 22 Feb 2024 08:50:09 +0100 Subject: [PATCH] Add cases --- flake.lock | 2 +- rtree-c/src/ReduceC.hs | 274 +++++++++++++----- rtree-c/test/cases/main.c | 1 - rtree-c/test/cases/while-loops.c | 6 + rtree-c/test/expected/main/main.c | 1 - rtree-c/test/expected/main/reduction/r.c | 1 - .../test/expected/main/reduction/r.choices | 0 .../typedef/reduction/{r00.c => r000.c} | 0 .../expected/typedef/reduction/r000.choices | 3 + .../test/expected/typedef/reduction/r001.c | 8 + .../expected/typedef/reduction/r001.choices | 3 + .../typedef/reduction/{r01.c => r010.c} | 0 .../expected/typedef/reduction/r010.choices | 3 + .../test/expected/typedef/reduction/r011.c | 5 + .../expected/typedef/reduction/r011.choices | 3 + .../typedef/reduction/{r10.c => r100.c} | 0 .../expected/typedef/reduction/r100.choices | 3 + .../test/expected/typedef/reduction/r101.c | 7 + .../expected/typedef/reduction/r101.choices | 3 + .../typedef/reduction/{r11.c => r110.c} | 0 .../expected/typedef/reduction/r110.choices | 3 + .../test/expected/typedef/reduction/r111.c | 4 + .../expected/typedef/reduction/r111.choices | 3 + rtree-c/test/expected/while-loops/main.c | 8 + .../test/expected/while-loops/reduction/r00.c | 8 + .../while-loops/reduction/r00.choices | 2 + .../test/expected/while-loops/reduction/r01.c | 7 + .../while-loops/reduction/r01.choices | 2 + .../test/expected/while-loops/reduction/r1.c | 4 + .../expected/while-loops/reduction/r1.choices | 1 + rtree-c/test/src/ReduceCSpec.hs | 74 +++-- rtree/src/Control/Monad/RTree.hs | 11 + 32 files changed, 348 insertions(+), 102 deletions(-) create mode 100644 rtree-c/test/cases/while-loops.c create mode 100644 rtree-c/test/expected/main/reduction/r.choices rename rtree-c/test/expected/typedef/reduction/{r00.c => r000.c} (100%) create mode 100644 rtree-c/test/expected/typedef/reduction/r000.choices create mode 100644 rtree-c/test/expected/typedef/reduction/r001.c create mode 100644 rtree-c/test/expected/typedef/reduction/r001.choices rename rtree-c/test/expected/typedef/reduction/{r01.c => r010.c} (100%) create mode 100644 rtree-c/test/expected/typedef/reduction/r010.choices create mode 100644 rtree-c/test/expected/typedef/reduction/r011.c create mode 100644 rtree-c/test/expected/typedef/reduction/r011.choices rename rtree-c/test/expected/typedef/reduction/{r10.c => r100.c} (100%) create mode 100644 rtree-c/test/expected/typedef/reduction/r100.choices create mode 100644 rtree-c/test/expected/typedef/reduction/r101.c create mode 100644 rtree-c/test/expected/typedef/reduction/r101.choices rename rtree-c/test/expected/typedef/reduction/{r11.c => r110.c} (100%) create mode 100644 rtree-c/test/expected/typedef/reduction/r110.choices create mode 100644 rtree-c/test/expected/typedef/reduction/r111.c create mode 100644 rtree-c/test/expected/typedef/reduction/r111.choices create mode 100644 rtree-c/test/expected/while-loops/main.c create mode 100644 rtree-c/test/expected/while-loops/reduction/r00.c create mode 100644 rtree-c/test/expected/while-loops/reduction/r00.choices create mode 100644 rtree-c/test/expected/while-loops/reduction/r01.c create mode 100644 rtree-c/test/expected/while-loops/reduction/r01.choices create mode 100644 rtree-c/test/expected/while-loops/reduction/r1.c create mode 100644 rtree-c/test/expected/while-loops/reduction/r1.choices diff --git a/flake.lock b/flake.lock index df4c0e1..fed1ed9 100644 --- a/flake.lock +++ b/flake.lock @@ -46,7 +46,7 @@ "dirtyRev": "979d17cf356e3a336dac8820c676ec813668222c-dirty", "dirtyShortRev": "979d17c-dirty", "lastModified": 1708504824, - "narHash": "sha256-4B1Tb847DHviKPsOLbKwjCUsrDCegXeKtDO7FTHgxts=", + "narHash": "sha256-g2sR8z7+KWFT2A09zXqqC5EvH64oSh36XqX5NNJV3rY=", "type": "git", "url": "file:///Users/chrg/Develop/repos/hspec-glitter" }, diff --git a/rtree-c/src/ReduceC.hs b/rtree-c/src/ReduceC.hs index 7703d29..1190e21 100644 --- a/rtree-c/src/ReduceC.hs +++ b/rtree-c/src/ReduceC.hs @@ -15,18 +15,31 @@ module ReduceC where import Control.Monad.Reader import Control.Monad.Reduce +import Control.Monad.Trans.Maybe (MaybeT (runMaybeT)) import Data.Data import Data.Foldable +import Data.Functor import qualified Data.Map.Strict as Map +import qualified Data.Set as Set +import Data.Vector.Internal.Check (HasCallStack) import qualified Language.C as C +import qualified Language.C.Data.Ident as C data Context = Context - { keepMain :: !Bool + { keywords :: !(Set.Set Keyword) , typeDefs :: !(Map.Map C.Ident [C.CDeclarationSpecifier C.NodeInfo]) } -defaultReduceC :: (CReducible a, MonadReduce String m) => a -> m a -defaultReduceC a = runReaderT (reduceC a) defaultContext +data Keyword + = KeepMain + | DoNoops + | NoSemantics + deriving (Show, Read, Enum, Eq, Ord) + +type CM m = (MonadReduce (String, C.Position) m, MonadReader Context m, MonadFail m) + +defaultReduceC :: (CReducible a, MonadReduce (String, C.Position) m) => a -> m (Maybe a) +defaultReduceC a = runMaybeT (runReaderT (reduceC a) defaultContext) addTypeDefs :: [C.Ident] -> [C.CDeclarationSpecifier C.NodeInfo] -> Context -> Context addTypeDefs ids cs Context{..} = @@ -39,60 +52,228 @@ addTypeDefs ids cs Context{..} = defaultContext :: Context defaultContext = Context - { keepMain = True + { keywords = Set.fromList [KeepMain] , typeDefs = Map.empty } +keyword :: (MonadReader Context m) => Keyword -> m Bool +keyword s = asks (Set.member s . keywords) + class CReducible a where - reduceC :: (MonadReduce String m) => a -> ReaderT Context m a + reduceC :: (CM m) => a -> m a instance CReducible C.CTranslUnit where reduceC (C.CTranslUnit es ni) = do - es' <- reduceDeclarations es + es' <- foldr reduceCExternalDeclaration (pure []) es pure $ C.CTranslUnit es' ni where - reduceDeclarations = \case - [] -> pure [] - r : rest -> reduceCExternalDeclaration r (reduceDeclarations rest) - reduceCExternalDeclaration r cont = do - shouldKeepMain <- asks keepMain + shouldKeepMain <- keyword KeepMain case r of C.CFDefExt fun | shouldKeepMain && maybe False (("main" ==) . C.identToString) (functionName fun) -> do r' <- C.CFDefExt <$> reduceC fun (r' :) <$> cont | otherwise -> - split ("remove function " <> show (functionName fun)) cont do + split ("remove function " <> maybe "" C.identToString (functionName fun), C.posOf r) cont do r' <- C.CFDefExt <$> reduceC fun (r' :) <$> cont C.CDeclExt result -> case result of -- A typedef - C.CDecl (C.CStorageSpec (C.CTypedef _) : rst) decl _ -> do - let ids = identifiers decl + C.CDecl (C.CStorageSpec (C.CTypedef n) : rst) decl _ -> do + let [ids] = identifiers decl split - ("inline typedefs " <> show ids) - (local (addTypeDefs ids rst) cont) - ((r :) <$> cont) - a -> error (show a) - _r -> error (show r) + ("inline typedef " <> C.identToString ids, C.posOf r) + (local (addTypeDefs [ids] rst) cont) + ((r :) <$> local (addTypeDefs [ids] [C.CTypeSpec (C.CTypeDef ids n)]) cont) + a -> don'tHandle a + _r -> don'tHandle r + +prettyIdent :: C.Identifier C.NodeInfo -> [Char] +prettyIdent (C.Ident s _ a) = s ++ " at " ++ show (C.posOfNode a) instance CReducible C.CFunDef where reduceC r = do C.CFunDef spc dec cdecls smt ni <- inlineTypeDefs r - pure $ C.CFunDef spc dec cdecls smt ni + smt' <- reduceC smt + pure $ C.CFunDef spc dec cdecls smt' ni + +reduceCCompoundBlockItem + :: (CM m) + => C.CCompoundBlockItem C.NodeInfo + -> m [C.CCompoundBlockItem C.NodeInfo] + -> m [C.CCompoundBlockItem C.NodeInfo] +reduceCCompoundBlockItem r cont = case r of + C.CBlockStmt smt -> do + split ("remove statement", C.posOf r) cont do + smt' <- reduceC smt + (C.CBlockStmt smt' :) <$> cont + C.CBlockDecl decl -> do + case decl of + C.CDecl{} -> do + (r :) <$> cont + d -> don'tHandle d + a -> don'tHandle a + +instance CReducible (C.CStatement C.NodeInfo) where + reduceC smt = case smt of + C.CCompound is cbi ni -> do + cbi' <- foldr reduceCCompoundBlockItem (pure []) cbi + pure $ C.CCompound is cbi' ni + C.CWhile e s dow ni -> do + e' <- reduceCExprOrZero e + s' <- reduceC s + pure $ C.CWhile e' s' dow ni + C.CExpr me ni -> do + case me of + Just e -> + splitOn DoNoops ("change to noop", C.posOf smt) (pure $ C.CExpr Nothing ni) do + e' <- reduceC e + pure $ C.CExpr (Just e') ni + Nothing -> + pure $ C.CExpr Nothing ni + C.CReturn me ni -> + case me of + Just e -> do + e' <- reduceCExprOrZero e + pure $ C.CReturn (Just e') ni + Nothing -> + pure $ C.CReturn Nothing ni + a -> don'tHandle a + +splitIf :: (MonadReduce l m) => Bool -> l -> m a -> m a -> m a +splitIf True s a b = split s a b +splitIf False _ _ b = b + +splitOn :: (MonadReduce l m, MonadReader Context m) => Keyword -> l -> m a -> m a -> m a +splitOn k s a b = do + con <- keyword k + splitIf con s a b -inlineTypeDefs :: forall d m. (Data d, MonadReader Context m) => d -> m d +-- C.CCompound is cbi ni -> do +-- cbi' <- collect (reduce @C.CCompoundBlockItem) cbi +-- pure $ C.CCompound is cbi' ni +-- C.CExpr e ni -> do +-- e' <- optional do +-- e' <- liftMaybe e +-- reduce @C.CExpression e' +-- pure $ C.CExpr e' ni +-- C.CIf e s els ni -> do +-- s' <- reduce s +-- e' <- optional do +-- reduce @C.CExpression e +-- els' <- optional do +-- els' <- liftMaybe els +-- given >> reduce els' +-- 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 +-- reduce s <| do +-- e1' <- reduce @C.CForInit e1 +-- e2' <- optional $ liftMaybe e2 >>= reduce @C.CExpression +-- e3' <- optional $ liftMaybe e3 >>= reduce @C.CExpression +-- s' <- reduce s +-- pure $ C.CFor e1' e2' e3' s' ni +-- C.CReturn e ni -> do +-- e' <- traverse (fmap orZero reduce) 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 -> do +-- -- todo fix attrs +-- s' <- reduce s +-- withFallback s' do +-- givenThat (Val.is i) +-- pure $ C.CLabel i s' [] ni +-- C.CGoto i ni -> +-- withFallback (C.CExpr Nothing ni) do +-- givenThat (Val.is i) +-- pure $ C.CGoto i ni +-- C.CWhile e s dow ni -> do +-- e' <- orZero (reduce @C.CExpression e) +-- s' <- reduce s +-- pure $ C.CWhile e' s' dow ni + +zeroExpr :: C.CExpression C.NodeInfo +zeroExpr = C.CConst (C.CIntConst (C.cInteger 0) C.undefNode) + +reduceCExprOrZero :: (CM m) => C.CExpr -> m C.CExpr +reduceCExprOrZero expr = + splitOn NoSemantics ("replace by zero", C.posOf expr) (pure zeroExpr) do + reduceC expr + +instance CReducible C.CExpr where + reduceC expr = case expr of + C.CBinary o elhs erhs ni -> + splitOn NoSemantics ("reduce to left", C.posOf elhs) (reduceC elhs) do + splitOn NoSemantics ("reduce to right", C.posOf erhs) (reduceC erhs) do + elhs' <- reduceC elhs + erhs' <- reduceC erhs + pure $ C.CBinary o elhs' erhs' ni + C.CVar i ni -> do + pure $ C.CVar i ni + C.CConst x -> do + pure $ C.CConst x + C.CUnary o elhs ni -> do + elhs' <- reduceC elhs + splitOn NoSemantics ("reduce to operant", C.posOf expr) (pure elhs') do + pure $ C.CUnary o elhs' ni + a -> error (show a) + +-- C.CCall e es ni -> do +-- e' <- reduce e +-- es' <- traverse (fmap orZero reduce) es +-- pure $ C.CCall e' es' ni +-- C.CCond ec et ef ni -> do +-- ec' <- reduce ec +-- ef' <- reduce ef +-- et' <- optional do +-- et' <- liftMaybe et +-- reduce et' +-- 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 <- reduce 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' <- reduce @C.CDeclaration cd +-- e' <- reduce e +-- pure $ C.CCast cd' e' ni +-- C.CAssign op e1 e2 ni -> onBothExpr e1 e2 \e1' e2' -> +-- pure $ C.CAssign op e1' e2' ni +-- C.CIndex e1 e2 ni -> do +-- e1' <- reduce e1 +-- e2' <- orZero (reduce e2) +-- pure $ C.CIndex e1' e2' ni +-- C.CMember e i b ni -> do +-- givenThat (Val.is i) +-- e' <- reduce e +-- pure $ C.CMember e' i b ni +-- C.CComma items ni -> do +-- C.CComma <$> collectNonEmpty' reduce items <*> pure ni +-- e -> error (show e) +-- where +-- onBothExpr elhs erhs = onBoth (reduce elhs) (reduce erhs) + +inlineTypeDefs :: forall d m. (Data d, MonadFail m, MonadReader Context m) => d -> m d inlineTypeDefs r = do case eqT @d @[C.CDeclarationSpecifier C.NodeInfo] of Just Refl -> do res' :: [[C.CDeclarationSpecifier C.NodeInfo]] <- forM r \case - a@(C.CTypeSpec (C.CTypeDef idx _)) -> do + C.CTypeSpec (C.CTypeDef idx _) -> do res <- asks (Map.lookup idx . typeDefs) case res of Just args -> pure args - Nothing -> pure [a] + Nothing -> fail ("could not find typedef:" <> show idx) a -> pure [a] pure (fold res') Nothing -> @@ -116,6 +297,9 @@ isMain (C.CFunDef _ (C.CDeclr (Just i) _ _ _ _) _ _ _) = C.identToString i == "main" isMain _ow = False +don'tHandle :: (HasCallStack, Functor f, Show (f ())) => f C.NodeInfo -> b +don'tHandle f = error (show (f $> ())) + -- instance CReducible C.CDeclaration where -- reduce = \case -- C.CDecl spc@(C.CStorageSpec (C.CTypedef _) : rst) decl ni -> do @@ -351,50 +535,6 @@ isMain _ow = False -- n' <- liftMaybe n -- reduce @C.CExpression n' -- --- instance CReducible C.CExpression where --- reduce = \case --- C.CVar i ni -> do --- givenThat (Val.is i) --- pure $ C.CVar i ni --- C.CCall e es ni -> do --- e' <- reduce e --- es' <- traverse (fmap orZero reduce) es --- pure $ C.CCall e' es' ni --- C.CCond ec et ef ni -> do --- ec' <- reduce ec --- ef' <- reduce ef --- et' <- optional do --- et' <- liftMaybe et --- reduce et' --- 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 <- reduce 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' <- reduce @C.CDeclaration cd --- e' <- reduce e --- pure $ C.CCast cd' e' ni --- C.CAssign op e1 e2 ni -> onBothExpr e1 e2 \e1' e2' -> --- pure $ C.CAssign op e1' e2' ni --- C.CIndex e1 e2 ni -> do --- e1' <- reduce e1 --- e2' <- orZero (reduce e2) --- pure $ C.CIndex e1' e2' ni --- C.CMember e i b ni -> do --- givenThat (Val.is i) --- e' <- reduce e --- pure $ C.CMember e' i b ni --- C.CComma items ni -> do --- C.CComma <$> collectNonEmpty' reduce items <*> pure ni --- e -> error (show e) --- where --- onBothExpr elhs erhs = onBoth (reduce elhs) (reduce erhs) -- -- zeroExp :: C.CExpression C.NodeInfo -- zeroExp = C.CConst (C.CIntConst (C.cInteger 0) C.undefNode) diff --git a/rtree-c/test/cases/main.c b/rtree-c/test/cases/main.c index 2cf2d12..fedd7ec 100644 --- a/rtree-c/test/cases/main.c +++ b/rtree-c/test/cases/main.c @@ -1,4 +1,3 @@ // A small test of basic reduction. Should not reduce. int main() { - return 0; } diff --git a/rtree-c/test/cases/while-loops.c b/rtree-c/test/cases/while-loops.c new file mode 100644 index 0000000..43d2e9e --- /dev/null +++ b/rtree-c/test/cases/while-loops.c @@ -0,0 +1,6 @@ +int main () { + int i = 0; + while (i < 10) { + i ++; + } +} diff --git a/rtree-c/test/expected/main/main.c b/rtree-c/test/expected/main/main.c index 905869d..5047a34 100644 --- a/rtree-c/test/expected/main/main.c +++ b/rtree-c/test/expected/main/main.c @@ -1,4 +1,3 @@ int main() { - return 0; } diff --git a/rtree-c/test/expected/main/reduction/r.c b/rtree-c/test/expected/main/reduction/r.c index 905869d..5047a34 100644 --- a/rtree-c/test/expected/main/reduction/r.c +++ b/rtree-c/test/expected/main/reduction/r.c @@ -1,4 +1,3 @@ int main() { - return 0; } diff --git a/rtree-c/test/expected/main/reduction/r.choices b/rtree-c/test/expected/main/reduction/r.choices new file mode 100644 index 0000000..e69de29 diff --git a/rtree-c/test/expected/typedef/reduction/r00.c b/rtree-c/test/expected/typedef/reduction/r000.c similarity index 100% rename from rtree-c/test/expected/typedef/reduction/r00.c rename to rtree-c/test/expected/typedef/reduction/r000.c diff --git a/rtree-c/test/expected/typedef/reduction/r000.choices b/rtree-c/test/expected/typedef/reduction/r000.choices new file mode 100644 index 0000000..b281d1c --- /dev/null +++ b/rtree-c/test/expected/typedef/reduction/r000.choices @@ -0,0 +1,3 @@ +0 remove statement at ("test/cases/typedef.c": line 9) +0 remove function f at ("test/cases/typedef.c": line 4) +0 inline typedef uint64 at ("test/cases/typedef.c": line 2) diff --git a/rtree-c/test/expected/typedef/reduction/r001.c b/rtree-c/test/expected/typedef/reduction/r001.c new file mode 100644 index 0000000..b7888ce --- /dev/null +++ b/rtree-c/test/expected/typedef/reduction/r001.c @@ -0,0 +1,8 @@ +typedef int uint64; +void f(uint64 a) +{ +} +int main() +{ + uint64 x = 1; +} diff --git a/rtree-c/test/expected/typedef/reduction/r001.choices b/rtree-c/test/expected/typedef/reduction/r001.choices new file mode 100644 index 0000000..a44e5d5 --- /dev/null +++ b/rtree-c/test/expected/typedef/reduction/r001.choices @@ -0,0 +1,3 @@ +1 remove statement at ("test/cases/typedef.c": line 9) +0 remove function f at ("test/cases/typedef.c": line 4) +0 inline typedef uint64 at ("test/cases/typedef.c": line 2) diff --git a/rtree-c/test/expected/typedef/reduction/r01.c b/rtree-c/test/expected/typedef/reduction/r010.c similarity index 100% rename from rtree-c/test/expected/typedef/reduction/r01.c rename to rtree-c/test/expected/typedef/reduction/r010.c diff --git a/rtree-c/test/expected/typedef/reduction/r010.choices b/rtree-c/test/expected/typedef/reduction/r010.choices new file mode 100644 index 0000000..dbaeb2c --- /dev/null +++ b/rtree-c/test/expected/typedef/reduction/r010.choices @@ -0,0 +1,3 @@ +0 remove statement at ("test/cases/typedef.c": line 9) +1 remove function f at ("test/cases/typedef.c": line 4) +0 inline typedef uint64 at ("test/cases/typedef.c": line 2) diff --git a/rtree-c/test/expected/typedef/reduction/r011.c b/rtree-c/test/expected/typedef/reduction/r011.c new file mode 100644 index 0000000..1ab8fec --- /dev/null +++ b/rtree-c/test/expected/typedef/reduction/r011.c @@ -0,0 +1,5 @@ +typedef int uint64; +int main() +{ + uint64 x = 1; +} diff --git a/rtree-c/test/expected/typedef/reduction/r011.choices b/rtree-c/test/expected/typedef/reduction/r011.choices new file mode 100644 index 0000000..92fc7ae --- /dev/null +++ b/rtree-c/test/expected/typedef/reduction/r011.choices @@ -0,0 +1,3 @@ +1 remove statement at ("test/cases/typedef.c": line 9) +1 remove function f at ("test/cases/typedef.c": line 4) +0 inline typedef uint64 at ("test/cases/typedef.c": line 2) diff --git a/rtree-c/test/expected/typedef/reduction/r10.c b/rtree-c/test/expected/typedef/reduction/r100.c similarity index 100% rename from rtree-c/test/expected/typedef/reduction/r10.c rename to rtree-c/test/expected/typedef/reduction/r100.c diff --git a/rtree-c/test/expected/typedef/reduction/r100.choices b/rtree-c/test/expected/typedef/reduction/r100.choices new file mode 100644 index 0000000..48c32e1 --- /dev/null +++ b/rtree-c/test/expected/typedef/reduction/r100.choices @@ -0,0 +1,3 @@ +0 remove statement at ("test/cases/typedef.c": line 9) +0 remove function f at ("test/cases/typedef.c": line 4) +1 inline typedef uint64 at ("test/cases/typedef.c": line 2) diff --git a/rtree-c/test/expected/typedef/reduction/r101.c b/rtree-c/test/expected/typedef/reduction/r101.c new file mode 100644 index 0000000..975eaa4 --- /dev/null +++ b/rtree-c/test/expected/typedef/reduction/r101.c @@ -0,0 +1,7 @@ +void f(int a) +{ +} +int main() +{ + int x = 1; +} diff --git a/rtree-c/test/expected/typedef/reduction/r101.choices b/rtree-c/test/expected/typedef/reduction/r101.choices new file mode 100644 index 0000000..76ba083 --- /dev/null +++ b/rtree-c/test/expected/typedef/reduction/r101.choices @@ -0,0 +1,3 @@ +1 remove statement at ("test/cases/typedef.c": line 9) +0 remove function f at ("test/cases/typedef.c": line 4) +1 inline typedef uint64 at ("test/cases/typedef.c": line 2) diff --git a/rtree-c/test/expected/typedef/reduction/r11.c b/rtree-c/test/expected/typedef/reduction/r110.c similarity index 100% rename from rtree-c/test/expected/typedef/reduction/r11.c rename to rtree-c/test/expected/typedef/reduction/r110.c diff --git a/rtree-c/test/expected/typedef/reduction/r110.choices b/rtree-c/test/expected/typedef/reduction/r110.choices new file mode 100644 index 0000000..e70ee97 --- /dev/null +++ b/rtree-c/test/expected/typedef/reduction/r110.choices @@ -0,0 +1,3 @@ +0 remove statement at ("test/cases/typedef.c": line 9) +1 remove function f at ("test/cases/typedef.c": line 4) +1 inline typedef uint64 at ("test/cases/typedef.c": line 2) diff --git a/rtree-c/test/expected/typedef/reduction/r111.c b/rtree-c/test/expected/typedef/reduction/r111.c new file mode 100644 index 0000000..a382e8e --- /dev/null +++ b/rtree-c/test/expected/typedef/reduction/r111.c @@ -0,0 +1,4 @@ +int main() +{ + int x = 1; +} diff --git a/rtree-c/test/expected/typedef/reduction/r111.choices b/rtree-c/test/expected/typedef/reduction/r111.choices new file mode 100644 index 0000000..bceac89 --- /dev/null +++ b/rtree-c/test/expected/typedef/reduction/r111.choices @@ -0,0 +1,3 @@ +1 remove statement at ("test/cases/typedef.c": line 9) +1 remove function f at ("test/cases/typedef.c": line 4) +1 inline typedef uint64 at ("test/cases/typedef.c": line 2) diff --git a/rtree-c/test/expected/while-loops/main.c b/rtree-c/test/expected/while-loops/main.c new file mode 100644 index 0000000..cf4c537 --- /dev/null +++ b/rtree-c/test/expected/while-loops/main.c @@ -0,0 +1,8 @@ +int main() +{ + int i = 0; + while (i < 10) + { + i++; + } +} diff --git a/rtree-c/test/expected/while-loops/reduction/r00.c b/rtree-c/test/expected/while-loops/reduction/r00.c new file mode 100644 index 0000000..cf4c537 --- /dev/null +++ b/rtree-c/test/expected/while-loops/reduction/r00.c @@ -0,0 +1,8 @@ +int main() +{ + int i = 0; + while (i < 10) + { + i++; + } +} diff --git a/rtree-c/test/expected/while-loops/reduction/r00.choices b/rtree-c/test/expected/while-loops/reduction/r00.choices new file mode 100644 index 0000000..4fee46c --- /dev/null +++ b/rtree-c/test/expected/while-loops/reduction/r00.choices @@ -0,0 +1,2 @@ +0 remove statement at ("test/cases/while-loops.c": line 4) +0 remove statement at ("test/cases/while-loops.c": line 3) diff --git a/rtree-c/test/expected/while-loops/reduction/r01.c b/rtree-c/test/expected/while-loops/reduction/r01.c new file mode 100644 index 0000000..714143b --- /dev/null +++ b/rtree-c/test/expected/while-loops/reduction/r01.c @@ -0,0 +1,7 @@ +int main() +{ + int i = 0; + while (i < 10) + { + } +} diff --git a/rtree-c/test/expected/while-loops/reduction/r01.choices b/rtree-c/test/expected/while-loops/reduction/r01.choices new file mode 100644 index 0000000..69ec6af --- /dev/null +++ b/rtree-c/test/expected/while-loops/reduction/r01.choices @@ -0,0 +1,2 @@ +1 remove statement at ("test/cases/while-loops.c": line 4) +0 remove statement at ("test/cases/while-loops.c": line 3) diff --git a/rtree-c/test/expected/while-loops/reduction/r1.c b/rtree-c/test/expected/while-loops/reduction/r1.c new file mode 100644 index 0000000..28c5719 --- /dev/null +++ b/rtree-c/test/expected/while-loops/reduction/r1.c @@ -0,0 +1,4 @@ +int main() +{ + int i = 0; +} diff --git a/rtree-c/test/expected/while-loops/reduction/r1.choices b/rtree-c/test/expected/while-loops/reduction/r1.choices new file mode 100644 index 0000000..efbb594 --- /dev/null +++ b/rtree-c/test/expected/while-loops/reduction/r1.choices @@ -0,0 +1 @@ +1 remove statement at ("test/cases/while-loops.c": line 3) diff --git a/rtree-c/test/src/ReduceCSpec.hs b/rtree-c/test/src/ReduceCSpec.hs index 00c2ba0..f447df9 100644 --- a/rtree-c/test/src/ReduceCSpec.hs +++ b/rtree-c/test/src/ReduceCSpec.hs @@ -17,11 +17,13 @@ import Test.Hspec.Glitter import qualified Language.C as C import qualified Text.PrettyPrint as P -import Control.Monad.RTree (extract, iinputs) +import Control.Monad.RTree (extract, iinputs, probe) +import Data.Bool import Data.Functor import Data.RPath import qualified Language.C.System.GCC as C import ReduceC +import System.Directory.Internal.Prelude (tryIOError) import System.Process.Typed spec :: Spec @@ -31,45 +33,55 @@ spec = do forM_ cases \cname -> do let cfrom = "test/cases" </> cname - c <- runIO $ parse cfrom - - let expected = "test/expected" </> dropExtensions cname - onGlitterWith (expected </> "main.c") (\fp () -> render fp c) do - it "should be valid" . foldMap $ \cf -> do - validate cf - - it "should be parsed equally" . foldMap $ \cf -> do - C.parseCFilePre cf >>= \case - Left err -> fail (show err) - Right c' -> c' $> () `shouldBe` c $> () - - describe "reduction" do - it "should extract itself" do - extract (defaultReduceC c) `shouldBe` c - - onGlitterWith - (expected </> "reduction/") - ( \a () -> do - removeDirectoryRecursive a - createDirectoryIfMissing True a - forM_ (take 20 $ iinputs (defaultReduceC c)) \(i, c') -> do - let rfile = expected </> "reduction" </> "r" <> debugShow i <.> "c" - render rfile c' - ) - do - it "should validate all reductions" . mapM_ $ \a -> do - validate a + describe cfrom do + c <- runIO $ parse cfrom + + let expected = "test/expected" </> dropExtensions cname + onGlitterWith (expected </> "main.c") (\fp () -> render fp c) do + it "should be valid" . foldMap $ \cf -> do + validate cf + + it "should be parsed equally" . foldMap $ \cf -> do + C.parseCFilePre cf >>= \case + Left err -> fail (show err) + Right c' -> c' $> () `shouldBe` c $> () + + describe "reduction" do + it "should extract itself" do + fmap ($> ()) (extract $ defaultReduceC c) `shouldBe` Just (c $> ()) + + onGlitterWith + (expected </> "reduction/") + ( \a () -> do + _ <- tryIOError (removeDirectoryRecursive a) + createDirectoryIfMissing True a + forM_ (iinputs (defaultReduceC c)) \(i, c') -> do + let rfile = expected </> "reduction" </> "r" <> debugShow i <.> "c" + maybe (writeFile rfile "") (render rfile) c' + let cofile = expected </> "reduction" </> "r" <> debugShow i <.> "choices" + writeFile + cofile + ( unlines + . map (\(choice, (reason, pos)) -> bool "0" "1" choice <> " " <> reason <> " at " <> show pos) + . snd + $ probe (defaultReduceC c) i + ) + ) + do + it "should validate all reductions" . mapM_ $ \a -> do + when (takeExtension a == ".c") do + validate a validate :: FilePath -> IO () validate fp = do - (ec, res) <- readProcessStderr (proc "clang" ["-o", "/dev/null", fp]) + (ec, _, stderr) <- readProcess (proc "clang" ["-o", "/dev/null", fp]) case ec of ExitFailure _ -> expectationFailure $ "could not validate " <> show fp <> "\n" - <> LazyText.unpack (LazyText.decodeUtf8 res) + <> LazyText.unpack (LazyText.decodeUtf8 stderr) ExitSuccess -> pure () render :: FilePath -> C.CTranslUnit -> IO () diff --git a/rtree/src/Control/Monad/RTree.hs b/rtree/src/Control/Monad/RTree.hs index 728b8db..f93cec6 100644 --- a/rtree/src/Control/Monad/RTree.hs +++ b/rtree/src/Control/Monad/RTree.hs @@ -17,6 +17,7 @@ module Control.Monad.RTree ( extract, inputs, iinputs, + probe, reduce, drawRTree, @@ -83,6 +84,16 @@ inputs = toList iinputs :: RTree l i -> [(RPath, i)] iinputs = itoList +probe :: RTree l i -> RPath -> (i, [(Bool, l)]) +probe tree pth = go 0 id tree + where + go idx res = \case + Done i -> (i, res []) + Split l rhs lhs -> + let b = pth `indexChoice` idx + in go (idx + 1) (((b, l) :) . res) (if b then rhs else lhs) +{-# INLINE probe #-} + -- | For debugging purposes drawRTree :: (l -> ShowS) -> (i -> ShowS) -> RTree l i -> String drawRTree ppl ppi = concat . go id -- GitLab