t-ruby 0.0.1

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.
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TRuby
4
+ class RBSGenerator
5
+ def initialize
6
+ # RBS generation configuration
7
+ end
8
+
9
+ def generate(functions, type_aliases)
10
+ lines = []
11
+
12
+ # Add type aliases
13
+ type_aliases.each do |type_alias|
14
+ lines << generate_type_alias(type_alias)
15
+ end
16
+
17
+ lines << "" if type_aliases.any? && functions.any?
18
+
19
+ # Add function signatures
20
+ functions.each do |func|
21
+ lines << generate_function_signature(func)
22
+ end
23
+
24
+ lines.compact.join("\n")
25
+ end
26
+
27
+ def generate_type_aliases(aliases)
28
+ aliases.map { |alias_def| generate_type_alias(alias_def) }.join("\n")
29
+ end
30
+
31
+ def generate_type_alias(type_alias)
32
+ name = type_alias[:name]
33
+ definition = type_alias[:definition]
34
+
35
+ "type #{name} = #{definition}"
36
+ end
37
+
38
+ def generate_function_signature(func)
39
+ name = func[:name]
40
+ params = func[:params] || []
41
+ return_type = func[:return_type]
42
+
43
+ param_str = format_parameters(params)
44
+ return_str = format_return_type(return_type)
45
+
46
+ "def #{name}: (#{param_str}) -> #{return_str}"
47
+ end
48
+
49
+ private
50
+
51
+ def format_parameters(params)
52
+ return if params.empty?
53
+
54
+ param_strs = params.map do |param|
55
+ param_name = param[:name]
56
+ param_type = param[:type] || "Object"
57
+
58
+ "#{param_name}: #{param_type}"
59
+ end
60
+
61
+ param_strs.join(", ")
62
+ end
63
+
64
+ def format_return_type(return_type)
65
+ return "void" if return_type == "void"
66
+ return "nil" if return_type.nil?
67
+
68
+ return_type
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,367 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TRuby
4
+ # Configuration for runtime validation
5
+ class ValidationConfig
6
+ attr_accessor :validate_all, :validate_public_only, :raise_on_error
7
+ attr_accessor :log_violations, :strict_mode
8
+
9
+ def initialize
10
+ @validate_all = true
11
+ @validate_public_only = false
12
+ @raise_on_error = true
13
+ @log_violations = false
14
+ @strict_mode = false
15
+ end
16
+ end
17
+
18
+ # Generates runtime type validation code
19
+ class RuntimeValidator
20
+ attr_reader :config
21
+
22
+ # Type mappings for runtime checks
23
+ TYPE_CHECKS = {
24
+ "String" => ".is_a?(String)",
25
+ "Integer" => ".is_a?(Integer)",
26
+ "Float" => ".is_a?(Float)",
27
+ "Numeric" => ".is_a?(Numeric)",
28
+ "Boolean" => " == true || %s == false",
29
+ "Symbol" => ".is_a?(Symbol)",
30
+ "Array" => ".is_a?(Array)",
31
+ "Hash" => ".is_a?(Hash)",
32
+ "nil" => ".nil?",
33
+ "Object" => ".is_a?(Object)",
34
+ "Class" => ".is_a?(Class)",
35
+ "Module" => ".is_a?(Module)",
36
+ "Proc" => ".is_a?(Proc)",
37
+ "Regexp" => ".is_a?(Regexp)",
38
+ "Range" => ".is_a?(Range)",
39
+ "Time" => ".is_a?(Time)",
40
+ "Date" => ".is_a?(Date)",
41
+ "IO" => ".is_a?(IO)",
42
+ "File" => ".is_a?(File)"
43
+ }.freeze
44
+
45
+ def initialize(config = nil)
46
+ @config = config || ValidationConfig.new
47
+ end
48
+
49
+ # Generate validation code for a function
50
+ def generate_function_validation(function_info)
51
+ validations = []
52
+
53
+ # Parameter validations
54
+ function_info[:params].each do |param|
55
+ next unless param[:type]
56
+
57
+ validation = generate_param_validation(param[:name], param[:type])
58
+ validations << validation if validation
59
+ end
60
+
61
+ # Return type validation (if specified)
62
+ if function_info[:return_type]
63
+ return_validation = generate_return_validation(function_info[:return_type])
64
+ validations << return_validation if return_validation
65
+ end
66
+
67
+ validations
68
+ end
69
+
70
+ # Generate validation for a single parameter
71
+ def generate_param_validation(param_name, type_annotation)
72
+ check_code = generate_type_check(param_name, type_annotation)
73
+ return nil unless check_code
74
+
75
+ error_message = "TypeError: #{param_name} must be #{type_annotation}, got \#{#{param_name}.class}"
76
+
77
+ if @config.raise_on_error
78
+ "raise #{error_message.inspect.gsub('\#{', '#{')} unless #{check_code}"
79
+ else
80
+ "warn #{error_message.inspect.gsub('\#{', '#{')} unless #{check_code}"
81
+ end
82
+ end
83
+
84
+ # Generate type check expression
85
+ def generate_type_check(var_name, type_annotation)
86
+ # Handle nil type
87
+ return "#{var_name}.nil?" if type_annotation == "nil"
88
+
89
+ # Handle union types
90
+ if type_annotation.include?("|")
91
+ return generate_union_check(var_name, type_annotation)
92
+ end
93
+
94
+ # Handle generic types
95
+ if type_annotation.include?("<")
96
+ return generate_generic_check(var_name, type_annotation)
97
+ end
98
+
99
+ # Handle intersection types
100
+ if type_annotation.include?("&")
101
+ return generate_intersection_check(var_name, type_annotation)
102
+ end
103
+
104
+ # Handle optional types (ending with ?)
105
+ if type_annotation.end_with?("?")
106
+ base_type = type_annotation[0..-2]
107
+ base_check = generate_simple_type_check(var_name, base_type)
108
+ return "(#{var_name}.nil? || #{base_check})"
109
+ end
110
+
111
+ # Simple type check
112
+ generate_simple_type_check(var_name, type_annotation)
113
+ end
114
+
115
+ # Generate simple type check
116
+ def generate_simple_type_check(var_name, type_name)
117
+ if type_name == "Boolean"
118
+ "(#{var_name} == true || #{var_name} == false)"
119
+ elsif TYPE_CHECKS.key?(type_name)
120
+ "#{var_name}#{TYPE_CHECKS[type_name]}"
121
+ else
122
+ # Custom type - use is_a? or respond_to?
123
+ "#{var_name}.is_a?(#{type_name})"
124
+ end
125
+ end
126
+
127
+ # Generate union type check
128
+ def generate_union_check(var_name, union_type)
129
+ types = union_type.split("|").map(&:strip)
130
+ checks = types.map { |t| generate_type_check(var_name, t) }
131
+ "(#{checks.join(' || ')})"
132
+ end
133
+
134
+ # Generate generic type check
135
+ def generate_generic_check(var_name, generic_type)
136
+ match = generic_type.match(/^(\w+)<(.+)>$/)
137
+ return nil unless match
138
+
139
+ container_type = match[1]
140
+ element_type = match[2]
141
+
142
+ case container_type
143
+ when "Array"
144
+ "#{var_name}.is_a?(Array) && #{var_name}.all? { |_e| #{generate_type_check('_e', element_type)} }"
145
+ when "Hash"
146
+ if element_type.include?(",")
147
+ key_type, value_type = element_type.split(",").map(&:strip)
148
+ "#{var_name}.is_a?(Hash) && #{var_name}.all? { |_k, _v| #{generate_type_check('_k', key_type)} && #{generate_type_check('_v', value_type)} }"
149
+ else
150
+ "#{var_name}.is_a?(Hash)"
151
+ end
152
+ when "Set"
153
+ "#{var_name}.is_a?(Set) && #{var_name}.all? { |_e| #{generate_type_check('_e', element_type)} }"
154
+ else
155
+ # Generic with unknown container - just check container type
156
+ "#{var_name}.is_a?(#{container_type})"
157
+ end
158
+ end
159
+
160
+ # Generate intersection type check
161
+ def generate_intersection_check(var_name, intersection_type)
162
+ types = intersection_type.split("&").map(&:strip)
163
+ checks = types.map { |t| generate_type_check(var_name, t) }
164
+ "(#{checks.join(' && ')})"
165
+ end
166
+
167
+ # Generate return value validation wrapper
168
+ def generate_return_validation(return_type)
169
+ {
170
+ type: :return,
171
+ check: generate_type_check("_result", return_type),
172
+ return_type: return_type
173
+ }
174
+ end
175
+
176
+ # Transform source code to include runtime validations
177
+ def transform(source, parse_result)
178
+ lines = source.split("\n")
179
+ output_lines = []
180
+ in_function = false
181
+ current_function = nil
182
+ function_indent = 0
183
+
184
+ parse_result[:functions].each do |func|
185
+ @function_validations ||= {}
186
+ @function_validations[func[:name]] = generate_function_validation(func)
187
+ end
188
+
189
+ lines.each_with_index do |line, idx|
190
+ # Check for function definition
191
+ if line.match?(/^\s*def\s+(\w+)/)
192
+ match = line.match(/^(\s*)def\s+(\w+)/)
193
+ function_indent = match[1].length
194
+ function_name = match[2]
195
+
196
+ # Add validation code after function definition
197
+ output_lines << line
198
+
199
+ if @function_validations && @function_validations[function_name]
200
+ validations = @function_validations[function_name]
201
+ param_validations = validations.select { |v| v.is_a?(String) }
202
+
203
+ param_validations.each do |validation|
204
+ output_lines << "#{' ' * (function_indent + 2)}#{validation}"
205
+ end
206
+ end
207
+
208
+ in_function = true
209
+ current_function = function_name
210
+ elsif in_function && line.match?(/^\s*end\s*$/)
211
+ # End of function
212
+ in_function = false
213
+ current_function = nil
214
+ output_lines << line
215
+ else
216
+ output_lines << line
217
+ end
218
+ end
219
+
220
+ output_lines.join("\n")
221
+ end
222
+
223
+ # Generate a validation module that can be included
224
+ def generate_validation_module(functions)
225
+ module_code = <<~RUBY
226
+ # frozen_string_literal: true
227
+ # Auto-generated runtime type validation module
228
+
229
+ module TRubyValidation
230
+ class TypeError < StandardError; end
231
+
232
+ def self.validate_type(value, expected_type, param_name = "value")
233
+ RUBY
234
+
235
+ module_code += <<~RUBY
236
+ case expected_type
237
+ when "String"
238
+ raise TypeError, "\#{param_name} must be String, got \#{value.class}" unless value.is_a?(String)
239
+ when "Integer"
240
+ raise TypeError, "\#{param_name} must be Integer, got \#{value.class}" unless value.is_a?(Integer)
241
+ when "Float"
242
+ raise TypeError, "\#{param_name} must be Float, got \#{value.class}" unless value.is_a?(Float)
243
+ when "Boolean"
244
+ raise TypeError, "\#{param_name} must be Boolean, got \#{value.class}" unless value == true || value == false
245
+ when "Symbol"
246
+ raise TypeError, "\#{param_name} must be Symbol, got \#{value.class}" unless value.is_a?(Symbol)
247
+ when "Array"
248
+ raise TypeError, "\#{param_name} must be Array, got \#{value.class}" unless value.is_a?(Array)
249
+ when "Hash"
250
+ raise TypeError, "\#{param_name} must be Hash, got \#{value.class}" unless value.is_a?(Hash)
251
+ when "nil"
252
+ raise TypeError, "\#{param_name} must be nil, got \#{value.class}" unless value.nil?
253
+ else
254
+ # Custom type check
255
+ begin
256
+ type_class = Object.const_get(expected_type)
257
+ raise TypeError, "\#{param_name} must be \#{expected_type}, got \#{value.class}" unless value.is_a?(type_class)
258
+ rescue NameError
259
+ # Unknown type, skip validation
260
+ end
261
+ end
262
+ true
263
+ end
264
+ RUBY
265
+
266
+ # Generate validation methods for each function
267
+ functions.each do |func|
268
+ next if func[:params].empty? && !func[:return_type]
269
+
270
+ method_name = "validate_#{func[:name]}_params"
271
+ param_list = func[:params].map { |p| p[:name] }.join(", ")
272
+
273
+ module_code += "\n def self.#{method_name}(#{param_list})\n"
274
+
275
+ func[:params].each do |param|
276
+ next unless param[:type]
277
+ module_code += " validate_type(#{param[:name]}, #{param[:type].inspect}, #{param[:name].inspect})\n"
278
+ end
279
+
280
+ module_code += " true\n end\n"
281
+ end
282
+
283
+ module_code += "end\n"
284
+ module_code
285
+ end
286
+
287
+ # Check if validation should be applied based on config
288
+ def should_validate?(visibility)
289
+ return true if @config.validate_all
290
+ return visibility == :public if @config.validate_public_only
291
+ false
292
+ end
293
+ end
294
+
295
+ # Runtime type error
296
+ class RuntimeTypeError < StandardError
297
+ attr_reader :expected_type, :actual_type, :value, :location
298
+
299
+ def initialize(message, expected_type: nil, actual_type: nil, value: nil, location: nil)
300
+ super(message)
301
+ @expected_type = expected_type
302
+ @actual_type = actual_type
303
+ @value = value
304
+ @location = location
305
+ end
306
+ end
307
+
308
+ # Mixin for adding runtime validation to classes
309
+ module RuntimeTypeChecks
310
+ def self.included(base)
311
+ base.extend(ClassMethods)
312
+ end
313
+
314
+ module ClassMethods
315
+ def validate_types!
316
+ @_validate_types = true
317
+ end
318
+
319
+ def skip_type_validation!
320
+ @_validate_types = false
321
+ end
322
+
323
+ def type_validation_enabled?
324
+ @_validate_types != false
325
+ end
326
+ end
327
+
328
+ private
329
+
330
+ def validate_param(value, expected_type, param_name)
331
+ return true unless self.class.type_validation_enabled?
332
+
333
+ validator = RuntimeValidator.new
334
+ check_code = validator.generate_type_check("value", expected_type)
335
+
336
+ # Evaluate the check
337
+ unless eval(check_code)
338
+ raise RuntimeTypeError.new(
339
+ "#{param_name} must be #{expected_type}, got #{value.class}",
340
+ expected_type: expected_type,
341
+ actual_type: value.class.to_s,
342
+ value: value
343
+ )
344
+ end
345
+
346
+ true
347
+ end
348
+
349
+ def validate_return(value, expected_type)
350
+ return value unless self.class.type_validation_enabled?
351
+
352
+ validator = RuntimeValidator.new
353
+ check_code = validator.generate_type_check("value", expected_type)
354
+
355
+ unless eval(check_code)
356
+ raise RuntimeTypeError.new(
357
+ "Return value must be #{expected_type}, got #{value.class}",
358
+ expected_type: expected_type,
359
+ actual_type: value.class.to_s,
360
+ value: value
361
+ )
362
+ end
363
+
364
+ value
365
+ end
366
+ end
367
+ end