copilot-c99-3.16.1: src/Copilot/Compile/C99/Compile.hs
{-# LANGUAGE GADTs #-}
-- | Compile Copilot specifications to C99 code.
module Copilot.Compile.C99.Compile
( compile
, compileWith
) where
-- External imports
import Data.List ( nub, union )
import Data.Maybe ( mapMaybe )
import Data.Typeable ( Typeable )
import Language.C99.Pretty ( pretty )
import qualified Language.C99.Simple as C
import System.Directory ( createDirectoryIfMissing )
import System.Exit ( exitFailure )
import System.FilePath ( (</>) )
import System.IO ( hPutStrLn, stderr )
import Text.PrettyPrint ( render )
-- Internal imports: Copilot
import Copilot.Core ( Expr (..), Spec (..), Stream (..), Struct (..),
Trigger (..), Type (..), UExpr (..), UType (..),
Value (..) )
-- Internal imports
import Copilot.Compile.C99.CodeGen ( mkAccessDecln, mkBuffDecln, mkExtCpyDecln,
mkExtDecln, mkGenFun, mkGenFunArray,
mkIndexDecln, mkStep, mkStructDecln,
mkStructForwDecln )
import Copilot.Compile.C99.External ( External, gatherExts )
import Copilot.Compile.C99.Name ( argNames, generatorName,
generatorOutputArgName, guardName )
import Copilot.Compile.C99.Settings ( CSettings, cSettingsOutputDirectory,
cSettingsStepFunctionName,
mkDefaultCSettings )
import Copilot.Compile.C99.Type ( transType )
-- | Compile a specification to a .h and a .c file.
--
-- The first argument is the settings for the C code generated.
--
-- The second argument is used as prefix for the .h and .c files generated.
compileWith :: CSettings -> String -> Spec -> IO ()
compileWith cSettings prefix spec
| null (specTriggers spec)
= do hPutStrLn stderr $
"Copilot error: attempt at compiling empty specification.\n"
++ "You must define at least one trigger to generate C monitors."
exitFailure
| otherwise
= do let cFile = render $ pretty $ C.translate $ compileC cSettings spec
hFile = render $ pretty $ C.translate $ compileH cSettings spec
typeDeclnsFile = safeCRender $ compileTypeDeclns cSettings spec
cMacros = unlines [ "#include <stdint.h>"
, "#include <stdbool.h>"
, "#include <string.h>"
, "#include <stdlib.h>"
, "#include <math.h>"
, ""
, "#include \"" ++ prefix ++ "_types.h\""
, "#include \"" ++ prefix ++ ".h\""
, ""
]
let dir = cSettingsOutputDirectory cSettings
createDirectoryIfMissing True dir
writeFile (dir </> prefix ++ ".c") $ cMacros ++ cFile
writeFile (dir </> prefix ++ ".h") hFile
writeFile (dir </> prefix ++ "_types.h") typeDeclnsFile
-- | Compile a specification to a .h and a .c file.
--
-- The first argument is used as prefix for the .h and .c files generated.
compile :: String -> Spec -> IO ()
compile = compileWith mkDefaultCSettings
-- | Generate the .c file from a 'Spec'.
--
-- The generated C file has the following structure:
--
-- * Include .h file.
-- * Declarations of global buffers and indices.
-- * Generator functions for streams, guards and trigger arguments.
-- * Declaration of the @step()@ function.
compileC :: CSettings -> Spec -> C.TransUnit
compileC cSettings spec = C.TransUnit declns funs
where
declns = mkExts exts
++ mkGlobals streams
funs = mkGenFuns streams triggers
++ [mkStep cSettings streams triggers exts]
streams = specStreams spec
triggers = specTriggers spec
exts = gatherExts streams triggers
-- Make declarations for copies of external variables.
mkExts :: [External] -> [C.Decln]
mkExts = map mkExtCpyDecln
-- Make buffer and index declarations for streams.
mkGlobals :: [Stream] -> [C.Decln]
mkGlobals streamList = map buffDecln streamList
++ map indexDecln streamList
where
buffDecln (Stream sId buff _ ty) = mkBuffDecln sId ty buff
indexDecln (Stream sId _ _ _ ) = mkIndexDecln sId
-- Make generator functions, including trigger arguments.
mkGenFuns :: [Stream] -> [Trigger] -> [C.FunDef]
mkGenFuns streamList triggerList = map accessDecln streamList
++ map streamGen streamList
++ concatMap triggerGen triggerList
where
accessDecln :: Stream -> C.FunDef
accessDecln (Stream sId buff _ ty) = mkAccessDecln sId ty buff
streamGen :: Stream -> C.FunDef
streamGen (Stream sId _ expr ty@(Array _)) =
mkGenFunArray (generatorName sId) (generatorOutputArgName sId) expr ty
streamGen (Stream sId _ expr ty) = mkGenFun (generatorName sId) expr ty
triggerGen :: Trigger -> [C.FunDef]
triggerGen (Trigger name guard args) = guardDef : argDefs
where
guardDef = mkGenFun (guardName name) guard Bool
argDefs = zipWith argGen (argNames name) args
argGen :: String -> UExpr -> C.FunDef
argGen argName (UExpr ty expr) = mkGenFun argName expr ty
-- | Generate the .h file from a 'Spec'.
compileH :: CSettings -> Spec -> C.TransUnit
compileH cSettings spec = C.TransUnit declns []
where
declns = mkStructForwDeclns exprs
++ mkExts exts
++ extFunDeclns triggers
++ [stepDecln]
exprs = gatherExprs streams triggers
exts = gatherExts streams triggers
streams = specStreams spec
triggers = specTriggers spec
mkStructForwDeclns :: [UExpr] -> [C.Decln]
mkStructForwDeclns es = mapMaybe mkDecln uTypes
where
mkDecln (UType ty) = case ty of
Struct _ -> Just $ mkStructForwDecln ty
_ -> Nothing
uTypes = nub $ concatMap (\(UExpr _ e) -> exprTypes e) es
-- Make declarations for external variables.
mkExts :: [External] -> [C.Decln]
mkExts = map mkExtDecln
extFunDeclns :: [Trigger] -> [C.Decln]
extFunDeclns = map extFunDecln
where
extFunDecln :: Trigger -> C.Decln
extFunDecln (Trigger name _ args) = C.FunDecln Nothing cTy name params
where
cTy = C.TypeSpec C.Void
params = zipWith mkParam (argNames name) args
mkParam paramName (UExpr ty _) = C.Param (mkParamTy ty) paramName
-- Special case for Struct, to pass struct arguments by reference.
-- Arrays are also passed by reference, but using C's array type
-- does that automatically.
mkParamTy ty =
case ty of
Struct _ -> C.Ptr (transType ty)
_ -> transType ty
-- Declaration for the step function.
stepDecln :: C.Decln
stepDecln = C.FunDecln Nothing (C.TypeSpec C.Void)
(cSettingsStepFunctionName cSettings) []
-- | Generate a C translation unit that contains all type declarations needed
-- by the Copilot specification.
compileTypeDeclns :: CSettings -> Spec -> C.TransUnit
compileTypeDeclns _cSettings spec = C.TransUnit declns []
where
declns = mkTypeDeclns exprs
exprs = gatherExprs streams triggers
streams = specStreams spec
triggers = specTriggers spec
-- Generate type declarations.
mkTypeDeclns :: [UExpr] -> [C.Decln]
mkTypeDeclns es = mapMaybe mkTypeDecln uTypes
where
uTypes = nub $ concatMap (\(UExpr _ e) -> exprTypes e) es
mkTypeDecln (UType ty) = case ty of
Struct _ -> Just $ mkStructDecln ty
_ -> Nothing
-- * Auxiliary definitions
-- | Render a C.TransUnit to a String, accounting for the case in which the
-- translation unit is empty.
safeCRender :: C.TransUnit -> String
safeCRender (C.TransUnit [] []) = ""
safeCRender transUnit = render $ pretty $ C.translate transUnit
-- ** Obtain information from Copilot Core Exprs and Types.
-- | List all types of an expression, returns items uniquely.
exprTypes :: Typeable a => Expr a -> [UType]
exprTypes e = case e of
Const ty _ -> typeTypes ty
Local ty1 ty2 _ e1 e2 -> typeTypes ty1 `union` typeTypes ty2
`union` exprTypes e1 `union` exprTypes e2
Var ty _ -> typeTypes ty
Drop ty _ _ -> typeTypes ty
ExternVar ty _ _ -> typeTypes ty
Op1 _ e1 -> exprTypes e1
Op2 _ e1 e2 -> exprTypes e1 `union` exprTypes e2
Op3 _ e1 e2 e3 -> exprTypes e1 `union` exprTypes e2
`union` exprTypes e3
Label ty _ _ -> typeTypes ty
-- | List all types of a type, returns items uniquely.
typeTypes :: Typeable a => Type a -> [UType]
typeTypes ty = case ty of
Array ty' -> typeTypes ty' `union` [UType ty]
Struct x -> concatMap (\(Value ty' _) -> typeTypes ty') (toValues x)
`union` [UType ty]
_ -> [UType ty]
-- | Collect all expression of a list of streams and triggers and wrap them
-- into an UEXpr.
gatherExprs :: [Stream] -> [Trigger] -> [UExpr]
gatherExprs streams triggers = map streamUExpr streams
++ concatMap triggerUExpr triggers
where
streamUExpr (Stream _ _ expr ty) = UExpr ty expr
triggerUExpr (Trigger _ guard args) = UExpr Bool guard : args