use lib_ruby_parser_nodes::{template::*, NodeField};
const TEMPLATE: &str = "// This file is auto-generated by {{ helper generated-by }}
use crate::traverse::finder::{Finder, PatternItem};
use crate::traverse::visitor::Visitor;
use crate::nodes::*;
use crate::Node;
impl Visitor for Finder {
{{ each node }}<dnl>
fn on_{{ helper node-lower-name }}(&mut self, node: &{{ helper node-camelcase-name }}) {
match self.pattern.unshift() {
{{ each node-field }}<dnl>
{{ helper visit-child-branch }}
{{ end }}<dnl>
None => {
// end of the search chain, match
self.result = Some(Node::{{ helper node-camelcase-name }}(node.clone()));
}
Some(_) => {
// end of the search chain, no match
}
}
}
{{ end }}
fn visit(&mut self, node: &Node) {
match node {
{{ each node }}<dnl>
Node::{{ helper node-camelcase-name }}(inner) => {
self.on_{{ helper node-lower-name }}(inner);
}
{{ end }}
}
}
}
fn visit_node_list(finder: &mut Finder, nodes: &[Node]) {
if let Some(PatternItem::Idx(idx)) = finder.pattern.unshift() {
if let Some(item) = nodes.get(idx) {
finder.visit(item)
}
} else {
// end of the search chain, no match
}
}
";
pub(crate) fn codegen() {
let template = TemplateRoot::new(TEMPLATE).unwrap();
let mut fns = crate::codegen::fns::default_fns!();
fns.register::<NodeField, F::Helper>("visit-child-branch", local_helpers::visit_child);
let contents = template.render(ALL_DATA, &fns);
std::fs::write("src/traverse/finder/finder_gen.rs", contents).unwrap();
}
mod local_helpers {
use lib_ruby_parser_nodes::NodeField;
pub(crate) fn visit_child(node_field: &NodeField) -> String {
let node = &node_field.node;
let field_name = crate::codegen::fns::rust::node_fields::rust_field_name(node_field);
let variant = {
fn capitalize_field_name(s: &str) -> String {
s.split("_").map(|word| capitalize_word(word)).collect()
}
fn capitalize_word(s: &str) -> String {
let mut c = s.chars();
match c.next() {
None => String::new(),
Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
}
}
match (&node.wqp_name[..], &field_name[..]) {
(_, "statements") => "Stmts".to_string(),
(_, "call") => "MethodCall".to_string(),
(_, "default") => "DefaultValue".to_string(),
(_, "items") => "MlhsItems".to_string(),
("when", "patterns") => "Args".to_string(),
("undef", "names") => "Args".to_string(),
("args", "args") => "Arglist".to_string(),
("procarg0", "args") => "Arglist".to_string(),
("rescue", "else_") => "ElseBody".to_string(),
_ => capitalize_field_name(&field_name),
}
};
use lib_ruby_parser_nodes::NodeFieldType::*;
let code = match node_field.field_type {
Node => {
format!("self.visit(&node.{});", field_name)
}
Nodes => {
format!("visit_node_list(self, &node.{})", field_name)
}
MaybeNode { .. } => {
format!(
"if let Some(inner) = node.{}.as_ref() {{ self.visit(inner); }}",
field_name
)
}
Loc | MaybeLoc | Str { .. } | MaybeStr { .. } | StringValue | U8 => {
return format!("// skip {}", field_name)
}
};
format!(
"Some(PatternItem::{variant}) => {{ {code} }}",
variant = variant,
code = code
)
}
}