t-ruby 0.0.43 → 0.0.46

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cb406ad473738afc16343a01b9f7e8e0f77a8b0fe700dd3ced2df65197bce626
4
- data.tar.gz: 95f1ce4ede5378e17393046c223c46b404d95e6506235e210aeba20a9615f8ac
3
+ metadata.gz: 509e1090df12fc29ea3109541a6e59133bf7516b31e4859bda8a6830d44f6999
4
+ data.tar.gz: 8dd1ef0eb0eeea4faccfd13372235d509997b65b9e556299542e880e6bb6ab85
5
5
  SHA512:
6
- metadata.gz: 6003aa038a7b476b8682aed9e32ba19ddbee6c124fe294c79b0371924527002f20c3ccdf45f71e2ee7360bef4e126d1ee0c15d93afd033df1166c20b5719187a
7
- data.tar.gz: c0c462fada5b5d98f46298dc307d6000b56d537b8d7f80af1faba2206c682cebf02e588277ca5ea2bf6f4e21141928540c733f0f8851adc48b4ca1be8ede6f81
6
+ metadata.gz: d97698f9bf39ac32ef8635e5f6a0e8ca1819490c5907bc3997563b6082c88d13bd5ad29e3c6a88aba808fcaf1802a15a43b0a4a0faea67b02f9cd6d2dec526c0
7
+ data.tar.gz: 5de7ceb419cf6667ff43c1662676d3222ef6c589fb9b9831c8e7e695dd7b4e5b07e3e70039818018ff8997328423b96e63f40a588a005985eda35b836ff52892
data/README.md CHANGED
@@ -243,7 +243,7 @@ watch:
243
243
 
244
244
  - **Type annotations** — Parameter and return types, erased at compile time
245
245
  - **Union types** — `String | Integer | nil`
246
- - **Generics** — `Array<User>`, `Hash<String, Integer>`
246
+ - **Generics** — `User[]`, `Array<User>`, `Hash<String, Integer>`
247
247
  - **Interfaces** — Define contracts between objects
248
248
  - **Type aliases** — `type UserID = Integer`
249
249
  - **RBS generation** — Works with Steep, Ruby LSP, Sorbet
@@ -284,6 +284,16 @@ See [ROADMAP.md](./ROADMAP.md) for details.
284
284
 
285
285
  Contributions are welcome! Please feel free to submit issues and pull requests.
286
286
 
287
+ ## ❤️ Support T-Ruby
288
+
289
+ If you find T-Ruby useful, consider sponsoring the project to support ongoing development and experimentation.
290
+
291
+ <p align="center">
292
+ <a href="https://github.com/sponsors/type-ruby">
293
+ <img src="https://img.shields.io/badge/Sponsor-❤️-ea4aaa" alt="Sponsor" />
294
+ </a>
295
+ </p>
296
+
287
297
  ## License
288
298
 
289
- [MIT](./LICENSE)
299
+ [BSD 2-Clause](./LICENSE)
data/bin/t-ruby ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/t_ruby"
5
+
6
+ TRuby::RunnerCLI.start(ARGV)
@@ -3,6 +3,7 @@
3
3
  require "benchmark"
4
4
  require "json"
5
5
  require "fileutils"
6
+ require "time"
6
7
 
7
8
  module TRuby
8
9
  # Benchmark suite for T-Ruby performance measurement
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "fileutils"
4
+ require "time"
4
5
 
5
6
  module TRuby
6
7
  # Integrates T-Ruby type packages with Bundler/RubyGems ecosystem
data/lib/t_ruby/cli.rb CHANGED
@@ -13,6 +13,7 @@ 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 run <file.trb> Run a .trb file directly (delegates to t-ruby)
16
17
  trc update Update t-ruby to the latest version
17
18
  trc --version, -v Show version (and check for updates)
18
19
  trc --help, -h Show this help
@@ -27,6 +28,7 @@ module TRuby
27
28
  trc --watch hello.trb Watch specific file for changes
28
29
  trc --decl hello.trb Generate hello.d.trb declaration file
29
30
  trc --lsp Start language server for VS Code
31
+ trc run hello.trb Run hello.trb directly without compilation
30
32
  HELP
31
33
 
32
34
  def self.run(args)
@@ -64,6 +66,11 @@ module TRuby
64
66
  return
