use crate::operation::AstEngine;
use crate::render::{render_expression, render_reference};
use crate::structure::{Literal, NodeId, NodeKind};
#[derive(Debug, Clone)]
pub struct NodeInfo {
pub node_id: NodeId,
pub label: String,
pub children: Vec<ChildEntry>,
}
#[derive(Debug, Clone)]
pub enum ChildEntry {
Node(NodeId),
Virtual {
label: String,
children: Vec<NodeId>,
},
}
pub trait TreeVisitor {
fn node_info(&self, engine: &AstEngine, node_id: NodeId) -> Option<NodeInfo>;
}
#[derive(Debug, Clone, Copy)]
pub struct DefaultTreeVisitor;
impl TreeVisitor for DefaultTreeVisitor {
fn node_info(&self, engine: &AstEngine, node_id: NodeId) -> Option<NodeInfo> {
let node = engine.structure.get(node_id)?;
let (label, children) = match node.kind() {
NodeKind::Scalar { name } => (format!("Scalar({})", name.as_str()), Vec::new()),
NodeKind::Array { name, length } => (
format!(
"Array({}, len={})",
name.as_str(),
render_expression(engine, length)
),
Vec::new(),
),
NodeKind::Matrix { name, rows, cols } => (
format!(
"Matrix({}, rows={}, cols={})",
name.as_str(),
render_reference(engine, rows),
render_reference(engine, cols)
),
Vec::new(),
),
NodeKind::Tuple { elements } => (
"Tuple".to_owned(),
elements.iter().map(|id| ChildEntry::Node(*id)).collect(),
),
NodeKind::Repeat {
count,
index_var,
body,
} => {
let label = match index_var {
Some(var) => format!(
"Repeat(count={}, {})",
render_expression(engine, count),
var.as_str()
),
None => format!("Repeat(count={})", render_expression(engine, count)),
};
(label, body.iter().map(|id| ChildEntry::Node(*id)).collect())
}
NodeKind::Section { header, body } => {
let mut children: Vec<ChildEntry> = Vec::new();
if let Some(h) = header {
children.push(ChildEntry::Node(*h));
}
children.extend(body.iter().map(|id| ChildEntry::Node(*id)));
("Section".to_owned(), children)
}
NodeKind::Sequence { children: kids } => (
"Sequence".to_owned(),
kids.iter().map(|id| ChildEntry::Node(*id)).collect(),
),
NodeKind::Choice { tag, variants } => {
let label = format!("Choice(tag={})", render_reference(engine, tag));
let children = variants
.iter()
.map(|(lit, kids)| {
let variant_label = match lit {
Literal::IntLit(v) => format!("Variant({v})"),
Literal::StrLit(s) => format!("Variant(\"{s}\")"),
};
ChildEntry::Virtual {
label: variant_label,
children: kids.clone(),
}
})
.collect();
(label, children)
}
NodeKind::Hole { expected_kind } => {
let label = match expected_kind {
Some(hint) => format!("Hole(expected={hint:?})"),
None => "Hole".to_owned(),
};
(label, Vec::new())
}
};
Some(NodeInfo {
node_id,
label,
children,
})
}
}
#[must_use]
pub fn node_display_name(engine: &AstEngine, node_id: NodeId) -> String {
engine.structure.get(node_id).map_or_else(
|| format!("#{}", node_id.value()),
|node| match node.kind() {
NodeKind::Scalar { name }
| NodeKind::Array { name, .. }
| NodeKind::Matrix { name, .. } => name.as_str().to_owned(),
NodeKind::Tuple { .. } => "Tuple".to_owned(),
NodeKind::Repeat { .. } => "Repeat".to_owned(),
NodeKind::Section { .. } => "Section".to_owned(),
NodeKind::Sequence { .. } => "Sequence".to_owned(),
NodeKind::Choice { .. } => "Choice".to_owned(),
NodeKind::Hole { .. } => "Hole".to_owned(),
},
)
}