t-ruby 0.0.41 → 0.0.43

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/lib/t_ruby/ast_type_inferrer.rb +2 -0
  3. data/lib/t_ruby/cache.rb +40 -10
  4. data/lib/t_ruby/cli.rb +14 -9
  5. data/lib/t_ruby/code_emitter.rb +254 -0
  6. data/lib/t_ruby/compiler.rb +186 -3
  7. data/lib/t_ruby/config.rb +18 -3
  8. data/lib/t_ruby/diagnostic.rb +115 -0
  9. data/lib/t_ruby/diagnostic_formatter.rb +162 -0
  10. data/lib/t_ruby/error_handler.rb +201 -35
  11. data/lib/t_ruby/error_reporter.rb +57 -0
  12. data/lib/t_ruby/ir.rb +39 -1
  13. data/lib/t_ruby/lsp_server.rb +40 -97
  14. data/lib/t_ruby/parser.rb +18 -4
  15. data/lib/t_ruby/parser_combinator/combinators/alternative.rb +20 -0
  16. data/lib/t_ruby/parser_combinator/combinators/chain_left.rb +34 -0
  17. data/lib/t_ruby/parser_combinator/combinators/choice.rb +29 -0
  18. data/lib/t_ruby/parser_combinator/combinators/flat_map.rb +21 -0
  19. data/lib/t_ruby/parser_combinator/combinators/label.rb +22 -0
  20. data/lib/t_ruby/parser_combinator/combinators/lookahead.rb +21 -0
  21. data/lib/t_ruby/parser_combinator/combinators/many.rb +29 -0
  22. data/lib/t_ruby/parser_combinator/combinators/many1.rb +32 -0
  23. data/lib/t_ruby/parser_combinator/combinators/map.rb +17 -0
  24. data/lib/t_ruby/parser_combinator/combinators/not_followed_by.rb +21 -0
  25. data/lib/t_ruby/parser_combinator/combinators/optional.rb +21 -0
  26. data/lib/t_ruby/parser_combinator/combinators/sep_by.rb +34 -0
  27. data/lib/t_ruby/parser_combinator/combinators/sep_by1.rb +34 -0
  28. data/lib/t_ruby/parser_combinator/combinators/sequence.rb +23 -0
  29. data/lib/t_ruby/parser_combinator/combinators/skip_right.rb +23 -0
  30. data/lib/t_ruby/parser_combinator/declaration_parser.rb +147 -0
  31. data/lib/t_ruby/parser_combinator/dsl.rb +115 -0
  32. data/lib/t_ruby/parser_combinator/parse_error.rb +48 -0
  33. data/lib/t_ruby/parser_combinator/parse_result.rb +46 -0
  34. data/lib/t_ruby/parser_combinator/parser.rb +84 -0
  35. data/lib/t_ruby/parser_combinator/primitives/end_of_input.rb +16 -0
  36. data/lib/t_ruby/parser_combinator/primitives/fail.rb +16 -0
  37. data/lib/t_ruby/parser_combinator/primitives/lazy.rb +18 -0
  38. data/lib/t_ruby/parser_combinator/primitives/literal.rb +21 -0
  39. data/lib/t_ruby/parser_combinator/primitives/pure.rb +16 -0
  40. data/lib/t_ruby/parser_combinator/primitives/regex.rb +25 -0
  41. data/lib/t_ruby/parser_combinator/primitives/satisfy.rb +21 -0
  42. data/lib/t_ruby/parser_combinator/token/expression_parser.rb +541 -0
  43. data/lib/t_ruby/parser_combinator/token/statement_parser.rb +644 -0
  44. data/lib/t_ruby/parser_combinator/token/token_alternative.rb +20 -0
  45. data/lib/t_ruby/parser_combinator/token/token_body_parser.rb +54 -0
  46. data/lib/t_ruby/parser_combinator/token/token_declaration_parser.rb +920 -0
  47. data/lib/t_ruby/parser_combinator/token/token_dsl.rb +16 -0
  48. data/lib/t_ruby/parser_combinator/token/token_label.rb +22 -0
  49. data/lib/t_ruby/parser_combinator/token/token_many.rb +29 -0
  50. data/lib/t_ruby/parser_combinator/token/token_many1.rb +32 -0
  51. data/lib/t_ruby/parser_combinator/token/token_map.rb +17 -0
  52. data/lib/t_ruby/parser_combinator/token/token_matcher.rb +29 -0
  53. data/lib/t_ruby/parser_combinator/token/token_optional.rb +21 -0
  54. data/lib/t_ruby/parser_combinator/token/token_parse_result.rb +40 -0
  55. data/lib/t_ruby/parser_combinator/token/token_parser.rb +62 -0
  56. data/lib/t_ruby/parser_combinator/token/token_sep_by.rb +34 -0
  57. data/lib/t_ruby/parser_combinator/token/token_sep_by1.rb +34 -0
  58. data/lib/t_ruby/parser_combinator/token/token_sequence.rb +23 -0
  59. data/lib/t_ruby/parser_combinator/token/token_skip_right.rb +23 -0
  60. data/lib/t_ruby/parser_combinator/type_parser.rb +103 -0
  61. data/lib/t_ruby/parser_combinator.rb +64 -936
  62. data/lib/t_ruby/ruby_version.rb +112 -0
  63. data/lib/t_ruby/scanner.rb +883 -0
  64. data/lib/t_ruby/version.rb +1 -1
  65. data/lib/t_ruby/watcher.rb +83 -76
  66. data/lib/t_ruby.rb +17 -1
  67. metadata +58 -7
  68. data/lib/t_ruby/body_parser.rb +0 -561
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TRuby
4
- VERSION = "0.0.41"
4
+ VERSION = "0.0.43"
5
5
  end