65
67
  end
66
68
 
69
+ if @args.first == "run"
70
+ run_direct
71
+ return
72
+ end
73
+
67
74
  if @args.include?("--watch") || @args.include?("-w")
68
75
  start_watch_mode
69
76
  return
@@ -186,6 +193,16 @@ module TRuby
186
193
  server.run
187
194
  end
188
195
 
196
+ def run_direct
197
+ remaining_args = @args[1..] || []
198
+
199
+ # Find t-ruby executable path
200
+ t_ruby_bin = File.expand_path("../../bin/t-ruby", __dir__)
201
+
202
+ # Execute t-ruby (replaces current process)
203
+ exec(t_ruby_bin, *remaining_args)
204
+ end
205
+
189
206
  def start_watch_mode
190
207
  # Get paths to watch (everything after --watch or -w flag)
191
208
  watch_index = @args.index("--watch") || @args.index("-w")
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "json"
4
4
  require "fileutils"
5
+ require "time"
5
6
 
6
7
  module TRuby
7
8
  # API Documentation Generator
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "time"
3
4
  require_relative "docs_example_verifier"
4
5
 
5
6
  module TRuby
data/lib/t_ruby/ir.rb CHANGED
@@ -646,7 +646,13 @@ module TRuby
646
646
  end
647
647
 
648
648
  def to_rbs
649
- "#{@inner_type.to_rbs}?"
649
+ inner_rbs = @inner_type.to_rbs
650
+ # Simple types can use ? suffix, complex types need (Type | nil) form
651
+ if @inner_type.is_a?(SimpleType)
652
+ "#{inner_rbs}?"
653
+ else
654
+ "(#{inner_rbs} | nil)"
655
+ end
650
656
  end
651
657
 
652
658
  def to_trb
@@ -829,77 +835,17 @@ module TRuby
829
835
  return nil unless type_str
830
836
 
831
837
  type_str = type_str.strip
838
+ return nil if type_str.empty?
832
839
 
833
- # Union type
834
- if type_str.include?("|")
835
- types = type_str.split("|").map { |t| parse_type(t.strip) }
836
- return UnionType.new(types: types)
837
- end
838
-
839
- # Intersection type
840
- if type_str.include?("&")
841
- types = type_str.split("&").map { |t| parse_type(t.strip) }
842
- return IntersectionType.new(types: types)
843
- end
840
+ # Use ParserCombinator::TypeParser for all type parsing
841
+ # Supports: simple types, generics, array shorthand, union, intersection, function types
842
+ @type_parser ||= TRuby::ParserCombinator::TypeParser.new
843
+ result = @type_parser.parse(type_str)
844
+ return result[:type] if result[:success]
844
845
 
845
- # Nullable type
846
- if type_str.end_with?("?")
847
- inner = parse_type(type_str[0..-2])
848
- return NullableType.new(inner_type: inner)
849
- end
850
-
851
- # Generic type
852
- if type_str.include?("<") && type_str.include?(">")
853
- match = type_str.match(/^(\w+)<(.+)>$/)
854
- if match
855
- base = match[1]
856
- args = parse_generic_args(match[2])
857
- return GenericType.new(base: base, type_args: args)
858
- end
859
- end
860
-
861
- # Function type
862
- if type_str.include?("->")
863
- match = type_str.match(/^\((.*)?\)\s*->\s*(.+)$/)
864
- if match
865
- param_types = match[1] ? match[1].split(",").map { |t| parse_type(t.strip) } : []
866
- return_type = parse_type(match[2])
867
- return FunctionType.new(param_types: param_types, return_type: return_type)
868
- end
869
- end
870
-
871
- # Simple type
846
+ # Fallback for unparseable types - return as SimpleType
872
847
  SimpleType.new(name: type_str)
873
848
  end
874
-
875
- def parse_generic_args(args_str)
876
- args = []
877
- current = ""
878
- depth = 0
879
-
880
- args_str.each_char do |char|
881
- case char
882
- when "<"
883
- depth += 1
884
- current += char
885
- when ">"
886
- depth -= 1
887
- current += char
888
- when ","
889
- if depth.zero?
890
- args << parse_type(current.strip)
891
- current = ""
892
- else
893
- current += char
894
- end
895
- else
896
- current += char
897
- end
898
- end
899
-
900
- args << parse_type(current.strip) unless current.empty?
901
- args
902
- end
903
849
  end
