packages feed

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

-- | Python serializer: converts Python AST to concrete syntax (source code).
-- Serializes the Python syntax model (Hydra.Python.Syntax) into properly formatted Python source code.

module Hydra.Sources.Python.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.Annotations                     as Annotations
import qualified Hydra.Dsl.Bootstrap                       as Bootstrap
import qualified Hydra.Dsl.LiteralTypes                    as LiteralTypes
import qualified Hydra.Dsl.Literals                        as Literals
import qualified Hydra.Dsl.Paths                      as Paths
import qualified Hydra.Dsl.Ast                        as Ast
import qualified Hydra.Dsl.Meta.Base                       as MetaBase
import qualified Hydra.Dsl.Coders                     as Coders
import qualified Hydra.Dsl.Util                    as Util
import qualified Hydra.Dsl.Meta.Core                       as Core
import qualified Hydra.Dsl.Meta.Graph                      as Graph
import qualified Hydra.Dsl.Json.Model                       as Json
import qualified Hydra.Dsl.Meta.Lib.Chars                  as Chars
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.Literals               as Literals
import qualified Hydra.Dsl.Meta.Lib.Logic                  as Logic
import qualified Hydra.Dsl.Meta.Lib.Maps                   as Maps
import qualified Hydra.Dsl.Meta.Lib.Math                   as Math
import qualified Hydra.Dsl.Meta.Lib.Maybes                 as Maybes
import qualified Hydra.Dsl.Meta.Lib.Pairs                  as Pairs
import qualified Hydra.Dsl.Meta.Lib.Sets                   as Sets
import qualified Hydra.Dsl.Packaging                     as Packaging
import qualified Hydra.Dsl.Meta.Terms                      as MetaTerms
import qualified Hydra.Dsl.Meta.Testing                    as Testing
import qualified Hydra.Dsl.Topology                   as Topology
import qualified Hydra.Dsl.Meta.Types                      as MetaTypes
import qualified Hydra.Dsl.Typing                     as Typing
import qualified Hydra.Dsl.Util                       as Util
import qualified Hydra.Dsl.Meta.Variants                   as Variants
import qualified Hydra.Dsl.Prims                           as Prims
import qualified Hydra.Dsl.Meta.Tabular                         as Tabular
import qualified Hydra.Dsl.Terms                           as Terms
import qualified Hydra.Dsl.Tests                           as Tests
import qualified Hydra.Dsl.Types                           as Types
import qualified Hydra.Sources.Decode.Core                 as DecodeCore
import qualified Hydra.Sources.Encode.Core                 as EncodeCore
import qualified Hydra.Sources.Kernel.Terms.Adapt           as Adapt
import qualified Hydra.Sources.Kernel.Terms.All            as KernelTerms
import qualified Hydra.Sources.Kernel.Terms.Annotations    as Annotations
import qualified Hydra.Sources.Kernel.Terms.Arity          as Arity
import qualified Hydra.Sources.Kernel.Terms.Checking       as Checking
import qualified Hydra.Sources.Kernel.Terms.Constants      as Constants
import qualified Hydra.Sources.Kernel.Terms.Extract.Core   as ExtractCore
import qualified Hydra.Sources.Kernel.Terms.Extract.Util   as ExtractUtil
import qualified Hydra.Sources.Kernel.Terms.Formatting     as Formatting
import qualified Hydra.Sources.Kernel.Terms.Inference      as Inference
import qualified Hydra.Sources.Kernel.Terms.Languages      as Languages
import qualified Hydra.Sources.Kernel.Terms.Lexical        as Lexical
import qualified Hydra.Sources.Kernel.Terms.Literals       as Literals
import qualified Hydra.Sources.Kernel.Terms.Names          as Names
import qualified Hydra.Sources.Kernel.Terms.Reduction      as Reduction
import qualified Hydra.Sources.Kernel.Terms.Reflect        as Reflect
import qualified Hydra.Sources.Kernel.Terms.Serialization  as Serialization
import qualified Hydra.Sources.Kernel.Terms.Show.Paths as ShowPaths
import qualified Hydra.Sources.Kernel.Terms.Show.Core      as ShowCore
import qualified Hydra.Sources.Kernel.Terms.Show.Graph     as ShowGraph
import qualified Hydra.Sources.Kernel.Terms.Show.Variants  as ShowVariants
import qualified Hydra.Sources.Kernel.Terms.Show.Typing    as ShowTyping
import qualified Hydra.Sources.Kernel.Terms.Sorting        as Sorting
import qualified Hydra.Sources.Kernel.Terms.Substitution   as Substitution
import qualified Hydra.Sources.Kernel.Terms.Templates      as Templates
import qualified Hydra.Sources.Kernel.Terms.Unification    as Unification
import qualified Hydra.Sources.Kernel.Types.All            as KernelTypes
import           Prelude hiding ((++))
import qualified Data.Int                                  as I
import qualified Data.List                                 as L
import qualified Data.Map                                  as M
import qualified Data.Set                                  as S
import qualified Data.Maybe                                as Y

-- Additional imports
import Hydra.Ast
import qualified Hydra.Python.Syntax as Py
import qualified Hydra.Sources.Python.Syntax as PySyntax


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

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

module_ :: Module
module_ = Module {
            moduleNamespace = ns,
            moduleDefinitions = definitions,
            moduleTermDependencies = [Constants.ns, Serialization.ns],
            moduleTypeDependencies = (PySyntax.ns:KernelTypes.kernelTypesNamespaces),
            moduleDescription = Just "Python serializer: converts Python AST to concrete syntax"}
  where
    definitions = [
      toDefinition encodeAnnotatedRhs,
      toDefinition encodeAnnotatedStatement,
      toDefinition encodeAnnotation,
      toDefinition encodeArgs,
      toDefinition encodeAssignment,
      toDefinition encodeAssignmentExpression,
      toDefinition encodeAtom,
      toDefinition encodeAttribute,
      toDefinition encodeAwaitPrimary,
      toDefinition encodeBitwiseAnd,
      toDefinition encodeBitwiseOr,
      toDefinition encodeBitwiseXor,
      toDefinition encodeBlock,
      toDefinition encodeCapturePattern,
      toDefinition encodeCaseBlock,
      toDefinition encodeClassDefinition,
      toDefinition encodeClassPattern,
      toDefinition encodeClosedPattern,
      toDefinition encodeComparison,
      toDefinition encodeCompoundStatement,
      toDefinition encodeConditional,
      toDefinition encodeConjunction,
      toDefinition encodeDecorators,
      toDefinition encodeDict,
      toDefinition encodeDisjunction,
      toDefinition encodeDottedAsName,
      toDefinition encodeDottedName,
      toDefinition encodeDoubleStarredKvpair,
      toDefinition encodeExpression,
      toDefinition encodeFactor,
      toDefinition encodeFunctionDefRaw,
      toDefinition encodeFunctionDefinition,
      toDefinition encodeGroup,
      toDefinition encodeGuard,
      toDefinition encodeImportFrom,
      toDefinition encodeImportFromAsName,
      toDefinition encodeImportFromTargets,
      toDefinition encodeImportName,
      toDefinition encodeImportStatement,
      toDefinition encodeInversion,
      toDefinition encodeKeywordPattern,
      toDefinition encodeKeywordPatterns,
      toDefinition encodeKvpair,
      toDefinition encodeKwarg,
      toDefinition encodeKwargOrDoubleStarred,
      toDefinition encodeKwargOrStarred,
      toDefinition encodeLambda,
      toDefinition encodeLambdaParamNoDefault,
      toDefinition encodeLambdaParameters,
      toDefinition encodeLambdaStarEtc,
      toDefinition encodeList,
      toDefinition encodeMatchStatement,
      toDefinition encodeModule,
      toDefinition encodeName,
      toDefinition encodeNameOrAttribute,
      toDefinition encodeNamedExpression,
      toDefinition encodeNumber,
      toDefinition encodeOrPattern,
      toDefinition encodeParam,
      toDefinition encodeParamNoDefault,
      toDefinition encodeParamNoDefaultParameters,
      toDefinition encodeParameters,
      toDefinition encodePattern,
      toDefinition encodePatternCaptureTarget,
      toDefinition encodePatterns,
      toDefinition encodePosArg,
      toDefinition encodePositionalPatterns,
      toDefinition encodePower,
      toDefinition encodePrimary,
      toDefinition encodePrimaryRhs,
      toDefinition encodePrimaryWithRhs,
      toDefinition encodeRaiseExpression,
      toDefinition encodeRaiseStatement,
      toDefinition encodeRelativeImportPrefix,
      toDefinition encodeReturnStatement,
      toDefinition encodeSet,
      toDefinition encodeShiftExpression,
      toDefinition encodeSimpleStatement,
      toDefinition encodeSimpleTypeParameter,
      toDefinition encodeSingleTarget,
      toDefinition encodeSlice,
      toDefinition encodeSliceOrStarredExpression,
      toDefinition encodeSlices,
      toDefinition encodeStarAtom,
      toDefinition encodeStarExpression,
      toDefinition encodeStarNamedExpression,
      toDefinition encodeStarTarget,
      toDefinition encodeStarredExpression,
      toDefinition encodeStatement,
      toDefinition encodeString,
      toDefinition encodeSubjectExpression,
      toDefinition encodeSum,
      toDefinition encodeTPrimary,
      toDefinition encodeTPrimaryAndName,
      toDefinition encodeTargetWithStarAtom,
      toDefinition encodeTerm_,
      toDefinition encodeTuple,
      toDefinition encodeTypeAlias,
      toDefinition encodeTypeParameter,
      toDefinition encodeTypedAssignment,
      toDefinition encodeUntypedAssignment,
      toDefinition encodeValuePattern,
      toDefinition encodeWhileStatement,
      toDefinition escapePythonString,
      toDefinition pythonFloatLiteralText,
      toDefinition toPythonComments]


