t-ruby 0.0.35 → 0.0.36
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.
- checksums.yaml +4 -4
- data/README.md +5 -4
- data/lib/t_ruby/benchmark.rb +16 -16
- data/lib/t_ruby/bundler_integration.rb +33 -35
- data/lib/t_ruby/cache.rb +46 -43
- data/lib/t_ruby/cli.rb +3 -3
- data/lib/t_ruby/compiler.rb +25 -27
- data/lib/t_ruby/config.rb +15 -11
- data/lib/t_ruby/constraint_checker.rb +14 -8
- data/lib/t_ruby/declaration_generator.rb +8 -8
- data/lib/t_ruby/doc_generator.rb +33 -33
- data/lib/t_ruby/docs_badge_generator.rb +8 -8
- data/lib/t_ruby/docs_example_verifier.rb +5 -6
- data/lib/t_ruby/error_handler.rb +21 -22
- data/lib/t_ruby/generic_type_parser.rb +3 -3
- data/lib/t_ruby/intersection_type_parser.rb +2 -2
- data/lib/t_ruby/ir.rb +21 -24
- data/lib/t_ruby/lsp_server.rb +128 -138
- data/lib/t_ruby/package_manager.rb +16 -18
- data/lib/t_ruby/parser.rb +7 -7
- data/lib/t_ruby/parser_combinator.rb +22 -22
- data/lib/t_ruby/rbs_generator.rb +2 -4
- data/lib/t_ruby/runtime_validator.rb +41 -37
- data/lib/t_ruby/smt_solver.rb +31 -29
- data/lib/t_ruby/type_alias_registry.rb +1 -0
- data/lib/t_ruby/type_checker.rb +64 -63
- data/lib/t_ruby/type_erasure.rb +2 -4
- data/lib/t_ruby/type_inferencer.rb +22 -26
- data/lib/t_ruby/union_type_parser.rb +3 -3
- data/lib/t_ruby/version.rb +1 -1
- data/lib/t_ruby/watcher.rb +18 -14
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f8847929796ade811f3b9c3cd3ddcb86448e18927e40c2318e562dc9c35e312b
|
|
4
|
+
data.tar.gz: 526a69c0dc752147ed3a29b208ebb85cd019a20b03efbd21ed28a25d5badc37b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 55d2c62ad5fd9071da3bfd363fad13e14d08f340c9ed8b19ab1c5b677db4a4e583dc8f183f67006efbfa44a2fbd1e0c77f14c6dd7af39b6c2d745978b938dcdf
|
|
7
|
+
data.tar.gz: 93d96b65f461f68dc3833a1be714b51a616edefc1b01e97d56016cddbba7a48843f098efa49634d3a55043c8ffca21bd47cad660200cd63fd75e5deb92a2811e
|
data/README.md
CHANGED
|
@@ -138,7 +138,7 @@ We still love Ruby, and we want this to be
|
|
|
138
138
|
gem install t-ruby
|
|
139
139
|
|
|
140
140
|
# from source
|
|
141
|
-
git clone https://github.com/
|
|
141
|
+
git clone https://github.com/type-ruby/t-ruby
|
|
142
142
|
cd t-ruby && bundle install
|
|
143
143
|
```
|
|
144
144
|
|
|
@@ -252,10 +252,11 @@ watch:
|
|
|
252
252
|
|
|
253
253
|
---
|
|
254
254
|
|
|
255
|
-
##
|
|
255
|
+
## Links
|
|
256
256
|
|
|
257
|
-
**
|
|
258
|
-
- [VS Code Extension](./docs/vscode/en/getting-started.md)
|
|
257
|
+
**IDE Support**
|
|
258
|
+
- [VS Code Extension (and Cursor)](./docs/vscode/en/getting-started.md)
|
|
259
|
+
- [JetBrains Plugin](./docs/jetbrains/en/getting-started.md)
|
|
259
260
|
- [Vim Setup](./docs/vim/en/getting-started.md)
|
|
260
261
|
- [Neovim Setup](./docs/neovim/en/getting-started.md)
|
|
261
262
|
|
data/lib/t_ruby/benchmark.rb
CHANGED
|
@@ -60,11 +60,11 @@ module TRuby
|
|
|
60
60
|
# Export results to JSON
|
|
61
61
|
def export_json(path = "benchmark_results.json")
|
|
62
62
|
File.write(path, JSON.pretty_generate({
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
timestamp: Time.now.iso8601,
|
|
64
|
+
ruby_version: RUBY_VERSION,
|
|
65
|
+
platform: RUBY_PLATFORM,
|
|
66
|
+
results: @results,
|
|
67
|
+
}))
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
# Export results to Markdown
|
|
@@ -86,7 +86,7 @@ module TRuby
|
|
|
86
86
|
benchmarks.each do |name, data|
|
|
87
87
|
time_ms = (data[:avg_time] * 1000).round(2)
|
|
88
88
|
memory_kb = (data[:memory] || 0).round(2)
|
|
89
|
-
ips = data[:avg_time]
|
|
89
|
+
ips = data[:avg_time].positive? ? (1.0 / data[:avg_time]).round(2) : 0
|
|
90
90
|
md << "| #{name} | #{time_ms} | #{memory_kb} | #{ips} |"
|
|
91
91
|
end
|
|
92
92
|
md << ""
|
|
@@ -116,7 +116,7 @@ module TRuby
|
|
|
116
116
|
current: data[:avg_time],
|
|
117
117
|
previous: prev_data[:avg_time],
|
|
118
118
|
diff_percent: diff,
|
|
119
|
-
improved: diff
|
|
119
|
+
improved: diff.negative?,
|
|
120
120
|
}
|
|
121
121
|
end
|
|
122
122
|
end
|
|
@@ -255,7 +255,7 @@ module TRuby
|
|
|
255
255
|
print_result(:incremental_single, results[:incremental_single])
|
|
256
256
|
|
|
257
257
|
# Calculate speedup
|
|
258
|
-
if results[:full_compile][:avg_time]
|
|
258
|
+
if results[:full_compile][:avg_time].positive?
|
|
259
259
|
speedup = results[:full_compile][:avg_time] / results[:incremental_single][:avg_time]
|
|
260
260
|
puts " Incremental speedup: #{speedup.round(2)}x"
|
|
261
261
|
end
|
|
@@ -317,7 +317,7 @@ module TRuby
|
|
|
317
317
|
print_result(:parallel_4, results[:parallel_4])
|
|
318
318
|
|
|
319
319
|
# Print speedups
|
|
320
|
-
if results[:sequential][:avg_time]
|
|
320
|
+
if results[:sequential][:avg_time].positive?
|
|
321
321
|
puts " Parallel(2) speedup: #{(results[:sequential][:avg_time] / results[:parallel_2][:avg_time]).round(2)}x"
|
|
322
322
|
puts " Parallel(4) speedup: #{(results[:sequential][:avg_time] / results[:parallel_4][:avg_time]).round(2)}x"
|
|
323
323
|
end
|
|
@@ -332,7 +332,7 @@ module TRuby
|
|
|
332
332
|
|
|
333
333
|
# Baseline memory
|
|
334
334
|
GC.start
|
|
335
|
-
|
|
335
|
+
get_memory_usage
|
|
336
336
|
|
|
337
337
|
# Parser memory
|
|
338
338
|
content = generate_test_content(0)
|
|
@@ -374,20 +374,20 @@ module TRuby
|
|
|
374
374
|
small_file: generate_test_content(0, lines: 10),
|
|
375
375
|
medium_file: generate_test_content(0, lines: 100),
|
|
376
376
|
large_file: generate_test_content(0, lines: 500),
|
|
377
|
-
complex_types: generate_complex_types_content
|
|
377
|
+
complex_types: generate_complex_types_content,
|
|
378
378
|
}
|
|
379
379
|
when :type_checking
|
|
380
380
|
{
|
|
381
381
|
simple_types: generate_simple_types_content,
|
|
382
382
|
generic_types: generate_generic_types_content,
|
|
383
383
|
union_types: generate_union_types_content,
|
|
384
|
-
interface_types: generate_interface_types_content
|
|
384
|
+
interface_types: generate_interface_types_content,
|
|
385
385
|
}
|
|
386
386
|
when :compilation
|
|
387
387
|
{
|
|
388
388
|
minimal: "def hello: void; end",
|
|
389
389
|
with_types: generate_test_content(0, lines: 50),
|
|
390
|
-
with_interfaces: generate_interface_types_content
|
|
390
|
+
with_interfaces: generate_interface_types_content,
|
|
391
391
|
}
|
|
392
392
|
else
|
|
393
393
|
{}
|
|
@@ -396,7 +396,7 @@ module TRuby
|
|
|
396
396
|
|
|
397
397
|
def generate_test_content(seed, lines: 50, modified: false)
|
|
398
398
|
content = []
|
|
399
|
-
content << "# Test file #{seed}#{
|
|
399
|
+
content << "# Test file #{seed}#{" (modified)" if modified}"
|
|
400
400
|
content << ""
|
|
401
401
|
content << "type CustomType#{seed} = String | Integer | nil"
|
|
402
402
|
content << ""
|
|
@@ -522,7 +522,7 @@ module TRuby
|
|
|
522
522
|
min_time: min,
|
|
523
523
|
max_time: max,
|
|
524
524
|
std_dev: std_dev,
|
|
525
|
-
iterations: times.length
|
|
525
|
+
iterations: times.length,
|
|
526
526
|
}
|
|
527
527
|
end
|
|
528
528
|
|
|
@@ -571,7 +571,7 @@ module TRuby
|
|
|
571
571
|
iterations.times do
|
|
572
572
|
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
573
573
|
yield
|
|
574
|
-
times << Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
|
|
574
|
+
times << (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start)
|
|
575
575
|
end
|
|
576
576
|
|
|
577
577
|
avg = times.sum / times.length
|
|
@@ -42,7 +42,7 @@ module TRuby
|
|
|
42
42
|
installed_gems = parse_gemfile_lock
|
|
43
43
|
type_packages = {}
|
|
44
44
|
|
|
45
|
-
installed_gems.
|
|
45
|
+
installed_gems.each_key do |gem_name|
|
|
46
46
|
type_gem = find_type_gem(gem_name)
|
|
47
47
|
type_packages[gem_name] = type_gem if type_gem
|
|
48
48
|
end
|
|
@@ -93,7 +93,7 @@ module TRuby
|
|
|
93
93
|
types_group: TYPES_GROUP.to_s,
|
|
94
94
|
type_gems: list_type_gems,
|
|
95
95
|
local_types: list_local_types,
|
|
96
|
-
generated_at: Time.now.iso8601
|
|
96
|
+
generated_at: Time.now.iso8601,
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
manifest_path = File.join(@project_dir, ".trb-bundle.json")
|
|
@@ -128,15 +128,15 @@ module TRuby
|
|
|
128
128
|
|
|
129
129
|
next unless base_version && type_version
|
|
130
130
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
|
|
131
|
+
next if versions_compatible?(base_version, type_version)
|
|
132
|
+
|
|
133
|
+
issues << {
|
|
134
|
+
gem: base_gem,
|
|
135
|
+
gem_version: base_version,
|
|
136
|
+
type_gem: type_info[:name],
|
|
137
|
+
type_version: type_version,
|
|
138
|
+
message: "Version mismatch: #{base_gem}@#{base_version} vs #{type_info[:name]}@#{type_version}",
|
|
139
|
+
}
|
|
140
140
|
end
|
|
141
141
|
|
|
142
142
|
issues
|
|
@@ -201,21 +201,21 @@ module TRuby
|
|
|
201
201
|
|
|
202
202
|
# Create a sample .d.trb file
|
|
203
203
|
sample_path = File.join(types_dir, "custom.d.trb")
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
204
|
+
return if File.exist?(sample_path)
|
|
205
|
+
|
|
206
|
+
File.write(sample_path, <<~TRB)
|
|
207
|
+
# Custom type definitions for your project
|
|
208
|
+
# These types are available throughout your T-Ruby code
|
|
209
|
+
|
|
210
|
+
# Example type alias
|
|
211
|
+
# type UserId = String
|
|
212
|
+
|
|
213
|
+
# Example interface
|
|
214
|
+
# interface Serializable
|
|
215
|
+
# to_json: String
|
|
216
|
+
# from_json: (String) -> self
|
|
217
|
+
# end
|
|
218
|
+
TRB
|
|
219
219
|
end
|
|
220
220
|
|
|
221
221
|
def append_to_gemfile(gem_name, version, group:)
|
|
@@ -276,7 +276,7 @@ module TRuby
|
|
|
276
276
|
# This is a simplified check - in production would query RubyGems API
|
|
277
277
|
{
|
|
278
278
|
name: type_gem_name,
|
|
279
|
-
available: check_gem_availability(type_gem_name)
|
|
279
|
+
available: check_gem_availability(type_gem_name),
|
|
280
280
|
}
|
|
281
281
|
end
|
|
282
282
|
|
|
@@ -305,7 +305,7 @@ module TRuby
|
|
|
305
305
|
name: name,
|
|
306
306
|
base_gem: base_gem,
|
|
307
307
|
version: version,
|
|
308
|
-
path: find_gem_path(name, version)
|
|
308
|
+
path: find_gem_path(name, version),
|
|
309
309
|
}
|
|
310
310
|
end
|
|
311
311
|
end
|
|
@@ -315,7 +315,7 @@ module TRuby
|
|
|
315
315
|
possible_paths = [
|
|
316
316
|
File.join(ENV["GEM_HOME"] || "", "gems", "#{gem_name}-#{version}"),
|
|
317
317
|
File.join(Dir.home, ".gem", "ruby", "*", "gems", "#{gem_name}-#{version}"),
|
|
318
|
-
File.join(@project_dir, "vendor", "bundle", "**", "gems", "#{gem_name}-#{version}")
|
|
318
|
+
File.join(@project_dir, "vendor", "bundle", "**", "gems", "#{gem_name}-#{version}"),
|
|
319
319
|
]
|
|
320
320
|
|
|
321
321
|
possible_paths.each do |pattern|
|
|
@@ -398,7 +398,7 @@ module TRuby
|
|
|
398
398
|
{
|
|
399
399
|
name: gem_info[:name],
|
|
400
400
|
base_gem: gem_info[:base_gem],
|
|
401
|
-
version: gem_info[:version]
|
|
401
|
+
version: gem_info[:version],
|
|
402
402
|
}
|
|
403
403
|
end
|
|
404
404
|
end
|
|
@@ -553,11 +553,9 @@ module TRuby
|
|
|
553
553
|
migrated = []
|
|
554
554
|
|
|
555
555
|
# Read existing T-Ruby manifest
|
|
556
|
-
|
|
557
|
-
@
|
|
558
|
-
|
|
559
|
-
migrated << result
|
|
560
|
-
end
|
|
556
|
+
@manifest&.dependencies&.each do |name, version|
|
|
557
|
+
result = @bundler.add_type_gem(name, version: version)
|
|
558
|
+
migrated << result
|
|
561
559
|
end
|
|
562
560
|
|
|
563
561
|
# Generate new bundle manifest
|
data/lib/t_ruby/cache.rb
CHANGED
|
@@ -32,7 +32,7 @@ module TRuby
|
|
|
32
32
|
key: @key,
|
|
33
33
|
value: @value,
|
|
34
34
|
created_at: @created_at.to_i,
|
|
35
|
-
hits: @hits
|
|
35
|
+
hits: @hits,
|
|
36
36
|
}
|
|
37
37
|
end
|
|
38
38
|
end
|
|
@@ -96,6 +96,7 @@ module TRuby
|
|
|
96
96
|
def hit_rate
|
|
97
97
|
total = @hits + @misses
|
|
98
98
|
return 0.0 if total.zero?
|
|
99
|
+
|
|
99
100
|
@hits.to_f / total
|
|
100
101
|
end
|
|
101
102
|
|
|
@@ -105,7 +106,7 @@ module TRuby
|
|
|
105
106
|
max_size: @max_size,
|
|
106
107
|
hits: @hits,
|
|
107
108
|
misses: @misses,
|
|
108
|
-
hit_rate: hit_rate
|
|
109
|
+
hit_rate: hit_rate,
|
|
109
110
|
}
|
|
110
111
|
end
|
|
111
112
|
|
|
@@ -160,7 +161,7 @@ module TRuby
|
|
|
160
161
|
|
|
161
162
|
def delete(key)
|
|
162
163
|
path = cache_path(key)
|
|
163
|
-
|
|
164
|
+
FileUtils.rm_f(path)
|
|
164
165
|
end
|
|
165
166
|
|
|
166
167
|
def clear
|
|
@@ -260,7 +261,7 @@ module TRuby
|
|
|
260
261
|
# Declaration file cache
|
|
261
262
|
class DeclarationCache
|
|
262
263
|
def initialize(cache_dir: ".t-ruby-cache/declarations")
|
|
263
|
-
@file_cache = FileCache.new(cache_dir: cache_dir, max_age:
|
|
264
|
+
@file_cache = FileCache.new(cache_dir: cache_dir, max_age: 86_400) # 24 hours
|
|
264
265
|
@memory_cache = MemoryCache.new(max_size: 200)
|
|
265
266
|
end
|
|
266
267
|
|
|
@@ -368,6 +369,7 @@ module TRuby
|
|
|
368
369
|
|
|
369
370
|
def file_hash(file_path)
|
|
370
371
|
return nil unless File.exist?(file_path)
|
|
372
|
+
|
|
371
373
|
Digest::SHA256.hexdigest(File.read(file_path))
|
|
372
374
|
end
|
|
373
375
|
end
|
|
@@ -412,7 +414,11 @@ module TRuby
|
|
|
412
414
|
threads = @thread_count.times.map do
|
|
413
415
|
Thread.new do
|
|
414
416
|
loop do
|
|
415
|
-
file =
|
|
417
|
+
file = begin
|
|
418
|
+
queue.pop(true)
|
|
419
|
+
rescue StandardError
|
|
420
|
+
break
|
|
421
|
+
end
|
|
416
422
|
result = block.call(file)
|
|
417
423
|
mutex.synchronize { results << result }
|
|
418
424
|
end
|
|
@@ -428,7 +434,7 @@ module TRuby
|
|
|
428
434
|
def determine_thread_count
|
|
429
435
|
# Use number of CPU cores, max 8
|
|
430
436
|
[Etc.nprocessors, 8].min
|
|
431
|
-
rescue
|
|
437
|
+
rescue StandardError
|
|
432
438
|
4
|
|
433
439
|
end
|
|
434
440
|
|
|
@@ -443,8 +449,8 @@ module TRuby
|
|
|
443
449
|
|
|
444
450
|
def initialize(type_checker: nil)
|
|
445
451
|
@type_checker = type_checker || TypeChecker.new
|
|
446
|
-
@file_types = {}
|
|
447
|
-
@global_registry = {}
|
|
452
|
+
@file_types = {} # file_path => { types: [], functions: [], interfaces: [] }
|
|
453
|
+
@global_registry = {} # name => { file: path, kind: :type/:func/:interface, definition: ... }
|
|
448
454
|
@errors = []
|
|
449
455
|
@warnings = []
|
|
450
456
|
end
|
|
@@ -489,7 +495,7 @@ module TRuby
|
|
|
489
495
|
{
|
|
490
496
|
success: @errors.empty?,
|
|
491
497
|
errors: @errors,
|
|
492
|
-
warnings: @warnings
|
|
498
|
+
warnings: @warnings,
|
|
493
499
|
}
|
|
494
500
|
end
|
|
495
501
|
|
|
@@ -502,24 +508,20 @@ module TRuby
|
|
|
502
508
|
when IR::MethodDef
|
|
503
509
|
# Check parameter types
|
|
504
510
|
decl.params.each do |param|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
end
|
|
512
|
-
end
|
|
511
|
+
next unless param.type_annotation && !type_exists?(param.type_annotation)
|
|
512
|
+
|
|
513
|
+
file_errors << {
|
|
514
|
+
file: file_path,
|
|
515
|
+
message: "Unknown type '#{type_name(param.type_annotation)}' in parameter '#{param.name}'",
|
|
516
|
+
}
|
|
513
517
|
end
|
|
514
518
|
|
|
515
519
|
# Check return type
|
|
516
|
-
if decl.return_type
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
}
|
|
522
|
-
end
|
|
520
|
+
if decl.return_type && !type_exists?(decl.return_type)
|
|
521
|
+
file_errors << {
|
|
522
|
+
file: file_path,
|
|
523
|
+
message: "Unknown return type '#{type_name(decl.return_type)}' in function '#{decl.name}'",
|
|
524
|
+
}
|
|
523
525
|
end
|
|
524
526
|
end
|
|
525
527
|
end
|
|
@@ -552,7 +554,7 @@ module TRuby
|
|
|
552
554
|
# Duplicate definition from different file
|
|
553
555
|
@warnings << {
|
|
554
556
|
message: "#{kind.to_s.capitalize} '#{name}' defined in multiple files",
|
|
555
|
-
files: [@global_registry[name][:file], file_path]
|
|
557
|
+
files: [@global_registry[name][:file], file_path],
|
|
556
558
|
}
|
|
557
559
|
end
|
|
558
560
|
|
|
@@ -568,7 +570,7 @@ module TRuby
|
|
|
568
570
|
duplicates.each do |name|
|
|
569
571
|
@errors << {
|
|
570
572
|
file: file,
|
|
571
|
-
message: "Duplicate definition of '#{name}'"
|
|
573
|
+
message: "Duplicate definition of '#{name}'",
|
|
572
574
|
}
|
|
573
575
|
end
|
|
574
576
|
end
|
|
@@ -580,12 +582,12 @@ module TRuby
|
|
|
580
582
|
info[:types].each do |type_info|
|
|
581
583
|
referenced_types = extract_type_references(type_info[:definition])
|
|
582
584
|
referenced_types.each do |ref|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
}
|
|
588
|
-
|
|
585
|
+
next if type_exists_by_name?(ref)
|
|
586
|
+
|
|
587
|
+
@errors << {
|
|
588
|
+
file: file_path,
|
|
589
|
+
message: "Unresolved type reference '#{ref}' in type alias '#{type_info[:name]}'",
|
|
590
|
+
}
|
|
589
591
|
end
|
|
590
592
|
end
|
|
591
593
|
end
|
|
@@ -608,13 +610,15 @@ module TRuby
|
|
|
608
610
|
when IR::NullableType
|
|
609
611
|
type_exists?(type_node.inner_type)
|
|
610
612
|
else
|
|
611
|
-
true
|
|
613
|
+
true # Assume valid for unknown types
|
|
612
614
|
end
|
|
613
615
|
end
|
|
614
616
|
|
|
615
617
|
def type_exists_by_name?(name)
|
|
616
|
-
return true if %w[String Integer Float Boolean Array Hash Symbol void nil Object Numeric
|
|
618
|
+
return true if %w[String Integer Float Boolean Array Hash Symbol void nil Object Numeric
|
|
619
|
+
Enumerable].include?(name)
|
|
617
620
|
return true if @global_registry[name]
|
|
621
|
+
|
|
618
622
|
false
|
|
619
623
|
end
|
|
620
624
|
|
|
@@ -655,7 +659,7 @@ module TRuby
|
|
|
655
659
|
|
|
656
660
|
def initialize(compiler, cache: nil, enable_cross_file: true)
|
|
657
661
|
super(compiler, cache: cache)
|
|
658
|
-
@ir_cache = {}
|
|
662
|
+
@ir_cache = {} # file_path => IR::Program
|
|
659
663
|
@cross_file_checker = CrossFileTypeChecker.new if enable_cross_file
|
|
660
664
|
end
|
|
661
665
|
|
|
@@ -685,11 +689,9 @@ module TRuby
|
|
|
685
689
|
|
|
686
690
|
# First pass: compile and register all files
|
|
687
691
|
file_paths.each do |file_path|
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
errors << { file: file_path, error: e.message }
|
|
692
|
-
end
|
|
692
|
+
results[file_path] = compile_with_ir(file_path)
|
|
693
|
+
rescue StandardError => e
|
|
694
|
+
errors << { file: file_path, error: e.message }
|
|
693
695
|
end
|
|
694
696
|
|
|
695
697
|
# Second pass: cross-file type checking
|
|
@@ -701,7 +703,7 @@ module TRuby
|
|
|
701
703
|
{
|
|
702
704
|
results: results,
|
|
703
705
|
errors: errors,
|
|
704
|
-
success: errors.empty
|
|
706
|
+
success: errors.empty?,
|
|
705
707
|
}
|
|
706
708
|
end
|
|
707
709
|
|
|
@@ -721,6 +723,7 @@ module TRuby
|
|
|
721
723
|
|
|
722
724
|
def file_hash(file_path)
|
|
723
725
|
return nil unless File.exist?(file_path)
|
|
726
|
+
|
|
724
727
|
Digest::SHA256.hexdigest(File.read(file_path))
|
|
725
728
|
end
|
|
726
729
|
end
|
|
@@ -751,7 +754,7 @@ module TRuby
|
|
|
751
754
|
@timings.sort_by { |_, v| -v }.each do |name, time|
|
|
752
755
|
calls = @call_counts[name]
|
|
753
756
|
avg = time / calls
|
|
754
|
-
puts "#{name}: #{format(
|
|
757
|
+
puts "#{name}: #{format("%.3f", time)}s total, #{calls} calls, #{format("%.3f", avg * 1000)}ms avg"
|
|
755
758
|
end
|
|
756
759
|
end
|
|
757
760
|
|
|
@@ -766,7 +769,7 @@ module TRuby
|
|
|
766
769
|
name: name,
|
|
767
770
|
total_time: time,
|
|
768
771
|
call_count: @call_counts[name],
|
|
769
|
-
avg_time: time / @call_counts[name]
|
|
772
|
+
avg_time: time / @call_counts[name],
|
|
770
773
|
}
|
|
771
774
|
end
|
|
772
775
|
end
|
data/lib/t_ruby/cli.rb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module TRuby
|
|
4
4
|
class CLI
|
|
5
|
-
HELP_TEXT = <<~HELP
|
|
5
|
+
HELP_TEXT = <<~HELP.freeze
|
|
6
6
|
t-ruby compiler (trc) v#{VERSION}
|
|
7
7
|
|
|
8
8
|
Usage:
|
|
@@ -145,10 +145,10 @@ module TRuby
|
|
|
145
145
|
|
|
146
146
|
# Output results
|
|
147
147
|
if created.any?
|
|
148
|
-
puts "Created: #{created.join(
|
|
148
|
+
puts "Created: #{created.join(", ")}"
|
|
149
149
|
end
|
|
150
150
|
if skipped.any?
|
|
151
|
-
puts "Skipped (already exists): #{skipped.join(
|
|
151
|
+
puts "Skipped (already exists): #{skipped.join(", ")}"
|
|
152
152
|
end
|
|
153
153
|
if created.empty? && skipped.any?
|
|
154
154
|
puts "Project already initialized."
|
data/lib/t_ruby/compiler.rb
CHANGED
|
@@ -42,7 +42,7 @@ module TRuby
|
|
|
42
42
|
FileUtils.mkdir_p(out_dir)
|
|
43
43
|
|
|
44
44
|
base_filename = File.basename(input_path, ".trb")
|
|
45
|
-
output_path = File.join(out_dir, base_filename
|
|
45
|
+
output_path = File.join(out_dir, "#{base_filename}.rb")
|
|
46
46
|
|
|
47
47
|
File.write(output_path, output)
|
|
48
48
|
|
|
@@ -98,19 +98,19 @@ module TRuby
|
|
|
98
98
|
{
|
|
99
99
|
ruby: ruby_output,
|
|
100
100
|
rbs: rbs_output,
|
|
101
|
-
errors: []
|
|
101
|
+
errors: [],
|
|
102
102
|
}
|
|
103
103
|
rescue ParseError => e
|
|
104
104
|
{
|
|
105
105
|
ruby: "",
|
|
106
106
|
rbs: "",
|
|
107
|
-
errors: [e.message]
|
|
107
|
+
errors: [e.message],
|
|
108
108
|
}
|
|
109
109
|
rescue StandardError => e
|
|
110
110
|
{
|
|
111
111
|
ruby: "",
|
|
112
112
|
rbs: "",
|
|
113
|
-
errors: ["Compilation error: #{e.message}"]
|
|
113
|
+
errors: ["Compilation error: #{e.message}"],
|
|
114
114
|
}
|
|
115
115
|
end
|
|
116
116
|
|
|
@@ -202,7 +202,7 @@ module TRuby
|
|
|
202
202
|
generator = IR::RBSGenerator.new
|
|
203
203
|
rbs_content = generator.generate(ir_program)
|
|
204
204
|
|
|
205
|
-
rbs_path = File.join(out_dir, base_filename
|
|
205
|
+
rbs_path = File.join(out_dir, "#{base_filename}.rbs")
|
|
206
206
|
File.write(rbs_path, rbs_content) unless rbs_content.strip.empty?
|
|
207
207
|
end
|
|
208
208
|
|
|
@@ -214,7 +214,7 @@ module TRuby
|
|
|
214
214
|
parse_result[:type_aliases] || []
|
|
215
215
|
)
|
|
216
216
|
|
|
217
|
-
rbs_path = File.join(out_dir, base_filename
|
|
217
|
+
rbs_path = File.join(out_dir, "#{base_filename}.rbs")
|
|
218
218
|
File.write(rbs_path, rbs_content) unless rbs_content.empty?
|
|
219
219
|
end
|
|
220
220
|
|
|
@@ -233,7 +233,7 @@ module TRuby
|
|
|
233
233
|
FileUtils.mkdir_p(out_dir)
|
|
234
234
|
|
|
235
235
|
base_filename = File.basename(input_path, ".rb")
|
|
236
|
-
output_path = File.join(out_dir, base_filename
|
|
236
|
+
output_path = File.join(out_dir, "#{base_filename}.rb")
|
|
237
237
|
|
|
238
238
|
# Copy the .rb file to output directory
|
|
239
239
|
FileUtils.cp(input_path, output_path)
|
|
@@ -250,7 +250,7 @@ module TRuby
|
|
|
250
250
|
|
|
251
251
|
# Generate RBS from Ruby file using rbs prototype
|
|
252
252
|
def generate_rbs_from_ruby(base_filename, out_dir, input_path)
|
|
253
|
-
rbs_path = File.join(out_dir, base_filename
|
|
253
|
+
rbs_path = File.join(out_dir, "#{base_filename}.rbs")
|
|
254
254
|
result = `rbs prototype rb #{input_path} 2>/dev/null`
|
|
255
255
|
File.write(rbs_path, result) unless result.strip.empty?
|
|
256
256
|
end
|
|
@@ -273,20 +273,20 @@ module TRuby
|
|
|
273
273
|
result = source.dup
|
|
274
274
|
|
|
275
275
|
# Collect type alias names to remove
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
276
|
+
program.declarations
|
|
277
|
+
.select { |d| d.is_a?(IR::TypeAlias) }
|
|
278
|
+
.map(&:name)
|
|
279
279
|
|
|
280
280
|
# Collect interface names to remove
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
281
|
+
program.declarations
|
|
282
|
+
.select { |d| d.is_a?(IR::Interface) }
|
|
283
|
+
.map(&:name)
|
|
284
284
|
|
|
285
285
|
# Remove type alias definitions
|
|
286
|
-
result = result.gsub(/^\s*type\s+\w+\s*=\s*.+?$\n?/,
|
|
286
|
+
result = result.gsub(/^\s*type\s+\w+\s*=\s*.+?$\n?/, "")
|
|
287
287
|
|
|
288
288
|
# Remove interface definitions (multi-line)
|
|
289
|
-
result = result.gsub(/^\s*interface\s+\w+.*?^\s*end\s*$/m,
|
|
289
|
+
result = result.gsub(/^\s*interface\s+\w+.*?^\s*end\s*$/m, "")
|
|
290
290
|
|
|
291
291
|
# Remove parameter type annotations using IR info
|
|
292
292
|
# Enhanced: Handle complex types (generics, unions, etc.)
|
|
@@ -296,9 +296,7 @@ module TRuby
|
|
|
296
296
|
result = erase_return_types(result)
|
|
297
297
|
|
|
298
298
|
# Clean up extra blank lines
|
|
299
|
-
result
|
|
300
|
-
|
|
301
|
-
result
|
|
299
|
+
result.gsub(/\n{3,}/, "\n\n")
|
|
302
300
|
end
|
|
303
301
|
|
|
304
302
|
private
|
|
@@ -308,11 +306,11 @@ module TRuby
|
|
|
308
306
|
result = source.dup
|
|
309
307
|
|
|
310
308
|
# Match function definitions and remove type annotations from parameters
|
|
311
|
-
result.gsub!(/^(\s*def\s+\w+\s*\()([^)]+)(\)\s*)(?::\s*[^\n]+)?(\s*$)/) do |
|
|
312
|
-
indent =
|
|
313
|
-
params =
|
|
314
|
-
close_paren =
|
|
315
|
-
ending =
|
|
309
|
+
result.gsub!(/^(\s*def\s+\w+\s*\()([^)]+)(\)\s*)(?::\s*[^\n]+)?(\s*$)/) do |_match|
|
|
310
|
+
indent = ::Regexp.last_match(1)
|
|
311
|
+
params = ::Regexp.last_match(2)
|
|
312
|
+
close_paren = ::Regexp.last_match(3)
|
|
313
|
+
ending = ::Regexp.last_match(4)
|
|
316
314
|
|
|
317
315
|
# Remove type annotations from each parameter
|
|
318
316
|
cleaned_params = remove_param_types(params)
|
|
@@ -340,7 +338,7 @@ module TRuby
|
|
|
340
338
|
depth -= 1
|
|
341
339
|
current += char
|
|
342
340
|
when ","
|
|
343
|
-
if depth
|
|
341
|
+
if depth.zero?
|
|
344
342
|
params << clean_param(current.strip)
|
|
345
343
|
current = ""
|
|
346
344
|
else
|
|
@@ -358,7 +356,7 @@ module TRuby
|
|
|
358
356
|
# Clean a single parameter (remove type annotation)
|
|
359
357
|
def clean_param(param)
|
|
360
358
|
# Match: name: Type or name
|
|
361
|
-
if match = param.match(/^(\w+)\s*:/)
|
|
359
|
+
if (match = param.match(/^(\w+)\s*:/))
|
|
362
360
|
match[1]
|
|
363
361
|
else
|
|
364
362
|
param
|
|
@@ -370,7 +368,7 @@ module TRuby
|
|
|
370
368
|
result = source.dup
|
|
371
369
|
|
|
372
370
|
# Remove return type: ): Type or ): Type<Foo> etc.
|
|
373
|
-
result.gsub!(/\)\s*:\s*[^\n]+?(?=\s*$)/m) do |
|
|
371
|
+
result.gsub!(/\)\s*:\s*[^\n]+?(?=\s*$)/m) do |_match|
|
|
374
372
|
")"
|
|
375
373
|
end
|
|
376
374
|
|