lean_agentic/
term.rs

1//! Core term representation for dependent type theory
2//!
3//! Terms are hash-consed and stored in an arena for efficient
4//! equality checking and memory management.
5
6use crate::level::LevelId;
7use crate::symbol::SymbolId;
8use std::fmt;
9
10/// Interned term ID for hash-consing
11#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
12pub struct TermId(u32);
13
14impl TermId {
15    /// Create a new term ID (internal use only)
16    pub(crate) fn new(id: u32) -> Self {
17        Self(id)
18    }
19
20    /// Get the raw ID value
21    pub fn raw(self) -> u32 {
22        self.0
23    }
24}
25
26/// Binder information for lambda and Pi types
27#[derive(Debug, Clone, PartialEq, Eq, Hash)]
28pub struct Binder {
29    /// Name of the bound variable (for pretty-printing)
30    pub name: SymbolId,
31
32    /// Type of the bound variable
33    pub ty: TermId,
34
35    /// Whether this is an implicit binder
36    pub implicit: bool,
37
38    /// Binder info (default, implicit, strict implicit, etc.)
39    pub info: BinderInfo,
40}
41
42/// Binder information flags
43#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
44pub enum BinderInfo {
45    /// Default binder (explicit)
46    Default,
47
48    /// Implicit argument
49    Implicit,
50
51    /// Strict implicit (must be resolved immediately)
52    StrictImplicit,
53
54    /// Instance implicit (for type classes)
55    InstImplicit,
56}
57
58impl Binder {
59    /// Create a new default binder
60    pub fn new(name: SymbolId, ty: TermId) -> Self {
61        Self {
62            name,
63            ty,
64            implicit: false,
65            info: BinderInfo::Default,
66        }
67    }
68
69    /// Create an implicit binder
70    pub fn implicit(name: SymbolId, ty: TermId) -> Self {
71        Self {
72            name,
73            ty,
74            implicit: true,
75            info: BinderInfo::Implicit,
76        }
77    }
78}
79
80/// Core term representation
81#[derive(Debug, Clone, PartialEq, Eq, Hash)]
82pub enum TermKind {
83    /// Universe sort (Type u)
84    Sort(LevelId),
85
86    /// Constant reference (name + universe parameters)
87    Const(SymbolId, Vec<LevelId>),
88
89    /// Bound variable (de Bruijn index)
90    Var(u32),
91
92    /// Application (f x)
93    App(TermId, TermId),
94
95    /// Lambda abstraction (λ x : τ, body)
96    Lam(Binder, TermId),
97
98    /// Dependent function type (Π x : τ, body) / forall
99    Pi(Binder, TermId),
100
101    /// Let binding (let x : τ := v in body)
102    Let(Binder, TermId, TermId),
103
104    /// Metavariable (hole to be filled during elaboration)
105    MVar(MetaVarId),
106
107    /// Literal values (for optimization)
108    Lit(Literal),
109}
110
111/// Metavariable ID
112#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
113pub struct MetaVarId(u32);
114
115impl MetaVarId {
116    /// Create a new metavariable ID
117    pub fn new(id: u32) -> Self {
118        Self(id)
119    }
120
121    /// Get the raw ID
122    pub fn raw(self) -> u32 {
123        self.0
124    }
125}
126
127/// Literal values
128#[derive(Debug, Clone, PartialEq, Eq, Hash)]
129pub enum Literal {
130    /// Natural number literal
131    Nat(u64),
132
133    /// String literal
134    String(String),
135}
136
137/// Wrapper around TermKind with additional metadata
138#[derive(Debug, Clone)]
139pub struct Term {
140    /// The term kind
141    pub kind: TermKind,
142
143    /// Cached hash for efficient lookup
144    hash: u64,
145}
146
147impl Term {
148    /// Create a new term
149    pub fn new(kind: TermKind) -> Self {
150        use std::collections::hash_map::DefaultHasher;
151        use std::hash::{Hash, Hasher};
152
153        let mut hasher = DefaultHasher::new();
154        kind.hash(&mut hasher);
155        let hash = hasher.finish();
156
157        Self { kind, hash }
158    }
159
160    /// Get the term's hash
161    pub fn hash(&self) -> u64 {
162        self.hash
163    }
164
165    /// Check if this is a sort
166    pub fn is_sort(&self) -> bool {
167        matches!(self.kind, TermKind::Sort(_))
168    }
169
170    /// Check if this is a variable
171    pub fn is_var(&self) -> bool {
172        matches!(self.kind, TermKind::Var(_))
173    }
174
175    /// Check if this is a lambda
176    pub fn is_lam(&self) -> bool {
177        matches!(self.kind, TermKind::Lam(_, _))
178    }
179
180    /// Check if this is a Pi type
181    pub fn is_pi(&self) -> bool {
182        matches!(self.kind, TermKind::Pi(_, _))
183    }
184
185    /// Check if this is an application
186    pub fn is_app(&self) -> bool {
187        matches!(self.kind, TermKind::App(_, _))
188    }
189}
190
191impl PartialEq for Term {
192    fn eq(&self, other: &Self) -> bool {
193        self.hash == other.hash && self.kind == other.kind
194    }
195}
196
197impl Eq for Term {}
198
199impl std::hash::Hash for Term {
200    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
201        self.hash.hash(state);
202    }
203}
204
205impl fmt::Display for TermKind {
206    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207        match self {
208            TermKind::Sort(l) => write!(f, "Sort({})", l.raw()),
209            TermKind::Const(name, levels) => {
210                write!(f, "Const({}, [", name.raw())?;
211                for (i, l) in levels.iter().enumerate() {
212                    if i > 0 {
213                        write!(f, ", ")?;
214                    }
215                    write!(f, "{}", l.raw())?;
216                }
217                write!(f, "])")
218            }
219            TermKind::Var(idx) => write!(f, "#{}", idx),
220            TermKind::App(func, arg) => write!(f, "({} {})", func.raw(), arg.raw()),
221            TermKind::Lam(binder, body) => {
222                write!(f, "(λ {} : {} . {})", binder.name.raw(), binder.ty.raw(), body.raw())
223            }
224            TermKind::Pi(binder, body) => {
225                write!(f, "(Π {} : {} . {})", binder.name.raw(), binder.ty.raw(), body.raw())
226            }
227            TermKind::Let(binder, val, body) => write!(
228                f,
229                "(let {} : {} := {} in {})",
230                binder.name.raw(),
231                binder.ty.raw(),
232                val.raw(),
233                body.raw()
234            ),
235            TermKind::MVar(id) => write!(f, "?{}", id.raw()),
236            TermKind::Lit(lit) => match lit {
237                Literal::Nat(n) => write!(f, "{}", n),
238                Literal::String(s) => write!(f, "\"{}\"", s),
239            },
240        }
241    }
242}
243
244#[cfg(test)]
245mod tests {
246    use super::*;
247
248    #[test]
249    fn test_term_creation() {
250        let term1 = Term::new(TermKind::Var(0));
251        let term2 = Term::new(TermKind::Var(0));
252
253        assert_eq!(term1.hash(), term2.hash());
254    }
255
256    #[test]
257    fn test_binder_info() {
258        let binder = Binder::new(SymbolId::new(0), TermId::new(0));
259        assert!(!binder.implicit);
260
261        let implicit = Binder::implicit(SymbolId::new(0), TermId::new(0));
262        assert!(implicit.implicit);
263    }
264}