@@ -1,6 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "listen"
3
+ # listen gem is optional - only required for watch mode
4
+ # This allows T-Ruby core functionality to work on Ruby 4.0+ where listen/ffi may not be available
5
+ begin
6
+ require "listen"
7
+ LISTEN_AVAILABLE = true
8
+ rescue LoadError
9
+ LISTEN_AVAILABLE = false
10
+ end
4
11
 
5
12
  module TRuby
6
13
  class Watcher
@@ -26,6 +33,7 @@ module TRuby
26
33
  @error_count = 0
27
34
  @file_count = 0
28
35
  @use_colors = $stdout.tty?
36
+ @file_diagnostics = {} # Cache diagnostics per file for incremental updates
29
37
 
30
38
  # Enhanced features
31
39
  @incremental = incremental
@@ -52,6 +60,14 @@ module TRuby
52
60
  end
53
61
 
54
62
  def watch
63
+ unless LISTEN_AVAILABLE
64
+ puts colorize(:red, "Error: Watch mode requires the 'listen' gem.")
65
+ puts colorize(:yellow, "The 'listen' gem is not available (possibly due to Ruby 4.0+ ffi compatibility).")
66
+ puts colorize(:dim, "Install with: gem install listen")
67
+ puts colorize(:dim, "Or run without watch mode: trc")
68
+ exit 1
69
+ end
70
+
55
71
  print_start_message
56
72
 
57
73
  # Initial compilation
@@ -123,48 +139,27 @@ module TRuby
123
139
  def compile_all
124
140
  @error_count = 0
125
141
  @file_count = 0
126
- errors = []
142
+ @file_diagnostics = {} # Reset diagnostics cache on full compile
127
143
 
128
144
  trb_files = find_trb_files
129
145
  rb_files = find_rb_files
130
146
  all_files = trb_files + rb_files
131
147
  @file_count = all_files.size
132
148
 
