Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/Agda2Hs/Compile.hs
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,16 @@ compile genv tlm _ def =
(CompileToPragma s , Function{}) -> [] <$ checkCompileToFunctionPragma def s

(ClassPragma ms , Record{} ) -> pure <$> compileRecord (ToClass ms) def
(NewTypePragma ds , Record{} ) -> pure <$> compileRecord (ToRecord True ds) def
(NewTypePragma ds , Datatype{}) -> compileData True ds def
(DefaultPragma ds , Datatype{}) -> compileData False ds def
(NewTypePragma ds , Record{} ) -> pure <$> compileRecord (ToRecord ToNewType ds) def
(NewTypePragma ds , Datatype{}) -> compileData ToNewType ds def
(GadtPragma ds , Datatype{}) -> compileData ToGadt ds def
(DefaultPragma ds , Datatype{}) -> compileData ToData ds def
(DerivePragma s , _ ) | isInstance -> pure <$> compileInstance (ToDerivation s) def
(DefaultPragma _ , Axiom{} ) | isInstance -> pure <$> compileInstance (ToDerivation Nothing) def
(DefaultPragma _ , _ ) | isInstance -> pure <$> compileInstance ToDefinition def
(DefaultPragma _ , Axiom{} ) -> compilePostulate def
(DefaultPragma _ , Function{}) -> compileFun True def
(DefaultPragma ds , Record{} ) -> pure <$> compileRecord (ToRecord False ds) def
(DefaultPragma ds , Record{} ) -> pure <$> compileRecord (ToRecord ToData ds) def

_ -> agda2hsErrorM $ text "Don't know how to compile" <+> prettyTCM (defName def)

Expand Down
35 changes: 24 additions & 11 deletions src/Agda2Hs/Compile/Data.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ import Agda.TypeChecking.Telescope

import Agda.Utils.Maybe
import Agda.Utils.Monad
import Agda.Utils.Singleton
import Agda.Utils.Impossible ( __IMPOSSIBLE__ )

import Agda2Hs.AgdaUtils

import Agda2Hs.Compile.Name
import Agda2Hs.Compile.Type ( compileDomType, compileTeleBinds, compileDom, DomOutput(..) )
import Agda2Hs.Compile.Type ( compileType, compileDomType, compileTeleBinds, compileDom, DomOutput(..) )
import Agda2Hs.Compile.Types
import Agda2Hs.Compile.Utils

Expand All @@ -34,8 +35,8 @@ checkNewtype name cs = do
(Hs.QualConDecl () _ _ (Hs.ConDecl () cName types):_) -> checkNewtypeCon cName types
_ -> __IMPOSSIBLE__

compileData :: AsNewType -> [Hs.Deriving ()] -> Definition -> C [Hs.Decl ()]
compileData newtyp ds def = do
compileData :: DataTarget -> [Hs.Deriving ()] -> Definition -> C [Hs.Decl ()]
compileData target ds def = do
let d = hsName $ prettyShow $ qnameName $ defName def
checkValidTypeName d
let Datatype{dataPars = n, dataIxs = numIxs, dataCons = cs} = theDef def
Expand All @@ -48,12 +49,22 @@ compileData newtyp ds def = do
-- TODO: filter out erased constructors
cs <- mapM (compileConstructor params) cs
let hd = foldl (Hs.DHApp ()) (Hs.DHead () d) binds
let htarget = toDataTarget target

let target = if newtyp then Hs.NewType () else Hs.DataType ()
when (target == ToNewType) (checkNewtype d $ map fst cs)

when newtyp (checkNewtype d cs)
return . singleton $ case target of
ToGadt -> Hs.GDataDecl () htarget Nothing hd Nothing (map (uncurry conToGadtCon) cs) ds
_ -> Hs.DataDecl () htarget Nothing hd (map fst cs) ds

return [Hs.DataDecl () target Nothing hd cs ds]
where
conToGadtCon :: Hs.QualConDecl () -> Hs.Type () -> Hs.GadtDecl ()
conToGadtCon (Hs.QualConDecl _ tys ctx con) rt = case con of
Hs.ConDecl () c ts ->
Hs.GadtDecl () c tys ctx Nothing $
foldr (Hs.TyFun ()) rt ts
Hs.InfixConDecl{} -> __IMPOSSIBLE__
Hs.RecDecl{} -> __IMPOSSIBLE__

allIndicesErased :: Type -> C ()
allIndicesErased t = reduce (unEl t) >>= \case
Expand All @@ -64,19 +75,20 @@ allIndicesErased t = reduce (unEl t) >>= \case
DomForall{} -> agda2hsError "Not supported: indexed datatypes"
_ -> return ()

