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,378 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Lowering transforms the SemanticAnalyzer analysis into IR::Program.
4
+ #
5
+ # Contract with SemanticAnalyzer::Analysis — the Lowerer reads these fields:
6
+ # .ast raw parsed AST
7
+ # .module_name module identifier string
8
+ # .module_kind :module or :raw_module
9
+ # .directives compiler/link/include directives
10
+ # .imports imported module bindings
11
+ # .types Hash[name → Types::Base]
12
+ # .interfaces Hash[name → InterfaceBinding|GenericInterfaceBinding]
13
+ # .attributes Hash[name → AttributeBinding]
14
+ # .attribute_applications Hash[...]
15
+ # .values Hash[name → ValueBinding]
16
+ # .functions Hash[name → FunctionBinding]
17
+ # .methods Hash[type → Hash[name → FunctionBinding]]
18
+ # .implemented_interfaces Hash[type → Set[InterfaceBinding]]
19
+ # .resolved_expr_types Hash[expression.object_id → Types::Base]
20
+ # .uses_parallel_for bool
21
+ #
22
+ # Cross-module access uses analysis_for_module(name) which returns
23
+ # another module's Analysis and its fields as listed above.
24
+ #
25
+ # The output is IR::Program, consumed by CBackend.
26
+
27
+ require_relative "lowering/scans"
28
+ require_relative "lowering/declarations"
29
+ require_relative "lowering/events"
30
+ require_relative "lowering/functions"
31
+ require_relative "lowering/async/analysis"
32
+ require_relative "lowering/async/normalization"
33
+ require_relative "lowering/async/lowering"
34
+ require_relative "lowering/async"
35
+ require_relative "lowering/block"
36
+ require_relative "lowering/proc"
37
+ require_relative "lowering/loops"
38
+ require_relative "lowering/expressions"
39
+ require_relative "lowering/calls"
40
+ require_relative "lowering/foreign_cstr"
41
+ require_relative "lowering/str_buffer"
42
+ require_relative "lowering/resolve"
43
+ require_relative "lowering/dyn"
44
+ require_relative "lowering/utils"
45
+ require_relative "lowering/artifacts"
46
+ require_relative "lowering/lowering_context"
47
+
48
+ module MilkTea
49
+ class LoweringError < StandardError
50
+ attr_reader :line, :column, :path
51
+
52
+ def initialize(msg = nil, line: nil, column: nil, path: nil)
53
+ super(msg)
54
+ @line = line
55
+ @column = column
56
+ @path = path
57
+ end
58
+
59
+ def code
60
+ "lowering/internal"
61
+ end
62
+ end
63
+
64
+ class Lowering
65
+ def self.lower(program)
66
+ lowerer = Lowerer.new(program)
67
+ ir_program, _modules, _synths = lowerer.lower_and_assemble
68
+ ir_program
69
+ end
70
+
71
+ def self.lower_incremental(program, cached: nil, cached_synthetics: nil)
72
+ lowerer = Lowerer.new(program)
73
+ lowerer.lower_and_assemble(cached:, cached_synthetics:)
74
+ end
75
+ end
76
+
77
+ ExplicitDefaultBinding = Data.define(:binding, :callee_name)
78
+ ExplicitHashBinding = Data.define(:binding, :callee_name)
79
+ ExplicitEqualBinding = Data.define(:binding, :callee_name)
80
+ ExplicitOrderBinding = Data.define(:binding, :callee_name)
81
+ ExplicitFormatBinding = Data.define(:length_binding, :length_callee_name, :append_binding, :append_callee_name)
82
+ DefaultResolution = Data.define(:target_type, :binding, :callee_name)
83
+ HashResolution = Data.define(:target_type, :binding, :callee_name)
84
+ EqualResolution = Data.define(:target_type, :binding, :callee_name)
85
+ OrderResolution = Data.define(:target_type, :binding, :callee_name)
86
+
87
+ class Lowerer
88
+ include CompatibilityHelpers
89
+
90
+ attr_accessor :bypass_sema_type_cache
91
+ attr_reader :recorded_expr_types
92
+
93
+ def initialize(program)
94
+ @program = program
95
+ @ctx = ModuleContext.new
96
+ @artifacts = Artifacts.new
97
+ @synthetic_proc_counter = 0
98
+ @parallel_for_counter = 0
99
+ @async_binding_counter = 0
100
+ @method_definitions = build_method_definitions
101
+ @bypass_sema_type_cache = false
102
+ end
103
+
104
+ def lower
105
+ @recorded_expr_types = {} if @bypass_sema_type_cache
106
+ ir_program, _modules, _synths = lower_and_assemble
107
+ ir_program
108
+ end
109
+
110
+ def lower_and_assemble(cached: nil, cached_synthetics: nil)
111
+ modules, per_module_synthetics = lower_modules(cached:, cached_synthetics:)
112
+ [assemble_modules(modules), modules, per_module_synthetics]
113
+ end
114
+
115
+ def lower_modules(cached: nil, cached_synthetics: nil)
116
+ if @program.root_analysis.module_kind == :raw_module
117
+ raise LoweringError, "cannot emit C for external file #{@program.root_analysis.module_name}"
118
+ end
119
+
120
+ per_module_synthetics = {}
121
+ per_module_funcs = Hash.new { |h, k| h[k] = [] }
122
+ modules = {}
123
+
124
+ cached_synthetics&.each do |_module_name, synths|
125
+ @artifacts.synthetic_structs.concat(synths[:structs] || [])
126
+ @artifacts.synthetic_enums.concat(synths[:enums] || [])
127
+ @artifacts.synthetic_functions.concat(synths[:functions] || [])
128
+ @artifacts.synthetic_constants.concat(synths[:constants] || [])
129
+ end
130
+
131
+ cached&.each do |module_name, cached_ir|
132
+ modules[module_name] = cached_ir.with(functions: [])
133
+ per_module_funcs[module_name] = cached_ir.functions.dup
134
+ cached_ir.functions.each { |f| @artifacts.lowered_function_linkage_names[f.linkage_name] = true }
135
+ end
136
+
137
+ ordered_analysis_pairs.each do |path, analysis|
138
+ next if analysis.module_kind == :raw_module
139
+ next if modules.key?(analysis.module_name)
140
+
141
+ prepare_analysis(analysis, source_path: path)
142
+ collect_structs
143
+
144
+ synth_before_s = @artifacts.synthetic_structs.length
145
+ synth_before_e = @artifacts.synthetic_enums.length
146
+ synth_before_f = @artifacts.synthetic_functions.length
147
+ synth_before_c = @artifacts.synthetic_constants.length
148
+
149
+ modules[analysis.module_name] = IR::Program.new(
150
+ module_name: analysis.module_name,
151
+ includes: [],
152
+ constants: lower_constants.dup,
153
+ globals: lower_globals.dup,
154
+ opaques: lower_opaques.dup,
155
+ structs: lower_structs.dup,
156
+ unions: lower_unions.dup,
157
+ enums: lower_enums.dup,
158
+ variants: lower_variants.dup,
159
+ static_asserts: lower_static_asserts.dup,
160
+ functions: [],
161
+ source_path: path,
162
+ )
163
+ per_module_funcs[analysis.module_name].concat(lower_functions)
164
+
165
+ per_module_synthetics[analysis.module_name] = {
166
+ structs: @artifacts.synthetic_structs[synth_before_s..] || [],
167
+ enums: @artifacts.synthetic_enums[synth_before_e..] || [],
168
+ functions: @artifacts.synthetic_functions[synth_before_f..] || [],
169
+ constants: @artifacts.synthetic_constants[synth_before_c..] || [],
170
+ }
171
+ end
172
+
173
+ pending = true
174
+ while pending
175
+ pending = false
176
+
177
+ ordered_analysis_pairs.each do |path, analysis|
178
+ next if analysis.module_kind == :raw_module
179
+
180
+ prepare_analysis(analysis, source_path: path)
181
+ ensure_events_for_analysis(analysis)
182
+
183
+ synth_before_s = @artifacts.synthetic_structs.length
184
+ synth_before_e = @artifacts.synthetic_enums.length
185
+ synth_before_f = @artifacts.synthetic_functions.length
186
+ synth_before_c = @artifacts.synthetic_constants.length
187
+
188
+ newly_lowered = lower_functions
189
+ next if newly_lowered.empty?
190
+
191
+ per_module_funcs[analysis.module_name].concat(newly_lowered)
192
+
193
+ delta = {
194
+ structs: @artifacts.synthetic_structs[synth_before_s..] || [],
195
+ enums: @artifacts.synthetic_enums[synth_before_e..] || [],
196
+ functions: @artifacts.synthetic_functions[synth_before_f..] || [],
197
+ constants: @artifacts.synthetic_constants[synth_before_c..] || [],
198
+ }
199
+ existing = per_module_synthetics[analysis.module_name]
200
+ per_module_synthetics[analysis.module_name] = existing ? merge_synthetics(existing, delta) : delta
201
+
202
+ pending = true
203
+ end
204
+ end
205
+
206
+ modules.transform_values! do |fragment|
207
+ fragment.with(functions: per_module_funcs[fragment.module_name])
208
+ end
209
+
210
+ [modules, per_module_synthetics]
211
+ end
212
+
213
+ def merge_synthetics(a, b)
214
+ {
215
+ structs: (a[:structs] || []) + (b[:structs] || []),
216
+ enums: (a[:enums] || []) + (b[:enums] || []),
217
+ functions: (a[:functions] || []) + (b[:functions] || []),
218
+ constants: (a[:constants] || []) + (b[:constants] || []),
219
+ }
220
+ end
221
+
222
+ def ensure_events_for_analysis(analysis)
223
+ analysis.ast.declarations.grep(AST::EventDecl).each do |decl|
224
+ event_type = analysis.values.fetch(decl.name).type
225
+ ensure_event_runtime(event_type)
226
+ end
227
+ end
228
+
229
+ def assemble_modules(modules)
230
+ if @program.root_analysis.module_kind == :raw_module
231
+ raise LoweringError, "cannot emit C for external file #{@program.root_analysis.module_name}"
232
+ end
233
+
234
+ regenerate_cross_module_synthetics
235
+
236
+ includes = collect_includes
237
+
238
+ ordered = ordered_module_fragments(modules)
239
+ all_constants = ordered.flat_map(&:constants)
240
+ all_constants.concat(@artifacts.synthetic_constants)
241
+ all_globals = ordered.flat_map(&:globals)
242
+ all_opaques = ordered.flat_map(&:opaques)
243
+ all_structs = ordered.flat_map(&:structs)
244
+ all_unions = ordered.flat_map(&:unions)
245
+ all_enums = ordered.flat_map(&:enums)
246
+ all_variants = ordered.flat_map(&:variants)
247
+ all_static_asserts = ordered.flat_map(&:static_asserts)
248
+ all_static_asserts.concat(@artifacts.external_layout_assertions)
249
+ all_functions = ordered.flat_map(&:functions)
250
+
251
+ all_opaques.concat(lower_imported_external_opaques)
252
+ all_structs.concat(@artifacts.synthetic_structs.uniq { |s| s.linkage_name })
253
+ all_enums.concat(@artifacts.synthetic_enums.uniq { |e| e.linkage_name })
254
+ all_functions.concat(@artifacts.synthetic_functions.uniq { |f| f.linkage_name })
255
+
256
+ # Add emit-generated declarations
257
+ @artifacts.emitted_declarations.each do |emitted|
258
+ case emitted
259
+ when IR::Function
260
+ all_functions << emitted
261
+ when IR::StructDecl
262
+ all_structs << emitted
263
+ when IR::Constant
264
+ all_constants << emitted
265
+ end
266
+ end
267
+
268
+ IR::Program.new(
269
+ module_name: @program.root_analysis.module_name,
270
+ includes:,
271
+ constants: all_constants,
272
+ globals: all_globals,
273
+ opaques: all_opaques,
274
+ structs: all_structs,
275
+ unions: all_unions,
276
+ enums: all_enums,
277
+ variants: all_variants,
278
+ static_asserts: all_static_asserts,
279
+ functions: all_functions,
280
+ source_path: @program.root_path,
281
+ )
282
+ end
283
+
284
+ def regenerate_cross_module_synthetics
285
+ ordered_analysis_pairs.each do |path, analysis|
286
+ next if analysis.module_kind == :raw_module
287
+
288
+ prepare_analysis(analysis, source_path: path)
289
+ analysis.ast.declarations.grep(AST::EventDecl).each do |decl|
290
+ event_type = analysis.values.fetch(decl.name).type
291
+ ensure_event_runtime(event_type) if event_type.is_a?(Types::Event)
292
+ end
293
+ end
294
+ end
295
+
296
+ # Canonical, deterministic iteration order over the program's analyses so
297
+ # that lowering — and therefore the emitted C — is byte-identical regardless
298
+ # of how the analyses were assembled (live ModuleLoader vs. JSON-reconstructed
299
+ # bundle). Modules are emitted in dependency-first topological order, with
300
+ # lexicographic module-name order as a stable tiebreaker for independent
301
+ # modules and as a cycle-breaking fallback.
302
+ def ordered_analysis_pairs
303
+ @ordered_analysis_pairs ||= compute_ordered_analysis_pairs
304
+ end
305
+
306
+ def compute_ordered_analysis_pairs
307
+ pairs = @program.analyses_by_path.to_a
308
+ by_name = {}
309
+ pairs.each do |path, analysis|
310
+ name = analysis.module_name.to_s
311
+ by_name[name] ||= [path, analysis]
312
+ end
313
+
314
+ present = by_name.keys.to_set
315
+ deps_of = Hash.new { |hash, key| hash[key] = [] }
316
+ pairs.each do |_path, analysis|
317
+ name = analysis.module_name.to_s
318
+ imported_module_names(analysis).each do |dep|
319
+ deps_of[name] << dep if present.include?(dep) && dep != name
320
+ end
321
+ end
322
+ deps_of.each_value(&:uniq!)
323
+
324
+ canonical_module_names(by_name.keys, deps_of).map { |name| by_name[name] }
325
+ end
326
+
327
+ def imported_module_names(analysis)
328
+ imports = analysis.respond_to?(:imports) ? analysis.imports : nil
329
+ return [] unless imports.respond_to?(:values)
330
+
331
+ imports.values.filter_map do |binding|
332
+ binding.name.to_s if binding.respond_to?(:name) && binding.name
333
+ end
334
+ end
335
+
336
+ def canonical_module_names(names, deps_of)
337
+ visited = {}
338
+ on_stack = {}
339
+ order = []
340
+ visit = nil
341
+ visit = lambda do |name|
342
+ return if visited[name] || on_stack[name]
343
+
344
+ on_stack[name] = true
345
+ (deps_of[name] || []).sort.each { |dep| visit.call(dep) }
346
+ on_stack.delete(name)
347
+ visited[name] = true
348
+ order << name
349
+ end
350
+ names.sort.each { |name| visit.call(name) }
351
+ order
352
+ end
353
+
354
+ def ordered_module_fragments(modules)
355
+ ordered = ordered_analysis_pairs.filter_map { |_path, analysis| modules[analysis.module_name] }
356
+ ordered.concat(modules.values - ordered)
357
+ ordered
358
+ end
359
+
360
+ private
361
+
362
+ include LowererScans
363
+ include LowererDeclarations
364
+ include LowererEvents
365
+ include LowererFunctions
366
+ include LowererAsync
367
+ include LowererBlock
368
+ include LowererProc
369
+ include LowererLoops
370
+ include LowererExpressions
371
+ include LowererCalls
372
+ include LowererForeignCstr
373
+ include LowererStrBuffer
374
+ include LowererResolve
375
+ include LowererDyn
376
+ include LowererUtils
377
+ end
378
+ end
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class ModuleBinder
5
+ def module_binding(analysis)
6
+ types = {}
7
+ type_declarations = {}
8
+ interfaces = {}
9
+ attributes = {}
10
+ private_types = {}
11
+ private_interfaces = {}
12
+ private_attributes = {}
13
+ values = {}
14
+ private_values = {}
15
+ functions = {}
16
+ private_functions = {}
17
+
18
+ analysis.ast.declarations.each do |declaration|
19
+ case declaration
20
+ when AST::StructDecl, AST::UnionDecl, AST::VariantDecl, AST::EnumDecl, AST::FlagsDecl, AST::OpaqueDecl, AST::TypeAliasDecl
21
+ type_declarations[declaration.name] = declaration
22
+ target = exported_declaration?(analysis, declaration) ? types : private_types
23
+ target[declaration.name] = analysis.types.fetch(declaration.name)
24
+ when AST::InterfaceDecl
25
+ target = exported_declaration?(analysis, declaration) ? interfaces : private_interfaces
26
+ target[declaration.name] = analysis.interfaces.fetch(declaration.name)
27
+ when AST::AttributeDecl
28
+ target = exported_declaration?(analysis, declaration) ? attributes : private_attributes
29
+ target[declaration.name] = analysis.attributes.fetch(declaration.name)
30
+ when AST::ConstDecl, AST::VarDecl, AST::EventDecl
31
+ target = exported_declaration?(analysis, declaration) ? values : private_values
32
+ target[declaration.name] = analysis.values.fetch(declaration.name)
33
+ when AST::FunctionDef, AST::ExternFunctionDecl, AST::ForeignFunctionDecl
34
+ target = exported_declaration?(analysis, declaration) ? functions : private_functions
35
+ target[declaration.name] = analysis.functions.fetch(declaration.name)
36
+ end
37
+ end
38
+
39
+ methods, private_methods = exported_methods(analysis, types)
40
+ implemented_interfaces, private_implemented_interfaces = exported_interface_implementations(analysis, types, interfaces)
41
+
42
+ ModuleBinding.new(
43
+ name: analysis.module_name,
44
+ types:,
45
+ type_declarations:,
46
+ interfaces:,
47
+ attributes:,
48
+ attribute_applications: analysis.attribute_applications,
49
+ values:,
50
+ functions:,
51
+ methods:,
52
+ implemented_interfaces:,
53
+ imports: analysis.imports,
54
+ private_types:,
55
+ private_interfaces:,
56
+ private_attributes:,
57
+ private_values:,
58
+ private_functions:,
59
+ private_methods:,
60
+ private_implemented_interfaces:,
61
+ )
62
+ end
63
+
64
+ private
65
+
66
+ def exported_declaration?(analysis, declaration)
67
+ return true if analysis.module_kind == :raw_module
68
+ return false unless declaration.respond_to?(:visibility)
69
+
70
+ declaration.visibility == :public
71
+ end
72
+
73
+ def exported_methods(analysis, exported_types)
74
+ methods = {}
75
+ private_methods = {}
76
+
77
+ analysis.methods.each do |receiver_type, bindings|
78
+ public_bindings = {}
79
+ hidden_bindings = {}
80
+
81
+ bindings.each do |name, binding|
82
+ visible = binding.ast.respond_to?(:visibility) &&
83
+ binding.ast.visibility == :public &&
84
+ exported_method_receiver?(receiver_type, analysis, exported_types)
85
+
86
+ if visible
87
+ public_bindings[name] = binding
88
+ else
89
+ hidden_bindings[name] = binding
90
+ end
91
+ end
92
+
93
+ methods[receiver_type] = public_bindings unless public_bindings.empty?
94
+ private_methods[receiver_type] = hidden_bindings unless hidden_bindings.empty?
95
+ end
96
+
97
+ [methods, private_methods]
98
+ end
99
+
100
+ def exported_interface_implementations(analysis, exported_types, exported_interfaces)
101
+ implemented_interfaces = {}
102
+ private_implemented_interfaces = {}
103
+
104
+ analysis.implemented_interfaces.each do |receiver_type, interfaces|
105
+ public_interfaces = []
106
+ hidden_interfaces = []
107
+
108
+ interfaces.each do |interface|
109
+ visible = exported_method_receiver?(receiver_type, analysis, exported_types) &&
110
+ exported_interface_binding?(interface, analysis, exported_interfaces) &&
111
+ exported_interface_methods?(receiver_type, interface, analysis, exported_types)
112
+ if visible
113
+ public_interfaces << interface
114
+ else
115
+ hidden_interfaces << interface
116
+ end
117
+ end
118
+
119
+ implemented_interfaces[receiver_type] = public_interfaces.freeze unless public_interfaces.empty?
120
+ private_implemented_interfaces[receiver_type] = hidden_interfaces.freeze unless hidden_interfaces.empty?
121
+ end
122
+
123
+ [implemented_interfaces.freeze, private_implemented_interfaces.freeze]
124
+ end
125
+
126
+ def exported_interface_methods?(receiver_type, interface, analysis, exported_types)
127
+ return false unless exported_method_receiver?(receiver_type, analysis, exported_types)
128
+
129
+ interface.methods.each_key.all? do |method_name|
130
+ binding = analysis.methods.fetch(receiver_type, {})[method_name]
131
+ binding && binding.ast.respond_to?(:visibility) && binding.ast.visibility == :public
132
+ end
133
+ end
134
+
135
+ def exported_interface_binding?(interface, analysis, exported_interfaces)
136
+ return true if exported_interfaces.value?(interface)
137
+
138
+ imported_interface_binding?(interface, analysis.imports)
139
+ end
140
+
141
+ def exported_method_receiver?(receiver_type, analysis, exported_types)
142
+ return true if receiver_type.is_a?(Types::StringView)
143
+ return true if receiver_type.is_a?(Types::Primitive)
144
+ return true if receiver_type.is_a?(Types::Vector)
145
+ return true if receiver_type.is_a?(Types::Matrix)
146
+ return true if receiver_type.is_a?(Types::Quaternion)
147
+ return true if receiver_type.is_a?(Types::SoA)
148
+ return true if exported_types.value?(receiver_type)
149
+ return true if imported_receiver_type?(receiver_type, analysis.imports)
150
+ return exported_method_receiver?(receiver_type.base, analysis, exported_types) if receiver_type.is_a?(Types::Nullable)
151
+ return receiver_type.arguments.all? { |argument| exported_method_receiver_argument?(argument, analysis, exported_types) } if receiver_type.is_a?(Types::GenericInstance)
152
+ return exported_types.value?(receiver_type.definition) || imported_receiver_type?(receiver_type.definition, analysis.imports) if receiver_type.is_a?(Types::VariantInstance)
153
+
154
+ receiver_type.is_a?(Types::StructInstance) &&
155
+ (exported_types.value?(receiver_type.definition) || imported_receiver_type?(receiver_type.definition, analysis.imports))
156
+ end
157
+
158
+ def exported_method_receiver_argument?(argument, analysis, exported_types)
159
+ return true if argument.is_a?(Types::LiteralTypeArg)
160
+ return true if argument.is_a?(Types::TypeVar)
161
+
162
+ exported_method_receiver?(argument, analysis, exported_types)
163
+ end
164
+
165
+ def imported_receiver_type?(receiver_type, imports)
166
+ imports.each_value do |module_binding|
167
+ return true if module_binding.types.value?(receiver_type)
168
+ end
169
+
170
+ false
171
+ end
172
+
173
+ def imported_interface_binding?(interface, imports)
174
+ imports.each_value do |module_binding|
175
+ return true if module_binding.interfaces.value?(interface)
176
+ end
177
+
178
+ false
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class ModuleLoadError < StandardError
5
+ attr_reader :path
6
+
7
+ def initialize(message, path:)
8
+ @path = path
9
+ super("#{message}: #{path}")
10
+ end
11
+
12
+ def code
13
+ "module/error"
14
+ end
15
+ end
16
+
17
+ class ModuleLoader
18
+ ImportResolution = Data.define(:modules, :errors)
19
+ ImportResolutionError = Data.define(:import, :error)
20
+ end
21
+ end