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,412 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class CBackend
5
+ module ControlFlowEmission
6
+ private
7
+
8
+ INVERTED_BOOLEAN_OPS = {
9
+ "==" => "!=",
10
+ "!=" => "==",
11
+ "<" => ">=",
12
+ "<=" => ">",
13
+ ">" => "<=",
14
+ ">=" => "<",
15
+ }.freeze
16
+
17
+ def loop_continue_label_name(statements)
18
+ return if statements.empty?
19
+
20
+ label = statements.last
21
+ return unless label.is_a?(IR::LabelStmt)
22
+ return unless label.name.include?("loop_continue")
23
+
24
+ label.name
25
+ end
26
+
27
+ def loop_break_label_name(statements, remaining_statements)
28
+ return if remaining_statements.empty?
29
+
30
+ label = remaining_statements.first
31
+ return unless label.is_a?(IR::LabelStmt)
32
+ return unless statements_reference_label?(statements, label.name)
33
+
34
+ label.name
35
+ end
36
+
37
+ def statements_reference_label?(statements, label_name)
38
+ statements.any? { |statement| statement_references_label?(statement, label_name) }
39
+ end
40
+
41
+ def statement_references_label?(statement, label_name)
42
+ case statement
43
+ when IR::BlockStmt, IR::WhileStmt, IR::ForStmt
44
+ statements_reference_label?(statement.body, label_name)
45
+ when IR::IfStmt
46
+ statements_reference_label?(statement.then_body, label_name) ||
47
+ (statement.else_body && statements_reference_label?(statement.else_body, label_name))
48
+ when IR::SwitchStmt
49
+ statement.cases.any? { |switch_case| statements_reference_label?(switch_case.body, label_name) }
50
+ when IR::GotoStmt
51
+ statement.label == label_name
52
+ else
53
+ false
54
+ end
55
+ end
56
+
57
+ def statements_need_explicit_break_label_after_emission?(statements, label_name, loop_break_label_active:)
58
+ statements.any? do |statement|
59
+ statement_needs_explicit_break_label_after_emission?(statement, label_name, loop_break_label_active:)
60
+ end
61
+ end
62
+
63
+ def statement_needs_explicit_break_label_after_emission?(statement, label_name, loop_break_label_active:)
64
+ case statement
65
+ when IR::BlockStmt, IR::IfStmt
66
+ then_body = statement.is_a?(IR::IfStmt) ? statement.then_body : statement.body
67
+ else_body = statement.is_a?(IR::IfStmt) ? statement.else_body : nil
68
+ statements_need_explicit_break_label_after_emission?(then_body, label_name, loop_break_label_active:) ||
69
+ (else_body && statements_need_explicit_break_label_after_emission?(else_body, label_name, loop_break_label_active:))
70
+ when IR::WhileStmt, IR::ForStmt
71
+ statements_need_explicit_break_label_after_emission?(statement.body, label_name, loop_break_label_active: false)
72
+ when IR::SwitchStmt
73
+ return false unless statement_references_label?(statement, label_name)
74
+
75
+ active = loop_break_label_active && switch_emittable_as_if?(statement, loop_break_label: label_name)
76
+ statements_need_explicit_break_label_after_emission?(
77
+ statement.cases.flat_map(&:body),
78
+ label_name,
79
+ loop_break_label_active: active,
80
+ )
81
+ when IR::GotoStmt
82
+ statement.label == label_name && !loop_break_label_active
83
+ else
84
+ false
85
+ end
86
+ end
87
+
88
+ def switch_emittable_as_if?(statement, loop_break_label: nil)
89
+ return false unless loop_break_label
90
+ return false unless statement.exhaustive
91
+ return false unless statement.cases.length == 2
92
+ return false unless side_effect_free_expression?(statement.expression)
93
+ return false unless statement_references_label?(statement, loop_break_label)
94
+
95
+ statement.cases.count { |switch_case| switch_case.is_a?(IR::SwitchDefaultCase) } <= 1
96
+ end
97
+
98
+ def emit_switch_as_if_statement(statement, level, function:, used_labels:, loop_continue_label:, loop_break_label:)
99
+ explicit_cases = statement.cases.select { |switch_case| switch_case.is_a?(IR::SwitchCase) }
100
+ default_case = statement.cases.find { |switch_case| switch_case.is_a?(IR::SwitchDefaultCase) }
101
+
102
+ condition, then_body, else_body = if default_case
103
+ explicit_case = explicit_cases.first
104
+ [
105
+ IR::Binary.new(operator: "==", left: statement.expression, right: explicit_case.value, type: nil),
106
+ strip_terminal_switch_break(explicit_case.body),
107
+ strip_terminal_switch_break(default_case.body),
108
+ ]
109
+ else
110
+ [
111
+ IR::Binary.new(operator: "==", left: statement.expression, right: explicit_cases.first.value, type: nil),
112
+ strip_terminal_switch_break(explicit_cases.first.body),
113
+ strip_terminal_switch_break(explicit_cases.last.body),
114
+ ]
115
+ end
116
+
117
+ emit_if_statement(
118
+ IR::IfStmt.new(condition:, then_body:, else_body:),
119
+ level,
120
+ function:,
121
+ used_labels:,
122
+ loop_continue_label:,
123
+ loop_break_label:,
124
+ )
125
+ end
126
+
127
+ def strip_terminal_switch_break(statements)
128
+ return statements unless statements.last.is_a?(IR::BreakStmt)
129
+
130
+ statements[0...-1]
131
+ end
132
+
133
+ def canonicalize_top_guarded_while(statement)
134
+ return statement unless constant_boolean_value(statement.condition) == true
135
+ return statement if statement.body.empty?
136
+
137
+ break_condition = top_guard_break_condition(statement.body.first)
138
+ return statement unless break_condition
139
+
140
+ IR::WhileStmt.new(
141
+ condition: invert_break_guard_condition(break_condition),
142
+ body: statement.body.drop(1),
143
+ )
144
+ end
145
+
146
+ def top_guard_break_condition(statement)
147
+ return unless statement.is_a?(IR::IfStmt)
148
+ return unless statement.else_body.nil? || statement.else_body.empty?
149
+ return unless statement.then_body.length == 1 && statement.then_body.first.is_a?(IR::BreakStmt)
150
+
151
+ statement.condition
152
+ end
153
+
154
+ def invert_break_guard_condition(expression)
155
+ case expression
156
+ when IR::BooleanLiteral
157
+ IR::BooleanLiteral.new(value: !expression.value, type: expression.type)
158
+ when IR::Unary
159
+ return expression.operand if expression.operator == "not"
160
+ when IR::Binary
161
+ if (operator = inverted_boolean_operator(expression.operator))
162
+ return IR::Binary.new(operator:, left: expression.left, right: expression.right, type: expression.type)
163
+ end
164
+ end
165
+
166
+ IR::Unary.new(operator: "not", operand: expression, type: expression.type)
167
+ end
168
+
169
+ def inverted_boolean_operator(operator)
170
+ INVERTED_BOOLEAN_OPS[operator]
171
+ end
172
+
173
+ def emit_if_statement(statement, level, function:, used_labels:, loop_continue_label: nil, loop_break_label: nil)
174
+ indent = INDENT * level
175
+
176
+ case constant_boolean_value(statement.condition)
177
+ when true
178
+ return emit_statement(IR::BlockStmt.new(body: statement.then_body), level, function:, used_labels:, loop_continue_label:, loop_break_label:)
179
+ when false
180
+ return [] unless statement.else_body && !statement.else_body.empty?
181
+
182
+ return emit_statement(IR::BlockStmt.new(body: statement.else_body), level, function:, used_labels:, loop_continue_label:, loop_break_label:)
183
+ end
184
+
185
+ lines = ["#{indent}if (#{emit_expression(statement.condition)}) {"]
186
+ lines.concat(emit_statement_sequence(statement.then_body, level + 1, function:, used_labels:, loop_continue_label:, loop_break_label:))
187
+
188
+ nested_else_if = nested_else_if_statement(statement.else_body)
189
+ if nested_else_if
190
+ nested_lines = emit_if_statement(nested_else_if, level, function:, used_labels:, loop_continue_label:, loop_break_label:)
191
+ lines << "#{indent}} else #{nested_lines.first.sub(/^#{Regexp.escape(indent)}/, "") }"
192
+ lines.concat(nested_lines.drop(1))
193
+ return lines
194
+ end
195
+
196
+ if statement.else_body && !statement.else_body.empty?
197
+ lines << "#{indent}} else {"
198
+ lines.concat(emit_statement_sequence(statement.else_body, level + 1, function:, used_labels:, loop_continue_label:, loop_break_label:))
199
+ end
200
+ lines << "#{indent}}"
201
+ lines
202
+ end
203
+
204
+ def nested_else_if_statement(else_body)
205
+ return unless else_body && else_body.length == 1
206
+
207
+ nested = else_body.first
208
+ nested if nested.is_a?(IR::IfStmt)
209
+ end
210
+
211
+ def body_terminates?(statements)
212
+ return false if statements.empty?
213
+
214
+ statement_terminates?(statements.last)
215
+ end
216
+
217
+ def body_needs_fallback_return?(statements)
218
+ return true if statements.empty?
219
+
220
+ !statement_prevents_c_fallthrough?(statements.last)
221
+ end
222
+
223
+ def constant_boolean_value(expression)
224
+ case expression
225
+ when IR::BooleanLiteral
226
+ expression.value
227
+ when IR::Unary
228
+ operand = constant_boolean_value(expression.operand)
229
+ return nil if operand.nil? || expression.operator != "not"
230
+
231
+ !operand
232
+ when IR::Binary
233
+ left_int = constant_integer_value(expression.left)
234
+ right_int = constant_integer_value(expression.right)
235
+ if !left_int.nil? && !right_int.nil?
236
+ return left_int == right_int if expression.operator == "=="
237
+ return left_int != right_int if expression.operator == "!="
238
+ return left_int < right_int if expression.operator == "<"
239
+ return left_int <= right_int if expression.operator == "<="
240
+ return left_int > right_int if expression.operator == ">"
241
+ return left_int >= right_int if expression.operator == ">="
242
+ end
243
+
244
+ left_bool = constant_boolean_value(expression.left)
245
+ right_bool = constant_boolean_value(expression.right)
246
+ if !left_bool.nil? && !right_bool.nil?
247
+ return left_bool == right_bool if expression.operator == "=="
248
+ return left_bool != right_bool if expression.operator == "!="
249
+ return left_bool && right_bool if expression.operator == "and"
250
+ return left_bool || right_bool if expression.operator == "or"
251
+ end
252
+
253
+ nil
254
+ else
255
+ nil
256
+ end
257
+ end
258
+
259
+ def constant_integer_value(expression)
260
+ case expression
261
+ when IR::IntegerLiteral
262
+ expression.value
263
+ when IR::Unary
264
+ operand = constant_integer_value(expression.operand)
265
+ return nil if operand.nil?
266
+
267
+ return operand if expression.operator == "+"
268
+ return -operand if expression.operator == "-"
269
+
270
+ nil
271
+ else
272
+ nil
273
+ end
274
+ end
275
+
276
+ def statement_terminates?(statement)
277
+ case statement
278
+ when IR::ReturnStmt
279
+ true
280
+ when IR::BreakStmt, IR::ContinueStmt
281
+ true
282
+ when IR::GotoStmt
283
+ true
284
+ when IR::BlockStmt
285
+ body_terminates?(statement.body)
286
+ when IR::IfStmt
287
+ statement.else_body && body_terminates?(statement.then_body) && body_terminates?(statement.else_body)
288
+ when IR::SwitchStmt
289
+ switch_statement_prevents_outer_fallthrough?(statement)
290
+ else
291
+ false
292
+ end
293
+ end
294
+
295
+ def statement_prevents_c_fallthrough?(statement)
296
+ case statement
297
+ when IR::ReturnStmt
298
+ true
299
+ when IR::BlockStmt
300
+ !body_needs_fallback_return?(statement.body)
301
+ when IR::IfStmt
302
+ statement.else_body && !body_needs_fallback_return?(statement.then_body) && !body_needs_fallback_return?(statement.else_body)
303
+ when IR::SwitchStmt
304
+ switch_statement_prevents_outer_fallthrough?(statement)
305
+ when IR::WhileStmt
306
+ constant_boolean_value(statement.condition) == true && !contains_visible_loop_exit?(statement.body)
307
+ else
308
+ false
309
+ end
310
+ end
311
+
312
+ def statement_prevents_sequential_fallthrough?(statement)
313
+ case statement
314
+ when IR::ReturnStmt, IR::BreakStmt, IR::ContinueStmt, IR::GotoStmt
315
+ true
316
+ when IR::BlockStmt
317
+ !body_has_sequential_fallthrough?(statement.body)
318
+ when IR::IfStmt
319
+ statement.else_body && !body_has_sequential_fallthrough?(statement.then_body) && !body_has_sequential_fallthrough?(statement.else_body)
320
+ when IR::SwitchStmt
321
+ switch_statement_prevents_outer_fallthrough?(statement)
322
+ when IR::WhileStmt
323
+ constant_boolean_value(statement.condition) == true && !contains_visible_loop_exit?(statement.body)
324
+ else
325
+ false
326
+ end
327
+ end
328
+
329
+ def switch_statement_prevents_outer_fallthrough?(statement)
330
+ return false unless statement.exhaustive && statement.cases.any?
331
+
332
+ statement.cases.all? { |switch_case| switch_case_body_prevents_outer_fallthrough?(switch_case.body) }
333
+ end
334
+
335
+ def switch_case_body_prevents_outer_fallthrough?(statements)
336
+ return false if statements.empty?
337
+
338
+ switch_case_statement_prevents_outer_fallthrough?(statements.last)
339
+ end
340
+
341
+ def switch_case_statement_prevents_outer_fallthrough?(statement)
342
+ case statement
343
+ when IR::ReturnStmt, IR::ContinueStmt, IR::GotoStmt
344
+ true
345
+ when IR::BreakStmt
346
+ false
347
+ when IR::BlockStmt
348
+ switch_case_body_prevents_outer_fallthrough?(statement.body)
349
+ when IR::IfStmt
350
+ statement.else_body &&
351
+ switch_case_body_prevents_outer_fallthrough?(statement.then_body) &&
352
+ switch_case_body_prevents_outer_fallthrough?(statement.else_body)
353
+ when IR::SwitchStmt
354
+ switch_statement_prevents_outer_fallthrough?(statement)
355
+ when IR::WhileStmt
356
+ constant_boolean_value(statement.condition) == true && !contains_visible_loop_exit?(statement.body)
357
+ else
358
+ false
359
+ end
360
+ end
361
+
362
+ def body_has_sequential_fallthrough?(statements)
363
+ return true if statements.empty?
364
+
365
+ !statement_prevents_sequential_fallthrough?(statements.last)
366
+ end
367
+
368
+ def contains_visible_loop_exit?(statements)
369
+ statements.any? do |statement|
370
+ case statement
371
+ when IR::BreakStmt, IR::GotoStmt
372
+ true
373
+ when IR::BlockStmt
374
+ contains_visible_loop_exit?(statement.body)
375
+ when IR::IfStmt
376
+ contains_visible_loop_exit?(statement.then_body) || (statement.else_body && contains_visible_loop_exit?(statement.else_body))
377
+ when IR::SwitchStmt
378
+ statement.cases.any? { |switch_case| contains_visible_loop_exit?(switch_case.body) }
379
+ when IR::WhileStmt, IR::ForStmt
380
+ false
381
+ else
382
+ false
383
+ end
384
+ end
385
+ end
386
+
387
+ def collect_used_labels(statements)
388
+ labels = []
389
+ collect_used_labels_from_statements(statements, labels)
390
+ labels.uniq
391
+ end
392
+
393
+ def collect_used_labels_from_statements(statements, labels)
394
+ statements.each do |statement|
395
+ case statement
396
+ when IR::BlockStmt, IR::WhileStmt, IR::ForStmt
397
+ collect_used_labels_from_statements(statement.body, labels)
398
+ when IR::IfStmt
399
+ collect_used_labels_from_statements(statement.then_body, labels)
400
+ collect_used_labels_from_statements(statement.else_body, labels) if statement.else_body
401
+ when IR::SwitchStmt
402
+ statement.cases.each do |switch_case|
403
+ collect_used_labels_from_statements(switch_case.body, labels)
404
+ end
405
+ when IR::GotoStmt
406
+ labels << statement.label
407
+ end
408
+ end
409
+ end
410
+ end
411
+ end
412
+ end