packages feed

hydra-0.15.0: src/main/haskell/Hydra/Sources/Rust/Syntax.hs

-- | A Rust syntax model, based on the Rust Reference grammar.
-- | See https://doc.rust-lang.org/reference/ (retrieved 2025-01-29)

module Hydra.Sources.Rust.Syntax where

-- Standard imports for type-level sources outside of the kernel
import           Hydra.Kernel
import           Hydra.Dsl.Annotations
import           Hydra.Dsl.Bootstrap
import           Hydra.Dsl.Types                 ((>:))
import qualified Hydra.Dsl.Types                 as T
import qualified Hydra.Sources.Kernel.Types.Core as Core
import qualified Data.List                       as L
import qualified Data.Map                        as M
import qualified Data.Set                        as S
import qualified Data.Maybe                      as Y


ns :: Namespace
ns = Namespace "hydra.rust.syntax"

define :: String -> Type -> Binding
define = datatype ns

rust :: String -> Type
rust = typeref ns

module_ :: Module
module_ = Module {
            moduleNamespace = ns,
            moduleDefinitions = (map toTypeDef definitions),
            moduleTermDependencies = [Core.ns],
            moduleTypeDependencies = [Core.ns],
            moduleDescription = Just ("A Rust syntax model, based on the Rust Reference grammar"
      ++ " (https://doc.rust-lang.org/reference/), retrieved 2025-01-29")}
  where
    definitions = crateLevel ++ items ++ useDeclarations ++ structDefinitions
      ++ enumDefinitions ++ functionDefinitions ++ typeAliases ++ constStaticMod
      ++ implBlocks ++ traitDefinitions ++ genericsAndWhere ++ types
      ++ expressions ++ statements ++ patterns ++ literals ++ attributesVisibility
      ++ helperTypes

    -- Crate-level constructs
    crateLevel = [
      crate,
      item,
      itemWithComments]

    -- Item definitions
    items = []

    -- Use declarations
    useDeclarations = [
      useDeclaration,
      useTree,
      usePath,
      useRename,
      useGroup]

    -- Struct definitions
    structDefinitions = [
      structDef,
      structBody,
      structField,
      tupleField]

    -- Enum definitions
    enumDefinitions = [
      enumDef,
      enumVariant,
      enumVariantBody]

    -- Function definitions
    functionDefinitions = [
      fnDef,
      fnParam,
      selfParam,
      methodParam]

    -- Type aliases
    typeAliases = [
      typeAlias]

    -- Const, static, and module definitions
    constStaticMod = [
      constDef,
      staticDef,
      modDef]

    -- Impl blocks
    implBlocks = [
      implBlock,
      implItem,
      implMethod]

    -- Trait definitions
    traitDefinitions = [
      traitDef,
      traitItem,
      traitMethod,
      traitType,
      traitConst]

    -- Generics and where clauses
    genericsAndWhere = [
      genericParam,
      typeParamBound,
      lifetime,
      whereClause,
      wherePredicate]

    -- Type expressions
    types = [
      type_,
      typePath,
      pathSegment,
      genericArguments,
      angleBracketedArgs,
      genericArg,
      typeBinding,
      parenthesizedArgs,
      referenceType,
      arrayType,
      fnPointerType,
      rawPointerType]

    -- Expressions
    expressions = [
      expression,
      exprPath,
      callExpr,
      methodCallExpr,
      fieldAccessExpr,
      tupleIndexExpr,
      closureExpr,
      closureParam,
      ifExpr,
      ifCondition,
      matchExpr,
      matchArm,
      loopExpr,
      whileExpr,
      forExpr,
      binaryExpr,
      binaryOp,
      unaryExpr,
      unaryOp,
      refExpr,
      structExpr,
      fieldValue,
      arrayExpr,
      indexExpr,
      rangeExpr,
      castExpr,
      typeAscriptionExpr,
      assignExpr,
      compoundAssignExpr,
      compoundAssignOp,
      macroInvocation,
      macroDelimiter]

    -- Statements
    statements = [
      statement,
      letStatement,
      block]

    -- Patterns
    patterns = [
      pattern,
      identifierPattern,
      refPattern,
      structPattern,
      fieldPattern,
      tupleStructPattern,
      rangePattern]

    -- Literals
    literals = [
      literal,
      integerLiteral,
      floatLiteral]

    -- Attributes and visibility
    attributesVisibility = [
      attribute,
      visibility]

    -- Helper types (used in expressions)
    helperTypes = [
      letCondition,
      arrayRepeat]


-- ================================================================================================
-- Crate-level / Module structure
-- ================================================================================================

crate :: Binding
crate = define "Crate" $
  doc "A Rust crate, represented as a collection of top-level items" $
  T.record [
    "items">:
      doc "The top-level items in the crate" $
      T.list itemWithComments]

item :: Binding
item = define "Item" $
  doc "A top-level item in a Rust module or crate" $
  T.union [
    "use">:
      doc "A use declaration" $
      rust "UseDeclaration",
    "struct">:
      doc "A struct definition" $
      rust "StructDef",
    "enum">:
      doc "An enum definition" $
      rust "EnumDef",
    "fn">:
      doc "A function definition" $
      rust "FnDef",
    "typeAlias">:
      doc "A type alias" $
      rust "TypeAlias",
    "impl">:
      doc "An impl block" $
      rust "ImplBlock",
    "trait">:
      doc "A trait definition" $
      rust "TraitDef",
    "mod">:
      doc "A module definition" $
      rust "ModDef",
    "const">:
      doc "A constant item" $
      rust "ConstDef",
    "static">:
      doc "A static item" $
      rust "StaticDef",
    "macro">:
      doc "A macro invocation as an item" $
      rust "MacroInvocation"]