904
850
 
905
851
  #==========================================================================
@@ -4,6 +4,7 @@ require "json"
4
4
  require "fileutils"
5
5
  require "net/http"
6
6
  require "uri"
7
+ require "time"
7
8
 
8
9
  module TRuby
9
10
  # Semantic version parsing and comparison
@@ -781,6 +781,7 @@ module TRuby
781
781
  end
782
782
 
783
783
  # Check for tuple type: (Type, Type) -> ReturnType
784
+ # or parenthesized type: (String | Integer)[]
784
785
  if tokens[position].type == :lparen
785
786
  position += 1
786
787
  param_types = []
@@ -811,6 +812,11 @@ module TRuby
811
812
 
812
813
  node = IR::FunctionType.new(param_types: param_types, return_type: return_result.value)
813
814
  return TokenParseResult.success(node, tokens, return_result.position)
815
+ elsif param_types.length == 1
816
+ # Single type in parentheses: (String | Integer)
817
+ # Check for postfix operators like [] or ?
818
+ type = param_types[0]
819
+ return parse_postfix_type_operators(tokens, position, type)
814
820
  else
815
821
  node = IR::TupleType.new(element_types: param_types)
816
822
  return TokenParseResult.success(node, tokens, position)
@@ -826,6 +832,13 @@ module TRuby
826
832
  type_name = tokens[position].value
827
833
  position += 1
828
834
 
835
+ # Handle type names ending with ? (e.g., "String?" scanned as single token)
836
+ # This happens because Ruby allows ? in method/identifier names
837
+ is_nullable_from_name = type_name.end_with?("?")
838
+ if is_nullable_from_name
839
+ type_name = type_name.chomp("?")
840
+ end
841
+
829
842
  # Check for generic arguments: Type<Args>
830
843
  if position < tokens.length && tokens[position].type == :lt
831
844
  position += 1
@@ -848,17 +861,46 @@ module TRuby
848
861
  position += 1
849
862
 
850
863
  node = IR::GenericType.new(base: type_name, type_args: type_args)
851
- TokenParseResult.success(node, tokens, position)
852
- elsif position < tokens.length && tokens[position].type == :question
853
- # Check for nullable: Type?
854
- position += 1
855
- inner = IR::SimpleType.new(name: type_name)
856
- node = IR::NullableType.new(inner_type: inner)
857
- TokenParseResult.success(node, tokens, position)
864
+ # Wrap in NullableType if the original type name ended with ?
865
+ node = IR::NullableType.new(inner_type: node) if is_nullable_from_name
866
+ # Apply postfix operators ([] or ?) to generic types too
867
+ parse_postfix_type_operators(tokens, position, node)
858
868
  else
869
+ # Simple type - apply postfix operators
859
870
  node = IR::SimpleType.new(name: type_name)
860
- TokenParseResult.success(node, tokens, position)
871
+ # Wrap in NullableType if the original type name ended with ?
872
+ node = IR::NullableType.new(inner_type: node) if is_nullable_from_name
873
+ parse_postfix_type_operators(tokens, position, node)
874
+ end
875
+ end
876
+
877
+ # Parse postfix type operators: [] (array shorthand) and ? (nullable)
878
+ # Handles patterns like:
879
+ # String[] => Array<String>
880
+ # Integer[][] => Array<Array<Integer>>
881
+ # String[]? => NullableType(Array<String>)
882
+ # String?[] => Array<NullableType(String)>
883
+ # (A | B)[] => Array<UnionType(A, B)>
884
+ def parse_postfix_type_operators(tokens, position, type)
885
+ loop do
886
+ break if position >= tokens.length
887
+
888
+ case tokens[position].type
889
+ when :lbracket
890
+ # Check for [] (empty brackets for array shorthand)
891
+ break unless tokens[position + 1]&.type == :rbracket
892
+
893
+ position += 2
894
+ type = IR::GenericType.new(base: "Array", type_args: [type])
895
+ when :question
896
+ position += 1
897
+ type = IR::NullableType.new(inner_type: type)
898
+ else
899
+ break
900
+ end
861
901
  end
