use {
std::fmt,
};
#[derive(Debug, Clone, Copy, PartialEq)]
enum Child {
None,
Node(usize),
Atom(usize),
}
impl Child {
pub fn is_some(self) -> bool {
match self {
Self::None => false,
_ => true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
enum TokenType {
Nothing,
Atom,
Operator,
OpeningPar,
ClosingPar,
}
#[derive(Debug, Clone)]
struct Node<Op>
where
Op: fmt::Debug + Clone + PartialEq,
{
operator: Option<Op>,
parent: Option<usize>,
left: Child,
right: Child,
unary: bool,
}
impl<Op> Node<Op>
where
Op: fmt::Debug + Clone + PartialEq,
{
fn is_full(&self) -> bool {
if self.unary {
self.left.is_some()
} else {
self.right.is_some()
}
}
fn empty() -> Self {
Self {
operator: None,
parent: None,
left: Child::None,
right: Child::None,
unary: false,
}
}
}
#[derive(Debug, Clone)]
pub struct BeTree<Op, Atom>
where
Op: fmt::Debug + Clone + PartialEq,
Atom: fmt::Debug + Clone,
{
atoms: Vec<Atom>,
nodes: Vec<Node<Op>>,
head: usize,
tail: usize,
last_pushed: TokenType,
op_count: usize,
openess: usize,
}
impl<Op, Atom> Default for BeTree<Op, Atom>
where
Op: fmt::Debug + Clone + PartialEq,
Atom: fmt::Debug + Clone,
{
fn default() -> Self {
Self {
atoms: Vec::new(),
nodes: vec![Node::empty()],
head: 0,
tail: 0,
last_pushed: TokenType::Nothing,
op_count: 0,
openess: 0,
}
}
}
impl<Op, Atom> BeTree<Op, Atom>
where
Op: fmt::Debug + Clone + PartialEq,
Atom: fmt::Debug + Clone,
{
pub fn new() -> Self {
Self::default()
}
pub fn is_empty(&self) -> bool {
self.atoms.is_empty()
}
pub fn is_atomic(&self) -> bool {
self.atoms.len() == 1 && self.op_count == 0
}
pub fn atoms(self) -> Vec<Atom> {
self.atoms
}
pub fn iter_atoms<'a>(&'a self) -> std::slice::Iter<'a, Atom> {
self.atoms.iter()
}
pub fn current_atom<'a>(&'a self) -> Option<&'a Atom> {
if self.last_pushed == TokenType::Atom {
self.atoms.last()
} else {
None
}
}
fn store_node(&mut self, node: Node<Op>) -> usize {
self.nodes.push(node);
self.nodes.len() - 1
}
fn store_atom(&mut self, atom: Atom) -> usize {
self.atoms.push(atom);
self.atoms.len() - 1
}
fn add_child(&mut self, child: Child) {
debug_assert!(!self.nodes[self.tail].is_full());
if !self.nodes[self.tail].left.is_some() {
self.nodes[self.tail].left = child;
} else {
self.nodes[self.tail].right = child;
}
}
fn add_child_node(&mut self, child_idx: usize) {
self.nodes[child_idx].parent = Some(self.tail);
self.add_child(Child::Node(child_idx));
self.tail = child_idx;
}
pub fn push_atom(&mut self, atom: Atom) {
self.last_pushed = TokenType::Atom;
let atom_idx = self.store_atom(atom);
if !self.nodes[self.tail].left.is_some() {
self.nodes[self.tail].left = Child::Atom(atom_idx);
} else {
self.nodes[self.tail].right = Child::Atom(atom_idx);
}
}
pub fn mutate_or_create_atom<Create>(&mut self, create: Create) -> &mut Atom
where
Create: Fn() -> Atom,
{
if self.last_pushed != TokenType::Atom {
self.push_atom(create());
}
self.atoms.last_mut().unwrap()
}
pub fn open_par(&mut self) {
self.last_pushed = TokenType::OpeningPar;
let node_idx = self.store_node(Node::empty());
self.add_child_node(node_idx);
self.openess += 1;
}
pub fn close_par(&mut self) {
self.last_pushed = TokenType::ClosingPar;
if let Some(parent) = self.nodes[self.tail].parent {
self.tail = parent;
self.openess -= 1;
}
}
fn push_unary_operator(&mut self, operator: Op) {
let node_idx = self.store_node(Node {
operator: Some(operator),
parent: Some(self.tail),
left: Child::None,
right: Child::None,
unary: true,
});
self.add_child(Child::Node(node_idx));
self.tail = node_idx;
}
fn push_binary_operator(&mut self, operator: Op) {
if self.nodes[self.tail].is_full() {
let new_idx = self.store_node(Node {
operator: Some(operator),
parent: self.nodes[self.tail].parent,
left: Child::Node(self.tail),
right: Child::None,
unary: false,
});
if let Some(parent_idx) = self.nodes[new_idx].parent {
if self.nodes[parent_idx].left == Child::Node(self.tail) {
self.nodes[parent_idx].left = Child::Node(new_idx);
} else {
debug_assert_eq!(self.nodes[parent_idx].right, Child::Node(self.tail));
self.nodes[parent_idx].right = Child::Node(new_idx);
}
} else {
self.head = new_idx;
}
self.nodes[self.tail].parent = Some(new_idx);
self.tail = new_idx;
} else {
self.nodes[self.tail].operator = Some(operator);
}
}
pub fn push_operator(&mut self, operator: Op) {
match self.last_pushed {
TokenType::Atom | TokenType::ClosingPar => {
self.push_binary_operator(operator);
}
_ => {
self.push_unary_operator(operator);
}
}
self.last_pushed = TokenType::Operator;
self.op_count += 1;
}
pub fn accept_unary_operator(&self) -> bool {
use TokenType::*;
match self.last_pushed {
Nothing | Operator | OpeningPar => true,
_ => false,
}
}
pub fn accept_binary_operator(&self) -> bool {
use TokenType::*;
match self.last_pushed {
Atom | ClosingPar => true,
_ => false,
}
}
pub fn accept_atom(&self) -> bool {
use TokenType::*;
match self.last_pushed {
Nothing | Operator | OpeningPar => true,
_ => false,
}
}
pub fn accept_opening_par(&self) -> bool {
use TokenType::*;
match self.last_pushed {
Nothing | Operator | OpeningPar => true,
_ => false,
}
}
pub fn accept_closing_par(&self) -> bool {
use TokenType::*;
match self.last_pushed {
Atom | ClosingPar if self.openess == 0 => true,
_ => false,
}
}
pub fn try_map_atoms<Atom2, Err, F>(&self, f: F) -> Result<BeTree<Op, Atom2>, Err>
where
Atom2: fmt::Debug + Clone,
F: Fn(&Atom) -> Result<Atom2, Err>,
{
let mut atoms = Vec::new();
for atom in &self.atoms {
atoms.push(f(atom)?);
}
Ok(BeTree {
atoms,
nodes: self.nodes.clone(),
head: self.head,
tail: self.tail,
last_pushed: self.last_pushed,
op_count: self.op_count,
openess: self.openess,
})
}
fn eval_child<Err, R, EvalAtom, EvalOp, ShortCircuit>(
&self,
eval_atom: &EvalAtom,
eval_op: &EvalOp,
short_circuit: &ShortCircuit,
child: Child,
) -> Result<Option<R>, Err>
where
EvalAtom: Fn(&Atom) -> Result<R, Err>,
EvalOp: Fn(&Op, R, Option<R>) -> Result<R, Err>,
ShortCircuit: Fn(&Op, &R) -> bool,
{
Ok(match child {
Child::None => None,
Child::Node(node_idx) => self.eval_node(eval_atom, eval_op, short_circuit, node_idx)?,
Child::Atom(atom_idx) => Some(eval_atom(&self.atoms[atom_idx])?),
})
}
fn eval_node<Err, R, EvalAtom, EvalOp, ShortCircuit>(
&self,
eval_atom: &EvalAtom,
eval_op: &EvalOp,
short_circuit: &ShortCircuit,
node_idx: usize,
) -> Result<Option<R>, Err>
where
EvalAtom: Fn(&Atom) -> Result<R, Err>,
EvalOp: Fn(&Op, R, Option<R>) -> Result<R, Err>,
ShortCircuit: Fn(&Op, &R) -> bool,
{
let node = &self.nodes[node_idx];
let left_value = self.eval_child(eval_atom, eval_op, short_circuit, node.left)?;
Ok(
if let Some(op) = &node.operator {
if let Some(left_value) = left_value {
if short_circuit(op, &left_value) {
Some(left_value)
} else {
let right_value = self.eval_child(eval_atom, eval_op, short_circuit, node.right)?;
Some(eval_op(op, left_value, right_value)?)
}
} else {
None
}
} else {
left_value
}
)
}
pub fn eval<Err, R, EvalAtom, EvalOp, ShortCircuit>(
&self,
eval_atom: EvalAtom,
eval_op: EvalOp,
short_circuit: ShortCircuit,
) -> Result<Option<R>, Err>
where
EvalAtom: Fn(&Atom) -> Result<R, Err>,
EvalOp: Fn(&Op, R, Option<R>) -> Result<R, Err>,
ShortCircuit: Fn(&Op, &R) -> bool,
{
self.eval_node(&eval_atom, &eval_op, &short_circuit, self.head)
}
}