moonlander_gp/
impl_astnode.rs#[macro_export]
macro_rules! impl_astnode {
(@asref $i:ident) => { $i.as_ref() };
(@retcap (data $i:ident $($gen:expr)*)) => { _ };
(@retcap $i:ident) => { ref $i };
(@mkvec () ($($acc:expr)*)) => { vec![$($acc),*] };
(@mkvec ((data $field:ident $($gen:expr)*) $($fields:tt)*) ($($acc:tt)*)) => {
impl_astnode!(@mkvec ($($fields)*) ($($acc)*))
};
(@mkvec ($field:ident $($fields:tt)*) ($($acc:tt)*)) => {
impl_astnode!(@mkvec ($($fields)*) ($($acc)* impl_astnode!(@asref $field)))
};
(@retpat $enum_name:ident $case_name:ident ()) => { $enum_name::$case_name };
(@retpat $enum_name:ident $case_name:ident ($($fields:tt),+)) => { $enum_name::$case_name($( impl_astnode!(@retcap $fields) ),+) };
(@retcrea $enum_name:ident $case_name:ident ($($fields:tt),*)) => { impl_astnode!(@mkvec ($($fields)*) ()) };
(@repcap (data $i:ident $($gen:expr)*)) => { ref $i };
(@repcap $i:ident) => { ref $i };
(@repret $old_child:ident $new_child:ident (data $i:ident $($gen:expr)*)) => { $i.clone() };
(@repret $old_child:ident $new_child:ident $i:ident) => { $crate::clone_or_replace($i, $old_child, $new_child) };
(@reppat $enum_name:ident $case_name:ident ()) => { $enum_name::$case_name };
(@reppat $enum_name:ident $case_name:ident ($($fields:tt),+)) => { $enum_name::$case_name($( impl_astnode!(@repcap $fields) ),+) };
(@repcrea $old_child:ident $new_child:ident $enum_name:ident $case_name:ident ()) => { $enum_name::$case_name };
(@repcrea $old_child:ident $new_child:ident $enum_name:ident $case_name:ident ($($fields:tt),+)) => { $enum_name::$case_name($( impl_astnode!(@repret $old_child $new_child $fields) ),+) };
(@randcrea $weights:ident $rng:ident $enum_name:ident $case_name:ident ()) => { $enum_name::$case_name };
(@randcrea $weights:ident $rng:ident $enum_name:ident $case_name:ident ($($fields:tt),+)) => {
$enum_name::$case_name($( impl_astnode!(@randchild $weights $rng $fields) ),+)
};
(@callgen $rng:ident) => { "You should pass a random-generating function to a 'data' field" };
(@callgen $rng:ident $gen:expr) => { $gen($rng) };
(@randchild $weights:ident $rng:ident (data $field:ident $($gen:expr)*)) => { impl_astnode!(@callgen $rng $($gen)*) };
(@randchild $weights:ident $rng:ident $field:ident) => { $weights.gen_child($rng) };
(@weight leaf $weights:expr) => { $weights.leaf() };
(@weight int $weights:expr) => { $weights.internal() };
($enum_name:ident, $type_id:expr, $( $case_type:ident $case_name:ident ($($fields:tt),*) ),* ) => {
impl $crate::AstNode for $enum_name {
fn node_type(&self) -> usize { $type_id }
fn children(&self) -> Vec<&$crate::AstNode> {
match *self {
$(
impl_astnode!(@retpat $enum_name $case_name($($fields),*))
=>
impl_astnode!(@retcrea $enum_name $case_name($($fields),*))
),*
}
}
fn replace_child(&self, _old_child: &$crate::AstNode, _new_child: &mut Option<Box<$crate::AstNode>>) -> Box<$crate::AstNode> {
Box::new(match *self {
$(
impl_astnode!(@reppat $enum_name $case_name($($fields),*))
=>
impl_astnode!(@repcrea _old_child _new_child $enum_name $case_name($($fields),*))
),*
})
}
}
impl $crate::RandNode for $enum_name {
fn rand(weights: $crate::NodeWeights, rng: &mut ::rand::Rng) -> $enum_name {
pick![rng,
$(
impl_astnode!(@weight $case_type weights),
impl_astnode!(@randcrea weights rng $enum_name $case_name( $( $fields ),* ))
),*
]
}
}
};
}
#[cfg(test)]
mod tests {
use super::super::ast::*;
#[derive(Clone,PartialEq,Eq,Debug)]
enum Tree {
Leaf(i32),
Node(Box<Tree>, Box<Tree>)
}
impl_astnode!(Tree, 666,
leaf Leaf((data d |rng: &mut ::rand::Rng| (rng.next_u32() % 100) as i32)),
int Node(left, right));
#[test]
fn test_children() {
let node = Tree::Node(Box::new(Tree::Leaf(1)), Box::new(Tree::Leaf(2)));
assert_eq!(2, node.children().len());
}
#[test]
fn copy_data() {
let node = Tree::Leaf(1);
let mut replacement : Option<Box<AstNode>> = Some(Box::new(Tree::Leaf(2)));
let new_node = node.replace_child(&node, &mut replacement).downcast::<Tree>().ok().unwrap();
assert_eq!(node, *new_node);
}
}