use std::collections::{HashMap, HashSet};
use super::nom_parser::{TreeDef, TreeSource};
use crate::{
error::{AddChildError, LoadError},
nodes::{IsTrueNode, SubtreeNode, INPUT},
BBMap, BehaviorNodeContainer, NumChildren, PortSpec, PortType, Registry, Symbol,
};
pub fn load(
tree_source: &TreeSource,
registry: &Registry,
check_ports: bool,
) -> Result<BehaviorNodeContainer, LoadError> {
let main = tree_source
.tree_defs
.iter()
.find(|tree| tree.name == "main")
.ok_or(LoadError::MissingTree)?;
let top = TreeStack {
name: "main",
parent: None,
};
let mut vars = HashSet::new();
load_recurse(
&main.root,
registry,
tree_source,
check_ports,
&top,
&mut vars,
)
}
struct TreeStack<'a, 'src> {
name: &'src str,
parent: Option<&'a TreeStack<'a, 'src>>,
}
impl<'a, 'src> TreeStack<'a, 'src> {
fn find(&self, name: &str) -> bool {
if self.name == name {
true
} else if let Some(parent) = self.parent {
parent.find(name)
} else {
false
}
}
}
fn load_recurse(
parent: &TreeDef,
registry: &Registry,
tree_source: &TreeSource,
check_ports: bool,
parent_stack: &TreeStack,
vars: &mut HashSet<Symbol>,
) -> Result<BehaviorNodeContainer, LoadError> {
let mut ret = if let Some(ret) = registry.build(parent.ty) {
BehaviorNodeContainer::new_raw_with_name(ret, parent.ty.to_string())
} else {
let tree = tree_source
.tree_defs
.iter()
.find(|tree| tree.name == parent.ty)
.ok_or_else(|| LoadError::MissingNode(parent.ty.to_owned()))?;
if parent_stack.find(parent.ty) {
return Err(LoadError::InfiniteRecursion {
node: parent.ty.to_owned(),
});
}
let tree_stack = TreeStack {
name: parent.ty,
parent: Some(parent_stack),
};
let mut vars = HashSet::new();
let loaded_subtree = load_recurse(
&tree.root,
registry,
tree_source,
check_ports,
&tree_stack,
&mut vars,
)?;
BehaviorNodeContainer {
name: parent.ty.to_owned(),
node: Box::new(SubtreeNode::new(
HashMap::new(),
tree.ports
.iter()
.map(|port| PortSpec {
key: port.name.into(),
ty: port.direction,
})
.collect(),
)),
blackboard_map: HashMap::new(),
child_nodes: vec![loaded_subtree],
last_result: None,
is_subtree: true,
subtree_expanded: std::cell::Cell::new(false),
}
};
for var_def in &parent.vars {
vars.insert(var_def.name.into());
}
for child in &parent.children {
let mut new_node = if child.port_maps.is_empty() && child.children.is_empty() {
if vars.contains(&child.ty.into()) {
let mut bbmap = BBMap::new();
bbmap.insert(
*INPUT,
crate::BlackboardValue::Ref(child.ty.into(), PortType::Input),
);
Some(
BehaviorNodeContainer::new(Box::new(IsTrueNode), bbmap)
.with_name("IsTrue".to_owned()),
)
} else {
None
}
} else {
None
};
if new_node.is_none() {
let mut child_node = load_recurse(
child,
registry,
tree_source,
check_ports,
parent_stack,
vars,
)?;
let provided_ports = child_node.node.provided_ports();
let mut bbmap = BBMap::new();
for entry in child.port_maps.iter() {
if check_ports {
if let Some(port) = provided_ports.iter().find(|p| p.key == entry.node_port) {
if port.ty != entry.ty {
return Err(LoadError::PortIOUnmatch {
node: child.ty.to_owned(),
port: entry.node_port.to_owned(),
});
}
} else {
return Err(LoadError::PortUnmatch {
node: child.ty.to_owned(),
port: entry.node_port.to_owned(),
});
}
}
bbmap.insert(
entry.node_port.into(),
match entry.blackboard_value {
super::nom_parser::BlackboardValue::Ref(ref value) => {
crate::BlackboardValue::Ref(value.into(), entry.ty)
}
super::nom_parser::BlackboardValue::Literal(ref value) => {
crate::BlackboardValue::Literal(value.clone())
}
},
);
}
child_node.blackboard_map = bbmap;
new_node = Some(child_node);
}
if let Some(new_node) = new_node {
if NumChildren::Finite(ret.child_nodes.len()) < ret.node.max_children() {
ret.child_nodes.push(new_node);
} else {
return Err(LoadError::AddChildError(
AddChildError::TooManyNodes,
parent.ty.to_string(),
));
}
}
}
Ok(ret)
}
#[cfg(test)]
mod test;