Skip to main content

oxilean_kernel/prettyprint/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use std::collections::{HashMap, VecDeque};
6use std::fmt;
7use std::fmt::Write as FmtWrite;
8
9use super::functions::*;
10use crate::{BinderInfo, Expr, Level, Name};
11
12/// Pretty printer for kernel expressions.
13pub struct ExprPrinter {
14    /// Output buffer
15    buffer: String,
16    /// Print configuration
17    pub(super) config: PrintConfig,
18}
19impl ExprPrinter {
20    /// Create a new expression printer with default config.
21    pub fn new() -> Self {
22        Self {
23            buffer: String::new(),
24            config: PrintConfig::default(),
25        }
26    }
27    /// Create a printer with a specific configuration.
28    pub fn with_config(config: PrintConfig) -> Self {
29        Self {
30            buffer: String::new(),
31            config,
32        }
33    }
34    /// Set whether to use unicode symbols.
35    pub fn with_unicode(mut self, unicode: bool) -> Self {
36        self.config.unicode = unicode;
37        self
38    }
39    /// Get the output.
40    pub fn output(self) -> String {
41        self.buffer
42    }
43    /// Print an expression.
44    pub fn print(&mut self, expr: &Expr) -> fmt::Result {
45        self.print_expr(expr, 0)
46    }
47    fn print_expr(&mut self, expr: &Expr, prec: u32) -> fmt::Result {
48        match expr {
49            Expr::Sort(level) => self.print_sort(level),
50            Expr::BVar(idx) => {
51                if self.config.show_indices {
52                    write!(self.buffer, "#{}", idx)
53                } else {
54                    write!(self.buffer, "_")
55                }
56            }
57            Expr::FVar(fvar) => write!(self.buffer, "@{}", fvar.0),
58            Expr::Const(name, levels) => {
59                self.print_name(name)?;
60                if self.config.show_universes && !levels.is_empty() {
61                    write!(self.buffer, ".{{")?;
62                    for (i, level) in levels.iter().enumerate() {
63                        if i > 0 {
64                            write!(self.buffer, ", ")?;
65                        }
66                        self.print_level(level)?;
67                    }
68                    write!(self.buffer, "}}")?;
69                }
70                Ok(())
71            }
72            Expr::App(fun, arg) => {
73                let (head, args) = collect_app_args(expr);
74                if args.len() > 1 {
75                    if prec > 10 {
76                        write!(self.buffer, "(")?;
77                    }
78                    self.print_expr(head, 10)?;
79                    for a in &args {
80                        write!(self.buffer, " ")?;
81                        self.print_expr(a, 11)?;
82                    }
83                    if prec > 10 {
84                        write!(self.buffer, ")")?;
85                    }
86                    Ok(())
87                } else {
88                    if prec > 10 {
89                        write!(self.buffer, "(")?;
90                    }
91                    self.print_expr(fun, 10)?;
92                    write!(self.buffer, " ")?;
93                    self.print_expr(arg, 11)?;
94                    if prec > 10 {
95                        write!(self.buffer, ")")?;
96                    }
97                    Ok(())
98                }
99            }
100            Expr::Lam(bi, name, ty, body) => {
101                if prec > 0 {
102                    write!(self.buffer, "(")?;
103                }
104                if self.config.unicode {
105                    write!(self.buffer, "λ ")?;
106                } else {
107                    write!(self.buffer, "fun ")?;
108                }
109                self.print_binder(*bi, name, ty)?;
110                let mut current_body = body.as_ref();
111                while let Expr::Lam(bi2, name2, ty2, body2) = current_body {
112                    write!(self.buffer, " ")?;
113                    self.print_binder(*bi2, name2, ty2)?;
114                    current_body = body2;
115                }
116                if self.config.unicode {
117                    write!(self.buffer, ", ")?;
118                } else {
119                    write!(self.buffer, " => ")?;
120                }
121                self.print_expr(current_body, 0)?;
122                if prec > 0 {
123                    write!(self.buffer, ")")?;
124                }
125                Ok(())
126            }
127            Expr::Pi(bi, name, ty, body) => {
128                if prec > 0 {
129                    write!(self.buffer, "(")?;
130                }
131                if name.is_anonymous() || *name == Name::str("_") {
132                    self.print_expr(ty, 25)?;
133                    if self.config.unicode {
134                        write!(self.buffer, " → ")?;
135                    } else {
136                        write!(self.buffer, " -> ")?;
137                    }
138                    self.print_expr(body, 24)?;
139                } else {
140                    if self.config.unicode {
141                        write!(self.buffer, "∀ ")?;
142                    } else {
143                        write!(self.buffer, "forall ")?;
144                    }
145                    self.print_binder(*bi, name, ty)?;
146                    let mut current_body = body.as_ref();
147                    while let Expr::Pi(bi2, name2, ty2, body2) = current_body {
148                        if !name2.is_anonymous() && *name2 != Name::str("_") {
149                            write!(self.buffer, " ")?;
150                            self.print_binder(*bi2, name2, ty2)?;
151                            current_body = body2;
152                        } else {
153                            break;
154                        }
155                    }
156                    write!(self.buffer, ", ")?;
157                    self.print_expr(current_body, 0)?;
158                }
159                if prec > 0 {
160                    write!(self.buffer, ")")?;
161                }
162                Ok(())
163            }
164            Expr::Let(name, ty, val, body) => {
165                write!(self.buffer, "let ")?;
166                self.print_name(name)?;
167                write!(self.buffer, " : ")?;
168                self.print_expr(ty, 0)?;
169                write!(self.buffer, " := ")?;
170                self.print_expr(val, 0)?;
171                write!(self.buffer, " in ")?;
172                self.print_expr(body, 0)
173            }
174            Expr::Lit(lit) => write!(self.buffer, "{}", lit),
175            Expr::Proj(name, idx, expr) => {
176                self.print_expr(expr, 11)?;
177                write!(self.buffer, ".{}.{}", name, idx)
178            }
179        }
180    }
181    /// Print a binder with its info.
182    fn print_binder(&mut self, bi: BinderInfo, name: &Name, ty: &Expr) -> fmt::Result {
183        if self.config.show_binder_info {
184            match bi {
185                BinderInfo::Default => {
186                    write!(self.buffer, "(")?;
187                    self.print_name(name)?;
188                    write!(self.buffer, " : ")?;
189                    self.print_expr(ty, 0)?;
190                    write!(self.buffer, ")")
191                }
192                BinderInfo::Implicit => {
193                    write!(self.buffer, "{{")?;
194                    self.print_name(name)?;
195                    write!(self.buffer, " : ")?;
196                    self.print_expr(ty, 0)?;
197                    write!(self.buffer, "}}")
198                }
199                BinderInfo::StrictImplicit => {
200                    if self.config.unicode {
201                        write!(self.buffer, "\u{2983}")?;
202                    } else {
203                        write!(self.buffer, "{{{{")?;
204                    }
205                    self.print_name(name)?;
206                    write!(self.buffer, " : ")?;
207                    self.print_expr(ty, 0)?;
208                    if self.config.unicode {
209                        write!(self.buffer, "\u{2984}")
210                    } else {
211                        write!(self.buffer, "}}}}")
212                    }
213                }
214                BinderInfo::InstImplicit => {
215                    write!(self.buffer, "[")?;
216                    self.print_name(name)?;
217                    write!(self.buffer, " : ")?;
218                    self.print_expr(ty, 0)?;
219                    write!(self.buffer, "]")
220                }
221            }
222        } else {
223            self.print_name(name)?;
224            write!(self.buffer, " : ")?;
225            self.print_expr(ty, 0)
226        }
227    }
228    /// Print a sort expression with smart level display.
229    fn print_sort(&mut self, level: &Level) -> fmt::Result {
230        match level {
231            Level::Zero => write!(self.buffer, "Prop"),
232            _ => {
233                if let Some(n) = level_to_nat(level) {
234                    if n == 1 {
235                        write!(self.buffer, "Type")
236                    } else {
237                        write!(self.buffer, "Type {}", n - 1)
238                    }
239                } else {
240                    write!(self.buffer, "Sort ")?;
241                    self.print_level(level)
242                }
243            }
244        }
245    }
246    pub(super) fn print_level(&mut self, level: &Level) -> fmt::Result {
247        match level {
248            Level::Zero => write!(self.buffer, "0"),
249            Level::Succ(_) => {
250                if let Some(n) = level_to_nat(level) {
251                    write!(self.buffer, "{}", n)
252                } else {
253                    let (base, offset) = level_to_offset(level);
254                    if offset > 0 {
255                        self.print_level(base)?;
256                        write!(self.buffer, "+{}", offset)
257                    } else {
258                        write!(self.buffer, "(")?;
259                        self.print_level(level)?;
260                        write!(self.buffer, ")")
261                    }
262                }
263            }
264            Level::Max(l1, l2) => {
265                write!(self.buffer, "max(")?;
266                self.print_level(l1)?;
267                write!(self.buffer, ", ")?;
268                self.print_level(l2)?;
269                write!(self.buffer, ")")
270            }
271            Level::IMax(l1, l2) => {
272                write!(self.buffer, "imax(")?;
273                self.print_level(l1)?;
274                write!(self.buffer, ", ")?;
275                self.print_level(l2)?;
276                write!(self.buffer, ")")
277            }
278            Level::Param(name) => self.print_name(name),
279            Level::MVar(id) => write!(self.buffer, "?u_{}", id.0),
280        }
281    }
282    fn print_name(&mut self, name: &Name) -> fmt::Result {
283        write!(self.buffer, "{}", name)
284    }
285}
286/// Prints an expression as an S-expression.
287#[allow(dead_code)]
288pub struct SExprPrinter {
289    indent: usize,
290}
291#[allow(dead_code)]
292impl SExprPrinter {
293    /// Creates a new S-expression printer.
294    pub fn new() -> Self {
295        Self { indent: 0 }
296    }
297    /// Renders a name applied to arguments as `(name arg1 arg2 ...)`.
298    pub fn app(&self, name: &str, args: &[&str]) -> String {
299        if args.is_empty() {
300            return name.to_string();
301        }
302        format!("({} {})", name, args.join(" "))
303    }
304    /// Renders a nested S-expression with indentation.
305    pub fn nested_app(&self, name: &str, args: &[String]) -> String {
306        if args.is_empty() {
307            return name.to_string();
308        }
309        let pad = " ".repeat(self.indent + 2);
310        let inner = args
311            .iter()
312            .map(|a| format!("{}{}", pad, a))
313            .collect::<Vec<_>>()
314            .join("\n");
315        format!("({}\n{})", name, inner)
316    }
317    /// Renders a list.
318    pub fn list(&self, items: &[String]) -> String {
319        format!("[{}]", items.join(", "))
320    }
321}
322/// A simple LRU cache backed by a linked list + hash map.
323#[allow(dead_code)]
324pub struct SimpleLruCache<K: std::hash::Hash + Eq + Clone, V: Clone> {
325    capacity: usize,
326    map: std::collections::HashMap<K, usize>,
327    keys: Vec<K>,
328    vals: Vec<V>,
329    order: Vec<usize>,
330}
331#[allow(dead_code)]
332impl<K: std::hash::Hash + Eq + Clone, V: Clone> SimpleLruCache<K, V> {
333    /// Creates a new LRU cache with the given capacity.
334    pub fn new(capacity: usize) -> Self {
335        assert!(capacity > 0);
336        Self {
337            capacity,
338            map: std::collections::HashMap::new(),
339            keys: Vec::new(),
340            vals: Vec::new(),
341            order: Vec::new(),
342        }
343    }
344    /// Inserts or updates a key-value pair.
345    pub fn put(&mut self, key: K, val: V) {
346        if let Some(&idx) = self.map.get(&key) {
347            self.vals[idx] = val;
348            self.order.retain(|&x| x != idx);
349            self.order.insert(0, idx);
350            return;
351        }
352        if self.keys.len() >= self.capacity {
353            let evict_idx = *self
354                .order
355                .last()
356                .expect("order list must be non-empty before eviction");
357            self.map.remove(&self.keys[evict_idx]);
358            self.order.pop();
359            self.keys[evict_idx] = key.clone();
360            self.vals[evict_idx] = val;
361            self.map.insert(key, evict_idx);
362            self.order.insert(0, evict_idx);
363        } else {
364            let idx = self.keys.len();
365            self.keys.push(key.clone());
366            self.vals.push(val);
367            self.map.insert(key, idx);
368            self.order.insert(0, idx);
369        }
370    }
371    /// Returns a reference to the value for `key`, promoting it.
372    pub fn get(&mut self, key: &K) -> Option<&V> {
373        let idx = *self.map.get(key)?;
374        self.order.retain(|&x| x != idx);
375        self.order.insert(0, idx);
376        Some(&self.vals[idx])
377    }
378    /// Returns the number of entries.
379    pub fn len(&self) -> usize {
380        self.keys.len()
381    }
382    /// Returns `true` if empty.
383    pub fn is_empty(&self) -> bool {
384        self.keys.is_empty()
385    }
386}
387/// An abstract pretty-print document.
388#[allow(dead_code)]
389#[derive(Clone, Debug)]
390pub enum PrettyDoc {
391    /// A literal text string.
392    Text(String),
393    /// A newline followed by indentation.
394    Newline,
395    /// Increase indentation for nested content.
396    Indent(Box<PrettyDoc>),
397    /// Concatenation of two documents.
398    Concat(Box<PrettyDoc>, Box<PrettyDoc>),
399    /// A group: try to fit on one line, break if too long.
400    Group(Box<PrettyDoc>),
401    /// An empty document.
402    Empty,
403}
404#[allow(dead_code)]
405impl PrettyDoc {
406    /// Creates a `Text` document.
407    pub fn text(s: impl Into<String>) -> Self {
408        PrettyDoc::Text(s.into())
409    }
410    /// Creates a `Concat` document.
411    pub fn concat(a: PrettyDoc, b: PrettyDoc) -> Self {
412        PrettyDoc::Concat(Box::new(a), Box::new(b))
413    }
414    /// Creates an `Indent` document.
415    pub fn indent(inner: PrettyDoc) -> Self {
416        PrettyDoc::Indent(Box::new(inner))
417    }
418    /// Creates a `Group` document.
419    pub fn group(inner: PrettyDoc) -> Self {
420        PrettyDoc::Group(Box::new(inner))
421    }
422    /// Renders the document to a `String` using the given line width and indent style.
423    pub fn render(&self, _width: usize, style: IndentStyle) -> String {
424        let mut out = String::new();
425        let mut depth = 0usize;
426        self.render_inner(&mut out, &mut depth, &style);
427        out
428    }
429    fn render_inner(&self, out: &mut String, depth: &mut usize, style: &IndentStyle) {
430        match self {
431            PrettyDoc::Text(s) => out.push_str(s),
432            PrettyDoc::Newline => {
433                out.push('\n');
434                out.push_str(&style.for_depth(*depth));
435            }
436            PrettyDoc::Indent(inner) => {
437                *depth += 1;
438                inner.render_inner(out, depth, style);
439                *depth -= 1;
440            }
441            PrettyDoc::Concat(a, b) => {
442                a.render_inner(out, depth, style);
443                b.render_inner(out, depth, style);
444            }
445            PrettyDoc::Group(inner) => inner.render_inner(out, depth, style),
446            PrettyDoc::Empty => {}
447        }
448    }
449}
450/// A pretty printer that works with `PrettyToken` streams.
451#[allow(dead_code)]
452pub struct TokenPrinter {
453    config: PrettyConfig,
454    scheme: ColorScheme,
455}
456#[allow(dead_code)]
457impl TokenPrinter {
458    /// Creates a new token printer.
459    pub fn new(config: PrettyConfig) -> Self {
460        Self {
461            config,
462            scheme: ColorScheme::MONO,
463        }
464    }
465    /// Sets the color scheme.
466    pub fn with_colors(mut self, scheme: ColorScheme) -> Self {
467        self.scheme = scheme;
468        self
469    }
470    /// Renders a slice of tokens to a `String`.
471    pub fn render(&self, tokens: &[PrettyToken]) -> String {
472        let mut out = String::new();
473        for tok in tokens {
474            if self.config.colored {
475                out.push_str(&tok.colored_text(&self.scheme));
476            } else {
477                out.push_str(tok.raw_text());
478            }
479        }
480        out
481    }
482    /// Renders with line wrapping at `config.width`.
483    pub fn render_wrapped(&self, tokens: &[PrettyToken]) -> String {
484        let mut out = String::new();
485        let mut col = 0usize;
486        for tok in tokens {
487            let text = tok.raw_text();
488            if matches!(tok, PrettyToken::LineBreak) {
489                out.push('\n');
490                col = 0;
491                continue;
492            }
493            if col + text.len() > self.config.width && col > 0 {
494                out.push('\n');
495                col = 0;
496            }
497            out.push_str(text);
498            col += text.len();
499        }
500        out
501    }
502}
503/// Utilities for computing display widths.
504#[allow(dead_code)]
505pub struct FmtWidth;
506#[allow(dead_code)]
507impl FmtWidth {
508    /// Returns the display width of a `usize` in decimal.
509    pub fn decimal_width(n: usize) -> usize {
510        if n == 0 {
511            return 1;
512        }
513        let mut w = 0;
514        let mut v = n;
515        while v > 0 {
516            v /= 10;
517            w += 1;
518        }
519        w
520    }
521    /// Pads `s` on the right with spaces to `width` total chars.
522    pub fn pad_right(s: &str, width: usize) -> String {
523        if s.len() >= width {
524            return s.to_string();
525        }
526        format!("{}{}", s, " ".repeat(width - s.len()))
527    }
528    /// Pads `s` on the left with spaces to `width` total chars.
529    pub fn pad_left(s: &str, width: usize) -> String {
530        if s.len() >= width {
531            return s.to_string();
532        }
533        format!("{}{}", " ".repeat(width - s.len()), s)
534    }
535    /// Centers `s` within `width` chars.
536    pub fn center(s: &str, width: usize) -> String {
537        if s.len() >= width {
538            return s.to_string();
539        }
540        let pad = width - s.len();
541        let left = pad / 2;
542        let right = pad - left;
543        format!("{}{}{}", " ".repeat(left), s, " ".repeat(right))
544    }
545}
546/// An indented document for pretty-printing with line wrapping.
547#[allow(dead_code)]
548#[derive(Clone, Debug)]
549pub enum Doc {
550    /// Empty document.
551    Empty,
552    /// A text fragment.
553    Text(String),
554    /// Append two documents.
555    Concat(Box<Doc>, Box<Doc>),
556    /// A line break; collapses to a space when flattened.
557    Line,
558    /// Indented document.
559    Nest(usize, Box<Doc>),
560    /// Choose between two layouts.
561    Union(Box<Doc>, Box<Doc>),
562}
563#[allow(dead_code)]
564impl Doc {
565    /// Create a text document.
566    pub fn text(s: impl Into<String>) -> Self {
567        Doc::Text(s.into())
568    }
569    /// Append two documents.
570    pub fn concat(self, other: Doc) -> Self {
571        Doc::Concat(Box::new(self), Box::new(other))
572    }
573    /// Add a line break.
574    pub fn line() -> Self {
575        Doc::Line
576    }
577    /// Indent a document by `n` spaces.
578    pub fn nest(n: usize, doc: Doc) -> Self {
579        Doc::Nest(n, Box::new(doc))
580    }
581    /// Render to a string with the given page width.
582    pub fn render(&self, width: usize) -> String {
583        let mut out = String::new();
584        self.render_impl(0, &mut out, width);
585        out
586    }
587    fn render_impl(&self, indent: usize, out: &mut String, _width: usize) {
588        match self {
589            Doc::Empty => {}
590            Doc::Text(s) => out.push_str(s),
591            Doc::Concat(a, b) => {
592                a.render_impl(indent, out, _width);
593                b.render_impl(indent, out, _width);
594            }
595            Doc::Line => {
596                out.push('\n');
597                for _ in 0..indent {
598                    out.push(' ');
599                }
600            }
601            Doc::Nest(n, doc) => {
602                doc.render_impl(indent + n, out, _width);
603            }
604            Doc::Union(a, _b) => {
605                a.render_impl(indent, out, _width);
606            }
607        }
608    }
609}
610/// A simple ASCII table formatter.
611#[allow(dead_code)]
612pub struct PrettyTable {
613    headers: Vec<String>,
614    rows: Vec<Vec<String>>,
615}
616#[allow(dead_code)]
617impl PrettyTable {
618    /// Creates a new table with the given headers.
619    pub fn new(headers: Vec<String>) -> Self {
620        Self {
621            headers,
622            rows: Vec::new(),
623        }
624    }
625    /// Adds a row.  Pads or truncates to match the number of columns.
626    pub fn add_row(&mut self, row: Vec<String>) {
627        let n = self.headers.len();
628        let mut r = row;
629        r.resize(n, String::new());
630        self.rows.push(r);
631    }
632    /// Renders the table as an ASCII grid.
633    pub fn render(&self) -> String {
634        let n = self.headers.len();
635        let mut widths: Vec<usize> = self.headers.iter().map(|h| h.len()).collect();
636        for row in &self.rows {
637            for (i, cell) in row.iter().enumerate() {
638                if i < n {
639                    widths[i] = widths[i].max(cell.len());
640                }
641            }
642        }
643        let sep: String = widths
644            .iter()
645            .map(|&w| "-".repeat(w + 2))
646            .collect::<Vec<_>>()
647            .join("+");
648        let sep = format!("+{}+", sep);
649        let mut out = String::new();
650        out.push_str(&sep);
651        out.push('\n');
652        let header_line: String = self
653            .headers
654            .iter()
655            .enumerate()
656            .map(|(i, h)| FmtWidth::pad_right(h, widths[i]))
657            .collect::<Vec<_>>()
658            .join(" | ");
659        out.push_str(&format!("| {} |", header_line));
660        out.push('\n');
661        out.push_str(&sep);
662        out.push('\n');
663        for row in &self.rows {
664            let line: String = (0..n)
665                .map(|i| {
666                    FmtWidth::pad_right(row.get(i).map(|s| s.as_str()).unwrap_or(""), widths[i])
667                })
668                .collect::<Vec<_>>()
669                .join(" | ");
670            out.push_str(&format!("| {} |", line));
671            out.push('\n');
672        }
673        out.push_str(&sep);
674        out
675    }
676}
677/// A FIFO work queue.
678#[allow(dead_code)]
679pub struct WorkQueue<T> {
680    items: std::collections::VecDeque<T>,
681}
682#[allow(dead_code)]
683impl<T> WorkQueue<T> {
684    /// Creates a new empty queue.
685    pub fn new() -> Self {
686        Self {
687            items: std::collections::VecDeque::new(),
688        }
689    }
690    /// Enqueues a work item.
691    pub fn enqueue(&mut self, item: T) {
692        self.items.push_back(item);
693    }
694    /// Dequeues the next work item.
695    pub fn dequeue(&mut self) -> Option<T> {
696        self.items.pop_front()
697    }
698    /// Returns `true` if empty.
699    pub fn is_empty(&self) -> bool {
700        self.items.is_empty()
701    }
702    /// Returns the number of pending items.
703    pub fn len(&self) -> usize {
704        self.items.len()
705    }
706}
707/// A key-value store for diagnostic metadata.
708#[allow(dead_code)]
709pub struct DiagMeta {
710    pub(super) entries: Vec<(String, String)>,
711}
712#[allow(dead_code)]
713impl DiagMeta {
714    /// Creates an empty metadata store.
715    pub fn new() -> Self {
716        Self {
717            entries: Vec::new(),
718        }
719    }
720    /// Adds a key-value pair.
721    pub fn add(&mut self, key: impl Into<String>, val: impl Into<String>) {
722        self.entries.push((key.into(), val.into()));
723    }
724    /// Returns the value for `key`, or `None`.
725    pub fn get(&self, key: &str) -> Option<&str> {
726        self.entries
727            .iter()
728            .find(|(k, _)| k == key)
729            .map(|(_, v)| v.as_str())
730    }
731    /// Returns the number of entries.
732    pub fn len(&self) -> usize {
733        self.entries.len()
734    }
735    /// Returns `true` if empty.
736    pub fn is_empty(&self) -> bool {
737        self.entries.is_empty()
738    }
739}
740/// A type-safe wrapper around a `u32` identifier.
741#[allow(dead_code)]
742#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
743pub struct TypedId<T> {
744    pub(super) id: u32,
745    _phantom: std::marker::PhantomData<T>,
746}
747#[allow(dead_code)]
748impl<T> TypedId<T> {
749    /// Creates a new typed ID.
750    pub const fn new(id: u32) -> Self {
751        Self {
752            id,
753            _phantom: std::marker::PhantomData,
754        }
755    }
756    /// Returns the raw `u32` ID.
757    pub fn raw(&self) -> u32 {
758        self.id
759    }
760}
761/// Mutable state threaded through a recursive pretty printer.
762#[allow(dead_code)]
763pub struct PrettyPrinterState {
764    output: String,
765    indent_depth: usize,
766    indent_str: String,
767    pub(crate) col: usize,
768    max_width: usize,
769}
770#[allow(dead_code)]
771impl PrettyPrinterState {
772    /// Creates a new printer state.
773    pub fn new(max_width: usize, indent: IndentStyle) -> Self {
774        Self {
775            output: String::new(),
776            indent_depth: 0,
777            indent_str: indent.one_level(),
778            col: 0,
779            max_width,
780        }
781    }
782    /// Writes a string token.
783    pub fn write(&mut self, s: &str) {
784        self.output.push_str(s);
785        self.col += s.len();
786    }
787    /// Writes a newline and current indentation.
788    pub fn newline(&mut self) {
789        self.output.push('\n');
790        let indent = self.indent_str.repeat(self.indent_depth);
791        self.output.push_str(&indent);
792        self.col = indent.len();
793    }
794    /// Increases indentation depth.
795    pub fn push_indent(&mut self) {
796        self.indent_depth += 1;
797    }
798    /// Decreases indentation depth.
799    pub fn pop_indent(&mut self) {
800        if self.indent_depth > 0 {
801            self.indent_depth -= 1;
802        }
803    }
804    /// Returns `true` if the current line is past `max_width`.
805    pub fn over_width(&self) -> bool {
806        self.col > self.max_width
807    }
808    /// Returns the accumulated output.
809    pub fn finish(self) -> String {
810        self.output
811    }
812}
813/// A set of non-overlapping integer intervals.
814#[allow(dead_code)]
815pub struct IntervalSet {
816    intervals: Vec<(i64, i64)>,
817}
818#[allow(dead_code)]
819impl IntervalSet {
820    /// Creates an empty interval set.
821    pub fn new() -> Self {
822        Self {
823            intervals: Vec::new(),
824        }
825    }
826    /// Adds the interval `[lo, hi]` to the set.
827    pub fn add(&mut self, lo: i64, hi: i64) {
828        if lo > hi {
829            return;
830        }
831        let mut new_lo = lo;
832        let mut new_hi = hi;
833        let mut i = 0;
834        while i < self.intervals.len() {
835            let (il, ih) = self.intervals[i];
836            if ih < new_lo - 1 {
837                i += 1;
838                continue;
839            }
840            if il > new_hi + 1 {
841                break;
842            }
843            new_lo = new_lo.min(il);
844            new_hi = new_hi.max(ih);
845            self.intervals.remove(i);
846        }
847        self.intervals.insert(i, (new_lo, new_hi));
848    }
849    /// Returns `true` if `x` is in the set.
850    pub fn contains(&self, x: i64) -> bool {
851        self.intervals.iter().any(|&(lo, hi)| x >= lo && x <= hi)
852    }
853    /// Returns the number of intervals.
854    pub fn num_intervals(&self) -> usize {
855        self.intervals.len()
856    }
857    /// Returns the total count of integers covered.
858    pub fn cardinality(&self) -> i64 {
859        self.intervals.iter().map(|&(lo, hi)| hi - lo + 1).sum()
860    }
861}
862/// A counter that dispenses monotonically increasing `TypedId` values.
863#[allow(dead_code)]
864pub struct IdDispenser<T> {
865    next: u32,
866    _phantom: std::marker::PhantomData<T>,
867}
868#[allow(dead_code)]
869impl<T> IdDispenser<T> {
870    /// Creates a new dispenser starting from zero.
871    pub fn new() -> Self {
872        Self {
873            next: 0,
874            _phantom: std::marker::PhantomData,
875        }
876    }
877    /// Dispenses the next ID.
878    #[allow(clippy::should_implement_trait)]
879    pub fn next(&mut self) -> TypedId<T> {
880        let id = TypedId::new(self.next);
881        self.next += 1;
882        id
883    }
884    /// Returns the number of IDs dispensed.
885    pub fn count(&self) -> u32 {
886        self.next
887    }
888}
889/// A key-value annotation table for arbitrary metadata.
890#[allow(dead_code)]
891pub struct AnnotationTable {
892    map: std::collections::HashMap<String, Vec<String>>,
893}
894#[allow(dead_code)]
895impl AnnotationTable {
896    /// Creates an empty annotation table.
897    pub fn new() -> Self {
898        Self {
899            map: std::collections::HashMap::new(),
900        }
901    }
902    /// Adds an annotation value for the given key.
903    pub fn annotate(&mut self, key: impl Into<String>, val: impl Into<String>) {
904        self.map.entry(key.into()).or_default().push(val.into());
905    }
906    /// Returns all annotations for `key`.
907    pub fn get_all(&self, key: &str) -> &[String] {
908        self.map.get(key).map(|v| v.as_slice()).unwrap_or(&[])
909    }
910    /// Returns the number of distinct annotation keys.
911    pub fn num_keys(&self) -> usize {
912        self.map.len()
913    }
914    /// Returns `true` if the table has any annotations for `key`.
915    pub fn has(&self, key: &str) -> bool {
916        self.map.contains_key(key)
917    }
918}
919/// Tracks the frequency of items.
920#[allow(dead_code)]
921pub struct FrequencyTable<T: std::hash::Hash + Eq + Clone> {
922    counts: std::collections::HashMap<T, u64>,
923}
924#[allow(dead_code)]
925impl<T: std::hash::Hash + Eq + Clone> FrequencyTable<T> {
926    /// Creates a new empty frequency table.
927    pub fn new() -> Self {
928        Self {
929            counts: std::collections::HashMap::new(),
930        }
931    }
932    /// Records one occurrence of `item`.
933    pub fn record(&mut self, item: T) {
934        *self.counts.entry(item).or_insert(0) += 1;
935    }
936    /// Returns the frequency of `item`.
937    pub fn freq(&self, item: &T) -> u64 {
938        self.counts.get(item).copied().unwrap_or(0)
939    }
940    /// Returns the item with the highest frequency.
941    pub fn most_frequent(&self) -> Option<(&T, u64)> {
942        self.counts
943            .iter()
944            .max_by_key(|(_, &v)| v)
945            .map(|(k, &v)| (k, v))
946    }
947    /// Returns the total number of recordings.
948    pub fn total(&self) -> u64 {
949        self.counts.values().sum()
950    }
951    /// Returns the number of distinct items.
952    pub fn distinct(&self) -> usize {
953        self.counts.len()
954    }
955}
956/// A builder for `PrettyDoc` values with a fluent API.
957#[allow(dead_code)]
958pub struct DocBuilder {
959    doc: PrettyDoc,
960}
961#[allow(dead_code)]
962impl DocBuilder {
963    /// Creates a builder from an existing doc.
964    pub fn from(doc: PrettyDoc) -> Self {
965        Self { doc }
966    }
967    /// Creates a text builder.
968    pub fn text(s: impl Into<String>) -> Self {
969        Self {
970            doc: PrettyDoc::text(s),
971        }
972    }
973    /// Creates an empty builder.
974    pub fn empty() -> Self {
975        Self {
976            doc: PrettyDoc::Empty,
977        }
978    }
979    /// Appends another document.
980    pub fn then(self, other: PrettyDoc) -> Self {
981        Self {
982            doc: PrettyDoc::concat(self.doc, other),
983        }
984    }
985    /// Appends a text string.
986    pub fn then_text(self, s: impl Into<String>) -> Self {
987        self.then(PrettyDoc::text(s))
988    }
989    /// Appends a newline.
990    pub fn then_newline(self) -> Self {
991        self.then(PrettyDoc::Newline)
992    }
993    /// Wraps in an indent.
994    pub fn indented(self) -> Self {
995        Self {
996            doc: PrettyDoc::indent(self.doc),
997        }
998    }
999    /// Returns the built document.
1000    pub fn build(self) -> PrettyDoc {
1001        self.doc
1002    }
1003}
1004/// A simple LIFO work queue.
1005#[allow(dead_code)]
1006pub struct WorkStack<T> {
1007    items: Vec<T>,
1008}
1009#[allow(dead_code)]
1010impl<T> WorkStack<T> {
1011    /// Creates a new empty stack.
1012    pub fn new() -> Self {
1013        Self { items: Vec::new() }
1014    }
1015    /// Pushes a work item.
1016    pub fn push(&mut self, item: T) {
1017        self.items.push(item);
1018    }
1019    /// Pops the next work item.
1020    pub fn pop(&mut self) -> Option<T> {
1021        self.items.pop()
1022    }
1023    /// Returns `true` if empty.
1024    pub fn is_empty(&self) -> bool {
1025        self.items.is_empty()
1026    }
1027    /// Returns the number of pending work items.
1028    pub fn len(&self) -> usize {
1029        self.items.len()
1030    }
1031}
1032/// A memoized computation slot that stores a cached value.
1033#[allow(dead_code)]
1034pub struct MemoSlot<T: Clone> {
1035    cached: Option<T>,
1036}
1037#[allow(dead_code)]
1038impl<T: Clone> MemoSlot<T> {
1039    /// Creates an uncomputed memo slot.
1040    pub fn new() -> Self {
1041        Self { cached: None }
1042    }
1043    /// Returns the cached value, computing it with `f` if absent.
1044    pub fn get_or_compute(&mut self, f: impl FnOnce() -> T) -> &T {
1045        if self.cached.is_none() {
1046            self.cached = Some(f());
1047        }
1048        self.cached
1049            .as_ref()
1050            .expect("cached value must be initialized before access")
1051    }
1052    /// Invalidates the cached value.
1053    pub fn invalidate(&mut self) {
1054        self.cached = None;
1055    }
1056    /// Returns `true` if the value has been computed.
1057    pub fn is_cached(&self) -> bool {
1058        self.cached.is_some()
1059    }
1060}
1061/// A counted-access cache that tracks hit and miss statistics.
1062#[allow(dead_code)]
1063#[allow(missing_docs)]
1064pub struct StatCache<K: std::hash::Hash + Eq + Clone, V: Clone> {
1065    /// The inner LRU cache.
1066    pub inner: SimpleLruCache<K, V>,
1067    /// Number of cache hits.
1068    pub hits: u64,
1069    /// Number of cache misses.
1070    pub misses: u64,
1071}
1072#[allow(dead_code)]
1073impl<K: std::hash::Hash + Eq + Clone, V: Clone> StatCache<K, V> {
1074    /// Creates a new stat cache with the given capacity.
1075    pub fn new(capacity: usize) -> Self {
1076        Self {
1077            inner: SimpleLruCache::new(capacity),
1078            hits: 0,
1079            misses: 0,
1080        }
1081    }
1082    /// Performs a lookup, tracking hit/miss.
1083    pub fn get(&mut self, key: &K) -> Option<&V> {
1084        let result = self.inner.get(key);
1085        if result.is_some() {
1086            self.hits += 1;
1087        } else {
1088            self.misses += 1;
1089        }
1090        None
1091    }
1092    /// Inserts a key-value pair.
1093    pub fn put(&mut self, key: K, val: V) {
1094        self.inner.put(key, val);
1095    }
1096    /// Returns the hit rate.
1097    pub fn hit_rate(&self) -> f64 {
1098        let total = self.hits + self.misses;
1099        if total == 0 {
1100            return 0.0;
1101        }
1102        self.hits as f64 / total as f64
1103    }
1104}
1105/// Helpers for string escaping in pretty output.
1106#[allow(dead_code)]
1107pub struct EscapeHelper;
1108#[allow(dead_code)]
1109impl EscapeHelper {
1110    /// Escapes `\n`, `\t`, `\\`, and `"` in `s`.
1111    pub fn escape_str(s: &str) -> String {
1112        let mut out = String::with_capacity(s.len() + 2);
1113        out.push('"');
1114        for c in s.chars() {
1115            match c {
1116                '"' => out.push_str("\\\""),
1117                '\\' => out.push_str("\\\\"),
1118                '\n' => out.push_str("\\n"),
1119                '\t' => out.push_str("\\t"),
1120                _ => out.push(c),
1121            }
1122        }
1123        out.push('"');
1124        out
1125    }
1126    /// Unescapes a string that may contain `\\n`, `\\t`, `\\\\`, `\\"`.
1127    pub fn unescape_str(s: &str) -> String {
1128        let s = if s.starts_with('"') && s.ends_with('"') && s.len() >= 2 {
1129            &s[1..s.len() - 1]
1130        } else {
1131            s
1132        };
1133        let mut out = String::new();
1134        let mut chars = s.chars().peekable();
1135        while let Some(c) = chars.next() {
1136            if c == '\\' {
1137                match chars.next() {
1138                    Some('n') => out.push('\n'),
1139                    Some('t') => out.push('\t'),
1140                    Some('\\') => out.push('\\'),
1141                    Some('"') => out.push('"'),
1142                    Some(x) => {
1143                        out.push('\\');
1144                        out.push(x);
1145                    }
1146                    None => out.push('\\'),
1147                }
1148            } else {
1149                out.push(c);
1150            }
1151        }
1152        out
1153    }
1154}
1155/// A simple event counter with named events.
1156#[allow(dead_code)]
1157pub struct EventCounter {
1158    counts: std::collections::HashMap<String, u64>,
1159}
1160#[allow(dead_code)]
1161impl EventCounter {
1162    /// Creates a new empty counter.
1163    pub fn new() -> Self {
1164        Self {
1165            counts: std::collections::HashMap::new(),
1166        }
1167    }
1168    /// Increments the counter for `event`.
1169    pub fn inc(&mut self, event: &str) {
1170        *self.counts.entry(event.to_string()).or_insert(0) += 1;
1171    }
1172    /// Adds `n` to the counter for `event`.
1173    pub fn add(&mut self, event: &str, n: u64) {
1174        *self.counts.entry(event.to_string()).or_insert(0) += n;
1175    }
1176    /// Returns the count for `event`.
1177    pub fn get(&self, event: &str) -> u64 {
1178        self.counts.get(event).copied().unwrap_or(0)
1179    }
1180    /// Returns the total count across all events.
1181    pub fn total(&self) -> u64 {
1182        self.counts.values().sum()
1183    }
1184    /// Resets all counters.
1185    pub fn reset(&mut self) {
1186        self.counts.clear();
1187    }
1188    /// Returns all event names.
1189    pub fn event_names(&self) -> Vec<&str> {
1190        self.counts.keys().map(|s| s.as_str()).collect()
1191    }
1192}
1193/// A bidirectional map between two types.
1194#[allow(dead_code)]
1195pub struct BiMap<A: std::hash::Hash + Eq + Clone, B: std::hash::Hash + Eq + Clone> {
1196    forward: std::collections::HashMap<A, B>,
1197    backward: std::collections::HashMap<B, A>,
1198}
1199#[allow(dead_code)]
1200impl<A: std::hash::Hash + Eq + Clone, B: std::hash::Hash + Eq + Clone> BiMap<A, B> {
1201    /// Creates an empty bidirectional map.
1202    pub fn new() -> Self {
1203        Self {
1204            forward: std::collections::HashMap::new(),
1205            backward: std::collections::HashMap::new(),
1206        }
1207    }
1208    /// Inserts a pair `(a, b)`.
1209    pub fn insert(&mut self, a: A, b: B) {
1210        self.forward.insert(a.clone(), b.clone());
1211        self.backward.insert(b, a);
1212    }
1213    /// Looks up `b` for a given `a`.
1214    pub fn get_b(&self, a: &A) -> Option<&B> {
1215        self.forward.get(a)
1216    }
1217    /// Looks up `a` for a given `b`.
1218    pub fn get_a(&self, b: &B) -> Option<&A> {
1219        self.backward.get(b)
1220    }
1221    /// Returns the number of pairs.
1222    pub fn len(&self) -> usize {
1223        self.forward.len()
1224    }
1225    /// Returns `true` if empty.
1226    pub fn is_empty(&self) -> bool {
1227        self.forward.is_empty()
1228    }
1229}
1230/// A slot that can hold a value, with lazy initialization.
1231#[allow(dead_code)]
1232pub struct Slot<T> {
1233    inner: Option<T>,
1234}
1235#[allow(dead_code)]
1236impl<T> Slot<T> {
1237    /// Creates an empty slot.
1238    pub fn empty() -> Self {
1239        Self { inner: None }
1240    }
1241    /// Fills the slot with `val`.  Panics if already filled.
1242    pub fn fill(&mut self, val: T) {
1243        assert!(self.inner.is_none(), "Slot: already filled");
1244        self.inner = Some(val);
1245    }
1246    /// Returns the slot's value, or `None`.
1247    pub fn get(&self) -> Option<&T> {
1248        self.inner.as_ref()
1249    }
1250    /// Returns `true` if the slot is filled.
1251    pub fn is_filled(&self) -> bool {
1252        self.inner.is_some()
1253    }
1254    /// Takes the value out of the slot.
1255    pub fn take(&mut self) -> Option<T> {
1256        self.inner.take()
1257    }
1258    /// Fills the slot if empty, returning a reference to the value.
1259    pub fn get_or_fill_with(&mut self, f: impl FnOnce() -> T) -> &T {
1260        if self.inner.is_none() {
1261            self.inner = Some(f());
1262        }
1263        self.inner
1264            .as_ref()
1265            .expect("inner value must be initialized before access")
1266    }
1267}
1268/// Interns strings to save memory (each unique string stored once).
1269#[allow(dead_code)]
1270pub struct StringInterner {
1271    strings: Vec<String>,
1272    map: std::collections::HashMap<String, u32>,
1273}
1274#[allow(dead_code)]
1275impl StringInterner {
1276    /// Creates a new string interner.
1277    pub fn new() -> Self {
1278        Self {
1279            strings: Vec::new(),
1280            map: std::collections::HashMap::new(),
1281        }
1282    }
1283    /// Interns `s` and returns its ID.
1284    pub fn intern(&mut self, s: &str) -> u32 {
1285        if let Some(&id) = self.map.get(s) {
1286            return id;
1287        }
1288        let id = self.strings.len() as u32;
1289        self.strings.push(s.to_string());
1290        self.map.insert(s.to_string(), id);
1291        id
1292    }
1293    /// Returns the string for `id`.
1294    pub fn get(&self, id: u32) -> Option<&str> {
1295        self.strings.get(id as usize).map(|s| s.as_str())
1296    }
1297    /// Returns the total number of interned strings.
1298    pub fn len(&self) -> usize {
1299        self.strings.len()
1300    }
1301    /// Returns `true` if empty.
1302    pub fn is_empty(&self) -> bool {
1303        self.strings.is_empty()
1304    }
1305}
1306/// Implements Wadler/Lindig-style pretty printing using a box model.
1307#[allow(dead_code)]
1308pub struct BoxedPrinter {
1309    width: usize,
1310}
1311#[allow(dead_code)]
1312impl BoxedPrinter {
1313    /// Creates a printer with the given line width.
1314    pub fn new(width: usize) -> Self {
1315        Self { width }
1316    }
1317    /// Renders a `PrettyDoc` to a `String`.
1318    pub fn render(&self, doc: &PrettyDoc) -> String {
1319        doc.render(self.width, IndentStyle::Spaces(2))
1320    }
1321}
1322/// Configuration for pretty printing.
1323#[derive(Debug, Clone)]
1324pub struct PrintConfig {
1325    /// Use unicode symbols (λ, ∀, →)
1326    pub unicode: bool,
1327    /// Show implicit arguments
1328    pub show_implicit: bool,
1329    /// Show universe levels
1330    pub show_universes: bool,
1331    /// Maximum line width for wrapping
1332    pub max_width: usize,
1333    /// Show binder info (default, implicit, etc.)
1334    pub show_binder_info: bool,
1335    /// Show de Bruijn indices
1336    pub show_indices: bool,
1337}
1338impl PrintConfig {
1339    /// ASCII-only configuration.
1340    pub fn ascii() -> Self {
1341        Self {
1342            unicode: false,
1343            ..Default::default()
1344        }
1345    }
1346    /// Verbose configuration showing everything.
1347    pub fn verbose() -> Self {
1348        Self {
1349            show_implicit: true,
1350            show_universes: true,
1351            show_binder_info: true,
1352            show_indices: true,
1353            ..Default::default()
1354        }
1355    }
1356}
1357/// Configuration for the pretty printer.
1358#[allow(dead_code)]
1359#[allow(missing_docs)]
1360pub struct PrettyConfig {
1361    /// Maximum line width.
1362    pub width: usize,
1363    /// Indentation style.
1364    pub indent: IndentStyle,
1365    /// Whether to use color.
1366    pub colored: bool,
1367}
1368#[allow(dead_code)]
1369impl PrettyConfig {
1370    /// Default config: 80 columns, 2-space indent, no color.
1371    pub fn default_config() -> Self {
1372        Self {
1373            width: 80,
1374            indent: IndentStyle::Spaces(2),
1375            colored: false,
1376        }
1377    }
1378    /// Creates a compact single-line config (no newlines honoured).
1379    pub fn compact() -> Self {
1380        Self {
1381            width: usize::MAX,
1382            indent: IndentStyle::Spaces(0),
1383            colored: false,
1384        }
1385    }
1386}
1387/// A simple sparse bit set.
1388#[allow(dead_code)]
1389pub struct SparseBitSet {
1390    words: Vec<u64>,
1391}
1392#[allow(dead_code)]
1393impl SparseBitSet {
1394    /// Creates a new bit set that can hold at least `capacity` bits.
1395    pub fn new(capacity: usize) -> Self {
1396        let words = (capacity + 63) / 64;
1397        Self {
1398            words: vec![0u64; words],
1399        }
1400    }
1401    /// Sets bit `i`.
1402    pub fn set(&mut self, i: usize) {
1403        let word = i / 64;
1404        let bit = i % 64;
1405        if word < self.words.len() {
1406            self.words[word] |= 1u64 << bit;
1407        }
1408    }
1409    /// Clears bit `i`.
1410    pub fn clear(&mut self, i: usize) {
1411        let word = i / 64;
1412        let bit = i % 64;
1413        if word < self.words.len() {
1414            self.words[word] &= !(1u64 << bit);
1415        }
1416    }
1417    /// Returns `true` if bit `i` is set.
1418    pub fn get(&self, i: usize) -> bool {
1419        let word = i / 64;
1420        let bit = i % 64;
1421        self.words.get(word).is_some_and(|w| w & (1u64 << bit) != 0)
1422    }
1423    /// Returns the number of set bits.
1424    pub fn count_ones(&self) -> u32 {
1425        self.words.iter().map(|w| w.count_ones()).sum()
1426    }
1427    /// Returns the union with another bit set.
1428    pub fn union(&self, other: &SparseBitSet) -> SparseBitSet {
1429        let len = self.words.len().max(other.words.len());
1430        let mut result = SparseBitSet {
1431            words: vec![0u64; len],
1432        };
1433        for i in 0..self.words.len() {
1434            result.words[i] |= self.words[i];
1435        }
1436        for i in 0..other.words.len() {
1437            result.words[i] |= other.words[i];
1438        }
1439        result
1440    }
1441}
1442/// A simple stack-based scope tracker.
1443#[allow(dead_code)]
1444pub struct ScopeStack {
1445    names: Vec<String>,
1446}
1447#[allow(dead_code)]
1448impl ScopeStack {
1449    /// Creates a new empty scope stack.
1450    pub fn new() -> Self {
1451        Self { names: Vec::new() }
1452    }
1453    /// Pushes a scope name.
1454    pub fn push(&mut self, name: impl Into<String>) {
1455        self.names.push(name.into());
1456    }
1457    /// Pops the current scope.
1458    pub fn pop(&mut self) -> Option<String> {
1459        self.names.pop()
1460    }
1461    /// Returns the current (innermost) scope name, or `None`.
1462    pub fn current(&self) -> Option<&str> {
1463        self.names.last().map(|s| s.as_str())
1464    }
1465    /// Returns the depth of the scope stack.
1466    pub fn depth(&self) -> usize {
1467        self.names.len()
1468    }
1469    /// Returns `true` if the stack is empty.
1470    pub fn is_empty(&self) -> bool {
1471        self.names.is_empty()
1472    }
1473    /// Returns the full path as a dot-separated string.
1474    pub fn path(&self) -> String {
1475        self.names.join(".")
1476    }
1477}
1478/// A monotonic timestamp in microseconds.
1479#[allow(dead_code)]
1480#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
1481pub struct Timestamp(u64);
1482#[allow(dead_code)]
1483impl Timestamp {
1484    /// Creates a timestamp from microseconds.
1485    pub const fn from_us(us: u64) -> Self {
1486        Self(us)
1487    }
1488    /// Returns the timestamp in microseconds.
1489    pub fn as_us(self) -> u64 {
1490        self.0
1491    }
1492    /// Returns the duration between two timestamps.
1493    pub fn elapsed_since(self, earlier: Timestamp) -> u64 {
1494        self.0.saturating_sub(earlier.0)
1495    }
1496}
1497/// A clock that measures elapsed time in a loop.
1498#[allow(dead_code)]
1499pub struct LoopClock {
1500    start: std::time::Instant,
1501    iters: u64,
1502}
1503#[allow(dead_code)]
1504impl LoopClock {
1505    /// Starts the clock.
1506    pub fn start() -> Self {
1507        Self {
1508            start: std::time::Instant::now(),
1509            iters: 0,
1510        }
1511    }
1512    /// Records one iteration.
1513    pub fn tick(&mut self) {
1514        self.iters += 1;
1515    }
1516    /// Returns the elapsed time in microseconds.
1517    pub fn elapsed_us(&self) -> f64 {
1518        self.start.elapsed().as_secs_f64() * 1e6
1519    }
1520    /// Returns the average microseconds per iteration.
1521    pub fn avg_us_per_iter(&self) -> f64 {
1522        if self.iters == 0 {
1523            return 0.0;
1524        }
1525        self.elapsed_us() / self.iters as f64
1526    }
1527    /// Returns the number of iterations.
1528    pub fn iters(&self) -> u64 {
1529        self.iters
1530    }
1531}
1532/// A stream of pretty-printable tokens.
1533#[allow(dead_code)]
1534#[derive(Clone, Debug)]
1535pub enum PrettyToken {
1536    /// A keyword.
1537    Keyword(String),
1538    /// An identifier.
1539    Ident(String),
1540    /// A literal (number, string, etc.).
1541    Literal(String),
1542    /// An operator or punctuation.
1543    Operator(String),
1544    /// Whitespace.
1545    Space,
1546    /// A line break.
1547    LineBreak,
1548}
1549#[allow(dead_code)]
1550impl PrettyToken {
1551    /// Returns the raw text of the token (no color).
1552    pub fn raw_text(&self) -> &str {
1553        match self {
1554            PrettyToken::Keyword(s) => s.as_str(),
1555            PrettyToken::Ident(s) => s.as_str(),
1556            PrettyToken::Literal(s) => s.as_str(),
1557            PrettyToken::Operator(s) => s.as_str(),
1558            PrettyToken::Space => " ",
1559            PrettyToken::LineBreak => "\n",
1560        }
1561    }
1562    /// Returns colored text using the given color scheme.
1563    pub fn colored_text(&self, cs: &ColorScheme) -> String {
1564        match self {
1565            PrettyToken::Keyword(s) => format!("{}{}{}", cs.keyword, s, cs.reset),
1566            PrettyToken::Ident(s) => format!("{}{}{}", cs.ident, s, cs.reset),
1567            PrettyToken::Literal(s) => format!("{}{}{}", cs.literal, s, cs.reset),
1568            PrettyToken::Operator(s) => format!("{}{}{}", cs.operator, s, cs.reset),
1569            PrettyToken::Space => " ".to_string(),
1570            PrettyToken::LineBreak => "\n".to_string(),
1571        }
1572    }
1573}
1574/// Describes the indentation style used by the pretty printer.
1575#[allow(dead_code)]
1576#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1577pub enum IndentStyle {
1578    /// Indent using `n` space characters.
1579    Spaces(usize),
1580    /// Indent using a single tab character.
1581    Tabs,
1582}
1583#[allow(dead_code)]
1584impl IndentStyle {
1585    /// Returns the indentation string for one level.
1586    pub fn one_level(self) -> String {
1587        match self {
1588            IndentStyle::Spaces(n) => " ".repeat(n),
1589            IndentStyle::Tabs => "\t".to_string(),
1590        }
1591    }
1592    /// Returns the indentation string for `depth` levels.
1593    pub fn for_depth(self, depth: usize) -> String {
1594        self.one_level().repeat(depth)
1595    }
1596}
1597/// A set of ANSI color codes for syntax-highlighted pretty printing.
1598#[allow(dead_code)]
1599pub struct ColorScheme {
1600    /// Color for keywords.
1601    pub keyword: &'static str,
1602    /// Color for identifiers.
1603    pub ident: &'static str,
1604    /// Color for literals.
1605    pub literal: &'static str,
1606    /// Color for operators.
1607    pub operator: &'static str,
1608    /// Reset color.
1609    pub reset: &'static str,
1610}
1611#[allow(dead_code)]
1612impl ColorScheme {
1613    /// The default ANSI color scheme.
1614    pub const DEFAULT: ColorScheme = ColorScheme {
1615        keyword: "\x1b[34m",
1616        ident: "\x1b[0m",
1617        literal: "\x1b[32m",
1618        operator: "\x1b[33m",
1619        reset: "\x1b[0m",
1620    };
1621    /// A monochrome (no color) scheme.
1622    pub const MONO: ColorScheme = ColorScheme {
1623        keyword: "",
1624        ident: "",
1625        literal: "",
1626        operator: "",
1627        reset: "",
1628    };
1629    /// Returns `true` if this is a colored scheme.
1630    pub fn is_colored(&self) -> bool {
1631        !self.keyword.is_empty()
1632    }
1633}