From 211f8fec04617178bf424542af7de9b4490d7436 Mon Sep 17 00:00:00 2001 From: Alex Rocha Date: Wed, 15 Oct 2025 18:46:56 +0100 Subject: [PATCH] Simplify Rust node generation with explicit naming Instead of auto-generating nested module paths from RBS nested naming conventions, use explicit rust_name fields in config.yml and generate flat structs. - Add rust_name field to all node definitions in config.yml - Remove complex module/path parsing logic from build.rs - Generate flat structs (e.g., ClassNode) instead of nested modules - Add Node enum to wrap all node types This makes the generated Rust code easier to work with. --- config.yml | 68 +++++++++++++++++++++++++++++ rust/ruby-rbs/build.rs | 99 ++++++------------------------------------ 2 files changed, 81 insertions(+), 86 deletions(-) diff --git a/config.yml b/config.yml index 32988c1b8..c272abfbe 100644 --- a/config.yml +++ b/config.yml @@ -1,19 +1,23 @@ nodes: - name: RBS::AST::Annotation + rust_name: AnnotationNode fields: - name: string c_type: rbs_string - name: RBS::AST::Bool + rust_name: BoolNode expose_to_ruby: false expose_location: false fields: - name: value c_type: bool - name: RBS::AST::Comment + rust_name: CommentNode fields: - name: string c_type: rbs_string - name: RBS::AST::Declarations::Class + rust_name: ClassNode fields: - name: name c_type: rbs_type_name @@ -28,12 +32,14 @@ nodes: - name: comment c_type: rbs_ast_comment - name: RBS::AST::Declarations::Class::Super + rust_name: ClassSuperNode fields: - name: name c_type: rbs_type_name - name: args c_type: rbs_node_list - name: RBS::AST::Declarations::ClassAlias + rust_name: ClassAliasNode fields: - name: new_name c_type: rbs_type_name @@ -44,6 +50,7 @@ nodes: - name: annotations c_type: rbs_node_list - name: RBS::AST::Declarations::Constant + rust_name: ConstantNode fields: - name: name c_type: rbs_type_name @@ -54,6 +61,7 @@ nodes: - name: annotations c_type: rbs_node_list - name: RBS::AST::Declarations::Global + rust_name: GlobalNode fields: - name: name c_type: rbs_ast_symbol @@ -64,6 +72,7 @@ nodes: - name: annotations c_type: rbs_node_list - name: RBS::AST::Declarations::Interface + rust_name: InterfaceNode fields: - name: name c_type: rbs_type_name @@ -76,6 +85,7 @@ nodes: - name: comment c_type: rbs_ast_comment - name: RBS::AST::Declarations::Module + rust_name: ModuleNode fields: - name: name c_type: rbs_type_name @@ -90,12 +100,14 @@ nodes: - name: comment c_type: rbs_ast_comment - name: RBS::AST::Declarations::Module::Self + rust_name: ModuleSelfNode fields: - name: name c_type: rbs_type_name - name: args c_type: rbs_node_list - name: RBS::AST::Declarations::ModuleAlias + rust_name: ModuleAliasNode fields: - name: new_name c_type: rbs_type_name @@ -106,6 +118,7 @@ nodes: - name: annotations c_type: rbs_node_list - name: RBS::AST::Declarations::TypeAlias + rust_name: TypeAliasNode fields: - name: name c_type: rbs_type_name @@ -118,21 +131,25 @@ nodes: - name: comment c_type: rbs_ast_comment - name: RBS::AST::Directives::Use + rust_name: UseNode fields: - name: clauses c_type: rbs_node_list - name: RBS::AST::Directives::Use::SingleClause + rust_name: UseSingleClauseNode fields: - name: type_name c_type: rbs_type_name - name: new_name c_type: rbs_ast_symbol - name: RBS::AST::Directives::Use::WildcardClause + rust_name: UseWildcardClauseNode fields: - name: namespace c_type: rbs_namespace c_name: rbs_namespace - name: RBS::AST::Members::Alias + rust_name: AliasNode fields: - name: new_name c_type: rbs_ast_symbol @@ -145,6 +162,7 @@ nodes: - name: comment c_type: rbs_ast_comment - name: RBS::AST::Members::AttrAccessor + rust_name: AttrAccessorNode fields: - name: name c_type: rbs_ast_symbol @@ -161,6 +179,7 @@ nodes: - name: visibility c_type: rbs_keyword - name: RBS::AST::Members::AttrReader + rust_name: AttrReaderNode fields: - name: name c_type: rbs_ast_symbol @@ -177,6 +196,7 @@ nodes: - name: visibility c_type: rbs_keyword - name: RBS::AST::Members::AttrWriter + rust_name: AttrWriterNode fields: - name: name c_type: rbs_ast_symbol @@ -193,6 +213,7 @@ nodes: - name: visibility c_type: rbs_keyword - name: RBS::AST::Members::ClassInstanceVariable + rust_name: ClassInstanceVariableNode fields: - name: name c_type: rbs_ast_symbol @@ -201,6 +222,7 @@ nodes: - name: comment c_type: rbs_ast_comment - name: RBS::AST::Members::ClassVariable + rust_name: ClassVariableNode fields: - name: name c_type: rbs_ast_symbol @@ -209,6 +231,7 @@ nodes: - name: comment c_type: rbs_ast_comment - name: RBS::AST::Members::Extend + rust_name: ExtendNode fields: - name: name c_type: rbs_type_name @@ -219,6 +242,7 @@ nodes: - name: comment c_type: rbs_ast_comment - name: RBS::AST::Members::Include + rust_name: IncludeNode fields: - name: name c_type: rbs_type_name @@ -229,6 +253,7 @@ nodes: - name: comment c_type: rbs_ast_comment - name: RBS::AST::Members::InstanceVariable + rust_name: InstanceVariableNode fields: - name: name c_type: rbs_ast_symbol @@ -237,6 +262,7 @@ nodes: - name: comment c_type: rbs_ast_comment - name: RBS::AST::Members::MethodDefinition + rust_name: MethodDefinitionNode fields: - name: name c_type: rbs_ast_symbol @@ -253,6 +279,7 @@ nodes: - name: visibility c_type: rbs_keyword - name: RBS::AST::Members::MethodDefinition::Overload + rust_name: MethodDefinitionOverloadNode expose_location: false fields: - name: annotations @@ -260,6 +287,7 @@ nodes: - name: method_type c_type: rbs_node - name: RBS::AST::Members::Prepend + rust_name: PrependNode fields: - name: name c_type: rbs_type_name @@ -270,8 +298,11 @@ nodes: - name: comment c_type: rbs_ast_comment - name: RBS::AST::Members::Private + rust_name: PrivateNode - name: RBS::AST::Members::Public + rust_name: PublicNode - name: RBS::AST::TypeParam + rust_name: TypeParamNode fields: - name: name c_type: rbs_ast_symbol @@ -286,18 +317,21 @@ nodes: - name: unchecked c_type: bool - name: RBS::AST::Integer + rust_name: IntegerNode expose_to_ruby: false expose_location: false fields: - name: string_representation c_type: rbs_string - name: RBS::AST::String + rust_name: StringNode expose_to_ruby: false expose_location: false fields: - name: string c_type: rbs_string - name: RBS::MethodType + rust_name: MethodTypeNode fields: - name: type_params c_type: rbs_node_list @@ -306,6 +340,7 @@ nodes: - name: block c_type: rbs_types_block - name: RBS::Namespace + rust_name: NamespaceNode expose_location: false fields: - name: path @@ -313,6 +348,7 @@ nodes: - name: absolute c_type: bool - name: RBS::Signature + rust_name: SignatureNode expose_to_ruby: false expose_location: false fields: @@ -321,6 +357,7 @@ nodes: - name: declarations c_type: rbs_node_list - name: RBS::TypeName + rust_name: TypeNameNode expose_location: false fields: - name: namespace @@ -329,24 +366,35 @@ nodes: - name: name c_type: rbs_ast_symbol - name: RBS::Types::Alias + rust_name: AliasTypeNode fields: - name: name c_type: rbs_type_name - name: args c_type: rbs_node_list - name: RBS::Types::Bases::Any + rust_name: AnyTypeNode fields: - name: todo c_type: bool - name: RBS::Types::Bases::Bool + rust_name: BoolTypeNode - name: RBS::Types::Bases::Bottom + rust_name: BottomTypeNode - name: RBS::Types::Bases::Class + rust_name: ClassTypeNode - name: RBS::Types::Bases::Instance + rust_name: InstanceTypeNode - name: RBS::Types::Bases::Nil + rust_name: NilTypeNode - name: RBS::Types::Bases::Self + rust_name: SelfTypeNode - name: RBS::Types::Bases::Top + rust_name: TopTypeNode - name: RBS::Types::Bases::Void + rust_name: VoidTypeNode - name: RBS::Types::Block + rust_name: BlockTypeNode expose_location: true fields: - name: type @@ -356,16 +404,19 @@ nodes: - name: self_type c_type: rbs_node - name: RBS::Types::ClassInstance + rust_name: ClassInstanceTypeNode fields: - name: name c_type: rbs_type_name - name: args c_type: rbs_node_list - name: RBS::Types::ClassSingleton + rust_name: ClassSingletonTypeNode fields: - name: name c_type: rbs_type_name - name: RBS::Types::Function + rust_name: FunctionTypeNode expose_location: false fields: - name: required_positionals @@ -385,30 +436,36 @@ nodes: - name: return_type c_type: rbs_node - name: RBS::Types::Function::Param + rust_name: FunctionParamNode fields: - name: type c_type: rbs_node - name: name c_type: rbs_ast_symbol - name: RBS::Types::Interface + rust_name: InterfaceTypeNode fields: - name: name c_type: rbs_type_name - name: args c_type: rbs_node_list - name: RBS::Types::Intersection + rust_name: IntersectionTypeNode fields: - name: types c_type: rbs_node_list - name: RBS::Types::Literal + rust_name: LiteralTypeNode fields: - name: literal c_type: rbs_node - name: RBS::Types::Optional + rust_name: OptionalTypeNode fields: - name: type c_type: rbs_node - name: RBS::Types::Proc + rust_name: ProcTypeNode fields: - name: type c_type: rbs_node @@ -417,10 +474,12 @@ nodes: - name: self_type c_type: rbs_node - name: RBS::Types::Record + rust_name: RecordTypeNode fields: - name: all_fields c_type: rbs_hash - name: RBS::Types::Record::FieldType + rust_name: RecordFieldTypeNode expose_to_ruby: false expose_location: false fields: @@ -429,29 +488,35 @@ nodes: - name: required c_type: bool - name: RBS::Types::Tuple + rust_name: TupleTypeNode fields: - name: types c_type: rbs_node_list - name: RBS::Types::Union + rust_name: UnionTypeNode fields: - name: types c_type: rbs_node_list - name: RBS::Types::UntypedFunction + rust_name: UntypedFunctionTypeNode expose_location: false fields: - name: return_type c_type: rbs_node - name: RBS::Types::Variable + rust_name: VariableTypeNode fields: - name: name c_type: rbs_ast_symbol - name: RBS::AST::Ruby::Annotations::NodeTypeAssertion + rust_name: NodeTypeAssertionNode fields: - name: prefix_location c_type: rbs_location - name: type c_type: rbs_node - name: RBS::AST::Ruby::Annotations::ColonMethodTypeAnnotation + rust_name: ColonMethodTypeAnnotationNode fields: - name: prefix_location c_type: rbs_location @@ -460,6 +525,7 @@ nodes: - name: method_type c_type: rbs_node - name: RBS::AST::Ruby::Annotations::MethodTypesAnnotation + rust_name: MethodTypesAnnotationNode fields: - name: prefix_location c_type: rbs_location @@ -468,6 +534,7 @@ nodes: - name: vertical_bar_locations c_type: rbs_location_list - name: RBS::AST::Ruby::Annotations::SkipAnnotation + rust_name: SkipAnnotationNode fields: - name: prefix_location c_type: rbs_location @@ -476,6 +543,7 @@ nodes: - name: comment_location c_type: rbs_location - name: RBS::AST::Ruby::Annotations::ReturnTypeAnnotation + rust_name: ReturnTypeAnnotationNode fields: - name: prefix_location c_type: rbs_location diff --git a/rust/ruby-rbs/build.rs b/rust/ruby-rbs/build.rs index d064a689d..299145f81 100644 --- a/rust/ruby-rbs/build.rs +++ b/rust/ruby-rbs/build.rs @@ -9,6 +9,7 @@ struct Config { #[derive(Debug, Deserialize)] struct Node { name: String, + rust_name: String, } fn main() -> Result<(), Box> { @@ -27,40 +28,6 @@ fn main() -> Result<(), Box> { Ok(()) } -fn replace_reserved_keyword(name: &str) -> &str { - match name { - "Use" => "UseDirective", - "Self" => "SelfType", - _ => name, - } -} - -fn safe_module_name(name: &str) -> String { - let name = replace_reserved_keyword(name); - - let chars: Vec = name.chars().collect(); - let mut result = String::new(); - - for (i, &ch) in chars.iter().enumerate() { - // Insert underscore before uppercase if: - // - Not at the start - // - Previous char was lowercase OR - // - Previous was uppercase but next is lowercase - // e.g., "RBSTypes" -> "RBS_Types" -> "rbs_types" - if i > 0 && ch.is_uppercase() { - let prev_was_lower = chars[i - 1].is_lowercase(); - let next_is_lower = chars.get(i + 1).is_some_and(|c| c.is_lowercase()); - - if prev_was_lower || (chars[i - 1].is_uppercase() && next_is_lower) { - result.push('_'); - } - } - result.push(ch); - } - - result.to_lowercase() -} - fn generate(config: &Config) -> Result<(), Box> { let out_dir = env::var("OUT_DIR")?; let dest_path = Path::new(&out_dir).join("bindings.rs"); @@ -71,62 +38,22 @@ fn generate(config: &Config) -> Result<(), Box> { writeln!(file, "// Nodes to generate: {}", config.nodes.len())?; writeln!(file)?; - let mut current_path: Vec = Vec::new(); - let mut first_in_module = true; - + // TODO: Go through all of the nodes and generate the structs to back them up for node in &config.nodes { - // Parse node path (skip "RBS" prefix) - let parts: Vec<_> = node.name.split("::").skip(1).collect(); - let (modules, struct_name) = parts.split_at(parts.len() - 1); - - // Transform module and struct names - let modules: Vec = modules.iter().map(|s| safe_module_name(s)).collect(); - let struct_name = { - let name = struct_name[0]; - replace_reserved_keyword(name).to_string() - }; - - // Find where paths diverge - let common_len = current_path - .iter() - .zip(&modules) - .take_while(|(a, b)| a == b) - .count(); - - // Close old modules - for depth in (common_len..current_path.len()).rev() { - writeln!(file, "{}}}", " ".repeat(depth))?; - first_in_module = false; - } - - // Open new modules - for (depth, module) in modules.iter().enumerate().skip(common_len) { - if !first_in_module { - writeln!(file)?; - } - writeln!(file, "{}pub mod {} {{", " ".repeat(depth), module)?; - first_in_module = true; - } - - // Write struct (with spacing if not first in module) - if !first_in_module { - writeln!(file)?; - } - writeln!( - file, - "{}pub struct {} {{}}", - " ".repeat(modules.len()), - struct_name - )?; - first_in_module = false; - - current_path = modules; + writeln!(file, "pub struct {} {{}}\n", node.rust_name)?; } - // Close remaining modules - for depth in (0..current_path.len()).rev() { - writeln!(file, "{}}}", " ".repeat(depth))?; + // Generate the Node enum to wrap all of the structs + writeln!(file, "pub enum Node {{")?; + for node in &config.nodes { + let variant_name = node + .rust_name + .strip_suffix("Node") + .unwrap_or(&node.rust_name); + + writeln!(file, " {}({}),", variant_name, node.rust_name)?; } + writeln!(file, "}}")?; Ok(()) }