Skip to content

Commit d95928f

Browse files
committed
support one-inductive datatypes
1 parent 47bf2ad commit d95928f

File tree

9 files changed

+174
-44
lines changed

9 files changed

+174
-44
lines changed

Playground.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ with odd (n : nat) :=
5252
| S n => even n
5353
end.
5454

55-
Definition prog := even 2 || odd 2.
55+
Definition prog := double.
5656

5757
MetaCoq Quote Recursively Definition ex1 := prog.
5858
Eval vm_compute in cic_to_box ex1.

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@ Compatible with Coq 8.20.0 and MetaCoq 1.3.2.
1212
- [x] Fix generation of Coq code
1313
- [x] Add Coq pretty-printing
1414
- [x] Support mutual definitions
15-
- [ ] Support one-inductive
15+
- [x] Support one-inductive
1616
- [ ] Support mutual inductives
1717
- [ ] Better error-reporting
1818
- [ ] "Support" modules
19+
- [ ] Check support for Agda-specific edge cases
20+
- [ ] Pattern-matching lambdas
21+
- [ ] With-generated lambdas
22+
- [ ] Module applications
1923
- [ ] Support literals (ints and floats)
2024
- [ ] Setup compilation to WASM/RUST
2125
- [ ] Setup proper testing infrastructure

agda2lambox.cabal

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@ source-repository head
1515
executable agda2lambox
1616
hs-source-dirs: src
1717
main-is: Main.hs
18-
other-modules: Agda.Lib,
18+
other-modules: Agda,
19+
Agda.Lib,
1920
Agda.Utils,
20-
Agda,
2121
Agda2Lambox.Monad,
2222
Agda2Lambox.Convert,
2323
Agda2Lambox.Convert.Class,
24-
Agda2Lambox.Convert.Term,
24+
Agda2Lambox.Convert.Data,
2525
Agda2Lambox.Convert.Function,
26+
Agda2Lambox.Convert.Term,
2627
Agda2Lambox,
2728
Utils,
2829
LambdaBox,
@@ -33,8 +34,8 @@ executable agda2lambox
3334
Agda >= 2.7 && <= 2.8,
3435
deepseq >= 1.4.4 && < 1.6,
3536
pretty-show,
36-
filepath,
3737
directory,
38+
filepath,
3839
mtl
3940
default-language: Haskell2010
4041
default-extensions:

