packages feed

baskell-0.1: src/Rename.hs

{-------------------------------------------------------------------------------

        Copyright:              Bernie Pope 2007

        Module:                 Rename

        Description:            Renames all lambda-bound identifiers apart in
                                expressions and programs. For example:

                                rename (\x -> plus 1 x) might produce
                                (\x_12 -> plus 1 x_12), assuming plus is not
                                lambda bound.

                                This is needed to avoid inadvertant name
                                capture during program evaluation. For example:

                                (\y -> \id -> (y, id)) id True

                                should reduce to:

                                (id, True)

                                and not:

                                (True, True)

        Primary Authors:        Bernie Pope

        Notes:

        We don't rename "top-level" identifiers (those defined in a declaration)
        because they are assumed to be unique. Baskell does not have local
        declarations so this does not cause any problems. It is a syntax error
        to have two top-level declarations with the same name, however Baskell
        might not check for this.

-------------------------------------------------------------------------------}

{-
    This file is part of baskell.

    baskell is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    baskell is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with baskell; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-}

module Rename
   ( renameExp
   , renameProgram
   )
   where

import AST
   ( Ident
   , Exp (..)
   , Decl (..)
   , Program (..)
   )

import qualified Data.Map as Map
   ( Map
   , empty
   , fromList
   , union
   , insert
   , lookup
   )

import Control.Monad
   ( liftM
   , liftM2
   )

import Control.Monad.State
   ( runStateT
   , evalStateT
   , get
   , put
   , StateT
   , gets
   , modify
   , execStateT
   )

import Control.Monad.Trans
   ( lift
   , liftIO
   )

import Control.Monad.Identity
   ( Identity
   , runIdentity
   )

import Control.Monad.Reader
   ( ReaderT
   , local
   , ask
   , runReaderT
   )

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

type NameEnv = Map.Map Ident Ident
type RnState = Int
type Rn a = ReaderT NameEnv (StateT RnState Identity) a

renameProgram :: Program -> RnState -> (Program, RnState)
renameProgram prog state =
   runIdentity $ runStateT (runReaderT (renameProgramM prog) Map.empty) state

renameExp :: Exp -> RnState -> Exp
renameExp exp state =
   runIdentity $ evalStateT (runReaderT (renameExpM exp) Map.empty) state

extendEnv :: Ident -> Ident -> Rn a -> Rn a
extendEnv identOld identNew action =
   local (Map.insert identOld identNew) action

lookupIdent :: Ident -> Rn String
lookupIdent oldName = do
   env <- ask
   case Map.lookup oldName env of
      Nothing      -> return oldName
      Just newName -> return newName

-- renamed identifiers are appended with an underscore and a number
-- this is safe because underscores are not allowed by the parser
-- so there is no chance we will introduce an inadvertant name
-- clash during renaming
renameIdent :: Ident -> Rn Ident
renameIdent oldName = do
   count <- lift get
   lift $ put (count + 1)
   return $ oldName ++ "_" ++ show count

renameDecl :: Decl -> Rn Decl
renameDecl decl@(Sig {}) = return decl
renameDecl (Decl name body) =
   liftM (Decl name) $ renameExpM body

renameProgramM :: Program -> Rn Program
renameProgramM(Program decls) =
   liftM Program $ mapM renameDecl decls

renameExpM :: Exp -> Rn Exp
renameExpM (Var ident) =
   liftM Var $ lookupIdent ident

renameExpM (Lam ident body) = do
   newIdent <- renameIdent ident
   liftM (Lam newIdent) $ extendEnv ident newIdent $ renameExpM body

renameExpM (LamStrict ident body) = do
   newIdent <- renameIdent ident
   liftM (LamStrict newIdent) $ extendEnv ident newIdent $ renameExpM body

renameExpM (App e1 e2) =
   liftM2 App (renameExpM e1) (renameExpM e2)

renameExpM exp@(Literal lit) = return exp

renameExpM (Tuple exps) =
   liftM Tuple $ mapM renameExpM exps

renameExpM exp@(Prim _name _impl) =
   return exp