module Feldspar.Compiler.Backend.C.CodeGeneration where
import Feldspar.Compiler.Imperative.Representation
import Feldspar.Compiler.Error (handleError, ErrorClass(..))
import Feldspar.Compiler.Backend.C.Options
import Feldspar.Compiler.Imperative.Frontend (isNativeArray)
import Text.PrettyPrint
codeGenerationError :: ErrorClass -> String -> a
codeGenerationError = handleError "CodeGeneration"
data PrintEnv = PEnv
{ options :: Options
}
compToCWithInfos :: Options -> Module () -> String
compToCWithInfos opts procedure = render $ cgen (PEnv opts) procedure
class CodeGen a
where
cgen :: PrintEnv -> a -> Doc
cgenList :: PrintEnv -> [a] -> Doc
instance CodeGen (Module ())
where
cgen env Module{..} = cgenList env entities
cgenList env = sep . map (cgen env)
instance CodeGen (Entity ())
where
cgen env StructDef{..} = text "struct" <+> text structName $+$ block env (cgenList env structMembers) <> semi
cgen env TypeDef{..} = text "typedef" <+> cgen env actualType <+> text typeName <> semi
cgen env Proc{..}
| Just body <- procBody = start $$ block env (cgen env body)
| otherwise = start <> semi
where
start = text "void" <+> text procName <> parens (pvars env $ inParams ++ outParams)
cgen env ValueDef{..} = static <+> typ <+> text "const" <+> name <+> equals <+> vals <> semi
where
static = text "static"
typ = cgen env (typeof valVar)
name = cgen env valVar <> size valValue
vals = cgen env valValue
size (ArrayConst vs) = brackets $ text $ show $ length vs
size _ = empty
cgenList env = vcat . punctuate (text "\n") . map (cgen env)
instance CodeGen (StructMember ())
where
cgen env StructMember{..} = cgen env structMemberType <+> text structMemberName <> sizeInBrackets structMemberType <> semi
cgenList env = vcat . map (cgen env)
instance CodeGen (Block ())
where
cgen env Block{..} = sep [ cgenList env locals
, if null locals then empty else text ""
, cgen env blockBody
]
cgenList env = sep . punctuate (text "\n") . map (cgen env)
instance CodeGen (Declaration ())
where
cgen env Declaration{..} = pvar env declVar <+> nest (nestSize $ options env) initial <> semi
where
initial = case (initVal, varType declVar) of
(Just i, _) -> equals <+> cgen env i
(_ , Pointer{}) -> equals <+> text "NULL"
(_ , ArrayType{}) -> equals <+> text "NULL"
_ -> empty
cgenList env = vcat . map (cgen env)
instance CodeGen (Program ())
where
cgen _ Empty = empty
cgen _ Comment{..}
| isBlockComment = blockComment $ map text $ lines commentValue
| otherwise = text "//" <+> text commentValue
cgen env Assign{..} = cgen env lhs <+> equals <+> nest (nestSize $ options env) (cgen env rhs) <> semi
cgen env ProcedureCall{..} = stmt $ call (text procCallName) (map (cgen env) procCallParams)
cgen env Sequence{..} = cgenList env sequenceProgs
cgen env (Switch scrut [(Pat (ConstExpr (BoolConst True)), thenBlock),
(Pat (ConstExpr (BoolConst False)), elseBlock)])
= text "if" <+> parens (cgen env scrut)
$$ block env (cgen env thenBlock)
$$ text "else"
$$ block env (cgen env elseBlock)
cgen env Switch{..} = text "switch" <+> parens (cgen env scrutinee)
$$ block env (vcat [ cgen env p $$ nest (nestSize $ options env) (cgen env b $$ text "break" <> semi) | (p, b) <- alts])
cgen env SeqLoop{..} = cgen env sLoopCondCalc
$$ text "while" <+> parens (cgen env sLoopCond)
$$ block env (cgen env sLoopBlock $+$ cgen env sLoopCondCalc)
cgen env ParLoop{..}
| pParallel && (name . platform . options $ env) == "c99OpenMp"
= text "#pragma omp parallel for" $$ forL
| otherwise = forL
where
forL = text "for" <+> parens (sep $ map (nest 4) $ punctuate semi [ini, guard, next])
$$ block env (cgen env pLoopBlock)
ixd = pvar env pLoopCounter
ixv = cgen env pLoopCounter
ini = ixd <+> equals <+> int 0
guard = ixv <+> char '<' <+> cgen env pLoopBound
next = ixv <+> text "+=" <+> cgen env pLoopStep
cgen env BlockProgram{..} = block env (cgen env blockProgram)
cgenList env = vcat . map (cgen env)
instance CodeGen (Pattern ())
where
cgen _ PatDefault = text "default" <> colon
cgen env (Pat c) = text "case" <+> cgen env c <> colon
cgenList env = vcat . map (cgen env)
instance CodeGen (ActualParameter ())
where
cgen env ValueParameter{..} = cgen env valueParam
cgen env TypeParameter{..} = cgen env typeParam
cgen _ FunParameter{..} = text funParamName
cgenList env = hsep . punctuate comma . map (cgen env)
instance CodeGen (Expression ())
where
cgen env VarExpr{..} = cgen env varExpr
cgen env e@ArrayElem{..}
| c@ConstExpr{} <- array
, (NativeArray _ t) <- typeof c
= parens (parens (cgen env t <> brackets empty) <> cgen env c) <> brackets (cgen env arrayIndex)
| isNativeArray $ typeof array
= cgen env array <> brackets (cgen env arrayIndex)
| otherwise = text "at" <> parens (hcat $ punctuate comma [ cgen env $ typeof e
, cgen env array
, cgen env arrayIndex
])
cgen env StructField{..} = parens (cgen env struct) <> char '.' <> text fieldName
cgen env ConstExpr{..} = cgen env constExpr
cgen env FunctionCall{..}
| funName function == "!" = call (text "at") $ map (cgen env) funCallParams
| funMode function == Infix
, [a,b] <- funCallParams = parens (cgen env a <+> text (funName function) <+> cgen env b)
| otherwise = call (text $ funName function) $ map (cgen env) funCallParams
cgen env Cast{..} = parens $ parens (cgen env castType) <> parens (cgen env castExpr)
cgen env AddrOf{..}
| VarExpr v@(Variable{..}) <- addrExpr
, Pointer t <- typeof addrExpr = cgen env (v{varType = t})
| otherwise = prefix <> cgen env addrExpr
where
prefix = case (addrExpr, typeof addrExpr) of
(e, Pointer{}) | specialConstruct e -> empty
where specialConstruct ArrayElem{} = True
specialConstruct StructField{} = True
specialConstruct _ = False
_ -> text "&"
cgen env SizeOf{..} = call (text "sizeof") [cgen env sizeOf]
cgenList env = sep . punctuate comma . map (cgen env)
instance CodeGen (Variable t)
where
cgen _ = go
where
go v@Variable{..} = case varType of
Pointer t -> text "*" <> go (v {varType = t})
_ -> text varName
cgenList env = hsep . punctuate comma . map (cgen env)
instance CodeGen (Constant ())
where
cgen env cnst@(IntConst c _) = maybe (integer c) text $ transformConst env cnst
cgen env cnst@(DoubleConst c) = maybe (double c) text $ transformConst env cnst
cgen env cnst@(FloatConst c) = maybe (float c) text $ transformConst env cnst
cgen env cnst@(BoolConst False) = maybe (int 0) text $ transformConst env cnst
cgen env cnst@(BoolConst True) = maybe (int 1) text $ transformConst env cnst
cgen env (ArrayConst cs) = braces (cgenList env cs)
cgen env cnst@ComplexConst{..} = maybe cmplxCnst text $ transformConst env cnst
where
cmplxCnst = text "complex" <> parens (cgenList env [realPartComplexValue, imagPartComplexValue])
cgenList env = sep . punctuate comma . map (cgen env)
transformConst :: PrintEnv -> Constant () -> Maybe String
transformConst PEnv{..} cnst = do
f <- lookup (typeof cnst) $ values $ platform options
return $ f cnst
instance CodeGen Type
where
cgen env = toC
where
toC VoidType = text "void"
toC ArrayType{} = text "struct array *"
toC IVarType{} = text "struct ivar"
toC (UserType u) = text u
toC (StructType n _) = text "struct" <+> text n
toC (NativeArray _ t) = toC t
toC (Pointer t) = toC t <+> text "*"
toC t | Just s <- lookup t pts = text s
toC t = codeGenerationError InternalError
$ unwords ["Unhandled type in platform ", name pfm, ": ", show t]
pfm = platform $ options env
pts = types pfm
cgenList env = sep . map (cgen env)
call :: Doc -> [Doc] -> Doc
call fn args = fn <> parens (hsep $ punctuate comma args)
blockComment :: [Doc] -> Doc
blockComment ds = vcat (zipWith (<+>) (text "/*" : repeat (text " *")) ds)
$$ text " */"
sizeInBrackets :: Type -> Doc
sizeInBrackets (NativeArray l t) = brackets (maybe empty (text . show) l) <> sizeInBrackets t
sizeInBrackets _ = empty
stmt :: Doc -> Doc
stmt = (<>semi)
block :: PrintEnv -> Doc -> Doc
block env d = lbrace $+$ nest (nestSize $ options env) d $+$ rbrace
pvar :: PrintEnv -> Variable t -> Doc
pvar env Variable{..} = typ <+> (name <> size)
where
typ = cgen env varType
size = sizeInBrackets varType
name = text varName
pvars :: PrintEnv -> [Variable t] -> Doc
pvars env = hsep . punctuate comma . map (pvar env)