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,405 @@
|
|
|
1
|
+
package konpeito.runtime;
|
|
2
|
+
|
|
3
|
+
import java.util.ArrayList;
|
|
4
|
+
import java.util.HashMap;
|
|
5
|
+
import java.util.List;
|
|
6
|
+
import java.util.Map;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* KJSON - Lightweight JSON parser and generator for Konpeito JVM backend.
|
|
10
|
+
* No external dependencies. Maps to KonpeitoJSON Ruby module.
|
|
11
|
+
*
|
|
12
|
+
* Supported types:
|
|
13
|
+
* JSON object -> HashMap<String, Object>
|
|
14
|
+
* JSON array -> ArrayList<Object>
|
|
15
|
+
* JSON string -> String
|
|
16
|
+
* JSON number -> Long (integers) or Double (decimals)
|
|
17
|
+
* JSON boolean -> Boolean
|
|
18
|
+
* JSON null -> null
|
|
19
|
+
*/
|
|
20
|
+
public class KJSON {
|
|
21
|
+
|
|
22
|
+
// ========================================================================
|
|
23
|
+
// Public API
|
|
24
|
+
// ========================================================================
|
|
25
|
+
|
|
26
|
+
/** KonpeitoJSON.parse(json_string) -> Object */
|
|
27
|
+
public static Object parse(String json) {
|
|
28
|
+
if (json == null || json.isEmpty()) {
|
|
29
|
+
throw new RuntimeException("Empty JSON input");
|
|
30
|
+
}
|
|
31
|
+
Parser parser = new Parser(json);
|
|
32
|
+
Object result = parser.parseValue();
|
|
33
|
+
parser.skipWhitespace();
|
|
34
|
+
if (parser.pos < parser.input.length()) {
|
|
35
|
+
throw new RuntimeException("Unexpected trailing content at position " + parser.pos);
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** KonpeitoJSON.generate(obj) -> String */
|
|
41
|
+
public static String generate(Object obj) {
|
|
42
|
+
StringBuilder sb = new StringBuilder();
|
|
43
|
+
writeValue(sb, obj, -1, 0);
|
|
44
|
+
return sb.toString();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** KonpeitoJSON.generate_pretty(obj, indent) -> String */
|
|
48
|
+
public static String generatePretty(Object obj, long indent) {
|
|
49
|
+
StringBuilder sb = new StringBuilder();
|
|
50
|
+
writeValue(sb, obj, (int) indent, 0);
|
|
51
|
+
sb.append('\n');
|
|
52
|
+
return sb.toString();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ========================================================================
|
|
56
|
+
// JSON Parser
|
|
57
|
+
// ========================================================================
|
|
58
|
+
|
|
59
|
+
private static class Parser {
|
|
60
|
+
final String input;
|
|
61
|
+
int pos;
|
|
62
|
+
|
|
63
|
+
Parser(String input) {
|
|
64
|
+
this.input = input;
|
|
65
|
+
this.pos = 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
Object parseValue() {
|
|
69
|
+
skipWhitespace();
|
|
70
|
+
if (pos >= input.length()) {
|
|
71
|
+
throw new RuntimeException("Unexpected end of JSON");
|
|
72
|
+
}
|
|
73
|
+
char c = input.charAt(pos);
|
|
74
|
+
switch (c) {
|
|
75
|
+
case '"': return parseString();
|
|
76
|
+
case '{': return parseObject();
|
|
77
|
+
case '[': return parseArray();
|
|
78
|
+
case 't': return parseLiteral("true", Boolean.TRUE);
|
|
79
|
+
case 'f': return parseLiteral("false", Boolean.FALSE);
|
|
80
|
+
case 'n': return parseLiteral("null", null);
|
|
81
|
+
default:
|
|
82
|
+
if (c == '-' || (c >= '0' && c <= '9')) {
|
|
83
|
+
return parseNumber();
|
|
84
|
+
}
|
|
85
|
+
throw new RuntimeException("Unexpected character '" + c + "' at position " + pos);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
String parseString() {
|
|
90
|
+
expect('"');
|
|
91
|
+
StringBuilder sb = new StringBuilder();
|
|
92
|
+
while (pos < input.length()) {
|
|
93
|
+
char c = input.charAt(pos++);
|
|
94
|
+
if (c == '"') return sb.toString();
|
|
95
|
+
if (c == '\\') {
|
|
96
|
+
if (pos >= input.length()) throw new RuntimeException("Unexpected end of string escape");
|
|
97
|
+
char esc = input.charAt(pos++);
|
|
98
|
+
switch (esc) {
|
|
99
|
+
case '"': sb.append('"'); break;
|
|
100
|
+
case '\\': sb.append('\\'); break;
|
|
101
|
+
case '/': sb.append('/'); break;
|
|
102
|
+
case 'b': sb.append('\b'); break;
|
|
103
|
+
case 'f': sb.append('\f'); break;
|
|
104
|
+
case 'n': sb.append('\n'); break;
|
|
105
|
+
case 'r': sb.append('\r'); break;
|
|
106
|
+
case 't': sb.append('\t'); break;
|
|
107
|
+
case 'u':
|
|
108
|
+
if (pos + 4 > input.length()) throw new RuntimeException("Invalid unicode escape");
|
|
109
|
+
String hex = input.substring(pos, pos + 4);
|
|
110
|
+
sb.append((char) Integer.parseInt(hex, 16));
|
|
111
|
+
pos += 4;
|
|
112
|
+
break;
|
|
113
|
+
default:
|
|
114
|
+
throw new RuntimeException("Invalid escape character: \\" + esc);
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
sb.append(c);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
throw new RuntimeException("Unterminated string");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
HashMap<String, Object> parseObject() {
|
|
124
|
+
expect('{');
|
|
125
|
+
HashMap<String, Object> map = new HashMap<>();
|
|
126
|
+
skipWhitespace();
|
|
127
|
+
if (pos < input.length() && input.charAt(pos) == '}') {
|
|
128
|
+
pos++;
|
|
129
|
+
return map;
|
|
130
|
+
}
|
|
131
|
+
while (true) {
|
|
132
|
+
skipWhitespace();
|
|
133
|
+
String key = parseString();
|
|
134
|
+
skipWhitespace();
|
|
135
|
+
expect(':');
|
|
136
|
+
Object value = parseValue();
|
|
137
|
+
map.put(key, value);
|
|
138
|
+
skipWhitespace();
|
|
139
|
+
if (pos >= input.length()) throw new RuntimeException("Unterminated object");
|
|
140
|
+
char c = input.charAt(pos++);
|
|
141
|
+
if (c == '}') return map;
|
|
142
|
+
if (c != ',') throw new RuntimeException("Expected ',' or '}' in object at position " + (pos - 1));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
ArrayList<Object> parseArray() {
|
|
147
|
+
expect('[');
|
|
148
|
+
ArrayList<Object> list = new ArrayList<>();
|
|
149
|
+
skipWhitespace();
|
|
150
|
+
if (pos < input.length() && input.charAt(pos) == ']') {
|
|
151
|
+
pos++;
|
|
152
|
+
return list;
|
|
153
|
+
}
|
|
154
|
+
while (true) {
|
|
155
|
+
list.add(parseValue());
|
|
156
|
+
skipWhitespace();
|
|
157
|
+
if (pos >= input.length()) throw new RuntimeException("Unterminated array");
|
|
158
|
+
char c = input.charAt(pos++);
|
|
159
|
+
if (c == ']') return list;
|
|
160
|
+
if (c != ',') throw new RuntimeException("Expected ',' or ']' in array at position " + (pos - 1));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
Object parseNumber() {
|
|
165
|
+
int start = pos;
|
|
166
|
+
if (pos < input.length() && input.charAt(pos) == '-') pos++;
|
|
167
|
+
if (pos >= input.length()) throw new RuntimeException("Invalid number");
|
|
168
|
+
|
|
169
|
+
// Integer part
|
|
170
|
+
if (input.charAt(pos) == '0') {
|
|
171
|
+
pos++;
|
|
172
|
+
} else if (input.charAt(pos) >= '1' && input.charAt(pos) <= '9') {
|
|
173
|
+
while (pos < input.length() && input.charAt(pos) >= '0' && input.charAt(pos) <= '9') pos++;
|
|
174
|
+
} else {
|
|
175
|
+
throw new RuntimeException("Invalid number at position " + pos);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
boolean isFloat = false;
|
|
179
|
+
// Fractional part
|
|
180
|
+
if (pos < input.length() && input.charAt(pos) == '.') {
|
|
181
|
+
isFloat = true;
|
|
182
|
+
pos++;
|
|
183
|
+
if (pos >= input.length() || input.charAt(pos) < '0' || input.charAt(pos) > '9') {
|
|
184
|
+
throw new RuntimeException("Invalid number: no digits after decimal point");
|
|
185
|
+
}
|
|
186
|
+
while (pos < input.length() && input.charAt(pos) >= '0' && input.charAt(pos) <= '9') pos++;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Exponent part
|
|
190
|
+
if (pos < input.length() && (input.charAt(pos) == 'e' || input.charAt(pos) == 'E')) {
|
|
191
|
+
isFloat = true;
|
|
192
|
+
pos++;
|
|
193
|
+
if (pos < input.length() && (input.charAt(pos) == '+' || input.charAt(pos) == '-')) pos++;
|
|
194
|
+
if (pos >= input.length() || input.charAt(pos) < '0' || input.charAt(pos) > '9') {
|
|
195
|
+
throw new RuntimeException("Invalid number: no digits in exponent");
|
|
196
|
+
}
|
|
197
|
+
while (pos < input.length() && input.charAt(pos) >= '0' && input.charAt(pos) <= '9') pos++;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
String numStr = input.substring(start, pos);
|
|
201
|
+
if (isFloat) {
|
|
202
|
+
return Double.parseDouble(numStr);
|
|
203
|
+
} else {
|
|
204
|
+
try {
|
|
205
|
+
return Long.parseLong(numStr);
|
|
206
|
+
} catch (NumberFormatException e) {
|
|
207
|
+
return Double.parseDouble(numStr); // Fallback for very large integers
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
Object parseLiteral(String expected, Object value) {
|
|
213
|
+
if (!input.startsWith(expected, pos)) {
|
|
214
|
+
throw new RuntimeException("Expected '" + expected + "' at position " + pos);
|
|
215
|
+
}
|
|
216
|
+
pos += expected.length();
|
|
217
|
+
return value;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
void expect(char c) {
|
|
221
|
+
if (pos >= input.length() || input.charAt(pos) != c) {
|
|
222
|
+
throw new RuntimeException("Expected '" + c + "' at position " + pos);
|
|
223
|
+
}
|
|
224
|
+
pos++;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
void skipWhitespace() {
|
|
228
|
+
while (pos < input.length()) {
|
|
229
|
+
char c = input.charAt(pos);
|
|
230
|
+
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
|
|
231
|
+
pos++;
|
|
232
|
+
} else {
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ========================================================================
|
|
240
|
+
// JSON Generator
|
|
241
|
+
// ========================================================================
|
|
242
|
+
|
|
243
|
+
@SuppressWarnings("unchecked")
|
|
244
|
+
private static void writeValue(StringBuilder sb, Object value, int indent, int depth) {
|
|
245
|
+
if (value == null) {
|
|
246
|
+
sb.append("null");
|
|
247
|
+
} else if (value instanceof String) {
|
|
248
|
+
writeString(sb, (String) value);
|
|
249
|
+
} else if (value instanceof Long) {
|
|
250
|
+
sb.append(((Long) value).longValue());
|
|
251
|
+
} else if (value instanceof Integer) {
|
|
252
|
+
sb.append(((Integer) value).intValue());
|
|
253
|
+
} else if (value instanceof Double) {
|
|
254
|
+
double d = (Double) value;
|
|
255
|
+
if (d == Math.floor(d) && !Double.isInfinite(d) && Math.abs(d) < 1e15) {
|
|
256
|
+
sb.append(String.format("%.1f", d));
|
|
257
|
+
} else {
|
|
258
|
+
sb.append(Double.toString(d));
|
|
259
|
+
}
|
|
260
|
+
} else if (value instanceof Float) {
|
|
261
|
+
sb.append(Float.toString((Float) value));
|
|
262
|
+
} else if (value instanceof Boolean) {
|
|
263
|
+
sb.append(((Boolean) value).booleanValue() ? "true" : "false");
|
|
264
|
+
} else if (value instanceof Map) {
|
|
265
|
+
writeObject(sb, (Map<String, Object>) value, indent, depth);
|
|
266
|
+
} else if (value instanceof List) {
|
|
267
|
+
writeArray(sb, (List<Object>) value, indent, depth);
|
|
268
|
+
} else if (value instanceof KArray) {
|
|
269
|
+
writeKArray(sb, (KArray) value, indent, depth);
|
|
270
|
+
} else if (value instanceof KHash) {
|
|
271
|
+
writeKHash(sb, (KHash) value, indent, depth);
|
|
272
|
+
} else {
|
|
273
|
+
writeString(sb, value.toString());
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private static void writeString(StringBuilder sb, String s) {
|
|
278
|
+
sb.append('"');
|
|
279
|
+
for (int i = 0; i < s.length(); i++) {
|
|
280
|
+
char c = s.charAt(i);
|
|
281
|
+
switch (c) {
|
|
282
|
+
case '"': sb.append("\\\""); break;
|
|
283
|
+
case '\\': sb.append("\\\\"); break;
|
|
284
|
+
case '\b': sb.append("\\b"); break;
|
|
285
|
+
case '\f': sb.append("\\f"); break;
|
|
286
|
+
case '\n': sb.append("\\n"); break;
|
|
287
|
+
case '\r': sb.append("\\r"); break;
|
|
288
|
+
case '\t': sb.append("\\t"); break;
|
|
289
|
+
default:
|
|
290
|
+
if (c < 0x20) {
|
|
291
|
+
sb.append(String.format("\\u%04x", (int) c));
|
|
292
|
+
} else {
|
|
293
|
+
sb.append(c);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
sb.append('"');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private static void writeObject(StringBuilder sb, Map<String, Object> map, int indent, int depth) {
|
|
301
|
+
if (map.isEmpty()) {
|
|
302
|
+
sb.append("{}");
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
boolean pretty = indent > 0;
|
|
306
|
+
sb.append('{');
|
|
307
|
+
boolean first = true;
|
|
308
|
+
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
|
309
|
+
if (!first) sb.append(',');
|
|
310
|
+
first = false;
|
|
311
|
+
if (pretty) {
|
|
312
|
+
sb.append('\n');
|
|
313
|
+
writeIndent(sb, indent, depth + 1);
|
|
314
|
+
}
|
|
315
|
+
writeString(sb, entry.getKey());
|
|
316
|
+
sb.append(pretty ? ": " : ":");
|
|
317
|
+
writeValue(sb, entry.getValue(), indent, depth + 1);
|
|
318
|
+
}
|
|
319
|
+
if (pretty) {
|
|
320
|
+
sb.append('\n');
|
|
321
|
+
writeIndent(sb, indent, depth);
|
|
322
|
+
}
|
|
323
|
+
sb.append('}');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private static void writeArray(StringBuilder sb, List<Object> list, int indent, int depth) {
|
|
327
|
+
if (list.isEmpty()) {
|
|
328
|
+
sb.append("[]");
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
boolean pretty = indent > 0;
|
|
332
|
+
sb.append('[');
|
|
333
|
+
for (int i = 0; i < list.size(); i++) {
|
|
334
|
+
if (i > 0) sb.append(',');
|
|
335
|
+
if (pretty) {
|
|
336
|
+
sb.append('\n');
|
|
337
|
+
writeIndent(sb, indent, depth + 1);
|
|
338
|
+
}
|
|
339
|
+
writeValue(sb, list.get(i), indent, depth + 1);
|
|
340
|
+
}
|
|
341
|
+
if (pretty) {
|
|
342
|
+
sb.append('\n');
|
|
343
|
+
writeIndent(sb, indent, depth);
|
|
344
|
+
}
|
|
345
|
+
sb.append(']');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
private static void writeKArray(StringBuilder sb, KArray arr, int indent, int depth) {
|
|
349
|
+
int size = (int) arr.length();
|
|
350
|
+
if (size == 0) {
|
|
351
|
+
sb.append("[]");
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
boolean pretty = indent > 0;
|
|
355
|
+
sb.append('[');
|
|
356
|
+
for (int i = 0; i < size; i++) {
|
|
357
|
+
if (i > 0) sb.append(',');
|
|
358
|
+
if (pretty) {
|
|
359
|
+
sb.append('\n');
|
|
360
|
+
writeIndent(sb, indent, depth + 1);
|
|
361
|
+
}
|
|
362
|
+
writeValue(sb, arr.get(i), indent, depth + 1);
|
|
363
|
+
}
|
|
364
|
+
if (pretty) {
|
|
365
|
+
sb.append('\n');
|
|
366
|
+
writeIndent(sb, indent, depth);
|
|
367
|
+
}
|
|
368
|
+
sb.append(']');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
@SuppressWarnings("unchecked")
|
|
372
|
+
private static void writeKHash(StringBuilder sb, KHash hash, int indent, int depth) {
|
|
373
|
+
KArray keys = hash.rubyKeys();
|
|
374
|
+
int size = (int) keys.length();
|
|
375
|
+
if (size == 0) {
|
|
376
|
+
sb.append("{}");
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
boolean pretty = indent > 0;
|
|
380
|
+
sb.append('{');
|
|
381
|
+
for (int i = 0; i < size; i++) {
|
|
382
|
+
if (i > 0) sb.append(',');
|
|
383
|
+
if (pretty) {
|
|
384
|
+
sb.append('\n');
|
|
385
|
+
writeIndent(sb, indent, depth + 1);
|
|
386
|
+
}
|
|
387
|
+
Object key = keys.get(i);
|
|
388
|
+
writeString(sb, key != null ? key.toString() : "null");
|
|
389
|
+
sb.append(pretty ? ": " : ":");
|
|
390
|
+
writeValue(sb, hash.get(key), indent, depth + 1);
|
|
391
|
+
}
|
|
392
|
+
if (pretty) {
|
|
393
|
+
sb.append('\n');
|
|
394
|
+
writeIndent(sb, indent, depth);
|
|
395
|
+
}
|
|
396
|
+
sb.append('}');
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
private static void writeIndent(StringBuilder sb, int indent, int depth) {
|
|
400
|
+
int spaces = indent * depth;
|
|
401
|
+
for (int i = 0; i < spaces; i++) {
|
|
402
|
+
sb.append(' ');
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
package konpeito.runtime;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* KMath - Math functions for Konpeito JVM backend.
|
|
5
|
+
* Maps to KonpeitoMath Ruby module (JVM-only).
|
|
6
|
+
* Uses java.lang.Math.
|
|
7
|
+
*/
|
|
8
|
+
public class KMath {
|
|
9
|
+
|
|
10
|
+
/** KonpeitoMath.sqrt(x) -> double */
|
|
11
|
+
public static double sqrt(double x) { return Math.sqrt(x); }
|
|
12
|
+
|
|
13
|
+
/** KonpeitoMath.sin(x) -> double */
|
|
14
|
+
public static double sin(double x) { return Math.sin(x); }
|
|
15
|
+
|
|
16
|
+
/** KonpeitoMath.cos(x) -> double */
|
|
17
|
+
public static double cos(double x) { return Math.cos(x); }
|
|
18
|
+
|
|
19
|
+
/** KonpeitoMath.tan(x) -> double */
|
|
20
|
+
public static double tan(double x) { return Math.tan(x); }
|
|
21
|
+
|
|
22
|
+
/** KonpeitoMath.log(x) -> double */
|
|
23
|
+
public static double log(double x) { return Math.log(x); }
|
|
24
|
+
|
|
25
|
+
/** KonpeitoMath.log10(x) -> double */
|
|
26
|
+
public static double log10(double x) { return Math.log10(x); }
|
|
27
|
+
|
|
28
|
+
/** KonpeitoMath.pow(x, y) -> double */
|
|
29
|
+
public static double pow(double x, double y) { return Math.pow(x, y); }
|
|
30
|
+
|
|
31
|
+
/** KonpeitoMath.pi -> double */
|
|
32
|
+
public static double pi() { return Math.PI; }
|
|
33
|
+
|
|
34
|
+
/** KonpeitoMath.e -> double */
|
|
35
|
+
public static double e() { return Math.E; }
|
|
36
|
+
|
|
37
|
+
/** KonpeitoMath.abs(x) -> double */
|
|
38
|
+
public static double abs(double x) { return Math.abs(x); }
|
|
39
|
+
|
|
40
|
+
/** KonpeitoMath.floor(x) -> double */
|
|
41
|
+
public static double floor(double x) { return Math.floor(x); }
|
|
42
|
+
|
|
43
|
+
/** KonpeitoMath.ceil(x) -> double */
|
|
44
|
+
public static double ceil(double x) { return Math.ceil(x); }
|
|
45
|
+
|
|
46
|
+
/** KonpeitoMath.round(x) -> long */
|
|
47
|
+
public static long round(double x) { return Math.round(x); }
|
|
48
|
+
|
|
49
|
+
/** KonpeitoMath.min(a, b) -> double */
|
|
50
|
+
public static double min(double a, double b) { return Math.min(a, b); }
|
|
51
|
+
|
|
52
|
+
/** KonpeitoMath.max(a, b) -> double */
|
|
53
|
+
public static double max(double a, double b) { return Math.max(a, b); }
|
|
54
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
package konpeito.runtime;
|
|
2
|
+
|
|
3
|
+
import java.util.concurrent.Callable;
|
|
4
|
+
import java.util.concurrent.ConcurrentHashMap;
|
|
5
|
+
import java.util.concurrent.CopyOnWriteArrayList;
|
|
6
|
+
import java.util.concurrent.LinkedBlockingQueue;
|
|
7
|
+
import java.util.concurrent.TimeUnit;
|
|
8
|
+
import java.util.List;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* KRactor - Ruby 4.0 Ractor implementation for JVM backend using Virtual Threads.
|
|
12
|
+
*
|
|
13
|
+
* Each Ractor = Virtual Thread + LinkedBlockingQueue (default port).
|
|
14
|
+
* No true isolation enforcement on JVM (objects shared by reference).
|
|
15
|
+
*/
|
|
16
|
+
public class KRactor {
|
|
17
|
+
private final Thread thread;
|
|
18
|
+
private final LinkedBlockingQueue<Object> defaultPort;
|
|
19
|
+
private volatile Object result;
|
|
20
|
+
private volatile Throwable error;
|
|
21
|
+
private volatile boolean closed;
|
|
22
|
+
private volatile boolean finished;
|
|
23
|
+
private final String name;
|
|
24
|
+
private final ConcurrentHashMap<String, Object> localStorage;
|
|
25
|
+
private final CopyOnWriteArrayList<KRactorPort> monitors;
|
|
26
|
+
|
|
27
|
+
private static final ThreadLocal<KRactor> CURRENT = new ThreadLocal<>();
|
|
28
|
+
private static final KRactor MAIN_RACTOR = new KRactor();
|
|
29
|
+
|
|
30
|
+
/** Private constructor for MAIN_RACTOR sentinel */
|
|
31
|
+
private KRactor() {
|
|
32
|
+
this.thread = null;
|
|
33
|
+
this.defaultPort = new LinkedBlockingQueue<>();
|
|
34
|
+
this.closed = false;
|
|
35
|
+
this.finished = false;
|
|
36
|
+
this.name = "main";
|
|
37
|
+
this.localStorage = new ConcurrentHashMap<>();
|
|
38
|
+
this.monitors = new CopyOnWriteArrayList<>();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Ruby: Ractor.new { block } */
|
|
42
|
+
public KRactor(Callable<Object> task) {
|
|
43
|
+
this(task, null);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Ruby: Ractor.new(name: "worker") { block } */
|
|
47
|
+
public KRactor(Callable<Object> task, String name) {
|
|
48
|
+
this.defaultPort = new LinkedBlockingQueue<>();
|
|
49
|
+
this.closed = false;
|
|
50
|
+
this.finished = false;
|
|
51
|
+
this.name = name;
|
|
52
|
+
this.localStorage = new ConcurrentHashMap<>();
|
|
53
|
+
this.monitors = new CopyOnWriteArrayList<>();
|
|
54
|
+
Thread.Builder.OfVirtual builder = Thread.ofVirtual();
|
|
55
|
+
if (name != null) {
|
|
56
|
+
builder = builder.name(name);
|
|
57
|
+
}
|
|
58
|
+
this.thread = builder.start(() -> {
|
|
59
|
+
CURRENT.set(this);
|
|
60
|
+
try {
|
|
61
|
+
result = task.call();
|
|
62
|
+
} catch (Throwable t) {
|
|
63
|
+
error = t;
|
|
64
|
+
} finally {
|
|
65
|
+
finished = true;
|
|
66
|
+
notifyMonitors();
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Ruby: ractor.send(msg) or ractor << msg */
|
|
72
|
+
public void send(Object msg) {
|
|
73
|
+
if (closed) {
|
|
74
|
+
throw new RuntimeException("can't send to a closed Ractor");
|
|
75
|
+
}
|
|
76
|
+
defaultPort.offer(msg);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Ruby: Ractor.receive -- receive on current Ractor's default port */
|
|
80
|
+
public static Object receiveOnCurrent() {
|
|
81
|
+
KRactor current = CURRENT.get();
|
|
82
|
+
if (current == null) {
|
|
83
|
+
current = MAIN_RACTOR;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
return current.defaultPort.take();
|
|
87
|
+
} catch (InterruptedException e) {
|
|
88
|
+
Thread.currentThread().interrupt();
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Ruby: ractor.join -- wait for Ractor completion */
|
|
94
|
+
public KRactor join() {
|
|
95
|
+
if (thread != null) {
|
|
96
|
+
try {
|
|
97
|
+
thread.join();
|
|
98
|
+
} catch (InterruptedException e) {
|
|
99
|
+
Thread.currentThread().interrupt();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Ruby: ractor.value -- wait and return result */
|
|
106
|
+
public Object getValue() {
|
|
107
|
+
join();
|
|
108
|
+
if (error != null) {
|
|
109
|
+
throw new RuntimeException(error);
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Ruby: ractor.close */
|
|
115
|
+
public void close() {
|
|
116
|
+
closed = true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Ruby: Ractor.current */
|
|
120
|
+
public static KRactor current() {
|
|
121
|
+
KRactor c = CURRENT.get();
|
|
122
|
+
return c != null ? c : MAIN_RACTOR;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Ruby: Ractor.main */
|
|
126
|
+
public static KRactor main() {
|
|
127
|
+
return MAIN_RACTOR;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Ruby: ractor.name */
|
|
131
|
+
public String getName() {
|
|
132
|
+
return name;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ========================================
|
|
136
|
+
// Ractor-local storage
|
|
137
|
+
// ========================================
|
|
138
|
+
|
|
139
|
+
/** Ruby: Ractor[:key] */
|
|
140
|
+
public Object getLocal(String key) {
|
|
141
|
+
return localStorage.get(key);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Ruby: Ractor[:key] = value */
|
|
145
|
+
public Object setLocal(String key, Object value) {
|
|
146
|
+
localStorage.put(key, value);
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ========================================
|
|
151
|
+
// Shareable API (compatibility stubs)
|
|
152
|
+
// ========================================
|
|
153
|
+
|
|
154
|
+
/** Ruby: Ractor.make_shareable(obj) -- on JVM, all objects are shared by reference */
|
|
155
|
+
public static Object makeSharable(Object obj) {
|
|
156
|
+
return obj;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Ruby: Ractor.shareable?(obj) -- on JVM, all objects are shareable */
|
|
160
|
+
public static boolean isSharable(Object obj) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ========================================
|
|
165
|
+
// Monitor API (death notification)
|
|
166
|
+
// ========================================
|
|
167
|
+
|
|
168
|
+
/** Ruby: ractor.monitor(port) -- register for death notification */
|
|
169
|
+
public void monitor(KRactorPort port) {
|
|
170
|
+
monitors.add(port);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Ruby: ractor.unmonitor(port) -- unregister from death notification */
|
|
174
|
+
public void unmonitor(KRactorPort port) {
|
|
175
|
+
monitors.remove(port);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Notify all monitors when Ractor terminates */
|
|
179
|
+
private void notifyMonitors() {
|
|
180
|
+
Object reason = error != null ? error.getMessage() : null;
|
|
181
|
+
Object[] notification = new Object[] { this, reason };
|
|
182
|
+
for (KRactorPort port : monitors) {
|
|
183
|
+
try {
|
|
184
|
+
port.send(notification);
|
|
185
|
+
} catch (Exception e) {
|
|
186
|
+
// Ignore errors sending to closed ports
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ========================================
|
|
192
|
+
// Efficient select using Virtual Thread watchers
|
|
193
|
+
// ========================================
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Ruby: Ractor.select(*ports_or_ractors)
|
|
197
|
+
* Uses Virtual Thread watchers for efficient blocking (no polling).
|
|
198
|
+
* Each source gets a watcher thread that blocks on receive.
|
|
199
|
+
* First result goes into a shared queue; remaining watchers are interrupted.
|
|
200
|
+
*/
|
|
201
|
+
public static Object[] select(Object[] sources) {
|
|
202
|
+
LinkedBlockingQueue<Object[]> resultQueue = new LinkedBlockingQueue<>(1);
|
|
203
|
+
List<Thread> watchers = new java.util.ArrayList<>(sources.length);
|
|
204
|
+
|
|
205
|
+
for (Object src : sources) {
|
|
206
|
+
final Object source = src;
|
|
207
|
+
Thread watcher = Thread.ofVirtual().start(() -> {
|
|
208
|
+
try {
|
|
209
|
+
Object msg = null;
|
|
210
|
+
if (source instanceof KRactorPort) {
|
|
211
|
+
msg = ((KRactorPort) source).receive();
|
|
212
|
+
} else if (source instanceof KRactor) {
|
|
213
|
+
msg = ((KRactor) source).defaultPort.take();
|
|
214
|
+
}
|
|
215
|
+
if (msg != null) {
|
|
216
|
+
resultQueue.offer(new Object[] { source, msg });
|
|
217
|
+
}
|
|
218
|
+
} catch (InterruptedException e) {
|
|
219
|
+
// Interrupted by another watcher that won
|
|
220
|
+
} catch (Exception e) {
|
|
221
|
+
// Source error
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
watchers.add(watcher);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
Object[] result = resultQueue.take();
|
|
229
|
+
// Interrupt remaining watchers
|
|
230
|
+
for (Thread w : watchers) {
|
|
231
|
+
if (w.isAlive()) {
|
|
232
|
+
w.interrupt();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return result;
|
|
236
|
+
} catch (InterruptedException e) {
|
|
237
|
+
Thread.currentThread().interrupt();
|
|
238
|
+
for (Thread w : watchers) {
|
|
239
|
+
w.interrupt();
|
|
240
|
+
}
|
|
241
|
+
return new Object[] { null, null };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|