logicaffeine_kernel/context.rs
1//! Typing context for the kernel.
2//!
3//! A context maps variable names to their types.
4//! Used during type checking to track what variables are in scope.
5
6use crate::term::Term;
7use std::collections::HashMap;
8
9/// Typing context: maps variable names to their types.
10///
11/// The context is immutable-by-default: `extend` creates a new context
12/// with the additional binding, preserving the original.
13///
14/// Also stores global definitions:
15/// - Inductive types (e.g., Nat : Type 0)
16/// - Constructors (e.g., Zero : Nat, Succ : Nat -> Nat)
17/// - Declarations (e.g., hypotheses like h1 : P -> Q)
18#[derive(Debug, Clone, Default)]
19pub struct Context {
20 /// Local variable bindings (from λ and Π)
21 bindings: HashMap<String, Term>,
22
23 /// Inductive type definitions: name -> sort (e.g., "Nat" -> Type 0)
24 inductives: HashMap<String, Term>,
25
26 /// Constructor definitions: name -> (inductive_name, type)
27 constructors: HashMap<String, (String, Term)>,
28
29 /// Order of constructor registration per inductive.
30 /// HashMap doesn't preserve insertion order, so we track it explicitly.
31 constructor_order: HashMap<String, Vec<String>>,
32
33 /// Declaration bindings (axioms/hypotheses): name -> type
34 /// Used for certifying proofs where hypotheses are assumed.
35 declarations: HashMap<String, Term>,
36
37 /// Definition bodies: name -> (type, body)
38 /// Definitions are transparent - they unfold during normalization.
39 /// Distinguished from declarations (axioms) which have no body.
40 definitions: HashMap<String, (Term, Term)>,
41
42 /// Hint database: theorem names marked as hints for auto tactic.
43 /// When auto fails with decision procedures, it tries to apply these hints.
44 hints: Vec<String>,
45}
46
47impl Context {
48 /// Create an empty context.
49 pub fn new() -> Self {
50 Context {
51 bindings: HashMap::new(),
52 inductives: HashMap::new(),
53 constructors: HashMap::new(),
54 constructor_order: HashMap::new(),
55 declarations: HashMap::new(),
56 definitions: HashMap::new(),
57 hints: Vec::new(),
58 }
59 }
60
61 /// Add a local binding to this context (mutates in place).
62 pub fn add(&mut self, name: &str, ty: Term) {
63 self.bindings.insert(name.to_string(), ty);
64 }
65
66 /// Look up a local variable's type in the context.
67 pub fn get(&self, name: &str) -> Option<&Term> {
68 self.bindings.get(name)
69 }
70
71 /// Create a new context extended with an additional local binding.
72 ///
73 /// Does not mutate the original context.
74 pub fn extend(&self, name: &str, ty: Term) -> Context {
75 let mut new_ctx = self.clone();
76 new_ctx.add(name, ty);
77 new_ctx
78 }
79
80 /// Register an inductive type.
81 ///
82 /// The `sort` is the type of the inductive (e.g., Type 0 for Nat).
83 pub fn add_inductive(&mut self, name: &str, sort: Term) {
84 self.inductives.insert(name.to_string(), sort);
85 }
86
87 /// Register a constructor for an inductive type.
88 ///
89 /// The `ty` is the full type of the constructor
90 /// (e.g., `Nat` for Zero, `Nat -> Nat` for Succ).
91 ///
92 /// Constructors are tracked in registration order for match expressions.
93 pub fn add_constructor(&mut self, name: &str, inductive: &str, ty: Term) {
94 self.constructors
95 .insert(name.to_string(), (inductive.to_string(), ty));
96
97 // Track constructor order for this inductive
98 self.constructor_order
99 .entry(inductive.to_string())
100 .or_default()
101 .push(name.to_string());
102 }
103
104 /// Add a declaration (typed assumption/hypothesis).
105 ///
106 /// Used for proof certification where hypotheses are assumed.
107 /// Example: h1 : P -> Q
108 pub fn add_declaration(&mut self, name: &str, ty: Term) {
109 self.declarations.insert(name.to_string(), ty);
110 }
111
112 /// Register a definition: name : type := body
113 ///
114 /// Definitions are transparent and unfold during normalization (delta reduction).
115 /// This distinguishes them from declarations (axioms) which have no body.
116 pub fn add_definition(&mut self, name: String, ty: Term, body: Term) {
117 self.definitions.insert(name, (ty, body));
118 }
119
120 /// Look up a global definition (inductive, constructor, definition, or declaration).
121 ///
122 /// Returns the type of the global.
123 pub fn get_global(&self, name: &str) -> Option<&Term> {
124 // Check inductives first
125 if let Some(sort) = self.inductives.get(name) {
126 return Some(sort);
127 }
128 // Check constructors
129 if let Some((_, ty)) = self.constructors.get(name) {
130 return Some(ty);
131 }
132 // Check definitions (return type, not body)
133 if let Some((ty, _)) = self.definitions.get(name) {
134 return Some(ty);
135 }
136 // Check declarations (axioms)
137 self.declarations.get(name)
138 }
139
140 /// Check if a name is a definition (has a body that can be unfolded).
141 pub fn is_definition(&self, name: &str) -> bool {
142 self.definitions.contains_key(name)
143 }
144
145 /// Get the body of a definition, if it exists.
146 ///
147 /// Returns None for axioms, constructors, and inductives (only definitions have bodies).
148 pub fn get_definition_body(&self, name: &str) -> Option<&Term> {
149 self.definitions.get(name).map(|(_, body)| body)
150 }
151
152 /// Get the type of a definition, if it exists.
153 pub fn get_definition_type(&self, name: &str) -> Option<&Term> {
154 self.definitions.get(name).map(|(ty, _)| ty)
155 }
156
157 /// Check if a name is a constructor.
158 pub fn is_constructor(&self, name: &str) -> bool {
159 self.constructors.contains_key(name)
160 }
161
162 /// Get the inductive type a constructor belongs to.
163 pub fn constructor_inductive(&self, name: &str) -> Option<&str> {
164 self.constructors.get(name).map(|(ind, _)| ind.as_str())
165 }
166
167 /// Check if a name is an inductive type.
168 pub fn is_inductive(&self, name: &str) -> bool {
169 self.inductives.contains_key(name)
170 }
171
172 /// Get all constructors for an inductive type, in registration order.
173 ///
174 /// Returns a vector of (constructor_name, constructor_type) pairs.
175 pub fn get_constructors(&self, inductive: &str) -> Vec<(&str, &Term)> {
176 self.constructor_order
177 .get(inductive)
178 .map(|names| {
179 names
180 .iter()
181 .filter_map(|name| {
182 self.constructors
183 .get(name)
184 .map(|(_, ty)| (name.as_str(), ty))
185 })
186 .collect()
187 })
188 .unwrap_or_default()
189 }
190
191 /// Iterate over all declarations (hypotheses).
192 ///
193 /// Used by the certifier to find hypothesis by type.
194 pub fn iter_declarations(&self) -> impl Iterator<Item = (&str, &Term)> {
195 self.declarations.iter().map(|(k, v)| (k.as_str(), v))
196 }
197
198 /// Iterate over all definitions.
199 ///
200 /// Used by the UI to display definitions.
201 pub fn iter_definitions(&self) -> impl Iterator<Item = (&str, &Term, &Term)> {
202 self.definitions.iter().map(|(k, (ty, body))| (k.as_str(), ty, body))
203 }
204
205 /// Iterate over all inductive types.
206 ///
207 /// Used by the UI to display inductive types.
208 pub fn iter_inductives(&self) -> impl Iterator<Item = (&str, &Term)> {
209 self.inductives.iter().map(|(k, v)| (k.as_str(), v))
210 }
211
212 /// Add a constructor with strict positivity checking.
213 ///
214 /// Returns an error if the inductive type appears negatively in the
215 /// constructor type. This prevents paradoxes like:
216 /// ```text
217 /// Inductive Bad := Cons : (Bad -> False) -> Bad
218 /// ```
219 pub fn add_constructor_checked(
220 &mut self,
221 name: &str,
222 inductive: &str,
223 ty: Term,
224 ) -> crate::error::KernelResult<()> {
225 // Check strict positivity first
226 crate::positivity::check_positivity(inductive, name, &ty)?;
227
228 // If it passes, add the constructor normally
229 self.add_constructor(name, inductive, ty);
230 Ok(())
231 }
232
233 /// Register a theorem as a hint for the auto tactic.
234 ///
235 /// Hints are theorems that auto will try to apply when decision
236 /// procedures fail. This allows auto to "learn" from proven theorems.
237 pub fn add_hint(&mut self, name: &str) {
238 if !self.hints.contains(&name.to_string()) {
239 self.hints.push(name.to_string());
240 }
241 }
242
243 /// Get all registered hints.
244 ///
245 /// Returns the names of theorems registered as hints.
246 pub fn get_hints(&self) -> &[String] {
247 &self.hints
248 }
249
250 /// Check if a theorem is registered as a hint.
251 pub fn is_hint(&self, name: &str) -> bool {
252 self.hints.contains(&name.to_string())
253 }
254}