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 }}

use super::internal::Internal{{ helper node-camelcase-name }};
use crate::blobs::{HasBlob, Blob};

{{ helper node-comment }}
#[repr(C)]
pub struct {{ helper node-camelcase-name }} {
    pub(crate) blob: Blob<Self>
}

impl std::fmt::Debug for {{ helper node-camelcase-name }} {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct(\"{{ helper node-camelcase-name }}\")
{{ each node-field }}<dnl>
            .field(\"{{ helper node-field-rust-field-name }}\", &self.get_{{ helper node-field-name }}())
{{ end }}<dnl>
            .finish()
    }
}

impl PartialEq for {{ helper node-camelcase-name }} {
    fn eq(&self, other: &Self) -> bool {
{{ each node-field }}<dnl>
        if self.get_{{ helper node-field-name }}() != other.get_{{ helper node-field-name }}() {
            return false
        }
{{ end }}<dnl>
        true
    }
}

impl {{ helper node-camelcase-name }} {
    // getters
{{ each node-field }}<dnl>
    /// Returns `{{ helper node-field-rust-field-name }}` field
    pub fn get_{{ helper node-field-name }}(&self) -> &{{ helper node-field-rust-field-type }} {
        unsafe {
            #[allow(trivial_casts)]
            ({{ helper external-getter-name }}(&self.blob) as *const {{ helper node-field-rust-field-type }})
                .as_ref()
                .unwrap()
        }
    }
{{ end }}<dnl>

    // setters
{{ each node-field }}<dnl>
    /// 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-rust-field-type }}) {
        unsafe { {{ helper external-setter-name }}(&mut self.blob, {{ helper node-field-rust-field-name }}.into_blob()) }
    }
{{ end }}<dnl>

    #[allow(dead_code)]
    pub(crate) fn into_internal(self) -> Internal{{ helper node-camelcase-name }} {
        unsafe { {{ helper external-into-internal-name }}(self.into_blob()) }
    }
}

extern \"C\" {
{{ each node-field }}<dnl>
    fn {{ helper external-getter-name }}(blob: *const Blob<{{ helper node-camelcase-name }}>) -> *mut Blob<{{ helper node-field-rust-field-type }}>;
    fn {{ helper external-setter-name }}(blob: *mut Blob<{{ helper node-camelcase-name }}>, blob: Blob<{{ helper node-field-rust-field-type }}>);
{{ end }}<dnl>
    fn {{ helper external-into-internal-name }}(blob: Blob<{{ helper node-camelcase-name }}>) -> Internal{{ helper node-camelcase-name }};
    fn {{ helper external-drop-name }}(blob: *mut Blob<{{ helper node-camelcase-name }}>);
}

impl InnerNode for {{ helper node-camelcase-name }} {
    fn expression(&self) -> &Loc {
        self.get_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>
    }
}

impl Drop for {{ helper node-camelcase-name }} {
    fn drop(&mut self) {
        unsafe { {{ helper external-drop-name }}(&mut self.blob) }
    }
}";

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::<NodeField, F::Helper>(
        "external-getter-name",
        lib_ruby_parser_bindings::helpers::nodes::field_getter::name,
    );
    fns.register::<NodeField, F::Helper>(
        "external-setter-name",
        lib_ruby_parser_bindings::helpers::nodes::field_setter::name,
    );
    fns.register::<Node, F::Helper>(
        "external-into-internal-name",
        lib_ruby_parser_bindings::helpers::nodes::into_internal::name,
    );
    fns.register::<Node, F::Helper>(
        "external-drop-name",
        lib_ruby_parser_bindings::helpers::nodes::drop_variant::name,
    );
    fns.register::<Node, F::Helper>("imports", local_helpers::imports);
    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/{}/external.rs", dir);
    std::fs::write(&path, contents).unwrap();
}

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

    use super::*;

    pub(crate) fn imports(node: &Node) -> String {
        let mut imports = vec![];
        imports.push("use crate::nodes::InnerNode;");
        imports.push("use crate::nodes::InspectVec;");
        imports.push("use crate::Loc;");

        use lib_ruby_parser_nodes::NodeFieldType::*;

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

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

        if node_has_field!(node, MaybeNode { .. } | MaybeLoc | MaybeStr { .. }) {
            imports.push("use crate::containers::ExternalMaybe as Maybe;");
        }

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

        if node_has_field!(node, Nodes) {
            imports.push("use crate::containers::ExternalList as List;");
        }

        if node_has_field!(node, Str { .. } | MaybeStr { .. }) {
            imports.push("use crate::containers::ExternalStringPtr as StringPtr;");
        }

        imports.join("\n")
    }

    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!(""),
        }
    }
}