lhc-0.6.20081216: src/E/ToHs.hs
module E.ToHs(compileToHs) where
import Char
import Control.Monad.Identity
import Control.Monad
import Control.Monad.RWS
import Control.Monad.Trans
import Control.Monad.Writer
import Data.Monoid
import System.IO
import Text.PrettyPrint.HughesPJ(render,($$),nest,Doc())
import qualified Data.Traversable as T
import qualified System
import qualified Data.Set as Set
import Data.DeriveTH
import Data.Derive.All
import PackedString
import C.Prims
import C.Arch
import DataConstructors
import Doc.PPrint
import Doc.DocLike
import E.E
import E.FreeVars
import E.Program
import E.Subst
import E.Traverse
import E.Values
import Name.Id
import Name.Name
import Name.Names
import Name.Prim
import Name.VConsts
import Numeric
import Options
import Support.CanType
import Support.FreeVars
import Util.Gen
import Util.SetLike
import qualified FlagDump as FD
progress str = wdump FD.Progress $ (putErrLn str) >> hFlush stderr
{-# NOINLINE compileToHs #-}
compileToHs :: Program -> IO ()
compileToHs prog = do
(v,_,Coll { collNames = ns, collPrims = prims }) <- runRWST (fromTM $ transE (programE prog)) emptyEnvironment 1
let rv = render (text "theRealMain = " <> v)
let data_decls = render (transDataTable (progDataTable prog) ns) ++ "\n"
let foreign_decls = render (transForeign $ Set.toList prims) ++ "\n"
let fn = optOutName options
let cf = (fn ++ "_code.hs")
progress ("Writing " ++ show cf)
name <- System.getProgName
args <- getArguments
let argstring = simpleQuote (name:args)
comm = shellQuote $ ["ghc", "-O", cf, "-o", fn ]
writeFile cf $ unlines ["-- " ++ argstring,"-- " ++ comm,"",render restate,data_decls,rv,"",foreign_decls]
progress ("Running: " ++ comm)
r <- System.system comm
when (r /= System.ExitSuccess) $ fail "Hs code did not compile."
return ()
cTypeInfoT (ELit LitCons { litAliasFor = Just af }) = cTypeInfoT af
cTypeInfoT (ELit LitCons { litName = n }) | (RawType,t) <- fromName n = cTypeInfo t
cTypeInfo "wchar_t" = ("Char#","C#","Char")
cTypeInfo "HsChar" = ("Char#","C#","Char")
cTypeInfo "HsPtr" = ("Addr#","Ptr","(Ptr ())")
cTypeInfo "HsFunPtr" = ("Addr#","Ptr","(Ptr ())")
cTypeInfo n = (if primTypeIsSigned pi then "Int#" else "Word#",v,i) where
(v,i) = if primTypeIsSigned pi then ('I':nn ++ "#","Int" ++ nn) else ('W':nn ++ "#","Word" ++ nn)
nn = show $ primTypeSizeOf pi * 8
Just pi = primitiveInfo n
showCType n = fst3 $ cTypeInfo n
restate = vcat $ map f restated where
f (n,nn,v) = g (nameType n) <> text v <+> text "=" <+> showTCName nn n
g DataConstructor = empty
g TypeConstructor = text "type "
restated = [
(dc_Cons,0,"lhc_Cons"),
(dc_EmptyList,0,"lhc_EmptyList"),
(tc_List,1,"ListTCon")
]
transForeign ps = vcat (map f ps) where
f (AddrOf s) = text $ "foreign import ccall \"&" ++ unpackPS s ++ "\" addr_" ++ mangleIdent (unpackPS s) ++ " :: Ptr ()"
f furc@Func { funcName = fn, funcIOLike = True, primArgTypes = as, primRetType = "void" } = ans <$> ans' where
ans = text $ "foreign import ccall unsafe \"" ++ unpackPS fn ++ "\" " ++ '_':cfuncname furc ++ " :: " ++ intercalate " -> " (map (snd . snd) vals ++ ["IO ()"])
ans' = text $ cfuncname furc <+> "w" <+> unwords (fsts vals) <+> " = case _" ++ cfuncname furc <+> intercalate " " [ parens (c <+> a) | (a,(c,_)) <- vals ] <+> "of IO f -> case f w of (# w, _ #) -> w"
vals = [ ('a':show n,ioInfo a) | a <- as | n <- naturals ]
f furc@Func { funcName = fn, funcIOLike = True, primArgTypes = as, primRetType = rt' } = ans <$> ans' where
ans = text $ "foreign import ccall unsafe \"" ++ unpackPS fn ++ "\" " ++ '_':cfuncname furc ++ " :: " ++ intercalate " -> " (map (snd . snd) vals ++ ["IO " ++ rt])
ans' = text $ cfuncname furc <+> "w" <+> unwords (fsts vals) <+> " = case _" ++ cfuncname furc <+> intercalate " " [ parens (c <+> a) | (a,(c,_)) <- vals ] <+> "of IO f -> case f w of (# w, " ++ rc ++ " r #) -> (# w, r #)"
vals = [ ('a':show n,ioInfo a) | a <- as | n <- naturals ]
(rc,rt) = ioInfo rt'
f furc@Func { funcName = fn, funcIOLike = False, primArgTypes = as, primRetType = rt' } = ans <$> ans' where
ans = text $ "foreign import ccall unsafe \"" ++ unpackPS fn ++ "\" " ++ '_':cfuncname furc ++ " :: " ++ intercalate " -> " (map (snd . snd) vals ++ [rt])
ans' = text $ cfuncname furc <+> unwords (fsts vals) <+> " = case _" ++ cfuncname furc <+> intercalate " " [ parens (c <+> a) | (a,(c,_)) <- vals ] <+> "of " ++ rc ++ " r -> r"
vals = [ ('a':show n,ioInfo a) | a <- as | n <- naturals ]
(rc,rt) = ioInfo rt'
-- f furc@Func { funcName = fn, funcIOLike = False, primArgTypes = as, primRetType = rt } = ans where
-- ans = text $ "foreign import ccall unsafe \"" ++ fn ++ "\" " ++ cfuncname furc ++ " :: " ++ intercalate " -> " (map showCType (as ++ [rt]))
-- f furc@Func { funcName = fn, funcIOLike = True, primArgTypes = as, primRetType = rt } = ans where
-- ans = text $ "foreign import ccall unsafe \"" ++ fn ++ "\" " ++ cfuncname furc ++ " :: " ++ intercalate " -> " ("World__":(map showCType as ++ ["(# World__, " ++ showCType rt ++ " #)"]))
f n = text "{- Foreign.Error " <+> tshow n <+> text "-}"
ioInfo n = (x,y) where
(_,x,y) = cTypeInfo n
transDataTable dataTable ns = vcat (theType:map g (lefts wtd)) where
wtd = Set.toList (mconcatMap f (Set.toList ns))
f (n,_,_) | n `elem` builtIns = Set.empty
f (n,_,_) | Just _ <- fromUnboxedNameTuple n = Set.empty
f (n,_,_) | Just _ <- fromTupname n = Set.empty
f w@(n,nn,tl) = case (nameType n,tl) of
(DataConstructor,False) -> Set.fromList $ do
c <- getConstructor n dataTable
return (Left $ conInhabits c)
(TypeConstructor,True) -> Set.singleton (Left n)
(TypeConstructor,False) -> Set.singleton (Right (n,nn))
(RawType,_) -> Set.empty
(nt,_) -> error (show (w,nt))
theType = text "data Type = Char | Int" <+> case rights wtd of
[] -> empty
as -> text "|" <+> hcat (punctuate (text " | ") (map tt as))
tt (n,nn) = hsep (showTCName nn n:replicate nn (text "Type"))
g n = ans where
ans = text "data" <+> hsep (showTCName (length $ conSlots con) n:[ text ('x':show i) | _ <- conSlots con | i <- [2::Int,4 ..] ]) <+> dchildren
Just con = getConstructor n dataTable
childs = conChildren con
dchildren | DataNormal [] <- childs = empty
| DataNormal childs <- childs = text "=" <+> hcat (punctuate (text " | ") (map dc childs))
| otherwise = empty
dc cn = ans where
ans = hsep (showCName cn: map showSlot (conSlots con))
Just con = getConstructor cn dataTable
showSlot (EVar v) = pprint v
showSlot (ELit LitCons { litArgs = es, litAliasFor = Just af }) = showSlot (foldl eAp af es)
showSlot (EPi TVr { tvrType = a } b) = parens $ showSlot a <+> text "->" <+> showSlot b
showSlot (ELit (LitCons { litName = c, litArgs = as })) = showCon c (map showSlot as)
--builtIns = [tc_Int,tc_Char,dc_Int,dc_Char,rt_int,rt_HsChar,tc_World__,tc_Array__,tc_MutArray__,tc_Ref__,rt_HsPtr]
builtIns = [tc_Int,tc_Char,dc_Int,dc_Char,tc_World__,tc_Array__,tc_MutArray__,tc_Ref__]
data Environment = Env {
envParen :: Bool,
envType :: Bool,
envCoerce :: IdSet
}
emptyEnvironment = Env {
envParen = False,
envType = False,
envCoerce = mempty
}
data Collect = Coll { collNames :: Set.Set (Name,Int,Bool), collPrims :: Set.Set Prim }
$(derive makeMonoid ''Collect)
newtype TM a = TM { fromTM :: RWST Environment Collect Int IO a }
deriving(MonadState Int,MonadReader Environment,MonadWriter Collect,Monad,Functor,MonadIO)
mparen xs = do
Env { envParen = p } <- ask
x <- local (\e -> e { envParen = True }) xs
if p then return $ parens x else return x
nparen xs = do local (\e -> e { envParen = True }) xs
noParens x = local (\e -> e { envParen = False }) x
showCon c ts | Just 0 <- fromUnboxedNameTuple c, nameType c == TypeConstructor = text "Nothing"
showCon c ts | Just 0 <- fromUnboxedNameTuple c, nameType c == DataConstructor = text "theNothing"
showCon c ts | Just _ <- fromUnboxedNameTuple c = text "(# " <> hsep (punctuate comma ts) <> text " #)"
showCon c ts | Just _ <- fromTupname c = text "(" <> hsep (punctuate comma ts) <> text ")"
showCon c [] | c == tc_World__ = text "World__"
showCon c [a] | c == tc_Array__ = parens $ text "Array__" <+> a
showCon c [a] | c == tc_MutArray__ = parens $ text "MutArray__" <+> a
showCon c [a] | c == tc_Ref__ = parens $ text "Ref__" <+> a
showCon c [] | (RawType,v) <- fromName c = text $ showCType v
showCon c [] | c == tc_Int = text "Int"
showCon c [] | c == tc_Char = text "Char"
showCon c [] = showTCName 0 c
showCon c ts = parens $ hsep (showTCName (length ts) c:ts)
showTCName n c | nameType c == TypeConstructor = showCName c <> text "_" <> tshow n
showTCName _ c = showCName c
showCName c | c == dc_Char = text "C#"
showCName c | c == dc_Int = text "I#"
showCName c = text $ case nameType c of
DataConstructor -> 'D':mangleIdent (show c)
TypeConstructor -> 'T':mangleIdent (show c)
n -> 'U':mangleIdent (show n ++ "_" ++ show c)
transType :: E -> TM Doc
transType e | typeLike e = return $ text "Type"
transType (EPi TVr {tvrType = a } b) = local (\e -> e { envType = True }) $ mparen $ do
a <- transType a
b <- transType b
return $ a <+> text "->" <+> b
transType (ELit LitCons { litArgs = es, litAliasFor = Just af }) = transType (foldl eAp af es)
transType (ELit LitCons { litName = c, litArgs = ts }) = nparen $ do
Env { envType = inType } <- ask
tell mempty { collNames = Set.singleton (c,length ts,inType) }
ts <- mapM transType ts
return $ showCon c ts
transType e = return $ text "{- ERROR " <> tshow e <> text " -} Type"
typeLike (ESort EStar) = True
typeLike (EPi TVr { tvrType = a } b) = typeLike a && typeLike b
typeLike _ = False
transE :: E -> TM Doc
transE (EError s _) = mparen $ return (text "error" <+> tshow s)
transE (EError s _) = mparen $ return (text "error__" <+> tshow s <> text "#" <+> text "`seq`" <+> text "undefined")
transE (ELit (LitInt num t)) = case cTypeInfoT t of
("Char#",_,_) -> return $ text (show $ chr $ fromIntegral num) <> text "#"
("Int#",_,_) | num < 0 -> mparen $ return $ text (show num) <> text "#"
| otherwise -> return $ text (show num) <> text "#"
("Addr#",_,_) | num == 0 -> return $ text "nullAddr#"
| otherwise -> mparen $ return $ text "int2Addr#" <+> text (show num) <> text "#"
("Word#",_,_) -> mparen $ text "int2Word# (" <> tshow num <> text "# )"
transE (ELit LitCons { litName = c, litArgs = ts }) = nparen $ do
Env { envType = inType } <- ask
tell mempty { collNames = Set.singleton (c,length ts,inType) }
ts <- mapM transE ts
return $ showCon c ts
transE ee | (e,ts@(_:_)) <- fromLam ee = mparen $ do
ts' <- mapM transTVr ts
e <- noParens $ transE e
return $ text "\\" <> hsep ts' <+> text "->" <+> e
transE (EVar tvr) = do
--env <- asks envCoerce
t <- transTVr tvr
--case tvrIdent tvr `member` env of
case hasBoxes (tvrType tvr) of
False -> return t
True -> mparen $ return $ text "unsafeCoerce#" <+> t
transE ee | (e,es@(_:_)) <- fromAp ee = mparen $ do
e <- transE e
es <- mapM transE es
return (hsep (e:es))
transE ELetRec { eDefs = ds, eBody = e } = mparen $ do
--local (\e -> e { envCoerce = envCoerce e `mappend` fromList [ tvrIdent t | (t,_) <- ds, hasBoxes (tvrType t)] }) $ do
ds' <- flip mapM ds $ \ (tvr,e) -> do
let (b,bs) = fromLam e
tt <- noParens $ transType (tvrType tvr)
t <- transTVr tvr
bs <- mapM transTVr bs
e <- case hasBoxes (tvrType tvr) of
False -> noParens $ transE b
True -> do
t <- transE b
return $ text "unsafeCoerce#" <+> t
return (t <+> text "::" <+> tt <> semi $$ hsep (t:bs) <+> text "=" <+> e)
e <- noParens $ transE e
return (text "let {" $$ nest 4 (vcat (punctuate (text ";") ds')) $$ text "} in" <+> e)
transE ECase { eCaseBind = TVr { tvrIdent = 0, tvrType = tt }, eCaseScrutinee = scrut, eCaseDefault = Just body, eCaseAlts = [] } | isLifted tt = mparen $ do
scrut <- transE scrut
body <- transE body
return (scrut <+> text "`seq`" <+> body)
transE ECase { eCaseBind = bind, eCaseScrutinee = scrut, eCaseDefault = md, eCaseAlts = as } = mparen $ do
scrut <- noParens $ transE scrut
let dobind = 0 /= tvrIdent bind
b <- transTVr bind
md <- T.mapM transE md
let md' = flip fmap md $ \e -> b <+> text "->" <+> if dobind && isLifted (getType bind) then text "seq" <+> b <+> e else e
as <- mapM (transAlt dobind b) as
let alts = as ++ maybeToMonad md'
return (text "case" <+> scrut <+> text "of {" $$ nest 4 (vcat (punctuate semi alts)) $$ text "}")
transE e | Just (e',_) <- from_unsafeCoerce e = mparen $ do
e' <- transE e'
return (text "unsafeCoerce#" <+> e')
transE e@(EPrim (APrim (PrimPrim prim) _) args _) = case (unpackPS prim,args) of
("dependingOn",[x,_y]) -> transE x -- XXX
(fs,args) | Just ghcprim <- lookup fs ghcPrimTable -> mparen $ mapM transE args >>= \args' -> return $ hsep (text ghcprim:args')
_ -> mparen $ return $ text "error" <+> tshow ("ToHs.Error: " ++ show e)
--transE (EPrim (APrim Operator { primOp = "-", primRetType = rt } _) [x] _) = mparen $ do
-- x <- transE x
-- return (hsep [text "negateInt#",x])
--transE (EPrim (APrim Operator { primOp = op, primRetType = rt } _) [x,y] _) | Just z <- op2Table (op,rt) = mparen $ do
-- x <- transE x
-- y <- transE y
-- return (hsep [text z,x,y])
--transE (EPrim (APrim Operator { primOp = op, primArgTypes = [at,_] } _) [x,y] _) | Just z <- op2TableCmp (op,showCType at) = mparen $ do
-- x <- transE x
-- y <- transE y
-- return $ text "fromBool" <+> (parens $ hsep [text z,x,y])
transE (EPrim (APrim CConst { primConst = ('"':rs) } _) [] _) = return (text ('"':rs) <> text "#")
transE (EPrim (APrim (PrimString ss) _) [] _) = return (tshow ss <> text "#")
transE (EPrim (APrim PrimTypeInfo { primArgType = at, primTypeInfo = c } _) [] _) = ans where
Just pi = primitiveInfo at
ans = case c of
PrimSizeOf -> return $ tshow (primTypeSizeOf pi) <> char '#'
transE (EPrim (APrim Peek { primArgType = at } _) [w,x] _) = mparen ans where
ans = do
w <- transE w
x <- transE x
return (text func <+> x <+> text "0#" <+> w)
(tt,_,_) = cTypeInfo at
Just pi = primitiveInfo at
size = primTypeSizeOf pi * 8
sign = primTypeIsSigned pi
func = case tt of
"Char#" -> "readWideCharOffAddr#"
"Addr#" -> "readAddrOffAddr#"
"Int#" -> "readInt" ++ show size ++ "OffAddr#"
"Word#" -> "readWord" ++ show size ++ "OffAddr#"
transE (EPrim (APrim Peek { primArgType = at } _) [x] (ELit LitCons { litName = n })) = mparen $ ans where
ans = do x <- ans'; castVal at (show n) x
ans' = mparen $ do
x <- transE x
return (text func <+> x <+> text "0#")
(tt,_,_) = cTypeInfo at
Just pi = primitiveInfo at
size = primTypeSizeOf pi * 8
sign = primTypeIsSigned pi
func = case tt of
"Char#" -> "indexWideCharOffAddr#"
"Addr#" -> "indexAddrOffAddr#"
"Int#" -> "indexInt" ++ show size ++ "OffAddr#"
"Word#" -> "indexWord" ++ show size ++ "OffAddr#"
transE (EPrim (APrim Poke { primArgType = at } _) [w,ptr,v] _) = mparen ans where
ans = do
w <- transE w
ptr <- transE ptr
v <- transE v
return (text func <+> ptr <+> text "0#" <+> v <+> w)
Just pi = primitiveInfo at
size = primTypeSizeOf pi * 8
sign = primTypeIsSigned pi
(tt,_,_) = cTypeInfo at
func = case tt of
"Char#" -> "writeWideCharOffAddr#"
"Addr#" -> "writeAddrOffAddr#"
"Int#" -> "writeInt" ++ show size ++ "OffAddr#"
"Word#" -> "writeWord" ++ show size ++ "OffAddr#"
transE (EPrim (APrim (AddrOf addr) _) [] _) = mparen $ do
tell mempty { collPrims = Set.singleton (AddrOf addr) }
return (text $ "unPtr addr_" ++ mangleIdent (unpackPS addr))
transE (EPrim (APrim func@Func {} _) xs _) = mparen $ do
tell mempty { collPrims = Set.singleton func }
xs <- mapM transE xs
return (hsep (text (cfuncname func):xs))
--transE (EPrim (APrim cast@CCast { primArgType = at, primRetType = rt } _) [x] _) = mparen $ transE x >>= \x -> castVal at rt x
transE e = mparen $ return $ text "error" <+> tshow ("ToHs.Error: " ++ show e)
ghcPrimTable = [
("newWorld__","newWorld__"),
("catch__","catch#"),
("raiseIO__","raiseIO#"),
("newRef__","newMutVar#"),
("readRef__","readMutVar#"),
("writeRef__","writeMutVar#"),
("newMutArray__","newArray#"),
("readArray__","readArray#"),
("writeArray__","writeArray#"),
("indexArray__","indexArray#"),
("unsafeFreezeArray__","unsafeFreezeArray#"),
("alloca__","alloca__")
]
castVal :: ExtType -> ExtType -> Doc -> TM Doc
castVal at rt x = case (showCType at,showCType rt) of
(a,b) | a == b -> return x
z | Just co <- lookup z coercions -> mparen $ return (text co <+> x)
xs -> fail $ "unknown coercion: " ++ show xs
where
coercions = [
(("Int#","Char#"),"chr#"),
(("Char#","Int#"),"ord#"),
(("Addr#","Int#"),"addr2Int#"),
(("Int#","Addr#"),"int2Addr#"),
(("Word#","Int#"),"word2Int#"),
(("Int#","Word#"),"int2Word#"),
(("Char#","Word#"),"char2Word__"),
(("Word#","Char#"),"word2Char__"),
(("Addr#","Word#"),"addr2Word__"),
(("Word#","Addr#"),"word2Addr__")
]
cfuncname Func { funcName = fn, funcIOLike = iol, primArgTypes = as, primRetType = r } = text $ ("func_" ++ (if iol then "io" else "pure") ++ "_" ++ unpackPS fn ++ intercalate "_" (r:as))
hasBoxes e = or $ execWriter (f e) where
f e | e == tBox = tell [True] >> return e
f e = emapEGH f f f e
op2Table (op,rt) = lookup (showCType rt) table >>= lookup op where
table = [ ("Int#",intTable),("Word#",wordTable),("Addr#",addrTable)]
intTable = [
("+","(+#)"),
("-","(-#)"),
("*","(*#)"),
("%","remInt#"),
("/","quotInt#")
]
wordTable = [
("+","plusWord#"),
("-","minusWord#"),
("*","timesWord#"),
("%","remWord#"),
("/","quotWord#")
]
addrTable = [ ("+","plusAddr__") ]
op2TableCmp (op,rt) = lookup rt table >>= lookup op where
table = [ ("Int#",intTable), ("Char#",charTable), ("Addr#",addrTable),("Word#",wordTable)]
intTable = [
(">","(>#)"),
("==","(==#)"),
("<","(<#)"),
(">=","(>=#)"),
("<=","(<=#)")
]
charTable = [
(">","gtChar#"),
("==","eqChar#"),
("<","ltChar#"),
(">=","gteChar#"),
("<=","lteChar#")
]
addrTable = [
(">","gtAddr#"),
("==","eqAddr#"),
("<","ltAddr#"),
(">=","gteAddr#"),
("<=","lteAddr#")
]
wordTable = [
(">","gtWord#"),
("==","eqWord#"),
("<","ltWord#"),
(">=","gteWord#"),
("<=","lteWord#")
]
transAlt :: Bool -> Doc -> Alt E -> TM Doc
transAlt dobind b (Alt LitInt { litNumber = i, litType = tt } e) = do
let (t,_,_) = cTypeInfoT tt
e <- noParens $ transE e
case t of
"Int#" -> return $ (if dobind then b <> char '@' else empty) <> tshow i <> text "#" <+> text "->" <+> e
"Char#" -> return $ (if dobind then b <> char '@' else empty) <> text (show $ chr $ fromIntegral i) <> text "#" <+> text "->" <+> e
_ -> do
let bvar = if dobind then b else text "_bvar"
Just eq = op2TableCmp ("==",t)
v <- transE (ELit (LitInt i tt))
return (bvar <+> text "|" <+> text eq <+> bvar <+> v <+> text "->" <+> e)
transAlt dobind b (Alt LitCons { litName = c, litArgs = ts } e) = do
tell mempty { collNames = Set.singleton (c,length ts,False) } -- XXX this shouldn't be needed
ts <- mapM transTVr ts
e <- noParens $ transE e
return ( (if dobind then b <> char '@' else empty) <> showCon c ts <+> text "->" <+> e)
transTVr :: TVr -> TM Doc
transTVr TVr { tvrIdent = 0 } = return $ char '_'
transTVr tvr = return (text $ 'v':mangleIdent (pprint tvr))
mangleIdent xs = concatMap f xs where
f '.' = "__"
f '@' = "_a"
f ',' = "_c"
f '(' = "_L"
f ')' = "_R"
f '$' = "_d"
f '%' = "_P"
f '#' = "_h"
f '/' = "_s"
f '=' = "_e"
f '+' = "_p"
f '-' = "_m"
f '!' = "_b"
f '>' = "_r"
f '<' = "_l"
f '\'' = "_t"
f '_' = "_u"
f c | isAlphaNum c = [c]
f c = '_':'x':showHex (ord c) ""