@@ -13,29 +13,79 @@ def initialize(file_path)
1313
1414 def generate
1515 content = File . read ( @file_path )
16- ast = Ripper . sexp ( content )
17- pp ast
18- class_name = extract_class_name ( ast )
19- method_names = extract_method_names ( ast )
20- puts "Class Name: #{ class_name } "
21- puts "Method Names: #{ method_names . join ( ", " ) } "
22- # puts "Method Names: #{method_names.join(", ")}"
23- # puts Ripper.sexp(content)
16+ result = Prism . parse ( content )
17+ class_node = find_first_class_node ( result . value )
18+ return unless class_node
19+
20+ class_name = class_node . name
21+ method_names = get_all_method_names ( class_node )
22+
23+ write_rbi_to_file ( class_name , method_names )
24+ puts "Generated RBI file: #{ @rbi_path } "
2425 end
2526
26- # Takes the syntax tree and extracts the class name
27- def extract_class_name ( ast )
28- class_node = ast [ 1 ] . find { |node | node [ 0 ] == :class }
29- class_node [ 1 ] [ 1 ] if class_node
27+ private
28+
29+ def write_rbi_to_file ( class_name , methods )
30+ rbi = +"class #{ class_name } \n "
31+
32+ methods . each do |method |
33+ name = method [ :name ]
34+ params = method [ :params ]
35+ sig = if params . empty?
36+ " sig { void }"
37+ else
38+ typed_params = params . map { |p | "#{ p } : T.untyped" } . join ( ", " )
39+ " sig { params(#{ typed_params } ).returns(T.untyped) }"
40+ end
41+ rbi << "#{ sig } \n "
42+ def_line = " def #{ name } "
43+ def_line << "(#{ params . join ( ", " ) } )" unless params . empty?
44+ def_line << "; end\n \n "
45+ rbi << def_line
46+ end
47+
48+ rbi << "end\n "
49+ File . write ( @rbi_path , rbi )
3050 end
3151
32- def extract_method_names ( ast )
33- class_node = ast [ 1 ] . find { |node | node [ 0 ] == :class }
34- body = class_node [ 3 ] # :bodystmt
35- return [ ] unless body && body [ 0 ] . is_a? ( Array )
52+ # Recursively find the first ClassNode in the AST
53+ def find_first_class_node ( node )
54+ return node if node . is_a? ( Prism ::ClassNode )
55+ return nil unless node . respond_to? ( :child_nodes )
56+
57+ node . child_nodes . each do |child |
58+ found = find_first_class_node ( child )
59+ return found if found
60+ end
61+ end
62+
63+ # Goes through the class and grab every child node (they will be defined as defNode)
64+ def get_all_method_names ( class_node )
65+ class_node . body . child_nodes
66+ . select { |child | child . is_a? ( Prism ::DefNode ) }
67+ . map do |method_node |
68+ {
69+ name : method_node . name ,
70+ params : get_method_params ( method_node )
71+ }
72+ end
73+ end
74+
75+ def get_method_params ( method_node )
76+ params_node = method_node . parameters
77+ return [ ] unless params_node
78+
79+ all_params = [ ]
3680
37- method_defs = body [ 0 ] . select { |node | node [ 0 ] == :def }
38- method_defs . map { |def_node | def_node [ 1 ] [ 1 ] } # => ["hello", "goodbye"]
81+ all_params += params_node . requireds . map { |p | p . name }
82+ all_params += params_node . optionals . map { |p | p . name }
83+ all_params << params_node . rest . name if params_node . rest
84+ all_params += params_node . posts . map { |p | p . name }
85+ all_params += params_node . keywords . map { |p | p . name }
86+ all_params << params_node . keyword_rest . name if params_node . keyword_rest
87+ all_params << params_node . block . name if params_node . block
88+ all_params . compact
3989 end
4090 end
4191end
0 commit comments