packages feed

hydra-0.15.0: src/main/haskell/Hydra/Sources/Coq/Serde.hs

{-# LANGUAGE FlexibleContexts #-}

-- | Coq serializer: converts the Coq syntax AST to concrete Coq source code (.v files).

module Hydra.Sources.Coq.Serde where

-- Standard imports for term-level sources outside of the kernel
import Hydra.Kernel
import Hydra.Sources.Libraries
import           Hydra.Dsl.Meta.Lib.Strings                as Strings
import           Hydra.Dsl.Meta.Phantoms                   as Phantoms
import qualified Hydra.Dsl.Meta.Lib.Eithers                as Eithers
import qualified Hydra.Dsl.Meta.Lib.Equality               as Equality
import qualified Hydra.Dsl.Meta.Lib.Lists                  as Lists
import qualified Hydra.Dsl.Meta.Lib.Logic                  as Logic
import qualified Hydra.Dsl.Meta.Lib.Maps                   as Maps
import qualified Hydra.Dsl.Meta.Lib.Maybes                 as Maybes
import qualified Hydra.Dsl.Meta.Lib.Pairs                  as Pairs
import qualified Hydra.Dsl.Meta.Lib.Literals               as Literals
import qualified Hydra.Dsl.Meta.Lib.Sets                   as Sets
import qualified Hydra.Sources.Kernel.Terms.Serialization  as Serialization
import qualified Hydra.Sources.Kernel.Terms.Constants       as Constants
import qualified Hydra.Sources.Kernel.Terms.Formatting     as Formatting
import qualified Hydra.Sources.Kernel.Types.All            as KernelTypes
import qualified Hydra.Sources.Coq.Syntax              as CoqSyntax
import           Prelude hiding ((++))
import qualified Data.Int                                  as I
import qualified Data.List                                 as DL
import qualified Data.Map                                  as M
import qualified Data.Set                                  as S
import qualified Data.Maybe                                as Y

-- Additional imports for Coq AST
import Hydra.Ast
import qualified Hydra.Coq.Syntax as C


define :: String -> TTerm a -> TTermDefinition a
define = definitionInModule module_

ns :: Namespace
ns = Namespace "hydra.coq.serde"

module_ :: Module
module_ = Module {
            moduleNamespace = ns,
            moduleDefinitions = definitions,
            moduleTermDependencies = [Constants.ns, Formatting.ns, Serialization.ns],
            moduleTypeDependencies = (CoqSyntax.ns:KernelTypes.kernelTypesNamespaces),
            moduleDescription = Just "Coq serializer: converts Coq AST to concrete Coq source code"}
  where
    definitions = [
      toDefinition applicationToExpr,
      toDefinition axiomDeclarationToExpr,
      toDefinition binderToExpr,
      toDefinition commentToExpr,
      toDefinition constructorToExpr,
      toDefinition definitionToExpr,
      toDefinition documentToExpr,
      toDefinition fixpointDefinitionToExpr,
      toDefinition identToExpr,
      toDefinition inductiveBodyToExpr,
      toDefinition inductiveDefinitionToExpr,
      toDefinition localityToExpr,
      toDefinition matchToExpr,
      toDefinition moduleDefinitionToExpr,
      toDefinition pattern0ToExpr,
      toDefinition pattern10ToExpr,
      toDefinition pattern1ToExpr,
      toDefinition patternToExpr,
      toDefinition qualidToExpr,
      toDefinition recordDefinitionToExpr,
      toDefinition recordFieldToExpr,
      toDefinition requireImportToExpr,
      toDefinition sectionDefinitionToExpr,
      toDefinition sentenceContentToExpr,
      toDefinition sentenceToExpr,
      toDefinition sortToExpr,
      toDefinition termToExpr,
      toDefinition term0ToExpr,
      toDefinition term1ToExpr,
      toDefinition term10ToExpr,
      toDefinition term100ToExpr,
      toDefinition theoremBodyToExpr,
      toDefinition typeToExpr]

-- ===========================================================================
-- Helpers
-- ===========================================================================

-- | Render a Coq keyword
kw :: String -> TTerm Expr
kw s = Serialization.cst @@ string s

-- | Render space-separated expressions
sp :: [TTerm Expr] -> TTerm Expr
sp xs = Serialization.spaceSep @@ list xs

-- | Render with a period terminator
withDot :: TTerm Expr -> TTerm Expr
withDot e = Serialization.suffix @@ string "." @@ e

-- | Render an optional part, yielding empty list or singleton
optPart :: TTerm (Maybe a) -> (TTerm a -> TTerm Expr) -> TTerm [Expr]
optPart opt f = Maybes.maybe (list ([] :: [TTerm Expr])) (lambda "x" $ list [f (var "x")]) opt

-- ===========================================================================
-- Term language serialization
-- ===========================================================================

-- | Serialize an Ident
identToExpr :: TTermDefinition (C.Ident -> Expr)
identToExpr = define "identToExpr" $
  lambda "ident" $
    Serialization.cst @@ (unwrap C._String @@ (unwrap C._Ident @@ var "ident"))

-- | Serialize a Qualid: ident.field1.field2
qualidToExpr :: TTermDefinition (C.Qualid -> Expr)
qualidToExpr = define "qualidToExpr" $
  lambda "q" $ lets [
    "idExpr">: identToExpr @@ (project C._Qualid C._Qualid_id @@ var "q"),
    "fieldIds">: project C._Qualid C._Qualid_fieldIds @@ var "q",
    "fieldExprs">: Lists.map
      (lambda "f" $ identToExpr @@ (unwrap C._FieldIdent @@ var "f"))
      (var "fieldIds")] $
    Logic.ifElse (Lists.null $ var "fieldExprs")
      (var "idExpr")
      (Serialization.dotSep @@ Lists.concat2 (list [var "idExpr"]) (var "fieldExprs"))

-- | Serialize a Sort
sortToExpr :: TTermDefinition (C.Sort -> Expr)
sortToExpr = define "sortToExpr" $
  lambda "s" $ cases C._Sort (var "s") Nothing [
    C._Sort_set>>: constant $ kw "Set",
    C._Sort_prop>>: constant $ kw "Prop",
    C._Sort_sProp>>: constant $ kw "SProp",
    C._Sort_type>>: constant $ kw "Type",
    C._Sort_typeWithAnyUniverse>>: constant $ kw "Type",
    C._Sort_typeWithUniverse>>: lambda "u" $
      Serialization.noSep @@ list [kw "Type", kw "@{", kw "}" ]]

-- | Serialize a Type (which is just a wrapper around Term)
typeToExpr :: TTermDefinition (C.Type -> Expr)
typeToExpr = define "typeToExpr" $
  lambda "t" $ termToExpr @@ (unwrap C._Type @@ var "t")

-- | Serialize a Binder
binderToExpr :: TTermDefinition (C.Binder -> Expr)
binderToExpr = define "binderToExpr" $
  lambda "b" $ cases C._Binder (var "b") Nothing [
    C._Binder_name>>: lambda "n" $
      Maybes.maybe (kw "_")
        (lambda "i" $ identToExpr @@ var "i")
        (unwrap C._Name @@ var "n"),
    C._Binder_type>>: lambda "tb" $ lets [
      "names">: Lists.map
        (lambda "n" $ Maybes.maybe (kw "_")
          (lambda "i" $ identToExpr @@ var "i")
          (unwrap C._Name @@ var "n"))
        (project C._TypeBinders C._TypeBinders_names @@ var "tb"),
      "ty">: typeToExpr @@ (project C._TypeBinders C._TypeBinders_type @@ var "tb")] $
      Serialization.parens @@ (sp [
        Serialization.spaceSep @@ var "names",
        kw ":",
        var "ty"]),
    C._Binder_term>>: lambda "lb" $ lets [
      "name">: Maybes.maybe (kw "_")
        (lambda "i" $ identToExpr @@ var "i")
        (unwrap C._Name @@ (project C._LetBinder C._LetBinder_name @@ var "lb")),
      "ty">: Maybes.maybe (list ([] :: [TTerm Expr]))
        (lambda "t" $ list [kw ":", typeToExpr @@ var "t"])
        (project C._LetBinder C._LetBinder_type @@ var "lb"),
      "body">: termToExpr @@ (project C._LetBinder C._LetBinder_term @@ var "lb")] $
      Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat (list [
        list [var "name"],
        var "ty",
        list [kw ":=", var "body"]])),
    C._Binder_implicit>>: lambda "ib" $
      cases C._ImplicitBinders (var "ib") Nothing [
        C._ImplicitBinders_maximallyInserted>>: lambda "tb" $ lets [
          "names">: Lists.map
            (lambda "n" $ Maybes.maybe (kw "_")
              (lambda "i" $ identToExpr @@ var "i")
              (unwrap C._Name @@ var "n"))
            (project C._TypeBinders C._TypeBinders_names @@ var "tb"),
          "ty">: typeToExpr @@ (project C._TypeBinders C._TypeBinders_type @@ var "tb")] $
          Serialization.brackets @@ (asTerm Serialization.curlyBraces) @@ (asTerm Serialization.inlineStyle) @@
            (sp [Serialization.spaceSep @@ var "names", kw ":", var "ty"]),
        C._ImplicitBinders_nonMaximallyInserted>>: lambda "tb" $ lets [
          "names">: Lists.map
            (lambda "n" $ Maybes.maybe (kw "_")
              (lambda "i" $ identToExpr @@ var "i")
              (unwrap C._Name @@ var "n"))
            (project C._TypeBinders C._TypeBinders_names @@ var "tb"),
          "ty">: typeToExpr @@ (project C._TypeBinders C._TypeBinders_type @@ var "tb")] $
          Serialization.brackets @@ (asTerm Serialization.squareBrackets) @@ (asTerm Serialization.inlineStyle) @@
            (sp [Serialization.spaceSep @@ var "names", kw ":", var "ty"])],
    C._Binder_generalizing>>: lambda "gb" $
      cases C._GeneralizingBinder (var "gb") Nothing [
        C._GeneralizingBinder_explicit>>: lambda "tc" $
          Serialization.parens @@ (termToExpr @@ (project C._TypeclassConstraint C._TypeclassConstraint_term @@ var "tc")),
        C._GeneralizingBinder_implicitMaximallyInserted>>: lambda "tc" $
          Serialization.brackets @@ (asTerm Serialization.curlyBraces) @@ (asTerm Serialization.inlineStyle) @@
            (termToExpr @@ (project C._TypeclassConstraint C._TypeclassConstraint_term @@ var "tc")),
        C._GeneralizingBinder_implicitNonMaximallyInserted>>: lambda "tc" $
          Serialization.brackets @@ (asTerm Serialization.squareBrackets) @@ (asTerm Serialization.inlineStyle) @@
            (termToExpr @@ (project C._TypeclassConstraint C._TypeclassConstraint_term @@ var "tc"))],
    C._Binder_pattern>>: lambda "p" $ kw "_"]

