packages feed

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

{-# LANGUAGE FlexibleContexts #-}

-- | Lisp serializer: converts the Lisp syntax AST to concrete syntax (source code).
-- A single serde handles all four dialects (Clojure, Emacs Lisp, Common Lisp, Scheme),
-- dispatching on the Dialect enum where concrete syntax diverges.

module Hydra.Sources.Lisp.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.Lisp.Syntax             as LispSyntax
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 Lisp AST
import Hydra.Ast
import qualified Hydra.Lisp.Syntax as L


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

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

module_ :: Module
module_ = Module {
            moduleNamespace = ns,
            moduleDefinitions = definitions,
            moduleTermDependencies = [Constants.ns, Formatting.ns, Serialization.ns],
            moduleTypeDependencies = (LispSyntax.ns:KernelTypes.kernelTypesNamespaces),
            moduleDescription = Just "Lisp serializer: converts Lisp AST to concrete syntax for Clojure, Emacs Lisp, Common Lisp, or Scheme"}
  where
    definitions = [
      toDefinition andExpressionToExpr,
      toDefinition applicationToExpr,
      toDefinition caseExpressionToExpr,
      toDefinition commentToExpr,
      toDefinition condExpressionToExpr,
      toDefinition constantDefinitionToExpr,
      toDefinition defKeyword,
      toDefinition defconstKeyword,
      toDefinition defnKeyword,
      toDefinition defrecordKeyword,
      toDefinition doExpressionToExpr,
      toDefinition docstringToExpr,
      toDefinition exportDeclarationToExpr,
      toDefinition expressionToExpr,
      toDefinition falseExpr,
      toDefinition fieldAccessToExpr,
      toDefinition formatLispFloat,
      toDefinition functionDefinitionToExpr,
      toDefinition ifExpressionToExpr,
      toDefinition importDeclarationToExpr,
      toDefinition keywordToExpr,
      toDefinition lambdaKeyword,
      toDefinition lambdaToExpr,
      toDefinition letExpressionToExpr,
      toDefinition listKeyword,
      toDefinition listLiteralToExpr,
      toDefinition literalToExpr,
      toDefinition macroDefinitionToExpr,
      toDefinition mapLiteralToExpr,
      toDefinition moduleDeclarationToExpr,
      toDefinition nilExpr,
      toDefinition notExpressionToExpr,
      toDefinition orExpressionToExpr,
      toDefinition programToExpr,
      toDefinition recordTypeDefinitionToExpr,
      toDefinition sExpressionToExpr,
      toDefinition setLiteralToExpr,
      toDefinition symbolToExpr,
      toDefinition topLevelFormToExpr,
      toDefinition topLevelFormWithCommentsToExpr,
      toDefinition trueExpr,
      toDefinition variableDefinitionToExpr,
      toDefinition variableReferenceToExpr,
      toDefinition vectorLiteralToExpr]

-- | Square brackets: [expr1 expr2 ...]
sqBrackets :: TTerm [Expr] -> TTerm Expr
sqBrackets exprs = Serialization.brackets @@ (asTerm Serialization.squareBrackets) @@ (asTerm Serialization.inlineStyle) @@
  (Serialization.spaceSep @@ exprs)

-- | Parenthesized space-separated list: (expr1 expr2 ...)
parenList :: TTerm [Expr] -> TTerm Expr
parenList exprs = Serialization.parens @@ (Serialization.spaceSep @@ exprs)

-- | Serialize an and expression: (and expr1 expr2 ...)
andExpressionToExpr :: TTermDefinition (L.Dialect -> L.AndExpression -> Expr)
andExpressionToExpr = define "andExpressionToExpr" $
  lambda "d" $ lambda "andExpr" $
    Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat2
      (list [Serialization.cst @@ string "and"])
      (Lists.map (expressionToExpr @@ var "d") (project L._AndExpression L._AndExpression_expressions @@ var "andExpr")))

-- | Serialize a function application: (f arg1 arg2 ...)
--   For Emacs Lisp: (funcall f arg1 arg2 ...) when f is not a simple variable
applicationToExpr :: TTermDefinition (L.Dialect -> L.Application -> Expr)
applicationToExpr = define "applicationToExpr" $
  lambda "d" $ lambda "app" $ lets [
    "funExpr">: project L._Application L._Application_function @@ var "app",
    "fun">: expressionToExpr @@ var "d" @@ var "funExpr",
    "args">: Lists.map (expressionToExpr @@ var "d") (project L._Application L._Application_arguments @@ var "app"),
    "needsFuncall">: cases L._Dialect (var "d") (Just $ boolean False)
      [L._Dialect_emacsLisp>>: lambda "u" $
        -- In Emacs Lisp, funcall is needed when the function is not a simple variable
        cases L._Expression (var "funExpr") (Just $ boolean True)
          [L._Expression_variable>>: lambda "s" $ boolean False]],
    "allParts">: Logic.ifElse (var "needsFuncall")
      (Lists.concat2 (list [Serialization.cst @@ string "funcall", var "fun"]) (var "args"))
      (Lists.concat2 (list [var "fun"]) (var "args"))] $
    Serialization.parens @@ (Serialization.spaceSep @@ var "allParts")

-- | Serialize a case expression
caseExpressionToExpr :: TTermDefinition (L.Dialect -> L.CaseExpression -> Expr)
caseExpressionToExpr = define "caseExpressionToExpr" $
  lambda "d" $ lambda "caseExpr" $ lets [
    "scrutinee">: expressionToExpr @@ var "d" @@ (project L._CaseExpression L._CaseExpression_scrutinee @@ var "caseExpr"),
    "clauses">: project L._CaseExpression L._CaseExpression_clauses @@ var "caseExpr",
    "dflt">: project L._CaseExpression L._CaseExpression_default @@ var "caseExpr",
    "clauseExprs">: Lists.map (lambda "c" $
      Serialization.parens @@ (Serialization.spaceSep @@ list [
        Serialization.parens @@ (Serialization.spaceSep @@
          Lists.map (expressionToExpr @@ var "d") (project L._CaseClause L._CaseClause_keys @@ var "c")),
        expressionToExpr @@ var "d" @@ (project L._CaseClause L._CaseClause_body @@ var "c")]))
      (var "clauses"),
    "defaultPart">: Maybes.maybe (list ([] :: [TTerm Expr]))
      (lambda "e" $ list [Serialization.parens @@ (Serialization.spaceSep @@ list [
        Serialization.cst @@ string "else",
        expressionToExpr @@ var "d" @@ var "e"])])
      (var "dflt")] $
    Serialization.parens @@ (Serialization.spaceSep @@ (Lists.concat (list [
      list [Serialization.cst @@ string "case", var "scrutinee"],
      var "clauseExprs",
      var "defaultPart"])))

-- | Serialize a comment. Empty text emits `;` (no trailing space) so
--   blank comment lines don't carry trailing whitespace.
commentToExpr :: TTermDefinition (L.Comment -> Expr)
commentToExpr = define "commentToExpr" $
  lambda "c" $ lets [
    "text">: project L._Comment L._Comment_text @@ var "c"] $
    Serialization.cst @@ Logic.ifElse (Equality.equal (var "text") (string ""))
      (string ";")
      (Strings.cat2 (string "; ") (var "text"))

-- | Serialize a cond expression
condExpressionToExpr :: TTermDefinition (L.Dialect -> L.CondExpression -> Expr)
condExpressionToExpr = define "condExpressionToExpr" $
  lambda "d" $ lambda "condExpr" $ lets [
    "clauses">: project L._CondExpression L._CondExpression_clauses @@ var "condExpr",
    "dflt">: project L._CondExpression L._CondExpression_default @@ var "condExpr"] $
    cases L._Dialect (var "d") Nothing [
      -- Clojure: (cond test1 expr1 test2 expr2 :else default)
      L._Dialect_clojure>>: constant $ lets [
        "clauseExprs">: Lists.concat (Lists.map (lambda "c" $ list [
          expressionToExpr @@ var "d" @@ (project L._CondClause L._CondClause_condition @@ var "c"),
          expressionToExpr @@ var "d" @@ (project L._CondClause L._CondClause_body @@ var "c")])
          (var "clauses")),
        "defaultPart">: Maybes.maybe (list ([] :: [TTerm Expr]))
          (lambda "e" $ list [Serialization.cst @@ string ":else", expressionToExpr @@ var "d" @@ var "e"])
          (var "dflt")] $
        Serialization.parens @@ (Serialization.spaceSep @@ (Lists.concat (list [
          list [Serialization.cst @@ string "cond"],
          var "clauseExprs",
          var "defaultPart"]))),
      -- Others: (cond (test1 expr1) (test2 expr2) (t/else default))
      L._Dialect_emacsLisp>>: constant $ condOther (var "d") (var "clauses") (var "dflt") (string "t"),
      L._Dialect_commonLisp>>: constant $ condOther (var "d") (var "clauses") (var "dflt") (string "t"),
      L._Dialect_scheme>>: constant $ condOther (var "d") (var "clauses") (var "dflt") (string "else")]
  where
    condOther :: TTerm L.Dialect -> TTerm [L.CondClause] -> TTerm (Maybe L.Expression) -> TTerm String -> TTerm Expr
    condOther d clauses dflt defaultKeyword = lets [
      "clauseExprs">: Lists.map (lambda "c" $
        Serialization.parens @@ (Serialization.spaceSep @@ list [
          expressionToExpr @@ d @@ (project L._CondClause L._CondClause_condition @@ var "c"),
          expressionToExpr @@ d @@ (project L._CondClause L._CondClause_body @@ var "c")]))
        clauses,
      "defaultPart">: Maybes.maybe (list ([] :: [TTerm Expr]))
        (lambda "e" $ list [Serialization.parens @@ (Serialization.spaceSep @@ list [
          Serialization.cst @@ defaultKeyword,
          expressionToExpr @@ d @@ var "e"])])
        dflt] $
      Serialization.parens @@ (Serialization.spaceSep @@ (Lists.concat (list [
        list [Serialization.cst @@ string "cond"],
        var "clauseExprs",
        var "defaultPart"])))