itemWithComments :: Binding
itemWithComments = define "ItemWithComments" $
  doc "An item together with optional doc comments and visibility" $
  T.record [
    "doc">:
      doc "Optional documentation comment" $
      T.maybe T.string,
    "visibility">:
      doc "The item's visibility" $
      rust "Visibility",
    "item">:
      doc "The item itself" $
      rust "Item"]


-- ================================================================================================
-- Use declarations
-- ================================================================================================

useDeclaration :: Binding
useDeclaration = define "UseDeclaration" $
  doc "A use declaration (e.g., use std::collections::BTreeMap;)" $
  T.record [
    "public">:
      doc "Whether the use is public (pub use)" $
      T.boolean,
    "tree">:
      doc "The use tree describing what is imported" $
      rust "UseTree"]

useTree :: Binding
useTree = define "UseTree" $
  doc "A use tree, representing the structure of a use path" $
  T.union [
    "path">:
      doc "A simple path import" $
      rust "UsePath",
    "rename">:
      doc "A renamed import (e.g., BTreeMap as Map)" $
      rust "UseRename",
    "glob">:
      doc "A glob import (e.g., std::collections::*)" $
      T.list T.string,
    "group">:
      doc "A grouped import (e.g., {BTreeMap, BTreeSet})" $
      rust "UseGroup"]

usePath :: Binding
usePath = define "UsePath" $
  doc "A simple path import within a use tree" $
  T.record [
    "segments">:
      doc "The path segments" $
      T.list T.string]

useRename :: Binding
useRename = define "UseRename" $
  doc "A renamed import (e.g., BTreeMap as Map)" $
  T.record [
    "path">:
      doc "The original path segments" $
      T.list T.string,
    "alias">:
      doc "The alias name" $
      T.string]

useGroup :: Binding
useGroup = define "UseGroup" $
  doc "A grouped import (e.g., std::collections::{BTreeMap, BTreeSet})" $
  T.record [
    "prefix">:
      doc "The common prefix path segments" $
      T.list T.string,
    "trees">:
      doc "The individual subtrees within the group" $
      T.list $ rust "UseTree"]


-- ================================================================================================
-- Struct definition
-- ================================================================================================

structDef :: Binding
structDef = define "StructDef" $
  doc "A struct definition (e.g., struct Foo<T> { bar: T })" $
  T.record [
    "name">:
      doc "The struct name" $
      T.string,
    "generics">:
      doc "Generic type parameters" $
      T.list $ rust "GenericParam",
    "whereClause">:
      doc "Optional where clause" $
      T.maybe $ rust "WhereClause",
    "body">:
      doc "The struct body (named fields, tuple fields, or unit)" $
      rust "StructBody",
    "derives">:
      doc "Derive macros to apply" $
      T.list T.string,
    "public">:
      doc "Whether the struct is public" $
      T.boolean,
    "doc">:
      doc "Optional doc comment" $
      T.maybe T.string]

structBody :: Binding
structBody = define "StructBody" $
  doc "The body of a struct definition" $
  T.union [
    "named">:
      doc "A struct with named fields" $
      T.list $ rust "StructField",
    "tuple">:
      doc "A tuple struct" $
      T.list $ rust "TupleField",
    "unit">:
      doc "A unit struct" $
      T.unit]

structField :: Binding
structField = define "StructField" $
  doc "A named field within a struct definition" $
  T.record [
    "name">:
      doc "The field name" $
      T.string,
    "type">:
      doc "The field type" $
      rust "Type",
    "public">:
      doc "Whether the field is public" $
      T.boolean,
    "doc">:
      doc "Optional doc comment for the field" $
      T.maybe T.string]

tupleField :: Binding
tupleField = define "TupleField" $
  doc "A positional field within a tuple struct" $
  T.record [
    "type">:
      doc "The field type" $
      rust "Type",
    "public">:
      doc "Whether the field is public" $
      T.boolean]


-- ================================================================================================
-- Enum definition
-- ================================================================================================

enumDef :: Binding
enumDef = define "EnumDef" $
  doc "An enum definition (e.g., enum Foo<T> { Bar(T), Baz { x: i32 } })" $
  T.record [
    "name">:
      doc "The enum name" $
      T.string,
    "generics">:
      doc "Generic type parameters" $
      T.list $ rust "GenericParam",
    "whereClause">:
      doc "Optional where clause" $
      T.maybe $ rust "WhereClause",
    "variants">:
      doc "The enum variants" $
      T.list $ rust "EnumVariant",
    "derives">:
      doc "Derive macros to apply" $
      T.list T.string,
    "public">:
      doc "Whether the enum is public" $
      T.boolean,
    "doc">:
      doc "Optional doc comment" $
      T.maybe T.string]

enumVariant :: Binding
enumVariant = define "EnumVariant" $
  doc "A variant of an enum definition" $
  T.record [
    "name">:
      doc "The variant name" $
      T.string,
    "body">:
      doc "The variant body" $
      rust "EnumVariantBody",
    "doc">:
      doc "Optional doc comment for the variant" $
      T.maybe T.string]

enumVariantBody :: Binding
enumVariantBody = define "EnumVariantBody" $
  doc "The body of an enum variant" $
  T.union [
    "unit">:
      doc "A unit variant (e.g., Foo)" $
      T.unit,
    "tuple">:
      doc "A tuple variant (e.g., Foo(i32, String))" $
      T.list $ rust "Type",
    "struct">:
      doc "A struct variant (e.g., Foo { x: i32 })" $
      T.list $ rust "StructField"]


-- ================================================================================================
-- Function definition
-- ================================================================================================

