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,223 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class CBackend
5
+ module TypeDeclaration
6
+ private
7
+
8
+ C_KEYWORDS = %w[
9
+ auto break case char const continue default do double
10
+ else enum extern float for goto if inline int long
11
+ register restrict return short signed sizeof static
12
+ struct switch typedef union unsigned void volatile while
13
+ _Bool _Complex _Imaginary alignas alignof bool complex
14
+ imaginary noreturn static_assert thread_local
15
+ ].to_set.freeze
16
+
17
+ def sanitize_c_identifier(name)
18
+ C_KEYWORDS.include?(name) ? "#{name}_" : name
19
+ end
20
+
21
+ def emit_forward_declarations(opaque_decls, aggregate_decls)
22
+ lines = []
23
+ opaque_decls.uniq { |opaque_decl| opaque_decl.linkage_name }.each do |opaque_decl|
24
+ next unless opaque_decl.forward_declarable
25
+ next unless opaque_decl.linkage_name.match?(/\A[A-Za-z_][A-Za-z0-9_]*\z/)
26
+
27
+ lines << "typedef struct #{opaque_decl.linkage_name} #{opaque_decl.linkage_name};"
28
+ end
29
+ aggregate_decls.uniq { |aggregate_decl| [aggregate_decl.class.name, aggregate_decl.linkage_name] }.each do |aggregate_decl|
30
+ case aggregate_decl
31
+ when IR::StructDecl
32
+ lines << "typedef struct #{aggregate_decl.linkage_name} #{aggregate_decl.linkage_name};"
33
+ when IR::UnionDecl
34
+ lines << "typedef union #{aggregate_decl.linkage_name} #{aggregate_decl.linkage_name};"
35
+ when IR::VariantDecl
36
+ lines << "typedef struct #{aggregate_decl.linkage_name} #{aggregate_decl.linkage_name};"
37
+ aggregate_decl.arms.each do |arm|
38
+ next if arm.fields.empty?
39
+ lines << "typedef struct #{arm.linkage_name} #{arm.linkage_name};"
40
+ end
41
+ end
42
+ end
43
+ lines
44
+ end
45
+
46
+ def emit_struct(struct_decl)
47
+ lines = []
48
+ lines << "struct #{struct_decl.linkage_name} {"
49
+ struct_decl.fields.each do |field|
50
+ lines << "#{INDENT}#{c_field_declaration(field.type, sanitize_c_identifier(field.name))};"
51
+ end
52
+ lines << "}#{struct_layout_attributes(struct_decl)};"
53
+ lines
54
+ end
55
+
56
+ def emit_union(union_decl)
57
+ lines = []
58
+ lines << "union #{union_decl.linkage_name} {"
59
+ union_decl.fields.each do |field|
60
+ lines << "#{INDENT}#{c_field_declaration(field.type, sanitize_c_identifier(field.name))};"
61
+ end
62
+ lines << "};"
63
+ lines
64
+ end
65
+
66
+ def variant_self_reference?(type, outer_c)
67
+ return false unless type.is_a?(Types::Variant) || type.is_a?(Types::VariantInstance)
68
+
69
+ named_type_c_name(type) == outer_c
70
+ end
71
+
72
+ def emit_variant(variant_decl)
73
+ lines = []
74
+ outer_c = variant_decl.linkage_name
75
+ payload_arms = variant_decl.arms.select { |a| a.fields.any? }
76
+
77
+ # Per-arm payload structs
78
+ payload_arms.each do |arm|
79
+ lines << "struct #{arm.linkage_name} {"
80
+ arm.fields.each do |field|
81
+ if variant_self_reference?(field.type, outer_c)
82
+ lines << "#{INDENT}#{c_declaration(field.type, "*#{sanitize_c_identifier(field.name)}")};"
83
+ else
84
+ lines << "#{INDENT}#{c_field_declaration(field.type, sanitize_c_identifier(field.name))};"
85
+ end
86
+ end
87
+ lines << "};"
88
+ end
89
+
90
+
91
+ # Kind enum
92
+ lines << "typedef int32_t #{outer_c}_kind;"
93
+ unless variant_decl.arms.empty?
94
+ lines << "enum {"
95
+ variant_decl.arms.each_with_index do |arm, index|
96
+ suffix = index == variant_decl.arms.length - 1 ? "" : ","
97
+ lines << "#{INDENT}#{outer_c}_kind_#{arm.name} = #{index}#{suffix}"
98
+ end
99
+ lines << "};"
100
+ end
101
+
102
+ # Data union (only if at least one arm has payload)
103
+ if payload_arms.any?
104
+ lines << "union #{outer_c}_data {"
105
+ payload_arms.each do |arm|
106
+ lines << "#{INDENT}struct #{arm.linkage_name} #{sanitize_c_identifier(arm.name)};"
107
+ end
108
+ lines << "};"
109
+ end
110
+
111
+ # Outer struct
112
+ lines << "struct #{outer_c} {"
113
+ lines << "#{INDENT}#{outer_c}_kind kind;"
114
+ lines << "#{INDENT}union #{outer_c}_data data;" if payload_arms.any?
115
+ lines << "};"
116
+ lines
117
+ end
118
+
119
+ def emit_enum(enum_decl)
120
+ lines = ["typedef #{c_type(enum_decl.backing_type)} #{enum_decl.linkage_name};"]
121
+ return lines if enum_decl.members.empty?
122
+
123
+ lines << "enum {"
124
+ enum_decl.members.each_with_index do |member, index|
125
+ suffix = index == enum_decl.members.length - 1 ? "" : ","
126
+ lines << "#{INDENT}#{member.linkage_name} = #{emit_expression(member.value)}#{suffix}"
127
+ end
128
+ lines << "};"
129
+ lines
130
+ end
131
+
132
+ def emit_function_declarations(functions)
133
+ functions.map { |function| "#{function_signature(function)};" }
134
+ end
135
+
136
+ def function_signature(function)
137
+ prefix = function.entry_point ? "" : "static "
138
+ "#{prefix}#{c_function_declaration(function.return_type, function.linkage_name, function_params(function))}"
139
+ end
140
+
141
+ def function_params(function)
142
+ params = []
143
+ params << array_out_param_declaration(function.return_type, ARRAY_OUT_PARAM_NAME) if array_type?(function.return_type)
144
+ params.concat(emitted_function_params(function).map { |param| c_declaration(param.pointer ? pointer_to(param.type) : param.type, param.linkage_name) })
145
+
146
+ if params.empty?
147
+ "void"
148
+ else
149
+ params.join(", ")
150
+ end
151
+ end
152
+
153
+ def emit_function(function)
154
+ @checked_index_alias_stack.clear
155
+ @checked_index_alias_id = 0
156
+ @suppressed_labels = []
157
+ body = compact_generated_statement_sequence(function.body)
158
+ lines = ["#{function_signature(function)} {"]
159
+ used_labels = collect_used_labels(body)
160
+ if body.empty?
161
+ lines << "}"
162
+ else
163
+ lines.concat(emit_statement_sequence(body, 1, function:, used_labels:))
164
+ lines << "}"
165
+ end
166
+ lines
167
+ end
168
+
169
+ def emit_string_type
170
+ [
171
+ "typedef struct mt_str {",
172
+ "#{INDENT}char* data;",
173
+ "#{INDENT}uintptr_t len;",
174
+ "} mt_str;",
175
+ ]
176
+ end
177
+
178
+ def emit_span_type(type)
179
+ span_type = span_type_name(type)
180
+ [
181
+ "typedef struct #{span_type} {",
182
+ "#{INDENT}#{c_declaration(pointer_to(type.element_type), 'data')};",
183
+ "#{INDENT}#{c_declaration(Types::Registry.primitive('ptr_uint'), 'len')};",
184
+ "} #{span_type};",
185
+ ]
186
+ end
187
+
188
+ def emit_soa_type(type)
189
+ name = soa_type_name(type)
190
+ lines = ["typedef struct #{name} {"]
191
+ type.fields.each do |field_name, field_type|
192
+ lines << "#{INDENT}#{c_field_declaration(field_type, sanitize_c_identifier(field_name))};"
193
+ end
194
+ lines << "} #{name};"
195
+ lines
196
+ end
197
+
198
+ def struct_layout_attributes(struct_decl)
199
+ attributes = []
200
+ attributes << "packed" if struct_decl.packed
201
+ attributes << "aligned(#{struct_decl.alignment})" if struct_decl.alignment
202
+ return "" if attributes.empty?
203
+
204
+ " __attribute__((#{attributes.join(', ')}))"
205
+ end
206
+
207
+ def emit_vector_math_types
208
+ lines = []
209
+ lines << "typedef struct mt_vec2 { float x; float y; } mt_vec2;"
210
+ lines << "typedef struct mt_vec3 { float x; float y; float z; } mt_vec3;"
211
+ lines << "typedef struct mt_vec4 { float x; float y; float z; float w; } mt_vec4;"
212
+ lines << "typedef struct mt_ivec2 { int32_t x; int32_t y; } mt_ivec2;"
213
+ lines << "typedef struct mt_ivec3 { int32_t x; int32_t y; int32_t z; } mt_ivec3;"
214
+ lines << "typedef struct mt_ivec4 { int32_t x; int32_t y; int32_t z; int32_t w; } mt_ivec4;"
215
+ lines << ""
216
+ lines << "typedef struct mt_mat3 { mt_vec3 col0; mt_vec3 col1; mt_vec3 col2; } mt_mat3;"
217
+ lines << "typedef struct mt_mat4 { mt_vec4 col0; mt_vec4 col1; mt_vec4 col2; mt_vec4 col3; } mt_mat4;"
218
+ lines << "typedef struct mt_quat { float x; float y; float z; float w; } mt_quat;"
219
+ lines
220
+ end
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,345 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class CBackend
5
+ module TypeSystem
6
+ private
7
+
8
+ PRIMITIVE_C_TYPE_MAP = {
9
+ "bool" => "bool",
10
+ "byte" => "int8_t",
11
+ "ubyte" => "uint8_t",
12
+ "char" => "char",
13
+ "short" => "int16_t",
14
+ "ushort" => "uint16_t",
15
+ "int" => "int32_t",
16
+ "uint" => "uint32_t",
17
+ "long" => "int64_t",
18
+ "ulong" => "uint64_t",
19
+ "ptr_int" => "intptr_t",
20
+ "ptr_uint" => "uintptr_t",
21
+ "float" => "float",
22
+ "double" => "double",
23
+ "void" => "void",
24
+ "cstr" => "const char*",
25
+ }.freeze
26
+
27
+ SANITIZE_NON_ALNUM_RE = /[^A-Za-z0-9_]+/
28
+ SANITIZE_UNDERSCORES_RE = /_+/
29
+ SANITIZE_LEADING_UNDERSCORES_RE = /^_+/
30
+ SANITIZE_TRAILING_UNDERSCORES_RE = /_+$/
31
+
32
+ def c_declaration(type, name)
33
+ base, declarator = c_declaration_parts(type, name)
34
+ declarator.empty? ? base : "#{base} #{declarator}"
35
+ end
36
+
37
+ def c_field_declaration(type, name)
38
+ return "uint8_t #{name}" if void_storage_field?(type)
39
+
40
+ c_declaration(type, name)
41
+ end
42
+
43
+ def c_function_declaration(return_type, name, params)
44
+ c_declaration(array_type?(return_type) ? void_type : return_type, "#{name}(#{params})")
45
+ end
46
+
47
+ def c_function_return_type(type)
48
+ c_type(array_type?(type) ? void_type : type)
49
+ end
50
+
51
+ def array_out_param_declaration(type, name)
52
+ c_declaration(type, "(*#{name})")
53
+ end
54
+
55
+ def c_declaration_parts(type, name)
56
+ name = name.to_s
57
+
58
+ if array_type?(type)
59
+ declarator = declarator_needs_grouping?(name) ? "(#{name})" : name
60
+ return c_declaration_parts(array_element_type(type), "#{declarator}[#{array_length(type)}]")
61
+ end
62
+
63
+ if type.is_a?(Types::Nullable) && type.base.is_a?(Types::Function)
64
+ return c_declaration_parts(type.base, name)
65
+ end
66
+
67
+ if type.is_a?(Types::Function)
68
+ params = []
69
+ params << array_out_param_declaration(type.return_type, ARRAY_OUT_PARAM_NAME) if array_type?(type.return_type)
70
+ params.concat(type.params.each_with_index.map do |param, index|
71
+ c_declaration(param.type, param.name || "arg#{index}")
72
+ end)
73
+ params << "..." if type.variadic
74
+ params = ["void"] if params.empty?
75
+ return [c_function_return_type(type.return_type), "(*#{name})(#{params.join(', ')})"]
76
+ end
77
+
78
+ if type.is_a?(Types::Proc)
79
+ return [proc_type_name(type), name]
80
+ end
81
+
82
+ if mutable_pointer_type?(type) || own_type?(type)
83
+ return c_declaration_parts(type.arguments.first, "*#{name}")
84
+ end
85
+
86
+ if const_pointer_type?(type)
87
+ return [generic_c_type(type), name]
88
+ end
89
+
90
+ if ref_type?(type)
91
+ ref_arguments = type.arguments
92
+ ref_target = ref_arguments.length == 1 ? ref_arguments.first : ref_arguments[1]
93
+ return c_declaration_parts(ref_target, "*#{name}")
94
+ end
95
+
96
+ [c_type(type), name]
97
+ end
98
+
99
+ def declarator_needs_grouping?(name)
100
+ !name.empty? && (name.start_with?("*") || name.include?("["))
101
+ end
102
+
103
+ def c_type(type, pointer: false)
104
+ case type
105
+ when Types::Nullable
106
+ return c_type(type.base, pointer:) if type.base.is_a?(Types::Function)
107
+
108
+ unless c_backend_pointer_like_type?(type.base)
109
+ base = nullable_opt_type_name(type)
110
+ return pointer ? "#{base}*" : base
111
+ end
112
+
113
+ base = c_type(type.base)
114
+ base.end_with?("*") ? base : "#{base}*"
115
+ when Types::StringView
116
+ base = "mt_str"
117
+ pointer ? "#{base}*" : base
118
+ when Types::Primitive
119
+ base = primitive_c_type(type.name)
120
+ pointer ? "#{base}*" : base
121
+ when Types::Span
122
+ base = span_type_name(type)
123
+ pointer ? "#{base}*" : base
124
+ when Types::Task
125
+ base = task_type_name(type)
126
+ pointer ? "#{base}*" : base
127
+ when Types::Proc
128
+ base = proc_type_name(type)
129
+ pointer ? "#{base}*" : base
130
+ when Types::Dyn
131
+ base = dyn_type_name(type)
132
+ pointer ? "#{base}*" : base
133
+ when Types::DynVtable
134
+ base = type.linkage_name
135
+ pointer ? "#{base}*" : base
136
+ when Types::Function
137
+ base = c_declaration(type, "")
138
+ pointer ? "#{base}*" : base
139
+ when Types::GenericInstance
140
+ base = generic_c_type(type)
141
+ pointer ? "#{base}*" : base
142
+ when Types::Struct, Types::StructInstance, Types::Union, Types::Enum, Types::Flags, Types::Variant, Types::VariantInstance, Types::VariantArmPayload, Types::Event, Types::Subscription
143
+ base = named_type_c_name(type)
144
+ pointer ? "#{base}*" : base
145
+ when Types::Opaque
146
+ if type.external
147
+ base = external_opaque_c_type(type)
148
+ pointer ? "#{base}*" : base
149
+ else
150
+ base = type.linkage_name || named_type_c_name(type)
151
+ pointer ? "#{base}**" : "#{base}*"
152
+ end
153
+ when Types::Vector
154
+ base = "mt_#{type.name}"
155
+ pointer ? "#{base}*" : base
156
+ when Types::Matrix
157
+ base = "mt_#{type.name}"
158
+ pointer ? "#{base}*" : base
159
+ when Types::Quaternion
160
+ base = "mt_#{type.name}"
161
+ pointer ? "#{base}*" : base
162
+ when Types::SoA
163
+ base = soa_type_name(type)
164
+ pointer ? "#{base}*" : base
165
+ when Types::Tuple
166
+ base = tuple_type_name(type)
167
+ pointer ? "#{base}*" : base
168
+ when Types::Handle
169
+ "void*"
170
+ else
171
+ raise CBackendError, "unsupported C type #{type.class.name}"
172
+ end
173
+ end
174
+
175
+ def constant_storage(type)
176
+ return "static const" if array_type?(type)
177
+
178
+ c_type(type).start_with?("const ") ? "static" : "static const"
179
+ end
180
+
181
+ def global_storage(_type)
182
+ "static"
183
+ end
184
+
185
+ def span_type_name(type)
186
+ "mt_span_#{sanitize_identifier(type.element_type.to_s)}"
187
+ end
188
+
189
+ def soa_type_name(type)
190
+ element_name = sanitize_identifier(type.element_type.respond_to?(:name) ? type.element_type.name : type.element_type.to_s)
191
+ "mt_soa_#{element_name}_#{type.count}"
192
+ end
193
+
194
+ def tuple_type_name(type)
195
+ sanitized = type.element_types.map { |et| sanitize_identifier(et.to_s) }.join("_")
196
+ base = "mt_tuple_#{sanitized}"
197
+ default_names = type.element_types.each_with_index.map { |_, i| "_#{i}" }
198
+ if type.field_names != default_names
199
+ base << "_" << type.field_names.map { |n| sanitize_identifier(n) }.join("_")
200
+ end
201
+ base
202
+ end
203
+
204
+ def task_type_name(type)
205
+ "mt_task_#{sanitize_identifier(type.result_type.to_s)}"
206
+ end
207
+
208
+ def proc_type_name(type)
209
+ "mt_proc_#{sanitize_identifier(type.to_s)}"
210
+ end
211
+
212
+ def dyn_type_name(type)
213
+ "mt_dyn_#{sanitize_identifier(type.interface_binding.name)}"
214
+ end
215
+
216
+ def nullable_opt_type_name(type)
217
+ "mt_opt_#{sanitize_identifier(c_type(type.base))}"
218
+ end
219
+
220
+ def named_type_c_name(type)
221
+ return task_type_name(type) if type.is_a?(Types::Task)
222
+ if type.is_a?(Types::VariantArmPayload)
223
+ return "#{named_type_c_name(type.variant_type)}_#{type.arm_name}"
224
+ end
225
+
226
+ if type.respond_to?(:linkage_name) && type.linkage_name
227
+ return type.linkage_name
228
+ end
229
+
230
+ base_name = type.module_name&.start_with?("std.c.") ? type.name : type.module_name ? "#{module_c_prefix(type.module_name)}_#{type.name}" : type.name
231
+ return base_name unless type.is_a?(Types::StructInstance) || type.is_a?(Types::VariantInstance)
232
+
233
+ "#{base_name}_#{sanitize_identifier(type.arguments.join('_'))}"
234
+ end
235
+
236
+ def external_opaque_c_type(type)
237
+ type.linkage_name || type.name
238
+ end
239
+
240
+ def sanitize_identifier(text)
241
+ identifier = text.gsub(SANITIZE_NON_ALNUM_RE, "_").gsub(SANITIZE_UNDERSCORES_RE, "_").sub(SANITIZE_LEADING_UNDERSCORES_RE, "").sub(SANITIZE_TRAILING_UNDERSCORES_RE, "")
242
+ identifier.empty? ? "value" : identifier
243
+ end
244
+
245
+ def module_c_prefix(module_name)
246
+ sanitize_identifier(module_name.to_s.tr('.', '_'))
247
+ end
248
+
249
+ def primitive_c_type(name)
250
+ PRIMITIVE_C_TYPE_MAP.fetch(name)
251
+ end
252
+
253
+ def generic_c_type(type)
254
+ case type.name
255
+ when "ptr"
256
+ raise CBackendError, "ptr requires exactly one type argument" unless type.arguments.length == 1
257
+
258
+ "#{c_type(type.arguments.first)}*"
259
+ when "const_ptr"
260
+ raise CBackendError, "const_ptr requires exactly one type argument" unless type.arguments.length == 1
261
+
262
+ "const #{c_type(type.arguments.first)}*"
263
+ when "own"
264
+ raise CBackendError, "own requires exactly one type argument" unless type.arguments.length == 1
265
+
266
+ "#{c_type(type.arguments.first)}*"
267
+ when "ref"
268
+ raise CBackendError, "ref requires at least one type argument" unless [1, 2].include?(type.arguments.length)
269
+
270
+ "#{c_type(type.arguments.length == 1 ? type.arguments.first : type.arguments[1])}*"
271
+ when "str_buffer"
272
+ raise CBackendError, "str_buffer requires exactly one type argument" unless str_buffer_type?(type)
273
+
274
+ str_buffer_type_name(type)
275
+ when "atomic"
276
+ "_Atomic #{c_type(type.arguments.first)}"
277
+ else
278
+ raise CBackendError, "unsupported generic C type #{type.name}"
279
+ end
280
+ end
281
+
282
+ def mutable_pointer_type?(type)
283
+ type.is_a?(Types::GenericInstance) && type.name == "ptr" && type.arguments.length == 1
284
+ end
285
+
286
+ def const_pointer_type?(type)
287
+ type.is_a?(Types::GenericInstance) && type.name == "const_ptr" && type.arguments.length == 1
288
+ end
289
+
290
+ def own_type?(type)
291
+ type.is_a?(Types::GenericInstance) && type.name == "own" && type.arguments.length == 1
292
+ end
293
+
294
+ def pointer_type?(type)
295
+ mutable_pointer_type?(type)
296
+ end
297
+
298
+ def c_backend_pointer_like_type?(type)
299
+ pointer_type?(type) || const_pointer_type?(type) || own_type?(type) || (type.is_a?(Types::Primitive) && type.name == "cstr") || type.is_a?(Types::Function) || type.is_a?(Types::Proc) || type.is_a?(Types::Opaque)
300
+ end
301
+
302
+ def raw_pointer_type?(type)
303
+ mutable_pointer_type?(type) || const_pointer_type?(type) || own_type?(type)
304
+ end
305
+
306
+ def ref_type?(type)
307
+ type.is_a?(Types::GenericInstance) && type.name == "ref" && [1, 2].include?(type.arguments.length)
308
+ end
309
+
310
+ def array_type?(type)
311
+ type.is_a?(Types::GenericInstance) && type.name == "array" && type.arguments.length == 2 &&
312
+ type.arguments[1].is_a?(Types::LiteralTypeArg)
313
+ end
314
+
315
+ def array_element_type(type)
316
+ type.arguments.first
317
+ end
318
+
319
+ def array_length(type)
320
+ type.arguments[1].value
321
+ end
322
+
323
+ def str_buffer_type?(type)
324
+ type.is_a?(Types::GenericInstance) && type.name == "str_buffer" && type.arguments.length == 1 &&
325
+ type.arguments.first.is_a?(Types::LiteralTypeArg) && type.arguments.first.value.is_a?(Integer)
326
+ end
327
+
328
+ def str_buffer_capacity(type)
329
+ type.arguments.first.value
330
+ end
331
+
332
+ def str_buffer_storage_capacity(type)
333
+ str_buffer_capacity(type) + 1
334
+ end
335
+
336
+ def str_buffer_type_name(type)
337
+ "mt_str_buffer_#{str_buffer_capacity(type)}"
338
+ end
339
+
340
+ def pointer_to(type)
341
+ Types::Registry.generic_instance("ptr", [type])
342
+ end
343
+ end
344
+ end
345
+ end