encodeAnnotatedRhs :: TTermDefinition (Py.AnnotatedRhs -> Expr)
encodeAnnotatedRhs = def "encodeAnnotatedRhs" $
  doc "Serialize an annotated RHS" $
  lambda "arhs" $
    Serialization.spaceSep @@ list [
      Serialization.cst @@ string "=",
      cases Py._AnnotatedRhs (var "arhs") Nothing [
        Py._AnnotatedRhs_star>>: lambda "ses" $
          Serialization.commaSep @@ Serialization.inlineStyle @@
            Lists.map encodeStarExpression (var "ses"),
        Py._AnnotatedRhs_yield>>: lambda "_" $ Serialization.cst @@ string "yield ..."]]

encodeAnnotatedStatement :: TTermDefinition (Py.AnnotatedStatement -> Expr)
encodeAnnotatedStatement = def "encodeAnnotatedStatement" $
  doc "Serialize an annotated statement (with optional doc comment)" $
  lambda "as_" $ lets [
    "doc_">: project Py._AnnotatedStatement Py._AnnotatedStatement_comment @@ var "as_",
    "stmt">: project Py._AnnotatedStatement Py._AnnotatedStatement_statement @@ var "as_"] $
    Serialization.newlineSep @@ list [
      Serialization.cst @@ (toPythonComments @@ var "doc_"),
      encodeStatement @@ var "stmt"]

encodeAnnotation :: TTermDefinition (Py.Annotation -> Expr)
encodeAnnotation = def "encodeAnnotation" $
  doc "Serialize a type annotation" $
  lambda "ann" $
    Serialization.spaceSep @@ list [
      Serialization.cst @@ string ":",
      encodeExpression @@ (unwrap Py._Annotation @@ var "ann")]

encodeArgs :: TTermDefinition (Py.Args -> Expr)
encodeArgs = def "encodeArgs" $
  doc "Serialize function arguments" $
  lambda "args" $ lets [
    "pos">: project Py._Args Py._Args_positional @@ var "args",
    "ks">: project Py._Args Py._Args_kwargOrStarred @@ var "args",
    "kss">: project Py._Args Py._Args_kwargOrDoubleStarred @@ var "args"] $
    Serialization.commaSep @@ Serialization.inlineStyle @@ Lists.concat (list [
      Lists.map encodePosArg (var "pos"),
      Lists.map encodeKwargOrStarred (var "ks"),
      Lists.map encodeKwargOrDoubleStarred (var "kss")])

encodeAssignment :: TTermDefinition (Py.Assignment -> Expr)
encodeAssignment = def "encodeAssignment" $
  doc "Serialize an assignment" $
  lambda "a" $
    cases Py._Assignment (var "a") Nothing [
      Py._Assignment_typed>>: lambda "t" $ encodeTypedAssignment @@ var "t",
      Py._Assignment_untyped>>: lambda "u" $ encodeUntypedAssignment @@ var "u",
      Py._Assignment_aug>>: lambda "_" $ Serialization.cst @@ string "... += ..."]

encodeAssignmentExpression :: TTermDefinition (Py.AssignmentExpression -> Expr)
encodeAssignmentExpression = def "encodeAssignmentExpression" $
  doc "Serialize an assignment expression (walrus operator)" $
  lambda "ae" $ lets [
    "name">: project Py._AssignmentExpression Py._AssignmentExpression_name @@ var "ae",
    "expr">: project Py._AssignmentExpression Py._AssignmentExpression_expression @@ var "ae"] $
    Serialization.spaceSep @@ list [
      encodeName @@ var "name",
      Serialization.cst @@ string ":=",
      encodeExpression @@ var "expr"]

encodeAtom :: TTermDefinition (Py.Atom -> Expr)
encodeAtom = def "encodeAtom" $
  doc "Serialize a Python atom (literal or basic expression)" $
  lambda "atom" $
    cases Py._Atom (var "atom") Nothing [
      Py._Atom_dict>>: lambda "d" $ encodeDict @@ var "d",
      Py._Atom_dictcomp>>: lambda "_" $ Serialization.cst @@ string "{...}",
      Py._Atom_ellipsis>>: constant $ Serialization.cst @@ string "...",
      Py._Atom_false>>: constant $ Serialization.cst @@ string "False",
      Py._Atom_genexp>>: lambda "_" $ Serialization.cst @@ string "(...)",
      Py._Atom_group>>: lambda "g" $ encodeGroup @@ var "g",
      Py._Atom_list>>: lambda "l" $ encodeList @@ var "l",
      Py._Atom_listcomp>>: lambda "_" $ Serialization.cst @@ string "[...]",
      Py._Atom_name>>: lambda "n" $ encodeName @@ var "n",
      Py._Atom_none>>: constant $ Serialization.cst @@ string "None",
      Py._Atom_number>>: lambda "n" $ encodeNumber @@ var "n",
      Py._Atom_set>>: lambda "s" $ encodeSet @@ var "s",
      Py._Atom_setcomp>>: lambda "_" $ Serialization.cst @@ string "{...}",
      Py._Atom_string>>: lambda "s" $ encodeString @@ var "s",
      Py._Atom_true>>: constant $ Serialization.cst @@ string "True",
      Py._Atom_tuple>>: lambda "t" $ encodeTuple @@ var "t"]

encodeAttribute :: TTermDefinition (Py.Attribute -> Expr)
encodeAttribute = def "encodeAttribute" $
  doc "Serialize an attribute access" $
  lambda "attr" $
    Serialization.dotSep @@ Lists.map encodeName (unwrap Py._Attribute @@ var "attr")

encodeAwaitPrimary :: TTermDefinition (Py.AwaitPrimary -> Expr)
encodeAwaitPrimary = def "encodeAwaitPrimary" $
  doc "Serialize an await primary expression" $
  lambda "ap" $ lets [
    "await_">: project Py._AwaitPrimary Py._AwaitPrimary_await @@ var "ap",
    "primary">: project Py._AwaitPrimary Py._AwaitPrimary_primary @@ var "ap"] $
    Logic.ifElse (var "await_")
      (Serialization.spaceSep @@ list [Serialization.cst @@ string "await", encodePrimary @@ var "primary"])
      (encodePrimary @@ var "primary")

encodeBitwiseAnd :: TTermDefinition (Py.BitwiseAnd -> Expr)
encodeBitwiseAnd = def "encodeBitwiseAnd" $
  doc "Serialize a bitwise AND expression" $
  lambda "band" $ lets [
    "lhs">: project Py._BitwiseAnd Py._BitwiseAnd_lhs @@ var "band",
    "rhs">: project Py._BitwiseAnd Py._BitwiseAnd_rhs @@ var "band"] $
    Serialization.spaceSep @@ Maybes.cat (list [
      Maybes.map (lambda "l" $
        Serialization.spaceSep @@ list [encodeBitwiseAnd @@ var "l", Serialization.cst @@ string "&"])
        (var "lhs"),
      just $ encodeShiftExpression @@ var "rhs"])

encodeBitwiseOr :: TTermDefinition (Py.BitwiseOr -> Expr)
encodeBitwiseOr = def "encodeBitwiseOr" $
  doc "Serialize a bitwise OR expression" $
  lambda "bor" $ lets [
    "lhs">: project Py._BitwiseOr Py._BitwiseOr_lhs @@ var "bor",
    "rhs">: project Py._BitwiseOr Py._BitwiseOr_rhs @@ var "bor"] $
    Serialization.spaceSep @@ Maybes.cat (list [
      Maybes.map (lambda "l" $
        Serialization.spaceSep @@ list [encodeBitwiseOr @@ var "l", Serialization.cst @@ string "|"])
        (var "lhs"),
      just $ encodeBitwiseXor @@ var "rhs"])

encodeBitwiseXor :: TTermDefinition (Py.BitwiseXor -> Expr)
encodeBitwiseXor = def "encodeBitwiseXor" $
  doc "Serialize a bitwise XOR expression" $
  lambda "bxor" $ lets [
    "lhs">: project Py._BitwiseXor Py._BitwiseXor_lhs @@ var "bxor",
    "rhs">: project Py._BitwiseXor Py._BitwiseXor_rhs @@ var "bxor"] $
    Serialization.spaceSep @@ Maybes.cat (list [
      Maybes.map (lambda "l" $
        Serialization.spaceSep @@ list [encodeBitwiseXor @@ var "l", Serialization.cst @@ string "^"])
        (var "lhs"),
      just $ encodeBitwiseAnd @@ var "rhs"])

encodeBlock :: TTermDefinition (Py.Block -> Expr)
encodeBlock = def "encodeBlock" $
  doc "Serialize a block" $
  lambda "b" $
    cases Py._Block (var "b") Nothing [
      Py._Block_indented>>: lambda "groups" $
        Serialization.tabIndentDoubleSpace @@ Lists.map
          (lambda "stmts" $ Serialization.newlineSep @@ Lists.map encodeStatement (var "stmts"))
          (var "groups"),
      Py._Block_simple>>: lambda "ss" $
        Serialization.semicolonSep @@ Lists.map encodeSimpleStatement (var "ss")]