fnDef :: Binding
fnDef = define "FnDef" $
  doc "A function definition (e.g., fn foo<T>(x: T) -> String { ... })" $
  T.record [
    "name">:
      doc "The function name" $
      T.string,
    "generics">:
      doc "Generic type parameters" $
      T.list $ rust "GenericParam",
    "whereClause">:
      doc "Optional where clause" $
      T.maybe $ rust "WhereClause",
    "params">:
      doc "The function parameters" $
      T.list $ rust "FnParam",
    "returnType">:
      doc "The return type (None means ())" $
      T.maybe $ rust "Type",
    "body">:
      doc "The function body" $
      rust "Block",
    "public">:
      doc "Whether the function is public" $
      T.boolean,
    "async">:
      doc "Whether the function is async" $
      T.boolean,
    "const">:
      doc "Whether the function is const" $
      T.boolean,
    "unsafe">:
      doc "Whether the function is unsafe" $
      T.boolean,
    "doc">:
      doc "Optional doc comment" $
      T.maybe T.string]

fnParam :: Binding
fnParam = define "FnParam" $
  doc "A function parameter" $
  T.record [
    "pattern">:
      doc "The parameter pattern" $
      rust "Pattern",
    "type">:
      doc "The parameter type" $
      rust "Type"]

selfParam :: Binding
selfParam = define "SelfParam" $
  doc "A self parameter in a method" $
  T.enum ["owned", "ref", "refMut"]

methodParam :: Binding
methodParam = define "MethodParam" $
  doc "A method parameter, which may be self or a regular parameter" $
  T.union [
    "self">:
      doc "A self parameter" $
      rust "SelfParam",
    "regular">:
      doc "A regular function parameter" $
      rust "FnParam"]


-- ================================================================================================
-- Type alias
-- ================================================================================================

typeAlias :: Binding
typeAlias = define "TypeAlias" $
  doc "A type alias definition (e.g., type Foo<T> = Bar<T>;)" $
  T.record [
    "name">:
      doc "The alias name" $
      T.string,
    "generics">:
      doc "Generic type parameters" $
      T.list $ rust "GenericParam",
    "type">:
      doc "The aliased type" $
      rust "Type",
    "public">:
      doc "Whether the alias is public" $
      T.boolean,
    "doc">:
      doc "Optional doc comment" $
      T.maybe T.string]


-- ================================================================================================
-- Const, static, and module definitions
-- ================================================================================================

constDef :: Binding
constDef = define "ConstDef" $
  doc "A constant item (e.g., const FOO: u32 = 42;)" $
  T.record [
    "name">:
      doc "The constant name" $
      T.string,
    "type">:
      doc "The constant type" $
      rust "Type",
    "value">:
      doc "The constant value expression" $
      rust "Expression",
    "public">:
      doc "Whether the constant is public" $
      T.boolean,
    "doc">:
      doc "Optional doc comment" $
      T.maybe T.string]

staticDef :: Binding
staticDef = define "StaticDef" $
  doc "A static item (e.g., static FOO: u32 = 42;)" $
  T.record [
    "name">:
      doc "The static name" $
      T.string,
    "type">:
      doc "The static type" $
      rust "Type",
    "value">:
      doc "The static value expression" $
      rust "Expression",
    "mutable">:
      doc "Whether the static is mutable" $
      T.boolean,
    "public">:
      doc "Whether the static is public" $
      T.boolean,
    "doc">:
      doc "Optional doc comment" $
      T.maybe T.string]

modDef :: Binding
modDef = define "ModDef" $
  doc "A module definition (either inline or external)" $
  T.record [
    "name">:
      doc "The module name" $
      T.string,
    "body">:
      doc "The module body (None for external file)" $
      T.maybe $ T.list $ rust "Item",
    "public">:
      doc "Whether the module is public" $
      T.boolean,
    "doc">:
      doc "Optional doc comment" $
      T.maybe T.string]


-- ================================================================================================
-- Impl block
-- ================================================================================================

implBlock :: Binding
implBlock = define "ImplBlock" $
  doc "An impl block (e.g., impl<T> Trait for Foo<T> { ... })" $
  T.record [
    "generics">:
      doc "Generic type parameters" $
      T.list $ rust "GenericParam",
    "whereClause">:
      doc "Optional where clause" $
      T.maybe $ rust "WhereClause",
    "trait">:
      doc "The trait being implemented, if any" $
      T.maybe $ rust "TypePath",
    "negative">:
      doc "Whether this is a negative impl" $
      T.boolean,
    "selfType">:
      doc "The type being implemented for" $
      rust "Type",
    "items">:
      doc "The items within the impl block" $
      T.list $ rust "ImplItem"]

implItem :: Binding
implItem = define "ImplItem" $
  doc "An item within an impl block" $
  T.union [
    "method">:
      doc "A method definition" $
      rust "ImplMethod",
    "type">:
      doc "An associated type definition" $
      rust "TypeAlias",
    "const">:
      doc "An associated constant" $
      rust "ConstDef"]

implMethod :: Binding
implMethod = define "ImplMethod" $
  doc "A method within an impl block" $
  T.record [
    "name">:
      doc "The method name" $
      T.string,
    "generics">:
      doc "Generic type parameters" $
      T.list $ rust "GenericParam",
    "whereClause">:
      doc "Optional where clause" $
      T.maybe $ rust "WhereClause",
    "params">:
      doc "The method parameters (including self)" $
      T.list $ rust "MethodParam",
    "returnType">:
      doc "The return type (None means ())" $
      T.maybe $ rust "Type",
    "body">:
      doc "The method body" $
      rust "Block",
    "public">:
      doc "Whether the method is public" $
      T.boolean,
    "default">:
      doc "Whether the method has a default implementation" $
      T.boolean,
    "doc">:
      doc "Optional doc comment" $
      T.maybe T.string]


-- ================================================================================================
-- Trait definition
-- ================================================================================================

