Skip to main content

logicaffeine_kernel/
term.rs

1//! Unified term representation for the Calculus of Constructions.
2//!
3//! In CoC, there is no distinction between terms and types.
4//! Everything is a Term in an infinite hierarchy of universes.
5
6use std::fmt;
7
8/// Primitive literal values.
9///
10/// These are opaque values that compute via hardware ALU, not recursion.
11#[derive(Debug, Clone, PartialEq)]
12pub enum Literal {
13    /// 64-bit signed integer
14    Int(i64),
15    /// 64-bit floating point
16    Float(f64),
17    /// UTF-8 string
18    Text(String),
19    /// Duration in nanoseconds (signed for negative offsets like "5 min early")
20    Duration(i64),
21    /// Calendar date as days since Unix epoch (i32 gives ±5.8 million year range)
22    Date(i32),
23    /// Instant in time as nanoseconds since Unix epoch (UTC)
24    Moment(i64),
25}
26
27impl Eq for Literal {}
28
29impl fmt::Display for Literal {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        match self {
32            Literal::Int(n) => write!(f, "{}", n),
33            Literal::Float(x) => write!(f, "{}", x),
34            Literal::Text(s) => write!(f, "{:?}", s),
35            Literal::Duration(nanos) => {
36                // Display in most human-readable unit
37                let abs = nanos.unsigned_abs();
38                let sign = if *nanos < 0 { "-" } else { "" };
39                if abs >= 3_600_000_000_000 {
40                    write!(f, "{}{}h", sign, abs / 3_600_000_000_000)
41                } else if abs >= 60_000_000_000 {
42                    write!(f, "{}{}min", sign, abs / 60_000_000_000)
43                } else if abs >= 1_000_000_000 {
44                    write!(f, "{}{}s", sign, abs / 1_000_000_000)
45                } else if abs >= 1_000_000 {
46                    write!(f, "{}{}ms", sign, abs / 1_000_000)
47                } else if abs >= 1_000 {
48                    write!(f, "{}{}μs", sign, abs / 1_000)
49                } else {
50                    write!(f, "{}{}ns", sign, abs)
51                }
52            }
53            Literal::Date(days) => {
54                // Convert days since epoch to ISO-8601 date
55                // Unix epoch is 1970-01-01 (day 0)
56                // We use a simple algorithm for display purposes
57                let days = *days as i64;
58                let (year, month, day) = days_to_ymd(days);
59                write!(f, "{:04}-{:02}-{:02}", year, month, day)
60            }
61            Literal::Moment(nanos) => {
62                // Convert to ISO-8601 datetime
63                let secs = nanos / 1_000_000_000;
64                let days = secs / 86400;
65                let time_secs = secs % 86400;
66                let hours = time_secs / 3600;
67                let mins = (time_secs % 3600) / 60;
68                let secs_rem = time_secs % 60;
69                let (year, month, day) = days_to_ymd(days);
70                write!(f, "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
71                       year, month, day, hours, mins, secs_rem)
72            }
73        }
74    }
75}
76
77/// Convert days since Unix epoch to (year, month, day).
78fn days_to_ymd(days: i64) -> (i64, u8, u8) {
79    // Civil date from days since epoch using the algorithm from Howard Hinnant
80    // https://howardhinnant.github.io/date_algorithms.html
81    let z = days + 719468;
82    let era = if z >= 0 { z / 146097 } else { (z - 146096) / 146097 };
83    let doe = (z - era * 146097) as u32;
84    let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
85    let y = yoe as i64 + era * 400;
86    let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
87    let mp = (5 * doy + 2) / 153;
88    let d = doy - (153 * mp + 2) / 5 + 1;
89    let m = if mp < 10 { mp + 3 } else { mp - 9 };
90    let year = if m <= 2 { y + 1 } else { y };
91    (year, m as u8, d as u8)
92}
93
94/// Universe levels in the type hierarchy.
95///
96/// The hierarchy is: Prop : Type 1 : Type 2 : Type 3 : ...
97///
98/// - `Prop` is the universe of propositions (proof-irrelevant in full CIC)
99/// - `Type(n)` is the universe of types at level n
100#[derive(Debug, Clone, PartialEq, Eq)]
101pub enum Universe {
102    /// Prop - the universe of propositions
103    Prop,
104    /// Type n - the universe of types at level n
105    Type(u32),
106}
107
108impl Universe {
109    /// Get the successor universe: Type n → Type (n+1)
110    pub fn succ(&self) -> Universe {
111        match self {
112            Universe::Prop => Universe::Type(1),
113            Universe::Type(n) => Universe::Type(n + 1),
114        }
115    }
116
117    /// Get the maximum of two universes (for Pi type formation)
118    pub fn max(&self, other: &Universe) -> Universe {
119        match (self, other) {
120            (Universe::Prop, u) | (u, Universe::Prop) => u.clone(),
121            (Universe::Type(a), Universe::Type(b)) => Universe::Type((*a).max(*b)),
122        }
123    }
124
125    /// Check if this universe is a subtype of another (cumulativity).
126    ///
127    /// Subtyping rules:
128    /// - Prop ≤ Type(i) for all i
129    /// - Type(i) ≤ Type(j) if i ≤ j
130    /// - Type(i) is NOT ≤ Prop
131    pub fn is_subtype_of(&self, other: &Universe) -> bool {
132        match (self, other) {
133            // Prop ≤ anything (Prop ≤ Prop, Prop ≤ Type(i))
134            (Universe::Prop, _) => true,
135            // Type(i) ≤ Type(j) if i ≤ j
136            (Universe::Type(i), Universe::Type(j)) => i <= j,
137            // Type(i) is NOT ≤ Prop
138            (Universe::Type(_), Universe::Prop) => false,
139        }
140    }
141}
142
143/// Unified term representation.
144///
145/// Every expression in CoC is a Term:
146/// - `Sort(u)` - universes (Type 0, Type 1, Prop)
147/// - `Var(x)` - variables
148/// - `Pi` - dependent function types: Π(x:A). B
149/// - `Lambda` - functions: λ(x:A). t
150/// - `App` - application: f x
151#[derive(Debug, Clone, PartialEq, Eq)]
152pub enum Term {
153    /// Universe: Type n or Prop
154    Sort(Universe),
155
156    /// Local variable reference (bound by λ or Π)
157    Var(String),
158
159    /// Global definition (inductive type or constructor)
160    Global(String),
161
162    /// Dependent function type: Π(x:A). B
163    ///
164    /// When B doesn't mention x, this is just A → B.
165    /// When B mentions x, this is a dependent type.
166    Pi {
167        param: String,
168        param_type: Box<Term>,
169        body_type: Box<Term>,
170    },
171
172    /// Lambda abstraction: λ(x:A). t
173    Lambda {
174        param: String,
175        param_type: Box<Term>,
176        body: Box<Term>,
177    },
178
179    /// Application: f x
180    App(Box<Term>, Box<Term>),
181
182    /// Pattern matching on inductive types.
183    ///
184    /// `match discriminant return motive with cases`
185    ///
186    /// - discriminant: the term being matched (must have inductive type)
187    /// - motive: λx:I. T — describes the return type
188    /// - cases: one case per constructor, in definition order
189    Match {
190        discriminant: Box<Term>,
191        motive: Box<Term>,
192        cases: Vec<Term>,
193    },
194
195    /// Fixpoint (recursive function).
196    ///
197    /// `fix name. body` binds `name` to itself within `body`.
198    /// Used for recursive definitions like addition.
199    Fix {
200        /// Name for self-reference within the body
201        name: String,
202        /// The body of the fixpoint (typically a lambda)
203        body: Box<Term>,
204    },
205
206    /// Primitive literal value.
207    ///
208    /// Hardware-native values like i64, f64, String.
209    /// These compute via CPU ALU, not recursion.
210    Lit(Literal),
211
212    /// Hole (implicit argument).
213    ///
214    /// Represents an argument that should be inferred by the type checker.
215    /// Used in Literate syntax like `X equals Y` where the type of X/Y is implicit.
216    Hole,
217}
218
219impl fmt::Display for Universe {
220    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
221        match self {
222            Universe::Prop => write!(f, "Prop"),
223            Universe::Type(n) => write!(f, "Type{}", n),
224        }
225    }
226}
227
228impl fmt::Display for Term {
229    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230        match self {
231            Term::Sort(u) => write!(f, "{}", u),
232            Term::Var(name) => write!(f, "{}", name),
233            Term::Global(name) => write!(f, "{}", name),
234            Term::Pi {
235                param,
236                param_type,
237                body_type,
238            } => {
239                // Use arrow notation for non-dependent functions (param = "_")
240                if param == "_" {
241                    write!(f, "{} -> {}", param_type, body_type)
242                } else {
243                    write!(f, "Π({}:{}). {}", param, param_type, body_type)
244                }
245            }
246            Term::Lambda {
247                param,
248                param_type,
249                body,
250            } => {
251                write!(f, "λ({}:{}). {}", param, param_type, body)
252            }
253            Term::App(func, arg) => {
254                // Arrow types (Pi with _) need inner parens when used as args
255                let arg_needs_inner_parens =
256                    matches!(arg.as_ref(), Term::Pi { param, .. } if param == "_");
257
258                if arg_needs_inner_parens {
259                    write!(f, "({} ({}))", func, arg)
260                } else {
261                    write!(f, "({} {})", func, arg)
262                }
263            }
264            Term::Match {
265                discriminant,
266                motive,
267                cases,
268            } => {
269                write!(f, "match {} return {} with ", discriminant, motive)?;
270                for (i, case) in cases.iter().enumerate() {
271                    if i > 0 {
272                        write!(f, " | ")?;
273                    }
274                    write!(f, "{}", case)?;
275                }
276                Ok(())
277            }
278            Term::Fix { name, body } => {
279                write!(f, "fix {}. {}", name, body)
280            }
281            Term::Lit(lit) => {
282                write!(f, "{}", lit)
283            }
284            Term::Hole => {
285                write!(f, "_")
286            }
287        }
288    }
289}