compileConstructor :: [Arg Term] -> QName -> C (Hs.QualConDecl ())
compileConstructor :: [Arg Term] -> QName -> C (Hs.QualConDecl (), Hs.Type ())
compileConstructor params c = do
reportSDoc "agda2hs.data.con" 15 $ text "compileConstructor" <+> prettyTCM c
reportSDoc "agda2hs.data.con" 20 $ text " params = " <+> prettyTCM params
ty <- defType <$> getConstInfo c
reportSDoc "agda2hs.data.con" 30 $ text " ty (before piApply) = " <+> prettyTCM ty
ty <- ty `piApplyM` params
reportSDoc "agda2hs.data.con" 20 $ text " ty = " <+> prettyTCM ty
TelV tel _ <- telView ty
TelV tel ret <- telView ty
let conName = hsName $ prettyShow $ qnameName c
checkValidConName conName
args <- compileConstructorArgs tel
return $ Hs.QualConDecl () Nothing Nothing $ Hs.ConDecl () conName args
hret <- addContext tel $ compileType $ unEl ret
return (Hs.QualConDecl () Nothing Nothing $ Hs.ConDecl () conName args, hret)

compileConstructorArgs :: Telescope -> C [Hs.Type ()]
compileConstructorArgs EmptyTel = return []
Expand Down Expand Up @@ -132,12 +144,13 @@ checkCompileToDataPragma def s = noCheckNames $ do
unless (length rcons == length dcons) $ fail
"they have a different number of constructors"
forM_ (zip dcons rcons) $ \(c1, c2) -> do
Hs.QualConDecl _ _ _ (Hs.ConDecl _ hsC1 args1) <-
(Hs.QualConDecl _ _ _ (Hs.ConDecl _ hsC1 args1) , rt1) <-
addContext dtel $ compileConstructor (teleArgs dtel) c1
-- rename parameters of r to match those of d
rtel' <- renameParameters dtel rtel
Hs.QualConDecl _ _ _ (Hs.ConDecl _ hsC2 args2) <-
(Hs.QualConDecl _ _ _ (Hs.ConDecl _ hsC2 args2) , rt2) <-
addContext rtel' $ compileConstructor (teleArgs rtel') c2
-- TODO: check that rt1 and rt2 are equal?
unless (hsC1 == hsC2) $ fail $
"name of constructor" <+> text (Hs.pp hsC1) <+>
"does not match" <+> text (Hs.pp hsC2)
Expand Down
6 changes: 3 additions & 3 deletions src/Agda2Hs/Compile/Record.hs
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,14 @@ compileRecord target def = do
assts -> Just (Hs.CxTuple () assts)
defaultDecls <- compileMinRecords def ms
return $ Hs.ClassDecl () context hd [] (Just (classDecls ++ map (Hs.ClsDecl ()) defaultDecls))
ToRecord newtyp ds -> do
ToRecord dtarget ds -> do
checkValidConName cName
when (theEtaEquality recEtaEquality' == YesEta) $ agda2hsErrorM $
"Agda records compiled to Haskell should have eta-equality disabled." <+>
"Add no-eta-equality to the definition of" <+> (text (pp rName) <> ".")
(constraints, fieldDecls) <- compileRecFields fieldDecl recFields fieldTel
when newtyp $ checkNewtypeCon cName fieldDecls
let target = if newtyp then Hs.NewType () else Hs.DataType ()
when (dtarget == ToNewType) $ checkNewtypeCon cName fieldDecls
let target = toDataTarget dtarget
compileDataRecord constraints fieldDecls target hd ds

where
Expand Down
11 changes: 8 additions & 3 deletions src/Agda2Hs/Compile/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -210,12 +210,17 @@ data CompiledDom
| DomDropped
-- ^ To nothing (e.g. erased proofs)

-- | Whether a datatype/record should be compiled as a @newtype@ haskell definition.
type AsNewType = Bool
-- | Whether a datatype/record should be compiled as a @newtype@ haskell definition,
-- a gadt-style datatype, or a regular datatype.
data DataTarget
= ToNewType
| ToGadt
| ToData
deriving (Eq)

-- | Compilation target for an Agda record definition.
data RecordTarget
= ToRecord AsNewType [Hs.Deriving ()]
= ToRecord DataTarget [Hs.Deriving ()]
| ToClass [String]

-- | Compilation target for an Agda instance declaration.
Expand Down
6 changes: 6 additions & 0 deletions src/Agda2Hs/Compile/Utils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ infixr 0 /\, \/
f /\ g = \x -> f x && g x
f \/ g = \x -> f x || g x

toDataTarget :: DataTarget -> Hs.DataOrNew ()
toDataTarget ToNewType = Hs.NewType ()
toDataTarget ToData = Hs.DataType ()
toDataTarget ToGadt = Hs.DataType ()

showTCM :: PrettyTCM a => a -> C String
showTCM x = liftTCM $ show <$> prettyTCM x

Expand Down Expand Up @@ -200,6 +205,7 @@ hasCompilePragma q = processPragma q <&> \case
TransparentPragma{} -> True
CompileToPragma{} -> True
NewTypePragma{} -> True
GadtPragma{} -> True
DerivePragma{} -> True

-- Exploits the fact that the name of the record type and the name of the record module are the
Expand Down
6 changes: 6 additions & 0 deletions src/Agda2Hs/Pragma.hs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ data ParsedPragma
| TransparentPragma
| NewTypePragma [Hs.Deriving ()]
| TuplePragma Hs.Boxed
| GadtPragma [Hs.Deriving ()]
| CompileToPragma String
| DerivePragma (Maybe (Hs.DerivStrategy ()))
deriving (Eq, Show)
Expand All @@ -72,6 +73,9 @@ parseStrategy _ = Nothing
newtypePragma :: String
newtypePragma = "newtype"

gadtPragma :: String
gadtPragma = "gadt"

processDeriving :: Range -> String -> ([Hs.Deriving ()] -> ParsedPragma) -> C ParsedPragma
processDeriving r d pragma = do
-- parse a deriving clause for a datatype by tacking it onto a
Expand All @@ -95,10 +99,12 @@ processPragma qn = liftTCM (getUniqueCompilerPragma pragmaName qn) >>= \case
| s == "transparent" -> return TransparentPragma
| s == "tuple" -> return $ TuplePragma Hs.Boxed
| s == "unboxed-tuple" -> return $ TuplePragma Hs.Unboxed
| s == "gadt" -> return $ GadtPragma []
| "to" `isPrefixOf` s -> return $ CompileToPragma (drop 3 s)
| s == newtypePragma -> return $ NewTypePragma []
| s == derivePragma -> return $ DerivePragma Nothing
| derivePragma `isPrefixOf` s -> return $ DerivePragma (parseStrategy (drop (length derivePragma + 1) s))
| "deriving" `isPrefixOf` s -> processDeriving r s DefaultPragma
| (newtypePragma ++ " deriving") `isPrefixOf` s -> processDeriving r (drop (length newtypePragma + 1) s) NewTypePragma
| (gadtPragma ++ " deriving") `isPrefixOf` s -> processDeriving r (drop (length gadtPragma + 1) s) GadtPragma
_ -> return $ DefaultPragma []
4 changes: 3 additions & 1 deletion test/AllTests.agda
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{-# OPTIONS --prop #-}
{-# OPTIONS --prop --polarity #-}
module AllTests where

import AllCubicalTests
Expand Down Expand Up @@ -106,6 +106,7 @@ import Issue306
import RelevantDotPattern1
import RelevantDotPattern2
import RelevantDotPattern3
import GadtSyntax

{-# FOREIGN AGDA2HS
import Issue14
Expand Down Expand Up @@ -209,4 +210,5 @@ import Issue306
import RelevantDotPattern1
import RelevantDotPattern2
import RelevantDotPattern3
import GadtSyntax
#-}
13 changes: 13 additions & 0 deletions test/GadtSyntax.agda
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{-# OPTIONS --polarity #-}

open import Haskell.Prelude

data Bol : Set where Tru Fls : Bol

{-# COMPILE AGDA2HS Bol gadt #-}

data Free (f : @++ Set → Set) (a : Set) : Set where
Return : a → Free f a
Roll : f (Free f a) → Free f a

{-# COMPILE AGDA2HS Free gadt #-}
1 change: 1 addition & 0 deletions test/golden/AllTests.hs
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,5 @@ import Issue306
import RelevantDotPattern1
import RelevantDotPattern2
import RelevantDotPattern3
import GadtSyntax

10 changes: 10 additions & 0 deletions test/golden/GadtSyntax.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module GadtSyntax where

data Bol where
Tru :: Bol
Fls :: Bol

data Free f a where
Return :: a -> Free f a
Roll :: f (Free f a) -> Free f a

Loading