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,1214 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
require "stringio"
|
|
5
|
+
|
|
6
|
+
module Konpeito
|
|
7
|
+
module Formatter
|
|
8
|
+
class Formatter
|
|
9
|
+
INDENT_SIZE = 2
|
|
10
|
+
|
|
11
|
+
def initialize(source, filepath: nil)
|
|
12
|
+
@source = source
|
|
13
|
+
@filepath = filepath
|
|
14
|
+
@result = Prism.parse(source, filepath: filepath)
|
|
15
|
+
@comments = @result.comments.dup
|
|
16
|
+
@output = StringIO.new
|
|
17
|
+
@indent = 0
|
|
18
|
+
@line_started = false
|
|
19
|
+
@last_was_newline = false
|
|
20
|
+
@current_line = 1
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def format
|
|
24
|
+
visit(@result.value)
|
|
25
|
+
flush_remaining_comments
|
|
26
|
+
result = @output.string
|
|
27
|
+
# Ensure trailing newline
|
|
28
|
+
result += "\n" unless result.end_with?("\n")
|
|
29
|
+
# Collapse 3+ consecutive newlines into 2
|
|
30
|
+
result.gsub(/\n{3,}/, "\n\n")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
# --- Core emit helpers ---
|
|
36
|
+
|
|
37
|
+
def emit(str)
|
|
38
|
+
return if str.nil? || str.empty?
|
|
39
|
+
if !@line_started
|
|
40
|
+
@output << (" " * (INDENT_SIZE * @indent))
|
|
41
|
+
@line_started = true
|
|
42
|
+
end
|
|
43
|
+
@output << str
|
|
44
|
+
@last_was_newline = false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def emit_newline
|
|
48
|
+
@output << "\n"
|
|
49
|
+
@line_started = false
|
|
50
|
+
@last_was_newline = true
|
|
51
|
+
@current_line += 1
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def emit_blank_line
|
|
55
|
+
# Ensure we end current line if needed, then add one blank line
|
|
56
|
+
unless @last_was_newline
|
|
57
|
+
emit_newline
|
|
58
|
+
end
|
|
59
|
+
@output << "\n"
|
|
60
|
+
@current_line += 1
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def indent
|
|
64
|
+
@indent += 1
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def dedent
|
|
68
|
+
@indent -= 1
|
|
69
|
+
@indent = 0 if @indent < 0
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# --- Comment handling ---
|
|
73
|
+
|
|
74
|
+
def emit_leading_comments(node)
|
|
75
|
+
return unless node.respond_to?(:location)
|
|
76
|
+
target_line = node.location.start_line
|
|
77
|
+
last_comment_line = nil
|
|
78
|
+
|
|
79
|
+
while (comment = @comments.first) && comment.location.start_line < target_line
|
|
80
|
+
@comments.shift
|
|
81
|
+
|
|
82
|
+
# Preserve blank line between comments/before this comment
|
|
83
|
+
if last_comment_line && comment.location.start_line - last_comment_line > 1
|
|
84
|
+
emit_blank_line
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
emit(comment.location.slice)
|
|
88
|
+
emit_newline
|
|
89
|
+
last_comment_line = comment.location.end_line
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Preserve blank line between last comment and the node
|
|
93
|
+
if last_comment_line && node.location.start_line - last_comment_line > 1
|
|
94
|
+
emit_blank_line
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def emit_inline_comment(node)
|
|
99
|
+
return unless node.respond_to?(:location)
|
|
100
|
+
end_line = node.location.end_line
|
|
101
|
+
|
|
102
|
+
if (comment = @comments.first) && comment.location.start_line == end_line
|
|
103
|
+
@comments.shift
|
|
104
|
+
emit(" ")
|
|
105
|
+
emit(comment.location.slice)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def flush_remaining_comments
|
|
110
|
+
@comments.each do |comment|
|
|
111
|
+
emit(comment.location.slice)
|
|
112
|
+
emit_newline
|
|
113
|
+
end
|
|
114
|
+
@comments.clear
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# --- Visit dispatch ---
|
|
118
|
+
|
|
119
|
+
def visit(node)
|
|
120
|
+
return unless node
|
|
121
|
+
|
|
122
|
+
method_name = :"visit_#{node.type}"
|
|
123
|
+
if respond_to?(method_name, true)
|
|
124
|
+
send(method_name, node)
|
|
125
|
+
else
|
|
126
|
+
# Fallback: emit original source
|
|
127
|
+
emit(node.location.slice)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def visit_list(nodes, separator: nil)
|
|
132
|
+
nodes.each_with_index do |node, i|
|
|
133
|
+
visit(node)
|
|
134
|
+
if separator && i < nodes.size - 1
|
|
135
|
+
emit(separator)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# --- Node visitors ---
|
|
141
|
+
|
|
142
|
+
def visit_program_node(node)
|
|
143
|
+
visit(node.statements)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def visit_statements_node(node)
|
|
147
|
+
body = node.body
|
|
148
|
+
body.each_with_index do |stmt, i|
|
|
149
|
+
emit_leading_comments(stmt)
|
|
150
|
+
visit(stmt)
|
|
151
|
+
emit_inline_comment(stmt)
|
|
152
|
+
emit_newline unless i == body.size - 1 && @last_was_newline
|
|
153
|
+
|
|
154
|
+
# Preserve blank lines between statements based on original source
|
|
155
|
+
if i < body.size - 1
|
|
156
|
+
next_stmt = body[i + 1]
|
|
157
|
+
current_end = stmt.location.end_line
|
|
158
|
+
# Check for leading comments on next node
|
|
159
|
+
next_start = next_stmt.location.start_line
|
|
160
|
+
if @comments.first && @comments.first.location.start_line < next_start
|
|
161
|
+
next_start = @comments.first.location.start_line
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
if next_start - current_end > 1 || needs_blank_line_between?(stmt, next_stmt)
|
|
165
|
+
emit_blank_line
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def needs_blank_line_between?(a, b)
|
|
172
|
+
[Prism::DefNode, Prism::ClassNode, Prism::ModuleNode].any? { |k| a.is_a?(k) || b.is_a?(k) }
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# --- Literals ---
|
|
176
|
+
|
|
177
|
+
def visit_integer_node(node)
|
|
178
|
+
emit(node.location.slice)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def visit_float_node(node)
|
|
182
|
+
emit(node.location.slice)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def visit_rational_node(node)
|
|
186
|
+
emit(node.location.slice)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def visit_imaginary_node(node)
|
|
190
|
+
emit(node.location.slice)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def visit_string_node(node)
|
|
194
|
+
emit(node.location.slice)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def visit_interpolated_string_node(node)
|
|
198
|
+
emit(node.location.slice)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def visit_symbol_node(node)
|
|
202
|
+
emit(node.location.slice)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def visit_interpolated_symbol_node(node)
|
|
206
|
+
emit(node.location.slice)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def visit_regular_expression_node(node)
|
|
210
|
+
emit(node.location.slice)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def visit_interpolated_regular_expression_node(node)
|
|
214
|
+
emit(node.location.slice)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def visit_x_string_node(node)
|
|
218
|
+
emit(node.location.slice)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def visit_true_node(node)
|
|
222
|
+
emit("true")
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def visit_false_node(node)
|
|
226
|
+
emit("false")
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def visit_nil_node(node)
|
|
230
|
+
emit("nil")
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def visit_self_node(node)
|
|
234
|
+
emit("self")
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def visit_source_file_node(node)
|
|
238
|
+
emit("__FILE__")
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def visit_source_line_node(node)
|
|
242
|
+
emit("__LINE__")
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def visit_source_encoding_node(node)
|
|
246
|
+
emit("__ENCODING__")
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# --- Variables ---
|
|
250
|
+
|
|
251
|
+
def visit_local_variable_read_node(node)
|
|
252
|
+
emit(node.name.to_s)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def visit_local_variable_write_node(node)
|
|
256
|
+
emit(node.name.to_s)
|
|
257
|
+
emit(" = ")
|
|
258
|
+
visit(node.value)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def visit_local_variable_operator_write_node(node)
|
|
262
|
+
emit(node.name.to_s)
|
|
263
|
+
emit(" #{node.binary_operator}= ")
|
|
264
|
+
visit(node.value)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def visit_local_variable_and_write_node(node)
|
|
268
|
+
emit(node.name.to_s)
|
|
269
|
+
emit(" &&= ")
|
|
270
|
+
visit(node.value)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def visit_local_variable_or_write_node(node)
|
|
274
|
+
emit(node.name.to_s)
|
|
275
|
+
emit(" ||= ")
|
|
276
|
+
visit(node.value)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def visit_local_variable_target_node(node)
|
|
280
|
+
emit(node.name.to_s)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def visit_instance_variable_read_node(node)
|
|
284
|
+
emit(node.name.to_s)
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def visit_instance_variable_write_node(node)
|
|
288
|
+
emit(node.name.to_s)
|
|
289
|
+
emit(" = ")
|
|
290
|
+
visit(node.value)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def visit_instance_variable_operator_write_node(node)
|
|
294
|
+
emit(node.name.to_s)
|
|
295
|
+
emit(" #{node.binary_operator}= ")
|
|
296
|
+
visit(node.value)
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def visit_instance_variable_and_write_node(node)
|
|
300
|
+
emit(node.name.to_s)
|
|
301
|
+
emit(" &&= ")
|
|
302
|
+
visit(node.value)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def visit_instance_variable_or_write_node(node)
|
|
306
|
+
emit(node.name.to_s)
|
|
307
|
+
emit(" ||= ")
|
|
308
|
+
visit(node.value)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def visit_instance_variable_target_node(node)
|
|
312
|
+
emit(node.name.to_s)
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def visit_class_variable_read_node(node)
|
|
316
|
+
emit(node.name.to_s)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def visit_class_variable_write_node(node)
|
|
320
|
+
emit(node.name.to_s)
|
|
321
|
+
emit(" = ")
|
|
322
|
+
visit(node.value)
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def visit_class_variable_operator_write_node(node)
|
|
326
|
+
emit(node.name.to_s)
|
|
327
|
+
emit(" #{node.binary_operator}= ")
|
|
328
|
+
visit(node.value)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def visit_class_variable_and_write_node(node)
|
|
332
|
+
emit(node.name.to_s)
|
|
333
|
+
emit(" &&= ")
|
|
334
|
+
visit(node.value)
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def visit_class_variable_or_write_node(node)
|
|
338
|
+
emit(node.name.to_s)
|
|
339
|
+
emit(" ||= ")
|
|
340
|
+
visit(node.value)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
def visit_global_variable_read_node(node)
|
|
344
|
+
emit(node.name.to_s)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def visit_global_variable_write_node(node)
|
|
348
|
+
emit(node.name.to_s)
|
|
349
|
+
emit(" = ")
|
|
350
|
+
visit(node.value)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def visit_constant_read_node(node)
|
|
354
|
+
emit(node.name.to_s)
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def visit_constant_write_node(node)
|
|
358
|
+
emit(node.name.to_s)
|
|
359
|
+
emit(" = ")
|
|
360
|
+
visit(node.value)
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def visit_constant_path_node(node)
|
|
364
|
+
if node.parent
|
|
365
|
+
visit(node.parent)
|
|
366
|
+
emit("::")
|
|
367
|
+
else
|
|
368
|
+
emit("::")
|
|
369
|
+
end
|
|
370
|
+
emit(node.name.to_s)
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def visit_constant_path_write_node(node)
|
|
374
|
+
visit(node.target)
|
|
375
|
+
emit(" = ")
|
|
376
|
+
visit(node.value)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
# --- Method definitions ---
|
|
380
|
+
|
|
381
|
+
def visit_def_node(node)
|
|
382
|
+
emit("def ")
|
|
383
|
+
if node.receiver
|
|
384
|
+
visit(node.receiver)
|
|
385
|
+
emit(".")
|
|
386
|
+
end
|
|
387
|
+
emit(node.name.to_s)
|
|
388
|
+
|
|
389
|
+
if node.parameters
|
|
390
|
+
emit("(")
|
|
391
|
+
visit(node.parameters)
|
|
392
|
+
emit(")")
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
if node.body
|
|
396
|
+
emit_newline
|
|
397
|
+
indent
|
|
398
|
+
visit(node.body)
|
|
399
|
+
emit_newline unless @last_was_newline
|
|
400
|
+
dedent
|
|
401
|
+
emit("end")
|
|
402
|
+
else
|
|
403
|
+
emit_newline
|
|
404
|
+
emit("end")
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
def visit_parameters_node(node)
|
|
409
|
+
params = []
|
|
410
|
+
|
|
411
|
+
node.requireds.each { |p| params << p }
|
|
412
|
+
node.optionals.each { |p| params << p }
|
|
413
|
+
params << node.rest if node.rest
|
|
414
|
+
node.posts.each { |p| params << p }
|
|
415
|
+
node.keywords.each { |p| params << p }
|
|
416
|
+
params << node.keyword_rest if node.keyword_rest
|
|
417
|
+
params << node.block if node.block
|
|
418
|
+
|
|
419
|
+
params.each_with_index do |param, i|
|
|
420
|
+
visit(param)
|
|
421
|
+
emit(", ") if i < params.size - 1
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
def visit_required_parameter_node(node)
|
|
426
|
+
emit(node.name.to_s)
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
def visit_optional_parameter_node(node)
|
|
430
|
+
emit(node.name.to_s)
|
|
431
|
+
emit(" = ")
|
|
432
|
+
visit(node.value)
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def visit_rest_parameter_node(node)
|
|
436
|
+
emit("*")
|
|
437
|
+
emit(node.name.to_s) if node.name
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def visit_keyword_rest_parameter_node(node)
|
|
441
|
+
emit("**")
|
|
442
|
+
emit(node.name.to_s) if node.name
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def visit_required_keyword_parameter_node(node)
|
|
446
|
+
emit(node.name.to_s)
|
|
447
|
+
emit(":")
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def visit_optional_keyword_parameter_node(node)
|
|
451
|
+
emit(node.name.to_s)
|
|
452
|
+
emit(": ")
|
|
453
|
+
visit(node.value)
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def visit_block_parameter_node(node)
|
|
457
|
+
emit("&")
|
|
458
|
+
emit(node.name.to_s) if node.name
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
# --- Class / Module ---
|
|
462
|
+
|
|
463
|
+
def visit_class_node(node)
|
|
464
|
+
emit("class ")
|
|
465
|
+
visit(node.constant_path)
|
|
466
|
+
if node.superclass
|
|
467
|
+
emit(" < ")
|
|
468
|
+
visit(node.superclass)
|
|
469
|
+
end
|
|
470
|
+
emit_newline
|
|
471
|
+
|
|
472
|
+
if node.body
|
|
473
|
+
indent
|
|
474
|
+
visit(node.body)
|
|
475
|
+
emit_newline unless @last_was_newline
|
|
476
|
+
dedent
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
emit("end")
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
def visit_module_node(node)
|
|
483
|
+
emit("module ")
|
|
484
|
+
visit(node.constant_path)
|
|
485
|
+
emit_newline
|
|
486
|
+
|
|
487
|
+
if node.body
|
|
488
|
+
indent
|
|
489
|
+
visit(node.body)
|
|
490
|
+
emit_newline unless @last_was_newline
|
|
491
|
+
dedent
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
emit("end")
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
def visit_singleton_class_node(node)
|
|
498
|
+
emit("class << ")
|
|
499
|
+
visit(node.expression)
|
|
500
|
+
emit_newline
|
|
501
|
+
|
|
502
|
+
if node.body
|
|
503
|
+
indent
|
|
504
|
+
visit(node.body)
|
|
505
|
+
emit_newline unless @last_was_newline
|
|
506
|
+
dedent
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
emit("end")
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
# --- Control flow ---
|
|
513
|
+
|
|
514
|
+
def visit_if_node(node)
|
|
515
|
+
# Ternary or modifier form — fall back to source
|
|
516
|
+
if node.location.slice.include?("?") && !node.location.slice.strip.start_with?("if")
|
|
517
|
+
emit(node.location.slice)
|
|
518
|
+
return
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
# Check for modifier if (single-line "expr if cond")
|
|
522
|
+
src = node.location.slice.strip
|
|
523
|
+
if !src.start_with?("if") && !src.start_with?("elsif")
|
|
524
|
+
emit(node.location.slice)
|
|
525
|
+
return
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
if src.start_with?("elsif")
|
|
529
|
+
emit("elsif ")
|
|
530
|
+
else
|
|
531
|
+
emit("if ")
|
|
532
|
+
end
|
|
533
|
+
visit(node.predicate)
|
|
534
|
+
emit_newline
|
|
535
|
+
|
|
536
|
+
if node.statements
|
|
537
|
+
indent
|
|
538
|
+
visit(node.statements)
|
|
539
|
+
emit_newline unless @last_was_newline
|
|
540
|
+
dedent
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
if node.subsequent
|
|
544
|
+
visit_if_subsequent(node.subsequent)
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
# Only top-level if gets 'end', elsif doesn't
|
|
548
|
+
emit("end") unless src.start_with?("elsif")
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
def visit_if_subsequent(node)
|
|
552
|
+
case node
|
|
553
|
+
when Prism::IfNode
|
|
554
|
+
# elsif
|
|
555
|
+
emit("elsif ")
|
|
556
|
+
visit(node.predicate)
|
|
557
|
+
emit_newline
|
|
558
|
+
if node.statements
|
|
559
|
+
indent
|
|
560
|
+
visit(node.statements)
|
|
561
|
+
emit_newline unless @last_was_newline
|
|
562
|
+
dedent
|
|
563
|
+
end
|
|
564
|
+
visit_if_subsequent(node.subsequent) if node.subsequent
|
|
565
|
+
when Prism::ElseNode
|
|
566
|
+
visit_else_node(node)
|
|
567
|
+
end
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
def visit_else_node(node)
|
|
571
|
+
emit("else")
|
|
572
|
+
emit_newline
|
|
573
|
+
if node.statements
|
|
574
|
+
indent
|
|
575
|
+
visit(node.statements)
|
|
576
|
+
emit_newline unless @last_was_newline
|
|
577
|
+
dedent
|
|
578
|
+
end
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
def visit_unless_node(node)
|
|
582
|
+
emit("unless ")
|
|
583
|
+
visit(node.predicate)
|
|
584
|
+
emit_newline
|
|
585
|
+
|
|
586
|
+
if node.statements
|
|
587
|
+
indent
|
|
588
|
+
visit(node.statements)
|
|
589
|
+
emit_newline unless @last_was_newline
|
|
590
|
+
dedent
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
if node.else_clause
|
|
594
|
+
visit_else_node(node.else_clause)
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
emit("end")
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
def visit_while_node(node)
|
|
601
|
+
emit("while ")
|
|
602
|
+
visit(node.predicate)
|
|
603
|
+
emit_newline
|
|
604
|
+
|
|
605
|
+
if node.statements
|
|
606
|
+
indent
|
|
607
|
+
visit(node.statements)
|
|
608
|
+
emit_newline unless @last_was_newline
|
|
609
|
+
dedent
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
emit("end")
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
def visit_until_node(node)
|
|
616
|
+
emit("until ")
|
|
617
|
+
visit(node.predicate)
|
|
618
|
+
emit_newline
|
|
619
|
+
|
|
620
|
+
if node.statements
|
|
621
|
+
indent
|
|
622
|
+
visit(node.statements)
|
|
623
|
+
emit_newline unless @last_was_newline
|
|
624
|
+
dedent
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
emit("end")
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
def visit_for_node(node)
|
|
631
|
+
emit("for ")
|
|
632
|
+
visit(node.index)
|
|
633
|
+
emit(" in ")
|
|
634
|
+
visit(node.collection)
|
|
635
|
+
emit_newline
|
|
636
|
+
|
|
637
|
+
if node.statements
|
|
638
|
+
indent
|
|
639
|
+
visit(node.statements)
|
|
640
|
+
emit_newline unless @last_was_newline
|
|
641
|
+
dedent
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
emit("end")
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
def visit_case_node(node)
|
|
648
|
+
emit("case ")
|
|
649
|
+
visit(node.predicate) if node.predicate
|
|
650
|
+
emit_newline
|
|
651
|
+
|
|
652
|
+
node.conditions.each do |cond|
|
|
653
|
+
visit(cond)
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
if node.else_clause
|
|
657
|
+
visit_else_node(node.else_clause)
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
emit("end")
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
def visit_when_node(node)
|
|
664
|
+
emit("when ")
|
|
665
|
+
visit_list(node.conditions, separator: ", ")
|
|
666
|
+
emit_newline
|
|
667
|
+
|
|
668
|
+
if node.statements
|
|
669
|
+
indent
|
|
670
|
+
visit(node.statements)
|
|
671
|
+
emit_newline unless @last_was_newline
|
|
672
|
+
dedent
|
|
673
|
+
end
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
def visit_case_match_node(node)
|
|
677
|
+
emit("case ")
|
|
678
|
+
visit(node.predicate) if node.predicate
|
|
679
|
+
emit_newline
|
|
680
|
+
|
|
681
|
+
node.conditions.each do |cond|
|
|
682
|
+
visit(cond)
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
if node.else_clause
|
|
686
|
+
visit_else_node(node.else_clause)
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
emit("end")
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
def visit_in_node(node)
|
|
693
|
+
emit("in ")
|
|
694
|
+
visit(node.pattern)
|
|
695
|
+
if node.statements
|
|
696
|
+
emit_newline
|
|
697
|
+
indent
|
|
698
|
+
visit(node.statements)
|
|
699
|
+
emit_newline unless @last_was_newline
|
|
700
|
+
dedent
|
|
701
|
+
else
|
|
702
|
+
emit_newline
|
|
703
|
+
end
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
# --- Method calls ---
|
|
707
|
+
|
|
708
|
+
def visit_call_node(node)
|
|
709
|
+
# Handle special operator calls
|
|
710
|
+
name = node.name.to_s
|
|
711
|
+
|
|
712
|
+
if node.receiver
|
|
713
|
+
visit(node.receiver)
|
|
714
|
+
|
|
715
|
+
if name == "[]"
|
|
716
|
+
emit("[")
|
|
717
|
+
visit(node.arguments) if node.arguments
|
|
718
|
+
emit("]")
|
|
719
|
+
visit(node.block) if node.block
|
|
720
|
+
return
|
|
721
|
+
elsif name == "[]="
|
|
722
|
+
emit("[")
|
|
723
|
+
args = node.arguments.arguments
|
|
724
|
+
visit(args.first)
|
|
725
|
+
emit("] = ")
|
|
726
|
+
visit(args.last)
|
|
727
|
+
return
|
|
728
|
+
end
|
|
729
|
+
|
|
730
|
+
# Binary operators
|
|
731
|
+
if binary_operator?(name) && node.arguments && node.arguments.arguments.size == 1
|
|
732
|
+
emit(" #{name} ")
|
|
733
|
+
visit(node.arguments.arguments.first)
|
|
734
|
+
visit(node.block) if node.block
|
|
735
|
+
return
|
|
736
|
+
end
|
|
737
|
+
|
|
738
|
+
# Unary operators
|
|
739
|
+
if unary_operator?(name) && (!node.arguments || node.arguments.arguments.empty?)
|
|
740
|
+
emit(name)
|
|
741
|
+
return
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
# Safe navigation
|
|
745
|
+
if node.call_operator_loc && node.call_operator_loc.slice == "&."
|
|
746
|
+
emit("&.")
|
|
747
|
+
else
|
|
748
|
+
emit(".")
|
|
749
|
+
end
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
emit(name)
|
|
753
|
+
|
|
754
|
+
if node.opening_loc
|
|
755
|
+
# Explicit parentheses in source
|
|
756
|
+
emit("(")
|
|
757
|
+
visit(node.arguments) if node.arguments
|
|
758
|
+
emit(")")
|
|
759
|
+
elsif node.arguments
|
|
760
|
+
# No parentheses in source — preserve that style
|
|
761
|
+
emit(" ")
|
|
762
|
+
visit(node.arguments)
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
visit(node.block) if node.block
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
def binary_operator?(name)
|
|
769
|
+
%w[+ - * / % ** == != < > <= >= <=> << >> & | ^ =~ !~ === .. ...].include?(name)
|
|
770
|
+
end
|
|
771
|
+
|
|
772
|
+
def unary_operator?(name)
|
|
773
|
+
%w[-@ +@].include?(name)
|
|
774
|
+
end
|
|
775
|
+
|
|
776
|
+
def visit_arguments_node(node)
|
|
777
|
+
node.arguments.each_with_index do |arg, i|
|
|
778
|
+
visit(arg)
|
|
779
|
+
emit(", ") if i < node.arguments.size - 1
|
|
780
|
+
end
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
# --- Blocks ---
|
|
784
|
+
|
|
785
|
+
def visit_block_node(node)
|
|
786
|
+
# Determine brace vs do/end style
|
|
787
|
+
single_line = single_line_block?(node)
|
|
788
|
+
|
|
789
|
+
if single_line
|
|
790
|
+
emit(" { ")
|
|
791
|
+
if node.parameters
|
|
792
|
+
emit("|")
|
|
793
|
+
visit(node.parameters)
|
|
794
|
+
emit("| ")
|
|
795
|
+
end
|
|
796
|
+
if node.body.is_a?(Prism::StatementsNode)
|
|
797
|
+
visit_list(node.body.body, separator: "; ")
|
|
798
|
+
elsif node.body
|
|
799
|
+
visit(node.body)
|
|
800
|
+
end
|
|
801
|
+
emit(" }")
|
|
802
|
+
else
|
|
803
|
+
emit(" do")
|
|
804
|
+
if node.parameters
|
|
805
|
+
emit(" |")
|
|
806
|
+
visit(node.parameters)
|
|
807
|
+
emit("|")
|
|
808
|
+
end
|
|
809
|
+
emit_newline
|
|
810
|
+
if node.body
|
|
811
|
+
indent
|
|
812
|
+
visit(node.body)
|
|
813
|
+
emit_newline unless @last_was_newline
|
|
814
|
+
dedent
|
|
815
|
+
end
|
|
816
|
+
emit("end")
|
|
817
|
+
end
|
|
818
|
+
end
|
|
819
|
+
|
|
820
|
+
def single_line_block?(node)
|
|
821
|
+
return false unless node.body
|
|
822
|
+
# If the block fits on one line in source, keep it that way
|
|
823
|
+
node.location.start_line == node.location.end_line
|
|
824
|
+
end
|
|
825
|
+
|
|
826
|
+
def visit_block_parameters_node(node)
|
|
827
|
+
params = node.parameters
|
|
828
|
+
if params
|
|
829
|
+
visit(params)
|
|
830
|
+
end
|
|
831
|
+
end
|
|
832
|
+
|
|
833
|
+
def visit_lambda_node(node)
|
|
834
|
+
emit("->")
|
|
835
|
+
if node.parameters
|
|
836
|
+
emit("(")
|
|
837
|
+
visit(node.parameters)
|
|
838
|
+
emit(")")
|
|
839
|
+
end
|
|
840
|
+
|
|
841
|
+
if node.body && node.location.start_line == node.location.end_line
|
|
842
|
+
emit(" { ")
|
|
843
|
+
visit(node.body)
|
|
844
|
+
emit(" }")
|
|
845
|
+
else
|
|
846
|
+
emit(" do")
|
|
847
|
+
emit_newline
|
|
848
|
+
if node.body
|
|
849
|
+
indent
|
|
850
|
+
visit(node.body)
|
|
851
|
+
emit_newline unless @last_was_newline
|
|
852
|
+
dedent
|
|
853
|
+
end
|
|
854
|
+
emit("end")
|
|
855
|
+
end
|
|
856
|
+
end
|
|
857
|
+
|
|
858
|
+
# --- Exception handling ---
|
|
859
|
+
|
|
860
|
+
def visit_begin_node(node)
|
|
861
|
+
emit("begin")
|
|
862
|
+
emit_newline
|
|
863
|
+
|
|
864
|
+
if node.statements
|
|
865
|
+
indent
|
|
866
|
+
visit(node.statements)
|
|
867
|
+
emit_newline unless @last_was_newline
|
|
868
|
+
dedent
|
|
869
|
+
end
|
|
870
|
+
|
|
871
|
+
visit(node.rescue_clause) if node.rescue_clause
|
|
872
|
+
|
|
873
|
+
if node.else_clause
|
|
874
|
+
visit_else_node(node.else_clause)
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
if node.ensure_clause
|
|
878
|
+
visit(node.ensure_clause)
|
|
879
|
+
end
|
|
880
|
+
|
|
881
|
+
emit("end")
|
|
882
|
+
end
|
|
883
|
+
|
|
884
|
+
def visit_rescue_node(node)
|
|
885
|
+
emit("rescue")
|
|
886
|
+
if node.exceptions && !node.exceptions.empty?
|
|
887
|
+
emit(" ")
|
|
888
|
+
visit_list(node.exceptions, separator: ", ")
|
|
889
|
+
end
|
|
890
|
+
if node.reference
|
|
891
|
+
emit(" => ")
|
|
892
|
+
visit(node.reference)
|
|
893
|
+
end
|
|
894
|
+
emit_newline
|
|
895
|
+
|
|
896
|
+
if node.statements
|
|
897
|
+
indent
|
|
898
|
+
visit(node.statements)
|
|
899
|
+
emit_newline unless @last_was_newline
|
|
900
|
+
dedent
|
|
901
|
+
end
|
|
902
|
+
|
|
903
|
+
visit(node.subsequent) if node.subsequent
|
|
904
|
+
end
|
|
905
|
+
|
|
906
|
+
def visit_ensure_node(node)
|
|
907
|
+
emit("ensure")
|
|
908
|
+
emit_newline
|
|
909
|
+
|
|
910
|
+
if node.statements
|
|
911
|
+
indent
|
|
912
|
+
visit(node.statements)
|
|
913
|
+
emit_newline unless @last_was_newline
|
|
914
|
+
dedent
|
|
915
|
+
end
|
|
916
|
+
end
|
|
917
|
+
|
|
918
|
+
def visit_rescue_modifier_node(node)
|
|
919
|
+
visit(node.expression)
|
|
920
|
+
emit(" rescue ")
|
|
921
|
+
visit(node.rescue_expression)
|
|
922
|
+
end
|
|
923
|
+
|
|
924
|
+
# --- Return / Break / Next ---
|
|
925
|
+
|
|
926
|
+
def visit_return_node(node)
|
|
927
|
+
emit("return")
|
|
928
|
+
if node.arguments
|
|
929
|
+
emit(" ")
|
|
930
|
+
visit(node.arguments)
|
|
931
|
+
end
|
|
932
|
+
end
|
|
933
|
+
|
|
934
|
+
def visit_break_node(node)
|
|
935
|
+
emit("break")
|
|
936
|
+
if node.arguments
|
|
937
|
+
emit(" ")
|
|
938
|
+
visit(node.arguments)
|
|
939
|
+
end
|
|
940
|
+
end
|
|
941
|
+
|
|
942
|
+
def visit_next_node(node)
|
|
943
|
+
emit("next")
|
|
944
|
+
if node.arguments
|
|
945
|
+
emit(" ")
|
|
946
|
+
visit(node.arguments)
|
|
947
|
+
end
|
|
948
|
+
end
|
|
949
|
+
|
|
950
|
+
def visit_yield_node(node)
|
|
951
|
+
emit("yield")
|
|
952
|
+
if node.arguments
|
|
953
|
+
emit("(")
|
|
954
|
+
visit(node.arguments)
|
|
955
|
+
emit(")")
|
|
956
|
+
end
|
|
957
|
+
end
|
|
958
|
+
|
|
959
|
+
# --- Array / Hash ---
|
|
960
|
+
|
|
961
|
+
def visit_array_node(node)
|
|
962
|
+
# Detect %w / %i literals
|
|
963
|
+
src = node.location.slice
|
|
964
|
+
if src.start_with?("%w") || src.start_with?("%i") || src.start_with?("%W") || src.start_with?("%I")
|
|
965
|
+
emit(src)
|
|
966
|
+
return
|
|
967
|
+
end
|
|
968
|
+
|
|
969
|
+
emit("[")
|
|
970
|
+
node.elements.each_with_index do |elem, i|
|
|
971
|
+
visit(elem)
|
|
972
|
+
emit(", ") if i < node.elements.size - 1
|
|
973
|
+
end
|
|
974
|
+
emit("]")
|
|
975
|
+
end
|
|
976
|
+
|
|
977
|
+
def visit_hash_node(node)
|
|
978
|
+
emit("{")
|
|
979
|
+
unless node.elements.empty?
|
|
980
|
+
emit(" ")
|
|
981
|
+
node.elements.each_with_index do |elem, i|
|
|
982
|
+
visit(elem)
|
|
983
|
+
emit(", ") if i < node.elements.size - 1
|
|
984
|
+
end
|
|
985
|
+
emit(" ")
|
|
986
|
+
end
|
|
987
|
+
emit("}")
|
|
988
|
+
end
|
|
989
|
+
|
|
990
|
+
def visit_assoc_node(node)
|
|
991
|
+
visit(node.key)
|
|
992
|
+
# Symbol key with shorthand: { foo: bar } vs { "foo" => bar }
|
|
993
|
+
if node.key.is_a?(Prism::SymbolNode) && node.operator_loc.nil?
|
|
994
|
+
emit(" ")
|
|
995
|
+
else
|
|
996
|
+
emit(" => ")
|
|
997
|
+
end
|
|
998
|
+
visit(node.value)
|
|
999
|
+
end
|
|
1000
|
+
|
|
1001
|
+
def visit_assoc_splat_node(node)
|
|
1002
|
+
emit("**")
|
|
1003
|
+
visit(node.value) if node.value
|
|
1004
|
+
end
|
|
1005
|
+
|
|
1006
|
+
def visit_splat_node(node)
|
|
1007
|
+
emit("*")
|
|
1008
|
+
visit(node.expression) if node.expression
|
|
1009
|
+
end
|
|
1010
|
+
|
|
1011
|
+
# --- Range ---
|
|
1012
|
+
|
|
1013
|
+
def visit_range_node(node)
|
|
1014
|
+
visit(node.left) if node.left
|
|
1015
|
+
if node.exclude_end?
|
|
1016
|
+
emit("...")
|
|
1017
|
+
else
|
|
1018
|
+
emit("..")
|
|
1019
|
+
end
|
|
1020
|
+
visit(node.right) if node.right
|
|
1021
|
+
end
|
|
1022
|
+
|
|
1023
|
+
# --- Assignments ---
|
|
1024
|
+
|
|
1025
|
+
def visit_multi_write_node(node)
|
|
1026
|
+
node.lefts.each_with_index do |target, i|
|
|
1027
|
+
visit(target)
|
|
1028
|
+
emit(", ") if i < node.lefts.size - 1
|
|
1029
|
+
end
|
|
1030
|
+
if node.rest
|
|
1031
|
+
emit(", ") unless node.lefts.empty?
|
|
1032
|
+
visit(node.rest)
|
|
1033
|
+
end
|
|
1034
|
+
emit(" = ")
|
|
1035
|
+
visit(node.value)
|
|
1036
|
+
end
|
|
1037
|
+
|
|
1038
|
+
# --- Require ---
|
|
1039
|
+
|
|
1040
|
+
def visit_call_or_write_node(node)
|
|
1041
|
+
emit(node.location.slice)
|
|
1042
|
+
end
|
|
1043
|
+
|
|
1044
|
+
# --- Super ---
|
|
1045
|
+
|
|
1046
|
+
def visit_super_node(node)
|
|
1047
|
+
emit("super")
|
|
1048
|
+
if node.arguments
|
|
1049
|
+
emit("(")
|
|
1050
|
+
visit(node.arguments)
|
|
1051
|
+
emit(")")
|
|
1052
|
+
elsif node.opening_loc
|
|
1053
|
+
emit("()")
|
|
1054
|
+
end
|
|
1055
|
+
visit(node.block) if node.block
|
|
1056
|
+
end
|
|
1057
|
+
|
|
1058
|
+
def visit_forwarding_super_node(node)
|
|
1059
|
+
emit("super")
|
|
1060
|
+
end
|
|
1061
|
+
|
|
1062
|
+
# --- Defined? ---
|
|
1063
|
+
|
|
1064
|
+
def visit_defined_node(node)
|
|
1065
|
+
emit("defined?(")
|
|
1066
|
+
visit(node.value)
|
|
1067
|
+
emit(")")
|
|
1068
|
+
end
|
|
1069
|
+
|
|
1070
|
+
# --- Alias ---
|
|
1071
|
+
|
|
1072
|
+
def visit_alias_method_node(node)
|
|
1073
|
+
emit("alias ")
|
|
1074
|
+
visit(node.new_name)
|
|
1075
|
+
emit(" ")
|
|
1076
|
+
visit(node.old_name)
|
|
1077
|
+
end
|
|
1078
|
+
|
|
1079
|
+
# --- Parentheses ---
|
|
1080
|
+
|
|
1081
|
+
def visit_parentheses_node(node)
|
|
1082
|
+
emit("(")
|
|
1083
|
+
visit(node.body) if node.body
|
|
1084
|
+
emit(")")
|
|
1085
|
+
end
|
|
1086
|
+
|
|
1087
|
+
# --- Keyword Hash (bare hash in arguments) ---
|
|
1088
|
+
|
|
1089
|
+
def visit_keyword_hash_node(node)
|
|
1090
|
+
node.elements.each_with_index do |elem, i|
|
|
1091
|
+
visit(elem)
|
|
1092
|
+
emit(", ") if i < node.elements.size - 1
|
|
1093
|
+
end
|
|
1094
|
+
end
|
|
1095
|
+
|
|
1096
|
+
# --- Embedded statements in strings ---
|
|
1097
|
+
|
|
1098
|
+
def visit_embedded_statements_node(node)
|
|
1099
|
+
emit(node.location.slice)
|
|
1100
|
+
end
|
|
1101
|
+
|
|
1102
|
+
# --- Numbered/it parameters ---
|
|
1103
|
+
|
|
1104
|
+
def visit_numbered_parameters_node(node)
|
|
1105
|
+
# Implicit, no output
|
|
1106
|
+
end
|
|
1107
|
+
|
|
1108
|
+
def visit_it_parameters_node(node)
|
|
1109
|
+
# Implicit, no output
|
|
1110
|
+
end
|
|
1111
|
+
|
|
1112
|
+
def visit_it_local_variable_read_node(node)
|
|
1113
|
+
emit("it")
|
|
1114
|
+
end
|
|
1115
|
+
|
|
1116
|
+
# --- And/Or ---
|
|
1117
|
+
|
|
1118
|
+
def visit_and_node(node)
|
|
1119
|
+
visit(node.left)
|
|
1120
|
+
# Use && vs 'and' based on source
|
|
1121
|
+
if node.operator_loc.slice == "and"
|
|
1122
|
+
emit(" and ")
|
|
1123
|
+
else
|
|
1124
|
+
emit(" && ")
|
|
1125
|
+
end
|
|
1126
|
+
visit(node.right)
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
def visit_or_node(node)
|
|
1130
|
+
visit(node.left)
|
|
1131
|
+
if node.operator_loc.slice == "or"
|
|
1132
|
+
emit(" or ")
|
|
1133
|
+
else
|
|
1134
|
+
emit(" || ")
|
|
1135
|
+
end
|
|
1136
|
+
visit(node.right)
|
|
1137
|
+
end
|
|
1138
|
+
|
|
1139
|
+
# --- Not ---
|
|
1140
|
+
|
|
1141
|
+
def visit_call_and_write_node(node)
|
|
1142
|
+
emit(node.location.slice)
|
|
1143
|
+
end
|
|
1144
|
+
|
|
1145
|
+
# --- Misc ---
|
|
1146
|
+
|
|
1147
|
+
def visit_frozen_string_literal_comment(node)
|
|
1148
|
+
emit(node.location.slice)
|
|
1149
|
+
end
|
|
1150
|
+
|
|
1151
|
+
def visit_heredoc_node(node)
|
|
1152
|
+
emit(node.location.slice)
|
|
1153
|
+
end
|
|
1154
|
+
|
|
1155
|
+
def visit_match_predicate_node(node)
|
|
1156
|
+
visit(node.value)
|
|
1157
|
+
emit(" in ")
|
|
1158
|
+
visit(node.pattern)
|
|
1159
|
+
end
|
|
1160
|
+
|
|
1161
|
+
def visit_match_required_node(node)
|
|
1162
|
+
visit(node.value)
|
|
1163
|
+
emit(" => ")
|
|
1164
|
+
visit(node.pattern)
|
|
1165
|
+
end
|
|
1166
|
+
|
|
1167
|
+
# Pattern nodes
|
|
1168
|
+
def visit_find_pattern_node(node)
|
|
1169
|
+
emit(node.location.slice)
|
|
1170
|
+
end
|
|
1171
|
+
|
|
1172
|
+
def visit_array_pattern_node(node)
|
|
1173
|
+
emit(node.location.slice)
|
|
1174
|
+
end
|
|
1175
|
+
|
|
1176
|
+
def visit_hash_pattern_node(node)
|
|
1177
|
+
emit(node.location.slice)
|
|
1178
|
+
end
|
|
1179
|
+
|
|
1180
|
+
def visit_pinned_variable_node(node)
|
|
1181
|
+
emit("^")
|
|
1182
|
+
visit(node.variable)
|
|
1183
|
+
end
|
|
1184
|
+
|
|
1185
|
+
def visit_pinned_expression_node(node)
|
|
1186
|
+
emit("^(")
|
|
1187
|
+
visit(node.expression)
|
|
1188
|
+
emit(")")
|
|
1189
|
+
end
|
|
1190
|
+
|
|
1191
|
+
def visit_capture_pattern_node(node)
|
|
1192
|
+
visit(node.value)
|
|
1193
|
+
emit(" => ")
|
|
1194
|
+
visit(node.target)
|
|
1195
|
+
end
|
|
1196
|
+
|
|
1197
|
+
def visit_alternation_pattern_node(node)
|
|
1198
|
+
visit(node.left)
|
|
1199
|
+
emit(" | ")
|
|
1200
|
+
visit(node.right)
|
|
1201
|
+
end
|
|
1202
|
+
|
|
1203
|
+
# --- Ternary (inline if) ---
|
|
1204
|
+
|
|
1205
|
+
def visit_if_node_ternary(node)
|
|
1206
|
+
visit(node.predicate)
|
|
1207
|
+
emit(" ? ")
|
|
1208
|
+
visit(node.statements)
|
|
1209
|
+
emit(" : ")
|
|
1210
|
+
visit(node.else_clause.statements) if node.else_clause
|
|
1211
|
+
end
|
|
1212
|
+
end
|
|
1213
|
+
end
|
|
1214
|
+
end
|