encodeCapturePattern :: TTermDefinition (Py.CapturePattern -> Expr)
encodeCapturePattern = def "encodeCapturePattern" $
  doc "Serialize a capture pattern" $
  lambda "cp" $
    encodePatternCaptureTarget @@ (unwrap Py._CapturePattern @@ var "cp")

encodeCaseBlock :: TTermDefinition (Py.CaseBlock -> Expr)
encodeCaseBlock = def "encodeCaseBlock" $
  doc "Serialize a case block" $
  lambda "cb" $ lets [
    "patterns">: project Py._CaseBlock Py._CaseBlock_patterns @@ var "cb",
    "guard">: project Py._CaseBlock Py._CaseBlock_guard @@ var "cb",
    "body">: project Py._CaseBlock Py._CaseBlock_body @@ var "cb"] $
    Serialization.newlineSep @@ list [
      Serialization.noSep @@ list [
        Serialization.spaceSep @@ Maybes.cat (list [
          just $ Serialization.cst @@ string "case",
          just $ encodePatterns @@ var "patterns",
          Maybes.map encodeGuard (var "guard")]),
        Serialization.cst @@ string ":"],
      encodeBlock @@ var "body"]

encodeClassDefinition :: TTermDefinition (Py.ClassDefinition -> Expr)
encodeClassDefinition = def "encodeClassDefinition" $
  doc "Serialize a class definition" $
  lambda "cd" $ lets [
    "decs">: project Py._ClassDefinition Py._ClassDefinition_decorators @@ var "cd",
    "name">: project Py._ClassDefinition Py._ClassDefinition_name @@ var "cd",
    "args">: project Py._ClassDefinition Py._ClassDefinition_arguments @@ var "cd",
    "body">: project Py._ClassDefinition Py._ClassDefinition_body @@ var "cd",
    "argPart">: Maybes.map (lambda "a" $ Serialization.noSep @@ list [
      Serialization.cst @@ string "(",
      encodeArgs @@ var "a",
      Serialization.cst @@ string ")"]) (var "args")] $
    Serialization.newlineSep @@ Maybes.cat (list [
      Maybes.map encodeDecorators (var "decs"),
      just $ Serialization.noSep @@ Maybes.cat (list [
        just $ Serialization.spaceSep @@ list [Serialization.cst @@ string "class", encodeName @@ var "name"],
        var "argPart",
        just $ Serialization.cst @@ string ":"]),
      just $ encodeBlock @@ var "body"])

encodeClassPattern :: TTermDefinition (Py.ClassPattern -> Expr)
encodeClassPattern = def "encodeClassPattern" $
  doc "Serialize a class pattern" $
  lambda "cp" $ lets [
    "noa">: project Py._ClassPattern Py._ClassPattern_nameOrAttribute @@ var "cp",
    "pos">: project Py._ClassPattern Py._ClassPattern_positionalPatterns @@ var "cp",
    "kw">: project Py._ClassPattern Py._ClassPattern_keywordPatterns @@ var "cp"] $
    Serialization.noSep @@ Maybes.cat (list [
      just $ encodeNameOrAttribute @@ var "noa",
      just $ Serialization.cst @@ string "(",
      Maybes.map encodePositionalPatterns (var "pos"),
      Maybes.map encodeKeywordPatterns (var "kw"),
      just $ Serialization.cst @@ string ")"])

encodeClosedPattern :: TTermDefinition (Py.ClosedPattern -> Expr)
encodeClosedPattern = def "encodeClosedPattern" $
  doc "Serialize a closed pattern" $
  lambda "cp" $
    cases Py._ClosedPattern (var "cp") Nothing [
      Py._ClosedPattern_literal>>: lambda "_" $ Serialization.cst @@ string "...",
      Py._ClosedPattern_capture>>: lambda "c" $ encodeCapturePattern @@ var "c",
      Py._ClosedPattern_wildcard>>: constant $ Serialization.cst @@ string "_",
      Py._ClosedPattern_value>>: lambda "v" $ encodeValuePattern @@ var "v",
      Py._ClosedPattern_group>>: lambda "_" $ Serialization.cst @@ string "(...)",
      Py._ClosedPattern_sequence>>: lambda "_" $ Serialization.cst @@ string "[...]",
      Py._ClosedPattern_mapping>>: lambda "_" $ Serialization.cst @@ string "{...}",
      Py._ClosedPattern_class>>: lambda "c" $ encodeClassPattern @@ var "c"]

encodeComparison :: TTermDefinition (Py.Comparison -> Expr)
encodeComparison = def "encodeComparison" $
  doc "Serialize a comparison expression" $
  lambda "cmp" $
    -- For now, just encode the LHS bitwise or; comparison operators are rarely used in generated code
    encodeBitwiseOr @@ (project Py._Comparison Py._Comparison_lhs @@ var "cmp")

encodeCompoundStatement :: TTermDefinition (Py.CompoundStatement -> Expr)
encodeCompoundStatement = def "encodeCompoundStatement" $
  doc "Serialize a compound (multi-line) Python statement" $
  lambda "cs" $
    cases Py._CompoundStatement (var "cs") Nothing [
      Py._CompoundStatement_function>>: lambda "f" $ encodeFunctionDefinition @@ var "f",
      Py._CompoundStatement_if>>: lambda "_" $ Serialization.cst @@ string "if ...",
      Py._CompoundStatement_classDef>>: lambda "c" $ encodeClassDefinition @@ var "c",
      Py._CompoundStatement_with>>: lambda "_" $ Serialization.cst @@ string "with ...",
      Py._CompoundStatement_for>>: lambda "_" $ Serialization.cst @@ string "for ...",
      Py._CompoundStatement_try>>: lambda "_" $ Serialization.cst @@ string "try ...",
      Py._CompoundStatement_while>>: lambda "w" $ encodeWhileStatement @@ var "w",
      Py._CompoundStatement_match>>: lambda "m" $ encodeMatchStatement @@ var "m"]

-- | Serialize a conditional expression: body if condition else elseExpr
encodeConditional :: TTermDefinition (Py.Conditional -> Expr)
encodeConditional = def "encodeConditional" $
  doc "Serialize a conditional expression (ternary)" $
  lambda "c" $ lets [
    "body">: project Py._Conditional Py._Conditional_body @@ var "c",
    "cond">: project Py._Conditional Py._Conditional_if @@ var "c",
    "elseExpr">: project Py._Conditional Py._Conditional_else @@ var "c"] $
    Serialization.spaceSep @@ list [
      encodeDisjunction @@ var "body",
      Serialization.cst @@ string "if",
      encodeDisjunction @@ var "cond",
      Serialization.cst @@ string "else",
      encodeExpression @@ var "elseExpr"]

encodeConjunction :: TTermDefinition (Py.Conjunction -> Expr)
encodeConjunction = def "encodeConjunction" $
  doc "Serialize a conjunction (and expression)" $
  lambda "c" $
    Serialization.symbolSep @@ string "and" @@ Serialization.inlineStyle @@
      Lists.map encodeInversion (unwrap Py._Conjunction @@ var "c")

encodeDecorators :: TTermDefinition (Py.Decorators -> Expr)
encodeDecorators = def "encodeDecorators" $
  doc "Serialize decorators" $
  lambda "decs" $
    Serialization.newlineSep @@ Lists.map
      (lambda "ne" $ Serialization.noSep @@ list [Serialization.cst @@ string "@", encodeNamedExpression @@ var "ne"])
      (unwrap Py._Decorators @@ var "decs")

encodeDict :: TTermDefinition (Py.Dict -> Expr)
encodeDict = def "encodeDict" $
  doc "Serialize a Python dictionary" $
  lambda "d" $
    Serialization.curlyBracesList @@ nothing @@ Serialization.halfBlockStyle @@
      Lists.map encodeDoubleStarredKvpair (unwrap Py._Dict @@ var "d")

encodeDisjunction :: TTermDefinition (Py.Disjunction -> Expr)
encodeDisjunction = def "encodeDisjunction" $
  doc "Serialize a disjunction (or expression)" $
  lambda "d" $
    Serialization.symbolSep @@ string "or" @@ Serialization.inlineStyle @@
      Lists.map encodeConjunction (unwrap Py._Disjunction @@ var "d")

encodeDottedAsName :: TTermDefinition (Py.DottedAsName -> Expr)
encodeDottedAsName = def "encodeDottedAsName" $
  doc "Serialize a dotted as name" $
  lambda "dan" $ lets [
    "name">: project Py._DottedAsName Py._DottedAsName_name @@ var "dan",
    "alias">: project Py._DottedAsName Py._DottedAsName_as @@ var "dan"] $
    Serialization.spaceSep @@ Maybes.cat (list [
      just $ encodeDottedName @@ var "name",
      Maybes.map (lambda "a" $ Serialization.spaceSep @@ list [Serialization.cst @@ string "as", encodeName @@ var "a"]) (var "alias")])

encodeDottedName :: TTermDefinition (Py.DottedName -> Expr)
encodeDottedName = def "encodeDottedName" $
  doc "Serialize a dotted name (e.g., module.submodule)" $
  lambda "dn" $
    Serialization.cst @@ Strings.intercalate (string ".") (Lists.map (lambda "n" $ unwrap Py._Name @@ var "n") (unwrap Py._DottedName @@ var "dn"))

