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 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(ref mut abstracted) => abstracted._apply(rhs, depth + 1),
App(boxed) => {
let (ref mut lhs_lhs, ref mut lhs_rhs) = **boxed;
lhs_lhs._apply(rhs, depth);
lhs_rhs._apply(rhs, depth)
}
}
}
fn update_free_variables(&mut self, added_depth: usize, own_depth: usize) {
match self {
Var(ref mut i) => {
if *i > own_depth {
*i += added_depth
}
}
Abs(ref mut abstracted) => abstracted.update_free_variables(added_depth, own_depth + 1),
App(boxed) => {
let (ref mut lhs, ref mut rhs) = **boxed;
lhs.update_free_variables(added_depth, own_depth);
rhs.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(&rhs).unwrap(); *self = lhs;
*count += 1;
}
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);
}
}
_ => (),
}
}
}
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",
}
)
}
}