1use notebook_types::v1::{ModelJudgment, MorDecl, Ob, ObDecl, ObType};
3use uuid::Uuid;
4
5use crate::dbl::{
6 VDblCategory,
7 theory::{DblTheory, DiscreteDblTheory},
8};
9use std::str::FromStr;
10
11use crate::{
12 tt::{context::*, eval::*, prelude::*, stx::*, toplevel::*, val::*},
13 zero::QualifiedName,
14};
15
16#[derive(Debug)]
37pub enum Error {
38 NoSuchObjectType(QualifiedName),
40 NoSuchVariable(VarName),
42 TabulatorUnsupported,
44 ModalUnsupported,
46 NonDiscreteUnsupported,
48 ExpectedObject,
50 MismatchedDomTypes {
52 expected: ObjectType,
54 synthesized: ObjectType,
56 },
57 MismatchedCodTypes {
59 expected: ObjectType,
61 synthesized: ObjectType,
63 },
64}
65
66pub struct LocatedError {
68 pub cell: Option<Uuid>,
70 pub content: Error,
72}
73
74pub struct Elaborator<'a> {
78 theory: Theory,
79 toplevel: &'a Toplevel,
80 current_cell: Option<Uuid>,
81 ctx: Context,
82 errors: Vec<LocatedError>,
83}
84
85struct ElaboratorCheckpoint {
86 ctx: ContextCheckpoint,
87}
88
89impl<'a> Elaborator<'a> {
90 pub fn new(theory: Theory, toplevel: &'a Toplevel) -> Self {
92 Self {
93 theory,
94 toplevel,
95 current_cell: None,
96 ctx: Context::new(),
97 errors: Vec::new(),
98 }
99 }
100
101 fn dbl_theory(&self) -> &DiscreteDblTheory {
102 &self.theory.definition
103 }
104
105 pub fn errors(&self) -> &[LocatedError] {
107 &self.errors
108 }
109
110 fn checkpoint(&self) -> ElaboratorCheckpoint {
111 ElaboratorCheckpoint {
112 ctx: self.ctx.checkpoint(),
113 }
114 }
115
116 fn reset_to(&mut self, c: ElaboratorCheckpoint) {
117 self.ctx.reset_to(c.ctx);
118 }
119
120 fn evaluator(&self) -> Evaluator<'a> {
121 Evaluator::new(self.toplevel, self.ctx.env.clone(), self.ctx.scope.len())
122 }
123
124 fn intro(&mut self, name: VarName, label: LabelSegment, ty: Option<TyV>) -> TmV {
125 let v = TmV::Neu(
126 TmN::var(self.ctx.scope.len().into(), name, label),
127 ty.clone().unwrap_or(TyV::unit()),
128 );
129 let v = if let Some(ty) = &ty {
130 self.evaluator().eta(&v, ty)
131 } else {
132 v
133 };
134 self.ctx.env = self.ctx.env.snoc(v.clone());
135 self.ctx.scope.push(VarInContext::new(name, label, ty));
136 v
137 }
138
139 fn error<T>(&mut self, error: Error) -> Option<T> {
140 self.errors.push(LocatedError {
141 cell: self.current_cell,
142 content: error,
143 });
144 None
145 }
146
147 fn ob_type(&mut self, ob_type: &ObType) -> Option<QualifiedName> {
148 match &ob_type {
149 ObType::Basic(name) => Some(QualifiedName::single(name_seg(*name))),
150 ObType::Tabulator(_) => self.error(Error::TabulatorUnsupported),
151 ObType::ModeApp { .. } => self.error(Error::ModalUnsupported),
152 }
153 }
154
155 fn object_cell(&mut self, ob_decl: &ObDecl) -> Option<(NameSegment, LabelSegment, TyS, TyV)> {
156 let ob_type = self.ob_type(&ob_decl.ob_type)?;
157 if !self.dbl_theory().has_ob(&ob_type) {
158 return self.error(Error::NoSuchObjectType(ob_type.clone()));
159 }
160 let name = NameSegment::Uuid(ob_decl.id);
161 let label = LabelSegment::Text(ustr(&ob_decl.name));
162 Some((name, label, TyS::object(ob_type.clone()), TyV::object(ob_type)))
163 }
164
165 fn lookup_tm(&mut self, name: VarName) -> Option<(TmS, TmV, TyV)> {
166 let Some((i, label, ty)) = self.ctx.lookup(name) else {
167 return self.error(Error::NoSuchVariable(name));
168 };
169 let v = self.ctx.env.get(*i).unwrap().clone();
170 Some((TmS::var(i, name, label), v, ty.clone().unwrap()))
171 }
172
173 fn ob(&mut self, n: &Ob) -> Option<(TmS, TmV, ObjectType)> {
174 match n {
175 Ob::Basic(name) => {
176 let (tm, val, ty) =
177 self.lookup_tm(NameSegment::Uuid(Uuid::from_str(name).unwrap()))?;
178 let ob_type = match &*ty {
179 TyV_::Object(ob_type) => ob_type.clone(),
180 _ => return self.error(Error::ExpectedObject),
181 };
182 Some((tm, val, ob_type))
183 }
184 Ob::App { .. } => self.error(Error::NonDiscreteUnsupported),
185 Ob::List { .. } => self.error(Error::ModalUnsupported),
186 Ob::Tabulated(_) => self.error(Error::TabulatorUnsupported),
187 }
188 }
189
190 fn morphism_cell(
191 &mut self,
192 mor_decl: &MorDecl,
193 ) -> Option<(NameSegment, LabelSegment, TyS, TyV)> {
194 let (mor_type, dom_ty, cod_ty) = match &mor_decl.mor_type {
195 notebook_types::v1::MorType::Basic(name) => {
196 let name = QualifiedName::single(name_seg(*name));
197 let mor_type = Path::single(name);
198 let dom_ty = self.dbl_theory().src_type(&mor_type);
199 let cod_ty = self.dbl_theory().tgt_type(&mor_type);
200 (MorphismType(mor_type), dom_ty, cod_ty)
201 }
202 notebook_types::v1::MorType::Hom(ob_type) => {
203 let ob_type = self.ob_type(ob_type)?;
204 (MorphismType(Path::Id(ob_type.clone())), ob_type.clone(), ob_type)
205 }
206 notebook_types::v1::MorType::Composite(_) => {
207 todo!()
208 }
209 notebook_types::v1::MorType::ModeApp { .. } => {
210 return self.error(Error::ModalUnsupported);
211 }
212 };
213 let name = NameSegment::Uuid(mor_decl.id);
214 let label = LabelSegment::Text(ustr(&mor_decl.name));
215 let dom = mor_decl.dom.as_ref().and_then(|ob| self.ob(ob));
216 let cod = mor_decl.cod.as_ref().and_then(|ob| self.ob(ob));
217 let ((dom_s, dom_v, synthed_dom_ty), (cod_s, cod_v, synthed_cod_ty)) = (dom?, cod?);
218 let correct_dom = if synthed_dom_ty != dom_ty {
219 self.error(Error::MismatchedDomTypes {
220 expected: dom_ty,
221 synthesized: synthed_dom_ty,
222 })
223 } else {
224 Some(())
225 };
226 let correct_cod = if synthed_cod_ty != cod_ty {
227 self.error(Error::MismatchedCodTypes {
228 expected: cod_ty,
229 synthesized: synthed_cod_ty,
230 })
231 } else {
232 Some(())
233 };
234 let _ = (correct_dom?, correct_cod?);
235 Some((
236 name,
237 label,
238 TyS::morphism(mor_type.clone(), dom_s, cod_s),
239 TyV::morphism(mor_type, dom_v, cod_v),
240 ))
241 }
242
243 pub fn notebook(&mut self, cells: &[(Uuid, &ModelJudgment)]) -> Option<(TyS, TyV)> {
245 let mut field_ty0s = Vec::new();
246 let mut field_ty_vs = Vec::new();
247 let self_var = self.intro(name_seg("self"), label_seg("self"), None).as_neu();
248 let c = self.checkpoint();
249 for (id, cell) in cells.iter() {
250 self.current_cell = Some(*id);
251 let (name, label, _, ty_v) = match cell {
252 ModelJudgment::Object(ob_decl) => self.object_cell(ob_decl),
253 ModelJudgment::Morphism(mor_decl) => self.morphism_cell(mor_decl),
254 }?;
255 field_ty0s.push((name, (label, ty_v.ty0())));
256 field_ty_vs.push((name, (label, ty_v.clone())));
257 self.ctx.scope.push(VarInContext::new(name, label, Some(ty_v.clone())));
258 self.ctx.env =
259 self.ctx.env.snoc(TmV::Neu(TmN::proj(self_var.clone(), name, label), ty_v));
260 }
261 self.reset_to(c);
262 let field_tys: Row<_> = field_ty_vs
263 .iter()
264 .map(|(name, (label, ty_v))| (*name, (*label, self.evaluator().quote_ty(ty_v))))
265 .collect();
266 let field_ty0s: Row<_> = field_ty0s.into_iter().collect();
267 let r_s = RecordS::new(field_ty0s.clone(), field_tys.clone());
268 let r_v = RecordV::new(field_ty0s, self.ctx.env.clone(), field_tys, Dtry::empty());
269 Some((TyS::record(r_s), TyV::record(r_v)))
270 }
271}
272
273#[cfg(test)]
274mod test {
275 use notebook_types::v1::ModelDocumentContent;
276 use serde_json;
277 use std::fmt::Write;
278 use std::{fs, rc::Rc};
279
280 use expect_test::{Expect, expect};
281
282 use crate::stdlib::th_schema;
283 use crate::tt::toplevel::{Theory, std_theories};
284 use crate::tt::{
285 modelgen::{generate, model_output},
286 notebook_elab::Elaborator,
287 toplevel::Toplevel,
288 };
289 use crate::zero::name;
290
291 fn elab_example(theory: &Theory, name: &str, expected: Expect) {
292 let src = fs::read_to_string(format!("examples/{name}.json")).unwrap();
293 let doc: ModelDocumentContent = serde_json::from_str(&src).unwrap();
294 let cells = doc
295 .notebook
296 .cells()
297 .filter_map(|c| match c {
298 notebook_types::v1::NotebookCell::Formal { id, content } => Some((*id, content)),
299 _ => None,
300 })
301 .collect::<Vec<_>>();
302 let toplevel = Toplevel::new(std_theories());
303 let mut elab = Elaborator::new(theory.clone(), &toplevel);
304 let mut out = String::new();
305 if let Some((_, ty_v)) = elab.notebook(&cells) {
306 let (model, name_translation) = generate(&toplevel, &theory, &ty_v);
307 model_output("", &mut out, &model, &name_translation).unwrap();
308 } else {
309 assert!(
310 !elab.errors().is_empty(),
311 "did not produce a model, but no errors were reported"
312 );
313 for error in elab.errors() {
314 writeln!(&mut out, "error at cell {:?} : {:?}", error.cell.unwrap(), error.content)
315 .unwrap()
316 }
317 }
318 expected.assert_eq(&out);
319 }
320
321 #[test]
322 fn examples() {
323 let th_schema = Theory::new(name("ThSchema"), Rc::new(th_schema()));
324 elab_example(
325 &th_schema,
326 "weighted_graph",
327 expect![[r#"
328 object generators:
329 E : Entity
330 V : Entity
331 Weight : AttrType
332 morphism generators:
333 weight : E -> Weight (Attr)
334 src : E -> V (Id Entity)
335 tgt : E -> V (Id Entity)
336 "#]],
337 );
338 }
339}