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.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +75 -0
- data/CONTRIBUTING.md +123 -0
- data/LICENSE +21 -0
- data/README.md +257 -0
- data/Rakefile +11 -0
- data/bin/konpeito +6 -0
- data/konpeito.gemspec +43 -0
- data/lib/konpeito/ast/typed_ast.rb +620 -0
- data/lib/konpeito/ast/visitor.rb +78 -0
- data/lib/konpeito/cache/cache_manager.rb +230 -0
- data/lib/konpeito/cache/dependency_graph.rb +192 -0
- data/lib/konpeito/cache.rb +8 -0
- data/lib/konpeito/cli/base_command.rb +187 -0
- data/lib/konpeito/cli/build_command.rb +220 -0
- data/lib/konpeito/cli/check_command.rb +104 -0
- data/lib/konpeito/cli/config.rb +231 -0
- data/lib/konpeito/cli/deps_command.rb +128 -0
- data/lib/konpeito/cli/doctor_command.rb +340 -0
- data/lib/konpeito/cli/fmt_command.rb +199 -0
- data/lib/konpeito/cli/init_command.rb +312 -0
- data/lib/konpeito/cli/lsp_command.rb +40 -0
- data/lib/konpeito/cli/run_command.rb +150 -0
- data/lib/konpeito/cli/test_command.rb +248 -0
- data/lib/konpeito/cli/watch_command.rb +212 -0
- data/lib/konpeito/cli.rb +301 -0
- data/lib/konpeito/codegen/builtin_methods.rb +229 -0
- data/lib/konpeito/codegen/cruby_backend.rb +1090 -0
- data/lib/konpeito/codegen/debug_info.rb +352 -0
- data/lib/konpeito/codegen/inliner.rb +486 -0
- data/lib/konpeito/codegen/jvm_backend.rb +197 -0
- data/lib/konpeito/codegen/jvm_generator.rb +13412 -0
- data/lib/konpeito/codegen/llvm_generator.rb +13191 -0
- data/lib/konpeito/codegen/loop_optimizer.rb +363 -0
- data/lib/konpeito/codegen/monomorphizer.rb +359 -0
- data/lib/konpeito/codegen/profile_runtime.c +341 -0
- data/lib/konpeito/codegen/profiler.rb +99 -0
- data/lib/konpeito/compiler.rb +592 -0
- data/lib/konpeito/dependency_resolver.rb +296 -0
- data/lib/konpeito/diagnostics/collector.rb +127 -0
- data/lib/konpeito/diagnostics/diagnostic.rb +237 -0
- data/lib/konpeito/diagnostics/renderer.rb +144 -0
- data/lib/konpeito/formatter/formatter.rb +1214 -0
- data/lib/konpeito/hir/builder.rb +7167 -0
- data/lib/konpeito/hir/nodes.rb +2465 -0
- data/lib/konpeito/lsp/document_manager.rb +820 -0
- data/lib/konpeito/lsp/server.rb +183 -0
- data/lib/konpeito/lsp/transport.rb +38 -0
- data/lib/konpeito/parser/prism_adapter.rb +65 -0
- data/lib/konpeito/platform.rb +103 -0
- data/lib/konpeito/profile/report.rb +136 -0
- data/lib/konpeito/rbs_inline/preprocessor.rb +199 -0
- data/lib/konpeito/stdlib/compression/compression.rb +72 -0
- data/lib/konpeito/stdlib/compression/compression.rbs +60 -0
- data/lib/konpeito/stdlib/compression/compression_native.c +415 -0
- data/lib/konpeito/stdlib/compression/extconf.rb +19 -0
- data/lib/konpeito/stdlib/crypto/crypto.rb +85 -0
- data/lib/konpeito/stdlib/crypto/crypto.rbs +74 -0
- data/lib/konpeito/stdlib/crypto/crypto_native.c +312 -0
- data/lib/konpeito/stdlib/crypto/extconf.rb +40 -0
- data/lib/konpeito/stdlib/http/extconf.rb +19 -0
- data/lib/konpeito/stdlib/http/http.rb +125 -0
- data/lib/konpeito/stdlib/http/http.rbs +57 -0
- data/lib/konpeito/stdlib/http/http_native.c +440 -0
- data/lib/konpeito/stdlib/json/extconf.rb +17 -0
- data/lib/konpeito/stdlib/json/json.rb +44 -0
- data/lib/konpeito/stdlib/json/json.rbs +33 -0
- data/lib/konpeito/stdlib/json/json_native.c +286 -0
- data/lib/konpeito/stdlib/ui/extconf.rb +216 -0
- data/lib/konpeito/stdlib/ui/konpeito_ui_native.cpp +1625 -0
- data/lib/konpeito/stdlib/ui/konpeito_ui_native.h +162 -0
- data/lib/konpeito/stdlib/ui/ui.rb +318 -0
- data/lib/konpeito/stdlib/ui/ui.rbs +247 -0
- data/lib/konpeito/type_checker/annotation_parser.rb +67 -0
- data/lib/konpeito/type_checker/hm_inferrer.rb +2565 -0
- data/lib/konpeito/type_checker/inferrer.rb +565 -0
- data/lib/konpeito/type_checker/rbs_loader.rb +1621 -0
- data/lib/konpeito/type_checker/type_resolver.rb +276 -0
- data/lib/konpeito/type_checker/types.rb +1434 -0
- data/lib/konpeito/type_checker/unification.rb +323 -0
- data/lib/konpeito/ui/animation/animated_state.rb +80 -0
- data/lib/konpeito/ui/animation/easing.rb +59 -0
- data/lib/konpeito/ui/animation/value_tween.rb +66 -0
- data/lib/konpeito/ui/app.rb +379 -0
- data/lib/konpeito/ui/box.rb +38 -0
- data/lib/konpeito/ui/castella.rb +70 -0
- data/lib/konpeito/ui/castella_native.rb +76 -0
- data/lib/konpeito/ui/chart/area_chart.rb +305 -0
- data/lib/konpeito/ui/chart/bar_chart.rb +288 -0
- data/lib/konpeito/ui/chart/base_chart.rb +210 -0
- data/lib/konpeito/ui/chart/chart_helpers.rb +79 -0
- data/lib/konpeito/ui/chart/gauge_chart.rb +171 -0
- data/lib/konpeito/ui/chart/heatmap_chart.rb +222 -0
- data/lib/konpeito/ui/chart/line_chart.rb +289 -0
- data/lib/konpeito/ui/chart/pie_chart.rb +219 -0
- data/lib/konpeito/ui/chart/scales.rb +77 -0
- data/lib/konpeito/ui/chart/scatter_chart.rb +303 -0
- data/lib/konpeito/ui/chart/stacked_bar_chart.rb +276 -0
- data/lib/konpeito/ui/column.rb +271 -0
- data/lib/konpeito/ui/core.rb +2199 -0
- data/lib/konpeito/ui/dsl.rb +443 -0
- data/lib/konpeito/ui/frame.rb +171 -0
- data/lib/konpeito/ui/frame_native.rb +494 -0
- data/lib/konpeito/ui/markdown/ast.rb +124 -0
- data/lib/konpeito/ui/markdown/mermaid/layout.rb +387 -0
- data/lib/konpeito/ui/markdown/mermaid/models.rb +232 -0
- data/lib/konpeito/ui/markdown/mermaid/parser.rb +519 -0
- data/lib/konpeito/ui/markdown/mermaid/renderer.rb +336 -0
- data/lib/konpeito/ui/markdown/parser.rb +805 -0
- data/lib/konpeito/ui/markdown/renderer.rb +639 -0
- data/lib/konpeito/ui/markdown/theme.rb +165 -0
- data/lib/konpeito/ui/render_node.rb +260 -0
- data/lib/konpeito/ui/row.rb +207 -0
- data/lib/konpeito/ui/spacer.rb +18 -0
- data/lib/konpeito/ui/style.rb +799 -0
- data/lib/konpeito/ui/theme.rb +563 -0
- data/lib/konpeito/ui/themes/material.rb +35 -0
- data/lib/konpeito/ui/themes/tokyo_night.rb +6 -0
- data/lib/konpeito/ui/widgets/button.rb +103 -0
- data/lib/konpeito/ui/widgets/calendar.rb +1034 -0
- data/lib/konpeito/ui/widgets/checkbox.rb +119 -0
- data/lib/konpeito/ui/widgets/container.rb +91 -0
- data/lib/konpeito/ui/widgets/data_table.rb +667 -0
- data/lib/konpeito/ui/widgets/divider.rb +29 -0
- data/lib/konpeito/ui/widgets/image.rb +105 -0
- data/lib/konpeito/ui/widgets/input.rb +485 -0
- data/lib/konpeito/ui/widgets/markdown.rb +57 -0
- data/lib/konpeito/ui/widgets/modal.rb +163 -0
- data/lib/konpeito/ui/widgets/multiline_input.rb +968 -0
- data/lib/konpeito/ui/widgets/multiline_text.rb +180 -0
- data/lib/konpeito/ui/widgets/net_image.rb +100 -0
- data/lib/konpeito/ui/widgets/progress_bar.rb +70 -0
- data/lib/konpeito/ui/widgets/radio_buttons.rb +93 -0
- data/lib/konpeito/ui/widgets/slider.rb +133 -0
- data/lib/konpeito/ui/widgets/switch.rb +84 -0
- data/lib/konpeito/ui/widgets/tabs.rb +157 -0
- data/lib/konpeito/ui/widgets/text.rb +110 -0
- data/lib/konpeito/ui/widgets/tree.rb +426 -0
- data/lib/konpeito/version.rb +5 -0
- data/lib/konpeito.rb +109 -0
- data/test_native_array.rb +172 -0
- data/test_native_array_class.rb +197 -0
- data/test_native_class.rb +151 -0
- data/tools/konpeito-asm/build.sh +65 -0
- data/tools/konpeito-asm/lib/asm-9.7.1.jar +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KArray.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCompression.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCrypto.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KFile.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHTTP.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON$Parser.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KMath.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactor.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactorPort.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KThread.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KTime.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/RubyDispatch.class +0 -0
- data/tools/konpeito-asm/src/ClassIntrospector.java +312 -0
- data/tools/konpeito-asm/src/KonpeitoAssembler.java +659 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KArray.java +390 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KCompression.java +168 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java +48 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KCrypto.java +151 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KFile.java +100 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KHTTP.java +113 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KHash.java +228 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KJSON.java +405 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KMath.java +54 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KRactor.java +244 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KRactorPort.java +53 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java +49 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KThread.java +49 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KTime.java +53 -0
- data/tools/konpeito-asm/src/konpeito/runtime/RubyDispatch.java +416 -0
- 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
|