src/Agda2Lambox/Convert/Data.hs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{-# LANGUAGE NamedFieldPuns #-}
2+
-- | Convert Agda datatypes to λ□ inductive declarations
3+
module Agda2Lambox.Convert.Data
4+
( convertDatatype
5+
) where
6+
7+
import Control.Monad.Reader ( ask, liftIO )
8+
import Control.Monad ( forM )
9+
import Data.List ( elemIndex )
10+
11+
import Utils
12+
13+
import Agda ( liftTCM )
14+
import Agda.Lib ()
15+
import Agda.Utils
16+
import Agda.Syntax.Abstract.Name ( qnameModule, qnameName )
17+
import Agda.TypeChecking.Monad.Base
18+
import Agda.TypeChecking.Monad.Env ( withCurrentModule )
19+
import Agda.TypeChecking.Datatypes ( ConstructorInfo(..), getConstructorInfo )
20+
import Agda.Compiler.ToTreeless ( toTreeless )
21+
import Agda.Compiler.Backend ( getConstInfo )
22+
import Agda.Syntax.Treeless ( EvaluationStrategy(EagerEvaluation) )
23+
import Agda.Syntax.Common.Pretty ( prettyShow )
24+
25+
import LambdaBox
26+
27+
import Agda2Lambox.Monad
28+
import Agda2Lambox.Convert.Class
29+
import Agda.Utils.Monad (guardWithError)
30+
31+
32+
-- | Convert a datatype definition to a Lambdabox declaration.
33+
convertDatatype :: Definition :~> GlobalDecl
34+
convertDatatype defn@Defn{defName, theDef} =
35+
withCurrentModule (qnameModule defName) do
36+
let Datatype{..} = theDef
37+
38+
ctors :: [ConstructorBody]
39+
<- forM dataCons \cname -> do
40+
DataCon arity <- getConstructorInfo cname
41+
return Ctor
42+
{ ctorName = prettyShow $ qnameName cname
43+
, ctorArgs = arity
44+
}
45+
46+
let
47+
inductive = OneInductive
48+
{ indName = prettyShow $ qnameName defName
49+
, indPropositional = False
50+
-- TODO(flupe): ^ take care of this (use datatypeSort to figure this out)
51+
, indKElim = IntoAny
52+
-- TODO(flupe): also take care of this (with the Sort)
53+
, indCtors = ctors
54+
, indProjs = []
55+
}
56+
57+
return $ InductiveDecl $ MutualInductive
58+
{ indFinite = Finite
59+
-- NOTE(flupe): Agda's datatypes are *always* finite?
60+
-- Co-induction is restricted to records.
61+
-- We may want to set BiFinite for non-recursive datatypes, but I don't know yet.
62+
, indPars = dataPars
63+
-- TODO(flupe):
64+
-- ^ double-check, but I don't think we'll ever share parameters.
65+
-- unless, perhaps, when defined inside a parametrized module (?).
66+
, indBodies = [inductive]
67+
}

src/Agda2Lambox/Convert/Function.hs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{-# LANGUAGE NamedFieldPuns #-}
2+
-- | Convert Agda functions to λ□ constant declarations
23
module Agda2Lambox.Convert.Function
34
( convertFunction
45
) where
@@ -39,28 +40,30 @@ convertFunctionBody Defn{defName} =
3940
Just t <- liftTCM $ toTreeless EagerEvaluation defName
4041
convert t
4142

42-
-- | Convert a function to a Lambdabox term.
43-
convertFunction :: Definition :~> Term
43+
-- | Convert a function definition to a λ□ declaration.
44+
convertFunction :: Definition :~> GlobalDecl
4445
convertFunction defn@Defn{defName, theDef} =
4546
withCurrentModule (qnameModule defName) do
4647
let Function{funMutual = Just ms} = theDef
4748

48-
if null ms then convertFunctionBody defn
49+
if null ms then
50+
ConstantDecl . Just <$> convertFunctionBody defn
4951
else do
5052
mdefs :: [Definition] <- mapM getConstInfo ms
5153

52-
if (any (not . isFunction) mdefs) then
54+
if any (not . isFunction) mdefs then
5355
fail "Induction-recursion and other weird mutual defs not supported."
5456
else inMutuals ms do
55-
-- NOTE(flupe):
57+
-- NOTE(flupe):
5658
-- maybe reverse ms here?
5759
-- it's unclear in which order mutual fixpoints are added to the local context
5860
let Just k = elemIndex defName ms
5961

60-
flip LFix k <$> forM mdefs \def@Defn{defName} -> do
61-
body <- convertFunctionBody def
62-
return Def
63-
{ dName = Named $ prettyShow defName
64-
, dBody = body
65-
, dArgs = 0 -- TODO(flupe): when is this ever not zero?
66-
}
62+
ConstantDecl . Just . flip LFix k <$>
63+
forM mdefs \def@Defn{defName} -> do
64+
body <- convertFunctionBody def
65+
return Def
66+
{ dName = Named $ prettyShow defName
67+
, dBody = body
68+
, dArgs = 0 -- TODO(flupe): when is this ever not zero?
69+
}

src/CoqGen.hs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ instance {-# OVERLAPPING #-} Pretty (ToCoq String) where
4141
instance Pretty (ToCoq Int) where
4242
pretty = pretty . unwrap
4343

44+
instance Pretty (ToCoq Bool) where
45+
pretty (ToCoq v) = if v then "true" else "false"
46+
4447
instance Pretty (ToCoq a) => Pretty (ToCoq (Maybe a)) where
4548
pretty (ToCoq x) =
4649
case x of
@@ -89,13 +92,55 @@ instance Pretty (ToCoq ModPath) where
8992
instance Pretty (ToCoq KerName) where
9093
pretty (ToCoq KerName{..}) = pcoq (kerModPath, kerName)
9194

95+
instance Pretty (ToCoq RecursivityKind) where
96+
pretty (ToCoq rk) =
97+
case rk of
98+
Finite -> ctor "Finite" []
99+
CoFinite -> ctor "CoFinite" []
100+
BiFinite -> ctor "BiFinite" []
101+
102+
instance Pretty (ToCoq AllowedElims) where
103+
pretty (ToCoq ae) =
104+
case ae of
105+
IntoSProp -> ctor "IntoSProp" []
106+
IntoPropSProp -> ctor "IntoPropSProp" []
107+
IntoSetPropSProp -> ctor "IntoSetPropSProp" []
108+
IntoAny -> ctor "IntoAny" []
109+
110+
instance Pretty (ToCoq ConstructorBody) where
111+
pretty (ToCoq Ctor{..}) =
112+
record [ ("cstr_name", pcoq ctorName)
113+
, ("cstr_nargs", pcoq ctorArgs)
114+
]
115+
116+
instance Pretty (ToCoq ProjectionBody) where
117+
pretty (ToCoq Proj{..}) =
118+
record [ ("proj_name", pcoq projName)
119+
]
120+
121+
instance Pretty (ToCoq OneInductiveBody) where
122+
pretty (ToCoq OneInductive{..}) =
123+
record [ ("ind_name", pcoq indName)
124+
, ("ind_propositional", pcoq indPropositional)
125+
, ("ind_kelim", pcoq indKElim)
126+
, ("ind_ctors", pcoq indCtors)
127+
, ("ind_projs", pcoq indProjs)
128+
]
129+
130+
instance Pretty (ToCoq MutualInductiveBody) where
131+
pretty (ToCoq MutualInductive{..}) =
132+
record [ ("ind_finite", pcoq indFinite)
133+
, ("ind_npars", pcoq indPars)
134+
, ("ind_bodies", pcoq indBodies)
135+
]
136+
92137
instance Pretty (ToCoq GlobalDecl) where
93138
pretty (ToCoq decl) =
94139
case decl of
95140
ConstantDecl body ->
96141
ctor "ConstantDecl" [record [("cst_body", pcoq body)]]
97142
InductiveDecl minds ->
98-
ctor "InductiveDecl" []
143+
ctor "InductiveDecl" [pcoq minds]
99144

100145
instance Pretty (ToCoq t) => Pretty (ToCoq (Def t)) where
101146
pretty (ToCoq Def{..}) =

src/LambdaBox.hs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,13 @@ data AllowedElims
149149
| IntoAny
150150
deriving (Eq, Show)
151151

152-
data ConstructorBody = Ctor
152+
data ConstructorBody = Ctor
153153
{ ctorName :: Ident
154154
, ctorArgs :: Int
155155
} deriving (Eq, Show)
156156

157157
data ProjectionBody = Proj
158158
{ projName :: Ident
159-
, projArgs :: Int
160159
} deriving (Eq, Show)
161160

162161
-- | Inductive datatype declaration body
@@ -168,17 +167,24 @@ data OneInductiveBody = OneInductive
168167
, indProjs :: [ProjectionBody]
169168
} deriving (Eq, Show)
170169

170+
data RecursivityKind
171+
= Finite -- ^ Inductive datatype.
172+
| CoFinite -- ^ Coinductive datatype.
173+
| BiFinite -- ^ Non-recursive.
174+
deriving (Eq, Show)
175+
171176
-- | Declaration of mutually defined inductive types
172177
data MutualInductiveBody = MutualInductive
173-
{ indPars :: Int
178+
{ indFinite :: RecursivityKind
179+
, indPars :: Int
174180
, indBodies :: [OneInductiveBody]
175181
} deriving (Eq, Show)
176182

177183
-- | Definition of a constant in the environment
178184
type ConstantBody = Maybe Term
179185

180186
-- | Global declarations.
181-
data GlobalDecl
187+
data GlobalDecl
182188
= ConstantDecl ConstantBody
183189
| InductiveDecl MutualInductiveBody
184190
deriving (Eq, Show)

src/Main.hs

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module Main where
55
import Control.Monad ( unless )
66
import Control.Monad.IO.Class ( MonadIO(liftIO) )
77
import Control.DeepSeq ( NFData(..) )
8-
import Data.Maybe ( fromMaybe, catMaybes, isJust )
8+
import Data.Maybe ( fromMaybe, catMaybes, isJust, isNothing )
99
import Data.Version ( showVersion )
1010
import GHC.Generics ( Generic )
1111
import System.Console.GetOpt ( OptDescr(Option), ArgDescr(ReqArg) )
@@ -21,9 +21,11 @@ import LambdaBox
2121
import qualified LambdaBox as L
2222
import Agda2Lambox.Convert ( convert )
2323
import Agda2Lambox.Convert.Function ( convertFunction )
24+
import Agda2Lambox.Convert.Data ( convertDatatype )
2425
import Agda2Lambox.Monad ( runC0, inMutuals )
2526
import CoqGen ( ToCoq(ToCoq) )
2627
import Agda.Syntax.Common.Pretty ( (<?>), pretty )
28+
import Agda.Syntax.Common (hasQuantityω)
2729

2830

2931
main :: IO ()
@@ -90,30 +92,28 @@ moduleSetup _ _ m _ = do
9092
setScope . iInsideScope =<< curIF
9193
return $ Recompile ()
9294

93-
-- | Which Agda definitions to ignore.
94-
ignoreDef :: Definition -> Bool
95-
ignoreDef d@Defn{..}
96-
| hasQuantity0 d
97-
= True
98-
| Function {..} <- theDef
99-
= (theDef ^. funInline) -- inlined functions (from module application)
100-
-- || funErasure -- @0 functions
101-
|| isJust funExtLam -- pattern-lambdas
102-
|| isJust funWith -- with-generated
103-
| otherwise
104-
= True
105-
10695
compile :: Options -> ModuleEnv -> IsMain -> Definition -> TCM (Maybe (KerName, GlobalDecl))
107-
compile _ _ _ defn | ignoreDef defn = return Nothing
108-
compile opts tlm _ def@Defn{..} = do
109-
body <- runC0 $ convertFunction def
110-
return $ Just $ ( qnameToKerName defName
111-
, ConstantDecl $ Just body)
96+
compile opts tlm _ def@Defn{..} =
97+
fmap (qnameToKerName defName,) <$> -- prepend kername
98+
case theDef of
99+
100+
-- TODO(flupe): offload the check to Convert.Function
101+
Function{..}
102+
| not (theDef ^. funInline) -- not inlined (from module application)
103+
, isNothing funExtLam -- not a pattern-lambda-generated function NOTE(flupe): ?
104+
, isNothing funWith -- not a with-generated function NOTE(flupe): ?
105+
, hasQuantityω def -- non-erased
106+
-> Just <$> runC0 (convertFunction def)
107+
108+
Datatype{} -> Just <$> runC0 (convertDatatype def)
109+
110+
_ -> Nothing <$ (liftIO $ putStrLn $ "Skipping " <> prettyShow defName)
111+
112112

113113
writeModule :: Options -> ModuleEnv -> IsMain -> TopLevelModuleName
114114
-> [Maybe (KerName, GlobalDecl)]
115115
-> TCM ModuleRes
116-
writeModule opts _ _ m (catMaybes -> cdefs) = do
116+
writeModule opts _ _ m (reverse . catMaybes -> cdefs) = do
117117
compDir <- compileDir
118118

119119
let outDir = fromMaybe compDir (optOutDir opts)
@@ -134,7 +134,7 @@ writeModule opts _ _ m (catMaybes -> cdefs) = do
134134
where
135135
coqModuleTemplate :: [(KerName, GlobalDecl)] -> String
136136
coqModuleTemplate coqterms = unlines
137-
[ "From MetaCoq.Common Require Import BasicAst Kernames."
137+
[ "From MetaCoq.Common Require Import BasicAst Kernames Universes."
138138
, "From MetaCoq.Erasure Require Import EAst."
139139
, "From MetaCoq.Utils Require Import bytestring MCString."
140140
, "From Coq Require Import List."

test/OddEven.agda

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ data Nat : Set where
44
zero : Nat
55
succ : Nat -> Nat
66

7+
double : Nat -> Nat
8+
double zero = zero
9+
double (succ n) = succ (succ (double n))
10+
711
odd even : Nat -> Bool
812

913
odd zero = false

0 commit comments

Comments
 (0)