encodeDoubleStarredKvpair :: TTermDefinition (Py.DoubleStarredKvpair -> Expr)
encodeDoubleStarredKvpair = def "encodeDoubleStarredKvpair" $
  doc "Serialize a double-starred key-value pair" $
  lambda "dskv" $
    cases Py._DoubleStarredKvpair (var "dskv") Nothing [
      Py._DoubleStarredKvpair_pair>>: lambda "p" $ encodeKvpair @@ var "p",
      Py._DoubleStarredKvpair_starred>>: lambda "e" $
        Serialization.noSep @@ list [Serialization.cst @@ string "**", encodeBitwiseOr @@ var "e"]]

encodeExpression :: TTermDefinition (Py.Expression -> Expr)
encodeExpression = def "encodeExpression" $
  doc "Serialize a Python expression" $
  lambda "expr" $
    cases Py._Expression (var "expr") Nothing [
      Py._Expression_simple>>: lambda "d" $ encodeDisjunction @@ var "d",
      Py._Expression_conditional>>: lambda "c" $ encodeConditional @@ var "c",
      Py._Expression_lambda>>: lambda "l" $ encodeLambda @@ var "l"]

encodeFactor :: TTermDefinition (Py.Factor -> Expr)
encodeFactor = def "encodeFactor" $
  doc "Serialize a factor expression" $
  lambda "f" $
    cases Py._Factor (var "f") Nothing [
      Py._Factor_positive>>: lambda "inner" $
        Serialization.noSep @@ list [Serialization.cst @@ string "+", encodeFactor @@ var "inner"],
      Py._Factor_negative>>: lambda "inner" $
        Serialization.noSep @@ list [Serialization.cst @@ string "-", encodeFactor @@ var "inner"],
      Py._Factor_complement>>: lambda "inner" $
        Serialization.noSep @@ list [Serialization.cst @@ string "~", encodeFactor @@ var "inner"],
      Py._Factor_simple>>: lambda "p" $ encodePower @@ var "p"]

encodeFunctionDefRaw :: TTermDefinition (Py.FunctionDefRaw -> Expr)
encodeFunctionDefRaw = def "encodeFunctionDefRaw" $
  doc "Serialize a raw function definition" $
  lambda "fdr" $ lets [
    "async_">: project Py._FunctionDefRaw Py._FunctionDefRaw_async @@ var "fdr",
    "name">: project Py._FunctionDefRaw Py._FunctionDefRaw_name @@ var "fdr",
    "tparams">: project Py._FunctionDefRaw Py._FunctionDefRaw_typeParams @@ var "fdr",
    "params">: project Py._FunctionDefRaw Py._FunctionDefRaw_params @@ var "fdr",
    "retType">: project Py._FunctionDefRaw Py._FunctionDefRaw_returnType @@ var "fdr",
    "block">: project Py._FunctionDefRaw Py._FunctionDefRaw_block @@ var "fdr",
    "asyncKw">: Logic.ifElse (var "async_") (just $ Serialization.cst @@ string "async") nothing,
    "tparamPart">: Logic.ifElse (Lists.null (var "tparams"))
      nothing
      (just $ Serialization.bracketList @@ Serialization.inlineStyle @@ Lists.map encodeTypeParameter (var "tparams")),
    "paramPart">: Maybes.map encodeParameters (var "params"),
    "retPart">: Maybes.map (lambda "t" $ Serialization.spaceSep @@ list [Serialization.cst @@ string "->", encodeExpression @@ var "t"]) (var "retType")] $
    Serialization.newlineSep @@ list [
      Serialization.noSep @@ list [
        Serialization.spaceSep @@ Maybes.cat (list [
          var "asyncKw",
          just $ Serialization.cst @@ string "def",
          just $ Serialization.noSep @@ Maybes.cat (list [
            just $ encodeName @@ var "name",
            var "tparamPart",
            just $ Serialization.cst @@ string "(",
            var "paramPart",
            just $ Serialization.cst @@ string ")"]),
          var "retPart"]),
        Serialization.cst @@ string ":"],
      encodeBlock @@ var "block"]

encodeFunctionDefinition :: TTermDefinition (Py.FunctionDefinition -> Expr)
encodeFunctionDefinition = def "encodeFunctionDefinition" $
  doc "Serialize a function definition" $
  lambda "fd" $ lets [
    "decs">: project Py._FunctionDefinition Py._FunctionDefinition_decorators @@ var "fd",
    "raw">: project Py._FunctionDefinition Py._FunctionDefinition_raw @@ var "fd"] $
    Serialization.newlineSep @@ Maybes.cat (list [
      Maybes.map encodeDecorators (var "decs"),
      just $ encodeFunctionDefRaw @@ var "raw"])

encodeGroup :: TTermDefinition (Py.Group -> Expr)
encodeGroup = def "encodeGroup" $
  doc "Serialize a parenthesized group" $
  lambda "g" $
    cases Py._Group (var "g") Nothing [
      Py._Group_expression>>: lambda "ne" $ encodeNamedExpression @@ var "ne",
      Py._Group_yield>>: lambda "_" $ Serialization.cst @@ string "(yield ...)"]

encodeGuard :: TTermDefinition (Py.Guard -> Expr)
encodeGuard = def "encodeGuard" $
  doc "Serialize a guard clause" $
  lambda "g" $
    Serialization.spaceSep @@ list [
      Serialization.cst @@ string "if",
      encodeNamedExpression @@ (unwrap Py._Guard @@ var "g")]

encodeImportFrom :: TTermDefinition (Py.ImportFrom -> Expr)
encodeImportFrom = def "encodeImportFrom" $
  doc "Serialize an import from statement" $
  lambda "if_" $ lets [
    "prefixes">: project Py._ImportFrom Py._ImportFrom_prefixes @@ var "if_",
    "name">: project Py._ImportFrom Py._ImportFrom_dottedName @@ var "if_",
    "targets">: project Py._ImportFrom Py._ImportFrom_targets @@ var "if_",
    "lhs">: Serialization.noSep @@ Maybes.cat (
      Lists.concat (list [
        Lists.map (lambda "p" $ just $ encodeRelativeImportPrefix @@ var "p") (var "prefixes"),
        list [Maybes.map encodeDottedName (var "name")]]))] $
    Serialization.spaceSep @@ list [
      Serialization.cst @@ string "from",
      var "lhs",
      Serialization.cst @@ string "import",
      encodeImportFromTargets @@ var "targets"]

encodeImportFromAsName :: TTermDefinition (Py.ImportFromAsName -> Expr)
encodeImportFromAsName = def "encodeImportFromAsName" $
  doc "Serialize an import from as name" $
  lambda "ifan" $ lets [
    "name">: project Py._ImportFromAsName Py._ImportFromAsName_name @@ var "ifan",
    "alias">: project Py._ImportFromAsName Py._ImportFromAsName_as @@ var "ifan"] $
    Maybes.maybe
      (encodeName @@ var "name")
      (lambda "a" $ Serialization.spaceSep @@ list [
        encodeName @@ var "name",
        Serialization.cst @@ string "as",
        encodeName @@ var "a"])
      (var "alias")

encodeImportFromTargets :: TTermDefinition (Py.ImportFromTargets -> Expr)
encodeImportFromTargets = def "encodeImportFromTargets" $
  doc "Serialize import from targets" $
  lambda "t" $
    cases Py._ImportFromTargets (var "t") Nothing [
      Py._ImportFromTargets_simple>>: lambda "names" $
        Serialization.commaSep @@ Serialization.inlineStyle @@
          Lists.map encodeImportFromAsName (var "names"),
      Py._ImportFromTargets_parens>>: lambda "names" $
        Serialization.noSep @@ list [
          Serialization.cst @@ string "(",
          Serialization.commaSep @@ Serialization.inlineStyle @@
            Lists.map encodeImportFromAsName (var "names"),
          Serialization.cst @@ string ")"],
      Py._ImportFromTargets_star>>: constant $ Serialization.cst @@ string "*"]

encodeImportName :: TTermDefinition (Py.ImportName -> Expr)
encodeImportName = def "encodeImportName" $
  doc "Serialize an import name" $
  lambda "in_" $
    Serialization.spaceSep @@ list [
      Serialization.cst @@ string "import",
      Serialization.commaSep @@ Serialization.inlineStyle @@
        Lists.map encodeDottedAsName (unwrap Py._ImportName @@ var "in_")]

encodeImportStatement :: TTermDefinition (Py.ImportStatement -> Expr)
encodeImportStatement = def "encodeImportStatement" $
  doc "Serialize an import statement" $
  lambda "is_" $
    cases Py._ImportStatement (var "is_") Nothing [
      Py._ImportStatement_name>>: lambda "n" $ encodeImportName @@ var "n",
      Py._ImportStatement_from>>: lambda "f" $ encodeImportFrom @@ var "f"]

encodeInversion :: TTermDefinition (Py.Inversion -> Expr)
encodeInversion = def "encodeInversion" $
  doc "Serialize an inversion (not expression)" $
  lambda "i" $
    cases Py._Inversion (var "i") Nothing [
      Py._Inversion_not>>: lambda "other" $
        Serialization.spaceSep @@ list [Serialization.cst @@ string "not", encodeInversion @@ var "other"],
      Py._Inversion_simple>>: lambda "c" $ encodeComparison @@ var "c"]

encodeKeywordPattern :: TTermDefinition (Py.KeywordPattern -> Expr)
encodeKeywordPattern = def "encodeKeywordPattern" $
  doc "Serialize a keyword pattern" $
  lambda "kp" $ lets [
    "name">: project Py._KeywordPattern Py._KeywordPattern_name @@ var "kp",
    "pat">: project Py._KeywordPattern Py._KeywordPattern_pattern @@ var "kp"] $
    Serialization.noSep @@ list [
      encodeName @@ var "name",
      Serialization.cst @@ string "=",
      encodePattern @@ var "pat"]