traitDef :: Binding
traitDef = define "TraitDef" $
  doc "A trait definition (e.g., trait Foo<T>: Bar + Baz { ... })" $
  T.record [
    "name">:
      doc "The trait name" $
      T.string,
    "generics">:
      doc "Generic type parameters" $
      T.list $ rust "GenericParam",
    "whereClause">:
      doc "Optional where clause" $
      T.maybe $ rust "WhereClause",
    "superTraits">:
      doc "Supertraits" $
      T.list $ rust "TypeParamBound",
    "items">:
      doc "The items within the trait" $
      T.list $ rust "TraitItem",
    "public">:
      doc "Whether the trait is public" $
      T.boolean,
    "unsafe">:
      doc "Whether the trait is unsafe" $
      T.boolean,
    "doc">:
      doc "Optional doc comment" $
      T.maybe T.string]

traitItem :: Binding
traitItem = define "TraitItem" $
  doc "An item within a trait definition" $
  T.union [
    "method">:
      doc "A method signature or default method" $
      rust "TraitMethod",
    "type">:
      doc "An associated type" $
      rust "TraitType",
    "const">:
      doc "An associated constant" $
      rust "TraitConst"]

traitMethod :: Binding
traitMethod = define "TraitMethod" $
  doc "A method signature or default method within a trait" $
  T.record [
    "name">:
      doc "The method name" $
      T.string,
    "generics">:
      doc "Generic type parameters" $
      T.list $ rust "GenericParam",
    "whereClause">:
      doc "Optional where clause" $
      T.maybe $ rust "WhereClause",
    "params">:
      doc "The method parameters (including self)" $
      T.list $ rust "MethodParam",
    "returnType">:
      doc "The return type" $
      T.maybe $ rust "Type",
    "defaultBody">:
      doc "Optional default body" $
      T.maybe $ rust "Block",
    "doc">:
      doc "Optional doc comment" $
      T.maybe T.string]

traitType :: Binding
traitType = define "TraitType" $
  doc "An associated type within a trait" $
  T.record [
    "name">:
      doc "The associated type name" $
      T.string,
    "bounds">:
      doc "Type parameter bounds" $
      T.list $ rust "TypeParamBound",
    "default">:
      doc "Optional default type" $
      T.maybe $ rust "Type",
    "doc">:
      doc "Optional doc comment" $
      T.maybe T.string]

traitConst :: Binding
traitConst = define "TraitConst" $
  doc "An associated constant within a trait" $
  T.record [
    "name">:
      doc "The constant name" $
      T.string,
    "type">:
      doc "The constant type" $
      rust "Type",
    "default">:
      doc "Optional default value" $
      T.maybe $ rust "Expression",
    "doc">:
      doc "Optional doc comment" $
      T.maybe T.string]


-- ================================================================================================
-- Generics and where clauses
-- ================================================================================================

genericParam :: Binding
genericParam = define "GenericParam" $
  doc "A generic type parameter (e.g., T: Clone + Debug)" $
  T.record [
    "name">:
      doc "The parameter name" $
      T.string,
    "bounds">:
      doc "Trait bounds on the parameter" $
      T.list $ rust "TypeParamBound"]

typeParamBound :: Binding
typeParamBound = define "TypeParamBound" $
  doc "A bound on a type parameter" $
  T.union [
    "trait">:
      doc "A trait bound" $
      rust "TypePath",
    "lifetime">:
      doc "A lifetime bound" $
      rust "Lifetime"]

lifetime :: Binding
lifetime = define "Lifetime" $
  doc "A lifetime (e.g., 'a, 'static)" $
  T.record [
    "name">:
      doc "The lifetime name (without the leading quote)" $
      T.string]

whereClause :: Binding
whereClause = define "WhereClause" $
  doc "A where clause (e.g., where T: Clone, U: Debug)" $
  T.record [
    "predicates">:
      doc "The predicates in the where clause" $
      T.list $ rust "WherePredicate"]

wherePredicate :: Binding
wherePredicate = define "WherePredicate" $
  doc "A single predicate in a where clause" $
  T.record [
    "type">:
      doc "The type being constrained" $
      rust "Type",
    "bounds">:
      doc "The bounds on the type" $
      T.list $ rust "TypeParamBound"]


-- ================================================================================================
-- Types
-- ================================================================================================

type_ :: Binding
type_ = define "Type" $
  doc "A Rust type expression" $
  T.union [
    "path">:
      doc "A path type, possibly with generic arguments" $
      rust "TypePath",
    "reference">:
      doc "A reference type" $
      rust "ReferenceType",
    "slice">:
      doc "A slice type" $
      rust "Type",
    "array">:
      doc "An array type with a fixed size" $
      rust "ArrayType",
    "tuple">:
      doc "A tuple type" $
      T.list $ rust "Type",
    "fnPointer">:
      doc "A function pointer type" $
      rust "FnPointerType",
    "implTrait">:
      doc "An impl Trait type" $
      T.list $ rust "TypeParamBound",
    "dynTrait">:
      doc "A dyn Trait type" $
      T.list $ rust "TypeParamBound",
    "inferred">:
      doc "The inferred type placeholder (_)" $
      T.unit,
    "unit">:
      doc "The unit type (())" $
      T.unit,
    "never">:
      doc "The never type (!)" $
      T.unit,
    "rawPointer">:
      doc "A raw pointer type" $
      rust "RawPointerType",
    "macro">:
      doc "A macro invocation in type position" $
      rust "MacroInvocation"]

typePath :: Binding
typePath = define "TypePath" $
  doc "A path-based type, possibly with generic arguments" $
  T.record [
    "global">:
      doc "Whether the path is absolute (starts with ::)" $
      T.boolean,
    "segments">:
      doc "The segments of the path" $
      T.list $ rust "PathSegment"]