-- | Serialize a Term
termToExpr :: TTermDefinition (C.Term -> Expr)
termToExpr = define "termToExpr" $
  lambda "t" $ cases C._Term (var "t") Nothing [
    C._Term_forallOrFun>>: lambda "fof" $
      cases C._ForallOrFun (var "fof") Nothing [
        C._ForallOrFun_forall>>: lambda "fa" $ sp [
          kw "forall",
          openBindersToExpr (project C._Forall C._Forall_binders @@ var "fa"),
          kw ",",
          typeToExpr @@ (project C._Forall C._Forall_type @@ var "fa")],
        C._ForallOrFun_fun>>: lambda "fn" $ sp [
          kw "fun",
          openBindersToExpr (project C._Fun C._Fun_binders @@ var "fn"),
          kw "=>",
          termToExpr @@ (project C._Fun C._Fun_body @@ var "fn")]],
    C._Term_let>>: lambda "lt" $ lets [
      "bindings">: project C._Let C._Let_bindings @@ var "lt",
      "body">: termToExpr @@ (project C._Let C._Let_in @@ var "lt")] $
      cases C._LetBindings (var "bindings") Nothing [
        C._LetBindings_named>>: lambda "ln" $ lets [
          "binder">: project C._LetNamed C._LetNamed_binder @@ var "ln",
          "name">: Maybes.maybe (kw "_")
            (lambda "i" $ identToExpr @@ var "i")
            (unwrap C._Name @@ (project C._LetBinder C._LetBinder_name @@ var "binder")),
          "binders">: Lists.map (lambda "b" $ binderToExpr @@ var "b")
            (project C._LetNamed C._LetNamed_binders @@ var "ln"),
          "ty">: Maybes.maybe (list ([] :: [TTerm Expr]))
            (lambda "t2" $ list [kw ":", typeToExpr @@ var "t2"])
            (project C._LetBinder C._LetBinder_type @@ var "binder"),
          "val">: termToExpr @@ (project C._LetBinder C._LetBinder_term @@ var "binder")] $
          sp [kw "let", Serialization.spaceSep @@ Lists.concat (list [
            list [var "name"],
            var "binders",
            var "ty",
            list [kw ":=", var "val"],
            list [kw "in"]]),
            var "body"],
        C._LetBindings_destructuring>>: lambda "ld" $ sp [kw "let", kw "...", kw "in", var "body"]],
    C._Term_if>>: lambda "ifE" $ lets [
      "cond">: termToExpr @@ (project C._If C._If_condition @@ var "ifE"),
      "thn">: termToExpr @@ (project C._If C._If_then @@ var "ifE"),
      "els">: termToExpr @@ (project C._If C._If_else @@ var "ifE")] $
      sp [kw "if", var "cond", kw "then", var "thn", kw "else", var "els"],
    C._Term_fix>>: lambda "fx" $
      cases C._Fix (var "fx") Nothing [
        C._Fix_decl>>: lambda "d" $ sp [kw "fix", fixDeclToExpr (var "d")],
        C._Fix_qual>>: lambda "q" $ kw "fix"],
    C._Term_cofix>>: lambda "cf" $ sp [kw "cofix", kw "..."],
    C._Term_term100>>: lambda "t100" $ term100ToExpr @@ var "t100"]

