module DDC.Core.Salt.Convert.Exp
( Config (..)
, Context (..)
, convBlockM
, convAltM
, convRValueM
, convPrimCallM)
where
import DDC.Core.Salt.Convert.Name
import DDC.Core.Salt.Convert.Prim
import DDC.Core.Salt.Convert.Base
import DDC.Core.Salt.Convert.Type
import DDC.Core.Salt.Name
import DDC.Core.Salt.Platform
import DDC.Core.Module
import DDC.Core.Exp.Annot
import DDC.Type.Env (KindEnv, TypeEnv)
import DDC.Base.Pretty
import DDC.Control.Monad.Check (throw)
import qualified DDC.Type.Env as Env
data Config a
= Config
{ configPlatform :: Platform
, configModule :: Module a Name }
data Context
= ContextTop
| ContextNest (Bind Name)
deriving Show
isContextNest :: Context -> Bool
isContextNest cc
= case cc of
ContextNest{} -> True
_ -> False
convBlockM
:: Show a
=> Config a
-> Context -> KindEnv Name -> TypeEnv Name
-> Exp a Name
-> ConvertM a Doc
convBlockM config context kenv tenv xx
= case xx of
XApp{}
| ContextTop <- context
-> case takeXPrimApps xx of
Just (NamePrimOp p, xs)
| isControlPrim p || isCallPrim p
-> do x1 <- convPrimCallM config kenv tenv p xs
return $ x1 <> semi
_ -> throw $ ErrorBodyMustPassControl xx
| ContextNest{} <- context
, Just (NamePrimOp p, xs) <- takeXPrimApps xx
, isControlPrim p || isCallPrim p
-> do x1 <- convPrimCallM config kenv tenv p xs
return $ x1 <> semi
_
| isRValue xx
, ContextNest (BName n _) <- context
, Just n' <- seaNameOfLocal n
-> do xx' <- convRValueM config kenv tenv xx
return $ vcat
[ fill 12 n' <+> equals <+> xx' <> semi ]
| isRValue xx
, ContextNest (BNone _) <- context
-> do xx' <- convRValueM config kenv tenv xx
return $ vcat
[ xx' <> semi ]
XLet _ (LLet b x1@XCase{}) x2
-> do
x1' <- convBlockM config (ContextNest b) kenv tenv x1
let tenv' = Env.extend b tenv
x2' <- convBlockM config context kenv tenv' x2
return $ vcat
[ x1'
, x2' ]
XLet _ (LLet b x1) x2
-> do x1' <- convRValueM config kenv tenv x1
x2' <- convBlockM config context kenv tenv x2
let dst = case b of
BName n@NameVar{} _
| Just n' <- seaNameOfLocal n
-> fill 12 n' <+> equals <> space
_ -> empty
return $ vcat
[ dst <> x1' <> semi
, x2' ]
XLet _ (LPrivate bs _mt ws) x
-> let kenv' = Env.extends bs kenv
tenv' = Env.extends ws tenv
in convBlockM config context kenv' tenv' x
XCase _ _x [AAlt PDefault x1]
-> do convBlockM config context kenv tenv x1
XCase _ _x (AAlt (PData DaConUnit []) x1 : _)
-> do convBlockM config context kenv tenv x1
XCase _ x [ AAlt (PData dc []) x1
, AAlt PDefault xFail]
| isFailX xFail
, Just n <- takeNameOfDaCon dc
, Just n' <- convDaConName n
-> do
x' <- convRValueM config kenv tenv x
x1' <- convBlockM config context kenv tenv x1
xFail' <- convBlockM config context kenv tenv xFail
return $ vcat
[ text "if"
<+> parens (x' <+> text "!=" <+> n')
<+> xFail'
, x1' ]
XCase _ x [ AAlt (PData dc1 []) x1
, AAlt (PData dc2 []) x2 ]
| Just (NamePrimLit (PrimLitBool True)) <- takeNameOfDaCon dc1
, Just (NamePrimLit (PrimLitBool False)) <- takeNameOfDaCon dc2
-> do x' <- convRValueM config kenv tenv x
x1' <- convBlockM config context kenv tenv x1
x2' <- convBlockM config context kenv tenv x2
return $ vcat
[ text "if" <> parens x'
, lbrace <> indent 7 x1' <> line <> rbrace
, text "else"
, lbrace <> indent 7 x2' <> line <> rbrace ]
XCase _ x alts
-> do x' <- convRValueM config kenv tenv x
alts' <- mapM (convAltM config context kenv tenv) alts
return $ vcat
[ text "switch" <+> parens x'
, lbrace <> indent 1 (vcat alts')
, rbrace ]
XCast _ _ x
-> convBlockM config context kenv tenv x
_ -> throw $ ErrorBodyInvalid xx
isControlPrim :: PrimOp -> Bool
isControlPrim pp
= case pp of
PrimControl{} -> True
_ -> False
isCallPrim :: PrimOp -> Bool
isCallPrim pp
= case pp of
PrimCall{} -> True
_ -> False
isFailX :: Exp a Name -> Bool
isFailX (XApp _ (XVar _ (UPrim (NamePrimOp (PrimControl PrimControlFail)) _)) _)
= True
isFailX _ = False
convAltM
:: Show a
=> Config a
-> Context -> KindEnv Name -> TypeEnv Name
-> Alt a Name
-> ConvertM a Doc
convAltM config context kenv tenv aa
= let end
| isContextNest context = line <> text "break;"
| otherwise = empty
in case aa of
AAlt PDefault x1
-> do x1' <- convBlockM config context kenv tenv x1
return $ vcat
[ text "default:"
, lbrace <> indent 5 (x1' <> end)
<> line
<> rbrace]
AAlt (PData dc []) x1
| Just n <- takeNameOfDaCon dc
, Just n' <- convDaConName n
-> do x1' <- convBlockM config context kenv tenv x1
return $ vcat
[ text "case" <+> n' <> colon
, lbrace <> indent 5 (x1' <> end)
<> line
<> rbrace]
AAlt{} -> throw $ ErrorAltInvalid aa
convDaConName :: Name -> Maybe Doc
convDaConName nn
| NamePrimVal (PrimValLit lit) <- nn
= case lit of
PrimLitBool True -> Just $ int 1
PrimLitBool False -> Just $ int 0
PrimLitNat i -> Just $ integer i
PrimLitInt i -> Just $ integer i
PrimLitWord i bits
| elem bits [8, 16, 32, 64]
-> Just $ integer i
PrimLitTag i -> Just $ integer i
_ -> Nothing
| otherwise
= Nothing
convRValueM
:: Show a
=> Config a
-> KindEnv Name -> TypeEnv Name
-> Exp a Name
-> ConvertM a Doc
convRValueM config kenv tenv xx
= case xx of
XVar _ (UName n)
| Just n' <- seaNameOfLocal n
-> return $ n'
XCon _ DaConUnit
-> return $ integer 0
XCon _ dc
| DaConPrim (NamePrimLit p) _ <- dc
-> case p of
PrimLitBool b
| b -> return $ integer 1
| otherwise -> return $ integer 0
PrimLitNat i -> return $ integer i
PrimLitInt i -> return $ integer i
PrimLitWord i _ -> return $ integer i
PrimLitTag i -> return $ integer i
PrimLitVoid -> return $ text "void"
_ -> throw $ ErrorRValueInvalid xx
XApp{}
| Just (NamePrimOp p, args) <- takeXPrimApps xx
-> convPrimCallM config kenv tenv p args
XApp{}
| Just (XVar _ (UName nSuper), args)
<- takeXApps xx
-> do
let Just nSuper'
= seaNameOfSuper
(lookup nSuper $ moduleImportValues $ configModule config)
(lookup nSuper $ moduleExportValues $ configModule config)
nSuper
args' <- mapM (convRValueM config kenv tenv)
$ filter keepFunArgX args
return $ nSuper' <> parenss args'
XType _ t
-> do t' <- convTypeM kenv t
return $ t'
XCast _ _ x
-> convRValueM config kenv tenv x
_ -> throw $ ErrorRValueInvalid xx
isRValue :: Exp a Name -> Bool
isRValue xx
= case xx of
XVar{} -> True
XCon{} -> True
XApp{} -> True
XCast _ _ x -> isRValue x
_ -> False
keepFunArgX :: Exp a n -> Bool
keepFunArgX xx
= case xx of
XType{} -> False
XWitness{} -> False
_ -> True
convPrimCallM
:: Show a
=> Config a
-> KindEnv Name -> TypeEnv Name
-> PrimOp -> [Exp a Name]
-> ConvertM a Doc
convPrimCallM config kenv tenv p xs
= let pp = configPlatform config
in case p of
PrimArith op
| [XType _ _, x1, x2] <- xs
, Just op' <- convPrimArith2 op
-> do x1' <- convRValueM config kenv tenv x1
x2' <- convRValueM config kenv tenv x2
return $ parens (x1' <+> op' <+> x2')
PrimCast PrimCastPromote
| [XType _ tDst, XType _ tSrc, x1] <- xs
, Just (NamePrimTyCon tcSrc, _) <- takePrimTyConApps tSrc
, Just (NamePrimTyCon tcDst, _) <- takePrimTyConApps tDst
, primCastPromoteIsValid pp tcSrc tcDst
-> do tDst' <- convTypeM kenv tDst
x1' <- convRValueM config kenv tenv x1
return $ parens tDst' <> parens x1'
PrimCast PrimCastTruncate
| [XType _ tDst, XType _ tSrc, x1] <- xs
, Just (NamePrimTyCon tcSrc, _) <- takePrimTyConApps tSrc
, Just (NamePrimTyCon tcDst, _) <- takePrimTyConApps tDst
, primCastTruncateIsValid pp tcSrc tcDst
-> do tDst' <- convTypeM kenv tDst
x1' <- convRValueM config kenv tenv x1
return $ parens tDst' <> parens x1'
PrimControl PrimControlReturn
| [XType _ _, x1] <- xs
-> do x1' <- convRValueM config kenv tenv x1
return $ text "return" <+> x1'
PrimControl PrimControlFail
| [XType _ _] <- xs
-> do return $ text "_FAIL()"
PrimCall (PrimCallTail arity)
| xFunTys : xsArgs <- drop (arity + 1) xs
, Just (xFun, _) <- takeXApps xFunTys
, XVar _ (UName nSuper) <- xFun
-> do
let Just nSuper'
= seaNameOfSuper
(lookup nSuper $ moduleImportValues $ configModule config)
(lookup nSuper $ moduleExportValues $ configModule config)
nSuper
xsArgs' <- mapM (convRValueM config kenv tenv) xsArgs
return $ text "return" <+> nSuper' <> parenss xsArgs'
PrimStore op
-> do let op' = convPrimStore op
xs' <- mapM (convRValueM config kenv tenv)
$ filter (keepPrimArgX kenv) xs
return $ op' <> parenss xs'
_ -> throw $ ErrorPrimCallInvalid p xs
keepPrimArgX :: KindEnv Name -> Exp a Name -> Bool
keepPrimArgX kenv xx
= case xx of
XType _ (TVar u)
| Just k <- Env.lookup u kenv
-> isDataKind k
XWitness{} -> False
_ -> True
parenss :: [Doc] -> Doc
parenss xs = encloseSep lparen rparen (comma <> space) xs