133
- if @incremental && @cross_file_check
134
- # Use enhanced incremental compiler with cross-file checking
135
- result = @incremental_compiler.compile_all_with_checking(trb_files)
136
- errors = result[:errors].map { |e| format_error(e[:file], e[:error] || e[:message]) }
137
- @error_count = errors.size
138
- @stats[:total_compilations] += trb_files.size
139
-
140
- # Also compile .rb files
141
- rb_files.each do |file|
142
- result = compile_file(file)
143
- errors.concat(result[:errors]) if result[:errors].any?
144
- end
145
- elsif @parallel && all_files.size > 1
146
- # Parallel compilation
147
- results = @parallel_processor.process_files(all_files) do |file|
148
- compile_file(file)
149
- end
150
- results.each do |result|
151
- errors.concat(result[:errors]) if result[:errors]&.any?
152
- end
153
- else
154
- # Sequential compilation
155
- all_files.each do |file|
156
- result = compile_file(file)
157
- errors.concat(result[:errors]) if result[:errors].any?
158
- end
149
+ # Use unified compile_with_diagnostics for all files
150
+ # Note: compile_file increments @stats[:total_compilations] internally
151
+ all_files.each do |file|
152
+ result = compile_file(file)
153
+ # Cache diagnostics per file
154
+ @file_diagnostics[file] = result[:diagnostics] || []
159
155
  end
160
156
 
161
- print_errors(errors)
157
+ all_diagnostics = @file_diagnostics.values.flatten
158
+ print_errors(all_diagnostics)
162
159
  print_summary
163
160
  end
164
161
 
165
162
  def compile_files_incremental(files)
166
- @error_count = 0
167
- errors = []
168
163
  compiled_count = 0
169
164
 
170
165
  if @incremental
@@ -172,7 +167,8 @@ module TRuby
172
167
  if @incremental_compiler.needs_compile?(file)
173
168
  @stats[:total_compilations] += 1
174
169
  result = compile_file_with_ir(file)
175
- errors.concat(result[:errors]) if result[:errors].any?
170
+ # Update cached diagnostics for this file
171
+ @file_diagnostics[file] = result[:diagnostics] || []
176
172
  compiled_count += 1
177
173
  else
178
174
  @stats[:incremental_hits] += 1
@@ -184,56 +180,59 @@ module TRuby
184
180
  if @cross_file_check && @incremental_compiler.cross_file_checker
185
181
  check_result = @incremental_compiler.cross_file_checker.check_all
186
182
  check_result[:errors].each do |e|
187
- errors << format_error(e[:file], e[:message])
183
+ # Add cross-file errors (these are not file-specific)
184
+ @file_diagnostics[:cross_file] ||= []
185
+ @file_diagnostics[:cross_file] << create_diagnostic_from_cross_file_error(e)
188
186
  end
189
187
  end
190
188
  else
191
189
  files.each do |file|
192
190
  result = compile_file(file)
193
- errors.concat(result[:errors]) if result[:errors].any?
191
+ # Update cached diagnostics for this file
192
+ @file_diagnostics[file] = result[:diagnostics] || []
194
193
  compiled_count += 1
195
194
  end
196
195
  end
197
196
 
197
+ # Collect all diagnostics from cache (includes unchanged files' errors)
198
+ all_diagnostics = @file_diagnostics.values.flatten
199
+
200
+ # Update error count from all cached diagnostics
201
+ @error_count = all_diagnostics.size
202
+
198
203
  @file_count = compiled_count
199
- print_errors(errors)
204
+ print_errors(all_diagnostics)
200
205
  print_summary
201
206
  print_watching_message
202
207
  end
203
208
 
204
209
  def compile_file_with_ir(file)
205
- result = { file: file, errors: [], success: false }
210
+ # Use unified compile_with_diagnostics from Compiler (same as compile_file)
211
+ # This ensures incremental compile returns the same diagnostics as full compile
212
+ compile_result = @compiler.compile_with_diagnostics(file)
206
213
 
