1use super::{declined, rejected, DiagnosticCode, Result};
40use crate::{Literal, Term, Universe};
41use serde_json::Value;
42use std::collections::HashMap;
43
44pub const REFERENCE_NODE_BUDGET: usize = 1_000_000;
48
49const REFERENCE_NATURAL_WORK_BUDGET: u64 = 1_000_000;
51
52fn admit_natural_literal<'a>(payload: &'a Value, work: &mut u64) -> Result<&'a str> {
56 let digits = payload
57 .as_str()
58 .ok_or_else(|| malformed("natural literal"))?;
59 *work = work
60 .checked_add(u64::try_from(digits.len()).unwrap_or(u64::MAX))
61 .filter(|sum| *sum <= REFERENCE_NATURAL_WORK_BUDGET)
62 .ok_or_else(|| {
63 declined(
64 DiagnosticCode::ResourceLimit,
65 "reference natural literal workload",
66 )
67 })?;
68 if digits.is_empty() {
69 return Err(malformed("natural literal"));
70 }
71 let mut first = digits.len();
72 for (index, byte) in digits.bytes().enumerate() {
73 if !byte.is_ascii_digit() {
74 return Err(malformed("natural literal"));
75 }
76 if first == digits.len() && byte != b'0' {
77 first = index;
78 }
79 }
80 let significant = &digits[first..];
81 let n = significant.len() as u64;
84 let conversion = n
85 .checked_mul(n / 16 + 1)
86 .and_then(|n| n.checked_mul(2))
87 .ok_or_else(|| {
88 declined(
89 DiagnosticCode::ResourceLimit,
90 "reference natural literal workload",
91 )
92 })?;
93 *work = work
94 .checked_add(conversion)
95 .filter(|sum| *sum <= REFERENCE_NATURAL_WORK_BUDGET)
96 .ok_or_else(|| {
97 declined(
98 DiagnosticCode::ResourceLimit,
99 "reference natural literal workload",
100 )
101 })?;
102 Ok(significant)
103}
104
105#[derive(Clone, Copy, Debug, Default)]
107pub(super) struct SourceMetrics {
108 pub(super) depth: usize,
109 pub(super) tree_nodes: u64,
110 pub(super) natural_work: u64,
112 pub(super) has_projection: bool,
113 pub(super) has_generated: bool,
114}
115
116impl Reference {
117 pub(super) fn recursor_block_shape(
120 &self,
121 declaration: &str,
122 ) -> Result<ReferenceInductiveBlock<u64>> {
123 self.tables
124 .read_recursor_block(declaration, Lookup::Mixed, &mut |root| Ok(root))
125 }
126
127 pub(super) fn family_shape(&self, declaration: &str) -> Result<ReferenceInductiveBlock<u64>> {
130 let block = self.tables.containing_family_block(declaration)?;
131 self.tables.read_block(block, false, &mut |root| Ok(root))
132 }
133
134 pub(super) fn recursor_block_roots(&self, declaration: &str) -> Result<Vec<u64>> {
136 Ok(self.recursor_block_shape(declaration)?.expression_roots())
137 }
138
139 pub(super) fn preflight_recursor_blocks(
142 &self,
143 declarations: &[String],
144 max_depth: usize,
145 max_tree_nodes: u64,
146 ) -> Result<SourceMetrics> {
147 let mut total = SourceMetrics::default();
148 self.preflight_recursor_blocks_into(
149 declarations,
150 max_depth,
151 max_tree_nodes,
152 &mut total,
153 Lookup::Mixed,
154 )?;
155 Ok(total)
156 }
157
158 pub(super) fn preflight_recursor_blocks_kernel(
159 &self,
160 declarations: &[String],
161 max_depth: usize,
162 max_tree_nodes: u64,
163 ) -> Result<SourceMetrics> {
164 let mut total = SourceMetrics::default();
165 self.preflight_recursor_blocks_into(
166 declarations,
167 max_depth,
168 max_tree_nodes,
169 &mut total,
170 Lookup::Kernel,
171 )?;
172 Ok(total)
173 }
174
175 fn preflight_recursor_blocks_into(
176 &self,
177 declarations: &[String],
178 max_depth: usize,
179 max_tree_nodes: u64,
180 total: &mut SourceMetrics,
181 lookup: Lookup,
182 ) -> Result<()> {
183 let mut seen = HashMap::<Vec<String>, ()>::new();
184 for declaration in declarations {
185 let shape = self
186 .tables
187 .read_recursor_block(declaration, lookup, &mut |root| Ok(root))?;
188 let identity: Vec<_> = shape
189 .recursors
190 .iter()
191 .map(|recursor| recursor.declaration.name.clone())
192 .collect();
193 if seen.insert(identity, ()).is_some() {
194 continue;
195 }
196 total.has_generated = true;
197 for root in shape.expression_roots() {
198 self.tables
199 .preflight_root(root, max_depth, max_tree_nodes, total)?;
200 }
201 }
202 Ok(())
203 }
204
205 pub(super) fn recursor_block(
207 &self,
208 declaration: &str,
209 ) -> Result<ReferenceInductiveBlock<Term>> {
210 self.recursor_block_with(declaration, &mut NoProjection)
211 }
212
213 pub(super) fn recursor_block_with(
217 &self,
218 declaration: &str,
219 projections: &mut impl ReferenceProjection,
220 ) -> Result<ReferenceInductiveBlock<Term>> {
221 self.preflight_recursor_blocks(
222 &[declaration.to_string()],
223 REFERENCE_NODE_BUDGET,
224 REFERENCE_NODE_BUDGET as u64,
225 )?;
226 self.tables
227 .read_recursor_block(declaration, Lookup::Mixed, &mut |root| {
228 self.tables.translate_with(&Value::from(root), projections)
229 })
230 }
231
232 pub(super) fn recursor_block_kernel_with(
234 &self,
235 declaration: &str,
236 projections: &mut impl ReferenceProjection,
237 ) -> Result<ReferenceInductiveBlock<Term>> {
238 self.preflight_recursor_blocks_kernel(
239 &[declaration.to_string()],
240 REFERENCE_NODE_BUDGET,
241 REFERENCE_NODE_BUDGET as u64,
242 )?;
243 self.tables
244 .read_recursor_block(declaration, Lookup::Kernel, &mut |root| {
245 self.tables.translate_with(&Value::from(root), projections)
246 })
247 }
248
249 pub(super) fn source_name(&self, name: &str) -> Result<String> {
251 let (record, _) = self.tables.find(name)?;
252 self.tables.kernel_name(&record["name"])
253 }
254
255 pub(super) fn source_lean_name(&self, name: &str) -> Result<String> {
257 let (record, _) = self.tables.find_lookup(name, Lookup::Lean)?;
258 self.tables.kernel_name(&record["name"])
259 }
260
261 pub(super) fn source_spelling(&self, name: &str) -> Result<(String, String)> {
263 let (record, _) = self.tables.find_kernel(name)?;
264 Ok((
265 self.tables.kernel_name(&record["name"])?,
266 self.tables.lean_name(&record["name"])?,
267 ))
268 }
269
270 pub(super) fn source_root_metrics(
273 &self,
274 name: &str,
275 value: bool,
276 max_depth: usize,
277 max_tree_nodes: u64,
278 ) -> Result<SourceMetrics> {
279 let (record, _) = self.tables.find_kernel(name)?;
280 let root = if value {
281 &record["value"]
282 } else {
283 &record["type"]
284 };
285 let mut metrics = SourceMetrics::default();
286 self.tables
287 .preflight_root(id(root)?, max_depth, max_tree_nodes, &mut metrics)?;
288 Ok(metrics)
289 }
290
291 pub(super) fn preflight(
294 &self,
295 declarations: &[(String, bool)],
296 max_depth: usize,
297 max_tree_nodes: u64,
298 ) -> Result<SourceMetrics> {
299 self.preflight_lookup(declarations, max_depth, max_tree_nodes, Lookup::Mixed)
300 }
301
302 pub(super) fn preflight_kernel(
303 &self,
304 declarations: &[(String, bool)],
305 max_depth: usize,
306 max_tree_nodes: u64,
307 ) -> Result<SourceMetrics> {
308 self.preflight_lookup(declarations, max_depth, max_tree_nodes, Lookup::Kernel)
309 }
310
311 fn preflight_lookup(
312 &self,
313 declarations: &[(String, bool)],
314 max_depth: usize,
315 max_tree_nodes: u64,
316 lookup: Lookup,
317 ) -> Result<SourceMetrics> {
318 let mut total = SourceMetrics::default();
319 let mut recursors = Vec::new();
320 for (name, body) in declarations {
321 let (record, kind) = self.tables.find_lookup(name, lookup)?;
322 if kind == ReferenceKind::Recursor {
323 recursors.push(name.clone());
324 }
325 let mut roots = vec![&record["type"]];
326 if *body {
327 roots.push(&record["value"]);
328 }
329 for root in roots {
330 self.tables
331 .preflight_root(id(root)?, max_depth, max_tree_nodes, &mut total)?;
332 }
333 }
334 self.preflight_recursor_blocks_into(
335 &recursors,
336 max_depth,
337 max_tree_nodes,
338 &mut total,
339 lookup,
340 )?;
341 Ok(total)
342 }
343}
344
345impl Tables {
346 fn containing_recursor_block(&self, declaration: &str, lookup: Lookup) -> Result<&Value> {
349 let (record, kind) = self.find_lookup(declaration, lookup)?;
350 if kind != ReferenceKind::Recursor {
351 return Err(malformed("requested declaration is not a recursor"));
352 }
353 let wanted = self.kernel_name(&record["name"])?;
354 let mut found = None;
355 for outer in &self.declarations {
356 let Some(block) = outer.get("inductive") else {
357 continue;
358 };
359 for recursor in block_array(block, "recs")? {
360 if self.kernel_name(&recursor["name"])? == wanted {
361 if found.replace(block).is_some() {
362 return Err(malformed("recursor has more than one containing block"));
363 }
364 }
365 }
366 }
367 found.ok_or_else(|| malformed("recursor has no containing inductive block"))
368 }
369
370 fn containing_family_block(&self, declaration: &str) -> Result<&Value> {
371 let (record, kind) = self.find_kernel(declaration)?;
372 if kind != ReferenceKind::Inductive {
373 return Err(malformed(
374 "requested projection parent is not an inductive family",
375 ));
376 }
377 let wanted = self.kernel_name(&record["name"])?;
378 let mut found = None;
379 for outer in &self.declarations {
380 let Some(block) = outer.get("inductive") else {
381 continue;
382 };
383 for member in block_array(block, "types")? {
384 if self.kernel_name(&member["name"])? == wanted {
385 if found.replace(block).is_some() {
386 return Err(malformed("family has more than one containing block"));
387 }
388 }
389 }
390 }
391 found.ok_or_else(|| malformed("family has no containing inductive block"))
392 }
393
394 fn block_names(&self, value: &Value, field: &str) -> Result<Vec<String>> {
395 block_array(value, field)?
396 .iter()
397 .map(|name| self.kernel_name(name))
398 .collect()
399 }
400
401 fn block_declaration<E>(
402 &self,
403 record: &Value,
404 expression: &mut impl FnMut(u64) -> Result<E>,
405 ) -> Result<ReferenceBlockDeclaration<E>> {
406 let level_params = block_array(record, "levelParams")?
407 .iter()
408 .map(|level| self.raw_name(level))
409 .collect::<Result<Vec<_>>>()?;
410 let mut seen = std::collections::HashSet::new();
411 if level_params.iter().any(|level| !seen.insert(level)) {
412 return Err(malformed("duplicate universe parameter in source block"));
413 }
414 Ok(ReferenceBlockDeclaration {
415 name: self.kernel_name(&record["name"])?,
416 lean_name: self.lean_name(&record["name"])?,
417 level_params,
418 ty: expression(id(&record["type"])?)?,
419 is_unsafe: block_bool(record, "isUnsafe")?,
420 })
421 }
422
423 fn read_recursor_block<E>(
424 &self,
425 declaration: &str,
426 lookup: Lookup,
427 expression: &mut impl FnMut(u64) -> Result<E>,
428 ) -> Result<ReferenceInductiveBlock<E>> {
429 let block = self.containing_recursor_block(declaration, lookup)?;
430 self.read_block(block, true, expression)
431 }
432
433 fn read_block<E>(
434 &self,
435 block: &Value,
436 include_recursors: bool,
437 expression: &mut impl FnMut(u64) -> Result<E>,
438 ) -> Result<ReferenceInductiveBlock<E>> {
439 block_fields(block, &["types", "ctors", "recs"])?;
440 let types = block_array(block, "types")?;
441 if types.is_empty() {
442 return Err(malformed("empty source inductive block"));
443 }
444 let mut members = Vec::new();
445 for member in types {
446 block_fields(
447 member,
448 &[
449 "name",
450 "levelParams",
451 "type",
452 "numParams",
453 "numIndices",
454 "numNested",
455 "isUnsafe",
456 "isRec",
457 "isReflexive",
458 "ctors",
459 "all",
460 ],
461 )?;
462 members.push(ReferenceInductiveMember {
463 declaration: self.block_declaration(member, expression)?,
464 num_params: block_count(member, "numParams")?,
465 num_indices: block_count(member, "numIndices")?,
466 num_nested: block_count(member, "numNested")?,
467 is_recursive: block_bool(member, "isRec")?,
468 is_reflexive: block_bool(member, "isReflexive")?,
469 constructors: self.block_names(member, "ctors")?,
470 all: self.block_names(member, "all")?,
471 });
472 }
473 let mut constructors = Vec::new();
474 for constructor in block_array(block, "ctors")? {
475 block_fields(
476 constructor,
477 &[
478 "name",
479 "levelParams",
480 "type",
481 "numParams",
482 "numFields",
483 "isUnsafe",
484 "induct",
485 "cidx",
486 ],
487 )?;
488 constructors.push(ReferenceConstructor {
489 declaration: self.block_declaration(constructor, expression)?,
490 parent: self.kernel_name(&constructor["induct"])?,
491 index: block_count(constructor, "cidx")?,
492 num_params: block_count(constructor, "numParams")?,
493 num_fields: block_count(constructor, "numFields")?,
494 });
495 }
496 let mut recursors = Vec::new();
497 for recursor in if include_recursors {
498 block_array(block, "recs")?
499 } else {
500 &[]
501 } {
502 block_fields(
503 recursor,
504 &[
505 "name",
506 "levelParams",
507 "type",
508 "numParams",
509 "numIndices",
510 "numMotives",
511 "numMinors",
512 "isUnsafe",
513 "all",
514 "k",
515 "rules",
516 ],
517 )?;
518 let declaration = self.block_declaration(recursor, expression)?;
519 let mut rules = Vec::new();
520 for rule in block_array(recursor, "rules")? {
521 block_fields(rule, &["ctor", "nfields", "rhs"])?;
522 rules.push(ReferenceRecursorRule {
523 constructor: self.kernel_name(&rule["ctor"])?,
524 num_fields: block_count(rule, "nfields")?,
525 rhs: expression(id(&rule["rhs"])?)?,
526 });
527 }
528 recursors.push(ReferenceRecursor {
529 declaration,
530 num_params: block_count(recursor, "numParams")?,
531 num_indices: block_count(recursor, "numIndices")?,
532 num_motives: block_count(recursor, "numMotives")?,
533 num_minors: block_count(recursor, "numMinors")?,
534 all: self.block_names(recursor, "all")?,
535 is_k: block_bool(recursor, "k")?,
536 rules,
537 });
538 }
539 let mut names = std::collections::HashSet::new();
540 for name in members
541 .iter()
542 .map(|member| &member.declaration.name)
543 .chain(constructors.iter().map(|ctor| &ctor.declaration.name))
544 .chain(recursors.iter().map(|recursor| &recursor.declaration.name))
545 {
546 if !names.insert(name) {
547 return Err(malformed(
548 "duplicate declaration identity in source inductive block",
549 ));
550 }
551 self.find_kernel(name)?;
553 }
554 Ok(ReferenceInductiveBlock {
555 members,
556 constructors,
557 recursors,
558 })
559 }
560
561 fn preflight_root(
562 &self,
563 root: u64,
564 max_depth: usize,
565 max_tree_nodes: u64,
566 total: &mut SourceMetrics,
567 ) -> Result<()> {
568 enum SourceNode {
571 Expression(u64),
572 Level(u64, usize),
573 }
574 let mut pending = vec![(SourceNode::Expression(root), 1usize)];
575 let mut root_nodes = 0u64;
576 while let Some((node, depth)) = pending.pop() {
577 if depth > max_depth {
578 return Err(declined(
579 DiagnosticCode::ResourceLimit,
580 "source expression depth exceeds the environment limit",
581 ));
582 }
583 total.depth = total.depth.max(depth);
584 let mut cost = 1u64;
585 match node {
586 SourceNode::Level(index, nesting) => {
587 if nesting > 512 {
589 return Err(declined(
590 DiagnosticCode::ResourceLimit,
591 "universe level nesting",
592 ));
593 }
594 if index != 0 {
595 let row = self
596 .levels
597 .get(&index)
598 .ok_or_else(|| malformed("unknown source level index"))?;
599 if let Some(inner) = row.get("succ") {
600 pending.push((
601 SourceNode::Level(id(inner)?, nesting + 1),
602 depth.saturating_add(1),
603 ));
604 } else if let Some(pair) = row.get("max").or_else(|| row.get("imax")) {
605 let pair = pair
606 .as_array()
607 .filter(|pair| pair.len() == 2)
608 .ok_or_else(|| malformed("level pair"))?;
609 for child in pair {
610 pending.push((
611 SourceNode::Level(id(child)?, nesting + 1),
612 depth.saturating_add(1),
613 ));
614 }
615 } else if row.get("param").is_none() {
616 return Err(malformed("unknown source level record"));
617 }
618 }
619 }
620 SourceNode::Expression(index) => {
621 let row = self
622 .exprs
623 .get(&index)
624 .ok_or_else(|| malformed("unknown source expression index"))?;
625 if let Some(natural) = row.get("natVal") {
626 admit_natural_literal(natural, &mut total.natural_work)?;
627 }
628 if let Some(level) = row.get("sort") {
629 pending.push((SourceNode::Level(id(level)?, 0), depth.saturating_add(1)));
630 } else if let Some(constant) = row.get("const") {
631 for level in constant["us"]
632 .as_array()
633 .ok_or_else(|| malformed("constant levels"))?
634 {
635 pending
636 .push((SourceNode::Level(id(level)?, 0), depth.saturating_add(1)));
637 }
638 } else if row.get("strVal").is_some() {
639 let scalars = row["strVal"]
643 .as_str()
644 .ok_or_else(|| malformed("string literal"))?
645 .chars()
646 .count();
647 cost = u64::try_from(scalars)
648 .unwrap_or(u64::MAX)
649 .saturating_mul(9)
650 .saturating_add(6);
651 let literal_depth = if scalars == 0 {
652 4
653 } else {
654 scalars.saturating_add(5)
655 };
656 let expanded_depth = depth.saturating_add(literal_depth.saturating_sub(1));
657 if expanded_depth > max_depth {
658 return Err(declined(
659 DiagnosticCode::ResourceLimit,
660 "source string expansion depth exceeds the environment limit",
661 ));
662 }
663 total.depth = total.depth.max(expanded_depth);
664 }
665 let children: Vec<&Value> = if let Some(app) = row.get("app") {
666 vec![&app["fn"], &app["arg"]]
667 } else if let Some(metadata) = row.get("mdata") {
668 vec![&metadata["expr"]]
669 } else if let Some(projection) = row.get("proj") {
670 total.has_projection = true;
671 vec![&projection["struct"]]
672 } else if let Some(binding) = row.get("letE") {
673 vec![&binding["type"], &binding["value"], &binding["body"]]
674 } else if let Some(binding) = row.get("lam").or_else(|| row.get("forallE")) {
675 vec![&binding["type"], &binding["body"]]
676 } else {
677 Vec::new()
678 };
679 for child in children {
680 pending.push((SourceNode::Expression(id(child)?), depth.saturating_add(1)));
681 }
682 }
683 }
684 root_nodes = root_nodes.checked_add(cost).ok_or_else(|| {
685 declined(DiagnosticCode::ResourceLimit, "source tree size overflow")
686 })?;
687 total.tree_nodes = total.tree_nodes.checked_add(cost).ok_or_else(|| {
688 declined(
689 DiagnosticCode::ResourceLimit,
690 "source aggregate tree size overflow",
691 )
692 })?;
693 if root_nodes > REFERENCE_NODE_BUDGET as u64 || total.tree_nodes > max_tree_nodes {
694 return Err(declined(
695 DiagnosticCode::ResourceLimit,
696 "source expanded tree nodes exceed the reference or environment limit",
697 ));
698 }
699 }
700 Ok(())
701 }
702}
703
704#[derive(Clone, Debug, PartialEq)]
708pub(super) struct ReferenceInductiveBlock<E> {
709 pub members: Vec<ReferenceInductiveMember<E>>,
710 pub constructors: Vec<ReferenceConstructor<E>>,
711 pub recursors: Vec<ReferenceRecursor<E>>,
712}
713
714#[derive(Clone, Debug, PartialEq)]
715pub(super) struct ReferenceBlockDeclaration<E> {
716 pub name: String,
717 pub lean_name: String,
718 pub level_params: Vec<String>,
719 pub ty: E,
720 pub is_unsafe: bool,
721}
722
723#[derive(Clone, Debug, PartialEq)]
724pub(super) struct ReferenceInductiveMember<E> {
725 pub declaration: ReferenceBlockDeclaration<E>,
726 pub num_params: usize,
727 pub num_indices: usize,
728 pub num_nested: usize,
729 pub is_recursive: bool,
730 pub is_reflexive: bool,
731 pub constructors: Vec<String>,
732 pub all: Vec<String>,
733}
734
735#[derive(Clone, Debug, PartialEq)]
736pub(super) struct ReferenceConstructor<E> {
737 pub declaration: ReferenceBlockDeclaration<E>,
738 pub parent: String,
739 pub index: usize,
740 pub num_params: usize,
741 pub num_fields: usize,
742}
743
744#[derive(Clone, Debug, PartialEq)]
745pub(super) struct ReferenceRecursor<E> {
746 pub declaration: ReferenceBlockDeclaration<E>,
747 pub num_params: usize,
748 pub num_indices: usize,
749 pub num_motives: usize,
750 pub num_minors: usize,
751 pub all: Vec<String>,
752 pub is_k: bool,
753 pub rules: Vec<ReferenceRecursorRule<E>>,
754}
755
756#[derive(Clone, Debug, PartialEq)]
757pub(super) struct ReferenceRecursorRule<E> {
758 pub constructor: String,
759 pub num_fields: usize,
760 pub rhs: E,
761}
762
763impl ReferenceInductiveBlock<u64> {
764 pub(super) fn expression_roots(&self) -> Vec<u64> {
767 let mut roots = Vec::new();
768 roots.extend(self.members.iter().map(|member| member.declaration.ty));
769 roots.extend(self.constructors.iter().map(|ctor| ctor.declaration.ty));
770 for recursor in &self.recursors {
771 roots.push(recursor.declaration.ty);
772 roots.extend(recursor.rules.iter().map(|rule| rule.rhs));
773 }
774 roots
775 }
776}
777
778#[derive(Clone, Copy, Debug, PartialEq, Eq)]
780pub enum ReferenceKind {
781 Definition,
782 Theorem,
783 Opaque,
784 Axiom,
785 Inductive,
786 Constructor,
787 Recursor,
788 Quotient,
789}
790
791impl ReferenceKind {
792 pub fn as_str(self) -> &'static str {
793 match self {
794 ReferenceKind::Definition => "def",
795 ReferenceKind::Theorem => "thm",
796 ReferenceKind::Opaque => "opaque",
797 ReferenceKind::Axiom => "axiom",
798 ReferenceKind::Inductive => "inductive",
799 ReferenceKind::Constructor => "ctor",
800 ReferenceKind::Recursor => "rec",
801 ReferenceKind::Quotient => "quot",
802 }
803 }
804}
805
806#[derive(Clone, Debug, PartialEq)]
808pub struct ReferenceDeclaration {
809 pub name: String,
811 pub lean_name: String,
813 pub kind: ReferenceKind,
814 pub level_params: Vec<String>,
816 pub ty: Term,
817}
818
819pub fn reference_type(export: &str, declaration: &str) -> Result<Term> {
824 reference_declaration(export, declaration).map(|d| d.ty)
825}
826
827pub fn reference_declaration(export: &str, declaration: &str) -> Result<ReferenceDeclaration> {
829 Reference::read(export)?.declaration(declaration)
830}
831
832pub(super) struct ReferenceBinding<'a> {
835 pub name: &'a str,
836 pub ty: &'a Term,
837 pub value: Option<&'a Term>,
838}
839
840pub(super) trait ReferenceProjection {
844 fn generated_depth_limit(&self) -> Option<usize> {
847 None
848 }
849
850 fn project(
851 &mut self,
852 parent: &str,
853 index: usize,
854 discriminant: Term,
855 scope: &[ReferenceBinding<'_>],
856 budget: &mut usize,
857 ) -> Result<Term>;
858}
859
860#[derive(Clone, Copy)]
863struct OutputShape {
864 depth: usize,
865 nodes: usize,
866 generated: bool,
867}
868
869impl OutputShape {
870 const LEAF: Self = Self {
871 depth: 1,
872 nodes: 1,
873 generated: false,
874 };
875
876 fn wrapper(children: &[Self], limit: Option<usize>) -> Result<Self> {
877 let mut result = Self::LEAF;
878 for child in children {
879 result.depth = result.depth.max(child.depth.checked_add(1).ok_or_else(|| {
880 declined(
881 DiagnosticCode::ResourceLimit,
882 "reference output depth overflow",
883 )
884 })?);
885 result.nodes = result.nodes.checked_add(child.nodes).ok_or_else(|| {
886 declined(
887 DiagnosticCode::ResourceLimit,
888 "reference output size overflow",
889 )
890 })?;
891 result.generated |= child.generated;
892 }
893 result.admit(limit)?;
894 Ok(result)
895 }
896
897 fn admit(self, limit: Option<usize>) -> Result<()> {
898 if self.nodes > REFERENCE_NODE_BUDGET {
899 return Err(declined(
900 DiagnosticCode::ResourceLimit,
901 "reference output tree node limit",
902 ));
903 }
904 if self.generated && limit.is_some_and(|limit| self.depth > limit) {
905 return Err(declined(
906 DiagnosticCode::ResourceLimit,
907 "reference generated output depth limit",
908 ));
909 }
910 Ok(())
911 }
912
913 fn callback(term: &Term, limit: Option<usize>) -> Result<Self> {
916 let mut shape = Self {
917 depth: 0,
918 nodes: 0,
919 generated: true,
920 };
921 let mut pending = vec![(term, 1usize)];
922 while let Some((term, depth)) = pending.pop() {
923 shape.nodes += 1;
924 shape.depth = shape.depth.max(depth);
925 shape.admit(limit)?;
926 let next = depth.checked_add(1).ok_or_else(|| {
927 declined(
928 DiagnosticCode::ResourceLimit,
929 "reference output depth overflow",
930 )
931 })?;
932 let child_count = match term {
934 Term::Pi { .. } | Term::Lambda { .. } | Term::App(..) => Some(2),
935 Term::Let { .. } => Some(3),
936 Term::Match { cases, .. } => cases.len().checked_add(2),
937 Term::MutualFix { defs, .. } => Some(defs.len()),
938 Term::Fix { .. } | Term::Annotated { .. } => Some(1),
939 _ => Some(0),
940 };
941 let child_count = child_count.ok_or_else(|| {
942 declined(
943 DiagnosticCode::ResourceLimit,
944 "reference output child inventory overflow",
945 )
946 })?;
947 if child_count
948 > REFERENCE_NODE_BUDGET
949 .saturating_sub(shape.nodes)
950 .saturating_sub(pending.len())
951 {
952 return Err(declined(
953 DiagnosticCode::ResourceLimit,
954 "reference output child inventory limit",
955 ));
956 }
957 match term {
958 Term::Pi {
959 param_type,
960 body_type,
961 ..
962 } => {
963 pending.push((param_type, next));
964 pending.push((body_type, next));
965 }
966 Term::Lambda {
967 param_type, body, ..
968 } => {
969 pending.push((param_type, next));
970 pending.push((body, next));
971 }
972 Term::App(fun, arg) => {
973 pending.push((fun, next));
974 pending.push((arg, next));
975 }
976 Term::Let {
977 ty, value, body, ..
978 } => {
979 pending.push((ty, next));
980 pending.push((value, next));
981 pending.push((body, next));
982 }
983 Term::Match {
984 discriminant,
985 motive,
986 cases,
987 } => {
988 pending.push((discriminant, next));
989 pending.push((motive, next));
990 pending.extend(cases.iter().map(|term| (term, next)));
991 }
992 Term::Fix { body, .. } => pending.push((body, next)),
993 Term::MutualFix { defs, .. } => {
994 pending.extend(defs.iter().map(|(_, term)| (term, next)))
995 }
996 Term::Annotated { term, .. } => pending.push((term, next)),
997 Term::Sort(_)
998 | Term::Const { .. }
999 | Term::Var(_)
1000 | Term::Global(_)
1001 | Term::Lit(_)
1002 | Term::Hole => {}
1003 }
1004 }
1005 Ok(shape)
1006 }
1007}
1008
1009struct OutputTerm {
1010 term: Term,
1011 shape: OutputShape,
1012}
1013impl OutputTerm {
1014 fn leaf(term: Term) -> Self {
1015 Self {
1016 term,
1017 shape: OutputShape::LEAF,
1018 }
1019 }
1020}
1021
1022struct NoProjection;
1023impl ReferenceProjection for NoProjection {
1024 fn project(
1025 &mut self,
1026 _: &str,
1027 _: usize,
1028 _: Term,
1029 _: &[ReferenceBinding<'_>],
1030 _: &mut usize,
1031 ) -> Result<Term> {
1032 Err(unsupported(
1033 "the reference translator needs a typed projection reader",
1034 ))
1035 }
1036}
1037
1038pub struct Reference {
1040 tables: Tables,
1041}
1042
1043impl Reference {
1044 pub fn read(export: &str) -> Result<Reference> {
1045 Ok(Reference {
1046 tables: Tables::read(export)?,
1047 })
1048 }
1049
1050 pub fn declaration(&self, declaration: &str) -> Result<ReferenceDeclaration> {
1052 self.declaration_with(declaration, &mut NoProjection)
1053 }
1054
1055 pub(super) fn declaration_with(
1056 &self,
1057 declaration: &str,
1058 projections: &mut impl ReferenceProjection,
1059 ) -> Result<ReferenceDeclaration> {
1060 self.declaration_lookup_with(declaration, projections, Lookup::Mixed)
1061 }
1062
1063 pub(super) fn declaration_kernel(&self, declaration: &str) -> Result<ReferenceDeclaration> {
1064 self.declaration_kernel_with(declaration, &mut NoProjection)
1065 }
1066
1067 pub(super) fn declaration_kernel_with(
1068 &self,
1069 declaration: &str,
1070 projections: &mut impl ReferenceProjection,
1071 ) -> Result<ReferenceDeclaration> {
1072 self.declaration_lookup_with(declaration, projections, Lookup::Kernel)
1073 }
1074
1075 fn declaration_lookup_with(
1076 &self,
1077 declaration: &str,
1078 projections: &mut impl ReferenceProjection,
1079 lookup: Lookup,
1080 ) -> Result<ReferenceDeclaration> {
1081 let tables = &self.tables;
1082 let (record, kind) = tables.find_lookup(declaration, lookup)?;
1083 let name = tables.kernel_name(&record["name"])?;
1084 let lean_name = tables.lean_name(&record["name"])?;
1085 let level_params = match record.get("levelParams") {
1086 Some(Value::Array(params)) => params
1087 .iter()
1088 .map(|p| tables.raw_name(p))
1089 .collect::<Result<Vec<_>>>()?,
1090 _ => return Err(malformed("declaration has no universe parameter list")),
1091 };
1092 let ty = tables.translate_with(&record["type"], projections)?;
1093 Ok(ReferenceDeclaration {
1094 name,
1095 lean_name,
1096 kind,
1097 level_params,
1098 ty,
1099 })
1100 }
1101
1102 pub(super) fn value(&self, declaration: &str) -> Result<Term> {
1105 self.value_with(declaration, &mut NoProjection)
1106 }
1107
1108 pub(super) fn value_with(
1109 &self,
1110 declaration: &str,
1111 projections: &mut impl ReferenceProjection,
1112 ) -> Result<Term> {
1113 self.value_lookup_with(declaration, projections, Lookup::Mixed)
1114 }
1115
1116 pub(super) fn value_kernel(&self, declaration: &str) -> Result<Term> {
1117 self.value_kernel_with(declaration, &mut NoProjection)
1118 }
1119
1120 pub(super) fn value_kernel_with(
1121 &self,
1122 declaration: &str,
1123 projections: &mut impl ReferenceProjection,
1124 ) -> Result<Term> {
1125 self.value_lookup_with(declaration, projections, Lookup::Kernel)
1126 }
1127
1128 fn value_lookup_with(
1129 &self,
1130 declaration: &str,
1131 projections: &mut impl ReferenceProjection,
1132 lookup: Lookup,
1133 ) -> Result<Term> {
1134 let (record, kind) = self.tables.find_lookup(declaration, lookup)?;
1135 if !matches!(
1136 kind,
1137 ReferenceKind::Definition | ReferenceKind::Theorem | ReferenceKind::Opaque
1138 ) {
1139 return Err(unsupported("the source declaration has no body"));
1140 }
1141 self.tables.translate_with(&record["value"], projections)
1142 }
1143
1144 pub fn names(&self) -> Result<Vec<(String, ReferenceKind)>> {
1146 let mut seen = std::collections::HashSet::new();
1147 let mut names = Vec::new();
1148 for (record, kind) in self.tables.candidates() {
1149 let name = self.tables.kernel_name(&record["name"])?;
1150 if !seen.insert(name.clone()) {
1151 return Err(malformed(format!(
1152 "the export declares kernel identity {name} more than once"
1153 )));
1154 }
1155 names.push((name, kind));
1156 }
1157 Ok(names)
1158 }
1159}
1160
1161pub fn kernel_name_of_components(components: &[&str]) -> String {
1167 let mut raw = String::new();
1168 for component in components {
1169 raw.push_str("/s:");
1170 raw.push_str(&serde_json::to_string(component).expect("a string serializes"));
1171 }
1172 primitive_identity(&raw)
1173}
1174
1175fn block_fields(value: &Value, expected: &[&str]) -> Result<()> {
1177 let object = value
1178 .as_object()
1179 .ok_or_else(|| malformed("expected a source block record"))?;
1180 if object.len() != expected.len() || expected.iter().any(|field| !object.contains_key(*field)) {
1181 return Err(malformed(
1182 "source block record has missing or unexpected fields",
1183 ));
1184 }
1185 Ok(())
1186}
1187
1188fn block_array<'a>(value: &'a Value, field: &str) -> Result<&'a [Value]> {
1189 value
1190 .get(field)
1191 .and_then(Value::as_array)
1192 .map(Vec::as_slice)
1193 .ok_or_else(|| malformed(format!("source block {field} must be an array")))
1194}
1195
1196fn block_bool(value: &Value, field: &str) -> Result<bool> {
1197 value
1198 .get(field)
1199 .and_then(Value::as_bool)
1200 .ok_or_else(|| malformed(format!("source block {field} must be a Boolean")))
1201}
1202
1203fn block_count(value: &Value, field: &str) -> Result<usize> {
1204 let count = value
1205 .get(field)
1206 .and_then(Value::as_u64)
1207 .ok_or_else(|| malformed(format!("source block {field} must be an unsigned integer")))?;
1208 usize::try_from(count)
1209 .map_err(|_| malformed(format!("source block {field} exceeds platform size")))
1210}
1211
1212fn malformed(message: impl Into<String>) -> super::Verdict {
1213 declined(DiagnosticCode::MalformedExport, message)
1214}
1215
1216fn unsupported(message: impl Into<String>) -> super::Verdict {
1217 declined(DiagnosticCode::UnsupportedFeature, message)
1218}
1219
1220fn primitive_identity(raw: &str) -> String {
1222 const NAT: &str = "/s:\"Nat\"";
1223 match raw {
1224 "/s:\"Quot\"" => "kernel.quot.type".into(),
1225 "/s:\"Quot\"/s:\"mk\"" => "kernel.quot.mk".into(),
1226 "/s:\"Quot\"/s:\"lift\"" => "kernel.quot.lift".into(),
1227 "/s:\"Quot\"/s:\"ind\"" => "kernel.quot.ind".into(),
1228 NAT => "Nat".into(),
1229 "/s:\"Nat\"/s:\"zero\"" => "Zero".into(),
1230 "/s:\"Nat\"/s:\"succ\"" => "Succ".into(),
1231 _ => match raw.strip_prefix("/s:\"Nat\"/") {
1232 Some(rest) => format!("Nat/{rest}"),
1233 None => raw.to_string(),
1234 },
1235 }
1236}
1237
1238#[derive(Clone)]
1240struct Name {
1241 raw: String,
1242 lean: String,
1243}
1244
1245#[derive(Clone, Copy)]
1248enum Lookup {
1249 Mixed,
1250 Kernel,
1251 Lean,
1252}
1253
1254struct Tables {
1255 names: HashMap<u64, Name>,
1256 levels: HashMap<u64, Value>,
1257 exprs: HashMap<u64, Value>,
1258 declarations: Vec<Value>,
1259 release: Option<String>,
1261}
1262
1263fn string_literal_packing(release: Option<&str>) -> Option<&'static str> {
1266 match release? {
1267 "4.14.0" => Some("/s:\"String\"/s:\"mk\""),
1268 "4.29.1" | "4.30.0" | "4.33.0" => Some("/s:\"String\"/s:\"ofList\""),
1269 _ => None,
1270 }
1271}
1272
1273fn id(v: &Value) -> Result<u64> {
1274 v.as_u64().ok_or_else(|| malformed("expected an index"))
1275}
1276
1277impl Tables {
1278 fn read(export: &str) -> Result<Tables> {
1279 let mut lines = export.lines();
1280 let header: Value =
1281 serde_json::from_str(lines.next().ok_or_else(|| malformed("empty export"))?)
1282 .map_err(|e| malformed(format!("export header: {e}")))?;
1283 if header["meta"]["format"]["version"] != "3.1.0" {
1284 return Err(declined(
1285 DiagnosticCode::UnsupportedExportVersion,
1286 "reference translation reads format 3.1.0 only",
1287 ));
1288 }
1289 let mut tables = Tables {
1290 names: HashMap::from([(
1291 0,
1292 Name {
1293 raw: String::new(),
1294 lean: String::new(),
1295 },
1296 )]),
1297 levels: HashMap::new(),
1298 exprs: HashMap::new(),
1299 declarations: Vec::new(),
1300 release: header["meta"]["lean"]["version"]
1301 .as_str()
1302 .map(str::to_owned),
1303 };
1304 for (at, line) in lines.enumerate() {
1305 let record: Value = serde_json::from_str(line)
1306 .map_err(|e| malformed(format!("line {}: {e}", at + 2)))?;
1307 if let Some(index) = record.get("in") {
1308 let index = id(index)?;
1309 let (tag, part) = if let Some(x) = record.get("str") {
1310 let s = x["str"]
1311 .as_str()
1312 .ok_or_else(|| malformed("string name component"))?;
1313 (
1314 "s",
1315 (
1316 serde_json::to_string(s).expect("a string serializes"),
1317 s.to_string(),
1318 ),
1319 )
1320 } else if let Some(x) = record.get("num") {
1321 let n = id(&x["i"])?;
1322 ("n", (n.to_string(), n.to_string()))
1323 } else {
1324 return Err(malformed("unknown name record"));
1325 };
1326 let pre_record = record
1327 .get("str")
1328 .or_else(|| record.get("num"))
1329 .expect("tagged above");
1330 let prefix = tables
1331 .names
1332 .get(&id(&pre_record["pre"])?)
1333 .ok_or_else(|| malformed("name prefix is not an earlier name"))?
1334 .clone();
1335 let lean = if prefix.lean.is_empty() {
1336 part.1
1337 } else {
1338 format!("{}.{}", prefix.lean, part.1)
1339 };
1340 let name = Name {
1341 raw: format!("{}/{tag}:{}", prefix.raw, part.0),
1342 lean,
1343 };
1344 if tables.names.insert(index, name).is_some() {
1345 return Err(malformed("duplicate name index"));
1346 }
1347 } else if let Some(index) = record.get("il") {
1348 if tables.levels.insert(id(index)?, record).is_some() {
1349 return Err(malformed("duplicate level index"));
1350 }
1351 } else if let Some(index) = record.get("ie") {
1352 if tables.exprs.insert(id(index)?, record).is_some() {
1353 return Err(malformed("duplicate expression index"));
1354 }
1355 } else {
1356 tables.declarations.push(record);
1357 }
1358 }
1359 Ok(tables)
1360 }
1361
1362 fn name_entry(&self, v: &Value) -> Result<&Name> {
1363 self.names
1364 .get(&id(v)?)
1365 .ok_or_else(|| malformed("unknown name index"))
1366 }
1367
1368 fn raw_name(&self, v: &Value) -> Result<String> {
1369 Ok(self.name_entry(v)?.raw.clone())
1370 }
1371
1372 fn kernel_name(&self, v: &Value) -> Result<String> {
1373 Ok(primitive_identity(&self.name_entry(v)?.raw))
1374 }
1375
1376 fn lean_name(&self, v: &Value) -> Result<String> {
1377 Ok(self.name_entry(v)?.lean.clone())
1378 }
1379
1380 fn candidates(&self) -> Vec<(&Value, ReferenceKind)> {
1383 let mut out = Vec::new();
1384 for record in &self.declarations {
1385 let Some(object) = record.as_object() else {
1386 continue;
1387 };
1388 for (tag, body) in object {
1389 match tag.as_str() {
1390 "def" => out.push((body, ReferenceKind::Definition)),
1391 "thm" => out.push((body, ReferenceKind::Theorem)),
1392 "opaque" => out.push((body, ReferenceKind::Opaque)),
1393 "axiom" => out.push((body, ReferenceKind::Axiom)),
1394 "quot" => out.push((body, ReferenceKind::Quotient)),
1395 "inductive" => {
1396 for (field, kind) in [
1397 ("types", ReferenceKind::Inductive),
1398 ("ctors", ReferenceKind::Constructor),
1399 ("recs", ReferenceKind::Recursor),
1400 ] {
1401 if let Some(Value::Array(items)) = body.get(field) {
1402 out.extend(items.iter().map(|item| (item, kind)));
1403 }
1404 }
1405 }
1406 _ => {}
1407 }
1408 }
1409 }
1410 out
1411 }
1412
1413 fn find(&self, declaration: &str) -> Result<(&Value, ReferenceKind)> {
1414 self.find_lookup(declaration, Lookup::Mixed)
1415 }
1416
1417 fn find_kernel(&self, declaration: &str) -> Result<(&Value, ReferenceKind)> {
1418 self.find_lookup(declaration, Lookup::Kernel)
1419 }
1420
1421 fn find_lookup(&self, declaration: &str, lookup: Lookup) -> Result<(&Value, ReferenceKind)> {
1422 let mut found = Vec::new();
1423 for (record, kind) in self.candidates() {
1424 let name = self.name_entry(&record["name"])?;
1425 let matches = match lookup {
1426 Lookup::Mixed => {
1427 name.lean == declaration
1428 || name.raw == declaration
1429 || primitive_identity(&name.raw) == declaration
1430 }
1431 Lookup::Kernel => primitive_identity(&name.raw) == declaration,
1432 Lookup::Lean => name.lean == declaration,
1433 };
1434 if matches {
1435 found.push((record, kind));
1436 }
1437 }
1438 match found.len() {
1439 0 => Err(rejected(
1440 DiagnosticCode::InvalidProof,
1441 format!("the export declares no {declaration}"),
1442 )),
1443 1 => Ok(found.remove(0)),
1444 _ => Err(rejected(
1445 DiagnosticCode::MalformedExport,
1446 format!("the export declares {declaration} more than once"),
1447 )),
1448 }
1449 }
1450
1451 fn level(&self, v: &Value) -> Result<Universe> {
1452 let index = id(v)?;
1453 if index == 0 {
1454 return Ok(Universe::Prop);
1455 }
1456 enum Task {
1457 Enter(u64, usize),
1458 Succ,
1459 Max,
1460 IMax,
1461 }
1462 let mut work = vec![Task::Enter(index, 0)];
1463 let mut out: Vec<Universe> = Vec::new();
1464 while let Some(task) = work.pop() {
1465 match task {
1466 Task::Enter(i, depth) => {
1467 if depth > 512 {
1468 return Err(declined(
1469 DiagnosticCode::ResourceLimit,
1470 "universe level nesting",
1471 ));
1472 }
1473 if i == 0 {
1474 out.push(Universe::Prop);
1475 continue;
1476 }
1477 let record = self
1478 .levels
1479 .get(&i)
1480 .ok_or_else(|| malformed("unknown level index"))?;
1481 if let Some(inner) = record.get("succ") {
1482 work.push(Task::Succ);
1483 work.push(Task::Enter(id(inner)?, depth + 1));
1484 } else if let Some(param) = record.get("param") {
1485 out.push(Universe::Var(self.raw_name(param)?));
1486 } else if let Some(pair) = record.get("max").or_else(|| record.get("imax")) {
1487 let pair = pair
1488 .as_array()
1489 .filter(|p| p.len() == 2)
1490 .ok_or_else(|| malformed("level pair"))?;
1491 work.push(if record.get("max").is_some() {
1492 Task::Max
1493 } else {
1494 Task::IMax
1495 });
1496 work.push(Task::Enter(id(&pair[1])?, depth + 1));
1497 work.push(Task::Enter(id(&pair[0])?, depth + 1));
1498 } else {
1499 return Err(malformed("unknown level record"));
1500 }
1501 }
1502 Task::Succ => {
1503 let u = out.pop().expect("successor operand");
1504 out.push(u.succ());
1505 }
1506 Task::Max | Task::IMax => {
1507 let b = out.pop().expect("right operand");
1508 let a = out.pop().expect("left operand");
1509 out.push(if matches!(task, Task::Max) {
1510 Universe::Max(Box::new(a), Box::new(b))
1511 } else {
1512 Universe::IMax(Box::new(a), Box::new(b))
1513 });
1514 }
1515 }
1516 }
1517 Ok(out.pop().expect("one level"))
1518 }
1519
1520 fn translate(&self, root: &Value) -> Result<Term> {
1523 self.translate_with(root, &mut NoProjection)
1524 }
1525
1526 fn translate_with(
1527 &self,
1528 root: &Value,
1529 projections: &mut impl ReferenceProjection,
1530 ) -> Result<Term> {
1531 self.preflight_root(
1535 id(root)?,
1536 REFERENCE_NODE_BUDGET,
1537 REFERENCE_NODE_BUDGET as u64,
1538 &mut SourceMetrics::default(),
1539 )?;
1540 enum Task<'a> {
1541 Enter(u64),
1542 App,
1543 Projection { parent: String, index: usize },
1544 Binder { kind: &'a str, record: &'a Value },
1545 Close { kind: &'a str, name: String },
1546 }
1547 let mut work = vec![Task::Enter(id(root)?)];
1548 let mut out: Vec<OutputTerm> = Vec::new();
1549 let generated_depth_limit = projections.generated_depth_limit();
1550 let mut depth: usize = 0;
1551 let mut scope: Vec<(String, usize, Option<usize>)> = Vec::new();
1554 let mut budget = REFERENCE_NODE_BUDGET;
1555 let mut natural_work = 0u64;
1556 while let Some(task) = work.pop() {
1557 match task {
1558 Task::Enter(i) => {
1559 budget = budget.checked_sub(1).ok_or_else(|| {
1560 declined(
1561 DiagnosticCode::ResourceLimit,
1562 "reference translation node budget",
1563 )
1564 })?;
1565 let record = self
1566 .exprs
1567 .get(&i)
1568 .ok_or_else(|| malformed("unknown expression index"))?;
1569 if let Some(u) = record.get("sort") {
1570 out.push(OutputTerm::leaf(Term::Sort(self.level(u)?)));
1571 } else if let Some(k) = record.get("bvar") {
1572 let k = usize::try_from(id(k)?).map_err(|_| malformed("bound index"))?;
1573 let at = depth.checked_sub(k + 1).ok_or_else(|| {
1574 rejected(DiagnosticCode::MalformedExport, "loose bound variable")
1575 })?;
1576 out.push(OutputTerm::leaf(Term::Var(format!("ref.{at}"))));
1577 } else if let Some(c) = record.get("const") {
1578 let name = self.kernel_name(&c["name"])?;
1579 let levels = c["us"]
1580 .as_array()
1581 .ok_or_else(|| malformed("constant levels"))?
1582 .iter()
1583 .map(|u| self.level(u))
1584 .collect::<Result<Vec<_>>>()?;
1585 out.push(OutputTerm::leaf(if levels.is_empty() {
1586 Term::Global(name)
1587 } else {
1588 Term::Const { name, levels }
1589 }));
1590 } else if let Some(a) = record.get("app") {
1591 work.push(Task::App);
1592 work.push(Task::Enter(id(&a["arg"])?));
1593 work.push(Task::Enter(id(&a["fn"])?));
1594 } else if let Some(m) = record.get("mdata") {
1595 work.push(Task::Enter(id(&m["expr"])?));
1596 } else if let Some((kind, b)) = ["forallE", "lam", "letE"]
1597 .iter()
1598 .find_map(|k| record.get(*k).map(|b| (*k, b)))
1599 {
1600 work.push(Task::Binder { kind, record: b });
1602 if kind == "letE" {
1603 work.push(Task::Enter(id(&b["value"])?));
1604 }
1605 work.push(Task::Enter(id(&b["type"])?));
1606 } else if let Some(n) = record.get("natVal") {
1607 let significant = admit_natural_literal(n, &mut natural_work)?;
1608 let value = if significant.is_empty() {
1609 crate::BigInt::zero()
1610 } else {
1611 crate::BigInt::parse_decimal(significant)
1612 .ok_or_else(|| malformed("natural literal"))?
1613 };
1614 out.push(OutputTerm::leaf(Term::Lit(Literal::Nat(value))));
1615 } else if let Some(proj) = record.get("proj") {
1616 let parent = self.kernel_name(&proj["typeName"])?;
1617 let index = usize::try_from(id(&proj["idx"])?)
1618 .map_err(|_| malformed("projection index"))?;
1619 work.push(Task::Projection { parent, index });
1620 work.push(Task::Enter(id(&proj["struct"])?));
1621 } else if let Some(text) = record.get("strVal") {
1622 let text = text.as_str().ok_or_else(|| malformed("string literal"))?;
1623 let packing = string_literal_packing(self.release.as_deref()).ok_or_else(|| declined(
1624 DiagnosticCode::UnsupportedExportVersion,
1625 format!("the reference translator has no string-literal reading for Lean release {:?}", self.release),
1626 ))?;
1627 let scalars: Vec<char> = text.chars().collect();
1630 let shape = OutputShape {
1633 depth: if scalars.is_empty() {
1634 3
1635 } else {
1636 scalars.len() + 4
1637 },
1638 nodes: scalars
1639 .len()
1640 .checked_mul(8)
1641 .and_then(|n| n.checked_add(5))
1642 .ok_or_else(|| {
1643 declined(
1644 DiagnosticCode::ResourceLimit,
1645 "reference string output size overflow",
1646 )
1647 })?,
1648 generated: false,
1649 };
1650 shape.admit(generated_depth_limit)?;
1651 budget = scalars
1652 .len()
1653 .checked_mul(4)
1654 .and_then(|n| n.checked_add(3))
1655 .and_then(|n| budget.checked_sub(n))
1656 .ok_or_else(|| {
1657 declined(
1658 DiagnosticCode::ResourceLimit,
1659 "reference translation node budget",
1660 )
1661 })?;
1662 let character = || Term::Global("/s:\"Char\"".into());
1663 let list_constant = |component: &str| Term::Const {
1664 name: format!("/s:\"List\"/s:\"{component}\""),
1665 levels: vec![Universe::Prop],
1666 };
1667 let mut list =
1668 Term::App(Box::new(list_constant("nil")), Box::new(character()));
1669 for scalar in scalars.into_iter().rev() {
1670 let code_point = Term::App(
1671 Box::new(Term::Global("/s:\"Char\"/s:\"ofNat\"".into())),
1672 Box::new(Term::Lit(Literal::Nat(crate::BigInt::from_u64(
1673 u64::from(scalar),
1674 )))),
1675 );
1676 let cons =
1677 Term::App(Box::new(list_constant("cons")), Box::new(character()));
1678 list = Term::App(
1679 Box::new(Term::App(Box::new(cons), Box::new(code_point))),
1680 Box::new(list),
1681 );
1682 }
1683 out.push(OutputTerm {
1684 term: Term::App(Box::new(Term::Global(packing.into())), Box::new(list)),
1685 shape,
1686 });
1687 } else {
1688 return Err(malformed("unknown expression record"));
1689 }
1690 }
1691 Task::App => {
1692 let arg = out.pop().expect("argument");
1693 let fun = out.pop().expect("function");
1694 let shape =
1695 OutputShape::wrapper(&[fun.shape, arg.shape], generated_depth_limit)?;
1696 out.push(OutputTerm {
1697 term: Term::App(Box::new(fun.term), Box::new(arg.term)),
1698 shape,
1699 });
1700 }
1701 Task::Projection { parent, index } => {
1702 let discriminant = out.pop().expect("projection discriminant");
1703 let bindings: Vec<_> = scope
1704 .iter()
1705 .map(|(name, ty, value)| ReferenceBinding {
1706 name,
1707 ty: &out[*ty].term,
1708 value: value.map(|at| &out[at].term),
1709 })
1710 .collect();
1711 let projected = projections.project(
1712 &parent,
1713 index,
1714 discriminant.term,
1715 &bindings,
1716 &mut budget,
1717 )?;
1718 let shape = OutputShape::callback(&projected, generated_depth_limit)?;
1719 out.push(OutputTerm {
1720 term: projected,
1721 shape,
1722 });
1723 }
1724 Task::Binder { kind, record } => {
1725 let name = format!("ref.{depth}");
1726 let (ty, value) = if kind == "letE" {
1727 (out.len() - 2, Some(out.len() - 1))
1728 } else {
1729 (out.len() - 1, None)
1730 };
1731 scope.push((name.clone(), ty, value));
1732 depth += 1;
1733 work.push(Task::Close { kind, name });
1734 work.push(Task::Enter(id(&record["body"])?));
1735 }
1736 Task::Close { kind, name } => {
1737 scope.pop().expect("active source binder");
1738 depth -= 1;
1739 let body = out.pop().expect("binder body");
1740 let output = match kind {
1741 "letE" => {
1742 let value = out.pop().expect("let value");
1743 let ty = out.pop().expect("let type");
1744 let shape = OutputShape::wrapper(
1745 &[ty.shape, value.shape, body.shape],
1746 generated_depth_limit,
1747 )?;
1748 OutputTerm {
1749 term: Term::Let {
1750 name,
1751 ty: Box::new(ty.term),
1752 value: Box::new(value.term),
1753 body: Box::new(body.term),
1754 },
1755 shape,
1756 }
1757 }
1758 "lam" => {
1759 let ty = out.pop().expect("binder domain");
1760 let shape = OutputShape::wrapper(
1761 &[ty.shape, body.shape],
1762 generated_depth_limit,
1763 )?;
1764 OutputTerm {
1765 term: Term::Lambda {
1766 param: name,
1767 param_type: Box::new(ty.term),
1768 body: Box::new(body.term),
1769 },
1770 shape,
1771 }
1772 }
1773 _ => {
1774 let ty = out.pop().expect("binder domain");
1775 let shape = OutputShape::wrapper(
1776 &[ty.shape, body.shape],
1777 generated_depth_limit,
1778 )?;
1779 OutputTerm {
1780 term: Term::Pi {
1781 param: name,
1782 param_type: Box::new(ty.term),
1783 body_type: Box::new(body.term),
1784 },
1785 shape,
1786 }
1787 }
1788 };
1789 out.push(output);
1790 }
1791 }
1792 }
1793 Ok(out.pop().expect("one term").term)
1794 }
1795}
1796
1797#[cfg(test)]
1798#[path = "../../../../logicaffeine_tests/unit/logicaffeine_kernel/interface/lean/reference/projection_scope_controls.rs"]
1799mod projection_scope_controls;
1800
1801#[cfg(test)]
1802#[path = "../../../../logicaffeine_tests/unit/logicaffeine_kernel/interface/lean/reference/block_controls.rs"]
1803mod block_controls;
1804
1805#[cfg(test)]
1806#[path = "../../../../logicaffeine_tests/unit/logicaffeine_kernel/interface/lean/reference/wrapper_controls.rs"]
1807mod wrapper_controls;
1808
1809#[cfg(test)]
1810#[path = "../../../../logicaffeine_tests/unit/logicaffeine_kernel/interface/lean/reference/natural_work_controls.rs"]
1811mod natural_work_controls;
1812
1813#[cfg(test)]
1814#[path = "../../../../logicaffeine_tests/unit/logicaffeine_kernel/interface/lean/reference/identity_controls.rs"]
1815mod identity_controls;