-- | Serialize OpenBinders
openBindersToExpr :: TTerm C.OpenBinders -> TTerm Expr
openBindersToExpr ob =
  cases C._OpenBinders ob Nothing [
    C._OpenBinders_type>>: lambda "tb" $ lets [
      "names">: Lists.map
        (lambda "n" $ Maybes.maybe (kw "_")
          (lambda "i" $ identToExpr @@ var "i")
          (unwrap C._Name @@ var "n"))
        (project C._TypeBinders C._TypeBinders_names @@ var "tb"),
      "ty">: typeToExpr @@ (project C._TypeBinders C._TypeBinders_type @@ var "tb")] $
      sp [Serialization.parens @@ (sp [Serialization.spaceSep @@ var "names", kw ":", var "ty"])],
    C._OpenBinders_binders>>: lambda "bs" $
      Serialization.spaceSep @@ Lists.map (lambda "b" $ binderToExpr @@ var "b") (var "bs")]

-- | Serialize a Fix_Decl
fixDeclToExpr :: TTerm C.Fix_Decl -> TTerm Expr
fixDeclToExpr d = lets [
  "name">: identToExpr @@ (project C._Fix_Decl C._Fix_Decl_ident @@ d),
  "binders">: Lists.map (lambda "b" $ binderToExpr @@ var "b")
    (project C._Fix_Decl C._Fix_Decl_binders @@ d),
  "ty">: Maybes.maybe (list ([] :: [TTerm Expr]))
    (lambda "t" $ list [kw ":", typeToExpr @@ var "t"])
    (project C._Fix_Decl C._Fix_Decl_type @@ d),
  "body">: termToExpr @@ (project C._Fix_Decl C._Fix_Decl_term @@ d)] $
  Serialization.spaceSep @@ Lists.concat (list [
    list [var "name"],
    var "binders",
    var "ty",
    list [kw ":=", var "body"]])

