catlog/tt/context.rs
1//! Contexts store the type and values of in-scope variables during elaboration.
2
3use derive_more::Constructor;
4
5use crate::tt::{prelude::*, val::*};
6
7/// Each variable in context is associated with a label and a type.
8///
9/// Multiple variables with the same name can show up in context; in this case
10/// the most recent one is selected, following the standard scope conventions.
11#[derive(Constructor)]
12pub struct VarInContext {
13 /// The name of the variable.
14 pub name: VarName,
15 /// The label for the variable.
16 pub label: LabelSegment,
17 /// The type of the variable.
18 ///
19 /// We allow the type to be null as a hack for the `self` variable before we
20 /// know the type of the `self` variable.
21 pub ty: Option<BaseTyV>,
22}
23
24/// Each *fiber* variable in context — a generator or sub-instance import
25/// introduced inside an instance body — with its label and fiber type.
26#[derive(Constructor)]
27pub struct FiberVarInContext {
28 /// The name of the fiber variable.
29 pub name: VarName,
30 /// The label for the fiber variable.
31 pub label: LabelSegment,
32 /// The fiber type of the variable.
33 pub ty: FiberTyV,
34}
35
36/// The variable context during elaboration.
37///
38/// Carries two scopes: the **base** context (`env`/`scope`) of ordinary
39/// terms typed by [`BaseTyV`], and a separate **fiber** context
40/// (`fiber_env`/`fiber_scope`) of instance generators and sub-instance
41/// imports typed by [`FiberTyV`]. The fiber scope is populated only while
42/// elaborating an instance body; the two never alias, so neither lookup
43/// can see the other's variables. See [`crate::tt::toplevel`] for why the
44/// two worlds are distinct.
45pub struct Context {
46 /// Stores the value of each of the base variables in context.
47 pub env: Env,
48 /// Stores the names and types of each of the base variables in context.
49 pub scope: Vec<VarInContext>,
50 /// Stores the value of each fiber variable in context.
51 pub fiber_env: FiberEnv,
52 /// Stores the names and fiber types of each fiber variable in context.
53 pub fiber_scope: Vec<FiberVarInContext>,
54}
55
56/// A checkpoint that we can return the context to.
57pub struct ContextCheckpoint {
58 env: Env,
59 scope: usize,
60 fiber_env: FiberEnv,
61 fiber_scope: usize,
62}
63
64impl Default for Context {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70impl Context {
71 /// Create an empty context.
72 pub fn new() -> Self {
73 Self {
74 env: Env::Nil,
75 scope: Vec::new(),
76 fiber_env: FiberEnv::Nil,
77 fiber_scope: Vec::new(),
78 }
79 }
80
81 /// Create a checkpoint from the current state of the context.
82 pub fn checkpoint(&self) -> ContextCheckpoint {
83 ContextCheckpoint {
84 env: self.env.clone(),
85 scope: self.scope.len(),
86 fiber_env: self.fiber_env.clone(),
87 fiber_scope: self.fiber_scope.len(),
88 }
89 }
90
91 /// Reset the context to a previously-saved checkpoint.
92 pub fn reset_to(&mut self, c: ContextCheckpoint) {
93 self.env = c.env;
94 self.scope.truncate(c.scope);
95 self.fiber_env = c.fiber_env;
96 self.fiber_scope.truncate(c.fiber_scope);
97 }
98
99 /// Add a new base variable to scope (note: does not add it to the environment).
100 pub fn push_scope(&mut self, name: VarName, label: LabelSegment, ty: Option<BaseTyV>) {
101 self.scope.push(VarInContext::new(name, label, ty))
102 }
103
104 /// Lookup a base variable by name.
105 pub fn lookup(&self, name: VarName) -> Option<(BwdIdx, LabelSegment, Option<BaseTyV>)> {
106 self.scope
107 .iter()
108 .rev()
109 .enumerate()
110 .find(|(_, v)| v.name == name)
111 .map(|(i, v)| (i.into(), v.label, v.ty.clone()))
112 }
113
114 /// Add a new fiber variable to scope (note: does not add it to the
115 /// fiber environment).
116 pub fn push_fiber(&mut self, name: VarName, label: LabelSegment, ty: FiberTyV) {
117 self.fiber_scope.push(FiberVarInContext::new(name, label, ty))
118 }
119
120 /// Lookup a fiber variable by name.
121 pub fn lookup_fiber(&self, name: VarName) -> Option<(BwdIdx, LabelSegment, FiberTyV)> {
122 self.fiber_scope
123 .iter()
124 .rev()
125 .enumerate()
126 .find(|(_, v)| v.name == name)
127 .map(|(i, v)| (i.into(), v.label, v.ty.clone()))
128 }
129}