mt-lang 0.2.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 (258) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/AUTHORS +3 -0
  4. data/Gemfile +19 -0
  5. data/Gemfile.lock +82 -0
  6. data/LICENSE +21 -0
  7. data/README.md +1208 -0
  8. data/Rakefile +332 -0
  9. data/bin/mtc +8 -0
  10. data/bin/profile-mtc-checks +133 -0
  11. data/bin/tracy-profiler +0 -0
  12. data/docs/build-guide.md +536 -0
  13. data/docs/index.html +2778 -0
  14. data/docs/language-design.md +1519 -0
  15. data/docs/language-manual.md +1534 -0
  16. data/lib/milk_tea/base.rb +49 -0
  17. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
  18. data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
  19. data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
  20. data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
  21. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
  22. data/lib/milk_tea/bindings/bindgen.rb +207 -0
  23. data/lib/milk_tea/bindings/cli.rb +121 -0
  24. data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
  25. data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
  26. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
  27. data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
  28. data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
  29. data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
  30. data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
  31. data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
  32. data/lib/milk_tea/bindings/steamworks.rb +629 -0
  33. data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
  34. data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
  35. data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
  36. data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
  37. data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
  38. data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
  39. data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
  40. data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
  41. data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
  42. data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
  43. data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
  44. data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
  45. data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
  46. data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
  47. data/lib/milk_tea/bindings.rb +23 -0
  48. data/lib/milk_tea/core/ast.rb +311 -0
  49. data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
  50. data/lib/milk_tea/core/binding_types.rb +18 -0
  51. data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
  52. data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
  53. data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
  54. data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
  55. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
  56. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
  57. data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
  58. data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
  59. data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
  60. data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
  61. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
  62. data/lib/milk_tea/core/c_backend/statements.rb +563 -0
  63. data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
  64. data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
  65. data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
  66. data/lib/milk_tea/core/c_backend.rb +287 -0
  67. data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
  68. data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
  69. data/lib/milk_tea/core/compile_time.rb +329 -0
  70. data/lib/milk_tea/core/control_flow/builder.rb +582 -0
  71. data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
  72. data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
  73. data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
  74. data/lib/milk_tea/core/control_flow/graph.rb +90 -0
  75. data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
  76. data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
  77. data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
  78. data/lib/milk_tea/core/control_flow/termination.rb +31 -0
  79. data/lib/milk_tea/core/control_flow.rb +34 -0
  80. data/lib/milk_tea/core/cst.rb +48 -0
  81. data/lib/milk_tea/core/cst_builder.rb +19 -0
  82. data/lib/milk_tea/core/ir.rb +85 -0
  83. data/lib/milk_tea/core/keywords.rb +97 -0
  84. data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
  85. data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
  86. data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
  87. data/lib/milk_tea/core/lexer/indentation.rb +62 -0
  88. data/lib/milk_tea/core/lexer/numbers.rb +96 -0
  89. data/lib/milk_tea/core/lexer/recovery.rb +32 -0
  90. data/lib/milk_tea/core/lexer/strings.rb +167 -0
  91. data/lib/milk_tea/core/lexer/symbols.rb +71 -0
  92. data/lib/milk_tea/core/lexer/trivia.rb +53 -0
  93. data/lib/milk_tea/core/lexer.rb +430 -0
  94. data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
  95. data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
  96. data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
  97. data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
  98. data/lib/milk_tea/core/lowering/async.rb +714 -0
  99. data/lib/milk_tea/core/lowering/block.rb +1052 -0
  100. data/lib/milk_tea/core/lowering/calls.rb +1565 -0
  101. data/lib/milk_tea/core/lowering/declarations.rb +214 -0
  102. data/lib/milk_tea/core/lowering/dyn.rb +206 -0
  103. data/lib/milk_tea/core/lowering/events.rb +1054 -0
  104. data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
  105. data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
  106. data/lib/milk_tea/core/lowering/functions.rb +242 -0
  107. data/lib/milk_tea/core/lowering/loops.rb +1087 -0
  108. data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
  109. data/lib/milk_tea/core/lowering/proc.rb +419 -0
  110. data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
  111. data/lib/milk_tea/core/lowering/scans.rb +220 -0
  112. data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
  113. data/lib/milk_tea/core/lowering/utils.rb +1453 -0
  114. data/lib/milk_tea/core/lowering.rb +378 -0
  115. data/lib/milk_tea/core/module_binder.rb +181 -0
  116. data/lib/milk_tea/core/module_loader/errors.rb +21 -0
  117. data/lib/milk_tea/core/module_loader.rb +479 -0
  118. data/lib/milk_tea/core/module_path_resolver.rb +153 -0
  119. data/lib/milk_tea/core/module_roots.rb +88 -0
  120. data/lib/milk_tea/core/parser/attributes.rb +71 -0
  121. data/lib/milk_tea/core/parser/blocks.rb +119 -0
  122. data/lib/milk_tea/core/parser/declarations.rb +749 -0
  123. data/lib/milk_tea/core/parser/expressions.rb +624 -0
  124. data/lib/milk_tea/core/parser/recovery.rb +131 -0
  125. data/lib/milk_tea/core/parser/statements.rb +756 -0
  126. data/lib/milk_tea/core/parser/types.rb +271 -0
  127. data/lib/milk_tea/core/parser.rb +400 -0
  128. data/lib/milk_tea/core/prelude_installer.rb +29 -0
  129. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
  130. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
  131. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
  132. data/lib/milk_tea/core/pretty_printer.rb +17 -0
  133. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
  134. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
  135. data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
  136. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
  137. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
  138. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
  139. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
  140. data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
  141. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
  142. data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
  143. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
  144. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
  145. data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
  146. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
  147. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
  148. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
  149. data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
  150. data/lib/milk_tea/core/token.rb +26 -0
  151. data/lib/milk_tea/core/token_stream.rb +30 -0
  152. data/lib/milk_tea/core/types/layout.rb +243 -0
  153. data/lib/milk_tea/core/types/predicates.rb +609 -0
  154. data/lib/milk_tea/core/types/registry.rb +83 -0
  155. data/lib/milk_tea/core/types/types.rb +1696 -0
  156. data/lib/milk_tea/core/types/visitor.rb +442 -0
  157. data/lib/milk_tea/core.rb +25 -0
  158. data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
  159. data/lib/milk_tea/dap/protocol.rb +58 -0
  160. data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
  161. data/lib/milk_tea/dap/server/debug_map.rb +291 -0
  162. data/lib/milk_tea/dap/server/handlers.rb +374 -0
  163. data/lib/milk_tea/dap/server/launch.rb +261 -0
  164. data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
  165. data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
  166. data/lib/milk_tea/dap/server/utilities.rb +160 -0
  167. data/lib/milk_tea/dap/server/wire.rb +55 -0
  168. data/lib/milk_tea/dap/server.rb +131 -0
  169. data/lib/milk_tea/dap/session.rb +152 -0
  170. data/lib/milk_tea/dap.rb +6 -0
  171. data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
  172. data/lib/milk_tea/lsp/diagnostics.rb +611 -0
  173. data/lib/milk_tea/lsp/protocol.rb +104 -0
  174. data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
  175. data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
  176. data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
  177. data/lib/milk_tea/lsp/server/completion.rb +1099 -0
  178. data/lib/milk_tea/lsp/server/configuration.rb +167 -0
  179. data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
  180. data/lib/milk_tea/lsp/server/definition.rb +779 -0
  181. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
  182. data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
  183. data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
  184. data/lib/milk_tea/lsp/server/formatting.rb +575 -0
  185. data/lib/milk_tea/lsp/server/hover.rb +1465 -0
  186. data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
  187. data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
  188. data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
  189. data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
  190. data/lib/milk_tea/lsp/server/progress.rb +47 -0
  191. data/lib/milk_tea/lsp/server/references.rb +433 -0
  192. data/lib/milk_tea/lsp/server/rename.rb +598 -0
  193. data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
  194. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
  195. data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
  196. data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
  197. data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
  198. data/lib/milk_tea/lsp/server/utilities.rb +520 -0
  199. data/lib/milk_tea/lsp/server.rb +415 -0
  200. data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
  201. data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
  202. data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
  203. data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
  204. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
  205. data/lib/milk_tea/lsp/workspace/store.rb +154 -0
  206. data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
  207. data/lib/milk_tea/lsp/workspace.rb +127 -0
  208. data/lib/milk_tea/lsp.rb +13 -0
  209. data/lib/milk_tea/packages/atomic_write.rb +63 -0
  210. data/lib/milk_tea/packages/dependency_solver.rb +194 -0
  211. data/lib/milk_tea/packages/graph.rb +139 -0
  212. data/lib/milk_tea/packages/lock.rb +421 -0
  213. data/lib/milk_tea/packages/manager_cli.rb +485 -0
  214. data/lib/milk_tea/packages/manifest.rb +356 -0
  215. data/lib/milk_tea/packages/manifest_editor.rb +134 -0
  216. data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
  217. data/lib/milk_tea/packages/registry_store.rb +420 -0
  218. data/lib/milk_tea/packages/services.rb +41 -0
  219. data/lib/milk_tea/packages/source_cache.rb +71 -0
  220. data/lib/milk_tea/packages/source_fetcher.rb +124 -0
  221. data/lib/milk_tea/packages/source_resolver.rb +389 -0
  222. data/lib/milk_tea/packages/version.rb +164 -0
  223. data/lib/milk_tea/packages.rb +16 -0
  224. data/lib/milk_tea/tooling/asset_pack.rb +141 -0
  225. data/lib/milk_tea/tooling/build.rb +1124 -0
  226. data/lib/milk_tea/tooling/build_cache.rb +240 -0
  227. data/lib/milk_tea/tooling/cli.rb +2688 -0
  228. data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
  229. data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
  230. data/lib/milk_tea/tooling/debug_map.rb +248 -0
  231. data/lib/milk_tea/tooling/docs_app.rb +369 -0
  232. data/lib/milk_tea/tooling/error_formatter.rb +86 -0
  233. data/lib/milk_tea/tooling/formatter.rb +787 -0
  234. data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
  235. data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
  236. data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
  237. data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
  238. data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
  239. data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
  240. data/lib/milk_tea/tooling/linter/rules.rb +593 -0
  241. data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
  242. data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
  243. data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
  244. data/lib/milk_tea/tooling/linter.rb +798 -0
  245. data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
  246. data/lib/milk_tea/tooling/public/css/docs.css +166 -0
  247. data/lib/milk_tea/tooling/public/js/docs.js +94 -0
  248. data/lib/milk_tea/tooling/run.rb +408 -0
  249. data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
  250. data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
  251. data/lib/milk_tea/tooling/views/404.erb +7 -0
  252. data/lib/milk_tea/tooling/views/index.erb +87 -0
  253. data/lib/milk_tea/tooling/views/layout.erb +69 -0
  254. data/lib/milk_tea/tooling/views/module.erb +97 -0
  255. data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
  256. data/lib/milk_tea/tooling.rb +20 -0
  257. data/lib/milk_tea.rb +8 -0
  258. metadata +426 -0
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module PrettyPrinter
5
+ class BaseFormatter
6
+ INDENT = " "
7
+
8
+ def initialize
9
+ @lines = []
10
+ @indent = 0
11
+ end
12
+
13
+ private
14
+
15
+ def finish
16
+ @lines.join("\n") + "\n"
17
+ end
18
+
19
+ def line(text = "")
20
+ content = "#{INDENT * @indent}#{text}"
21
+ @lines << content.rstrip
22
+ end
23
+
24
+ def blank_line
25
+ @lines << "" unless @lines.empty? || @lines.last.empty?
26
+ end
27
+
28
+ def with_indent
29
+ @indent += 1
30
+ yield
31
+ ensure
32
+ @indent -= 1
33
+ end
34
+
35
+ # Renders into the line buffer via the given block, then extracts and
36
+ # returns those lines (removing them from the buffer). Used to embed a
37
+ # multi-line block (e.g. a proc body) inside an expression string while
38
+ # keeping correct absolute indentation.
39
+ def capture_lines
40
+ start = @lines.length
41
+ yield
42
+ captured = @lines[start..] || []
43
+ @lines.slice!(start..)
44
+ captured
45
+ end
46
+
47
+ def binding_name(name, linkage_name)
48
+ return linkage_name if name.nil? || name.empty? || name == linkage_name
49
+
50
+ "#{name} as #{linkage_name}"
51
+ end
52
+
53
+ def precedence(operator)
54
+ case operator
55
+ when "or"
56
+ 10
57
+ when "and"
58
+ 20
59
+ when "|"
60
+ 30
61
+ when "^"
62
+ 35
63
+ when "&"
64
+ 40
65
+ when "==", "!="
66
+ 50
67
+ when "<", "<=", ">", ">="
68
+ 55
69
+ when "<<", ">>"
70
+ 60
71
+ when "+", "-"
72
+ 65
73
+ when "*", "/", "%"
74
+ 70
75
+ else
76
+ 0
77
+ end
78
+ end
79
+
80
+ def wrap(text, parent_precedence, current_precedence)
81
+ return text if current_precedence >= parent_precedence
82
+
83
+ "(#{text})"
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,300 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module PrettyPrinter
5
+ class IRFormatter < BaseFormatter
6
+ IF_EXPRESSION_PRECEDENCE = 5
7
+ POSTFIX_PRECEDENCE = 90
8
+ UNARY_PRECEDENCE = 80
9
+
10
+ def format(node)
11
+ emit_program(node)
12
+ finish
13
+ end
14
+
15
+ private
16
+
17
+ def emit_program(program)
18
+ module_name = program.module_name || "(anonymous)"
19
+ line("program #{module_name}")
20
+ emit_section("includes", program.includes) { |include_directive| line("include #{include_directive.header}") }
21
+ emit_section("constants", program.constants) { |constant| emit_constant(constant) }
22
+ emit_section("globals", program.globals) { |global_directive| emit_global(global_directive) }
23
+ emit_section("opaques", program.opaques) { |opaque_declaration| emit_opaque(opaque_declaration) }
24
+ emit_section("structs", program.structs) { |struct_decl| emit_struct(struct_decl) }
25
+ emit_section("unions", program.unions) { |union_decl| emit_union(union_decl) }
26
+ emit_section("enums", program.enums) { |enum_decl| emit_enum(enum_decl) }
27
+ emit_section("variants", program.variants) { |variant_declaration| emit_variant(variant_declaration) }
28
+ emit_section("static_asserts", program.static_asserts) { |static_assert| emit_static_assert(static_assert) }
29
+ emit_section("functions", program.functions) { |function| emit_function(function) }
30
+ end
31
+
32
+ def emit_section(title, items)
33
+ return if items.empty?
34
+
35
+ blank_line
36
+ line("#{title}:")
37
+ with_indent do
38
+ items.each_with_index do |item, index|
39
+ yield(item)
40
+ blank_line if index < (items.length - 1)
41
+ end
42
+ end
43
+ end
44
+
45
+ def emit_constant(constant)
46
+ line("const #{binding_name(constant.name, constant.linkage_name)}: #{constant.type} = #{render_expression(constant.value)}")
47
+ end
48
+
49
+ def emit_global(global)
50
+ line("var #{binding_name(global.name, global.linkage_name)}: #{global.type} = #{render_expression(global.value)}")
51
+ end
52
+
53
+ def emit_opaque(opaque_decl)
54
+ line("opaque #{binding_name(opaque_decl.name, opaque_decl.linkage_name)}")
55
+ end
56
+
57
+ def emit_variant(variant_decl)
58
+ line("variant #{binding_name(variant_decl.name, variant_decl.linkage_name)}:")
59
+ with_indent do
60
+ variant_decl.arms.each do |arm|
61
+ text = binding_name(arm.name, arm.linkage_name)
62
+ unless arm.fields.empty?
63
+ field_texts = arm.fields.map { |f| "#{f.name}: #{f.type}" }
64
+ text += "(#{field_texts.join(', ')})"
65
+ end
66
+ line(text)
67
+ end
68
+ end
69
+ end
70
+
71
+ def emit_struct(struct_decl)
72
+ header = "struct #{binding_name(struct_decl.name, struct_decl.linkage_name)}"
73
+ params = []
74
+ if struct_decl.respond_to?(:lifetime_params) && struct_decl.lifetime_params&.any?
75
+ params.push(*struct_decl.lifetime_params)
76
+ end
77
+ modifiers = []
78
+ modifiers << "packed" if struct_decl.respond_to?(:packed) && struct_decl.packed
79
+ modifiers << "align(#{struct_decl.alignment})" if struct_decl.respond_to?(:alignment) && struct_decl.alignment
80
+ params.concat(modifiers) unless modifiers.empty?
81
+ header += " [#{params.join(', ')}]" unless params.empty?
82
+ header += ":"
83
+ line(header)
84
+ with_indent do
85
+ struct_decl.fields.each do |field|
86
+ line("#{field.name}: #{field.type}")
87
+ end
88
+ end
89
+ end
90
+
91
+ def emit_union(union_decl)
92
+ line("union #{binding_name(union_decl.name, union_decl.linkage_name)}:")
93
+ with_indent do
94
+ union_decl.fields.each do |field|
95
+ line("#{field.name}: #{field.type}")
96
+ end
97
+ end
98
+ end
99
+
100
+ def emit_enum(enum_decl)
101
+ kind = enum_decl.flags ? "flags" : "enum"
102
+ line("#{kind} #{binding_name(enum_decl.name, enum_decl.linkage_name)}: #{enum_decl.backing_type}")
103
+ with_indent do
104
+ enum_decl.members.each do |member|
105
+ line("#{binding_name(member.name, member.linkage_name)} = #{render_expression(member.value)}")
106
+ end
107
+ end
108
+ end
109
+
110
+ def emit_static_assert(static_assert)
111
+ line("static_assert(#{render_expression(static_assert.condition)}, #{render_expression(static_assert.message)})")
112
+ end
113
+
114
+ def emit_function(function)
115
+ header = "fn #{binding_name(function.name || function.linkage_name, function.linkage_name)}(#{function.params.map { |param| render_param(param) }.join(', ')}) -> #{function.return_type}"
116
+ header += " [entry]" if function.entry_point
117
+ header += ":"
118
+ line(header)
119
+ with_indent do
120
+ function.body.each do |statement|
121
+ emit_statement(statement)
122
+ end
123
+ end
124
+ end
125
+
126
+ def render_param(param)
127
+ type = param.pointer ? "ptr[#{param.type}]" : param.type.to_s
128
+ "#{binding_name(param.name, param.linkage_name)}: #{type}"
129
+ end
130
+
131
+ def emit_statement(statement)
132
+ case statement
133
+ when IR::LocalDecl
134
+ line("let #{binding_name(statement.name, statement.linkage_name)}: #{statement.type} = #{render_expression(statement.value)}")
135
+ when IR::Assignment
136
+ line("#{render_expression(statement.target)} #{statement.operator} #{render_expression(statement.value)}")
137
+ when IR::BlockStmt
138
+ line("block:")
139
+ with_indent do
140
+ statement.body.each { |nested| emit_statement(nested) }
141
+ end
142
+ when IR::IfStmt
143
+ line("if #{render_expression(statement.condition)}:")
144
+ with_indent do
145
+ statement.then_body.each { |nested| emit_statement(nested) }
146
+ end
147
+ if statement.else_body && !statement.else_body.empty?
148
+ line("else:")
149
+ with_indent do
150
+ statement.else_body.each { |nested| emit_statement(nested) }
151
+ end
152
+ end
153
+ when IR::SwitchStmt
154
+ line("switch #{render_expression(statement.expression)}:")
155
+ with_indent do
156
+ statement.cases.each do |switch_case|
157
+ if switch_case.is_a?(IR::SwitchDefaultCase)
158
+ line("default:")
159
+ else
160
+ line("case #{render_expression(switch_case.value)}:")
161
+ end
162
+ with_indent do
163
+ switch_case.body.each { |nested| emit_statement(nested) }
164
+ end
165
+ end
166
+ end
167
+ when IR::WhileStmt
168
+ line("while #{render_expression(statement.condition)}:")
169
+ with_indent do
170
+ statement.body.each { |nested| emit_statement(nested) }
171
+ end
172
+ when IR::ForStmt
173
+ init = render_for_clause_statement(statement.init)
174
+ post = render_for_clause_statement(statement.post)
175
+ line("for #{init}; #{render_expression(statement.condition)}; #{post}:")
176
+ with_indent do
177
+ statement.body.each { |nested| emit_statement(nested) }
178
+ end
179
+ when IR::BreakStmt
180
+ line("break")
181
+ when IR::ContinueStmt
182
+ line("continue")
183
+ when IR::GotoStmt
184
+ line("goto #{statement.label}")
185
+ when IR::LabelStmt
186
+ line("label #{statement.name}")
187
+ when IR::StaticAssert
188
+ emit_static_assert(statement)
189
+ when IR::ReturnStmt
190
+ line(statement.value ? "return #{render_expression(statement.value)}" : "return")
191
+ when IR::ExpressionStmt
192
+ line(render_expression(statement.expression))
193
+ else
194
+ raise ArgumentError, "unsupported IR statement #{statement.class.name}"
195
+ end
196
+ end
197
+
198
+ def render_expression(expression, parent_precedence = 0)
199
+ case expression
200
+ when IR::Name
201
+ expression.name
202
+ when IR::Member
203
+ operator = pointer_receiver_expression?(expression.receiver) ? "->" : "."
204
+ wrap("#{render_postfix(expression.receiver)}#{operator}#{expression.member}", parent_precedence, POSTFIX_PRECEDENCE)
205
+ when IR::Index
206
+ wrap("#{render_postfix(expression.receiver)}[#{render_expression(expression.index)}]", parent_precedence, POSTFIX_PRECEDENCE)
207
+ when IR::CheckedIndex
208
+ "checked_index<#{expression.receiver_type}>(#{render_expression(expression.receiver)}, #{render_expression(expression.index)})"
209
+ when IR::CheckedSpanIndex
210
+ "checked_span_index<#{expression.receiver_type}>(#{render_expression(expression.receiver)}, #{render_expression(expression.index)})"
211
+ when IR::NullableIndex
212
+ "nullable_index<#{expression.receiver_type}>(#{render_expression(expression.receiver)}, #{render_expression(expression.index)})"
213
+ when IR::NullableSpanIndex
214
+ "nullable_span_index<#{expression.receiver_type}>(#{render_expression(expression.receiver)}, #{render_expression(expression.index)})"
215
+ when IR::Call
216
+ wrap("#{expression.callee}(#{expression.arguments.map { |argument| render_expression(argument) }.join(', ')})", parent_precedence, POSTFIX_PRECEDENCE)
217
+ when IR::Unary
218
+ operand = render_expression(expression.operand, UNARY_PRECEDENCE)
219
+ text = expression.operator == "not" ? "not #{operand}" : "#{expression.operator}#{operand}"
220
+ wrap(text, parent_precedence, UNARY_PRECEDENCE)
221
+ when IR::Binary
222
+ current_precedence = precedence(expression.operator)
223
+ left = render_expression(expression.left, current_precedence)
224
+ right = render_expression(expression.right, current_precedence + 1)
225
+ wrap("#{left} #{expression.operator} #{right}", parent_precedence, current_precedence)
226
+ when IR::Conditional
227
+ condition = render_expression(expression.condition, IF_EXPRESSION_PRECEDENCE)
228
+ then_expression = render_expression(expression.then_expression, IF_EXPRESSION_PRECEDENCE)
229
+ else_expression = render_expression(expression.else_expression, IF_EXPRESSION_PRECEDENCE)
230
+ wrap("if #{condition}: #{then_expression} else: #{else_expression}", parent_precedence, IF_EXPRESSION_PRECEDENCE)
231
+ when IR::ReinterpretExpr
232
+ "reinterpret[#{expression.target_type} <- #{expression.source_type}](#{render_expression(expression.expression)})"
233
+ when IR::SizeofExpr
234
+ "size_of(#{expression.target_type})"
235
+ when IR::AlignofExpr
236
+ "align_of(#{expression.target_type})"
237
+ when IR::OffsetofExpr
238
+ "offset_of(#{expression.target_type}, #{expression.field})"
239
+ when IR::IntegerLiteral, IR::FloatLiteral
240
+ expression.value.to_s
241
+ when IR::StringLiteral
242
+ expression.cstring ? "c#{expression.value.inspect}" : expression.value.inspect
243
+ when IR::BooleanLiteral
244
+ expression.value ? "true" : "false"
245
+ when IR::NullLiteral
246
+ "null"
247
+ when IR::ZeroInit
248
+ "zero[#{expression.type}]"
249
+ when IR::AddressOf
250
+ wrap("&#{render_expression(expression.expression, UNARY_PRECEDENCE)}", parent_precedence, UNARY_PRECEDENCE)
251
+ when IR::Cast
252
+ "#{expression.target_type}<-#{render_expression(expression.expression)}"
253
+ when IR::AggregateLiteral
254
+ "#{expression.type}(#{expression.fields.map { |field| "#{field.name} = #{render_expression(field.value)}" }.join(', ')})"
255
+ when IR::VariantLiteral
256
+ if expression.fields.empty?
257
+ "#{expression.type}.#{expression.arm_name}"
258
+ else
259
+ "#{expression.type}.#{expression.arm_name}(#{expression.fields.map { |field| "#{field.name} = #{render_expression(field.value)}" }.join(', ')})"
260
+ end
261
+ when IR::ArrayLiteral
262
+ "#{expression.type}(#{expression.elements.map { |element| render_expression(element) }.join(', ')})"
263
+ else
264
+ raise ArgumentError, "unsupported IR expression #{expression.class.name}"
265
+ end
266
+ end
267
+
268
+ def render_for_clause_statement(statement)
269
+ case statement
270
+ when IR::LocalDecl
271
+ "#{statement.linkage_name}: #{statement.type} = #{render_expression(statement.value)}"
272
+ when IR::Assignment
273
+ "#{render_expression(statement.target)} #{statement.operator} #{render_expression(statement.value)}"
274
+ when IR::ExpressionStmt
275
+ render_expression(statement.expression)
276
+ else
277
+ raise ArgumentError, "unsupported for clause #{statement.class.name}"
278
+ end
279
+ end
280
+
281
+ def render_postfix(expression)
282
+ return render_expression(expression, POSTFIX_PRECEDENCE) if postfix_expression?(expression)
283
+
284
+ "(#{render_expression(expression)})"
285
+ end
286
+
287
+ def postfix_expression?(expression)
288
+ expression.is_a?(IR::Name) || expression.is_a?(IR::Member) || expression.is_a?(IR::Index) || expression.is_a?(IR::Call)
289
+ end
290
+
291
+ def pointer_receiver_expression?(expression)
292
+ (expression.is_a?(IR::Name) && expression.pointer) || (expression.respond_to?(:type) && pointer_type?(expression.type))
293
+ end
294
+
295
+ def pointer_type?(type)
296
+ type.is_a?(Types::GenericInstance) && type.name == "ptr" && type.arguments.length == 1
297
+ end
298
+ end
299
+ end
300
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "pretty_printer/base_formatter"
4
+ require_relative "pretty_printer/ast_formatter"
5
+ require_relative "pretty_printer/ir_formatter"
6
+
7
+ module MilkTea
8
+ module PrettyPrinter
9
+ def self.format_ast(node, trivia: [])
10
+ ASTFormatter.new.format(node, trivia:)
11
+ end
12
+
13
+ def self.format_ir(node)
14
+ IRFormatter.new.format(node)
15
+ end
16
+ end
17
+ end