-- | Serialize a Term100
term100ToExpr :: TTermDefinition (C.Term100 -> Expr)
term100ToExpr = define "term100ToExpr" $
  lambda "t" $ cases C._Term100 (var "t") Nothing [
    C._Term100_cast>>: lambda "tc" $ sp [
      term10ToExpr @@ (project C._TypeCast C._TypeCast_term @@ var "tc"),
      kw ":",
      typeToExpr @@ (project C._TypeCast C._TypeCast_type @@ var "tc")],
    C._Term100_term10>>: lambda "t10" $ term10ToExpr @@ var "t10"]

-- | Serialize a Term10
term10ToExpr :: TTermDefinition (C.Term10 -> Expr)
term10ToExpr = define "term10ToExpr" $
  lambda "t" $ cases C._Term10 (var "t") Nothing [
    C._Term10_application>>: lambda "app" $ applicationToExpr @@ var "app",
    C._Term10_oneTerm>>: lambda "ot" $
      cases C._OneTerm (var "ot") Nothing [
        C._OneTerm_explicit>>: lambda "qa" $ qualidAnnotatedToExpr (var "qa"),
        C._OneTerm_term1>>: lambda "t1" $ term1ToExpr @@ var "t1"]]

-- | Serialize a Term1
term1ToExpr :: TTermDefinition (C.Term1 -> Expr)
term1ToExpr = define "term1ToExpr" $
  lambda "t" $ cases C._Term1 (var "t") Nothing [
    C._Term1_projection>>: constant $ kw "?projection",
    C._Term1_scope>>: constant $ kw "?scope",
    C._Term1_term0>>: lambda "t0" $ term0ToExpr @@ var "t0"]

-- | Serialize a Term0
term0ToExpr :: TTermDefinition (C.Term0 -> Expr)
term0ToExpr = define "term0ToExpr" $
  lambda "t" $ cases C._Term0 (var "t") Nothing [
    C._Term0_qualidAnnotated>>: lambda "qa" $ qualidAnnotatedToExpr (var "qa"),
    C._Term0_sort>>: lambda "s" $ sortToExpr @@ var "s",
    C._Term0_primitiveNotations>>: lambda "pn" $
      cases C._PrimitiveNotations (var "pn") Nothing [
        C._PrimitiveNotations_number>>: lambda "n" $ lets [
          "v">: unwrap C._Number @@ var "n"] $
          Serialization.cst @@ (Literals.showBigfloat (var "v")),
        C._PrimitiveNotations_string>>: lambda "s2" $
          sp [kw "\"", Serialization.cst @@ (unwrap C._String @@ var "s2"), kw "\""]],
    C._Term0_evar>>: lambda "ev" $ kw "?evar",
    C._Term0_match>>: lambda "m" $ matchToExpr @@ var "m",
    C._Term0_record>>: constant $ kw "{| |}",
    C._Term0_generalizing>>: constant $ kw "`( )",
    C._Term0_ltac>>: constant $ kw "ltac:( )",
    C._Term0_parens>>: lambda "inner" $
      Serialization.parens @@ (termToExpr @@ var "inner")]

-- | Serialize a QualidAnnotated
qualidAnnotatedToExpr :: TTerm C.QualidAnnotated -> TTerm Expr
qualidAnnotatedToExpr qa = qualidToExpr @@ (project C._QualidAnnotated C._QualidAnnotated_qualid @@ qa)

-- | Serialize an Application
applicationToExpr :: TTermDefinition (C.Application -> Expr)
applicationToExpr = define "applicationToExpr" $
  lambda "app" $ cases C._Application (var "app") Nothing [
    C._Application_normal>>: lambda "na" $ sp [
      term1ToExpr @@ (project C._NormalApplication C._NormalApplication_lhs @@ var "na"),
      Serialization.spaceSep @@ Lists.map
        (lambda "a" $ cases C._Arg (var "a") Nothing [
          C._Arg_ident>>: lambda "ia" $ sp [
            Serialization.parens @@ (sp [
              identToExpr @@ (project C._IdentArg C._IdentArg_ident @@ var "ia"),
              kw ":=",
              termToExpr @@ (project C._IdentArg C._IdentArg_term @@ var "ia")])],
          C._Arg_natural>>: lambda "na2" $ lets [
            "v">: unwrap C._Natural @@ (project C._NaturalArg C._NaturalArg_natural @@ var "na2")] $
            Serialization.cst @@ (Literals.showBigint (var "v")),
          C._Arg_term>>: lambda "t1" $ term1ToExpr @@ var "t1"])
        (project C._NormalApplication C._NormalApplication_rhs @@ var "na")],
    C._Application_annotated>>: lambda "aa" $ sp [
      kw "@",
      qualidAnnotatedToExpr (project C._AnnotatedApplication C._AnnotatedApplication_annot @@ var "aa")]]

-- | Serialize an AxiomDeclaration: `Axiom name : type.`
axiomDeclarationToExpr :: TTermDefinition (C.AxiomDeclaration -> Expr)
axiomDeclarationToExpr = define "axiomDeclarationToExpr" $
  lambda "a" $
    withDot $ sp [
      kw "Axiom",
      identToExpr @@ (project C._AxiomDeclaration C._AxiomDeclaration_name @@ var "a"),
      kw ":",
      typeToExpr @@ (project C._AxiomDeclaration C._AxiomDeclaration_type @@ var "a")]