pathSegment :: Binding
pathSegment = define "PathSegment" $
  doc "A segment within a type path" $
  T.record [
    "name">:
      doc "The segment name" $
      T.string,
    "arguments">:
      doc "Generic arguments, if any" $
      rust "GenericArguments"]

genericArguments :: Binding
genericArguments = define "GenericArguments" $
  doc "Generic arguments to a path segment" $
  T.union [
    "none">:
      doc "No generic arguments" $
      T.unit,
    "angleBracketed">:
      doc "Angle-bracketed arguments" $
      rust "AngleBracketedArgs",
    "parenthesized">:
      doc "Parenthesized arguments for Fn traits" $
      rust "ParenthesizedArgs"]

angleBracketedArgs :: Binding
angleBracketedArgs = define "AngleBracketedArgs" $
  doc "Angle-bracketed generic arguments" $
  T.record [
    "args">:
      doc "The generic arguments" $
      T.list $ rust "GenericArg"]

genericArg :: Binding
genericArg = define "GenericArg" $
  doc "A single generic argument" $
  T.union [
    "type">:
      doc "A type argument" $
      rust "Type",
    "lifetime">:
      doc "A lifetime argument" $
      rust "Lifetime",
    "const">:
      doc "A const expression argument" $
      rust "Expression",
    "binding">:
      doc "An associated type binding" $
      rust "TypeBinding"]

typeBinding :: Binding
typeBinding = define "TypeBinding" $
  doc "An associated type binding within generic arguments" $
  T.record [
    "name">:
      doc "The associated type name" $
      T.string,
    "type">:
      doc "The bound type" $
      rust "Type"]

parenthesizedArgs :: Binding
parenthesizedArgs = define "ParenthesizedArgs" $
  doc "Parenthesized generic arguments for Fn traits" $
  T.record [
    "inputs">:
      doc "The input types" $
      T.list $ rust "Type",
    "output">:
      doc "The output type" $
      T.maybe $ rust "Type"]

referenceType :: Binding
referenceType = define "ReferenceType" $
  doc "A reference type (e.g., &T, &mut T, &'a T)" $
  T.record [
    "lifetime">:
      doc "Optional lifetime annotation" $
      T.maybe $ rust "Lifetime",
    "mutable">:
      doc "Whether the reference is mutable" $
      T.boolean,
    "type">:
      doc "The referenced type" $
      rust "Type"]

arrayType :: Binding
arrayType = define "ArrayType" $
  doc "An array type with a fixed size (e.g., [T; 3])" $
  T.record [
    "element">:
      doc "The element type" $
      rust "Type",
    "length">:
      doc "The array length (as a constant expression)" $
      rust "Expression"]

fnPointerType :: Binding
fnPointerType = define "FnPointerType" $
  doc "A function pointer type (e.g., fn(i32, i32) -> i32)" $
  T.record [
    "params">:
      doc "The parameter types" $
      T.list $ rust "Type",
    "returnType">:
      doc "The return type" $
      rust "Type"]

rawPointerType :: Binding
rawPointerType = define "RawPointerType" $
  doc "A raw pointer type (e.g., *const T, *mut T)" $
  T.record [
    "mutable">:
      doc "Whether the pointer is mutable (*mut vs *const)" $
      T.boolean,
    "type">:
      doc "The pointed-to type" $
      rust "Type"]


-- ================================================================================================
-- Expressions
-- ================================================================================================

expression :: Binding
expression = define "Expression" $
  doc "A Rust expression" $
  T.union [
    "literal">:
      doc "A literal value" $
      rust "Literal",
    "path">:
      doc "A path expression" $
      rust "ExprPath",
    "block">:
      doc "A block expression" $
      rust "Block",
    "call">:
      doc "A function call expression" $
      rust "CallExpr",
    "methodCall">:
      doc "A method call expression" $
      rust "MethodCallExpr",
    "fieldAccess">:
      doc "A field access expression" $
      rust "FieldAccessExpr",
    "tupleIndex">:
      doc "A tuple index expression" $
      rust "TupleIndexExpr",
    "closure">:
      doc "A closure expression" $
      rust "ClosureExpr",
    "if">:
      doc "An if expression, including if let" $
      rust "IfExpr",
    "match">:
      doc "A match expression" $
      rust "MatchExpr",
    "loop">:
      doc "A loop expression" $
      rust "LoopExpr",
    "while">:
      doc "A while expression, including while let" $
      rust "WhileExpr",
    "for">:
      doc "A for expression" $
      rust "ForExpr",
    "binary">:
      doc "A binary operation" $
      rust "BinaryExpr",
    "unary">:
      doc "A unary operation" $
      rust "UnaryExpr",
    "reference">:
      doc "A reference expression" $
      rust "RefExpr",
    "dereference">:
      doc "A dereference expression" $
      rust "Expression",
    "struct">:
      doc "A struct literal expression" $
      rust "StructExpr",
    "tuple">:
      doc "A tuple expression" $
      T.list $ rust "Expression",
    "array">:
      doc "An array expression" $
      rust "ArrayExpr",
    "index">:
      doc "An index expression" $
      rust "IndexExpr",
    "range">:
      doc "A range expression" $
      rust "RangeExpr",
    "return">:
      doc "A return expression" $
      T.maybe $ rust "Expression",
    "break">:
      doc "A break expression" $
      T.maybe $ rust "Expression",
    "continue">:
      doc "A continue expression" $
      T.unit,
    "try">:
      doc "A try expression (expr?)" $
      rust "Expression",
    "cast">:
      doc "A type cast expression" $
      rust "CastExpr",
    "typeAscription">:
      doc "A type ascription expression" $
      rust "TypeAscriptionExpr",
    "await">:
      doc "An await expression" $
      rust "Expression",
    "assign">:
      doc "An assignment expression" $
      rust "AssignExpr",
    "compoundAssign">:
      doc "A compound assignment expression" $
      rust "CompoundAssignExpr",
    "macro">:
      doc "A macro invocation expression" $
      rust "MacroInvocation",
    "paren">:
      doc "A parenthesized expression" $
      rust "Expression"]

