diff --git a/flake.lock b/flake.lock index 4511fea6298812ad362e88ecdb38403dcc45b95f..df4c0e197201cac7815a661a39efe1f0f52b006d 100644 --- a/flake.lock +++ b/flake.lock @@ -46,7 +46,7 @@ "dirtyRev": "979d17cf356e3a336dac8820c676ec813668222c-dirty", "dirtyShortRev": "979d17c-dirty", "lastModified": 1708504824, - "narHash": "sha256-xr0mfkl3yzwGcI+oDIi168tB/ERzehI1qO4W5bfji0s=", + "narHash": "sha256-4B1Tb847DHviKPsOLbKwjCUsrDCegXeKtDO7FTHgxts=", "type": "git", "url": "file:///Users/chrg/Develop/repos/hspec-glitter" }, diff --git a/rtree-c/package.yaml b/rtree-c/package.yaml index 9b4cfa28f18a716fa6222cf5e62f591babfc7b1e..41b1531366a781a2fd25dc59e27359407ade0539 100644 --- a/rtree-c/package.yaml +++ b/rtree-c/package.yaml @@ -50,3 +50,4 @@ tests: - directory - filepath - typed-process + - text diff --git a/rtree-c/rtree-c.cabal b/rtree-c/rtree-c.cabal index 72531846565992aa5b65cbe66705f3a1a03e3e67..e7b721695b53c28904bbd07164bf1fb506acd53a 100644 --- a/rtree-c/rtree-c.cabal +++ b/rtree-c/rtree-c.cabal @@ -80,6 +80,7 @@ test-suite rtree-c-test , pretty-simple , rtree , rtree-c + , text , transformers , typed-process , vector diff --git a/rtree-c/src/ReduceC.hs b/rtree-c/src/ReduceC.hs index cfaebdf3cc80576b92c4c9511ec562d1bedaadc4..7703d295767db9a3da13feb4cec7bc63c643e5d3 100644 --- a/rtree-c/src/ReduceC.hs +++ b/rtree-c/src/ReduceC.hs @@ -5,35 +5,167 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE RankNTypes #-} +{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE NoMonomorphismRestriction #-} module ReduceC where +import Control.Monad.Reader import Control.Monad.Reduce +import Data.Data +import Data.Foldable +import qualified Data.Map.Strict as Map import qualified Language.C as C data Context = Context + { keepMain :: !Bool + , typeDefs :: !(Map.Map C.Ident [C.CDeclarationSpecifier C.NodeInfo]) + } + +defaultReduceC :: (CReducible a, MonadReduce String m) => a -> m a +defaultReduceC a = runReaderT (reduceC a) defaultContext + +addTypeDefs :: [C.Ident] -> [C.CDeclarationSpecifier C.NodeInfo] -> Context -> Context +addTypeDefs ids cs Context{..} = + Context + { typeDefs = + foldl' (\a i -> Map.insert i cs a) typeDefs ids + , .. + } + +defaultContext :: Context +defaultContext = + Context + { keepMain = True + , typeDefs = Map.empty + } class CReducible a where - reduceC :: (MonadReduce String m) => a -> m a + reduceC :: (MonadReduce String m) => a -> ReaderT Context m a instance CReducible C.CTranslUnit where reduceC (C.CTranslUnit es ni) = do - es' <- rList es - -- es' <- collect reduceCExternalDeclaration es + es' <- reduceDeclarations es pure $ C.CTranslUnit es' ni where - rList (a : as) = rList as <| ((a :) <$> rList as) - rList [] = pure [] + reduceDeclarations = \case + [] -> pure [] + r : rest -> reduceCExternalDeclaration r (reduceDeclarations rest) + + reduceCExternalDeclaration r cont = do + shouldKeepMain <- asks 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 + 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 + split + ("inline typedefs " <> show ids) + (local (addTypeDefs ids rst) cont) + ((r :) <$> cont) + a -> error (show a) + _r -> error (show r) + +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 + +inlineTypeDefs :: forall d m. (Data d, 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 + res <- asks (Map.lookup idx . typeDefs) + case res of + Just args -> pure args + Nothing -> pure [a] + a -> pure [a] + pure (fold res') + Nothing -> + gmapM inlineTypeDefs r + +-- instance CReducible C.CExtDecl where +-- reduceC (C.CFunDef spc dec cdecls smt ni) = do +-- pure $ C.CFunDef spc dec cdecls smt ni + +identifiers :: forall a. (Data a) => a -> [C.Ident] +identifiers d = case cast d of + Just l -> [l] + Nothing -> concat $ gmapQ identifiers d --- reduceCExternalDeclaration = \case --- C.CFDefExt fun -> do --- C.CFDefExt <$> reduce @C.CFunctionDef fun --- C.CDeclExt decl -> --- C.CDeclExt <$> reduce @C.CDeclaration decl --- [] -> error (show a) +functionName :: C.CFunctionDef C.NodeInfo -> Maybe C.Ident +functionName = \case + C.CFunDef _ (C.CDeclr ix _ _ _ _) _ _ _ -> ix + +isMain :: C.CFunctionDef C.NodeInfo -> Bool +isMain (C.CFunDef _ (C.CDeclr (Just i) _ _ _ _) _ _ _) = + C.identToString i == "main" +isMain _ow = False + +-- 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) -- import Control.Monad.Reduce -- diff --git a/rtree-c/test/cases/typedef.c b/rtree-c/test/cases/typedef.c new file mode 100644 index 0000000000000000000000000000000000000000..c52771a3562daf1818c4651c20cea87cd942f7e4 --- /dev/null +++ b/rtree-c/test/cases/typedef.c @@ -0,0 +1,10 @@ +// Test typdefs +typedef int uint64; + +void f(uint64 a) { +} + +int main () { + uint64 x = 1; + return x; +} diff --git a/rtree-c/test/expected/main/reduction/r0.c b/rtree-c/test/expected/main/reduction/r.c similarity index 100% rename from rtree-c/test/expected/main/reduction/r0.c rename to rtree-c/test/expected/main/reduction/r.c diff --git a/rtree-c/test/expected/main/reduction/r1.c b/rtree-c/test/expected/main/reduction/r1.c deleted file mode 100644 index 8b137891791fe96927ad78e64b0aad7bded08bdc..0000000000000000000000000000000000000000 --- a/rtree-c/test/expected/main/reduction/r1.c +++ /dev/null @@ -1 +0,0 @@ - diff --git a/rtree-c/test/expected/typedef/main.c b/rtree-c/test/expected/typedef/main.c new file mode 100644 index 0000000000000000000000000000000000000000..2d12abfdb9cc226bafde9fb5149242644c4495a5 --- /dev/null +++ b/rtree-c/test/expected/typedef/main.c @@ -0,0 +1,9 @@ +typedef int uint64; +void f(uint64 a) +{ +} +int main() +{ + uint64 x = 1; + return x; +} diff --git a/rtree-c/test/expected/typedef/reduction/r00.c b/rtree-c/test/expected/typedef/reduction/r00.c new file mode 100644 index 0000000000000000000000000000000000000000..2d12abfdb9cc226bafde9fb5149242644c4495a5 --- /dev/null +++ b/rtree-c/test/expected/typedef/reduction/r00.c @@ -0,0 +1,9 @@ +typedef int uint64; +void f(uint64 a) +{ +} +int main() +{ + uint64 x = 1; + return x; +} diff --git a/rtree-c/test/expected/typedef/reduction/r01.c b/rtree-c/test/expected/typedef/reduction/r01.c new file mode 100644 index 0000000000000000000000000000000000000000..e187d4389678c2fe0beb909748d5068576881ad6 --- /dev/null +++ b/rtree-c/test/expected/typedef/reduction/r01.c @@ -0,0 +1,6 @@ +typedef int uint64; +int main() +{ + uint64 x = 1; + return x; +} diff --git a/rtree-c/test/expected/typedef/reduction/r10.c b/rtree-c/test/expected/typedef/reduction/r10.c new file mode 100644 index 0000000000000000000000000000000000000000..3ec3f43dc2f6d9812a949c37909b202f4e869600 --- /dev/null +++ b/rtree-c/test/expected/typedef/reduction/r10.c @@ -0,0 +1,8 @@ +void f(int a) +{ +} +int main() +{ + int x = 1; + return x; +} diff --git a/rtree-c/test/expected/typedef/reduction/r11.c b/rtree-c/test/expected/typedef/reduction/r11.c new file mode 100644 index 0000000000000000000000000000000000000000..2eb183391c1758706c3c6b3eb3d4f15461b17699 --- /dev/null +++ b/rtree-c/test/expected/typedef/reduction/r11.c @@ -0,0 +1,5 @@ +int main() +{ + int x = 1; + return x; +} diff --git a/rtree-c/test/src/ReduceCSpec.hs b/rtree-c/test/src/ReduceCSpec.hs index 0375b6757602d635bedda21465d7d5bc5c242b16..00c2ba06d9e96f5959aa97a07991d54668f3997b 100644 --- a/rtree-c/test/src/ReduceCSpec.hs +++ b/rtree-c/test/src/ReduceCSpec.hs @@ -9,6 +9,9 @@ import System.Directory import System.FilePath import Test.Hspec +import qualified Data.Text.Lazy as LazyText +import qualified Data.Text.Lazy.Encoding as LazyText + import Test.Hspec.Glitter import qualified Language.C as C @@ -42,13 +45,14 @@ spec = do describe "reduction" do it "should extract itself" do - extract (reduceC c) `shouldBe` c + extract (defaultReduceC c) `shouldBe` c onGlitterWith - (expected </> "reduction") + (expected </> "reduction/") ( \a () -> do + removeDirectoryRecursive a createDirectoryIfMissing True a - forM_ (take 20 $ iinputs (reduceC c)) \(i, c') -> do + forM_ (take 20 $ iinputs (defaultReduceC c)) \(i, c') -> do let rfile = expected </> "reduction" </> "r" <> debugShow i <.> "c" render rfile c' ) @@ -58,9 +62,14 @@ spec = do validate :: FilePath -> IO () validate fp = do - ec <- runProcess (proc "clang" ["-o", "/dev/null", fp]) + (ec, res) <- readProcessStderr (proc "clang" ["-o", "/dev/null", fp]) case ec of - ExitFailure _ -> fail ("could not validate " <> show fp) + ExitFailure _ -> + expectationFailure $ + "could not validate " + <> show fp + <> "\n" + <> LazyText.unpack (LazyText.decodeUtf8 res) ExitSuccess -> pure () render :: FilePath -> C.CTranslUnit -> IO ()