konpeito 0.1.0

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 (180) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/CHANGELOG.md +75 -0
  4. data/CONTRIBUTING.md +123 -0
  5. data/LICENSE +21 -0
  6. data/README.md +257 -0
  7. data/Rakefile +11 -0
  8. data/bin/konpeito +6 -0
  9. data/konpeito.gemspec +43 -0
  10. data/lib/konpeito/ast/typed_ast.rb +620 -0
  11. data/lib/konpeito/ast/visitor.rb +78 -0
  12. data/lib/konpeito/cache/cache_manager.rb +230 -0
  13. data/lib/konpeito/cache/dependency_graph.rb +192 -0
  14. data/lib/konpeito/cache.rb +8 -0
  15. data/lib/konpeito/cli/base_command.rb +187 -0
  16. data/lib/konpeito/cli/build_command.rb +220 -0
  17. data/lib/konpeito/cli/check_command.rb +104 -0
  18. data/lib/konpeito/cli/config.rb +231 -0
  19. data/lib/konpeito/cli/deps_command.rb +128 -0
  20. data/lib/konpeito/cli/doctor_command.rb +340 -0
  21. data/lib/konpeito/cli/fmt_command.rb +199 -0
  22. data/lib/konpeito/cli/init_command.rb +312 -0
  23. data/lib/konpeito/cli/lsp_command.rb +40 -0
  24. data/lib/konpeito/cli/run_command.rb +150 -0
  25. data/lib/konpeito/cli/test_command.rb +248 -0
  26. data/lib/konpeito/cli/watch_command.rb +212 -0
  27. data/lib/konpeito/cli.rb +301 -0
  28. data/lib/konpeito/codegen/builtin_methods.rb +229 -0
  29. data/lib/konpeito/codegen/cruby_backend.rb +1090 -0
  30. data/lib/konpeito/codegen/debug_info.rb +352 -0
  31. data/lib/konpeito/codegen/inliner.rb +486 -0
  32. data/lib/konpeito/codegen/jvm_backend.rb +197 -0
  33. data/lib/konpeito/codegen/jvm_generator.rb +13412 -0
  34. data/lib/konpeito/codegen/llvm_generator.rb +13191 -0
  35. data/lib/konpeito/codegen/loop_optimizer.rb +363 -0
  36. data/lib/konpeito/codegen/monomorphizer.rb +359 -0
  37. data/lib/konpeito/codegen/profile_runtime.c +341 -0
  38. data/lib/konpeito/codegen/profiler.rb +99 -0
  39. data/lib/konpeito/compiler.rb +592 -0
  40. data/lib/konpeito/dependency_resolver.rb +296 -0
  41. data/lib/konpeito/diagnostics/collector.rb +127 -0
  42. data/lib/konpeito/diagnostics/diagnostic.rb +237 -0
  43. data/lib/konpeito/diagnostics/renderer.rb +144 -0
  44. data/lib/konpeito/formatter/formatter.rb +1214 -0
  45. data/lib/konpeito/hir/builder.rb +7167 -0
  46. data/lib/konpeito/hir/nodes.rb +2465 -0
  47. data/lib/konpeito/lsp/document_manager.rb +820 -0
  48. data/lib/konpeito/lsp/server.rb +183 -0
  49. data/lib/konpeito/lsp/transport.rb +38 -0
  50. data/lib/konpeito/parser/prism_adapter.rb +65 -0
  51. data/lib/konpeito/platform.rb +103 -0
  52. data/lib/konpeito/profile/report.rb +136 -0
  53. data/lib/konpeito/rbs_inline/preprocessor.rb +199 -0
  54. data/lib/konpeito/stdlib/compression/compression.rb +72 -0
  55. data/lib/konpeito/stdlib/compression/compression.rbs +60 -0
  56. data/lib/konpeito/stdlib/compression/compression_native.c +415 -0
  57. data/lib/konpeito/stdlib/compression/extconf.rb +19 -0
  58. data/lib/konpeito/stdlib/crypto/crypto.rb +85 -0
  59. data/lib/konpeito/stdlib/crypto/crypto.rbs +74 -0
  60. data/lib/konpeito/stdlib/crypto/crypto_native.c +312 -0
  61. data/lib/konpeito/stdlib/crypto/extconf.rb +40 -0
  62. data/lib/konpeito/stdlib/http/extconf.rb +19 -0
  63. data/lib/konpeito/stdlib/http/http.rb +125 -0
  64. data/lib/konpeito/stdlib/http/http.rbs +57 -0
  65. data/lib/konpeito/stdlib/http/http_native.c +440 -0
  66. data/lib/konpeito/stdlib/json/extconf.rb +17 -0
  67. data/lib/konpeito/stdlib/json/json.rb +44 -0
  68. data/lib/konpeito/stdlib/json/json.rbs +33 -0
  69. data/lib/konpeito/stdlib/json/json_native.c +286 -0
  70. data/lib/konpeito/stdlib/ui/extconf.rb +216 -0
  71. data/lib/konpeito/stdlib/ui/konpeito_ui_native.cpp +1625 -0
  72. data/lib/konpeito/stdlib/ui/konpeito_ui_native.h +162 -0
  73. data/lib/konpeito/stdlib/ui/ui.rb +318 -0
  74. data/lib/konpeito/stdlib/ui/ui.rbs +247 -0
  75. data/lib/konpeito/type_checker/annotation_parser.rb +67 -0
  76. data/lib/konpeito/type_checker/hm_inferrer.rb +2565 -0
  77. data/lib/konpeito/type_checker/inferrer.rb +565 -0
  78. data/lib/konpeito/type_checker/rbs_loader.rb +1621 -0
  79. data/lib/konpeito/type_checker/type_resolver.rb +276 -0
  80. data/lib/konpeito/type_checker/types.rb +1434 -0
  81. data/lib/konpeito/type_checker/unification.rb +323 -0
  82. data/lib/konpeito/ui/animation/animated_state.rb +80 -0
  83. data/lib/konpeito/ui/animation/easing.rb +59 -0
  84. data/lib/konpeito/ui/animation/value_tween.rb +66 -0
  85. data/lib/konpeito/ui/app.rb +379 -0
  86. data/lib/konpeito/ui/box.rb +38 -0
  87. data/lib/konpeito/ui/castella.rb +70 -0
  88. data/lib/konpeito/ui/castella_native.rb +76 -0
  89. data/lib/konpeito/ui/chart/area_chart.rb +305 -0
  90. data/lib/konpeito/ui/chart/bar_chart.rb +288 -0
  91. data/lib/konpeito/ui/chart/base_chart.rb +210 -0
  92. data/lib/konpeito/ui/chart/chart_helpers.rb +79 -0
  93. data/lib/konpeito/ui/chart/gauge_chart.rb +171 -0
  94. data/lib/konpeito/ui/chart/heatmap_chart.rb +222 -0
  95. data/lib/konpeito/ui/chart/line_chart.rb +289 -0
  96. data/lib/konpeito/ui/chart/pie_chart.rb +219 -0
  97. data/lib/konpeito/ui/chart/scales.rb +77 -0
  98. data/lib/konpeito/ui/chart/scatter_chart.rb +303 -0
  99. data/lib/konpeito/ui/chart/stacked_bar_chart.rb +276 -0
  100. data/lib/konpeito/ui/column.rb +271 -0
  101. data/lib/konpeito/ui/core.rb +2199 -0
  102. data/lib/konpeito/ui/dsl.rb +443 -0
  103. data/lib/konpeito/ui/frame.rb +171 -0
  104. data/lib/konpeito/ui/frame_native.rb +494 -0
  105. data/lib/konpeito/ui/markdown/ast.rb +124 -0
  106. data/lib/konpeito/ui/markdown/mermaid/layout.rb +387 -0
  107. data/lib/konpeito/ui/markdown/mermaid/models.rb +232 -0
  108. data/lib/konpeito/ui/markdown/mermaid/parser.rb +519 -0
  109. data/lib/konpeito/ui/markdown/mermaid/renderer.rb +336 -0
  110. data/lib/konpeito/ui/markdown/parser.rb +805 -0
  111. data/lib/konpeito/ui/markdown/renderer.rb +639 -0
  112. data/lib/konpeito/ui/markdown/theme.rb +165 -0
  113. data/lib/konpeito/ui/render_node.rb +260 -0
  114. data/lib/konpeito/ui/row.rb +207 -0
  115. data/lib/konpeito/ui/spacer.rb +18 -0
  116. data/lib/konpeito/ui/style.rb +799 -0
  117. data/lib/konpeito/ui/theme.rb +563 -0
  118. data/lib/konpeito/ui/themes/material.rb +35 -0
  119. data/lib/konpeito/ui/themes/tokyo_night.rb +6 -0
  120. data/lib/konpeito/ui/widgets/button.rb +103 -0
  121. data/lib/konpeito/ui/widgets/calendar.rb +1034 -0
  122. data/lib/konpeito/ui/widgets/checkbox.rb +119 -0
  123. data/lib/konpeito/ui/widgets/container.rb +91 -0
  124. data/lib/konpeito/ui/widgets/data_table.rb +667 -0
  125. data/lib/konpeito/ui/widgets/divider.rb +29 -0
  126. data/lib/konpeito/ui/widgets/image.rb +105 -0
  127. data/lib/konpeito/ui/widgets/input.rb +485 -0
  128. data/lib/konpeito/ui/widgets/markdown.rb +57 -0
  129. data/lib/konpeito/ui/widgets/modal.rb +163 -0
  130. data/lib/konpeito/ui/widgets/multiline_input.rb +968 -0
  131. data/lib/konpeito/ui/widgets/multiline_text.rb +180 -0
  132. data/lib/konpeito/ui/widgets/net_image.rb +100 -0
  133. data/lib/konpeito/ui/widgets/progress_bar.rb +70 -0
  134. data/lib/konpeito/ui/widgets/radio_buttons.rb +93 -0
  135. data/lib/konpeito/ui/widgets/slider.rb +133 -0
  136. data/lib/konpeito/ui/widgets/switch.rb +84 -0
  137. data/lib/konpeito/ui/widgets/tabs.rb +157 -0
  138. data/lib/konpeito/ui/widgets/text.rb +110 -0
  139. data/lib/konpeito/ui/widgets/tree.rb +426 -0
  140. data/lib/konpeito/version.rb +5 -0
  141. data/lib/konpeito.rb +109 -0
  142. data/test_native_array.rb +172 -0
  143. data/test_native_array_class.rb +197 -0
  144. data/test_native_class.rb +151 -0
  145. data/tools/konpeito-asm/build.sh +65 -0
  146. data/tools/konpeito-asm/lib/asm-9.7.1.jar +0 -0
  147. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KArray.class +0 -0
  148. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCompression.class +0 -0
  149. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class +0 -0
  150. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCrypto.class +0 -0
  151. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KFile.class +0 -0
  152. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHTTP.class +0 -0
  153. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class +0 -0
  154. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON$Parser.class +0 -0
  155. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON.class +0 -0
  156. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KMath.class +0 -0
  157. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactor.class +0 -0
  158. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactorPort.class +0 -0
  159. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class +0 -0
  160. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KThread.class +0 -0
  161. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KTime.class +0 -0
  162. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/RubyDispatch.class +0 -0
  163. data/tools/konpeito-asm/src/ClassIntrospector.java +312 -0
  164. data/tools/konpeito-asm/src/KonpeitoAssembler.java +659 -0
  165. data/tools/konpeito-asm/src/konpeito/runtime/KArray.java +390 -0
  166. data/tools/konpeito-asm/src/konpeito/runtime/KCompression.java +168 -0
  167. data/tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java +48 -0
  168. data/tools/konpeito-asm/src/konpeito/runtime/KCrypto.java +151 -0
  169. data/tools/konpeito-asm/src/konpeito/runtime/KFile.java +100 -0
  170. data/tools/konpeito-asm/src/konpeito/runtime/KHTTP.java +113 -0
  171. data/tools/konpeito-asm/src/konpeito/runtime/KHash.java +228 -0
  172. data/tools/konpeito-asm/src/konpeito/runtime/KJSON.java +405 -0
  173. data/tools/konpeito-asm/src/konpeito/runtime/KMath.java +54 -0
  174. data/tools/konpeito-asm/src/konpeito/runtime/KRactor.java +244 -0
  175. data/tools/konpeito-asm/src/konpeito/runtime/KRactorPort.java +53 -0
  176. data/tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java +49 -0
  177. data/tools/konpeito-asm/src/konpeito/runtime/KThread.java +49 -0
  178. data/tools/konpeito-asm/src/konpeito/runtime/KTime.java +53 -0
  179. data/tools/konpeito-asm/src/konpeito/runtime/RubyDispatch.java +416 -0
  180. metadata +267 -0
