From 8d446a03052e89a8187f9792aee64c9d22973afd Mon Sep 17 00:00:00 2001 From: Christian Gram Kalhauge <chrg@dtu.dk> Date: Tue, 20 Feb 2024 22:48:20 +0100 Subject: [PATCH] A step closer to a good solution --- rtree-c/.hspec | 1 - rtree-c/.hspec-failures | 1 + rtree-c/develop.sh | 1 + rtree-c/hie.yml | 6 + rtree-c/package.yaml | 6 +- rtree-c/rtree-c.cabal | 13 +- rtree-c/src/ReduceC.hs | 510 +++++++++++----------- rtree-c/test/cases/main/main.c | 3 + rtree-c/test/cases/main/pp.c | 4 + rtree-c/test/expected/main/pp.c | 4 + rtree-c/test/expected/main/reduction/r0.c | 4 + rtree-c/test/expected/main/reduction/r1.c | 1 + rtree-c/test/src/ReduceCSpec.hs | 84 +++- rtree/src/Data/RPath.hs | 6 +- 14 files changed, 391 insertions(+), 253 deletions(-) create mode 100644 rtree-c/.hspec-failures create mode 100755 rtree-c/develop.sh create mode 100644 rtree-c/hie.yml create mode 100644 rtree-c/test/cases/main/main.c create mode 100644 rtree-c/test/cases/main/pp.c create mode 100644 rtree-c/test/expected/main/pp.c create mode 100644 rtree-c/test/expected/main/reduction/r0.c create mode 100644 rtree-c/test/expected/main/reduction/r1.c diff --git a/rtree-c/.hspec b/rtree-c/.hspec index c2400d4..6e9b625 100644 --- a/rtree-c/.hspec +++ b/rtree-c/.hspec @@ -1,3 +1,2 @@ --failure-report .hspec-failures ---rerun --rerun-all-on-success diff --git a/rtree-c/.hspec-failures b/rtree-c/.hspec-failures new file mode 100644 index 0000000..fc74144 --- /dev/null +++ b/rtree-c/.hspec-failures @@ -0,0 +1 @@ +FailureReport {failureReportSeed = 860042728, failureReportMaxSuccess = 100, failureReportMaxSize = 100, failureReportMaxDiscardRatio = 10, failureReportPaths = [(["ReduceC","test/expected/main/reduction"],"should validate all reductions"),(["ReduceC","test/expected/main/reduction"],"should not have changed")]} \ No newline at end of file diff --git a/rtree-c/develop.sh b/rtree-c/develop.sh new file mode 100755 index 0000000..a661dcf --- /dev/null +++ b/rtree-c/develop.sh @@ -0,0 +1 @@ +ghcid --command='cabal repl rtree-c-test' -r --reload=test/cases diff --git a/rtree-c/hie.yml b/rtree-c/hie.yml new file mode 100644 index 0000000..41aa90e --- /dev/null +++ b/rtree-c/hie.yml @@ -0,0 +1,6 @@ +cradle: + cabal: + - path: "./src" + component: "lib:rtree-c" + - path: "./test/src" + component: "test:rtree-c-test" diff --git a/rtree-c/package.yaml b/rtree-c/package.yaml index 626ea07..9b4cfa2 100644 --- a/rtree-c/package.yaml +++ b/rtree-c/package.yaml @@ -16,6 +16,7 @@ dependencies: - pretty-simple - transformers - language-c + - pretty library: source-dirs: src @@ -36,7 +37,7 @@ executables: - text tests: - rtree-test: + rtree-c-test: source-dirs: test/src main: Main.hs dependencies: @@ -46,3 +47,6 @@ tests: - hspec-discover - hspec-expectations-pretty-diff - hspec-glitter + - directory + - filepath + - typed-process diff --git a/rtree-c/rtree-c.cabal b/rtree-c/rtree-c.cabal index 631e4ed..7253184 100644 --- a/rtree-c/rtree-c.cabal +++ b/rtree-c/rtree-c.cabal @@ -21,6 +21,7 @@ library , containers , language-c , mtl + , pretty , pretty-simple , rtree , transformers @@ -54,10 +55,12 @@ executable rtree-c , vector default-language: Haskell2010 -test-suite rtree-test +test-suite rtree-c-test type: exitcode-stdio-1.0 main-is: Main.hs other-modules: + ReduceCSpec + Spec Paths_rtree_c hs-source-dirs: test/src @@ -65,11 +68,19 @@ test-suite rtree-test build-depends: base >=4.9 && <5 , containers + , directory + , filepath + , hspec + , hspec-discover + , hspec-expectations-pretty-diff + , hspec-glitter , language-c , mtl + , pretty , pretty-simple , rtree , rtree-c , transformers + , typed-process , vector default-language: Haskell2010 diff --git a/rtree-c/src/ReduceC.hs b/rtree-c/src/ReduceC.hs index 778e8c0..cfaebdf 100644 --- a/rtree-c/src/ReduceC.hs +++ b/rtree-c/src/ReduceC.hs @@ -6,265 +6,279 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE NoMonomorphismRestriction #-} module ReduceC where import Control.Monad.Reduce - -import qualified Data.Valuation as Val - -import Control.Applicative -import Control.Monad.State -import Control.Monad.Trans.Maybe -import Data.Data -import Data.Function -import Data.Functor -import qualified Data.Map.Strict as Map -import Data.Maybe (catMaybes) import qualified Language.C as C -type Lab = C.Ident - -data LabInfo - = Type [C.CDeclarationSpecifier C.NodeInfo] - -type CState = Map.Map Lab LabInfo - -reduceC :: (MonadReduce Lab m, MonadState CState m) => C.CTranslUnit -> m C.CTranslUnit -reduceC (C.CTranslUnit es ni) = do - es' <- collect reduceCExternalDeclaration es - pure $ C.CTranslUnit es' ni - where - reduceCExternalDeclaration = \case - C.CFDefExt fun -> do - C.CFDefExt <$> reduce @C.CFunctionDef fun - C.CDeclExt decl -> - C.CDeclExt <$> reduce @C.CDeclaration decl - a -> error (show a) - -identifiers :: forall a. (Data a) => a -> [Lab] -identifiers d = case cast d of - Just l -> [l] - Nothing -> concat $ gmapQ identifiers d - -type Reducer m a = a -> m a - -class CReducible c where - reduce :: (MonadReducePlus Lab m, MonadState CState m) => Reducer m (c C.NodeInfo) - -cDeclaratorIdentifiers :: C.CDeclarator C.NodeInfo -> (Maybe Lab, [Lab]) -cDeclaratorIdentifiers (C.CDeclr mi dd _ la _) = - (mi, identifiers dd <> identifiers la) - -instance CReducible C.CFunctionDef where - reduce (C.CFunDef spc dec cdecls smt ni) = do - let (fn, ids) = cDeclaratorIdentifiers dec - let requirements = identifiers spc <> identifiers cdecls <> ids - case fn of - Just fn' -> - conditionalGivenThat requirements (Val.is fn') - Nothing -> - mapM_ (givenThat . Val.is) requirements - smt' <- reduce @C.CStatement smt - pure $ C.CFunDef spc dec cdecls smt' ni - -instance CReducible C.CDeclaration where - reduce = \case - C.CDecl spc@(C.CStorageSpec (C.CTypedef _) : rst) decl ni -> do - decl' <- - decl & collectNonEmpty' \case - C.CDeclarationItem d Nothing Nothing -> do - let (x, _) = cDeclaratorIdentifiers d - case x of - Just x' -> - splitOn - (Val.is x') - ( do - modify (Map.insert x' (Type rst)) - mzero - ) - (pure $ C.CDeclarationItem d Nothing Nothing) - Nothing -> - pure $ C.CDeclarationItem d Nothing Nothing - a -> error (show a) - pure (C.CDecl spc decl' ni) - C.CDecl spc@[C.CTypeSpec (C.CTypeDef i ni')] decl ni -> do - x <- gets (Map.lookup i) - case x of - Just (Type rst) -> do - decl' <- collectNonEmpty' (reduceCDeclarationItem $ identifiers rst) decl - pure $ C.CDecl rst decl' ni - Nothing -> do - decl' <- collectNonEmpty' (reduceCDeclarationItem $ identifiers spc) decl - pure $ C.CDecl spc decl' ni - C.CDecl spc decl ni -> do - decl' <- collectNonEmpty' (reduceCDeclarationItem $ identifiers spc) decl - pure $ C.CDecl spc decl' ni - a -> error (show a) - where - reduceCDeclarationItem rq' = \case - C.CDeclarationItem d i e -> do - let (fn, reqs) = cDeclaratorIdentifiers d - case fn of - Just fn' -> - conditionalGivenThat (rq' <> reqs) (Val.is fn') - Nothing -> - mapM_ (givenThat . Val.is) (rq' <> reqs) - - i' <- optional do - liftMaybe i >>= reduce @C.CInitializer - e' <- optional do - liftMaybe e >>= reduce @C.CExpression +data Context = Context - pure (C.CDeclarationItem d i' e') - a -> error (show a) +class CReducible a where + reduceC :: (MonadReduce String m) => a -> m a -instance CReducible C.CInitializer where - reduce = \case - C.CInitExpr e ni -> reduce @C.CExpression e <&> \e' -> C.CInitExpr e' ni - C.CInitList (C.CInitializerList items) ni -> do - collectNonEmpty' rmCInitializerListItem items <&> \items' -> - C.CInitList (C.CInitializerList items') ni +instance CReducible C.CTranslUnit where + reduceC (C.CTranslUnit es ni) = do + es' <- rList es + -- es' <- collect reduceCExternalDeclaration es + pure $ C.CTranslUnit es' ni where - rmCInitializerListItem (pds, is) = do - pds' <- collect rmCPartDesignator pds - is' <- reduce is - pure (pds', is') - - rmCPartDesignator = \case - a -> error (show a) - -instance CReducible C.CStatement where - reduce = \case - 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 - a -> error (show a) - -instance CReducible C.CForInit where - reduce = \case - C.CForDecl decl -> withFallback (C.CForInitializing Nothing) do - C.CForDecl <$> reduce @C.CDeclaration decl - C.CForInitializing n -> do - C.CForInitializing <$> optional do - 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) - -withFallback :: (Alternative m) => a -> m a -> m a -withFallback a ma = ma <|> pure a - -orZero :: (Alternative m) => m (C.CExpression C.NodeInfo) -> m (C.CExpression C.NodeInfo) -orZero = withFallback zeroExp - -instance CReducible C.CCompoundBlockItem where - reduce = \case - C.CBlockStmt s -> - C.CBlockStmt <$> do - given >> reduce @C.CStatement s - C.CBlockDecl d -> - C.CBlockDecl <$> do - reduce @C.CDeclaration d - a -> error (show a) + rList (a : as) = rList as <| ((a :) <$> rList as) + rList [] = pure [] -mtry :: (Functor m) => MaybeT m a -> MaybeT m (Maybe a) -mtry (MaybeT mt) = MaybeT (Just <$> mt) +-- reduceCExternalDeclaration = \case +-- C.CFDefExt fun -> do +-- C.CFDefExt <$> reduce @C.CFunctionDef fun +-- C.CDeclExt decl -> +-- C.CDeclExt <$> reduce @C.CDeclaration decl +-- [] -> error (show a) -mlift :: (Applicative m) => Maybe a -> MaybeT m a -mlift a = MaybeT (pure a) +-- import Control.Monad.Reduce +-- +-- import qualified Data.Valuation as Val +-- +-- import Control.Applicative +-- import Control.Monad.State +-- import Control.Monad.Trans.Maybe +-- import Data.Data +-- import Data.Function +-- import Data.Functor +-- import qualified Data.Map.Strict as Map +-- import Data.Maybe (catMaybes) +-- import qualified Language.C as C -munder :: (Monad m) => Maybe a -> (a -> MaybeT m b) -> MaybeT m b -munder a mf = mlift a >>= mf +-- type Lab = C.Ident +-- +-- data LabInfo +-- = Type [C.CDeclarationSpecifier C.NodeInfo] +-- +-- type CState = Map.Map Lab LabInfo +-- +-- reduceC :: (MonadReduce Lab m, MonadState CState m) => C.CTranslUnit -> m C.CTranslUnit +-- reduceC (C.CTranslUnit es ni) = do +-- es' <- collect reduceCExternalDeclaration es +-- pure $ C.CTranslUnit es' ni +-- where +-- reduceCExternalDeclaration = \case +-- C.CFDefExt fun -> do +-- C.CFDefExt <$> reduce @C.CFunctionDef fun +-- C.CDeclExt decl -> +-- C.CDeclExt <$> reduce @C.CDeclaration decl +-- a -> error (show a) +-- +-- identifiers :: forall a. (Data a) => a -> [Lab] +-- identifiers d = case cast d of +-- Just l -> [l] +-- Nothing -> concat $ gmapQ identifiers d +-- +-- type Reducer m a = a -> m a +-- +-- class CReducible c where +-- reduce :: (MonadReducePlus Lab m, MonadState CState m) => Reducer m (c C.NodeInfo) +-- +-- cDeclaratorIdentifiers :: C.CDeclarator C.NodeInfo -> (Maybe Lab, [Lab]) +-- cDeclaratorIdentifiers (C.CDeclr mi dd _ la _) = +-- (mi, identifiers dd <> identifiers la) +-- +-- instance CReducible C.CFunctionDef where +-- reduce (C.CFunDef spc dec cdecls smt ni) = do +-- let (fn, ids) = cDeclaratorIdentifiers dec +-- let requirements = identifiers spc <> identifiers cdecls <> ids +-- case fn of +-- Just fn' -> +-- conditionalGivenThat requirements (Val.is fn') +-- Nothing -> +-- mapM_ (givenThat . Val.is) requirements +-- smt' <- reduce @C.CStatement smt +-- pure $ C.CFunDef spc dec cdecls smt' ni +-- +-- instance CReducible C.CDeclaration where +-- reduce = \case +-- C.CDecl spc@(C.CStorageSpec (C.CTypedef _) : rst) decl ni -> do +-- decl' <- +-- decl & collectNonEmpty' \case +-- C.CDeclarationItem d Nothing Nothing -> do +-- let (x, _) = cDeclaratorIdentifiers d +-- case x of +-- Just x' -> +-- splitOn +-- (Val.is x') +-- ( do +-- modify (Map.insert x' (Type rst)) +-- mzero +-- ) +-- (pure $ C.CDeclarationItem d Nothing Nothing) +-- Nothing -> +-- pure $ C.CDeclarationItem d Nothing Nothing +-- a -> error (show a) +-- pure (C.CDecl spc decl' ni) +-- C.CDecl spc@[C.CTypeSpec (C.CTypeDef i ni')] decl ni -> do +-- x <- gets (Map.lookup i) +-- case x of +-- Just (Type rst) -> do +-- decl' <- collectNonEmpty' (reduceCDeclarationItem $ identifiers rst) decl +-- pure $ C.CDecl rst decl' ni +-- Nothing -> do +-- decl' <- collectNonEmpty' (reduceCDeclarationItem $ identifiers spc) decl +-- pure $ C.CDecl spc decl' ni +-- C.CDecl spc decl ni -> do +-- decl' <- collectNonEmpty' (reduceCDeclarationItem $ identifiers spc) decl +-- pure $ C.CDecl spc decl' ni +-- a -> error (show a) +-- where +-- reduceCDeclarationItem rq' = \case +-- C.CDeclarationItem d i e -> do +-- let (fn, reqs) = cDeclaratorIdentifiers d +-- case fn of +-- Just fn' -> +-- conditionalGivenThat (rq' <> reqs) (Val.is fn') +-- Nothing -> +-- mapM_ (givenThat . Val.is) (rq' <> reqs) +-- +-- i' <- optional do +-- liftMaybe i >>= reduce @C.CInitializer +-- e' <- optional do +-- liftMaybe e >>= reduce @C.CExpression +-- +-- pure (C.CDeclarationItem d i' e') +-- a -> error (show a) +-- +-- instance CReducible C.CInitializer where +-- reduce = \case +-- C.CInitExpr e ni -> reduce @C.CExpression e <&> \e' -> C.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' <- collect rmCPartDesignator pds +-- is' <- reduce is +-- pure (pds', is') +-- +-- rmCPartDesignator = \case +-- a -> error (show a) +-- +-- instance CReducible C.CStatement where +-- reduce = \case +-- 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 +-- a -> error (show a) +-- +-- instance CReducible C.CForInit where +-- reduce = \case +-- C.CForDecl decl -> withFallback (C.CForInitializing Nothing) do +-- C.CForDecl <$> reduce @C.CDeclaration decl +-- C.CForInitializing n -> do +-- C.CForInitializing <$> optional do +-- 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) +-- +-- withFallback :: (Alternative m) => a -> m a -> m a +-- withFallback a ma = ma <|> pure a +-- +-- orZero :: (Alternative m) => m (C.CExpression C.NodeInfo) -> m (C.CExpression C.NodeInfo) +-- orZero = withFallback zeroExp +-- +-- instance CReducible C.CCompoundBlockItem where +-- reduce = \case +-- C.CBlockStmt s -> +-- C.CBlockStmt <$> do +-- given >> reduce @C.CStatement s +-- C.CBlockDecl d -> +-- C.CBlockDecl <$> do +-- reduce @C.CDeclaration d +-- a -> error (show a) diff --git a/rtree-c/test/cases/main/main.c b/rtree-c/test/cases/main/main.c new file mode 100644 index 0000000..33c14ce --- /dev/null +++ b/rtree-c/test/cases/main/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/rtree-c/test/cases/main/pp.c b/rtree-c/test/cases/main/pp.c new file mode 100644 index 0000000..905869d --- /dev/null +++ b/rtree-c/test/cases/main/pp.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/rtree-c/test/expected/main/pp.c b/rtree-c/test/expected/main/pp.c new file mode 100644 index 0000000..905869d --- /dev/null +++ b/rtree-c/test/expected/main/pp.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/rtree-c/test/expected/main/reduction/r0.c b/rtree-c/test/expected/main/reduction/r0.c new file mode 100644 index 0000000..905869d --- /dev/null +++ b/rtree-c/test/expected/main/reduction/r0.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/rtree-c/test/expected/main/reduction/r1.c b/rtree-c/test/expected/main/reduction/r1.c new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/rtree-c/test/expected/main/reduction/r1.c @@ -0,0 +1 @@ + diff --git a/rtree-c/test/src/ReduceCSpec.hs b/rtree-c/test/src/ReduceCSpec.hs index 1fe5d64..31e87d2 100644 --- a/rtree-c/test/src/ReduceCSpec.hs +++ b/rtree-c/test/src/ReduceCSpec.hs @@ -1,3 +1,85 @@ +{-# LANGUAGE BlockArguments #-} +{-# LANGUAGE LambdaCase #-} + module ReduceCSpec where -spec = pure () +import Control.Monad + +import System.Directory +import System.FilePath +import Test.Hspec + +import Test.Hspec.Glitter + +import qualified Language.C as C +import qualified Text.PrettyPrint as P + +import Control.Monad.RTree (extract, iinputs) +import Data.Functor +import Data.RPath +import qualified Language.C.System.GCC as C +import ReduceC +import System.Process.Typed + +spec :: Spec +spec = do + cases <- runIO (listDirectory "test/cases") + + forM_ cases \cname -> do + let cfrom = "test/cases" </> cname </> "main.c" + let expected = "test/expected" </> cname + onGlitterWith + (expected </> "pp.c") + (preproc cfrom) + do + it "should be valid" \(cf, _) -> + validate cf + + it "should be parsed equally" \(cf, c) -> do + C.parseCFilePre cf >>= \case + Left err -> fail (show err) + Right c' -> c' $> () `shouldBe` c $> () + + describe "reduction" do + it "should extract itself" \(_, c) -> do + extract (reduceC c) `shouldBe` c + + onGlitterWith + (expected </> "reduction") + ( \a () -> do + c <- parse cfrom + createDirectoryIfMissing True a + forM_ (take 20 $ iinputs (reduceC c)) \(i, c') -> do + let rfile = expected </> "reduction" </> "r" <> debugShow i <.> "c" + render rfile c' + pure a + ) + do + it "should validate all reductions" \a -> do + listDirectory a >>= mapM_ \x -> + validate (a </> x) + +validate :: FilePath -> IO () +validate fp = do + ec <- runProcess (proc "clang" ["-o", "/dev/null", fp]) + case ec of + ExitFailure _ -> fail ("could not validate " <> show fp) + ExitSuccess -> pure () + +render :: FilePath -> C.CTranslUnit -> IO () +render cto c = do + createDirectoryIfMissing True (takeDirectory cto) + writeFile cto (P.render (C.pretty c) <> "\n") + +parse :: FilePath -> IO C.CTranslUnit +parse cfrom = do + cf <- C.parseCFile (C.newGCC "clang") Nothing [] cfrom + case cf of + Left err -> fail (show err) + Right cf' -> pure cf' + +preproc :: FilePath -> FilePath -> () -> IO (FilePath, C.CTranslUnit) +preproc cfrom cto _ = do + cf' <- parse cfrom + render cto cf' + pure (cfrom, cf') diff --git a/rtree/src/Data/RPath.hs b/rtree/src/Data/RPath.hs index 1018591..e3f2c46 100644 --- a/rtree/src/Data/RPath.hs +++ b/rtree/src/Data/RPath.hs @@ -11,6 +11,7 @@ module Data.RPath ( toPredicateDebug, -- * Helpers + debugShow, debugShowWithDepth, ) where @@ -72,8 +73,11 @@ debugShowWithDepth rp i = (map (bool '0' '1') . take (i + 1) . toChoiceList $ rp) <> replicate (numberOfChoices rp - i - 1) '*' +debugShow :: RPath -> String +debugShow = map (bool '0' '1') . toChoiceList + instance Show RPath where - show = show . map (bool '0' '1') . toChoiceList + show = show . debugShow instance Read RPath where readsPrec i s = [(fromString a, r) | (a, r) <- readsPrec i s] -- GitLab