{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}

-- |
-- Module      : Jikka.Core.Convert.RemoveUnusedVars
-- Description : removes unused variables. / 使われていない変数を削除します。
-- Copyright   : (c) Kimiyuki Onaka, 2020
-- License     : Apache License 2.0
-- Maintainer  : kimiyuki95@gmail.com
-- Stability   : experimental
-- Portability : portable
--
-- `Jikka.Language.Core.RemoveUnusedVars` remove unused variables from exprs.
module Jikka.Core.Convert.RemoveUnusedVars
  ( run,
    run',
  )
where

import Jikka.Common.Error
import Jikka.Core.Language.Expr
import Jikka.Core.Language.FreeVars (isUnusedVar)
import Jikka.Core.Language.Lint
import Jikka.Core.Language.Util

runLet :: VarName -> Type -> Expr -> Expr -> Expr
runLet :: VarName -> Type -> Expr -> Expr -> Expr
runLet VarName
x Type
t Expr
e1 Expr
e2
  | VarName -> Expr -> Bool
isUnusedVar VarName
x Expr
e2 = Expr
e2
  | Bool
otherwise = VarName -> Type -> Expr -> Expr -> Expr
Let VarName
x Type
t Expr
e1 Expr
e2

runExpr :: Expr -> Expr
runExpr :: Expr -> Expr
runExpr = \case
  Var VarName
x -> VarName -> Expr
Var VarName
x
  Lit Literal
lit -> Literal -> Expr
Lit Literal
lit
  App Expr
f Expr
e -> Expr -> Expr -> Expr
App (Expr -> Expr
runExpr Expr
f) (Expr -> Expr
runExpr Expr
e)
  Lam VarName
x Type
t Expr
e -> VarName -> Type -> Expr -> Expr
Lam VarName
x Type
t (Expr -> Expr
runExpr Expr
e)
  Let VarName
x Type
t Expr
e1 Expr
e2 -> VarName -> Type -> Expr -> Expr -> Expr
runLet VarName
x Type
t (Expr -> Expr
runExpr Expr
e1) (Expr -> Expr
runExpr Expr
e2)

runToplevelExpr :: ToplevelExpr -> ToplevelExpr
runToplevelExpr :: ToplevelExpr -> ToplevelExpr
runToplevelExpr = \case
  ResultExpr Expr
e -> Expr -> ToplevelExpr
ResultExpr (Expr -> ToplevelExpr) -> Expr -> ToplevelExpr
forall a b. (a -> b) -> a -> b
$ Expr -> Expr
runExpr Expr
e
  ToplevelLet VarName
x Type
t Expr
e ToplevelExpr
cont -> VarName -> Type -> Expr -> ToplevelExpr -> ToplevelExpr
ToplevelLet VarName
x Type
t (Expr -> Expr
runExpr Expr
e) (ToplevelExpr -> ToplevelExpr
runToplevelExpr ToplevelExpr
cont)
  ToplevelLetRec VarName
f [(VarName, Type)]
args Type
ret Expr
body ToplevelExpr
cont ->
    let body' :: Expr
body' = Expr -> Expr
runExpr Expr
body
        cont' :: ToplevelExpr
cont' = ToplevelExpr -> ToplevelExpr
runToplevelExpr ToplevelExpr
cont
     in if VarName -> Expr -> Bool
isUnusedVar VarName
f Expr
body'
          then VarName -> Type -> Expr -> ToplevelExpr -> ToplevelExpr
ToplevelLet VarName
f ([Type] -> Type -> Type
curryFunTy (((VarName, Type) -> Type) -> [(VarName, Type)] -> [Type]
forall a b. (a -> b) -> [a] -> [b]
map (VarName, Type) -> Type
forall a b. (a, b) -> b
snd [(VarName, Type)]
args) Type
ret) ([(VarName, Type)] -> Expr -> Expr
curryLam [(VarName, Type)]
args Expr
body') ToplevelExpr
cont'
          else VarName
-> [(VarName, Type)]
-> Type
-> Expr
-> ToplevelExpr
-> ToplevelExpr
ToplevelLetRec VarName
f [(VarName, Type)]
args Type
ret Expr
body' ToplevelExpr
cont'

run' :: Program -> Program
run' :: ToplevelExpr -> ToplevelExpr
run' = ToplevelExpr -> ToplevelExpr
runToplevelExpr

-- | `run` removes unused variables in given programs.
--
-- This also removes variables for recursion, i.e. "rec" flags.
-- `ToplevelLetRec` may becomes `ToplevelLet`.
--
-- For example, this converts
--
-- > let rec solve x =
-- >     let y = 0
-- >     in x
-- > in solve
--
-- to
--
-- > let solve x =
-- >     x
-- > in solve
run :: MonadError Error m => Program -> m Program
run :: ToplevelExpr -> m ToplevelExpr
run ToplevelExpr
prog = String -> m ToplevelExpr -> m ToplevelExpr
forall (m :: * -> *) a. MonadError Error m => String -> m a -> m a
wrapError' String
"Jikka.Core.Convert.RemoveUnusedVars" (m ToplevelExpr -> m ToplevelExpr)
-> m ToplevelExpr -> m ToplevelExpr
forall a b. (a -> b) -> a -> b
$ do
  m () -> m ()
forall (m :: * -> *) a. MonadError Error m => m a -> m a
precondition (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ do
    ToplevelExpr -> m ()
forall (m :: * -> *). MonadError Error m => ToplevelExpr -> m ()
ensureWellTyped ToplevelExpr
prog
  ToplevelExpr
prog <- ToplevelExpr -> m ToplevelExpr
forall (m :: * -> *) a. Monad m => a -> m a
return (ToplevelExpr -> m ToplevelExpr) -> ToplevelExpr -> m ToplevelExpr
forall a b. (a -> b) -> a -> b
$ ToplevelExpr -> ToplevelExpr
run' ToplevelExpr
prog
  m () -> m ()
forall (m :: * -> *) a. MonadError Error m => m a -> m a
postcondition (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ do
    ToplevelExpr -> m ()
forall (m :: * -> *). MonadError Error m => ToplevelExpr -> m ()
ensureWellTyped ToplevelExpr
prog
  ToplevelExpr -> m ToplevelExpr
forall (m :: * -> *) a. Monad m => a -> m a
return ToplevelExpr
prog