pub use self::Order::*;
use crate::term::Term::*;
use crate::term::{Term, TermError};
use std::{cmp, fmt, mem};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Order {
NOR,
CBN,
HSP,
HNO,
APP,
CBV,
HAP,
}
pub fn eta(mut term: Term, limit: usize) -> Term {
term.eta(limit);
term
}
pub fn beta(mut term: Term, order: Order, limit: usize) -> Term {
term.reduce(order, limit);
term
}
impl Term {
pub fn apply(&mut self, rhs: &Term) -> Result<(), TermError> {
self.unabs_ref()?;
self._apply(rhs, 0);
let ret = mem::replace(self, Var(0)); *self = ret.unabs().unwrap();
Ok(())
}
fn _apply(&mut self, rhs: &Term, depth: usize) {
match self {
Var(i) => match (*i).cmp(&depth) {
cmp::Ordering::Equal => {
rhs.clone_into(self); self.update_free_variables(depth - 1, 0); }
cmp::Ordering::Greater => {
*self = Var(*i - 1); }
_ => {}
},
Abs(t) => t._apply(rhs, depth + 1),
App(boxed) => {
boxed.0._apply(rhs, depth);
boxed.1._apply(rhs, depth);
}
}
}
fn update_free_variables(&mut self, added_depth: usize, own_depth: usize) {
if added_depth == 0 {
return;
}
match self {
Var(i) => {
if *i > own_depth {
*i += added_depth
}
}
Abs(t) => t.update_free_variables(added_depth, own_depth + 1),
App(boxed) => {
boxed.0.update_free_variables(added_depth, own_depth);
boxed.1.update_free_variables(added_depth, own_depth);
}
}
}
fn eval(&mut self, count: &mut usize) {
let to_apply = mem::replace(self, Var(0)); let (mut lhs, rhs) = to_apply.unapp().unwrap(); lhs.apply_owned(rhs); *self = lhs;
*count += 1;
}
fn apply_owned(&mut self, rhs: Term) {
debug_assert!(self.unabs_ref().is_ok());
let mut remaining = self.count_occurrences(0);
if remaining == 0 {
drop(rhs);
self._apply_owned(&mut None, 0, &mut 0);
} else {
self._apply_owned(&mut Some(rhs), 0, &mut remaining);
}
let ret = mem::replace(self, Var(0)); *self = ret.unabs().unwrap(); }
fn count_occurrences(&self, depth: usize) -> usize {
match self {
Var(i) => (*i == depth) as usize,
Abs(t) => t.count_occurrences(depth + 1),
App(boxed) => boxed.0.count_occurrences(depth) + boxed.1.count_occurrences(depth),
}
}
fn _apply_owned(&mut self, rhs: &mut Option<Term>, depth: usize, remaining: &mut usize) {
match self {
Var(i) => match (*i).cmp(&depth) {
cmp::Ordering::Equal => {
*remaining -= 1;
if *remaining == 0 {
*self = rhs.take().expect("occurrence count is exact");
} else {
rhs.as_ref().expect("occurrences remain").clone_into(self);
}
self.update_free_variables(depth - 1, 0); }
cmp::Ordering::Greater => {
*self = Var(*i - 1); }
_ => {}
},
Abs(t) => t._apply_owned(rhs, depth + 1, remaining),
App(boxed) => {
boxed.0._apply_owned(rhs, depth, remaining);
boxed.1._apply_owned(rhs, depth, remaining);
}
}
}
fn is_reducible(&self, limit: usize, count: usize) -> bool {
self.lhs_ref().and_then(|t| t.unabs_ref()).is_ok() && (limit == 0 || count < limit)
}
pub fn reduce(&mut self, order: Order, limit: usize) -> usize {
let mut count = 0;
match order {
CBN => self.beta_cbn(limit, &mut count),
NOR => self.beta_nor(limit, &mut count),
CBV => self.beta_cbv(limit, &mut count),
APP => self.beta_app(limit, &mut count),
HSP => self.beta_hsp(limit, &mut count),
HNO => self.beta_hno(limit, &mut count),
HAP => self.beta_hap(limit, &mut count),
}
count
}
fn beta_cbn(&mut self, limit: usize, count: &mut usize) {
if limit != 0 && *count == limit {
return;
}
if let App(_) = *self {
self.lhs_mut().unwrap().beta_cbn(limit, count);
if self.is_reducible(limit, *count) {
self.eval(count);
self.beta_cbn(limit, count);
}
}
}
fn beta_nor(&mut self, limit: usize, count: &mut usize) {
if limit != 0 && *count == limit {
return;
}
match *self {
Abs(ref mut abstracted) => abstracted.beta_nor(limit, count),
App(_) => {
self.lhs_mut().unwrap().beta_cbn(limit, count);
if self.is_reducible(limit, *count) {
self.eval(count);
self.beta_nor(limit, count);
} else {
self.lhs_mut().unwrap().beta_nor(limit, count);
self.rhs_mut().unwrap().beta_nor(limit, count);
}
}
_ => (),
}
}
fn beta_cbv(&mut self, limit: usize, count: &mut usize) {
if limit != 0 && *count == limit {
return;
}
if let App(_) = *self {
self.lhs_mut().unwrap().beta_cbv(limit, count);
self.rhs_mut().unwrap().beta_cbv(limit, count);
if self.is_reducible(limit, *count) {
self.eval(count);
self.beta_cbv(limit, count);
}
}
}
fn beta_app(&mut self, limit: usize, count: &mut usize) {
if limit != 0 && *count == limit {
return;
}
match *self {
Abs(ref mut abstracted) => abstracted.beta_app(limit, count),
App(_) => {
self.lhs_mut().unwrap().beta_app(limit, count);
self.rhs_mut().unwrap().beta_app(limit, count);
if self.is_reducible(limit, *count) {
self.eval(count);
self.beta_app(limit, count);
}
}
_ => (),
}
}
fn beta_hap(&mut self, limit: usize, count: &mut usize) {
if limit != 0 && *count == limit {
return;
}
match *self {
Abs(ref mut abstracted) => abstracted.beta_hap(limit, count),
App(_) => {
self.lhs_mut().unwrap().beta_cbv(limit, count);
self.rhs_mut().unwrap().beta_hap(limit, count);
if self.is_reducible(limit, *count) {
self.eval(count);
self.beta_hap(limit, count);
} else {
self.lhs_mut().unwrap().beta_hap(limit, count);
}
}
_ => (),
}
}
fn beta_hsp(&mut self, limit: usize, count: &mut usize) {
if limit != 0 && *count == limit {
return;
}
match *self {
Abs(ref mut abstracted) => abstracted.beta_hsp(limit, count),
App(_) => {
self.lhs_mut().unwrap().beta_hsp(limit, count);
if self.is_reducible(limit, *count) {
self.eval(count);
self.beta_hsp(limit, count)
}
}
_ => (),
}
}
fn beta_hno(&mut self, limit: usize, count: &mut usize) {
if limit != 0 && *count == limit {
return;
}
match *self {
Abs(ref mut abstracted) => abstracted.beta_hno(limit, count),
App(_) => {
self.lhs_mut().unwrap().beta_hsp(limit, count);
if self.is_reducible(limit, *count) {
self.eval(count);
self.beta_hno(limit, count)
} else {
self.lhs_mut().unwrap().beta_hno(limit, count);
self.rhs_mut().unwrap().beta_hno(limit, count);
}
}
_ => (),
}
}
fn _refers_to_binder(&self, depth: usize) -> bool {
match self {
Var(0) => true,
Var(i) => *i == depth,
Abs(t) => t._refers_to_binder(depth + 1),
App(boxed) => boxed.0._refers_to_binder(depth) || boxed.1._refers_to_binder(depth),
}
}
fn _shift_down_eta(&mut self, depth: usize) {
match self {
Var(0) => {}
Var(i) => {
if *i > depth {
*i -= 1;
}
}
Abs(t) => t._shift_down_eta(depth + 1),
App(boxed) => {
boxed.0._shift_down_eta(depth);
boxed.1._shift_down_eta(depth);
}
}
}
pub fn eta(&mut self, limit: usize) -> usize {
let mut count = 0;
self._eta_impl(limit, &mut count);
count
}
fn _eta_step(&mut self, limit: usize, count: &mut usize) -> bool {
if limit != 0 && *count == limit {
return false;
}
let can_eta = match self {
Abs(boxed) => match &**boxed {
App(inner) => match &inner.1 {
Var(1) => !inner.0._refers_to_binder(1),
_ => false,
},
_ => false,
},
_ => false,
};
if can_eta {
let body = mem::replace(self, Var(0)).unabs().unwrap();
let (mut lhs, _rhs) = body.unapp().unwrap();
lhs._shift_down_eta(1);
*self = lhs;
*count += 1;
true
} else {
false
}
}
fn _eta_impl(&mut self, limit: usize, count: &mut usize) {
loop {
if !self._eta_step(limit, count) {
break;
}
}
match self {
Abs(boxed) => {
boxed._eta_impl(limit, count);
loop {
if !self._eta_step(limit, count) {
break;
}
}
}
App(boxed) => {
boxed.0._eta_impl(limit, count);
boxed.1._eta_impl(limit, count);
}
_ => {}
}
}
}
impl fmt::Display for Order {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match *self {
NOR => "normal",
CBN => "call-by-name",
HSP => "head spine",
HNO => "hybrid normal",
APP => "applicative",
CBV => "call-by-value",
HAP => "hybrid applicative",
}
)
}
}