-- | Serialize a Pattern0 (primitive / grouped pattern)
pattern0ToExpr :: TTermDefinition (C.Pattern0 -> Expr)
pattern0ToExpr = define "pattern0ToExpr" $
  lambda "p" $ cases C._Pattern0 (var "p") Nothing [
    C._Pattern0_qualid>>: lambda "q" $ qualidToExpr @@ var "q",
    C._Pattern0_qualIdAndPattern>>: constant $ kw "...",
    C._Pattern0_placeholder>>: constant $ kw "_",
    C._Pattern0_parens>>: lambda "ps" $ Serialization.parens @@
      (Serialization.infixWsList @@ string ", " @@
        Lists.map (lambda "p2" $ patternToExpr @@ var "p2") (var "ps")),
    C._Pattern0_number>>: lambda "n" $ lets [
      "v">: unwrap C._Number @@ var "n"] $
      Serialization.cst @@ (Literals.showBigfloat (var "v")),
    C._Pattern0_string>>: lambda "s" $ sp [
      kw "\"", Serialization.cst @@ (unwrap C._String @@ var "s"), kw "\""]]

-- | Serialize a Pattern1 (scoped pattern; we ignore the scope for now)
pattern1ToExpr :: TTermDefinition (C.Pattern1 -> Expr)
pattern1ToExpr = define "pattern1ToExpr" $
  lambda "p" $ pattern0ToExpr @@ (project C._Pattern1 C._Pattern1_pattern @@ var "p")

-- | Serialize a Pattern10 (as-binding, juxtaposition, or qualid-with-args)
pattern10ToExpr :: TTermDefinition (C.Pattern10 -> Expr)
pattern10ToExpr = define "pattern10ToExpr" $
  lambda "p" $ cases C._Pattern10 (var "p") Nothing [
    C._Pattern10_as>>: lambda "pa" $ sp [
      pattern1ToExpr @@ (project C._Pattern10_As C._Pattern10_As_pattern @@ var "pa"),
      kw "as",
      Maybes.maybe (kw "_")
        (lambda "i" $ identToExpr @@ var "i")
        (unwrap C._Name @@ (project C._Pattern10_As C._Pattern10_As_as @@ var "pa"))],
    C._Pattern10_patterns>>: lambda "pps" $ lets [
      "first">: pattern1ToExpr @@ (project C._Pattern10_Patterns C._Pattern10_Patterns_pattern @@ var "pps"),
      "rest">: Lists.map (lambda "p2" $ pattern1ToExpr @@ var "p2")
        (project C._Pattern10_Patterns C._Pattern10_Patterns_patterns @@ var "pps")] $
      Serialization.spaceSep @@ Lists.cons (var "first") (var "rest"),
    C._Pattern10_qualiid>>: lambda "pq" $ lets [
      "q">: qualidToExpr @@ (project C._Pattern10_Qualid C._Pattern10_Qualid_qualid @@ var "pq"),
      "args">: Lists.map (lambda "p2" $ pattern1ToExpr @@ var "p2")
        (project C._Pattern10_Qualid C._Pattern10_Qualid_patterns @@ var "pq")] $
      Logic.ifElse (Lists.null (var "args"))
        (var "q")
        (Serialization.spaceSep @@ Lists.cons (var "q") (var "args"))]

-- | Serialize a Pattern (top-level; we delegate to Pattern10 and ignore the annotation term)
patternToExpr :: TTermDefinition (C.Pattern -> Expr)
patternToExpr = define "patternToExpr" $
  lambda "p" $ cases C._Pattern (var "p") Nothing [
    C._Pattern_pattern>>: lambda "p10" $ pattern10ToExpr @@ var "p10",
    C._Pattern_term>>: constant $ kw "_"]

-- | Serialize a Match expression
matchToExpr :: TTermDefinition (C.Match -> Expr)
matchToExpr = define "matchToExpr" $
  lambda "m" $ lets [
    "items">: Lists.map
      (lambda "ci" $ lets [
        "t">: term100ToExpr @@ (project C._CaseItem C._CaseItem_term @@ var "ci"),
        "asP">: Maybes.maybe (list ([] :: [TTerm Expr]))
          (lambda "n" $ list [kw "as", Maybes.maybe (kw "_") (lambda "i" $ identToExpr @@ var "i")
            (unwrap C._Name @@ var "n")])
          (project C._CaseItem C._CaseItem_as @@ var "ci")] $
        Serialization.spaceSep @@ Lists.concat (list [list [var "t"], var "asP"]))
      (project C._Match C._Match_caseItems @@ var "m"),
    "ret">: Maybes.maybe (list ([] :: [TTerm Expr]))
      (lambda "r" $ list [kw "return", term100ToExpr @@ var "r"])
      (project C._Match C._Match_return @@ var "m"),
    "eqs">: Lists.map
      (lambda "eq" $ lets [
        "patGroups">: Lists.map
          (lambda "grp" $ Serialization.spaceSep @@
            Lists.map (lambda "p" $ patternToExpr @@ var "p") (var "grp"))
          (project C._Equation C._Equation_pattern @@ var "eq"),
        "pats">: Serialization.infixWsList @@ string " | " @@ (var "patGroups"),
        "body">: termToExpr @@ (project C._Equation C._Equation_term @@ var "eq")] $
        sp [kw "|", var "pats", kw "=>", var "body"])
      (project C._Match C._Match_equations @@ var "m")] $
    Serialization.newlineSep @@ Lists.concat (list [
      list [Serialization.spaceSep @@ Lists.concat (list [
        list [kw "match"],
        var "items",
        var "ret",
        list [kw "with"]])],
      var "eqs",
      list [kw "end"]])