207
- begin
208
- @incremental_compiler.compile_with_ir(file)
209
- result[:success] = true
210
- rescue ArgumentError => e
211
- @error_count += 1
212
- result[:errors] << format_error(file, e.message)
213
- rescue StandardError => e
214
- @error_count += 1
215
- result[:errors] << format_error(file, e.message)
216
- end
214
+ # Update incremental compiler's file hash to track changes
215
+ @incremental_compiler&.update_file_hash(file)
217
216
 
218
- result
217
+ {
218
+ file: file,
219
+ diagnostics: compile_result[:diagnostics],
220
+ success: compile_result[:success],
221
+ }
219
222
  end
220
223
 
221
224
  def compile_file(file)
222
- result = { file: file, errors: [], success: false }
225
+ # Use unified compile_with_diagnostics from Compiler
226
+ compile_result = @compiler.compile_with_diagnostics(file)
223
227
 
224
- begin
225
- @compiler.compile(file)
226
- result[:success] = true
227
- @stats[:total_compilations] += 1
228
- rescue ArgumentError => e
229
- @error_count += 1
230
- result[:errors] << format_error(file, e.message)
231
- rescue StandardError => e
232
- @error_count += 1
233
- result[:errors] << format_error(file, e.message)
234
- end
228
+ @error_count += compile_result[:diagnostics].size
229
+ @stats[:total_compilations] += 1
235
230
 
236
- result
231
+ {
232
+ file: file,
233
+ diagnostics: compile_result[:diagnostics],
234
+ success: compile_result[:success],
235
+ }
237
236
  end
238
237
 
239
238
  def find_trb_files
@@ -269,9 +268,15 @@ module TRuby
269
268
  files.uniq
270
269
  end
271
270
 
272
- def format_error(file, message)
273
- # Parse error message for line/column info if available
274
- # Format: file:line:col - error TRB0001: message
271
+ # Create a Diagnostic for cross-file check errors
272
+ def create_diagnostic_from_cross_file_error(error)
273
+ file = error[:file]
274
+ source = File.exist?(file) ? File.read(file) : nil
275
+ create_generic_diagnostic(file, error[:message], source)
276
+ end
277
+
278
+ # Create a generic Diagnostic for standard errors
279
+ def create_generic_diagnostic(file, message, source = nil)
275
280
  line = 1
276
281
  col = 1
277
282
 
@@ -280,23 +285,25 @@ module TRuby
280
285
  line = ::Regexp.last_match(1).to_i
281
286
  end
282
287
 
283
- {
284
- file: file,
285
- line: line,
286
- col: col,
288
+ source_line = source&.split("\n")&.at(line - 1)
289
+
290
+ Diagnostic.new(
291
+ code: "TR0001",
287
292
  message: message,
288
- }
293
+ file: relative_path(file),
294
+ line: line,
295
+ column: col,
296
+ source_line: source_line
297
+ )
289
298
  end
290
299
 
291
- def print_errors(errors)
292
- errors.each do |error|
300
+ def print_errors(diagnostics)
301
+ return if diagnostics.empty?
302
+
303
+ formatter = DiagnosticFormatter.new(use_colors: @use_colors)
304
+ diagnostics.each do |diagnostic|
293
305
  puts
294
- # TypeScript-style error format: file:line:col - error TSXXXX: message
295
- location = "#{colorize(:cyan,
296
- relative_path(error[:file]))}:#{colorize(:yellow,
297
- error[:line])}:#{colorize(:yellow,
298
- error[:col])}"
299
- puts "#{location} - #{colorize(:red, "error")} #{colorize(:gray, "TRB0001")}: #{error[:message]}"
306
+ puts formatter.format(diagnostic)
300
307
  end
301
308
  end
302
309
 
data/lib/t_ruby.rb CHANGED
@@ -2,24 +2,29 @@
2
2
 
3
3
  require_relative "t_ruby/version"
4
4
  require_relative "t_ruby/version_checker"
5
+ require_relative "t_ruby/ruby_version"
6
+ require_relative "t_ruby/code_emitter"
5
7
  require_relative "t_ruby/config"
6
8
 
7
9
  # Core infrastructure (must be loaded first)
