mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-18 20:31:40 +03:00
203 lines
5.7 KiB
Haskell
203 lines
5.7 KiB
Haskell
|
{-
|
||
|
Generate FromNoun and ToNoun instances
|
||
|
-}
|
||
|
|
||
|
{-# LANGUAGE TypeOperators #-}
|
||
|
{-# LANGUAGE DefaultSignatures #-}
|
||
|
|
||
|
module Data.Noun.Poet.TH where
|
||
|
|
||
|
import ClassyPrelude hiding (fromList)
|
||
|
import Control.Lens
|
||
|
import Data.Noun.Poet hiding (hsToHoon)
|
||
|
|
||
|
import Data.Noun
|
||
|
import Data.Noun.Atom
|
||
|
import Data.Noun.Pill
|
||
|
import Data.Void
|
||
|
import Data.Word
|
||
|
import GHC.Natural
|
||
|
import GHC.Generics hiding (from)
|
||
|
|
||
|
import Language.Haskell.TH
|
||
|
import Language.Haskell.TH.Syntax
|
||
|
|
||
|
import qualified Prelude as P
|
||
|
import qualified GHC.Generics as GHC
|
||
|
|
||
|
import Data.Typeable (Typeable)
|
||
|
import RIO (decodeUtf8Lenient)
|
||
|
|
||
|
import qualified Data.Char as C
|
||
|
import qualified Control.Monad.Fail as Fail
|
||
|
|
||
|
|
||
|
data Foo = Foo Int | Bar Int
|
||
|
|
||
|
|
||
|
-- Types For Hoon Constructs ---------------------------------------------------
|
||
|
|
||
|
-- data Deriving = Deriving { tyCon :: Name, tyVar :: Name }
|
||
|
|
||
|
data Shape
|
||
|
= Tup Name Con
|
||
|
| Enu Name [Name]
|
||
|
| Sum Name [Con]
|
||
|
deriving (Eq, Ord, Show)
|
||
|
|
||
|
nameStr :: Name -> String
|
||
|
nameStr (Name (OccName n) _) = n
|
||
|
|
||
|
typeShape :: Name -> Q Shape
|
||
|
typeShape tyName = do
|
||
|
(tyConName, tyVars, cs) <- reify tyName >>= \case
|
||
|
TyConI (DataD _ nm tyVars _ cs _) -> pure (nm, tyVars, cs)
|
||
|
TyConI (NewtypeD _ nm tyVars _ c _) -> pure (nm, tyVars, [c])
|
||
|
TyConI _ -> fail badSynonym
|
||
|
_ -> fail "not type"
|
||
|
|
||
|
allEmpty <- all id <$> (traverse emptyCon cs)
|
||
|
|
||
|
if allEmpty
|
||
|
then do
|
||
|
conNames :: [Name] <- traverse conName' cs
|
||
|
pure (Enu tyConName conNames)
|
||
|
else
|
||
|
case cs of
|
||
|
[] -> pure $ Enu tyConName []
|
||
|
[c] -> pure $ Tup tyConName c
|
||
|
cs -> pure $ Sum tyConName cs
|
||
|
|
||
|
where
|
||
|
badSynonym = "deriveFunctor: tyCon may not be a type synonym."
|
||
|
|
||
|
conName' :: Con -> Q Name
|
||
|
conName' = \case
|
||
|
NormalC nm bangType -> pure nm
|
||
|
RecC nm varBangTypes -> pure nm
|
||
|
InfixC bangType1 nm bangType2 -> fail "Infix constructors are not supported"
|
||
|
ForallC tyVarBndrs ctx con -> fail "Polymorphic types are not supported"
|
||
|
GadtC nm bangTypes ty -> fail "GADTs are not supported"
|
||
|
RecGadtC nm varBangTypes ty -> fail "GADTs are not supported"
|
||
|
|
||
|
emptyCon :: Con -> Q Bool
|
||
|
emptyCon = \case
|
||
|
NormalC nm bangType -> pure (null bangType)
|
||
|
RecC nm varBangTypes -> pure (null varBangTypes)
|
||
|
InfixC bangType1 nm bangType2 -> fail "Infix constructors are not supported"
|
||
|
ForallC tyVarBndrs ctx con -> fail "Polymorphic types are not supported"
|
||
|
GadtC nm bangTypes ty -> fail "GADTs are not supported"
|
||
|
RecGadtC nm varBangTypes ty -> fail "GADTs are not supported"
|
||
|
|
||
|
|
||
|
deriveNoun :: Name -> Q [Dec]
|
||
|
deriveNoun tyName = do
|
||
|
(<>) <$> deriveToNoun tyName <*> deriveFromNoun tyName
|
||
|
|
||
|
deriveToNoun :: Name -> Q [Dec]
|
||
|
deriveToNoun tyName = do
|
||
|
let t = conT tyName
|
||
|
|
||
|
shape <- typeShape tyName
|
||
|
|
||
|
traceM (show shape)
|
||
|
|
||
|
body <- case shape of
|
||
|
Tup nm con -> pure [| \_ -> Atom 0 |]
|
||
|
Enu nm cons -> enumToAtom cons
|
||
|
Sum nm cons -> pure [| \_ -> Atom 0 |]
|
||
|
|
||
|
[d|
|
||
|
instance ToNoun $t where
|
||
|
toNoun = $body
|
||
|
|]
|
||
|
|
||
|
enumToAtom :: [Name] -> Q ExpQ
|
||
|
enumToAtom cons = do
|
||
|
matches <- traverse mkMatch cons
|
||
|
pure (pure (LamCaseE matches))
|
||
|
where
|
||
|
mkMatch :: Name -> Q Match
|
||
|
mkMatch nm = pure $ Match (ConP nm []) (NormalB body) []
|
||
|
where
|
||
|
body = AppE (VarE 'toNoun) $ AppE (ConE 'Cord) strLit
|
||
|
strLit = LitE $ StringL $ unpack $ hsToHoon $ nameStr nm
|
||
|
|
||
|
deriveFromNoun :: Name -> Q [Dec]
|
||
|
deriveFromNoun tyName =
|
||
|
[d|
|
||
|
instance FromNoun $t where
|
||
|
parseNoun = $body
|
||
|
|]
|
||
|
where
|
||
|
t = conT tyName
|
||
|
|
||
|
body = [| \_ -> fail "unimplemented" |]
|
||
|
|
||
|
|
||
|
{-
|
||
|
(tyConName, tyVars, cs) <- reify tyName >>= \case
|
||
|
TyConI (DataD _ nm tyVars _ cs _) -> pure (nm, tyVars, cs)
|
||
|
TyConI (NewtypeD _ nm tyVars _ c _) -> pure (nm, tyVars, [c])
|
||
|
TyConI _ -> fail badSynonym
|
||
|
_ -> fail "not type"
|
||
|
|
||
|
let KindedTV tyVar StarT = P.last tyVars
|
||
|
|
||
|
let instanceType = (conT ''ToNoun)
|
||
|
`appT`
|
||
|
varT (foldl' apply (conT tyConName) (P.init tyVars))
|
||
|
|
||
|
putQ $ Deriving tyConName tyVar
|
||
|
sequence [instanceD (pure []) instanceType [genToNoun cs]]
|
||
|
-}
|
||
|
|
||
|
{-
|
||
|
apply t (PlainTV name) = appT t (varT name)
|
||
|
apply t (KindedTV name _) = appT t (varT name)
|
||
|
|
||
|
badSynonym = "deriveFunctor: tyCon may not be a type synonym."
|
||
|
|
||
|
genToNoun :: [Con] -> DecQ
|
||
|
genToNoun cons = funD 'toNoun (genToNounClause <$> cons)
|
||
|
|
||
|
genToNounClause :: Con -> Q Clause
|
||
|
genToNounClause c@(NormalC name fieldTypes) = do
|
||
|
f <- newName "f"
|
||
|
fieldNames <- replicateM (length fieldTypes) (newName "x")
|
||
|
|
||
|
let pats = varP f:[conP name (map varP fieldNames)]
|
||
|
body = normalB $ appsE $
|
||
|
conE name : map (newField f) (zip fieldNames fieldTypes)
|
||
|
|
||
|
clause pats body []
|
||
|
genToNounClause _ = fail "wut"
|
||
|
-}
|
||
|
|
||
|
{-
|
||
|
newField :: Name -> (Name, StrictType) -> Q Exp
|
||
|
newField f (x, (_, fieldType)) = do
|
||
|
Just (Deriving typeCon typeVar) <- getQ
|
||
|
case fieldType of
|
||
|
VarT typeVar' | typeVar' == typeVar ->
|
||
|
[| $(varE f) $(varE x) |]
|
||
|
ty `AppT` VarT typeVar' |
|
||
|
leftmost ty == (ConT typeCon) && typeVar' == typeVar ->
|
||
|
[| fmap $(varE f) $(varE x) |]
|
||
|
_ -> [| $(varE x) |]
|
||
|
|
||
|
leftmost :: Type -> Type
|
||
|
leftmost (AppT ty1 _) = leftmost ty1
|
||
|
leftmost ty = ty
|
||
|
-}
|
||
|
|
||
|
hsToHoon :: String -> Text
|
||
|
hsToHoon = go []
|
||
|
where
|
||
|
go acc [] = pack $ intercalate "-" $ reverse acc
|
||
|
go acc (c:cs) = go (elem:acc) remain
|
||
|
where
|
||
|
head = C.toLower c
|
||
|
(tail, remain) = break C.isUpper cs
|
||
|
elem = head:tail
|