-- | Serialize a constant definition
constantDefinitionToExpr :: TTermDefinition (L.Dialect -> L.ConstantDefinition -> Expr)
constantDefinitionToExpr = define "constantDefinitionToExpr" $
  lambda "d" $ lambda "cdef" $ lets [
    "name">: symbolToExpr @@ (project L._ConstantDefinition L._ConstantDefinition_name @@ var "cdef"),
    "value">: expressionToExpr @@ var "d" @@ (project L._ConstantDefinition L._ConstantDefinition_value @@ var "cdef")] $
    Serialization.parens @@ (Serialization.spaceSep @@ list [
      Serialization.cst @@ (defconstKeyword @@ var "d"),
      var "name",
      var "value"])

-- | The keyword for variable definitions
defKeyword :: TTermDefinition (L.Dialect -> String)
defKeyword = define "defKeyword" $
  lambda "d" $ cases L._Dialect (var "d") Nothing [
    L._Dialect_clojure>>: constant $ string "def",
    L._Dialect_emacsLisp>>: constant $ string "defvar",
    L._Dialect_commonLisp>>: constant $ string "cl:defvar",
    L._Dialect_scheme>>: constant $ string "define"]

-- | The keyword for constant definitions
defconstKeyword :: TTermDefinition (L.Dialect -> String)
defconstKeyword = define "defconstKeyword" $
  lambda "d" $ cases L._Dialect (var "d") Nothing [
    L._Dialect_clojure>>: constant $ string "def",
    L._Dialect_emacsLisp>>: constant $ string "defconst",
    L._Dialect_commonLisp>>: constant $ string "cl:defconstant",
    L._Dialect_scheme>>: constant $ string "define"]

-- | The keyword for named function definitions
defnKeyword :: TTermDefinition (L.Dialect -> String)
defnKeyword = define "defnKeyword" $
  lambda "d" $ cases L._Dialect (var "d") Nothing [
    L._Dialect_clojure>>: constant $ string "defn",
    L._Dialect_emacsLisp>>: constant $ string "defun",
    L._Dialect_commonLisp>>: constant $ string "cl:defun",
    L._Dialect_scheme>>: constant $ string "define"]

-- | The keyword for record/struct definitions
defrecordKeyword :: TTermDefinition (L.Dialect -> String)
defrecordKeyword = define "defrecordKeyword" $
  lambda "d" $ cases L._Dialect (var "d") Nothing [
    L._Dialect_clojure>>: constant $ string "defrecord",
    L._Dialect_emacsLisp>>: constant $ string "cl-defstruct",
    L._Dialect_commonLisp>>: constant $ string "cl:defstruct",
    L._Dialect_scheme>>: constant $ string "define-record-type"]

-- | Serialize a do/progn/begin expression
doExpressionToExpr :: TTermDefinition (L.Dialect -> L.DoExpression -> Expr)
doExpressionToExpr = define "doExpressionToExpr" $
  lambda "d" $ lambda "doExpr" $ lets [
    "kw">: cases L._Dialect (var "d") Nothing [
      L._Dialect_clojure>>: constant $ string "do",
      L._Dialect_emacsLisp>>: constant $ string "progn",
      L._Dialect_commonLisp>>: constant $ string "progn",
      L._Dialect_scheme>>: constant $ string "begin"]] $
    Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat2
      (list [Serialization.cst @@ var "kw"])
      (Lists.map (expressionToExpr @@ var "d") (project L._DoExpression L._DoExpression_expressions @@ var "doExpr")))

-- | Serialize a docstring as a comment. Empty text emits `;;` (no
--   trailing space) so blank docstring lines don't carry trailing whitespace.
docstringToExpr :: TTermDefinition (L.Docstring -> Expr)
docstringToExpr = define "docstringToExpr" $
  lambda "ds" $ lets [
    "text">: unwrap L._Docstring @@ var "ds"] $
    Serialization.cst @@ Logic.ifElse (Equality.equal (var "text") (string ""))
      (string ";;")
      (Strings.cat (list [string ";; ", var "text"]))

-- | Serialize an export declaration
exportDeclarationToExpr :: TTermDefinition (L.Dialect -> L.ExportDeclaration -> Expr)
exportDeclarationToExpr = define "exportDeclarationToExpr" $
  lambda "d" $ lambda "edecl" $ lets [
    "syms">: Lists.map symbolToExpr (project L._ExportDeclaration L._ExportDeclaration_symbols @@ var "edecl")] $
    cases L._Dialect (var "d") Nothing [
      -- Clojure: public by default, no export form
      L._Dialect_clojure>>: constant $ Serialization.cst @@ string "",
      -- (provide 'name)
      L._Dialect_emacsLisp>>: constant $
        Serialization.newlineSep @@ Lists.map (lambda "s" $
          Serialization.parens @@ (Serialization.spaceSep @@ list [
            Serialization.cst @@ string "provide",
            Serialization.noSep @@ list [Serialization.cst @@ string "'", var "s"]]))
          (var "syms"),
      -- (:export :sym1 :sym2)
      L._Dialect_commonLisp>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat2
          (list [Serialization.cst @@ string ":export"])
          (Lists.map (lambda "s" $ Serialization.noSep @@ list [Serialization.cst @@ string ":", var "s"]) (var "syms"))),
      -- (export sym1 sym2)
      L._Dialect_scheme>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat2
          (list [Serialization.cst @@ string "export"])
          (var "syms"))]