8
10
  require_relative "t_ruby/string_utils"
9
11
  require_relative "t_ruby/ir"
10
12
  require_relative "t_ruby/parser_combinator"
13
+ require_relative "t_ruby/scanner"
11
14
  require_relative "t_ruby/smt_solver"
12
15
 
13
16
  # Basic components
14
17
  require_relative "t_ruby/type_alias_registry"
15
18
  require_relative "t_ruby/heredoc_detector"
16
- require_relative "t_ruby/body_parser"
17
19
  require_relative "t_ruby/parser"
18
20
  require_relative "t_ruby/union_type_parser"
19
21
  require_relative "t_ruby/generic_type_parser"
20
22
  require_relative "t_ruby/intersection_type_parser"
21
23
  require_relative "t_ruby/type_erasure"
22
24
  require_relative "t_ruby/error_handler"
25
+ require_relative "t_ruby/diagnostic"
26
+ require_relative "t_ruby/diagnostic_formatter"
27
+ require_relative "t_ruby/error_reporter"
23
28
  require_relative "t_ruby/declaration_generator"
24
29
  require_relative "t_ruby/compiler"
25
30
  require_relative "t_ruby/lsp_server"
@@ -49,4 +54,15 @@ require_relative "t_ruby/docs_example_verifier"
49
54
  require_relative "t_ruby/docs_badge_generator"
50
55
 
51
56
  module TRuby
57
+ # Parse error for T-Ruby source code
58
+ class ParseError < StandardError
59
+ attr_reader :line, :column, :source
60
+
61
+ def initialize(message, line: nil, column: nil, source: nil)
62
+ @line = line
63
+ @column = column
64
+ @source = source
65
+ super(message)
66
+ end
67
+ end
52
68
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: t-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.41
4
+ version: 0.0.43
5
5
  platform: ruby
6
6
  authors:
7
7
  - Y. Fred Kim
@@ -10,19 +10,19 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: listen
13
+ name: benchmark
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - "~>"
16
+ - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '3.8'
18
+ version: '0'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
- - - "~>"
23
+ - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: '3.8'
25
+ version: '0'
26
26
  description: t-ruby compiles .trb files with type annotations to executable Ruby (.rb)
27
27
  and optional type signature files (.rbs)
28
28
  email:
@@ -38,19 +38,22 @@ files:
38
38
  - lib/t_ruby.rb
39
39
  - lib/t_ruby/ast_type_inferrer.rb
40
40
  - lib/t_ruby/benchmark.rb
41
- - lib/t_ruby/body_parser.rb
42
41
  - lib/t_ruby/bundler_integration.rb
43
42
  - lib/t_ruby/cache.rb
44
43
  - lib/t_ruby/cli.rb
44
+ - lib/t_ruby/code_emitter.rb
45
45
  - lib/t_ruby/compiler.rb
46
46
  - lib/t_ruby/config.rb
47
47
  - lib/t_ruby/constraint_checker.rb
48
48
  - lib/t_ruby/declaration_generator.rb
49
+ - lib/t_ruby/diagnostic.rb
50
+ - lib/t_ruby/diagnostic_formatter.rb
49
51
  - lib/t_ruby/doc_generator.rb
50
52
  - lib/t_ruby/docs_badge_generator.rb
51
53
  - lib/t_ruby/docs_example_extractor.rb
52
54
  - lib/t_ruby/docs_example_verifier.rb
53
55
  - lib/t_ruby/error_handler.rb
56
+ - lib/t_ruby/error_reporter.rb
54
57
  - lib/t_ruby/generic_type_parser.rb
55
58
  - lib/t_ruby/heredoc_detector.rb
56
59
  - lib/t_ruby/intersection_type_parser.rb
@@ -59,7 +62,55 @@ files:
59
62
  - lib/t_ruby/package_manager.rb
60
63
  - lib/t_ruby/parser.rb
61
64
  - lib/t_ruby/parser_combinator.rb
