module Core.Convert(makeCore,CoreImport(..)) where
import Id
import TokenId
import Util.Extra(mixLine,mixSpace,mix)
import PosCode
import StrPos
import List
import Data.Char
import Util.Extra
import Error
import IntState
import Maybe
import NT
import ForeignCode
import Control.Monad.State
import qualified Data.Map as Map
import qualified Data.Set as Set
import SysDeps(unpackPS, packString)
import Yhc.Core
-- | internal compiler state
data CState = CState {
csState :: IntState,
csBinds :: Map.Map Id String,
csBound :: Set.Set Id,
csFail :: CoreExpr,
csNextFail :: Int,
csImports :: Set.Set CoreImport
}
-- | a symbol imported to core
data CoreImport = CoreImportCtor {
coreImportName :: String,
coreImportCtorData :: CoreData
} | CoreImportFunc {
coreImportName :: String,
coreImportFuncArity :: Int
}
deriving (Ord,Eq)
instance Show CoreImport where
show (CoreImportCtor name dat) = name ++ " from " ++ show dat
show (CoreImportFunc name arity) = name ++ " " ++ intersperse ' ' (replicate arity '_')
-- | compiler monad
type CMonad a = State CState a
-- | convert pos lambda to yhc core
makeCore :: [String] -> IntState -> [Id] -> [(Id,PosLambda)] -> (Core,[CoreImport])
makeCore imports state datas funcs = (core,coreimports)
where
modu = getModuleId state
(core,cstate) = runState (cProgram imports datas funcs) $ CState state Map.empty Set.empty nofail 0 Set.empty
nofail = error "makeCore: no failure on stack"
coreimports = builtinImports ++ (Set.toList $ csImports cstate)
-- | imports for Prelude.True and Prelude.False which are used internally in compiling ifs
-- | also includes the one tuple as that can't be expressed in Haskell
builtinImports :: [CoreImport]
builtinImports = dataToImp dataBool ++ dataToImp dataOne
where
dataToImp dat = map (\ctor -> CoreImportCtor (coreCtorName ctor) dat) $ coreDataCtors dat
dataBool = CoreData "Prelude;Bool" [] [ CoreCtor "Prelude;True" [], CoreCtor "Prelude;False" [] ]
dataOne = CoreData "Prelude;1()" ["a"] [ CoreCtor "Prelude;1()" [("a",Nothing)] ]
-- | convert a program to a core program
cProgram :: [String] -> [Id] -> [(Id,PosLambda)] -> CMonad Core
cProgram imports datas funcs = do
datas' <- mapM cData datas
funcs' <- mapM cFunc funcs
state <- getState
return $ Core (getModuleId state) imports datas' funcs'
-- | convert a data to a core data
cData :: Id -> CMonad CoreData
cData i = do
state <- getState
let (cdata,binds) = dataToCore state i
mapM_ (uncurry bind) binds
return cdata
-- | convert a data to a core data, pure version
dataToCore :: IntState -> Id -> (CoreData,[(Id,String)])
dataToCore state i = (cdata, (i,name) : map snd ctors)
where
name = encodeName state i
NewType free _ _ _ = typ
(InfoData _ _ _ typ children) = fst $ getInfo i () state
childs = case children of
(DataNewType _ x) -> x
(Data _ x) -> x
ctors = map (constrToCore state) childs
cdata = CoreData name (map strTVar free) (map fst ctors)
-- | convert a constructor to core, pure version
constrToCore :: IntState -> Id -> (CoreCtor,(Id,String))
constrToCore state i = (ctor,(i,name))
where
ctor = CoreCtor name $ zip (map cType targs) (map cField fields)
name = encodeName state i
NewType _ _ _ targs = typ
(InfoConstr _ _ _ _ typ fields _) = fst $ getInfo i () state
cField Nothing = Nothing
cField (Just x) = Just $ dropModule $ strIS state x
cType x = strNT (strIS state) strTVar x
-- | convert a function to core
cFunc :: (Id,PosLambda) -> CMonad CoreFunc
cFunc (i, PosLambda pos int fvs bvs e) = do
name <- bindGlobal i
args <- mapM (bindLocal . snd) bvs
e' <- cExpr e
return $ CoreFunc name args (wrapPos pos e')
where
wrapPos pos x | pos == noPos = x
| otherwise = CorePos (show pos) x
-- | convert a foreign function
cFunc (i, PosForeign pos id arity cname cc Imported) = do
state <- getState
name <- bindGlobal i
let arity = arityIS state i
(InfoVar un tok ex fix nt ar) = fromJust $ lookupIS state i
cname' = if cname == "" then getUnqualified tok else cname
cconv = show cc
syms = getSymbolTable state
memo = foreignMemo syms
forn = toForeign syms memo cc Imported cname arity i
case forn of
Foreign ie proto style mpath _ htok arity args res -> do
let types = map foreignArgType args ++ [foreignArgType res]
return $ CorePrim name arity cname' cconv True types
-- | calculate the type name for a foreign arg
foreignArgType :: Arg -> String
foreignArgType x =
case x of
Int8 -> "Data.Int;Int8"
Int16 -> "Data.Int;Int16"
Int32 -> "Data.Int;Int32"
Int64 -> "Data.Int;Int64"
Word8 -> "Data.Word;Word8"
Word16 -> "Data.Word;Word16"
Word32 -> "Data.Word;Word32"
Word64 -> "Data.Word;Word64"
Int -> "Prelude;Int"
Float -> "Prelude;Float"
Double -> "Prelude;Double"
Char -> "Prelude;Char"
Bool -> "Prelude;Bool"
Ptr -> "Foreign.Ptr;Ptr"
(FunPtr _) -> "Foreign.Ptr;FunPtr"
StablePtr -> "Foreign.StablePtr;StablePtr"
ForeignPtr -> "Foreign.ForeignPtr;ForeignPtr"
PackedString -> "Data.PackedString;PackedString"
Integer -> "Prelude;Integer"
(HaskellFun _) -> "Prelude;->"
(Unknown _) -> "Prelude;a"
Unit -> "Prelude;()"
-- | convert an expression to core
cExpr :: PosExp -> CMonad CoreExpr
cExpr x = case x of
-- literals
PosInt _ i -> return $ CoreLit $ CoreInt i
PosInteger _ i -> return $ CoreLit $ CoreInteger i
PosChar _ c -> return $ CoreLit $ CoreChr (chr c)
PosString _ s -> return $ CoreLit $ CoreStr s
PosFloat _ f -> return $ CoreLit $ CoreFloat f
PosDouble _ d -> return $ CoreLit $ CoreDouble d
-- simple expressions
PosExpDict e -> cExpr e
PosExpThunk p _ args -> cExpr (PosExpApp p args)
PosCon _ i -> liftM CoreCon $ bindGlobal i
PosPrim _ prim _ -> return $ CoreFun $ strPrim prim
PosVar _ i -> do
free <- isFree i
if free then do
name <- bindGlobal i
return (CoreFun name)
else do
name <- bindLocal i
return (CoreVar name)
PosExpApp _ (a:as) -> do
a' <- cExpr a
if a' == CoreFun "STRING" then cExpr (head as)
else do
as' <- mapM cExpr as
return (CoreApp a' as')
-- let bindings
PosExpLet _ _ [] e -> cExpr e
PosExpLet _ _ bs e -> inNewEnv $ do
ns <- mapM (\(i,_) -> bindLocal i) bs
binds <- zipWithM (\(i,PosLambda _ _ _ _ e) n -> do { x <- cExpr e ; return (n,x) }) bs ns
e' <- cExpr e
return (CoreLet binds e')
-- If and Case
PosExpIf pos g e1 e2 e3 -> do
e1' <- cExpr e1
e2' <- cExpr e2
e3' <- cExpr e3
let true = PatCon "Prelude;True" []
false = PatCon "Prelude;False" []
return $ CoreCase e1' [(true, e2'),(false,e3')]
PosExpCase pos e alts -> do
e' <- cExpr e
alts' <- mapM cAlt alts
return $ CoreCase e' alts'
where
cAlt (PosAltInt pos i False e) = do { x <- cExpr e ; return (PatLit $ CoreChr (chr i), x) }
cAlt (PosAltInt pos i True e) = do { x <- cExpr e ; return (PatLit $ CoreInt i, x) }
cAlt (PosAltCon pos c vars e) = inNewEnv $ do
vs <- mapM (bindLocal . snd) vars
con <- bindCon c
e' <- cExpr e
return (PatCon con vs, e')
-- fat bar and fail
PosExpFatBar _ e1@(PosExpCase {}) PosExpFail -> do
CoreCase a b <- cExpr e1
failExp <- getFailExpr
return $ CoreCase a (b ++ [(PatDefault,failExp)])
PosExpFatBar _ e1 PosExpFail -> cExpr e1
PosExpFatBar pos e1 e2 -> do
e2' <- cExpr e2
inNewFailure (\v -> CoreVar $ "v_fail_"++show v) $ \ var -> do
e1' <- cExpr (PosExpFatBar pos e1 PosExpFail)
return $ CoreLet [(fromCoreVar var, e2')] e1'
PosExpFail -> getFailExpr
other -> do
state <- getState
raiseError $ ErrorInternal "Core.Core.cExpr" (strPExp (strIS state) "" other)
-- | perform computation inside a new environment
inNewEnv :: CMonad a -> CMonad a
inNewEnv f = do
cs <- get
x <- f
modify $ \ cs' -> cs' { csBound = csBound cs }
return x
-- | perform computation inside new failure group
inNewFailure :: (Int -> CoreExpr) -> (CoreExpr -> CMonad a) -> CMonad a
inNewFailure fexp f = do
fnum <- State $ \ cs -> let n = csNextFail cs in (n,cs {csNextFail = n+1})
let exp = fexp fnum
oldFail <- State $ \ cs -> (csFail cs, cs { csFail = exp })
x <- f exp
modify $ \ cs -> cs { csFail = oldFail }
return x
-- | retrieve the current failure
getFailExpr :: CMonad CoreExpr
getFailExpr = gets csFail
-- | add a variable to the list of bound variables
addBound :: Id -> CMonad ()
addBound id = modify $ \ cs -> cs { csBound = Set.insert id (csBound cs) }
-- | test whether a variable is free
isFree :: Id -> CMonad Bool
isFree id = gets $ \ cs -> not (Set.member id (csBound cs))
-- | bind a variable to a name
bind :: Id -> String -> CMonad Bool
bind i s = State $ \ cs ->
let binds = csBinds cs
in case Map.lookup i binds of
Just s' -> if s /= s' then error $ "bind: rebind mismatch '"++s++"' '"++s'++"'"
else (False,cs)
Nothing ->
let cs' = cs { csBinds = Map.insert i s binds }
in (True,cs')
-- | build an import item from an identifier and its core name
buildImportItem :: IntState -> Id -> String -> CoreImport
buildImportItem state id name
| isConstr info = CoreImportCtor name coredata
| otherwise =
case info of
-- ermm I really wonder why tuple uses InfoName? [TS]
InfoName _ (TupleId n) _ _ _ -> CoreImportCtor name (tupleData n)
_ -> CoreImportFunc name arity
where
info = fromJust $ lookupIS state id
tid = tidI info
dataid = belongstoI info
(coredata,_) = dataToCore state dataid
arity = arityIS state id
-- build a datatype for a tuple ...
tupleData n = CoreData name types [ctor]
where
ctor = CoreCtor name $ map (\t -> (t,Nothing)) types
types = map (\x -> [x]) $ take n ['a'..]
-- | bind a global, this fixes issues with lambdas
bindGlobal :: Id -> CMonad String
bindGlobal i = do
(mod,item) <- gets $ \ cs -> encodeNamePair (csState cs) i
let name = mod ++ ";" ++ item
newBind <- bind i name
when newBind $ do
state <- getState
case lookupIS state i of
Nothing -> return ()
Just info -> do
-- if this is an import then build an import item
when (getModuleId state /= mod) $ do
let imp = buildImportItem state i name
modify $ \ cs -> cs { csImports = Set.insert imp (csImports cs) }
return name
-- | bind a local name, also adds to the list of bound variables
bindLocal :: Id -> CMonad String
bindLocal i = do
name <- gets $ \ cs ->
let (mod,item) = encodeNamePair (csState cs) i
thismod = getModuleId (csState cs)
in if mod /= thismod then error $ "bindLocal: ("++mod++";"++item++") in "++thismod
else item
bind i name
addBound i
return name
-- | bind a constructor
bindCon :: Id -> CMonad CoreCtorName
bindCon i = bindGlobal i
-- | get the state
getState :: CMonad IntState
getState = gets csState
-- | encode a name to a string
encodeName :: IntState -> Id -> String
encodeName state id = mod ++ ";" ++ item
where (mod,item) = encodeNamePair state id
-- | encode a name into a (module,item) pair
encodeNamePair :: IntState -> Id -> (String,String)
encodeNamePair state id =
case lookupIS state id of
Just info -> encode (tidI info)
Nothing -> encode (Visible $ packString $ reverse $ "v"++show id)
where
encode tok =
case tok of
TupleId n -> ("Prelude",tupleName n)
Visible rps -> (getModuleId state, unpackRPS rps)
Qualified mrps irps -> (unpackRPS mrps, unpackRPS irps)
Qualified2 mrps ctok ttok -> (unpackRPS mrps, encode' ttok ++ ";" ++ encode' ctok)
Qualified3 mrps ctok ttok mtok ->
case fromJust $ lookupIS state id of
InfoVar{} -> localFun
InfoName _ _ _ _ True -> localFun
_ -> (unpackRPS mrps, encode' ttok ++ ";" ++ encode' ctok ++ ";" ++ getUnqualified mtok)
where
localFun = case ttok of
TupleId n -> (unpackRPS mrps, show n ++ "_" ++ getUnqualified mtok)
_ -> error $ "encodeNamePair: '"++show tok++"' marked as local function!"
encode' tok = show tok -- does this work? let (mod,item) = encode tok in mod ++ "." ++ item
isDataType tok = isUpper $ head $ getUnqualified tok
unpackRPS rps = reverse $ unpackPS rps
-- | get the name of a tuple
tupleName :: Int -> String
tupleName 1 = "1()"
tupleName n = "("++replicate (n-1) ','++")"
|