pub use self::selector::{CFSelector, CFSelectorSegment};
use std::ops::{Index, IndexMut};
mod selector;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ControlFlowElement {
Block {
content: Vec<ControlFlowElement>,
},
If {
condition: Box<ControlFlowElement>,
on_success: Vec<ControlFlowElement>,
on_failure: Vec<ControlFlowElement>,
},
Loop {
content: Vec<ControlFlowElement>,
},
BasicBlock {
id: usize,
},
}
impl ControlFlowElement {
pub fn new_node(index: usize) -> Self {
Self::BasicBlock { id: index }
}
pub fn new_block(content: Vec<ControlFlowElement>) -> Self {
Self::Block { content }
}
pub fn first_basic_block_id(&self) -> usize {
match self {
ControlFlowElement::Block { content } => content[0].first_basic_block_id(),
ControlFlowElement::If { condition, .. } => condition.first_basic_block_id(),
ControlFlowElement::Loop { content } => content[0].first_basic_block_id(),
ControlFlowElement::BasicBlock { id: node_id } => *node_id,
}
}
pub fn first_basic_block_selector(&self) -> CFSelector {
match self {
ControlFlowElement::Block { content } | ControlFlowElement::Loop { content } => {
let mut result = content[0].first_basic_block_selector();
result.push_front(CFSelectorSegment::ContentAtIndex(0));
result
}
ControlFlowElement::If { .. } => {
CFSelector::from_segment(CFSelectorSegment::IfCondition)
}
ControlFlowElement::BasicBlock { .. } => CFSelector::new_empty(),
}
}
fn select_mut_by_segment(&mut self, segment: CFSelectorSegment) -> &mut ControlFlowElement {
match (self, segment) {
(ControlFlowElement::Block { content }, CFSelectorSegment::ContentAtIndex(index))
| (ControlFlowElement::Loop { content }, CFSelectorSegment::ContentAtIndex(index)) => {
&mut content[index]
}
(ControlFlowElement::If { condition, .. }, CFSelectorSegment::IfCondition) => {
condition.as_mut()
}
(
ControlFlowElement::If { on_success, .. },
CFSelectorSegment::IndexInSuccess(index),
) => &mut on_success[index],
(
ControlFlowElement::If { on_failure, .. },
CFSelectorSegment::IndexInFailure(index),
) => &mut on_failure[index],
(
_,
CFSelectorSegment::IfCondition
| CFSelectorSegment::IndexInFailure(_)
| CFSelectorSegment::IndexInSuccess(_),
) => unreachable!(),
(ControlFlowElement::If { .. }, CFSelectorSegment::ContentAtIndex(_)) => unreachable!(),
(ControlFlowElement::BasicBlock { .. }, _) => unreachable!(),
}
}
fn select_by_segment(&self, segment: CFSelectorSegment) -> Option<&ControlFlowElement> {
match (self, segment) {
(ControlFlowElement::Block { content }, CFSelectorSegment::ContentAtIndex(index))
| (ControlFlowElement::Loop { content }, CFSelectorSegment::ContentAtIndex(index)) => {
content.get(index)
}
(ControlFlowElement::If { condition, .. }, CFSelectorSegment::IfCondition) => {
Some(condition.as_ref())
}
(
ControlFlowElement::If { on_success, .. },
CFSelectorSegment::IndexInSuccess(index),
) => on_success.get(index),
(
ControlFlowElement::If { on_failure, .. },
CFSelectorSegment::IndexInFailure(index),
) => on_failure.get(index),
(
_,
CFSelectorSegment::IfCondition
| CFSelectorSegment::IndexInFailure(_)
| CFSelectorSegment::IndexInSuccess(_),
) => unreachable!(),
(ControlFlowElement::If { .. }, CFSelectorSegment::ContentAtIndex(_)) => unreachable!(),
(ControlFlowElement::BasicBlock { .. }, _) => unreachable!(),
}
}
pub fn unwrap_node(&self) -> usize {
if let Self::BasicBlock { id: node_id } = self {
*node_id
} else {
unreachable!()
}
}
pub fn unwrap_content_mut(&mut self) -> &mut Vec<ControlFlowElement> {
match self {
Self::Block { content, .. } | Self::Loop { content, .. } => content,
_ => unreachable!(),
}
}
fn exists(&self, element: &CFSelector) -> bool {
if let Some((first, rest)) = element.clone().split_first() {
let subcontent = match (self, first) {
(
ControlFlowElement::Block { content } | ControlFlowElement::Loop { content },
CFSelectorSegment::ContentAtIndex(i),
) => content.get(i),
(ControlFlowElement::BasicBlock { .. }, _) => return false,
(ControlFlowElement::If { .. }, CFSelectorSegment::IfCondition) => {
return rest.is_empty()
}
(
ControlFlowElement::If { on_success, .. },
CFSelectorSegment::IndexInSuccess(i),
) => on_success.get(i),
(
ControlFlowElement::If { on_failure, .. },
CFSelectorSegment::IndexInFailure(i),
) => on_failure.get(i),
_ => unreachable!(),
};
if let Some(subcontent) = subcontent {
subcontent.exists(&rest)
} else {
false
}
} else {
true
}
}
pub fn next_element_sibling(&self, element: &CFSelector) -> Option<CFSelector> {
let mut result = element.clone();
let back = result.pop_back().unwrap();
let back = match back {
CFSelectorSegment::ContentAtIndex(i) => CFSelectorSegment::ContentAtIndex(i + 1),
CFSelectorSegment::IndexInSuccess(i) => CFSelectorSegment::IndexInSuccess(i + 1),
CFSelectorSegment::IndexInFailure(i) => CFSelectorSegment::IndexInFailure(i + 1),
CFSelectorSegment::IfCondition => return None, };
result.push_back(back);
if self.exists(&result) {
Some(result)
} else {
None
}
}
pub fn find_node(&self, node_id: usize) -> Option<CFSelector> {
match self {
ControlFlowElement::BasicBlock { id: self_node_id } if *self_node_id == node_id => {
Some(CFSelector::new_empty())
}
ControlFlowElement::BasicBlock { .. } => None,
ControlFlowElement::Block { content } | ControlFlowElement::Loop { content } => {
for (i, c) in content.iter().enumerate() {
if let Some(mut subresult) = c.find_node(node_id) {
subresult.push_front(CFSelectorSegment::ContentAtIndex(i));
return Some(subresult);
}
}
None
}
ControlFlowElement::If {
condition,
on_success,
on_failure,
} => {
if let Some(mut subresult) = condition.find_node(node_id) {
subresult.push_front(CFSelectorSegment::IfCondition);
return Some(subresult);
}
for (i, c) in on_success.iter().enumerate() {
if let Some(mut subresult) = c.find_node(node_id) {
subresult.push_front(CFSelectorSegment::IndexInSuccess(i));
return Some(subresult);
}
}
for (i, c) in on_failure.iter().enumerate() {
if let Some(mut subresult) = c.find_node(node_id) {
subresult.push_front(CFSelectorSegment::IndexInFailure(i));
return Some(subresult);
}
}
None
}
}
}
pub fn replace(&mut self, selector: &CFSelector, to_element: ControlFlowElement) {
assert!(!selector.is_empty());
let (parent_selector, last_segment) = selector.clone().split_last().unwrap();
let parent = &mut self[&parent_selector];
match (parent, last_segment) {
(
ControlFlowElement::Block { content } | ControlFlowElement::Loop { content },
CFSelectorSegment::ContentAtIndex(index),
) => {
content[index] = to_element;
}
(ControlFlowElement::If { condition, .. }, CFSelectorSegment::IfCondition) => {
*condition = Box::new(to_element);
}
(
ControlFlowElement::If { on_success, .. },
CFSelectorSegment::IndexInSuccess(index),
) => on_success[index] = to_element,
(
ControlFlowElement::If { on_failure, .. },
CFSelectorSegment::IndexInFailure(index),
) => on_failure[index] = to_element,
(ControlFlowElement::If { .. }, CFSelectorSegment::ContentAtIndex(_)) => unreachable!(),
(ControlFlowElement::Block { .. }, _) => unreachable!(),
(ControlFlowElement::Loop { .. }, _) => unreachable!(),
(ControlFlowElement::BasicBlock { .. }, _) => unreachable!(),
}
}
pub fn remove(&mut self, element: &CFSelector) -> ControlFlowElement {
assert!(!element.is_empty());
let (parent_selector, last_segment) = element.clone().split_last().unwrap();
let parent = &mut self[&parent_selector];
match (parent, last_segment) {
(ControlFlowElement::Block { content }, CFSelectorSegment::ContentAtIndex(i)) => {
content.remove(i)
}
(ControlFlowElement::If { on_success, .. }, CFSelectorSegment::IndexInSuccess(i)) => {
on_success.remove(i)
}
(ControlFlowElement::If { on_failure, .. }, CFSelectorSegment::IndexInFailure(i)) => {
on_failure.remove(i)
}
(ControlFlowElement::Loop { content }, CFSelectorSegment::ContentAtIndex(i)) => {
content.remove(i)
}
(_, CFSelectorSegment::IfCondition) => panic!("You should not remove the condition of an if statement, maybe try remove the whole if statement instead?"),
(ControlFlowElement::Block { .. }, _) => unreachable!(),
(ControlFlowElement::If { .. }, _) => unreachable!(),
(ControlFlowElement::Loop { .. }, _) => unreachable!(),
(ControlFlowElement::BasicBlock { .. }, _) => unreachable!(),
}
}
pub fn get(&self, index: &CFSelector) -> Option<&ControlFlowElement> {
if index.is_empty() {
Some(self)
} else {
let (first, rest) = index.clone().split_first()?;
let first_result = self.select_by_segment(first);
first_result?.get(&rest)
}
}
pub fn block_content(&self) -> Option<&Vec<ControlFlowElement>> {
if let ControlFlowElement::Block { content } = self {
Some(content)
} else {
None
}
}
}
impl IndexMut<&CFSelector> for ControlFlowElement {
fn index_mut(&mut self, index: &CFSelector) -> &mut Self::Output {
if index.is_empty() {
self
} else {
let (first, rest) = index.clone().split_first().unwrap();
let first_result = self.select_mut_by_segment(first);
first_result.index_mut(&rest)
}
}
}
impl Index<&CFSelector> for ControlFlowElement {
type Output = ControlFlowElement;
fn index(&self, index: &CFSelector) -> &Self::Output {
self.get(index).unwrap()
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use itertools::Itertools;
use super::*;
#[test]
fn test_find_node() {
let current_result = (0..5)
.map(|it| ControlFlowElement::BasicBlock { id: it })
.collect_vec();
let content = ControlFlowElement::Block {
content: current_result,
};
let selector = content.find_node(0);
assert_eq!(selector.unwrap(), CFSelector::from_str("0").unwrap());
let selector = content.find_node(1);
assert_eq!(selector.unwrap(), CFSelector::from_str("1").unwrap());
let content = ControlFlowElement::Block {
content: vec![
ControlFlowElement::BasicBlock { id: 0 },
ControlFlowElement::If {
condition: Box::new(ControlFlowElement::BasicBlock { id: 1 }),
on_success: Vec::new(),
on_failure: Vec::new(),
},
ControlFlowElement::BasicBlock { id: 2 },
],
};
let selector = content.find_node(1);
assert_eq!(
selector.unwrap(),
CFSelector::from_str("1/if_condition").unwrap()
);
let content = ControlFlowElement::Block {
content: vec![
ControlFlowElement::BasicBlock { id: 0 },
ControlFlowElement::If {
condition: Box::new(ControlFlowElement::BasicBlock { id: 1 }),
on_success: vec![ControlFlowElement::BasicBlock { id: 2 }],
on_failure: Vec::new(),
},
ControlFlowElement::BasicBlock { id: 3 },
],
};
let selector = content.find_node(1);
assert_eq!(
selector.unwrap(),
CFSelector::from_str("1/if_condition").unwrap()
);
let selector = content.find_node(2);
assert_eq!(
selector.unwrap(),
CFSelector::from_str("1/success->0").unwrap()
);
let selector = content.find_node(3);
assert_eq!(selector.unwrap(), CFSelector::from_str("2").unwrap());
}
}