encodeKeywordPatterns :: TTermDefinition (Py.KeywordPatterns -> Expr)
encodeKeywordPatterns = def "encodeKeywordPatterns" $
  doc "Serialize keyword patterns" $
  lambda "kp" $
    Serialization.commaSep @@ Serialization.inlineStyle @@
      Lists.map encodeKeywordPattern (unwrap Py._KeywordPatterns @@ var "kp")

encodeKvpair :: TTermDefinition (Py.Kvpair -> Expr)
encodeKvpair = def "encodeKvpair" $
  doc "Serialize a key-value pair" $
  lambda "kv" $ lets [
    "k">: project Py._Kvpair Py._Kvpair_key @@ var "kv",
    "v">: project Py._Kvpair Py._Kvpair_value @@ var "kv"] $
    Serialization.spaceSep @@ list [
      Serialization.noSep @@ list [encodeExpression @@ var "k", Serialization.cst @@ string ":"],
      encodeExpression @@ var "v"]

encodeKwarg :: TTermDefinition (Py.Kwarg -> Expr)
encodeKwarg = def "encodeKwarg" $
  doc "Serialize a keyword argument" $
  lambda "k" $ lets [
    "name">: project Py._Kwarg Py._Kwarg_name @@ var "k",
    "expr">: project Py._Kwarg Py._Kwarg_value @@ var "k"] $
    Serialization.noSep @@ list [
      encodeName @@ var "name",
      Serialization.cst @@ string "=",
      encodeExpression @@ var "expr"]

encodeKwargOrDoubleStarred :: TTermDefinition (Py.KwargOrDoubleStarred -> Expr)
encodeKwargOrDoubleStarred = def "encodeKwargOrDoubleStarred" $
  doc "Serialize a kwarg or double starred" $
  lambda "kds" $
    cases Py._KwargOrDoubleStarred (var "kds") Nothing [
      Py._KwargOrDoubleStarred_kwarg>>: lambda "k" $ encodeKwarg @@ var "k",
      Py._KwargOrDoubleStarred_doubleStarred>>: lambda "e" $
        Serialization.noSep @@ list [Serialization.cst @@ string "**", encodeExpression @@ var "e"]]

encodeKwargOrStarred :: TTermDefinition (Py.KwargOrStarred -> Expr)
encodeKwargOrStarred = def "encodeKwargOrStarred" $
  doc "Serialize a kwarg or starred" $
  lambda "ks" $
    cases Py._KwargOrStarred (var "ks") Nothing [
      Py._KwargOrStarred_kwarg>>: lambda "k" $ encodeKwarg @@ var "k",
      Py._KwargOrStarred_starred>>: lambda "se" $ encodeStarredExpression @@ var "se"]

encodeLambda :: TTermDefinition (Py.Lambda -> Expr)
encodeLambda = def "encodeLambda" $
  doc "Serialize a lambda expression" $
  lambda "l" $ lets [
    "params">: project Py._Lambda Py._Lambda_params @@ var "l",
    "body">: project Py._Lambda Py._Lambda_body @@ var "l"] $
    Serialization.parens @@ (Serialization.spaceSep @@ list [
      Serialization.cst @@ string "lambda",
      Serialization.noSep @@ list [encodeLambdaParameters @@ var "params", Serialization.cst @@ string ":"],
      encodeExpression @@ var "body"])

encodeLambdaParamNoDefault :: TTermDefinition (Py.LambdaParamNoDefault -> Expr)
encodeLambdaParamNoDefault = def "encodeLambdaParamNoDefault" $
  doc "Serialize a lambda parameter without default" $
  lambda "p" $
    encodeName @@ (unwrap Py._LambdaParamNoDefault @@ var "p")

encodeLambdaParameters :: TTermDefinition (Py.LambdaParameters -> Expr)
encodeLambdaParameters = def "encodeLambdaParameters" $
  doc "Serialize lambda parameters" $
  lambda "lp" $ lets [
    "nodef">: project Py._LambdaParameters Py._LambdaParameters_paramNoDefault @@ var "lp"] $
    Serialization.commaSep @@ Serialization.inlineStyle @@
      Lists.map encodeLambdaParamNoDefault (var "nodef")

encodeLambdaStarEtc :: TTermDefinition (Py.LambdaStarEtc -> Expr)
encodeLambdaStarEtc = def "encodeLambdaStarEtc" $
  doc "Serialize lambda star etc" $
  lambda "lse" $
    cases Py._LambdaStarEtc (var "lse") Nothing [
      Py._LambdaStarEtc_paramNoDefault>>: lambda "p" $ encodeLambdaParamNoDefault @@ var "p",
      Py._LambdaStarEtc_star>>: lambda "_" $ Serialization.cst @@ string "*...",
      Py._LambdaStarEtc_paramMaybeDefault>>: lambda "_" $ Serialization.cst @@ string "...",
      Py._LambdaStarEtc_kwds>>: lambda "_" $ Serialization.cst @@ string "**..."]

encodeList :: TTermDefinition (Py.List -> Expr)
encodeList = def "encodeList" $
  doc "Serialize a Python list" $
  lambda "l" $
    Serialization.bracketListAdaptive @@ Lists.map encodeStarNamedExpression (unwrap Py._List @@ var "l")

encodeMatchStatement :: TTermDefinition (Py.MatchStatement -> Expr)
encodeMatchStatement = def "encodeMatchStatement" $
  doc "Serialize a match statement" $
  lambda "ms" $ lets [
    "subj">: project Py._MatchStatement Py._MatchStatement_subject @@ var "ms",
    "cases">: project Py._MatchStatement Py._MatchStatement_cases @@ var "ms"] $
    Serialization.newlineSep @@ list [
      Serialization.spaceSep @@ list [
        Serialization.cst @@ string "match",
        Serialization.noSep @@ list [encodeSubjectExpression @@ var "subj", Serialization.cst @@ string ":"]],
      Serialization.tabIndentDoubleSpace @@ Lists.map encodeCaseBlock (var "cases")]

encodeModule :: TTermDefinition (Py.Module -> Expr)
encodeModule = def "encodeModule" $
  doc "Serialize a Python module to an AST expression" $
  lambda "mod" $ lets [
    "warning">: Serialization.cst @@ (toPythonComments @@ Constants.warningAutoGeneratedFile),
    "groups">: Lists.map
      (lambda "group" $ Serialization.newlineSep @@ Lists.map encodeStatement (var "group"))
      (unwrap Py._Module @@ var "mod")] $
    Serialization.doubleNewlineSep @@ Lists.cons (var "warning") (var "groups")

encodeName :: TTermDefinition (Py.Name -> Expr)
encodeName = def "encodeName" $
  doc "Serialize a Python name/identifier" $
  lambda "n" $
    Serialization.cst @@ (unwrap Py._Name @@ var "n")

encodeNameOrAttribute :: TTermDefinition (Py.NameOrAttribute -> Expr)
encodeNameOrAttribute = def "encodeNameOrAttribute" $
  doc "Serialize a name or attribute" $
  lambda "noa" $
    Serialization.dotSep @@ Lists.map encodeName (unwrap Py._NameOrAttribute @@ var "noa")

encodeNamedExpression :: TTermDefinition (Py.NamedExpression -> Expr)
encodeNamedExpression = def "encodeNamedExpression" $
  doc "Serialize a named expression" $
  lambda "ne" $
    cases Py._NamedExpression (var "ne") Nothing [
      Py._NamedExpression_simple>>: lambda "e" $ encodeExpression @@ var "e",
      Py._NamedExpression_assignment>>: lambda "ae" $ encodeAssignmentExpression @@ var "ae"]

encodeNumber :: TTermDefinition (Py.Number -> Expr)
encodeNumber = def "encodeNumber" $
  doc "Serialize a Python number literal" $
  lambda "num" $
    cases Py._Number (var "num") Nothing [
      Py._Number_float>>: lambda "f" $
        Serialization.cst @@ (pythonFloatLiteralText @@ Literals.showBigfloat (var "f")),
      Py._Number_integer>>: lambda "i" $ Serialization.cst @@ Literals.showBigint (var "i")]

encodeOrPattern :: TTermDefinition (Py.OrPattern -> Expr)
encodeOrPattern = def "encodeOrPattern" $
  doc "Serialize an or pattern" $
  lambda "op" $
    Serialization.symbolSep @@ string "|" @@ Serialization.inlineStyle @@
      Lists.map encodeClosedPattern (unwrap Py._OrPattern @@ var "op")

encodeParam :: TTermDefinition (Py.Param -> Expr)
encodeParam = def "encodeParam" $
  doc "Serialize a parameter" $
  lambda "p" $ lets [
    "name">: project Py._Param Py._Param_name @@ var "p",
    "ann">: project Py._Param Py._Param_annotation @@ var "p"] $
    Serialization.noSep @@ Maybes.cat (list [
      just $ encodeName @@ var "name",
      Maybes.map encodeAnnotation (var "ann")])

encodeParamNoDefault :: TTermDefinition (Py.ParamNoDefault -> Expr)
encodeParamNoDefault = def "encodeParamNoDefault" $
  doc "Serialize a parameter without default" $
  lambda "pnd" $
    encodeParam @@ (project Py._ParamNoDefault Py._ParamNoDefault_param @@ var "pnd")

