Agda-2.3.2.2: src/prototyping/eval/DeBruijnCBN7.hs
{-# OPTIONS -fglasgow-exts -fallow-undecidable-instances #-}
-- Int names (IntMap is only marginally faster than Map, so let's go with Map
-- for the time being)
module DeBruijnCBN7 where
import Data.List
import Data.Maybe
import Data.Monoid
import Data.Map (Map, (!))
import Data.Set (Set)
import Data.Trie (Trie)
import qualified Data.Map as Map
import qualified Data.Trie as Trie
import qualified Data.Set as Set
import qualified Syntax as S
import Syntax ( AppView(..), Pat(..), appView )
import Utils
import Pretty
data Exp = Var Int [Exp]
| Con Name [Exp]
| Def Name [Exp]
| Lam Exp [Exp]
data Case
= Done Exp
| Skip Case
| Bind Case
| Split (NameMap Case)
type Name = Int
type NameMap = Map Name
apps :: Exp -> [Exp] -> Exp
apps e [] = e
apps (Var n es) es' = Var n $ es ++ es'
apps (Con c es) es' = Con c $ es ++ es'
apps (Def c es) es' = Def c $ es ++ es'
apps (Lam e es) es' = Lam e $ es ++ es'
type Sig = NameMap Case
class Names a where
getNames :: a -> Set S.Name
instance Names S.Exp where
getNames e = case e of
S.Var _ -> Set.empty
S.Def c -> Set.singleton c
S.Con c -> Set.singleton c
S.Lam e -> getNames e
S.App u v -> Set.union (getNames u) (getNames v)
instance Names S.Pat where
getNames p = case p of
VarP -> Set.empty
WildP -> Set.empty
ConP c ps -> Set.insert c $ getNames ps
instance Names S.Clause where
getNames (S.Clause ps v) = getNames (ps, v)
instance Names a => Names (Map k a) where
getNames = getNames . Map.elems
instance Names a => Names [a] where
getNames = Set.unions . map getNames
instance (Names a, Names b) => Names (a, b) where
getNames (x, y) = Set.union (getNames x) (getNames y)
class Compile a c | a -> c where
compile :: a -> c
instance Compile S.Sig (Sig, NameMap S.Name, Map S.Name Name) where
compile sig = (Map.fromList $ map comp $ Map.toList sig, nameMap, idMap)
where
ns = Set.toList $ Set.union (getNames sig) (Set.fromList $ Map.keys sig)
nameMap = Map.fromList $ zip [0..] ns
idMap = Map.fromList $ zip ns [0..]
comp (c, cl) = (idMap ! c, compile cl idMap)
instance Compile S.Exp (Map S.Name Name -> Exp) where
compile e nmap = case appView e of
Apps (S.Var n) es -> Var n $ comps es
Apps (S.Con c) es -> Con (nmap ! c) $ comps es
Apps (S.Def c) es -> Def (nmap ! c) $ comps es
Apps (S.Lam v) es -> Lam (compile v nmap) $ comps es
where
comps es = map (flip compile nmap) es
instance Compile [S.Clause] (Map S.Name Name -> Case) where
compile cs nmap = case nextPatterns cs of
Right [v] -> Done v
Right [] -> error $ "no rhs: " ++ show cs
Right (_:_) -> error $ "overlapping patterns: " ++ show cs
Left pcs -> case conOrVar pcs of
Left cs -> Bind $ compile cs nmap
Right ccs -> Split $ Map.map (flip compile nmap) $ Map.fromList ccs
where
patterns (S.Clause ps _) = ps
body (S.Clause _ v) = compile v nmap
next (S.Clause (p:ps) v) = (p, S.Clause ps v)
nextPatterns :: [S.Clause] -> Either [(Pat, S.Clause)] [Exp]
nextPatterns cs
| all null pss = Right $ map body cs
| otherwise = Left $ map next cs
where
pss = map patterns cs
conOrVar :: [(Pat, S.Clause)] -> Either [S.Clause] [(Name, [S.Clause])]
conOrVar cs
| all (isVar . fst) cs = Left $ map snd cs
| all (isCon . fst) cs = Right $
map splitCon
$ groupBy ((==) `on` conName `on` fst)
$ sortBy (compare `on` conName `on` fst)
$ cs
| otherwise = error $ "bad clauses: " ++ show cs
where
splitCon :: [(Pat, S.Clause)] -> (Name, [S.Clause])
splitCon cs = ( nmap ! conName (fst $ head cs)
, map amendClause cs
)
where
amendClause (ConP _ ps, S.Clause qs v) = S.Clause (ps ++ qs) v
isVar VarP = True
isVar WildP = True
isVar _ = False
isCon (ConP _ _) = True
isCon _ = False
conName (ConP c _ ) = c
conArgs (ConP c vs) = vs
decompile :: NameMap S.Name -> Exp -> S.Exp
decompile nmap e = case e of
Var n es -> S.Var n `S.apps` map dec es
Con c es -> S.Con (nmap ! c) `S.apps` map dec es
Def c es -> S.Def (nmap ! c) `S.apps` map dec es
Lam e es -> S.Lam (dec e) `S.apps` map dec es
where
dec = decompile nmap
(!) = (Map.!)
-- Evaluation
raiseFrom :: Int -> Int -> Exp -> Exp
raiseFrom n k e = case e of
Var m es
| m < n -> Var m $ map (raiseFrom n k) es
| otherwise -> Var (m + k) $ map (raiseFrom n k) es
Con c es -> Con c $ map (raiseFrom n k) es
Def c es -> Def c $ map (raiseFrom n k) es
Lam e es -> Lam (raiseFrom (n + 1) k e) $ map (raiseFrom n k) es
raise :: Int -> Exp -> Exp
raise = raiseFrom 0
subst :: [Exp] -> Exp -> Exp
subst us v = case v of
Var m es -> ({-# SCC "subst!!" #-} us !! m) `apps` map (subst us) es
Con c es -> Con c $ map (subst us) es
Def c es -> Def c $ map (subst us) es
Lam t es -> Lam (subst (Var 0 [] : map (raise 1) us) t) $ map (subst us) es
data Reduction a b = NotReduced a | Reduced b
matchDef :: Sig -> Case -> [Exp] -> Reduction [Exp] Exp
matchDef sig c vs = case match c [] [] vs of
Reduced v -> Reduced v
NotReduced vs -> NotReduced $ reverse vs
where
match (Done v) old sub vs = Reduced $ subst sub v `apps` vs
match _ old sub [] = NotReduced old
match (Skip c) old sub (v:vs) = match c (v : old) sub vs
match (Bind c) old sub (v:vs) = match c (v : old) (v : sub) vs
match (Split m) old sub (v:vs) = case whnf sig v of
Con c ws -> case Map.lookup c m of
Just c' -> case match c' old sub (ws ++ vs) of
Reduced v -> Reduced v
NotReduced us -> NotReduced $ Con c (reverse us0) : us1
where (us0, us1) = splitAt (length ws) us
Nothing -> NotReduced $ Con c ws : old
v -> NotReduced $ v : old
iota :: Sig -> Name -> [Exp] -> Exp
iota sig c vs = fromMaybe (Con c vs) $ do
cs <- {-# SCC "iotaLookup" #-} Map.lookup c sig
case matchDef sig cs vs of
NotReduced vs -> return $ Con c vs
Reduced v -> return $ whnf sig v
top :: Exp -> [Exp]
top v = v : map (flip Var []) [0..]
whnf :: Sig -> Exp -> Exp
whnf sig v = case v of
Var n vs -> Var n vs
Con c vs -> Con c vs
Def c vs -> iota sig c vs
Lam u (v : vs) -> whnf sig (subst (top v) u `apps` vs)
Lam u [] -> Lam u []
eval' :: Sig -> Exp -> Exp
eval' sig v = case whnf sig v of
Lam u [] -> Lam (eval' sig u) []
Var n vs -> Var n $ map (eval' sig) vs
Con c vs -> Con c $ map (eval' sig) vs
Def c vs -> Def c $ map (eval' sig) vs
eval :: S.Sig -> S.Exp -> S.Exp
eval sig e = decompile nmap $ eval' sig' (compile e imap)
where
(sig', nmap, imap) = compile sig