Skip to content
Snippets Groups Projects
ReduceC.hs 26 KiB
Newer Older
  • Learn to ignore specific revisions
  • chrg's avatar
    chrg committed
    {-# LANGUAGE BlockArguments #-}
    
    {-# LANGUAGE ConstraintKinds #-}
    
    chrg's avatar
    chrg committed
    {-# LANGUAGE FlexibleContexts #-}
    
    {-# LANGUAGE FlexibleInstances #-}
    
    chrg's avatar
    chrg committed
    {-# LANGUAGE GADTs #-}
    {-# LANGUAGE LambdaCase #-}
    
    {-# LANGUAGE RankNTypes #-}
    
    {-# LANGUAGE RecordWildCards #-}
    
    {-# LANGUAGE ScopedTypeVariables #-}
    
    {-# LANGUAGE TypeApplications #-}
    
    chrg's avatar
    chrg committed
    {-# LANGUAGE TypeFamilies #-}
    {-# LANGUAGE NoMonomorphismRestriction #-}
    
    
    chrg's avatar
    chrg committed
    module ReduceC (
      defaultReduceC,
      reduceCTranslUnit,
    
      -- * Context
      Context (..),
      defaultContext,
    
      -- * Helpers
      prettyIdent,
    ) where
    
    chrg's avatar
    chrg committed
    
    import Control.Monad.Reduce
    
    import Data.Data
    import Data.Foldable
    
    chrg's avatar
    chrg committed
    import Data.Function
    
    chrg's avatar
    chrg committed
    import Data.Functor
    
    import qualified Data.Map.Strict as Map
    
    chrg's avatar
    chrg committed
    import Data.Maybe
    
    chrg's avatar
    chrg committed
    import qualified Data.Set as Set
    import Data.Vector.Internal.Check (HasCallStack)
    
    chrg's avatar
    chrg committed
    import qualified Language.C as C
    
    chrg's avatar
    chrg committed
    import qualified Language.C.Data.Ident as C
    
    chrg's avatar
    chrg committed
    
    
    data Context = Context
    
    chrg's avatar
    chrg committed
      { keywords :: !(Set.Set Keyword)
    
      , typeDefs :: !(Map.Map C.Ident [C.CDeclarationSpecifier C.NodeInfo])
    
    chrg's avatar
    chrg committed
      , inlineExprs :: !(Map.Map C.Ident C.CExpr)
    
    chrg's avatar
    chrg committed
    data Keyword
      = KeepMain
      | DoNoops
      | NoSemantics
    
    chrg's avatar
    chrg committed
      | AllowEmptyDeclarations
    
    chrg's avatar
    chrg committed
      | DisallowVariableInlining
    
    chrg's avatar
    chrg committed
      deriving (Show, Read, Enum, Eq, Ord)
    
    
    chrg's avatar
    chrg committed
    type Lab = (String, C.Position)
    
    chrg's avatar
    chrg committed
    
    
    chrg's avatar
    chrg committed
    defaultReduceC :: (MonadReduce (String, C.Position) m) => C.CTranslUnit -> m C.CTranslUnit
    defaultReduceC a = reduceCTranslUnit 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
        , ..
        }
    
    
    chrg's avatar
    chrg committed
    addInlineExpr :: C.Ident -> C.CExpr -> Context -> Context
    addInlineExpr i e Context{..} =
      Context
        { inlineExprs = Map.insert i e inlineExprs
        , ..
        }
    
    
    chrg's avatar
    chrg committed
    addKeyword :: Keyword -> Context -> Context
    addKeyword k Context{..} =
      Context
        { keywords = Set.insert k keywords
        , ..
        }
    
    -- deleteKeyword :: Keyword -> Context -> Context
    -- deleteKeyword k Context{..} =
    --   Context
    --     { keywords = Set.delete k keywords
    --     , ..
    --     }
    
    
    defaultContext :: Context
    defaultContext =
      Context
    
    chrg's avatar
    chrg committed
        { keywords = Set.fromList [KeepMain]
    
        , typeDefs = Map.empty
    
    chrg's avatar
    chrg committed
        , inlineExprs = Map.empty
    
    chrg's avatar
    chrg committed
    
    
    chrg's avatar
    chrg committed
    isIn :: Keyword -> Context -> Bool
    isIn k = Set.member k . keywords
    
    prettyIdent :: C.Identifier C.NodeInfo -> [Char]
    prettyIdent (C.Ident s _ a) = s ++ " at " ++ show (C.posOfNode a)
    
    reduceCTranslUnit
      :: (MonadReduce Lab m)
      => C.CTranslationUnit C.NodeInfo
      -> Context
      -> m (C.CTranslationUnit C.NodeInfo)
    reduceCTranslUnit (C.CTranslUnit es ni) ctx = do
      es' <- foldr reduceCExternalDeclaration (\_ -> pure []) es ctx
      pure $ C.CTranslUnit es' ni
    
    reduceCExternalDeclaration
      :: (MonadReduce Lab m)
      => C.CExternalDeclaration C.NodeInfo
      -> (Context -> m [C.CExternalDeclaration C.NodeInfo])
      -> Context
      -> m [C.CExternalDeclaration C.NodeInfo]
    reduceCExternalDeclaration r cont ctx = do
      case inlineTypeDefs r ctx of
        C.CFDefExt fun
          | KeepMain `isIn` ctx && maybe False (("main" ==) . C.identToString) (functionName fun) -> do
              r' <- C.CFDefExt <$> reduceCFunDef fun ctx
              (r' :) <$> cont ctx
          | otherwise ->
              split ("remove function " <> maybe "" C.identToString (functionName fun), C.posOf r) (cont ctx) do
                r' <- C.CFDefExt <$> reduceCFunDef fun ctx
    
                (r' :) <$> case functionName fun of
                  Just fid -> cont (addInlineExpr fid (C.CVar fid C.undefNode) ctx)
                  Nothing -> cont ctx
    
    chrg's avatar
    chrg committed
        C.CDeclExt result ->
          case result of
            -- A typedef
            C.CDecl (C.CStorageSpec (C.CTypedef n) : rst) decl _ -> do
              let [ids] = identifiers decl
              split
                ("inline typedef " <> C.identToString ids, C.posOf r)
                (cont (addTypeDefs [ids] rst ctx))
                ((r :) <$> cont (addTypeDefs [ids] [C.CTypeSpec (C.CTypeDef ids n)] ctx))
            -- A const
            C.CDecl rec decl ni' -> do
              (decl', ctx') <- foldr reduceCDeclarationItem (pure ([], ctx)) decl
              case decl' of
                []
                  | AllowEmptyDeclarations `isIn` ctx' ->
                      split ("remove empty declaration", C.posOf r) (cont ctx') do
                        (C.CDeclExt (C.CDecl rec decl' ni') :) <$> cont ctx'
                  | otherwise -> cont ctx'
                _ow -> (C.CDeclExt (C.CDecl rec decl' ni') :) <$> cont ctx'
            a -> don'tHandle a
        _r -> don'tHandle r
    
    reduceCFunDef
      :: (MonadReduce Lab m)
      => C.CFunctionDef C.NodeInfo
      -> Context
      -> m (C.CFunctionDef C.NodeInfo)
    reduceCFunDef (C.CFunDef spc dec cdecls smt ni) ctx = do
      smt' <- reduceCStatementOrEmptyBlock smt ctx
      pure $ C.CFunDef spc dec cdecls smt' ni
    
    reduceCCompoundBlockItem
      :: (MonadReduce Lab m)
      => C.CCompoundBlockItem C.NodeInfo
      -> (Context -> m [C.CCompoundBlockItem C.NodeInfo])
      -> Context
      -> m [C.CCompoundBlockItem C.NodeInfo]
    reduceCCompoundBlockItem r cont ctx = do
      case r of
        C.CBlockStmt smt -> do
          case reduceCStatement smt ctx of
            Just rsmt -> split ("remove statement", C.posOf r) (cont ctx) do
              smt' <- rsmt
              case smt' of
                C.CCompound [] ss _ -> do
                  split ("expand compound statment", C.posOf r) ((ss <>) <$> cont ctx) do
                    (C.CBlockStmt smt' :) <$> cont ctx
                _ow -> do
                  (C.CBlockStmt smt' :) <$> cont ctx
            Nothing -> cont ctx
        C.CBlockDecl declr -> do
          case declr of
            C.CDecl rec decl ni' -> do
              (decl', ctx') <- foldr reduceCDeclarationItem (pure ([], ctx)) decl
              case decl' of
                []
                  | AllowEmptyDeclarations `isIn` ctx' ->
                      split ("remove empty declaration", C.posOf r) (cont ctx') do
                        (C.CBlockDecl (C.CDecl rec decl' ni') :) <$> cont ctx'
                  | otherwise -> cont ctx'
                _ow -> (C.CBlockDecl (C.CDecl rec decl' ni') :) <$> cont ctx'
            d -> don'tHandle d
        a -> don'tHandle a
    
    chrg's avatar
    chrg committed
    
    
    chrg's avatar
    chrg committed
    reduceCDeclarationItem
    
    chrg's avatar
    chrg committed
      :: (MonadReduce Lab m)
    
    chrg's avatar
    chrg committed
      => C.CDeclarationItem C.NodeInfo
    
    chrg's avatar
    chrg committed
      -> m ([C.CDeclarationItem C.NodeInfo], Context)
      -> m ([C.CDeclarationItem C.NodeInfo], Context)
    
    chrg's avatar
    chrg committed
    reduceCDeclarationItem d ma = case d of
      C.CDeclarationItem
    
    chrg's avatar
    chrg committed
        dr@(C.CDeclr (Just i) [] Nothing [] ni)
        (Just (C.CInitExpr c ni'))
    
    chrg's avatar
    chrg committed
        Nothing -> do
    
    chrg's avatar
    chrg committed
          (ds, ctx) <- ma
          c' <- fromMaybe (pure zeroExpr) (reduceCExpr c ctx)
    
    chrg's avatar
    chrg committed
          split
            ("inline variable " <> C.identToString i, C.posOf ni)
    
    chrg's avatar
    chrg committed
            (pure (ds, addInlineExpr i c' ctx))
            ( pure
                ( C.CDeclarationItem dr (Just (C.CInitExpr c' ni')) Nothing : ds
                , addInlineExpr i (C.CVar i ni) ctx
                )
            )
    
      C.CDeclarationItem (C.CDeclr (Just i) _ Nothing [] ni) Nothing Nothing -> do
    
    chrg's avatar
    chrg committed
        (ds, ctx) <- ma
        split
          ("remove variable " <> C.identToString i, C.posOf ni)
          (pure (ds, ctx))
          (pure (d : ds, addInlineExpr i (C.CVar i ni) ctx))
    
    chrg's avatar
    chrg committed
      a -> don'tHandle a
    
    
    chrg's avatar
    chrg committed
    reduceCStatementOrEmptyBlock
      :: (MonadReduce Lab m)
      => C.CStatement C.NodeInfo
      -> Context
      -> m (C.CStatement C.NodeInfo)
    reduceCStatementOrEmptyBlock stmt ctx = do
      case reduceCStatement stmt ctx of
        Just ex -> do
          ex
        Nothing -> do
          pure emptyBlock
     where
      emptyBlock = C.CCompound [] [] C.undefNode
    
    reduceCStatement
      :: (MonadReduce Lab m)
      => C.CStatement C.NodeInfo
      -> Context
      -> Maybe (m (C.CStatement C.NodeInfo))
    reduceCStatement smt ctx = case smt of
      C.CCompound is cbi ni -> Just do
        cbi' <- foldr reduceCCompoundBlockItem (\_ -> pure []) cbi ctx
        pure $ C.CCompound is cbi' ni
      C.CWhile e s dow ni -> do
        rs <- reduceCStatement s ctx
        Just do
          e' <- reduceCExprOrZero e ctx
          s' <- rs
          pure $ C.CWhile e' s' dow ni
      C.CExpr me ni -> do
        case me of
          Just e -> do
            if DoNoops `isIn` ctx
              then Just do
                e' <- maybeSplit ("change to noop", C.posOf smt) $ reduceCExpr e ctx
                pure $ C.CExpr e' ni
              else do
                re <- reduceCExpr e ctx
                Just do
                  e' <- re
                  pure $ C.CExpr (Just e') ni
          Nothing ->
            Just $ pure $ C.CExpr Nothing ni
      C.CReturn me ni -> Just do
        case me of
          Just e -> do
            e' <- reduceCExprOrZero e ctx
            pure $ C.CReturn (Just e') ni
          Nothing ->
            pure $ C.CReturn Nothing ni
      C.CIf e s els ni -> Just do
        e' <- maybeSplit ("remove condition", C.posOf e) $ reduceCExpr e ctx
        els' <- case els of
          Just els' -> do
            maybeSplit ("remove else branch", C.posOf els') do
              reduceCStatement els' ctx
          Nothing -> pure Nothing
        s' <- reduceCStatementOrEmptyBlock s ctx
        case (e', els') of
          (Nothing, Nothing) -> pure s'
          (Just e'', Nothing) -> pure $ C.CIf e'' s' Nothing ni
          (Nothing, Just x) -> pure $ C.CIf zeroExpr s' (Just x) ni
          (Just e'', Just x) -> pure $ C.CIf e'' s' (Just x) ni
      C.CFor e1 e2 e3 s ni -> Just $ do
        (me1', ctx') <- case e1 of
          C.CForDecl (C.CDecl rec decl ni') -> do
            (decl', ctx') <- foldr reduceCDeclarationItem (pure ([], ctx)) decl
            res <-
              if null decl'
                then
                  whenSplit
                    (AllowEmptyDeclarations `isIn` ctx')
                    ("remove empty declaration", C.posOf ni')
                    (pure Nothing)
                    (pure $ Just $ C.CForDecl (C.CDecl rec decl' ni'))
                else pure $ Just $ C.CForDecl (C.CDecl rec decl' ni')
            pure (res, ctx')
          C.CForInitializing e ->
            whenSplit
              (AllowEmptyDeclarations `isIn` ctx)
              ("remove empty declaration", C.posOf ni)
              (pure (Nothing, ctx))
              (pure (Just $ C.CForInitializing e, ctx))
    
    chrg's avatar
    chrg committed
          d -> don'tHandle d
    
    
    chrg's avatar
    chrg committed
        s' <- reduceCStatementOrEmptyBlock s ctx'
        case me1' of
          Nothing -> do
            split ("remove the for loop", C.posOf smt) (pure s') do
              e2' <- case e2 of
                Just e2' -> maybeSplit ("remove check", C.posOf e2') (reduceCExpr e2' ctx')
                Nothing -> pure Nothing
              e3' <- case e3 of
                Just e3' -> maybeSplit ("remove iterator", C.posOf e3') (reduceCExpr e3' ctx')
                Nothing -> pure Nothing
              pure $ C.CFor (C.CForInitializing Nothing) e2' e3' s' ni
          Just e1' -> do
            e2' <- case e2 of
              Just e2' -> maybeSplit ("remove check", C.posOf e2') (reduceCExpr e2' ctx')
              Nothing -> pure Nothing
            e3' <- case e3 of
              Just e3' -> maybeSplit ("remove iterator", C.posOf e3') (reduceCExpr e3' ctx')
              Nothing -> pure Nothing
            pure $ C.CFor e1' e2' e3' s' ni
      C.CBreak ni -> Just do
        pure (C.CBreak ni)
      C.CLabel i s [] ni -> Just do
        s' <- reduceCStatementOrEmptyBlock s ctx
        pure $ C.CLabel i s' [] ni
      C.CGoto i ni -> Just do
        pure $ C.CGoto i ni
      a -> don'tHandle a
    
    chrg's avatar
    chrg committed
    
    --     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.CReturn e ni -> do
    --       e' <- traverse (fmap orZero reduce) e
    --       pure $ C.CReturn e' 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.CWhile e s dow ni -> do
    --       e' <- orZero (reduce @C.CExpression e)
    --       s' <- reduce s
    --       pure $ C.CWhile e' s' dow ni
    
    
    chrg's avatar
    chrg committed
    -- | If the condition is statisfied try to reduce to the a.
    whenSplit :: (MonadReduce Lab m) => Bool -> Lab -> m a -> m a -> m a
    whenSplit cn lab a b
      | cn = split lab a b
      | otherwise = b
    
    maybeSplit :: (MonadReduce Lab m) => Lab -> Maybe (m a) -> m (Maybe a)
    maybeSplit lab = \case
      Just r -> do
        split lab (pure Nothing) (Just <$> r)
      Nothing -> do
        pure Nothing
    
    
    chrg's avatar
    chrg committed
    zeroExpr :: C.CExpression C.NodeInfo
    zeroExpr = C.CConst (C.CIntConst (C.cInteger 0) C.undefNode)
    
    
    chrg's avatar
    chrg committed
    reduceCExprOrZero :: (MonadReduce Lab m) => C.CExpr -> Context -> m C.CExpr
    reduceCExprOrZero expr ctx = do
      case reduceCExpr expr ctx of
        Just ex -> do
          split ("replace by zero", C.posOf expr) (pure zeroExpr) ex
        Nothing -> do
          pure zeroExpr
    
    reduceCExpr :: (MonadReduce Lab m) => C.CExpr -> Context -> Maybe (m C.CExpr)
    reduceCExpr expr ctx = case expr of
      C.CBinary o elhs erhs ni -> do
        case reduceCExpr elhs ctx of
          Just elhs' -> case reduceCExpr erhs ctx of
            Just erhs' -> pure do
              split ("reduce to left", C.posOf elhs) elhs' do
                split ("reduce to right", C.posOf erhs) erhs' do
                  l' <- elhs'
                  r' <- erhs'
                  pure $ C.CBinary o l' r' ni
            Nothing ->
              fail "could not reduce right hand side"
          Nothing
            | otherwise -> fail "could not reduce left hand side"
      C.CAssign o elhs erhs ni ->
        case reduceCExpr elhs (addKeyword DisallowVariableInlining ctx) of
          Just elhs' -> case reduceCExpr erhs ctx of
            Just erhs' -> pure do
              split ("reduce to left", C.posOf elhs) elhs' do
                split ("reduce to right", C.posOf erhs) erhs' do
                  l' <- elhs'
                  r' <- erhs'
                  pure $ C.CAssign o l' r' ni
            Nothing ->
              fail "could not reduce right hand side"
          Nothing
            | otherwise -> fail "could not reduce left hand side"
      C.CVar i _ ->
        case Map.lookup i . inlineExprs $ ctx of
          Just mx -> case mx of
            C.CVar _ _ -> pure (pure mx)
            _
              | DisallowVariableInlining `isIn` ctx -> Nothing
              | otherwise -> pure (pure mx)
          Nothing -> fail ("Could not find " <> show i)
      C.CConst x -> Just do
        pure $ C.CConst x
      C.CUnary o elhs ni -> do
        elhs' <- reduceCExpr elhs (addKeyword DisallowVariableInlining ctx)
        Just $ split ("reduce to operant", C.posOf expr) elhs' do
          e <- elhs'
          pure $ C.CUnary o e ni
    
      C.CCall e es ni -> do
        re <- reduceCExpr e (addKeyword DisallowVariableInlining ctx)
        Just $ do
          e' <- re
          es' <- traverse (`reduceCExprOrZero` ctx) es
          pure $ C.CCall e' es' ni
    
    chrg's avatar
    chrg committed
      a -> error (show a)
    
    chrg's avatar
    chrg committed
    
    --     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.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)
    
    
    chrg's avatar
    chrg committed
    -- 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
    --
    -- maybeSplit
    --   :: (MonadReduce l m)
    --   => l
    --   -> Maybe (m a)
    --   -> Maybe (m a)
    --   -> Maybe (m a)
    -- maybeSplit s a b = case a of
    --   Just a' -> case b of
    --     Just b' -> Just do
    --       split s a' b'
    --     Nothing -> Just a'
    --   Nothing -> b
    --
    -- maybeSplitOn
    --   :: (MonadReduce l m)
    --   => Keyword
    --   -> l
    --   -> ReaderT Context Maybe (m a)
    --   -> ReaderT Context Maybe (m a)
    --   -> ReaderT Context Maybe (m a)
    -- maybeSplitOn k s a b = do
    --   con <- keyword k
    --   if con
    --     then b
    --     else ReaderT \ctx ->
    --       case runReaderT a ctx of
    --         Just a' -> case runReaderT b ctx of
    --           Just b' -> Just $ split s a' b'
    --           Nothing -> Just a'
    --         Nothing -> runReaderT b ctx
    
    inlineTypeDefs :: forall d. (Data d) => d -> Context -> d
    inlineTypeDefs r ctx = do
    
      case eqT @d @[C.CDeclarationSpecifier C.NodeInfo] of
    
    chrg's avatar
    chrg committed
        Just Refl ->
          r & concatMap \case
    
    chrg's avatar
    chrg committed
            C.CTypeSpec (C.CTypeDef idx _) -> do
    
    chrg's avatar
    chrg committed
              case Map.lookup idx . typeDefs $ ctx of
                Just args -> args
                Nothing -> error ("could not find typedef:" <> show idx)
            a -> [a]
    
        Nothing ->
    
    chrg's avatar
    chrg committed
          gmapT (`inlineTypeDefs` ctx) 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
    
    functionName :: C.CFunctionDef C.NodeInfo -> Maybe C.Ident
    functionName = \case
      C.CFunDef _ (C.CDeclr ix _ _ _ _) _ _ _ -> ix
    
    
    chrg's avatar
    chrg committed
    -- isMain :: C.CFunctionDef C.NodeInfo -> Bool
    -- isMain (C.CFunDef _ (C.CDeclr (Just i) _ _ _ _) _ _ _) =
    --   C.identToString i == "main"
    -- isMain _ow = False
    
    chrg's avatar
    chrg committed
    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
    --       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)
    
    chrg's avatar
    chrg committed
    
    
    -- 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
    
    chrg's avatar
    chrg committed
    
    
    -- 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
    
    chrg's avatar
    chrg committed
    --
    
    --         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'
    --
    --
    -- 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)