-- | Serialize a Lisp expression
expressionToExpr :: TTermDefinition (L.Dialect -> L.Expression -> Expr)
expressionToExpr = define "expressionToExpr" $
  lambda "d" $ lambda "expr" $
    cases L._Expression (var "expr") Nothing [
      L._Expression_application>>: lambda "a" $ applicationToExpr @@ var "d" @@ var "a",
      L._Expression_lambda>>: lambda "l" $ lambdaToExpr @@ var "d" @@ var "l",
      L._Expression_let>>: lambda "l" $ letExpressionToExpr @@ var "d" @@ var "l",
      L._Expression_if>>: lambda "i" $ ifExpressionToExpr @@ var "d" @@ var "i",
      L._Expression_cond>>: lambda "c" $ condExpressionToExpr @@ var "d" @@ var "c",
      L._Expression_case>>: lambda "c" $ caseExpressionToExpr @@ var "d" @@ var "c",
      L._Expression_and>>: lambda "a" $ andExpressionToExpr @@ var "d" @@ var "a",
      L._Expression_or>>: lambda "o" $ orExpressionToExpr @@ var "d" @@ var "o",
      L._Expression_not>>: lambda "n" $ notExpressionToExpr @@ var "d" @@ var "n",
      L._Expression_do>>: lambda "e" $ doExpressionToExpr @@ var "d" @@ var "e",
      L._Expression_begin>>: lambda "e" $
        Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat2
          (list [Serialization.cst @@ string "begin"])
          (Lists.map (expressionToExpr @@ var "d") (project L._BeginExpression L._BeginExpression_expressions @@ var "e"))),
      L._Expression_variable>>: lambda "v" $ variableReferenceToExpr @@ var "d" @@ var "v",
      L._Expression_literal>>: lambda "l" $ literalToExpr @@ var "d" @@ var "l",
      L._Expression_list>>: lambda "l" $ listLiteralToExpr @@ var "d" @@ var "l",
      L._Expression_vector>>: lambda "v" $ vectorLiteralToExpr @@ var "d" @@ var "v",
      L._Expression_map>>: lambda "m" $ mapLiteralToExpr @@ var "d" @@ var "m",
      L._Expression_set>>: lambda "s" $ setLiteralToExpr @@ var "d" @@ var "s",
      L._Expression_cons>>: lambda "c" $
        Serialization.parens @@ (Serialization.spaceSep @@ list [
          Serialization.cst @@ string "cons",
          expressionToExpr @@ var "d" @@ (project L._ConsExpression L._ConsExpression_head @@ var "c"),
          expressionToExpr @@ var "d" @@ (project L._ConsExpression L._ConsExpression_tail @@ var "c")]),
      L._Expression_dottedPair>>: lambda "p" $
        Serialization.parens @@ (Serialization.spaceSep @@ list [
          expressionToExpr @@ var "d" @@ (project L._DottedPair L._DottedPair_car @@ var "p"),
          Serialization.cst @@ string ".",
          expressionToExpr @@ var "d" @@ (project L._DottedPair L._DottedPair_cdr @@ var "p")]),
      L._Expression_fieldAccess>>: lambda "fa" $ fieldAccessToExpr @@ var "d" @@ var "fa",
      L._Expression_typeAnnotation>>: lambda "ta" $
        expressionToExpr @@ var "d" @@ (project L._TypeAnnotation L._TypeAnnotation_expression @@ var "ta"),
      L._Expression_quote>>: lambda "q" $
        Serialization.noSep @@ list [
          Serialization.cst @@ string "'",
          expressionToExpr @@ var "d" @@ (project L._QuoteExpression L._QuoteExpression_body @@ var "q")],
      L._Expression_quasiquote>>: lambda "q" $
        Serialization.noSep @@ list [
          Serialization.cst @@ string "`",
          expressionToExpr @@ var "d" @@ (project L._QuasiquoteExpression L._QuasiquoteExpression_body @@ var "q")],
      L._Expression_unquote>>: lambda "u" $
        unquoteToExpr (var "d") (var "u"),
      L._Expression_splicingUnquote>>: lambda "su" $
        splicingUnquoteToExpr (var "d") (var "su"),
      L._Expression_sExpression>>: lambda "s" $ sExpressionToExpr @@ var "s"]
  where
    unquoteToExpr :: TTerm L.Dialect -> TTerm a -> TTerm Expr
    unquoteToExpr d u =
      cases L._Dialect d Nothing [
        L._Dialect_clojure>>: constant $
          Serialization.noSep @@ list [
            Serialization.cst @@ string "~",
            expressionToExpr @@ d @@ (project L._UnquoteExpression L._UnquoteExpression_body @@ u)],
        L._Dialect_emacsLisp>>: constant $
          Serialization.noSep @@ list [
            Serialization.cst @@ string ",",
            expressionToExpr @@ d @@ (project L._UnquoteExpression L._UnquoteExpression_body @@ u)],
        L._Dialect_commonLisp>>: constant $
          Serialization.noSep @@ list [
            Serialization.cst @@ string ",",
            expressionToExpr @@ d @@ (project L._UnquoteExpression L._UnquoteExpression_body @@ u)],
        L._Dialect_scheme>>: constant $
          Serialization.noSep @@ list [
            Serialization.cst @@ string ",",
            expressionToExpr @@ d @@ (project L._UnquoteExpression L._UnquoteExpression_body @@ u)]]
    splicingUnquoteToExpr :: TTerm L.Dialect -> TTerm a -> TTerm Expr
    splicingUnquoteToExpr d su =
      cases L._Dialect d Nothing [
        L._Dialect_clojure>>: constant $
          Serialization.noSep @@ list [
            Serialization.cst @@ string "~@",
            expressionToExpr @@ d @@ (project L._SplicingUnquoteExpression L._SplicingUnquoteExpression_body @@ su)],
        L._Dialect_emacsLisp>>: constant $
          Serialization.noSep @@ list [
            Serialization.cst @@ string ",@",
            expressionToExpr @@ d @@ (project L._SplicingUnquoteExpression L._SplicingUnquoteExpression_body @@ su)],
        L._Dialect_commonLisp>>: constant $
          Serialization.noSep @@ list [
            Serialization.cst @@ string ",@",
            expressionToExpr @@ d @@ (project L._SplicingUnquoteExpression L._SplicingUnquoteExpression_body @@ su)],
        L._Dialect_scheme>>: constant $
          Serialization.noSep @@ list [
            Serialization.cst @@ string ",@",
            expressionToExpr @@ d @@ (project L._SplicingUnquoteExpression L._SplicingUnquoteExpression_body @@ su)]]

-- | Boolean false expression
falseExpr :: TTermDefinition (L.Dialect -> Expr)
falseExpr = define "falseExpr" $
  lambda "d" $ cases L._Dialect (var "d") Nothing [
    L._Dialect_clojure>>: constant $ Serialization.cst @@ string "false",
    L._Dialect_emacsLisp>>: constant $ Serialization.cst @@ string "nil",
    L._Dialect_commonLisp>>: constant $ Serialization.cst @@ string "cl:nil",
    L._Dialect_scheme>>: constant $ Serialization.cst @@ string "#f"]

-- | Serialize a field access expression
fieldAccessToExpr :: TTermDefinition (L.Dialect -> L.FieldAccess -> Expr)
fieldAccessToExpr = define "fieldAccessToExpr" $
  lambda "d" $ lambda "fa" $ lets [
    "rtype">: symbolToExpr @@ (project L._FieldAccess L._FieldAccess_recordType @@ var "fa"),
    "field">: symbolToExpr @@ (project L._FieldAccess L._FieldAccess_field @@ var "fa"),
    "target">: expressionToExpr @@ var "d" @@ (project L._FieldAccess L._FieldAccess_target @@ var "fa")] $
    cases L._Dialect (var "d") Nothing [
      -- (:field target) in Clojure
      L._Dialect_clojure>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ list [
          Serialization.noSep @@ list [Serialization.cst @@ string ":", var "field"],
          var "target"]),
      -- (record-type-field target) in others
      L._Dialect_emacsLisp>>: constant $ fieldAccessOther (var "rtype") (var "field") (var "target"),
      L._Dialect_commonLisp>>: constant $ fieldAccessOther (var "rtype") (var "field") (var "target"),
      L._Dialect_scheme>>: constant $ fieldAccessOther (var "rtype") (var "field") (var "target")]
  where
    fieldAccessOther :: TTerm Expr -> TTerm Expr -> TTerm Expr -> TTerm Expr
    fieldAccessOther rtype field target =
      Serialization.parens @@ (Serialization.spaceSep @@ list [
        Serialization.noSep @@ list [rtype, Serialization.cst @@ string "-", field],
        target])

-- | Serialize a function definition.
-- Clojure: (defn name [params] body)
-- Elisp/CL: (defun name (params) body)
-- Scheme: (define (name params) body)
functionDefinitionToExpr :: TTermDefinition (L.Dialect -> L.FunctionDefinition -> Expr)
functionDefinitionToExpr = define "functionDefinitionToExpr" $
  lambda "d" $ lambda "fdef" $ lets [
    "name">: symbolToExpr @@ (project L._FunctionDefinition L._FunctionDefinition_name @@ var "fdef"),
    "params">: Lists.map symbolToExpr (project L._FunctionDefinition L._FunctionDefinition_params @@ var "fdef"),
    "body">: Lists.map (expressionToExpr @@ var "d") (project L._FunctionDefinition L._FunctionDefinition_body @@ var "fdef")] $
    cases L._Dialect (var "d") Nothing [
      -- (defn name [params] body...)
      L._Dialect_clojure>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ (Lists.concat (list [
          list [Serialization.cst @@ string "defn", var "name"],
          list [sqBrackets (var "params")],
          var "body"]))),
      -- (defun name (params) body...)
      L._Dialect_emacsLisp>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ (Lists.concat (list [
          list [Serialization.cst @@ string "defun", var "name"],
          list [Serialization.parens @@ (Serialization.spaceSep @@ var "params")],
          var "body"]))),
      -- (defun name (params) body...)
      L._Dialect_commonLisp>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ (Lists.concat (list [
          list [Serialization.cst @@ string "defun", var "name"],
          list [Serialization.parens @@ (Serialization.spaceSep @@ var "params")],
          var "body"]))),
      -- (define (name params...) body...)
      L._Dialect_scheme>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ (Lists.concat (list [
          list [Serialization.cst @@ string "define"],
          list [Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat2 (list [var "name"]) (var "params"))],
          var "body"])))]

-- | Serialize an if expression: (if test then else)
ifExpressionToExpr :: TTermDefinition (L.Dialect -> L.IfExpression -> Expr)
ifExpressionToExpr = define "ifExpressionToExpr" $
  lambda "d" $ lambda "ifExpr" $ lets [
    "cond">: expressionToExpr @@ var "d" @@ (project L._IfExpression L._IfExpression_condition @@ var "ifExpr"),
    "then">: expressionToExpr @@ var "d" @@ (project L._IfExpression L._IfExpression_then @@ var "ifExpr"),
    "else">: project L._IfExpression L._IfExpression_else @@ var "ifExpr",
    "elsePart">: Maybes.maybe
      (list ([] :: [TTerm Expr]))
      (lambda "e" $ list [expressionToExpr @@ var "d" @@ var "e"])
      (var "else")] $
    Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat (list [
      list [Serialization.cst @@ string "if", var "cond", var "then"],
      var "elsePart"]))

-- | Serialize an import declaration
importDeclarationToExpr :: TTermDefinition (L.Dialect -> L.ImportDeclaration -> Expr)
importDeclarationToExpr = define "importDeclarationToExpr" $
  lambda "d" $ lambda "idecl" $ lets [
    "modName">: unwrap L._NamespaceName @@ (project L._ImportDeclaration L._ImportDeclaration_module @@ var "idecl")] $
    cases L._Dialect (var "d") Nothing [
      -- (:require [name])
      L._Dialect_clojure>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ list [
          Serialization.cst @@ string ":require",
          sqBrackets (list [Serialization.cst @@ var "modName"])]),
      -- (require 'name)
      L._Dialect_emacsLisp>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ list [
          Serialization.cst @@ string "require",
          Serialization.noSep @@ list [Serialization.cst @@ string "'", Serialization.cst @@ var "modName"]]),
      -- (:use :name)
      L._Dialect_commonLisp>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ list [
          Serialization.cst @@ string ":use",
          Serialization.cst @@ Strings.cat2 (string ":") (var "modName")]),
      -- (import (name))
      L._Dialect_scheme>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ list [
          Serialization.cst @@ string "import",
          Serialization.parens @@ (Serialization.cst @@ var "modName")])]