encodeParamNoDefaultParameters :: TTermDefinition (Py.ParamNoDefaultParameters -> Expr)
encodeParamNoDefaultParameters = def "encodeParamNoDefaultParameters" $
  doc "Serialize parameters without defaults" $
  lambda "pndp" $ lets [
    "nodef">: project Py._ParamNoDefaultParameters Py._ParamNoDefaultParameters_paramNoDefault @@ var "pndp"] $
    Serialization.commaSep @@ Serialization.inlineStyle @@
      Lists.map encodeParamNoDefault (var "nodef")

encodeParameters :: TTermDefinition (Py.Parameters -> Expr)
encodeParameters = def "encodeParameters" $
  doc "Serialize function parameters" $
  lambda "p" $
    cases Py._Parameters (var "p") Nothing [
      Py._Parameters_paramNoDefault>>: lambda "pnd" $ encodeParamNoDefaultParameters @@ var "pnd",
      Py._Parameters_slashNoDefault>>: lambda "_" $ Serialization.cst @@ string "...",
      Py._Parameters_slashWithDefault>>: lambda "_" $ Serialization.cst @@ string "..."]

encodePattern :: TTermDefinition (Py.Pattern -> Expr)
encodePattern = def "encodePattern" $
  doc "Serialize a pattern" $
  lambda "p" $
    cases Py._Pattern (var "p") Nothing [
      Py._Pattern_or>>: lambda "op" $ encodeOrPattern @@ var "op",
      Py._Pattern_as>>: lambda "_" $ Serialization.cst @@ string "... as ..."]

encodePatternCaptureTarget :: TTermDefinition (Py.PatternCaptureTarget -> Expr)
encodePatternCaptureTarget = def "encodePatternCaptureTarget" $
  doc "Serialize a pattern capture target" $
  lambda "pct" $
    encodeName @@ (unwrap Py._PatternCaptureTarget @@ var "pct")

encodePatterns :: TTermDefinition (Py.Patterns -> Expr)
encodePatterns = def "encodePatterns" $
  doc "Serialize patterns" $
  lambda "ps" $
    cases Py._Patterns (var "ps") Nothing [
      Py._Patterns_pattern>>: lambda "p" $ encodePattern @@ var "p",
      Py._Patterns_sequence>>: lambda "_" $ Serialization.cst @@ string "..."]

encodePosArg :: TTermDefinition (Py.PosArg -> Expr)
encodePosArg = def "encodePosArg" $
  doc "Serialize a positional argument" $
  lambda "pa" $
    cases Py._PosArg (var "pa") Nothing [
      Py._PosArg_starred>>: lambda "se" $ encodeStarredExpression @@ var "se",
      Py._PosArg_assignment>>: lambda "ae" $ encodeAssignmentExpression @@ var "ae",
      Py._PosArg_expression>>: lambda "e" $ encodeExpression @@ var "e"]

encodePositionalPatterns :: TTermDefinition (Py.PositionalPatterns -> Expr)
encodePositionalPatterns = def "encodePositionalPatterns" $
  doc "Serialize positional patterns" $
  lambda "pp" $
    Serialization.commaSep @@ Serialization.inlineStyle @@
      Lists.map encodePattern (unwrap Py._PositionalPatterns @@ var "pp")

encodePower :: TTermDefinition (Py.Power -> Expr)
encodePower = def "encodePower" $
  doc "Serialize a power expression" $
  lambda "p" $ lets [
    "lhs">: project Py._Power Py._Power_lhs @@ var "p",
    "rhs">: project Py._Power Py._Power_rhs @@ var "p"] $
    Serialization.spaceSep @@ Maybes.cat (list [
      just $ encodeAwaitPrimary @@ var "lhs",
      Maybes.map (lambda "r" $
        Serialization.spaceSep @@ list [Serialization.cst @@ string "**", encodeFactor @@ var "r"])
        (var "rhs")])

encodePrimary :: TTermDefinition (Py.Primary -> Expr)
encodePrimary = def "encodePrimary" $
  doc "Serialize a primary expression" $
  lambda "p" $
    cases Py._Primary (var "p") Nothing [
      Py._Primary_simple>>: lambda "a" $ encodeAtom @@ var "a",
      Py._Primary_compound>>: lambda "pwr" $ encodePrimaryWithRhs @@ var "pwr"]

encodePrimaryRhs :: TTermDefinition (Py.PrimaryRhs -> Expr)
encodePrimaryRhs = def "encodePrimaryRhs" $
  doc "Serialize a primary RHS" $
  lambda "rhs" $
    cases Py._PrimaryRhs (var "rhs") Nothing [
      Py._PrimaryRhs_call>>: lambda "args" $
        Serialization.noSep @@ list [Serialization.cst @@ string "(", encodeArgs @@ var "args", Serialization.cst @@ string ")"],
      Py._PrimaryRhs_project>>: lambda "name" $
        Serialization.noSep @@ list [Serialization.cst @@ string ".", encodeName @@ var "name"],
      Py._PrimaryRhs_slices>>: lambda "slices" $
        Serialization.noSep @@ list [Serialization.cst @@ string "[", encodeSlices @@ var "slices", Serialization.cst @@ string "]"],
      Py._PrimaryRhs_genexp>>: lambda "_" $ Serialization.cst @@ string "[...]"]

encodePrimaryWithRhs :: TTermDefinition (Py.PrimaryWithRhs -> Expr)
encodePrimaryWithRhs = def "encodePrimaryWithRhs" $
  doc "Serialize a primary with RHS" $
  lambda "pwr" $ lets [
    "prim">: project Py._PrimaryWithRhs Py._PrimaryWithRhs_primary @@ var "pwr",
    "rhs">: project Py._PrimaryWithRhs Py._PrimaryWithRhs_rhs @@ var "pwr"] $
    Serialization.noSep @@ list [encodePrimary @@ var "prim", encodePrimaryRhs @@ var "rhs"]

encodeRaiseExpression :: TTermDefinition (Py.RaiseExpression -> Expr)
encodeRaiseExpression = def "encodeRaiseExpression" $
  doc "Serialize a raise expression" $
  lambda "re" $ lets [
    "expr">: project Py._RaiseExpression Py._RaiseExpression_expression @@ var "re",
    "from_">: project Py._RaiseExpression Py._RaiseExpression_from @@ var "re"] $
    Serialization.spaceSep @@ Maybes.cat (list [
      just $ encodeExpression @@ var "expr",
      Maybes.map (lambda "f" $
        Serialization.spaceSep @@ list [Serialization.cst @@ string "from", encodeExpression @@ var "f"])
        (var "from_")])

encodeRaiseStatement :: TTermDefinition (Py.RaiseStatement -> Expr)
encodeRaiseStatement = def "encodeRaiseStatement" $
  doc "Serialize a raise statement" $
  lambda "rs" $
    Serialization.spaceSep @@ Maybes.cat (list [
      just $ Serialization.cst @@ string "raise",
      Maybes.map encodeRaiseExpression (unwrap Py._RaiseStatement @@ var "rs")])

encodeRelativeImportPrefix :: TTermDefinition (Py.RelativeImportPrefix -> Expr)
encodeRelativeImportPrefix = def "encodeRelativeImportPrefix" $
  doc "Serialize a relative import prefix" $
  lambda "p" $
    cases Py._RelativeImportPrefix (var "p") Nothing [
      Py._RelativeImportPrefix_dot>>: constant $ Serialization.cst @@ string ".",
      Py._RelativeImportPrefix_ellipsis>>: constant $ Serialization.cst @@ string "..."]

encodeReturnStatement :: TTermDefinition (Py.ReturnStatement -> Expr)
encodeReturnStatement = def "encodeReturnStatement" $
  doc "Serialize a return statement" $
  lambda "rs" $
    Serialization.spaceSep @@ list [
      Serialization.cst @@ string "return",
      Serialization.commaSep @@ Serialization.inlineStyle @@
        Lists.map encodeStarExpression (unwrap Py._ReturnStatement @@ var "rs")]

encodeSet :: TTermDefinition (Py.Set -> Expr)
encodeSet = def "encodeSet" $
  doc "Serialize a Python set" $
  lambda "s" $
    Serialization.bracesListAdaptive @@ Lists.map encodeStarNamedExpression (unwrap Py._Set @@ var "s")

encodeShiftExpression :: TTermDefinition (Py.ShiftExpression -> Expr)
encodeShiftExpression = def "encodeShiftExpression" $
  doc "Serialize a shift expression" $
  lambda "se" $
    -- Shift operators are rarely used; just encode the sum
    encodeSum @@ (project Py._ShiftExpression Py._ShiftExpression_rhs @@ var "se")

