From 4bd2472b64e6a1b03ac82fc3ff8a84f9ac6b93c4 Mon Sep 17 00:00:00 2001 From: Christian Gram Kalhauge <chrg@dtu.dk> Date: Tue, 12 Dec 2023 14:03:46 +0100 Subject: [PATCH] Many good changes --- bin/rtree-c/Main.hs | 366 ++++++++++++++++++++++++++++++++++--------- flake.lock | 6 +- flake.nix | 5 +- package.yaml | 9 ++ rtree.cabal | 13 ++ src/Control/RTree.hs | 280 +++++++++++++++++++++------------ test/data/simple1.c | 3 +- 7 files changed, 501 insertions(+), 181 deletions(-) diff --git a/bin/rtree-c/Main.hs b/bin/rtree-c/Main.hs index c079c58..7710988 100644 --- a/bin/rtree-c/Main.hs +++ b/bin/rtree-c/Main.hs @@ -1,98 +1,322 @@ +{-# LANGUAGE ApplicativeDo #-} {-# LANGUAGE BlockArguments #-} {-# LANGUAGE DerivingVia #-} +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE LambdaCase #-} -{-# LANGUAGE PackageImports #-} +{-# LANGUAGE OverloadedStrings #-} import Control.RTree -import Data.Maybe (catMaybes, fromMaybe) +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 Language.C qualified as C -import "pretty" Text.PrettyPrint qualified as P -import "transformers" Control.Monad.Trans.Maybe -import "typed-process" System.Process.Typed +import Options.Applicative +import System.Directory +import System.Exit +import System.FilePath + +import Data.Text qualified as Text + +-- import System.Process.Typed + +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) 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 +main = + join + . execParser + $ info run + . 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 where - output l = do - writeFile "test.c" (P.render (C.pretty l)) + 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"] + + pure + $ usingLoggerT (cmap fmtMessage logTextStdout) + $ do + let + test f = process D ("test " <> Text.pack f) do + err <- liftIO $ runProcess (proc "clang" ["-O0", "test.c"]) + 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 + + check f _ c = MaybeT do + when validity do + liftIO $ copyFile file (file <.> "last") + output f c + t <- test f + if t + then pure (Just ()) + else do + liftIO $ when validity do + copyFile file (file <.> "fail") + copyFile (file <.> "last") file + exitFailure + pure Nothing - 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 + 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 <- runMaybeT (reduceFast (check file) (Map.singleton (C.internalIdent "main") True) (reduceC c)) + + case l of + Just l' -> do + output file l' + logInfo "Success" + Nothing -> do + logError "Unable to produce results" + liftIO exitFailure + 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 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 +reduceC :: (MonadReduce Lab m) => C.CTranslUnit -> m C.CTranslUnit +reduceC (C.CTranslUnit es ni) = do + es' <- collect mrCExternalDeclaration es + pure $ C.CTranslUnit es' ni -rCExternalDeclaration - :: C.CExternalDeclaration C.NodeInfo - -> RTree' Lab (Maybe (C.CExternalDeclaration C.NodeInfo)) -rCExternalDeclaration e = case e of +mrCExternalDeclaration :: (MonadReduce Lab m) => C.CExternalDeclaration C.NodeInfo -> MaybeT m (C.CExternalDeclaration C.NodeInfo) +mrCExternalDeclaration = \case C.CFDefExt fun -> split (funName fun) - (pure Nothing) - (Just . C.CFDefExt <$> rCFunctionDef fun) - _ -> pure Nothing <| pure (Just e) + empty + (C.CFDefExt <$> rCFunctionDef fun) + C.CDeclExt decl -> + C.CDeclExt <$> mrCDeclaration decl + a -> error (show a) 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 +mrCDeclarationItem :: (MonadReduce Lab m) => C.CDeclarationItem C.NodeInfo -> MaybeT m (C.CDeclarationItem C.NodeInfo) +mrCDeclarationItem = \case + C.CDeclarationItem d@(C.CDeclr x _ _ _ _) i e -> + split x empty do + i' <- mtry $ munder i mrCInitializer + e' <- mtry $ munder e mrCExpression + pure (C.CDeclarationItem d i' e') + 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 smt' <- rCStatement smt - pure $ C.CFunDef spc dec cdecls smt' C.undefNode + pure $ C.CFunDef spc dec cdecls smt' ni -rCStatement :: C.CStatement C.NodeInfo -> RTree' Lab (C.CStatement C.NodeInfo) +rCStatement :: (MonadReduce Lab m) => C.CStatement C.NodeInfo -> m (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 + C.CCompound is cbi ni -> do + cbi' <- collect mrCCompoundBlockItem cbi + pure $ C.CCompound is cbi' ni + C.CExpr e ni -> do + e' <- runMaybeT $ mlift e >>= mrCExpression + 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 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) + 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.CVar i ni -> + splitOn i empty (pure $ C.CVar i ni) + 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 -> ejoin 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 + C.CAssign op e1 e2 ni -> ejoin e1 e2 \e1' e2' -> + pure $ C.CAssign op e1' e2' ni + C.CIndex e1 e2 ni -> ejoin e1 e2 \e1' e2' -> + pure $ C.CIndex e1' e2' ni + C.CMember e i b ni -> do + splitOn i empty do + 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 + ejoin elhs erhs = mjoin (mrCExpression elhs) (mrCExpression erhs) + +mjoin :: (Monad m) => MaybeT m a -> MaybeT m a -> (a -> a -> MaybeT m a) -> MaybeT m a +mjoin mlhs mrhs fn = MaybeT do + lhs <- runMaybeT mlhs + case lhs of + Nothing -> runMaybeT mrhs + Just l -> do + rhs <- runMaybeT mrhs + case rhs of + Nothing -> pure (Just l) + Just r -> runMaybeT (fn l r) + +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) diff --git a/flake.lock b/flake.lock index 4a4c323..f37ab3b 100644 --- a/flake.lock +++ b/flake.lock @@ -21,11 +21,11 @@ "language-c": { "flake": false, "locked": { - "lastModified": 1701177364, - "narHash": "sha256-SwRI8+PNfzfHOjFcn7bvgAylJeUMaFsvlJPm2r3QhTY=", + "lastModified": 1702044640, + "narHash": "sha256-jCpGlWLH6qnsskMnEOCAnYCKCwknpZv46cq2BmA4/cw=", "owner": "kalhauge", "repo": "language-c", - "rev": "bdbf9f641149f5879dc23eb9e153e573d9355cbd", + "rev": "cca7c0b315cb0594071a546587bea79292e0c3d7", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index b59c471..eee7af2 100644 --- a/flake.nix +++ b/flake.nix @@ -42,7 +42,7 @@ rtree = hpkgs.rtree; }; devShells = let - buildInputs = with hpkgs; [ + nativeBuildInputs = with hpkgs; [ cabal-install ghcid haskell-language-server @@ -56,7 +56,8 @@ { name = "rtree-shell"; packages = p: [p.rtree]; - inherit buildInputs withHoogle; + doBenchmark = true; + inherit nativeBuildInputs withHoogle; }; }; }); diff --git a/package.yaml b/package.yaml index 6edd0bd..3024540 100644 --- a/package.yaml +++ b/package.yaml @@ -13,7 +13,10 @@ dependencies: - free - data-fix - mtl + - directory - containers + - text + - pretty-simple library: source-dirs: src @@ -24,9 +27,15 @@ executables: main: Main.hs dependencies: - rtree + - optparse-applicative - language-c - typed-process + - directory - pretty + - filepath + - co-log + - time + - text tests: rtree-test: diff --git a/rtree.cabal b/rtree.cabal index 63f91f5..2a020e9 100644 --- a/rtree.cabal +++ b/rtree.cabal @@ -20,8 +20,11 @@ library base >=4.9 && <5 , containers , data-fix + , directory , free , mtl + , pretty-simple + , text , transformers default-language: Haskell2010 @@ -34,13 +37,20 @@ executable rtree-c ghc-options: -Wall -fno-warn-incomplete-uni-patterns build-depends: base >=4.9 && <5 + , co-log , containers , data-fix + , directory + , filepath , free , language-c , mtl + , optparse-applicative , pretty + , pretty-simple , rtree + , text + , time , transformers , typed-process default-language: Haskell2010 @@ -62,8 +72,11 @@ test-suite rtree-test , diagrams-core , diagrams-lib , diagrams-svg + , directory , free , mtl + , pretty-simple , rtree + , text , transformers default-language: Haskell2010 diff --git a/src/Control/RTree.hs b/src/Control/RTree.hs index 0fc4b15..de354a9 100644 --- a/src/Control/RTree.hs +++ b/src/Control/RTree.hs @@ -3,13 +3,12 @@ {-# LANGUAGE DerivingVia #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE LambdaCase #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE PackageImports #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeSynonymInstances #-} +{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ViewPatterns #-} {- | @@ -17,139 +16,212 @@ Module: Control.RTree -} module Control.RTree where -import Control.Applicative (Alternative ((<|>))) -import Data.Coerce (coerce) -import Data.Functor.Classes +import Control.Applicative +import Control.Monad.Reader +import Control.Monad.Trans.Maybe import qualified Data.List.NonEmpty as NE +import qualified Data.Map.Strict as Map +import Data.Maybe import Data.Void +import GHC.IORef -import qualified Data.List as L - -import Control.Monad.Reader -import "free" Control.Monad.Free.Church - --- | The base functor for the reduction tree. -data RTreeF l f - = Split (Maybe l) f f - deriving (Show, Eq, Functor) - -instance (Show l) => Show1 (RTreeF l) where - liftShowsPrec = undefined - -newtype RTree l i = RTree {rtreeFree :: F (RTreeF l) i} - deriving (Functor, Applicative, Monad) via (F (RTreeF l)) - -instance MonadFree (RTreeF l) (RTree l) where - wrap x = RTree (wrap (fmap rtreeFree x)) +class (Monad m) => MonadReduce l m | m -> l where + split :: Maybe l -> m i -> m i -> m i infixr 3 <| infixl 3 |> {-# INLINE (<|) #-} -(<|) :: (MonadFree (RTreeF l) r) => r i -> r i -> r i -r1 <| r2 = wrap (Split Nothing r1 r2) +(<|) :: (MonadReduce l r) => r i -> r i -> r i +r1 <| r2 = split Nothing 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) - -{-# INLINE split #-} -split :: (MonadFree (RTreeF l) r) => Maybe l -> r i -> r i -> r i -split l r1 r2 = wrap (Split l r1 r2) +splitOn :: (MonadReduce l r) => l -> r i -> r i -> r i +splitOn l = split (Just l) {-# INLINE (|>) #-} -(|>) :: (MonadFree (RTreeF l) r) => r i -> r i -> r i +(|>) :: (MonadReduce 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 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)) +data RTree l i + = Split (RTree l i) !(RTree l i) + | SplitOn !l (RTree l i) !(RTree l i) | Done i + deriving (Functor) -extract' :: RTree' l i -> i -extract' = \case - RTree' (Split _ _ v) -> extract' v +extract :: RTree l i -> i +extract = \case + Split _ rhs -> extract rhs + SplitOn _ _ rhs -> extract rhs 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 +instance Applicative (RTree l) where pure = Done (<*>) = ap -instance Monad (RTree' l) where +instance Monad (RTree l) where ma >>= f = case ma of Done i -> f i - RTree' r -> - RTree' - (fmap (>>= f) r) + Split lhs rhs -> + Split (lhs >>= f) (rhs >>= f) + SplitOn l lhs rhs -> + SplitOn l (lhs >>= f) (rhs >>= f) -instance MonadFree (RTreeF l) (RTree' l) where - wrap = RTree' - {-# INLINE wrap #-} +instance MonadReduce l (RTree l) where + split = \case + Just n -> SplitOn n + Nothing -> Split --- | Reduce an input using a monad. -reduce' +reduce :: forall m l i . (Alternative m) => (i -> m ()) - -> RTree' l i + -> RTree l i -> m i -reduce' p = checkgo +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 + (Split lhs rhs) -> + (checkgo lhs Control.Applicative.<|> go rhs) + (SplitOn _ lhs rhs) -> + (checkgo lhs Control.Applicative.<|> go rhs) + checkgo rt = p (extract rt) *> go rt +{-# SPECIALIZE reduce :: (i -> MaybeT IO ()) -> RTree l i -> MaybeT IO i #-} + +type Valuation l = Map.Map l Bool + +extractL :: (Ord l) => Valuation l -> RTree l i -> i +extractL v = \case + Split _ rhs -> extractL v rhs + SplitOn l lhs rhs -> case Map.lookup l v of + Just False -> extractL v lhs + _ -> extractL v rhs + Done i -> i + +reduceL + :: forall m l i + . (Alternative m, Ord l) + => (Valuation l -> i -> m ()) + -> Valuation l + -> RTree l i + -> m i +reduceL p = checkgo + where + checkgo v r = p v (extractL v r) *> go v r + go v = \case + Done i -> pure i + SplitOn l lhs rhs -> case Map.lookup l v of + Just True -> checkgo v rhs + Just False -> checkgo v lhs + Nothing -> checkgo (Map.insert l False v) lhs <|> go (Map.insert l True v) rhs + Split lhs rhs -> (checkgo v lhs <|> go v rhs) +{-# INLINE reduceL #-} + +data ReState l = ReState ![Bool] !(Valuation l) + +newtype ReReduce l i = ReReduce {runReReduce :: IORef (ReState l) -> IO i} + deriving (Functor, Applicative, Monad) via (ReaderT (IORef (ReState l)) IO) + +instance (Ord l) => MonadReduce l (ReReduce l) where + split ml r1 r2 = ReReduce \ref -> do + test <- case ml of + Nothing -> do + atomicModifyIORef' + ref + ( \case + ReState (a : as) v -> (ReState as v, a) + ReState [] v -> (ReState [] v, False) + ) + Just l -> do + atomicModifyIORef' + ref + ( \case + ReState as v@(Map.lookup l -> Just x) -> (ReState as v, not x) + ReState (a : as) v -> (ReState as (Map.insert l (not a) v), a) + ReState [] v -> (ReState [] (Map.insert l True v), False) + ) + if test + then runReReduce r1 ref + else runReReduce r2 ref + +reduceFast + :: forall m l i + . (MonadIO m, Ord l) + => (Valuation l -> i -> MaybeT m ()) + -> Valuation l + -> ReReduce l i + -> MaybeT m i +reduceFast p v (ReReduce ext) = MaybeT $ go [] + where + go pth = do + ref <- liftIO $ newIORef (ReState pth v) + i <- liftIO $ ext ref + ReState r v' <- liftIO $ readIORef ref + case r of + [] -> do + t <- isJust <$> runMaybeT (p v' i) + if t + then go (pth <> [True]) + else go (pth <> [False]) + _ + | null pth -> + pure Nothing + | otherwise -> + pure (Just i) +{-# INLINE reduceFast #-} + +-- Combinators + +type MRTree l = MaybeT (RTree l) + +instance (MonadReduce l m) => MonadReduce l (MaybeT m) where + split m (MaybeT lhs) (MaybeT rhs) = MaybeT (split m lhs rhs) + +-- | Given a list of item try to remove each of them the list. +collect :: (MonadReduce l m) => (a -> MaybeT m b) -> [a] -> m [b] +collect fn = fmap catMaybes . traverse (runMaybeT . fn) +{-# INLINE collect #-} + +collectNonEmpty' :: (MonadReduce l m) => (a -> MaybeT m b) -> [a] -> MaybeT m [b] +collectNonEmpty' fn as = + NE.toList <$> collectNonEmpty fn as +{-# INLINE collectNonEmpty' #-} + +collectNonEmpty :: (MonadReduce l m) => (a -> MaybeT m b) -> [a] -> MaybeT m (NE.NonEmpty b) +collectNonEmpty fn as = do + as' <- lift . fmap catMaybes . traverse (runMaybeT . fn) $ as + MaybeT . pure $ NE.nonEmpty as' +{-# INLINE collectNonEmpty #-} + +-- newtype LTree l i = LTree {runLTree :: Valuation l -> Maybe (RTree l i)} +-- deriving (Functor) +-- +-- instance Applicative (LTree l) where +-- pure i = LTree{runLTree = \_ -> Just $ Done i} +-- (<*>) = ap +-- +-- instance Monad (LTree l) where +-- LTree ma >>= f = LTree \l -> +-- case ma l of +-- Done i -> f i +-- Split l lhs rhs -> + +-- 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 MonadFree (RTreeF l) (RTree' l) where +-- wrap = RTree' +-- {-# INLINE wrap #-} + +-- | Reduce an input using a monad. -- newtype I l i = I ([(l, Bool)] -> RTreeI l i) -- diff --git a/test/data/simple1.c b/test/data/simple1.c index 3edd15b..206e198 100644 --- a/test/data/simple1.c +++ b/test/data/simple1.c @@ -2,5 +2,6 @@ extern int printf (const char *, ...); int main (void) { - return 0; + printf("hello"); + return 0; } -- GitLab