use std::mem;
use std::ops::{Index, IndexMut};
use crate::microcode::Microcode;
pub use path::{ElementIndex, ElementPath, ElementPathBuf, ElementSelector};
mod path;
#[derive(Debug, Clone)]
pub enum Element {
Microcode(Microcode),
Block(Block),
Branch(Branch),
}
impl Element {
pub fn flatten(&self) -> Vec<Microcode> {
match self {
Self::Microcode(microcode) => vec![*microcode],
Self::Block(block) => block.flatten(),
Self::Branch(branch) => branch.flatten(),
}
}
pub fn extend_block(&mut self, other: Element) {
match (self, other) {
(Self::Block(ref mut self_block), Self::Block(mut other_block)) => {
self_block.elements.append(&mut other_block.elements)
}
(Self::Block(ref mut self_block), other) => self_block.elements.push(other),
(this, mut other @ Self::Block(_)) => {
mem::swap(this, &mut other);
match this {
Self::Block(ref mut old_other_block) => {
old_other_block
.elements
.insert(0, other)
}
_ => unreachable!(),
}
}
(this, other) => {
let old_self = mem::replace(
this,
Element::Block(Block {
elements: Vec::with_capacity(2),
}),
);
match this {
Self::Block(ref mut self_block) => {
self_block.elements.push(old_self);
self_block.elements.push(other);
}
_ => unreachable!(),
}
}
}
}
pub fn ends_with_terminal(&self) -> bool {
match self {
Element::Microcode(microcode) => microcode.is_terminal(),
Element::Block(block) => match block.elements.last() {
Some(last) => last.ends_with_terminal(),
None => false,
},
Element::Branch(Branch {
code_if_true,
code_if_false,
}) => code_if_true.ends_with_terminal() && code_if_false.ends_with_terminal(),
}
}
pub fn get<I>(&self, index: I) -> Option<&Element>
where
I: ElementIndex,
{
let mut elem = self;
for selector in index.path_selectors() {
match elem {
Element::Microcode(_) => panic!("Cannot index into Microcode elements"),
Element::Block(block) => elem = block.get(selector)?,
Element::Branch(branch) => elem = branch.get(selector),
}
}
Some(elem)
}
pub fn get_mut<I>(&mut self, index: I) -> Option<&mut Element>
where
I: ElementIndex,
{
let mut elem = self;
for selector in index.path_selectors() {
match elem {
Element::Microcode(_) => panic!("Cannot index into Microcode elements"),
Element::Block(block) => elem = block.get_mut(selector)?,
Element::Branch(branch) => elem = branch.get_mut(selector),
}
}
Some(elem)
}
}
impl Default for Element {
#[inline]
fn default() -> Self {
Self::Block(Default::default())
}
}
impl<I: ElementIndex> Index<I> for Element {
type Output = Element;
#[inline]
fn index(&self, index: I) -> &Self::Output {
self.get(index).expect("Index out of Bounds")
}
}
impl<I: ElementIndex> IndexMut<I> for Element {
#[inline]
fn index_mut(&mut self, index: I) -> &mut Self::Output {
self.get_mut(index).expect("Index out of Bounds")
}
}
#[derive(Default, Debug, Clone)]
pub struct Block {
pub elements: Vec<Element>,
}
impl Block {
pub fn flatten(&self) -> Vec<Microcode> {
self.elements
.iter()
.flat_map(|elem| elem.flatten().into_iter())
.collect()
}
pub fn get(&self, selector: ElementSelector) -> Option<&Element> {
match selector {
ElementSelector::Block(idx) => self.elements.get(idx),
ElementSelector::Branch(_) => panic!("Cannot index a Block with a Branch selector"),
}
}
pub fn get_mut(&mut self, selector: ElementSelector) -> Option<&mut Element> {
match selector {
ElementSelector::Block(idx) => self.elements.get_mut(idx),
ElementSelector::Branch(_) => panic!("Cannot index a Block with a Branch selector"),
}
}
}
#[derive(Debug, Clone)]
pub struct Branch {
pub code_if_true: Box<Element>,
pub code_if_false: Box<Element>,
}
impl Branch {
pub fn flatten(&self) -> Vec<Microcode> {
let code_if_true = self.code_if_true.flatten();
let code_if_false = self.code_if_false.flatten();
if code_if_true.is_empty() && code_if_false.is_empty() {
vec![Microcode::Discard8]
} else if code_if_true.is_empty() {
let steps = code_if_false.len();
let mut res = code_if_false;
res.insert(0, Microcode::SkipIf { steps });
res
} else if code_if_false.is_empty() {
let steps = code_if_true.len();
let mut res = code_if_true;
res.splice(0..0, [Microcode::Not, Microcode::SkipIf { steps }]);
res
} else {
let true_steps = code_if_true.len();
let false_steps = code_if_false.len() + 1;
let mut res = code_if_false;
res.insert(0, Microcode::SkipIf { steps: false_steps });
res.push(Microcode::Skip { steps: true_steps });
res.extend_from_slice(&code_if_true);
res
}
}
pub fn get(&self, selector: ElementSelector) -> &Element {
match selector {
ElementSelector::Block(_) => panic!("Cannot index a Branch with a Block selector"),
ElementSelector::Branch(true) => &self.code_if_true,
ElementSelector::Branch(false) => &self.code_if_false,
}
}
pub fn get_mut(&mut self, selector: ElementSelector) -> &mut Element {
match selector {
ElementSelector::Block(_) => panic!("Cannot index a Branch with a Block selector"),
ElementSelector::Branch(true) => &mut self.code_if_true,
ElementSelector::Branch(false) => &mut self.code_if_false,
}
}
}