exprPath :: Binding
exprPath = define "ExprPath" $
  doc "A path used as an expression" $
  T.record [
    "global">:
      doc "Whether the path is global" $
      T.boolean,
    "segments">:
      doc "The path segments" $
      T.list $ rust "PathSegment"]

callExpr :: Binding
callExpr = define "CallExpr" $
  doc "A function call expression" $
  T.record [
    "function">:
      doc "The function being called" $
      rust "Expression",
    "args">:
      doc "The arguments" $
      T.list $ rust "Expression"]

methodCallExpr :: Binding
methodCallExpr = define "MethodCallExpr" $
  doc "A method call expression" $
  T.record [
    "receiver">:
      doc "The receiver expression" $
      rust "Expression",
    "method">:
      doc "The method name" $
      T.string,
    "turbofish">:
      doc "Optional turbofish generic arguments" $
      T.list $ rust "Type",
    "args">:
      doc "The arguments (excluding the receiver)" $
      T.list $ rust "Expression"]

fieldAccessExpr :: Binding
fieldAccessExpr = define "FieldAccessExpr" $
  doc "A field access expression" $
  T.record [
    "object">:
      doc "The expression being accessed" $
      rust "Expression",
    "field">:
      doc "The field name" $
      T.string]

tupleIndexExpr :: Binding
tupleIndexExpr = define "TupleIndexExpr" $
  doc "A tuple index expression" $
  T.record [
    "tuple">:
      doc "The tuple expression" $
      rust "Expression",
    "index">:
      doc "The index (0-based)" $
      T.int32]

closureExpr :: Binding
closureExpr = define "ClosureExpr" $
  doc "A closure expression" $
  T.record [
    "move">:
      doc "Whether the closure captures by move" $
      T.boolean,
    "params">:
      doc "The closure parameters" $
      T.list $ rust "ClosureParam",
    "returnType">:
      doc "Optional return type annotation" $
      T.maybe $ rust "Type",
    "body">:
      doc "The closure body" $
      rust "Expression"]

closureParam :: Binding
closureParam = define "ClosureParam" $
  doc "A closure parameter" $
  T.record [
    "pattern">:
      doc "The parameter pattern" $
      rust "Pattern",
    "type">:
      doc "Optional type annotation" $
      T.maybe $ rust "Type"]

ifExpr :: Binding
ifExpr = define "IfExpr" $
  doc "An if expression, optionally with if let" $
  T.record [
    "condition">:
      doc "The condition" $
      rust "IfCondition",
    "thenBlock">:
      doc "The then block" $
      rust "Block",
    "elseBranch">:
      doc "An optional else branch" $
      T.maybe $ rust "Expression"]

ifCondition :: Binding
ifCondition = define "IfCondition" $
  doc "The condition of an if expression" $
  T.union [
    "bool">:
      doc "A boolean condition" $
      rust "Expression",
    "let">:
      doc "A let condition" $
      rust "LetCondition"]

matchExpr :: Binding
matchExpr = define "MatchExpr" $
  doc "A match expression" $
  T.record [
    "scrutinee">:
      doc "The expression being matched" $
      rust "Expression",
    "arms">:
      doc "The match arms" $
      T.list $ rust "MatchArm"]

matchArm :: Binding
matchArm = define "MatchArm" $
  doc "A single arm in a match expression" $
  T.record [
    "pattern">:
      doc "The pattern to match" $
      rust "Pattern",
    "guard">:
      doc "Optional guard expression" $
      T.maybe $ rust "Expression",
    "body">:
      doc "The body expression" $
      rust "Expression"]

loopExpr :: Binding
loopExpr = define "LoopExpr" $
  doc "A loop expression" $
  T.record [
    "label">:
      doc "Optional loop label" $
      T.maybe T.string,
    "body">:
      doc "The loop body" $
      rust "Block"]

whileExpr :: Binding
whileExpr = define "WhileExpr" $
  doc "A while expression, optionally with while let" $
  T.record [
    "label">:
      doc "Optional loop label" $
      T.maybe T.string,
    "condition">:
      doc "The condition" $
      rust "IfCondition",
    "body">:
      doc "The loop body" $
      rust "Block"]

forExpr :: Binding
forExpr = define "ForExpr" $
  doc "A for expression" $
  T.record [
    "label">:
      doc "Optional loop label" $
      T.maybe T.string,
    "pattern">:
      doc "The loop variable pattern" $
      rust "Pattern",
    "iter">:
      doc "The iterator expression" $
      rust "Expression",
    "body">:
      doc "The loop body" $
      rust "Block"]

binaryExpr :: Binding
binaryExpr = define "BinaryExpr" $
  doc "A binary operation" $
  T.record [
    "left">:
      doc "The left-hand operand" $
      rust "Expression",
    "op">:
      doc "The binary operator" $
      rust "BinaryOp",
    "right">:
      doc "The right-hand operand" $
      rust "Expression"]

binaryOp :: Binding
binaryOp = define "BinaryOp" $
  doc "A binary operator" $
  T.enum [
    "add", "sub", "mul", "div", "rem",
    "and", "or",
    "bitAnd", "bitOr", "bitXor", "shl", "shr",
    "eq", "ne", "lt", "le", "gt", "ge"]

unaryExpr :: Binding
unaryExpr = define "UnaryExpr" $
  doc "A unary operation" $
  T.record [
    "op">:
      doc "The unary operator" $
      rust "UnaryOp",
    "operand">:
      doc "The operand" $
      rust "Expression"]