encodeSimpleStatement :: TTermDefinition (Py.SimpleStatement -> Expr)
encodeSimpleStatement = def "encodeSimpleStatement" $
  doc "Serialize a simple (single-line) Python statement" $
  lambda "ss" $
    cases Py._SimpleStatement (var "ss") Nothing [
      Py._SimpleStatement_assignment>>: lambda "a" $ encodeAssignment @@ var "a",
      Py._SimpleStatement_starExpressions>>: lambda "es" $
        Serialization.newlineSep @@ Lists.map encodeStarExpression (var "es"),
      Py._SimpleStatement_return>>: lambda "r" $ encodeReturnStatement @@ var "r",
      Py._SimpleStatement_raise>>: lambda "r" $ encodeRaiseStatement @@ var "r",
      Py._SimpleStatement_pass>>: constant $ Serialization.cst @@ string "pass",
      Py._SimpleStatement_break>>: constant $ Serialization.cst @@ string "break",
      Py._SimpleStatement_continue>>: constant $ Serialization.cst @@ string "continue",
      Py._SimpleStatement_import>>: lambda "i" $ encodeImportStatement @@ var "i",
      Py._SimpleStatement_typeAlias>>: lambda "t" $ encodeTypeAlias @@ var "t",
      Py._SimpleStatement_assert>>: lambda "_" $ Serialization.cst @@ string "assert ...",
      Py._SimpleStatement_global>>: lambda "_" $ Serialization.cst @@ string "global ...",
      Py._SimpleStatement_nonlocal>>: lambda "_" $ Serialization.cst @@ string "nonlocal ...",
      Py._SimpleStatement_del>>: lambda "_" $ Serialization.cst @@ string "del ..."]

encodeSimpleTypeParameter :: TTermDefinition (Py.SimpleTypeParameter -> Expr)
encodeSimpleTypeParameter = def "encodeSimpleTypeParameter" $
  doc "Serialize a simple type parameter" $
  lambda "stp" $
    encodeName @@ (project Py._SimpleTypeParameter Py._SimpleTypeParameter_name @@ var "stp")

encodeSingleTarget :: TTermDefinition (Py.SingleTarget -> Expr)
encodeSingleTarget = def "encodeSingleTarget" $
  doc "Serialize a single target" $
  lambda "st" $
    cases Py._SingleTarget (var "st") Nothing [
      Py._SingleTarget_name>>: lambda "n" $ encodeName @@ var "n",
      Py._SingleTarget_parens>>: lambda "_" $ Serialization.cst @@ string "(...)",
      Py._SingleTarget_subscriptAttributeTarget>>: lambda "_" $ Serialization.cst @@ string "..."]

encodeSlice :: TTermDefinition (Py.Slice -> Expr)
encodeSlice = def "encodeSlice" $
  doc "Serialize a slice" $
  lambda "s" $
    cases Py._Slice (var "s") Nothing [
      Py._Slice_named>>: lambda "ne" $ encodeNamedExpression @@ var "ne",
      Py._Slice_slice_>>: lambda "_" $ Serialization.cst @@ string ":"]

encodeSliceOrStarredExpression :: TTermDefinition (Py.SliceOrStarredExpression -> Expr)
encodeSliceOrStarredExpression = def "encodeSliceOrStarredExpression" $
  doc "Serialize a slice or starred expression" $
  lambda "s" $
    cases Py._SliceOrStarredExpression (var "s") Nothing [
      Py._SliceOrStarredExpression_slice>>: lambda "sl" $ encodeSlice @@ var "sl",
      Py._SliceOrStarredExpression_starred>>: lambda "se" $ encodeStarredExpression @@ var "se"]

encodeSlices :: TTermDefinition (Py.Slices -> Expr)
encodeSlices = def "encodeSlices" $
  doc "Serialize slices" $
  lambda "s" $ lets [
    "hd">: project Py._Slices Py._Slices_head @@ var "s",
    "tl">: project Py._Slices Py._Slices_tail @@ var "s"] $
    Serialization.commaSep @@ Serialization.inlineStyle @@
      Lists.cons (encodeSlice @@ var "hd") (Lists.map encodeSliceOrStarredExpression (var "tl"))

encodeStarAtom :: TTermDefinition (Py.StarAtom -> Expr)
encodeStarAtom = def "encodeStarAtom" $
  doc "Serialize a star atom" $
  lambda "sa" $
    cases Py._StarAtom (var "sa") Nothing [
      Py._StarAtom_name>>: lambda "n" $ encodeName @@ var "n",
      Py._StarAtom_targetWithStarAtom>>: lambda "_" $ Serialization.cst @@ string "(...)",
      Py._StarAtom_starTargetsTupleSeq>>: lambda "_" $ Serialization.cst @@ string "(...)",
      Py._StarAtom_starTargetsListSeq>>: lambda "_" $ Serialization.cst @@ string "[...]"]

encodeStarExpression :: TTermDefinition (Py.StarExpression -> Expr)
encodeStarExpression = def "encodeStarExpression" $
  doc "Serialize a star expression" $
  lambda "se" $
    cases Py._StarExpression (var "se") Nothing [
      Py._StarExpression_star>>: lambda "bor" $
        Serialization.noSep @@ list [Serialization.cst @@ string "*", encodeBitwiseOr @@ var "bor"],
      Py._StarExpression_simple>>: lambda "e" $ encodeExpression @@ var "e"]

encodeStarNamedExpression :: TTermDefinition (Py.StarNamedExpression -> Expr)
encodeStarNamedExpression = def "encodeStarNamedExpression" $
  doc "Serialize a star named expression" $
  lambda "sne" $
    cases Py._StarNamedExpression (var "sne") Nothing [
      Py._StarNamedExpression_star>>: lambda "bor" $
        Serialization.noSep @@ list [Serialization.cst @@ string "*", encodeBitwiseOr @@ var "bor"],
      Py._StarNamedExpression_simple>>: lambda "ne" $ encodeNamedExpression @@ var "ne"]

encodeStarTarget :: TTermDefinition (Py.StarTarget -> Expr)
encodeStarTarget = def "encodeStarTarget" $
  doc "Serialize a star target" $
  lambda "st" $
    cases Py._StarTarget (var "st") Nothing [
      Py._StarTarget_unstarred>>: lambda "t" $ encodeTargetWithStarAtom @@ var "t",
      Py._StarTarget_starred>>: lambda "inner" $
        Serialization.noSep @@ list [Serialization.cst @@ string "*", encodeStarTarget @@ var "inner"]]

encodeStarredExpression :: TTermDefinition (Py.StarredExpression -> Expr)
encodeStarredExpression = def "encodeStarredExpression" $
  doc "Serialize a starred expression" $
  lambda "se" $
    Serialization.noSep @@ list [
      Serialization.cst @@ string "*",
      encodeExpression @@ (unwrap Py._StarredExpression @@ var "se")]

encodeStatement :: TTermDefinition (Py.Statement -> Expr)
encodeStatement = def "encodeStatement" $
  doc "Serialize a Python statement" $
  lambda "stmt" $
    cases Py._Statement (var "stmt") Nothing [
      Py._Statement_annotated>>: lambda "a" $ encodeAnnotatedStatement @@ var "a",
      Py._Statement_simple>>: lambda "ss" $
        Serialization.newlineSep @@ Lists.map encodeSimpleStatement (var "ss"),
      Py._Statement_compound>>: lambda "c" $ encodeCompoundStatement @@ var "c"]

encodeString :: TTermDefinition (Py.String_ -> Expr)
encodeString = def "encodeString" $
  doc "Serialize a Python string literal" $
  lambda "s" $ lets [
    "content">: project Py._String Py._String_value @@ var "s",
    "style">: project Py._String Py._String_quoteStyle @@ var "s"] $
    cases Py._QuoteStyle (var "style") Nothing [
      Py._QuoteStyle_single>>: constant $ Serialization.cst @@ (escapePythonString @@ false @@ var "content"),
      Py._QuoteStyle_double>>: constant $ Serialization.cst @@ (escapePythonString @@ true @@ var "content"),
      Py._QuoteStyle_triple>>: constant $ Serialization.noSep @@ list [
        Serialization.cst @@ string "r\"\"\"",
        Serialization.cst @@ var "content",
        Serialization.cst @@ string "\"\"\""]]

encodeSubjectExpression :: TTermDefinition (Py.SubjectExpression -> Expr)
encodeSubjectExpression = def "encodeSubjectExpression" $
  doc "Serialize a subject expression" $
  lambda "se" $
    cases Py._SubjectExpression (var "se") Nothing [
      Py._SubjectExpression_simple>>: lambda "ne" $ encodeNamedExpression @@ var "ne",
      Py._SubjectExpression_tuple>>: lambda "_" $ Serialization.cst @@ string "*..."]

encodeSum :: TTermDefinition (Py.Sum -> Expr)
encodeSum = def "encodeSum" $
  doc "Serialize a sum expression" $
  lambda "s" $
    -- Just encode the term for now; sum operators (+/-) rarely used in generated code
    encodeTerm_ @@ (project Py._Sum Py._Sum_rhs @@ var "s")

-- | Serialize a TPrimary (target-side primary expression)
encodeTPrimary :: TTermDefinition (Py.TPrimary -> Expr)
encodeTPrimary = def "encodeTPrimary" $
  doc "Serialize a target-side primary expression" $
  lambda "tp" $
    cases Py._TPrimary (var "tp") Nothing [
      Py._TPrimary_atom>>: lambda "a" $ encodeAtom @@ var "a",
      Py._TPrimary_primaryAndName>>: lambda "pn" $ encodeTPrimaryAndName @@ var "pn",
      Py._TPrimary_primaryAndSlices>>: lambda "_" $ Serialization.cst @@ string "...",
      Py._TPrimary_primaryAndGenexp>>: lambda "_" $ Serialization.cst @@ string "...",
      Py._TPrimary_primaryAndArguments>>: lambda "_" $ Serialization.cst @@ string "..."]

