packages feed

free-foil-0.0.2: src/Control/Monad/Foil/TH/MkFromFoil.hs

{-# OPTIONS_GHC -fno-warn-type-defaults #-}
{-# LANGUAGE GADTs                 #-}
{-# LANGUAGE LambdaCase            #-}
{-# LANGUAGE RankNTypes            #-}
{-# LANGUAGE QuasiQuotes           #-}
{-# LANGUAGE TemplateHaskellQuotes #-}
module Control.Monad.Foil.TH.MkFromFoil (mkFromFoil) where

import           Language.Haskell.TH
import Language.Haskell.TH.Syntax (addModFinalizer)

import qualified Control.Monad.Foil  as Foil
import Control.Monad.Foil.TH.Util

-- | Generate conversion functions from raw to scope-safe representation.
mkFromFoil
  :: Name -- ^ Type name for raw terms.
  -> Name -- ^ Type name for raw variable identifiers.
  -> Name -- ^ Type name for raw scoped terms.
  -> Name -- ^ Type name for raw patterns.
  -> Q [Dec]
mkFromFoil termT nameT scopeT patternT = do
  n <- newName "n"
  let ntype = return (VarT n)
  l <- newName "l"
  let ltype = return (VarT l)
  r <- newName "r"
  let rtype = return (VarT r)
  TyConI (DataD _ctx _name patternTVars _kind patternCons _deriv) <- reify patternT
  TyConI (DataD _ctx _name scopeTVars _kind scopeCons _deriv) <- reify scopeT
  TyConI (DataD _ctx _name termTVars _kind termCons _deriv) <- reify termT

  let termParams = map (VarT . tvarName) termTVars
  let scopeParams = map (VarT . tvarName) scopeTVars
  let patternParams = map (VarT . tvarName) patternTVars

  fromFoilTermSignature <-
    SigD fromFoilTermT <$>
      [t| [$(return (ConT nameT))]
          -> Foil.NameMap $ntype $(return (ConT nameT))
          -> $(return (PeelConT foilTermT termParams)) $ntype
          -> $(return (PeelConT termT termParams))
        |]
  fromFoilScopedSignature <-
    SigD fromFoilScopedT <$>
      [t| [$(return (ConT nameT))]
          -> Foil.NameMap $ntype $(return (ConT nameT))
          -> $(return (PeelConT foilScopeT scopeParams)) $ntype
          -> $(return (PeelConT scopeT scopeParams))
        |]
  fromFoilPatternSignature <-
    SigD fromFoilPatternT <$>
      [t| [$(return (ConT nameT))]
          -> Foil.NameMap $ntype $(return (ConT nameT))
          -> $(return (PeelConT foilPatternT patternParams)) $ntype $ltype
          -> ([$(return (ConT nameT))] -> Foil.NameMap $ltype $(return (ConT nameT)) -> $(return (PeelConT patternT patternParams)) -> $rtype)
          -> $rtype
        |]

  addModFinalizer $ putDoc (DeclDoc fromFoilTermT)
    "Convert a scope-safe term into a raw term."
  addModFinalizer $ putDoc (DeclDoc fromFoilPatternT)
    "Convert a scope-safe pattern into a raw pattern."
  addModFinalizer $ putDoc (DeclDoc fromFoilScopedT)
    "Convert a scope-safe scoped term into a raw scoped term."

  return
    [ fromFoilTermSignature
    , fromFoilTermBody termCons
    , fromFoilPatternSignature
    , fromFoilPatternBody patternCons
    , fromFoilScopedSignature
    , fromFoilScopedBody scopeCons
    ]
  where
    foilTermT = mkName ("Foil" ++ nameBase termT)
    foilScopeT = mkName ("Foil" ++ nameBase scopeT)
    foilPatternT = mkName ("Foil" ++ nameBase patternT)

    fromFoilTermT = mkName ("fromFoil" ++ nameBase termT)
    fromFoilPatternT = mkName ("fromFoil" ++ nameBase patternT)
    fromFoilScopedT = mkName ("fromFoil" ++ nameBase scopeT)

    fromFoilTermBody termCons = FunD fromFoilTermT
      [Clause [VarP freshVars, VarP env, VarP term] (NormalB (CaseE (VarE term) (map toMatch termCons))) []]
      where
        freshVars = mkName "freshVars"
        env = mkName "env"
        term = mkName "term"

        toMatch (NormalC conName params) =
          Match (ConP foilConName [] conParamPatterns) (NormalB conMatchBody) []
          where
            conMatchBody = go 1 (VarE freshVars) (VarE env) (ConE conName) params

            go _i _freshVars' _env' p [] = p
            go i freshVars' env' p ((_bang, PeelConT tyName _tyParams) : conParams)
              | tyName == nameT =
                  go (i+1) freshVars' env' (AppE p (AppE (AppE (VarE 'Foil.lookupName) (VarE xi)) env')) conParams
              | tyName == termT =
                  go (i+1) freshVars' env' (AppE p (AppE (AppE (AppE (VarE fromFoilTermT) (VarE freshVars)) (VarE env)) (VarE xi))) conParams
              | tyName == scopeT =
                  go (i+1) freshVars' env' (AppE p (AppE (AppE (AppE (VarE fromFoilScopedT) freshVars') env') (VarE xi))) conParams
              | tyName == patternT =
                  AppE
                    (AppE (AppE (AppE (VarE fromFoilPatternT) freshVars') env') (VarE xi))
                    (LamE [VarP freshVarsi, VarP envi, VarP xi']
                      (go (i+1) (VarE freshVarsi) (VarE envi) (AppE p (VarE xi')) conParams))
              where
                xi = mkName ("x" <> show i)
                xi' = mkName ("x" <> show i <> "'")
                freshVarsi = mkName ("freshVars" <> show i)
                envi = mkName ("env" <> show i)
            go i freshVars' env' p (_ : conParams) =
              go (i + 1) freshVars' env' (AppE p (VarE xi)) conParams
              where
                xi = mkName ("x" <> show i)

            foilConName = mkName ("Foil" ++ nameBase conName)
            conParamPatterns = map VarP conParamVars

            conParamVars = zipWith mkConParamVar params [1..]

            mkConParamVar :: BangType -> Int -> Name
            mkConParamVar _ty i = mkName ("x" <> show i)
        toMatch RecC{} = error "Record constructors (RecC) are not supported yet!"
        toMatch InfixC{} = error "Infix constructors (InfixC) are not supported yet!"
        toMatch ForallC{} = error "Existential constructors (ForallC) are not supported yet!"
        toMatch GadtC{} = error "GADT constructors (GadtC) are not supported yet!"
        toMatch RecGadtC{} = error "Record GADT constructors (RecGadtC) are not supported yet!"

    fromFoilPatternBody patternCons = FunD fromFoilPatternT
      [Clause [VarP freshVars, VarP env, VarP pattern, VarP cont] (NormalB (CaseE (VarE pattern) (map toMatch patternCons))) []]
      where
        freshVars = mkName "freshVars"
        env = mkName "env"
        pattern = mkName "pattern"
        cont = mkName "cont"

        toMatch (NormalC conName params) =
          Match (ConP foilConName [] conParamPatterns) (NormalB conMatchBody) []
          where
            conMatchBody = go 1 (VarE freshVars) (VarE env) (ConE conName) params

            go _i freshVars' env' p [] = AppE (AppE (AppE (VarE cont) freshVars') env') p
            go i freshVars' env' p ((_bang, PeelConT tyName _tyParams) : conParams)
              | tyName == nameT =
                  CaseE freshVars'
                    [ Match (ListP []) (NormalB (AppE (VarE 'error) (LitE (StringL "not enough fresh variables")))) []
                    , Match (InfixP (VarP var) '(:) (VarP vars))
                        (NormalB (LetE
                          [ValD (VarP envi) (NormalB (AppE (AppE (AppE (VarE 'Foil.addNameBinder) (VarE xi)) (VarE var)) env')) []]
                          (go (i + 1) (VarE vars) (VarE envi) (AppE p (VarE var)) conParams)))
                        []
                    ]
              | tyName == patternT =
                  AppE
                    (AppE (AppE (AppE (VarE fromFoilPatternT) freshVars') env') (VarE xi))
                    (LamE [VarP freshVarsi, VarP envi, VarP xi']
                      (go (i+1) (VarE freshVarsi) (VarE envi) (AppE p (VarE xi')) conParams))
              where
                var = mkName "var"
                vars = mkName "vars"
                xi = mkName ("x" <> show i)
                xi' = mkName ("x" <> show i <> "'")
                freshVarsi = mkName ("freshVars" <> show i)
                envi = mkName ("env" <> show i)
            go i freshVars' env' p (_ : conParams) =
              go (i + 1) freshVars' env' (AppE p (VarE xi)) conParams
              where
                xi = mkName ("x" <> show i)

            foilConName = mkName ("Foil" ++ nameBase conName)
            conParamPatterns = map VarP conParamVars

            conParamVars = zipWith mkConParamVar params [1..]

            mkConParamVar :: BangType -> Int -> Name
            mkConParamVar _ i = mkName ("x" <> show i)
        toMatch RecC{} = error "Record constructors (RecC) are not supported yet!"
        toMatch InfixC{} = error "Infix constructors (InfixC) are not supported yet!"
        toMatch ForallC{} = error "Existential constructors (ForallC) are not supported yet!"
        toMatch GadtC{} = error "GADT constructors (GadtC) are not supported yet!"
        toMatch RecGadtC{} = error "Record GADT constructors (RecGadtC) are not supported yet!"

    fromFoilScopedBody freshVarsCons = FunD fromFoilScopedT
      [Clause [VarP freshVars, VarP env, VarP term] (NormalB (CaseE (VarE term) (map toMatch freshVarsCons))) []]
      where
        freshVars = mkName "freshVars"
        env = mkName "env"
        term = mkName "term"

        toMatch (NormalC conName params) =
          Match (ConP foilConName [] conParamPatterns) (NormalB conMatchBody) []
          where
            conMatchBody = go 1 (VarE freshVars) (VarE env) (ConE conName) params

            go _i _freshVars' _env' p [] = p
            go i freshVars' env' p ((_bang, PeelConT tyName _tyParams) : conParams)
              | tyName == nameT =
                  go (i+1) freshVars' env' (AppE p (AppE (AppE (VarE 'Foil.lookupName) (VarE xi)) env')) conParams
              | tyName == termT =
                  go (i+1) freshVars' env' (AppE p (AppE (AppE (AppE (VarE fromFoilTermT) (VarE freshVars)) (VarE env)) (VarE xi))) conParams
              | tyName == scopeT =
                  go (i+1) freshVars' env' (AppE p (AppE (AppE (AppE (VarE fromFoilScopedT) freshVars') env') (VarE xi))) conParams
              | tyName == patternT =
                  AppE
                    (AppE (AppE (AppE (VarE fromFoilPatternT) freshVars') env') (VarE xi))
                    (LamE [VarP freshVarsi, VarP envi, VarP xi']
                      (go (i+1) (VarE freshVarsi) (VarE envi) (AppE p (VarE xi')) conParams))
              where
                xi = mkName ("x" <> show i)
                xi' = mkName ("x" <> show i <> "'")
                freshVarsi = mkName ("freshVars" <> show i)
                envi = mkName ("env" <> show i)
            go i freshVars' env' p (_ : conParams) =
              go (i + 1) freshVars' env' (AppE p (VarE xi)) conParams
              where
                xi = mkName ("x" <> show i)

            foilConName = mkName ("Foil" ++ nameBase conName)
            conParamPatterns = map VarP conParamVars

            conParamVars = zipWith mkConParamVar params [1..]

            mkConParamVar :: BangType -> Int -> Name
            mkConParamVar _ty i = mkName ("x" <> show i)
        toMatch RecC{} = error "Record constructors (RecC) are not supported yet!"
        toMatch InfixC{} = error "Infix constructors (InfixC) are not supported yet!"
        toMatch ForallC{} = error "Existential constructors (ForallC) are not supported yet!"
        toMatch GadtC{} = error "GADT constructors (GadtC) are not supported yet!"
        toMatch RecGadtC{} = error "Record GADT constructors (RecGadtC) are not supported yet!"