Thanks to previous help from @quinedot I got one big issue sorted but I am still stuck
Outline, I have a parser that emits a concrete AST, the AST nodes struct are generated programmatically.
Then I have a visitor which visits that tree. I want to be able to run different visitors against the same tree.
I am stuck trying to work out how to specify a generic return type for the visitor methods.
Code essentials
Base for the nodes
pub trait IBaseNode: AsAny {
fn get_name(&self) -> String;
fn add_child(&mut self, child: Box<dyn IBaseNode>);
fn accept(&self, visitor: &mut dyn LoxVisitor);
}
pub struct BaseNode {
children: Vec<Box<dyn IBaseNode>>,
}
base for the visitors
pub trait AsVisitor {
fn as_visitor(&mut self) -> &mut dyn LoxVisitor;
}
impl<T: LoxVisitor /* + Sized */> AsVisitor for T {
fn as_visitor(&mut self) -> &mut dyn LoxVisitor {
self
}
}
visitor defintion, the trait has default implementations that do nothing but propagate the visit down the tree
pub trait LoxVisitor: AsVisitor {
fn visit_program(&mut self, node: &ProgramNode) {
println!("Visit Program");
for child in &node.base.children {
child.accept(self.as_visitor())
}
}
fn visit_variable_declaration(&mut self, node: &Variable_declarationNode) {
println!("Visit Variable_declaration");
for child in &node.base.children {
child.accept(self.as_visitor())
}
}
......
}
a node
pub struct ProgramNode {
base: BaseNode,
text: String,
}
impl IBaseNode for ProgramNode {
fn get_name(&self) -> String {
"ProgramNode".to_string()
}
fn add_child(&mut self, child: Box<dyn IBaseNode>) {
self.base.children.push(child);
}
fn accept(&self, visitor: &mut dyn LoxVisitor) {
visitor.visit_program(self);
}
}
impl ProgramNode {
pub fn new(text: &str) -> Self {
Self {
base: BaseNode { children: vec![] },
text: text.to_string(),
}
}
pub fn new_dyn(text: &str) -> Box<dyn IBaseNode> {
Box::new(Self {
base: BaseNode { children: vec![] },
text: text.to_string(),
})
}
pub fn get_text(&self) -> &str {
&self.text
}
}
... // many more follow
my problem is that I need the visit_xxx methods to return a value. The type of the value is specific to the visitor. So I clearly need something like this in the visitor trait
pub trait LoxVisitor: AsVisitor {
type TR:Default
fn visit_program(&mut self, node: &ProgramNode) -> Self::TR {
println!("Visit Program");
for child in &node.base.children {
child.accept(self.as_visitor())
}
}
but when I follow through on that I end up with the node type being specialized to the visitor. So I cannot run two visitors through the same AST because the AST type (in particular BaseNode and IBaseNode) is specialized to the visitor type.
I know this is doable because I had a visitor implementation generated by antlr4rust, but the code that antrl4rust generated is incredibly convoluted and I cannot work out how it works (its what driving me to use pest and my own ast/visitor infrastructure). I cant tell which bits are needed for the multiple visitors work and which bits are used to implement other things that I dont need