-- | Serialize a keyword: :name (or 'name in Scheme)
keywordToExpr :: TTermDefinition (L.Dialect -> L.Keyword -> Expr)
keywordToExpr = define "keywordToExpr" $
  lambda "d" $ lambda "k" $ lets [
    "name">: project L._Keyword L._Keyword_name @@ var "k",
    "ns">: project L._Keyword L._Keyword_namespace @@ var "k"] $
    cases L._Dialect (var "d") (Just $
      -- Default: :name or ns/:name
      Serialization.cst @@ Maybes.maybe
        (Strings.cat2 (string ":") (var "name"))
        (lambda "n" $ Strings.cat (list [var "n", string "/:", var "name"]))
        (var "ns")) [
      -- Scheme: 'name (quoted symbol, since Scheme has no keywords)
      L._Dialect_scheme>>: constant $
        Serialization.noSep @@ list [
          Serialization.cst @@ string "'",
          Serialization.cst @@ var "name"]]

-- | The keyword for anonymous functions
lambdaKeyword :: TTermDefinition (L.Dialect -> String)
lambdaKeyword = define "lambdaKeyword" $
  lambda "d" $ cases L._Dialect (var "d") Nothing [
    L._Dialect_clojure>>: constant $ string "fn",
    L._Dialect_emacsLisp>>: constant $ string "lambda",
    L._Dialect_commonLisp>>: constant $ string "cl:lambda",
    L._Dialect_scheme>>: constant $ string "lambda"]

-- | Serialize a lambda expression
lambdaToExpr :: TTermDefinition (L.Dialect -> L.Lambda -> Expr)
lambdaToExpr = define "lambdaToExpr" $
  lambda "d" $ lambda "lam" $ lets [
    "params">: Lists.map symbolToExpr (project L._Lambda L._Lambda_params @@ var "lam"),
    "body">: Lists.map (expressionToExpr @@ var "d") (project L._Lambda L._Lambda_body @@ var "lam"),
    "mname">: project L._Lambda L._Lambda_name @@ var "lam",
    "kw">: lambdaKeyword @@ var "d"] $
    cases L._Dialect (var "d") Nothing [
      -- (fn [params] body...) or (fn name [params] body...) if named
      L._Dialect_clojure>>: constant $
        Maybes.maybe
          -- Unnamed: (fn [params] body...)
          (Serialization.parens @@ (Serialization.spaceSep @@ (Lists.concat (list [
            list [Serialization.cst @@ var "kw"],
            list [sqBrackets (var "params")],
            var "body"]))))
          -- Named: (fn name [params] body...)
          (lambda "sym" $
            Serialization.parens @@ (Serialization.spaceSep @@ (Lists.concat (list [
              list [Serialization.cst @@ var "kw", symbolToExpr @@ var "sym"],
              list [sqBrackets (var "params")],
              var "body"]))))
          (var "mname"),
      -- (lambda (params) body...)
      L._Dialect_emacsLisp>>: constant $ lambdaOther (var "kw") (var "params") (var "body"),
      L._Dialect_commonLisp>>: constant $ lambdaOther (var "kw") (var "params") (var "body"),
      L._Dialect_scheme>>: constant $ lambdaOther (var "kw") (var "params") (var "body")]
  where
    lambdaOther :: TTerm String -> TTerm [Expr] -> TTerm [Expr] -> TTerm Expr
    lambdaOther kw params body =
      Serialization.parens @@ (Serialization.spaceSep @@ (Lists.concat (list [
        list [Serialization.cst @@ kw],
        list [Serialization.parens @@ (Serialization.spaceSep @@ params)],
        body])))

-- | Serialize a let expression
-- Scheme/CL/Elisp: (let ((name val) ...) body...) or (let*/letrec ...)
-- Clojure: (let [name val ...] body...)
letExpressionToExpr :: TTermDefinition (L.Dialect -> L.LetExpression -> Expr)
letExpressionToExpr = define "letExpressionToExpr" $
  lambda "d" $ lambda "letExpr" $ lets [
    "kind">: project L._LetExpression L._LetExpression_kind @@ var "letExpr",
    "bindings">: project L._LetExpression L._LetExpression_bindings @@ var "letExpr",
    "body">: Lists.map (expressionToExpr @@ var "d") (project L._LetExpression L._LetExpression_body @@ var "letExpr"),
    -- Extract name and value from each binding (all are simple bindings)
    "bindingPairs">: Lists.map (lambda "b" $
      cases L._LetBinding (var "b") Nothing [
        L._LetBinding_simple>>: lambda "sb" $ pair
          (symbolToExpr @@ (project L._SimpleBinding L._SimpleBinding_name @@ var "sb"))
          (expressionToExpr @@ var "d" @@ (project L._SimpleBinding L._SimpleBinding_value @@ var "sb")),
        L._LetBinding_destructuring>>: constant $ pair
          (Serialization.cst @@ string "<destructuring>")
          (Serialization.cst @@ string "<destructuring>")])
      (var "bindings")] $
    cases L._Dialect (var "d") Nothing [
      -- Clojure: (let [name val ...] body...) or (letfn [(name [params] body) ...] body...) for recursive
      L._Dialect_clojure>>: constant $
        cases L._LetKind (var "kind") Nothing [
          -- Recursive: emit letfn (Clojure's mutually recursive form for fn bindings).
          -- The coder marks let recursive only when an SCC cycle exists, and in
          -- the cycles produced by the kernel all bindings are lambdas, so letfn
          -- is well-formed. Non-lambda bindings would need thunking, not handled here.
          L._LetKind_recursive>>: constant $
            clojureLetfn (var "d") (var "bindings") (var "body"),
          -- Non-recursive: (let [name val ...] body...)
          L._LetKind_parallel>>: constant $ clojureLet (var "bindingPairs") (var "body"),
          L._LetKind_sequential>>: constant $ clojureLet (var "bindingPairs") (var "body")],
      -- Others: (let/let*/letrec ((name val) ...) body...)
      L._Dialect_emacsLisp>>: constant $ letOther (var "kind") (var "bindingPairs") (var "body"),
      -- Common Lisp: emit letrec (the loader transforms it to labels or mutable cells)
      L._Dialect_commonLisp>>: constant $ letOther (var "kind") (var "bindingPairs") (var "body"),
      L._Dialect_scheme>>: constant $ letOther (var "kind") (var "bindingPairs") (var "body")]
  where
    clojureLet :: TTerm [(Expr, Expr)] -> TTerm [Expr] -> TTerm Expr
    clojureLet bindingPairs body =
      Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat (list [
        list [Serialization.cst @@ string "let"],
        list [sqBrackets (Lists.concat (Lists.map (lambda "p" $
          list [Pairs.first (var "p"), Pairs.second (var "p")]) bindingPairs))],
        body]))
    -- (letfn [(name [params] body) (name2 [params2] body2) ...] body)
    -- Each binding's value must be an Expression_lambda; otherwise it cannot
    -- legally appear inside letfn and we fall back to the underlying lambda
    -- serialization, producing invalid Clojure that will fail loudly at load.
    clojureLetfn :: TTerm L.Dialect -> TTerm [L.LetBinding] -> TTerm [Expr] -> TTerm Expr
    clojureLetfn d bindings body = lets [
      "fnSpecs">: Lists.map (lambda "b" $
        cases L._LetBinding (var "b") Nothing [
          L._LetBinding_simple>>: lambda "sb" $ lets [
            "name">: symbolToExpr @@ (project L._SimpleBinding L._SimpleBinding_name @@ var "sb"),
            "val">: project L._SimpleBinding L._SimpleBinding_value @@ var "sb"] $
            cases L._Expression (var "val") (Just $
              Serialization.parens @@ (Serialization.spaceSep @@ list [
                var "name", expressionToExpr @@ d @@ var "val"])) [
              L._Expression_lambda>>: lambda "lam" $ lets [
                "params">: Lists.map symbolToExpr (project L._Lambda L._Lambda_params @@ var "lam"),
                "lbody">: Lists.map (expressionToExpr @@ d) (project L._Lambda L._Lambda_body @@ var "lam")] $
                Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat (list [
                  list [var "name"],
                  list [sqBrackets (var "params")],
                  var "lbody"]))],
          L._LetBinding_destructuring>>: constant $
            Serialization.cst @@ string "<destructuring>"])
        bindings] $
      Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat (list [
        list [Serialization.cst @@ string "letfn"],
        list [sqBrackets (var "fnSpecs")],
        body]))
    letOther :: TTerm L.LetKind -> TTerm [(Expr, Expr)] -> TTerm [Expr] -> TTerm Expr
    letOther kind bindingPairs body = lets [
      "kw">: cases L._LetKind kind Nothing [
        L._LetKind_parallel>>: constant $ string "let",
        L._LetKind_sequential>>: constant $ string "let*",
        L._LetKind_recursive>>: constant $ string "letrec"],
      "bindingExprs">: Lists.map (lambda "p" $
        Serialization.parens @@ (Serialization.spaceSep @@ list [
          Pairs.first (var "p"), Pairs.second (var "p")]))
        bindingPairs] $
      Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat (list [
        list [Serialization.cst @@ var "kw"],
        list [Serialization.parens @@ (Serialization.spaceSep @@ var "bindingExprs")],
        body]))