-- ===========================================================================
-- Vernacular serialization
-- ===========================================================================

-- | Serialize a Comment
commentToExpr :: TTermDefinition (C.Comment -> Expr)
commentToExpr = define "commentToExpr" $
  lambda "c" $
    Serialization.cst @@ Strings.cat (list [
      string "(* ", unwrap C._Comment @@ var "c", string " *)"])

-- | Serialize a Locality qualifier
localityToExpr :: TTermDefinition (C.Locality -> Expr)
localityToExpr = define "localityToExpr" $
  lambda "loc" $ cases C._Locality (var "loc") Nothing [
    C._Locality_local>>: constant $ kw "Local",
    C._Locality_global>>: constant $ kw "Global"]

-- | Serialize a RequireImport
requireImportToExpr :: TTermDefinition (C.RequireImport -> Expr)
requireImportToExpr = define "requireImportToExpr" $
  lambda "ri" $ lets [
    "fromPart">: Maybes.maybe (list ([] :: [TTerm Expr]))
      (lambda "q" $ list [kw "From", qualidToExpr @@ var "q"])
      (project C._RequireImport C._RequireImport_from @@ var "ri"),
    "requirePart">: Logic.ifElse (project C._RequireImport C._RequireImport_require @@ var "ri")
      (list [kw "Require"])
      (list ([] :: [TTerm Expr])),
    "qualPart">: Maybes.maybe (list ([] :: [TTerm Expr]))
      (lambda "q" $ cases C._ImportQualification (var "q") Nothing [
        C._ImportQualification_import>>: constant $ list [kw "Import"],
        C._ImportQualification_export>>: constant $ list [kw "Export"]])
      (project C._RequireImport C._RequireImport_qualification @@ var "ri"),
    "mods">: Lists.map (lambda "m" $ qualidToExpr @@ var "m")
      (project C._RequireImport C._RequireImport_modules @@ var "ri")] $
    withDot $ Serialization.spaceSep @@ Lists.concat (list [
      var "fromPart",
      var "requirePart",
      var "qualPart",
      var "mods"])

-- | Serialize a Constructor
constructorToExpr :: TTermDefinition (C.Constructor -> Expr)
constructorToExpr = define "constructorToExpr" $
  lambda "c" $ lets [
    "name">: identToExpr @@ (project C._Constructor C._Constructor_name @@ var "c"),
    "binders">: Lists.map (lambda "b" $ binderToExpr @@ var "b")
      (project C._Constructor C._Constructor_binders @@ var "c"),
    "ty">: Maybes.maybe (list ([] :: [TTerm Expr]))
      (lambda "t" $ list [kw ":", typeToExpr @@ var "t"])
      (project C._Constructor C._Constructor_type @@ var "c")] $
    Serialization.spaceSep @@ Lists.concat (list [
      list [kw "|", var "name"],
      var "binders",
      var "ty"])

-- | Serialize an InductiveBody
inductiveBodyToExpr :: TTermDefinition (C.InductiveBody -> Expr)
inductiveBodyToExpr = define "inductiveBodyToExpr" $
  lambda "ib" $ lets [
    "name">: identToExpr @@ (project C._InductiveBody C._InductiveBody_name @@ var "ib"),
    "binders">: Lists.map (lambda "b" $ binderToExpr @@ var "b")
      (project C._InductiveBody C._InductiveBody_binders @@ var "ib"),
    "ty">: Maybes.maybe (list ([] :: [TTerm Expr]))
      (lambda "t" $ list [kw ":", typeToExpr @@ var "t"])
      (project C._InductiveBody C._InductiveBody_type @@ var "ib"),
    "constrs">: Lists.map (lambda "c" $ constructorToExpr @@ var "c")
      (project C._InductiveBody C._InductiveBody_constructors @@ var "ib")] $
    Serialization.newlineSep @@ Lists.concat (list [
      list [Serialization.spaceSep @@ Lists.concat (list [
        list [var "name"],
        var "binders",
        var "ty",
        list [kw ":="]])],
      var "constrs"])

-- | Serialize an InductiveDefinition.
-- Produces "[Locality] Inductive body1 with body2 with body3." for mutual inductives.
inductiveDefinitionToExpr :: TTermDefinition (C.InductiveDefinition -> Expr)
inductiveDefinitionToExpr = define "inductiveDefinitionToExpr" $
  lambda "id" $ lets [
    "locPart">: Maybes.maybe (list ([] :: [TTerm Expr]))
      (lambda "l" $ list [localityToExpr @@ var "l"])
      (project C._InductiveDefinition C._InductiveDefinition_locality @@ var "id"),
    "kwPart">: Logic.ifElse (project C._InductiveDefinition C._InductiveDefinition_coinductive @@ var "id")
      (kw "CoInductive")
      (kw "Inductive"),
    "bodyExprs">: Lists.map (lambda "b" $ inductiveBodyToExpr @@ var "b")
      (project C._InductiveDefinition C._InductiveDefinition_bodies @@ var "id"),
    "firstBody">: Maybes.fromMaybe (Serialization.cst @@ string "") (Lists.maybeHead $ var "bodyExprs"),
    "restBodies">: Lists.map
      (lambda "b" $ Serialization.spaceSep @@ list [kw "with", var "b"])
      (Lists.drop (int32 1) $ var "bodyExprs"),
    "firstLine">: Serialization.spaceSep @@ Lists.concat (list [
      var "locPart",
      list [var "kwPart", var "firstBody"]])] $
    withDot $ Serialization.newlineSep @@ Lists.cons (var "firstLine") (var "restBodies")

