diff --git a/.gitignore b/.gitignore index 1910193313905b3d221358da66f945645b6dee28..b15d36b9801de73621543eb91dddd8d21d273fe4 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,7 @@ cabal.project.local~ .DS_Store result + +a.out +rtree-c +test.c diff --git a/bin/rtree-c/Main.hs b/bin/rtree-c/Main.hs new file mode 100644 index 0000000000000000000000000000000000000000..c079c585d0650a38ffc02cbf18db85bd32a6d699 --- /dev/null +++ b/bin/rtree-c/Main.hs @@ -0,0 +1,98 @@ +{-# LANGUAGE BlockArguments #-} +{-# LANGUAGE DerivingVia #-} +{-# LANGUAGE ImportQualifiedPost #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE PackageImports #-} + +import Control.RTree + +import Data.Maybe (catMaybes, fromMaybe) +import Language.C qualified as C +import "pretty" Text.PrettyPrint qualified as P +import "transformers" Control.Monad.Trans.Maybe +import "typed-process" System.Process.Typed + +main :: IO () +main = do + C.parseCFilePre "test/data/file2.c" >>= \case + Right file -> do + l <- runMaybeT (reduce' check (reduceC file)) + case l of + Just l' -> output l' + Nothing -> + putStrLn "Failure" + Left err -> + print err + where + output l = do + writeFile "test.c" (P.render (C.pretty l)) + + check l = MaybeT do + putStrLn "Outputting test" + output l + putStrLn "Running test" + err <- runProcess (proc "clang" ["-O0", "test.c"]) + putStrLn $ "Done test" <> show err + pure $ if err == ExitSuccess then Just () else Nothing + +type Lab = C.Ident + +reduceC :: C.CTranslUnit -> RTree' Lab C.CTranslUnit +reduceC (C.CTranslUnit es _) = do + es' <- traverse rCExternalDeclaration es + pure $ C.CTranslUnit (catMaybes es') C.undefNode + +rCExternalDeclaration + :: C.CExternalDeclaration C.NodeInfo + -> RTree' Lab (Maybe (C.CExternalDeclaration C.NodeInfo)) +rCExternalDeclaration e = case e of + C.CFDefExt fun -> + split + (funName fun) + (pure Nothing) + (Just . C.CFDefExt <$> rCFunctionDef fun) + _ -> pure Nothing <| pure (Just e) + where + funName (C.CFunDef _ (C.CDeclr x _ _ _ _) _ _ _) = + x + +rCFunctionDef :: C.CFunctionDef C.NodeInfo -> RTree' Lab (C.CFunctionDef C.NodeInfo) +rCFunctionDef (C.CFunDef spc dec cdecls smt _) = do + smt' <- rCStatement smt + pure $ C.CFunDef spc dec cdecls smt' C.undefNode + +rCStatement :: C.CStatement C.NodeInfo -> RTree' Lab (C.CStatement C.NodeInfo) +rCStatement = \case + C.CCompound is cbi _ -> do + cbi' <- traverse rCCompoundBlockItem cbi + pure $ C.CCompound is (catMaybes cbi') C.undefNode + C.CExpr (Just e) _ -> do + e' <- rCExpression e + pure $ C.CExpr e' C.undefNode + a -> pure a + +rCExpression :: C.CExpression C.NodeInfo -> RTree' Lab (Maybe (C.CExpression C.NodeInfo)) +rCExpression = \case + C.CVar i _ -> + splitOn + i + (pure Nothing) + (pure . Just $ C.CVar i C.undefNode) + C.CCall e es _ -> do + me' <- rCExpression e + case me' of + Nothing -> pure Nothing + Just e' -> do + es' <- + traverse + ( fmap + ( fromMaybe (C.CConst (C.CIntConst (C.cInteger 0) C.undefNode)) + ) + . rCExpression + ) + es + pure . Just $ C.CCall e' es' C.undefNode + e -> pure Nothing <| pure (Just e) + +rCCompoundBlockItem :: C.CCompoundBlockItem C.NodeInfo -> RTree' Lab (Maybe (C.CCompoundBlockItem C.NodeInfo)) +rCCompoundBlockItem a = pure Nothing <| pure (Just a) diff --git a/flake.lock b/flake.lock index 435e42fb71f3e769c19867595b0f6492ae18a28d..4a4c3230cba0c52cb026adb2acba9734b3878b93 100644 --- a/flake.lock +++ b/flake.lock @@ -21,11 +21,11 @@ "language-c": { "flake": false, "locked": { - "lastModified": 1664454938, - "narHash": "sha256-GDjXcq0oYNDGSIWO6kkIgF13RMwykDpUyAQAWRYEOUc=", + "lastModified": 1701177364, + "narHash": "sha256-SwRI8+PNfzfHOjFcn7bvgAylJeUMaFsvlJPm2r3QhTY=", "owner": "kalhauge", "repo": "language-c", - "rev": "0b2f7bf94789b09bbf1e7a1ab80b62f99e1e92f7", + "rev": "bdbf9f641149f5879dc23eb9e153e573d9355cbd", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 3bcb152f47721245f6da431f0c95c4f14fb25543..b59c471762e70910ab449448786f3f673ba69ff1 100644 --- a/flake.nix +++ b/flake.nix @@ -2,63 +2,62 @@ inputs = { nixpkgs.url = github:nixos/nixpkgs/nixpkgs-unstable; flake-utils.url = github:numtide/flake-utils; - language-c = { url = github:kalhauge/language-c; flake = false; }; + language-c = { + url = github:kalhauge/language-c; + flake = false; + }; }; - outputs = - { self - , nixpkgs - , flake-utils - , ... - }@inputs: - let - packages = final: p: { - "rtree" = p.callCabal2nixWithOptions "rtree" "${self}" "" { }; - "language-c" = - final.haskell.lib.overrideCabal - (p.callCabal2nixWithOptions "language-c" inputs.language-c "" { }) - { - doCheck = false; - }; - }; - overlays = final: prev: { - haskellPackages = prev.haskellPackages.extend (p: _: packages final p); - }; - in + outputs = { + self, + nixpkgs, + flake-utils, + ... + } @ inputs: let + packages = final: p: { + "rtree" = p.callCabal2nixWithOptions "rtree" "${self}" "" {}; + "language-c" = + final.haskell.lib.overrideCabal + (p.callCabal2nixWithOptions "language-c" inputs.language-c "" {}) { + doCheck = false; + }; + }; + overlays = final: prev: { + haskellPackages = prev.haskellPackages.extend (p: _: packages final p); + }; + in { overlays.default = overlays; - } // - flake-utils.lib.eachDefaultSystem - (system: - let - hpkgs = (import nixpkgs { + } + // flake-utils.lib.eachDefaultSystem + (system: let + hpkgs = + (import nixpkgs { inherit system; - overlays = [ overlays ]; - }).haskellPackages; - in - rec { - packages = { - default = hpkgs.rtree; - rtree = hpkgs.rtree; - }; - devShells = - let - buildInputs = with hpkgs; [ - cabal-install - ghcid - haskell-language-server - hpack - fourmolu - ]; - withHoogle = true; - in + overlays = [overlays]; + }) + .haskellPackages; + in rec { + packages = { + default = hpkgs.rtree; + rtree = hpkgs.rtree; + }; + devShells = let + buildInputs = with hpkgs; [ + cabal-install + ghcid + haskell-language-server + hpack + fourmolu + ]; + withHoogle = true; + in { + default = + hpkgs.shellFor { - default = hpkgs.shellFor - { - name = "rtree-shell"; - packages = p: - [ p.rtree ]; - inherit buildInputs withHoogle; - }; + name = "rtree-shell"; + packages = p: [p.rtree]; + inherit buildInputs withHoogle; }; - }); + }; + }); } diff --git a/package.yaml b/package.yaml index 4b6f4f7f083f70aee90800274baaa337ae24f103..6edd0bdbf6ef7aa45efaa9a473343692cdf2b043 100644 --- a/package.yaml +++ b/package.yaml @@ -5,7 +5,7 @@ name: rtree # category: categories # extra-source-files: [] -ghc-options: -Wall -fno-warn-incomplete-uni-patterns -Werror +ghc-options: -Wall -fno-warn-incomplete-uni-patterns dependencies: - base >= 4.9 && < 5 @@ -13,11 +13,21 @@ dependencies: - free - data-fix - mtl - - language-c + - containers library: source-dirs: src +executables: + rtree-c: + source-dirs: bin/rtree-c + main: Main.hs + dependencies: + - rtree + - language-c + - typed-process + - pretty + tests: rtree-test: source-dirs: test/src diff --git a/rtree.cabal b/rtree.cabal index 102208a5df085cbe23777c5615a511699ebe71f3..63f91f524025cf901b9ccad63b5fab7ffb18c937 100644 --- a/rtree.cabal +++ b/rtree.cabal @@ -15,14 +15,34 @@ library Paths_rtree hs-source-dirs: src - ghc-options: -Wall -fno-warn-incomplete-uni-patterns -Werror + ghc-options: -Wall -fno-warn-incomplete-uni-patterns build-depends: base >=4.9 && <5 + , containers + , data-fix + , free + , mtl + , transformers + default-language: Haskell2010 + +executable rtree-c + main-is: Main.hs + other-modules: + Paths_rtree + hs-source-dirs: + bin/rtree-c + ghc-options: -Wall -fno-warn-incomplete-uni-patterns + build-depends: + base >=4.9 && <5 + , containers , data-fix , free , language-c , mtl + , pretty + , rtree , transformers + , typed-process default-language: Haskell2010 test-suite rtree-test @@ -32,9 +52,10 @@ test-suite rtree-test Paths_rtree hs-source-dirs: test/src - ghc-options: -Wall -fno-warn-incomplete-uni-patterns -Werror + ghc-options: -Wall -fno-warn-incomplete-uni-patterns build-depends: base >=4.9 && <5 + , containers , data-fix , diagrams , diagrams-contrib @@ -42,7 +63,6 @@ test-suite rtree-test , diagrams-lib , diagrams-svg , free - , language-c , mtl , rtree , transformers diff --git a/src/Control/RTree.hs b/src/Control/RTree.hs index 857cc8208a937e66684edb96490a8d6bfc21315a..0fc4b159ef76219527ba46320d2a522eaa2b1741 100644 --- a/src/Control/RTree.hs +++ b/src/Control/RTree.hs @@ -2,11 +2,13 @@ {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PackageImports #-} {-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE ViewPatterns #-} @@ -16,61 +18,191 @@ Module: Control.RTree module Control.RTree where import Control.Applicative (Alternative ((<|>))) -import Data.Functor +import Data.Coerce (coerce) import Data.Functor.Classes import qualified Data.List.NonEmpty as NE +import Data.Void -import Data.Coerce (coerce) -import "free" Control.Monad.Free +import qualified Data.List as L + +import Control.Monad.Reader +import "free" Control.Monad.Free.Church --- | The reduction tree -data RTreeF f - = f :<| f - | Lab String f f +-- | The base functor for the reduction tree. +data RTreeF l f + = Split (Maybe l) f f deriving (Show, Eq, Functor) -instance Show1 RTreeF where +instance (Show l) => Show1 (RTreeF l) where liftShowsPrec = undefined -newtype RTree i = RTree {rtreeFree :: Free RTreeF i} - deriving (Show) - deriving (Functor, Applicative, Monad) via (Free RTreeF) +newtype RTree l i = RTree {rtreeFree :: F (RTreeF l) i} + deriving (Functor, Applicative, Monad) via (F (RTreeF l)) -instance MonadFree RTreeF RTree where - wrap x = RTree (Free (fmap rtreeFree x)) +instance MonadFree (RTreeF l) (RTree l) where + wrap x = RTree (wrap (fmap rtreeFree x)) infixr 3 <| infixl 3 |> -(<|) :: (MonadFree RTreeF r) => r i -> r i -> r i -r1 <| r2 = wrap (r1 :<| r2) +{-# INLINE (<|) #-} +(<|) :: (MonadFree (RTreeF l) r) => r i -> r i -> r i +r1 <| r2 = wrap (Split Nothing r1 r2) -lab :: (MonadFree RTreeF r) => String -> r i -> r i -> r i -lab l r1 r2 = wrap (Lab l r1 r2) +{-# INLINE splitOn #-} +splitOn :: (MonadFree (RTreeF l) r) => l -> r i -> r i -> r i +splitOn l r1 r2 = wrap (Split (Just l) r1 r2) -(|>) :: (MonadFree RTreeF r) => r i -> r i -> r i -r1 |> r2 = wrap (r2 :<| r1) +{-# INLINE split #-} +split :: (MonadFree (RTreeF l) r) => Maybe l -> r i -> r i -> r i +split l r1 r2 = wrap (Split l r1 r2) -foldR :: (RTreeF a -> a) -> RTree a -> a +{-# INLINE (|>) #-} +(|>) :: (MonadFree (RTreeF l) r) => r i -> r i -> r i +r1 |> r2 = r2 <| r1 + +{-# INLINE foldR #-} +foldR :: (RTreeF l a -> a) -> RTree l a -> a foldR fn = coerce $ iter fn +foldRM :: (Monad m) => (RTreeF l (m a) -> m a) -> RTree l a -> m a +foldRM fn = coerce $ iterM fn + -- | Extract the input from the reducer. -extract :: RTree i -> i -extract = foldR \case - (_ :<| e) -> e - Lab _ _ e -> e +extract :: RTree l i -> i +extract = foldR \(Split _ _ e) -> e + +-- | Remove all labels from a RTree by expanding all choices. +flatten :: forall i l. (Eq l) => RTree l i -> Maybe (RTree Void i) +flatten t = foldR go (fmap (const . Just . pure) t) [] + where + go (Split ml lhs rhs) lst = + case ml of + Just l -> case l `L.lookup` lst of + Nothing -> do + join' (lhs $ (l, False) : lst) (rhs $ (l, True) : lst) + Just True -> + join' (lhs lst) (rhs lst) + Just False -> + Nothing + Nothing -> join' (lhs lst) (rhs lst) + + join' mlhs mrhs = do + case (mlhs, mrhs) of + (Just lhs', Just rhs') -> pure (lhs' <| rhs') + _ -> mlhs <|> mrhs + +-- | Reduce an input using a monad. +reduce + :: forall m i + . (Alternative m) + => (i -> m ()) + -> RTree Void i + -> m i +reduce p t = do + let (mi, i') = foldR go $ fmap (\i -> (pure i, i)) t + p i' *> mi + where + go :: RTreeF l (m i, i) -> (m i, i) + go (Split _ (lhs, le) (rhs, re)) = + ((p le *> lhs) <|> rhs, re) +{-# INLINE reduce #-} + +data RTree' l i + = RTree' (RTreeF l (RTree' l i)) + | Done i + +extract' :: RTree' l i -> i +extract' = \case + RTree' (Split _ _ v) -> extract' v + Done v -> v + +instance Functor (RTree' l) where + fmap f (Done i) = Done (f i) + fmap f (RTree' r) = RTree' (fmap (fmap f) r) + +instance Applicative (RTree' l) where + pure = Done + (<*>) = ap + +instance Monad (RTree' l) where + ma >>= f = case ma of + Done i -> f i + RTree' r -> + RTree' + (fmap (>>= f) r) + +instance MonadFree (RTreeF l) (RTree' l) where + wrap = RTree' + {-# INLINE wrap #-} -- | Reduce an input using a monad. -reduce :: (Alternative m) => (i -> m ()) -> RTree i -> m i -reduce fn = - ( foldR \case - lhs :<| rhs -> lhs <|> rhs - Lab _ lhs rhs -> lhs <|> rhs - ) - . fmap (\i -> fn i $> i) +reduce' + :: forall m l i + . (Alternative m) + => (i -> m ()) + -> RTree' l i + -> m i +reduce' p = checkgo + where + go = \case + (Done i) -> pure i + (RTree' (Split _ lhs rhs)) -> + (checkgo lhs <|> go rhs) + checkgo rt = p (extract' rt) *> go rt + +-- newtype I l i = I ([(l, Bool)] -> RTreeI l i) +-- +-- data RTreeI l i +-- = RTreeI (RTreeF l (I l i)) +-- | DoneI !i + +-- -- This is not a great defintions, as the i does not depend on +-- -- the current i, but instead on the final I. +-- data RTreeIO j i = RTreeIO ((j -> IO Bool) -> IO i) j +-- +-- extractIO :: RTreeIO j i -> j +-- extractIO (RTreeIO _ i) = i + +-- instance Functor (RTreeIO j) where +-- fmap f (RTreeIO mf i) = RTreeIO (\h -> f <$> mf (h . f)) (f i) +-- +-- instance Applicative (RTreeIO j) where +-- pure i = RTreeIO (\_ -> pure i) i +-- (<*>) = ap +-- +-- -- RTreeIO f fi <*> RTreeIO a ai = RTreeIO (f <*> a) (fi ai) +-- +-- instance Monad (RTreeIO j) where +-- RTreeIO (ma :: ((a -> IO Bool) -> IO a)) a >>= (f :: (a -> RTreeIO b)) = +-- RTreeIO undefined (extractIO $ f a) +-- +-- instance MonadFree (RTreeF Void) (RTreeIO j) where +-- wrap (Split Nothing (RTreeIO lhs le) (RTreeIO rhs re)) = +-- RTreeIO +-- ( \p -> +-- p le >>= \case +-- True -> lhs p +-- False -> rhs p +-- ) +-- re +-- wrap (Split (Just x) _ _) = absurd x + +-- reduceIO +-- :: forall i +-- . (i -> IO Bool) +-- -> RTreeIO j i +-- -> IO (Maybe i) +-- reduceIO p (RTreeIO rt i) = runMaybeT do +-- let (mi, i') = foldR go $ fmap (\i -> (pure i, i)) t +-- p i' *> mi +-- where +-- go :: RTreeF l (IO i, i) -> (IO i, i) +-- go (Split _ (lhs, le) (rhs, re)) = +-- ((p le *> lhs) <|> rhs, re) -- | Split the world on a fact. False it does not happen, and True it does happen. -given :: RTree Bool +given :: RTree Void Bool given = pure False <| pure True {- | A reducer should extract itself @@ -78,11 +210,11 @@ given = pure False <| pure True extract . red = id @ -} -lawReduceId :: (Eq i) => (i -> RTree i) -> i -> Bool +lawReduceId :: (Eq i) => (i -> RTree l i) -> i -> Bool lawReduceId red i = extract (red i) == i -- | Reducing a list one element at a time. -rList :: [a] -> RTree [a] +rList :: [a] -> RTree l [a] rList = \case [] -> pure [] a : as -> rList as <| (a :) <$> rList as @@ -90,7 +222,7 @@ rList = \case {- | Binary reduction on the list assumming suffixes all contain eachother: @[] < [c] < [b, c] < [a,b,c]@ -} -rSuffixList :: [a] -> RTree [a] +rSuffixList :: [a] -> RTree l [a] rSuffixList as = do res <- exponentialSearch (NE.tails as) case res of @@ -100,7 +232,7 @@ rSuffixList as = do {- | Given a progression of inputs that are progressively larger, pick the smallest using binary search. -} -binarySearch :: NE.NonEmpty i -> RTree i +binarySearch :: NE.NonEmpty i -> RTree l i binarySearch = \case a NE.:| [] -> pure a d -> binarySearch l <| binarySearch f @@ -110,7 +242,7 @@ binarySearch = \case {- | Given a progression of inputs that are progressively larger, pick the smallest using binary search. -} -exponentialSearch :: NE.NonEmpty i -> RTree i +exponentialSearch :: NE.NonEmpty i -> RTree l i exponentialSearch = go 1 where go n = \case @@ -126,15 +258,15 @@ nonEmptyOr msg ls = case NE.nonEmpty ls of Nothing -> error msg -- | Given a list of orderd options, the -linearSearch :: NE.NonEmpty i -> RTree i +linearSearch :: NE.NonEmpty i -> RTree l i linearSearch = foldr1 (<|) . fmap pure -- | Given a list of orderd options, the -linearSearch' :: [i] -> RTree (Maybe i) +linearSearch' :: [i] -> RTree l (Maybe i) linearSearch' is = linearSearch (NE.fromList $ fmap Just is ++ [Nothing]) -- | Given -ddmin :: [i] -> RTree [i] +ddmin :: [i] -> RTree l [i] ddmin = \case [] -> pure [] [a] -> pure [a] diff --git a/test/data/.gitignore b/test/data/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..74345629919684fb947bcfb72f363716baa1d1c1 --- /dev/null +++ b/test/data/.gitignore @@ -0,0 +1 @@ +file2.c diff --git a/test/data/simple1.c b/test/data/simple1.c new file mode 100644 index 0000000000000000000000000000000000000000..3edd15b6dd90bb2f7bff68ab0888b5b2357f84c2 --- /dev/null +++ b/test/data/simple1.c @@ -0,0 +1,6 @@ +extern int printf (const char *, ...); + +int main (void) +{ + return 0; +}