Skip to main content

logicaffeine_kernel/dag/
export.rs

1//! Reading a `lean4export` NDJSON stream straight into the kernel core.
2//!
3//! The export is already a maximally shared DAG: every name, level and expression is a numbered
4//! record defined before it is used. Each record becomes one interned node, so the export's
5//! sharing is preserved exactly and nothing is ever expanded into a tree. Declarations are checked
6//! the moment they arrive; nothing of the input is retained but the interned DAG.
7
8use super::check::{Decl, DefnDecl, Header};
9use super::env::{Const, CtorInfo, Hint, InductiveInfo, Kind, QuotKind, RecInfo, RecRule, Safety};
10use super::error::{FailKind, KResult, KernelFailure};
11use super::expr::{ExprId, Node};
12use super::inductive::{IndType, InductiveDecl};
13use super::json::{Tape, Tok};
14use super::level::{LevelId, LevelNode};
15use super::name::NameId;
16use super::nat::Nat;
17use super::tc::Kernel;
18use rustc_hash::FxHashMap as FxMap;
19use std::io::BufRead;
20use std::rc::Rc;
21
22const ABSENT: u32 = u32::MAX;
23
24/// Export index → interned id; dense for the usual contiguous numbering, a map for the rest.
25struct IdMap {
26    dense: Vec<u32>,
27    sparse: FxMap<u64, u32>,
28}
29
30impl IdMap {
31    fn new() -> IdMap {
32        IdMap {
33            dense: Vec::new(),
34            sparse: FxMap::default(),
35        }
36    }
37
38    #[inline]
39    fn insert(&mut self, k: u64, v: u32) -> bool {
40        if k == self.dense.len() as u64 && k < u32::MAX as u64 {
41            self.dense.push(v);
42            return true;
43        }
44        if k < (self.dense.len() as u64) * 2 + 1024 && k < u32::MAX as u64 {
45            let k = k as usize;
46            if self.dense.len() <= k {
47                self.dense.resize(k + 1, ABSENT);
48            }
49            let fresh = self.dense[k] == ABSENT;
50            self.dense[k] = v;
51            fresh
52        } else {
53            self.sparse.insert(k, v).is_none()
54        }
55    }
56
57    fn get(&self, k: u64) -> Option<u32> {
58        match self.dense.get(k as usize) {
59            Some(&v) if v != ABSENT => Some(v),
60            _ => self.sparse.get(&k).copied(),
61        }
62    }
63}
64
65pub struct Stats {
66    /// The kernel's memory breakdown at the end of the run.
67    pub memory: Vec<(&'static str, usize)>,
68    pub records: u64,
69    pub declarations: u64,
70    /// Primitive `Nat` operations proven to meet their equations, and so computed natively.
71    pub validated_primitives: Vec<String>,
72}
73
74/// Observes each declaration the reader hands to the kernel: `begin` just before the kernel
75/// checks it and `end` just after, with its name, its record kind and whether it was admitted. A
76/// measurement hook; checking never depends on it.
77pub trait DeclMeter {
78    fn begin(&mut self);
79    fn end(&mut self, name: &str, kind: &'static str, admitted: bool);
80}
81
82/// How a run is observed, beside the kernel's own configuration.
83#[derive(Default)]
84pub struct Observe {
85    /// Told about every declaration the kernel checks.
86    pub meter: Option<Box<dyn DeclMeter>>,
87    /// The declarations before this index are admitted as the export states them, unchecked —
88    /// except the primitives, the quotient and any inductive block declaring a primitive, which are
89    /// checked so that the literal operations validated match a full run. A diagnostic for
90    /// measuring declarations deep in an export without re-checking its prefix: a run with a
91    /// trusted prefix claims nothing, and always declines.
92    pub trust_before: u64,
93}
94
95pub struct Reader<'k> {
96    pub k: &'k mut Kernel,
97    names: IdMap,
98    levels: IdMap,
99    exprs: IdMap,
100    pending_mutual: Vec<(Rc<[NameId]>, DefnDecl)>,
101    /// Named reference terms (`{"ref":{"key":…,"value":…}}`), for files of kernel-internal terms.
102    pub refs: FxMap<String, ExprId>,
103    /// Read and intern every record but check no declaration: a measurement of the reader alone.
104    pub parse_only: bool,
105    pub stats: Stats,
106    pub meter: Option<Box<dyn DeclMeter>>,
107    /// See [`Observe::trust_before`].
108    pub trust_before: u64,
109    /// Read the common record layouts byte for byte (the default). Off, every record goes
110    /// through the general parser — which must build exactly the same store.
111    pub fast_paths: bool,
112}
113
114enum NamePart<'a> {
115    Str(&'a str),
116    Num(u64),
117}
118
119/// Powers of ten up to one eight-digit chunk's scale.
120const POW10: [u64; 9] = [
121    1,
122    10,
123    100,
124    1_000,
125    10_000,
126    100_000,
127    1_000_000,
128    10_000_000,
129    100_000_000,
130];
131
132/// Eight input bytes, each XOR `'0'` (so a digit becomes its value, first byte lowest): how many
133/// lead with digits, and their value — the whole run converted with three multiply-adds instead
134/// of a loop over bytes.
135#[inline(always)]
136fn swar_digits(x: u64) -> (u32, u64) {
137    // A byte is a digit iff it is now at most 9; `b + 0x76` sets bit 7 exactly for bytes above 9
138    // (a carry out of a byte only happens above 0x89, which is already not a digit, and only
139    // disturbs later bytes, past the first non-digit).
140    let non_digit = (x.wrapping_add(0x7676_7676_7676_7676) | x) & 0x8080_8080_8080_8080;
141    let n = if non_digit == 0 {
142        8
143    } else {
144        non_digit.trailing_zeros() / 8
145    };
146    if n == 0 {
147        return (0, 0);
148    }
149    // Keep the digits, moved to the high bytes: the leading positions become zeros, which are
150    // leading zeros of the number.
151    let mut d = x << (64 - 8 * n);
152    d = (d * 10 + (d >> 8)) & 0x00FF_00FF_00FF_00FF;
153    d = (d * 100 + (d >> 16)) & 0x0000_FFFF_0000_FFFF;
154    d = (d * 10_000 + (d >> 32)) & 0x0000_0000_FFFF_FFFF;
155    (n, d)
156}
157
158/// The window a fixed-layout record read may look at: every load of [`wide_app`] stays inside it,
159/// so one length check covers them all.
160const WIDE: usize = 64;
161
162#[inline(always)]
163fn word(w: &[u8; WIDE], at: usize) -> u64 {
164    u64::from_le_bytes(*w[at..].first_chunk::<8>().unwrap())
165}
166
167/// An id of one to eight digits at `at`, without a leading zero: its value and its length. The
168/// caller checks the byte after it, which rejects a ninth digit.
169#[inline(always)]
170fn wide_id(w: &[u8; WIDE], at: usize) -> Option<(u64, usize)> {
171    let x = word(w, at) ^ 0x3030_3030_3030_3030;
172    let (n, v) = swar_digits(x);
173    if n == 0 || (n > 1 && x & 0xFF == 0) {
174        return None;
175    }
176    Some((v, n as usize))
177}
178
179/// `{"app":{"arg":A,"fn":F},"ie":I}` and its newline, each id of at most eight digits, from the
180/// front of a window: eight word loads and compares, no loop over bytes. Anything else — a longer
181/// id, a leading zero, other spacing — is `None`, for the general record path. Returns the bytes
182/// consumed and the ids `(I, F, A)`.
183#[inline(always)]
184fn wide_app(w: &[u8; WIDE]) -> Option<(usize, u64, u64, u64)> {
185    const HEAD: u64 = u64::from_le_bytes(*b"{\"app\":{");
186    const ARG: u64 = u64::from_le_bytes(*b":{\"arg\":");
187    const FN: u64 = u64::from_le_bytes(*b",\"fn\":\0\0");
188    const IE: u64 = u64::from_le_bytes(*b"},\"ie\":\0");
189    if word(w, 0) != HEAD || word(w, 6) != ARG {
190        return None;
191    }
192    let (a, n) = wide_id(w, 14)?;
193    let p = 14 + n;
194    if word(w, p) & 0xFFFF_FFFF_FFFF != FN {
195        return None;
196    }
197    let (f, n) = wide_id(w, p + 6)?;
198    let q = p + 6 + n;
199    if word(w, q) & 0xFF_FFFF_FFFF_FFFF != IE {
200        return None;
201    }
202    let (i, n) = wide_id(w, q + 7)?;
203    let r = q + 7 + n;
204    if w[r..r + 2] != *b"}\n" {
205        return None;
206    }
207    Some((r + 2, i, f, a))
208}
209
210/// A cursor over one record's bytes for the fast path.
211struct Cur<'a> {
212    b: &'a [u8],
213    i: usize,
214}
215
216impl<'a> Cur<'a> {
217    /// The fixed literal `s` (its length known at compile time, so the comparison is inline).
218    #[inline(always)]
219    fn lit<const N: usize>(&mut self, s: &[u8; N]) -> Option<()> {
220        let got: &[u8; N] = self.b.get(self.i..self.i + N)?.try_into().ok()?;
221        if got == s {
222            self.i += N;
223            Some(())
224        } else {
225            None
226        }
227    }
228
229    /// The literal `s` and then the newline that ends the record.
230    #[inline(always)]
231    fn close<const N: usize>(&mut self, s: &[u8; N]) -> Option<()> {
232        self.lit(s)?;
233        if self.b.get(self.i) == Some(&b'\n') {
234            self.i += 1;
235            Some(())
236        } else {
237            None
238        }
239    }
240
241    #[inline]
242    fn peek(&self) -> Option<u8> {
243        self.b.get(self.i).copied()
244    }
245
246    /// A plain JSON integer of at most 19 digits (so it cannot overflow `u64`), with no leading
247    /// zero unless it is `0`. Longer numbers take the general path.
248    #[inline(always)]
249    fn num(&mut self) -> Option<u64> {
250        let start = self.i;
251        let Some(chunk) = self.b.get(start..start + 8) else {
252            return self.num_tail(start, 0);
253        };
254        let x = u64::from_le_bytes(chunk.try_into().unwrap()) ^ 0x3030_3030_3030_3030;
255        let (n, v) = swar_digits(x);
256        // The first byte, XOR '0', is zero exactly for a leading zero.
257        if n < 8 {
258            if n == 0 || (n > 1 && x & 0xFF == 0) {
259                return None;
260            }
261            self.i = start + n as usize;
262            return Some(v);
263        }
264        // Eight digits — every export id from 10^7 to 10^8 — end here unless a ninth follows.
265        match self.b.get(start + 8) {
266            Some(c) if !c.is_ascii_digit() => {
267                if x & 0xFF == 0 {
268                    return None;
269                }
270                self.i = start + 8;
271                Some(v)
272            }
273            _ => self.num_long(start, v),
274        }
275    }
276
277    /// A number of eight or more digits, its first eight already read as `v`. At most 19 digits
278    /// are accepted, checked before each multiply, so the value never overflows.
279    #[cold]
280    fn num_long(&mut self, start: usize, mut v: u64) -> Option<u64> {
281        if self.b[start] == b'0' {
282            return None;
283        }
284        self.i = start + 8;
285        loop {
286            let Some(chunk) = self.b.get(self.i..self.i + 8) else {
287                return self.num_tail(start, v);
288            };
289            let x = u64::from_le_bytes(chunk.try_into().unwrap()) ^ 0x3030_3030_3030_3030;
290            let (n, digits) = swar_digits(x);
291            if self.i - start + n as usize > 19 {
292                return None;
293            }
294            v = v * POW10[n as usize] + digits;
295            self.i += n as usize;
296            if n < 8 {
297                return Some(v);
298            }
299        }
300    }
301
302    /// The digits left when fewer than eight bytes of input remain, one at a time, onto the `v`
303    /// read from `start` so far.
304    #[cold]
305    fn num_tail(&mut self, start: usize, mut v: u64) -> Option<u64> {
306        while let Some(&d) = self.b.get(self.i) {
307            let d = d.wrapping_sub(b'0');
308            if d > 9 {
309                break;
310            }
311            if self.i - start == 19 {
312                return None;
313            }
314            v = v * 10 + d as u64;
315            self.i += 1;
316        }
317        let len = self.i - start;
318        (len > 0 && (len == 1 || self.b[start] != b'0')).then_some(v)
319    }
320
321    /// The remainder of a binder-info string (after its opening quote), one of Lean's four.
322    #[inline]
323    fn binder_info(&mut self) -> Option<()> {
324        if self.lit(b"default\"").is_some()
325            || self.lit(b"implicit\"").is_some()
326            || self.lit(b"instImplicit\"").is_some()
327            || self.lit(b"strictImplicit\"").is_some()
328        {
329            Some(())
330        } else {
331            None
332        }
333    }
334
335    /// A string body up to its closing quote, only when it contains no escape or control byte.
336    #[inline]
337    fn plain_string(&mut self) -> Option<&'a [u8]> {
338        let start = self.i;
339        while let Some(&c) = self.b.get(self.i) {
340            match c {
341                b'"' => {
342                    let s = &self.b[start..self.i];
343                    self.i += 1;
344                    return Some(s);
345                }
346                b'\\' => return None,
347                c if c < 0x20 => return None,
348                _ => self.i += 1,
349            }
350        }
351        None
352    }
353}
354
355fn bad(m: impl Into<String>) -> KernelFailure {
356    KernelFailure::reject(format!("malformed export: {}", m.into()))
357}
358
359fn recycle<'b>(mut v: Vec<Tok<'_>>) -> Vec<Tok<'b>> {
360    v.clear();
361    v.into_iter().map(|_| unreachable!()).collect()
362}
363
364impl<'k> Reader<'k> {
365    pub fn new(k: &'k mut Kernel) -> Reader<'k> {
366        let mut names = IdMap::new();
367        names.insert(0, NameId::ANON.0);
368        let mut levels = IdMap::new();
369        levels.insert(0, LevelId::ZERO.0);
370        Reader {
371            k,
372            names,
373            levels,
374            exprs: IdMap::new(),
375            pending_mutual: Vec::new(),
376            refs: FxMap::default(),
377            parse_only: false,
378            stats: Stats {
379                memory: Vec::new(),
380                records: 0,
381                declarations: 0,
382                validated_primitives: Vec::new(),
383            },
384            meter: None,
385            trust_before: 0,
386            fast_paths: true,
387        }
388    }
389
390    /// Prepares the stores for an export of `bytes` bytes (about one expression per 45 bytes).
391    pub fn size_hint(&mut self, bytes: u64) {
392        let nodes = (bytes / 45).min(u32::MAX as u64 / 2) as usize;
393        self.k.s.reserve_persistent(nodes);
394        self.exprs.dense.reserve(nodes);
395    }
396
397    /// Reads and checks a whole export. `Ok` means every declaration was admitted.
398    pub fn run(&mut self, mut input: impl BufRead) -> KResult<()> {
399        let mut buf: Vec<u8> = vec![0; 1 << 22];
400        let (mut start, mut end, mut eof) = (0usize, 0usize, false);
401        let mut toks: Vec<Tok<'static>> = Vec::new();
402        let mut line_no = 0u64;
403        let at_line = |mut e: KernelFailure, line_no: u64| {
404            if e.detail.is_none() {
405                e.detail = Some(format!("at export line {line_no}"));
406            }
407            e
408        };
409        loop {
410            if self.fast_paths && start < end {
411                let (used, read, r) = self.fast_records(&buf[start..end]);
412                line_no += read;
413                self.stats.records += read;
414                start += used;
415                r.map_err(|e| at_line(e, line_no + 1))?;
416            }
417            if start < end {
418                let rest = &buf[start..end];
419                let line_end = match rest.iter().position(|&b| b == b'\n') {
420                    Some(nl) => Some(nl),
421                    None if eof => Some(rest.len()),
422                    None => None,
423                };
424                if let Some(len) = line_end {
425                    line_no += 1;
426                    let mut line = &buf[start..start + len];
427                    while let [head @ .., b'\r'] = line {
428                        line = head;
429                    }
430                    start = (start + len + 1).min(end);
431                    if line.is_empty() {
432                        continue;
433                    }
434                    let tape = match Tape::parse(line, recycle(toks)) {
435                        Ok(t) => t,
436                        Err(_) => return Err(bad(format!("line {line_no} is not valid JSON"))),
437                    };
438                    let r = self.record(&tape);
439                    toks = recycle(tape.toks);
440                    r.map_err(|e| at_line(e, line_no))?;
441                    self.stats.records += 1;
442                    continue;
443                }
444            } else if eof {
445                break;
446            }
447            if start > 0 {
448                buf.copy_within(start..end, 0);
449                end -= start;
450                start = 0;
451            }
452            if end == buf.len() {
453                buf.resize(buf.len() * 2, 0);
454            }
455            let n = input
456                .read(&mut buf[end..])
457                .map_err(|e| KernelFailure::resource(format!("cannot read input: {e}")))?;
458            if n == 0 {
459                eof = true;
460            } else {
461                end += n;
462            }
463        }
464        if let Some((all, _)) = self.pending_mutual.first() {
465            let missing: Vec<String> = all.iter().map(|n| self.k.s.names.display(*n)).collect();
466            return Err(bad(format!(
467                "incomplete mutual block {{{}}}",
468                missing.join(", ")
469            )));
470        }
471        Ok(())
472    }
473
474    /// Reads records from the front of `buf` for as long as each is one of the layouts
475    /// [`Reader::fast_record`] recognises, keeping the loop's state in registers. Returns the
476    /// bytes consumed, the records read, and the failure of the record after them, if one failed;
477    /// otherwise the record there (if any) is for the general parser, or not yet whole.
478    fn fast_records(&mut self, buf: &[u8]) -> (usize, u64, KResult<()>) {
479        let (mut at, mut read) = (0usize, 0u64);
480        loop {
481            let rest = &buf[at..];
482            // Applications are three records in four: read them from a fixed window when one fits.
483            if let Some(w) = rest.first_chunk::<WIDE>() {
484                if let Some((used, i, f, a)) = wide_app(w) {
485                    if let Err(e) = self.fast_app(i, f, a) {
486                        return (at, read, Err(e));
487                    }
488                    at += used;
489                    read += 1;
490                    continue;
491                }
492            }
493            match self.fast_record(rest) {
494                Some((used, Ok(()))) => {
495                    at += used;
496                    read += 1;
497                }
498                Some((_, Err(e))) => return (at, read, Err(e)),
499                None => return (at, read, Ok(())),
500            }
501        }
502    }
503
504    /// The common record layouts `lean4export` writes, recognised byte for byte in the input
505    /// buffer, through the newline that ends the record, without building a token tape. Anything
506    /// that is not exactly one of these layouts (another key order, spacing, an escaped string, a
507    /// number with a sign or exponent, a record not yet wholly in the buffer) returns `None` and
508    /// goes through the general parser, so this path accepts only what the general path would
509    /// read identically. Returns the bytes consumed.
510    ///
511    /// [`Reader::fast_records`] reads runs of these; this is one record, from any window.
512    fn fast_record(&mut self, line: &[u8]) -> Option<(usize, KResult<()>)> {
513        let mut c = Cur { b: line, i: 0 };
514        c.lit(b"{\"")?;
515        match *line.get(2)? {
516            b'a' => {
517                c.lit(b"app\":{\"arg\":")?;
518                let a = c.num()?;
519                c.lit(b",\"fn\":")?;
520                let f = c.num()?;
521                c.lit(b"},\"ie\":")?;
522                let i = c.num()?;
523                c.close(b"}")?;
524                Some((c.i, self.fast_app(i, f, a)))
525            }
526            b'f' => {
527                c.lit(b"forallE\":{\"binderInfo\":\"")?;
528                c.binder_info()?;
529                c.lit(b",\"body\":")?;
530                let b = c.num()?;
531                c.lit(b",\"name\":")?;
532                c.num()?;
533                c.lit(b",\"type\":")?;
534                let t = c.num()?;
535                c.lit(b"},\"ie\":")?;
536                let i = c.num()?;
537                c.close(b"}")?;
538                Some((c.i, self.fast_binder(i, t, b, true)))
539            }
540            b'b' => {
541                c.lit(b"bvar\":")?;
542                let k = c.num()?;
543                c.lit(b",\"ie\":")?;
544                let i = c.num()?;
545                c.close(b"}")?;
546                let k = u32::try_from(k).ok()?;
547                let e = self.k.s.bvar(k);
548                Some((c.i, self.put_expr(i, e)))
549            }
550            b'c' => {
551                c.lit(b"const\":{\"name\":")?;
552                let n = c.num()?;
553                c.lit(b",\"us\":[")?;
554                let mut us: smallvec::SmallVec<[u64; 4]> = smallvec::SmallVec::new();
555                if c.peek() != Some(b']') {
556                    us.push(c.num()?);
557                    while c.peek() == Some(b',') {
558                        c.i += 1;
559                        us.push(c.num()?);
560                    }
561                }
562                c.lit(b"]},\"ie\":")?;
563                let i = c.num()?;
564                c.close(b"}")?;
565                Some((c.i, self.fast_const(i, n, &us)))
566            }
567            b'i' => {
568                c.lit(b"i")?;
569                match c.peek()? {
570                    b'e' => {
571                        c.lit(b"e\":")?;
572                        let i = c.num()?;
573                        c.lit(b",\"")?;
574                        match c.peek()? {
575                            b'l' => {
576                                if c.lit(b"lam\":{\"binderInfo\":\"").is_some() {
577                                    c.binder_info()?;
578                                    c.lit(b",\"body\":")?;
579                                    let b = c.num()?;
580                                    c.lit(b",\"name\":")?;
581                                    c.num()?;
582                                    c.lit(b",\"type\":")?;
583                                    let t = c.num()?;
584                                    c.close(b"}}")?;
585                                    Some((c.i, self.fast_binder(i, t, b, false)))
586                                } else {
587                                    None
588                                }
589                            }
590                            b's' => {
591                                c.lit(b"sort\":")?;
592                                let l = c.num()?;
593                                c.close(b"}")?;
594                                Some((c.i, self.fast_sort(i, l)))
595                            }
596                            _ => None,
597                        }
598                    }
599                    b'n' => {
600                        c.lit(b"n\":")?;
601                        let i = c.num()?;
602                        c.lit(b",\"")?;
603                        match c.peek()? {
604                            b's' => {
605                                c.lit(b"str\":{\"pre\":")?;
606                                let p = c.num()?;
607                                c.lit(b",\"str\":\"")?;
608                                let text = c.plain_string()?;
609                                c.close(b"}}")?;
610                                let text = std::str::from_utf8(text).ok()?;
611                                Some((c.i, self.fast_name(i, p, NamePart::Str(text))))
612                            }
613                            b'n' => {
614                                c.lit(b"num\":{\"i\":")?;
615                                let k = c.num()?;
616                                c.lit(b",\"pre\":")?;
617                                let p = c.num()?;
618                                c.close(b"}}")?;
619                                Some((c.i, self.fast_name(i, p, NamePart::Num(k))))
620                            }
621                            _ => None,
622                        }
623                    }
624                    _ => None,
625                }
626            }
627            _ => None,
628        }
629    }
630
631    fn put_expr(&mut self, idx: u64, e: ExprId) -> KResult<()> {
632        if !self.exprs.insert(idx, e.0) {
633            return Err(bad(format!("expression #{idx} is defined twice")));
634        }
635        Ok(())
636    }
637
638    fn expr_ix(&self, idx: u64) -> KResult<ExprId> {
639        self.exprs
640            .get(idx)
641            .map(ExprId)
642            .ok_or_else(|| bad(format!("undefined expression #{idx}")))
643    }
644
645    fn level_ix(&self, idx: u64) -> KResult<LevelId> {
646        self.levels
647            .get(idx)
648            .map(LevelId)
649            .ok_or_else(|| bad(format!("undefined level #{idx}")))
650    }
651
652    fn name_ix(&self, idx: u64) -> KResult<NameId> {
653        self.names
654            .get(idx)
655            .map(NameId)
656            .ok_or_else(|| bad(format!("undefined name #{idx}")))
657    }
658
659    fn fast_app(&mut self, i: u64, f: u64, a: u64) -> KResult<()> {
660        let (f, a) = (self.expr_ix(f)?, self.expr_ix(a)?);
661        let e = self.k.s.app(f, a);
662        self.put_expr(i, e)
663    }
664
665    fn fast_binder(&mut self, i: u64, t: u64, b: u64, pi: bool) -> KResult<()> {
666        let (t, b) = (self.expr_ix(t)?, self.expr_ix(b)?);
667        let e = if pi {
668            self.k.s.pi(t, b)
669        } else {
670            self.k.s.lam(t, b)
671        };
672        self.put_expr(i, e)
673    }
674
675    fn fast_sort(&mut self, i: u64, l: u64) -> KResult<()> {
676        let l = self.level_ix(l)?;
677        let e = self.k.s.sort(l);
678        self.put_expr(i, e)
679    }
680
681    fn fast_const(&mut self, i: u64, n: u64, us: &[u64]) -> KResult<()> {
682        let n = self.name_ix(n)?;
683        let us: KResult<smallvec::SmallVec<[LevelId; 4]>> =
684            us.iter().map(|u| self.level_ix(*u)).collect();
685        let e = self.k.s.cnst_levels(n, &us?);
686        self.put_expr(i, e)
687    }
688
689    fn fast_name(&mut self, i: u64, p: u64, part: NamePart<'_>) -> KResult<()> {
690        let pre = self.name_ix(p)?;
691        let id = match part {
692            NamePart::Str(s) => self.k.s.names.str(pre, s),
693            NamePart::Num(k) => self.k.s.names.num(pre, k),
694        };
695        if i == 0 || !self.names.insert(i, id.0) {
696            return Err(bad(format!("name #{i} is defined twice")));
697        }
698        Ok(())
699    }
700
701    fn name(&self, t: &Tape, i: usize) -> KResult<NameId> {
702        let idx = t.u64(i).ok_or_else(|| bad("name index expected"))?;
703        self.names
704            .get(idx)
705            .map(NameId)
706            .ok_or_else(|| bad(format!("undefined name #{idx}")))
707    }
708
709    fn level(&self, t: &Tape, i: usize) -> KResult<LevelId> {
710        let idx = t.u64(i).ok_or_else(|| bad("level index expected"))?;
711        self.levels
712            .get(idx)
713            .map(LevelId)
714            .ok_or_else(|| bad(format!("undefined level #{idx}")))
715    }
716
717    fn expr(&self, t: &Tape, i: usize) -> KResult<ExprId> {
718        let idx = t.u64(i).ok_or_else(|| bad("expression index expected"))?;
719        self.exprs
720            .get(idx)
721            .map(ExprId)
722            .ok_or_else(|| bad(format!("undefined expression #{idx}")))
723    }
724
725    fn field(&self, t: &Tape, obj: usize, key: &str) -> KResult<usize> {
726        t.get(obj, key)
727            .ok_or_else(|| bad(format!("missing field '{key}'")))
728    }
729
730    fn name_field(&self, t: &Tape, obj: usize, key: &str) -> KResult<NameId> {
731        let i = self.field(t, obj, key)?;
732        self.name(t, i)
733    }
734
735    fn expr_field(&self, t: &Tape, obj: usize, key: &str) -> KResult<ExprId> {
736        let i = self.field(t, obj, key)?;
737        self.expr(t, i)
738    }
739
740    fn u64_field(&self, t: &Tape, obj: usize, key: &str) -> KResult<u64> {
741        let i = self.field(t, obj, key)?;
742        t.u64(i)
743            .ok_or_else(|| bad(format!("field '{key}' is not a number")))
744    }
745
746    fn u32_field(&self, t: &Tape, obj: usize, key: &str) -> KResult<u32> {
747        let v = self.u64_field(t, obj, key)?;
748        u32::try_from(v).map_err(|_| bad(format!("field '{key}' is out of range")))
749    }
750
751    fn bool_field(&self, t: &Tape, obj: usize, key: &str) -> KResult<bool> {
752        let i = self.field(t, obj, key)?;
753        t.bool(i)
754            .ok_or_else(|| bad(format!("field '{key}' is not a boolean")))
755    }
756
757    fn names_field(&self, t: &Tape, obj: usize, key: &str) -> KResult<Rc<[NameId]>> {
758        let i = self.field(t, obj, key)?;
759        let v: KResult<Vec<NameId>> = t.elems(i).map(|e| self.name(t, e)).collect();
760        Ok(v?.into())
761    }
762
763    fn record(&mut self, t: &Tape) -> KResult<()> {
764        if !matches!(t.toks.first(), Some(Tok::Obj { .. })) {
765            return Err(bad("record is not an object"));
766        }
767        let mut id_key: Option<(&[u8], u64)> = None;
768        let mut kind: Option<(&[u8], usize)> = None;
769        for (k, v) in t.members(0) {
770            match k {
771                b"in" | b"il" | b"ie" => {
772                    id_key = Some((
773                        k,
774                        t.u64(v)
775                            .ok_or_else(|| bad("record index is not a number"))?,
776                    ));
777                }
778                _ => {
779                    if kind.is_some() {
780                        return Err(bad("record has more than one payload"));
781                    }
782                    kind = Some((k, v));
783                }
784            }
785        }
786        let Some((kind, v)) = kind else {
787            return Err(bad("record has no payload"));
788        };
789        match id_key {
790            Some((b"in", idx)) => self.name_record(t, idx, kind, v),
791            Some((b"il", idx)) => self.level_record(t, idx, kind, v),
792            Some((b"ie", idx)) => self.expr_record(t, idx, kind, v),
793            Some(_) => unreachable!(),
794            None => self.decl_record(t, kind, v),
795        }
796    }
797
798    fn name_record(&mut self, t: &Tape, idx: u64, kind: &[u8], v: usize) -> KResult<()> {
799        let pre = self.name_field(t, v, "pre")?;
800        let id = match kind {
801            b"str" => {
802                let si = self.field(t, v, "str")?;
803                let s = t
804                    .str(si)
805                    .ok_or_else(|| bad("name component is not a string"))?;
806                self.k.s.names.str(pre, &s)
807            }
808            b"num" => {
809                let n = self.u64_field(t, v, "i")?;
810                self.k.s.names.num(pre, n)
811            }
812            _ => return Err(bad("unknown name record")),
813        };
814        if idx == 0 || !self.names.insert(idx, id.0) {
815            return Err(bad(format!("name #{idx} is defined twice")));
816        }
817        Ok(())
818    }
819
820    fn level_record(&mut self, t: &Tape, idx: u64, kind: &[u8], v: usize) -> KResult<()> {
821        let pair = |r: &Reader| -> KResult<(LevelId, LevelId)> {
822            let mut it = t.elems(v);
823            let a = it.next().ok_or_else(|| bad("level pair expected"))?;
824            let b = it.next().ok_or_else(|| bad("level pair expected"))?;
825            if it.next().is_some() {
826                return Err(bad("level pair expected"));
827            }
828            Ok((r.level(t, a)?, r.level(t, b)?))
829        };
830        let node = match kind {
831            b"succ" => LevelNode::Succ(self.level(t, v)?),
832            b"max" => {
833                let (a, b) = pair(self)?;
834                LevelNode::Max(a, b)
835            }
836            b"imax" => {
837                let (a, b) = pair(self)?;
838                LevelNode::IMax(a, b)
839            }
840            b"param" => LevelNode::Param(self.name(t, v)?),
841            _ => return Err(bad("unknown level record")),
842        };
843        let id = self.k.s.levels.intern(node);
844        if idx == 0 || !self.levels.insert(idx, id.0) {
845            return Err(bad(format!("level #{idx} is defined twice")));
846        }
847        Ok(())
848    }
849
850    fn expr_record(&mut self, t: &Tape, idx: u64, kind: &[u8], v: usize) -> KResult<()> {
851        let s = &mut self.k.s;
852        let e = match kind {
853            b"bvar" => {
854                let i = t.u64(v).ok_or_else(|| bad("bvar index"))?;
855                s.bvar(u32::try_from(i).map_err(|_| bad("bvar index out of range"))?)
856            }
857            b"sort" => {
858                let l = self.level(t, v)?;
859                self.k.s.sort(l)
860            }
861            b"const" => {
862                let n = self.name_field(t, v, "name")?;
863                let us_i = self.field(t, v, "us")?;
864                let us: KResult<Vec<LevelId>> = t.elems(us_i).map(|x| self.level(t, x)).collect();
865                let us = us?;
866                self.k.s.cnst_levels(n, &us)
867            }
868            b"app" => {
869                let f = self.expr_field(t, v, "fn")?;
870                let a = self.expr_field(t, v, "arg")?;
871                self.k.s.app(f, a)
872            }
873            b"lam" | b"forallE" => {
874                let ty = self.expr_field(t, v, "type")?;
875                let body = self.expr_field(t, v, "body")?;
876                if kind == b"lam" {
877                    self.k.s.lam(ty, body)
878                } else {
879                    self.k.s.pi(ty, body)
880                }
881            }
882            b"letE" => {
883                let ty = self.expr_field(t, v, "type")?;
884                let val = self.expr_field(t, v, "value")?;
885                let body = self.expr_field(t, v, "body")?;
886                self.k.s.let_(ty, val, body)
887            }
888            b"proj" => {
889                let sn = self.name_field(t, v, "typeName")?;
890                let i = self.u32_field(t, v, "idx")?;
891                let x = self.expr_field(t, v, "struct")?;
892                self.k.s.proj(sn, i, x)
893            }
894            b"natVal" => {
895                let digits = t.str(v).ok_or_else(|| bad("natVal is not a string"))?;
896                let n = Nat::parse_decimal(&digits)
897                    .ok_or_else(|| bad("natVal is not a natural number"))?;
898                self.k.s.nat_lit(n)
899            }
900            b"strVal" => {
901                let st = t.str(v).ok_or_else(|| bad("strVal is not a string"))?;
902                self.k.s.str_lit(&st)
903            }
904            b"mdata" => self.expr_field(t, v, "expr")?,
905            _ => {
906                return Err(KernelFailure::resource(format!(
907                    "unsupported expression record '{}'",
908                    String::from_utf8_lossy(kind)
909                )))
910            }
911        };
912        if !self.exprs.insert(idx, e.0) {
913            return Err(bad(format!("expression #{idx} is defined twice")));
914        }
915        Ok(())
916    }
917
918    fn header(&self, t: &Tape, v: usize) -> KResult<Header> {
919        Ok(Header {
920            name: self.name_field(t, v, "name")?,
921            lparams: self.names_field(t, v, "levelParams")?,
922            ty: self.expr_field(t, v, "type")?,
923        })
924    }
925
926    fn decl_record(&mut self, t: &Tape, kind: &[u8], v: usize) -> KResult<()> {
927        if self.parse_only && kind != b"meta" && kind != b"ref" {
928            return Ok(());
929        }
930        match kind {
931            b"meta" => {
932                let fmt = t
933                    .get(v, "format")
934                    .and_then(|f| t.get(f, "version"))
935                    .and_then(|x| t.str(x));
936                match fmt {
937                    Some(ver) if ver.starts_with("3.") => Ok(()),
938                    _ => Err(KernelFailure::resource("unsupported export format version")),
939                }
940            }
941            b"axiom" => {
942                let header = self.header(t, v)?;
943                let is_unsafe = self.bool_field(t, v, "isUnsafe")?;
944                self.decl(Decl::Axiom { header, is_unsafe })
945            }
946            b"thm" => {
947                let header = self.header(t, v)?;
948                let value = self.expr_field(t, v, "value")?;
949                self.decl(Decl::Thm { header, value })
950            }
951            b"opaque" => {
952                let header = self.header(t, v)?;
953                let value = self.expr_field(t, v, "value")?;
954                let is_unsafe = self.bool_field(t, v, "isUnsafe")?;
955                self.decl(Decl::Opaque {
956                    header,
957                    value,
958                    is_unsafe,
959                })
960            }
961            b"def" => {
962                let header = self.header(t, v)?;
963                let value = self.expr_field(t, v, "value")?;
964                let hi = self.field(t, v, "hints")?;
965                let hint = match t.str(hi).as_deref() {
966                    Some("abbrev") => Hint::Abbrev,
967                    Some("opaque") => Hint::Opaque,
968                    Some(_) => return Err(bad("unknown reducibility hint")),
969                    None => {
970                        let r = t
971                            .get(hi, "regular")
972                            .ok_or_else(|| bad("unknown reducibility hint"))?;
973                        Hint::Regular(
974                            t.u64(r)
975                                .and_then(|x| u32::try_from(x).ok())
976                                .ok_or_else(|| bad("height"))?,
977                        )
978                    }
979                };
980                let si = self.field(t, v, "safety")?;
981                let safety = match t.str(si).as_deref() {
982                    Some("safe") => Safety::Safe,
983                    Some("unsafe") => Safety::Unsafe,
984                    Some("partial") => Safety::Partial,
985                    _ => return Err(bad("unknown safety")),
986                };
987                let all = self.names_field(t, v, "all")?;
988                let d = DefnDecl {
989                    header,
990                    value,
991                    hint,
992                    safety,
993                };
994                if safety != Safety::Safe && all.len() > 1 {
995                    self.pending_mutual.push((all.clone(), d));
996                    self.flush_mutual(&all)
997                } else {
998                    self.decl(Decl::Defn(d))
999                }
1000            }
1001            b"quot" => {
1002                let header = self.header(t, v)?;
1003                let ki = self.field(t, v, "kind")?;
1004                let kind = match t.str(ki).as_deref() {
1005                    Some("type") => QuotKind::Type,
1006                    Some("ctor") => QuotKind::Ctor,
1007                    Some("lift") => QuotKind::Lift,
1008                    Some("ind") => QuotKind::Ind,
1009                    _ => return Err(bad("unknown quotient kind")),
1010                };
1011                self.decl(Decl::Quot { header, kind })
1012            }
1013            b"inductive" => self.inductive_record(t, v),
1014            b"ref" => {
1015                let key = self.field(t, v, "key")?;
1016                let key = t
1017                    .str(key)
1018                    .ok_or_else(|| bad("ref key is not a string"))?
1019                    .into_owned();
1020                let value = self.expr_field(t, v, "value")?;
1021                self.refs.insert(key, value);
1022                Ok(())
1023            }
1024            _ => Err(KernelFailure::resource(format!(
1025                "unsupported declaration record '{}'",
1026                String::from_utf8_lossy(kind)
1027            ))),
1028        }
1029    }
1030
1031    fn decl(&mut self, d: Decl) -> KResult<()> {
1032        let index = self.stats.declarations;
1033        self.stats.declarations += 1;
1034        if index < self.trust_before && self.k.may_trust(&d) {
1035            self.k.admit_trusted(d);
1036            return Ok(());
1037        }
1038        if self.meter.is_none() {
1039            return self.k.add_decl(d);
1040        }
1041        let (name, kind) = (d.name(), d.kind_name());
1042        self.metered(name, kind, |k| k.add_decl(d))
1043    }
1044
1045    /// `check` on the kernel, reported to the meter.
1046    fn metered(
1047        &mut self,
1048        name: NameId,
1049        kind: &'static str,
1050        check: impl FnOnce(&mut Kernel) -> KResult<()>,
1051    ) -> KResult<()> {
1052        if let Some(m) = self.meter.as_mut() {
1053            m.begin();
1054        }
1055        let r = check(self.k);
1056        if let Some(m) = self.meter.as_mut() {
1057            m.end(&self.k.s.names.display(name), kind, r.is_ok());
1058        }
1059        r
1060    }
1061
1062    fn flush_mutual(&mut self, all: &Rc<[NameId]>) -> KResult<()> {
1063        let have: Vec<NameId> = self
1064            .pending_mutual
1065            .iter()
1066            .filter(|(a, _)| a == all)
1067            .map(|(_, d)| d.header.name)
1068            .collect();
1069        if !all.iter().all(|n| have.contains(n)) {
1070            return Ok(());
1071        }
1072        let mut block = Vec::new();
1073        for n in all.iter() {
1074            let pos = self
1075                .pending_mutual
1076                .iter()
1077                .position(|(a, d)| a == all && d.header.name == *n)
1078                .unwrap();
1079            block.push(self.pending_mutual.remove(pos).1);
1080        }
1081        self.decl(Decl::Mutual(block))
1082    }
1083
1084    fn inductive_record(&mut self, t: &Tape, v: usize) -> KResult<()> {
1085        let types_i = self.field(t, v, "types")?;
1086        let ctors_i = self.field(t, v, "ctors")?;
1087        let recs_i = self.field(t, v, "recs")?;
1088        let mut claimed = Vec::new();
1089        let mut ctor_types: FxMap<NameId, ExprId> = FxMap::default();
1090        for c in t.elems(ctors_i) {
1091            let h = self.header(t, c)?;
1092            ctor_types.insert(h.name, h.ty);
1093            claimed.push(Const {
1094                name: h.name,
1095                lparams: h.lparams,
1096                ty: h.ty,
1097                kind: Kind::Ctor(CtorInfo {
1098                    induct: self.name_field(t, c, "induct")?,
1099                    cidx: self.u32_field(t, c, "cidx")?,
1100                    num_params: self.u32_field(t, c, "numParams")?,
1101                    num_fields: self.u32_field(t, c, "numFields")?,
1102                    is_unsafe: self.bool_field(t, c, "isUnsafe")?,
1103                }),
1104            });
1105        }
1106        let mut types = Vec::new();
1107        let mut first: Option<(Rc<[NameId]>, u32, bool)> = None;
1108        for ti in t.elems(types_i) {
1109            let h = self.header(t, ti)?;
1110            let ctors = self.names_field(t, ti, "ctors")?;
1111            let num_params = self.u32_field(t, ti, "numParams")?;
1112            let is_unsafe = self.bool_field(t, ti, "isUnsafe")?;
1113            if first.is_none() {
1114                first = Some((h.lparams.clone(), num_params, is_unsafe));
1115            }
1116            let mut cs = Vec::new();
1117            for c in ctors.iter() {
1118                let ty = *ctor_types.get(c).ok_or_else(|| {
1119                    KernelFailure::reject(format!(
1120                        "inductive '{}' lists constructor '{}' but the block does not declare it",
1121                        self.k.s.names.display(h.name),
1122                        self.k.s.names.display(*c)
1123                    ))
1124                })?;
1125                cs.push((*c, ty));
1126            }
1127            types.push(IndType {
1128                name: h.name,
1129                ty: h.ty,
1130                ctors: cs,
1131            });
1132            claimed.push(Const {
1133                name: h.name,
1134                lparams: h.lparams,
1135                ty: h.ty,
1136                kind: Kind::Induct(InductiveInfo {
1137                    num_params,
1138                    num_indices: self.u32_field(t, ti, "numIndices")?,
1139                    all: self.names_field(t, ti, "all")?,
1140                    ctors,
1141                    num_nested: self.u32_field(t, ti, "numNested")?,
1142                    is_rec: self.bool_field(t, ti, "isRec")?,
1143                    is_reflexive: self.bool_field(t, ti, "isReflexive")?,
1144                    is_unsafe,
1145                    rec: self.k.rec_name(h.name),
1146                }),
1147            });
1148        }
1149        for ri in t.elems(recs_i) {
1150            let h = self.header(t, ri)?;
1151            let rules_i = self.field(t, ri, "rules")?;
1152            let mut rules = Vec::new();
1153            for r in t.elems(rules_i) {
1154                rules.push(RecRule {
1155                    ctor: self.name_field(t, r, "ctor")?,
1156                    nfields: self.u32_field(t, r, "nfields")?,
1157                    rhs: self.expr_field(t, r, "rhs")?,
1158                });
1159            }
1160            let mut info = RecInfo {
1161                all: self.names_field(t, ri, "all")?,
1162                num_params: self.u32_field(t, ri, "numParams")?,
1163                num_indices: self.u32_field(t, ri, "numIndices")?,
1164                num_motives: self.u32_field(t, ri, "numMotives")?,
1165                num_minors: self.u32_field(t, ri, "numMinors")?,
1166                rules: rules.into(),
1167                k: self.bool_field(t, ri, "k")?,
1168                is_unsafe: self.bool_field(t, ri, "isUnsafe")?,
1169                major_induct: NameId::ANON,
1170            };
1171            info.major_induct = self.major_induct(h.ty, info.major_idx());
1172            claimed.push(Const {
1173                name: h.name,
1174                lparams: h.lparams,
1175                ty: h.ty,
1176                kind: Kind::Rec(info),
1177            });
1178        }
1179        let Some((lparams, nparams, is_unsafe)) = first else {
1180            return Err(KernelFailure::reject("inductive block declares no types"));
1181        };
1182        for c in &claimed {
1183            if self.k.env.contains(c.name) {
1184                return Err(KernelFailure::reject(format!(
1185                    "'{}' has already been declared",
1186                    self.k.s.names.display(c.name)
1187                )));
1188            }
1189        }
1190        let index = self.stats.declarations;
1191        self.stats.declarations += 1;
1192        if index < self.trust_before && self.k.may_trust_block(&claimed) {
1193            self.k.admit_trusted_block(claimed);
1194            return Ok(());
1195        }
1196        let name = types.first().map_or(NameId::ANON, |t| t.name);
1197        let decl = InductiveDecl {
1198            lparams,
1199            nparams,
1200            types,
1201            is_unsafe,
1202        };
1203        if self.meter.is_none() {
1204            return self.k.add_inductive(decl, claimed);
1205        }
1206        self.metered(name, "inductive", |k| k.add_inductive(decl, claimed))
1207    }
1208
1209    fn major_induct(&self, mut ty: ExprId, idx: usize) -> NameId {
1210        for _ in 0..idx {
1211            match self.k.s.node(ty) {
1212                Node::Pi(_, b) => ty = b,
1213                _ => return NameId::ANON,
1214            }
1215        }
1216        match self.k.s.node(ty) {
1217            Node::Pi(d, _) => self.k.s.const_head(d).map_or(NameId::ANON, |(n, _)| n),
1218            _ => NameId::ANON,
1219        }
1220    }
1221}
1222
1223/// The verdict of checking one export, in the Arena's exit-code protocol.
1224pub enum Verdict {
1225    Accept,
1226    Reject(KernelFailure),
1227    Decline(KernelFailure),
1228}
1229
1230impl Verdict {
1231    pub fn exit_code(&self) -> i32 {
1232        match self {
1233            Verdict::Accept => 0,
1234            Verdict::Reject(_) => 1,
1235            Verdict::Decline(_) => 2,
1236        }
1237    }
1238}
1239
1240/// Checks a whole export stream.
1241pub fn check_export(input: impl BufRead) -> (Verdict, Stats) {
1242    check_export_with(input, |_| {})
1243}
1244
1245/// Checks a whole export with a kernel configured by `configure` first.
1246pub fn check_export_with(
1247    input: impl BufRead,
1248    configure: impl FnOnce(&mut Kernel),
1249) -> (Verdict, Stats) {
1250    check_export_sized(input, None, configure)
1251}
1252
1253/// [`check_export_with`] for an input whose size is known, so the stores are sized once.
1254pub fn check_export_sized(
1255    input: impl BufRead,
1256    bytes: Option<u64>,
1257    configure: impl FnOnce(&mut Kernel),
1258) -> (Verdict, Stats) {
1259    check_export_observed(input, bytes, configure, Observe::default())
1260}
1261
1262/// [`check_export_sized`], observed as `observe` asks.
1263pub fn check_export_observed(
1264    input: impl BufRead,
1265    bytes: Option<u64>,
1266    configure: impl FnOnce(&mut Kernel),
1267    observe: Observe,
1268) -> (Verdict, Stats) {
1269    let mut k = Kernel::new();
1270    configure(&mut k);
1271    check_export_in(&mut k, input, bytes, observe)
1272}
1273
1274/// Checks a whole export into the caller's kernel, which keeps everything admitted — a command
1275/// that exits once it has reported need never pay for tearing the kernel down.
1276pub fn check_export_in(
1277    k: &mut Kernel,
1278    input: impl BufRead,
1279    bytes: Option<u64>,
1280    observe: Observe,
1281) -> (Verdict, Stats) {
1282    let mut r = Reader::new(k);
1283    r.meter = observe.meter;
1284    r.trust_before = observe.trust_before;
1285    if let Some(b) = bytes {
1286        r.size_hint(b);
1287    }
1288    let outcome = r.run(input);
1289    let v = if observe.trust_before > 0 {
1290        let prefix = format!(
1291            "trusted-prefix run: the declarations before #{} were admitted unchecked",
1292            observe.trust_before
1293        );
1294        Verdict::Decline(match outcome {
1295            Ok(()) => {
1296                KernelFailure::resource(format!("{prefix}; every checked declaration passed"))
1297            }
1298            Err(e) => {
1299                KernelFailure::resource(format!("{prefix}; a checked declaration failed: {e}"))
1300            }
1301        })
1302    } else {
1303        match outcome {
1304            Ok(()) => Verdict::Accept,
1305            Err(e) if e.kind == FailKind::Reject => Verdict::Reject(e),
1306            Err(e) => Verdict::Decline(e),
1307        }
1308    };
1309    let mut stats = r.stats;
1310    let mut prims: Vec<String> = k
1311        .validated_primitives
1312        .iter()
1313        .map(|n| k.s.names.display(*n))
1314        .collect();
1315    prims.sort();
1316    stats.validated_primitives = prims;
1317    stats.memory = k.memory_report();
1318    (v, stats)
1319}
1320
1321/// Loads a file of named reference terms into `k`'s store.
1322pub fn load_refs(k: &mut Kernel, input: impl BufRead) -> KResult<FxMap<String, ExprId>> {
1323    let mut r = Reader::new(k);
1324    r.run(input)?;
1325    Ok(std::mem::take(&mut r.refs))
1326}
1327
1328#[cfg(test)]
1329#[path = "../../../logicaffeine_tests/unit/logicaffeine_kernel/dag/export/number_tests.rs"]
1330mod number_tests;