-- | Serialize a RecordField
recordFieldToExpr :: TTermDefinition (C.RecordField -> Expr)
recordFieldToExpr = define "recordFieldToExpr" $
  lambda "rf" $ sp [
    identToExpr @@ (project C._RecordField C._RecordField_name @@ var "rf"),
    kw ":",
    typeToExpr @@ (project C._RecordField C._RecordField_type @@ var "rf")]

-- | Serialize a RecordDefinition
recordDefinitionToExpr :: TTermDefinition (C.RecordDefinition -> Expr)
recordDefinitionToExpr = define "recordDefinitionToExpr" $
  lambda "rd" $ lets [
    "locPart">: Maybes.maybe (list ([] :: [TTerm Expr]))
      (lambda "l" $ list [localityToExpr @@ var "l"])
      (project C._RecordDefinition C._RecordDefinition_locality @@ var "rd"),
    "name">: identToExpr @@ (project C._RecordDefinition C._RecordDefinition_name @@ var "rd"),
    "binders">: Lists.map (lambda "b" $ binderToExpr @@ var "b")
      (project C._RecordDefinition C._RecordDefinition_binders @@ var "rd"),
    "sortPart">: Maybes.maybe (list ([] :: [TTerm Expr]))
      (lambda "s" $ list [kw ":", sortToExpr @@ var "s"])
      (project C._RecordDefinition C._RecordDefinition_sort @@ var "rd"),
    "body">: project C._RecordDefinition C._RecordDefinition_body @@ var "rd",
    "constrPart">: Maybes.maybe (list ([] :: [TTerm Expr]))
      (lambda "c" $ list [identToExpr @@ var "c"])
      (project C._RecordBody C._RecordBody_constructor @@ var "body"),
    "fields">: Lists.map (lambda "f" $ Serialization.suffix @@ string " ;" @@ (recordFieldToExpr @@ var "f"))
      (project C._RecordBody C._RecordBody_fields @@ var "body")] $
    withDot $ Serialization.newlineSep @@ Lists.concat (list [
      list [Serialization.spaceSep @@ Lists.concat (list [
        var "locPart",
        list [kw "Record"],
        list [var "name"],
        var "binders",
        var "sortPart",
        list [kw ":="],
        var "constrPart",
        list [kw "{"]])],
      var "fields",
      list [kw "}"]])

-- | Serialize a Definition
definitionToExpr :: TTermDefinition (C.Definition -> Expr)
definitionToExpr = define "definitionToExpr" $
  lambda "d" $ lets [
    "locPart">: Maybes.maybe (list ([] :: [TTerm Expr]))
      (lambda "l" $ list [localityToExpr @@ var "l"])
      (project C._Definition C._Definition_locality @@ var "d"),
    "name">: identToExpr @@ (project C._Definition C._Definition_name @@ var "d"),
    "binders">: Lists.map (lambda "b" $ binderToExpr @@ var "b")
      (project C._Definition C._Definition_binders @@ var "d"),
    "ty">: Maybes.maybe (list ([] :: [TTerm Expr]))
      (lambda "t" $ list [kw ":", typeToExpr @@ var "t"])
      (project C._Definition C._Definition_type @@ var "d"),
    "body">: termToExpr @@ (project C._Definition C._Definition_body @@ var "d")] $
    withDot $ Serialization.spaceSep @@ Lists.concat (list [
      var "locPart",
      list [kw "Definition", var "name"],
      var "binders",
      var "ty",
      list [kw ":=", var "body"]])

-- | Serialize a FixpointDefinition
fixpointDefinitionToExpr :: TTermDefinition (C.FixpointDefinition -> Expr)
fixpointDefinitionToExpr = define "fixpointDefinitionToExpr" $
  lambda "fd" $ lets [
    "locPart">: Maybes.maybe (list ([] :: [TTerm Expr]))
      (lambda "l" $ list [localityToExpr @@ var "l"])
      (project C._FixpointDefinition C._FixpointDefinition_locality @@ var "fd"),
    "name">: identToExpr @@ (project C._FixpointDefinition C._FixpointDefinition_name @@ var "fd"),
    "binders">: Lists.map (lambda "b" $ binderToExpr @@ var "b")
      (project C._FixpointDefinition C._FixpointDefinition_binders @@ var "fd"),
    "ty">: Maybes.maybe (list ([] :: [TTerm Expr]))
      (lambda "t" $ list [kw ":", typeToExpr @@ var "t"])
      (project C._FixpointDefinition C._FixpointDefinition_type @@ var "fd"),
    "body">: termToExpr @@ (project C._FixpointDefinition C._FixpointDefinition_body @@ var "fd")] $
    withDot $ Serialization.spaceSep @@ Lists.concat (list [
      var "locPart",
      list [kw "Fixpoint", var "name"],
      var "binders",
      var "ty",
      list [kw ":=", var "body"]])

