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,320 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "listen"
4
+
5
+ module TRuby
6
+ class Watcher
7
+ # ANSI color codes (similar to tsc output style)
8
+ COLORS = {
9
+ reset: "\e[0m",
10
+ bold: "\e[1m",
11
+ dim: "\e[2m",
12
+ red: "\e[31m",
13
+ green: "\e[32m",
14
+ yellow: "\e[33m",
15
+ blue: "\e[34m",
16
+ cyan: "\e[36m",
17
+ gray: "\e[90m"
18
+ }.freeze
19
+
20
+ attr_reader :incremental_compiler, :stats
21
+
22
+ def initialize(paths: ["."], config: nil, incremental: true, cross_file_check: true, parallel: true)
23
+ @paths = paths.map { |p| File.expand_path(p) }
24
+ @config = config || Config.new
25
+ @compiler = Compiler.new(@config)
26
+ @error_count = 0
27
+ @file_count = 0
28
+ @use_colors = $stdout.tty?
29
+
30
+ # Enhanced features
31
+ @incremental = incremental
32
+ @cross_file_check = cross_file_check
33
+ @parallel = parallel
34
+
35
+ # Initialize incremental compiler
36
+ if @incremental
37
+ @incremental_compiler = EnhancedIncrementalCompiler.new(
38
+ @compiler,
39
+ enable_cross_file: @cross_file_check
40
+ )
41
+ end
42
+
43
+ # Parallel processor
44
+ @parallel_processor = ParallelProcessor.new if @parallel
45
+
46
+ # Statistics
47
+ @stats = {
48
+ total_compilations: 0,
49
+ incremental_hits: 0,
50
+ total_time: 0.0
51
+ }
52
+ end
53
+
54
+ def watch
55
+ print_start_message
56
+
57
+ # Initial compilation
58
+ start_time = Time.now
59
+ compile_all
60
+ @stats[:total_time] += Time.now - start_time
61
+
62
+ # Start watching
63
+ listener = Listen.to(*watch_directories, only: /\.trb$/) do |modified, added, removed|
64
+ handle_changes(modified, added, removed)
65
+ end
66
+
67
+ listener.start
68
+
69
+ print_watching_message
70
+
71
+ # Keep the process running
72
+ begin
73
+ sleep
74
+ rescue Interrupt
75
+ puts "\n#{colorize(:dim, timestamp)} #{colorize(:cyan, "Stopping watch mode...")}"
76
+ print_stats if @incremental
77
+ listener.stop
78
+ end
79
+ end
80
+
81
+ private
82
+
83
+ def watch_directories
84
+ @paths.map do |path|
85
+ if File.directory?(path)
86
+ path
87
+ else
88
+ File.dirname(path)
89
+ end
90
+ end.uniq
91
+ end
92
+
93
+ def handle_changes(modified, added, removed)
94
+ changed_files = (modified + added).select { |f| f.end_with?(".trb") }
95
+ return if changed_files.empty? && removed.empty?
96
+
97
+ puts
98
+ print_file_change_message
99
+
100
+ if removed.any?
101
+ removed.each do |file|
102
+ puts "#{colorize(:gray, timestamp)} #{colorize(:yellow, "File removed:")} #{relative_path(file)}"
103
+ # Clear from incremental compiler cache
104
+ @incremental_compiler&.file_hashes&.delete(file)
105
+ end
106
+ end
107
+
108
+ if changed_files.any?
109
+ start_time = Time.now
110
+ compile_files_incremental(changed_files)
111
+ @stats[:total_time] += Time.now - start_time
112
+ else
113
+ print_watching_message
114
+ end
115
+ end
116
+
117
+ def compile_all
118
+ @error_count = 0
119
+ @file_count = 0
120
+ errors = []
121
+
122
+ trb_files = find_trb_files
123
+ @file_count = trb_files.size
124
+
125
+ if @incremental && @cross_file_check
126
+ # Use enhanced incremental compiler with cross-file checking
127
+ result = @incremental_compiler.compile_all_with_checking(trb_files)
128
+ errors = result[:errors].map { |e| format_error(e[:file], e[:error] || e[:message]) }
129
+ @error_count = errors.size
130
+ @stats[:total_compilations] += trb_files.size
131
+ elsif @parallel && trb_files.size > 1
132
+ # Parallel compilation
133
+ results = @parallel_processor.process_files(trb_files) do |file|
134
+ compile_file(file)
135
+ end
136
+ results.each do |result|
137
+ errors.concat(result[:errors]) if result[:errors]&.any?
138
+ end
139
+ else
140
+ # Sequential compilation
141
+ trb_files.each do |file|
142
+ result = compile_file(file)
143
+ errors.concat(result[:errors]) if result[:errors].any?
144
+ end
145
+ end
146
+
147
+ print_errors(errors)
148
+ print_summary
149
+ end
150
+
151
+ def compile_files_incremental(files)
152
+ @error_count = 0
153
+ errors = []
154
+ compiled_count = 0
155
+
156
+ if @incremental
157
+ files.each do |file|
158
+ if @incremental_compiler.needs_compile?(file)
159
+ @stats[:total_compilations] += 1
160
+ result = compile_file_with_ir(file)
161
+ errors.concat(result[:errors]) if result[:errors].any?
162
+ compiled_count += 1
163
+ else
164
+ @stats[:incremental_hits] += 1
165
+ puts "#{colorize(:gray, timestamp)} #{colorize(:dim, "Skipping unchanged:")} #{relative_path(file)}"
166
+ end
167
+ end
168
+
169
+ # Run cross-file check if enabled
170
+ if @cross_file_check && @incremental_compiler.cross_file_checker
171
+ check_result = @incremental_compiler.cross_file_checker.check_all
172
+ check_result[:errors].each do |e|
173
+ errors << format_error(e[:file], e[:message])
174
+ end
175
+ end
176
+ else
177
+ files.each do |file|
178
+ result = compile_file(file)
179
+ errors.concat(result[:errors]) if result[:errors].any?
180
+ compiled_count += 1
181
+ end
182
+ end
183
+
184
+ @file_count = compiled_count
185
+ print_errors(errors)
186
+ print_summary
187
+ print_watching_message
188
+ end
189
+
190
+ def compile_file_with_ir(file)
191
+ result = { file: file, errors: [], success: false }
192
+
193
+ begin
194
+ @incremental_compiler.compile_with_ir(file)
195
+ result[:success] = true
196
+ rescue ArgumentError => e
197
+ @error_count += 1
198
+ result[:errors] << format_error(file, e.message)
199
+ rescue StandardError => e
200
+ @error_count += 1
201
+ result[:errors] << format_error(file, e.message)
202
+ end
203
+
204
+ result
205
+ end
206
+
207
+ def compile_file(file)
208
+ result = { file: file, errors: [], success: false }
209
+
210
+ begin
211
+ @compiler.compile(file)
212
+ result[:success] = true
213
+ @stats[:total_compilations] += 1
214
+ rescue ArgumentError => e
215
+ @error_count += 1
216
+ result[:errors] << format_error(file, e.message)
217
+ rescue StandardError => e
218
+ @error_count += 1
219
+ result[:errors] << format_error(file, e.message)
220
+ end
221
+
222
+ result
223
+ end
224
+
225
+ def find_trb_files
226
+ files = []
227
+ @paths.each do |path|
228
+ if File.directory?(path)
229
+ files.concat(Dir.glob(File.join(path, "**", "*.trb")))
230
+ elsif File.file?(path) && path.end_with?(".trb")
231
+ files << path
232
+ end
233
+ end
234
+ files.uniq
235
+ end
236
+
237
+ def format_error(file, message)
238
+ # Parse error message for line/column info if available
239
+ # Format: file:line:col - error TRB0001: message
240
+ line = 1
241
+ col = 1
242
+
243
+ # Try to extract line info from error message
244
+ if message =~ /line (\d+)/i
245
+ line = ::Regexp.last_match(1).to_i
246
+ end
247
+
248
+ {
249
+ file: file,
250
+ line: line,
251
+ col: col,
252
+ message: message
253
+ }
254
+ end
255
+
256
+ def print_errors(errors)
257
+ errors.each do |error|
258
+ puts
259
+ # TypeScript-style error format: file:line:col - error TSXXXX: message
260
+ location = "#{colorize(:cyan, relative_path(error[:file]))}:#{colorize(:yellow, error[:line])}:#{colorize(:yellow, error[:col])}"
261
+ puts "#{location} - #{colorize(:red, "error")} #{colorize(:gray, "TRB0001")}: #{error[:message]}"
262
+ end
263
+ end
264
+
265
+ def print_start_message
266
+ puts "#{colorize(:gray, timestamp)} #{colorize(:bold, "Starting compilation in watch mode...")}"
267
+ puts
268
+ end
269
+
270
+ def print_file_change_message
271
+ puts "#{colorize(:gray, timestamp)} #{colorize(:bold, "File change detected. Starting incremental compilation...")}"
272
+ puts
273
+ end
274
+
275
+ def print_summary
276
+ puts
277
+ if @error_count.zero?
278
+ msg = "Found #{colorize(:green, "0 errors")}. Watching for file changes."
279
+ puts "#{colorize(:gray, timestamp)} #{msg}"
280
+ else
281
+ error_word = @error_count == 1 ? "error" : "errors"
282
+ msg = "Found #{colorize(:red, "#{@error_count} #{error_word}")}. Watching for file changes."
283
+ puts "#{colorize(:gray, timestamp)} #{msg}"
284
+ end
285
+ end
286
+
287
+ def print_watching_message
288
+ # Just print a blank line for readability
289
+ end
290
+
291
+ def print_stats
292
+ puts
293
+ puts "#{colorize(:gray, timestamp)} #{colorize(:bold, "Watch Mode Statistics:")}"
294
+ puts " Total compilations: #{@stats[:total_compilations]}"
295
+ puts " Incremental cache hits: #{@stats[:incremental_hits]}"
296
+ hit_rate = if @stats[:total_compilations] + @stats[:incremental_hits] > 0
297
+ (@stats[:incremental_hits].to_f / (@stats[:total_compilations] + @stats[:incremental_hits]) * 100).round(1)
298
+ else
299
+ 0
300
+ end
301
+ puts " Cache hit rate: #{hit_rate}%"
302
+ puts " Total compile time: #{@stats[:total_time].round(2)}s"
303
+ end
304
+
305
+ def timestamp
306
+ Time.now.strftime("[%I:%M:%S %p]")
307
+ end
308
+
309
+ def relative_path(file)
310
+ file.sub("#{Dir.pwd}/", "")
311
+ end
312
+
313
+ def colorize(color, text)
314
+ return text unless @use_colors
315
+ return text unless COLORS[color]
316
+
317
+ "#{COLORS[color]}#{text}#{COLORS[:reset]}"
318
+ end
319
+ end
320
+ end
data/lib/t_ruby.rb ADDED
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "t_ruby/version"
4
+ require_relative "t_ruby/config"
5
+
6
+ # Core infrastructure (must be loaded first)
7
+ require_relative "t_ruby/ir"
8
+ require_relative "t_ruby/parser_combinator"
9
+ require_relative "t_ruby/smt_solver"
10
+
11
+ # Basic components
12
+ require_relative "t_ruby/type_alias_registry"
13
+ require_relative "t_ruby/parser"
14
+ require_relative "t_ruby/union_type_parser"
15
+ require_relative "t_ruby/generic_type_parser"
16
+ require_relative "t_ruby/intersection_type_parser"
17
+ require_relative "t_ruby/type_erasure"
18
+ require_relative "t_ruby/error_handler"
19
+ require_relative "t_ruby/rbs_generator"
20
+ require_relative "t_ruby/declaration_generator"
21
+ require_relative "t_ruby/compiler"
22
+ require_relative "t_ruby/lsp_server"
23
+ require_relative "t_ruby/watcher"
24
+ require_relative "t_ruby/cli"
25
+
26
+ # Milestone 4: Advanced Features
27
+ require_relative "t_ruby/constraint_checker"
28
+ require_relative "t_ruby/type_inferencer"
29
+ require_relative "t_ruby/runtime_validator"
30
+ require_relative "t_ruby/type_checker"
31
+ require_relative "t_ruby/cache"
32
+ require_relative "t_ruby/package_manager"
33
+
34
+ # Milestone 5: Bundler Integration
35
+ require_relative "t_ruby/bundler_integration"
36
+
37
+ # Milestone 6: Quality & Documentation
38
+ require_relative "t_ruby/benchmark"
39
+ require_relative "t_ruby/doc_generator"
40
+
41
+ module TRuby
42
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: t-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Y. Fred Kim
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: listen
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '3.8'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '3.8'
26
+ description: t-ruby compiles .trb files with type annotations to executable Ruby (.rb)
27
+ and optional type signature files (.rbs)
28
+ email:
29
+ - yhkks1038@gmail.com
30
+ executables:
31
+ - trc
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - LICENSE
36
+ - README.md
37
+ - bin/trc
38
+ - lib/t_ruby.rb
39
+ - lib/t_ruby/benchmark.rb
40
+ - lib/t_ruby/bundler_integration.rb
41
+ - lib/t_ruby/cache.rb
42
+ - lib/t_ruby/cli.rb
43
+ - lib/t_ruby/compiler.rb
44
+ - lib/t_ruby/config.rb
45
+ - lib/t_ruby/constraint_checker.rb
46
+ - lib/t_ruby/declaration_generator.rb
47
+ - lib/t_ruby/doc_generator.rb
48
+ - lib/t_ruby/error_handler.rb
49
+ - lib/t_ruby/generic_type_parser.rb
50
+ - lib/t_ruby/intersection_type_parser.rb
51
+ - lib/t_ruby/ir.rb
52
+ - lib/t_ruby/lsp_server.rb
53
+ - lib/t_ruby/package_manager.rb
54
+ - lib/t_ruby/parser.rb
55
+ - lib/t_ruby/parser_combinator.rb
56
+ - lib/t_ruby/rbs_generator.rb
57
+ - lib/t_ruby/runtime_validator.rb
58
+ - lib/t_ruby/smt_solver.rb
59
+ - lib/t_ruby/type_alias_registry.rb
60
+ - lib/t_ruby/type_checker.rb
61
+ - lib/t_ruby/type_erasure.rb
62
+ - lib/t_ruby/type_inferencer.rb
63
+ - lib/t_ruby/union_type_parser.rb
64
+ - lib/t_ruby/version.rb
65
+ - lib/t_ruby/watcher.rb
66
+ homepage: https://github.com/type-ruby/t-ruby
67
+ licenses:
68
+ - MIT
69
+ metadata: {}
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 3.0.0
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubygems_version: 3.7.2
85
+ specification_version: 4
86
+ summary: T-Ruby - A TypeScript-inspired typed layer for Ruby
87
+ test_files: []