module Object.Templates(
makeName,
makeObject,
makeObjectFlexible
) where
import Object.Letters
import Object.Types
import Prelude hiding ((.))
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import Data.Char
import Data.Maybe
makeName :: String -> Q [Dec]
makeName name = makeName' name *> fst
makeName' :: String -> Q ([Dec],(Name,Name))
makeName' name = go where
go
| [] <- name = fail "can't make empty variable"
| not $ isLower $ head name = fail $ name ++ ": does not start with a lower letter"
| (first:rest) <- name = do
typeTuple <- mapM typeCon name
dataTuple <- mapM dataCon name
typeName <- newName $ [toUpper first] ++ rest ++ ['_']
dataName <- newName $ [first] ++ rest
typ <- [t| Method $(return $ foldl AppT (TupleT (length typeTuple)) typeTuple) |]
dat <- [| Method $(return $ (TupE dataTuple)) :: $(return $ ConT typeName) |]
let typeDecl = TySynD typeName [] typ
let dataDecl = ValD (VarP dataName) (NormalB dat) []
return ([typeDecl,dataDecl],(typeName,dataName))
typeCon c = do
Just res <- lookupTypeName $ "T_" ++ [c]
return $ ConT res
dataCon c = do
Just res <- lookupValueName $ "T_" ++ [c]
return $ ConE res
getInfo :: Info -> Q (Name, [Name], [VarStrictType])
getInfo (TyConI (DataD context typeName vars [RecC constrName fields] _)) = go where
go = return (typeName, map getVar vars, fields)
getVar (PlainTV n) = n
getVar (KindedTV n _) = n
getInfo _ = fail $ "type needs to have a single constructor record type"
getFieldName (fieldName,strictness,type')
| nameBase fieldName !! 0 /= '_' || not (isLower $ nameBase fieldName !! 1)
= fail $ show fieldName ++
": all fieldNames must commence with a '_' \
\and continue with a lower case letter"
| otherwise = nameBase fieldName
makeObject :: Name -> Q [Dec]
makeObject = makeObject' False
makeObjectFlexible = makeObject' True
makeObject' :: Bool -> Name -> Q [Dec]
makeObject' flexible name = go name where
go :: Name -> Q [Dec]
go obj = do
(name, vars, fields) <- reify name >>= getInfo
let objType = foldl AppT (ConT name) (VarT<*vars)
outputDecls <- if flexible
then return []
else [d|
type instance Output $(return objType) (Method m) =
MethodOutput $(return objType) (Method m)
type instance Output $(return objType) (Method m := input) =
MethodOutput $(return objType) (Method m := input)
|]
fieldDecls <- (sequence $ makeField name vars <* fields) *> concat
return $ outputDecls ++ fieldDecls
makeField :: Name -> [Name] -> VarStrictType -> Q [Dec]
makeField _ _ (name,_,_) | '_' /= head (nameBase name) = fail $ show name ++ " did not start with underscore"
makeField name vars (fName, _, fType) = do
(decs1,(typeName,dataName)) <- makeName' (tail $ nameBase fName)
methodOutput <- lookupTypeName "Object.Types.MethodOutput" *> fromMaybe (error "no MethodOutput in scope")
let objType = foldl AppT (ConT name) (VarT<*vars)
let methodOutInst = TySynInstD methodOutput $ TySynEqn [objType, ConT typeName] fType
actionInst <- [d|
instance Action $(return objType) $(return $ ConT typeName) where
object . _ = $(return $ VarE fName) object
|]
matchType <- [t| $(return $ ConT typeName) := $(return $ VarT $ mkName "value") |]
let methodSetOutInst = TySynInstD methodOutput $ TySynEqn [objType, matchType] objType
actionSetInst <- [d|
instance (value ~ $(return fType)) => Action $(return objType) $(return matchType) where
object . ( _ := v) = $(recUpdE [e|object|] [return (fName, VarE $ mkName "v")])
|]
return $ [methodOutInst,methodSetOutInst] ++ actionInst ++ actionSetInst ++ decs1