-- | The keyword for list construction
listKeyword :: TTermDefinition (L.Dialect -> String)
listKeyword = define "listKeyword" $
  lambda "d" $ cases L._Dialect (var "d") Nothing [
    L._Dialect_clojure>>: constant $ string "list",
    L._Dialect_emacsLisp>>: constant $ string "list",
    L._Dialect_commonLisp>>: constant $ string "cl:list",
    L._Dialect_scheme>>: constant $ string "list"]

-- | Serialize a list literal
listLiteralToExpr :: TTermDefinition (L.Dialect -> L.ListLiteral -> Expr)
listLiteralToExpr = define "listLiteralToExpr" $
  lambda "d" $ lambda "ll" $ lets [
    "elems">: Lists.map (expressionToExpr @@ var "d") (project L._ListLiteral L._ListLiteral_elements @@ var "ll"),
    "quoted">: project L._ListLiteral L._ListLiteral_quoted @@ var "ll"] $
    Logic.ifElse (var "quoted")
      -- '(a b c)
      (Serialization.noSep @@ list [
        Serialization.cst @@ string "'",
        Serialization.parens @@ (Serialization.spaceSep @@ var "elems")])
      -- (list a b c) or (cl:list a b c)
      (Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat2
        (list [Serialization.cst @@ (listKeyword @@ var "d")])
        (var "elems")))

-- | Format a bigfloat value as a dialect-specific literal string.
-- Special values (NaN, ±Infinity) use dialect-specific syntax.
formatLispFloat :: TTermDefinition (L.Dialect -> Double -> String)
formatLispFloat = define "formatLispFloat" $
  lambda "d" $ lambda "v" $
    "s" <~ Literals.showBigfloat (var "v") $
    Logic.ifElse (Equality.equal (var "s") (string "NaN"))
      (cases L._Dialect (var "d") Nothing [
        L._Dialect_clojure>>: constant $ string "Double/NaN",
        L._Dialect_scheme>>: constant $ string "+nan.0",
        L._Dialect_commonLisp>>: constant $ string "+hydra-nan+",
        L._Dialect_emacsLisp>>: constant $ string "0.0e+NaN"]) $
    Logic.ifElse (Equality.equal (var "s") (string "Infinity"))
      (cases L._Dialect (var "d") Nothing [
        L._Dialect_clojure>>: constant $ string "Double/POSITIVE_INFINITY",
        L._Dialect_scheme>>: constant $ string "+inf.0",
        L._Dialect_commonLisp>>: constant $ string "+hydra-pos-inf+",
        L._Dialect_emacsLisp>>: constant $ string "1.0e+INF"]) $
    Logic.ifElse (Equality.equal (var "s") (string "-Infinity"))
      (cases L._Dialect (var "d") Nothing [
        L._Dialect_clojure>>: constant $ string "Double/NEGATIVE_INFINITY",
        L._Dialect_scheme>>: constant $ string "-inf.0",
        L._Dialect_commonLisp>>: constant $ string "+hydra-neg-inf+",
        L._Dialect_emacsLisp>>: constant $ string "-1.0e+INF"])
      (var "s")

-- | Serialize a literal value
literalToExpr :: TTermDefinition (L.Dialect -> L.Literal -> Expr)
literalToExpr = define "literalToExpr" $
  lambda "d" $ lambda "lit" $
    cases L._Literal (var "lit") Nothing [
      L._Literal_integer>>: lambda "i" $
        Serialization.cst @@ (Literals.showBigint (project L._IntegerLiteral L._IntegerLiteral_value @@ var "i")),
      L._Literal_float>>: lambda "f" $
        Serialization.cst @@ (formatLispFloat @@ var "d" @@ (project L._FloatLiteral L._FloatLiteral_value @@ var "f")),
      L._Literal_string>>: lambda "s" $
        -- Escape backslashes first, then control characters and double-quotes.
        -- Common Lisp does not support \n, \t, \r escape sequences in strings,
        -- so for CL we embed the literal characters directly (multi-line strings are valid).
        "e1" <~ Strings.intercalate (string "\\\\") (Strings.splitOn (string "\\") (var "s")) $
        cases L._Dialect (var "d") Nothing [
          L._Dialect_commonLisp>>: constant $
            -- CL only: escape double-quotes; leave control characters as literal bytes
            "escaped" <~ Strings.intercalate (string "\\\"") (Strings.splitOn (string "\"") (var "e1")) $
            Serialization.cst @@ (Strings.cat (list [string "\"", var "escaped", string "\""])),
          L._Dialect_clojure>>: constant $
            "e2" <~ Strings.intercalate (string "\\n") (Strings.splitOn (Strings.fromList (list [int32 10])) (var "e1")) $
            "e3" <~ Strings.intercalate (string "\\r") (Strings.splitOn (Strings.fromList (list [int32 13])) (var "e2")) $
            "e4" <~ Strings.intercalate (string "\\t") (Strings.splitOn (Strings.fromList (list [int32 9])) (var "e3")) $
            "escaped" <~ Strings.intercalate (string "\\\"") (Strings.splitOn (string "\"") (var "e4")) $
            Serialization.cst @@ (Strings.cat (list [string "\"", var "escaped", string "\""])),
          L._Dialect_emacsLisp>>: constant $
            "e2" <~ Strings.intercalate (string "\\n") (Strings.splitOn (Strings.fromList (list [int32 10])) (var "e1")) $
            "e3" <~ Strings.intercalate (string "\\r") (Strings.splitOn (Strings.fromList (list [int32 13])) (var "e2")) $
            "e4" <~ Strings.intercalate (string "\\t") (Strings.splitOn (Strings.fromList (list [int32 9])) (var "e3")) $
            "escaped" <~ Strings.intercalate (string "\\\"") (Strings.splitOn (string "\"") (var "e4")) $
            Serialization.cst @@ (Strings.cat (list [string "\"", var "escaped", string "\""])),
          L._Dialect_scheme>>: constant $
            "e2" <~ Strings.intercalate (string "\\n") (Strings.splitOn (Strings.fromList (list [int32 10])) (var "e1")) $
            "e3" <~ Strings.intercalate (string "\\r") (Strings.splitOn (Strings.fromList (list [int32 13])) (var "e2")) $
            "e4" <~ Strings.intercalate (string "\\t") (Strings.splitOn (Strings.fromList (list [int32 9])) (var "e3")) $
            "escaped" <~ Strings.intercalate (string "\\\"") (Strings.splitOn (string "\"") (var "e4")) $
            Serialization.cst @@ (Strings.cat (list [string "\"", var "escaped", string "\""]))],
      L._Literal_character>>: lambda "c" $ lets [
        "ch">: project L._CharacterLiteral L._CharacterLiteral_value @@ var "c"] $
        cases L._Dialect (var "d") Nothing [
          L._Dialect_clojure>>: constant $ Serialization.cst @@ Strings.cat2 (string "\\") (var "ch"),
          L._Dialect_emacsLisp>>: constant $ Serialization.cst @@ Strings.cat2 (string "?") (var "ch"),
          L._Dialect_commonLisp>>: constant $ Serialization.cst @@ Strings.cat2 (string "#\\") (var "ch"),
          L._Dialect_scheme>>: constant $ Serialization.cst @@ Strings.cat2 (string "#\\") (var "ch")],
      L._Literal_boolean>>: lambda "b" $
        Logic.ifElse (var "b") (trueExpr @@ var "d") (falseExpr @@ var "d"),
      L._Literal_nil>>: constant $ nilExpr @@ var "d",
      L._Literal_keyword>>: lambda "k" $ keywordToExpr @@ var "d" @@ var "k",
      L._Literal_symbol>>: lambda "s" $
        Serialization.noSep @@ list [Serialization.cst @@ string "'", symbolToExpr @@ var "s"]]

-- | Serialize a macro definition
macroDefinitionToExpr :: TTermDefinition (L.Dialect -> L.MacroDefinition -> Expr)
macroDefinitionToExpr = define "macroDefinitionToExpr" $
  lambda "d" $ lambda "mdef" $ lets [
    "name">: symbolToExpr @@ (project L._MacroDefinition L._MacroDefinition_name @@ var "mdef"),
    "params">: Lists.map symbolToExpr (project L._MacroDefinition L._MacroDefinition_params @@ var "mdef"),
    "body">: Lists.map (expressionToExpr @@ var "d") (project L._MacroDefinition L._MacroDefinition_body @@ var "mdef")] $
    cases L._Dialect (var "d") Nothing [
      -- (defmacro name [params] body)
      L._Dialect_clojure>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ (Lists.concat (list [
          list [Serialization.cst @@ string "defmacro", var "name"],
          list [sqBrackets (var "params")],
          var "body"]))),
      -- (defmacro name (params) body)
      L._Dialect_emacsLisp>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ (Lists.concat (list [
          list [Serialization.cst @@ string "defmacro", var "name"],
          list [Serialization.parens @@ (Serialization.spaceSep @@ var "params")],
          var "body"]))),
      -- (defmacro name (params) body)
      L._Dialect_commonLisp>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ (Lists.concat (list [
          list [Serialization.cst @@ string "defmacro", var "name"],
          list [Serialization.parens @@ (Serialization.spaceSep @@ var "params")],
          var "body"]))),
      -- (define-syntax name ...)
      L._Dialect_scheme>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ (Lists.concat (list [
          list [Serialization.cst @@ string "define-syntax", var "name"],
          var "body"])))]

