tdlib-gen-0.3.0: src/Language/Haskell/Codegen.hs
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedLabels #-}
module Language.Haskell.Codegen where
import Control.Lens
import Data.Generics.Labels ()
import Data.List
import Data.String
import Data.Text (Text)
import qualified Data.Text as T
import Data.Text.Prettyprint.Doc
import Data.Text.Prettyprint.Doc.Internal
import GHC.Generics
type Ann = Maybe Text
io :: Type
io = Type "IO"
prettyDoc :: Ann -> Doc ann
prettyDoc (Just d) =
let ls = T.lines d
ds = fmap unsafeTextWithoutNewlines ls
in "-- |" <+> foldl1 (<>) (intersperse (line <> "--" <> line <> "--") ds) <> line
prettyDoc Nothing = mempty
data ADT
= ADT
{ name :: Text,
ann :: Ann,
constr :: [Constr]
}
deriving (Show, Eq, Generic)
constructors :: ADT -> Int
constructors ADT {..} = length constr
prettyConstrs :: [Doc ann] -> Doc ann
prettyConstrs [] = mempty
prettyConstrs (x : xs) =
vsep
[ foldl1 (<>) (intersperse (line <> "| ") (("=" <+> x) : xs)),
"deriving (Show, Eq, Generic)"
]
instance Pretty ADT where
pretty ADT {..} =
let doc = prettyDoc ann
n = unsafeTextWithoutNewlines name
cs = fmap pretty constr
in doc
<> vsep
[ "data" <+> n,
indent 2 (prettyConstrs cs)
]
-- >>> pretty $ ADT "A" (Just "A") [Constr "A" Nothing [("a", Type "A"), ("b", Type "A")], Constr "B" Nothing []]
data Field
= Field
{ name :: Text,
ann :: Ann,
ty :: Type
}
deriving (Show, Eq, Generic)
instance Pretty Field where
pretty Field {..} =
prettyDoc ann
<> unsafeTextWithoutNewlines name <+> "::" <+> pretty ty
data Constr
= Constr
{ name :: Text,
ann :: Ann,
fields :: [Field]
}
deriving (Show, Eq, Generic)
arity :: Constr -> Int
arity = length . fields
instance Pretty Constr where
pretty Constr {..} =
let doc = prettyDoc ann
n = unsafeTextWithoutNewlines name
fs = foldl (<>) mempty (intersperse ("," <> line) (fmap pretty fields))
in doc
<> vsep
[ n,
indent 2 ("{" <+> align fs <> line <> "}")
]
data Type
= Type Text
| Arr Type Type
| App Type Type
deriving (Show, Eq, Generic)
instance Pretty Type where
pretty (Type t) = unsafeTextWithoutNewlines t
pretty (Arr ty ty') = pretty ty <+> "->" <+> pretty ty'
pretty (App (Type "[]") ty) = "[" <> pretty ty <> "]"
pretty (App tyCon ty) = "(" <> pretty tyCon <> ")" <+> "(" <> pretty ty <> ")"
data TypeSig
= Result
{ ty :: Type,
ann :: Ann
}
| Conn
{ ty :: Type,
ann :: Ann,
res :: TypeSig
}
deriving (Show, Eq, Generic)
instance Pretty TypeSig where
pretty (Result ty doc) =
prettyDoc doc <> "Sem r" <+> "(" <> "Error ∪" <+> pretty ty <> ")"
pretty (Conn ty doc res) =
prettyDoc doc
<> vsep
[ pretty ty <+> "->",
pretty res
]
type Annotated = (Type, Ann)
formArr :: [Annotated] -> Annotated -> TypeSig
formArr [] (ty, ann) = Result ty ann
formArr ((ty, ann) : xs) a = Conn ty ann (formArr xs a)
data FunDef
= FunDef
{ name :: Text,
ann :: Ann,
constr :: Constr,
res :: Type
}
deriving (Show, Eq, Generic)
getAnn :: Field -> Annotated
getAnn Field {..} = (ty, ann)
flattenSig :: FunDef -> Doc ann
flattenSig FunDef {..} =
let n = unsafeTextWithoutNewlines name
doc = prettyDoc ann
sig = pretty $ formArr (fmap getAnn (fields constr)) (res, Nothing)
in vsep
[ doc <> n <+> "::",
indent 2 "Member TDLib r =>",
indent 2 sig
]
vars :: Int -> Doc ann
vars i = hsep $ fmap (fromString . ("_" <>) . show) [1 .. i]
flattenBody :: FunDef -> Doc ann
flattenBody FunDef {..} =
let n = unsafeTextWithoutNewlines name
c = unsafeTextWithoutNewlines (constr ^. #name)
ar = arity constr
v = vars ar
in hsep [n, v, "=", "runCmd $", c, v]
flattenPrint :: FunDef -> Doc ann
flattenPrint def =
vsep
[ flattenSig def,
flattenBody def
]
simplePretty :: FunDef -> Doc ann
simplePretty FunDef {..} =
let doc = prettyDoc ann
n = unsafeTextWithoutNewlines name
cmd = unsafeTextWithoutNewlines (constr ^. #name)
resTy = pretty res
in doc
<> vsep
[ n <+> "::" <+> "Member TDLib r" <+> "=>" <+> cmd <+> "->" <+> "Sem r (Error ∪ " <> resTy <> ")",
n <+> "=" <+> "runCmd"
]
instance Pretty FunDef where
pretty d@FunDef {..} = flattenPrint d