mt-lang 0.3.8 → 0.3.10

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 (200) hide show
  1. checksums.yaml +4 -4
  2. data/lib/milk_tea/base.rb +1 -2
  3. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
  4. data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
  5. data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
  6. data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
  7. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
  8. data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
  9. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
  10. data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
  11. data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
  12. data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
  13. data/lib/milk_tea/bindings/steamworks.rb +12 -16
  14. data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
  15. data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
  16. data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
  17. data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
  18. data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
  19. data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
  20. data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
  21. data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
  22. data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
  23. data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
  24. data/lib/milk_tea/core/ast.rb +103 -45
  25. data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
  26. data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
  27. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
  28. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
  29. data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
  30. data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
  31. data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
  32. data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
  33. data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
  34. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
  35. data/lib/milk_tea/core/c_backend/statements.rb +528 -530
  36. data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
  37. data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
  38. data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
  39. data/lib/milk_tea/core/c_backend.rb +17 -9
  40. data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
  41. data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
  42. data/lib/milk_tea/core/compile_time.rb +32 -38
  43. data/lib/milk_tea/core/control_flow/builder.rb +3 -5
  44. data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
  45. data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
  46. data/lib/milk_tea/core/ir.rb +10 -10
  47. data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
  48. data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
  49. data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
  50. data/lib/milk_tea/core/lexer/indentation.rb +0 -2
  51. data/lib/milk_tea/core/lexer/numbers.rb +0 -2
  52. data/lib/milk_tea/core/lexer/recovery.rb +0 -2
  53. data/lib/milk_tea/core/lexer/strings.rb +0 -2
  54. data/lib/milk_tea/core/lexer/symbols.rb +0 -2
  55. data/lib/milk_tea/core/lexer/trivia.rb +0 -2
  56. data/lib/milk_tea/core/lexer.rb +0 -2
  57. data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
  58. data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
  59. data/lib/milk_tea/core/lowering/async/normalization.rb +0 -2
  60. data/lib/milk_tea/core/lowering/async.rb +627 -630
  61. data/lib/milk_tea/core/lowering/block.rb +413 -416
  62. data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
  63. data/lib/milk_tea/core/lowering/declarations.rb +180 -183
  64. data/lib/milk_tea/core/lowering/dyn.rb +48 -50
  65. data/lib/milk_tea/core/lowering/events.rb +983 -985
  66. data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
  67. data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
  68. data/lib/milk_tea/core/lowering/functions.rb +185 -188
  69. data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
  70. data/lib/milk_tea/core/lowering/proc.rb +363 -366
  71. data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
  72. data/lib/milk_tea/core/lowering/scans.rb +179 -181
  73. data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
  74. data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
  75. data/lib/milk_tea/core/lowering.rb +5 -7
  76. data/lib/milk_tea/core/module_binder.rb +0 -2
  77. data/lib/milk_tea/core/module_loader/errors.rb +4 -2
  78. data/lib/milk_tea/core/module_loader.rb +113 -20
  79. data/lib/milk_tea/core/module_path_resolver.rb +0 -2
  80. data/lib/milk_tea/core/module_roots.rb +6 -8
  81. data/lib/milk_tea/core/parser/attributes.rb +0 -2
  82. data/lib/milk_tea/core/parser/blocks.rb +0 -2
  83. data/lib/milk_tea/core/parser/declarations.rb +0 -2
  84. data/lib/milk_tea/core/parser/expressions.rb +31 -14
  85. data/lib/milk_tea/core/parser/recovery.rb +0 -2
  86. data/lib/milk_tea/core/parser/statements.rb +0 -2
  87. data/lib/milk_tea/core/parser/types.rb +8 -10
  88. data/lib/milk_tea/core/parser.rb +1 -3
  89. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
  90. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
  91. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
  92. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
  93. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
  94. data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
  95. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
  96. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
  97. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
  98. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +12 -15
  99. data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
  100. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
  101. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
  102. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
  103. data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
  104. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
  105. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
  106. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +102 -105
  107. data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
  108. data/lib/milk_tea/core/types/layout.rb +21 -22
  109. data/lib/milk_tea/core/types/predicates.rb +0 -2
  110. data/lib/milk_tea/core/types/registry.rb +16 -18
  111. data/lib/milk_tea/core/types/visitor.rb +0 -4
  112. data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
  113. data/lib/milk_tea/dap/protocol.rb +1 -1
  114. data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
  115. data/lib/milk_tea/dap/server/debug_map.rb +0 -2
  116. data/lib/milk_tea/dap/server/handlers.rb +0 -2
  117. data/lib/milk_tea/dap/server/launch.rb +0 -2
  118. data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
  119. data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
  120. data/lib/milk_tea/dap/server/utilities.rb +0 -2
  121. data/lib/milk_tea/dap/server/wire.rb +0 -2
  122. data/lib/milk_tea/dap/server.rb +0 -2
  123. data/lib/milk_tea/lsp/diagnostics.rb +16 -16
  124. data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
  125. data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
  126. data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
  127. data/lib/milk_tea/lsp/server/completion.rb +873 -875
  128. data/lib/milk_tea/lsp/server/configuration.rb +129 -131
  129. data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
  130. data/lib/milk_tea/lsp/server/definition.rb +564 -565
  131. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
  132. data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
  133. data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
  134. data/lib/milk_tea/lsp/server/formatting.rb +478 -480
  135. data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
  136. data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
  137. data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
  138. data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
  139. data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
  140. data/lib/milk_tea/lsp/server/progress.rb +0 -2
  141. data/lib/milk_tea/lsp/server/references.rb +316 -318
  142. data/lib/milk_tea/lsp/server/rename.rb +445 -447
  143. data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
  144. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1318 -1320
  145. data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
  146. data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
  147. data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
  148. data/lib/milk_tea/lsp/server/utilities.rb +397 -399
  149. data/lib/milk_tea/lsp/server.rb +0 -3
  150. data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
  151. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
  152. data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
  153. data/lib/milk_tea/packages/atomic_write.rb +4 -5
  154. data/lib/milk_tea/packages/dependency_solver.rb +15 -15
  155. data/lib/milk_tea/packages/lock.rb +4 -4
  156. data/lib/milk_tea/packages/manager_cli.rb +1 -1
  157. data/lib/milk_tea/packages/manifest.rb +3 -3
  158. data/lib/milk_tea/packages/registry_store.rb +4 -6
  159. data/lib/milk_tea/packages/services.rb +16 -16
  160. data/lib/milk_tea/packages/source_cache.rb +4 -6
  161. data/lib/milk_tea/packages/source_resolver.rb +44 -44
  162. data/lib/milk_tea/packages/version.rb +8 -8
  163. data/lib/milk_tea/tooling/build.rb +3 -7
  164. data/lib/milk_tea/tooling/build_cache.rb +0 -2
  165. data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
  166. data/lib/milk_tea/tooling/debug_map.rb +8 -10
  167. data/lib/milk_tea/tooling/docs_app.rb +0 -2
  168. data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
  169. data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
  170. data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
  171. data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
  172. data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
  173. data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
  174. data/lib/milk_tea/tooling/linter/rules.rb +87 -89
  175. data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
  176. data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
  177. data/lib/milk_tea/tooling/linter/visitors.rb +162 -50
  178. data/lib/milk_tea/tooling/linter.rb +0 -2
  179. data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
  180. data/lib/milk_tea/tooling/run.rb +0 -2
  181. data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
  182. data/std/cli.mt +13 -32
  183. data/std/color.mt +28 -28
  184. data/std/encoding.mt +4 -4
  185. data/std/graph.mt +1 -2
  186. data/std/hash.mt +14 -14
  187. data/std/http/server.mt +3 -3
  188. data/std/input.mt +1 -1
  189. data/std/json.mt +1 -1
  190. data/std/linear_algebra.mt +6 -6
  191. data/std/net/mux.mt +1 -1
  192. data/std/noise.mt +23 -23
  193. data/std/parse.mt +2 -2
  194. data/std/process.mt +3 -4
  195. data/std/sparse_set.mt +1 -1
  196. data/std/steering.mt +5 -5
  197. data/std/str.mt +1 -1
  198. data/std/tween.mt +4 -4
  199. data/std/utility.mt +3 -3
  200. metadata +2 -2
