Skip to content

Commit

Permalink
Removed a lot of recursion
Browse files Browse the repository at this point in the history
  • Loading branch information
ginty committed Sep 27, 2023
1 parent cf53902 commit 9a8d2d9
Show file tree
Hide file tree
Showing 5 changed files with 364 additions and 224 deletions.
254 changes: 195 additions & 59 deletions rust/origen_metal/src/ast/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ use super::processors::ToString;
//use crate::{Error, Operation, STATUS};
use crate::ast::processor::{Processor, Return};
use crate::Result;
use std::fmt::{self, Display};
use std::fmt::{self, Debug, Display};

pub trait Attrs: Clone + std::cmp::PartialEq + serde::Serialize + Display {}
impl<T: Clone + std::cmp::PartialEq + serde::Serialize + Display> Attrs for T {}
pub trait Attrs: Clone + std::cmp::PartialEq + serde::Serialize + Display + Debug {}
impl<T: Clone + std::cmp::PartialEq + serde::Serialize + Display + Debug> Attrs for T {}

#[derive(Clone, PartialEq, Serialize)]
#[derive(Clone, PartialEq, Serialize, Debug)]
pub struct Node<T> {
pub attrs: T,
pub inline: bool,
Expand All @@ -29,18 +29,23 @@ impl<T: Attrs> fmt::Display for Node<T> {
}
}

impl<T: Attrs> fmt::Debug for Node<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_string())
}
}

impl<T: Attrs> PartialEq<AST<T>> for Node<T> {
fn eq(&self, ast: &AST<T>) -> bool {
*self == ast.to_node()
}
}

enum PostProcessAction {
None,
Unwrap,
}

enum Handler {
OnNode,
OnEndOfBlock,
OnProcessedNode,
}

