packages feed

exference-1.6.0.0: src/Language/Haskell/Exference/Core/Expression.hs

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE MonadComprehensions #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE DataKinds #-}

module Language.Haskell.Exference.Core.Expression
  ( Expression (..)
  , showExpression
  , fillExprHole
  , collectVarTypes
  )
where



import Language.Haskell.Exference.Core.Types
import Language.Haskell.Exference.Core.TypeUtils
import Data.List ( intercalate )
import Data.Function ( on )
import Data.Maybe ( fromMaybe )
import Control.Monad ( forM, forM_, liftM )
import Data.Functor.Identity ( runIdentity )
import Data.Functor ( (<$>) )

import Control.DeepSeq.Generics
import Control.DeepSeq
import GHC.Generics

import Control.Monad.Trans.MultiRWS
import Data.HList.ContainsType

-- import Debug.Hood.Observe
import Data.Map ( Map )
import qualified Data.Map as M



data Expression = ExpVar TVarId HsType -- a
                   -- (type is just for choosing better id when printing)
                | ExpName QualifiedName -- Prelude.zip
                | ExpLambda TVarId HsType Expression -- \x -> exp
                | ExpApply Expression Expression -- f x
                | ExpHole TVarId                 -- h
                | ExpLetMatch QualifiedName [(TVarId, HsType)] Expression Expression
                            -- let (Foo a b c) = bExp in inExp
                | ExpLet TVarId HsType Expression Expression
                            -- let x = bExp in inExp
                | ExpCaseMatch
                    Expression
                    [(QualifiedName, [(TVarId, HsType)], Expression)]
                     -- case mExp of Foo a b -> e1; Bar c d -> e2
  deriving (Eq, Generic)

instance NFData Expression where rnf = genericRnf

-- instance Show Expression where
--   showsPrec _ (ExpVar i) = showString $ showVar i
--   showsPrec d (ExpName s) = showsPrec d s
--   showsPrec d (ExpLambda i e) =
--     showParen (d>0) $ showString ("\\" ++ showVar i ++ " -> ") . showsPrec 1 e
--   showsPrec d (ExpApply e1 e2) =
--     showParen (d>1) $ showsPrec 2 e1 . showString " " . showsPrec 3 e2
--   showsPrec _ (ExpHole i) = showString $ "_" ++ showVar i
--   showsPrec d (ExpLetMatch n vars bindExp inExp) =
--       showParen (d>2)
--     $ showString ("let ("++show n++" "++intercalate " " (map showVar vars) ++ ") = ")
--     . shows bindExp . showString " in " . showsPrec 0 inExp
--   showsPrec d (ExpLet i bindExp inExp) =
--       showParen (d>2)
--     $ showString ("let " ++ showVar i ++ " = ")
--     . showsPrec 3 bindExp
--     . showString " in "
--     . showsPrec 0 inExp
--   showsPrec d (ExpCaseMatch bindExp alts) =
--       showParen (d>2)
--     $ showString ("case ")
--     . showsPrec 3 bindExp
--     . showString " of { "
--     . ( \s -> intercalate "; "
--            (map (\(cons, vars, expr) ->
--               show cons++" "++intercalate " " (map showVar vars)++" -> "
--               ++showsPrec 3 expr "")
--             alts)
--          ++ s
--       )
--     . showString " }"

refreshVarTypeBinding :: forall m
                       . MonadMultiState (Map TVarId HsType) m
                      => TVarId
                      -> HsType
                      -> m ()
refreshVarTypeBinding i ty = do
  m <- mGet
  case M.lookup i m of
    Nothing             -> mSet $ M.insert i ty m
    Just TypeVar{}      -> mSet $ M.insert i ty m
    Just TypeConstant{} -> mSet $ M.insert i ty m
    _                   -> return ()

collectVarTypes :: forall m
                 . MonadMultiState (Map TVarId HsType) m
                => Expression
                -> m ()
collectVarTypes (ExpVar i ty)       = refreshVarTypeBinding i ty
collectVarTypes ExpName{}           = return ()
collectVarTypes (ExpLambda i ty se) = do
  refreshVarTypeBinding i ty
  collectVarTypes se
collectVarTypes (ExpApply e1 e2)    = do
  collectVarTypes e1
  collectVarTypes e2