-- | Serialize a TheoremBody
theoremBodyToExpr :: TTermDefinition (C.TheoremBody -> Expr)
theoremBodyToExpr = define "theoremBodyToExpr" $
  lambda "tb" $ lets [
    "kindKw">: cases C._TheoremKind (project C._TheoremBody C._TheoremBody_kind @@ var "tb") Nothing [
      C._TheoremKind_theorem>>: constant $ kw "Theorem",
      C._TheoremKind_lemma>>: constant $ kw "Lemma",
      C._TheoremKind_proposition>>: constant $ kw "Proposition",
      C._TheoremKind_corollary>>: constant $ kw "Corollary",
      C._TheoremKind_example>>: constant $ kw "Example"],
    "name">: identToExpr @@ (project C._TheoremBody C._TheoremBody_name @@ var "tb"),
    "binders">: Lists.map (lambda "b" $ binderToExpr @@ var "b")
      (project C._TheoremBody C._TheoremBody_binders @@ var "tb"),
    "ty">: typeToExpr @@ (project C._TheoremBody C._TheoremBody_type @@ var "tb"),
    "proof">: termToExpr @@ (project C._TheoremBody C._TheoremBody_proof @@ var "tb")] $
    Serialization.newlineSep @@ list [
      withDot $ Serialization.spaceSep @@ Lists.concat (list [
        list [var "kindKw", var "name"],
        var "binders",
        list [kw ":", var "ty"]]),
      kw "Proof.",
      sp [kw "exact", Serialization.parens @@ var "proof"],
      kw "Qed."]

-- | Serialize a ModuleDefinition
moduleDefinitionToExpr :: TTermDefinition (C.ModuleDefinition -> Expr)
moduleDefinitionToExpr = define "moduleDefinitionToExpr" $
  lambda "md" $ lets [
    "name">: identToExpr @@ (project C._ModuleDefinition C._ModuleDefinition_name @@ var "md"),
    "sentences">: Lists.map (lambda "s" $ sentenceToExpr @@ var "s")
      (project C._ModuleDefinition C._ModuleDefinition_sentences @@ var "md")] $
    Serialization.doubleNewlineSep @@ Lists.concat (list [
      list [withDot $ sp [kw "Module", var "name"]],
      var "sentences",
      list [withDot $ sp [kw "End", var "name"]]])

-- | Serialize a SectionDefinition
sectionDefinitionToExpr :: TTermDefinition (C.SectionDefinition -> Expr)
sectionDefinitionToExpr = define "sectionDefinitionToExpr" $
  lambda "sd" $ lets [
    "name">: identToExpr @@ (project C._SectionDefinition C._SectionDefinition_name @@ var "sd"),
    "sentences">: Lists.map (lambda "s" $ sentenceToExpr @@ var "s")
      (project C._SectionDefinition C._SectionDefinition_sentences @@ var "sd")] $
    Serialization.doubleNewlineSep @@ Lists.concat (list [
      list [withDot $ sp [kw "Section", var "name"]],
      var "sentences",
      list [withDot $ sp [kw "End", var "name"]]])

-- | Serialize a SentenceContent
sentenceContentToExpr :: TTermDefinition (C.SentenceContent -> Expr)
sentenceContentToExpr = define "sentenceContentToExpr" $
  lambda "sc" $ cases C._SentenceContent (var "sc") Nothing [
    C._SentenceContent_axiom>>: lambda "a" $ axiomDeclarationToExpr @@ var "a",
    C._SentenceContent_definition>>: lambda "d" $ definitionToExpr @@ var "d",
    C._SentenceContent_fixpoint>>: lambda "f" $ fixpointDefinitionToExpr @@ var "f",
    C._SentenceContent_inductive>>: lambda "i" $ inductiveDefinitionToExpr @@ var "i",
    C._SentenceContent_module>>: lambda "m" $ moduleDefinitionToExpr @@ var "m",
    C._SentenceContent_notation>>: lambda "n" $ kw "(* notation *)",
    C._SentenceContent_record>>: lambda "r" $ recordDefinitionToExpr @@ var "r",
    C._SentenceContent_requireImport>>: lambda "ri" $ requireImportToExpr @@ var "ri",
    C._SentenceContent_section>>: lambda "s" $ sectionDefinitionToExpr @@ var "s",
    C._SentenceContent_theorem>>: lambda "t" $ theoremBodyToExpr @@ var "t"]

-- | Serialize a Sentence (optional comment + content)
sentenceToExpr :: TTermDefinition (C.Sentence -> Expr)
sentenceToExpr = define "sentenceToExpr" $
  lambda "s" $ lets [
    "cmtPart">: Maybes.maybe (list ([] :: [TTerm Expr]))
      (lambda "c" $ list [commentToExpr @@ var "c"])
      (project C._Sentence C._Sentence_comment @@ var "s"),
    "content">: sentenceContentToExpr @@ (project C._Sentence C._Sentence_content @@ var "s")] $
    Serialization.newlineSep @@ Lists.concat (list [var "cmtPart", list [var "content"]])

-- | Serialize a Document (complete .v file)
documentToExpr :: TTermDefinition (C.Document -> Expr)
documentToExpr = define "documentToExpr" $
  lambda "doc" $
    Serialization.doubleNewlineSep @@ Lists.map
      (lambda "s" $ sentenceToExpr @@ var "s")
      (project C._Document C._Document_sentences @@ var "doc")