@@ -3,410 +3,408 @@
3
3
  module MilkTea
4
4
  class CBackend
5
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
6
+ INVERTED_BOOLEAN_OPS = {
7
+ "==" => "!=",
8
+ "!=" => "==",
9
+ "<" => ">=",
10
+ "<=" => ">",
11
+ ">" => "<=",
12
+ ">=" => "<",
13
+ }.freeze
14
+
15
+ def loop_continue_label_name(statements)
16
+ return if statements.empty?
17
+
18
+ label = statements.last
19
+ return unless label.is_a?(IR::LabelStmt)
20
+ return unless label.name.include?("loop_continue")
21
+
22
+ label.name
23
+ end
24
+
25
+ def loop_break_label_name(statements, remaining_statements)
26
+ return if remaining_statements.empty?
27
+
28
+ label = remaining_statements.first
29
+ return unless label.is_a?(IR::LabelStmt)
30
+ return unless statements_reference_label?(statements, label.name)
31
+
32
+ label.name
33
+ end
34
+
35
+ def statements_reference_label?(statements, label_name)
36
+ statements.any? { |statement| statement_references_label?(statement, label_name) }
37
+ end
38
+
39
+ def statement_references_label?(statement, label_name)
40
+ case statement
41
+ when IR::BlockStmt, IR::WhileStmt, IR::ForStmt
42
+ statements_reference_label?(statement.body, label_name)
43
+ when IR::IfStmt
44
+ statements_reference_label?(statement.then_body, label_name) ||
45
+ (statement.else_body && statements_reference_label?(statement.else_body, label_name))
46
+ when IR::SwitchStmt
47
+ statement.cases.any? { |switch_case| statements_reference_label?(switch_case.body, label_name) }
48
+ when IR::GotoStmt
49
+ statement.label == label_name
50
+ else
51
+ false
52
+ end
53
+ end
54
+
55
+ def statements_need_explicit_break_label_after_emission?(statements, label_name, loop_break_label_active:)
56
+ statements.any? do |statement|
57
+ statement_needs_explicit_break_label_after_emission?(statement, label_name, loop_break_label_active:)
58
+ end
59
+ end
60
+
61
+ def statement_needs_explicit_break_label_after_emission?(statement, label_name, loop_break_label_active:)
62
+ case statement
63
+ when IR::BlockStmt, IR::IfStmt
64
+ then_body = statement.is_a?(IR::IfStmt) ? statement.then_body : statement.body
65
+ else_body = statement.is_a?(IR::IfStmt) ? statement.else_body : nil
66
+ statements_need_explicit_break_label_after_emission?(then_body, label_name, loop_break_label_active:) ||
67
+ (else_body && statements_need_explicit_break_label_after_emission?(else_body, label_name, loop_break_label_active:))
68
+ when IR::WhileStmt, IR::ForStmt
69
+ statements_need_explicit_break_label_after_emission?(statement.body, label_name, loop_break_label_active: false)
70
+ when IR::SwitchStmt
71
+ return false unless statement_references_label?(statement, label_name)
72
+
73
+ active = loop_break_label_active && switch_emittable_as_if?(statement, loop_break_label: label_name)
74
+ statements_need_explicit_break_label_after_emission?(
75
+ statement.cases.flat_map(&:body),
76
+ label_name,
77
+ loop_break_label_active: active,
78
+ )
79
+ when IR::GotoStmt
80
+ statement.label == label_name && !loop_break_label_active
81
+ else
82
+ false
83
+ end
84
+ end
85
+
86
+ def switch_emittable_as_if?(statement, loop_break_label: nil)
87
+ return false unless loop_break_label
88
+ return false unless statement.exhaustive
89
+ return false unless statement.cases.length == 2
90
+ return false unless side_effect_free_expression?(statement.expression)
91
+ return false unless statement_references_label?(statement, loop_break_label)
92
+
93
+ statement.cases.count { |switch_case| switch_case.is_a?(IR::SwitchDefaultCase) } <= 1
94
+ end
95
+
96
+ def emit_switch_as_if_statement(statement, level, function:, used_labels:, loop_continue_label:, loop_break_label:)
97
+ explicit_cases = statement.cases.select { |switch_case| switch_case.is_a?(IR::SwitchCase) }
98
+ default_case = statement.cases.find { |switch_case| switch_case.is_a?(IR::SwitchDefaultCase) }
99
+
100
+ condition, then_body, else_body = if default_case
101
+ explicit_case = explicit_cases.first
102
+ [
103
+ IR::Binary.new(operator: "==", left: statement.expression, right: explicit_case.value, type: nil),
104
+ strip_terminal_switch_break(explicit_case.body),
105
+ strip_terminal_switch_break(default_case.body),
106
+ ]
107
+ else
108
+ [
109
+ IR::Binary.new(operator: "==", left: statement.expression, right: explicit_cases.first.value, type: nil),
110
+ strip_terminal_switch_break(explicit_cases.first.body),
111
+ strip_terminal_switch_break(explicit_cases.last.body),
112
+ ]
113
+ end
114
+
115
+ emit_if_statement(
116
+ IR::IfStmt.new(condition:, then_body:, else_body:),
117
+ level,
118
+ function:,
119
+ used_labels:,
120
+ loop_continue_label:,
121
+ loop_break_label:,
122
+ )
123
+ end
124
+
125
+ def strip_terminal_switch_break(statements)
126
+ return statements unless statements.last.is_a?(IR::BreakStmt)
127
+
128
+ statements[0...-1]
129
+ end
130
+
131
+ def canonicalize_top_guarded_while(statement)
132
+ return statement unless constant_boolean_value(statement.condition) == true
133
+ return statement if statement.body.empty?
134
+
135
+ break_condition = top_guard_break_condition(statement.body.first)
136
+ return statement unless break_condition
137
+
138
+ IR::WhileStmt.new(
139
+ condition: invert_break_guard_condition(break_condition),
140
+ body: statement.body.drop(1),
141
+ )
142
+ end
143
+
144
+ def top_guard_break_condition(statement)
145
+ return unless statement.is_a?(IR::IfStmt)
146
+ return unless statement.else_body.nil? || statement.else_body.empty?
147
+ return unless statement.then_body.length == 1 && statement.then_body.first.is_a?(IR::BreakStmt)
148
+
149
+ statement.condition
150
+ end
151
+
152
+ def invert_break_guard_condition(expression)
153
+ case expression
154
+ when IR::BooleanLiteral
155
+ IR::BooleanLiteral.new(value: !expression.value, type: expression.type)
156
+ when IR::Unary
157
+ return expression.operand if expression.operator == "not"
158
+ when IR::Binary
159
+ if (operator = inverted_boolean_operator(expression.operator))
160
+ return IR::Binary.new(operator:, left: expression.left, right: expression.right, type: expression.type)
35
161
  end
