pec-0.2.0: Pec/Desugar.hs
{-# OPTIONS -Wall #-}
-- The pec embedded compiler
-- Copyright 2011-2012, Brett Letner
module Pec.Desugar (desugar) where
import Data.Char
import Data.Either
import Data.Generics.Uniplate.Data
import Data.List
import Data.Maybe
import Grm.Prims
import Language.Pec.Abs
import Text.PrettyPrint.Leijen
import qualified Language.Pds.Abs as D
import Pec.PUtil
desugar :: Module Point -> D.Module
desugar = rewriteBi dTyCxt . dModule . tModule
tModule :: Module Point -> Module Point
tModule m =
rewriteBi dBlockE $ -- BAL: make it so we don't have to call these twice
rewriteBi dConstructedE $
rewriteBi dTupleE $
rewriteBi dPatE $
rewriteBi dBlockE $
rewriteBi dConstructedE $
rewriteBi unProcD $
transformBi (sortBy cmpFieldD) $
transformBi (sortBy cmpFieldT) $
transformBi (sortBy cmpConC) $
transformBi (\(Con p s) -> Con (p :: Point) $ usern s) $
transformBi (\(Field p s) -> Field (p :: Point) $ usern s) $
transformBi (\(Var p s) -> Var (p :: Point) $ usern s) m
unCxt :: D.Type -> D.Type
unCxt x = case x of
D.TyCxt _ b -> b
_ -> x
isTyCxt :: D.Type -> Bool
isTyCxt x = case x of
D.TyCxt{} -> True
_ -> False
cxtOf :: D.Type -> D.CxtList
cxtOf x = case x of
D.TyCxt a _ -> a
_ -> []
tyCxt :: [D.Cxt] -> D.Type -> D.Type
tyCxt xs y = D.TyCxt (sort $ nub xs) y
dTyCxt :: D.Type -> Maybe D.Type
dTyCxt x = case x of
D.TyCxt a b | isTyCxt b -> Just $ tyCxt (a ++ cxtOf b) (unCxt b)
D.TyFun a b | any isTyCxt [a,b] ->
Just $ tyCxt (cxtOf a ++ cxtOf b) $ D.TyFun (unCxt a) (unCxt b)
D.TyConstr a bs | any isTyCxt bs ->
Just $ tyCxt (concatMap cxtOf bs) $ D.TyConstr a $ map unCxt bs
_ -> Nothing
dModule :: Module Point -> D.Module
dModule (Module _ a b c ds) = D.Module (dModid a)
(dExportDecls ys xs b) (dImportDecls c) ys zs
(concatMap constrs ys ++ concatMap deconstrs ys ++ xs)
where
(ws,xs) = partitionEithers $ map dTopDecl $ unAscribeDs ds
(ys,zs) = partitionEithers ws
usern :: String -> String
usern s = case a of
"" -> b ++ "_"
_ -> a ++ "." ++ b ++ "_"
where
(a,b) = breakQual s
breakQual :: String -> (String,String)
breakQual s = (vModid $ qual $ init ss, last ss)
where ss = unqual s
vModid :: String -> String
vModid s
| null s = s
| '_' `elem` s =
error $ "underscore not allowed in module identifier:" ++ s
| otherwise = qual_to_und s ++ "_"
dModid :: Modid Point -> String
dModid = vModid . ppShow
cmpFieldD :: FieldD Point -> FieldD Point -> Ordering
cmpFieldD (FieldD _ a _) (FieldD _ b _) = compare a b
cmpFieldT :: FieldT Point -> FieldT Point -> Ordering
cmpFieldT (FieldT _ a _) (FieldT _ b _) = compare a b
cmpConC :: ConC Point -> ConC Point -> Ordering
cmpConC (ConC _ a _) (ConC _ b _) = compare a b
dExportDecls :: [D.TypeD] -> [D.VarD] -> ExportDecls Point -> [D.ExportD]
dExportDecls xs ys z = case z of
ExpAllD p -> concatMap (dSpec (Both p)) xs ++ map exVarD ys
ExpListD _ es -> concatMap (dExport xs) es
dExport :: [D.TypeD] -> Export Point -> [D.ExportD]
dExport xs y = case y of
VarEx{} -> [D.VarEx $ ppShow y]
TypeEx _ a b -> case [ d | d@(D.TypeD c _ _) <- xs, c == ppShow a ] of
[] -> error $ "unknown type in export list:" ++ ppShow a
(z:_) -> dSpec b z
exVarD :: D.VarD -> D.ExportD
exVarD (D.VarD a _ _) = D.VarEx a
tTypeD :: D.TypeD -> D.Type
tTypeD (D.TypeD a bs c) =
D.TyCxt cxt $ D.TyConstr a $ map (D.TyVarT . ppShow) bs
where cxt = nub $ universeBi c
tTag :: D.Type -> D.Type
tTag t = D.TyConstr "Tag" [t]
constrs :: D.TypeD -> [D.VarD]
constrs d@(D.TypeD _ _ x) = case x of
D.TySyn{} -> []
D.TyNewtype a b -> [ fun2 "mk" "nt" a $ f b t ]
D.TyUnit b -> [ fun2 "mk" "uni" b t ]
D.TyEnum bs -> [ fun2 "mk" "tg" b t | b <- bs ]
D.TyRecord{} -> []
D.TyTagged ys -> [ fun "mk" a $ f b t | D.ConC a b <- ys ]
where
t = tTypeD d
f ta tb = if ta == D.TyVoid then tb else D.TyFun ta tb
deconstrs :: D.TypeD -> [D.VarD]
deconstrs d@(D.TypeD _ _ x) = case x of
D.TySyn{} -> []
D.TyNewtype a b ->
[ fun "unwrap_" a (D.TyFun t b)
, let p = uId a "p" in
fun "unwrapptr_" a (D.TyFun (aptr p t) (aptr p b)) ]
D.TyUnit b -> [ fun2 "tg" "uni" b t ]
D.TyEnum bs -> [ fun "tg" b t | b <- bs ]
D.TyRecord ys ->
[ let p = uId a "p" in fun "fld" a (D.TyFun (aptr p t) (aptr p b))
| D.FieldT a b <- ys ]
D.TyTagged ys ->
[ fun "tg" a $ D.TyFun (rptr_ t) (tTag t) | D.ConC a _ <- ys ] ++
[ let p = uId a "p" in fun "un" a $
D.TyFun (rptr p t) (if b == D.TyVoid then b else rptr p b)
| D.ConC a b <- ys
]
where
t = tTypeD d
rptr :: String -> D.Type -> D.Type
rptr p a =
D.TyCxt [ D.Cxt "Load_" [D.Var p]] $ aptr p a
aptr :: String -> D.Type -> D.Type
aptr p a = D.TyConstr "Pointer_" [D.TyVarT p ,a]
rptr_ :: D.Type -> D.Type
rptr_ a = let p = uId a "p" in rptr p a
fun2 :: Pretty a => String -> String -> a -> D.Type -> D.VarD
fun2 a x b c = D.VarD (lowercase s ++ a) D.Macro $
D.AscribeE (D.AppE (D.VarE x) (dExp $ stringE s)) c
where s = ppShow b
fun :: Pretty a => String -> a -> D.Type -> D.VarD
fun a = fun2 a a
dSpec :: Spec Point -> D.TypeD -> [D.ExportD]
dSpec a b@(D.TypeD c _ _) = case a of
Neither _ -> [D.TypeEx c]
Decon p -> dSpec (Neither p) b ++ map exVarD (deconstrs b)
Both p -> dSpec (Decon p) b ++ map exVarD (constrs b)
un :: Pretty a => a -> String
un x = lowercase (ppShow x) ++ "un"
fld :: Pretty a => a -> String
fld x = ppShow x ++ "fld"
dImportDecls :: ImportDecls Point -> [D.ImportD]
dImportDecls x = case x of
ImpListD _ a -> map f a
ImpNoneD _ -> []
where
f y = case y of
Import _ a (AsAS _ b) -> D.ImportD (dModid a) (D.AsAS $ dModid b)
Import _ a (EmptyAS _) -> D.ImportD (dModid a) D.EmptyAS
dVar :: Pretty a => a -> D.Var
dVar = D.Var . ppShow
dTyArity :: D.Type -> Int
dTyArity x = case x of
D.TyCxt _ b -> dTyArity b
D.TyFun _ b -> 1 + dTyArity b
_ -> 0
dTopDecl :: TopDecl Point -> Either (Either D.TypeD D.InstD) D.VarD
dTopDecl (VarD _ a b c) = Right $ D.VarD (ppShow a) (dDeclSym b) (dExp c)
dTopDecl (ExternD p a b c) = Right $ D.VarD (ppShow b) D.Macro
(dExp $ AscribeE p (AppE p (AppE p (varE "extern") (stringE v))
(stringE $ show $ dTyArity $ dType c)) c)
where
v = case a of
SomeNm _ x -> x
NoneNm _ -> init $ ppShow b
dTopDecl (TypeD0 p a bs) =
dTopDecl (TypeD p a bs $ TyTagged p [ConC p (Con p c) []]) -- BAL: should also use module name to avoid clashes.
where c = uId a (ppShow a)
dTopDecl (TypeD _ a bs c) =
Left $ Left $ D.TypeD (ppShow a) (map dVar bs) $
case c of
TySyn _ x -> D.TySyn $ dType x
TyRecord _ xs -> D.TyRecord [ D.FieldT (ppShow y) (dType z)
| FieldT _ y z <- xs ]
TyTagged _ xs -> case xs of
[] -> error "unused:dTopDecl:[]" -- BAL: support?
[ConC _ y []] -> D.TyUnit (ppShow y)
[ConC p y zs] -> D.TyNewtype (ppShow y) $ dType $ TyTuple p zs
_ | all isEnumC xs ->
D.TyEnum [ D.EnumC (ppShow y) | ConC _ y [] <- xs ]
_ -> D.TyTagged [ D.ConC (ppShow y) (dType $ TyTuple p zs)
| ConC p y zs <- xs ]
dTopDecl (InstD _ a b) = Left $ Right $ D.InstD (ppShow a) (dType b)
dTopDecl _ = error "unused:dTopDecl"
unAscribeDs :: [TopDecl Point] -> [TopDecl Point]
unAscribeDs xs = foldr unAscribeD zs ys
where
(ys,zs) = partition isAscribeD xs
unProcD :: TopDecl Point -> Maybe (TopDecl Point)
unProcD (ProcD p a bs c d) = Just $ VarD p a c $ LamE p bs d
unProcD _ = Nothing
unAscribeD :: TopDecl Point -> [TopDecl Point] -> [TopDecl Point]
unAscribeD (AscribeD _ x y) xs = case ys of
[] -> error $ "unknown ascription:" ++ ppShow x
_ -> [ VarD p a b $ AscribeE p c y | VarD p a b c <- ys ] ++ zs
where
(ys,zs) = partition f xs
f (VarD _ a _ _) = ppShow a == ppShow x
f _ = False
unAscribeD _ _ = error "unused:unAscribeD"
isAscribeD :: TopDecl Point -> Bool
isAscribeD (AscribeD{}) = True
isAscribeD _ = False
isEnumC :: ConC Point -> Bool
isEnumC (ConC _ _ xs) = null xs
dTyVar :: TyVar Point -> D.Type
dTyVar x = case x of
VarTV _ a -> D.TyVarT $ usern a
CntTV _ a -> D.TyCxt [D.Cxt "Count" [D.Var s]] $ D.TyVarT s
where s = usern a
dDeclSym :: DeclSym Point -> D.DeclSym
dDeclSym x = case x of
Macro _ -> D.Macro
Define _ -> D.Define
dType :: Type Point -> D.Type
dType x = case x of
TyCxt _ a b -> D.TyCxt (map dCxt a) (dType b)
TyFun _ a b -> D.TyFun (dType a) (dType b)
TyConstr _ a b -> D.TyConstr (ppShow a) $ map dType b
TyTuple p ts -> case ts of
[] -> D.TyVoid
[t] -> dType t
[a,b] -> dType $ TyConstr p (Con p "Pair_") [a,b]
(a:bs) -> dType $ TyTuple p [a, TyTuple p bs]
TyVarT _ a -> dTyVar a
TyConstr0 p a -> dType $ TyConstr p a []
TyArray p a b -> dType $ TyConstr p (Con p "Array_") [a,b]
TyCount p (Count _ a)
| all isDigit a -> dType $ TyConstr0 p (Con p $ "Cnt" ++ ppShow a)
| otherwise -> error "literal count types must be in decimal form"
dCxt :: Cxt Point -> D.Cxt
dCxt (Cxt _ a bs) = D.Cxt (ppShow a) $ map dVar bs
varE :: Lident -> Exp Point
varE = VarE p . Var p
where p = noPoint
stringE :: String -> Exp Point
stringE = LitE p . StringL p
where p = noPoint
nmbrE :: Integer -> Exp Point
nmbrE = LitE p . NmbrL p . show
where p = noPoint
apps :: [Exp Point] -> Exp Point
apps = foldl1 (AppE noPoint)
dSwitchAlt :: SwitchAlt Point -> D.SwitchAlt
dSwitchAlt (SwitchAlt _ a b) = D.SwitchAlt (dLit "tg" a) (dExp b)
dCaseAlt :: Exp Point -> CaseAlt Point -> D.SwitchAlt
dCaseAlt e (CaseAlt p a bs c) = D.SwitchAlt
(D.AppE (dLit "tg" $ EnumL p a) (dExp e))
(dExp $ rewriteBi dPatE $ LetE p (TupleE p $ map (VarE p) bs) (Macro p)
(AppE p (varE $ un a) e) c)
tagv :: Exp Point -> D.Exp
tagv a = dExp $ AppE (point a) (varE "tagv") a
defE :: Exp Point -> Var Point -> Exp Point -> D.Exp
defE a b c = dExp $ AppE (point a) (LamE (point c) [VarE (point b) b] c) a
dBlockE :: Exp Point -> Maybe (Exp Point)
dBlockE x = case x of
BlockE p0 ys -> case ys of
[] -> error "unused:dBlockE"
[y] -> Just y
(e:es) -> case e of
LetS p1 a b c -> Just $ LetE p1 a b c $ BlockE p0 es
_ -> Just $ apps [varE "then_", e, BlockE p0 es]
_ -> Nothing
dPatE :: Exp Point -> Maybe (Exp Point)
dPatE x = case x of
LamE p bs c -> case bs of
[] -> error "unused:dPatE"
[b] -> case unPat b c of
Nothing -> Nothing
Just (b1,c1) -> Just $ LamE p [b1] c1
(d:ds) -> Just $ LamE p [d] $ LamE p ds c
LetE p a b c d -> case unPat a d of
Nothing -> Nothing
Just (a1,d1) -> Just $ LetE p a1 b c d1
_ -> Nothing
unPat :: Exp Point -> Exp Point -> Maybe (Exp Point, Exp Point)
unPat x y = case x of
TupleE p xs -> case xs of
[] -> Just (AscribeE p (varE "_u") (TyTuple p []), y)
[a] -> Just (a, y)
[a,b] -> Just (v, LetE p a (Macro p) (apps [varE "fst_fld", v])
(LetE p b (Macro p) (apps [varE "snd_fld", v]) y))
where
v = varE $ uId x "p"
(e:es) -> Just (TupleE p [e, TupleE p es], y)
AscribeE _ a _ -> Just (a, apps [varE "ignore_", x, y])
VarE{} -> Nothing
_ -> error $ "unexpected pattern:" ++ ppShow x
dExp :: Exp Point -> D.Exp
dExp x = case x of
BlockE{} -> error "unused:dExp:BlockE"
LetS{} ->
error "malformed let statement (must not be the last expr in a block)"
LetE _ a b c d -> case a of
VarE{} -> D.LetE (ppShow a) (dDeclSym b) (dExp c) (dExp d)
_ -> error $ "unused:dExp:LetE:" ++ ppShow a
LamE _ bs c -> case bs of
[VarE _ (Var _ a)] -> D.LamE a (dExp c)
_ -> error "unused:dExp:LamE"
SwitchE _ a bs (DefaultNone{}) ->
D.SwitchE (dExp a) D.DefaultNone $ map dSwitchAlt bs
SwitchE _ a@(VarE{}) bs (DefaultAlt _ c d) ->
D.SwitchE (dExp a) (D.DefaultSome $ defE a c d) (map dSwitchAlt bs)
SwitchE p a bs c -> dExp $ LetE p v (Define p) a $ SwitchE p v bs c
where v = varE $ uId x "s"
CaseE _ a@(VarE{}) bs (DefaultNone{}) ->
D.SwitchE (tagv a) D.DefaultNone (map (dCaseAlt a) bs)
CaseE _ a@(VarE{}) bs (DefaultAlt _ c d) ->
D.SwitchE (tagv a) (D.DefaultSome $ defE a c d) (map (dCaseAlt a) bs)
CaseE p a bs c -> dExp $ LetE p v (Define p) a $ CaseE p v bs c
where v = varE $ uId x "c"
BranchE _ ys0 z -> case ys0 of
[] -> dExp z
(BranchAlt p a b : ys) ->
dExp $ apps [varE "if_", a, b, BranchE p ys z]
StoreE _ a b -> dExp $ apps [varE "store_", a, b]
BinOpE _ a b c -> dExp $ apps [opE b, a, c]
UnOpE p a b -> dExp $ AppE p (opE a) b
IdxE _ a b -> dExp $ apps [varE "idx_", a, b]
FldE p a b -> dExp $ AppE p (varE $ fld b) a
CountE _ (Count _ a) -> dCnt $ ppShow a
AppE _ a b -> D.AppE (dExp a) (dExp b)
ArrayE{} -> error "unused:dExp:ArrayE"
RecordE{} -> error "unused:dExp:RecordE"
TupleE{} -> error "unused:dExp:TupleE"
AscribeE _ a b -> D.AscribeE (dExp a) (dType b)
VarE _ a -> D.VarE $ ppShow a
LitE _ a -> dLit "mk" a
dTupleE :: Exp Point -> Maybe (Exp Point)
dTupleE (TupleE p xs) = case xs of
[] -> Just $ VarE p (Var p "void_")
[a] -> Just a
[a,b] -> Just $ RecordE p [ fieldD "fst_" a, fieldD "snd_" b ]
(a:bs) -> Just $ TupleE p [a, TupleE p bs]
dTupleE _ = Nothing
fieldD :: String -> Exp Point -> FieldD Point
fieldD a e = FieldD p (Field p a) e
where p = point e
countE :: Int -> Exp Point
countE = CountE noPoint . Count noPoint . show
dConstructedE :: Exp Point -> Maybe (Exp Point)
dConstructedE (ArrayE p xs) = Just $ BlockE p $
[ LetS p v (Define p) $ AppE p (varE "unsafe_alloca_array_")
(countE $ length xs) ] ++
[ StoreE p (IdxE p v $ nmbrE i) x | (i,x) <- zip [0 .. ] xs ] ++
[ AppE p (varE "load_") v ]
where
v = varE $ uId xs "a"
dConstructedE (RecordE p0 xs) = Just $ BlockE p0 $
[ LetS p0 v (Define p0) $ varE "unsafe_alloca_" ] ++
[ StoreE p (FldE p v a) b | FieldD p a b <- xs ] ++
[ AppE p0 (varE "load_") v ]
where
v = varE $ uId xs "r"
dConstructedE _ = Nothing
dCnt :: String -> D.Exp
dCnt x = dExp $ varE $ "cnt" ++ x
dLit :: String -> Lit Point -> D.Exp
dLit suf x = case x of
CharL _ a -> D.LitE $ D.CharL a
StringL _ a -> D.LitE $ D.StringL a
NmbrL _ a -> D.LitE $ D.NmbrL a
EnumL{} -> dExp $ varE $ lowercase $ ppShow x ++ suf
opE :: Pretty a => a -> Exp Point
opE a = varE $ concatMap f $ ppShow a
where
f x = fromMaybe err $ lookup x
[ ('!', "bang_"), ('"', "dquote_"), ('#', "hash_"), ('$', "dollar_")
, ('%', "rem_"), ('&', "band_"), ('\'', "squote_"), ('(', "lparen_")
, (')', "rparen_"), ('*', "mul_"), ('+', "add_"), (',', "comma_")
, ('-', "sub_"), ('.', "dot_"), ('/', "div_"), (':', "colon_")
, (';', "semi_"), ('<', "lt_"), ('=', "eq_"), ('>', "gt_")
, ('?', "ques_"), ('@', "load_"), ('[', "lbrack_"), ('\\', "bslash_")
, (']', "rbrack_"), ('^', "bxor_"), ('`', "grave_"), ('{', "lbrace_")
, ('|', "bor_"), ('}', "rbrace_"), ('~', "bnot_")
]
where err = error $ "unknown character in operator:" ++ show x