902
+
903
+ TokenParseResult.success(type, tokens, position)
862
904
  end
863
905
 
864
906
  # Parse hash literal type: { key: Type, key2: Type }
@@ -81,9 +81,24 @@ module TRuby
81
81
  generic_type
82
82
  )
83
83
 
84
- # With optional nullable suffix
85
- base_type = (primary_type >> nullable_suffix.optional).map do |(type, nullable)|
86
- nullable ? IR::NullableType.new(inner_type: type) : type
84
+ # Array shorthand suffix: [] (can be repeated for nested arrays)
85
+ array_suffix = string("[]")
86
+
87
+ # Postfix operators: ([] | ?)*
88
+ # Handles: String[], Integer[][], String[]?, String?[], etc.
89
+ postfix_op = array_suffix | nullable_suffix
90
+
91
+ base_type = (primary_type >> postfix_op.many).map do |(initial_type, ops)|
92
+ ops.reduce(initial_type) do |type, op|
93
+ case op
94
+ when "[]"
95
+ IR::GenericType.new(base: "Array", type_args: [type])
96
+ when "?"
97
+ IR::NullableType.new(inner_type: type)
98
+ else
99
+ type
100
+ end
101
+ end
87
102
  end
88
103
 
89
104
  # Union type: Type | Type | ...
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+
5
+ module TRuby
6
+ # Thor-based CLI for t-ruby command
7
+ # Runs .trb files directly without generating intermediate files
8
+ class RunnerCLI < Thor
9
+ def self.exit_on_failure?
10
+ true
11
+ end
12
+
13
+ # Override Thor's default behavior to treat unknown arguments as the file to run
14
+ def self.start(given_args = ARGV, _config = {})
15
+ # Handle version flag
16
+ if given_args.include?("--version") || given_args.include?("-v")
17
+ new.version
18
+ return
19
+ end
20
+
21
+ # Handle help flag or no arguments
22
+ if given_args.empty? || given_args.include?("--help") || given_args.include?("-h")
23
+ new.help
24
+ return
25
+ end
26
+
27
+ # Treat first argument as file, rest as script arguments
28
+ file = given_args.first
29
+ args = given_args[1..] || []
30
+
31
+ runner = Runner.new
32
+ runner.run_file(file, args)
33
+ end
34
+
35
+ desc "FILE [ARGS...]", "Run a .trb file directly without generating files"
36
+ def run_file(file, *args)
37
+ runner = Runner.new
38
+ runner.run_file(file, args)
39
+ end
40
+
41
+ map %w[--version -v] => :version
42
+ desc "--version, -v", "Show version"
43
+ def version
44
+ puts "t-ruby #{VERSION}"
45
+ end
46
+
47
+ desc "--help, -h", "Show help"
48
+ def help
49
+ puts <<~HELP
50
+ t-ruby v#{VERSION} - Run T-Ruby files directly
51
+
52
+ Usage:
53
+ t-ruby <file.trb> Run a .trb file directly
54
+ t-ruby <file.trb> [args...] Run with arguments passed to the script
55
+ t-ruby --version, -v Show version
56
+ t-ruby --help, -h Show this help
57
+
58
+ Examples:
59
+ t-ruby hello.trb Run hello.trb
60
+ t-ruby server.trb 8080 Run with argument 8080
61
+ t-ruby script.trb foo bar Run with multiple arguments
62
+
63
+ Notes:
64
+ - No .rb or .rbs files are generated
65
+ - Type annotations are stripped at runtime
66
+ - Arguments after the file are passed to ARGV
67
+ HELP
68
+ end
69
+ end
70
+
71
+ # Runner class - executes T-Ruby code directly
72
+ # Can be used as a library or through RunnerCLI
73
+ class Runner
74
+ def initialize(config = nil)
75
+ @config = config || Config.new
76
+ @compiler = Compiler.new(@config)
77
+ end
78
+
79
+ # Run a .trb file directly
80
+ # @param input_path [String] Path to the .trb file
81
+ # @param argv [Array<String>] Arguments to pass to the script via ARGV
82
+ def run_file(input_path, argv = [])
83
+ unless File.exist?(input_path)
84
+ warn "Error: File not found: #{input_path}"
85
+ exit 1
86
+ end
87
+
88
+ source = File.read(input_path)
89
+ result = @compiler.compile_string(source)
90
+
91
+ if result[:errors].any?
92
+ result[:errors].each { |e| warn e }
93
+ exit 1
94
+ end
95
+
96
+ execute_ruby(result[:ruby], input_path, argv)
97
+ end
98
+
99
+ # Run T-Ruby source code from a string
100
+ # @param source [String] T-Ruby source code
101
+ # @param filename [String] Filename for error reporting
102
+ # @param argv [Array<String>] Arguments to pass via ARGV
103
+ # @return [Boolean] true if execution succeeded
104
+ def run_string(source, filename: "(t-ruby)", argv: [])
105
+ result = @compiler.compile_string(source)
106
+
107
+ if result[:errors].any?
108
+ result[:errors].each { |e| warn e }
109
+ return false
110
+ end
111
+
112
+ execute_ruby(result[:ruby], filename, argv)
113
+ true
114
+ end
115
+
116
+ private
117
+
118
+ # Execute Ruby code with proper script environment
119
+ # @param ruby_code [String] Ruby code to execute
120
+ # @param filename [String] Script filename (for $0 and stack traces)
121
+ # @param argv [Array<String>] Script arguments
122
+ def execute_ruby(ruby_code, filename, argv)
123
+ # Set up script environment
124
+ ARGV.replace(argv)
125
+ $0 = filename
126
+
127
+ # Execute using eval with filename and line number preserved
128
+ # This ensures stack traces point to the original .trb file
129
+ TOPLEVEL_BINDING.eval(ruby_code, filename, 1)
130
+ end
131
+ end
132
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TRuby
4
- VERSION = "0.0.43"
4
+ VERSION = "0.0.46"
5
5
  end
