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,151 @@
1
+ package konpeito.runtime;
2
+
3
+ import java.nio.charset.StandardCharsets;
4
+ import java.security.MessageDigest;
5
+ import java.security.SecureRandom;
6
+ import javax.crypto.Mac;
7
+ import javax.crypto.spec.SecretKeySpec;
8
+
9
+ /**
10
+ * KCrypto - Cryptographic operations for Konpeito JVM backend.
11
+ * Maps to KonpeitoCrypto Ruby module.
12
+ * Uses java.security.* and javax.crypto.* (no external dependencies).
13
+ */
14
+ public class KCrypto {
15
+
16
+ private static final SecureRandom SECURE_RANDOM = new SecureRandom();
17
+ private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
18
+
19
+ // ========================================================================
20
+ // Hash Functions
21
+ // ========================================================================
22
+
23
+ /** KonpeitoCrypto.sha256(data) -> String (hex) */
24
+ public static String sha256(String data) {
25
+ return hexDigest("SHA-256", data);
26
+ }
27
+
28
+ /** KonpeitoCrypto.sha512(data) -> String (hex) */
29
+ public static String sha512(String data) {
30
+ return hexDigest("SHA-512", data);
31
+ }
32
+
33
+ /** KonpeitoCrypto.sha256_binary(data) -> String (binary via ISO-8859-1) */
34
+ public static String sha256Binary(String data) {
35
+ return binaryDigest("SHA-256", data);
36
+ }
37
+
38
+ /** KonpeitoCrypto.sha512_binary(data) -> String (binary via ISO-8859-1) */
39
+ public static String sha512Binary(String data) {
40
+ return binaryDigest("SHA-512", data);
41
+ }
42
+
43
+ // ========================================================================
44
+ // HMAC
45
+ // ========================================================================
46
+
47
+ /** KonpeitoCrypto.hmac_sha256(key, data) -> String (hex) */
48
+ public static String hmacSha256(String key, String data) {
49
+ return hexHmac("HmacSHA256", key, data);
50
+ }
51
+
52
+ /** KonpeitoCrypto.hmac_sha512(key, data) -> String (hex) */
53
+ public static String hmacSha512(String key, String data) {
54
+ return hexHmac("HmacSHA512", key, data);
55
+ }
56
+
57
+ /** KonpeitoCrypto.hmac_sha256_binary(key, data) -> String (binary) */
58
+ public static String hmacSha256Binary(String key, String data) {
59
+ return binaryHmac("HmacSHA256", key, data);
60
+ }
61
+
62
+ // ========================================================================
63
+ // Random
64
+ // ========================================================================
65
+
66
+ /** KonpeitoCrypto.random_bytes(count) -> String (binary via ISO-8859-1) */
67
+ public static String randomBytes(long count) {
68
+ byte[] bytes = new byte[(int) Math.min(count, Integer.MAX_VALUE)];
69
+ SECURE_RANDOM.nextBytes(bytes);
70
+ return new String(bytes, StandardCharsets.ISO_8859_1);
71
+ }
72
+
73
+ /** KonpeitoCrypto.random_hex(count) -> String (hex, 2*count chars) */
74
+ public static String randomHex(long count) {
75
+ byte[] bytes = new byte[(int) Math.min(count, Integer.MAX_VALUE)];
76
+ SECURE_RANDOM.nextBytes(bytes);
77
+ return bytesToHex(bytes);
78
+ }
79
+
80
+ // ========================================================================
81
+ // Comparison
82
+ // ========================================================================
83
+
84
+ /** KonpeitoCrypto.secure_compare(a, b) -> boolean (constant-time) */
85
+ public static boolean secureCompare(String a, String b) {
86
+ byte[] aBytes = a.getBytes(StandardCharsets.UTF_8);
87
+ byte[] bBytes = b.getBytes(StandardCharsets.UTF_8);
88
+ if (aBytes.length != bBytes.length) return false;
89
+ int result = 0;
90
+ for (int i = 0; i < aBytes.length; i++) {
91
+ result |= aBytes[i] ^ bBytes[i];
92
+ }
93
+ return result == 0;
94
+ }
95
+
96
+ // ========================================================================
97
+ // Internal Helpers
98
+ // ========================================================================
99
+
100
+ private static String hexDigest(String algorithm, String data) {
101
+ try {
102
+ MessageDigest md = MessageDigest.getInstance(algorithm);
103
+ byte[] hash = md.digest(data.getBytes(StandardCharsets.UTF_8));
104
+ return bytesToHex(hash);
105
+ } catch (Exception e) {
106
+ throw new RuntimeException("Digest failed: " + e.getMessage(), e);
107
+ }
108
+ }
109
+
110
+ private static String binaryDigest(String algorithm, String data) {
111
+ try {
112
+ MessageDigest md = MessageDigest.getInstance(algorithm);
113
+ byte[] hash = md.digest(data.getBytes(StandardCharsets.UTF_8));
114
+ return new String(hash, StandardCharsets.ISO_8859_1);
115
+ } catch (Exception e) {
116
+ throw new RuntimeException("Digest failed: " + e.getMessage(), e);
117
+ }
118
+ }
119
+
120
+ private static String hexHmac(String algorithm, String key, String data) {
121
+ try {
122
+ Mac mac = Mac.getInstance(algorithm);
123
+ mac.init(new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), algorithm));
124
+ byte[] hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
125
+ return bytesToHex(hash);
126
+ } catch (Exception e) {
127
+ throw new RuntimeException("HMAC failed: " + e.getMessage(), e);
128
+ }
129
+ }
130
+
131
+ private static String binaryHmac(String algorithm, String key, String data) {
132
+ try {
133
+ Mac mac = Mac.getInstance(algorithm);
134
+ mac.init(new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), algorithm));
135
+ byte[] hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
136
+ return new String(hash, StandardCharsets.ISO_8859_1);
137
+ } catch (Exception e) {
138
+ throw new RuntimeException("HMAC failed: " + e.getMessage(), e);
139
+ }
140
+ }
141
+
142
+ private static String bytesToHex(byte[] bytes) {
143
+ char[] hex = new char[bytes.length * 2];
144
+ for (int i = 0; i < bytes.length; i++) {
145
+ int v = bytes[i] & 0xFF;
146
+ hex[i * 2] = HEX_CHARS[v >>> 4];
147
+ hex[i * 2 + 1] = HEX_CHARS[v & 0x0F];
148
+ }
149
+ return new String(hex);
150
+ }
151
+ }
@@ -0,0 +1,100 @@
1
+ package konpeito.runtime;
2
+
3
+ import java.io.IOException;
4
+ import java.nio.charset.StandardCharsets;
5
+ import java.nio.file.Files;
6
+ import java.nio.file.Path;
7
+ import java.util.List;
8
+
9
+ /**
10
+ * KFile - File I/O operations for Konpeito JVM backend.
11
+ * Maps to KonpeitoFile Ruby module (JVM-only).
12
+ * Uses java.nio.file.* (Java 7+).
13
+ */
14
+ public class KFile {
15
+
16
+ /** KonpeitoFile.read(path) -> String */
17
+ public static String read(String path) {
18
+ try {
19
+ return Files.readString(Path.of(path), StandardCharsets.UTF_8);
20
+ } catch (IOException e) {
21
+ throw new RuntimeException("File read failed: " + e.getMessage(), e);
22
+ }
23
+ }
24
+
25
+ /** KonpeitoFile.write(path, content) -> String (content) */
26
+ public static String write(String path, String content) {
27
+ try {
28
+ Files.writeString(Path.of(path), content, StandardCharsets.UTF_8);
29
+ return content;
30
+ } catch (IOException e) {
31
+ throw new RuntimeException("File write failed: " + e.getMessage(), e);
32
+ }
33
+ }
34
+
35
+ /** KonpeitoFile.exist?(path) -> boolean */
36
+ public static boolean exists(String path) {
37
+ return Files.exists(Path.of(path));
38
+ }
39
+
40
+ /** KonpeitoFile.delete(path) -> boolean */
41
+ public static boolean delete(String path) {
42
+ try {
43
+ return Files.deleteIfExists(Path.of(path));
44
+ } catch (IOException e) {
45
+ throw new RuntimeException("File delete failed: " + e.getMessage(), e);
46
+ }
47
+ }
48
+
49
+ /** KonpeitoFile.size(path) -> long */
50
+ public static long size(String path) {
51
+ try {
52
+ return Files.size(Path.of(path));
53
+ } catch (IOException e) {
54
+ throw new RuntimeException("File size failed: " + e.getMessage(), e);
55
+ }
56
+ }
57
+
58
+ /** KonpeitoFile.readlines(path) -> KArray<String> */
59
+ public static KArray<String> readlines(String path) {
60
+ try {
61
+ List<String> lines = Files.readAllLines(Path.of(path), StandardCharsets.UTF_8);
62
+ KArray<String> result = new KArray<>();
63
+ for (String line : lines) {
64
+ result.add(line);
65
+ }
66
+ return result;
67
+ } catch (IOException e) {
68
+ throw new RuntimeException("File readlines failed: " + e.getMessage(), e);
69
+ }
70
+ }
71
+
72
+ /** KonpeitoFile.basename(path) -> String */
73
+ public static String basename(String path) {
74
+ Path p = Path.of(path).getFileName();
75
+ return p != null ? p.toString() : "";
76
+ }
77
+
78
+ /** KonpeitoFile.dirname(path) -> String */
79
+ public static String dirname(String path) {
80
+ Path p = Path.of(path).getParent();
81
+ return p != null ? p.toString() : ".";
82
+ }
83
+
84
+ /** KonpeitoFile.extname(path) -> String */
85
+ public static String extname(String path) {
86
+ String name = Path.of(path).getFileName().toString();
87
+ int dot = name.lastIndexOf('.');
88
+ return dot > 0 ? name.substring(dot) : "";
89
+ }
90
+
91
+ /** KonpeitoFile.mkdir(path) -> boolean */
92
+ public static boolean mkdir(String path) {
93
+ try {
94
+ Files.createDirectories(Path.of(path));
95
+ return true;
96
+ } catch (IOException e) {
97
+ throw new RuntimeException("mkdir failed: " + e.getMessage(), e);
98
+ }
99
+ }
100
+ }
@@ -0,0 +1,113 @@
1
+ package konpeito.runtime;
2
+
3
+ import java.net.URI;
4
+ import java.net.http.HttpClient;
5
+ import java.net.http.HttpRequest;
6
+ import java.net.http.HttpResponse;
7
+ import java.time.Duration;
8
+ import java.util.Map;
9
+
10
+ /**
11
+ * KHTTP - HTTP client for Konpeito JVM backend.
12
+ * Maps to KonpeitoHTTP Ruby module.
13
+ * Uses java.net.http.HttpClient (Java 11+).
14
+ */
15
+ public class KHTTP {
16
+ private static final HttpClient CLIENT = HttpClient.newBuilder()
17
+ .followRedirects(HttpClient.Redirect.NORMAL)
18
+ .connectTimeout(Duration.ofSeconds(30))
19
+ .build();
20
+
21
+ /** KonpeitoHTTP.get(url) -> String (body) */
22
+ public static String get(String url) {
23
+ try {
24
+ HttpRequest request = HttpRequest.newBuilder()
25
+ .uri(URI.create(url))
26
+ .timeout(Duration.ofSeconds(30))
27
+ .GET()
28
+ .build();
29
+ HttpResponse<String> response = CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
30
+ return response.body();
31
+ } catch (Exception e) {
32
+ throw new RuntimeException("HTTP GET failed: " + e.getMessage(), e);
33
+ }
34
+ }
35
+
36
+ /** KonpeitoHTTP.post(url, body) -> String (body) */
37
+ public static String post(String url, String body) {
38
+ try {
39
+ HttpRequest request = HttpRequest.newBuilder()
40
+ .uri(URI.create(url))
41
+ .timeout(Duration.ofSeconds(30))
42
+ .POST(HttpRequest.BodyPublishers.ofString(body != null ? body : ""))
43
+ .header("Content-Type", "application/json")
44
+ .build();
45
+ HttpResponse<String> response = CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
46
+ return response.body();
47
+ } catch (Exception e) {
48
+ throw new RuntimeException("HTTP POST failed: " + e.getMessage(), e);
49
+ }
50
+ }
51
+
52
+ /** KonpeitoHTTP.get_response(url) -> KHash {status, body, headers} */
53
+ @SuppressWarnings("unchecked")
54
+ public static KHash<String, Object> getResponse(String url) {
55
+ try {
56
+ HttpRequest request = HttpRequest.newBuilder()
57
+ .uri(URI.create(url))
58
+ .timeout(Duration.ofSeconds(30))
59
+ .GET()
60
+ .build();
61
+ HttpResponse<String> response = CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
62
+ return buildResponseHash(response);
63
+ } catch (Exception e) {
64
+ throw new RuntimeException("HTTP GET failed: " + e.getMessage(), e);
65
+ }
66
+ }
67
+
68
+ /** KonpeitoHTTP.request(method, url, body, headers) -> KHash */
69
+ @SuppressWarnings("unchecked")
70
+ public static KHash<String, Object> request(String method, String url, String body, KHash<String, String> headers) {
71
+ try {
72
+ HttpRequest.Builder builder = HttpRequest.newBuilder()
73
+ .uri(URI.create(url))
74
+ .timeout(Duration.ofSeconds(30));
75
+
76
+ // Set method and body
77
+ if (body != null && !body.isEmpty()) {
78
+ builder.method(method.toUpperCase(), HttpRequest.BodyPublishers.ofString(body));
79
+ } else {
80
+ builder.method(method.toUpperCase(), HttpRequest.BodyPublishers.noBody());
81
+ }
82
+
83
+ // Set custom headers
84
+ if (headers != null) {
85
+ for (Map.Entry<String, String> entry : headers.entrySet()) {
86
+ builder.header(entry.getKey(), entry.getValue());
87
+ }
88
+ }
89
+
90
+ HttpResponse<String> response = CLIENT.send(builder.build(), HttpResponse.BodyHandlers.ofString());
91
+ return buildResponseHash(response);
92
+ } catch (Exception e) {
93
+ throw new RuntimeException("HTTP request failed: " + e.getMessage(), e);
94
+ }
95
+ }
96
+
97
+ @SuppressWarnings("unchecked")
98
+ private static KHash<String, Object> buildResponseHash(HttpResponse<String> response) {
99
+ KHash<String, Object> result = new KHash<>();
100
+ result.put("status", (long) response.statusCode());
101
+ result.put("body", response.body());
102
+
103
+ // Build headers hash
104
+ KHash<String, Object> headersHash = new KHash<>();
105
+ response.headers().map().forEach((key, values) -> {
106
+ if (!values.isEmpty()) {
107
+ headersHash.put(key, values.get(0));
108
+ }
109
+ });
110
+ result.put("headers", headersHash);
111
+ return result;
112
+ }
113
+ }
@@ -0,0 +1,228 @@
1
+ package konpeito.runtime;
2
+
3
+ import java.util.*;
4
+
5
+ /**
6
+ * KHash - Ruby Hash implementation for JVM backend.
7
+ *
8
+ * Implements java.util.Map<K,V> via composition (wrapping LinkedHashMap<K,V>),
9
+ * providing both Java collection interop and Ruby-specific methods.
10
+ * LinkedHashMap preserves insertion order, matching Ruby Hash semantics.
11
+ *
12
+ * @param <K> Key type
13
+ * @param <V> Value type
14
+ */
15
+ public class KHash<K, V> implements Map<K, V> {
16
+ private final LinkedHashMap<K, V> data;
17
+
18
+ // ========================================================================
19
+ // Constructors
20
+ // ========================================================================
21
+
22
+ public KHash() {
23
+ this.data = new LinkedHashMap<>();
24
+ }
25
+
26
+ public KHash(Map<? extends K, ? extends V> m) {
27
+ this.data = new LinkedHashMap<>(m);
28
+ }
29
+
30
+ // ========================================================================
31
+ // Ruby-specific methods
32
+ // ========================================================================
33
+
34
+ /** Ruby: hash.deconstruct_keys(keys) — returns self (for pattern matching) */
35
+ @SuppressWarnings("unchecked")
36
+ public KHash<K, V> deconstruct_keys(Object keys) {
37
+ // In Ruby, deconstruct_keys returns a hash with only the specified keys.
38
+ // For simplicity, return self — the pattern matcher will check keys individually.
39
+ return this;
40
+ }
41
+
42
+ /** Ruby: hash.length / hash.size — returns long (Ruby Integer) */
43
+ public long length() {
44
+ return data.size();
45
+ }
46
+
47
+ /** Ruby: hash.has_key?(key) / hash.key?(key) */
48
+ public boolean hasKey(K key) {
49
+ return data.containsKey(key);
50
+ }
51
+
52
+ /** Ruby: hash.has_value?(value) / hash.value?(value) */
53
+ public boolean hasValue(V value) {
54
+ return data.containsValue(value);
55
+ }
56
+
57
+ /** Ruby: hash.keys — returns KArray */
58
+ public KArray<K> rubyKeys() {
59
+ return new KArray<>(data.keySet());
60
+ }
61
+
62
+ /** Ruby: hash.values — returns KArray */
63
+ public KArray<V> rubyValues() {
64
+ return new KArray<>(data.values());
65
+ }
66
+
67
+ /** Ruby: hash.empty? */
68
+ public boolean isEmpty_() {
69
+ return data.isEmpty();
70
+ }
71
+
72
+ /** Ruby: hash.fetch(key, default) */
73
+ public V fetch(K key, V defaultValue) {
74
+ V value = data.get(key);
75
+ return value != null ? value : defaultValue;
76
+ }
77
+
78
+ /** Ruby: hash.merge(other) — returns new hash */
79
+ public KHash<K, V> merge(KHash<K, V> other) {
80
+ KHash<K, V> result = new KHash<>(this.data);
81
+ result.data.putAll(other.data);
82
+ return result;
83
+ }
84
+
85
+ /** Ruby: hash.to_a — returns array of [key, value] pairs */
86
+ public KArray<KArray<Object>> toArray_() {
87
+ KArray<KArray<Object>> result = new KArray<>();
88
+ for (Map.Entry<K, V> entry : data.entrySet()) {
89
+ KArray<Object> pair = new KArray<>(2);
90
+ pair.add(entry.getKey());
91
+ pair.add(entry.getValue());
92
+ result.add(pair);
93
+ }
94
+ return result;
95
+ }
96
+
97
+ /** Ruby: hash.count (without block) */
98
+ public long count() {
99
+ return data.size();
100
+ }
101
+
102
+ /** Ruby: hash.merge!(other) / hash.update(other) — mutating merge */
103
+ @SuppressWarnings("unchecked")
104
+ public KHash<K, V> mergeInPlace(KHash<K, V> other) {
105
+ this.data.putAll(other.data);
106
+ return this;
107
+ }
108
+
109
+ /** Ruby: hash.each_key { |k| ... } support — returns keys as KArray */
110
+ public KArray<K> eachKeys() {
111
+ return new KArray<>(data.keySet());
112
+ }
113
+
114
+ /** Ruby: hash.each_value { |v| ... } support — returns values as KArray */
115
+ public KArray<V> eachValues() {
116
+ return new KArray<>(data.values());
117
+ }
118
+
119
+ // ========================================================================
120
+ // Map<K,V> interface delegation
121
+ // ========================================================================
122
+
123
+ @Override
124
+ public int size() {
125
+ return data.size();
126
+ }
127
+
128
+ @Override
129
+ public boolean isEmpty() {
130
+ return data.isEmpty();
131
+ }
132
+
133
+ @Override
134
+ public boolean containsKey(Object key) {
135
+ return data.containsKey(key);
136
+ }
137
+
138
+ @Override
139
+ public boolean containsValue(Object value) {
140
+ return data.containsValue(value);
141
+ }
142
+
143
+ @Override
144
+ public V get(Object key) {
145
+ return data.get(key);
146
+ }
147
+
148
+ @Override
149
+ public V put(K key, V value) {
150
+ return data.put(key, value);
151
+ }
152
+
153
+ @Override
154
+ public V remove(Object key) {
155
+ return data.remove(key);
156
+ }
157
+
158
+ @Override
159
+ public void putAll(Map<? extends K, ? extends V> m) {
160
+ data.putAll(m);
161
+ }
162
+
163
+ @Override
164
+ public void clear() {
165
+ data.clear();
166
+ }
167
+
168
+ @Override
169
+ public Set<K> keySet() {
170
+ return data.keySet();
171
+ }
172
+
173
+ @Override
174
+ public Collection<V> values() {
175
+ return data.values();
176
+ }
177
+
178
+ @Override
179
+ public Set<Map.Entry<K, V>> entrySet() {
180
+ return data.entrySet();
181
+ }
182
+
183
+ // ========================================================================
184
+ // Ruby-compatible toString: {"a" => 1, "b" => 2}
185
+ // ========================================================================
186
+
187
+ @Override
188
+ public String toString() {
189
+ StringBuilder sb = new StringBuilder("{");
190
+ boolean first = true;
191
+ for (Map.Entry<K, V> entry : data.entrySet()) {
192
+ if (!first) sb.append(", ");
193
+ first = false;
194
+ K key = entry.getKey();
195
+ V value = entry.getValue();
196
+ if (key instanceof String) {
197
+ sb.append("\"").append(key).append("\"");
198
+ } else {
199
+ sb.append(key);
200
+ }
201
+ sb.append(" => ");
202
+ if (value instanceof String) {
203
+ sb.append("\"").append(value).append("\"");
204
+ } else {
205
+ sb.append(value);
206
+ }
207
+ }
208
+ sb.append("}");
209
+ return sb.toString();
210
+ }
211
+
212
+ @Override
213
+ public boolean equals(Object o) {
214
+ if (this == o) return true;
215
+ if (o instanceof KHash) {
216
+ return data.equals(((KHash<?, ?>) o).data);
217
+ }
218
+ if (o instanceof Map) {
219
+ return data.equals(o);
220
+ }
221
+ return false;
222
+ }
223
+
224
+ @Override
225
+ public int hashCode() {
226
+ return data.hashCode();
227
+ }
228
+ }