collectVarTypes ExpHole{}           = return ()
collectVarTypes (ExpLetMatch _ vars e1 e2) = do
  vars `forM_` uncurry refreshVarTypeBinding
  collectVarTypes e1
  collectVarTypes e2
collectVarTypes (ExpLet i ty e1 e2) = do
  refreshVarTypeBinding i ty
  collectVarTypes e1
  collectVarTypes e2
collectVarTypes (ExpCaseMatch se matches) = do
  collectVarTypes se
  matches `forM_` \(_, vars, me) -> do
    vars `forM_` uncurry refreshVarTypeBinding
    collectVarTypes me

showExpression :: Expression -> String
showExpression e = runIdentity
                 $ runMultiRWSTNil
                 $ withMultiStateA (M.empty :: Map TVarId HsType)
                 $ [ shs ""
                   | _ <- collectVarTypes e
                   , shs <- h 0 e
                   ]
 where
  h :: Int -> Expression -> MultiRWS r w (Map TVarId HsType ': s) ShowS
  h _ (ExpVar i _) = showString <$> showTypedVar i
  h _ (ExpName s)  = return $ shows s
  h d (ExpLambda i _ e1) =
    [ showParen (d>0) $ showString ("\\" ++ vname ++ " -> ") . eShows
    | eShows <- h 1 e1
    , vname <- showTypedVar i
    ]
  h d (ExpApply e1 e2) =
    [ showParen (d>1) $ e1Shows . showString " " . e2Shows
    | e1Shows <- h 2 e1
    , e2Shows <- h 3 e2
    ]    
  h _ (ExpHole i) = return $ showString $ "_" ++ showVar i
  h d (ExpLetMatch n vars bindExp inExp) =
    [ showParen (d>2)
    $ showString ("let ("
                  ++nStr
                  ++" "
                  ++intercalate " " varNames
                  ++ ") = ")
    . bindShows . showString " in " . inShows
    | bindShows <- h 0 bindExp
    , inShows   <- h 0 inExp
    , let nStr = show n
    , varNames  <- mapM (showTypedVar . fst) vars
    ]
      
  h d (ExpLet i _ bindExp inExp) =
    [ showParen (d>2)
    $ showString ("let " ++ varName ++ " = ")
    . bindShows
    . showString " in "
    . inShows
    | bindShows <- h 3 bindExp
    , inShows <- h 0 inExp
    , varName <- showTypedVar i
    ]
  h d (ExpCaseMatch bindExp alts) =
    [ showParen (d>2)
    $ showString "case "
    . bindShows
    . showString " of { "
    . ( \s -> intercalate "; "
           (map (\(cons, varNames, expr) ->
              cons ""++" "++intercalate " " varNames++" -> "
              ++expr "")
            altsShows)
         ++ s
      )
    . showString " }"
    | bindShows <- h 3 bindExp
    , altsShows <- alts `forM` \(cons, vars, expr) ->
        [ ( shows cons
          , varNames
          , exprShows
          )
        | exprShows <- h 3 expr
        , varNames <- mapM (showTypedVar . fst) vars
        ]
    ]

-- instance Observable Expression where
--   observer x = observeOpaque (show x) x

fillExprHole :: TVarId -> Expression -> Expression -> Expression
fillExprHole vid t orig@(ExpHole j) | vid==j = t
                                    | otherwise = orig
fillExprHole vid t (ExpLambda i ty e) = ExpLambda i ty $ fillExprHole vid t e
fillExprHole vid t (ExpApply e1 e2) = ExpApply (fillExprHole vid t e1)
                                               (fillExprHole vid t e2)
fillExprHole vid t (ExpLetMatch n vars bindExp inExp) =
  ExpLetMatch n vars (fillExprHole vid t bindExp) (fillExprHole vid t inExp)
fillExprHole vid t (ExpLet i ty bindExp inExp) =
  ExpLet i ty (fillExprHole vid t bindExp) (fillExprHole vid t inExp)
fillExprHole vid t (ExpCaseMatch bindExp alts) =
  ExpCaseMatch (fillExprHole vid t bindExp) [ (c, vars, fillExprHole vid t expr)
                                            | (c, vars, expr) <- alts
                                            ]
fillExprHole _ _ t@ExpName{} = t
fillExprHole _ _ t@ExpVar{}  = t