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,468 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class CBackend
5
+ module Expressions
6
+ private
7
+
8
+
9
+ def emit_expression(expression)
10
+ case expression
11
+ when IR::Name
12
+ expression.name
13
+ when IR::Member
14
+ operator = pointer_member_receiver?(expression.receiver) ? "->" : "."
15
+ member = "#{wrap_member_receiver(expression.receiver)}#{operator}#{sanitize_c_identifier(expression.member)}"
16
+ expression.type.is_a?(Types::Primitive) && expression.type.void? ? "((void)(#{member}))" : member
17
+ when IR::Index
18
+ "#{wrap_index_receiver(expression.receiver)}[#{emit_expression(expression.index)}]"
19
+ when IR::CheckedIndex
20
+ if (alias_name = checked_index_alias(expression))
21
+ "(*#{alias_name})"
22
+ else
23
+ "(*#{checked_array_index_helper_name(expression.receiver_type)}(#{emit_address_of_operand(expression.receiver)}, #{emit_expression(expression.index)}))"
24
+ end
25
+ when IR::CheckedSpanIndex
26
+ if (alias_name = checked_index_alias(expression))
27
+ "(*#{alias_name})"
28
+ else
29
+ "(*#{checked_span_index_helper_name(expression.receiver_type)}(#{emit_expression(expression.receiver)}, #{emit_expression(expression.index)}))"
30
+ end
31
+ when IR::NullableIndex
32
+ "#{nullable_array_index_helper_name(expression.receiver_type)}(#{emit_address_of_operand(expression.receiver)}, #{emit_expression(expression.index)})"
33
+ when IR::NullableSpanIndex
34
+ "#{nullable_span_index_helper_name(expression.receiver_type)}(#{emit_expression(expression.receiver)}, #{emit_expression(expression.index)})"
35
+ when IR::Call
36
+ raise CBackendError, "array-return call must be materialized before C emission" if array_type?(expression.type)
37
+
38
+ emit_call_expression(expression)
39
+ when IR::Unary
40
+ if expression.operator == "not"
41
+ "!#{wrap_expression(expression.operand)}"
42
+ else
43
+ "#{expression.operator}#{wrap_expression(expression.operand)}"
44
+ end
45
+ when IR::Binary
46
+ return emit_str_equality_expression(expression) if str_equality_expression?(expression)
47
+ return emit_variant_equality_expression(expression) if variant_equality_expression?(expression)
48
+ return emit_nullable_null_comparison(expression) if nullable_null_comparison?(expression)
49
+
50
+ emit_binary_expression(expression)
51
+ when IR::Conditional
52
+ emit_conditional_expression(expression)
53
+ when IR::ReinterpretExpr
54
+ if no_op_reinterpret?(expression.target_type, expression.source_type)
55
+ emit_expression(expression.expression)
56
+ else
57
+ "#{reinterpret_helper_name(expression.target_type, expression.source_type)}(#{emit_expression(expression.expression)})"
58
+ end
59
+ when IR::SizeofExpr
60
+ "sizeof(#{layout_type_expression(expression.target_type)})"
61
+ when IR::AlignofExpr
62
+ "_Alignof(#{layout_type_expression(expression.target_type)})"
63
+ when IR::OffsetofExpr
64
+ "offsetof(#{layout_type_expression(expression.target_type)}, #{sanitize_c_identifier(expression.field)})"
65
+ when IR::IntegerLiteral
66
+ expression.value.to_s
67
+ when IR::FloatLiteral
68
+ emit_float_literal(expression)
69
+ when IR::StringLiteral
70
+ expression.type.is_a?(Types::StringView) ? emit_str_literal(expression) : expression.value.inspect
71
+ when IR::BooleanLiteral
72
+ expression.value ? "true" : "false"
73
+ when IR::NullLiteral
74
+ nullable_value_type?(expression.type) ? emit_zero_expression(expression.type) : "NULL"
75
+ when IR::ZeroInit
76
+ emit_zero_expression(expression.type)
77
+ when IR::AddressOf
78
+ case expression.expression
79
+ when IR::CheckedIndex
80
+ alias_name = checked_index_alias(expression.expression)
81
+ alias_name || "#{checked_array_index_helper_name(expression.expression.receiver_type)}(#{emit_address_of_operand(expression.expression.receiver)}, #{emit_expression(expression.expression.index)})"
82
+ when IR::CheckedSpanIndex
83
+ alias_name = checked_index_alias(expression.expression)
84
+ alias_name || "#{checked_span_index_helper_name(expression.expression.receiver_type)}(#{emit_expression(expression.expression.receiver)}, #{emit_expression(expression.expression.index)})"
85
+ else
86
+ emit_address_of_operand(expression.expression)
87
+ end
88
+ when IR::Cast
89
+ if no_op_cast?(expression)
90
+ emit_expression(expression.expression)
91
+ else
92
+ "(#{c_type(expression.target_type)}) #{emit_cast_operand(expression.expression)}"
93
+ end
94
+ when IR::AggregateLiteral
95
+ emit_aggregate_literal(expression)
96
+ when IR::ArrayLiteral
97
+ emit_array_compound_literal(expression)
98
+ when IR::VariantLiteral
99
+ emit_variant_literal(expression)
100
+ else
101
+ raise CBackendError, "unsupported IR expression #{expression.class.name}"
102
+ end
103
+ end
104
+
105
+ def str_equality_expression?(expression)
106
+ EQUALITY_OPERATORS.include?(expression.operator) && expression.left.type.is_a?(Types::StringView) && expression.right.type.is_a?(Types::StringView)
107
+ end
108
+
109
+ def emit_binary_expression(expression)
110
+ parent_precedence = binary_precedence(expression.operator)
111
+ left = emit_binary_operand(expression.left, parent_precedence, side: :left)
112
+ right = emit_binary_operand(expression.right, parent_precedence, side: :right)
113
+ "#{left} #{c_operator(expression.operator)} #{right}"
114
+ end
115
+
116
+ def emit_binary_operand(expression, parent_precedence, side:)
117
+ text = emit_expression(expression)
118
+
119
+ case expression
120
+ when IR::Conditional
121
+ "(#{text})"
122
+ when IR::Binary
123
+ child_precedence = binary_precedence(expression.operator)
124
+ if child_precedence < parent_precedence || (side == :right && child_precedence == parent_precedence)
125
+ "(#{text})"
126
+ else
127
+ text
128
+ end
129
+ else
130
+ text
131
+ end
132
+ end
133
+
134
+ def emit_conditional_expression(expression)
135
+ condition = emit_conditional_condition(expression.condition)
136
+ then_expression = emit_expression(expression.then_expression)
137
+ else_expression = emit_expression(expression.else_expression)
138
+ "#{condition} ? #{then_expression} : #{else_expression}"
139
+ end
140
+
141
+ def emit_conditional_condition(expression)
142
+ text = emit_expression(expression)
143
+ expression.is_a?(IR::Conditional) ? "(#{text})" : text
144
+ end
145
+
146
+ def binary_precedence(operator)
147
+ case operator
148
+ when "or" then 1
149
+ when "and" then 2
150
+ when "|" then 3
151
+ when "^" then 4
152
+ when "&" then 5
153
+ when "==", "!=" then 6
154
+ when "<", "<=", ">", ">=" then 7
155
+ when "<<", ">>" then 8
156
+ when "+", "-" then 9
157
+ when "*", "/", "%" then 10
158
+ else
159
+ raise CBackendError, "unsupported binary operator #{operator}"
160
+ end
161
+ end
162
+
163
+ def emit_str_equality_expression(expression)
164
+ call = "mt_str_equal(#{emit_expression(expression.left)}, #{emit_expression(expression.right)})"
165
+ expression.operator == "!=" ? "!#{call}" : call
166
+ end
167
+
168
+ def variant_equality_expression?(expression)
169
+ EQUALITY_OPERATORS.include?(expression.operator) &&
170
+ (expression.left.type.is_a?(Types::Variant) || expression.left.type.is_a?(Types::VariantArmPayload))
171
+ end
172
+
173
+ def emit_variant_equality_expression(expression)
174
+ helper_name = variant_equality_helper_name(expression.left.type)
175
+ call = "#{helper_name}(#{emit_expression(expression.left)}, #{emit_expression(expression.right)})"
176
+ expression.operator == "!=" ? "!#{call}" : call
177
+ end
178
+
179
+ def variant_equality_helper_name(type)
180
+ variant = type.is_a?(Types::VariantArmPayload) ? type.variant_type : type
181
+ "mt_variant_eq_#{named_type_c_name(variant)}"
182
+ end
183
+
184
+ def nullable_value_type?(type)
185
+ type.is_a?(Types::Nullable) && !c_backend_pointer_like_type?(type.base)
186
+ end
187
+
188
+ def nullable_null_comparison?(expression)
189
+ return false unless EQUALITY_OPERATORS.include?(expression.operator)
190
+
191
+ (nullable_value_type?(expression.left.type) && expression.right.is_a?(IR::NullLiteral)) ||
192
+ (nullable_value_type?(expression.right.type) && expression.left.is_a?(IR::NullLiteral))
193
+ end
194
+
195
+ def emit_nullable_null_comparison(expression)
196
+ operand = expression.left.is_a?(IR::NullLiteral) ? expression.right : expression.left
197
+ access = "#{wrap_member_receiver(operand)}.has_value"
198
+ expression.operator == "==" ? "!#{access}" : access
199
+ end
200
+
201
+ def emit_initializer(expression)
202
+ case expression
203
+ when IR::ArrayLiteral
204
+ emit_array_initializer(expression)
205
+ when IR::AggregateLiteral
206
+ emit_aggregate_initializer(expression)
207
+ when IR::VariantLiteral
208
+ emit_variant_initializer(expression)
209
+ when IR::StringLiteral
210
+ expression.type.is_a?(Types::StringView) ? emit_str_initializer(expression) : emit_expression(expression)
211
+ when IR::ZeroInit
212
+ emit_zero_initializer(expression.type)
213
+ else
214
+ emit_expression(expression)
215
+ end
216
+ end
217
+
218
+ def emit_aggregate_initializer(expression)
219
+ return emit_zero_initializer(expression.type) if expression.fields.empty?
220
+
221
+ fields = expression.fields.map do |field|
222
+ ".#{sanitize_c_identifier(field.name)} = #{emit_aggregate_field_initializer(expression.type, field)}"
223
+ end.join(", ")
224
+ "{ #{fields} }"
225
+ end
226
+
227
+ def emit_variant_initializer(expression)
228
+ outer_c = named_type_c_name(expression.type)
229
+ kind_constant = "#{outer_c}_kind_#{expression.arm_name}"
230
+ if expression.fields.empty?
231
+ "{ .kind = #{kind_constant} }"
232
+ else
233
+ payload_fields = expression.fields.map { |field| ".#{sanitize_c_identifier(field.name)} = #{emit_variant_field_initializer(expression.type, expression.arm_name, field)}" }.join(", ")
234
+ "{ .kind = #{kind_constant}, .data.#{sanitize_c_identifier(expression.arm_name)} = { #{payload_fields} } }"
235
+ end
236
+ end
237
+
238
+ def emit_str_initializer(expression)
239
+ if @str_literal_map && @str_literal_map[expression.value]
240
+ @str_literal_map[expression.value]
241
+ else
242
+ "{ .data = #{expression.value.inspect}, .len = #{expression.value.bytesize} }"
243
+ end
244
+ end
245
+
246
+ def emit_aggregate_literal(expression)
247
+ return emit_zero_expression(expression.type) if expression.fields.empty?
248
+
249
+ fields = expression.fields.map do |field|
250
+ ".#{sanitize_c_identifier(field.name)} = #{emit_aggregate_field_initializer(expression.type, field)}"
251
+ end.join(", ")
252
+ "(#{c_type(expression.type)}){ #{fields} }"
253
+ end
254
+
255
+ def emit_variant_literal(expression)
256
+ outer_c = named_type_c_name(expression.type)
257
+ kind_constant = "#{outer_c}_kind_#{expression.arm_name}"
258
+ if expression.fields.empty?
259
+ "(#{outer_c}){ .kind = #{kind_constant} }"
260
+ else
261
+ arm_c = "#{outer_c}_#{expression.arm_name}"
262
+ payload_fields = expression.fields.map { |field| ".#{sanitize_c_identifier(field.name)} = #{emit_variant_field_initializer(expression.type, expression.arm_name, field)}" }.join(", ")
263
+ "(#{outer_c}){ .kind = #{kind_constant}, .data.#{sanitize_c_identifier(expression.arm_name)} = (struct #{arm_c}){ #{payload_fields} } }"
264
+ end
265
+ end
266
+
267
+ def emit_call_expression(expression, array_out_argument: nil)
268
+ callee = expression.callee.is_a?(String) ? expression.callee : emit_call_callee(expression.callee)
269
+ omit_receiver = omitted_method_receiver_call?(expression)
270
+ arguments = []
271
+ arguments << array_out_argument if array_out_argument
272
+ arguments.concat(expression.arguments.drop(omit_receiver ? 1 : 0).map { |argument| emit_expression(argument) })
273
+ call = "#{callee}(#{arguments.join(', ')})"
274
+ return call unless omit_receiver && expression.arguments.any?
275
+ return call if side_effect_free_expression?(expression.arguments.first)
276
+
277
+ "(#{discarded_expression(expression.arguments.first)}, #{call})"
278
+ end
279
+
280
+ def emit_array_call_statement(expression, out_argument, indent)
281
+ "#{indent}#{emit_call_expression(expression, array_out_argument: out_argument)};"
282
+ end
283
+
284
+ def emit_array_copy_statement(destination, source, indent)
285
+ "#{indent}memcpy(#{destination}, #{emit_expression(source)}, sizeof(#{destination}));"
286
+ end
287
+
288
+ def emit_array_return(expression, indent)
289
+ if expression.is_a?(IR::Call) && array_type?(expression.type)
290
+ return [emit_array_call_statement(expression, ARRAY_OUT_PARAM_NAME, indent), "#{indent}return;"]
291
+ end
292
+
293
+ [
294
+ emit_array_copy_statement("*#{ARRAY_OUT_PARAM_NAME}", expression, indent),
295
+ "#{indent}return;",
296
+ ]
297
+ end
298
+
299
+ def emitted_function_params(function)
300
+ omitted_method_receiver_function?(function) ? function.params.drop(1) : function.params
301
+ end
302
+
303
+ def omitted_method_receiver_call?(expression)
304
+ expression.callee.is_a?(String) && omitted_method_receiver_function_names.key?(expression.callee)
305
+ end
306
+
307
+ def omitted_method_receiver_function_names
308
+ @omitted_method_receiver_function_names ||= emitted_functions.each_with_object({}) do |function, omitted|
309
+ omitted[function.linkage_name] = true if omitted_method_receiver_function?(function)
310
+ end
311
+ end
312
+
313
+ def omitted_method_receiver_function?(function)
314
+ function.method_receiver_param &&
315
+ function.params.first &&
316
+ name_reference_count_in_statements(function.body, function.params.first.linkage_name).zero?
317
+ end
318
+
319
+ def discarded_expression(expression)
320
+ "(void)#{wrap_expression(expression)}"
321
+ end
322
+
323
+ def emit_address_of_operand(expression)
324
+ return emit_expression(expression.operand) if expression.is_a?(IR::Unary) && expression.operator == "*"
325
+
326
+ "&#{wrap_expression(expression)}"
327
+ end
328
+
329
+ def emit_cast_operand(expression)
330
+ case expression
331
+ when IR::Name, IR::IntegerLiteral, IR::FloatLiteral, IR::StringLiteral, IR::BooleanLiteral, IR::NullLiteral, IR::ZeroInit, IR::Member, IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex, IR::Call, IR::AggregateLiteral, IR::ArrayLiteral, IR::ReinterpretExpr, IR::SizeofExpr, IR::AlignofExpr, IR::OffsetofExpr, IR::AddressOf, IR::Cast, IR::Unary
332
+ emit_expression(expression)
333
+ else
334
+ "(#{emit_expression(expression)})"
335
+ end
336
+ end
337
+
338
+ def emit_call_callee(expression)
339
+ case expression
340
+ when IR::Name, IR::Member, IR::Index, IR::CheckedIndex, IR::CheckedSpanIndex, IR::NullableIndex, IR::NullableSpanIndex, IR::Call
341
+ emit_expression(expression)
342
+ else
343
+ wrap_expression(expression)
344
+ end
345
+ end
346
+
347
+ def emit_array_out_argument(destination)
348
+ "&#{destination}"
349
+ end
350
+
351
+ def emit_array_initializer(expression)
352
+ return "{ 0 }" if expression.elements.empty?
353
+
354
+ elements = expression.elements.map { |element| emit_initializer(element) }.join(", ")
355
+ "{ #{elements} }"
356
+ end
357
+
358
+ def emit_array_compound_literal(expression)
359
+ "(#{c_declaration(expression.type, '')}) #{emit_array_initializer(expression)}"
360
+ end
361
+
362
+ def emit_zero_initializer(type)
363
+ return "{ 0 }" if type.is_a?(Types::StringView)
364
+ return "{ 0 }" if array_type?(type)
365
+ return "NULL" if type.is_a?(Types::Nullable) && c_backend_pointer_like_type?(type.base)
366
+ return "{ 0 }" if type.is_a?(Types::Nullable)
367
+ return "NULL" if raw_pointer_type?(type) || ref_type?(type)
368
+ return "NULL" if type.is_a?(Types::Opaque) && !type.external
369
+ return "false" if type.is_a?(Types::Primitive) && type.boolean?
370
+ return "0.0" if type.is_a?(Types::Primitive) && type.float?
371
+ return "0" if type.is_a?(Types::Primitive) && !type.void?
372
+ return "(#{c_type(type)}) 0" if type.is_a?(Types::EnumBase)
373
+
374
+ "{ 0 }"
375
+ end
376
+
377
+ def emit_zero_expression(type)
378
+ return "(#{c_type(type)}) #{emit_zero_initializer(type)}" if type.is_a?(Types::StringView)
379
+ return "NULL" if type.is_a?(Types::Nullable) && c_backend_pointer_like_type?(type.base)
380
+ return "(#{c_type(type)}){ 0 }" if type.is_a?(Types::Nullable)
381
+ return emit_zero_initializer(type) if type.is_a?(Types::Primitive)
382
+ return emit_zero_initializer(type) if type.is_a?(Types::EnumBase)
383
+
384
+ "(#{c_declaration(type, '')}) #{emit_zero_initializer(type)}"
385
+ end
386
+
387
+ def aggregate_field_type(type, field_name)
388
+ return proc_field_types(type).fetch(field_name) if type.is_a?(Types::Proc)
389
+ if type.is_a?(Types::Nullable)
390
+ return Types::Registry.primitive("bool") if field_name == "has_value"
391
+ return type.base if field_name == "value"
392
+ end
393
+ return type.field(field_name) if type.respond_to?(:field)
394
+
395
+ raise CBackendError, "unsupported aggregate field lookup for #{type}"
396
+ end
397
+
398
+ def emit_aggregate_field_initializer(type, field)
399
+ field_type = aggregate_field_type(type, field.name)
400
+ if field_type.is_a?(Types::Nullable) && !field.value.type.is_a?(Types::Nullable) && !c_backend_pointer_like_type?(field_type.base)
401
+ emit_nullable_some_initializer(field_type, field.value)
402
+ elsif void_storage_field?(field_type)
403
+ emit_void_field_initializer(field.value)
404
+ else
405
+ emit_initializer(field.value)
406
+ end
407
+ end
408
+
409
+ def emit_nullable_some_initializer(nullable_type, value)
410
+ "(#{c_type(nullable_type)}){ .has_value = true, .value = #{emit_initializer(value)} }"
411
+ end
412
+
413
+ def emit_variant_field_initializer(type, arm_name, field)
414
+ field_type = type.arm(arm_name).fetch(field.name)
415
+ if field_type.is_a?(Types::Nullable) && !field.value.type.is_a?(Types::Nullable) && !c_backend_pointer_like_type?(field_type.base)
416
+ emit_nullable_some_initializer(field_type, field.value)
417
+ elsif field.value.is_a?(IR::AddressOf) && !field_type.is_a?(Types::Nullable)
418
+ c_type_name = named_type_c_name(field_type)
419
+ inner = field.value.expression
420
+ "((#{c_type_name}*)memcpy(malloc(sizeof(#{c_type_name})), &(#{emit_expression(inner)}), sizeof(#{c_type_name})))"
421
+ elsif void_storage_field?(field_type)
422
+ emit_void_field_initializer(field.value)
423
+ else
424
+ emit_initializer(field.value)
425
+ end
426
+ end
427
+
428
+ def emit_void_field_initializer(expression)
429
+ "(#{emit_expression(expression)}, 0)"
430
+ end
431
+
432
+ def void_storage_field?(type)
433
+ type.is_a?(Types::Primitive) && type.void?
434
+ end
435
+
436
+ def emit_str_literal(expression)
437
+ if @str_literal_map && @str_literal_map[expression.value]
438
+ @str_literal_map[expression.value]
439
+ else
440
+ "(mt_str){ .data = #{expression.value.inspect}, .len = #{expression.value.bytesize} }"
441
+ end
442
+ end
443
+
444
+ def emit_float_literal(expression)
445
+ value = expression.value
446
+ literal = if value.finite? && value == value.to_i
447
+ format("%.1f", value)
448
+ else
449
+ value.to_s
450
+ end
451
+ expression.type.name == "float" ? "#{literal}f" : literal
452
+ end
453
+
454
+ def wrap_expression(expression)
455
+ case expression
456
+ when IR::Name, IR::IntegerLiteral, IR::FloatLiteral, IR::StringLiteral, IR::BooleanLiteral, IR::NullLiteral, IR::ZeroInit, IR::Member, IR::Index, IR::Call, IR::AggregateLiteral, IR::ArrayLiteral, IR::ReinterpretExpr, IR::SizeofExpr, IR::AlignofExpr, IR::OffsetofExpr
457
+ emit_expression(expression)
458
+ else
459
+ "(#{emit_expression(expression)})"
460
+ end
461
+ end
462
+
463
+ def layout_type_expression(type)
464
+ c_declaration(type, "")
465
+ end
466
+ end
467
+ end
468
+ end