65
+ - lib/t_ruby/parser_combinator/combinators/alternative.rb
66
+ - lib/t_ruby/parser_combinator/combinators/chain_left.rb
67
+ - lib/t_ruby/parser_combinator/combinators/choice.rb
68
+ - lib/t_ruby/parser_combinator/combinators/flat_map.rb
69
+ - lib/t_ruby/parser_combinator/combinators/label.rb
70
+ - lib/t_ruby/parser_combinator/combinators/lookahead.rb
71
+ - lib/t_ruby/parser_combinator/combinators/many.rb
72
+ - lib/t_ruby/parser_combinator/combinators/many1.rb
73
+ - lib/t_ruby/parser_combinator/combinators/map.rb
74
+ - lib/t_ruby/parser_combinator/combinators/not_followed_by.rb
75
+ - lib/t_ruby/parser_combinator/combinators/optional.rb
76
+ - lib/t_ruby/parser_combinator/combinators/sep_by.rb
77
+ - lib/t_ruby/parser_combinator/combinators/sep_by1.rb
78
+ - lib/t_ruby/parser_combinator/combinators/sequence.rb
79
+ - lib/t_ruby/parser_combinator/combinators/skip_right.rb
80
+ - lib/t_ruby/parser_combinator/declaration_parser.rb
81
+ - lib/t_ruby/parser_combinator/dsl.rb
82
+ - lib/t_ruby/parser_combinator/parse_error.rb
83
+ - lib/t_ruby/parser_combinator/parse_result.rb
84
+ - lib/t_ruby/parser_combinator/parser.rb
85
+ - lib/t_ruby/parser_combinator/primitives/end_of_input.rb
86
+ - lib/t_ruby/parser_combinator/primitives/fail.rb
87
+ - lib/t_ruby/parser_combinator/primitives/lazy.rb
88
+ - lib/t_ruby/parser_combinator/primitives/literal.rb
89
+ - lib/t_ruby/parser_combinator/primitives/pure.rb
90
+ - lib/t_ruby/parser_combinator/primitives/regex.rb
91
+ - lib/t_ruby/parser_combinator/primitives/satisfy.rb
92
+ - lib/t_ruby/parser_combinator/token/expression_parser.rb
93
+ - lib/t_ruby/parser_combinator/token/statement_parser.rb
94
+ - lib/t_ruby/parser_combinator/token/token_alternative.rb
95
+ - lib/t_ruby/parser_combinator/token/token_body_parser.rb
96
+ - lib/t_ruby/parser_combinator/token/token_declaration_parser.rb
97
+ - lib/t_ruby/parser_combinator/token/token_dsl.rb
98
+ - lib/t_ruby/parser_combinator/token/token_label.rb
99
+ - lib/t_ruby/parser_combinator/token/token_many.rb
100
+ - lib/t_ruby/parser_combinator/token/token_many1.rb
101
+ - lib/t_ruby/parser_combinator/token/token_map.rb
102
+ - lib/t_ruby/parser_combinator/token/token_matcher.rb
103
+ - lib/t_ruby/parser_combinator/token/token_optional.rb
104
+ - lib/t_ruby/parser_combinator/token/token_parse_result.rb
105
+ - lib/t_ruby/parser_combinator/token/token_parser.rb
106
+ - lib/t_ruby/parser_combinator/token/token_sep_by.rb
107
+ - lib/t_ruby/parser_combinator/token/token_sep_by1.rb
108
+ - lib/t_ruby/parser_combinator/token/token_sequence.rb
109
+ - lib/t_ruby/parser_combinator/token/token_skip_right.rb
110
+ - lib/t_ruby/parser_combinator/type_parser.rb
111
+ - lib/t_ruby/ruby_version.rb
62
112
  - lib/t_ruby/runtime_validator.rb
113
+ - lib/t_ruby/scanner.rb
63
114
  - lib/t_ruby/smt_solver.rb
64
115
  - lib/t_ruby/string_utils.rb
65
116
  - lib/t_ruby/type_alias_registry.rb