36
-
37
- def statements_reference_label?(statements, label_name)
38
- statements.any? { |statement| statement_references_label?(statement, label_name) }
162
+ end
163
+
164
+ IR::Unary.new(operator: "not", operand: expression, type: expression.type)
165
+ end
166
+
167
+ def inverted_boolean_operator(operator)
168
+ INVERTED_BOOLEAN_OPS[operator]
169
+ end
170
+
171
+ def emit_if_statement(statement, level, function:, used_labels:, loop_continue_label: nil, loop_break_label: nil)
172
+ indent = INDENT * level
173
+
174
+ case constant_boolean_value(statement.condition)
175
+ when true
176
+ return emit_statement(IR::BlockStmt.new(body: statement.then_body), level, function:, used_labels:, loop_continue_label:, loop_break_label:)
177
+ when false
178
+ return [] unless statement.else_body && !statement.else_body.empty?
179
+
180
+ return emit_statement(IR::BlockStmt.new(body: statement.else_body), level, function:, used_labels:, loop_continue_label:, loop_break_label:)
181
+ end
182
+
183
+ lines = ["#{indent}if (#{emit_expression(statement.condition)}) {"]
184
+ lines.concat(emit_statement_sequence(statement.then_body, level + 1, function:, used_labels:, loop_continue_label:, loop_break_label:))
185
+
186
+ nested_else_if = nested_else_if_statement(statement.else_body)
187
+ if nested_else_if
188
+ nested_lines = emit_if_statement(nested_else_if, level, function:, used_labels:, loop_continue_label:, loop_break_label:)
189
+ lines << "#{indent}} else #{nested_lines.first.sub(/^#{Regexp.escape(indent)}/, "") }"
190
+ lines.concat(nested_lines.drop(1))
191
+ return lines
192
+ end
193
+
194
+ if statement.else_body && !statement.else_body.empty?
195
+ lines << "#{indent}} else {"
196
+ lines.concat(emit_statement_sequence(statement.else_body, level + 1, function:, used_labels:, loop_continue_label:, loop_break_label:))
197
+ end
198
+ lines << "#{indent}}"
199
+ lines
200
+ end
201
+
202
+ def nested_else_if_statement(else_body)
203
+ return unless else_body && else_body.length == 1
204
+
205
+ nested = else_body.first
206
+ nested if nested.is_a?(IR::IfStmt)
207
+ end
208
+
209
+ def body_terminates?(statements)
210
+ return false if statements.empty?
211
+
212
+ statement_terminates?(statements.last)
213
+ end
214
+
215
+ def body_needs_fallback_return?(statements)
216
+ return true if statements.empty?
217
+
218
+ !statement_prevents_c_fallthrough?(statements.last)
219
+ end
220
+
221
+ def constant_boolean_value(expression)
222
+ case expression
223
+ when IR::BooleanLiteral
224
+ expression.value
225
+ when IR::Unary
226
+ operand = constant_boolean_value(expression.operand)
227
+ return nil if operand.nil? || expression.operator != "not"
228
+
229
+ !operand
230
+ when IR::Binary
231
+ left_int = constant_integer_value(expression.left)
232
+ right_int = constant_integer_value(expression.right)
233
+ if !left_int.nil? && !right_int.nil?
234
+ return left_int == right_int if expression.operator == "=="
235
+ return left_int != right_int if expression.operator == "!="
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 == ">="
39
240
  end