unaryOp :: Binding
unaryOp = define "UnaryOp" $
  doc "A unary operator" $
  T.enum ["neg", "not"]

refExpr :: Binding
refExpr = define "RefExpr" $
  doc "A reference expression" $
  T.record [
    "mutable">:
      doc "Whether the reference is mutable" $
      T.boolean,
    "expr">:
      doc "The expression being referenced" $
      rust "Expression"]

structExpr :: Binding
structExpr = define "StructExpr" $
  doc "A struct literal expression" $
  T.record [
    "path">:
      doc "The struct path" $
      rust "ExprPath",
    "fields">:
      doc "The field assignments" $
      T.list $ rust "FieldValue",
    "rest">:
      doc "Optional base expression for struct update syntax" $
      T.maybe $ rust "Expression"]

fieldValue :: Binding
fieldValue = define "FieldValue" $
  doc "A field-value pair in a struct literal" $
  T.record [
    "name">:
      doc "The field name" $
      T.string,
    "value">:
      doc "The field value (None for shorthand syntax)" $
      T.maybe $ rust "Expression"]

arrayExpr :: Binding
arrayExpr = define "ArrayExpr" $
  doc "An array expression" $
  T.union [
    "elements">:
      doc "An array literal" $
      T.list $ rust "Expression",
    "repeat">:
      doc "An array repeat expression" $
      rust "ArrayRepeat"]

indexExpr :: Binding
indexExpr = define "IndexExpr" $
  doc "An index expression" $
  T.record [
    "object">:
      doc "The expression being indexed" $
      rust "Expression",
    "index">:
      doc "The index expression" $
      rust "Expression"]

rangeExpr :: Binding
rangeExpr = define "RangeExpr" $
  doc "A range expression" $
  T.record [
    "from">:
      doc "The lower bound (optional)" $
      T.maybe $ rust "Expression",
    "to">:
      doc "The upper bound (optional)" $
      T.maybe $ rust "Expression",
    "inclusive">:
      doc "Whether the range is inclusive" $
      T.boolean]

castExpr :: Binding
castExpr = define "CastExpr" $
  doc "A type cast expression" $
  T.record [
    "expr">:
      doc "The expression being cast" $
      rust "Expression",
    "type">:
      doc "The target type" $
      rust "Type"]

typeAscriptionExpr :: Binding
typeAscriptionExpr = define "TypeAscriptionExpr" $
  doc "A type ascription expression" $
  T.record [
    "expr">:
      doc "The expression" $
      rust "Expression",
    "type">:
      doc "The ascribed type" $
      rust "Type"]

assignExpr :: Binding
assignExpr = define "AssignExpr" $
  doc "An assignment expression" $
  T.record [
    "target">:
      doc "The left-hand side (target)" $
      rust "Expression",
    "value">:
      doc "The right-hand side (value)" $
      rust "Expression"]

compoundAssignExpr :: Binding
compoundAssignExpr = define "CompoundAssignExpr" $
  doc "A compound assignment expression" $
  T.record [
    "target">:
      doc "The left-hand side (target)" $
      rust "Expression",
    "op">:
      doc "The compound assignment operator" $
      rust "CompoundAssignOp",
    "value">:
      doc "The right-hand side (value)" $
      rust "Expression"]

compoundAssignOp :: Binding
compoundAssignOp = define "CompoundAssignOp" $
  doc "A compound assignment operator" $
  T.enum [
    "addAssign", "subAssign", "mulAssign", "divAssign", "remAssign",
    "bitAndAssign", "bitOrAssign", "bitXorAssign", "shlAssign", "shrAssign"]

macroInvocation :: Binding
macroInvocation = define "MacroInvocation" $
  doc "A macro invocation" $
  T.record [
    "path">:
      doc "The macro path" $
      T.list T.string,
    "delimiter">:
      doc "The delimiter style used" $
      rust "MacroDelimiter",
    "tokens">:
      doc "The token stream as a raw string" $
      T.string]

macroDelimiter :: Binding
macroDelimiter = define "MacroDelimiter" $
  doc "The delimiter style for a macro invocation" $
  T.enum ["paren", "bracket", "brace"]


-- ================================================================================================
-- Statements
-- ================================================================================================

statement :: Binding
statement = define "Statement" $
  doc "A statement within a block" $
  T.union [
    "let">:
      doc "A let binding" $
      rust "LetStatement",
    "expression">:
      doc "An expression statement" $
      rust "Expression",
    "item">:
      doc "An item declaration within a block" $
      rust "Item",
    "empty">:
      doc "An empty statement" $
      T.unit]

letStatement :: Binding
letStatement = define "LetStatement" $
  doc "A let statement" $
  T.record [
    "pattern">:
      doc "The binding pattern" $
      rust "Pattern",
    "mutable">:
      doc "Whether the binding is mutable" $
      T.boolean,
    "type">:
      doc "Optional type annotation" $
      T.maybe $ rust "Type",
    "init">:
      doc "Optional initializer expression" $
      T.maybe $ rust "Expression"]

block :: Binding
block = define "Block" $
  doc "A block expression" $
  T.record [
    "statements">:
      doc "The statements in the block" $
      T.list $ rust "Statement",
    "expression">:
      doc "An optional trailing expression" $
      T.maybe $ rust "Expression"]


-- ================================================================================================
-- Patterns
-- ================================================================================================