-- | Serialize a map literal
mapLiteralToExpr :: TTermDefinition (L.Dialect -> L.MapLiteral -> Expr)
mapLiteralToExpr = define "mapLiteralToExpr" $
  lambda "d" $ lambda "ml" $ lets [
    "entries">: project L._MapLiteral L._MapLiteral_entries @@ var "ml"] $
    cases L._Dialect (var "d") Nothing [
      -- {:key1 val1 :key2 val2} in Clojure
      L._Dialect_clojure>>: constant $
        Serialization.brackets @@ (asTerm Serialization.curlyBraces) @@ (asTerm Serialization.inlineStyle) @@
          (Serialization.spaceSep @@ (Lists.concat (Lists.map (lambda "e" $ list [
            expressionToExpr @@ var "d" @@ (project L._MapEntry L._MapEntry_key @@ var "e"),
            expressionToExpr @@ var "d" @@ (project L._MapEntry L._MapEntry_value @@ var "e")])
            (var "entries")))),
      -- alist: '((key1 . val1) (key2 . val2)) in CL/EL — quoted because CL defstruct
      -- constructors take keyword args, and the bootstrap data uses positional calls
      L._Dialect_emacsLisp>>: constant $ mapAsAlist (var "d") (var "entries"),
      L._Dialect_commonLisp>>: constant $ mapAsAlist (var "d") (var "entries"),
      -- (list (cons key1 val1) (cons key2 val2) ...) in Scheme — evaluated, not quoted
      L._Dialect_scheme>>: constant $ mapAsConsExpressions (var "d") (var "entries")]
  where
    mapAsAlist :: TTerm L.Dialect -> TTerm [L.MapEntry] -> TTerm Expr
    mapAsAlist d entries =
      Serialization.noSep @@ list [
        Serialization.cst @@ string "'",
        Serialization.parens @@ (Serialization.spaceSep @@
          Lists.map (lambda "e" $
            Serialization.parens @@ (Serialization.spaceSep @@ list [
              expressionToExpr @@ d @@ (project L._MapEntry L._MapEntry_key @@ var "e"),
              Serialization.cst @@ string ".",
              expressionToExpr @@ d @@ (project L._MapEntry L._MapEntry_value @@ var "e")]))
            entries)]
    mapAsConsExpressions :: TTerm L.Dialect -> TTerm [L.MapEntry] -> TTerm Expr
    mapAsConsExpressions d entries =
      Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat2
        (list [Serialization.cst @@ string "list"])
        (Lists.map (lambda "e" $
          Serialization.parens @@ (Serialization.spaceSep @@ list [
            Serialization.cst @@ string "cons",
            expressionToExpr @@ d @@ (project L._MapEntry L._MapEntry_key @@ var "e"),
            expressionToExpr @@ d @@ (project L._MapEntry L._MapEntry_value @@ var "e")]))
          entries))

-- | Serialize a module declaration
moduleDeclarationToExpr :: TTermDefinition (L.Dialect -> L.ModuleDeclaration -> Expr)
moduleDeclarationToExpr = define "moduleDeclarationToExpr" $
  lambda "d" $ lambda "mdecl" $ lets [
    "name">: unwrap L._NamespaceName @@ (project L._ModuleDeclaration L._ModuleDeclaration_name @@ var "mdecl")] $
    cases L._Dialect (var "d") Nothing [
      -- (ns name)
      L._Dialect_clojure>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ list [
          Serialization.cst @@ string "ns",
          Serialization.cst @@ var "name"]),
      -- (require 'cl-lib) (provide 'name)
      L._Dialect_emacsLisp>>: constant $
        Serialization.newlineSep @@ list [
          Serialization.parens @@ (Serialization.spaceSep @@ list [
            Serialization.cst @@ string "require",
            Serialization.noSep @@ list [Serialization.cst @@ string "'", Serialization.cst @@ string "cl-lib"]]),
          Serialization.parens @@ (Serialization.spaceSep @@ list [
            Serialization.cst @@ string "provide",
            Serialization.noSep @@ list [Serialization.cst @@ string "'", Serialization.cst @@ var "name"]])],
      -- (defpackage :name) (in-package :name)
      L._Dialect_commonLisp>>: constant $
        Serialization.newlineSep @@ list [
          Serialization.parens @@ (Serialization.spaceSep @@ list [
            Serialization.cst @@ string "defpackage",
            Serialization.cst @@ Strings.cat2 (string ":") (var "name")]),
          Serialization.parens @@ (Serialization.spaceSep @@ list [
            Serialization.cst @@ string "in-package",
            Serialization.cst @@ Strings.cat2 (string ":") (var "name")])],
      -- (define-library (name))
      L._Dialect_scheme>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ list [
          Serialization.cst @@ string "define-library",
          Serialization.parens @@ (Serialization.cst @@ var "name")])]

-- | Nil expression
nilExpr :: TTermDefinition (L.Dialect -> Expr)
nilExpr = define "nilExpr" $
  lambda "d" $ cases L._Dialect (var "d") Nothing [
    L._Dialect_clojure>>: constant $ Serialization.cst @@ string "nil",
    L._Dialect_emacsLisp>>: constant $ Serialization.cst @@ string "nil",
    L._Dialect_commonLisp>>: constant $ Serialization.cst @@ string "cl:nil",
    L._Dialect_scheme>>: constant $ Serialization.cst @@ string "'()"]

-- | Serialize a not expression: (not expr)
notExpressionToExpr :: TTermDefinition (L.Dialect -> L.NotExpression -> Expr)
notExpressionToExpr = define "notExpressionToExpr" $
  lambda "d" $ lambda "notExpr" $
    Serialization.parens @@ (Serialization.spaceSep @@ list [
      Serialization.cst @@ string "not",
      expressionToExpr @@ var "d" @@ (project L._NotExpression L._NotExpression_expression @@ var "notExpr")])

-- | Serialize an or expression: (or expr1 expr2 ...)
orExpressionToExpr :: TTermDefinition (L.Dialect -> L.OrExpression -> Expr)
orExpressionToExpr = define "orExpressionToExpr" $
  lambda "d" $ lambda "orExpr" $
    Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat2
      (list [Serialization.cst @@ string "or"])
      (Lists.map (expressionToExpr @@ var "d") (project L._OrExpression L._OrExpression_expressions @@ var "orExpr")))

