kanayago 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +12 -0
  3. data/.ruby-version +1 -0
  4. data/README.md +20 -29
  5. data/Rakefile +24 -120
  6. data/ext/kanayago/extconf.rb +14 -0
  7. data/ext/kanayago/kanayago.c +554 -235
  8. data/ext/kanayago/kanayago.h +5 -0
  9. data/ext/kanayago/literal_node.c +343 -0
  10. data/ext/kanayago/literal_node.h +30 -0
  11. data/ext/kanayago/pattern_node.c +78 -0
  12. data/ext/kanayago/pattern_node.h +13 -0
  13. data/ext/kanayago/scope_node.c +34 -0
  14. data/ext/kanayago/scope_node.h +8 -0
  15. data/ext/kanayago/statement_node.c +795 -0
  16. data/ext/kanayago/statement_node.h +66 -0
  17. data/ext/kanayago/string_node.c +192 -0
  18. data/ext/kanayago/string_node.h +19 -0
  19. data/ext/kanayago/variable_node.c +72 -0
  20. data/ext/kanayago/variable_node.h +12 -0
  21. data/lib/kanayago/literal_node.rb +87 -0
  22. data/lib/kanayago/pattern_node.rb +19 -0
  23. data/lib/kanayago/statement_node.rb +222 -0
  24. data/lib/kanayago/string_node.rb +43 -0
  25. data/lib/kanayago/variable_node.rb +23 -0
  26. data/lib/kanayago/version.rb +1 -1
  27. data/lib/kanayago.rb +22 -0
  28. data/patch/3.4/copy_target.rb +78 -0
  29. data/patch/3.4/kanayago.patch +162 -0
  30. data/patch/head/copy_target.rb +84 -0
  31. data/patch/head/kanayago.patch +162 -0
  32. data/sample/minitest_generator.rb +266 -0
  33. data/sample/test_generator.rb +272 -0
  34. data/script/setup_parser.rb +136 -0
  35. data/typeprof.conf.json +9 -0
  36. metadata +30 -64
  37. data/ext/kanayago/ccan/check_type/check_type.h +0 -63
  38. data/ext/kanayago/ccan/container_of/container_of.h +0 -142
  39. data/ext/kanayago/ccan/list/list.h +0 -791
  40. data/ext/kanayago/ccan/str/str.h +0 -17
  41. data/ext/kanayago/constant.h +0 -53
  42. data/ext/kanayago/id.h +0 -347
  43. data/ext/kanayago/id_table.h +0 -39
  44. data/ext/kanayago/internal/array.h +0 -151
  45. data/ext/kanayago/internal/basic_operators.h +0 -64
  46. data/ext/kanayago/internal/bignum.h +0 -244
  47. data/ext/kanayago/internal/bits.h +0 -568
  48. data/ext/kanayago/internal/compile.h +0 -34
  49. data/ext/kanayago/internal/compilers.h +0 -107
  50. data/ext/kanayago/internal/complex.h +0 -29
  51. data/ext/kanayago/internal/encoding.h +0 -36
  52. data/ext/kanayago/internal/error.h +0 -218
  53. data/ext/kanayago/internal/fixnum.h +0 -184
  54. data/ext/kanayago/internal/gc.h +0 -322
  55. data/ext/kanayago/internal/hash.h +0 -191
  56. data/ext/kanayago/internal/imemo.h +0 -261
  57. data/ext/kanayago/internal/io.h +0 -140
  58. data/ext/kanayago/internal/numeric.h +0 -274
  59. data/ext/kanayago/internal/parse.h +0 -117
  60. data/ext/kanayago/internal/rational.h +0 -71
  61. data/ext/kanayago/internal/re.h +0 -28
  62. data/ext/kanayago/internal/ruby_parser.h +0 -125
  63. data/ext/kanayago/internal/sanitizers.h +0 -297
  64. data/ext/kanayago/internal/serial.h +0 -23
  65. data/ext/kanayago/internal/static_assert.h +0 -16
  66. data/ext/kanayago/internal/string.h +0 -186
  67. data/ext/kanayago/internal/symbol.h +0 -45
  68. data/ext/kanayago/internal/thread.h +0 -79
  69. data/ext/kanayago/internal/variable.h +0 -72
  70. data/ext/kanayago/internal/vm.h +0 -137
  71. data/ext/kanayago/internal/warnings.h +0 -16
  72. data/ext/kanayago/internal.h +0 -108
  73. data/ext/kanayago/lex.c +0 -302
  74. data/ext/kanayago/method.h +0 -255
  75. data/ext/kanayago/node.c +0 -440
  76. data/ext/kanayago/node.h +0 -111
  77. data/ext/kanayago/node_name.inc +0 -224
  78. data/ext/kanayago/parse.c +0 -26931
  79. data/ext/kanayago/parse.h +0 -244
  80. data/ext/kanayago/parse.tmp.y +0 -16145
  81. data/ext/kanayago/parser_bits.h +0 -564
  82. data/ext/kanayago/parser_node.h +0 -32
  83. data/ext/kanayago/parser_st.c +0 -164
  84. data/ext/kanayago/parser_st.h +0 -162
  85. data/ext/kanayago/parser_value.h +0 -106
  86. data/ext/kanayago/probes.h +0 -4
  87. data/ext/kanayago/ruby_assert.h +0 -14
  88. data/ext/kanayago/ruby_atomic.h +0 -23
  89. data/ext/kanayago/ruby_parser.c +0 -1165
  90. data/ext/kanayago/rubyparser.h +0 -1391
  91. data/ext/kanayago/shape.h +0 -234
  92. data/ext/kanayago/st.c +0 -2339
  93. data/ext/kanayago/symbol.h +0 -123
  94. data/ext/kanayago/thread_pthread.h +0 -168
  95. data/ext/kanayago/universal_parser.c +0 -230
  96. data/ext/kanayago/vm_core.h +0 -2215
  97. data/ext/kanayago/vm_opts.h +0 -67