pattern :: Binding
pattern = define "Pattern" $
  doc "A Rust pattern (used in let, match, function parameters, etc.)" $
  T.union [
    "wildcard">:
      doc "A wildcard pattern (_)" $
      T.unit,
    "identifier">:
      doc "An identifier pattern" $
      rust "IdentifierPattern",
    "literal">:
      doc "A literal pattern" $
      rust "Literal",
    "reference">:
      doc "A reference pattern" $
      rust "RefPattern",
    "struct">:
      doc "A struct pattern" $
      rust "StructPattern",
    "tupleStruct">:
      doc "A tuple struct pattern" $
      rust "TupleStructPattern",
    "tuple">:
      doc "A tuple pattern" $
      T.list $ rust "Pattern",
    "slice">:
      doc "A slice pattern" $
      T.list $ rust "Pattern",
    "or">:
      doc "An or-pattern" $
      T.list $ rust "Pattern",
    "path">:
      doc "A path pattern" $
      rust "ExprPath",
    "range">:
      doc "A range pattern" $
      rust "RangePattern",
    "rest">:
      doc "A rest pattern (..)" $
      T.unit,
    "paren">:
      doc "A parenthesized pattern" $
      rust "Pattern"]

identifierPattern :: Binding
identifierPattern = define "IdentifierPattern" $
  doc "An identifier pattern" $
  T.record [
    "name">:
      doc "The identifier name" $
      T.string,
    "mutable">:
      doc "Whether the binding is mutable" $
      T.boolean,
    "atPattern">:
      doc "Optional sub-pattern (e.g., x @ Some(_))" $
      T.maybe $ rust "Pattern"]

refPattern :: Binding
refPattern = define "RefPattern" $
  doc "A reference pattern" $
  T.record [
    "mutable">:
      doc "Whether the reference is mutable" $
      T.boolean,
    "pattern">:
      doc "The inner pattern" $
      rust "Pattern"]

structPattern :: Binding
structPattern = define "StructPattern" $
  doc "A struct pattern" $
  T.record [
    "path">:
      doc "The struct path" $
      rust "ExprPath",
    "fields">:
      doc "The field patterns" $
      T.list $ rust "FieldPattern",
    "rest">:
      doc "Whether the pattern has a rest (..) at the end" $
      T.boolean]

fieldPattern :: Binding
fieldPattern = define "FieldPattern" $
  doc "A field pattern within a struct pattern" $
  T.record [
    "name">:
      doc "The field name" $
      T.string,
    "pattern">:
      doc "The field pattern (None for shorthand)" $
      T.maybe $ rust "Pattern"]

tupleStructPattern :: Binding
tupleStructPattern = define "TupleStructPattern" $
  doc "A tuple struct pattern" $
  T.record [
    "path">:
      doc "The path to the tuple struct or variant" $
      rust "ExprPath",
    "elements">:
      doc "The element patterns" $
      T.list $ rust "Pattern"]

rangePattern :: Binding
rangePattern = define "RangePattern" $
  doc "A range pattern" $
  T.record [
    "from">:
      doc "The lower bound" $
      T.maybe $ rust "Pattern",
    "to">:
      doc "The upper bound" $
      T.maybe $ rust "Pattern",
    "inclusive">:
      doc "Whether the range is inclusive" $
      T.boolean]


-- ================================================================================================
-- Literals
-- ================================================================================================

literal :: Binding
literal = define "Literal" $
  doc "A literal value" $
  T.union [
    "integer">:
      doc "An integer literal" $
      rust "IntegerLiteral",
    "float">:
      doc "A floating-point literal" $
      rust "FloatLiteral",
    "string">:
      doc "A string literal" $
      T.string,
    "rawString">:
      doc "A raw string literal" $
      T.string,
    "byteString">:
      doc "A byte string literal" $
      T.binary,
    "char">:
      doc "A character literal" $
      T.uint32,
    "byte">:
      doc "A byte literal" $
      T.uint8,
    "bool">:
      doc "A boolean literal" $
      T.boolean]

integerLiteral :: Binding
integerLiteral = define "IntegerLiteral" $
  doc "An integer literal with optional suffix" $
  T.record [
    "value">:
      doc "The integer value" $
      T.bigint,
    "suffix">:
      doc "Optional type suffix" $
      T.maybe T.string]

floatLiteral :: Binding
floatLiteral = define "FloatLiteral" $
  doc "A floating-point literal with optional suffix" $
  T.record [
    "value">:
      doc "The float value" $
      T.float64,
    "suffix">:
      doc "Optional type suffix" $
      T.maybe T.string]


-- ================================================================================================
-- Attributes and visibility
-- ================================================================================================

attribute :: Binding
attribute = define "Attribute" $
  doc "An attribute (e.g., #[derive(Clone)], #[cfg(test)])" $
  T.record [
    "inner">:
      doc "Whether the attribute is an inner attribute (#![...] vs #[...])" $
      T.boolean,
    "path">:
      doc "The attribute path" $
      T.list T.string,
    "tokens">:
      doc "The attribute arguments as a raw token string" $
      T.maybe T.string]

visibility :: Binding
visibility = define "Visibility" $
  doc "A visibility qualifier" $
  T.union [
    "public">:
      doc "Public (pub)" $
      T.unit,
    "crate">:
      doc "Crate-visible (pub(crate))" $
      T.unit,
    "restricted">:
      doc "Visible to a specific path (pub(in path))" $
      T.list T.string,
    "private">:
      doc "Private (default)" $
      T.unit]


-- ================================================================================================
-- Helper types (used in expressions)
-- ================================================================================================

-- | A let condition in an if-let or while-let
letCondition :: Binding
letCondition = define "LetCondition" $
  doc "A let condition (e.g., let Some(x) = opt)" $
  T.record [
    "pattern">:
      doc "The pattern" $
      rust "Pattern",
    "expr">:
      doc "The expression being matched" $
      rust "Expression"]

-- | An array repeat expression
arrayRepeat :: Binding
arrayRepeat = define "ArrayRepeat" $
  doc "An array repeat expression (e.g., [0; 10])" $
  T.record [
    "element">:
      doc "The element expression" $
      rust "Expression",
    "length">:
      doc "The length expression" $
      rust "Expression"]