pinchot-0.24.0.0: lib/Pinchot/Names.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-- | Template Haskell names and values.
module Pinchot.Names where
import Control.Monad.Trans.Class (lift)
import qualified Control.Monad.Trans.State as St
import Data.Map (Map)
import qualified Data.Map as Map
import qualified Language.Haskell.TH as T
-- | @t@
nameT :: T.Name
nameT = T.mkName "t"
-- | @a@
nameA :: T.Name
nameA = T.mkName "a"
-- | @r@
nameR :: T.Name
nameR = T.mkName "r"
-- | @t@ as a type
typeT :: T.TypeQ
typeT = T.varT nameT
-- | @a@ as a type
typeA :: T.TypeQ
typeA = T.varT nameA
-- | @r@ as a type
typeR :: T.TypeQ
typeR = T.varT nameR
-- | @t@ as a TyVarBndr
tyVarBndrT :: T.TyVarBndr
tyVarBndrT = T.PlainTV nameT
-- | @a@ as a TyVarBndr
tyVarBndrA :: T.TyVarBndr
tyVarBndrA = T.PlainTV nameA
-- | @r@ as a TyVarBndr
tyVarBndrR :: T.TyVarBndr
tyVarBndrR = T.PlainTV nameR
productionsStr :: String
productionsStr = "Productions"
-- | @Productions@
productions :: T.Name
productions = T.mkName productionsStr
-- | @a'@ followed by the given string.
recordName :: String -> T.Name
recordName n = T.mkName $ "a'" ++ n
-- | Qualified record name.
qualRecordName :: Qualifier -> String -> String
qualRecordName q s = quald q ("a'" ++ s)
-- | Environment for the creation of new names. Each name is
-- associated with an arbitrary String. Useful for assigning a new
-- unique name to match a particular Pinchot identifier. Use
-- 'getName' to get the name associated with a particular identifier,
-- creating it if necessary.
newtype Namer a = Namer (St.StateT (Map String T.Name) T.Q a)
deriving (Functor, Applicative, Monad)
liftQ :: T.Q a -> Namer a
liftQ = Namer . lift
namerNewName :: Namer T.Name
namerNewName = Namer $ lift (T.newName "_namerNewName")
runNamer :: Namer a -> T.Q a
runNamer (Namer n) = fmap fst $ (St.runStateT n) Map.empty
-- | Get th Name that corresponds to a particular string. If
-- necessary, creates the name.
getName :: String -> Namer T.Name
getName str = Namer $ do
names <- St.get
case Map.lookup str names of
Just n -> return n
Nothing -> do
new <- lift $ T.newName ("_getName_" ++ str)
let newMap = Map.insert str new names
St.put newMap
return new
lookupValueName :: String -> T.Q T.Name
lookupValueName str = do
mayName <- T.lookupValueName str
case mayName of
Nothing -> fail $ "name not found: " ++ str
Just r -> return r
lookupTypeName :: String -> T.Q T.Name
lookupTypeName str = do
mayName <- T.lookupTypeName str
case mayName of
Nothing -> fail $ "name not found: " ++ str
Just r -> return r
-- | Many functions take an argument that holds the name qualifier
-- for the module that contains the data types created by applying a
-- function such as 'Pinchot.SyntaxTree.syntaxTrees' or
-- 'Pinchot.Earley.earleyProduct'.
--
-- You will have to make sure that these data types are in scope.
-- The spliced Template Haskell code has to know where to
-- look for these data types. If you did an unqualified @import@ or
-- if the types are in the same module as the function that takes a
-- 'Qualifier' argument, just pass the empty string here. If you did a
-- qualified import, use the appropriate qualifier here.
--
-- For example, if you used @import qualified MyAst@, pass
-- @\"MyAst\"@ here. If you used @import qualified
-- Data.MyLibrary.MyAst as MyLibrary.MyAst@, pass
-- @\"MyLibrary.MyAst\"@ here.
type Qualifier = String
-- | Prepends a qualifier to a string, and returns the resulting
-- Name.
quald
:: Qualifier
-> String
-- ^ Item to be named - constructor, value, etc.
-> String
quald qual suf
| null qual = suf
| otherwise = (qual ++ '.':suf)