40
241
 
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
242
+ left_bool = constant_boolean_value(expression.left)
243
+ right_bool = constant_boolean_value(expression.right)
244
+ if !left_bool.nil? && !right_bool.nil?
245
+ return left_bool == right_bool if expression.operator == "=="
246
+ return left_bool != right_bool if expression.operator == "!="
247
+ return left_bool && right_bool if expression.operator == "and"
248
+ return left_bool || right_bool if expression.operator == "or"
55
249
  end
56
250
 
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
251
+ nil
252
+ else
253
+ nil
254
+ end
255
+ end
256
+
257
+ def constant_integer_value(expression)
258
+ case expression
259
+ when IR::IntegerLiteral
260
+ expression.value
261
+ when IR::Unary
262
+ operand = constant_integer_value(expression.operand)
263
+ return nil if operand.nil?
264
+
265
+ return operand if expression.operator == "+"
266
+ return -operand if expression.operator == "-"
267
+
268
+ nil
269
+ else
270
+ nil
271
+ end
272
+ end
273
+
274
+ def statement_terminates?(statement)
275
+ case statement
276
+ when IR::ReturnStmt
277
+ true
278
+ when IR::BreakStmt, IR::ContinueStmt
279
+ true
280
+ when IR::GotoStmt
281
+ true
282
+ when IR::BlockStmt
283
+ body_terminates?(statement.body)
284
+ when IR::IfStmt
285
+ statement.else_body && body_terminates?(statement.then_body) && body_terminates?(statement.else_body)
286
+ when IR::SwitchStmt
287
+ switch_statement_prevents_outer_fallthrough?(statement)
288
+ else
289
+ false
290
+ end
291
+ end
292
+
293
+ def statement_prevents_c_fallthrough?(statement)
294
+ case statement
295
+ when IR::ReturnStmt
296
+ true
297
+ when IR::BlockStmt
298
+ !body_needs_fallback_return?(statement.body)
299
+ when IR::IfStmt
300
+ statement.else_body && !body_needs_fallback_return?(statement.then_body) && !body_needs_fallback_return?(statement.else_body)
301
+ when IR::SwitchStmt
302
+ switch_statement_prevents_outer_fallthrough?(statement)
303
+ when IR::WhileStmt
304
+ constant_boolean_value(statement.condition) == true && !contains_visible_loop_exit?(statement.body)
305
+ else
306
+ false
307
+ end
308
+ end
309
+
310
+ def statement_prevents_sequential_fallthrough?(statement)
311
+ case statement
312
+ when IR::ReturnStmt, IR::BreakStmt, IR::ContinueStmt, IR::GotoStmt
313
+ true
314
+ when IR::BlockStmt
315
+ !body_has_sequential_fallthrough?(statement.body)
316
+ when IR::IfStmt
317
+ statement.else_body && !body_has_sequential_fallthrough?(statement.then_body) && !body_has_sequential_fallthrough?(statement.else_body)
318
+ when IR::SwitchStmt
319
+ switch_statement_prevents_outer_fallthrough?(statement)
320
+ when IR::WhileStmt
321
+ constant_boolean_value(statement.condition) == true && !contains_visible_loop_exit?(statement.body)
322
+ else
323
+ false
324
+ end
325
+ end
326
+
327
+ def switch_statement_prevents_outer_fallthrough?(statement)
328
+ return false unless statement.exhaustive && statement.cases.any?
329
+
330
+ statement.cases.all? { |switch_case| switch_case_body_prevents_outer_fallthrough?(switch_case.body) }
331
+ end
332
+
333
+ def switch_case_body_prevents_outer_fallthrough?(statements)
334
+ return false if statements.empty?
335
+
336
+ switch_case_statement_prevents_outer_fallthrough?(statements.last)
337
+ end
338
+
339
+ def switch_case_statement_prevents_outer_fallthrough?(statement)
340
+ case statement
341
+ when IR::ReturnStmt, IR::ContinueStmt, IR::GotoStmt
342
+ true
343
+ when IR::BreakStmt
344
+ false
345
+ when IR::BlockStmt
346
+ switch_case_body_prevents_outer_fallthrough?(statement.body)
347
+ when IR::IfStmt
348
+ statement.else_body &&
349
+ switch_case_body_prevents_outer_fallthrough?(statement.then_body) &&
350
+ switch_case_body_prevents_outer_fallthrough?(statement.else_body)
351
+ when IR::SwitchStmt
352
+ switch_statement_prevents_outer_fallthrough?(statement)
353
+ when IR::WhileStmt
354
+ constant_boolean_value(statement.condition) == true && !contains_visible_loop_exit?(statement.body)
355
+ else
356
+ false
357
+ end
358
+ end
359
+
360
+ def body_has_sequential_fallthrough?(statements)
361
+ return true if statements.empty?
362
+
363
+ !statement_prevents_sequential_fallthrough?(statements.last)
364
+ end
365
+
366
+ def contains_visible_loop_exit?(statements)
367
+ statements.any? do |statement|
368
+ case statement
369
+ when IR::BreakStmt, IR::GotoStmt
370
+ true
371
+ when IR::BlockStmt
372
+ contains_visible_loop_exit?(statement.body)
373
+ when IR::IfStmt
374
+ contains_visible_loop_exit?(statement.then_body) || (statement.else_body && contains_visible_loop_exit?(statement.else_body))
375
+ when IR::SwitchStmt
376
+ statement.cases.any? { |switch_case| contains_visible_loop_exit?(switch_case.body) }
377
+ when IR::WhileStmt, IR::ForStmt
378
+ false
379
+ else
380
+ false
202
381
  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
382
+ end
383
+ end
384
+
385
+ def collect_used_labels(statements)
386
+ labels = []
387
+ collect_used_labels_from_statements(statements, labels)
388
+ labels.uniq
389
+ end
390
+
391
+ def collect_used_labels_from_statements(statements, labels)
392
+ statements.each do |statement|
393
+ case statement
394
+ when IR::BlockStmt, IR::WhileStmt, IR::ForStmt
395
+ collect_used_labels_from_statements(statement.body, labels)
396
+ when IR::IfStmt
397
+ collect_used_labels_from_statements(statement.then_body, labels)
398
+ collect_used_labels_from_statements(statement.else_body, labels) if statement.else_body
399
+ when IR::SwitchStmt
400
+ statement.cases.each do |switch_case|
401
+ collect_used_labels_from_statements(switch_case.body, labels)
408
402
  end
403
+ when IR::GotoStmt
404
+ labels << statement.label
409
405
  end
406
+ end
407
+ end
410
408
  end
411
409
  end
412
410
  end