{-# OPTIONS -fglasgow-exts -fallow-undecidable-instances #-} -- case compilation module DeBruijnCBN6 where import Data.List import Data.Maybe import Data.Monoid import Data.Map (Map) import qualified Data.Map as Map import qualified Syntax as S import Syntax ( Pat(..), Name, AppView(..), 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 (Map Name Case) 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 = Map Name Case class Compile a c where compile :: a -> c instance Compile a b => Compile (Map k a) (Map k b) where compile = fmap compile instance Compile a b => Compile [a] [b] where compile = fmap compile instance Compile S.Exp Exp where compile e = case appView e of Apps (S.Var n) es -> Var n $ compile es Apps (S.Con c) es -> Con c $ compile es Apps (S.Def c) es -> Def c $ compile es Apps (S.Lam v) es -> Lam (compile v) $ compile es instance Compile [S.Clause] Case where compile cs = 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 Right ccs -> Split $ Map.map compile $ Map.fromList ccs where patterns (S.Clause ps _) = ps body (S.Clause _ v) = compile v 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 = ( 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 :: Exp -> S.Exp decompile e = case e of Var n es -> S.Var n `S.apps` map decompile es Con c es -> S.Con c `S.apps` map decompile es Def c es -> S.Def c `S.apps` map decompile es Lam e es -> S.Lam (decompile e) `S.apps` map decompile es -- 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 -> (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 -> String -> [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 $ eval' (compile sig) (compile e)