use super::functions::*;
use std::collections::{BTreeSet, HashMap};
#[allow(dead_code)]
pub struct ErasedLiveness {
live: std::collections::BTreeSet<u32>,
}
#[allow(dead_code)]
impl ErasedLiveness {
pub fn new() -> Self {
Self {
live: std::collections::BTreeSet::new(),
}
}
pub fn mark_live(&mut self, i: u32) {
self.live.insert(i);
}
pub fn is_live(&self, i: u32) -> bool {
self.live.contains(&i)
}
pub fn count(&self) -> usize {
self.live.len()
}
pub fn merge(&mut self, other: &ErasedLiveness) {
for &i in &other.live {
self.live.insert(i);
}
}
pub fn max_live(&self) -> Option<u32> {
self.live.iter().next_back().copied()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasedAst {
pub expr: ErasedExpr,
pub label: String,
pub stack_depth: usize,
}
#[allow(dead_code)]
impl ErasedAst {
pub fn new(expr: ErasedExpr, label: impl Into<String>) -> Self {
let depth = erased_expr_depth(&expr);
Self {
expr,
label: label.into(),
stack_depth: depth as usize,
}
}
pub fn size(&self) -> usize {
erased_expr_apps(&self.expr)
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasurePass {
pub name: String,
pub total_transforms: u64,
}
#[allow(dead_code)]
impl ErasurePass {
pub fn new(name: impl Into<String>) -> Self {
ErasurePass {
name: name.into(),
total_transforms: 0,
}
}
pub fn run(&mut self, expr: ErasedExprExt) -> ErasedExprExt {
let mut opt = ErasedOptimizer::new(1000);
let result = opt.optimize(expr);
self.total_transforms += opt.total_transforms();
result
}
pub fn run_module(&mut self, mut module: ErasedModule) -> ErasedModule {
let new_decls = module
.decls
.drain(..)
.map(|decl| match decl {
ErasedDecl::Def { name, body } => {
let new_body = self.run(body);
ErasedDecl::Def {
name,
body: new_body,
}
}
other => other,
})
.collect();
module.decls = new_decls;
module
}
}
#[allow(dead_code)]
pub struct ErasedTupleOps;
#[allow(dead_code)]
impl ErasedTupleOps {
pub fn make_pair(a: ErasedExprExt, b: ErasedExprExt) -> ErasedExprExt {
ErasedExprExt::App(
Box::new(ErasedExprExt::App(
Box::new(ErasedExprExt::Const("Prod.mk".to_string())),
Box::new(a),
)),
Box::new(b),
)
}
pub fn fst(pair: ErasedExprExt) -> ErasedExprExt {
ErasedExprExt::App(
Box::new(ErasedExprExt::Const("Prod.fst".to_string())),
Box::new(pair),
)
}
pub fn snd(pair: ErasedExprExt) -> ErasedExprExt {
ErasedExprExt::App(
Box::new(ErasedExprExt::Const("Prod.snd".to_string())),
Box::new(pair),
)
}
pub fn n_tuple(exprs: Vec<ErasedExprExt>) -> ErasedExprExt {
assert!(!exprs.is_empty(), "n_tuple: empty tuple");
let mut iter = exprs.into_iter().rev();
let last = iter
.next()
.expect("iterator must have at least one element");
iter.fold(last, |acc, e| Self::make_pair(e, acc))
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasedBetaReducer {
pub steps: u64,
pub max_steps: u64,
}
#[allow(dead_code)]
impl ErasedBetaReducer {
pub fn new(max_steps: u64) -> Self {
ErasedBetaReducer {
steps: 0,
max_steps,
}
}
pub fn step(&mut self, expr: ErasedExprExt) -> ErasedExprExt {
if self.steps >= self.max_steps {
return expr;
}
match expr {
ErasedExprExt::App(f, arg) => match *f {
ErasedExprExt::Lam(body) => {
self.steps += 1;
subst_bvar0(*body, *arg)
}
other_f => ErasedExprExt::App(Box::new(other_f), arg),
},
ErasedExprExt::Lam(b) => ErasedExprExt::Lam(Box::new(self.step(*b))),
other => other,
}
}
pub fn is_exhausted(&self) -> bool {
self.steps >= self.max_steps
}
}
#[allow(dead_code)]
pub struct ErasedTypeMap {
entries: Vec<(u64, ErasedExprExt)>,
}
#[allow(dead_code)]
impl ErasedTypeMap {
pub fn new() -> Self {
ErasedTypeMap {
entries: Vec::new(),
}
}
pub fn insert(&mut self, id: u64, erased: ErasedExprExt) {
if let Some(e) = self.entries.iter_mut().find(|(i, _)| *i == id) {
e.1 = erased;
} else {
self.entries.push((id, erased));
}
}
pub fn get(&self, id: u64) -> Option<&ErasedExprExt> {
self.entries.iter().find(|(i, _)| *i == id).map(|(_, e)| e)
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
#[derive(Debug, Clone)]
pub struct ErasedMatchArm {
pub pattern: ErasedPattern,
pub body: ErasedExprExt,
}
#[allow(dead_code)]
impl ErasedMatchArm {
pub fn new(pattern: ErasedPattern, body: ErasedExprExt) -> Self {
ErasedMatchArm { pattern, body }
}
pub fn is_catch_all(&self) -> bool {
self.pattern.is_irrefutable()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasedClosureEnv {
pub captures: Vec<String>,
pub values: Vec<ErasedExprExt>,
}
#[allow(dead_code)]
impl ErasedClosureEnv {
pub fn new() -> Self {
Self {
captures: Vec::new(),
values: Vec::new(),
}
}
pub fn capture(&mut self, name: impl Into<String>, val: ErasedExprExt) {
self.captures.push(name.into());
self.values.push(val);
}
pub fn lookup(&self, name: &str) -> Option<&ErasedExprExt> {
self.captures
.iter()
.position(|n| n == name)
.map(|i| &self.values[i])
}
pub fn size(&self) -> usize {
self.captures.len()
}
}
#[allow(dead_code)]
pub struct PipelineRunner {
passes: Vec<Box<dyn ErasedPass>>,
}
#[allow(dead_code)]
impl PipelineRunner {
pub fn new() -> Self {
Self { passes: Vec::new() }
}
pub fn add_pass(&mut self, pass: Box<dyn ErasedPass>) {
self.passes.push(pass);
}
pub fn run_all(&mut self, expr: ErasedExpr) -> ErasedExpr {
let mut current = expr;
for pass in self.passes.iter_mut() {
current = pass.run(current);
}
current
}
pub fn run_on_module(&mut self, decls: &mut Vec<ErasedDecl>) {
for pass in self.passes.iter_mut() {
pass.run_on_module(decls);
}
}
pub fn num_passes(&self) -> usize {
self.passes.len()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasedInliner {
defs: Vec<(String, ErasedExprExt)>,
pub inlined: u64,
}
#[allow(dead_code)]
impl ErasedInliner {
pub fn new() -> Self {
ErasedInliner {
defs: Vec::new(),
inlined: 0,
}
}
pub fn register(&mut self, name: &str, def: ErasedExprExt) {
self.defs.push((name.to_string(), def));
}
pub fn inline(&mut self, expr: ErasedExprExt) -> ErasedExprExt {
match expr {
ErasedExprExt::Const(ref name) => {
if let Some((_, def)) = self.defs.iter().find(|(n, _)| n == name) {
self.inlined += 1;
def.clone()
} else {
expr
}
}
ErasedExprExt::Lam(b) => ErasedExprExt::Lam(Box::new(self.inline(*b))),
ErasedExprExt::App(f, x) => {
ErasedExprExt::App(Box::new(self.inline(*f)), Box::new(self.inline(*x)))
}
ErasedExprExt::Let(v, b) => {
ErasedExprExt::Let(Box::new(self.inline(*v)), Box::new(self.inline(*b)))
}
other => other,
}
}
}
#[allow(dead_code)]
pub struct ErasureContext {
type_vars: Vec<bool>,
}
#[allow(dead_code)]
impl ErasureContext {
pub fn new() -> Self {
ErasureContext {
type_vars: Vec::new(),
}
}
pub fn push(&mut self, is_type: bool) {
self.type_vars.push(is_type);
}
pub fn pop(&mut self) {
self.type_vars.pop();
}
pub fn is_type_at(&self, i: u32) -> bool {
let len = self.type_vars.len();
if (i as usize) < len {
self.type_vars[len - 1 - i as usize]
} else {
false
}
}
pub fn depth(&self) -> usize {
self.type_vars.len()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasedCodegen {
pub output: String,
indent: usize,
}
#[allow(dead_code)]
impl ErasedCodegen {
pub fn new() -> Self {
ErasedCodegen {
output: String::new(),
indent: 0,
}
}
pub fn emit(&mut self, line: &str) {
for _ in 0..self.indent {
self.output.push_str(" ");
}
self.output.push_str(line);
self.output.push('\n');
}
pub fn gen_expr(&mut self, expr: &ErasedExprExt) -> String {
match expr {
ErasedExprExt::Lit(n) => n.to_string(),
ErasedExprExt::BVar(i) => format!("v{}", i),
ErasedExprExt::FVar(name) => name.clone(),
ErasedExprExt::Const(name) => name.clone(),
ErasedExprExt::Unit => "()".to_string(),
ErasedExprExt::TypeErased => "_".to_string(),
ErasedExprExt::Lam(b) => format!("(fun x -> {})", self.gen_expr(b)),
ErasedExprExt::App(f, x) => {
format!("({} {})", self.gen_expr(f), self.gen_expr(x))
}
ErasedExprExt::Let(v, b) => {
format!("(let _ = {} in {})", self.gen_expr(v), self.gen_expr(b))
}
ErasedExprExt::CtorTag(t) => format!("Ctor({})", t),
}
}
pub fn gen_module(&mut self, module: &ErasedModule) {
self.emit(&format!("(* Module: {} *)", module.name));
for decl in &module.decls {
match decl {
ErasedDecl::Def { name, body } => {
let body_str = self.gen_expr(body);
self.emit(&format!("let {} = {}", name, body_str));
}
ErasedDecl::Axiom { name } => {
self.emit(&format!("let {} = failwith \"axiom\"", name));
}
ErasedDecl::Inductive { name, ctor_count } => {
self.emit(&format!(
"(* Inductive {} with {} ctors *)",
name, ctor_count
));
}
}
}
}
}
#[allow(dead_code)]
pub struct ErasedEnv {
names: Vec<String>,
values: Vec<ErasedExpr>,
}
#[allow(dead_code)]
impl ErasedEnv {
pub fn new() -> Self {
ErasedEnv {
names: Vec::new(),
values: Vec::new(),
}
}
pub fn bind(&mut self, name: &str, val: ErasedExpr) {
self.names.push(name.to_string());
self.values.push(val);
}
pub fn get(&self, name: &str) -> Option<&ErasedExpr> {
self.names
.iter()
.rev()
.zip(self.values.iter().rev())
.find(|(n, _)| n.as_str() == name)
.map(|(_, v)| v)
}
pub fn len(&self) -> usize {
self.names.len()
}
pub fn is_empty(&self) -> bool {
self.names.is_empty()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasedModule {
pub name: String,
pub decls: Vec<ErasedDecl>,
}
#[allow(dead_code)]
impl ErasedModule {
pub fn new(name: impl Into<String>) -> Self {
ErasedModule {
name: name.into(),
decls: Vec::new(),
}
}
pub fn add(&mut self, decl: ErasedDecl) {
self.decls.push(decl);
}
pub fn find(&self, name: &str) -> Option<&ErasedDecl> {
self.decls.iter().find(|d| d.name() == name)
}
pub fn len(&self) -> usize {
self.decls.len()
}
pub fn is_empty(&self) -> bool {
self.decls.is_empty()
}
pub fn function_names(&self) -> Vec<&str> {
self.decls
.iter()
.filter(|d| d.is_def())
.map(|d| d.name())
.collect()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
#[derive(Debug, Clone)]
pub enum ErasedHeapObj {
Lit(u64),
Ctor { tag: u32, fields: Vec<usize> },
Closure {
arity: u32,
fn_ptr: usize,
num_caps: u32,
},
Str(String),
Thunk { code: usize },
}
impl ErasedHeapObj {
#[allow(dead_code)]
pub fn is_ctor(&self) -> bool {
matches!(self, ErasedHeapObj::Ctor { .. })
}
#[allow(dead_code)]
pub fn is_closure(&self) -> bool {
matches!(self, ErasedHeapObj::Closure { .. })
}
#[allow(dead_code)]
pub fn is_thunk(&self) -> bool {
matches!(self, ErasedHeapObj::Thunk { .. })
}
#[allow(dead_code)]
pub fn ctor_tag(&self) -> Option<u32> {
if let ErasedHeapObj::Ctor { tag, .. } = self {
Some(*tag)
} else {
None
}
}
}
#[allow(dead_code)]
pub struct ErasedOptimizer {
reducer: ErasedBetaReducer,
dce: ErasedDCE,
}
#[allow(dead_code)]
impl ErasedOptimizer {
pub fn new(max_steps: u64) -> Self {
ErasedOptimizer {
reducer: ErasedBetaReducer::new(max_steps),
dce: ErasedDCE::new(),
}
}
pub fn optimize(&mut self, expr: ErasedExprExt) -> ErasedExprExt {
let after_beta = self.reducer.step(expr);
self.dce.elim(after_beta)
}
pub fn total_transforms(&self) -> u64 {
self.reducer.steps + self.dce.eliminated
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ErasedExprExt {
BVar(u32),
FVar(String),
Lit(u64),
CtorTag(u32),
Lam(Box<ErasedExprExt>),
App(Box<ErasedExprExt>, Box<ErasedExprExt>),
Const(String),
Let(Box<ErasedExprExt>, Box<ErasedExprExt>),
TypeErased,
Unit,
}
impl ErasedExprExt {
#[allow(dead_code)]
pub fn is_lit(&self) -> bool {
matches!(self, ErasedExprExt::Lit(_))
}
#[allow(dead_code)]
pub fn is_lam(&self) -> bool {
matches!(self, ErasedExprExt::Lam(_))
}
#[allow(dead_code)]
pub fn is_app(&self) -> bool {
matches!(self, ErasedExprExt::App(_, _))
}
#[allow(dead_code)]
pub fn is_type_erased(&self) -> bool {
*self == ErasedExprExt::TypeErased
}
#[allow(dead_code)]
pub fn size(&self) -> usize {
match self {
ErasedExprExt::Lam(b) => 1 + b.size(),
ErasedExprExt::App(f, x) => 1 + f.size() + x.size(),
ErasedExprExt::Let(v, b) => 1 + v.size() + b.size(),
_ => 1,
}
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasedDCE {
pub eliminated: u64,
}
#[allow(dead_code)]
impl ErasedDCE {
pub fn new() -> Self {
ErasedDCE { eliminated: 0 }
}
pub fn elim(&mut self, expr: ErasedExprExt) -> ErasedExprExt {
match expr {
ErasedExprExt::App(f, x) => {
let f = self.elim(*f);
let x = self.elim(*x);
if x == ErasedExprExt::TypeErased {
self.eliminated += 1;
f
} else {
ErasedExprExt::App(Box::new(f), Box::new(x))
}
}
ErasedExprExt::Lam(b) => {
let b = self.elim(*b);
if b == ErasedExprExt::TypeErased {
self.eliminated += 1;
ErasedExprExt::TypeErased
} else {
ErasedExprExt::Lam(Box::new(b))
}
}
ErasedExprExt::Let(v, b) => {
let v = self.elim(*v);
let b = self.elim(*b);
ErasedExprExt::Let(Box::new(v), Box::new(b))
}
other => other,
}
}
}
#[allow(dead_code)]
pub struct ErasedStack {
stack: Vec<ErasedExpr>,
}
#[allow(dead_code)]
impl ErasedStack {
pub fn new() -> Self {
ErasedStack { stack: Vec::new() }
}
pub fn push(&mut self, expr: ErasedExpr) {
self.stack.push(expr);
}
pub fn pop(&mut self) -> Option<ErasedExpr> {
self.stack.pop()
}
pub fn peek(&self) -> Option<&ErasedExpr> {
self.stack.last()
}
pub fn depth(&self) -> usize {
self.stack.len()
}
pub fn is_empty(&self) -> bool {
self.stack.is_empty()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasedFlatApp {
pub head: ErasedExpr,
pub args: Vec<ErasedExpr>,
}
#[allow(dead_code)]
impl ErasedFlatApp {
pub fn from_expr(mut expr: ErasedExpr) -> Self {
let mut args = Vec::new();
loop {
match expr {
ErasedExpr::App(f, a) => {
args.push(*a);
expr = *f;
}
other => {
args.reverse();
return Self { head: other, args };
}
}
}
}
pub fn into_expr(self) -> ErasedExpr {
let mut result = self.head;
for arg in self.args {
result = ErasedExpr::App(Box::new(result), Box::new(arg));
}
result
}
pub fn arity(&self) -> usize {
self.args.len()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasedInterpreter {
pub steps: u64,
pub max_steps: u64,
scope: ErasedScope,
}
#[allow(dead_code)]
impl ErasedInterpreter {
pub fn new(max_steps: u64) -> Self {
ErasedInterpreter {
steps: 0,
max_steps,
scope: ErasedScope::new(),
}
}
pub fn eval(&mut self, expr: ErasedExprExt) -> Option<ErasedValue> {
if self.steps >= self.max_steps {
return None;
}
self.steps += 1;
match expr {
ErasedExprExt::Lit(n) => Some(ErasedValue::Int(n)),
ErasedExprExt::Unit => Some(ErasedValue::Unit),
ErasedExprExt::TypeErased => Some(ErasedValue::Unit),
ErasedExprExt::Lam(b) => Some(ErasedValue::Closure {
body: b,
env: Vec::new(),
}),
ErasedExprExt::BVar(_) => None,
ErasedExprExt::FVar(name) => self.scope.lookup(&name).cloned(),
ErasedExprExt::Const(_) => None,
ErasedExprExt::CtorTag(t) => Some(ErasedValue::Ctor(t, Vec::new())),
ErasedExprExt::App(f, x) => {
let f_val = self.eval(*f)?;
let x_val = self.eval(*x)?;
match f_val {
ErasedValue::Closure { body, env: _ } => {
let subst =
subst_bvar0(*body, ErasedExprExt::Lit(x_val.as_int().unwrap_or(0)));
self.eval(subst)
}
ErasedValue::Ctor(t, mut fields) => {
fields.push(x_val);
Some(ErasedValue::Ctor(t, fields))
}
_ => None,
}
}
ErasedExprExt::Let(v, b) => {
let v_val = self.eval(*v)?;
let subst = subst_bvar0(*b, ErasedExprExt::Lit(v_val.as_int().unwrap_or(0)));
self.eval(subst)
}
}
}
pub fn is_exhausted(&self) -> bool {
self.steps >= self.max_steps
}
}
#[allow(dead_code)]
pub struct ErasedConstantPool {
pool: Vec<i64>,
index: std::collections::HashMap<i64, usize>,
}
#[allow(dead_code)]
impl ErasedConstantPool {
pub fn new() -> Self {
Self {
pool: Vec::new(),
index: std::collections::HashMap::new(),
}
}
pub fn intern(&mut self, val: i64) -> usize {
if let Some(&idx) = self.index.get(&val) {
return idx;
}
let idx = self.pool.len();
self.pool.push(val);
self.index.insert(val, idx);
idx
}
pub fn get(&self, idx: usize) -> Option<i64> {
self.pool.get(idx).copied()
}
pub fn len(&self) -> usize {
self.pool.len()
}
pub fn is_empty(&self) -> bool {
self.pool.is_empty()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasedPrinter {
pub output: String,
depth: usize,
}
#[allow(dead_code)]
impl ErasedPrinter {
pub fn new() -> Self {
ErasedPrinter {
output: String::new(),
depth: 0,
}
}
pub fn print(&mut self, expr: &ErasedExprExt) {
let s = pretty_print_erased(expr);
for _ in 0..self.depth {
self.output.push_str(" ");
}
self.output.push_str(&s);
self.output.push('\n');
}
pub fn result(&self) -> &str {
&self.output
}
pub fn clear(&mut self) {
self.output.clear();
self.depth = 0;
}
}
pub struct TypeEraser {
config: EraseConfig,
}
impl TypeEraser {
pub fn new() -> Self {
TypeEraser {
config: EraseConfig::default(),
}
}
pub fn with_config(config: EraseConfig) -> Self {
TypeEraser { config }
}
pub fn erase_sort(&self) -> ErasedExpr {
ErasedExpr::TypeErased
}
pub fn erase_pi(&self) -> ErasedExpr {
ErasedExpr::TypeErased
}
pub fn erase_lam_body(&self, body: ErasedExpr) -> ErasedExpr {
ErasedExpr::Lam(Box::new(body))
}
pub fn erase_app(f: ErasedExpr, arg: ErasedExpr) -> ErasedExpr {
ErasedExpr::App(Box::new(f), Box::new(arg))
}
pub fn erase_lit(n: u64) -> ErasedExpr {
ErasedExpr::Lit(n)
}
pub fn optimize(&self, expr: ErasedExpr) -> ErasedExpr {
match expr {
ErasedExpr::App(f, arg) => {
let f_opt = self.optimize(*f);
let arg_opt = self.optimize(*arg);
match f_opt {
ErasedExpr::TypeErased => ErasedExpr::TypeErased,
_ => {
if !self.config.keep_props && arg_opt == ErasedExpr::TypeErased {
if let ErasedExpr::Lam(body) = f_opt {
return self.optimize(subst_bvar(
*body,
0,
&ErasedExpr::TypeErased,
));
}
}
ErasedExpr::App(Box::new(f_opt), Box::new(arg_opt))
}
}
}
ErasedExpr::Lam(body) => ErasedExpr::Lam(Box::new(self.optimize(*body))),
ErasedExpr::Let(val, body) => ErasedExpr::Let(
Box::new(self.optimize(*val)),
Box::new(self.optimize(*body)),
),
other => other,
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ErasedPattern {
Wildcard,
Ctor(u32, Vec<ErasedPattern>),
Lit(u64),
Var(String),
}
impl ErasedPattern {
#[allow(dead_code)]
pub fn depth(&self) -> usize {
match self {
ErasedPattern::Ctor(_, pats) => 1 + pats.iter().map(|p| p.depth()).max().unwrap_or(0),
_ => 1,
}
}
#[allow(dead_code)]
pub fn is_irrefutable(&self) -> bool {
matches!(self, ErasedPattern::Wildcard | ErasedPattern::Var(_))
}
}
#[allow(dead_code)]
pub struct ErasedSubstMap {
map: std::collections::HashMap<u32, ErasedExpr>,
}
#[allow(dead_code)]
impl ErasedSubstMap {
pub fn new() -> Self {
Self {
map: std::collections::HashMap::new(),
}
}
pub fn insert(&mut self, i: u32, expr: ErasedExpr) {
self.map.insert(i, expr);
}
pub fn get(&self, i: u32) -> Option<&ErasedExpr> {
self.map.get(&i)
}
pub fn len(&self) -> usize {
self.map.len()
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct EraseConfig {
pub keep_props: bool,
pub inline_defs: bool,
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasedNormalizer {
pub beta_steps: u64,
pub const_folds: u64,
max_beta: u64,
}
#[allow(dead_code)]
impl ErasedNormalizer {
pub fn new(max_beta: u64) -> Self {
ErasedNormalizer {
beta_steps: 0,
const_folds: 0,
max_beta,
}
}
pub fn whnf(&mut self, expr: ErasedExpr) -> ErasedExpr {
if self.beta_steps >= self.max_beta {
return expr;
}
match expr {
ErasedExpr::App(f, x) => {
let f_whnf = self.whnf(*f);
match f_whnf {
ErasedExpr::Lam(_b) => {
self.beta_steps += 1;
self.whnf(*x)
}
other_f => ErasedExpr::App(Box::new(other_f), x),
}
}
other => other,
}
}
pub fn const_fold_add(&mut self, expr: ErasedExpr) -> ErasedExpr {
match &expr {
ErasedExpr::App(f, x) => {
if let ErasedExpr::App(g, a) = f.as_ref() {
if let ErasedExpr::Const(name) = g.as_ref() {
if name == "Nat.add" {
if let (ErasedExpr::Lit(av), ErasedExpr::Lit(bv)) =
(a.as_ref(), x.as_ref())
{
self.const_folds += 1;
return ErasedExpr::Lit(av.saturating_add(*bv));
}
}
}
}
expr
}
_ => expr,
}
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct AnfConverter {
fresh_counter: u64,
pub let_count: u64,
}
#[allow(dead_code)]
impl AnfConverter {
pub fn new() -> Self {
AnfConverter {
fresh_counter: 0,
let_count: 0,
}
}
pub fn fresh(&mut self) -> String {
let name = format!("_anf_{}", self.fresh_counter);
self.fresh_counter += 1;
name
}
pub fn convert(&mut self, expr: ErasedExprExt) -> ErasedExprExt {
match expr {
ErasedExprExt::App(f, x) => {
let f = self.convert(*f);
let x = self.convert(*x);
if !is_atom(&f) {
let _v = self.fresh();
self.let_count += 1;
ErasedExprExt::Let(
Box::new(f),
Box::new(ErasedExprExt::App(
Box::new(ErasedExprExt::BVar(0)),
Box::new(shift_up(x, 1)),
)),
)
} else {
ErasedExprExt::App(Box::new(f), Box::new(x))
}
}
ErasedExprExt::Lam(b) => ErasedExprExt::Lam(Box::new(self.convert(*b))),
other => other,
}
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
#[derive(Debug, Clone, PartialEq)]
pub enum ErasedValue {
Int(u64),
Ctor(u32, Vec<ErasedValue>),
Closure {
body: Box<ErasedExprExt>,
env: Vec<ErasedValue>,
},
Unit,
Thunk(Box<ErasedExprExt>),
}
#[allow(dead_code)]
impl ErasedValue {
pub fn is_int(&self) -> bool {
matches!(self, ErasedValue::Int(_))
}
pub fn is_ctor(&self) -> bool {
matches!(self, ErasedValue::Ctor(_, _))
}
pub fn as_int(&self) -> Option<u64> {
if let ErasedValue::Int(n) = self {
Some(*n)
} else {
None
}
}
}
#[allow(dead_code)]
pub struct ErasedBitOps;
#[allow(dead_code)]
impl ErasedBitOps {
pub fn fold_and(vals: &[u64]) -> u64 {
vals.iter().fold(u64::MAX, |acc, &v| acc & v)
}
pub fn fold_or(vals: &[u64]) -> u64 {
vals.iter().fold(0u64, |acc, &v| acc | v)
}
pub fn fold_binop<F: Fn(u64, u64) -> u64>(vals: &[u64], init: u64, f: F) -> u64 {
vals.iter().fold(init, |acc, &v| f(acc, v))
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasedRenamer {
map: Vec<(String, String)>,
pub renames: u64,
}
#[allow(dead_code)]
impl ErasedRenamer {
pub fn new(map: Vec<(String, String)>) -> Self {
ErasedRenamer { map, renames: 0 }
}
pub fn rename(&mut self, expr: ErasedExprExt) -> ErasedExprExt {
match expr {
ErasedExprExt::FVar(name) => {
if let Some(new) = self
.map
.iter()
.find(|(old, _)| old == &name)
.map(|(_, n)| n.clone())
{
self.renames += 1;
ErasedExprExt::FVar(new)
} else {
ErasedExprExt::FVar(name)
}
}
ErasedExprExt::Const(name) => {
if let Some(new) = self
.map
.iter()
.find(|(old, _)| old == &name)
.map(|(_, n)| n.clone())
{
self.renames += 1;
ErasedExprExt::Const(new)
} else {
ErasedExprExt::Const(name)
}
}
ErasedExprExt::Lam(b) => ErasedExprExt::Lam(Box::new(self.rename(*b))),
ErasedExprExt::App(f, x) => {
ErasedExprExt::App(Box::new(self.rename(*f)), Box::new(self.rename(*x)))
}
ErasedExprExt::Let(v, b) => {
ErasedExprExt::Let(Box::new(self.rename(*v)), Box::new(self.rename(*b)))
}
other => other,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ErasedExpr {
Var(String),
BVar(u32),
Lam(Box<ErasedExpr>),
App(Box<ErasedExpr>, Box<ErasedExpr>),
Let(Box<ErasedExpr>, Box<ErasedExpr>),
Const(String),
Lit(u64),
StrLit(String),
TypeErased,
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasedCallSite {
pub callee: String,
pub arity: usize,
pub is_tail: bool,
}
#[allow(dead_code)]
impl ErasedCallSite {
pub fn new(callee: impl Into<String>, arity: usize, is_tail: bool) -> Self {
Self {
callee: callee.into(),
arity,
is_tail,
}
}
pub fn is_self_tail(&self, current: &str) -> bool {
self.is_tail && self.callee == current
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasedSizeBound {
pub max_size: usize,
}
#[allow(dead_code)]
impl ErasedSizeBound {
pub fn new(max_size: usize) -> Self {
ErasedSizeBound { max_size }
}
pub fn check(&self, expr: &ErasedExprExt) -> bool {
expr.size() <= self.max_size
}
pub fn size_of(&self, expr: &ErasedExprExt) -> usize {
expr.size()
}
}
#[derive(Debug, Clone, Default)]
pub struct ErasureStats {
pub sorts_erased: usize,
pub pis_erased: usize,
pub terms_kept: usize,
}
impl ErasureStats {
pub fn new() -> Self {
ErasureStats::default()
}
pub fn add_sort(&mut self) {
self.sorts_erased += 1;
}
pub fn add_pi(&mut self) {
self.pis_erased += 1;
}
pub fn add_term(&mut self) {
self.terms_kept += 1;
}
pub fn ratio_erased(&self) -> f64 {
let total = self.sorts_erased + self.pis_erased + self.terms_kept;
if total == 0 {
0.0
} else {
(self.sorts_erased + self.pis_erased) as f64 / total as f64
}
}
}
#[allow(dead_code)]
pub struct ErasedReachability {
reachable: Vec<String>,
roots: Vec<String>,
}
#[allow(dead_code)]
impl ErasedReachability {
pub fn new() -> Self {
ErasedReachability {
reachable: Vec::new(),
roots: Vec::new(),
}
}
pub fn add_root(&mut self, name: &str) {
if !self.roots.contains(&name.to_string()) {
self.roots.push(name.to_string());
self.mark_reachable(name);
}
}
pub fn mark_reachable(&mut self, name: &str) {
if !self.reachable.contains(&name.to_string()) {
self.reachable.push(name.to_string());
}
}
pub fn is_reachable(&self, name: &str) -> bool {
self.reachable.contains(&name.to_string())
}
pub fn reachable_names(&self) -> &[String] {
&self.reachable
}
pub fn reachable_count(&self) -> usize {
self.reachable.len()
}
pub fn analyze_module(&mut self, module: &ErasedModule) {
let root_names = self.roots.clone();
for root in &root_names {
if let Some(ErasedDecl::Def { body, .. }) = module.find(root) {
for c in collect_consts(body) {
self.mark_reachable(&c);
}
}
}
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasedLetChain {
pub bindings: Vec<(String, ErasedExprExt)>,
pub body: ErasedExprExt,
}
#[allow(dead_code)]
impl ErasedLetChain {
pub fn new(body: ErasedExprExt) -> Self {
Self {
bindings: Vec::new(),
body,
}
}
pub fn push(&mut self, name: impl Into<String>, rhs: ErasedExprExt) {
self.bindings.push((name.into(), rhs));
}
pub fn into_expr(self) -> ErasedExprExt {
let mut result = self.body;
for (_name, rhs) in self.bindings.into_iter().rev() {
result = ErasedExprExt::Let(Box::new(rhs), Box::new(result));
}
result
}
pub fn len(&self) -> usize {
self.bindings.len()
}
pub fn is_empty(&self) -> bool {
self.bindings.is_empty()
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
pub struct ErasedConstFolder {
pub folds: u64,
}
#[allow(dead_code)]
impl ErasedConstFolder {
pub fn new() -> Self {
ErasedConstFolder { folds: 0 }
}
pub fn fold_add(&mut self, expr: ErasedExprExt) -> ErasedExprExt {
match expr {
ErasedExprExt::App(f, x) => {
let f = self.fold_add(*f);
let x = self.fold_add(*x);
match (&f, &x) {
(ErasedExprExt::App(g, a), ErasedExprExt::Lit(b)) => {
if let ErasedExprExt::Const(name) = g.as_ref() {
if name == "Nat.add" {
if let ErasedExprExt::Lit(av) = a.as_ref() {
self.folds += 1;
return ErasedExprExt::Lit(av.saturating_add(*b));
}
}
}
ErasedExprExt::App(
Box::new(ErasedExprExt::App(
Box::new(*g.clone()),
Box::new(*a.clone()),
)),
Box::new(x),
)
}
_ => ErasedExprExt::App(Box::new(f), Box::new(x)),
}
}
ErasedExprExt::Lam(b) => ErasedExprExt::Lam(Box::new(self.fold_add(*b))),
other => other,
}
}
}
#[allow(dead_code)]
#[allow(missing_docs)]
#[derive(Debug, Clone)]
pub enum ErasedDecl {
Def { name: String, body: ErasedExprExt },
Axiom { name: String },
Inductive { name: String, ctor_count: u32 },
}
#[allow(dead_code)]
impl ErasedDecl {
pub fn name(&self) -> &str {
match self {
ErasedDecl::Def { name, .. } => name,
ErasedDecl::Axiom { name } => name,
ErasedDecl::Inductive { name, .. } => name,
}
}
pub fn is_def(&self) -> bool {
matches!(self, ErasedDecl::Def { .. })
}
}
#[allow(dead_code)]
pub struct ErasedScope {
vars: Vec<(String, ErasedValue)>,
}
#[allow(dead_code)]
impl ErasedScope {
pub fn new() -> Self {
ErasedScope { vars: Vec::new() }
}
pub fn bind(&mut self, name: &str, val: ErasedValue) {
self.vars.push((name.to_string(), val));
}
pub fn lookup(&self, name: &str) -> Option<&ErasedValue> {
self.vars
.iter()
.rev()
.find(|(n, _)| n == name)
.map(|(_, v)| v)
}
pub fn depth(&self) -> usize {
self.vars.len()
}
pub fn save(&self) -> usize {
self.vars.len()
}
pub fn restore(&mut self, checkpoint: usize) {
self.vars.truncate(checkpoint);
}
}