{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
module Language.C.Types
(
P.CIdentifier
, P.unCIdentifier
, P.cIdentifierFromString
, P.StorageClassSpecifier(..)
, P.TypeQualifier(..)
, P.FunctionSpecifier(..)
, P.ArrayType(..)
, Specifiers(..)
, Type(..)
, TypeSpecifier(..)
, Sign(..)
, ParameterDeclaration(..)
, P.TypeNames
, P.CParser
, P.CParserContext
, P.cCParserContext
, P.runCParser
, P.quickCParser
, P.quickCParser_
, parseParameterDeclaration
, parseParameterList
, parseIdentifier
, parseEnableCpp
, parseType
, UntangleErr(..)
, untangleParameterDeclaration
, tangleParameterDeclaration
, describeParameterDeclaration
, describeType
) where
import Control.Arrow (second)
import Control.Monad (when, unless, forM_, forM)
import Control.Monad.State (execState, modify)
import Control.Monad.Reader (ask)
import Data.List (partition, intersperse)
import Data.Maybe (fromMaybe)
import Data.Typeable (Typeable)
import Text.PrettyPrint.ANSI.Leijen ((</>), (<+>))
import qualified Text.PrettyPrint.ANSI.Leijen as PP
#if MIN_VERSION_base(4,9,0)
import Data.Semigroup (Semigroup, (<>))
#else
import Data.Monoid ((<>))
#endif
#if __GLASGOW_HASKELL__ < 710
import Data.Foldable (Foldable)
import Data.Functor ((<$>))
import Data.Monoid (Monoid(..))
import Data.Traversable (Traversable)
#endif
import qualified Language.C.Types.Parse as P
data TypeSpecifier
= Void
| Bool
| Char (Maybe Sign)
| Short Sign
| Int Sign
| Long Sign
| LLong Sign
| Float
| Double
| LDouble
| TypeName P.CIdentifier
| Struct P.CIdentifier
| Enum P.CIdentifier
| Template P.CIdentifier [TypeSpecifier]
| TemplateConst String
| TemplatePointer TypeSpecifier
deriving (Typeable, Show, Eq, Ord)
data Specifiers = Specifiers
{ storageClassSpecifiers :: [P.StorageClassSpecifier]
, typeQualifiers :: [P.TypeQualifier]
, functionSpecifiers :: [P.FunctionSpecifier]
} deriving (Typeable, Show, Eq)
#if MIN_VERSION_base(4,9,0)
instance Semigroup Specifiers where
Specifiers x1 y1 z1 <> Specifiers x2 y2 z2 =
Specifiers (x1 ++ x2) (y1 ++ y2) (z1 ++ z2)
#endif
instance Monoid Specifiers where
mempty = Specifiers [] [] []
#if !MIN_VERSION_base(4,11,0)
mappend (Specifiers x1 y1 z1) (Specifiers x2 y2 z2) =
Specifiers (x1 ++ x2) (y1 ++ y2) (z1 ++ z2)
#endif
data Type i
= TypeSpecifier Specifiers TypeSpecifier
| Ptr [P.TypeQualifier] (Type i)
| Array (P.ArrayType i) (Type i)
| Proto (Type i) [ParameterDeclaration i]
deriving (Typeable, Show, Eq, Functor, Foldable, Traversable)
data Sign
= Signed
| Unsigned
deriving (Typeable, Show, Eq, Ord)
data ParameterDeclaration i = ParameterDeclaration
{ parameterDeclarationId :: Maybe i
, parameterDeclarationType :: (Type i)
} deriving (Typeable, Show, Eq, Functor, Foldable, Traversable)
data UntangleErr
= MultipleDataTypes [P.DeclarationSpecifier]
| NoDataTypes [P.DeclarationSpecifier]
| IllegalSpecifiers String [P.TypeSpecifier]
deriving (Typeable, Show, Eq)
failConversion :: UntangleErr -> Either UntangleErr a
failConversion = Left
untangleParameterDeclaration
:: P.ParameterDeclaration i -> Either UntangleErr (ParameterDeclaration i)
untangleParameterDeclaration P.ParameterDeclaration{..} = do
(specs, tySpec) <- untangleDeclarationSpecifiers parameterDeclarationSpecifiers
let baseTy = TypeSpecifier specs tySpec
(mbS, ty) <- case parameterDeclarationDeclarator of
P.IsDeclarator decltor -> do
(s, ty) <- untangleDeclarator baseTy decltor
return (Just s, ty)
P.IsAbstractDeclarator decltor ->
(Nothing, ) <$> untangleAbstractDeclarator baseTy decltor
return $ ParameterDeclaration mbS ty
untangleDeclarationSpecifiers
:: [P.DeclarationSpecifier] -> Either UntangleErr (Specifiers, TypeSpecifier)
untangleDeclarationSpecifiers declSpecs = do
let (pStorage, pTySpecs, pTyQuals, pFunSpecs) = flip execState ([], [], [], []) $ do
forM_ (reverse declSpecs) $ \declSpec -> case declSpec of
P.StorageClassSpecifier x -> modify $ \(a, b, c, d) -> (x:a, b, c, d)
P.TypeSpecifier x -> modify $ \(a, b, c, d) -> (a, x:b, c, d)
P.TypeQualifier x -> modify $ \(a, b, c, d) -> (a, b, x:c, d)
P.FunctionSpecifier x -> modify $ \(a, b, c, d) -> (a, b, c, x:d)
let (dataTypes, specs) =
partition (\x -> not (x `elem` [P.SIGNED, P.UNSIGNED, P.LONG, P.SHORT])) pTySpecs
let illegalSpecifiers s = failConversion $ IllegalSpecifiers s specs
mbSign0 <- case filter (== P.SIGNED) specs of
[] -> return Nothing
[_] -> return $ Just Signed
_:_ -> illegalSpecifiers "conflicting/duplicate sign information"
mbSign <- case (mbSign0, filter (== P.UNSIGNED) specs) of
(Nothing, []) -> return Nothing
(Nothing, [_]) -> return $ Just Unsigned
(Just b, []) -> return $ Just b
_ -> illegalSpecifiers "conflicting/duplicate sign information"
let sign = fromMaybe Signed mbSign
let longs = length $ filter (== P.LONG) specs
let shorts = length $ filter (== P.SHORT) specs
when (longs > 0 && shorts > 0) $ illegalSpecifiers "both long and short"
dataType <- case dataTypes of
[x] -> return x
[] | longs > 0 || shorts > 0 -> return P.INT
[] -> failConversion $ NoDataTypes declSpecs
_:_ -> failConversion $ MultipleDataTypes declSpecs
let checkNoSpecs =
unless (null specs) $ illegalSpecifiers "expecting no specifiers"
let checkNoLength =
when (longs > 0 || shorts > 0) $ illegalSpecifiers "unexpected long/short"
let type2type dat = case dat of
P.Template s args -> do
checkNoSpecs
args' <- forM args type2type
return $ Template s args'
P.TemplateConst s -> do
checkNoSpecs
return $ TemplateConst s
P.TemplatePointer s -> do
checkNoSpecs
s' <- type2type s
return $ TemplatePointer s'
P.TypeName s -> do
checkNoSpecs
return $ TypeName s
P.Struct s -> do
checkNoSpecs
return $ Struct s
P.Enum s -> do
checkNoSpecs
return $ Enum s
P.VOID -> do
checkNoSpecs
return Void
P.BOOL -> do
checkNoLength
return $ Bool
P.CHAR -> do
checkNoLength
return $ Char mbSign
P.INT | longs == 0 && shorts == 0 -> do
return $ Int sign
P.INT | longs == 1 -> do
return $ Long sign
P.INT | longs == 2 -> do
return $ LLong sign
P.INT | shorts == 1 -> do
return $ Short sign
P.INT -> do
illegalSpecifiers "too many long/short"
P.FLOAT -> do
checkNoLength
return Float
P.DOUBLE -> do
if longs == 1
then return LDouble
else do
checkNoLength
return Double
_ -> do
error $ "untangleDeclarationSpecifiers impossible: " ++ show dataType
tySpec <- type2type dataType
return (Specifiers pStorage pTyQuals pFunSpecs, tySpec)
untangleDeclarator
:: forall i. Type i -> P.Declarator i -> Either UntangleErr (i, Type i)
untangleDeclarator ty0 (P.Declarator ptrs0 directDecltor) = go ty0 ptrs0
where
go :: Type i -> [P.Pointer] -> Either UntangleErr (i, Type i)
go ty [] = goDirect ty directDecltor
go ty (P.Pointer quals : ptrs) = go (Ptr quals ty) ptrs
goDirect :: Type i -> P.DirectDeclarator i -> Either UntangleErr (i, Type i)
goDirect ty direct0 = case direct0 of
P.DeclaratorRoot s -> return (s, ty)
P.ArrayOrProto direct (P.Array arrayType) ->
goDirect (Array arrayType ty) direct
P.ArrayOrProto direct (P.Proto params) -> do
params' <- mapM untangleParameterDeclaration params
goDirect (Proto ty params') direct
P.DeclaratorParens decltor ->
untangleDeclarator ty decltor
untangleAbstractDeclarator
:: forall i. Type i -> P.AbstractDeclarator i -> Either UntangleErr (Type i)
untangleAbstractDeclarator ty0 (P.AbstractDeclarator ptrs0 mbDirectDecltor) =
go ty0 ptrs0
where
go :: Type i -> [P.Pointer] -> Either UntangleErr (Type i)
go ty [] = case mbDirectDecltor of
Nothing -> return ty
Just directDecltor -> goDirect ty directDecltor
go ty (P.Pointer quals : ptrs) = go (Ptr quals ty) ptrs
goDirect :: Type i -> P.DirectAbstractDeclarator i -> Either UntangleErr (Type i)
goDirect ty direct0 = case direct0 of
P.ArrayOrProtoThere direct (P.Array arrayType) ->
goDirect (Array arrayType ty) direct
P.ArrayOrProtoThere direct (P.Proto params) -> do
params' <- mapM untangleParameterDeclaration params
goDirect (Proto ty params') direct
P.ArrayOrProtoHere (P.Array arrayType) ->
return $ Array arrayType ty
P.ArrayOrProtoHere (P.Proto params) -> do
params' <- mapM untangleParameterDeclaration params
return $ Proto ty params'
P.AbstractDeclaratorParens decltor ->
untangleAbstractDeclarator ty decltor
tangleParameterDeclaration
:: forall i. ParameterDeclaration i -> P.ParameterDeclaration i
tangleParameterDeclaration (ParameterDeclaration mbId ty00) =
uncurry P.ParameterDeclaration $ case mbId of
Nothing -> second P.IsAbstractDeclarator $ goAbstractDirect ty00 Nothing
Just id' -> second P.IsDeclarator $ goConcreteDirect ty00 $ P.DeclaratorRoot id'
where
goAbstractDirect
:: Type i -> Maybe (P.DirectAbstractDeclarator i)
-> ([P.DeclarationSpecifier], P.AbstractDeclarator i)
goAbstractDirect ty0 mbDirect = case ty0 of
TypeSpecifier specifiers tySpec ->
let declSpecs = tangleTypeSpecifier specifiers tySpec
in (declSpecs, P.AbstractDeclarator [] mbDirect)
Ptr tyQuals ty ->
goAbstract ty [P.Pointer tyQuals] mbDirect
Array arrType ty ->
let arr = P.Array arrType
in case mbDirect of
Nothing ->
goAbstractDirect ty $ Just $ P.ArrayOrProtoHere arr
Just decltor ->
goAbstractDirect ty $ Just $ P.ArrayOrProtoThere decltor arr
Proto ty params ->
let proto = P.Proto $ map tangleParameterDeclaration params
in case mbDirect of
Nothing ->
goAbstractDirect ty $ Just $ P.ArrayOrProtoHere proto
Just decltor ->
goAbstractDirect ty $ Just $ P.ArrayOrProtoThere decltor proto
goAbstract
:: Type i -> [P.Pointer] -> Maybe (P.DirectAbstractDeclarator i)
-> ([P.DeclarationSpecifier], P.AbstractDeclarator i)
goAbstract ty0 ptrs mbDirect = case ty0 of
TypeSpecifier specifiers tySpec ->
let declSpecs = tangleTypeSpecifier specifiers tySpec
in (declSpecs, P.AbstractDeclarator ptrs mbDirect)
Ptr tyQuals ty ->
goAbstract ty (P.Pointer tyQuals : ptrs) mbDirect
Array{} ->
goAbstractDirect ty0 $ Just $ P.AbstractDeclaratorParens $
P.AbstractDeclarator ptrs mbDirect
Proto{} ->
goAbstractDirect ty0 $ Just $ P.AbstractDeclaratorParens $
P.AbstractDeclarator ptrs mbDirect
goConcreteDirect
:: Type i -> P.DirectDeclarator i
-> ([P.DeclarationSpecifier], P.Declarator i)
goConcreteDirect ty0 direct = case ty0 of
TypeSpecifier specifiers tySpec ->
let declSpecs = tangleTypeSpecifier specifiers tySpec
in (declSpecs, P.Declarator [] direct)
Ptr tyQuals ty ->
goConcrete ty [P.Pointer tyQuals] direct
Array arrType ty ->
goConcreteDirect ty $ P.ArrayOrProto direct $ P.Array arrType
Proto ty params ->
goConcreteDirect ty $ P.ArrayOrProto direct $
P.Proto $ map tangleParameterDeclaration params
goConcrete
:: Type i -> [P.Pointer] -> P.DirectDeclarator i
-> ([P.DeclarationSpecifier], P.Declarator i)
goConcrete ty0 ptrs direct = case ty0 of
TypeSpecifier specifiers tySpec ->
let declSpecs = tangleTypeSpecifier specifiers tySpec
in (declSpecs, P.Declarator ptrs direct)
Ptr tyQuals ty ->
goConcrete ty (P.Pointer tyQuals : ptrs) direct
Array{} ->
goConcreteDirect ty0 $ P.DeclaratorParens $ P.Declarator ptrs direct
Proto{} ->
goConcreteDirect ty0 $ P.DeclaratorParens $ P.Declarator ptrs direct
tangleTypeSpecifier :: Specifiers -> TypeSpecifier -> [P.DeclarationSpecifier]
tangleTypeSpecifier (Specifiers storages tyQuals funSpecs) tySpec =
let pTySpecs ty = case ty of
Void -> [P.VOID]
Bool -> [P.BOOL]
Char Nothing -> [P.CHAR]
Char (Just Signed) -> [P.SIGNED, P.CHAR]
Char (Just Unsigned) -> [P.UNSIGNED, P.CHAR]
Short Signed -> [P.SHORT]
Short Unsigned -> [P.UNSIGNED, P.SHORT]
Int Signed -> [P.INT]
Int Unsigned -> [P.UNSIGNED]
Long Signed -> [P.LONG]
Long Unsigned -> [P.UNSIGNED, P.LONG]
LLong Signed -> [P.LONG, P.LONG]
LLong Unsigned -> [P.UNSIGNED, P.LONG, P.LONG]
Float -> [P.FLOAT]
Double -> [P.DOUBLE]
LDouble -> [P.LONG, P.DOUBLE]
TypeName s -> [P.TypeName s]
Struct s -> [P.Struct s]
Enum s -> [P.Enum s]
Template s types -> [P.Template s (concat (map pTySpecs types))]
TemplateConst s -> [P.TemplateConst s]
TemplatePointer type' -> [P.TemplatePointer (head (pTySpecs type'))]
in map P.StorageClassSpecifier storages ++
map P.TypeQualifier tyQuals ++
map P.FunctionSpecifier funSpecs ++
map P.TypeSpecifier (pTySpecs tySpec)
describeParameterDeclaration :: PP.Pretty i => ParameterDeclaration i -> PP.Doc
describeParameterDeclaration (ParameterDeclaration mbId ty) =
let idDoc = case mbId of
Nothing -> ""
Just id' -> PP.pretty id' <+> "is a "
in idDoc <> describeType ty
describeType :: PP.Pretty i => Type i -> PP.Doc
describeType ty0 = case ty0 of
TypeSpecifier specs tySpec -> engSpecs specs <> PP.pretty tySpec
Ptr quals ty -> engQuals quals <> "ptr to" <+> describeType ty
Array arrTy ty -> engArrTy arrTy <> "of" <+> describeType ty
Proto retTy params ->
"function from" <+> engParams params <> "returning" <+> describeType retTy
where
engSpecs (Specifiers [] [] []) = ""
engSpecs (Specifiers x y z) =
let xs = map P.StorageClassSpecifier x ++ map P.TypeQualifier y ++
map P.FunctionSpecifier z
in PP.hsep (map PP.pretty xs) <> " "
engQuals = PP.hsep . map PP.pretty
engArrTy arrTy = case arrTy of
P.VariablySized -> "variably sized array "
P.SizedByInteger n -> "array of size" <+> PP.text (show n) <> " "
P.SizedByIdentifier s -> "array of size" <+> PP.pretty s <> " "
P.Unsized -> "array "
engParams [] = ""
engParams params0 = "(" <> go params0 <> ") "
where
go xs = case xs of
[] -> ""
[x] -> describeParameterDeclaration x
(x:xs') -> describeParameterDeclaration x <> "," <+> go xs'
untangleParameterDeclaration'
:: (P.CParser i m, PP.Pretty i)
=> P.ParameterDeclaration i -> m (ParameterDeclaration i)
untangleParameterDeclaration' pDecl =
case untangleParameterDeclaration pDecl of
Left err -> fail $ pretty80 $
"Error while parsing declaration:" </> PP.pretty err </> PP.pretty pDecl
Right x -> return x
parseParameterDeclaration
:: (P.CParser i m, PP.Pretty i) => m (ParameterDeclaration i)
parseParameterDeclaration =
untangleParameterDeclaration' =<< P.parameter_declaration
parseParameterList
:: (P.CParser i m, PP.Pretty i)
=> m [ParameterDeclaration i]
parseParameterList =
mapM untangleParameterDeclaration' =<< P.parameter_list
parseIdentifier :: P.CParser i m => m i
parseIdentifier = P.identifier_no_lex
parseEnableCpp :: P.CParser i m => m Bool
parseEnableCpp = do
ctx <- ask
return (P.cpcEnableCpp ctx)
parseType :: (P.CParser i m, PP.Pretty i) => m (Type i)
parseType = parameterDeclarationType <$> parseParameterDeclaration
instance PP.Pretty TypeSpecifier where
pretty tySpec = case tySpec of
Void -> "void"
Bool -> "bool"
Char Nothing -> "char"
Char (Just Signed) -> "signed char"
Char (Just Unsigned) -> "unsigned char"
Short Signed -> "short"
Short Unsigned -> "unsigned short"
Int Signed -> "int"
Int Unsigned -> "unsigned"
Long Signed -> "long"
Long Unsigned -> "unsigned long"
LLong Signed -> "long long"
LLong Unsigned -> "unsigned long long"
Float -> "float"
Double -> "double"
LDouble -> "long double"
TypeName s -> PP.pretty s
Struct s -> "struct" <+> PP.pretty s
Enum s -> "enum" <+> PP.pretty s
Template s args -> PP.pretty s <+> "<" <+> mconcat (intersperse "," (map PP.pretty args)) <+> ">"
TemplateConst s -> PP.pretty s
TemplatePointer s -> PP.pretty s <+> "*"
instance PP.Pretty UntangleErr where
pretty err = case err of
MultipleDataTypes specs ->
"Multiple data types in" </> PP.prettyList specs
IllegalSpecifiers s specs ->
"Illegal specifiers, " <+> PP.text s <+> ", in" </> PP.prettyList specs
NoDataTypes specs ->
"No data types in " </> PP.prettyList specs
instance PP.Pretty i => PP.Pretty (ParameterDeclaration i) where
pretty = PP.pretty . tangleParameterDeclaration
instance PP.Pretty i => PP.Pretty (Type i) where
pretty ty =
PP.pretty $ tangleParameterDeclaration $ ParameterDeclaration Nothing ty
pretty80 :: PP.Doc -> String
pretty80 x = PP.displayS (PP.renderPretty 0.8 80 x) ""