use std::collections::{HashMap, VecDeque};
use std::fmt;
use std::fmt::Write as FmtWrite;
use super::functions::*;
use crate::{BinderInfo, Expr, Level, Name};
pub struct ExprPrinter {
buffer: String,
pub(super) config: PrintConfig,
}
impl ExprPrinter {
pub fn new() -> Self {
Self {
buffer: String::new(),
config: PrintConfig::default(),
}
}
pub fn with_config(config: PrintConfig) -> Self {
Self {
buffer: String::new(),
config,
}
}
pub fn with_unicode(mut self, unicode: bool) -> Self {
self.config.unicode = unicode;
self
}
pub fn output(self) -> String {
self.buffer
}
pub fn print(&mut self, expr: &Expr) -> fmt::Result {
self.print_expr(expr, 0)
}
fn print_expr(&mut self, expr: &Expr, prec: u32) -> fmt::Result {
match expr {
Expr::Sort(level) => self.print_sort(level),
Expr::BVar(idx) => {
if self.config.show_indices {
write!(self.buffer, "#{}", idx)
} else {
write!(self.buffer, "_")
}
}
Expr::FVar(fvar) => write!(self.buffer, "@{}", fvar.0),
Expr::Const(name, levels) => {
self.print_name(name)?;
if self.config.show_universes && !levels.is_empty() {
write!(self.buffer, ".{{")?;
for (i, level) in levels.iter().enumerate() {
if i > 0 {
write!(self.buffer, ", ")?;
}
self.print_level(level)?;
}
write!(self.buffer, "}}")?;
}
Ok(())
}
Expr::App(fun, arg) => {
let (head, args) = collect_app_args(expr);
if args.len() > 1 {
if prec > 10 {
write!(self.buffer, "(")?;
}
self.print_expr(head, 10)?;
for a in &args {
write!(self.buffer, " ")?;
self.print_expr(a, 11)?;
}
if prec > 10 {
write!(self.buffer, ")")?;
}
Ok(())
} else {
if prec > 10 {
write!(self.buffer, "(")?;
}
self.print_expr(fun, 10)?;
write!(self.buffer, " ")?;
self.print_expr(arg, 11)?;
if prec > 10 {
write!(self.buffer, ")")?;
}
Ok(())
}
}
Expr::Lam(bi, name, ty, body) => {
if prec > 0 {
write!(self.buffer, "(")?;
}
if self.config.unicode {
write!(self.buffer, "λ ")?;
} else {
write!(self.buffer, "fun ")?;
}
self.print_binder(*bi, name, ty)?;
let mut current_body = body.as_ref();
while let Expr::Lam(bi2, name2, ty2, body2) = current_body {
write!(self.buffer, " ")?;
self.print_binder(*bi2, name2, ty2)?;
current_body = body2;
}
if self.config.unicode {
write!(self.buffer, ", ")?;
} else {
write!(self.buffer, " => ")?;
}
self.print_expr(current_body, 0)?;
if prec > 0 {
write!(self.buffer, ")")?;
}
Ok(())
}
Expr::Pi(bi, name, ty, body) => {
if prec > 0 {
write!(self.buffer, "(")?;
}
if name.is_anonymous() || *name == Name::str("_") {
self.print_expr(ty, 25)?;
if self.config.unicode {
write!(self.buffer, " → ")?;
} else {
write!(self.buffer, " -> ")?;
}
self.print_expr(body, 24)?;
} else {
if self.config.unicode {
write!(self.buffer, "∀ ")?;
} else {
write!(self.buffer, "forall ")?;
}
self.print_binder(*bi, name, ty)?;
let mut current_body = body.as_ref();
while let Expr::Pi(bi2, name2, ty2, body2) = current_body {
if !name2.is_anonymous() && *name2 != Name::str("_") {
write!(self.buffer, " ")?;
self.print_binder(*bi2, name2, ty2)?;
current_body = body2;
} else {
break;
}
}
write!(self.buffer, ", ")?;
self.print_expr(current_body, 0)?;
}
if prec > 0 {
write!(self.buffer, ")")?;
}
Ok(())
}
Expr::Let(name, ty, val, body) => {
write!(self.buffer, "let ")?;
self.print_name(name)?;
write!(self.buffer, " : ")?;
self.print_expr(ty, 0)?;
write!(self.buffer, " := ")?;
self.print_expr(val, 0)?;
write!(self.buffer, " in ")?;
self.print_expr(body, 0)
}
Expr::Lit(lit) => write!(self.buffer, "{}", lit),
Expr::Proj(name, idx, expr) => {
self.print_expr(expr, 11)?;
write!(self.buffer, ".{}.{}", name, idx)
}
}
}
fn print_binder(&mut self, bi: BinderInfo, name: &Name, ty: &Expr) -> fmt::Result {
if self.config.show_binder_info {
match bi {
BinderInfo::Default => {
write!(self.buffer, "(")?;
self.print_name(name)?;
write!(self.buffer, " : ")?;
self.print_expr(ty, 0)?;
write!(self.buffer, ")")
}
BinderInfo::Implicit => {
write!(self.buffer, "{{")?;
self.print_name(name)?;
write!(self.buffer, " : ")?;
self.print_expr(ty, 0)?;
write!(self.buffer, "}}")
}
BinderInfo::StrictImplicit => {
if self.config.unicode {
write!(self.buffer, "\u{2983}")?;
} else {
write!(self.buffer, "{{{{")?;
}
self.print_name(name)?;
write!(self.buffer, " : ")?;
self.print_expr(ty, 0)?;
if self.config.unicode {
write!(self.buffer, "\u{2984}")
} else {
write!(self.buffer, "}}}}")
}
}
BinderInfo::InstImplicit => {
write!(self.buffer, "[")?;
self.print_name(name)?;
write!(self.buffer, " : ")?;
self.print_expr(ty, 0)?;
write!(self.buffer, "]")
}
}
} else {
self.print_name(name)?;
write!(self.buffer, " : ")?;
self.print_expr(ty, 0)
}
}
fn print_sort(&mut self, level: &Level) -> fmt::Result {
match level {
Level::Zero => write!(self.buffer, "Prop"),
_ => {
if let Some(n) = level_to_nat(level) {
if n == 1 {
write!(self.buffer, "Type")
} else {
write!(self.buffer, "Type {}", n - 1)
}
} else {
write!(self.buffer, "Sort ")?;
self.print_level(level)
}
}
}
}
pub(super) fn print_level(&mut self, level: &Level) -> fmt::Result {
match level {
Level::Zero => write!(self.buffer, "0"),
Level::Succ(_) => {
if let Some(n) = level_to_nat(level) {
write!(self.buffer, "{}", n)
} else {
let (base, offset) = level_to_offset(level);
if offset > 0 {
self.print_level(base)?;
write!(self.buffer, "+{}", offset)
} else {
write!(self.buffer, "(")?;
self.print_level(level)?;
write!(self.buffer, ")")
}
}
}
Level::Max(l1, l2) => {
write!(self.buffer, "max(")?;
self.print_level(l1)?;
write!(self.buffer, ", ")?;
self.print_level(l2)?;
write!(self.buffer, ")")
}
Level::IMax(l1, l2) => {
write!(self.buffer, "imax(")?;
self.print_level(l1)?;
write!(self.buffer, ", ")?;
self.print_level(l2)?;
write!(self.buffer, ")")
}
Level::Param(name) => self.print_name(name),
Level::MVar(id) => write!(self.buffer, "?u_{}", id.0),
}
}
fn print_name(&mut self, name: &Name) -> fmt::Result {
write!(self.buffer, "{}", name)
}
}
#[allow(dead_code)]
pub struct SExprPrinter {
indent: usize,
}
#[allow(dead_code)]
impl SExprPrinter {
pub fn new() -> Self {
Self { indent: 0 }
}
pub fn app(&self, name: &str, args: &[&str]) -> String {
if args.is_empty() {
return name.to_string();
}
format!("({} {})", name, args.join(" "))
}
pub fn nested_app(&self, name: &str, args: &[String]) -> String {
if args.is_empty() {
return name.to_string();
}
let pad = " ".repeat(self.indent + 2);
let inner = args
.iter()
.map(|a| format!("{}{}", pad, a))
.collect::<Vec<_>>()
.join("\n");
format!("({}\n{})", name, inner)
}
pub fn list(&self, items: &[String]) -> String {
format!("[{}]", items.join(", "))
}
}
#[allow(dead_code)]
pub struct SimpleLruCache<K: std::hash::Hash + Eq + Clone, V: Clone> {
capacity: usize,
map: std::collections::HashMap<K, usize>,
keys: Vec<K>,
vals: Vec<V>,
order: Vec<usize>,
}
#[allow(dead_code)]
impl<K: std::hash::Hash + Eq + Clone, V: Clone> SimpleLruCache<K, V> {
pub fn new(capacity: usize) -> Self {
assert!(capacity > 0);
Self {
capacity,
map: std::collections::HashMap::new(),
keys: Vec::new(),
vals: Vec::new(),
order: Vec::new(),
}
}
pub fn put(&mut self, key: K, val: V) {
if let Some(&idx) = self.map.get(&key) {
self.vals[idx] = val;
self.order.retain(|&x| x != idx);
self.order.insert(0, idx);
return;
}
if self.keys.len() >= self.capacity {
let evict_idx = *self
.order
.last()
.expect("order list must be non-empty before eviction");
self.map.remove(&self.keys[evict_idx]);
self.order.pop();
self.keys[evict_idx] = key.clone();
self.vals[evict_idx] = val;
self.map.insert(key, evict_idx);
self.order.insert(0, evict_idx);
} else {
let idx = self.keys.len();
self.keys.push(key.clone());
self.vals.push(val);
self.map.insert(key, idx);
self.order.insert(0, idx);
}
}
pub fn get(&mut self, key: &K) -> Option<&V> {
let idx = *self.map.get(key)?;
self.order.retain(|&x| x != idx);
self.order.insert(0, idx);
Some(&self.vals[idx])
}
pub fn len(&self) -> usize {
self.keys.len()
}
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub enum PrettyDoc {
Text(String),
Newline,
Indent(Box<PrettyDoc>),
Concat(Box<PrettyDoc>, Box<PrettyDoc>),
Group(Box<PrettyDoc>),
Empty,
}
#[allow(dead_code)]
impl PrettyDoc {
pub fn text(s: impl Into<String>) -> Self {
PrettyDoc::Text(s.into())
}
pub fn concat(a: PrettyDoc, b: PrettyDoc) -> Self {
PrettyDoc::Concat(Box::new(a), Box::new(b))
}
pub fn indent(inner: PrettyDoc) -> Self {
PrettyDoc::Indent(Box::new(inner))
}
pub fn group(inner: PrettyDoc) -> Self {
PrettyDoc::Group(Box::new(inner))
}
pub fn render(&self, _width: usize, style: IndentStyle) -> String {
let mut out = String::new();
let mut depth = 0usize;
self.render_inner(&mut out, &mut depth, &style);
out
}
fn render_inner(&self, out: &mut String, depth: &mut usize, style: &IndentStyle) {
match self {
PrettyDoc::Text(s) => out.push_str(s),
PrettyDoc::Newline => {
out.push('\n');
out.push_str(&style.for_depth(*depth));
}
PrettyDoc::Indent(inner) => {
*depth += 1;
inner.render_inner(out, depth, style);
*depth -= 1;
}
PrettyDoc::Concat(a, b) => {
a.render_inner(out, depth, style);
b.render_inner(out, depth, style);
}
PrettyDoc::Group(inner) => inner.render_inner(out, depth, style),
PrettyDoc::Empty => {}
}
}
}
#[allow(dead_code)]
pub struct TokenPrinter {
config: PrettyConfig,
scheme: ColorScheme,
}
#[allow(dead_code)]
impl TokenPrinter {
pub fn new(config: PrettyConfig) -> Self {
Self {
config,
scheme: ColorScheme::MONO,
}
}
pub fn with_colors(mut self, scheme: ColorScheme) -> Self {
self.scheme = scheme;
self
}
pub fn render(&self, tokens: &[PrettyToken]) -> String {
let mut out = String::new();
for tok in tokens {
if self.config.colored {
out.push_str(&tok.colored_text(&self.scheme));
} else {
out.push_str(tok.raw_text());
}
}
out
}
pub fn render_wrapped(&self, tokens: &[PrettyToken]) -> String {
let mut out = String::new();
let mut col = 0usize;
for tok in tokens {
let text = tok.raw_text();
if matches!(tok, PrettyToken::LineBreak) {
out.push('\n');
col = 0;
continue;
}
if col + text.len() > self.config.width && col > 0 {
out.push('\n');
col = 0;
}
out.push_str(text);
col += text.len();
}
out
}
}
#[allow(dead_code)]
pub struct FmtWidth;
#[allow(dead_code)]
impl FmtWidth {
pub fn decimal_width(n: usize) -> usize {
if n == 0 {
return 1;
}
let mut w = 0;
let mut v = n;
while v > 0 {
v /= 10;
w += 1;
}
w
}
pub fn pad_right(s: &str, width: usize) -> String {
if s.len() >= width {
return s.to_string();
}
format!("{}{}", s, " ".repeat(width - s.len()))
}
pub fn pad_left(s: &str, width: usize) -> String {
if s.len() >= width {
return s.to_string();
}
format!("{}{}", " ".repeat(width - s.len()), s)
}
pub fn center(s: &str, width: usize) -> String {
if s.len() >= width {
return s.to_string();
}
let pad = width - s.len();
let left = pad / 2;
let right = pad - left;
format!("{}{}{}", " ".repeat(left), s, " ".repeat(right))
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub enum Doc {
Empty,
Text(String),
Concat(Box<Doc>, Box<Doc>),
Line,
Nest(usize, Box<Doc>),
Union(Box<Doc>, Box<Doc>),
}
#[allow(dead_code)]
impl Doc {
pub fn text(s: impl Into<String>) -> Self {
Doc::Text(s.into())
}
pub fn concat(self, other: Doc) -> Self {
Doc::Concat(Box::new(self), Box::new(other))
}
pub fn line() -> Self {
Doc::Line
}
pub fn nest(n: usize, doc: Doc) -> Self {
Doc::Nest(n, Box::new(doc))
}
pub fn render(&self, width: usize) -> String {
let mut out = String::new();
self.render_impl(0, &mut out, width);
out
}
fn render_impl(&self, indent: usize, out: &mut String, _width: usize) {
match self {
Doc::Empty => {}
Doc::Text(s) => out.push_str(s),
Doc::Concat(a, b) => {
a.render_impl(indent, out, _width);
b.render_impl(indent, out, _width);
}
Doc::Line => {
out.push('\n');
for _ in 0..indent {
out.push(' ');
}
}
Doc::Nest(n, doc) => {
doc.render_impl(indent + n, out, _width);
}
Doc::Union(a, _b) => {
a.render_impl(indent, out, _width);
}
}
}
}
#[allow(dead_code)]
pub struct PrettyTable {
headers: Vec<String>,
rows: Vec<Vec<String>>,
}
#[allow(dead_code)]
impl PrettyTable {
pub fn new(headers: Vec<String>) -> Self {
Self {
headers,
rows: Vec::new(),
}
}
pub fn add_row(&mut self, row: Vec<String>) {
let n = self.headers.len();
let mut r = row;
r.resize(n, String::new());
self.rows.push(r);
}
pub fn render(&self) -> String {
let n = self.headers.len();
let mut widths: Vec<usize> = self.headers.iter().map(|h| h.len()).collect();
for row in &self.rows {
for (i, cell) in row.iter().enumerate() {
if i < n {
widths[i] = widths[i].max(cell.len());
}
}
}
let sep: String = widths
.iter()
.map(|&w| "-".repeat(w + 2))
.collect::<Vec<_>>()
.join("+");
let sep = format!("+{}+", sep);
let mut out = String::new();
out.push_str(&sep);
out.push('\n');
let header_line: String = self
.headers
.iter()
.enumerate()
.map(|(i, h)| FmtWidth::pad_right(h, widths[i]))
.collect::<Vec<_>>()
.join(" | ");
out.push_str(&format!("| {} |", header_line));
out.push('\n');
out.push_str(&sep);
out.push('\n');
for row in &self.rows {
let line: String = (0..n)
.map(|i| {
FmtWidth::pad_right(row.get(i).map(|s| s.as_str()).unwrap_or(""), widths[i])
})
.collect::<Vec<_>>()
.join(" | ");
out.push_str(&format!("| {} |", line));
out.push('\n');
}
out.push_str(&sep);
out
}
}
#[allow(dead_code)]
pub struct WorkQueue<T> {
items: std::collections::VecDeque<T>,
}
#[allow(dead_code)]
impl<T> WorkQueue<T> {
pub fn new() -> Self {
Self {
items: std::collections::VecDeque::new(),
}
}
pub fn enqueue(&mut self, item: T) {
self.items.push_back(item);
}
pub fn dequeue(&mut self) -> Option<T> {
self.items.pop_front()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn len(&self) -> usize {
self.items.len()
}
}
#[allow(dead_code)]
pub struct DiagMeta {
pub(super) entries: Vec<(String, String)>,
}
#[allow(dead_code)]
impl DiagMeta {
pub fn new() -> Self {
Self {
entries: Vec::new(),
}
}
pub fn add(&mut self, key: impl Into<String>, val: impl Into<String>) {
self.entries.push((key.into(), val.into()));
}
pub fn get(&self, key: &str) -> Option<&str> {
self.entries
.iter()
.find(|(k, _)| k == key)
.map(|(_, v)| v.as_str())
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TypedId<T> {
pub(super) id: u32,
_phantom: std::marker::PhantomData<T>,
}
#[allow(dead_code)]
impl<T> TypedId<T> {
pub const fn new(id: u32) -> Self {
Self {
id,
_phantom: std::marker::PhantomData,
}
}
pub fn raw(&self) -> u32 {
self.id
}
}
#[allow(dead_code)]
pub struct PrettyPrinterState {
output: String,
indent_depth: usize,
indent_str: String,
pub(crate) col: usize,
max_width: usize,
}
#[allow(dead_code)]
impl PrettyPrinterState {
pub fn new(max_width: usize, indent: IndentStyle) -> Self {
Self {
output: String::new(),
indent_depth: 0,
indent_str: indent.one_level(),
col: 0,
max_width,
}
}
pub fn write(&mut self, s: &str) {
self.output.push_str(s);
self.col += s.len();
}
pub fn newline(&mut self) {
self.output.push('\n');
let indent = self.indent_str.repeat(self.indent_depth);
self.output.push_str(&indent);
self.col = indent.len();
}
pub fn push_indent(&mut self) {
self.indent_depth += 1;
}
pub fn pop_indent(&mut self) {
if self.indent_depth > 0 {
self.indent_depth -= 1;
}
}
pub fn over_width(&self) -> bool {
self.col > self.max_width
}
pub fn finish(self) -> String {
self.output
}
}
#[allow(dead_code)]
pub struct IntervalSet {
intervals: Vec<(i64, i64)>,
}
#[allow(dead_code)]
impl IntervalSet {
pub fn new() -> Self {
Self {
intervals: Vec::new(),
}
}
pub fn add(&mut self, lo: i64, hi: i64) {
if lo > hi {
return;
}
let mut new_lo = lo;
let mut new_hi = hi;
let mut i = 0;
while i < self.intervals.len() {
let (il, ih) = self.intervals[i];
if ih < new_lo - 1 {
i += 1;
continue;
}
if il > new_hi + 1 {
break;
}
new_lo = new_lo.min(il);
new_hi = new_hi.max(ih);
self.intervals.remove(i);
}
self.intervals.insert(i, (new_lo, new_hi));
}
pub fn contains(&self, x: i64) -> bool {
self.intervals.iter().any(|&(lo, hi)| x >= lo && x <= hi)
}
pub fn num_intervals(&self) -> usize {
self.intervals.len()
}
pub fn cardinality(&self) -> i64 {
self.intervals.iter().map(|&(lo, hi)| hi - lo + 1).sum()
}
}
#[allow(dead_code)]
pub struct IdDispenser<T> {
next: u32,
_phantom: std::marker::PhantomData<T>,
}
#[allow(dead_code)]
impl<T> IdDispenser<T> {
pub fn new() -> Self {
Self {
next: 0,
_phantom: std::marker::PhantomData,
}
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> TypedId<T> {
let id = TypedId::new(self.next);
self.next += 1;
id
}
pub fn count(&self) -> u32 {
self.next
}
}
#[allow(dead_code)]
pub struct AnnotationTable {
map: std::collections::HashMap<String, Vec<String>>,
}
#[allow(dead_code)]
impl AnnotationTable {
pub fn new() -> Self {
Self {
map: std::collections::HashMap::new(),
}
}
pub fn annotate(&mut self, key: impl Into<String>, val: impl Into<String>) {
self.map.entry(key.into()).or_default().push(val.into());
}
pub fn get_all(&self, key: &str) -> &[String] {
self.map.get(key).map(|v| v.as_slice()).unwrap_or(&[])
}
pub fn num_keys(&self) -> usize {
self.map.len()
}
pub fn has(&self, key: &str) -> bool {
self.map.contains_key(key)
}
}
#[allow(dead_code)]
pub struct FrequencyTable<T: std::hash::Hash + Eq + Clone> {
counts: std::collections::HashMap<T, u64>,
}
#[allow(dead_code)]
impl<T: std::hash::Hash + Eq + Clone> FrequencyTable<T> {
pub fn new() -> Self {
Self {
counts: std::collections::HashMap::new(),
}
}
pub fn record(&mut self, item: T) {
*self.counts.entry(item).or_insert(0) += 1;
}
pub fn freq(&self, item: &T) -> u64 {
self.counts.get(item).copied().unwrap_or(0)
}
pub fn most_frequent(&self) -> Option<(&T, u64)> {
self.counts
.iter()
.max_by_key(|(_, &v)| v)
.map(|(k, &v)| (k, v))
}
pub fn total(&self) -> u64 {
self.counts.values().sum()
}
pub fn distinct(&self) -> usize {
self.counts.len()
}
}
#[allow(dead_code)]
pub struct DocBuilder {
doc: PrettyDoc,
}
#[allow(dead_code)]
impl DocBuilder {
pub fn from(doc: PrettyDoc) -> Self {
Self { doc }
}
pub fn text(s: impl Into<String>) -> Self {
Self {
doc: PrettyDoc::text(s),
}
}
pub fn empty() -> Self {
Self {
doc: PrettyDoc::Empty,
}
}
pub fn then(self, other: PrettyDoc) -> Self {
Self {
doc: PrettyDoc::concat(self.doc, other),
}
}
pub fn then_text(self, s: impl Into<String>) -> Self {
self.then(PrettyDoc::text(s))
}
pub fn then_newline(self) -> Self {
self.then(PrettyDoc::Newline)
}
pub fn indented(self) -> Self {
Self {
doc: PrettyDoc::indent(self.doc),
}
}
pub fn build(self) -> PrettyDoc {
self.doc
}
}
#[allow(dead_code)]
pub struct WorkStack<T> {
items: Vec<T>,
}
#[allow(dead_code)]
impl<T> WorkStack<T> {
pub fn new() -> Self {
Self { items: Vec::new() }
}
pub fn push(&mut self, item: T) {
self.items.push(item);
}
pub fn pop(&mut self) -> Option<T> {
self.items.pop()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn len(&self) -> usize {
self.items.len()
}
}
#[allow(dead_code)]
pub struct MemoSlot<T: Clone> {
cached: Option<T>,
}
#[allow(dead_code)]
impl<T: Clone> MemoSlot<T> {
pub fn new() -> Self {
Self { cached: None }
}
pub fn get_or_compute(&mut self, f: impl FnOnce() -> T) -> &T {
if self.cached.is_none() {
self.cached = Some(f());
}
self.cached
.as_ref()
.expect("cached value must be initialized before access")
}
pub fn invalidate(&mut self) {
self.cached = None;
}
pub fn is_cached(&self) -> bool {
self.cached.is_some()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct StatCache<K: std::hash::Hash + Eq + Clone, V: Clone> {
pub inner: SimpleLruCache<K, V>,
pub hits: u64,
pub misses: u64,
}
#[allow(dead_code)]
impl<K: std::hash::Hash + Eq + Clone, V: Clone> StatCache<K, V> {
pub fn new(capacity: usize) -> Self {
Self {
inner: SimpleLruCache::new(capacity),
hits: 0,
misses: 0,
}
}
pub fn get(&mut self, key: &K) -> Option<&V> {
let result = self.inner.get(key);
if result.is_some() {
self.hits += 1;
} else {
self.misses += 1;
}
None
}
pub fn put(&mut self, key: K, val: V) {
self.inner.put(key, val);
}
pub fn hit_rate(&self) -> f64 {
let total = self.hits + self.misses;
if total == 0 {
return 0.0;
}
self.hits as f64 / total as f64
}
}
#[allow(dead_code)]
pub struct EscapeHelper;
#[allow(dead_code)]
impl EscapeHelper {
pub fn escape_str(s: &str) -> String {
let mut out = String::with_capacity(s.len() + 2);
out.push('"');
for c in s.chars() {
match c {
'"' => out.push_str("\\\""),
'\\' => out.push_str("\\\\"),
'\n' => out.push_str("\\n"),
'\t' => out.push_str("\\t"),
_ => out.push(c),
}
}
out.push('"');
out
}
pub fn unescape_str(s: &str) -> String {
let s = if s.starts_with('"') && s.ends_with('"') && s.len() >= 2 {
&s[1..s.len() - 1]
} else {
s
};
let mut out = String::new();
let mut chars = s.chars().peekable();
while let Some(c) = chars.next() {
if c == '\\' {
match chars.next() {
Some('n') => out.push('\n'),
Some('t') => out.push('\t'),
Some('\\') => out.push('\\'),
Some('"') => out.push('"'),
Some(x) => {
out.push('\\');
out.push(x);
}
None => out.push('\\'),
}
} else {
out.push(c);
}
}
out
}
}
#[allow(dead_code)]
pub struct EventCounter {
counts: std::collections::HashMap<String, u64>,
}
#[allow(dead_code)]
impl EventCounter {
pub fn new() -> Self {
Self {
counts: std::collections::HashMap::new(),
}
}
pub fn inc(&mut self, event: &str) {
*self.counts.entry(event.to_string()).or_insert(0) += 1;
}
pub fn add(&mut self, event: &str, n: u64) {
*self.counts.entry(event.to_string()).or_insert(0) += n;
}
pub fn get(&self, event: &str) -> u64 {
self.counts.get(event).copied().unwrap_or(0)
}
pub fn total(&self) -> u64 {
self.counts.values().sum()
}
pub fn reset(&mut self) {
self.counts.clear();
}
pub fn event_names(&self) -> Vec<&str> {
self.counts.keys().map(|s| s.as_str()).collect()
}
}
#[allow(dead_code)]
pub struct BiMap<A: std::hash::Hash + Eq + Clone, B: std::hash::Hash + Eq + Clone> {
forward: std::collections::HashMap<A, B>,
backward: std::collections::HashMap<B, A>,
}
#[allow(dead_code)]
impl<A: std::hash::Hash + Eq + Clone, B: std::hash::Hash + Eq + Clone> BiMap<A, B> {
pub fn new() -> Self {
Self {
forward: std::collections::HashMap::new(),
backward: std::collections::HashMap::new(),
}
}
pub fn insert(&mut self, a: A, b: B) {
self.forward.insert(a.clone(), b.clone());
self.backward.insert(b, a);
}
pub fn get_b(&self, a: &A) -> Option<&B> {
self.forward.get(a)
}
pub fn get_a(&self, b: &B) -> Option<&A> {
self.backward.get(b)
}
pub fn len(&self) -> usize {
self.forward.len()
}
pub fn is_empty(&self) -> bool {
self.forward.is_empty()
}
}
#[allow(dead_code)]
pub struct Slot<T> {
inner: Option<T>,
}
#[allow(dead_code)]
impl<T> Slot<T> {
pub fn empty() -> Self {
Self { inner: None }
}
pub fn fill(&mut self, val: T) {
assert!(self.inner.is_none(), "Slot: already filled");
self.inner = Some(val);
}
pub fn get(&self) -> Option<&T> {
self.inner.as_ref()
}
pub fn is_filled(&self) -> bool {
self.inner.is_some()
}
pub fn take(&mut self) -> Option<T> {
self.inner.take()
}
pub fn get_or_fill_with(&mut self, f: impl FnOnce() -> T) -> &T {
if self.inner.is_none() {
self.inner = Some(f());
}
self.inner
.as_ref()
.expect("inner value must be initialized before access")
}
}
#[allow(dead_code)]
pub struct StringInterner {
strings: Vec<String>,
map: std::collections::HashMap<String, u32>,
}
#[allow(dead_code)]
impl StringInterner {
pub fn new() -> Self {
Self {
strings: Vec::new(),
map: std::collections::HashMap::new(),
}
}
pub fn intern(&mut self, s: &str) -> u32 {
if let Some(&id) = self.map.get(s) {
return id;
}
let id = self.strings.len() as u32;
self.strings.push(s.to_string());
self.map.insert(s.to_string(), id);
id
}
pub fn get(&self, id: u32) -> Option<&str> {
self.strings.get(id as usize).map(|s| s.as_str())
}
pub fn len(&self) -> usize {
self.strings.len()
}
pub fn is_empty(&self) -> bool {
self.strings.is_empty()
}
}
#[allow(dead_code)]
pub struct BoxedPrinter {
width: usize,
}
#[allow(dead_code)]
impl BoxedPrinter {
pub fn new(width: usize) -> Self {
Self { width }
}
pub fn render(&self, doc: &PrettyDoc) -> String {
doc.render(self.width, IndentStyle::Spaces(2))
}
}
#[derive(Debug, Clone)]
pub struct PrintConfig {
pub unicode: bool,
pub show_implicit: bool,
pub show_universes: bool,
pub max_width: usize,
pub show_binder_info: bool,
pub show_indices: bool,
}
impl PrintConfig {
pub fn ascii() -> Self {
Self {
unicode: false,
..Default::default()
}
}
pub fn verbose() -> Self {
Self {
show_implicit: true,
show_universes: true,
show_binder_info: true,
show_indices: true,
..Default::default()
}
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct PrettyConfig {
pub width: usize,
pub indent: IndentStyle,
pub colored: bool,
}
#[allow(dead_code)]
impl PrettyConfig {
pub fn default_config() -> Self {
Self {
width: 80,
indent: IndentStyle::Spaces(2),
colored: false,
}
}
pub fn compact() -> Self {
Self {
width: usize::MAX,
indent: IndentStyle::Spaces(0),
colored: false,
}
}
}
#[allow(dead_code)]
pub struct SparseBitSet {
words: Vec<u64>,
}
#[allow(dead_code)]
impl SparseBitSet {
pub fn new(capacity: usize) -> Self {
let words = (capacity + 63) / 64;
Self {
words: vec![0u64; words],
}
}
pub fn set(&mut self, i: usize) {
let word = i / 64;
let bit = i % 64;
if word < self.words.len() {
self.words[word] |= 1u64 << bit;
}
}
pub fn clear(&mut self, i: usize) {
let word = i / 64;
let bit = i % 64;
if word < self.words.len() {
self.words[word] &= !(1u64 << bit);
}
}
pub fn get(&self, i: usize) -> bool {
let word = i / 64;
let bit = i % 64;
self.words.get(word).is_some_and(|w| w & (1u64 << bit) != 0)
}
pub fn count_ones(&self) -> u32 {
self.words.iter().map(|w| w.count_ones()).sum()
}
pub fn union(&self, other: &SparseBitSet) -> SparseBitSet {
let len = self.words.len().max(other.words.len());
let mut result = SparseBitSet {
words: vec![0u64; len],
};
for i in 0..self.words.len() {
result.words[i] |= self.words[i];
}
for i in 0..other.words.len() {
result.words[i] |= other.words[i];
}
result
}
}
#[allow(dead_code)]
pub struct ScopeStack {
names: Vec<String>,
}
#[allow(dead_code)]
impl ScopeStack {
pub fn new() -> Self {
Self { names: Vec::new() }
}
pub fn push(&mut self, name: impl Into<String>) {
self.names.push(name.into());
}
pub fn pop(&mut self) -> Option<String> {
self.names.pop()
}
pub fn current(&self) -> Option<&str> {
self.names.last().map(|s| s.as_str())
}
pub fn depth(&self) -> usize {
self.names.len()
}
pub fn is_empty(&self) -> bool {
self.names.is_empty()
}
pub fn path(&self) -> String {
self.names.join(".")
}
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Timestamp(u64);
#[allow(dead_code)]
impl Timestamp {
pub const fn from_us(us: u64) -> Self {
Self(us)
}
pub fn as_us(self) -> u64 {
self.0
}
pub fn elapsed_since(self, earlier: Timestamp) -> u64 {
self.0.saturating_sub(earlier.0)
}
}
#[allow(dead_code)]
pub struct LoopClock {
start: std::time::Instant,
iters: u64,
}
#[allow(dead_code)]
impl LoopClock {
pub fn start() -> Self {
Self {
start: std::time::Instant::now(),
iters: 0,
}
}
pub fn tick(&mut self) {
self.iters += 1;
}
pub fn elapsed_us(&self) -> f64 {
self.start.elapsed().as_secs_f64() * 1e6
}
pub fn avg_us_per_iter(&self) -> f64 {
if self.iters == 0 {
return 0.0;
}
self.elapsed_us() / self.iters as f64
}
pub fn iters(&self) -> u64 {
self.iters
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub enum PrettyToken {
Keyword(String),
Ident(String),
Literal(String),
Operator(String),
Space,
LineBreak,
}
#[allow(dead_code)]
impl PrettyToken {
pub fn raw_text(&self) -> &str {
match self {
PrettyToken::Keyword(s) => s.as_str(),
PrettyToken::Ident(s) => s.as_str(),
PrettyToken::Literal(s) => s.as_str(),
PrettyToken::Operator(s) => s.as_str(),
PrettyToken::Space => " ",
PrettyToken::LineBreak => "\n",
}
}
pub fn colored_text(&self, cs: &ColorScheme) -> String {
match self {
PrettyToken::Keyword(s) => format!("{}{}{}", cs.keyword, s, cs.reset),
PrettyToken::Ident(s) => format!("{}{}{}", cs.ident, s, cs.reset),
PrettyToken::Literal(s) => format!("{}{}{}", cs.literal, s, cs.reset),
PrettyToken::Operator(s) => format!("{}{}{}", cs.operator, s, cs.reset),
PrettyToken::Space => " ".to_string(),
PrettyToken::LineBreak => "\n".to_string(),
}
}
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum IndentStyle {
Spaces(usize),
Tabs,
}
#[allow(dead_code)]
impl IndentStyle {
pub fn one_level(self) -> String {
match self {
IndentStyle::Spaces(n) => " ".repeat(n),
IndentStyle::Tabs => "\t".to_string(),
}
}
pub fn for_depth(self, depth: usize) -> String {
self.one_level().repeat(depth)
}
}
#[allow(dead_code)]
pub struct ColorScheme {
pub keyword: &'static str,
pub ident: &'static str,
pub literal: &'static str,
pub operator: &'static str,
pub reset: &'static str,
}
#[allow(dead_code)]
impl ColorScheme {
pub const DEFAULT: ColorScheme = ColorScheme {
keyword: "\x1b[34m",
ident: "\x1b[0m",
literal: "\x1b[32m",
operator: "\x1b[33m",
reset: "\x1b[0m",
};
pub const MONO: ColorScheme = ColorScheme {
keyword: "",
ident: "",
literal: "",
operator: "",
reset: "",
};
pub fn is_colored(&self) -> bool {
!self.keyword.is_empty()
}
}