Plan 9 from Bell Labs’s /usr/web/sources/contrib/fernan/nhc98/src/compiler98/Core/Convert.hs

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


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) ','++")"


Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.