lib-ruby-parser 3.0.11

Ruby parser
Documentation
use crate::codegen::rust::nodes::helpers::filename;
use lib_ruby_parser_nodes::{template::*, Node, NodeField};

const TEMPLATE: &str = "// This file is auto-generated by {{ helper generated-by }}

{{ helper imports }}

{{ helper node-comment }}
#[derive(Debug, Clone, PartialEq)]
#[repr(C)]
pub struct {{ helper node-camelcase-name }} {
{{ each node-field }}<dnl>
{{ helper node-field-comment }}
    pub {{ helper node-field-rust-field-name }}: {{ helper node-field-native-type }},

{{ end }}<dnl>
}

impl {{ helper node-camelcase-name }} {
    // getters
{{ each node-field }}
    /// Returns `{{ helper node-field-rust-field-name }}` field
    pub fn get_{{ helper node-field-name }}(&self) -> &{{ helper node-field-native-type }} {
        &self.{{ helper node-field-rust-field-name }}
    }
{{ end }}

    // setters
{{ each node-field }}
    /// Sets `{{ helper node-field-rust-field-name }}` field
    pub fn set_{{ helper node-field-name }}(&mut self, {{ helper node-field-rust-field-name }}: {{ helper node-field-native-type }}) {
        self.{{ helper node-field-rust-field-name }} = {{ helper node-field-rust-field-name }};
    }
{{ end }}

    #[allow(dead_code)]
    pub(crate) fn into_internal(self) -> super::Internal{{ helper node-camelcase-name }} {
        let Self { {{ each node-field }}{{ helper node-field-rust-field-name }}, {{ end }} } = self;
        super::Internal{{ helper node-camelcase-name }} { {{ each node-field }}{{ helper node-field-rust-field-name }}, {{ end }} }
    }
}

impl InnerNode for {{ helper node-camelcase-name }} {
    fn expression(&self) -> &Loc {
        &self.expression_l
    }

    fn inspected_children(&self, indent: usize) -> Vec<String> {
        let mut result = InspectVec::new(indent);
{{ each node-field }}<dnl>
        {{ helper inspect-field }}
{{ end }}<dnl>
        result.strings()
    }

    fn str_type(&self) -> &'static str {
        \"{{ helper node-str-type }}\"
    }

    fn print_with_locs(&self) {
        println!(\"{}\", self.inspect(0));
{{ each node-field }}<dnl>
        {{ helper print-field-with-loc }}
{{ end }}<dnl>
    }
}
";

pub(crate) fn codegen(node: &lib_ruby_parser_nodes::Node) {
    let template = NodeTemplateRoot::new(TEMPLATE).unwrap();
    let mut fns = crate::codegen::fns::default_fns!();

    fns.register::<Node, F::Helper>("imports", local_helpers::imports);
    fns.register::<NodeField, F::Helper>(
        "node-field-native-type",
        local_helpers::native_field_type,
    );
    fns.register::<NodeField, F::Helper>("inspect-field", local_helpers::inspect_field);
    fns.register::<NodeField, F::Helper>(
        "print-field-with-loc",
        local_helpers::print_field_with_loc,
    );

    let contents = template.render(node, &fns);

    let dir = filename(node);
    let path = format!("src/nodes/types/{}/native.rs", dir);
    std::fs::write(&path, contents).unwrap();
}

mod local_helpers {
    use lib_ruby_parser_nodes::{node_has_field, Node, NodeField};

    pub(crate) fn imports(node: &Node) -> String {
        use lib_ruby_parser_nodes::NodeFieldType::*;

        let mut imports = vec![];
        imports.push("use crate::nodes::InnerNode;");
        imports.push("use crate::nodes::InspectVec;");
        imports.push("use crate::Loc;");

        if node_has_field!(node, Node | Nodes | MaybeNode { .. }) {
            imports.push("use crate::Node;");
        }

        if node_has_field!(node, StringValue) {
            imports.push("use crate::Bytes;");
        }

        imports.join("\n")
    }

    pub(crate) fn native_field_type(node_field: &NodeField) -> String {
        use lib_ruby_parser_nodes::NodeFieldType;
        match node_field.field_type {
            NodeFieldType::Node => "Box<Node>",
            NodeFieldType::Nodes => "Vec<Node>",
            NodeFieldType::MaybeNode { .. } => "Option<Box<Node>>",
            NodeFieldType::Loc => "Loc",
            NodeFieldType::MaybeLoc => "Option<Loc>",
            NodeFieldType::Str { .. } => "String",
            NodeFieldType::MaybeStr { .. } => "Option<String>",
            NodeFieldType::StringValue => "Bytes",
            NodeFieldType::U8 => "u8",
        }
        .to_string()
    }

    pub(crate) fn inspect_field(node_field: &NodeField) -> String {
        use lib_ruby_parser_nodes::NodeFieldType::*;

        let method_name = match node_field.field_type {
            Node => "push_node",
            Nodes => "push_nodes",
            MaybeNode { regexp_options } => {
                if regexp_options {
                    "push_regex_options"
                } else if node_field.always_print {
                    "push_maybe_node_or_nil"
                } else {
                    "push_maybe_node"
                }
            }
            Loc => return format!(""),
            MaybeLoc => return format!(""),
            Str { raw } => {
                if raw {
                    "push_raw_str"
                } else {
                    "push_str"
                }
            }
            MaybeStr { chars } => {
                if chars {
                    "push_chars"
                } else {
                    "push_maybe_str"
                }
            }
            StringValue => "push_string_value",
            U8 => "push_u8",
        };

        format!(
            "result.{}(self.get_{}());",
            method_name, node_field.snakecase_name
        )
    }

    pub(crate) fn print_field_with_loc(node_field: &NodeField) -> String {
        use lib_ruby_parser_nodes::NodeFieldType::*;

        match node_field.field_type {
            Node => format!(
                "self.get_{field_name}().inner_ref().print_with_locs();",
                field_name = node_field.snakecase_name
            ),
            Nodes =>
                format!(
                    "for node in self.get_{field_name}().iter() {{ node.inner_ref().print_with_locs(); }}",
                    field_name = node_field.snakecase_name
                ),
            MaybeNode { .. } => format!(
                "if let Some(node) = self.get_{field_name}().as_ref() {{ node.inner_ref().print_with_locs() }}",
                field_name = node_field.snakecase_name
            ),
            Loc => format!(
                "self.get_{field_name}().print(\"{printable_field_name}\");",
                field_name = node_field.snakecase_name,
                printable_field_name = node_field
                    .snakecase_name
                    .strip_suffix("_l")
                    .expect("expected loc field to end with _l")
            ),
            MaybeLoc => format!(
                "if let Some(loc) = self.get_{field_name}().as_ref() {{ loc.print(\"{printable_field_name}\") }}",
                field_name = node_field.snakecase_name,
                printable_field_name = node_field
                    .snakecase_name
                    .strip_suffix("_l")
                    .expect("expected loc field to end with _l"),
            ),
            Str { .. } => format!(""),
            MaybeStr { .. } => format!(""),
            StringValue => format!(""),
            U8 => format!(""),
        }
    }
}