@@ -0,0 +1,266 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../lib/kanayago'
4
+
5
+ class MinitestGenerator
6
+ def initialize(code)
7
+ @code = code
8
+ @ast = Kanayago.parse(code)
9
+ @current_visibility = :public
10
+ end
11
+
12
+ def generate
13
+ class_infos = extract_classes(@ast)
14
+
15
+ return '# No classes found in the code' if class_infos.empty?
16
+
17
+ # Generate tests for all classes
18
+ class_infos.map { |class_info| generate_minitest(class_info) }.join("\n\n")
19
+ end
20
+
21
+ private
22
+
23
+ # rubocop:disable Metrics/CyclomaticComplexity
24
+ def extract_classes(node, namespace = [])
25
+ classes = []
26
+
27
+ case node
28
+ when Kanayago::ModuleNode
29
+ # Process module and its contents
30
+ module_name = extract_constant_name(node.cpath)
31
+ new_namespace = namespace + [module_name]
32
+ classes.concat(extract_classes(node.body, new_namespace)) if node.respond_to?(:body)
33
+
34
+ when Kanayago::ClassNode
35
+ # Process class and collect its methods
36
+ class_name = extract_constant_name(node.cpath)
37
+ full_name = (namespace + [class_name]).join('::')
38
+ methods = extract_methods(node.body)
39
+
40
+ classes << {
41
+ name: full_name,
42
+ simple_name: class_name,
43
+ methods: methods
44
+ }
45
+
46
+ # Also look for nested classes within this class
47
+ new_namespace = namespace + [class_name]
48
+ classes.concat(extract_classes(node.body, new_namespace)) if node.respond_to?(:body)
49
+
50
+ when Kanayago::ScopeNode
51
+ classes.concat(extract_classes(node.body, namespace)) if node.respond_to?(:body)
52
+
53
+ when Kanayago::BlockNode
54
+ # BlockNode is an Array, iterate through its elements
55
+ node.each do |child|
56
+ classes.concat(extract_classes(child, namespace))
57
+ end
58
+ end
59
+
60
+ classes
61
+ end
62
+ # rubocop:enable Metrics/CyclomaticComplexity
63
+
64
+ def extract_constant_name(node)
65
+ case node
66
+ when Kanayago::Colon2Node
67
+ # Extract the class name from mid (Symbol)
68
+ node.mid.to_s
69
+ else
70
+ 'UnknownClass'
71
+ end
72
+ end
73
+
74
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
75
+ def extract_methods(node, visibility = :public)
76
+ methods = []
77
+ current_visibility = visibility
78
+
79
+ return methods unless node
80
+
81
+ case node
82
+ when Kanayago::DefinitionNode
83
+ method_info = {
84
+ name: node.mid.to_s,
85
+ visibility: current_visibility,
86
+ parameters: extract_parameters(node)
87
+ }
88
+ methods << method_info if current_visibility == :public
89
+
90
+ when Kanayago::ScopeNode
91
+ # ScopeNode contains body, process it recursively
92
+ methods.concat(extract_methods(node.body, current_visibility)) if node.respond_to?(:body)
93
+
94
+ when Kanayago::BlockNode
95
+ # BlockNode is an Array, iterate through its elements
96
+ node.each do |child|
97
+ # Check if this is a visibility modifier
98
+ if child.is_a?(Kanayago::VariableCallNode) && child.respond_to?(:mid)
99
+ case child.mid.to_s
100
+ when 'private'
101
+ current_visibility = :private
102
+ when 'protected'
103
+ current_visibility = :protected
104
+ when 'public'
105
+ current_visibility = :public
106
+ end
107
+ else
108
+ # Process other nodes (including DefinitionNodes)
109
+ methods.concat(extract_methods(child, current_visibility))
110
+ end
111
+ end
112
+ end
113
+
114
+ methods
115
+ end
116
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
117
+
118
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
119
+ def extract_parameters(def_node)
120
+ params = []
121
+ return params unless def_node.respond_to?(:defn)
122
+
123
+ defn = def_node.defn
124
+ return params unless defn.respond_to?(:args)
125
+
126
+ args = defn.args
127
+ return params unless args.respond_to?(:ainfo)
128
+
129
+ ainfo = args.ainfo
130
+ return params unless ainfo.is_a?(Hash)
131
+
132
+ # Handle required positional parameters (pre_args)
133
+ pre_count = ainfo[:pre_args_num] || 0
134
+ pre_count.times do |i|
135
+ params << {
136
+ name: "arg#{i + 1}",
137
+ type: :required
138
+ }
139
+ end
140
+
141
+ # Handle optional parameters
142
+ if ainfo[:opt_args]
143
+ current_opt = ainfo[:opt_args]
144
+ while current_opt
145
+ if current_opt.respond_to?(:body) && current_opt.body.respond_to?(:id)
146
+ params << {
147
+ name: current_opt.body.id.to_s,
148
+ type: :optional
149
+ }
150
+ end
151
+ current_opt = current_opt.respond_to?(:next) ? current_opt.next : nil
152
+ end
153
+ end
154
+
155
+ # Handle keyword parameters
156
+ if ainfo[:kw_args]
157
+ current_kw = ainfo[:kw_args]
158
+ while current_kw
159
+ if current_kw.respond_to?(:body) && current_kw.body.respond_to?(:id)
160
+ params << {
161
+ name: current_kw.body.id.to_s,
162
+ type: :keyword
163
+ }
164
+ end
165
+ current_kw = current_kw.respond_to?(:next) ? current_kw.next : nil
166
+ end
167
+ end
168
+
169
+ params
170
+ end
171
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
172
+
173
+ def generate_minitest(class_info)
174
+ output = []
175
+ output << "require 'test_helper'"
176
+ output << ''
177
+ output << "class #{class_info[:name]}Test < Minitest::Test"
178
+
179
+ if class_info[:methods].empty?
180
+ output << ' # No public methods found'
181
+ else
182
+ class_info[:methods].each do |method|
183
+ output << generate_method_test(method)
184
+ end
185
+ end
186
+
187
+ output << 'end'
188
+ output.join("\n")
189
+ end
190
+
191
+ def generate_method_test(method)
192
+ lines = []
193
+ method_name = method[:name]
194
+ params = method[:parameters]
195
+
196
+ # Convert method name to test method name
197
+ test_base = method_name.gsub('?', '').gsub('!', '')
198
+
199
+ if method_name.end_with?('?')
200
+ # Boolean methods - generate true/false cases
201
+ lines << " def test_#{test_base}_returns_true_when_condition_is_met"
202
+ lines << ' # TODO: implement'
203
+ lines << ' end'
204
+ lines << ''
205
+ lines << " def test_#{test_base}_returns_false_when_condition_is_not_met"
206
+ lines << ' # TODO: implement'
207
+ lines << ' end'
208
+ elsif method_name == 'initialize'
209
+ # Constructor - check for optional parameters
210
+ lines << ' def test_initialize_creates_new_instance'
211
+ lines << ' # TODO: implement'
212
+ lines << ' end'
213
+
214
+ if params.any? { |p| %i[optional keyword].include?(p[:type]) }
215
+ lines << ''
216
+ lines << ' def test_initialize_uses_default_values_when_optional_parameters_are_not_provided'
217
+ lines << ' # TODO: implement'
218
+ lines << ' end'
219
+ end
220
+ else
221
+ # Regular methods
222
+ lines << " def test_#{test_base}_works_correctly"
223
+ lines << ' # TODO: implement'
224
+ lines << ' end'
225
+
226
+ if params.any? { |p| %i[optional keyword].include?(p[:type]) }
227
+ lines << ''
228
+ lines << " def test_#{test_base}_handles_optional_parameters"
229
+ lines << ' # TODO: implement'
230
+ lines << ' end'
231
+ end
232
+ end
233
+
234
+ lines << ''
235
+ lines.join("\n")
236
+ end
237
+ end
238
+
239
+ # Usage example
240
+ sample_code = <<~RUBY
241
+ class User
242
+ class Profile
243
+ def initialize(name, age = 20)
244
+ @name = name
245
+ @age = age
246
+ end
247
+
248
+ def adult?
249
+ @age >= 18
250
+ end
251
+
252
+ def greet(message = "Hello")
253
+ "\#{message}, \#{@name}!"
254
+ end
255
+
256
+ private
257
+
258
+ def internal_method
259
+ # This should not appear in tests
260
+ end
261
+ end
262
+ end
263
+ RUBY
264
+
265
+ generator = MinitestGenerator.new(sample_code)
266
+ puts generator.generate
@@ -0,0 +1,272 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../lib/kanayago'
4
+
5
+ class TestGenerator
6
+ def initialize(code)
7
+ @code = code
8
+ @ast = Kanayago.parse(code)
9
+ @current_visibility = :public
10
+ end
11
+
12
+ def generate
13
+ class_infos = extract_classes(@ast)
14
+
15
+ return '# No classes found in the code' if class_infos.empty?
16
+
17
+ # Generate tests for all classes
18
+ class_infos.map { |class_info| generate_rspec(class_info) }.join("\n\n")
19
+ end
20
+
21
+ private
22
+
23
+ # rubocop:disable Metrics/CyclomaticComplexity
24
+ def extract_classes(node, namespace = [])
25
+ classes = []
26
+
27
+ case node
28
+ when Kanayago::ModuleNode
29
+ # Process module and its contents
30
+ module_name = extract_constant_name(node.cpath)
31
+ new_namespace = namespace + [module_name]
32
+ classes.concat(extract_classes(node.body, new_namespace)) if node.respond_to?(:body)
33
+
34
+ when Kanayago::ClassNode
35
+ # Process class and collect its methods
36
+ class_name = extract_constant_name(node.cpath)
37
+ full_name = (namespace + [class_name]).join('::')
38
+ methods = extract_methods(node.body)
39
+
40
+ classes << {
41
+ name: full_name,
42
+ simple_name: class_name,
43
+ methods: methods
44
+ }
45
+
46
+ # Also look for nested classes within this class
47
+ new_namespace = namespace + [class_name]
48
+ classes.concat(extract_classes(node.body, new_namespace)) if node.respond_to?(:body)
49
+
50
+ when Kanayago::ScopeNode
51
+ classes.concat(extract_classes(node.body, namespace)) if node.respond_to?(:body)
52
+
53
+ when Kanayago::BlockNode
54
+ # BlockNode is an Array, iterate through its elements
55
+ node.each do |child|
56
+ classes.concat(extract_classes(child, namespace))
57
+ end
58
+ end
59
+
60
+ classes
61
+ end
62
+ # rubocop:enable Metrics/CyclomaticComplexity
63
+
64
+ def extract_constant_name(node)
65
+ case node
66
+ when Kanayago::Colon2Node
67
+ # Extract the class name from mid (Symbol)
68
+ node.mid.to_s
69
+ else
70
+ 'UnknownClass'
71
+ end
72
+ end
73
+
74
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
75
+ def extract_methods(node, visibility = :public)
76
+ methods = []
77
+ current_visibility = visibility
78
+
79
+ return methods unless node
80
+
81
+ case node
82
+ when Kanayago::DefinitionNode
83
+ method_info = {
84
+ name: node.mid.to_s,
85
+ visibility: current_visibility,
86
+ parameters: extract_parameters(node)
87
+ }
88
+ methods << method_info if current_visibility == :public
89
+
90
+ when Kanayago::ScopeNode
91
+ # ScopeNode contains body, process it recursively
92
+ methods.concat(extract_methods(node.body, current_visibility)) if node.respond_to?(:body)
93
+
94
+ when Kanayago::BlockNode
95
+ # BlockNode is an Array, iterate through its elements
96
+ node.each do |child|
97
+ # Check if this is a visibility modifier
98
+ if child.is_a?(Kanayago::VariableCallNode) && child.respond_to?(:mid)
99
+ case child.mid.to_s
100
+ when 'private'
101
+ current_visibility = :private
102
+ when 'protected'
103
+ current_visibility = :protected
104
+ when 'public'
105
+ current_visibility = :public
106
+ end
107
+ else
108
+ # Process other nodes (including DefinitionNodes)
109
+ methods.concat(extract_methods(child, current_visibility))
110
+ end
111
+ end
112
+ end
113
+
114
+ methods
115
+ end
116
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
117
+
118
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
119
+ def extract_parameters(def_node)
120
+ params = []
121
+ return params unless def_node.respond_to?(:defn)
122
+
123
+ defn = def_node.defn
124
+ return params unless defn.respond_to?(:args)
125
+
126
+ args = defn.args
127
+ return params unless args.respond_to?(:ainfo)
128
+
129
+ ainfo = args.ainfo
130
+ return params unless ainfo.is_a?(Hash)
131
+
132
+ # Handle required positional parameters (pre_args)
133
+ pre_count = ainfo[:pre_args_num] || 0
134
+ pre_count.times do |i|
135
+ params << {
136
+ name: "arg#{i + 1}",
137
+ type: :required
138
+ }
139
+ end
140
+
141
+ # Handle optional parameters
142
+ if ainfo[:opt_args]
143
+ current_opt = ainfo[:opt_args]
144
+ while current_opt
145
+ if current_opt.respond_to?(:body) && current_opt.body.respond_to?(:id)
146
+ params << {
147
+ name: current_opt.body.id.to_s,
148
+ type: :optional
149
+ }
150
+ end
151
+ current_opt = current_opt.respond_to?(:next) ? current_opt.next : nil
152
+ end
153
+ end
154
+
155
+ # Handle keyword parameters
156
+ if ainfo[:kw_args]
157
+ current_kw = ainfo[:kw_args]
158
+ while current_kw
159
+ if current_kw.respond_to?(:body) && current_kw.body.respond_to?(:id)
160
+ params << {
161
+ name: current_kw.body.id.to_s,
162
+ type: :keyword
163
+ }
164
+ end
165
+ current_kw = current_kw.respond_to?(:next) ? current_kw.next : nil
166
+ end
167
+ end
168
+
169
+ params
170
+ end
171
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
172
+
173
+ def generate_rspec(class_info)
174
+ output = []
175
+ output << "require 'spec_helper'"
176
+ output << ''
177
+ output << "RSpec.describe #{class_info[:name]} do"
178
+
179
+ if class_info[:methods].empty?
180
+ output << ' # No public methods found'
181
+ else
182
+ class_info[:methods].each do |method|
183
+ output << generate_method_spec(method)
184
+ end
185
+ end
186
+
187
+ output << 'end'
188
+ output.join("\n")
189
+ end
190
+
191
+ def generate_method_spec(method)
192
+ lines = []
193
+ method_name = method[:name]
194
+ params = method[:parameters]
195
+
196
+ lines << " describe '##{method_name}' do"
197
+
198
+ if method_name.end_with?('?')
199
+ # Boolean methods - generate true/false cases
200
+ lines << " context 'when condition is met' do"
201
+ lines << " it 'returns true' do"
202
+ lines << ' # TODO: implement'
203
+ lines << ' end'
204
+ lines << ' end'
205
+ lines << ''
206
+ lines << " context 'when condition is not met' do"
207
+ lines << " it 'returns false' do"
208
+ lines << ' # TODO: implement'
209
+ lines << ' end'
210
+ lines << ' end'
211
+ elsif method_name == 'initialize'
212
+ # Constructor - check for optional parameters
213
+ lines << " it 'creates a new instance' do"
214
+ lines << ' # TODO: implement'
215
+ lines << ' end'
216
+
217
+ if params.any? { |p| %i[optional keyword].include?(p[:type]) }
218
+ lines << ''
219
+ lines << " it 'uses default values when optional parameters are not provided' do"
220
+ lines << ' # TODO: implement'
221
+ lines << ' end'
222
+ end
223
+ else
224
+ # Regular methods
225
+ lines << " it 'works correctly' do"
226
+ lines << ' # TODO: implement'
227
+ lines << ' end'
228
+
229
+ if params.any? { |p| %i[optional keyword].include?(p[:type]) }
230
+ lines << ''
231
+ lines << " context 'with optional parameters' do"
232
+ lines << " it 'handles optional parameters' do"
233
+ lines << ' # TODO: implement'
234
+ lines << ' end'
235
+ lines << ' end'
236
+ end
237
+ end
238
+
239
+ lines << ' end'
240
+ lines << ''
241
+ lines.join("\n")
242
+ end
243
+ end
244
+
245
+ # Usage example
246
+ sample_code = <<~RUBY
247
+ class User
248
+ class Profile
249
+ def initialize(name, age = 20)
250
+ @name = name
251
+ @age = age
252
+ end
253
+
254
+ def adult?
255
+ @age >= 18
256
+ end
257
+
258
+ def greet(message = "Hello")
259
+ "\#{message}, \#{@name}!"
260
+ end
261
+
262
+ private
263
+
264
+ def internal_method
265
+ # This should not appear in tests
266
+ end
267
+ end
268
+ end
269
+ RUBY
270
+
271
+ generator = TestGenerator.new(sample_code)
272
+ puts generator.generate
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'fileutils'
5
+
6
+ # Ruby parser setup script for Kanayago
7
+ # This script downloads Ruby source code and prepares parser files for building
8
+ module KanayagoSetup
9
+ class << self
10
+ def run
11
+ puts 'Setting up Ruby parser files for Kanayago...'
12
+
13
+ # Determine Ruby version
14
+ ruby_version = detect_ruby_version
15
+ puts "Detected Ruby version: #{ruby_version}"
16
+
17
+ # Load copy targets for this Ruby version
18
+ load_copy_targets(ruby_version)
19
+
20
+ # Import parser files
21
+ import_parser_files(ruby_version)
22
+
23
+ # Apply patch
24
+ apply_patch(ruby_version)
25
+
26
+ puts 'Setup completed successfully!'
27
+ rescue StandardError => e
28
+ warn "Error during setup: #{e.message}"
29
+ warn e.backtrace.join("\n")
30
+ exit 1
31
+ end
32
+
33
+ private
34
+
35
+ def detect_ruby_version
36
+ if RUBY_DESCRIPTION.include?('dev')
37
+ 'head'
38
+ else
39
+ RUBY_VERSION[0..2]
40
+ end
41
+ end
42
+
43
+ def load_copy_targets(version)
44
+ copy_target_path = File.expand_path("../patch/#{version}/copy_target.rb", __dir__)
45
+
46
+ raise "Copy target file not found: #{copy_target_path}" unless File.exist?(copy_target_path)
47
+
48
+ require copy_target_path
49
+ end
50
+
51
+ def import_parser_files(version) # rubocop:disable Metrics/PerceivedComplexity
52
+ puts 'Importing Ruby parser files...'
53
+
54
+ tar_name = if version == 'head'
55
+ 'snapshot/snapshot-master.tar.gz'
56
+ else
57
+ "#{version}/ruby-#{RUBY_VERSION}.tar.gz"
58
+ end
59
+
60
+ # Get project root directory
61
+ project_root = File.expand_path('..', __dir__)
62
+ tmp_dir = File.join(project_root, 'tmp')
63
+ tmp_ruby_dir = File.join(tmp_dir, 'ruby')
64
+ tmp_tar_file = File.join(tmp_dir, 'ruby.tar.gz')
65
+
66
+ # Create temporary directory
67
+ FileUtils.mkdir_p(tmp_ruby_dir)
68
+
69
+ # Download Ruby source
70
+ puts 'Downloading Ruby source from cache.ruby-lang.org...'
71
+ system("curl -L https://cache.ruby-lang.org/pub/ruby/#{tar_name} -o #{tmp_tar_file}") ||
72
+ raise('Failed to download Ruby source')
73
+
74
+ # Extract
75
+ puts 'Extracting Ruby source...'
76
+ system("tar -zxf #{tmp_tar_file} -C #{tmp_ruby_dir} --strip-components 1") ||
77
+ raise('Failed to extract Ruby source')
78
+
79
+ dist = File.join(project_root, 'ext', 'kanayago')
80
+
81
+ # Create necessary directories
82
+ MAKE_DIRECTORIES.each do |dir|
83
+ dir_path = File.join(dist, dir)
84
+ FileUtils.mkdir_p(dir_path)
85
+ end
86
+
87
+ # Copy files
88
+ puts 'Copying parser files...'
89
+ RUBY_PARSER_COPY_TARGETS.each do |target|
90
+ src = File.join(tmp_ruby_dir, target)
91
+ dst = File.join(dist, target)
92
+
93
+ if File.exist?(src)
94
+ FileUtils.cp(src, dst)
95
+ else
96
+ warn "Warning: Source file not found: #{src}"
97
+ end
98
+ end
99
+
100
+ # Generate probes.h
101
+ puts 'Generating probes.h...'
102
+ probes_h_path = File.join(dist, 'probes.h')
103
+ File.open(probes_h_path, 'w+') do |f|
104
+ f << <<~SRC
105
+ #define RUBY_DTRACE_PARSE_BEGIN_ENABLED() (0)
106
+ #define RUBY_DTRACE_PARSE_BEGIN(arg0, arg1) (void)(arg0), (void)(arg1);
107
+ #define RUBY_DTRACE_PARSE_END_ENABLED() (0)
108
+ #define RUBY_DTRACE_PARSE_END(arg0, arg1) (void)(arg0), (void)(arg1);
109
+ SRC
110
+ end
111
+
112
+ # Cleanup
113
+ puts 'Cleaning up temporary files...'
114
+ FileUtils.rm_rf(tmp_ruby_dir)
115
+ FileUtils.rm_f(tmp_tar_file)
116
+ end
117
+
118
+ def apply_patch(version)
119
+ puts "Applying patch for Ruby #{version}..."
120
+
121
+ patch_file = File.expand_path("../patch/#{version}/kanayago.patch", __dir__)
122
+
123
+ raise "Patch file not found: #{patch_file}" unless File.exist?(patch_file)
124
+
125
+ # Change to project root directory before applying patch
126
+ project_root = File.expand_path('..', __dir__)
127
+ Dir.chdir(project_root) do
128
+ system("patch -p1 < #{patch_file}") ||
129
+ raise('Failed to apply patch')
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ # Run setup if this script is executed directly
136
+ KanayagoSetup.run if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,9 @@
1
+ {
2
+ "typeprof_version": "experimental",
3
+ "rbs_dir": "sig/",
4
+ "analysis_unit_dirs": [
5
+ "lib/",
6
+ "lib/kanayago/"
7
+ ],
8
+ "diagnostic_severity": "error"
9
+ }