impl<T: Attrs> Node<T> {
pub fn new(attrs: T) -> Node<T> {
Node {
Expand Down Expand Up @@ -94,8 +99,184 @@ impl<T: Attrs> Node<T> {
/// Returning None means that the processor has decided that the node should be removed
/// from the next stage AST.
pub fn process(&self, processor: &mut dyn Processor<T>) -> Result<Option<Node<T>>> {
let r = { processor.on_node(&self)? };
self.process_return_code(r, processor)
self.process_(processor, Handler::OnNode)
}

fn process_(
&self,
processor: &mut dyn Processor<T>,
handler: Handler,
) -> Result<Option<Node<T>>> {
let mut node = self;
let mut open_nodes: Vec<(Node<T>, PostProcessAction)> = vec![];
let mut to_be_processed: Vec<Vec<&Node<T>>> = vec![];

loop {
let r = {
match handler {
Handler::OnNode => processor.on_node(node)?,
Handler::OnEndOfBlock => processor.on_end_of_block(node)?,
Handler::OnProcessedNode => processor.on_processed_node(node)?,
}
};

match r {
// Terminal return codes
Return::None => {
if matches!(handler, Handler::OnProcessedNode) {
return Ok(None);
}
}
Return::Unmodified => {
if matches!(handler, Handler::OnProcessedNode) {
return Ok(Some(node.clone()));
}
open_nodes.push((node.clone(), PostProcessAction::None));
to_be_processed.push(vec![]);
}
Return::Replace(node) => {
open_nodes.push((node, PostProcessAction::None));
to_be_processed.push(vec![]);
}
// We can't return multiple nodes from this function, so we return them
// wrapped in a meta-node and the process_children method will identify
// this and remove the wrapper to inline the contained nodes.
Return::Unwrap => {
open_nodes.push((
Node::inline(node.children.clone(), node.attrs.clone()),
PostProcessAction::None,
));
to_be_processed.push(vec![]);
}
Return::Inline(nodes) => {
open_nodes.push((
Node::inline(
nodes.into_iter().map(|n| Box::new(n)).collect(),
node.attrs.clone(),
),
PostProcessAction::None,
));
to_be_processed.push(vec![]);
}
Return::InlineBoxed(nodes) => {
open_nodes.push((
Node::inline(nodes, node.attrs.clone()),
PostProcessAction::None,
));
to_be_processed.push(vec![]);
}
Return::ReplaceChildren(nodes) => {
open_nodes.push((
node.replace_unboxed_children(nodes),
PostProcessAction::None,
));
to_be_processed.push(vec![]);
}

// Child processing return codes
Return::ProcessChildren => {
open_nodes.push((node.without_children(), PostProcessAction::None));
let mut children: Vec<&Node<T>> = vec![];
for child in &node.children {
children.push(child);
}
children.reverse();
to_be_processed.push(children);
}

Return::UnwrapWithProcessedChildren => {
open_nodes.push((node.without_children(), PostProcessAction::Unwrap));
let mut children: Vec<&Node<T>> = vec![];
for child in &node.children {
children.push(child);
}
children.reverse();
to_be_processed.push(children);
}

Return::InlineWithProcessedChildren(nodes) => {
open_nodes.push((
Node::inline(
nodes.into_iter().map(|n| Box::new(n)).collect(),
node.attrs.clone(),
),
PostProcessAction::None,
));
let mut children: Vec<&Node<T>> = vec![];
for child in &node.children {
children.push(child);
}
children.reverse();
to_be_processed.push(children);
}
}

loop {
if !to_be_processed.is_empty() {
let last_group = to_be_processed.last_mut().unwrap();
if !last_group.is_empty() {
node = last_group.pop().unwrap();
break;
}
to_be_processed.pop();
// Just completed all the children of the last open node
let (mut node, action) = open_nodes.pop().unwrap();
match action {
PostProcessAction::None => {}
PostProcessAction::Unwrap => {
node = Node::inline(node.children, node.attrs);
}
}

// Call the end of block handler, giving the processor a chance to do any
// internal clean up or inject some more nodes at the end
//if let Some(n) = self.process_(processor, Handler::OnEndOfBlock)? {
// if n.inline {
// for c in n.children {
// node.add_child(*c);
// }
// } else {
// node.add_child(n);
// }
//}

if matches!(handler, Handler::OnNode) {
if let Some(n) = node.process_(processor, Handler::OnProcessedNode)? {
node = n;
} else {
continue;
}
}

if open_nodes.is_empty() {
if node.inline && node.children.len() == 1 {
return Ok(Some(*node.children[0].clone()));
} else {
return Ok(Some(node));
}
} else {
let (parent, _) = open_nodes.last_mut().unwrap();
if node.inline {
for c in node.children {
parent.add_child(*c);
}
} else {
parent.add_child(node);
}
}
} else {
if open_nodes.is_empty() {
return Ok(None);
} else if open_nodes.len() == 1 {
return Ok(Some(open_nodes.pop().unwrap().0));
} else {
bail!(
"Internal error: open_nodes should be empty or have a single node left"
)
}
}
}
}
}

fn inline(nodes: Vec<Box<Node<T>>>, example_attrs: T) -> Node<T> {
Expand Down Expand Up @@ -222,49 +403,6 @@ impl<T: Attrs> Node<T> {
}
}

pub fn process_return_code(
&self,
code: Return<T>,
processor: &mut dyn Processor<T>,
) -> Result<Option<Node<T>>> {
match code {
Return::None => Ok(None),
Return::ProcessChildren => Ok(Some(self.process_and_update_children(processor)?)),
Return::Unmodified => Ok(Some(self.clone())),
Return::Replace(node) => Ok(Some(node)),
// We can't return multiple nodes from this function, so we return them
// wrapped in a meta-node and the process_children method will identify
// this and remove the wrapper to inline the contained nodes.
Return::Unwrap => Ok(Some(Node::inline(
self.children.clone(),
self.attrs.clone(),
))),
Return::Inline(nodes) => Ok(Some(Node::inline(
nodes.into_iter().map(|n| Box::new(n)).collect(),
self.attrs.clone(),
))),
Return::InlineBoxed(nodes) => Ok(Some(Node::inline(nodes, self.attrs.clone()))),
Return::UnwrapWithProcessedChildren => {
let nodes = self.process_children(processor)?;
Ok(Some(Node::inline(
nodes.into_iter().map(|n| Box::new(n)).collect(),
self.attrs.clone(),
)))
}
Return::InlineWithProcessedChildren(mut nodes) => {
nodes.append(&mut self.process_children(processor)?);
Ok(Some(Node::inline(
nodes.into_iter().map(|n| Box::new(n)).collect(),
self.attrs.clone(),
)))
}
Return::ReplaceChildren(nodes) => {
let new_node = self.replace_unboxed_children(nodes);
Ok(Some(new_node))
}
}
}

/// Returns a new node which is a copy of self with its children replaced
/// by their processed counterparts.
pub fn process_and_update_children(&self, processor: &mut dyn Processor<T>) -> Result<Node<T>> {
Expand Down Expand Up @@ -293,8 +431,7 @@ impl<T: Attrs> Node<T> {
}
// Call the end of block handler, giving the processor a chance to do any
// internal clean up or inject some more nodes at the end
let r = processor.on_end_of_block(&self)?;
if let Some(node) = self.process_return_code(r, processor)? {
if let Some(node) = self.process_(processor, Handler::OnEndOfBlock)? {
if node.inline {
for c in node.children {
nodes.push(c);
Expand Down Expand Up @@ -322,8 +459,7 @@ impl<T: Attrs> Node<T> {
}
// Call the end of block handler, giving the processor a chance to do any
// internal clean up or inject some more nodes at the end
let r = processor.on_end_of_block(&self)?;
if let Some(node) = self.process_return_code(r, processor)? {
if let Some(node) = self.process_(processor, Handler::OnEndOfBlock)? {
if node.inline {
for c in node.children {
nodes.push(*c);
Expand Down
4 changes: 4 additions & 0 deletions rust/origen_metal/src/ast/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,8 @@ pub trait Processor<T> {
fn on_end_of_block(&mut self, _node: &Node<T>) -> Result<Return<T>> {
Ok(Return::None)
}

fn on_processed_node(&mut self, _node: &Node<T>) -> Result<Return<T>> {
Ok(Return::Unmodified)
}
}
7 changes: 5 additions & 2 deletions rust/origen_metal/src/ast/processors/to_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ impl<T: Attrs> Processor<T> for ToString<T> {
self.output += &" ".repeat(self.indent);
self.output += &format!("{}\n", node.attrs);
self.indent += 4;
node.process_children(self)?;
Ok(Return::ProcessChildren)
}

fn on_processed_node(&mut self, _node: &Node<T>) -> Result<Return<T>> {
self.indent -= 4;
Ok(Return::Unmodified)
Ok(Return::None)
}
}
2 changes: 1 addition & 1 deletion rust/origen_metal/src/stil/nodes.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::stil;

#[derive(Clone, Debug, PartialEq, Serialize)]
#[derive(Clone, PartialEq, Serialize, Debug)]
pub enum STIL {
Root,
SourceFile(String),
Expand Down
Loading

0 comments on commit 9a8d2d9

Please sign in to comment.