t-ruby 0.0.35 → 0.0.37
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 +7 -6
- 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 +29 -5
- data/lib/t_ruby/compiler.rb +106 -52
- data/lib/t_ruby/config.rb +15 -18
- data/lib/t_ruby/constraint_checker.rb +14 -8
- data/lib/t_ruby/declaration_generator.rb +25 -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/version_checker.rb +50 -0
- data/lib/t_ruby/watcher.rb +18 -14
- data/lib/t_ruby.rb +1 -0
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: af10457f1fd4a0f0f3290c1cfead779eeca70c90cf81fa18320532e8c079a529
|
|
4
|
+
data.tar.gz: 15f674d20a11d3bb305198822fdd398447c9a7915377c2000eb480b4ab92318c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c0f281354fe79043c23ce37760c8a177bcab6de92d135d499ae3757b27e0944daed045fc3ba3334b32ab82a0ea16e028b0076afb0d8964238b042f644f5a1e5c
|
|
7
|
+
data.tar.gz: 5f91cac0ffedca7444fb68e4974435a6c01157ec0373b7ebe430e240643802e946c02fb7d91da147e823db17982b342be8e5ca03b74c1ca8263312227da82d78
|
data/README.md
CHANGED
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<img src="https://img.shields.io/
|
|
12
|
+
<a href="https://github.com/type-ruby/t-ruby/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/type-ruby/t-ruby/ci.yml?label=CI" alt="CI" /></a>
|
|
13
13
|
<img src="https://img.shields.io/badge/ruby-3.0+-cc342d" alt="Ruby 3.0+" />
|
|
14
14
|
<a href="https://rubygems.org/gems/t-ruby"><img src="https://img.shields.io/gem/v/t-ruby" alt="Gem Version" /></a>
|
|
15
15
|
<img src="https://img.shields.io/gem/dt/t-ruby" alt="Downloads" />
|
|
16
|
-
<img src="https://
|
|
16
|
+
<a href="https://coveralls.io/github/type-ruby/t-ruby?branch=main"><img src="https://coveralls.io/repos/github/type-ruby/t-ruby/badge.svg?branch=main" alt="Coverage" /></a>
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
19
|
<p align="center">
|
|
@@ -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:
|
|
@@ -13,7 +13,8 @@ module TRuby
|
|
|
13
13
|
trc --watch, -w Watch input files and recompile on change
|
|
14
14
|
trc --decl <file.trb> Generate .d.trb declaration file
|
|
15
15
|
trc --lsp Start LSP server (for IDE integration)
|
|
16
|
-
trc
|
|
16
|
+
trc update Update t-ruby to the latest version
|
|
17
|
+
trc --version, -v Show version (and check for updates)
|
|
17
18
|
trc --help, -h Show this help
|
|
18
19
|
|
|
19
20
|
Examples:
|
|
@@ -44,6 +45,12 @@ module TRuby
|
|
|
44
45
|
|
|
45
46
|
if @args.include?("--version") || @args.include?("-v")
|
|
46
47
|
puts "trc #{VERSION}"
|
|
48
|
+
check_for_updates
|
|
49
|
+
return
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
if @args.include?("update")
|
|
53
|
+
update_gem
|
|
47
54
|
return
|
|
48
55
|
end
|
|
49
56
|
|
|
@@ -78,6 +85,24 @@ module TRuby
|
|
|
78
85
|
|
|
79
86
|
private
|
|
80
87
|
|
|
88
|
+
def check_for_updates
|
|
89
|
+
result = VersionChecker.check
|
|
90
|
+
return unless result
|
|
91
|
+
|
|
92
|
+
puts ""
|
|
93
|
+
puts "New version available: #{result[:latest]} (current: #{result[:current]})"
|
|
94
|
+
puts "Run 'trc update' to update"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def update_gem
|
|
98
|
+
puts "Updating t-ruby..."
|
|
99
|
+
if VersionChecker.update
|
|
100
|
+
puts "Successfully updated t-ruby!"
|
|
101
|
+
else
|
|
102
|
+
puts "Update failed. Try: gem install t-ruby"
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
81
106
|
def init_project
|
|
82
107
|
config_file = "trbconfig.yml"
|
|
83
108
|
src_dir = "src"
|
|
@@ -105,7 +130,6 @@ module TRuby
|
|
|
105
130
|
output:
|
|
106
131
|
ruby_dir: #{build_dir}
|
|
107
132
|
# rbs_dir: sig # Optional: separate directory for .rbs files
|
|
108
|
-
preserve_structure: true
|
|
109
133
|
# clean_before_build: false
|
|
110
134
|
|
|
111
135
|
compiler:
|
|
@@ -145,10 +169,10 @@ module TRuby
|
|
|
145
169
|
|
|
146
170
|
# Output results
|
|
147
171
|
if created.any?
|
|
148
|
-
puts "Created: #{created.join(
|
|
172
|
+
puts "Created: #{created.join(", ")}"
|
|
149
173
|
end
|
|
150
174
|
if skipped.any?
|
|
151
|
-
puts "Skipped (already exists): #{skipped.join(
|
|
175
|
+
puts "Skipped (already exists): #{skipped.join(", ")}"
|
|
152
176
|
end
|
|
153
177
|
if created.empty? && skipped.any?
|
|
154
178
|
puts "Project already initialized."
|