alms-0.4.9: src/Sigma.hs
{-# LANGUAGE
GeneralizedNewtypeDeriving,
PatternGuards,
ViewPatterns #-}
module Sigma (
makeBangPatt, parseBangPatt, exSigma
) where
import Syntax
import Util
import qualified Control.Monad.State as CMS
import Data.Generics (Data, everywhere, mkT, extT)
import qualified Data.List as L
import qualified Data.Map as M
import qualified Data.Set as S
import Data.Foldable (Foldable, toList)
-- | To lift a binder to bind effect variables rather than
-- normal variables. (Boolean specifies whether the result
-- should include the effect variables.)
exSigma :: Id i =>
Bool ->
(Patt i -> Expr i -> a) ->
Patt i -> Expr i -> a
exSigma ret binder patt body =
let (b_vars, b_code) = transform (dv patt) body in
binder (ren patt) $
exLet' (paVar r1 -:: b_vars) b_code $
if ret
then exPair (exBVar r1) (patt2expr (ren (flatpatt patt)))
else exBVar r1
-- | To lift a binder to bind effect variables rather than
-- normal variables.
exAddSigma :: Id i =>
Bool ->
([Lid i] -> Patt i -> Expr i -> a) ->
S.Set (Lid i) -> Patt i -> Expr i -> a
exAddSigma ret binder env patt body =
let env' = dv patt
(b_vars, b_code) = transform (env' `S.union` env) body
vars = [ v | v <- b_vars, v `S.notMember` ren env' ]
in binder vars (ren patt) $
exLet' (paVar r1 -:: b_vars) b_code $
if ret
then exPair (exBVar r1) (patt2expr (ren (flatpatt patt))) +:: vars
else exBVar r1 +:: vars
{-
---- The one variable case:
(x is the variable name, y is the fresh state name)
fun !(x:t) -> e === fun y:t -> [[ e ]]
let !x = e1 in e2 === let y = e1 in [[ e ]]
[[ e1 x ]] = let (r, y) = [[ e1 ]] in
r y
[[ e1 e2 ]] = let (r1, y) = [[ e1 ]] in
let (r2, y) = [[ e2 ]] in
(r1 r2, y)
[[ x ]] = (y, ())
[[ v ]] = (v, y)
[[ match e with
| p1 -> e1
| ...
| pk -> ek ]]
= let (r, y) = [[ e ]] in
match r with
| p1 -> [[ e1 ]]
| ...
| pk -> [[ ek ]]
[[ e [t] ]] = let (r, y) = [[ e ]] in
(r [t], y)
[[ c e ]] = let (r, y) = [[ e ]] in
(c r, y)
-- The pattern case (2):
(p! is a renaming of p)
fun !(p:t) -> e === fun p!:t ->
let (r1, e.vars) = e.code
in (r1, p!)
where e.env = dv p in
let !p = e1 in e2 === let p! = e1 in
let (r1, e.vars) = e.code
in (r1, p!)
where e.env = dv p in
e ::= e1 p2 | dv p2 `subseteq` dv e.env && dv p2 != empty
e1.env = e.env
e.vars = e1.vars `union` dv p2!
e.code = let (r1, e1.vars) = e1.code in
let (r2, p2!) = r1 p2! in
(r2, e.vars)
e ::= e1 e2
e1.env = e2.env = e.env
e.vars = e1.vars `union` e2.vars
e.code = let (r1, e1.vars) = e1.code in
let (r2, e2.vars) = e2.code in
(r1 r2, e.vars)
e ::= x | x `member` dv p
e.vars = x!
e.code = (x!, ())
e ::= v
e.vars = fv v `intersect` env
e.code = let e.vars = e.vars! in
(v, [ () | _ <- e.vars ])
e ::= match p0 with
| p1 -> e1
| ...
| pk -> ek
| dv p0 `subseteq` dv e.env && dv p0 != empty
if p1 is a bang pattern
then e1.env = e.env `union` dv p1
else e1.env = e.env - (dv p1 - dv p0)
...
if pk is a bang pattern
then ek.env = e.env `union` dv pk
else ek.env = e.env - (dv pk - dv p0)
e.vars = e.env `intersection` (e1.vars `union` ... `union` ek.vars)
e.code = match p0! with
| p1[p0!/p0] -> let (p0 - p1)! = ((), ..., ()) in
let (r2, e1.vars) = e1.code in (r2, e.vars)
| ...
(if pk is not a bang pattern then)
| pk[p0!/p0] -> let (p0 - pk)! = ((), ..., ()) in
let (r2, e1.vars) = e1.code in (r2, e.vars)
(else)
| pk! -> let (p0 - pk)! = ((), ..., ()) in
let (r2, e1.vars) = e1.code in (r2, e.vars)
e ::= match e0 with
| p1 -> e1
| ...
| pk -> ek
e0.env = e.env
e1.env = e.env - dv p1
...
ek.env = e.env - dv pk
e.vars = e.env `intersection`
(e0.vars `union` e1.vars `union` ... `union` ek.vars)
e.code = let (r1, e0.vars) = e0.code in
match r1 with
| p1 -> let (r2, e1.vars) = e1.code in (r2, e.vars)
| ...
| pk -> let (r2, ek.vars) = ek.code in (r2, e.vars)
e ::= let rec f1 = v1
and ...
and fk = vk
in e1
captured = { x `in` (fv v1 `union` ... `union` fv vk)
| x! `in` e.env }
e1.env = e.env - { f1, ..., fk }
e.vars = e1.vars `union` captured!
e.code = let captured = captured! in
let captured! = ((), ..., ()) in
let rec f1 = v1
and ...
and fk = vk
in let (r1, e1.vars) = e1.code
in (r1, e.vars)
e ::= e1[t]
e1.env = e.env
e.vars = e1.vars
e.code = let (r1, e1.vars) = e1.code in
(r1[t], e.vars)
e ::= let !p1 = e1 in e2
e1.env = e.env
e2.env = e.env `union` dv p1
e.vars = e1.vars `union` (e2.vars `intersection` e.env)
e.code = let (p1!, e1.vars) = e1.code in
let (r2, e2.vars) = e2.code in
((r2, p1!), e.vars)
[assuming no shadowing]
-}
transform :: Id i => S.Set (Lid i) -> Expr i -> ([Lid i], Expr i)
transform env = loop where
capture e1
| vars <- [ v | J [] v <- M.keys (fv e1),
v `S.member` env ],
code <- translate paVar (exBVar . ren) vars .
kill (ren vars)
= Just (ren vars, code)
| otherwise
= Nothing
unop kont (e1_vars, e1_code)
| Just (k_vars, k_code) <- capture (kont exUnit),
vars <- k_vars `L.union` e1_vars,
code <- k_code $
exLet' (paVar r1 -:: e1_vars) e1_code $
(kont (exBVar r1) +:: vars)
= (vars, code)
unop kont ([], e1_code)
= ([], kont e1_code +:: [])
unop kont (e1_vars, e1_code)
| vars <- e1_vars,
code <- exLet' (paPair (paVar r1) (paVar r2)) e1_code $
exPair (kont (exBVar r1)) (exBVar r2)
= (vars, code)
binder kont (e1_vars, e1_code)
| Just (k_vars, k_code) <- capture (kont exUnit),
vars <- k_vars `L.union` e1_vars,
code <- k_code $
kont $
exLet' (paVar r1 -:: e1_vars) e1_code $
(exBVar r1 +:: vars)
= (vars, code)
| vars <- e1_vars,
code <- kont e1_code
= (vars, code)
binop kont e1 e2 =
case (loop e1, loop e2) of
(([], e1_code), ([], e2_code))
-> ([], kont e1_code e2_code +:: [])
(([], e1_code), (e2_vars, e2_code))
| syntacticValue e1_code,
vars <- e2_vars,
code <- exLet' (paVar r2 -:: e2_vars) e2_code $
kont e1_code (exBVar r2) +:: vars
-> (vars, code)
((e1_vars, e1_code), ([], e2_code))
| syntacticValue e2_code,
vars <- e1_vars,
code <- exLet' (paVar r1 -:: e1_vars) e1_code $
kont (exBVar r1) e2_code +:: vars
-> (vars, code)
((e1_vars, e1_code), (e2_vars, e2_code))
| vars <- e1_vars `L.union` e2_vars,
code <- exLet' (paVar r1 -:: e1_vars) e1_code $
exLet' (paVar r2 -:: e2_vars) e2_code $
kont (exBVar r1) (exBVar r2) +:: vars
-> (vars, code)
shadow vs e = transform (env `S.difference` vs) e
loop e = let (vars, e') = loop' e in (vars, e' <<@ e)
loop' e = case view e of
ExId (J [] (Var x))
| x `S.member` env,
vars <- [ren x]
-> (vars, ren (exBVar x) +:+ [exUnit])
ExCase e0 bs
| Just p0 <- expr2patt env S.empty e0,
not (dv p0 `disjoint` env),
e0_vars <- toList (dv (ren p0)),
e0_code <- ren e0,
bs' <-
[ case parseBangPatt pj of
Nothing ->
(renOnly (dv p0) pj,
shadow (dv pj `S.difference` dv p0) ej)
Just pj' ->
(ren pj',
transform (env `S.union` dv pj) ej)
| N _ (CaClause pj ej) <- bs ],
vars <- [ v | v <- foldl L.union e0_vars (map (fst . snd) bs'),
v `S.member` ren env ],
code <- exCase e0_code $
[ caClause pj (kill (dv (ren p0) `S.difference` dv pj) $
exLet' (paVar r1 -:: ej_vars) ej_code $
(exBVar r1 +:: vars))
| (pj, (ej_vars, ej_code)) <- bs' ]
-> (vars, code)
| (e0_vars, e0_code) <- loop e0,
bs' <-
[ case parseBangPatt pj of
Nothing -> (pj, shadow (dv pj) ej)
Just pj' -> exAddSigma
(length bs == 1)
(\vars patt expr -> (patt, (vars, expr)))
env pj' ej
| N _ (CaClause pj ej) <- bs ],
vars <- foldl L.union e0_vars (map (fst . snd) bs'),
code <- exLet' (paVar r1 -:: e0_vars) e0_code $
exCase (exBVar r1) $
[ caClause pj
(exLet' (paVar r2 -:: ej_vars) ej_code $
exBVar r2 +:: vars)
| (pj, (ej_vars, ej_code)) <- bs' ]
-> (vars, code)
ExLetRec bs e1
-> binder (exLetRec bs)
(shadow (S.fromList (map (bnvar . dataOf) bs)) e1)
ExLetDecl ds e1
-> binder (exLetDecl ds) (loop e1)
ExPair e1 e2
-> binop exPair e1 e2
ExApp e1 e2
| Just p2 <- expr2patt env S.empty e2,
not (dv p2 `disjoint` env),
(e1_vars, e1_code) <- loop e1,
vars <- e1_vars `L.union` toList (dv (ren p2)),
(v1, f1) <- if null e1_vars
then (e1_code, id)
else (exBVar r1,
exLet' (paVar r1 -:: e1_vars) e1_code),
code <- f1 $
exLet' (paPair (paVar r2) (flatpatt (ren p2)))
(exApp v1 (ren e2)) $
exBVar r2 +:: vars
-> (vars, code)
| otherwise
-> binop exApp e1 e2
ExTApp e1 t2
-> unop (flip exTApp t2) (loop e1)
ExPack mt t1 e2
-> unop (exPack mt t1) (loop e2)
ExCast e1 t2 b
-> unop (flip (flip exCast t2) b) (loop e1)
_ | Just (k_vars, k_code) <- capture e
-> (k_vars, k_code $ e +:: k_vars)
| vars <- []
-> (vars, e +:: vars)
(+:+) :: Id i => Expr i -> [Expr i] -> Expr i
(+:+) = foldl exPair
(+::) :: Id i => Expr i -> [Lid i] -> Expr i
e +:: vs = e +:+ map exBVar vs
(-:-) :: Id i => Patt i -> [Patt i] -> Patt i
(-:-) = foldl paPair
(-::) :: Id i => Patt i -> [Lid i] -> Patt i
p -:: vs = p -:- map paVar vs
r1, r2 :: Id i => Lid i
r1 = lid "r1.!"
r2 = lid "r2.!"
{-
expr2vs :: Expr i -> Maybe [Lid i]
expr2vs e = case view e of
ExId (J [] (Var l)) -> return [l]
ExPair e1 e2
| ExId (J [] (Var l)) <- view e2 -> do
vs <- expr2vs e1
return (vs ++ [l])
_ -> mzero
-}
makeBangPatt :: Id i => Patt i -> Patt i
makeBangPatt p = paCon (J [] (uid "!")) (Just p)
parseBangPatt :: Id i => Patt i -> Maybe (Patt i)
parseBangPatt (dataOf -> PaCon (J [] (Uid i "!")) mp)
| isTrivial i = mp
parseBangPatt _ = Nothing
{-
fbvSet :: Expr i -> S.Set (Lid i)
fbvSet e = S.fromList [ lid | J [] lid <- M.keys (fv e) ]
-}
disjoint :: Ord a => S.Set a -> S.Set a -> Bool
disjoint s1 s2 = S.null (s1 `S.intersection` s2)
-- | Transform an expression into a pattern, if possible, using only
-- the specified variables and type variables
expr2patt :: Id i =>
S.Set (Lid i) -> S.Set (TyVar i) -> Expr i -> Maybe (Patt i)
expr2patt vs0 tvs0 e0 = CMS.evalStateT (loop e0) (vs0, tvs0) where
loop e = case view e of
ExId ident -> case view ident of
Left (J [] l) -> do
sawVar l
return (paVar l)
Left (J _ _) -> mzero
Right qu -> return (paCon qu Nothing)
-- no string or integer literals
ExPair e1 e2 -> do
p1 <- loop e1
p2 <- loop e2
return (paPair p1 p2)
ExApp e1 e2 |
ExId ident <- view (snd (unfoldExTApp e1)),
Right qu <- view ident
-> do
p2 <- loop e2
return (paCon qu (Just p2))
ExTApp e1 _ -> loop e1
ExPack Nothing (dataOf -> TyVar tv) e2 -> do
sawTyVar tv
p2 <- loop e2
return (paPack tv p2)
_ -> mzero
sawVar v = do
(vs, tvs) <- CMS.get
if v `S.member` vs
then CMS.put (v `S.delete` vs, tvs)
else mzero
sawTyVar tv = do
(vs, tvs) <- CMS.get
if tv `S.member` tvs
then CMS.put (vs, tv `S.delete` tvs)
else mzero
-- | Transform a pattern to an expression.
patt2expr :: Id i => Patt i -> Expr i
patt2expr p = case dataOf p of
PaWild -> exUnit
PaVar l -> exBVar l
PaCon u Nothing
-> exCon u
PaCon u (Just p2)
-> exApp e1 e2 where
e1 = patt2expr (paCon u Nothing)
e2 = patt2expr p2
PaPair p1 p2 -> exPair e1 e2 where
e1 = patt2expr p1
e2 = patt2expr p2
PaLit lt -> exLit lt
PaAs _ l -> exBVar l
PaPack a p2 -> exPack Nothing (tyVar a) (patt2expr p2)
PaAnti a -> antierror "exSigma" a
-- | Transform a pattern to a flattened pattern.
flatpatt :: Id i => Patt i -> Patt i
flatpatt p0 = case loop p0 of
[] -> paUnit
p:ps -> foldl paPair p ps
where
loop p = case dataOf p of
PaWild -> []
PaVar l -> [paVar l]
PaCon _ Nothing
-> []
PaCon _ (Just p2)
-> loop p2
PaPair p1 p2 -> loop p1 ++ loop p2
PaLit _ -> []
PaAs _ l -> [paVar l]
PaPack a p2 -> [paPack a (flatpatt p2)]
PaAnti a -> antierror "exSigma" a
ren :: Data a => a -> a
ren = everywhere (mkT eachRaw `extT` eachRen) where
eachRaw :: Lid Raw -> Lid Raw
eachRen :: Lid Renamed -> Lid Renamed
eachRaw = each; eachRen = each
each (Lid _ s) = lid (s ++ "!")
each (LidAnti a) = LidAnti a
renOnly :: (Data a, Id i) => S.Set (Lid i) -> a -> a
renOnly set = everywhere (mkT each) where
each l | l `S.member` set = lid (unLid l ++ "!")
| otherwise = l
{-
remove :: Data a => S.Set Lid -> a -> a
remove set = everywhere (mkT expr `extT` patt) where
patt (PaVar v)
| v `S.member` set = paUnit
patt p = p
expr :: Ident -> Ident
expr (J [] (Var v))
| v `S.member` set = J [] (Con (Uid "()"))
expr e = e
-}
kill :: (Id i, Foldable f) => f (Lid i) -> Expr i -> Expr i
kill = translate paVar (const exUnit)
translate :: (Id i, Foldable f) =>
(Lid i -> Patt i) -> (Lid i -> Expr i) ->
f (Lid i) -> Expr i -> Expr i
translate mkpatt mkexpr set =
case toList set of
[] -> id
v:vs -> exLet' (mkpatt v -:- map mkpatt vs)
(mkexpr v +:+ map mkexpr vs)
exUnit :: Id i => Expr i
exUnit = exCon (quid "()")
paUnit :: Id i => Patt i
paUnit = paCon (quid "()") Nothing