----------------------------------------------------------------------------- -- | -- Module : IL -- Copyright : (c) Masahiro Sakai 2007-2009 -- License : BSD3-style (see LICENSE) -- -- Maintainer: masahiro.sakai@gmail.com -- Stability : experimental -- Portability : non-portable {-# LANGUAGE CPP #-} module IL ( Type (..) , renderType , Op1 (..) , Op2 (..) , Binder (..) , Expr (..) , Scope (..) , Name , lambda , forall , exists , int , ext , (<@>) , abstract , instantiate , normalize , renderExpr , typeCheck ) where import Control.Monad.RWS import Data.List import Data.Monoid import Data.Function -- -------------------------------------------------------------------------- infixr 0 :-> data Type = Prop | E | S Type | (:->) Type Type deriving (Eq, Ord) instance Show Type where #ifdef USE_UTF8 showsPrec = renderType True #else showsPrec = renderType False #endif renderType :: Bool -> Int -> Type -> ShowS renderType unicode = f where f _ Prop = showString "t" f _ E = showString "e" f d (S t) = showParen (d > 0) $ showString "s" . arr . f 0 t f d (t1 :-> t2) = showParen (d > 0) $ f 1 t1 . arr . f 0 t2 arr = if unicode then showString "→" else showString "->" -- -------------------------------------------------------------------------- infixl 9 :@ data Expr = FVar Name -- 自由変数 | BVar !Int -- 束縛変数 | Expr :@ Expr -- 関数適用 | Const Name -- 定数 (≒自由変数) | Op1 !Op1 Expr -- 前置演算子 | Op2 !Op2 Expr Expr -- 中置演算子 | Bind !Binder Type Scope -- 変数束縛 data Op1 = Not | Box | F | H | Int | Ext deriving (Eq,Ord,Show) data Op2 = And | Or | Imply | Equiv | Id deriving (Eq,Ord,Show) data Binder = Lambda | Forall | Exists deriving (Eq,Ord,Show) newtype Scope = Sc Expr deriving Show type Name = (String, Type) instance Show Expr where #ifdef USE_UTF8 showsPrec = renderExpr True False #else showsPrec = renderExpr False False #endif lambda :: Name -> Expr -> Expr lambda name expr = Bind Lambda (snd name) (abstract name expr) forall :: Name -> Expr -> Expr forall name expr = Bind Forall (snd name) (abstract name expr) exists :: Name -> Expr -> Expr exists name expr = Bind Exists (snd name) (abstract name expr) int :: Expr -> Expr int = Op1 Int ext :: Expr -> Expr ext = Op1 Ext -- 「a{b}」を「a <@> b」と表記 infixl 9 <@> (<@>) :: Expr -> Expr -> Expr fun <@> arg = ext fun :@ arg varChange :: (Int -> Name -> Expr) -> (Int -> Int -> Expr) -> Expr -> Expr varChange f g = h 0 where h :: Int -> Expr -> Expr h outer (FVar name) = f outer name h outer (BVar index) = g outer index h outer (Bind q t (Sc body)) = Bind q t (Sc (h (outer+1) body)) h outer (fun :@ arg) = h outer fun :@ h outer arg h _ (Const s) = Const s h outer (Op1 op a) = Op1 op (h outer a) h outer (Op2 op a b) = Op2 op (h outer a) (h outer b) abstract :: Name -> Expr -> Scope abstract name expr = Sc (varChange f g expr) where f outer name' | name==name' = BVar outer | otherwise = FVar name' g outer index | index>=outer = BVar (index+1) | otherwise = BVar index instantiate :: Expr -> Scope -> Expr instantiate image (Sc body) = varChange f g body where f _ name = FVar name g outer index | index==outer = varShift outer image | index>outer = BVar (index-1) | otherwise = BVar index -- 外を指している変数のインデックスをずらす varShift :: Int -> Expr -> Expr varShift 0 = id varShift n = varChange f g where f _ name = FVar name g outer index | index>=outer = BVar (index+n) | otherwise = BVar index normalize :: Expr -> Expr normalize (Bind Lambda t (Sc body)) = case normalize body of f :@ BVar 0 | not (0 `elem` bvs f) -> varShift (-1) f -- η-conversion body' -> Bind Lambda t (Sc body') normalize (Bind q t (Sc body)) = Bind q t (Sc (normalize body)) normalize (fun :@ arg) = case normalize fun of Bind Lambda t body -> normalize (instantiate arg' body) -- β-reduction fun' -> fun' :@ arg' where arg' = normalize arg normalize (Op1 Ext a) = case normalize a of Op1 Int b -> b a' -> Op1 Ext a' normalize (Op1 op a) = Op1 op (normalize a) normalize (Op2 op a b) = Op2 op (normalize a) (normalize b) normalize x = x bvs :: Expr -> [Int] bvs (FVar _) = [] bvs (BVar n) = [n] bvs (f :@ x) = bvs f ++ bvs x bvs (Const _) = [] bvs (Op1 _ e) = bvs e bvs (Op2 _ e1 e2) = bvs e1 ++ bvs e2 bvs (Bind _ _ (Sc e)) = [n - 1 | n <- bvs e, n /= 0] -- -------------------------------------------------------------------------- type RenderM = RWS [Name] () Int renderExpr :: Bool -> Bool -> Int -> Expr -> ShowS renderExpr unicode uncurrying d e = case runRWS (h d e) [] 0 of (a, _, _) -> a where h d e = case e of FVar name -> return $ showString (fst name) BVar index -> do vs <- ask return $ showString $ fst (vs !! index) Bind q _ _ -> f d q e a :@ b | uncurrying -> f a [b] where f (e1 :@ e2) xs = f e1 (e2:xs) f e xs = uncurriedApp e xs Op1 Ext a :@ b -> do a' <- h (app_prec+1) a b' <- h 0 b return $ showParen (d > app_prec) $ a' . showString " {" . b' . showChar '}' a :@ b -> do a' <- h app_prec a b' <- h (app_prec+1) b return $ showParen (d > app_prec) $ a' . showChar ' ' . b' Const s -> return $ showString (fst s) Op1 op a -> do t <- h (prec+1) a return $ showParen (d > prec) $ showString s . t where s = case op of Not -> if unicode then "¬" else "not " -- ¬ (U+00AC) Box -> if unicode then "◻" else "[]" -- ◻ (U+25FB) が正しそうだが □ (U+25A1) を使うのが無難か? F -> "F " H -> "H " Int -> if unicode then "˄" else "Int " -- ˄ (U+02C4) Ext -> if unicode then "˅" else "Ext " -- ˅ (U+02C5) prec = case op of Int | unicode -> app_prec + 1 Ext | unicode -> app_prec + 1 _ -> app_prec Op2 op a b -> do a' <- h (l prec) a b' <- h (r prec) b return $ showParen (d > prec) $ a' . showChar ' ' . showString s . showChar ' ' . b' where (s,prec,l,r) = case op of And -> (if unicode then "∧" else "&&", 4, id, id) -- ∧ (U+2227) Or -> (if unicode then "∨" else "||", 3, id, id) -- ∨ (U+2228) Imply -> (if unicode then "→" else "->", 1, (+1), id) -- → (U+2192) Equiv -> (if unicode then "↔" else "<->", 1, (+1), (+1)) -- ↔ (U+2194) Id -> ("=", 5, (+1), (+1)) f :: Int -> Binder -> Expr -> RenderM ShowS f d b e = do (xs, s) <- go e let b' = case b of Lambda -> if unicode then "λ" else "\\" -- λ (U+03BB) Forall -> if unicode then "∀" else "forall " -- ∀ (U+2200) Exists -> if unicode then "∃" else "exists " -- ∃ (U+2203) ys :: [(Type, [String])] ys = [(snd (head ys), map fst ys) | ys <- groupBy ((==) `on` snd) xs] ws :: [String] ws = [intercalate ", " zs ++ " : "++ renderType unicode 0 t "" | (t, zs) <- ys] return $ showParen (d > 0) $ showString b' . showString (intercalate ", " ws) . showString ". " . s where go :: Expr -> RenderM ([Name], ShowS) go (Bind b' t (Sc body)) | b==b' = do x <- gensym t (xs, s) <- local (x:) $ go body return (x:xs, s) go e = do s <- h 0 e return ([], s) uncurriedApp :: Expr -> [Expr] -> RenderM ShowS uncurriedApp e xs = do bs <- mapM (liftM Endo . h 0) $ reverse xs let cs = appEndo $ mconcat $ intersperse (Endo (showString ", ")) bs case e of Op1 Ext e2 -> do a <- h (app_prec+1) e2 return $ showParen (d > app_prec) $ a . showString "{" . cs . showChar '}' _ -> do a <- h (app_prec+1) e return $ showParen (d > app_prec) $ a . showString "(" . cs . showChar ')' app_prec :: Int app_prec = 10 gensym :: Type -> RenderM Name gensym t = do i <- get put (i+1) return ("x"++show i, t) -- --------------------------------------------------------------------------- typeCheck ::[Type] -> Expr -> Maybe Type typeCheck = f where f _ (FVar (_,t)) = return t f env (BVar n) = return (env !! n) f env (e1 :@ e2) = do (t1 :-> t2) <- f env e1 t3 <- f env e2 guard $ t1 == t3 return t2 f env (Const (_,t)) = return t f env (Op1 Ext e) = do S t <- f env e return t f env (Op1 Int e) = do t <- f env e return (S t) f env (Op1 op e) = do Prop <- f env e return Prop f env (Op2 Id e1 e2) = do t1 <- f env e1 t2 <- f env e2 guard $ t1 == t2 return Prop f env (Op2 op e1 e2) = do Prop <- f env e1 Prop <- f env e2 return Prop f env (Bind Lambda t (Sc e)) = do t2 <- f (t:env) e return (t :-> t2) f env (Bind b t (Sc e)) = do Prop <- f (t:env) e return Prop