-- | Serialize a full Lisp program
programToExpr :: TTermDefinition (L.Program -> Expr)
programToExpr = define "programToExpr" $
  lambda "prog" $ lets [
    "d">: project L._Program L._Program_dialect @@ var "prog",
    "modDecl">: project L._Program L._Program_module @@ var "prog",
    "imports">: project L._Program L._Program_imports @@ var "prog",
    "exports">: project L._Program L._Program_exports @@ var "prog",
    "forms">: project L._Program L._Program_forms @@ var "prog",
    "formPart">: Lists.map (topLevelFormWithCommentsToExpr @@ var "d") (var "forms"),
    -- Helper: get import module names as strings
    "importNames">: Lists.map (lambda "idecl" $
      unwrap L._NamespaceName @@ (project L._ImportDeclaration L._ImportDeclaration_module @@ var "idecl"))
      (var "imports"),
    -- Helper: get all export symbols as expr strings
    "exportSyms">: Lists.concat (Lists.map (lambda "edecl" $
      Lists.map symbolToExpr (project L._ExportDeclaration L._ExportDeclaration_symbols @@ var "edecl"))
      (var "exports"))] $
    cases L._Dialect (var "d") Nothing [
      -- Clojure: (ns name (:require [dep1 :refer :all] [dep2 :refer :all] ...)) then forms
      L._Dialect_clojure>>: constant $
        Maybes.maybe
          (Serialization.doubleNewlineSep @@ var "formPart")
          (lambda "m" $ lets [
            "nameStr">: unwrap L._NamespaceName @@ (project L._ModuleDeclaration L._ModuleDeclaration_name @@ var "m"),
            "requireClauses">: Lists.map (lambda "imp" $
              sqBrackets (list [
                Serialization.cst @@ var "imp",
                Serialization.cst @@ string ":refer",
                Serialization.cst @@ string ":all"]))
              (var "importNames"),
            "nsForm">: Logic.ifElse (Lists.null (var "requireClauses"))
              (Serialization.parens @@ (Serialization.spaceSep @@ list [
                Serialization.cst @@ string "ns",
                Serialization.cst @@ var "nameStr"]))
              (Serialization.parens @@ (Serialization.newlineSep @@ list [
                Serialization.spaceSep @@ list [
                  Serialization.cst @@ string "ns",
                  Serialization.cst @@ var "nameStr"],
                Serialization.spaceSep @@ Lists.concat2
                  (list [Serialization.cst @@ string "  (:require"])
                  (var "requireClauses"),
                Serialization.cst @@ string ")"]))] $
            -- Extract variable definition names for forward declaration
            "varNames" <~ Lists.concat (Lists.map (lambda "fwc" $
              lets ["form">: project L._TopLevelFormWithComments L._TopLevelFormWithComments_form @@ var "fwc"] $
              cases L._TopLevelForm (var "form") (Just (list ([] :: [TTerm Expr]))) [
                L._TopLevelForm_variable>>: lambda "vd" $
                  list [symbolToExpr @@ (project L._VariableDefinition L._VariableDefinition_name @@ var "vd")],
                L._TopLevelForm_function>>: lambda "fd" $
                  list [symbolToExpr @@ (project L._FunctionDefinition L._FunctionDefinition_name @@ var "fd")]])
              (var "forms")) $
            -- (declare name1 name2 ...) for forward references
            "declareForm" <~ Logic.ifElse (Lists.null (var "varNames"))
              (list ([] :: [TTerm Expr]))
              (list [Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat2
                (list [Serialization.cst @@ string "declare"])
                (var "varNames"))]) $
            Serialization.doubleNewlineSep @@ Lists.concat (list [
              list [var "nsForm"],
              var "declareForm",
              var "formPart"]))
          (var "modDecl"),
      -- Emacs Lisp: (require 'cl-lib) (require 'dep1) ... forms... (provide 'name)
      L._Dialect_emacsLisp>>: constant $
        Maybes.maybe
          (Serialization.doubleNewlineSep @@ var "formPart")
          (lambda "m" $ lets [
            "nameStr">: unwrap L._NamespaceName @@ (project L._ModuleDeclaration L._ModuleDeclaration_name @@ var "m"),
            "requireClLib">: Serialization.parens @@ (Serialization.spaceSep @@ list [
              Serialization.cst @@ string "require",
              Serialization.noSep @@ list [Serialization.cst @@ string "'", Serialization.cst @@ string "cl-lib"]]),
            "requireImports">: Lists.map (lambda "imp" $
              Serialization.parens @@ (Serialization.spaceSep @@ list [
                Serialization.cst @@ string "require",
                Serialization.noSep @@ list [Serialization.cst @@ string "'", Serialization.cst @@ var "imp"]]))
              (var "importNames"),
            "provideForm">: Serialization.parens @@ (Serialization.spaceSep @@ list [
              Serialization.cst @@ string "provide",
              Serialization.noSep @@ list [Serialization.cst @@ string "'", Serialization.cst @@ var "nameStr"]])] $
            Serialization.doubleNewlineSep @@ Lists.concat (list [
              list [var "requireClLib"],
              var "requireImports",
              var "formPart",
              list [var "provideForm"]]))
          (var "modDecl"),
      -- Common Lisp: (defpackage :name (:use :cl :dep1 :dep2 ...) (:export :sym1 :sym2 ...)) (in-package :name) forms...
      L._Dialect_commonLisp>>: constant $
        Maybes.maybe
          (Serialization.doubleNewlineSep @@ var "formPart")
          (lambda "m" $ lets [
            "nameStr">: unwrap L._NamespaceName @@ (project L._ModuleDeclaration L._ModuleDeclaration_name @@ var "m"),
            "colonName">: Strings.cat2 (string ":") (var "nameStr"),
            -- (:use :cl :dep1 :dep2 ...)
            "useClause">: Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat2
              (list [Serialization.cst @@ string ":use", Serialization.cst @@ string ":cl"])
              (Lists.map (lambda "imp" $ Serialization.cst @@ Strings.cat2 (string ":") (var "imp"))
                (var "importNames"))),
            -- (:export :sym1 :sym2 ...)
            "exportClause">: Logic.ifElse (Lists.null (var "exportSyms"))
              (list ([] :: [TTerm Expr]))
              (list [Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat2
                (list [Serialization.cst @@ string ":export"])
                (Lists.map (lambda "s" $ Serialization.noSep @@ list [Serialization.cst @@ string ":", var "s"]) (var "exportSyms")))]),
            -- (defpackage :name (:use ...) (:export ...))
            "defpkgForm">: Serialization.parens @@ (Serialization.newlineSep @@ Lists.concat (list [
              list [Serialization.spaceSep @@ list [
                Serialization.cst @@ string "defpackage",
                Serialization.cst @@ var "colonName"]],
              list [var "useClause"],
              var "exportClause"])),
            "inpkgForm">: Serialization.parens @@ (Serialization.spaceSep @@ list [
              Serialization.cst @@ string "in-package",
              Serialization.cst @@ var "colonName"])] $
            Serialization.doubleNewlineSep @@ Lists.concat (list [
              list [var "defpkgForm", var "inpkgForm"],
              var "formPart"]))
          (var "modDecl"),
      -- Scheme: wrap everything in (define-library (name ...) (export ...) (import ...) (begin ...))
      L._Dialect_scheme>>: constant $
        Maybes.maybe
          (Serialization.doubleNewlineSep @@ var "formPart")
          (lambda "m" $ lets [
            "nameStr">: unwrap L._NamespaceName @@ (project L._ModuleDeclaration L._ModuleDeclaration_name @@ var "m"),
            "nameParts">: Lists.map (lambda "p" $ Formatting.convertCaseCamelToLowerSnake @@ var "p")
              (Strings.splitOn (string ".") (var "nameStr")),
            "nameExpr">: Serialization.parens @@ (Serialization.spaceSep @@
              Lists.map (lambda "p" $ Serialization.cst @@ var "p") (var "nameParts")),
            -- Build import library names: split each namespace on dots for R7RS (import (hydra core) ...)
            "domainImportExprs">: Lists.map (lambda "idecl" $ lets [
              "nsName">: unwrap L._NamespaceName @@ (project L._ImportDeclaration L._ImportDeclaration_module @@ var "idecl"),
              "nsParts">: Lists.map (lambda "p" $ Formatting.convertCaseCamelToLowerSnake @@ var "p")
                (Strings.splitOn (string ".") (var "nsName"))] $
              Serialization.parens @@ (Serialization.spaceSep @@
                Lists.map (lambda "p" $ Serialization.cst @@ var "p") (var "nsParts")))
              (var "imports"),
            -- Combine (scheme base) + domain imports into a single (import ...) clause
            "schemeBaseExpr">: Serialization.parens @@ (Serialization.spaceSep @@ list [
              Serialization.cst @@ string "scheme",
              Serialization.cst @@ string "base"]),
            "allImportExprs">: Lists.concat2
              (list [var "schemeBaseExpr"])
              (var "domainImportExprs"),
            "importClause">: Serialization.parens @@ (Serialization.spaceSep @@
              Lists.concat2 (list [Serialization.cst @@ string "import"]) (var "allImportExprs")),
            -- Export clause (only if there are exports)
            "exportClauses">: Lists.map (lambda "edecl" $ exportDeclarationToExpr @@ var "d" @@ var "edecl") (var "exports"),
            "beginClause">: Serialization.parens @@ (Serialization.newlineSep @@
              Lists.concat2
                (list [Serialization.cst @@ string "begin"])
                (var "formPart"))] $
            Serialization.parens @@ (Serialization.newlineSep @@
              Lists.concat (list [
                list [Serialization.spaceSep @@ list [
                  Serialization.cst @@ string "define-library",
                  var "nameExpr"]],
                var "exportClauses",
                list [var "importClause"],
                list [var "beginClause"]])))
          (var "modDecl")]

-- | Serialize a record type definition
recordTypeDefinitionToExpr :: TTermDefinition (L.Dialect -> L.RecordTypeDefinition -> Expr)
recordTypeDefinitionToExpr = define "recordTypeDefinitionToExpr" $
  lambda "d" $ lambda "rdef" $ lets [
    "name">: symbolToExpr @@ (project L._RecordTypeDefinition L._RecordTypeDefinition_name @@ var "rdef"),
    "fields">: Lists.map (lambda "f" $ symbolToExpr @@ (project L._FieldDefinition L._FieldDefinition_name @@ var "f"))
      (project L._RecordTypeDefinition L._RecordTypeDefinition_fields @@ var "rdef")] $
    cases L._Dialect (var "d") Nothing [
      -- (defrecord Name [field1 field2])
      -- Also emit (defn make-Name [field1 field2] (->Name field1 field2)) for uniform constructor calls
      L._Dialect_clojure>>: constant $ lets [
        "nameStr">: unwrap L._Symbol @@ (project L._RecordTypeDefinition L._RecordTypeDefinition_name @@ var "rdef"),
        "fieldNames">: Lists.map (lambda "f" $ unwrap L._Symbol @@ (project L._FieldDefinition L._FieldDefinition_name @@ var "f"))
          (project L._RecordTypeDefinition L._RecordTypeDefinition_fields @@ var "rdef"),
        "defrecordForm">: Serialization.parens @@ (Serialization.spaceSep @@ list [
          Serialization.cst @@ string "defrecord",
          var "name",
          sqBrackets (var "fields")]),
        "makeAlias">: Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat (list [
          list [Serialization.cst @@ string "defn",
            Serialization.cst @@ Strings.cat2 (string "make-") (var "nameStr")],
          list [sqBrackets (var "fields")],
          list [Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat2
            (list [Serialization.cst @@ Strings.cat2 (string "->") (var "nameStr")])
            (Lists.map (lambda "fn" $ Serialization.cst @@ var "fn") (var "fieldNames")))]]))] $
        Serialization.newlineSep @@ list [var "defrecordForm", var "makeAlias"],
      -- (cl-defstruct name field1 field2)
      L._Dialect_emacsLisp>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ (Lists.concat (list [
          list [Serialization.cst @@ string "cl-defstruct", var "name"],
          var "fields"]))),
      -- (cl:defstruct name field1 field2)
      L._Dialect_commonLisp>>: constant $
        Serialization.parens @@ (Serialization.spaceSep @@ (Lists.concat (list [
          list [Serialization.cst @@ string "cl:defstruct", var "name"],
          var "fields"]))),
      -- R7RS/SRFI-9: (define-record-type name (make-name f1 f2) name? (f1 name-f1) (f2 name-f2))
      -- The Coder passes snake_case names directly, so no case conversion is needed here.
      L._Dialect_scheme>>: constant $ lets [
        "nameStr">: unwrap L._Symbol @@ (project L._RecordTypeDefinition L._RecordTypeDefinition_name @@ var "rdef"),
        "fieldNames">: Lists.map (lambda "f" $ unwrap L._Symbol @@ (project L._FieldDefinition L._FieldDefinition_name @@ var "f"))
          (project L._RecordTypeDefinition L._RecordTypeDefinition_fields @@ var "rdef"),
        "constructor">: Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat2
          (list [Serialization.cst @@ Strings.cat2 (string "make-") (var "nameStr")])
          (Lists.map (lambda "fn" $ Serialization.cst @@ var "fn") (var "fieldNames"))),
        "predicate">: Serialization.cst @@ Strings.cat2 (var "nameStr") (string "?"),
        "accessors">: Lists.map (lambda "fn" $
          Serialization.parens @@ (Serialization.spaceSep @@ list [
            Serialization.cst @@ var "fn",
            Serialization.cst @@ Strings.cat (list [var "nameStr", string "-", var "fn"])]))
          (var "fieldNames")] $
        Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat (list [
          list [Serialization.cst @@ string "define-record-type", var "name", var "constructor", var "predicate"],
          var "accessors"]))]