-- | Serialize a TPrimaryAndName (e.g., obj.attr)
encodeTPrimaryAndName :: TTermDefinition (Py.TPrimaryAndName -> Expr)
encodeTPrimaryAndName = def "encodeTPrimaryAndName" $
  doc "Serialize a TPrimaryAndName as primary.name" $
  lambda "pn" $ lets [
    "prim">: project Py._TPrimaryAndName Py._TPrimaryAndName_primary @@ var "pn",
    "name_">: project Py._TPrimaryAndName Py._TPrimaryAndName_name @@ var "pn"] $
    Serialization.noSep @@ list [encodeTPrimary @@ var "prim", Serialization.cst @@ string ".", encodeName @@ var "name_"]

encodeTargetWithStarAtom :: TTermDefinition (Py.TargetWithStarAtom -> Expr)
encodeTargetWithStarAtom = def "encodeTargetWithStarAtom" $
  doc "Serialize a target with star atom" $
  lambda "t" $
    cases Py._TargetWithStarAtom (var "t") Nothing [
      Py._TargetWithStarAtom_atom>>: lambda "a" $ encodeStarAtom @@ var "a",
      Py._TargetWithStarAtom_project>>: lambda "pn" $ encodeTPrimaryAndName @@ var "pn",
      Py._TargetWithStarAtom_slices>>: lambda "_" $ Serialization.cst @@ string "..."]

encodeTerm_ :: TTermDefinition (Py.Term -> Expr)
encodeTerm_ = def "encodeTerm" $
  doc "Serialize a term expression" $
  lambda "t" $
    -- Just encode the factor; multiplication rarely used in generated code
    encodeFactor @@ (project Py._Term Py._Term_rhs @@ var "t")

encodeTuple :: TTermDefinition (Py.Tuple -> Expr)
encodeTuple = def "encodeTuple" $
  doc "Serialize a Python tuple" $
  lambda "t" $ lets [
    "es">: unwrap Py._Tuple @@ var "t"] $
    Maybes.fromMaybe
      (Serialization.parenList @@ false @@ Lists.map encodeStarNamedExpression (var "es"))
      (Maybes.map
        (lambda "firstEs" $
          Logic.ifElse (Equality.equal (Lists.length (var "es")) (int32 1))
            (Serialization.parens @@ (Serialization.noSep @@ list [
              encodeStarNamedExpression @@ var "firstEs",
              Serialization.cst @@ string ","]))
            (Serialization.parenList @@ false @@ Lists.map encodeStarNamedExpression (var "es")))
        (Lists.maybeHead (var "es")))

encodeTypeAlias :: TTermDefinition (Py.TypeAlias -> Expr)
encodeTypeAlias = def "encodeTypeAlias" $
  doc "Serialize a type alias" $
  lambda "ta" $ lets [
    "name">: project Py._TypeAlias Py._TypeAlias_name @@ var "ta",
    "tparams">: project Py._TypeAlias Py._TypeAlias_typeParams @@ var "ta",
    "expr">: project Py._TypeAlias Py._TypeAlias_expression @@ var "ta",
    "alias">: Serialization.noSep @@ Maybes.cat (list [
      just $ encodeName @@ var "name",
      Logic.ifElse (Lists.null (var "tparams"))
        nothing
        (just $ Serialization.bracketList @@ Serialization.inlineStyle @@ Lists.map encodeTypeParameter (var "tparams"))])] $
    Serialization.spaceSep @@ list [
      Serialization.cst @@ string "type",
      var "alias",
      Serialization.cst @@ string "=",
      encodeExpression @@ var "expr"]

encodeTypeParameter :: TTermDefinition (Py.TypeParameter -> Expr)
encodeTypeParameter = def "encodeTypeParameter" $
  doc "Serialize a type parameter" $
  lambda "tp" $
    cases Py._TypeParameter (var "tp") Nothing [
      Py._TypeParameter_simple>>: lambda "s" $ encodeSimpleTypeParameter @@ var "s",
      Py._TypeParameter_star>>: lambda "_" $ Serialization.cst @@ string "*...",
      Py._TypeParameter_doubleStar>>: lambda "_" $ Serialization.cst @@ string "**..."]

encodeTypedAssignment :: TTermDefinition (Py.TypedAssignment -> Expr)
encodeTypedAssignment = def "encodeTypedAssignment" $
  doc "Serialize a typed assignment" $
  lambda "ta" $ lets [
    "lhs">: project Py._TypedAssignment Py._TypedAssignment_lhs @@ var "ta",
    "typ">: project Py._TypedAssignment Py._TypedAssignment_type @@ var "ta",
    "rhs">: project Py._TypedAssignment Py._TypedAssignment_rhs @@ var "ta"] $
    Serialization.spaceSep @@ Maybes.cat (list [
      just $ Serialization.noSep @@ list [encodeSingleTarget @@ var "lhs", Serialization.cst @@ string ":"],
      just $ encodeExpression @@ var "typ",
      Maybes.map encodeAnnotatedRhs (var "rhs")])

encodeUntypedAssignment :: TTermDefinition (Py.UntypedAssignment -> Expr)
encodeUntypedAssignment = def "encodeUntypedAssignment" $
  doc "Serialize an untyped assignment" $
  lambda "ua" $ lets [
    "targets">: project Py._UntypedAssignment Py._UntypedAssignment_targets @@ var "ua",
    "rhs">: project Py._UntypedAssignment Py._UntypedAssignment_rhs @@ var "ua"] $
    Serialization.spaceSep @@ Lists.concat (list [
      Lists.map encodeStarTarget (var "targets"),
      list [encodeAnnotatedRhs @@ var "rhs"]])

encodeValuePattern :: TTermDefinition (Py.ValuePattern -> Expr)
encodeValuePattern = def "encodeValuePattern" $
  doc "Serialize a value pattern" $
  lambda "vp" $
    encodeAttribute @@ (unwrap Py._ValuePattern @@ var "vp")

encodeWhileStatement :: TTermDefinition (Py.WhileStatement -> Expr)
encodeWhileStatement = def "encodeWhileStatement" $
  doc "Serialize a while statement" $
  lambda "ws" $ lets [
    "cond">: project Py._WhileStatement Py._WhileStatement_condition @@ var "ws",
    "body">: project Py._WhileStatement Py._WhileStatement_body @@ var "ws",
    "else_">: project Py._WhileStatement Py._WhileStatement_else @@ var "ws"] $
    Serialization.newlineSep @@ Maybes.cat (list [
      just $ Serialization.newlineSep @@ list [
        Serialization.spaceSep @@ list [
          Serialization.cst @@ string "while",
          Serialization.noSep @@ list [
            encodeNamedExpression @@ var "cond",
            Serialization.cst @@ string ":"]],
        encodeBlock @@ var "body"],
      Maybes.map (lambda "eb" $
        Serialization.newlineSep @@ list [
          Serialization.cst @@ string "else:",
          encodeBlock @@ var "eb"])
        (var "else_")])

escapePythonString :: TTermDefinition (Bool -> String -> String)
escapePythonString = def "escapePythonString" $
  doc "Escape special characters in a Python string and wrap in quotes" $
  lambda "doubleQuoted" $ lambda "s" $ lets [
    -- Helper to replace a substring
    "replace">: lambda "old" $ lambda "new" $ lambda "str" $
      Strings.intercalate (var "new") (Strings.splitOn (var "old") (var "str")),
    -- Escape backslashes first (must be first!)
    "s1">: var "replace" @@ string "\\" @@ string "\\\\" @@ var "s",
    -- Escape null bytes
    "s2">: var "replace" @@ string "\0" @@ string "\\x00" @@ var "s1",
    -- Escape newlines
    "s3">: var "replace" @@ string "\n" @@ string "\\n" @@ var "s2",
    -- Escape tabs
    "s4">: var "replace" @@ string "\t" @@ string "\\t" @@ var "s3",
    -- Escape carriage returns
    "s5">: var "replace" @@ string "\r" @@ string "\\r" @@ var "s4",
    -- Escape quotes based on quote style
    "escaped">: Logic.ifElse (var "doubleQuoted")
      (var "replace" @@ string "\"" @@ string "\\\"" @@ var "s5")
      (var "replace" @@ string "'" @@ string "\\'" @@ var "s5"),
    -- Add surrounding quotes
    "quote">: Logic.ifElse (var "doubleQuoted") (string "\"") (string "'")] $
    Strings.cat2 (var "quote") (Strings.cat2 (var "escaped") (var "quote"))

-- | Convert a showBigfloat result into valid Python source syntax, mapping
-- NaN and ±Infinity to float() constructor calls.
pythonFloatLiteralText :: TTermDefinition (String -> String)
pythonFloatLiteralText = def "pythonFloatLiteralText" $
  lambda "s" $
    Logic.ifElse (Equality.equal (var "s") (string "NaN")) (string "float('nan')") $
    Logic.ifElse (Equality.equal (var "s") (string "Infinity")) (string "float('inf')") $
    Logic.ifElse (Equality.equal (var "s") (string "-Infinity")) (string "float('-inf')")
      (var "s")

toPythonComments :: TTermDefinition (String -> String)
toPythonComments = def "toPythonComments" $
  doc ("Convert a doc string to Python comment format. Empty source lines"
    <> " emit `#` (no trailing space) so blank comment lines don't carry"
    <> " trailing whitespace into the generated file.") $
  lambda "doc_" $
    Logic.ifElse (Equality.equal (var "doc_") (string ""))
      (string "")
      (Strings.intercalate (string "\n") (Lists.map (lambda "line" $
          Logic.ifElse (Equality.equal (var "line") (string ""))
            (string "#")
            (Strings.cat2 (string "# ") (var "line")))
        (Strings.lines (var "doc_"))))