1use super::{canonical_name, declined, rejected, DiagnosticCode, Result};
22use serde_json::{Map, Value};
23use std::collections::{HashMap, HashSet};
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub struct Profile {
28 pub lean_version: &'static str,
29 pub format_version: &'static str,
30 pub exporter_name: &'static str,
31 pub exporter_version: &'static str,
32 pub allowed_axioms: &'static [&'static str],
34}
35
36pub const STAGE_A: Profile = Profile {
39 lean_version: "4.14.0",
40 format_version: "3.1.0",
41 exporter_name: "lean4export",
42 exporter_version: "3.1.0",
43 allowed_axioms: &["propext", "Quot.sound", "Classical.choice"],
44};
45
46pub const TOOLCHAIN: &str = "leanprover/lean4:v4.14.0";
48pub const EXPORTER_REVISION: &str = "20c4a64aa1b76f7c79a6db15d089084359f8c3dc";
50pub const EXPORTER_SOURCES: [(&str, &str); 2] = [
53 (
54 "Main.lean",
55 "bd8e350903f1382b25fe767f9086dd9882000f01085f7046ea06545e7848842a",
56 ),
57 (
58 "Export.lean",
59 "16fa1fec370500f4ca03f042fc3e65cac27839b640b32c5a921e085206c7031b",
60 ),
61];
62pub const EXPORTER_FLAGS: [&str; 2] = ["--export-mdata", "--export-unsafe"];
65
66#[derive(Clone, Debug, PartialEq, Eq)]
68pub struct Header {
69 pub lean_version: String,
70 pub lean_githash: String,
71 pub exporter_name: String,
72 pub exporter_version: String,
73 pub format_version: String,
74}
75
76#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78pub enum DeclarationKind {
79 Axiom,
80 Definition,
81 Theorem,
82 Opaque,
83 Quotient,
84 Inductive,
85 Constructor,
86 Recursor,
87}
88
89impl DeclarationKind {
90 pub fn as_str(self) -> &'static str {
91 match self {
92 DeclarationKind::Axiom => "axiom",
93 DeclarationKind::Definition => "def",
94 DeclarationKind::Theorem => "thm",
95 DeclarationKind::Opaque => "opaque",
96 DeclarationKind::Quotient => "quot",
97 DeclarationKind::Inductive => "inductive",
98 DeclarationKind::Constructor => "ctor",
99 DeclarationKind::Recursor => "rec",
100 }
101 }
102}
103
104#[derive(Clone, Debug, PartialEq, Eq)]
106pub struct Declaration {
107 pub kernel_name: String,
109 pub lean_name: String,
111 pub kind: DeclarationKind,
112 pub line: usize,
114 record: usize,
116}
117
118struct Record {
120 roots: Vec<usize>,
121 members: Vec<usize>,
122}
123
124pub struct Intake {
126 header: Header,
127 declarations: Vec<Declaration>,
128 records: Vec<Record>,
129 by_kernel_name: HashMap<String, usize>,
130 expressions: HashMap<usize, (Vec<usize>, Option<usize>)>,
132 declared: HashMap<usize, usize>,
134}
135
136impl Intake {
137 pub fn header(&self) -> &Header {
138 &self.header
139 }
140 pub fn declarations(&self) -> &[Declaration] {
142 &self.declarations
143 }
144 pub fn declaration(&self, kernel_name: &str) -> Option<&Declaration> {
146 self.by_kernel_name
147 .get(kernel_name)
148 .map(|&i| &self.declarations[i])
149 }
150 pub fn closure(&self, kernel_name: &str) -> Result<Vec<String>> {
154 let &start = self.by_kernel_name.get(kernel_name).ok_or_else(|| {
155 rejected(
156 DiagnosticCode::MissingDeclaration,
157 format!("the export does not declare {}", lean_name(kernel_name)),
158 )
159 .in_declaration(lean_name(kernel_name))
160 })?;
161 let mut records_seen = HashSet::new();
162 let mut expressions_seen = HashSet::new();
163 let mut in_closure = vec![false; self.declarations.len()];
164 let mut pending_records = vec![self.declarations[start].record];
165 let mut pending_expressions = Vec::new();
166 while let Some(record) = pending_records.pop() {
167 if !records_seen.insert(record) {
168 continue;
169 }
170 for &member in &self.records[record].members {
171 in_closure[member] = true;
172 }
173 pending_expressions.extend(self.records[record].roots.iter().copied());
174 while let Some(expression) = pending_expressions.pop() {
175 if !expressions_seen.insert(expression) {
176 continue;
177 }
178 let Some((children, constant)) = self.expressions.get(&expression) else {
179 continue;
180 };
181 pending_expressions.extend(children.iter().copied());
182 if let Some(declaration) = constant.and_then(|name| self.declared.get(&name)) {
183 pending_records.push(self.declarations[*declaration].record);
184 }
185 }
186 }
187 Ok(self
188 .declarations
189 .iter()
190 .zip(in_closure)
191 .filter(|(_, member)| *member)
192 .map(|(d, _)| d.kernel_name.clone())
193 .collect())
194 }
195}
196
197pub fn read(export: &str, profile: &Profile) -> Result<Intake> {
199 let mut lines = export.lines();
200 let first = lines.next().ok_or_else(|| {
201 declined(DiagnosticCode::MalformedExport, "the export is empty").at_line(1)
202 })?;
203 let header = read_header(first, profile).map_err(|v| v.at_line(1))?;
204 let mut reader = Reader::default();
205 reader.names.insert(0, String::new());
206 reader.levels.insert(0);
207 for (offset, text) in lines.enumerate() {
208 let line = offset + 2;
209 if text.trim().is_empty() {
210 continue;
211 }
212 let record: Value = serde_json::from_str(text).map_err(|e| {
213 declined(DiagnosticCode::MalformedExport, format!("line {line}: {e}")).at_line(line)
214 })?;
215 reader.record(&record, line).map_err(|v| v.at_line(line))?;
216 }
217 reader.finish(header)
218}
219
220pub fn read_header(first: &str, profile: &Profile) -> Result<Header> {
223 let value: Value = serde_json::from_str(first).map_err(|e| {
224 declined(
225 DiagnosticCode::MalformedExport,
226 format!("the export header is not JSON: {e}"),
227 )
228 })?;
229 if let Some(format) = value
230 .pointer("/meta/format/version")
231 .and_then(Value::as_str)
232 {
233 if format != profile.format_version {
234 return Err(declined(
235 DiagnosticCode::UnsupportedExportVersion,
236 format!(
237 "export format {format} is not the supported format {}",
238 profile.format_version
239 ),
240 ));
241 }
242 }
243 let root = object(&value, "the export header")?;
244 fields(root, "the export header", &["meta"], &[])?;
245 let meta = object(&root["meta"], "meta")?;
246 fields(meta, "meta", &["exporter", "lean", "format"], &[])?;
247 let exporter = object(&meta["exporter"], "meta.exporter")?;
248 fields(exporter, "meta.exporter", &["name", "version"], &[])?;
249 let lean = object(&meta["lean"], "meta.lean")?;
250 fields(lean, "meta.lean", &["githash", "version"], &[])?;
251 let format = object(&meta["format"], "meta.format")?;
252 fields(format, "meta.format", &["version"], &[])?;
253 let header = Header {
254 lean_version: string(&lean["version"], "meta.lean.version")?,
255 lean_githash: string(&lean["githash"], "meta.lean.githash")?,
256 exporter_name: string(&exporter["name"], "meta.exporter.name")?,
257 exporter_version: string(&exporter["version"], "meta.exporter.version")?,
258 format_version: string(&format["version"], "meta.format.version")?,
259 };
260 if header.format_version != profile.format_version {
261 return Err(declined(
262 DiagnosticCode::UnsupportedExportVersion,
263 format!(
264 "export format {} is not the supported format {}",
265 header.format_version, profile.format_version
266 ),
267 ));
268 }
269 if header.lean_version != profile.lean_version
270 || header.exporter_name != profile.exporter_name
271 || header.exporter_version != profile.exporter_version
272 {
273 return Err(declined(
274 DiagnosticCode::UnsupportedToolchain,
275 format!(
276 "the export was produced by Lean {} with {} {}; the supported profile is Lean {} with {} {}",
277 header.lean_version,
278 header.exporter_name,
279 header.exporter_version,
280 profile.lean_version,
281 profile.exporter_name,
282 profile.exporter_version
283 ),
284 ));
285 }
286 Ok(header)
287}
288
289fn object<'v>(value: &'v Value, what: &str) -> Result<&'v Map<String, Value>> {
290 value.as_object().ok_or_else(|| {
291 declined(
292 DiagnosticCode::MalformedExport,
293 format!("{what} is not a JSON object"),
294 )
295 })
296}
297
298fn string(value: &Value, what: &str) -> Result<String> {
299 value.as_str().map(str::to_string).ok_or_else(|| {
300 declined(
301 DiagnosticCode::MalformedExport,
302 format!("{what} is not a string"),
303 )
304 })
305}
306
307fn fields(
309 object: &Map<String, Value>,
310 what: &str,
311 required: &[&str],
312 optional: &[&str],
313) -> Result<()> {
314 if let Some(unknown) = object
315 .keys()
316 .find(|k| !required.contains(&k.as_str()) && !optional.contains(&k.as_str()))
317 {
318 return Err(declined(
319 DiagnosticCode::UnknownField,
320 format!("{what} has the field {unknown:?}, which export format 3.1.0 does not define"),
321 ));
322 }
323 if let Some(missing) = required.iter().find(|k| !object.contains_key(**k)) {
324 return Err(declined(
325 DiagnosticCode::MalformedExport,
326 format!("{what} lacks its mandatory field {missing:?}"),
327 ));
328 }
329 Ok(())
330}
331
332fn natural(value: &Value, what: &str) -> Result<usize> {
333 value
334 .as_u64()
335 .and_then(|n| usize::try_from(n).ok())
336 .ok_or_else(|| {
337 declined(
338 DiagnosticCode::MalformedExport,
339 format!("{what} is not a natural number"),
340 )
341 })
342}
343
344fn boolean(value: &Value, what: &str) -> Result<()> {
345 value.as_bool().map(|_| ()).ok_or_else(|| {
346 declined(
347 DiagnosticCode::MalformedExport,
348 format!("{what} is not a boolean"),
349 )
350 })
351}
352
353fn naturals(value: &Value, what: &str) -> Result<Vec<usize>> {
354 value
355 .as_array()
356 .ok_or_else(|| {
357 declined(
358 DiagnosticCode::MalformedExport,
359 format!("{what} is not an array"),
360 )
361 })?
362 .iter()
363 .map(|v| natural(v, what))
364 .collect()
365}
366
367#[derive(Default)]
368struct Reader {
369 names: HashMap<usize, String>,
370 levels: HashSet<usize>,
371 expressions: HashMap<usize, (Vec<usize>, Option<usize>)>,
372 referenced: HashMap<usize, usize>,
374 declarations: Vec<Declaration>,
375 records: Vec<Record>,
376 by_kernel_name: HashMap<String, usize>,
377 declared: HashMap<usize, usize>,
378}
379
380impl Reader {
381 fn record(&mut self, record: &Value, line: usize) -> Result<()> {
382 let map = record.as_object().ok_or_else(|| {
383 declined(
384 DiagnosticCode::MalformedExport,
385 "the record is not a JSON object",
386 )
387 })?;
388 if map.contains_key("in") {
389 return self.name(map);
390 }
391 if map.contains_key("il") {
392 return self.level(map);
393 }
394 if map.contains_key("ie") {
395 return self.expression(map, line);
396 }
397 if map.len() != 1 {
398 let kinds: Vec<&String> = map.keys().collect();
399 return Err(declined(
400 DiagnosticCode::UnknownRecordKind,
401 format!("a record with fields {kinds:?} is no record kind of export format 3.1.0"),
402 ));
403 }
404 let (kind, body) = map.iter().next().expect("one field");
405 let body = object(body, kind)?;
406 match kind.as_str() {
407 "axiom" => {
408 fields(
409 body,
410 "axiom",
411 &["name", "levelParams", "type", "isUnsafe"],
412 &[],
413 )?;
414 boolean(&body["isUnsafe"], "axiom.isUnsafe")?;
415 self.simple(body, DeclarationKind::Axiom, line, &["type"])
416 }
417 "def" => {
418 fields(
419 body,
420 "def",
421 &[
422 "name",
423 "levelParams",
424 "type",
425 "value",
426 "hints",
427 "safety",
428 "all",
429 ],
430 &[],
431 )?;
432 naturals(&body["all"], "def.all")?;
433 match &body["safety"] {
434 Value::String(s) if ["safe", "unsafe", "partial"].contains(&s.as_str()) => {}
435 other => {
436 return Err(declined(
437 DiagnosticCode::MalformedExport,
438 format!("def.safety {other} is not safe, unsafe or partial"),
439 ))
440 }
441 }
442 self.simple(body, DeclarationKind::Definition, line, &["type", "value"])
443 }
444 "opaque" => {
445 fields(
446 body,
447 "opaque",
448 &["name", "levelParams", "type", "value", "isUnsafe", "all"],
449 &[],
450 )?;
451 boolean(&body["isUnsafe"], "opaque.isUnsafe")?;
452 naturals(&body["all"], "opaque.all")?;
453 self.simple(body, DeclarationKind::Opaque, line, &["type", "value"])
454 }
455 "thm" => {
456 fields(
457 body,
458 "thm",
459 &["name", "levelParams", "type", "value", "all"],
460 &[],
461 )?;
462 naturals(&body["all"], "thm.all")?;
463 self.simple(body, DeclarationKind::Theorem, line, &["type", "value"])
464 }
465 "quot" => {
466 fields(body, "quot", &["name", "levelParams", "type", "kind"], &[])?;
467 match body["kind"].as_str() {
468 Some("type" | "ctor" | "lift" | "ind") => {}
469 _ => {
470 return Err(declined(
471 DiagnosticCode::UnknownRecordKind,
472 format!(
473 "quot.kind {} is no quotient kind of export format 3.1.0",
474 body["kind"]
475 ),
476 ))
477 }
478 }
479 self.simple(body, DeclarationKind::Quotient, line, &["type"])
480 }
481 "inductive" => self.inductive(body, line),
482 other => Err(declined(
483 DiagnosticCode::UnknownRecordKind,
484 format!("{other:?} is no declaration kind of export format 3.1.0"),
485 )),
486 }
487 }
488
489 fn name(&mut self, map: &Map<String, Value>) -> Result<()> {
490 let index = natural(&map["in"], "in")?;
491 let (part, body) = if map.contains_key("str") {
492 fields(map, "a name record", &["in", "str"], &[])?;
493 let body = object(&map["str"], "str")?;
494 fields(body, "str", &["pre", "str"], &[])?;
495 let text = string(&body["str"], "str.str")?;
496 (
497 format!(
498 "s:{}",
499 serde_json::to_string(&text).expect("a string serializes")
500 ),
501 body,
502 )
503 } else if map.contains_key("num") {
504 fields(map, "a name record", &["in", "num"], &[])?;
505 let body = object(&map["num"], "num")?;
506 fields(body, "num", &["pre", "i"], &[])?;
507 (format!("n:{}", natural(&body["i"], "num.i")?), body)
508 } else {
509 return Err(declined(
510 DiagnosticCode::UnknownRecordKind,
511 "a name record that is neither str nor num",
512 ));
513 };
514 let prefix = self
515 .names
516 .get(&natural(&body["pre"], "pre")?)
517 .ok_or_else(|| {
518 declined(
519 DiagnosticCode::MalformedExport,
520 "a name's prefix is not an earlier name",
521 )
522 })?
523 .clone();
524 if self
525 .names
526 .insert(index, format!("{prefix}/{part}"))
527 .is_some()
528 {
529 return Err(declined(
530 DiagnosticCode::MalformedExport,
531 format!("name index {index} is defined twice"),
532 ));
533 }
534 Ok(())
535 }
536
537 fn level(&mut self, map: &Map<String, Value>) -> Result<()> {
538 let index = natural(&map["il"], "il")?;
539 let kind = ["succ", "max", "imax", "param"]
540 .into_iter()
541 .find(|k| map.contains_key(*k))
542 .ok_or_else(|| {
543 declined(
544 DiagnosticCode::UnknownRecordKind,
545 "a level record of no level kind of export format 3.1.0",
546 )
547 })?;
548 fields(map, "a level record", &["il", kind], &[])?;
549 let earlier = |this: &Self, v: &Value| -> Result<()> {
550 let j = natural(v, kind)?;
551 if this.levels.contains(&j) {
552 Ok(())
553 } else {
554 Err(declined(
555 DiagnosticCode::MalformedExport,
556 format!("level {index} refers to level {j}, which is not an earlier level"),
557 ))
558 }
559 };
560 match kind {
561 "succ" => earlier(self, &map["succ"])?,
562 "param" => {
563 let name = natural(&map["param"], "param")?;
564 if !self.names.contains_key(&name) {
565 return Err(declined(
566 DiagnosticCode::MalformedExport,
567 "a universe parameter's name is not an earlier name",
568 ));
569 }
570 }
571 _ => {
572 let pair = map[kind]
573 .as_array()
574 .filter(|p| p.len() == 2)
575 .ok_or_else(|| {
576 declined(
577 DiagnosticCode::MalformedExport,
578 format!("{kind} is not a pair of levels"),
579 )
580 })?;
581 earlier(self, &pair[0])?;
582 earlier(self, &pair[1])?;
583 }
584 }
585 if !self.levels.insert(index) {
586 return Err(declined(
587 DiagnosticCode::MalformedExport,
588 format!("level index {index} is defined twice"),
589 ));
590 }
591 Ok(())
592 }
593
594 fn expression(&mut self, map: &Map<String, Value>, line: usize) -> Result<()> {
595 let index = natural(&map["ie"], "ie")?;
596 if map.contains_key("mvar") || map.contains_key("fvar") {
597 return Err(rejected(
598 DiagnosticCode::UnresolvedMetavariable,
599 format!("expression {index} is a metavariable or free variable: the declaration was never fully elaborated"),
600 ));
601 }
602 const KINDS: [&str; 11] = [
603 "bvar", "sort", "const", "app", "lam", "forallE", "letE", "proj", "natVal", "strVal",
604 "mdata",
605 ];
606 let kind = KINDS
607 .into_iter()
608 .find(|k| map.contains_key(*k))
609 .ok_or_else(|| {
610 let fields: Vec<&String> = map.keys().collect();
611 declined(DiagnosticCode::UnknownRecordKind, format!("expression {index} ({fields:?}) is no expression kind of export format 3.1.0"))
612 })?;
613 fields(map, "an expression record", &["ie", kind], &[])?;
614 let body = &map[kind];
615 let mut children = Vec::new();
616 let mut constant = None;
617 match kind {
618 "bvar" => {
619 natural(body, "bvar")?;
620 }
621 "sort" => self.level_ref(body)?,
622 "natVal" | "strVal" => {
623 string(body, kind)?;
624 }
625 "const" => {
626 let c = object(body, "const")?;
627 fields(c, "const", &["name", "us"], &[])?;
628 let name = natural(&c["name"], "const.name")?;
629 if !self.names.contains_key(&name) {
630 return Err(declined(
631 DiagnosticCode::MalformedExport,
632 "a constant's name is not an earlier name",
633 ));
634 }
635 for u in c["us"].as_array().ok_or_else(|| {
636 declined(DiagnosticCode::MalformedExport, "const.us is not an array")
637 })? {
638 self.level_ref(u)?;
639 }
640 self.referenced.entry(name).or_insert(line);
641 constant = Some(name);
642 }
643 "app" => {
644 let a = object(body, "app")?;
645 fields(a, "app", &["fn", "arg"], &[])?;
646 children.push(natural(&a["fn"], "app.fn")?);
647 children.push(natural(&a["arg"], "app.arg")?);
648 }
649 "lam" | "forallE" => {
650 let b = object(body, kind)?;
651 fields(b, kind, &["name", "type", "body", "binderInfo"], &[])?;
652 self.name_ref(&b["name"])?;
653 match b["binderInfo"].as_str() {
654 Some("default" | "implicit" | "strictImplicit" | "instImplicit") => {}
655 _ => {
656 return Err(declined(
657 DiagnosticCode::UnknownField,
658 format!(
659 "{kind}.binderInfo {} is no binder information of export format 3.1.0",
660 b["binderInfo"]
661 ),
662 ))
663 }
664 }
665 children.push(natural(&b["type"], kind)?);
666 children.push(natural(&b["body"], kind)?);
667 }
668 "letE" => {
669 let b = object(body, "letE")?;
670 fields(b, "letE", &["name", "type", "value", "body", "nondep"], &[])?;
671 self.name_ref(&b["name"])?;
672 boolean(&b["nondep"], "letE.nondep")?;
673 for part in ["type", "value", "body"] {
674 children.push(natural(&b[part], "letE")?);
675 }
676 }
677 "proj" => {
678 let p = object(body, "proj")?;
679 fields(p, "proj", &["typeName", "idx", "struct"], &[])?;
680 let structure = natural(&p["typeName"], "proj.typeName")?;
681 if !self.names.contains_key(&structure) {
682 return Err(declined(
683 DiagnosticCode::MalformedExport,
684 "a projection's structure is not an earlier name",
685 ));
686 }
687 natural(&p["idx"], "proj.idx")?;
688 self.referenced.entry(structure).or_insert(line);
689 constant = Some(structure);
690 children.push(natural(&p["struct"], "proj.struct")?);
691 }
692 "mdata" => {
693 let m = object(body, "mdata")?;
694 fields(m, "mdata", &["expr", "data"], &[])?;
695 object(&m["data"], "mdata.data")?;
696 children.push(natural(&m["expr"], "mdata.expr")?);
697 }
698 _ => unreachable!("every expression kind is matched"),
699 }
700 if let Some(child) = children.iter().find(|c| !self.expressions.contains_key(c)) {
701 return Err(declined(DiagnosticCode::MalformedExport, format!("expression {index} refers to expression {child}, which is not an earlier expression")));
702 }
703 if self
704 .expressions
705 .insert(index, (children, constant))
706 .is_some()
707 {
708 return Err(declined(
709 DiagnosticCode::MalformedExport,
710 format!("expression index {index} is defined twice"),
711 ));
712 }
713 Ok(())
714 }
715
716 fn level_ref(&self, value: &Value) -> Result<()> {
717 let level = natural(value, "level")?;
718 if self.levels.contains(&level) {
719 Ok(())
720 } else {
721 Err(declined(
722 DiagnosticCode::MalformedExport,
723 format!("level {level} is not an earlier level"),
724 ))
725 }
726 }
727
728 fn name_ref(&self, value: &Value) -> Result<usize> {
729 let name = natural(value, "name")?;
730 if self.names.contains_key(&name) {
731 Ok(name)
732 } else {
733 Err(declined(
734 DiagnosticCode::MalformedExport,
735 format!("name {name} is not an earlier name"),
736 ))
737 }
738 }
739
740 fn expression_ref(&self, value: &Value, what: &str) -> Result<usize> {
741 let expression = natural(value, what)?;
742 if self.expressions.contains_key(&expression) {
743 Ok(expression)
744 } else {
745 Err(declined(
746 DiagnosticCode::MalformedExport,
747 format!(
748 "{what} refers to expression {expression}, which is not an earlier expression"
749 ),
750 ))
751 }
752 }
753
754 fn level_params(&self, value: &Value, what: &str) -> Result<()> {
755 for name in naturals(value, what)? {
756 if !self.names.contains_key(&name) {
757 return Err(declined(
758 DiagnosticCode::MalformedExport,
759 format!("{what} names an unknown universe parameter"),
760 ));
761 }
762 }
763 Ok(())
764 }
765
766 fn simple(
768 &mut self,
769 body: &Map<String, Value>,
770 kind: DeclarationKind,
771 line: usize,
772 roots: &[&str],
773 ) -> Result<()> {
774 self.level_params(&body["levelParams"], "levelParams")?;
775 let roots = roots
776 .iter()
777 .map(|field| self.expression_ref(&body[*field], field))
778 .collect::<Result<Vec<_>>>()?;
779 let name = self.name_ref(&body["name"])?;
780 let record = self.records.len();
781 self.records.push(Record {
782 roots,
783 members: Vec::new(),
784 });
785 self.declare(name, kind, line, record)
786 }
787
788 fn inductive(&mut self, body: &Map<String, Value>, line: usize) -> Result<()> {
789 fields(body, "inductive", &["types", "ctors", "recs"], &[])?;
790 let record = self.records.len();
791 self.records.push(Record {
792 roots: Vec::new(),
793 members: Vec::new(),
794 });
795 let list = |field: &str| -> Result<Vec<Value>> {
796 body[field].as_array().cloned().ok_or_else(|| {
797 declined(
798 DiagnosticCode::MalformedExport,
799 format!("inductive.{field} is not an array"),
800 )
801 })
802 };
803 let (types, ctors, recs) = (list("types")?, list("ctors")?, list("recs")?);
804 let mut roots = Vec::new();
805 let mut members = Vec::new();
806 for t in &types {
807 let t = object(t, "an inductive type")?;
808 fields(
809 t,
810 "an inductive type",
811 &[
812 "name",
813 "levelParams",
814 "type",
815 "numParams",
816 "numIndices",
817 "all",
818 "ctors",
819 "numNested",
820 "isRec",
821 "isUnsafe",
822 "isReflexive",
823 ],
824 &[],
825 )?;
826 for n in ["numParams", "numIndices", "numNested"] {
827 natural(&t[n], n)?;
828 }
829 for b in ["isRec", "isUnsafe", "isReflexive"] {
830 boolean(&t[b], b)?;
831 }
832 naturals(&t["all"], "all")?;
833 naturals(&t["ctors"], "ctors")?;
834 self.level_params(&t["levelParams"], "levelParams")?;
835 roots.push(self.expression_ref(&t["type"], "type")?);
836 members.push((self.name_ref(&t["name"])?, DeclarationKind::Inductive));
837 }
838 for c in &ctors {
839 let c = object(c, "a constructor")?;
840 fields(
841 c,
842 "a constructor",
843 &[
844 "name",
845 "levelParams",
846 "type",
847 "induct",
848 "cidx",
849 "numParams",
850 "numFields",
851 "isUnsafe",
852 ],
853 &[],
854 )?;
855 for n in ["induct", "cidx", "numParams", "numFields"] {
856 natural(&c[n], n)?;
857 }
858 boolean(&c["isUnsafe"], "isUnsafe")?;
859 self.level_params(&c["levelParams"], "levelParams")?;
860 roots.push(self.expression_ref(&c["type"], "type")?);
861 members.push((self.name_ref(&c["name"])?, DeclarationKind::Constructor));
862 }
863 for r in &recs {
864 let r = object(r, "a recursor")?;
865 fields(
866 r,
867 "a recursor",
868 &[
869 "name",
870 "levelParams",
871 "type",
872 "all",
873 "numParams",
874 "numIndices",
875 "numMotives",
876 "numMinors",
877 "rules",
878 "k",
879 "isUnsafe",
880 ],
881 &[],
882 )?;
883 for n in ["numParams", "numIndices", "numMotives", "numMinors"] {
884 natural(&r[n], n)?;
885 }
886 for b in ["k", "isUnsafe"] {
887 boolean(&r[b], b)?;
888 }
889 naturals(&r["all"], "all")?;
890 self.level_params(&r["levelParams"], "levelParams")?;
891 roots.push(self.expression_ref(&r["type"], "type")?);
892 for rule in r["rules"]
893 .as_array()
894 .ok_or_else(|| declined(DiagnosticCode::MalformedExport, "rules is not an array"))?
895 {
896 let rule = object(rule, "a recursor rule")?;
897 fields(rule, "a recursor rule", &["ctor", "nfields", "rhs"], &[])?;
898 self.name_ref(&rule["ctor"])?;
899 natural(&rule["nfields"], "nfields")?;
900 roots.push(self.expression_ref(&rule["rhs"], "rhs")?);
901 }
902 members.push((self.name_ref(&r["name"])?, DeclarationKind::Recursor));
903 }
904 self.records[record].roots = roots;
905 for (name, kind) in members {
906 self.declare(name, kind, line, record)?;
907 }
908 Ok(())
909 }
910
911 fn declare(
912 &mut self,
913 name: usize,
914 kind: DeclarationKind,
915 line: usize,
916 record: usize,
917 ) -> Result<()> {
918 let encoded = &self.names[&name];
919 let kernel_name = canonical_name(encoded);
920 let lean = lean_name(&kernel_name);
921 if let Some(&earlier) = self.by_kernel_name.get(&kernel_name) {
922 return Err(rejected(
923 DiagnosticCode::DuplicateDeclaration,
924 format!(
925 "{lean} is declared again (first declared at line {})",
926 self.declarations[earlier].line
927 ),
928 )
929 .in_declaration(lean));
930 }
931 let index = self.declarations.len();
932 self.by_kernel_name.insert(kernel_name.clone(), index);
933 self.declared.insert(name, index);
934 self.records[record].members.push(index);
935 self.declarations.push(Declaration {
936 kernel_name,
937 lean_name: lean,
938 kind,
939 line,
940 record,
941 });
942 Ok(())
943 }
944
945 fn finish(self, header: Header) -> Result<Intake> {
948 let mut missing: Vec<(usize, usize)> = self
949 .referenced
950 .iter()
951 .filter(|(name, _)| !self.declared.contains_key(name))
952 .map(|(&name, &line)| (line, name))
953 .collect();
954 missing.sort();
955 let intake = Intake {
956 header,
957 declarations: self.declarations,
958 records: self.records,
959 by_kernel_name: self.by_kernel_name,
960 expressions: self.expressions,
961 declared: self.declared,
962 };
963 let Some(&(first_line, name)) = missing.first() else {
964 return Ok(intake);
965 };
966 let lean = lean_name(&canonical_name(&self.names[&name]));
967 let missing_names: HashSet<usize> = missing.iter().map(|(_, n)| *n).collect();
968 let referrer = intake.declarations.iter().find(|d| {
969 let mut seen = HashSet::new();
970 let mut work = intake.records[d.record].roots.clone();
971 while let Some(e) = work.pop() {
972 if !seen.insert(e) {
973 continue;
974 }
975 if let Some((children, constant)) = intake.expressions.get(&e) {
976 if constant.is_some_and(|c| missing_names.contains(&c)) {
977 return true;
978 }
979 work.extend(children.iter().copied());
980 }
981 }
982 false
983 });
984 let refusal = rejected(
985 DiagnosticCode::MissingDeclaration,
986 format!("{lean} is referenced but never declared"),
987 );
988 Err(match referrer {
989 Some(d) => refusal.at_line(d.line).in_declaration(d.lean_name.clone()),
990 None => refusal.at_line(first_line),
991 })
992 }
993}
994
995impl std::fmt::Debug for Intake {
996 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
997 f.debug_struct("Intake")
998 .field("header", &self.header)
999 .field("declarations", &self.declarations.len())
1000 .finish()
1001 }
1002}
1003
1004#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
1010pub enum Component {
1011 Str(String),
1012 Num(u64),
1013}
1014
1015pub fn parse_lean_name(text: &str) -> Result<Vec<Component>> {
1018 let invalid = || {
1019 declined(
1020 DiagnosticCode::Usage,
1021 format!("{text:?} is not a Lean name"),
1022 )
1023 };
1024 let mut components = Vec::new();
1025 let mut rest = text;
1026 loop {
1027 let (component, after) = if let Some(quoted) = rest.strip_prefix('«') {
1028 let end = quoted.find('»').ok_or_else(invalid)?;
1029 (
1030 Component::Str(quoted[..end].to_string()),
1031 "ed[end + '»'.len_utf8()..],
1032 )
1033 } else {
1034 let end = rest.find('.').unwrap_or(rest.len());
1035 let part = &rest[..end];
1036 if part.is_empty() {
1037 return Err(invalid());
1038 }
1039 let component = if part.bytes().all(|b| b.is_ascii_digit()) {
1040 Component::Num(part.parse().map_err(|_| invalid())?)
1041 } else {
1042 Component::Str(part.to_string())
1043 };
1044 (component, &rest[end..])
1045 };
1046 components.push(component);
1047 if after.is_empty() {
1048 return Ok(components);
1049 }
1050 rest = after.strip_prefix('.').ok_or_else(invalid)?;
1051 if rest.is_empty() {
1052 return Err(invalid());
1053 }
1054 }
1055}
1056
1057pub fn encode(components: &[Component]) -> String {
1059 components
1060 .iter()
1061 .map(|c| match c {
1062 Component::Str(s) => format!(
1063 "/s:{}",
1064 serde_json::to_string(s).expect("a string serializes")
1065 ),
1066 Component::Num(n) => format!("/n:{n}"),
1067 })
1068 .collect()
1069}
1070
1071pub fn kernel_name(lean: &str) -> Result<String> {
1073 Ok(canonical_name(&encode(&parse_lean_name(lean)?)))
1074}
1075
1076pub fn components(kernel: &str) -> Option<Vec<Component>> {
1078 let encoded = uncanonical(kernel)?;
1079 let mut rest = encoded.as_str();
1080 let mut out = Vec::new();
1081 while !rest.is_empty() {
1082 rest = rest.strip_prefix('/')?;
1083 if let Some(s) = rest.strip_prefix("s:") {
1084 let mut stream = serde_json::Deserializer::from_str(s).into_iter::<String>();
1085 let text = stream.next()?.ok()?;
1086 let used = stream.byte_offset();
1087 out.push(Component::Str(text));
1088 rest = &s[used..];
1089 } else if let Some(n) = rest.strip_prefix("n:") {
1090 let end = n.find('/').unwrap_or(n.len());
1091 out.push(Component::Num(n[..end].parse().ok()?));
1092 rest = &n[end..];
1093 } else {
1094 return None;
1095 }
1096 }
1097 (!out.is_empty()).then_some(out)
1098}
1099
1100pub fn lean_name(kernel: &str) -> String {
1103 match components(kernel) {
1104 Some(components) => print_lean_name(&components),
1105 None => kernel.to_string(),
1106 }
1107}
1108
1109pub fn print_lean_name(components: &[Component]) -> String {
1111 components
1112 .iter()
1113 .map(|c| match c {
1114 Component::Num(n) => n.to_string(),
1115 Component::Str(s) if is_identifier(s) => s.clone(),
1116 Component::Str(s) => format!("«{s}»"),
1117 })
1118 .collect::<Vec<_>>()
1119 .join(".")
1120}
1121
1122fn is_identifier(s: &str) -> bool {
1123 let mut chars = s.chars();
1124 let Some(first) = chars.next() else {
1125 return false;
1126 };
1127 (first.is_alphabetic() || first == '_')
1128 && !s.bytes().all(|b| b.is_ascii_digit())
1129 && chars.all(|c| c.is_alphanumeric() || matches!(c, '_' | '\'' | '!' | '?'))
1130}
1131
1132fn uncanonical(kernel: &str) -> Option<String> {
1134 use crate::quotient::Kind;
1135 let quotients = [
1136 (Kind::Type, "/s:\"Quot\""),
1137 (Kind::Constructor, "/s:\"Quot\"/s:\"mk\""),
1138 (Kind::Lift, "/s:\"Quot\"/s:\"lift\""),
1139 (Kind::Induction, "/s:\"Quot\"/s:\"ind\""),
1140 ];
1141 if let Some((_, encoded)) = quotients.iter().find(|(kind, _)| kind.name() == kernel) {
1142 return Some(encoded.to_string());
1143 }
1144 Some(match kernel {
1145 "Nat" => "/s:\"Nat\"".to_string(),
1146 "Zero" => "/s:\"Nat\"/s:\"zero\"".to_string(),
1147 "Succ" => "/s:\"Nat\"/s:\"succ\"".to_string(),
1148 _ => match kernel.strip_prefix("Nat/") {
1149 Some(suffix) => format!("/s:\"Nat\"/{suffix}"),
1150 None if kernel.starts_with('/') => kernel.to_string(),
1151 None => return None,
1152 },
1153 })
1154}