@@ -0,0 +1,220 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Konpeito
4
+ module Commands
5
+ # Build command - compiles Ruby source to native code
6
+ class BuildCommand < BaseCommand
7
+ def self.command_name
8
+ "build"
9
+ end
10
+
11
+ def self.description
12
+ "Compile Ruby source to native code (CRuby extension)"
13
+ end
14
+
15
+ def run
16
+ parse_options!
17
+
18
+ if args.empty?
19
+ error("No source file specified. Usage: konpeito build <source.rb>")
20
+ end
21
+
22
+ source_file = args.first
23
+
24
+ unless File.exist?(source_file)
25
+ error("File not found: #{source_file}")
26
+ end
27
+
28
+ compile(source_file)
29
+ end
30
+
31
+ protected
32
+
33
+ def default_options
34
+ {
35
+ output: config.build_output,
36
+ format: config.build_format,
37
+ verbose: false,
38
+ rbs_paths: config.rbs_paths.dup,
39
+ require_paths: config.require_paths.dup,
40
+ color: $stderr.tty?,
41
+ debug: config.debug?,
42
+ profile: config.profile?,
43
+ incremental: config.incremental?,
44
+ clean_cache: false,
45
+ inline_rbs: false,
46
+ target: :native,
47
+ run_after: false,
48
+ emit_ir: false,
49
+ classpath: nil,
50
+ library: false,
51
+ stats: false,
52
+ quiet: false
53
+ }
54
+ end
55
+
56
+ def setup_option_parser(opts)
57
+ opts.on("-o", "--output FILE", "Output file name") do |file|
58
+ options[:output] = file
59
+ end
60
+
61
+ opts.on("-f", "--format FORMAT", %i[cruby_ext standalone],
62
+ "Output format (cruby_ext, standalone)") do |format|
63
+ options[:format] = format
64
+ end
65
+
66
+ opts.on("-g", "--debug", "Generate debug info (DWARF) for lldb/gdb") do
67
+ options[:debug] = true
68
+ end
69
+
70
+ opts.on("-p", "--profile", "Enable profiling (function call counts and timing)") do
71
+ options[:profile] = true
72
+ end
73
+
74
+ opts.on("-I", "--require-path PATH", "Add require search path (can be used multiple times)") do |path|
75
+ options[:require_paths] << path
76
+ end
77
+
78
+ opts.on("--rbs FILE", "RBS type definition file (can be used multiple times)") do |file|
79
+ options[:rbs_paths] << file
80
+ end
81
+
82
+ opts.on("--incremental", "Enable incremental compilation (cache unchanged files)") do
83
+ options[:incremental] = true
84
+ end
85
+
86
+ opts.on("--clean-cache", "Clear compilation cache before building") do
87
+ options[:clean_cache] = true
88
+ end
89
+
90
+ opts.on("--inline", "Use inline RBS annotations (# @rbs, #:) from Ruby source") do
91
+ options[:inline_rbs] = true
92
+ end
93
+
94
+ opts.on("--target TARGET", %i[native jvm], "Target platform (native, jvm)") do |target|
95
+ options[:target] = target
96
+ end
97
+
98
+ opts.on("--run", "Run the compiled program after building") do
99
+ options[:run_after] = true
100
+ end
101
+
102
+ opts.on("--emit-ir", "Emit intermediate representation for debugging") do
103
+ options[:emit_ir] = true
104
+ end
105
+
106
+ opts.on("--classpath PATH", "Add external JARs/directories to classpath (colon-separated)") do |path|
107
+ options[:classpath] = path
108
+ end
109
+
110
+ opts.on("--lib", "Build as library JAR (no Main-Class, JVM target only)") do
111
+ options[:library] = true
112
+ end
113
+
114
+ opts.on("--stats", "Show optimization statistics") do
115
+ options[:stats] = true
116
+ end
117
+
118
+ opts.on("-q", "--quiet", "Suppress non-error output") do
119
+ options[:quiet] = true
120
+ end
121
+
122
+ super
123
+ end
124
+
125
+ def banner
126
+ <<~BANNER.chomp
127
+ Usage: konpeito build [options] <source.rb>
128
+
129
+ Examples:
130
+ konpeito build src/main.rb Compile to CRuby extension
131
+ konpeito build --target jvm src/main.rb Compile to JAR
132
+ konpeito build --stats src/main.rb Show optimization stats
133
+ konpeito build -g src/main.rb With debug info (DWARF)
134
+ konpeito build --run src/main.rb Build and run immediately
135
+ BANNER
136
+ end
137
+
138
+ private
139
+
140
+ def compile(source_file)
141
+ output_file = options[:output] || default_output_name(source_file, format: options[:format],
142
+ target: options[:target])
143
+
144
+ # Auto-add lib/*.jar to classpath for JVM target
145
+ classpath = options[:classpath]
146
+ if options[:target] == :jvm
147
+ lib_jars = Dir.glob("lib/*.jar")
148
+ unless lib_jars.empty?
149
+ lib_cp = lib_jars.join(Platform.classpath_separator)
150
+ classpath = classpath ? "#{classpath}#{Platform.classpath_separator}#{lib_cp}" : lib_cp
151
+ end
152
+ end
153
+
154
+ target_label = options[:target] == :jvm ? "jvm" : "native"
155
+ emit("Compiling", "#{source_file} (#{target_label})") unless options[:quiet]
156
+
157
+ compiler = Compiler.new(
158
+ source_file: source_file,
159
+ output_file: output_file,
160
+ format: options[:format],
161
+ verbose: options[:verbose],
162
+ rbs_paths: options[:rbs_paths],
163
+ require_paths: options[:require_paths],
164
+ debug: options[:debug],
165
+ profile: options[:profile],
166
+ incremental: options[:incremental],
167
+ clean_cache: options[:clean_cache],
168
+ inline_rbs: options[:inline_rbs],
169
+ target: options[:target],
170
+ run_after: options[:run_after],
171
+ emit_ir: options[:emit_ir],
172
+ classpath: classpath,
173
+ library: options[:library]
174
+ )
175
+
176
+ compiler.compile
177
+ display_diagnostics(compiler.diagnostics)
178
+
179
+ if compiler.diagnostics.any?(&:error?)
180
+ error_count = compiler.diagnostics.count(&:error?)
181
+ emit_error("Failed", "with #{error_count} error(s)") unless options[:quiet]
182
+ exit 1
183
+ end
184
+
185
+ unless options[:quiet]
186
+ stats = compiler.compile_stats
187
+
188
+ # Show optimization stats if --stats
189
+ if options[:stats] && stats
190
+ resolved_msg = "#{stats.resolved_files} file(s)"
191
+ resolved_msg += ", #{stats.rbs_count} RBS definition(s)" if stats.rbs_count > 0
192
+ emit("Resolved", resolved_msg)
193
+
194
+ opt_parts = []
195
+ opt_parts << "#{stats.specializations} specialization(s)" if stats.specializations > 0
196
+ opt_parts << "#{stats.inlined} inlined" if stats.inlined > 0
197
+ opt_parts << "#{stats.hoisted} hoisted" if stats.hoisted > 0
198
+ emit("Optimized", opt_parts.join(", ")) unless opt_parts.empty?
199
+ end
200
+
201
+ # Show finished line with timing and size
202
+ if stats
203
+ duration_str = "%.2fs" % stats.duration_s
204
+ size_str = File.exist?(output_file) ? " (#{format_size(File.size(output_file))})" : ""
205
+ emit("Finished", "in #{duration_str} -> #{output_file}#{size_str}")
206
+ end
207
+ end
208
+ rescue Konpeito::DependencyError => e
209
+ display_dependency_error(e)
210
+ exit 1
211
+ rescue Konpeito::ParseError => e
212
+ $stderr.puts "Error: #{e.message}"
213
+ exit 1
214
+ rescue Konpeito::Error => e
215
+ $stderr.puts "Error: #{e.message}"
216
+ exit 1
217
+ end
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Konpeito
4
+ module Commands
5
+ # Check command - type check only (no code generation)
6
+ class CheckCommand < BaseCommand
7
+ def self.command_name
8
+ "check"
9
+ end
10
+
11
+ def self.description
12
+ "Type check Ruby source (no code generation)"
13
+ end
14
+
15
+ def run
16
+ parse_options!
17
+
18
+ if args.empty?
19
+ error("No source file specified. Usage: konpeito check <source.rb>")
20
+ end
21
+
22
+ source_file = args.first
23
+
24
+ unless File.exist?(source_file)
25
+ error("File not found: #{source_file}")
26
+ end
27
+
28
+ type_check(source_file)
29
+ end
30
+
31
+ protected
32
+
33
+ def default_options
34
+ {
35
+ verbose: false,
36
+ rbs_paths: config.rbs_paths.dup,
37
+ require_paths: config.require_paths.dup,
38
+ color: $stderr.tty?
39
+ }
40
+ end
41
+
42
+ def setup_option_parser(opts)
43
+ opts.on("-I", "--require-path PATH", "Add require search path (can be used multiple times)") do |path|
44
+ options[:require_paths] << path
45
+ end
46
+
47
+ opts.on("--rbs FILE", "RBS type definition file (can be used multiple times)") do |file|
48
+ options[:rbs_paths] << file
49
+ end
50
+
51
+ super
52
+ end
53
+
54
+ def banner
55
+ <<~BANNER.chomp
56
+ Usage: konpeito check [options] <source.rb>
57
+
58
+ Examples:
59
+ konpeito check src/main.rb Type check source file
60
+ konpeito check --rbs sig/types.rbs src/main.rb With explicit RBS
61
+ BANNER
62
+ end
63
+
64
+ private
65
+
66
+ def type_check(source_file)
67
+ emit("Checking", source_file)
68
+
69
+ compiler = Compiler.new(
70
+ source_file: source_file,
71
+ output_file: nil,
72
+ format: :cruby_ext,
73
+ verbose: options[:verbose],
74
+ rbs_paths: options[:rbs_paths],
75
+ require_paths: options[:require_paths]
76
+ )
77
+
78
+ compiler.type_check
79
+ display_diagnostics(compiler.diagnostics)
80
+
81
+ if compiler.diagnostics.any?(&:error?)
82
+ error_count = compiler.diagnostics.count(&:error?)
83
+ emit_error("Failed", "with #{error_count} error(s)")
84
+ exit 1
85
+ end
86
+
87
+ stats = compiler.compile_stats
88
+ if stats
89
+ duration_str = "%.2fs" % stats.duration_s
90
+ emit("Finished", "type check in #{duration_str} (no errors)")
91
+ end
92
+ rescue Konpeito::DependencyError => e
93
+ display_dependency_error(e)
94
+ exit 1
95
+ rescue Konpeito::ParseError => e
96
+ $stderr.puts "Error: #{e.message}"
97
+ exit 1
98
+ rescue Konpeito::Error => e
99
+ $stderr.puts "Error: #{e.message}"
100
+ exit 1
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,231 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Konpeito
4
+ module Commands
5
+ # Configuration file loader for konpeito.toml
6
+ class Config
7
+ CONFIG_FILE_NAME = "konpeito.toml"
8
+
9
+ DEFAULT_CONFIG = {
10
+ "name" => nil,
11
+ "version" => "0.1.0",
12
+ "build" => {
13
+ "output" => nil,
14
+ "format" => "cruby_ext",
15
+ "target" => "native",
16
+ "rbs_paths" => [],
17
+ "require_paths" => [],
18
+ "debug" => false,
19
+ "profile" => false,
20
+ "incremental" => false
21
+ },
22
+ "jvm" => {
23
+ "classpath" => "",
24
+ "java_home" => "",
25
+ "library" => false,
26
+ "main_class" => ""
27
+ },
28
+ "deps" => {
29
+ "jars" => []
30
+ },
31
+ "test" => {
32
+ "pattern" => "test/**/*_test.rb"
33
+ },
34
+ "fmt" => {
35
+ "indent" => 2
36
+ },
37
+ "watch" => {
38
+ "paths" => ["src", "sig"],
39
+ "extensions" => ["rb", "rbs"]
40
+ }
41
+ }.freeze
42
+
43
+ attr_reader :config, :config_path
44
+
45
+ def initialize(base_dir = Dir.pwd)
46
+ @base_dir = base_dir
47
+ @config_path = find_config_file
48
+ @config = load_config
49
+ end
50
+
51
+ def [](key)
52
+ @config[key]
53
+ end
54
+
55
+ def dig(*keys)
56
+ @config.dig(*keys)
57
+ end
58
+
59
+ def project_name
60
+ @config["name"] || File.basename(@base_dir)
61
+ end
62
+
63
+ def build_output
64
+ @config.dig("build", "output")
65
+ end
66
+
67
+ def build_format
68
+ @config.dig("build", "format")&.to_sym || :cruby_ext
69
+ end
70
+
71
+ def rbs_paths
72
+ @config.dig("build", "rbs_paths") || []
73
+ end
74
+
75
+ def require_paths
76
+ @config.dig("build", "require_paths") || []
77
+ end
78
+
79
+ def debug?
80
+ @config.dig("build", "debug") || false
81
+ end
82
+
83
+ def profile?
84
+ @config.dig("build", "profile") || false
85
+ end
86
+
87
+ def incremental?
88
+ @config.dig("build", "incremental") || false
89
+ end
90
+
91
+ def target
92
+ (@config.dig("build", "target") || "native").to_sym
93
+ end
94
+
95
+ def jvm_classpath
96
+ @config.dig("jvm", "classpath") || ""
97
+ end
98
+
99
+ def jvm_java_home
100
+ @config.dig("jvm", "java_home") || ""
101
+ end
102
+
103
+ def jvm_library?
104
+ @config.dig("jvm", "library") || false
105
+ end
106
+
107
+ def jvm_main_class
108
+ @config.dig("jvm", "main_class") || ""
109
+ end
110
+
111
+ def deps_jars
112
+ @config.dig("deps", "jars") || []
113
+ end
114
+
115
+ def test_pattern
116
+ @config.dig("test", "pattern") || "test/**/*_test.rb"
117
+ end
118
+
119
+ def fmt_indent
120
+ @config.dig("fmt", "indent") || 2
121
+ end
122
+
123
+ def watch_paths
124
+ @config.dig("watch", "paths") || ["src", "sig"]
125
+ end
126
+
127
+ def watch_extensions
128
+ @config.dig("watch", "extensions") || ["rb", "rbs"]
129
+ end
130
+
131
+ def exists?
132
+ !@config_path.nil?
133
+ end
134
+
135
+ private
136
+
137
+ def find_config_file
138
+ dir = @base_dir
139
+ loop do
140
+ config_file = File.join(dir, CONFIG_FILE_NAME)
141
+ return config_file if File.exist?(config_file)
142
+
143
+ parent = File.dirname(dir)
144
+ break if parent == dir
145
+
146
+ dir = parent
147
+ end
148
+ nil
149
+ end
150
+
151
+ def load_config
152
+ return deep_dup(DEFAULT_CONFIG) unless @config_path
153
+
154
+ content = File.read(@config_path)
155
+ parsed = parse_toml(content)
156
+ deep_merge(deep_dup(DEFAULT_CONFIG), parsed)
157
+ end
158
+
159
+ # Simple TOML parser (supports basic key-value pairs and sections)
160
+ def parse_toml(content)
161
+ result = {}
162
+ current_section = result
163
+
164
+ content.each_line do |line|
165
+ line = line.strip
166
+
167
+ # Skip empty lines and comments
168
+ next if line.empty? || line.start_with?("#")
169
+
170
+ # Section header [section]
171
+ if line.match?(/^\[(.+)\]$/)
172
+ section_name = line[1..-2]
173
+ result[section_name] ||= {}
174
+ current_section = result[section_name]
175
+ # Key = value
176
+ elsif line.include?("=")
177
+ key, value = line.split("=", 2).map(&:strip)
178
+ current_section[key] = parse_value(value)
179
+ end
180
+ end
181
+
182
+ result
183
+ end
184
+
185
+ def parse_value(value)
186
+ case value
187
+ when /^"(.*)"$/, /^'(.*)'$/
188
+ # String
189
+ Regexp.last_match(1)
190
+ when /^\[(.*)?\]$/
191
+ # Array
192
+ content = Regexp.last_match(1)
193
+ return [] if content.nil? || content.strip.empty?
194
+
195
+ content.split(",").map { |v| parse_value(v.strip) }
196
+ when "true"
197
+ true
198
+ when "false"
199
+ false
200
+ when /^\d+$/
201
+ value.to_i
202
+ when /^\d+\.\d+$/
203
+ value.to_f
204
+ else
205
+ value
206
+ end
207
+ end
208
+
209
+ def deep_dup(obj)
210
+ case obj
211
+ when Hash
212
+ obj.transform_values { |v| deep_dup(v) }
213
+ when Array
214
+ obj.map { |v| deep_dup(v) }
215
+ else
216
+ obj.dup rescue obj
217
+ end
218
+ end
219
+
220
+ def deep_merge(base, override)
221
+ base.merge(override) do |_key, old_val, new_val|
222
+ if old_val.is_a?(Hash) && new_val.is_a?(Hash)
223
+ deep_merge(old_val, new_val)
224
+ else
225
+ new_val
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,128 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "fileutils"
5
+
6
+ module Konpeito
7
+ module Commands
8
+ # Deps command - download Maven dependencies
9
+ class DepsCommand < BaseCommand
10
+ MAVEN_CENTRAL_BASE = "https://repo1.maven.org/maven2"
11
+
12
+ def self.command_name
13
+ "deps"
14
+ end
15
+
16
+ def self.description
17
+ "Download JAR dependencies from Maven Central"
18
+ end
19
+
20
+ def run
21
+ parse_options!
22
+
23
+ jars = config.deps_jars
24
+
25
+ if jars.empty?
26
+ puts "No dependencies configured in konpeito.toml [deps] section."
27
+ puts ""
28
+ puts "Example:"
29
+ puts ' [deps]'
30
+ puts ' jars = ["com.google.code.gson:gson:2.10.1"]'
31
+ return
32
+ end
33
+
34
+ lib_dir = options[:output_dir]
35
+ FileUtils.mkdir_p(lib_dir)
36
+
37
+ jars.each do |spec|
38
+ download_jar(spec, lib_dir)
39
+ end
40
+
41
+ puts ""
42
+ puts "#{jars.size} dependency(ies) downloaded to #{lib_dir}/"
43
+ end
44
+
45
+ protected
46
+
47
+ def default_options
48
+ {
49
+ verbose: false,
50
+ color: $stderr.tty?,
51
+ output_dir: "lib"
52
+ }
53
+ end
54
+
55
+ def setup_option_parser(opts)
56
+ opts.on("-d", "--dir DIR", "Output directory (default: lib)") do |dir|
57
+ options[:output_dir] = dir
58
+ end
59
+
60
+ super
61
+ end
62
+
63
+ def banner
64
+ <<~BANNER.chomp
65
+ Usage: konpeito deps [options]
66
+
67
+ Examples:
68
+ konpeito deps Download all configured JARs
69
+ konpeito deps -d vendor/lib Download to custom directory
70
+ BANNER
71
+ end
72
+
73
+ private
74
+
75
+ def download_jar(spec, lib_dir)
76
+ parts = spec.split(":")
77
+ unless parts.size == 3
78
+ $stderr.puts "Invalid dependency format: #{spec} (expected group:artifact:version)"
79
+ return
80
+ end
81
+
82
+ group, artifact, version = parts
83
+ group_path = group.gsub(".", "/")
84
+ filename = "#{artifact}-#{version}.jar"
85
+ dest = File.join(lib_dir, filename)
86
+
87
+ if File.exist?(dest)
88
+ puts_verbose "Already downloaded: #{filename}"
89
+ return
90
+ end
91
+
92
+ url = "#{MAVEN_CENTRAL_BASE}/#{group_path}/#{artifact}/#{version}/#{filename}"
93
+ puts "Downloading #{spec}..."
94
+ puts_verbose " URL: #{url}"
95
+
96
+ begin
97
+ download_file(url, dest)
98
+ puts " -> #{dest}"
99
+ rescue => e
100
+ $stderr.puts " Failed to download #{spec}: #{e.message}"
101
+ FileUtils.rm_f(dest)
102
+ end
103
+ end
104
+
105
+ def download_file(url, dest)
106
+ uri = URI(url)
107
+ max_redirects = 5
108
+ redirects = 0
109
+
110
+ loop do
111
+ response = Net::HTTP.get_response(uri)
112
+
113
+ case response
114
+ when Net::HTTPSuccess
115
+ File.binwrite(dest, response.body)
116
+ return
117
+ when Net::HTTPRedirection
118
+ redirects += 1
119
+ raise "Too many redirects" if redirects > max_redirects
120
+ uri = URI(response["location"])
121
+ else
122
+ raise "HTTP #{response.code}: #{response.message}"
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end