packages feed

copilot-c99-3.2: testing/Copilot/Compile/C99/Driver.hs

{-# LANGUAGE GADTs #-}

module Copilot.Compile.C99.Driver (writedriver) where

import Data.List                      (intersperse)

import Language.C99.Simple as C99

import Copilot.Core       as Core
  ( Spec    (..)
  , UExpr   (..)
  , Trigger (..)
  , Type    (..)
  , Value   (..)
  , toValues
  , fieldname
  , tylength
  )
import Copilot.Compile.C99.Translate  (transtype)
import Copilot.Compile.C99.Util       (argnames)

import Text.PrettyPrint               (render)
import Language.C99.Simple            (translate)
import Language.C99.Pretty            (pretty)


-- | Write the complete driver .c file code based on the specification.
writedriver :: String -> Spec -> Int -> String
writedriver specname spec iters = unlines (includes) ++ code
  where
    includes = [ "#include <stdio.h>"
               , "#include <stdint.h>"
               , "#include <stdbool.h>"
               , "#include \"" ++ specname ++ ".h\""
               ]
    code = render $ pretty $ translate $ mktransunit spec iters


-- | Write a C driver mimicking Copilot's interpreter.
mktransunit :: Spec -> Int -> TransUnit
mktransunit spec iters = TransUnit vardefs fundefs
  where
    vardefs   = []
    fundefs   = ctriggers ++ [ mkmain iters spec ]
    ctriggers = map mktrigger (specTriggers spec)


-- | Write a trigger function which prints all its arguments in a CSV like
-- format.
mktrigger :: Trigger -> FunDef
mktrigger (Trigger name guard args) = FunDef returntype name params [] body
  where
    returntype = TypeSpec Void
    namedargs  = zip (argnames name) args
    params     = map mkparam namedargs
    body       = [Expr $ mkprintfcsv name namedargs]

    mkparam :: (String, UExpr) -> Param
    mkparam (name, UExpr ty _) = Param (transtype ty) name


-- | Write the main function. The purpose of this function is to call the
-- step-function a number of times.
mkmain :: Int -> Spec -> FunDef
mkmain iters spec = FunDef (TypeSpec Int) "main" params decln body
  where
    params = [ Param (TypeSpec Int) "argc"
             , Param (Const $ C99.Array (Ptr $ TypeSpec Char) Nothing) "argv"
             ]
    decln  = [VarDecln Nothing (TypeSpec Int) "i" (Just $ InitExpr $ LitInt 0)]
    body   = [For (Ident "i" .= LitInt 0)
                  (Ident "i" .< LitInt (fromIntegral iters))
                  (UnaryOp Inc (Ident "i"))
                  [ Expr $ Funcall (Ident "printf") [LitString "#\n"]
                  , Expr $ Funcall (Ident "step") []
                  ]
             ]


-- | Write a call to printf with a format resembling CSV.
mkprintfcsv :: String -> [(String, UExpr)] -> Expr
mkprintfcsv trigname namedargs = Funcall (Ident "printf") (fmt:vals)
  where
    fmt    = LitString $ trigname ++ "," ++ concat (intersperse "," argfmt) ++ "\n"
    argfmt = map (uexprfmt.snd) namedargs
    vals   = concatMap (\(name, UExpr ty _) -> mkidents name ty) namedargs

    uexprfmt :: UExpr -> String
    uexprfmt (UExpr ty _) = tyfmt ty

    tyfmt :: Core.Type a -> String
    tyfmt ty = case ty of
      Core.Bool       -> "%s"
      Core.Int8       -> "%d"
      Core.Int16      -> "%d"
      Core.Int32      -> "%d"
      Core.Int64      -> "%ld"
      Core.Word8      -> "%u"
      Core.Word16     -> "%u"
      Core.Word32     -> "%u"
      Core.Word64     -> "%lu"
      Core.Float      -> "%f"
      Core.Double     -> "%f"
      Core.Struct s   -> "<" ++ elems ++ ">"
        where
          elems = concat $ intersperse "," $ map fieldfmt (toValues s)
          fieldfmt :: Core.Value a -> String
          fieldfmt (Core.Value ty f) = Core.fieldname f ++ ":" ++ tyfmt ty
      Core.Array  ty' -> "[" ++ elems ++ "]"
        where
          elems = concat $ intersperse "," $ map tyfmt types
          types = replicate (Core.tylength ty) ty'

    mkidents :: String -> Core.Type a -> [Expr]
    mkidents name ty = case ty of
      Core.Struct _ -> error "mkidents: Struct not implemented yet."
      Core.Array  _ -> idents ty [Ident name]
        where
          idents :: Core.Type a -> [Expr] -> [Expr]
          idents ty' es = case ty' of
            Core.Array ty'' -> idents ty'' [ Index es' (LitInt i)
                                           | es' <- es
                                           , i <- take (tylength ty') [0..]
                                           ]
            Core.Bool -> [Cond g (LitString "True") (LitString "False") | g <- es]
            _         -> es
      Core.Bool     -> [Cond (Ident name) (LitString "true") (LitString "false")]
      _             -> [Ident name]