use std::collections::BTreeMap;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Ref(pub u64);
impl std::fmt::Display for Ref {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "#{}", self.0)
}
}
pub struct Emitter {
next: u64,
lines: Vec<String>,
counts: BTreeMap<&'static str, usize>,
interned: HashMap<String, Ref>,
}
impl Emitter {
pub fn new() -> Self {
Emitter {
next: 1,
lines: Vec::new(),
counts: BTreeMap::new(),
interned: HashMap::new(),
}
}
pub fn emit(&mut self, type_: &'static str, params: &str) -> Ref {
let id = self.next;
self.next += 1;
self.lines.push(format!("#{id} = {type_}({params});"));
*self.counts.entry(type_).or_insert(0) += 1;
Ref(id)
}
pub fn emit_raw(&mut self, tally: &'static str, body: &str) -> Ref {
let id = self.next;
self.next += 1;
self.lines.push(format!("#{id} = {body};"));
*self.counts.entry(tally).or_insert(0) += 1;
Ref(id)
}
pub fn emit_interned(&mut self, type_: &'static str, params: &str) -> Ref {
let key = format!("{type_}|{params}");
if let Some(r) = self.interned.get(&key) {
return *r;
}
let r = self.emit(type_, params);
self.interned.insert(key, r);
r
}
pub fn counts(&self) -> BTreeMap<String, usize> {
self.counts
.iter()
.map(|(type_, count)| ((*type_).to_string(), *count))
.collect()
}
pub fn total(&self) -> usize {
self.lines.len()
}
pub fn into_lines(self) -> Vec<String> {
self.lines
}
}
pub fn real(v: f64) -> String {
if !v.is_finite() {
return "0.".to_string();
}
let mut s = format!("{v}");
if let Some(e_pos) = s.find(['e', 'E']) {
let (mantissa, exp) = s.split_at(e_pos);
let exp = &exp[1..];
let mantissa = if mantissa.contains('.') {
mantissa.to_string()
} else {
format!("{mantissa}.")
};
let exp = if let Some(rest) = exp.strip_prefix('-') {
format!("-{rest}")
} else {
exp.strip_prefix('+').unwrap_or(exp).to_string()
};
return format!("{mantissa}E{exp}");
}
if !s.contains('.') {
s.push('.');
}
s
}
pub fn string(s: &str) -> String {
format!("'{}'", crate::strings::encode(s))
}
pub fn refs(items: &[Ref]) -> String {
use std::fmt::Write as _;
let mut out = String::from("(");
for (i, r) in items.iter().enumerate() {
if i > 0 {
out.push(',');
}
write!(out, "{r}").expect("writing to a String cannot fail");
}
out.push(')');
out
}