-- | Serialize an S-expression (escape hatch)
sExpressionToExpr :: TTermDefinition (L.SExpression -> Expr)
sExpressionToExpr = define "sExpressionToExpr" $
  lambda "sexpr" $
    cases L._SExpression (var "sexpr") Nothing [
      L._SExpression_atom>>: lambda "a" $ Serialization.cst @@ var "a",
      L._SExpression_list>>: lambda "elems" $
        Serialization.parens @@ (Serialization.spaceSep @@ Lists.map sExpressionToExpr (var "elems"))]

-- | Serialize a set literal
setLiteralToExpr :: TTermDefinition (L.Dialect -> L.SetLiteral -> Expr)
setLiteralToExpr = define "setLiteralToExpr" $
  lambda "d" $ lambda "sl" $ lets [
    "elems">: Lists.map (expressionToExpr @@ var "d") (project L._SetLiteral L._SetLiteral_elements @@ var "sl")] $
    cases L._Dialect (var "d") Nothing [
      -- #{1 2 3} in Clojure
      L._Dialect_clojure>>: constant $
        Serialization.noSep @@ list [
          Serialization.cst @@ string "#",
          Serialization.brackets @@ (asTerm Serialization.curlyBraces) @@ (asTerm Serialization.inlineStyle) @@
            (Serialization.spaceSep @@ var "elems")],
      -- (list->set (list 1 2 3)) as a fallback in others
      L._Dialect_emacsLisp>>: constant $ setAsList (string "list") (var "elems"),
      L._Dialect_commonLisp>>: constant $ setAsList (string "cl:list") (var "elems"),
      L._Dialect_scheme>>: constant $ setAsList (string "list") (var "elems")]
  where
    setAsList :: TTerm String -> TTerm [Expr] -> TTerm Expr
    setAsList kw elems =
      Serialization.parens @@ (Serialization.spaceSep @@ Lists.concat2
        (list [Serialization.cst @@ kw])
        elems)

-- | Serialize a symbol
symbolToExpr :: TTermDefinition (L.Symbol -> Expr)
symbolToExpr = define "symbolToExpr" $
  lambda "s" $ Serialization.cst @@ (unwrap L._Symbol @@ var "s")

-- | Serialize a top-level form
topLevelFormToExpr :: TTermDefinition (L.Dialect -> L.TopLevelForm -> Expr)
topLevelFormToExpr = define "topLevelFormToExpr" $
  lambda "d" $ lambda "form" $
    cases L._TopLevelForm (var "form") Nothing [
      L._TopLevelForm_function>>: lambda "f" $ functionDefinitionToExpr @@ var "d" @@ var "f",
      L._TopLevelForm_variable>>: lambda "v" $ variableDefinitionToExpr @@ var "d" @@ var "v",
      L._TopLevelForm_constant>>: lambda "c" $ constantDefinitionToExpr @@ var "d" @@ var "c",
      L._TopLevelForm_recordType>>: lambda "r" $ recordTypeDefinitionToExpr @@ var "d" @@ var "r",
      L._TopLevelForm_macro>>: lambda "m" $ macroDefinitionToExpr @@ var "d" @@ var "m",
      L._TopLevelForm_expression>>: lambda "e" $ expressionToExpr @@ var "d" @@ var "e"]

-- | Serialize a top-level form with comments
topLevelFormWithCommentsToExpr :: TTermDefinition (L.Dialect -> L.TopLevelFormWithComments -> Expr)
topLevelFormWithCommentsToExpr = define "topLevelFormWithCommentsToExpr" $
  lambda "d" $ lambda "fwc" $ lets [
    "mdoc">: project L._TopLevelFormWithComments L._TopLevelFormWithComments_doc @@ var "fwc",
    "mcomment">: project L._TopLevelFormWithComments L._TopLevelFormWithComments_comment @@ var "fwc",
    "form">: project L._TopLevelFormWithComments L._TopLevelFormWithComments_form @@ var "fwc",
    "docPart">: Maybes.maybe
      (list ([] :: [TTerm Expr]))
      (lambda "ds" $ list [docstringToExpr @@ var "ds"])
      (var "mdoc"),
    "commentPart">: Maybes.maybe
      (list ([] :: [TTerm Expr]))
      (lambda "c" $ list [commentToExpr @@ var "c"])
      (var "mcomment"),
    "formExpr">: topLevelFormToExpr @@ var "d" @@ var "form"] $
    Serialization.newlineSep @@ (Lists.concat (list [
      var "commentPart", var "docPart", list [var "formExpr"]]))

-- | Boolean true expression
trueExpr :: TTermDefinition (L.Dialect -> Expr)
trueExpr = define "trueExpr" $
  lambda "d" $ cases L._Dialect (var "d") Nothing [
    L._Dialect_clojure>>: constant $ Serialization.cst @@ string "true",
    L._Dialect_emacsLisp>>: constant $ Serialization.cst @@ string "t",
    L._Dialect_commonLisp>>: constant $ Serialization.cst @@ string "cl:t",
    L._Dialect_scheme>>: constant $ Serialization.cst @@ string "#t"]

-- | Serialize a variable definition
variableDefinitionToExpr :: TTermDefinition (L.Dialect -> L.VariableDefinition -> Expr)
variableDefinitionToExpr = define "variableDefinitionToExpr" $
  lambda "d" $ lambda "vdef" $ lets [
    "name">: symbolToExpr @@ (project L._VariableDefinition L._VariableDefinition_name @@ var "vdef"),
    "value">: expressionToExpr @@ var "d" @@ (project L._VariableDefinition L._VariableDefinition_value @@ var "vdef")] $
    Serialization.parens @@ (Serialization.spaceSep @@ list [
      Serialization.cst @@ (defKeyword @@ var "d"),
      var "name",
      var "value"])

-- | Serialize a variable reference
variableReferenceToExpr :: TTermDefinition (L.Dialect -> L.VariableReference -> Expr)
variableReferenceToExpr = define "variableReferenceToExpr" $
  lambda "d" $ lambda "vref" $ lets [
    "name">: symbolToExpr @@ (project L._VariableReference L._VariableReference_name @@ var "vref"),
    "isFnNs">: project L._VariableReference L._VariableReference_functionNamespace @@ var "vref"] $
    Logic.ifElse (var "isFnNs")
      (cases L._Dialect (var "d") Nothing [
        L._Dialect_commonLisp>>: constant $
          Serialization.noSep @@ list [Serialization.cst @@ string "#'", var "name"],
        L._Dialect_clojure>>: constant $ var "name",
        L._Dialect_emacsLisp>>: constant $ var "name",
        L._Dialect_scheme>>: constant $ var "name"])
      (var "name")

-- | Serialize a vector literal
vectorLiteralToExpr :: TTermDefinition (L.Dialect -> L.VectorLiteral -> Expr)
vectorLiteralToExpr = define "vectorLiteralToExpr" $
  lambda "d" $ lambda "vl" $ lets [
    "elems">: Lists.map (expressionToExpr @@ var "d") (project L._VectorLiteral L._VectorLiteral_elements @@ var "vl")] $
    cases L._Dialect (var "d") Nothing [
      -- [1 2 3] in Clojure and Emacs Lisp
      L._Dialect_clojure>>: constant $ sqBrackets (var "elems"),
      L._Dialect_emacsLisp>>: constant $ sqBrackets (var "elems"),
      -- #(1 2 3) in Common Lisp and Scheme
      L._Dialect_commonLisp>>: constant $
        Serialization.noSep @@ list [
          Serialization.cst @@ string "#",
          Serialization.parens @@ (Serialization.spaceSep @@ var "elems")],
      L._Dialect_scheme>>: constant $
        Serialization.noSep @@ list [
          Serialization.cst @@ string "#",
          Serialization.parens @@ (Serialization.spaceSep @@ var "elems")]]