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,620 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Konpeito
4
+ module AST
5
+ # Wrapper for Prism AST nodes with type information
6
+ class TypedNode
7
+ attr_reader :node, :type, :children
8
+
9
+ def initialize(node, type, children = [])
10
+ @node = node
11
+ @type = type
12
+ @children = children
13
+ end
14
+
15
+ def location
16
+ node.location
17
+ end
18
+
19
+ def node_type
20
+ node.class.name.split("::").last.sub(/Node$/, "").gsub(/([a-z])([A-Z])/, '\1_\2').downcase.to_sym
21
+ end
22
+
23
+ def inspect
24
+ "#<TypedNode #{node_type} : #{type}>"
25
+ end
26
+ end
27
+
28
+ # Builds a typed AST from Prism AST
29
+ class TypedASTBuilder < Visitor
30
+ attr_reader :inferrer, :rbs_loader, :use_hm
31
+
32
+ def initialize(rbs_loader, use_hm: true, file_path: nil, source: nil)
33
+ @rbs_loader = rbs_loader
34
+ @use_hm = use_hm
35
+ @file_path = file_path
36
+ @source = source
37
+
38
+ if use_hm
39
+ @hm_inferrer = TypeChecker::HMInferrer.new(rbs_loader, file_path: file_path, source: source)
40
+ @inferrer = nil # Will use HM inferrer
41
+ else
42
+ @hm_inferrer = nil
43
+ @inferrer = TypeChecker::Inferrer.new(rbs_loader)
44
+ end
45
+ end
46
+
47
+ # Get diagnostics from the HM inferrer
48
+ def diagnostics
49
+ @hm_inferrer&.diagnostics || []
50
+ end
51
+
52
+ def build(ast)
53
+ # If using HM inference, run whole-program analysis first
54
+ if @use_hm && @hm_inferrer
55
+ @hm_inferrer.analyze(ast)
56
+ end
57
+
58
+ visit(ast)
59
+ end
60
+
61
+ # Get type for a node (uses HM inferrer if available)
62
+ def infer_type(node)
63
+ if @use_hm && @hm_inferrer
64
+ @hm_inferrer.type_for(node)
65
+ else
66
+ @inferrer.infer(node)
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def visit(node)
73
+ return nil unless node
74
+
75
+ method_name = :"visit_#{node_type(node)}"
76
+ if respond_to?(method_name, true)
77
+ send(method_name, node)
78
+ else
79
+ visit_default(node)
80
+ end
81
+ end
82
+
83
+ def visit_default(node)
84
+ type = infer_type(node)
85
+ children = node.compact_child_nodes.map { |child| visit(child) }.compact
86
+ TypedNode.new(node, type, children)
87
+ end
88
+
89
+ def visit_program(node)
90
+ type = infer_type(node)
91
+ children = [visit(node.statements)].compact
92
+ TypedNode.new(node, type, children)
93
+ end
94
+
95
+ def visit_statements(node)
96
+ type = infer_type(node)
97
+ children = node.body.map { |stmt| visit(stmt) }.compact
98
+ TypedNode.new(node, type, children)
99
+ end
100
+
101
+ def visit_def(node)
102
+ # Create a new scope for method body
103
+ type = infer_type(node)
104
+
105
+ children = []
106
+ children << visit(node.parameters) if node.parameters
107
+ children << visit(node.body) if node.body
108
+ children.compact!
109
+
110
+ TypedNode.new(node, type, children)
111
+ end
112
+
113
+ def visit_class(node)
114
+ type = infer_type(node)
115
+
116
+ children = []
117
+ children << visit(node.constant_path) if node.constant_path
118
+ children << visit(node.superclass) if node.superclass
119
+ children << visit(node.body) if node.body
120
+ children.compact!
121
+
122
+ TypedNode.new(node, type, children)
123
+ end
124
+
125
+ def visit_module(node)
126
+ type = infer_type(node)
127
+
128
+ children = []
129
+ children << visit(node.constant_path) if node.constant_path
130
+ children << visit(node.body) if node.body
131
+ children.compact!
132
+
133
+ TypedNode.new(node, type, children)
134
+ end
135
+
136
+ def visit_if(node)
137
+ type = infer_type(node)
138
+
139
+ children = []
140
+ children << visit(node.predicate)
141
+ children << visit(node.statements) if node.statements
142
+ children << visit(node.subsequent) if node.subsequent
143
+ children.compact!
144
+
145
+ TypedNode.new(node, type, children)
146
+ end
147
+
148
+ def visit_unless(node)
149
+ type = infer_type(node)
150
+
151
+ children = []
152
+ children << visit(node.predicate)
153
+ children << visit(node.statements) if node.statements
154
+ children << visit(node.else_clause) if node.else_clause
155
+ children.compact!
156
+
157
+ TypedNode.new(node, type, children)
158
+ end
159
+
160
+ def visit_and(node)
161
+ type = infer_type(node)
162
+
163
+ children = []
164
+ children << visit(node.left)
165
+ children << visit(node.right)
166
+ children.compact!
167
+
168
+ TypedNode.new(node, type, children)
169
+ end
170
+
171
+ def visit_or(node)
172
+ type = infer_type(node)
173
+
174
+ children = []
175
+ children << visit(node.left)
176
+ children << visit(node.right)
177
+ children.compact!
178
+
179
+ TypedNode.new(node, type, children)
180
+ end
181
+
182
+ # Compound assignment visitors
183
+ def visit_local_variable_operator_write(node)
184
+ type = infer_type(node)
185
+ children = [visit(node.value)].compact
186
+ TypedNode.new(node, type, children)
187
+ end
188
+
189
+ def visit_local_variable_or_write(node)
190
+ type = infer_type(node)
191
+ children = [visit(node.value)].compact
192
+ TypedNode.new(node, type, children)
193
+ end
194
+
195
+ def visit_local_variable_and_write(node)
196
+ type = infer_type(node)
197
+ children = [visit(node.value)].compact
198
+ TypedNode.new(node, type, children)
199
+ end
200
+
201
+ def visit_instance_variable_operator_write(node)
202
+ type = infer_type(node)
203
+ children = [visit(node.value)].compact
204
+ TypedNode.new(node, type, children)
205
+ end
206
+
207
+ def visit_instance_variable_or_write(node)
208
+ type = infer_type(node)
209
+ children = [visit(node.value)].compact
210
+ TypedNode.new(node, type, children)
211
+ end
212
+
213
+ def visit_instance_variable_and_write(node)
214
+ type = infer_type(node)
215
+ children = [visit(node.value)].compact
216
+ TypedNode.new(node, type, children)
217
+ end
218
+
219
+ def visit_class_variable_operator_write(node)
220
+ type = infer_type(node)
221
+ children = [visit(node.value)].compact
222
+ TypedNode.new(node, type, children)
223
+ end
224
+
225
+ def visit_class_variable_or_write(node)
226
+ type = infer_type(node)
227
+ children = [visit(node.value)].compact
228
+ TypedNode.new(node, type, children)
229
+ end
230
+
231
+ def visit_class_variable_and_write(node)
232
+ type = infer_type(node)
233
+ children = [visit(node.value)].compact
234
+ TypedNode.new(node, type, children)
235
+ end
236
+
237
+ def visit_while(node)
238
+ type = infer_type(node)
239
+
240
+ children = []
241
+ children << visit(node.predicate)
242
+ children << visit(node.statements) if node.statements
243
+ children.compact!
244
+
245
+ TypedNode.new(node, type, children)
246
+ end
247
+
248
+ def visit_until(node)
249
+ type = infer_type(node)
250
+
251
+ children = []
252
+ children << visit(node.predicate)
253
+ children << visit(node.statements) if node.statements
254
+ children.compact!
255
+
256
+ TypedNode.new(node, type, children)
257
+ end
258
+
259
+ def visit_break(node)
260
+ type = infer_type(node)
261
+ children = []
262
+ children << visit(node.arguments) if node.arguments
263
+ children.compact!
264
+ TypedNode.new(node, type, children)
265
+ end
266
+
267
+ def visit_next(node)
268
+ type = infer_type(node)
269
+ children = []
270
+ children << visit(node.arguments) if node.arguments
271
+ children.compact!
272
+ TypedNode.new(node, type, children)
273
+ end
274
+
275
+ def visit_range(node)
276
+ type = infer_type(node)
277
+ children = []
278
+ children << visit(node.left) if node.left
279
+ children << visit(node.right) if node.right
280
+ children.compact!
281
+ TypedNode.new(node, type, children)
282
+ end
283
+
284
+ def visit_global_variable_read(node)
285
+ type = infer_type(node)
286
+ TypedNode.new(node, type, [])
287
+ end
288
+
289
+ def visit_global_variable_write(node)
290
+ type = infer_type(node)
291
+ children = [visit(node.value)].compact
292
+ TypedNode.new(node, type, children)
293
+ end
294
+
295
+ def visit_multi_write(node)
296
+ type = infer_type(node)
297
+ children = []
298
+ # Visit the value (RHS)
299
+ children << visit(node.value) if node.value
300
+ children.compact!
301
+ TypedNode.new(node, type, children)
302
+ end
303
+
304
+ def visit_super(node)
305
+ type = infer_type(node)
306
+ children = []
307
+ children << visit(node.arguments) if node.arguments
308
+ children.compact!
309
+ TypedNode.new(node, type, children)
310
+ end
311
+
312
+ def visit_forwarding_super(node)
313
+ type = infer_type(node)
314
+ TypedNode.new(node, type, [])
315
+ end
316
+
317
+ def visit_case(node)
318
+ type = infer_type(node)
319
+
320
+ children = []
321
+ children << visit(node.predicate) if node.predicate
322
+ node.conditions.each { |cond| children << visit(cond) }
323
+ children << visit(node.else_clause) if node.else_clause
324
+ children.compact!
325
+
326
+ TypedNode.new(node, type, children)
327
+ end
328
+
329
+ def visit_when(node)
330
+ type = infer_type(node)
331
+
332
+ children = []
333
+ node.conditions.each { |cond| children << visit(cond) }
334
+ children << visit(node.statements) if node.statements
335
+ children.compact!
336
+
337
+ TypedNode.new(node, type, children)
338
+ end
339
+
340
+ # ========================================
341
+ # Pattern Matching visitors
342
+ # ========================================
343
+
344
+ # case/in statement
345
+ def visit_case_match(node)
346
+ type = infer_type(node)
347
+
348
+ children = []
349
+ children << visit(node.predicate) if node.predicate
350
+ node.conditions.each { |cond| children << visit(cond) }
351
+ children << visit(node.else_clause) if node.else_clause
352
+ children.compact!
353
+
354
+ TypedNode.new(node, type, children)
355
+ end
356
+
357
+ # in clause
358
+ def visit_in(node)
359
+ type = infer_type(node)
360
+
361
+ children = []
362
+ children << visit(node.pattern) if node.pattern
363
+ children << visit(node.statements) if node.statements
364
+ children.compact!
365
+
366
+ TypedNode.new(node, type, children)
367
+ end
368
+
369
+ # expr in pattern (returns boolean)
370
+ def visit_match_predicate(node)
371
+ type = TypeChecker::Types::BOOL
372
+
373
+ children = []
374
+ children << visit(node.value)
375
+ children << visit(node.pattern)
376
+ children.compact!
377
+
378
+ TypedNode.new(node, type, children)
379
+ end
380
+
381
+ # expr => pattern (raises on failure)
382
+ def visit_match_required(node)
383
+ type = infer_type(node)
384
+
385
+ children = []
386
+ children << visit(node.value)
387
+ children << visit(node.pattern)
388
+ children.compact!
389
+
390
+ TypedNode.new(node, type, children)
391
+ end
392
+
393
+ # [a, b, *rest] pattern
394
+ def visit_array_pattern(node)
395
+ type = TypeChecker::Types::UNTYPED
396
+
397
+ children = []
398
+ children << visit(node.constant) if node.constant
399
+ node.requireds.each { |req| children << visit(req) }
400
+ children << visit(node.rest) if node.rest
401
+ node.posts.each { |post| children << visit(post) }
402
+ children.compact!
403
+
404
+ TypedNode.new(node, type, children)
405
+ end
406
+
407
+ # {x:, y: pattern} pattern
408
+ def visit_hash_pattern(node)
409
+ type = TypeChecker::Types::UNTYPED
410
+
411
+ children = []
412
+ children << visit(node.constant) if node.constant
413
+ node.elements.each { |elem| children << visit(elem) }
414
+ children << visit(node.rest) if node.rest
415
+ children.compact!
416
+
417
+ TypedNode.new(node, type, children)
418
+ end
419
+
420
+ # a | b pattern
421
+ def visit_alternation_pattern(node)
422
+ type = TypeChecker::Types::UNTYPED
423
+
424
+ children = []
425
+ children << visit(node.left)
426
+ children << visit(node.right)
427
+ children.compact!
428
+
429
+ TypedNode.new(node, type, children)
430
+ end
431
+
432
+ # pattern => var capture
433
+ def visit_capture_pattern(node)
434
+ type = TypeChecker::Types::UNTYPED
435
+
436
+ children = []
437
+ children << visit(node.value)
438
+ children << visit(node.target)
439
+ children.compact!
440
+
441
+ TypedNode.new(node, type, children)
442
+ end
443
+
444
+ # ^var pinned pattern
445
+ def visit_pinned_variable(node)
446
+ type = TypeChecker::Types::UNTYPED
447
+
448
+ children = []
449
+ children << visit(node.variable)
450
+ children.compact!
451
+
452
+ TypedNode.new(node, type, children)
453
+ end
454
+
455
+ # ^(expr) pinned expression
456
+ def visit_pinned_expression(node)
457
+ type = TypeChecker::Types::UNTYPED
458
+
459
+ children = []
460
+ children << visit(node.expression)
461
+ children.compact!
462
+
463
+ TypedNode.new(node, type, children)
464
+ end
465
+
466
+ # [*a, pattern, *b] find pattern
467
+ def visit_find_pattern(node)
468
+ type = TypeChecker::Types::UNTYPED
469
+
470
+ children = []
471
+ children << visit(node.left) if node.left
472
+ node.requireds.each { |req| children << visit(req) }
473
+ children << visit(node.right) if node.right
474
+ children.compact!
475
+
476
+ TypedNode.new(node, type, children)
477
+ end
478
+
479
+ # *rest splat
480
+ def visit_splat(node)
481
+ type = TypeChecker::Types::UNTYPED
482
+ children = []
483
+ children << visit(node.expression) if node.expression
484
+ children.compact!
485
+ TypedNode.new(node, type, children)
486
+ end
487
+
488
+ # **rest keyword splat (in pattern context)
489
+ def visit_assoc_splat(node)
490
+ type = TypeChecker::Types::UNTYPED
491
+ children = []
492
+ children << visit(node.value) if node.value
493
+ children.compact!
494
+ TypedNode.new(node, type, children)
495
+ end
496
+
497
+ # x: in hash pattern (shorthand)
498
+ def visit_no_keywords(node)
499
+ type = TypeChecker::Types::UNTYPED
500
+ TypedNode.new(node, type, [])
501
+ end
502
+
503
+ def visit_call(node)
504
+ type = infer_type(node)
505
+
506
+ children = []
507
+ children << visit(node.receiver) if node.receiver
508
+ children << visit(node.arguments) if node.arguments
509
+ children << visit(node.block) if node.block
510
+ children.compact!
511
+
512
+ TypedNode.new(node, type, children)
513
+ end
514
+
515
+ def visit_arguments(node)
516
+ type = TypeChecker::Types::UNTYPED
517
+ children = node.arguments.map { |arg| visit(arg) }.compact
518
+ TypedNode.new(node, type, children)
519
+ end
520
+
521
+ def visit_block(node)
522
+ type = infer_type(node)
523
+
524
+ children = []
525
+ children << visit(node.parameters) if node.parameters
526
+ children << visit(node.body) if node.body
527
+ children.compact!
528
+
529
+ TypedNode.new(node, type, children)
530
+ end
531
+
532
+ def visit_array(node)
533
+ type = infer_type(node)
534
+ children = node.elements.map { |elem| visit(elem) }.compact
535
+ TypedNode.new(node, type, children)
536
+ end
537
+
538
+ def visit_hash(node)
539
+ type = infer_type(node)
540
+ children = node.elements.map { |elem| visit(elem) }.compact
541
+ TypedNode.new(node, type, children)
542
+ end
543
+
544
+ def visit_assoc(node)
545
+ type = TypeChecker::Types::UNTYPED
546
+ children = [visit(node.key), visit(node.value)].compact
547
+ TypedNode.new(node, type, children)
548
+ end
549
+
550
+ def visit_begin(node)
551
+ type = infer_type(node)
552
+
553
+ children = []
554
+ children << visit(node.statements) if node.statements
555
+ children << visit(node.rescue_clause) if node.rescue_clause
556
+ children << visit(node.else_clause) if node.else_clause
557
+ children << visit(node.ensure_clause) if node.ensure_clause
558
+ children.compact!
559
+
560
+ TypedNode.new(node, type, children)
561
+ end
562
+
563
+ def visit_rescue(node)
564
+ type = infer_type(node)
565
+
566
+ children = []
567
+ node.exceptions&.each { |ex| children << visit(ex) }
568
+ children << visit(node.statements) if node.statements
569
+ children << visit(node.subsequent) if node.subsequent
570
+ children.compact!
571
+
572
+ TypedNode.new(node, type, children)
573
+ end
574
+
575
+ # Terminal nodes (no children)
576
+ %i[
577
+ integer float string symbol true false nil
578
+ local_variable_read instance_variable_read class_variable_read
579
+ constant_read self source_file source_line
580
+ ].each do |name|
581
+ define_method(:"visit_#{name}") do |node|
582
+ type = infer_type(node)
583
+ TypedNode.new(node, type, [])
584
+ end
585
+ end
586
+
587
+ # Assignment nodes
588
+ def visit_local_variable_write(node)
589
+ type = infer_type(node)
590
+ children = [visit(node.value)].compact
591
+ TypedNode.new(node, type, children)
592
+ end
593
+
594
+ def visit_instance_variable_write(node)
595
+ type = infer_type(node)
596
+ children = [visit(node.value)].compact
597
+ TypedNode.new(node, type, children)
598
+ end
599
+
600
+ def visit_class_variable_write(node)
601
+ type = infer_type(node)
602
+ children = [visit(node.value)].compact
603
+ TypedNode.new(node, type, children)
604
+ end
605
+
606
+ def visit_constant_write(node)
607
+ type = infer_type(node)
608
+ children = [visit(node.value)].compact
609
+ TypedNode.new(node, type, children)
610
+ end
611
+
612
+ def visit_return(node)
613
+ type = infer_type(node)
614
+ children = node.arguments ? [visit(node.arguments)] : []
615
+ children.compact!
616
+ TypedNode.new(node, type, children)
617
+ end
618
+ end
619
+ end
620
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Konpeito
4
+ module AST
5
+ # Base visitor class for traversing Prism AST nodes
6
+ # Subclass and override visit_* methods to handle specific node types
7
+ class Visitor
8
+ def visit(node)
9
+ return unless node
10
+
11
+ method_name = :"visit_#{node_type(node)}"
12
+ if respond_to?(method_name, true)
13
+ send(method_name, node)
14
+ else
15
+ visit_default(node)
16
+ end
17
+ end
18
+
19
+ def visit_all(nodes)
20
+ nodes.each { |node| visit(node) }
21
+ end
22
+
23
+ private
24
+
25
+ def visit_default(node)
26
+ visit_children(node)
27
+ end
28
+
29
+ def visit_children(node)
30
+ node.compact_child_nodes.each { |child| visit(child) }
31
+ end
32
+
33
+ def node_type(node)
34
+ # Convert Prism::FooNode to :foo
35
+ node.class.name.split("::").last.sub(/Node$/, "").gsub(/([a-z])([A-Z])/, '\1_\2').downcase
36
+ end
37
+
38
+ # Override these methods in subclasses to handle specific node types
39
+ # Example method signatures:
40
+
41
+ # def visit_program(node); end
42
+ # def visit_statements(node); end
43
+ # def visit_def(node); end
44
+ # def visit_class(node); end
45
+ # def visit_module(node); end
46
+ # def visit_if(node); end
47
+ # def visit_unless(node); end
48
+ # def visit_while(node); end
49
+ # def visit_until(node); end
50
+ # def visit_for(node); end
51
+ # def visit_case(node); end
52
+ # def visit_call(node); end
53
+ # def visit_local_variable_read(node); end
54
+ # def visit_local_variable_write(node); end
55
+ # def visit_instance_variable_read(node); end
56
+ # def visit_instance_variable_write(node); end
57
+ # def visit_class_variable_read(node); end
58
+ # def visit_class_variable_write(node); end
59
+ # def visit_constant_read(node); end
60
+ # def visit_constant_write(node); end
61
+ # def visit_integer(node); end
62
+ # def visit_float(node); end
63
+ # def visit_string(node); end
64
+ # def visit_symbol(node); end
65
+ # def visit_array(node); end
66
+ # def visit_hash(node); end
67
+ # def visit_block(node); end
68
+ # def visit_lambda(node); end
69
+ # def visit_begin(node); end
70
+ # def visit_rescue(node); end
71
+ # def visit_ensure(node); end
72
+ # def visit_return(node); end
73
+ # def visit_break(node); end
74
+ # def visit_next(node); end
75
+ # def visit_yield(node); end
76
+ end
77
+ end
78
+ end