catlog/tt/stx.rs
1//! Syntax for types and terms.
2//!
3//! See [crate::tt] for what this means.
4
5use derive_more::{Constructor, Deref};
6use std::fmt;
7use std::fmt::Write as _;
8
9use super::{prelude::*, theory::*};
10use crate::zero::LabelSegment;
11
12/// A metavariable.
13///
14/// Metavariables are emitted on elaboration error or when explicitly
15/// requested with `@hole`.
16///
17/// Metavariables in notebook elaboration are namespaced to the notebook.
18#[derive(Constructor, Clone, Copy, PartialEq, Eq)]
19pub struct MetaVar {
20 ref_id: Option<Ustr>,
21 id: usize,
22}
23
24impl fmt::Display for MetaVar {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 write!(f, "?{}", self.id)
27 }
28}
29
30/// Inner enum for [BaseTyS].
31pub enum BaseTyS_ {
32 /// A reference to a top-level declaration.
33 TopVar(TopVarName),
34 /// Type constructor for object types.
35 ///
36 /// Example syntax: `Entity` (top-level constants are bound by the elaborator to
37 /// various object types).
38 ///
39 /// A term of type `Object(ot)` represents an object of object type `ot`.
40 Object(ObType),
41
42 /// Type constructor for morphism types.
43 ///
44 /// Example syntax: `Attr x a` (top-level constants are bound by the elaborator
45 /// to constructors for morphism types).
46 ///
47 /// A term of type `Morphism(mt, dom, cod)` represents an morphism of morphism
48 /// type `mt` from `dom` to `cod`.
49 Morphism(MorType, BaseTmS, BaseTmS),
50
51 /// Type constructor for record types.
52 ///
53 /// Example syntax: `[x : A, y : B]`.
54 ///
55 /// A term `x` of type `Record(r)` represents a record where field `f` has type
56 /// `eval(env.snoc(eval(env, x)), r.fields1[f])`.
57 Record(Row<BaseTyS>),
58
59 /// Type constructor for singleton types.
60 ///
61 /// Example syntax: `@sing a` (assuming `a` is a term that synthesizes a type).
62 ///
63 /// A term `x` of type `Sing(ty, tm)` is a term of `ty` that is convertible with
64 /// `tm`.
65 Sing(BaseTyS, BaseTmS),
66
67 /// Type constructor for identity types.
68 ///
69 /// Example syntax: `a == b` (assuming `a` and `b` are terms that synthesize the same type).
70 ///
71 /// A term `p` of type `a == b` is a proof that `a` and `b` are equal.
72 Id(BaseTyS, BaseTmS, BaseTmS),
73
74 /// Type constructor for specialized types.
75 ///
76 /// Example syntax: `A & [ .x : @sing a ]`.
77 ///
78 /// A term `x` of type `Specialize(ty, d)` is a term of `ty` where additionally
79 /// for each path `p` (e.g. `.x`, `.a.b`, etc.) in `d`, `x.p` is of type `d[p]`.
80 ///
81 /// In order to form this type, it must be the case that `d[p]` is a subtype of
82 /// the type of the field at path `p`.
83 Specialize(BaseTyS, Vec<(Vec<(FieldName, LabelSegment)>, BaseTyS)>),
84
85 /// A metavar.
86 ///
87 /// Currently, this is only used for handling elaboration errors, we might
88 /// add more unification/holes later.
89 Meta(MetaVar),
90}
91
92/// Syntax for total types, dereferences to [BaseTyS_].
93///
94/// See [crate::tt] for an explanation of what total types are, and for an
95/// explanation of our approach to Rc pointers in abstract syntax trees.
96#[derive(Clone, Deref)]
97#[deref(forward)]
98pub struct BaseTyS(Rc<BaseTyS_>);
99
100impl BaseTyS {
101 /// Smart constructor for [BaseTyS], [BaseTyS_::TopVar] case.
102 pub fn topvar(name: TopVarName) -> Self {
103 Self(Rc::new(BaseTyS_::TopVar(name)))
104 }
105
106 /// Smart constructor for [BaseTyS], [BaseTyS_::Object] case.
107 pub fn object(object_type: ObType) -> Self {
108 Self(Rc::new(BaseTyS_::Object(object_type)))
109 }
110
111 /// Smart constructor for [BaseTyS], [BaseTyS_::Morphism] case.
112 pub fn morphism(morphism_type: MorType, dom: BaseTmS, cod: BaseTmS) -> Self {
113 Self(Rc::new(BaseTyS_::Morphism(morphism_type, dom, cod)))
114 }
115
116 /// Smart constructor for [BaseTyS], [BaseTyS_::Record] case.
117 pub fn record(fields: Row<BaseTyS>) -> Self {
118 Self(Rc::new(BaseTyS_::Record(fields)))
119 }
120
121 /// Smart constructor for [BaseTyS], [BaseTyS_::Sing] case.
122 pub fn sing(ty: BaseTyS, tm: BaseTmS) -> Self {
123 Self(Rc::new(BaseTyS_::Sing(ty, tm)))
124 }
125
126 /// Smart constructor for [BaseTyS], [BaseTyS_::Id] case.
127 pub fn id(ty: BaseTyS, tm1: BaseTmS, tm2: BaseTmS) -> Self {
128 Self(Rc::new(BaseTyS_::Id(ty, tm1, tm2)))
129 }
130
131 /// Smart constructor for [BaseTyS], [BaseTyS_::Specialize] case.
132 pub fn specialize(
133 ty: BaseTyS,
134 specializations: Vec<(Vec<(FieldName, LabelSegment)>, BaseTyS)>,
135 ) -> Self {
136 Self(Rc::new(BaseTyS_::Specialize(ty, specializations)))
137 }
138
139 /// Smart constructor for [BaseTyS], [BaseTyS_::Meta] case.
140 pub fn meta(mv: MetaVar) -> Self {
141 Self(Rc::new(BaseTyS_::Meta(mv)))
142 }
143}
144
145impl ToDoc for BaseTyS {
146 fn to_doc<'a>(&self) -> D<'a> {
147 match &**self {
148 BaseTyS_::TopVar(name) => t(format!("{}", name)),
149 BaseTyS_::Object(ob_type) => t(format!("{}", ob_type)),
150 BaseTyS_::Morphism(mor_type, dom, cod) => {
151 mor_type.to_doc().parens() + tuple([dom.to_doc(), cod.to_doc()])
152 }
153 BaseTyS_::Record(fields) => tuple(fields.iter().map(|(_, (label, ty))| {
154 binop(t(":"), t(format!("{}", label)).group(), ty.to_doc())
155 })),
156 BaseTyS_::Sing(_, tm) => t("@sing") + s() + tm.to_doc(),
157 BaseTyS_::Id(_, tm1, tm2) => binop(t("=="), tm1.to_doc(), tm2.to_doc()),
158 BaseTyS_::Specialize(ty, d) => binop(
159 t("&"),
160 ty.to_doc(),
161 tuple(
162 d.iter().map(|(name, ty)| binop(t(":"), t(path_to_string(name)), ty.to_doc())),
163 ),
164 ),
165 BaseTyS_::Meta(mv) => t(format!("?{}", mv.id)),
166 }
167 }
168}
169
170fn path_to_string(path: &[(FieldName, LabelSegment)]) -> String {
171 let mut out = String::new();
172 for (_, seg) in path {
173 write!(&mut out, ".{}", seg).unwrap();
174 }
175 out
176}
177
178impl fmt::Display for BaseTyS {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 write!(f, "{}", self.to_doc().group().pretty())
181 }
182}
183
184/// Inner enum for [BaseTmS].
185pub enum BaseTmS_ {
186 /// An application of a top-level term judgment to arguments.
187 ///
188 /// A closed term (a nullary `def`, e.g. `tt : Unit`) is the empty-argument
189 /// case `TopApp(name, [])`.
190 TopApp(TopVarName, Vec<BaseTmS>),
191 /// Variable syntax.
192 ///
193 /// We use a backward index, as when we evaluate we store the
194 /// environment in a [bwd::Bwd], and this indexes into that.
195 Var(BwdIdx, VarName, LabelSegment),
196 /// Record introduction.
197 Cons(Row<BaseTmS>),
198 /// Record elimination.
199 Proj(BaseTmS, FieldName, LabelSegment),
200 /// Identity morphism at an object.
201 Id(BaseTmS),
202 /// Tabulation of a morphism.
203 Tab(BaseTmS),
204 /// Composite of two morphisms.
205 Compose(BaseTmS, BaseTmS),
206 /// Application of an object operation in the theory.
207 ObApp(VarName, BaseTmS),
208 /// List of objects.
209 List(Vec<BaseTmS>),
210 /// A metavar.
211 ///
212 /// This only appears when we have an error in elaboration.
213 Meta(MetaVar),
214}
215
216/// Syntax for total terms, dereferences to [BaseTmS_].
217///
218/// See [crate::tt] for an explanation of what total types are, and for an
219/// explanation of our approach to Rc pointers in abstract syntax trees.
220#[derive(Clone, Deref)]
221#[deref(forward)]
222pub struct BaseTmS(Rc<BaseTmS_>);
223
224impl BaseTmS {
225 /// Smart constructor for [BaseTmS], [BaseTmS_::TopApp] case.
226 pub fn topapp(var_name: VarName, args: Vec<BaseTmS>) -> Self {
227 Self(Rc::new(BaseTmS_::TopApp(var_name, args)))
228 }
229
230 /// Smart constructor for [BaseTmS], [BaseTmS_::Var] case.
231 pub fn var(bwd_idx: BwdIdx, var_name: VarName, label: LabelSegment) -> Self {
232 Self(Rc::new(BaseTmS_::Var(bwd_idx, var_name, label)))
233 }
234
235 /// Smart constructor for [BaseTmS], [BaseTmS_::Cons] case.
236 pub fn cons(row: Row<BaseTmS>) -> Self {
237 Self(Rc::new(BaseTmS_::Cons(row)))
238 }
239
240 /// Smart constructor for [BaseTmS], [BaseTmS_::Proj] case.
241 pub fn proj(tm_s: BaseTmS, field_name: FieldName, label: LabelSegment) -> Self {
242 Self(Rc::new(BaseTmS_::Proj(tm_s, field_name, label)))
243 }
244
245 /// Smart constructor for [BaseTmS], [BaseTmS_::Id] case.
246 pub fn id(ob: BaseTmS) -> Self {
247 Self(Rc::new(BaseTmS_::Id(ob)))
248 }
249
250 /// Smart constructor for [BaseTmS], [BaseTmS_::Tab] case.
251 pub fn tab(mor: BaseTmS) -> Self {
252 Self(Rc::new(BaseTmS_::Tab(mor)))
253 }
254
255 /// Smart constructor for [BaseTmS], [BaseTmS_::Compose] case.
256 pub fn compose(f: BaseTmS, g: BaseTmS) -> Self {
257 Self(Rc::new(BaseTmS_::Compose(f, g)))
258 }
259
260 /// Smart constructor for [BaseTmS], [BaseTmS_::ObApp] case.
261 pub fn ob_app(name: VarName, x: BaseTmS) -> Self {
262 Self(Rc::new(BaseTmS_::ObApp(name, x)))
263 }
264
265 /// Smart constructor for [BaseTmS], [BaseTmS_::List] case.
266 pub fn list(elems: Vec<BaseTmS>) -> Self {
267 Self(Rc::new(BaseTmS_::List(elems)))
268 }
269
270 /// Smart constructor for [BaseTmS], [BaseTmS_::Meta] case.
271 pub fn meta(mv: MetaVar) -> Self {
272 Self(Rc::new(BaseTmS_::Meta(mv)))
273 }
274}
275
276impl ToDoc for BaseTmS {
277 fn to_doc<'a>(&self) -> D<'a> {
278 match &**self {
279 BaseTmS_::TopApp(name, args) if args.is_empty() => t(format!("{}", name)),
280 BaseTmS_::TopApp(name, args) => {
281 t(format!("{}", name)) + tuple(args.iter().map(|arg| arg.to_doc()))
282 }
283 BaseTmS_::Var(_, _, label) => t(format!("{}", label)),
284 BaseTmS_::Proj(tm, _, label) => tm.to_doc() + t(format!(".{}", label)),
285 BaseTmS_::Cons(fields) => tuple(fields.iter().map(|(_, (label, field))| {
286 binop(t(":="), t(format!("{}", label)), field.to_doc())
287 })),
288 BaseTmS_::Id(ob) => (t("@id") + s() + ob.to_doc()).parens(),
289 BaseTmS_::Tab(mor) => (t("@tab") + s() + mor.to_doc()).parens(),
290 BaseTmS_::Compose(f, g) => binop(t("·"), f.to_doc(), g.to_doc()),
291 BaseTmS_::ObApp(name, x) => unop(t(format!("@{name}")), x.to_doc()),
292 BaseTmS_::List(elems) => tuple(elems.iter().map(|elem| elem.to_doc())),
293 BaseTmS_::Meta(mv) => t(format!("?{}", mv.id)),
294 }
295 }
296}
297
298impl fmt::Display for BaseTmS {
299 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300 write!(f, "{}", self.to_doc().group().pretty())
301 }
302}
303
304/// Inner enum for [FiberTyS].
305///
306/// Fiber types type the fiber world — instances of a model and their
307/// elements — mirroring how [`BaseTyS`] types the base world (models).
308/// See [`crate::tt::toplevel`] for the comprehension-category picture.
309/// The constructors parallel the base world: [`TopVar`](Self::TopVar)
310/// references a top-level instance, [`Over`](Self::Over) is the atomic
311/// fiber-element type, [`Record`](Self::Record) assembles them into an
312/// instance, and [`Id`](Self::Id) imposes a (propositional) equation —
313/// just as [`BaseTyS_::TopVar`] and [`BaseTyS_::Id`] do in the base.
314pub enum FiberTyS_ {
315 /// A reference to a top-level instance declaration, as in a
316 /// sub-instance import `we : Edge`. Mirrors [`BaseTyS_::TopVar`]: it
317 /// appears only in *syntax* and exists to preserve the instance's
318 /// name for display — like base top-vars, it is resolved away in the
319 /// value world (there is no `FiberTyV_::TopVar`), where it becomes the
320 /// referenced instance's [`Record`](Self::Record).
321 TopVar(TopVarName),
322 /// The type of a fiber element lying over a codomain object `obj`.
323 ///
324 /// `obj` is a base object *term* (rooted at the codomain model), so it
325 /// may be a plain generator (`self.V`), or a modal object such as a
326 /// list `[M, M]` or a tensor `@tensor [H, M]`. Comparing two
327 /// `Over` types is comparing their base objects, so modal objects need
328 /// no special handling. No surface syntax — its inhabitants
329 /// ([`FiberTmS`]) are introduced by set-literal clauses `field :=
330 /// [...]`, projection out of a sub-instance import, fiber list/object
331 /// -operation literals, and codomain-morphism application.
332 Over(BaseTmS),
333 /// An instance of a model — an object of the fiber over the codomain
334 /// model — presented as a record of fiber types. A generator is an
335 /// [`Over`](Self::Over) field, a sub-instance import is a nested
336 /// [`Record`](Self::Record) field, and an equation is an
337 /// [`Id`](Self::Id) field. This is what `instance I : X := [...]`
338 /// elaborates to, and also the type of a sub-instance import `we :
339 /// Edge` (whose generators are then projected as `we.e`).
340 Record(Row<FiberTyS>),
341 /// A propositional equation between two fiber elements of the given
342 /// fiber type, asserted to hold in the enclosing instance. Mirrors
343 /// [`BaseTyS_::Id`]; like it, these are proof-irrelevant.
344 Id(FiberTyS, FiberTmS, FiberTmS),
345}
346
347/// Syntax for fiber types, dereferences to [FiberTyS_].
348#[derive(Clone, Deref)]
349#[deref(forward)]
350pub struct FiberTyS(Rc<FiberTyS_>);
351
352impl FiberTyS {
353 /// Smart constructor for [FiberTyS], [FiberTyS_::TopVar] case.
354 pub fn topvar(name: TopVarName) -> Self {
355 Self(Rc::new(FiberTyS_::TopVar(name)))
356 }
357
358 /// Smart constructor for [FiberTyS], [FiberTyS_::Over] case.
359 pub fn over(obj: BaseTmS) -> Self {
360 Self(Rc::new(FiberTyS_::Over(obj)))
361 }
362
363 /// Smart constructor for [FiberTyS], [FiberTyS_::Record] case.
364 pub fn record(fields: Row<FiberTyS>) -> Self {
365 Self(Rc::new(FiberTyS_::Record(fields)))
366 }
367
368 /// Smart constructor for [FiberTyS], [FiberTyS_::Id] case.
369 pub fn id(ty: FiberTyS, tm1: FiberTmS, tm2: FiberTmS) -> Self {
370 Self(Rc::new(FiberTyS_::Id(ty, tm1, tm2)))
371 }
372}
373
374impl ToDoc for FiberTyS {
375 fn to_doc<'a>(&self) -> D<'a> {
376 match &**self {
377 FiberTyS_::TopVar(name) => t(format!("{}", name)),
378 FiberTyS_::Over(obj) => t("Over(") + obj.to_doc() + t(")"),
379 FiberTyS_::Record(fields) => tuple(fields.iter().map(|(_, (label, ty))| {
380 binop(t(":"), t(format!("{}", label)).group(), ty.to_doc())
381 })),
382 FiberTyS_::Id(_, tm1, tm2) => binop(t("=="), tm1.to_doc(), tm2.to_doc()),
383 }
384 }
385}
386
387impl fmt::Display for FiberTyS {
388 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389 write!(f, "{}", self.to_doc().group().pretty())
390 }
391}
392
393/// Inner enum for [FiberTmS]: a term of a fiber type, i.e. an element of
394/// an instance.
395///
396/// Fiber terms reference the elaborator's *fiber* scope (generators and
397/// sub-instance imports), which is separate from the base context; see
398/// [`crate::tt::context::Context`]. They are all neutral — there is no
399/// fiber introduction form yet (mapping out of an instance by a record
400/// literal is future work), so a fiber term is always a variable, a
401/// projection, or a codomain-morphism application.
402pub enum FiberTmS_ {
403 /// A fiber-context variable: a generator or a sub-instance import.
404 /// Backward index into the fiber environment.
405 Var(BwdIdx, VarName, LabelSegment),
406 /// Projection of a generator out of a sub-instance import, e.g.
407 /// `we.e`.
408 Proj(FiberTmS, FieldName, LabelSegment),
409 /// A fiber list literal `[a, b, ...]` (possibly empty). Its fiber type
410 /// is `Over([A, B, ...])` where each `x_i : Over(A_i)`. Mirrors base
411 /// [`BaseTmS_::List`]; used to supply the (modal) list argument of a
412 /// multi-ary morphism, e.g. `op[x, x]`.
413 List(Vec<FiberTmS>),
414 /// Application of a theory object-operation to a fiber element, e.g.
415 /// `@tensor [a, b]`. Mirrors base [`BaseTmS_::ObApp`]; its fiber type
416 /// is `Over(@op ...)` over the operation applied to the argument's
417 /// base object.
418 ObApp(VarName, FiberTmS),
419 /// Application of a codomain morphism to a fiber element. Arguments,
420 /// in order: the *path* to the morphism in the codomain (a single
421 /// segment like `src`, or a nested one like `Add.op` for a morphism of
422 /// a sub-model), the codomain object it lands at (a base object term,
423 /// stored so the result fiber type is recoverable without re-deriving
424 /// it), and the fiber-typed argument (e.g. the elaboration of `we.e`,
425 /// or a fiber list `[x, x]` for a multi-ary morphism).
426 ///
427 /// Example: in `src(we.e) := v1`, the LHS elaborates to
428 /// `OverApp([src], self.V, Proj(Var(we), e, e))` of fiber type
429 /// `Over(self.V)`.
430 OverApp(Vec<(FieldName, LabelSegment)>, BaseTmS, FiberTmS),
431 /// A metavar (elaboration-error placeholder).
432 Meta(MetaVar),
433}
434
435/// Syntax for fiber terms, dereferences to [FiberTmS_].
436#[derive(Clone, Deref)]
437#[deref(forward)]
438pub struct FiberTmS(Rc<FiberTmS_>);
439
440impl FiberTmS {
441 /// Smart constructor for [FiberTmS], [FiberTmS_::Var] case.
442 pub fn var(bwd_idx: BwdIdx, var_name: VarName, label: LabelSegment) -> Self {
443 Self(Rc::new(FiberTmS_::Var(bwd_idx, var_name, label)))
444 }
445
446 /// Smart constructor for [FiberTmS], [FiberTmS_::Proj] case.
447 pub fn proj(tm: FiberTmS, field_name: FieldName, label: LabelSegment) -> Self {
448 Self(Rc::new(FiberTmS_::Proj(tm, field_name, label)))
449 }
450
451 /// Smart constructor for [FiberTmS], [FiberTmS_::List] case.
452 pub fn list(elems: Vec<FiberTmS>) -> Self {
453 Self(Rc::new(FiberTmS_::List(elems)))
454 }
455
456 /// Smart constructor for [FiberTmS], [FiberTmS_::ObApp] case.
457 pub fn ob_app(name: VarName, arg: FiberTmS) -> Self {
458 Self(Rc::new(FiberTmS_::ObApp(name, arg)))
459 }
460
461 /// Smart constructor for [FiberTmS], [FiberTmS_::OverApp] case.
462 pub fn over_app(mor: Vec<(FieldName, LabelSegment)>, cod: BaseTmS, inner: FiberTmS) -> Self {
463 Self(Rc::new(FiberTmS_::OverApp(mor, cod, inner)))
464 }
465
466 /// Smart constructor for [FiberTmS], [FiberTmS_::Meta] case.
467 pub fn meta(mv: MetaVar) -> Self {
468 Self(Rc::new(FiberTmS_::Meta(mv)))
469 }
470}
471
472impl ToDoc for FiberTmS {
473 fn to_doc<'a>(&self) -> D<'a> {
474 match &**self {
475 FiberTmS_::Var(_, _, label) => t(format!("{}", label)),
476 FiberTmS_::Proj(tm, _, label) => tm.to_doc() + t(format!(".{}", label)),
477 FiberTmS_::List(elems) => tuple(elems.iter().map(|e| e.to_doc())),
478 FiberTmS_::ObApp(name, arg) => unop(t(format!("@{name}")), arg.to_doc()),
479 FiberTmS_::OverApp(path, _, inner) => {
480 let mut d = inner.to_doc();
481 for (_, label) in path {
482 d = d + t(format!(".{label}"));
483 }
484 d
485 }
486 FiberTmS_::Meta(mv) => t(format!("?{}", mv.id)),
487 }
488 }
489}
490
491impl fmt::Display for FiberTmS {
492 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493 write!(f, "{}", self.to_doc().group().pretty())
494 }
495}