packages feed

PTQ-0.0.4: src/Expr.hs

-----------------------------------------------------------------------------
-- |
-- Module      :  Expr
-- Copyright   :  (c) Masahiro Sakai 2007-2009
-- License     :  BSD3-style (see LICENSE)
-- 
-- Maintainer:    masahiro.sakai@gmail.com
-- Stability   :  experimental
-- Portability :  non-portable

{-# LANGUAGE CPP, GeneralizedNewtypeDeriving #-}
module Expr
    ( Op1 (..)
    , Op2 (..)
    , Binder (..)
    , Expr (..)
    , Scope (..)
    , Name
    , lambda
    , forall
    , exists
    , int
    , ext
    , (<@>)
    , abstract
    , instantiate
    , normalize
    , RenderM
    , runRenderM
    , renderExpr
    , renderExprU
    ) where

import Control.Monad.RWS
import Data.List

-----------------------------------------------------------------------------

data Op1 = Not | Box | F | H | Int | Ext deriving (Eq,Show)
data Op2 = And | Or | Imply | Equiv | Id deriving (Eq,Show)
data Binder = Lambda | Forall | Exists deriving (Eq,Show)

infixl 9 :@

data Expr
    = FVar Name          -- 自由変数
    | BVar !Int          -- 束縛変数
    | Expr :@ Expr       -- 関数適用
    | Const String       -- 定数 (≒自由変数)
    | Op1 !Op1 Expr      -- 前置演算子
    | Op2 !Op2 Expr Expr -- 中置演算子
    | Bind !Binder Scope -- 変数束縛

instance Show Expr where
#ifdef USE_UTF8
    showsPrec d x = runRenderM (renderExprU d x)
#else
    showsPrec d x = runRenderM (renderExpr d x)
#endif

newtype Scope = Sc Expr deriving Show
type Name = String

-----------------------------------------------------------------------------

lambda :: Name -> Expr -> Expr
lambda name expr = Bind Lambda (abstract name expr)

forall :: Name -> Expr -> Expr
forall name expr = Bind Forall (abstract name expr)

exists :: Name -> Expr -> Expr
exists name expr = Bind Exists (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 (Sc body)) = Bind q (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 (Sc body)) =
  case normalize body of
    f :@ BVar 0 | not (0 `elem` bvs f) -> varShift (-1) f -- η-conversion
    body -> Bind Lambda (Sc body)
normalize (Bind q (Sc body)) = Bind q (Sc (normalize body))
normalize (fun :@ arg) =
  case normalize fun of
    Bind Lambda 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 b (Sc e)) = [n - 1 | n <- bvs e, n /= 0]

-----------------------------------------------------------------------------

newtype RenderM a = RenderM (RWS [Name] () Int a)
  deriving (Monad, MonadReader [Name])

runRenderM :: RenderM a -> a
runRenderM (RenderM m) = 
  case runRWS m [] 0 of
    (a, _, _) -> a

render :: Bool -> Int -> Expr -> RenderM ShowS
render u = h
  where
    h d e = case e of
      FVar name  -> return $ showString name
      BVar index -> do
        vs <- ask
        return $ showString (vs !! index)
      Bind q _ -> f d q e
      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 s
      Op1 op a -> do
        t <- h (prec+1) a
        return $ showParen (d > prec)
               $ showString s . t
        where
          s = case op of
                Not -> if u then "¬" else "not " -- ¬ (U+00AC)
                Box -> if u then "◻" else "[]"  -- ◻ (U+25FB) が正しそうだが □ (U+25A1) を使うのが無難か?
                F   -> "F "
                H   -> "H "
                Int -> if u then "˄" else "Int " -- ˄ (U+02C4)
                Ext -> if u then "˅" else "Ext " -- ˅ (U+02C5)
          prec = case op of
                   Int | u -> app_prec + 1
                   Ext | u -> 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 u then "∧" else "&&",  4, id, id)     -- ∧ (U+2227)
              Or    -> (if u then "∨" else "||",  3, id, id)     -- ∨ (U+2228)
              Imply -> (if u then "→" else "->",  1, (+1), id)   -- → (U+2192)
              Equiv -> (if u then "↔" else "<->", 1, (+1), (+1)) -- ↔ (U+2194)
              Id    -> ("=",   2, (+1), (+1))

    f :: Int -> Binder -> Expr -> RenderM ShowS
    f d b e = do
      (xs, s) <- go e
      return $ showParen (d > 0) $
        showString (g b) . showString (intercalate " " xs) . showString ". " . s
      where
        go :: Expr -> RenderM ([Name], ShowS)
        go (Bind b' (Sc body)) | b==b' = do
          x <- gensym 
          (xs, s) <- local (x:) $ go body
          return (x:xs, s)
        go e = do
          s <- h 0 e
          return ([], s)

    g :: Binder -> String
    g Lambda = if u then "λ" else "\\" -- λ (U+03BB)
    g Forall = if u then "∀" else "forall " -- ∀ (U+2200)
    g Exists = if u then "∃" else "exists " -- ∃ (U+2203)


renderExpr :: Int -> Expr -> RenderM ShowS
renderExpr = render False

renderExprU :: Int -> Expr -> RenderM ShowS
renderExprU = render True

app_prec :: Int
app_prec = 10

gensym :: RenderM Name
gensym = RenderM $ do 
    i <- get
    put (i+1)
    return ("x"++show i)