Newer
Older
-- * Context
Context (..),
defaultContext,
-- * Helpers
prettyIdent,
) where
import Control.Applicative (Alternative (empty, (<|>)))
import Control.Monad (
foldM,
forM,
guard,
join,
mapAndUnzipM,
unless,
void,
when,
)
collect,
exceptIf,
liftMaybe,
)
import Control.Monad.State (
MonadState (get, state),
MonadTrans (lift),
StateT (runStateT),
evalStateT,
gets,
modify',
)
import Control.Monad.Trans.Maybe (MaybeT (runMaybeT))
import Data.Function ((&))
import Data.Functor (($>), (<&>))
import Data.Maybe (
catMaybes,
fromMaybe,
isJust,
isNothing,
mapMaybe,
)
import qualified Data.Set as Set
import Data.Vector.Internal.Check (HasCallStack)
defaultReduceCWithKeywords :: (MonadReduce (String, C.Position) m) => [Keyword] -> C.CTranslUnit -> m C.CTranslUnit
defaultReduceCWithKeywords keywords a = reduceCTranslUnit a (defaultContext{keywords = Set.fromList keywords})
{-# SPECIALIZE defaultReduceCWithKeywords :: [Keyword] -> C.CTranslUnit -> IRTree.IRTree (String, C.Position) C.CTranslUnit #-}
defaultReduceC :: (MonadReduce (String, C.Position) m) => C.CTranslUnit -> m C.CTranslUnit
defaultReduceC a = reduceCTranslUnit a defaultContext
{-# SPECIALIZE defaultReduceC :: C.CTranslUnit -> IRTree.IRTree (String, C.Position) C.CTranslUnit #-}
reduceCTranslUnit ::
(MonadReduce Lab m) =>
C.CTranslationUnit C.NodeInfo ->
Context ->
m (C.CTranslationUnit C.NodeInfo)
let _functions = foldMap (findFunctions (: [])) es
let funmap :: [(C.Ident, Maybe Function)] =
List.sortOn (maybe 0 (negate . funSize) . snd)
. Map.toList
. Map.fromListWith const
. map (\f -> (funName f, Just f))
. List.sortOn funSize
$ _functions
let reduce funcs = forM funcs \(k, mf) ->
(k,) <$> runMaybeT do
f <- liftMaybe mf
let fstr = C.identToString (funName f)
when (C.identToString (funName f) /= "main" || LoseMain `isIn` ctx) do
exceptIf ("remove function " <> fstr <> " (" <> show (funSize f) <> ")", funPosition f)
isStatic <-
if funIsStatic f
then
split
("remove static from " <> fstr, funPosition f)
(pure False)
(pure True)
else pure False
pure f{funIsStatic = isStatic}
-- try remove static
functions2 <- do
funmap' <- reduce funmap
if ComputeFunctionFixpoint `isIn` ctx
then reduce funmap
else pure funmap'
functions3 <- forM functions2 \(k, mf) ->
(k,) <$> runMaybeT do
f <- liftMaybe mf
if C.identToString (funName f) /= "main" || LoseMain `isIn` ctx
then do
params <- case funParams f of
Just params -> do
Just <$> forM (zip [1 :: Int ..] params) \(i, p) ->
if p
then split ("remove parameter " <> show i <> " from " <> C.identToString (funName f), funPosition f) (pure False) (pure True)
else pure False
ow -> pure ow
pure f{funParams = params}
else do
pure f
let builtins =
[ ("fabsf", FunType (NonVoid TNum) (Params [Just TNum] False))
, ("fabs", FunType (NonVoid TNum) (Params [Just TNum] False))
[ ( funName
, Just $
Function
{ funIsStatic = False
, funPosition = C.posOf funName
, funSize = 0
, funParams = case funTypeParams funType of
VoidParams -> Nothing
Params _ True -> Nothing
Params fx False -> Just [isJust f | f <- fx]
, ..
}
)
| (C.builtinIdent -> funName, funType) <- builtins
]
<> functions3
let ctx' =
ctx
{ functions = functions'''
, inlineExprs =
inlineExprs ctx
<> Map.fromList
[(C.builtinIdent f, IEKeep (TFun ft)) | (f, ft) <- builtins]
}
res' <- evalStateT (mapM reduceCExternalDeclaration es) ctx'
pure $ C.CTranslUnit (catMaybes res') ni
{ sfKeepStatic :: Bool
}
keepAll :: SpecifierFilter
keepAll = SpecifierFilter{sfKeepStatic = True}
{- | Update the CDeclarationSpecifier's to match the context. Specifically, update
updateSpec ::
C.CDeclarationSpecifier C.NodeInfo ->
Maybe (m [C.CDeclarationSpecifier C.NodeInfo])
updateSpec a = case a of
C.CTypeSpec t -> case t of
C.CSUType (C.CStruct st (Just i) (Just declrs) attr x) b -> do
fields <- case lookupStruct ctx i of
ISDelete -> empty
ISDeclared _ -> empty
ISKeep s -> do
pure $ structTypeFields s
Just do
declrs' <- filterStruct fields declrs
pure [C.CTypeSpec (C.CSUType (C.CStruct st (Just i) (Just declrs') attr x) b)]
C.CTypeDef idx _ -> do
case Map.lookup idx . typeDefs $ ctx of
Just (ITKeep _) -> Just $ pure [C.CTypeSpec t]
Just (ITInline _ res) -> Just $ pure res
Just ITDelete -> Nothing
Nothing -> error ("could not find typedef: " <> show idx)
_ow -> Just $ pure [C.CTypeSpec t]
C.CStorageSpec (C.CStatic _) -> Just $ pure [a | sfKeepStatic sf]
C.CFunSpec (C.CInlineQual _) -> Just $ pure [a | sfKeepStatic sf]
_ow -> Just $ pure [a]
filterStruct ::
[(a1, Maybe a2)] ->
[C.CDeclaration C.NodeInfo] ->
m [C.CDeclaration C.NodeInfo]
filterStruct fields declrs =
flip evalStateT fields do
declrs' <- forM declrs $ \case
C.CDecl spec2 items l -> runMaybeT do
items' <- forM items $ \case
C.CDeclarationItem (C.CDeclr mid dd sl attr ni2) enit ni1 -> runMaybeT do
_ <- liftMaybe =<< state (\((_, t) : tps) -> (t, tps))
(_, dd') <- evalStateT (updateCDerivedDeclarators Void (repeat True) dd) ctx
pure (C.CDeclarationItem (C.CDeclr mid dd' sl attr ni2) enit ni1)
a' -> notSupportedYet a' l
(_, spec2') <- joinLiftMaybe (updateCDeclarationSpecifiers keepAll ctx spec2)
let items'' = catMaybes items'
guard $ not (List.null items'')
pure (C.CDecl spec2' items'' l)
a' -> notSupportedYet' a'
pure $ catMaybes declrs'
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
typeFromCDeclarationSpecifiers ::
forall m.
( MonadPlus m
, HasCallStack
) =>
Context ->
[C.CDeclarationSpecifier C.NodeInfo] ->
m Voidable
typeFromCDeclarationSpecifiers ctx =
liftMaybe
. exactlyOne
. map \case
C.CVoidType _ -> Just Void
C.CSUType c _ -> NonVoid . TStruct <$> structId c
C.CCharType _ -> Just $ NonVoid TNum
C.CShortType _ -> Just $ NonVoid TNum
C.CIntType _ -> Just $ NonVoid TNum
C.CFloatType _ -> Just $ NonVoid TNum
C.CDoubleType _ -> Just $ NonVoid TNum
C.CSignedType _ -> Just $ NonVoid TNum
C.CUnsigType _ -> Just $ NonVoid TNum
C.CBoolType _ -> Just $ NonVoid TNum
C.CLongType _ -> Just $ NonVoid TNum
C.CInt128Type _ -> Just $ NonVoid TNum
C.CFloatNType{} -> Just $ NonVoid TNum
C.CEnumType (C.CEnum (Just ix) _ _ _) _ ->
NonVoid TNum
<$ guard (lookupEnum ctx ix == INKeep)
C.CEnumType (C.CEnum Nothing _ _ _) _ -> Just $ NonVoid TNum
C.CTypeDef idx _ ->
case Map.lookup idx (typeDefs ctx) of
Just (ITKeep t') -> Just t'
Just ITDelete -> Nothing
Just (ITInline t' _) -> Just t'
Nothing -> error "error"
a -> notSupportedYet (void a) a
. typeSpecs
where
typeSpecs = mapMaybe \case
C.CTypeSpec ts -> Just ts
_ow -> Nothing
exactlyOne =
maybe
(error "no type in type-specs")
( \case
(t, []) -> NonEmpty.head t
(t, rs) -> error ("more than one type in type-specs: " <> show (t : rs))
)
. List.uncons
. NonEmpty.group
structId (C.CStruct t mi md _ ni) =
case mi of
Just ix -> case lookupStruct ctx ix of
ISDelete -> Nothing
_ow -> Just $ Left ix
Nothing ->
let p' =
maybe
(error $ "invalid struct at" <> show (C.posOf ni))
(concatMap namesAndTypeOf)
md
in pure $ Right (StructType t Nothing p')
namesAndTypeOf = \case
C.CDecl spec2 items ni ->
flip map items \case
C.CDeclarationItem decl@(C.CDeclr (Just ix) _ _ _ _) _ _ ->
(ix, nonVoid <$> typeOf spec2 decl)
a -> notSupportedYet (void a) ni
a -> notSupportedYet' a
typeOf spec2 decl = typeFromCDeclarationSpecifiers ctx spec2 >>= extendTypeWith decl
extendTypeWith (C.CDeclr _ dd _ _ _) t =
foldr applyDD (Just t) dd
where
applyDD = \case
C.CPtrDeclr _ _ -> fmap (NonVoid . TPointer)
C.CArrDeclr{} -> fmap (NonVoid . TPointer)
C.CFunDeclr params _ ni -> \c ->
case params of
C.CFunParamsNew params' varadic -> do
c' <- c
Just $ NonVoid $ TFun (FunType c' (findParams varadic params'))
b -> notSupportedYet b ni
findParams varadic = \case
[C.CDecl [C.CTypeSpec (C.CVoidType _)] [] _] -> VoidParams
rst -> flip Params varadic $ flip map rst \case
C.CDecl spec' [] _ ->
nonVoid <$> typeFromCDeclarationSpecifiers ctx spec'
C.CDecl spec' [C.CDeclarationItem decl _ _] _ ->
nonVoid <$> typeOf spec' decl
a -> notSupportedYet' a
typeFromCDerivedDeclarators ::
forall m.
( MonadPlus m
) =>
Voidable ->
Context ->
[C.CDerivedDeclarator C.NodeInfo] ->
m Voidable
typeFromCDerivedDeclarators bt ctx dd =
foldM applyDD bt (reverse dd)
where
applyDD ::
(r ~ Voidable) =>
r ->
C.CDerivedDeclarator C.NodeInfo ->
m r
applyDD t d = case d of
C.CPtrDeclr _ _ -> do
pure (NonVoid . TPointer $ t)
C.CArrDeclr{} -> do
pure (NonVoid . TPointer $ t)
C.CFunDeclr params _ ni -> do
case params of
C.CFunParamsNew params' varadic -> do
tp <- findParams varadic params'
let t' = NonVoid $ TFun (FunType t tp)
pure t'
b -> notSupportedYet b ni
findParams ::
Bool ->
[C.CDeclaration C.NodeInfo] ->
m Params
findParams varadic decls = case decls of
[C.CDecl [C.CTypeSpec (C.CVoidType _)] [] _] ->
pure VoidParams
_ow -> do
result <-
forM decls $ \case
C.CDecl spec items ni -> do
bt' <- typeFromCDeclarationSpecifiers ctx spec
case items of
[] -> do
pure $ nonVoid bt'
[C.CDeclarationItem (C.CDeclr _ dd2 Nothing [] _) Nothing _] -> do
(nonVoid -> t) <- typeFromCDerivedDeclarators bt' ctx dd2
pure t
_ow -> notSupportedYet items ni
a -> notSupportedYet' a
pure (Params (map Just result) varadic)
updateCDerivedDeclarators ::
forall m.
( MonadState Context m
) =>
Voidable ->
[Bool] ->
[C.CDerivedDeclarator C.NodeInfo] ->
m (Voidable, [C.CDerivedDeclarator C.NodeInfo])
updateCDerivedDeclarators bt ff dd = do
foldM applyDD (bt, []) (reverse dd)
where
applyDD ::
(r ~ (Voidable, [C.CDerivedDeclarator C.NodeInfo])) =>
r ->
C.CDerivedDeclarator C.NodeInfo ->
m r
applyDD (t, dd') d = case d of
C.CPtrDeclr _ _ -> do
pure (NonVoid . TPointer $ t, d : dd')
C.CArrSize _ _ | not (DontReduceArrays `isIn` ctx) -> do
pure $ if b then C.CArrDeclr r (C.CNoArrSize False) ni else d
_ -> pure d
pure (NonVoid . TPointer $ t, d' : dd')
C.CFunDeclr params arr ni -> do
case params of
C.CFunParamsNew params' varadic -> do
let t' = NonVoid $ TFun (FunType t tp)
pure (t', C.CFunDeclr (C.CFunParamsNew params'' varadic) arr ni : dd')
b -> notSupportedYet b ni
findParams ::
Bool ->
[C.CDeclaration C.NodeInfo] ->
findParams varadic decls = case decls of
[C.CDecl [C.CTypeSpec (C.CVoidType _)] [] _] ->
pure (VoidParams, decls)
_ow -> flip evalStateT ff do
result <-
forM decls $ \case
C.CDecl spec items ni -> do
keep <- state (\(t : tps) -> (t, tps))
lift . runMaybeT $ do
ctx <- get
(bt', spec') <- join (liftMaybe $ updateCDeclarationSpecifiers keepAll ctx spec)
(t, items') <- case items of
[] -> do
guard keep
pure (nonVoid bt', [])
[C.CDeclarationItem (C.CDeclr mid dd2 Nothing [] ni3) Nothing ni2] -> do
(t, dd2') <- case mid of
Just ix -> do
(nonVoid -> t, dd2') <- updateCDerivedDeclarators bt' [] dd2
guard keep
modify' (addInlineExpr ix (IEKeep t))
pure (t, dd2')
Nothing -> do
(nonVoid -> t, dd2') <- updateCDerivedDeclarators bt' [] dd2
guard keep
pure (t, dd2')
pure (t, [C.CDeclarationItem (C.CDeclr mid dd2' Nothing [] ni3) Nothing ni2])
_ow -> notSupportedYet items ni
pure (t, C.CDecl spec' items' ni)
a -> notSupportedYet' a
let (ts, decls') = unzip $ flip map result \case
Just (t, d') -> (Just t, [d'])
Nothing -> (Nothing, [])
pure (Params ts varadic, concat decls')
joinLiftMaybe :: (MonadPlus m) => Maybe (m a) -> m a
joinLiftMaybe = join . liftMaybe
reduceCExternalDeclaration ::
(HasCallStack, MonadReduce Lab m) =>
C.CExternalDeclaration C.NodeInfo ->
StateT Context m (Maybe (C.CExternalDeclaration C.NodeInfo))
reduceCExternalDeclaration r = case r of
C.CFDefExt (C.CFunDef spec declr [] stmt ni) -> runMaybeT do
ctx <- get
let C.CDeclr mid dd Nothing [] ni2 = declr
Just <$> liftMaybe (lookupFunction ctx fid)
Nothing ->
pure Nothing
let keepStatic = maybe True funIsStatic mfun
-- TODO handle this edgecase (struct declared in function declaration)
_ <- reduceStructDeclaration spec
(bt, spec') <- joinLiftMaybe $ updateCDeclarationSpecifiers keepAll{sfKeepStatic = keepStatic} ctx spec
((t', dd'), ctx'') <- runStateT (updateCDerivedDeclarators bt (fromMaybe (repeat True) (mfun >>= funParams)) dd) ctx
case mfun of
Just fun -> do
modify' (addInlineExpr (funName fun) (IEKeep t))
Nothing -> do
exceptIf ("remove function", C.posOf r)
labs <- flip collect (labelsOf stmt) \l -> do
exceptIf ("remove label" <> show l, C.posOf l)
pure l
stmt' <-
reduceCStatementOrEmptyBlock stmt StmtContext{stmtLabels = labs, stmtInLoop = False} $
C.CFunDef spec' (C.CDeclr mid dd' Nothing [] ni2) [] stmt' ni
C.CDeclExt (C.CDecl (C.CStorageSpec (C.CTypedef nif) : rst) [item] ni) -> runMaybeT do
(ix, dd, wrap) <- case item of
C.CDeclarationItem (C.CDeclr (Just ix) dd Nothing extras _) Nothing Nothing ->
case extras of
[] -> pure (ix, dd, id)
[C.CAttr (C.Ident "__vector_size__" _ _) [a] _] -> do
case a of
C.CBinary C.CMulOp (C.CConst (C.CIntConst (C.CInteger n _ _) _)) (C.CSizeofType _ _) _ ->
-- todo assuming this is a checked size
pure
( ix
, dd
, NonVoid . TVector (fromInteger n)
)
_ -> notSupportedYet a ni
a -> notSupportedYet (map void a) ni
ctx <- get
(bt, rst') <- joinLiftMaybe $ updateCDeclarationSpecifiers keepAll ctx rst
exceptIf ("inline typedef " <> C.identToString ix, C.posOf ni)
pure $ C.CDeclExt $ C.CDecl (C.CStorageSpec (C.CTypedef nif) : rst') [item] ni
-- The rest.
C.CDeclExt (C.CDecl spec items ni) -> runMaybeT do
ctx <- get
markDeleted items
-- TODO: Actually we should split it up here
let forceStatic =
getAny <$> flip foldMap items \case
(C.CDeclarationItem (C.CDeclr (Just fid) (C.CFunDeclr{} : _) _ _ _) _ _) -> do
Any . funIsStatic <$> lookupFunction ctx fid
_ow -> Nothing
if any isStaticSpec spec && not (DontRemoveStatic `isIn` ctx)
ctx' <- get
(bt, spec') <- joinLiftMaybe $ updateCDeclarationSpecifiers keepAll{sfKeepStatic = isStatic} ctx' spec
di@(C.CDeclarationItem (C.CDeclr mid dd Nothing [] ni2) einit size) -> do
case dd of
C.CFunDeclr{} : _ -> do
mfun <- case mid of
Just fid ->
Nothing ->
pure Nothing
let ff = fromMaybe (repeat True) (mfun >>= funParams)
(nonVoid -> t, dd') <-
modify' (addInlineExpr fid IEDelete)
exceptIf ("remove function declaration", C.posOf ni2)
pure (C.CDeclarationItem (C.CDeclr mid dd' Nothing [] ni2) einit size)
-- Somtimes we just declare a struct or a typedef.
when (not keep && List.null items') do
guard (AllowEmptyDeclarations `isIn` ctx' || List.null items)
isStaticSpec :: C.CDeclarationSpecifier C.NodeInfo -> Bool
isStaticSpec = \case
C.CStorageSpec (C.CStatic _) -> True
_ -> False
wrapCCompound :: C.CStatement C.NodeInfo -> C.CStatement C.NodeInfo
wrapCCompound = \case
s@(C.CCompound{}) -> s
s -> C.CCompound [] [C.CBlockStmt s] C.undefNode
isStaticFromSpecs :: [C.CDeclarationSpecifier C.NodeInfo] -> Bool
isStaticFromSpecs = any \case
(C.CStorageSpec (C.CStatic _)) -> True
_ow -> False
{- | This checks the current declaration and reduces any new struct found here.
Returns true if the specifier is requried.
-}
reduceStructDeclaration ::
( MonadReduce Lab m
, MonadState Context m
, MonadPlus m
) =>
[C.CDeclarationSpecifier C.NodeInfo] ->
m Bool
C.CTypeSpec (C.CEnumType (C.CEnum mid mf _ _) ni) -> do
case mid of
Just eid -> do
case mf of
Just times -> forM_ times \(C.CEnumVar ix _) -> do
modify' (addInlineExpr ix IEDelete)
Nothing -> pure ()
modify' (addEnum eid INDelete)
exceptIf ("delete enum " <> C.identToString eid, C.posOf ni)
modify' (addEnum eid INKeep)
case mf of
Just times -> forM_ times \(C.CEnumVar ix _) -> do
modify' (addInlineExpr ix (IEKeep TNum))
Nothing -> pure ()
pure True
Nothing -> do
pure False
C.CTypeSpec (C.CSUType (C.CStruct tag mid mfields _ _) ni) -> case mid of
Just sid -> do
struct <- gets (Map.lookup sid . structs)
let reduce fields = do
exceptIf ("remove struct " <> C.identToString sid, C.posOf ni)
modify' (addStruct sid (ISDeclared tag))
(ft, _) <- mapAndUnzipM (structField sid) fields
modify' (addStruct sid (ISKeep (StructType tag (Just sid) (concat ft))))
pure True
Just (ISDeclared _) ->
case mfields of
Just fields -> reduce fields
Nothing -> pure False
Just (ISKeep _) -> do
Just fields -> reduce fields
Nothing -> pure True
Nothing -> do
modify' (addStruct sid ISDelete)
case mfields of
Just fields -> reduce fields
exceptIf ("remove struct declaration " <> C.identToString sid, C.posOf ni)
modify' (addStruct sid (ISDeclared tag))
ctx <- get
case updateCDeclarationSpecifiers keepAll ctx spec of
Just fn -> do
(bt, spec') <- fn
(fields, items') <- flip mapAndUnzipM items \case
(C.CDeclarationItem (C.CDeclr mid dd Nothing [] ni3) ini ni2) -> do
let fid = fromMaybe (error "all struct fields should be named") mid
res' <- runMaybeT $ do
(nonVoid -> t, dd') <- updateCDerivedDeclarators bt (repeat True) dd
exceptIf ("remove field " <> C.identToString sid <> "." <> C.identToString fid, C.posOf ni)
pure (t, dd')
case res' of
Nothing -> pure ((fid, Nothing), Nothing)
Just (t, dd') -> pure ((fid, Just t), Just $ C.CDeclarationItem (C.CDeclr mid dd' Nothing [] ni3) ini ni2)
a -> notSupportedYet a ni
case catMaybes items' of
[] -> pure (fields, Nothing)
items'' -> pure (fields, Just (C.CDecl spec' items'' ni))
Nothing ->
pure
( map (\i -> (fromMaybe (error "all struct fields should be named") (name i), Nothing)) items
, Nothing
)
reduceCDeclarationItem ::
( MonadReduce Lab m
, MonadState Context m
, MonadPlus m
) =>
Voidable ->
C.CDeclarationItem C.NodeInfo ->
m (C.CDeclarationItem C.NodeInfo)
di@(C.CDeclarationItem (C.CDeclr mid dd Nothing [] ni) einit Nothing) -> do
(nonVoid -> t, dd') <- updateCDerivedDeclarators bt (repeat True) dd
ctx <- get
einit' <-
whenSplit
nullable
("remove initialization", C.posOf ni)
(pure Nothing)
Just e' -> do
modify' (addInlineExpr vid (IEInline e'))
exceptIf ("inline variable " <> C.identToString vid, C.posOf ni)
Nothing -> do
exceptIf ("delete variable", C.posOf ni)
exceptIf ("delete uninitilized variable", C.posOf ni)
("initilize variable", C.posOf ni)
(pure . Just $ C.CInitExpr zeroExpr C.undefNode)
(pure Nothing)
modify' (addInlineExpr vid (IEKeep t))
let decl' = C.CDeclr mid dd' Nothing [] ni
pure (C.CDeclarationItem decl' einit' Nothing)
Nothing -> do
exceptIf ("remove unnamed declaration item", C.posOf ni)
pure di
a -> notSupportedYet a C.undefNode
getInlinable :: C.CInitializer C.NodeInfo -> Maybe C.CExpr
getInlinable = \case
C.CInitExpr e _ -> case e of
C.CConst _ -> Just e
C.CVar _ _ -> Just e
_ -> Nothing
C.CInitList _ _ -> Nothing
reduceCInitializer ::
(MonadReduce Lab m) =>
Type ->
C.CInitializer C.NodeInfo ->
Context ->
reduceCInitializer t einit ctx toplevel = case einit of
let me = reduceCExpr e (exactly t) ctx
case (me, t) of
(Just es, _) -> do
e' <- es
(Nothing, TVector n _) -> do
let items' = [([], C.CInitExpr zeroExpr ni2) | _ <- replicate n ()]
C.CInitList (C.CInitializerList items) ni2 -> do
items' <- case t of
TStruct stct -> do
let fields = fieldsOfStruct ctx stct
let i'' = catMaybes $ zipWith (\(_, t') i -> (i,) <$> t') fields items
| toplevel && not (DontReduceArrays `isIn` ctx) -> do
items' <- reverseRemoveList ni2 (reverse items)
forM (reverse items') \(p, r) -> do
r' <- reduceCInitializer t' r ctx False
pure (p, r')
| otherwise -> do
forM items \(p, r) -> do
r' <- reduceCInitializer t' r ctx False
pure (p, r')
_ow ->
-- "Unexpected type of init list: " <> show t <> " at " <> show (C.posOf ni2)
pure items
where
reverseRemoveList ni2 = \case
[] -> pure []
items@(_ : rst) -> do
b <- check ("remove the last item", C.posOf ni2)
if b
then reverseRemoveList ni2 rst
else pure items
reduceCCompoundBlockItem ::
(MonadReduce Lab m, HasCallStack) =>
StmtContext ->
C.CCompoundBlockItem C.NodeInfo ->
StateT Context m [C.CCompoundBlockItem C.NodeInfo]
ctx <- get
msmt <- runMaybeT $ reduceCStatement smt lab ctx
case msmt of
Just smt' -> do
("expand compound statment", C.posOf r)
(pure ss)
(pure [C.CBlockStmt smt'])
_ow -> pure [C.CBlockStmt smt']
Nothing -> pure []
C.CBlockDecl (C.CDecl spec items ni) -> fmap (fromMaybe []) . runMaybeT $ do
(bt, spec') <- joinLiftMaybe $ updateCDeclarationSpecifiers keepAll ctx spec
items' <- collect (reduceCDeclarationItem bt False) items
-- Somtimes we just declare a struct or a typedef.
when (not keep && List.null items') do
markDeleted :: (MonadState Context m) => [C.CDeclarationItem C.NodeInfo] -> m ()
markDeleted = mapM_ \case
C.CDeclarationItem (name -> Just ix) _ _ -> do
modify' (addInlineExpr ix IEDelete)
_a -> pure ()
reduceCStatementOrEmptyBlock ::
(MonadReduce Lab m, HasCallStack) =>
C.CStatement C.NodeInfo ->
StmtContext ->
Context ->
m (C.CStatement C.NodeInfo)
fromMaybe emptyBlock
<$> runMaybeT
( wrapCCompound <$> reduceCStatement stmt ids ctx
)
reduceCStatementOrEmptyExpr ::
(MonadReduce Lab m, HasCallStack) =>
C.CStatement C.NodeInfo ->
StmtContext ->
Context ->
m (C.CStatement C.NodeInfo)
reduceCStatementOrEmptyExpr stmt ids ctx = do
fromMaybe (C.CExpr Nothing C.undefNode)
<$> runMaybeT (reduceCStatement stmt ids ctx)
emptyBlock :: C.CStatement C.NodeInfo
emptyBlock = C.CCompound [] [] C.undefNode
data StmtContext = StmtContext
{ stmtLabels :: ![C.Ident]
, stmtInLoop :: !Bool
}
deriving (Show, Eq)
etAny :: EType
etAny = EType ETAny False
etNum :: EType
etNum = EType (ETExactly TNum) False
exactly :: Type -> EType
exactly c = EType (ETExactly c) False
-- | Reduce given a list of required labels reduce a c statement, possibly into nothingness.
reduceCStatement ::
forall m.
(MonadReduce Lab m, HasCallStack) =>
C.CStatement C.NodeInfo ->
StmtContext ->
Context ->
MaybeT m (C.CStatement C.NodeInfo)
reduceCStatement smt labs ctx = case smt of
C.CCompound is cbi ni -> do
cbi' <- lift $ evalStateT (mapM (reduceCCompoundBlockItem labs) cbi) ctx
pure (C.CCompound is (concat cbi') ni)
C.CWhile e s dow ni -> split
("remove while loop", C.posOf ni)
do
reduceCStatement s labs ctx
do
s' <- reduceCStatement s labs{stmtInLoop = True} ctx
e' <- fromMaybe (pure zeroExpr) (reduceCExpr e etNum ctx)
C.CExpr me ni -> do
case me of
Just e -> do
if DoNoops `isIn` ctx
e' <-
maybeSplit ("change to noop", C.posOf smt) $
reduceCExpr e etAny ctx
re' <- liftMaybe $ reduceCExpr e etAny ctx
exceptIf ("remove expr statement", C.posOf smt)
e' <- re'
pure $ C.CExpr (Just e') ni
case returnType ctx of
NonVoid rt -> do
res :: (m C.CExpr) <- liftMaybe (reduceCExpr e (exactly rt) ctx)
pure (Just <$> res)
Void -> pure (pure Nothing)
Nothing -> pure (pure Nothing)
exceptIf ("remove return statement", C.posOf smt)
e <- lift re
pure $ C.CReturn e ni
e' <- maybeSplit ("remove condition", C.posOf e) $ reduceCExpr e etNum ctx
ms' <- lift . runMaybeT $ do
exceptIf ("remove if branch", C.posOf e)
reduceCStatement s labs ctx
(Just e'', Just s', Nothing) -> pure $ C.CIf e'' s' Nothing ni
(Nothing, Just s', Just x) -> pure $ C.CIf zeroExpr s' (Just x) ni
(Just e'', Just s', Just x) -> pure $ C.CIf e'' s' (Just x) ni
(Just e'', Nothing, Nothing) -> pure $ C.CExpr (Just e'') C.undefNode
(Nothing, Nothing, Just x) -> pure x
(Just e'', Nothing, Just x) -> pure $ C.CIf e'' emptyBlock (Just x) ni
(Nothing, Just s', Nothing) -> pure s'
C.CFor e1 e2 e3 s ni -> case e1 of
C.CForDecl d@(C.CDecl spec items ni') -> split
("remove the for loop", C.posOf ni)
(reduceCStatement (C.CCompound [] [C.CBlockDecl d, C.CBlockStmt s] C.undefNode) labs ctx)
do
(bt, spec') <- joinLiftMaybe $ updateCDeclarationSpecifiers keepAll ctx spec
(items', ctx') <- flip runStateT ctx do
markDeleted items
re2' <- liftMaybe (reduceCExpr e2' etAny ctx')
exceptIf ("remove check", C.posOf e2')
re2'
e3' <- runMaybeT do
e3' <- liftMaybe e3
re3' <- liftMaybe (reduceCExpr e3' etAny ctx')
exceptIf ("remove iterator", C.posOf e3')
re3'
let e2'' =
if AllowInfiniteForLoops `isIn` ctx || isNothing e2
then e2'
else e2' <|> Just zeroExpr
s' <- reduceCStatementOrEmptyExpr s labs{stmtInLoop = True} ctx'
pure $ C.CFor (C.CForDecl (C.CDecl spec' items' ni')) e2'' e3' s' ni
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
C.CForInitializing e -> split
("remove the for loop", C.posOf ni)
( reduceCStatement
( C.CCompound
[]
[C.CBlockStmt (C.CExpr e C.undefNode), C.CBlockStmt s]
C.undefNode
)
labs
ctx
)
do
e' <-
maybeSplit ("remove initializer", C.posOf ni) $
e >>= \e' ->
reduceCExpr e' etAny ctx
e2' <- runMaybeT do
e2' <- liftMaybe e2
re2' <- liftMaybe (reduceCExpr e2' etNum ctx)
exceptIf ("remove check", C.posOf e2')
re2'
e3' <- runMaybeT do
e3' <- liftMaybe e3
re3' <- liftMaybe (reduceCExpr e3' etAny ctx)
exceptIf ("remove iterator", C.posOf e3')
re3'
let e2'' =
if AllowInfiniteForLoops `isIn` ctx || isNothing e2
then e2'
else e2' <|> Just zeroExpr
s' <- reduceCStatementOrEmptyExpr s labs{stmtInLoop = True} ctx
pure $ C.CFor (C.CForInitializing e') e2'' e3' s' ni
d -> notSupportedYet d ni
pure $ C.CLabel i s' [] ni
else do
empty
C.CGoto i ni ->
if i `List.elem` stmtLabels labs
then do
exceptIf ("remove goto", C.posOf smt)
pure $ C.CGoto i ni
else empty
C.CBreak n ->
if stmtInLoop labs
then do
exceptIf ("remove break", C.posOf smt)
pure $ C.CBreak n
else empty
C.CCont n ->
if stmtInLoop labs
then do
exceptIf ("remove continue", C.posOf smt)
pure $ C.CCont n
-- | 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
zeroExpr :: C.CExpression C.NodeInfo
zeroExpr = C.CConst (C.CIntConst (C.cInteger 0) C.undefNode)
-- | The expected type
data EType = EType
{ etSet :: !ETSet
, etAssignable :: !Bool
}
deriving (Show, Eq)
data ETSet
= ETExactly !Type
| ETStructWithField !C.Ident !ETSet
| ETPointer !ETSet
| ETAny
deriving (Show, Eq)
checkExpectedType :: (MonadPlus m) => Context -> Voidable -> EType -> m ()
checkExpectedType ctx (NonVoid t) et = guard $ isExpectedType ctx t et
checkExpectedType _ Void _ = pure ()
match :: Type -> Type -> Bool
match = curry \case
(TPointer Void, TPointer _) -> True
(TPointer _, TPointer Void) -> True
(TPointer (NonVoid a), TPointer (NonVoid b)) -> a `match` b
(t1, t2) -> t1 == t2
isExpectedType :: Context -> Type -> EType -> Bool
isExpectedType ctx = \c et ->
-- pTraceWith (\a -> "check " <> show a <> " " <> show c <> " " <> show et) $
go c (etSet et)
where
go c = \case
ETAny -> True
ETStructWithField ix et -> case c of
TStruct s -> fromMaybe False do
(_, mt) <- liftMaybe $ List.find (\(a, _) -> ix == a) fields
t' <- liftMaybe mt
pure $ go t' et
_ow -> False
ETPointer t' ->
case c of
TPointer Void -> True
TPointer (NonVoid c') -> go c' t'
fieldsOfStruct :: (HasCallStack) => Context -> Either C.Ident StructType -> [(C.Ident, Maybe Type)]
fieldsOfStruct ctx (Left ix) =
case lookupStruct ctx ix of
ISKeep a -> structTypeFields a
etUnPointer :: EType -> Maybe EType
etUnPointer t =
-- pTraceWith (\t' -> "unpoint " <> show t <> " " <> show t') $
ETPointer t' -> Just t{etSet = t'}
ETExactly (TPointer Void) -> Just t{etSet = ETAny}
ETExactly (TPointer (NonVoid t')) -> Just t{etSet = ETExactly t'}
_ow -> Nothing
checkNotAssignable :: (MonadPlus m) => EType -> m ()
checkNotAssignable = guard . not . etAssignable
msplit :: (MonadReduce Lab m) => Lab -> Maybe (m a) -> Maybe (m a) -> Maybe (m a)
msplit l m1 m2 = do
case m1 of
Just a -> Just $ case m2 of
Just b -> split l a b
Nothing -> a
Nothing -> m2
inferType :: Context -> C.CExpr -> Maybe Voidable
inferType ctx = \case
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
case lookupVariable ctx i of
IEInline e -> inferType ctx e
IEKeep t -> pure (NonVoid t)
IEDelete -> Nothing
C.CUnary i e _ -> do
t <- inferType ctx e
case i of
C.CIndOp -> case t of
NonVoid (TPointer t') -> pure t'
Void -> pure Void
_ow -> Nothing
C.CAdrOp -> pure (NonVoid (TPointer t))
_ow -> pure t
C.CConst x -> pure . NonVoid $ case x of
(C.CStrConst _ _) ->
TPointer (NonVoid TNum)
_ow ->
TNum
C.CIndex a x _ -> do
t1 <- inferType ctx a
t2 <- inferType ctx x
case (t1, t2) of
(NonVoid (TPointer x'), NonVoid TNum) -> pure x'
_ow -> error (show ("index", t1, t2))
C.CMember a l t _ -> do
t1 <- inferType ctx a
s' <- case (t1, t) of
(NonVoid (TPointer (NonVoid (TStruct s))), True) -> pure s
(NonVoid (TStruct s), False) -> pure s
_ow -> error (show ("member", a, l))
let fields = fieldsOfStruct ctx s'
NonVoid <$> (join . List.lookup l $ fields)
C.CBinary o lhs _ _ -> do
if o `elem` [C.CNeqOp, C.CEqOp, C.CGeqOp, C.CLeqOp, C.CGrOp, C.CLeOp]
then pure (NonVoid TNum)
else inferType ctx lhs
C.CCast decl@(C.CDecl spec items _) _ _ -> do
-- todo is this a good handling of this?
case items of
[C.CDeclarationItem (C.CDeclr Nothing dd Nothing [] _) _ _] -> do
[] ->
pure bt
_ow -> notSupportedYet' decl
C.CCall f _ ni -> do
ft <- inferType ctx f
case ft of
NonVoid (TFun (FunType rt _)) -> pure rt
a -> do
C.CAssign _ lhs _ _ -> do
inferType ctx lhs
-- inferType ctx rhs
-- if t1 == t2 then pure t1 else error (show ("assign", o, t1, t2))
C.CComma items _ -> do
inferType ctx (List.last items)
reduceCExpr ::
forall m.
(MonadReduce Lab m, HasCallStack) =>
C.CExpr ->
EType ->
Context ->
Maybe (m C.CExpr)
msplit ("reduce to left", C.posOf elhs) (reduceCExpr elhs t ctx) do
msplit ("reduce to right", C.posOf erhs) (reduceCExpr erhs t ctx) do
checkNotAssignable t
when (o `elem` [C.CNeqOp, C.CEqOp, C.CGeqOp, C.CLeqOp, C.CGrOp, C.CLeOp]) do
c <- inferType ctx elhs
let t' = fromVoid etAny exactly c
rl <- reduceCExpr elhs t' ctx
rr <- reduceCExpr erhs t' ctx
Just do
let r'' = case o of
C.CDivOp -> case r' of
C.CConst (C.CIntConst i _)
| i == C.cInteger 0 ->
C.CConst (C.CIntConst (C.cInteger 1) C.undefNode)
C.CUnary o' (C.CConst (C.CIntConst i _)) _
| i == C.cInteger 0 ->
C.CUnary o' (C.CConst (C.CIntConst (C.cInteger 1) C.undefNode)) C.undefNode
_ow -> r'
_ow -> r'
pure $ C.CBinary o l' r'' ni
msplit ("reduce to left", C.posOf elhs) (reduceCExpr elhs t ctx) do
msplit ("reduce to right", C.posOf erhs) (reduceCExpr erhs t ctx) do
-- in this case we change type, so we need to keep the operation
rl <- reduceCExpr elhs t'{etAssignable = True} ctx
rr <- reduceCExpr erhs t' ctx
Just do
l' <- rl
r' <- rr
pure $ C.CAssign o l' r' ni
case lookupVariable ctx i of
IEKeep c -> do
-- case i of
-- (C.Ident "test1char8" _ _) -> error (show (i, c))
-- _ -> pure ()
Just (pure expr)
IEInline mx' -> do
guard (not $ DisallowVariableInlining `isIn` ctx)
reduceCExpr mx' t ctx
IEDelete ->
Nothing
C.CConst x -> do
case x of
C.CStrConst _ _ -> do
checkNotAssignable t
-- guard ( `match` etSet t)
Just (pure expr)
C.CIntConst (C.getCInteger -> 0) _ -> do
checkNotAssignable t
checkExpectedType ctx (NonVoid (TPointer Void)) t
<|> checkExpectedType ctx (NonVoid TNum) t
Just (pure expr)
_ow -> do
checkNotAssignable t
Just (pure expr)
C.CUnary o eopr ni -> do
msplit ("reduce to operant", C.posOf eopr) (reduceCExpr eopr t ctx) do
case o of
C.CIndOp -> do
ropr <- case etSet t of
ETAny -> reduceCExpr eopr t ctx
_ -> reduceCExpr eopr (t{etSet = ETPointer (etSet t), etAssignable = True}) ctx
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
Just do
eopr' <- ropr
pure $ C.CUnary o eopr' ni
C.CAdrOp -> do
t' <- etUnPointer t
-- pTraceShowM (t', void eopr)
ropr <- reduceCExpr eopr (t'{etAssignable = True}) ctx
Just do
eopr' <- ropr
pure $ C.CUnary o eopr' ni
e
| e `List.elem` [C.CPreIncOp, C.CPreDecOp, C.CPostIncOp, C.CPostDecOp] -> do
reduceCExpr eopr t{etAssignable = True} ctx <&> \ropr -> do
eopr' <- ropr
pure $ C.CUnary o eopr' ni
| otherwise -> do
reduceCExpr eopr t ctx <&> \ropr -> do
eopr' <- ropr
pure $ C.CUnary o eopr' ni
C.CCall ef args ni -> do
(\fn a -> foldr fn a args)
(\e -> msplit ("reduce to expression", C.posOf e) (reduceCExpr e t ctx))
do
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
ref <- reduceCExpr ef (exactly ft) ctx
let targs = case fargs of
Params targs' v ->
let cons = if v then repeat (Just ETAny) else []
in map (fmap ETExactly) targs' <> cons
VoidParams -> repeat (Just ETAny)
let pargs = mapMaybe (\(ta, a) -> (,a) <$> ta) (zip targs args)
rargs <- forM pargs \(ta, a) ->
reduceCExpr a (EType ta False) ctx
Just do
ef' <- ref
args' <- sequence rargs
pure $ C.CCall ef' args' ni
ow -> do
error $
"Original c code does not type-check: exepected function, got "
<> show ow
<> " at "
<> show (C.posOf ef)
C.CCond et (Just ec) ef ni -> do
msplit ("reduce to true branch", C.posOf et) (reduceCExpr et t ctx) do
msplit ("reduce to false branch", C.posOf ef) (reduceCExpr ef t ctx) do
msplit ("reduce to condtion", C.posOf ef) (reduceCExpr ec t ctx) do
checkNotAssignable t
ret <- reduceCExpr et t ctx
ref <- reduceCExpr ef t ctx
rec <- reduceCExpr ec etAny ctx
et' <- ret
ef' <- ref
ec' <- rec
pure $ C.CCond et' (Just ec') ef' ni
msplit ("do not cast", C.posOf ni) (reduceCExpr e t ctx) do
fn <- updateCDeclarationSpecifiers keepAll ctx spec
hole <- case items of
[C.CDeclarationItem (C.CDeclr Nothing dd Nothing [] a) b c] -> do
e' <- reduceCExpr e etAny ctx
Just do
(bt, spec') <- fn
(_, dd') <- evalStateT (updateCDerivedDeclarators bt (repeat True) dd) ctx
ee' <- e'
pure (spec', [C.CDeclarationItem (C.CDeclr Nothing dd' Nothing [] a) b c], ee')
[] -> do
e' <- reduceCExpr e etAny ctx
Just do
(_, spec') <- fn
ee' <- e'
pure (spec', [], ee')
msplit ("reduce to indexee", C.posOf e1) (reduceCExpr e1 t ctx) do
msplit ("reduce to index", C.posOf e2) (reduceCExpr e2 t ctx) do
re1 <- reduceCExpr e1 t{etSet = ETPointer (etSet t), etAssignable = True} ctx
Just do
e1' <- re1
e2' <-
fromMaybe (pure zeroExpr) $
reduceCExpr e2 etNum ctx
pure $ C.CIndex e1' e2' ni
(x, rst) <- List.uncons (reverse items)
(\fn a -> foldr fn a (reverse items))
(\e -> msplit ("reduce to expression", C.posOf e) (reduceCExpr e t ctx))
do
rx <- reduceCExpr x t ctx
Just do
rst' <- flip collect rst \e -> do
re <- liftMaybe (reduceCExpr e (EType ETAny False) ctx)
e' <- re
exceptIf ("remove expression", C.posOf e)
pure (e' :: C.CExpr)
x' <- rx
pure $ C.CComma (reverse (x' : rst')) ni
re <- reduceCExpr e t{etSet = ETStructWithField i (etSet t)} ctx
Just do
e' <- re
pure (C.CMember e' i l ni)
lookupFunction :: (HasCallStack) => Context -> C.Ident -> Maybe Function
lookupFunction ctx k =
fromMaybe (error ("could not find function " <> C.identToString k)) $
functions ctx Map.!? k
lookupVariable :: (HasCallStack) => Context -> C.Ident -> InlineExpr
lookupVariable ctx k =
fromMaybe (error ("could not find variable " <> C.identToString k)) $
inlineExprs ctx Map.!? k
lookupStruct :: (HasCallStack) => Context -> C.Ident -> InlineStruct
fromMaybe (error ("could not find struct " <> C.identToString k)) $
lookupEnum :: (HasCallStack) => Context -> C.Ident -> InlineEnum
lookupEnum ctx k =
fromMaybe (error ("could not find enum " <> C.identToString k)) $
enums ctx Map.!? k
labelsOf :: C.CStatement C.NodeInfo -> [C.Ident]
labelsOf = \case
C.CLabel i s [] _ -> i : labelsOf s
C.CWhile _ s _ _ -> labelsOf s
C.CCase _ s _ -> labelsOf s
C.CDefault s _ -> labelsOf s
C.CCompound _ ss _ ->
ss & concatMap \case
C.CBlockStmt s -> labelsOf s
_ow -> []
C.CCases _ _ s _ -> labelsOf s
C.CIf _ l r _ -> labelsOf l <> maybe [] labelsOf r
C.CSwitch _ s _ -> labelsOf s
C.CFor _ _ _ s _ -> labelsOf s
_ow -> []
, structs :: !(Map.Map C.Ident InlineStruct)
, enums :: !(Map.Map C.Ident InlineEnum)
= ITKeep !Voidable
| ITInline !Voidable ![C.CDeclarationSpecifier C.NodeInfo]
data InlineStruct
= ISKeep !StructType
| ISDeclared !C.CStructTag
| ISDelete
deriving (Show, Eq)
data InlineEnum
= INKeep
| INDelete
deriving (Show, Eq)
deriving (Show, Eq)
data Keyword
= LoseMain
| DoNoops
| DisallowVariableInlining
| AllowInfiniteForLoops
deriving (Show, Read, Enum, Eq, Ord)
type Lab = (String, C.Position)
addTypeDef :: C.Ident -> InlineType -> Context -> Context
addTypeDef i cs ctx = ctx{typeDefs = Map.insert i cs $ typeDefs ctx}
addInlineExpr :: C.Ident -> InlineExpr -> Context -> Context
addInlineExpr i e Context{..} =
Context{inlineExprs = Map.insert i e inlineExprs, ..}
addStruct :: C.Identifier C.NodeInfo -> InlineStruct -> Context -> Context
addStruct i cs ctx = ctx{structs = Map.insert i cs $ structs ctx}
addEnum :: C.Identifier C.NodeInfo -> InlineEnum -> Context -> Context
addEnum i cs ctx = ctx{enums = Map.insert i cs $ enums ctx}
defaultContext :: Context
defaultContext =
Context
{ keywords = Set.fromList []
, typeDefs = Map.fromList [(C.builtinIdent "__builtin_va_list", ITKeep (NonVoid (TPointer Void)))]
[ (C.builtinIdent "__PRETTY_FUNCTION__", IEKeep (TPointer (NonVoid TNum)))
, (C.builtinIdent "__FUNCTION__", IEKeep (TPointer (NonVoid TNum)))
}
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)
data Struct = Struct
{ structName :: !C.Ident
, structPosition :: !C.Position
}
deriving (Show, Eq)
data Function = Function
{ funName :: !C.Ident
, funIsStatic :: !Bool
, funSize :: !Int
, funPosition :: !C.Position
}
deriving (Show, Eq)
findFunctions ::
(Monoid m) =>
(Function -> m) ->
C.CExternalDeclaration C.NodeInfo ->
m
C.CFDefExt (C.CFunDef spec declr [] _ ni) ->
findFunctionsInDeclarator ni spec declr
-- # for now let's not anlyse function declarations.
C.CFDefExt def@(C.CFunDef{}) ->
notSupportedYet (void def) def
C.CDeclExt (C.CDecl spec items ni) -> flip foldMap items \case
C.CDeclarationItem declr Nothing Nothing ->
findFunctionsInDeclarator ni spec declr
_ow -> mempty
C.CDeclExt a@(C.CStaticAssert{}) ->
notSupportedYet (void a) a
C.CAsmExt _ _ -> mempty
where
findFunctionsInDeclarator ni spec = \case
case mid of
Just funName -> inject Function{..}
where
funIsStatic = isStaticFromSpecs spec
funSize = fromMaybe 0 (C.lengthOfNode ni)
funPosition = C.posOf ni
funParams = case param of
C.CFunParamsNew declr var ->
case declr of
| var -> Nothing
| otherwise -> Just [True | _ <- declr]
a -> notSupportedYet (void a) ni
Nothing -> mempty
_ow -> mempty
class Named f where
name :: f a -> Maybe (C.Identifier a)
instance Named C.CDeclarator where
name (C.CDeclr idx _ _ _ _) = idx
instance Named C.CDeclarationItem where
name = \case
C.CDeclarationItem decl _ _ -> name decl
C.CDeclarationExpr _ -> Nothing
data Params
= VoidParams
| Params ![Maybe Type] !Bool
deriving (Show, Eq)
data FunType = FunType
{ funTypeReturn :: !Voidable
, funTypeParams :: !Params
}
deriving (Show, Eq)
data StructType = StructType
{ structTypeTag :: !C.CStructTag
, structTypeName :: !(Maybe C.Ident)
}
deriving (Show, Eq)
data Type
= TNum
| TStruct !(Either C.Ident StructType)
| TPointer !Voidable
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
| TFun !FunType
deriving (Show, Eq)
data Voidable
= Void
| NonVoid !Type
deriving (Show, Eq)
fromVoid :: a -> (Type -> a) -> Voidable -> a
fromVoid a fn = \case
Void -> a
NonVoid t -> fn t
{-# INLINE fromVoid #-}
nonVoid :: (HasCallStack) => Voidable -> Type
nonVoid = fromVoid (error "expected non void type") id
{-# INLINE nonVoid #-}
notSupportedYet :: (HasCallStack, Show a, C.Pos n) => a -> n -> b
notSupportedYet a ni = error (show a <> " at " <> show (C.posOf ni))
notSupportedYet' :: (HasCallStack, Show (a ()), Functor a, C.Pos (a C.NodeInfo)) => a C.NodeInfo -> b
notSupportedYet' a = notSupportedYet (void a) a