data/lib/t_ruby.rb CHANGED
@@ -29,6 +29,7 @@ require_relative "t_ruby/declaration_generator"
29
29
  require_relative "t_ruby/compiler"
30
30
  require_relative "t_ruby/lsp_server"
31
31
  require_relative "t_ruby/watcher"
32
+ require_relative "t_ruby/runner"
32
33
  require_relative "t_ruby/cli"
33
34
 
34
35
  # Milestone 4: Advanced Features
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: t-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.43
4
+ version: 0.0.46
5
5
  platform: ruby
6
6
  authors:
7
7
  - Y. Fred Kim
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-01-21 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: benchmark
@@ -23,17 +24,33 @@ dependencies:
23
24
  - - ">="
24
25
  - !ruby/object:Gem::Version
25
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
26
41
  description: t-ruby compiles .trb files with type annotations to executable Ruby (.rb)
27
42
  and optional type signature files (.rbs)
28
43
  email:
29
44
  - yhkks1038@gmail.com
30
45
  executables:
31
46
  - trc
47
+ - t-ruby
32
48
  extensions: []
33
49
  extra_rdoc_files: []
34
50
  files:
35
51
  - LICENSE
36
52
  - README.md
53
+ - bin/t-ruby
37
54
  - bin/trc
38
55
  - lib/t_ruby.rb
39
56
  - lib/t_ruby/ast_type_inferrer.rb
@@ -109,6 +126,7 @@ files:
109
126
  - lib/t_ruby/parser_combinator/token/token_skip_right.rb
110
127
  - lib/t_ruby/parser_combinator/type_parser.rb
111
128
  - lib/t_ruby/ruby_version.rb
129
+ - lib/t_ruby/runner.rb
112
130
  - lib/t_ruby/runtime_validator.rb
113
131
  - lib/t_ruby/scanner.rb
114
132
  - lib/t_ruby/smt_solver.rb
@@ -127,6 +145,7 @@ licenses:
127
145
  - BSD-2-Clause
128
146
  metadata:
129
147
  rubygems_mfa_required: 'true'
148
+ post_install_message:
130
149
  rdoc_options: []
131
150
  require_paths:
132
151
  - lib
@@ -141,7 +160,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
160
  - !ruby/object:Gem::Version
142
161
  version: '0'
143
162
  requirements: []
144
- rubygems_version: 4.0.1
163
+ rubygems_version: 3.5.22
164
+ signing_key:
145
165
  specification_version: 4
146
166
  summary: T-Ruby - TypeScript-style types for Ruby
147
167
  test_files: []