fn contents() -> String {
format!(
"// This file is autogenerated by {generator}
use super::LocName;
use crate::{{Node, Loc}};
crate::use_native_or_external!(Maybe);
impl LocName {{
{loc_getters}
pub(crate) fn get(&self, node: &Node) -> Maybe<Loc> {{
match self {{
{loc_branches}
}}
}}
}}
",
generator = file!(),
loc_getters = map_loc(&loc_getter).join("\n\n "),
loc_branches = map_loc(&loc_branch).join("\n ")
)
}
pub(crate) fn codegen() {
std::fs::write("src/test_helpers/loc_matcher/loc_name_gen.rs", contents()).unwrap();
}
#[derive(Debug)]
pub enum LocName {
Begin,
End,
Expression,
Keyword,
Name,
Assignment,
Colon,
DoubleColon,
Else,
HeredocBody,
Operator,
Selector,
Assoc,
Question,
HeredocEnd,
}
impl LocName {
fn to_str(&self) -> &'static str {
match self {
LocName::Begin => "begin_l",
LocName::End => "end_l",
LocName::Expression => "expression_l",
LocName::Keyword => "keyword_l",
LocName::Name => "name_l",
LocName::Assignment => "assignment_l",
LocName::Colon => "colon_l",
LocName::DoubleColon => "double_colon_l",
LocName::Else => "else_l",
LocName::HeredocBody => "heredoc_body_l",
LocName::Operator => "operator_l",
LocName::Selector => "selector_l",
LocName::Assoc => "assoc_l",
LocName::Question => "question_l",
LocName::HeredocEnd => "heredoc_end_l",
}
}
}
const LOC_NAMES: &[&'static LocName] = &[
&LocName::Begin,
&LocName::End,
&LocName::Expression,
&LocName::Keyword,
&LocName::Name,
&LocName::Assignment,
&LocName::Colon,
&LocName::DoubleColon,
&LocName::Else,
&LocName::HeredocBody,
&LocName::Operator,
&LocName::Selector,
&LocName::Assoc,
&LocName::Question,
&LocName::HeredocEnd,
];
fn map_loc(f: &dyn Fn(&LocName) -> String) -> Vec<String> {
LOC_NAMES.iter().map(|l| f(*l)).collect()
}
fn loc_getter(loc_name: &LocName) -> String {
let mut variants = vec![];
for node in lib_ruby_parser_nodes::nodes().iter() {
for field in node.fields.iter() {
if field.snakecase_name == loc_name.to_str() {
match field.field_type {
lib_ruby_parser_nodes::NodeFieldType::Loc => {
variants.push((node.clone(), false))
}
lib_ruby_parser_nodes::NodeFieldType::MaybeLoc => {
variants.push((node.clone(), true))
}
_ => {}
}
}
}
}
let statements: Vec<String> = variants
.into_iter()
.map(|(node, nullable)| {
let get_loc = if nullable {
format!(
"return inner.get_{loc_name}().clone()",
loc_name = loc_name.to_str()
)
} else {
format!(
"return Maybe::some(inner.get_{loc_name}().clone())",
loc_name = loc_name.to_str()
)
};
format!(
"if let Some(inner) = node.as_{lower_node_name}() {{
{get_loc}
}}",
lower_node_name = node.lower_name(),
get_loc = get_loc
)
})
.collect();
let statements = statements.join(" else ");
format!(
"fn get_{loc_name}(node: &Node) -> Maybe<Loc> {{
{statements}
else {{
panic!(\"node {{}} doesn't support {loc_name} loc\", node.str_type())
}}
}}",
loc_name = loc_name.to_str(),
statements = statements
)
}
fn loc_branch(loc_name: &LocName) -> String {
let camelcase_loc_name = format!("{:?}", loc_name);
format!(
"LocName::{camelcase_loc_name} => Self::get_{loc_name}(node),",
camelcase_loc_name = camelcase_loc_name,
loc_name = loc_name.to_str()
)
}