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,1080 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class CBackend
5
+ module RuntimeHelpers
6
+ private
7
+
8
+ def emit_fatal_helper
9
+ lines = []
10
+
11
+ if uses_mt_fatal_helper?
12
+ lines.concat([
13
+ "static _Noreturn void mt_fatal(const char* message) {",
14
+ "#{INDENT}fputs(message, stderr);",
15
+ "#{INDENT}fputc('\\n', stderr);",
16
+ "#{INDENT}abort();",
17
+ "}",
18
+ ])
19
+ end
20
+
21
+ if uses_mt_fatal_str_helper?
22
+ lines << "" unless lines.empty?
23
+ lines.concat([
24
+ "static _Noreturn void mt_fatal_str(mt_str message) {",
25
+ "#{INDENT}fwrite(message.data, 1, message.len, stderr);",
26
+ "#{INDENT}fputc('\\n', stderr);",
27
+ "#{INDENT}abort();",
28
+ "}",
29
+ ])
30
+ end
31
+
32
+ lines
33
+ end
34
+
35
+ def emit_str_equality_helper
36
+ [
37
+ "static bool mt_str_equal(mt_str left, mt_str right) {",
38
+ "#{INDENT}if (left.len != right.len) return false;",
39
+ "#{INDENT}for (uintptr_t index = 0; index < left.len; index++) {",
40
+ "#{INDENT * 2}if (left.data[index] != right.data[index]) return false;",
41
+ "#{INDENT}}",
42
+ "#{INDENT}return true;",
43
+ "}",
44
+ ]
45
+ end
46
+
47
+ def emit_variant_equality_helpers
48
+ emitted_aggregate_variants.flat_map { |variant_decl| emit_variant_equality_helper(variant_decl) }
49
+ end
50
+
51
+ def emit_variant_equality_helper(variant_decl)
52
+ outer_c = variant_decl.linkage_name
53
+ lines = ["static bool mt_variant_eq_#{outer_c}(struct #{outer_c} left, struct #{outer_c} right) {"]
54
+ lines << "#{INDENT}if (left.kind != right.kind) return false;"
55
+ lines << "#{INDENT}switch (left.kind) {"
56
+
57
+ variant_decl.arms.each do |arm|
58
+ lines << "#{INDENT * 2}case #{outer_c}_kind_#{arm.name}:"
59
+ if arm.fields.empty?
60
+ lines << "#{INDENT * 3}return true;"
61
+ else
62
+ arm.fields.each do |field|
63
+ field_type = field.type
64
+ left_expr = "left.data.#{sanitize_c_identifier(arm.name)}.#{sanitize_c_identifier(field.name)}"
65
+ right_expr = "right.data.#{sanitize_c_identifier(arm.name)}.#{sanitize_c_identifier(field.name)}"
66
+ if field_type.is_a?(Types::StringView)
67
+ lines << "#{INDENT * 3}if (!mt_str_equal(#{left_expr}, #{right_expr})) return false;"
68
+ elsif field_type.is_a?(Types::Variant)
69
+ lines << "#{INDENT * 3}if (!mt_variant_eq_#{named_type_c_name(field_type)}(#{left_expr}, #{right_expr})) return false;"
70
+ elsif field_type.is_a?(Types::Primitive) || field_type.is_a?(Types::EnumBase) || field_type.is_a?(Types::Nullable)
71
+ lines << "#{INDENT * 3}if (#{left_expr} != #{right_expr}) return false;"
72
+ else
73
+ lines << "#{INDENT * 3}if (#{left_expr} != #{right_expr}) return false;"
74
+ end
75
+ end
76
+ lines << "#{INDENT * 3}return true;"
77
+ end
78
+ end
79
+
80
+ lines << "#{INDENT * 2}default: return true;"
81
+ lines << "#{INDENT}}"
82
+ lines << "}"
83
+ lines
84
+ end
85
+
86
+ def emit_async_memory_helpers
87
+ [
88
+ "#define MT_ASYNC_HEADER_SIZE (sizeof(uint64_t) + sizeof(uintptr_t))",
89
+ "#define MT_ASYNC_MAGIC UINT64_C(0x4D5441464D454D00)",
90
+ "",
91
+ "static void* mt_async_alloc(uintptr_t size) {",
92
+ "#{INDENT}char* raw = (char*) calloc(1, (size_t)(MT_ASYNC_HEADER_SIZE + size));",
93
+ "#{INDENT}if (raw == NULL) {",
94
+ "#{INDENT * 2}abort();",
95
+ "#{INDENT}}",
96
+ "#{INDENT}*(uint64_t*)raw = MT_ASYNC_MAGIC;",
97
+ "#{INDENT}*(uintptr_t*)(raw + sizeof(uint64_t)) = 1;",
98
+ "#{INDENT}return raw + MT_ASYNC_HEADER_SIZE;",
99
+ "}",
100
+ "",
101
+ "static void mt_async_retain(void* frame) {",
102
+ "#{INDENT}char* raw = (char*)frame - MT_ASYNC_HEADER_SIZE;",
103
+ "#{INDENT}if (*(uint64_t*)raw != MT_ASYNC_MAGIC) return;",
104
+ "#{INDENT}uintptr_t* ref = (uintptr_t*)(raw + sizeof(uint64_t));",
105
+ "#{INDENT}(*ref)++;",
106
+ "}",
107
+ "",
108
+ "static void mt_async_free(void* frame) {",
109
+ "#{INDENT}char* raw = (char*)frame - MT_ASYNC_HEADER_SIZE;",
110
+ "#{INDENT}if (*(uint64_t*)raw != MT_ASYNC_MAGIC) return;",
111
+ "#{INDENT}uintptr_t* ref = (uintptr_t*)(raw + sizeof(uint64_t));",
112
+ "#{INDENT}if (--(*ref) == 0) {",
113
+ "#{INDENT * 2}free(raw);",
114
+ "#{INDENT}}",
115
+ "}",
116
+ ]
117
+ end
118
+
119
+ def emit_parallel_for_helper
120
+ [
121
+ "typedef struct {",
122
+ "#{INDENT}void (*work)(void* data, int64_t start, int64_t end);",
123
+ "#{INDENT}void* data;",
124
+ "#{INDENT}int64_t start;",
125
+ "#{INDENT}int64_t end;",
126
+ "} mt_pfor_chunk;",
127
+ "",
128
+ "static void mt_pfor_runner(void* arg) {",
129
+ "#{INDENT}mt_pfor_chunk* chunk = (mt_pfor_chunk*)arg;",
130
+ "#{INDENT}chunk->work(chunk->data, chunk->start, chunk->end);",
131
+ "}",
132
+ "",
133
+ "static void mt_parallel_for(void (*work)(void* data, int64_t start, int64_t end), void* data, int64_t count) {",
134
+ "#{INDENT}if (count <= 0) return;",
135
+ "#{INDENT}uv_cpu_info_t* cpu_info;",
136
+ "#{INDENT}int ncpu = 1;",
137
+ "#{INDENT}if (uv_cpu_info(&cpu_info, &ncpu) == 0) {",
138
+ "#{INDENT * 2}uv_free_cpu_info(cpu_info, ncpu);",
139
+ "#{INDENT}}",
140
+ "#{INDENT}if (ncpu < 1) ncpu = 1;",
141
+ "#{INDENT}if (ncpu > 64) ncpu = 64;",
142
+ "#{INDENT}if (count < (int64_t)ncpu) ncpu = (int)count;",
143
+ "#{INDENT}int64_t chunk_size = (count + ncpu - 1) / ncpu;",
144
+ "#{INDENT}mt_pfor_chunk chunks[64];",
145
+ "#{INDENT}uv_thread_t threads[64];",
146
+ "#{INDENT}int nworkers = 0;",
147
+ "#{INDENT}for (int t = 1; t < ncpu; t++) {",
148
+ "#{INDENT * 2}int64_t s = t * chunk_size;",
149
+ "#{INDENT * 2}int64_t e = s + chunk_size;",
150
+ "#{INDENT * 2}if (e > count) e = count;",
151
+ "#{INDENT * 2}if (s >= count) break;",
152
+ "#{INDENT * 2}chunks[nworkers].work = work;",
153
+ "#{INDENT * 2}chunks[nworkers].data = data;",
154
+ "#{INDENT * 2}chunks[nworkers].start = s;",
155
+ "#{INDENT * 2}chunks[nworkers].end = e;",
156
+ "#{INDENT * 2}uv_thread_create(&threads[nworkers], mt_pfor_runner, &chunks[nworkers]);",
157
+ "#{INDENT * 2}nworkers++;",
158
+ "#{INDENT}}",
159
+ "#{INDENT}int64_t first_end = chunk_size < count ? chunk_size : count;",
160
+ "#{INDENT}work(data, 0, first_end);",
161
+ "#{INDENT}for (int t = 0; t < nworkers; t++) {",
162
+ "#{INDENT * 2}uv_thread_join(&threads[t]);",
163
+ "#{INDENT}}",
164
+ "}",
165
+ ]
166
+ end
167
+
168
+ def emit_spawn_all_helper
169
+ [
170
+ "typedef struct {",
171
+ "#{INDENT}void (*work)(void* data);",
172
+ "#{INDENT}void* data;",
173
+ "} mt_spawn_item;",
174
+ "",
175
+ "static void mt_spawn_item_runner(void* arg) {",
176
+ "#{INDENT}mt_spawn_item* item = (mt_spawn_item*)arg;",
177
+ "#{INDENT}item->work(item->data);",
178
+ "}",
179
+ "",
180
+ "static void mt_spawn_all(mt_spawn_item* items, int count) {",
181
+ "#{INDENT}if (count <= 0) return;",
182
+ "#{INDENT}uv_thread_t threads[64];",
183
+ "#{INDENT}int nworkers = 0;",
184
+ "#{INDENT}for (int t = 1; t < count && nworkers < 63; t++) {",
185
+ "#{INDENT * 2}uv_thread_create(&threads[nworkers], mt_spawn_item_runner, &items[t]);",
186
+ "#{INDENT * 2}nworkers++;",
187
+ "#{INDENT}}",
188
+ "#{INDENT}items[0].work(items[0].data);",
189
+ "#{INDENT}for (int t = 0; t < nworkers; t++) {",
190
+ "#{INDENT * 2}uv_thread_join(&threads[t]);",
191
+ "#{INDENT}}",
192
+ "}",
193
+ ]
194
+ end
195
+
196
+ def emit_detach_helpers
197
+ [
198
+ "typedef struct {",
199
+ "#{INDENT}uv_thread_t thread;",
200
+ "} mt_detach_handle;",
201
+ "",
202
+ "static void* mt_detach_run(void (*work)(void*), void* cap) {",
203
+ "#{INDENT}mt_detach_handle* h = (mt_detach_handle*)malloc(sizeof(mt_detach_handle));",
204
+ "#{INDENT}uv_thread_create(&h->thread, work, cap);",
205
+ "#{INDENT}return h;",
206
+ "}",
207
+ "",
208
+ "static void mt_detach_join(void* handle) {",
209
+ "#{INDENT}if (!handle) return;",
210
+ "#{INDENT}mt_detach_handle* h = (mt_detach_handle*)handle;",
211
+ "#{INDENT}uv_thread_join(&h->thread);",
212
+ "#{INDENT}free(h);",
213
+ "}",
214
+ ]
215
+ end
216
+
217
+ def emit_format_helpers
218
+ helpers = used_format_helpers
219
+ lines = []
220
+
221
+ if helpers['mt_format_str_make']
222
+ lines.concat([
223
+ "static mt_str mt_format_str_make(uintptr_t len) {",
224
+ "#{INDENT}char* data = (char*)malloc((size_t)(len + 1));",
225
+ "#{INDENT}if (data == NULL) mt_fatal(\"format string allocation failed\");",
226
+ "#{INDENT}data[len] = '\\0';",
227
+ "#{INDENT}return (mt_str){ .data = data, .len = len };",
228
+ "}",
229
+ ])
230
+ end
231
+
232
+ if helpers['mt_format_str_release']
233
+ lines << "" unless lines.empty?
234
+ lines.concat([
235
+ "static void mt_format_str_release(mt_str value) {",
236
+ "#{INDENT}free(value.data);",
237
+ "}",
238
+ ])
239
+ end
240
+
241
+ if helpers['mt_format_check_capacity']
242
+ lines << "" unless lines.empty?
243
+ lines.concat([
244
+ "static void mt_format_check_capacity(mt_str target, uintptr_t offset, uintptr_t len) {",
245
+ "#{INDENT}if (offset > target.len || len > target.len - offset) mt_fatal(\"format string append exceeds capacity\");",
246
+ "}",
247
+ ])
248
+ end
249
+
250
+ if helpers['mt_format_append_bytes']
251
+ lines << "" unless lines.empty?
252
+ lines.concat([
253
+ "static uintptr_t mt_format_append_bytes(mt_str target, uintptr_t offset, const char* data, uintptr_t len) {",
254
+ "#{INDENT}mt_format_check_capacity(target, offset, len);",
255
+ "#{INDENT}if (len > 0) memcpy(target.data + offset, data, (size_t)len);",
256
+ "#{INDENT}offset += len;",
257
+ "#{INDENT}target.data[offset] = '\\0';",
258
+ "#{INDENT}return offset;",
259
+ "}",
260
+ ])
261
+ end
262
+
263
+ if helpers['mt_format_cstr_len']
264
+ lines << "" unless lines.empty?
265
+ lines.concat([
266
+ "static uintptr_t mt_format_cstr_len(const char* value) {",
267
+ "#{INDENT}return (uintptr_t)strlen(value);",
268
+ "}",
269
+ ])
270
+ end
271
+
272
+ if helpers['mt_format_bool_len']
273
+ lines << "" unless lines.empty?
274
+ lines.concat([
275
+ "static uintptr_t mt_format_bool_len(bool value) {",
276
+ "#{INDENT}return value ? 4 : 5;",
277
+ "}",
278
+ ])
279
+ end
280
+
281
+ if helpers['mt_format_ptr_uint_len']
282
+ lines << "" unless lines.empty?
283
+ lines.concat([
284
+ "static uintptr_t mt_format_ptr_uint_len(uintptr_t value) {",
285
+ "#{INDENT}uintptr_t len = 1;",
286
+ "#{INDENT}while (value >= 10) {",
287
+ "#{INDENT * 2}value /= 10;",
288
+ "#{INDENT * 2}len += 1;",
289
+ "#{INDENT}}",
290
+ "#{INDENT}return len;",
291
+ "}",
292
+ ])
293
+ end
294
+
295
+ if helpers['mt_format_ulong_len']
296
+ lines << "" unless lines.empty?
297
+ lines.concat([
298
+ "static uintptr_t mt_format_ulong_len(uint64_t value) {",
299
+ "#{INDENT}uintptr_t len = 1;",
300
+ "#{INDENT}while (value >= 10) {",
301
+ "#{INDENT * 2}value /= 10;",
302
+ "#{INDENT * 2}len += 1;",
303
+ "#{INDENT}}",
304
+ "#{INDENT}return len;",
305
+ "}",
306
+ ])
307
+ end
308
+
309
+ if helpers['mt_format_ulong_hex_len']
310
+ lines << "" unless lines.empty?
311
+ lines.concat([
312
+ "static uintptr_t mt_format_ulong_hex_len(uint64_t value) {",
313
+ "#{INDENT}int written = snprintf(NULL, 0, \"%llx\", (unsigned long long)value);",
314
+ "#{INDENT}if (written < 0) mt_fatal(\"format string could not measure unsigned hex\");",
315
+ "#{INDENT}return (uintptr_t)written;",
316
+ "}",
317
+ ])
318
+ end
319
+
320
+ if helpers['mt_format_uint_len']
321
+ lines << "" unless lines.empty?
322
+ lines.concat([
323
+ "static uintptr_t mt_format_uint_len(uint32_t value) {",
324
+ "#{INDENT}return mt_format_ptr_uint_len((uintptr_t)value);",
325
+ "}",
326
+ ])
327
+ end
328
+
329
+ if helpers['mt_format_long_len']
330
+ lines << "" unless lines.empty?
331
+ lines.concat([
332
+ "static uintptr_t mt_format_long_len(int64_t value) {",
333
+ "#{INDENT}if (value < 0) return 1 + mt_format_ulong_len(((uint64_t)(-(value + 1))) + 1);",
334
+ "#{INDENT}return mt_format_ulong_len((uint64_t)value);",
335
+ "}",
336
+ ])
337
+ end
338
+
339
+ if helpers['mt_format_long_hex_len']
340
+ lines << "" unless lines.empty?
341
+ lines.concat([
342
+ "static uintptr_t mt_format_long_hex_len(int64_t value) {",
343
+ "#{INDENT}if (value < 0) return 1 + mt_format_ulong_hex_len(((uint64_t)(-(value + 1))) + 1);",
344
+ "#{INDENT}return mt_format_ulong_hex_len((uint64_t)value);",
345
+ "}",
346
+ ])
347
+ end
348
+
349
+ if helpers['mt_format_ulong_oct_len']
350
+ lines << "" unless lines.empty?
351
+ lines.concat([
352
+ "static uintptr_t mt_format_ulong_oct_len(uint64_t value) {",
353
+ "#{INDENT}int written = snprintf(NULL, 0, \"%llo\", (unsigned long long)value);",
354
+ "#{INDENT}if (written < 0) mt_fatal(\"format string could not measure unsigned octal\");",
355
+ "#{INDENT}return (uintptr_t)written;",
356
+ "}",
357
+ ])
358
+ end
359
+
360
+ if helpers['mt_format_long_oct_len']
361
+ lines << "" unless lines.empty?
362
+ lines.concat([
363
+ "static uintptr_t mt_format_long_oct_len(int64_t value) {",
364
+ "#{INDENT}if (value < 0) return 1 + mt_format_ulong_oct_len(((uint64_t)(-(value + 1))) + 1);",
365
+ "#{INDENT}return mt_format_ulong_oct_len((uint64_t)value);",
366
+ "}",
367
+ ])
368
+ end
369
+
370
+ if helpers['mt_format_ulong_bin_len']
371
+ lines << "" unless lines.empty?
372
+ lines.concat([
373
+ "static uintptr_t mt_format_ulong_bin_len(uint64_t value) {",
374
+ "#{INDENT}uintptr_t len = 1;",
375
+ "#{INDENT}while (value >= 2) {",
376
+ "#{INDENT * 2}value >>= 1;",
377
+ "#{INDENT * 2}len += 1;",
378
+ "#{INDENT}}",
379
+ "#{INDENT}return len;",
380
+ "}",
381
+ ])
382
+ end
383
+
384
+ if helpers['mt_format_long_bin_len']
385
+ lines << "" unless lines.empty?
386
+ lines.concat([
387
+ "static uintptr_t mt_format_long_bin_len(int64_t value) {",
388
+ "#{INDENT}if (value < 0) return 1 + mt_format_ulong_bin_len(((uint64_t)(-(value + 1))) + 1);",
389
+ "#{INDENT}return mt_format_ulong_bin_len((uint64_t)value);",
390
+ "}",
391
+ ])
392
+ end
393
+
394
+ if helpers['mt_format_int_len']
395
+ lines << "" unless lines.empty?
396
+ lines.concat([
397
+ "static uintptr_t mt_format_int_len(int32_t value) {",
398
+ "#{INDENT}if (value < 0) return 1 + mt_format_ptr_uint_len((uintptr_t)(-((int64_t)value)));",
399
+ "#{INDENT}return mt_format_ptr_uint_len((uintptr_t)value);",
400
+ "}",
401
+ ])
402
+ end
403
+
404
+ if helpers['mt_format_float_len']
405
+ lines << "" unless lines.empty?
406
+ lines.concat([
407
+ "static uintptr_t mt_format_float_len(float value) {",
408
+ "#{INDENT}int written = snprintf(NULL, 0, \"%g\", (double)value);",
409
+ "#{INDENT}if (written < 0) mt_fatal(\"format string could not measure float\");",
410
+ "#{INDENT}return (uintptr_t)written;",
411
+ "}",
412
+ ])
413
+ end
414
+
415
+ if helpers['mt_format_double_len']
416
+ lines << "" unless lines.empty?
417
+ lines.concat([
418
+ "static uintptr_t mt_format_double_len(double value) {",
419
+ "#{INDENT}int written = snprintf(NULL, 0, \"%g\", value);",
420
+ "#{INDENT}if (written < 0) mt_fatal(\"format string could not measure double\");",
421
+ "#{INDENT}return (uintptr_t)written;",
422
+ "}",
423
+ ])
424
+ end
425
+
426
+ if helpers['mt_format_double_precision_len']
427
+ lines << "" unless lines.empty?
428
+ lines.concat([
429
+ "static uintptr_t mt_format_double_precision_len(double value, int32_t precision) {",
430
+ "#{INDENT}int written = snprintf(NULL, 0, \"%.*f\", precision, value);",
431
+ "#{INDENT}if (written < 0) mt_fatal(\"format string could not measure double precision\");",
432
+ "#{INDENT}return (uintptr_t)written;",
433
+ "}",
434
+ ])
435
+ end
436
+
437
+ if helpers['mt_format_append_str']
438
+ lines << "" unless lines.empty?
439
+ lines.concat([
440
+ "static uintptr_t mt_format_append_str(mt_str target, uintptr_t offset, mt_str value) {",
441
+ "#{INDENT}return mt_format_append_bytes(target, offset, value.data, value.len);",
442
+ "}",
443
+ ])
444
+ end
445
+
446
+ if helpers['mt_format_append_cstr']
447
+ lines << "" unless lines.empty?
448
+ lines.concat([
449
+ "static uintptr_t mt_format_append_cstr(mt_str target, uintptr_t offset, const char* value) {",
450
+ "#{INDENT}uintptr_t len = mt_format_cstr_len(value);",
451
+ "#{INDENT}return mt_format_append_bytes(target, offset, value, len);",
452
+ "}",
453
+ ])
454
+ end
455
+
456
+ if helpers['mt_format_append_bool']
457
+ lines << "" unless lines.empty?
458
+ lines.concat([
459
+ "static uintptr_t mt_format_append_bool(mt_str target, uintptr_t offset, bool value) {",
460
+ "#{INDENT}return value ? mt_format_append_bytes(target, offset, \"true\", 4) : mt_format_append_bytes(target, offset, \"false\", 5);",
461
+ "}",
462
+ ])
463
+ end
464
+
465
+ if helpers['mt_format_append_ptr_uint']
466
+ lines << "" unless lines.empty?
467
+ lines.concat([
468
+ "static uintptr_t mt_format_append_ptr_uint(mt_str target, uintptr_t offset, uintptr_t value) {",
469
+ "#{INDENT}uintptr_t len = mt_format_ptr_uint_len(value);",
470
+ "#{INDENT}uintptr_t index = offset + len;",
471
+ "#{INDENT}mt_format_check_capacity(target, offset, len);",
472
+ "#{INDENT}target.data[index] = '\\0';",
473
+ "#{INDENT}do {",
474
+ "#{INDENT * 2}index -= 1;",
475
+ "#{INDENT * 2}target.data[index] = (char)('0' + (value % 10));",
476
+ "#{INDENT * 2}value /= 10;",
477
+ "#{INDENT}} while (index > offset);",
478
+ "#{INDENT}return offset + len;",
479
+ "}",
480
+ ])
481
+ end
482
+
483
+ if helpers['mt_format_append_ulong']
484
+ lines << "" unless lines.empty?
485
+ lines.concat([
486
+ "static uintptr_t mt_format_append_ulong(mt_str target, uintptr_t offset, uint64_t value) {",
487
+ "#{INDENT}uintptr_t len = mt_format_ulong_len(value);",
488
+ "#{INDENT}uintptr_t index = offset + len;",
489
+ "#{INDENT}mt_format_check_capacity(target, offset, len);",
490
+ "#{INDENT}target.data[index] = '\\0';",
491
+ "#{INDENT}do {",
492
+ "#{INDENT * 2}index -= 1;",
493
+ "#{INDENT * 2}target.data[index] = (char)('0' + (value % 10));",
494
+ "#{INDENT * 2}value /= 10;",
495
+ "#{INDENT}} while (index > offset);",
496
+ "#{INDENT}return offset + len;",
497
+ "}",
498
+ ])
499
+ end
500
+
501
+ if helpers['mt_format_append_uint']
502
+ lines << "" unless lines.empty?
503
+ lines.concat([
504
+ "static uintptr_t mt_format_append_uint(mt_str target, uintptr_t offset, uint32_t value) {",
505
+ "#{INDENT}return mt_format_append_ptr_uint(target, offset, (uintptr_t)value);",
506
+ "}",
507
+ ])
508
+ end
509
+
510
+ if helpers['mt_format_append_long']
511
+ lines << "" unless lines.empty?
512
+ lines.concat([
513
+ "static uintptr_t mt_format_append_long(mt_str target, uintptr_t offset, int64_t value) {",
514
+ "#{INDENT}if (value < 0) {",
515
+ "#{INDENT * 2}offset = mt_format_append_bytes(target, offset, \"-\", 1);",
516
+ "#{INDENT * 2}return mt_format_append_ulong(target, offset, ((uint64_t)(-(value + 1))) + 1);",
517
+ "#{INDENT}}",
518
+ "#{INDENT}return mt_format_append_ulong(target, offset, (uint64_t)value);",
519
+ "}",
520
+ ])
521
+ end
522
+
523
+ if helpers['mt_format_append_ulong_hex']
524
+ lines << "" unless lines.empty?
525
+ lines.concat([
526
+ "static uintptr_t mt_format_append_ulong_hex(mt_str target, uintptr_t offset, uint64_t value) {",
527
+ "#{INDENT}uintptr_t len = mt_format_ulong_hex_len(value);",
528
+ "#{INDENT}mt_format_check_capacity(target, offset, len);",
529
+ "#{INDENT}int written = snprintf(target.data + offset, (size_t)(target.len - offset + 1), \"%llx\", (unsigned long long)value);",
530
+ "#{INDENT}if (written < 0 || (uintptr_t)written != len) mt_fatal(\"format string could not format unsigned hex\");",
531
+ "#{INDENT}return offset + len;",
532
+ "}",
533
+ ])
534
+ end
535
+
536
+ if helpers['mt_format_append_ulong_hex_upper']
537
+ lines << "" unless lines.empty?
538
+ lines.concat([
539
+ "static uintptr_t mt_format_append_ulong_hex_upper(mt_str target, uintptr_t offset, uint64_t value) {",
540
+ "#{INDENT}uintptr_t len = mt_format_ulong_hex_len(value);",
541
+ "#{INDENT}mt_format_check_capacity(target, offset, len);",
542
+ "#{INDENT}int written = snprintf(target.data + offset, (size_t)(target.len - offset + 1), \"%llX\", (unsigned long long)value);",
543
+ "#{INDENT}if (written < 0 || (uintptr_t)written != len) mt_fatal(\"format string could not format unsigned hex\");",
544
+ "#{INDENT}return offset + len;",
545
+ "}",
546
+ ])
547
+ end
548
+
549
+ if helpers['mt_format_append_long_hex']
550
+ lines << "" unless lines.empty?
551
+ lines.concat([
552
+ "static uintptr_t mt_format_append_long_hex(mt_str target, uintptr_t offset, int64_t value) {",
553
+ "#{INDENT}if (value < 0) {",
554
+ "#{INDENT * 2}offset = mt_format_append_bytes(target, offset, \"-\", 1);",
555
+ "#{INDENT * 2}return mt_format_append_ulong_hex(target, offset, ((uint64_t)(-(value + 1))) + 1);",
556
+ "#{INDENT}}",
557
+ "#{INDENT}return mt_format_append_ulong_hex(target, offset, (uint64_t)value);",
558
+ "}",
559
+ ])
560
+ end
561
+
562
+ if helpers['mt_format_append_long_hex_upper']
563
+ lines << "" unless lines.empty?
564
+ lines.concat([
565
+ "static uintptr_t mt_format_append_long_hex_upper(mt_str target, uintptr_t offset, int64_t value) {",
566
+ "#{INDENT}if (value < 0) {",
567
+ "#{INDENT * 2}offset = mt_format_append_bytes(target, offset, \"-\", 1);",
568
+ "#{INDENT * 2}return mt_format_append_ulong_hex_upper(target, offset, ((uint64_t)(-(value + 1))) + 1);",
569
+ "#{INDENT}}",
570
+ "#{INDENT}return mt_format_append_ulong_hex_upper(target, offset, (uint64_t)value);",
571
+ "}",
572
+ ])
573
+ end
574
+
575
+ if helpers['mt_format_append_ulong_oct']
576
+ lines << "" unless lines.empty?
577
+ lines.concat([
578
+ "static uintptr_t mt_format_append_ulong_oct(mt_str target, uintptr_t offset, uint64_t value) {",
579
+ "#{INDENT}uintptr_t len = mt_format_ulong_oct_len(value);",
580
+ "#{INDENT}mt_format_check_capacity(target, offset, len);",
581
+ "#{INDENT}int written = snprintf(target.data + offset, (size_t)(target.len - offset + 1), \"%llo\", (unsigned long long)value);",
582
+ "#{INDENT}if (written < 0 || (uintptr_t)written != len) mt_fatal(\"format string could not format unsigned octal\");",
583
+ "#{INDENT}return offset + len;",
584
+ "}",
585
+ ])
586
+ end
587
+
588
+ if helpers['mt_format_append_long_oct']
589
+ lines << "" unless lines.empty?
590
+ lines.concat([
591
+ "static uintptr_t mt_format_append_long_oct(mt_str target, uintptr_t offset, int64_t value) {",
592
+ "#{INDENT}if (value < 0) {",
593
+ "#{INDENT * 2}offset = mt_format_append_bytes(target, offset, \"-\", 1);",
594
+ "#{INDENT * 2}return mt_format_append_ulong_oct(target, offset, ((uint64_t)(-(value + 1))) + 1);",
595
+ "#{INDENT}}",
596
+ "#{INDENT}return mt_format_append_ulong_oct(target, offset, (uint64_t)value);",
597
+ "}",
598
+ ])
599
+ end
600
+
601
+ if helpers['mt_format_append_ulong_bin']
602
+ lines << "" unless lines.empty?
603
+ lines.concat([
604
+ "static uintptr_t mt_format_append_ulong_bin(mt_str target, uintptr_t offset, uint64_t value) {",
605
+ "#{INDENT}uintptr_t len = mt_format_ulong_bin_len(value);",
606
+ "#{INDENT}uintptr_t index = offset + len;",
607
+ "#{INDENT}mt_format_check_capacity(target, offset, len);",
608
+ "#{INDENT}target.data[index] = '\\0';",
609
+ "#{INDENT}do {",
610
+ "#{INDENT * 2}index -= 1;",
611
+ "#{INDENT * 2}target.data[index] = (char)('0' + (value & 1));",
612
+ "#{INDENT * 2}value >>= 1;",
613
+ "#{INDENT}} while (index > offset);",
614
+ "#{INDENT}return offset + len;",
615
+ "}",
616
+ ])
617
+ end
618
+
619
+ if helpers['mt_format_append_long_bin']
620
+ lines << "" unless lines.empty?
621
+ lines.concat([
622
+ "static uintptr_t mt_format_append_long_bin(mt_str target, uintptr_t offset, int64_t value) {",
623
+ "#{INDENT}if (value < 0) {",
624
+ "#{INDENT * 2}offset = mt_format_append_bytes(target, offset, \"-\", 1);",
625
+ "#{INDENT * 2}return mt_format_append_ulong_bin(target, offset, ((uint64_t)(-(value + 1))) + 1);",
626
+ "#{INDENT}}",
627
+ "#{INDENT}return mt_format_append_ulong_bin(target, offset, (uint64_t)value);",
628
+ "}",
629
+ ])
630
+ end
631
+
632
+ if helpers['mt_format_append_int']
633
+ lines << "" unless lines.empty?
634
+ lines.concat([
635
+ "static uintptr_t mt_format_append_int(mt_str target, uintptr_t offset, int32_t value) {",
636
+ "#{INDENT}if (value < 0) {",
637
+ "#{INDENT * 2}offset = mt_format_append_bytes(target, offset, \"-\", 1);",
638
+ "#{INDENT * 2}return mt_format_append_ptr_uint(target, offset, (uintptr_t)(-((int64_t)value)));",
639
+ "#{INDENT}}",
640
+ "#{INDENT}return mt_format_append_ptr_uint(target, offset, (uintptr_t)value);",
641
+ "}",
642
+ ])
643
+ end
644
+
645
+ if helpers['mt_format_append_float']
646
+ lines << "" unless lines.empty?
647
+ lines.concat([
648
+ "static uintptr_t mt_format_append_float(mt_str target, uintptr_t offset, float value) {",
649
+ "#{INDENT}uintptr_t len = mt_format_float_len(value);",
650
+ "#{INDENT}mt_format_check_capacity(target, offset, len);",
651
+ "#{INDENT}int written = snprintf(target.data + offset, (size_t)(target.len - offset + 1), \"%g\", (double)value);",
652
+ "#{INDENT}if (written < 0 || (uintptr_t)written != len) mt_fatal(\"format string could not format float\");",
653
+ "#{INDENT}return offset + len;",
654
+ "}",
655
+ ])
656
+ end
657
+
658
+ if helpers['mt_format_append_double']
659
+ lines << "" unless lines.empty?
660
+ lines.concat([
661
+ "static uintptr_t mt_format_append_double(mt_str target, uintptr_t offset, double value) {",
662
+ "#{INDENT}uintptr_t len = mt_format_double_len(value);",
663
+ "#{INDENT}mt_format_check_capacity(target, offset, len);",
664
+ "#{INDENT}int written = snprintf(target.data + offset, (size_t)(target.len - offset + 1), \"%g\", value);",
665
+ "#{INDENT}if (written < 0 || (uintptr_t)written != len) mt_fatal(\"format string could not format double\");",
666
+ "#{INDENT}return offset + len;",
667
+ "}",
668
+ ])
669
+ end
670
+
671
+ if helpers['mt_format_append_double_precision']
672
+ lines << "" unless lines.empty?
673
+ lines.concat([
674
+ "static uintptr_t mt_format_append_double_precision(mt_str target, uintptr_t offset, double value, int32_t precision) {",
675
+ "#{INDENT}uintptr_t len = mt_format_double_precision_len(value, precision);",
676
+ "#{INDENT}mt_format_check_capacity(target, offset, len);",
677
+ "#{INDENT}int written = snprintf(target.data + offset, (size_t)(target.len - offset + 1), \"%.*f\", precision, value);",
678
+ "#{INDENT}if (written < 0 || (uintptr_t)written != len) mt_fatal(\"format string could not format double precision\");",
679
+ "#{INDENT}return offset + len;",
680
+ "}",
681
+ ])
682
+ end
683
+
684
+ lines
685
+ end
686
+
687
+ def emit_fmt_builder_helpers
688
+ return [] unless uses_fmt_builder?
689
+
690
+ [
691
+ "typedef struct {",
692
+ "#{INDENT}char* data;",
693
+ "#{INDENT}uintptr_t capacity;",
694
+ "#{INDENT}uintptr_t offset;",
695
+ "} mt_fmt_builder;",
696
+ "",
697
+ "static inline mt_fmt_builder mt_fmt_begin(uintptr_t capacity) {",
698
+ "#{INDENT}mt_fmt_builder b;",
699
+ "#{INDENT}b.data = (char*)malloc((size_t)capacity);",
700
+ "#{INDENT}b.capacity = capacity;",
701
+ "#{INDENT}b.offset = 0;",
702
+ "#{INDENT}return b;",
703
+ "}",
704
+ "",
705
+ "static inline void mt_fmt_cleanup(mt_fmt_builder b) {",
706
+ "#{INDENT}free(b.data);",
707
+ "}",
708
+ "",
709
+ "static inline mt_str mt_fmt_finish(mt_fmt_builder* b) {",
710
+ "#{INDENT}return (mt_str){ .data = b->data, .len = b->offset };",
711
+ "}",
712
+ "",
713
+ "static inline void mt_fmt_write_bytes(mt_fmt_builder* b, const char* data, uintptr_t len) {",
714
+ "#{INDENT}mt_fmt_builder buf = *b;",
715
+ "#{INDENT}b->offset = mt_format_append_bytes((mt_str){ .data = buf.data, .len = buf.capacity }, buf.offset, data, len);",
716
+ "}",
717
+ "",
718
+ "static inline void mt_fmt_write_str(mt_fmt_builder* b, mt_str value) {",
719
+ "#{INDENT}mt_fmt_write_bytes(b, value.data, value.len);",
720
+ "}",
721
+ "",
722
+ "static inline void mt_fmt_write_int(mt_fmt_builder* b, int32_t value) {",
723
+ "#{INDENT}mt_fmt_builder buf = *b;",
724
+ "#{INDENT}b->offset = mt_format_append_int((mt_str){ .data = buf.data, .len = buf.capacity }, buf.offset, value);",
725
+ "}",
726
+ "",
727
+ "static inline void mt_fmt_write_ptr_uint(mt_fmt_builder* b, uintptr_t value) {",
728
+ "#{INDENT}mt_fmt_builder buf = *b;",
729
+ "#{INDENT}b->offset = mt_format_append_ptr_uint((mt_str){ .data = buf.data, .len = buf.capacity }, buf.offset, value);",
730
+ "}",
731
+ "",
732
+ "static inline void mt_fmt_write_long_hex(mt_fmt_builder* b, int64_t value) {",
733
+ "#{INDENT}mt_fmt_builder buf = *b;",
734
+ "#{INDENT}b->offset = mt_format_append_long_hex((mt_str){ .data = buf.data, .len = buf.capacity }, buf.offset, value);",
735
+ "}",
736
+ "",
737
+ "static inline void mt_fmt_write_long_hex_upper(mt_fmt_builder* b, int64_t value) {",
738
+ "#{INDENT}mt_fmt_builder buf = *b;",
739
+ "#{INDENT}b->offset = mt_format_append_long_hex_upper((mt_str){ .data = buf.data, .len = buf.capacity }, buf.offset, value);",
740
+ "}",
741
+ "",
742
+ "static inline void mt_fmt_write_long_oct(mt_fmt_builder* b, int64_t value) {",
743
+ "#{INDENT}mt_fmt_builder buf = *b;",
744
+ "#{INDENT}b->offset = mt_format_append_long_oct((mt_str){ .data = buf.data, .len = buf.capacity }, buf.offset, value);",
745
+ "}",
746
+ "",
747
+ "static inline void mt_fmt_write_long_bin(mt_fmt_builder* b, int64_t value) {",
748
+ "#{INDENT}mt_fmt_builder buf = *b;",
749
+ "#{INDENT}b->offset = mt_format_append_long_bin((mt_str){ .data = buf.data, .len = buf.capacity }, buf.offset, value);",
750
+ "}",
751
+ "",
752
+ "static inline void mt_fmt_write_double_precision(mt_fmt_builder* b, double value, int32_t precision) {",
753
+ "#{INDENT}mt_fmt_builder buf = *b;",
754
+ "#{INDENT}b->offset = mt_format_append_double_precision((mt_str){ .data = buf.data, .len = buf.capacity }, buf.offset, value, precision);",
755
+ "}",
756
+ ]
757
+ end
758
+
759
+ def emit_foreign_temp_cstr_helpers
760
+ lines = []
761
+
762
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_foreign_str_to_cstr_temp]) }
763
+ lines.concat([
764
+ "static const char* mt_foreign_str_to_cstr_temp(mt_str value) {",
765
+ "#{INDENT}char* data = (char*)malloc(value.len + 1);",
766
+ "#{INDENT}uintptr_t index = 0;",
767
+ "#{INDENT}if (data == NULL) mt_fatal(\"foreign str temporary allocation failed\");",
768
+ "#{INDENT}while (index < value.len) {",
769
+ "#{INDENT * 2}data[index] = value.data[index];",
770
+ "#{INDENT * 2}index++;",
771
+ "#{INDENT}}",
772
+ "#{INDENT}data[value.len] = '\\0';",
773
+ "#{INDENT}return data;",
774
+ "}",
775
+ ])
776
+ end
777
+
778
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_free_foreign_cstr_temp]) }
779
+ lines << "" unless lines.empty?
780
+ lines.concat([
781
+ "static void mt_free_foreign_cstr_temp(const char* value) {",
782
+ "#{INDENT}free((void*)value);",
783
+ "}",
784
+ ])
785
+ end
786
+
787
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_foreign_strs_to_cstrs_temp]) }
788
+ lines << "" unless lines.empty?
789
+ lines.concat([
790
+ "static void mt_foreign_strs_to_cstrs_temp(mt_span_str values, char*** items_out, char** data_out, uintptr_t* len_out) {",
791
+ "#{INDENT}uintptr_t total_bytes = 0;",
792
+ "#{INDENT}uintptr_t index = 0;",
793
+ "#{INDENT}uintptr_t offset = 0;",
794
+ "#{INDENT}char** items = NULL;",
795
+ "#{INDENT}char* data = NULL;",
796
+ "#{INDENT}while (index < values.len) {",
797
+ "#{INDENT * 2}total_bytes += values.data[index].len + 1;",
798
+ "#{INDENT * 2}index++;",
799
+ "#{INDENT}}",
800
+ "#{INDENT}if (values.len > 0) {",
801
+ "#{INDENT * 2}items = (char**)malloc(values.len * sizeof(char*));",
802
+ "#{INDENT * 2}if (items == NULL) mt_fatal(\"foreign string-list temporary allocation failed\");",
803
+ "#{INDENT}}",
804
+ "#{INDENT}if (total_bytes > 0) {",
805
+ "#{INDENT * 2}data = (char*)malloc(total_bytes);",
806
+ "#{INDENT * 2}if (data == NULL) {",
807
+ "#{INDENT * 3}free(items);",
808
+ "#{INDENT * 3}mt_fatal(\"foreign string-list temporary allocation failed\");",
809
+ "#{INDENT * 2}}",
810
+ "#{INDENT}}",
811
+ "#{INDENT}index = 0;",
812
+ "#{INDENT}while (index < values.len) {",
813
+ "#{INDENT * 2}mt_str value = values.data[index];",
814
+ "#{INDENT * 2}uintptr_t byte_index = 0;",
815
+ "#{INDENT * 2}items[index] = data + offset;",
816
+ "#{INDENT * 2}while (byte_index < value.len) {",
817
+ "#{INDENT * 3}data[offset + byte_index] = value.data[byte_index];",
818
+ "#{INDENT * 3}byte_index++;",
819
+ "#{INDENT * 2}}",
820
+ "#{INDENT * 2}data[offset + value.len] = '\\0';",
821
+ "#{INDENT * 2}offset += value.len + 1;",
822
+ "#{INDENT * 2}index++;",
823
+ "#{INDENT}}",
824
+ "#{INDENT}*items_out = items;",
825
+ "#{INDENT}*data_out = data;",
826
+ "#{INDENT}*len_out = values.len;",
827
+ "}",
828
+ ])
829
+ end
830
+
831
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_free_foreign_cstrs_temp]) }
832
+ lines << "" unless lines.empty?
833
+ lines.concat([
834
+ "static void mt_free_foreign_cstrs_temp(char** items, char* data) {",
835
+ "#{INDENT}free(items);",
836
+ "#{INDENT}free(data);",
837
+ "}",
838
+ ])
839
+ end
840
+
841
+ lines
842
+ end
843
+
844
+ def emit_entrypoint_argv_helpers
845
+ lines = []
846
+
847
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_entry_argv_to_span_str]) }
848
+ lines.concat([
849
+ "static mt_span_str mt_entry_argv_to_span_str(int32_t argc, char** argv, mt_str** items_out) {",
850
+ "#{INDENT}uintptr_t len = argc > 1 ? (uintptr_t)(argc - 1) : 0;",
851
+ "#{INDENT}mt_str* items = NULL;",
852
+ "#{INDENT}uintptr_t index = 0;",
853
+ "#{INDENT}if (len > 0) {",
854
+ "#{INDENT * 2}items = (mt_str*)malloc(len * sizeof(mt_str));",
855
+ "#{INDENT * 2}if (items == NULL) abort();",
856
+ "#{INDENT}}",
857
+ "#{INDENT}while (index < len) {",
858
+ "#{INDENT * 2}char* value = argv[index + 1];",
859
+ "#{INDENT * 2}items[index] = (mt_str){ .data = value, .len = (uintptr_t)strlen(value) };",
860
+ "#{INDENT * 2}index++;",
861
+ "#{INDENT}}",
862
+ "#{INDENT}*items_out = items;",
863
+ "#{INDENT}return (mt_span_str){ .data = items, .len = len };",
864
+ "}",
865
+ ])
866
+ end
867
+
868
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_free_entry_argv_strs]) }
869
+ lines << "" unless lines.empty?
870
+ lines.concat([
871
+ "static void mt_free_entry_argv_strs(mt_str* items) {",
872
+ "#{INDENT}free(items);",
873
+ "}",
874
+ ])
875
+ end
876
+
877
+ lines
878
+ end
879
+
880
+ def emit_text_buffer_helpers
881
+ [
882
+ "static bool mt_is_utf8_continuation_byte(unsigned char byte) {",
883
+ "#{INDENT}return (byte & 0xC0u) == 0x80u;",
884
+ "}",
885
+ "",
886
+ "static bool mt_is_valid_utf8(const char* data, uintptr_t len) {",
887
+ "#{INDENT}uintptr_t index = 0;",
888
+ "#{INDENT}while (index < len) {",
889
+ "#{INDENT * 2}unsigned char lead = (unsigned char) data[index];",
890
+ "#{INDENT * 2}if (lead < 0x80u) {",
891
+ "#{INDENT * 3}index++;",
892
+ "#{INDENT * 3}continue;",
893
+ "#{INDENT * 2}}",
894
+ "#{INDENT * 2}if (lead < 0xC2u) return false;",
895
+ "#{INDENT * 2}if (lead < 0xE0u) {",
896
+ "#{INDENT * 3}if (index + 1 >= len) return false;",
897
+ "#{INDENT * 3}unsigned char byte1 = (unsigned char) data[index + 1];",
898
+ "#{INDENT * 3}if (!mt_is_utf8_continuation_byte(byte1)) return false;",
899
+ "#{INDENT * 3}index += 2;",
900
+ "#{INDENT * 3}continue;",
901
+ "#{INDENT * 2}}",
902
+ "#{INDENT * 2}if (lead < 0xF0u) {",
903
+ "#{INDENT * 3}if (index + 2 >= len) return false;",
904
+ "#{INDENT * 3}unsigned char byte1 = (unsigned char) data[index + 1];",
905
+ "#{INDENT * 3}unsigned char byte2 = (unsigned char) data[index + 2];",
906
+ "#{INDENT * 3}if (lead == 0xE0u) {",
907
+ "#{INDENT * 4}if (byte1 < 0xA0u || byte1 > 0xBFu) return false;",
908
+ "#{INDENT * 3}} else if (lead == 0xEDu) {",
909
+ "#{INDENT * 4}if (byte1 < 0x80u || byte1 > 0x9Fu) return false;",
910
+ "#{INDENT * 3}} else if (!mt_is_utf8_continuation_byte(byte1)) {",
911
+ "#{INDENT * 4}return false;",
912
+ "#{INDENT * 3}}",
913
+ "#{INDENT * 3}if (!mt_is_utf8_continuation_byte(byte2)) return false;",
914
+ "#{INDENT * 3}index += 3;",
915
+ "#{INDENT * 3}continue;",
916
+ "#{INDENT * 2}}",
917
+ "#{INDENT * 2}if (lead < 0xF5u) {",
918
+ "#{INDENT * 3}if (index + 3 >= len) return false;",
919
+ "#{INDENT * 3}unsigned char byte1 = (unsigned char) data[index + 1];",
920
+ "#{INDENT * 3}unsigned char byte2 = (unsigned char) data[index + 2];",
921
+ "#{INDENT * 3}unsigned char byte3 = (unsigned char) data[index + 3];",
922
+ "#{INDENT * 3}if (lead == 0xF0u) {",
923
+ "#{INDENT * 4}if (byte1 < 0x90u || byte1 > 0xBFu) return false;",
924
+ "#{INDENT * 3}} else if (lead == 0xF4u) {",
925
+ "#{INDENT * 4}if (byte1 < 0x80u || byte1 > 0x8Fu) return false;",
926
+ "#{INDENT * 3}} else if (!mt_is_utf8_continuation_byte(byte1)) {",
927
+ "#{INDENT * 4}return false;",
928
+ "#{INDENT * 3}}",
929
+ "#{INDENT * 3}if (!mt_is_utf8_continuation_byte(byte2) || !mt_is_utf8_continuation_byte(byte3)) return false;",
930
+ "#{INDENT * 3}index += 4;",
931
+ "#{INDENT * 3}continue;",
932
+ "#{INDENT * 2}}",
933
+ "#{INDENT * 2}return false;",
934
+ "#{INDENT}}",
935
+ "#{INDENT}return true;",
936
+ "}",
937
+ ]
938
+ end
939
+
940
+ def emit_str_buffer_helpers
941
+ lines = []
942
+
943
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_len mt_str_buffer_as_cstr mt_str_buffer_append]) }
944
+ lines.concat([
945
+ "static uintptr_t mt_str_buffer_len(char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
946
+ "#{INDENT}if (*dirty) {",
947
+ "#{INDENT * 2}uintptr_t current = 0;",
948
+ "#{INDENT * 2}while (current < cap + 1 && data[current] != '\\0') {",
949
+ "#{INDENT * 3}current++;",
950
+ "#{INDENT * 2}}",
951
+ "#{INDENT * 2}if (current > cap) mt_fatal(\"str_buffer text requires a trailing NUL within capacity\");",
952
+ "#{INDENT * 2}if (!mt_is_valid_utf8(data, current)) mt_fatal(\"str_buffer text must be valid UTF-8\");",
953
+ "#{INDENT * 2}*len = current;",
954
+ "#{INDENT * 2}*dirty = false;",
955
+ "#{INDENT}}",
956
+ "#{INDENT}return *len;",
957
+ "}",
958
+ ])
959
+ end
960
+
961
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_as_cstr]) }
962
+ lines << "" unless lines.empty?
963
+ lines.concat([
964
+ "static const char* mt_str_buffer_as_cstr(char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
965
+ "#{INDENT}(void)mt_str_buffer_len(data, cap, len, dirty);",
966
+ "#{INDENT}return data;",
967
+ "}",
968
+ ])
969
+ end
970
+
971
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_clear]) }
972
+ lines << "" unless lines.empty?
973
+ lines.concat([
974
+ "static void mt_str_buffer_clear(char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
975
+ "#{INDENT}memset(data, 0, cap + 1);",
976
+ "#{INDENT}*len = 0;",
977
+ "#{INDENT}*dirty = false;",
978
+ "}",
979
+ ])
980
+ end
981
+
982
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_assign]) }
983
+ lines << "" unless lines.empty?
984
+ lines.concat([
985
+ "static void mt_str_buffer_assign(mt_str value, char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
986
+ "#{INDENT}if (value.len > cap) mt_fatal(\"str_buffer.assign exceeds capacity\");",
987
+ "#{INDENT}memcpy(data, value.data, value.len);",
988
+ "#{INDENT}data[value.len] = '\\0';",
989
+ "#{INDENT}if (value.len < cap + 1) memset(data + value.len + 1, 0, cap - value.len);",
990
+ "#{INDENT}*len = value.len;",
991
+ "#{INDENT}*dirty = false;",
992
+ "}",
993
+ ])
994
+ end
995
+
996
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_append]) }
997
+ lines << "" unless lines.empty?
998
+ lines.concat([
999
+ "static void mt_str_buffer_append(mt_str value, char* data, uintptr_t cap, uintptr_t* len, bool* dirty) {",
1000
+ "#{INDENT}uintptr_t current = mt_str_buffer_len(data, cap, len, dirty);",
1001
+ "#{INDENT}if (value.len > cap - current) mt_fatal(\"str_buffer.append exceeds capacity\");",
1002
+ "#{INDENT}memcpy(data + current, value.data, value.len);",
1003
+ "#{INDENT}current += value.len;",
1004
+ "#{INDENT}data[current] = '\\0';",
1005
+ "#{INDENT}*len = current;",
1006
+ "#{INDENT}*dirty = false;",
1007
+ "}",
1008
+ ])
1009
+ end
1010
+
1011
+ if emitted_functions.any? { |function| function_uses_named_call?(function, %w[mt_str_buffer_prepare_write]) }
1012
+ lines << "" unless lines.empty?
1013
+ lines.concat([
1014
+ "static char* mt_str_buffer_prepare_write(char* data, uintptr_t cap, bool* dirty) {",
1015
+ "#{INDENT}data[cap] = '\\0';",
1016
+ "#{INDENT}*dirty = true;",
1017
+ "#{INDENT}return data;",
1018
+ "}",
1019
+ ])
1020
+ end
1021
+
1022
+ lines
1023
+ end
1024
+
1025
+ def emit_checked_array_index_helper(type)
1026
+ helper_name = checked_array_index_helper_name(type)
1027
+ params = [c_declaration(type, '(*array)'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
1028
+ [
1029
+ "static inline #{c_function_declaration(pointer_to(array_element_type(type)), helper_name, params)} {",
1030
+ "#{INDENT}if (index >= #{array_length(type)}) mt_fatal(\"array index out of bounds\");",
1031
+ "#{INDENT}return &(*array)[index];",
1032
+ "}",
1033
+ ]
1034
+ end
1035
+
1036
+ def emit_checked_span_index_helper(type)
1037
+ helper_name = checked_span_index_helper_name(type)
1038
+ params = [c_declaration(type, 'span'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
1039
+ [
1040
+ "static inline #{c_function_declaration(pointer_to(type.element_type), helper_name, params)} {",
1041
+ "#{INDENT}if (index >= span.len) mt_fatal(\"span index out of bounds\");",
1042
+ "#{INDENT}return &span.data[index];",
1043
+ "}",
1044
+ ]
1045
+ end
1046
+
1047
+ def emit_nullable_array_index_helper(type)
1048
+ helper_name = nullable_array_index_helper_name(type)
1049
+ params = [c_declaration(type, '(*array)'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
1050
+ [
1051
+ "static inline #{c_function_declaration(pointer_to(array_element_type(type)), helper_name, params)} {",
1052
+ "#{INDENT}if (index >= #{array_length(type)}) return NULL;",
1053
+ "#{INDENT}return &(*array)[index];",
1054
+ "}",
1055
+ ]
1056
+ end
1057
+
1058
+ def emit_nullable_span_index_helper(type)
1059
+ helper_name = nullable_span_index_helper_name(type)
1060
+ params = [c_declaration(type, 'span'), c_declaration(Types::Registry.primitive('ptr_uint'), 'index')].join(', ')
1061
+ [
1062
+ "static inline #{c_function_declaration(pointer_to(type.element_type), helper_name, params)} {",
1063
+ "#{INDENT}if (index >= span.len) return NULL;",
1064
+ "#{INDENT}return &span.data[index];",
1065
+ "}",
1066
+ ]
1067
+ end
1068
+
1069
+ def emit_str_literal_constants(literals)
1070
+ literals.each_with_index.map do |value, i|
1071
+ "static const mt_str #{str_literal_name(i)} = { .data = #{value.inspect}, .len = #{value.bytesize} };"
1072
+ end
1073
+ end
1074
+
1075
+ def str_literal_name(index)
1076
+ "mt_str_lit_#{index}"
1077
+ end
1078
+ end
1079
+ end
1080
+ end