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,547 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "rexml/document"
5
+ require "set"
6
+
7
+ module MilkTea
8
+ module OpenGLRegistry
9
+ class Error < StandardError; end
10
+
11
+ DEFAULT_API = "gl"
12
+ DEFAULT_VERSION = "4.6"
13
+ DEFAULT_PROFILE = "core"
14
+ SOURCE_NAME = "opengl_registry"
15
+ HELPER_HEADER_BASENAME = "gl_registry_helpers.h"
16
+ IMPLEMENTATION_DEFINE = "MT_LANG_GL_REGISTRY_HELPERS_IMPLEMENTATION"
17
+
18
+ TypeEntry = Data.define(:name, :source, :order)
19
+ EnumEntry = Data.define(:name, :value, :alias_of, :order)
20
+ CommandParam = Data.define(:name, :source)
21
+ CommandEntry = Data.define(:name, :return_type, :params, :order)
22
+ BlockEntry = Data.define(:kind, :profile, :api, :types, :enums, :commands)
23
+ FeatureEntry = Data.define(:name, :api, :number, :blocks)
24
+ ExtensionEntry = Data.define(:name, :supported, :blocks)
25
+
26
+ module_function
27
+
28
+ def helper_header_path(root: MilkTea.root)
29
+ root.join("std/c/#{HELPER_HEADER_BASENAME}")
30
+ end
31
+
32
+ def source(root: MilkTea.root)
33
+ UpstreamSources.default_sources(root:).find { |entry| entry.name == SOURCE_NAME } ||
34
+ raise(Error, "missing upstream source definition for #{SOURCE_NAME}")
35
+ end
36
+
37
+ def prepare!(root: MilkTea.root, api: DEFAULT_API, version: DEFAULT_VERSION, profile: DEFAULT_PROFILE, extensions: [])
38
+ registry_source = source(root:)
39
+ registry_source.bootstrap!
40
+
41
+ generated = Generator.new(
42
+ xml_path: registry_source.checkout_root.join("xml/gl.xml"),
43
+ api:,
44
+ version:,
45
+ profile:,
46
+ extensions:,
47
+ ).generate
48
+
49
+ path = helper_header_path(root:)
50
+ FileUtils.mkdir_p(path.dirname)
51
+ return if File.exist?(path) && File.read(path) == generated
52
+
53
+ File.write(path, generated)
54
+ end
55
+
56
+ class Generator
57
+ KHRONOS_TYPE_REWRITES = {
58
+ "khronos_float_t" => "float",
59
+ "khronos_int8_t" => "int8_t",
60
+ "khronos_uint8_t" => "uint8_t",
61
+ "khronos_int16_t" => "int16_t",
62
+ "khronos_uint16_t" => "uint16_t",
63
+ "khronos_int32_t" => "int32_t",
64
+ "khronos_uint32_t" => "uint32_t",
65
+ "khronos_int64_t" => "int64_t",
66
+ "khronos_uint64_t" => "uint64_t",
67
+ "khronos_intptr_t" => "intptr_t",
68
+ "khronos_ssize_t" => "ptrdiff_t",
69
+ }.freeze
70
+
71
+ def initialize(xml_path: nil, xml_source: nil, api: DEFAULT_API, version: DEFAULT_VERSION, profile: DEFAULT_PROFILE, extensions: [])
72
+ raise ArgumentError, "xml_path or xml_source is required" if xml_path.nil? && xml_source.nil?
73
+
74
+ @xml_source = xml_source || File.read(xml_path)
75
+ @source_label = xml_path ? File.expand_path(xml_path.to_s) : "<memory>"
76
+ @api = api
77
+ @version = version
78
+ @profile = profile
79
+ @extensions = extensions.to_set
80
+ end
81
+
82
+ def generate
83
+ registry = parse_registry
84
+ active_enums, active_commands = active_surface(registry)
85
+
86
+ emit_header(
87
+ types: registry.fetch(:types),
88
+ enums: active_enums,
89
+ commands: active_commands,
90
+ )
91
+ end
92
+
93
+ private
94
+
95
+ def parse_registry
96
+ document = REXML::Document.new(@xml_source)
97
+ root = document.root
98
+
99
+ {
100
+ types: parse_types(root),
101
+ enums: parse_enums(root),
102
+ commands: parse_commands(root),
103
+ features: parse_features(root),
104
+ extensions: parse_extensions(root),
105
+ }
106
+ end
107
+
108
+ def parse_types(root)
109
+ order = 0
110
+ types = []
111
+
112
+ root.elements.each("types/type") do |type_element|
113
+ name = type_element.attributes["name"] || type_element.elements["name"]&.text&.strip
114
+ next if name.nil? || name.empty?
115
+
116
+ source = flatten_node(type_element).strip
117
+ types << TypeEntry.new(name:, source:, order:)
118
+ order += 1
119
+ end
120
+
121
+ types
122
+ end
123
+
124
+ def parse_enums(root)
125
+ order = 0
126
+ enums = {}
127
+
128
+ root.elements.each("enums/enum") do |enum_element|
129
+ name = enum_element.attributes["name"]
130
+ next if name.nil? || name.empty?
131
+
132
+ enums[name] ||= EnumEntry.new(
133
+ name:,
134
+ value: enum_element.attributes["value"],
135
+ alias_of: enum_element.attributes["alias"],
136
+ order:,
137
+ )
138
+ order += 1
139
+ end
140
+
141
+ enums
142
+ end
143
+
144
+ def parse_commands(root)
145
+ order = 0
146
+ commands = {}
147
+
148
+ root.elements.each("commands/command") do |command_element|
149
+ proto = command_element.elements["proto"]
150
+ next if proto.nil?
151
+
152
+ name = proto.elements["name"]&.text&.strip
153
+ next if name.nil? || name.empty?
154
+
155
+ commands[name] ||= CommandEntry.new(
156
+ name:,
157
+ return_type: normalize_c_fragment(flatten_node(proto, omit_name: true)),
158
+ params: command_element.get_elements("param").map do |param_element|
159
+ CommandParam.new(
160
+ name: param_element.elements["name"]&.text&.strip,
161
+ source: normalize_c_fragment(flatten_node(param_element)),
162
+ )
163
+ end,
164
+ order:,
165
+ )
166
+ order += 1
167
+ end
168
+
169
+ commands
170
+ end
171
+
172
+ def parse_features(root)
173
+ root.get_elements("feature").map do |feature_element|
174
+ FeatureEntry.new(
175
+ name: feature_element.attributes["name"],
176
+ api: feature_element.attributes["api"],
177
+ number: feature_element.attributes["number"],
178
+ blocks: parse_blocks(feature_element),
179
+ )
180
+ end
181
+ end
182
+
183
+ def parse_extensions(root)
184
+ root.get_elements("extensions/extension").each_with_object({}) do |extension_element, extensions|
185
+ name = extension_element.attributes["name"]
186
+ next if name.nil? || name.empty?
187
+
188
+ extensions[name] = ExtensionEntry.new(
189
+ name:,
190
+ supported: extension_element.attributes["supported"],
191
+ blocks: parse_blocks(extension_element),
192
+ )
193
+ end
194
+ end
195
+
196
+ def parse_blocks(parent_element)
197
+ parent_element.elements.each_with_object([]) do |child, blocks|
198
+ next unless %w[require remove].include?(child.name)
199
+
200
+ blocks << BlockEntry.new(
201
+ kind: child.name.to_sym,
202
+ profile: child.attributes["profile"],
203
+ api: child.attributes["api"],
204
+ types: child.get_elements("type").filter_map { |element| element.attributes["name"] },
205
+ enums: child.get_elements("enum").filter_map { |element| element.attributes["name"] },
206
+ commands: child.get_elements("command").filter_map { |element| element.attributes["name"] },
207
+ )
208
+ end
209
+ end
210
+
211
+ def active_surface(registry)
212
+ enums = Set.new
213
+ commands = Set.new
214
+
215
+ registry.fetch(:features)
216
+ .select { |feature| api_matches?(feature.api) && version_at_most?(feature.number, @version) }
217
+ .sort_by { |feature| version_key(feature.number) }
218
+ .each { |feature| apply_blocks(feature.blocks, enums:, commands:) }
219
+
220
+ @extensions.each do |extension_name|
221
+ extension = registry.fetch(:extensions)[extension_name]
222
+ next if extension.nil?
223
+ next unless api_list_matches?(extension.supported)
224
+
225
+ apply_blocks(extension.blocks, enums:, commands:)
226
+ end
227
+
228
+ active_enums = enums.map { |name| registry.fetch(:enums).fetch(name) }.sort_by(&:order)
229
+ active_commands = commands.map { |name| registry.fetch(:commands).fetch(name) }.sort_by(&:order)
230
+ [active_enums, active_commands]
231
+ end
232
+
233
+ def apply_blocks(blocks, enums:, commands:)
234
+ blocks.each do |block|
235
+ next unless block_matches?(block)
236
+
237
+ case block.kind
238
+ when :require
239
+ block.enums.each { |name| enums << name }
240
+ block.commands.each { |name| commands << name }
241
+ when :remove
242
+ block.enums.each { |name| enums.delete(name) }
243
+ block.commands.each { |name| commands.delete(name) }
244
+ end
245
+ end
246
+ end
247
+
248
+ def block_matches?(block)
249
+ profile_matches?(block.profile) && api_list_matches?(block.api)
250
+ end
251
+
252
+ def profile_matches?(profile)
253
+ profile.nil? || profile.empty? || profile == @profile
254
+ end
255
+
256
+ def api_matches?(api)
257
+ api == @api
258
+ end
259
+
260
+ def api_list_matches?(api_list)
261
+ return true if api_list.nil? || api_list.empty?
262
+
263
+ api_list.split(/[\s,|]+/).include?(@api)
264
+ end
265
+
266
+ def version_at_most?(left, right)
267
+ compare_versions(left, right) <= 0
268
+ end
269
+
270
+ def version_key(version)
271
+ version.to_s.split(".").map(&:to_i)
272
+ end
273
+
274
+ def compare_versions(left, right)
275
+ lhs = version_key(left)
276
+ rhs = version_key(right)
277
+ width = [lhs.length, rhs.length].max
278
+
279
+ lhs.fill(0, lhs.length...width)
280
+ rhs.fill(0, rhs.length...width)
281
+ lhs <=> rhs
282
+ end
283
+
284
+ def emit_header(types:, enums:, commands:)
285
+ [
286
+ "/* generated by mtc opengl-registry from #{@source_label} */",
287
+ "#ifndef MT_LANG_GL_REGISTRY_HELPERS_H",
288
+ "#define MT_LANG_GL_REGISTRY_HELPERS_H",
289
+ "",
290
+ "#include <stddef.h>",
291
+ "#include <stdint.h>",
292
+ "",
293
+ "#if defined(_WIN32) && !defined(__CYGWIN__)",
294
+ "#define MTLANG_GL_APIENTRY __stdcall",
295
+ "#else",
296
+ "#define MTLANG_GL_APIENTRY",
297
+ "#endif",
298
+ "",
299
+ emit_types(types),
300
+ emit_enums(enums),
301
+ emit_loader_api,
302
+ emit_public_command_declarations(commands),
303
+ "#ifdef #{IMPLEMENTATION_DEFINE}",
304
+ "#if defined(MT_LANG_GL_REGISTRY_HAVE_GLFW)",
305
+ "#include <GLFW/glfw3.h>",
306
+ "#endif",
307
+ "#if defined(MT_LANG_GL_REGISTRY_HAVE_SDL3)",
308
+ "#include <SDL3/SDL.h>",
309
+ "#endif",
310
+ "#if defined(MT_LANG_GL_REGISTRY_HAVE_RAYLIB)",
311
+ "void *rlGetProcAddress(const char *proc_name);",
312
+ "#endif",
313
+ "#include <stdio.h>",
314
+ "#include <stdlib.h>",
315
+ "",
316
+ emit_command_proc_typedefs(commands),
317
+ emit_command_storage(commands),
318
+ emit_loader_implementation(commands),
319
+ emit_command_definitions(commands),
320
+ "#endif",
321
+ "",
322
+ "#endif",
323
+ "",
324
+ ].reject(&:empty?).join("\n")
325
+ end
326
+
327
+ def emit_types(types)
328
+ rendered = types.filter_map do |type|
329
+ next if type.name == "khrplatform"
330
+
331
+ rewrite_type_source(type.source)
332
+ end
333
+
334
+ return "" if rendered.empty?
335
+
336
+ rendered.join("\n\n")
337
+ end
338
+
339
+ def emit_enums(enums)
340
+ return "" if enums.empty?
341
+
342
+ enums_by_name = enums.each_with_object({}) { |entry, hash| hash[entry.name] = entry }
343
+ enums.map do |entry|
344
+ "#define #{entry.name} #{resolve_enum_value(entry, enums_by_name)}"
345
+ end.join("\n")
346
+ end
347
+
348
+ def emit_loader_api
349
+ <<~C.rstrip
350
+ void mt_gl_reset_loader(void);
351
+ void mt_gl_use_glfw_loader(void);
352
+ void mt_gl_use_sdl_loader(void);
353
+ void mt_gl_use_raylib_loader(void);
354
+ C
355
+ end
356
+
357
+ def emit_public_command_declarations(commands)
358
+ return "" if commands.empty?
359
+
360
+ commands.map do |command|
361
+ "#{command.return_type} MTLANG_GL_APIENTRY #{command.name}(#{command_parameter_list(command)});"
362
+ end.join("\n")
363
+ end
364
+
365
+ def emit_command_proc_typedefs(commands)
366
+ return "" if commands.empty?
367
+
368
+ commands.map do |command|
369
+ "typedef #{command.return_type} (MTLANG_GL_APIENTRY *#{command_proc_type_name(command)})(#{command_parameter_list(command)});"
370
+ end.join("\n")
371
+ end
372
+
373
+ def emit_command_storage(commands)
374
+ return "" if commands.empty?
375
+
376
+ lines = [
377
+ "typedef void (*mtlang_gl_function)(void);",
378
+ "typedef mtlang_gl_function (*mtlang_gl_loader_proc)(const char *name);",
379
+ "",
380
+ "static mtlang_gl_loader_proc mtlang_gl_loader;",
381
+ ]
382
+ lines.concat(commands.map { |command| "static #{command_proc_type_name(command)} #{command_proc_var_name(command)};" })
383
+ lines.join("\n")
384
+ end
385
+
386
+ def emit_loader_implementation(commands)
387
+ reset_lines = commands.map { |command| " #{command_proc_var_name(command)} = NULL;" }
388
+
389
+ <<~C.rstrip
390
+ static void mtlang_gl_reset_cache(void)
391
+ {
392
+ #{reset_lines.join("\n")}
393
+ }
394
+
395
+ static mtlang_gl_function mtlang_gl_require_proc(const char *name)
396
+ {
397
+ mtlang_gl_function proc;
398
+
399
+ if (mtlang_gl_loader == NULL)
400
+ {
401
+ fprintf(stderr, "OpenGL loader is not configured before calling %s\\n", name);
402
+ abort();
403
+ }
404
+
405
+ proc = mtlang_gl_loader(name);
406
+ if (proc == NULL)
407
+ {
408
+ fprintf(stderr, "OpenGL symbol %s is unavailable for the current context\\n", name);
409
+ abort();
410
+ }
411
+
412
+ return proc;
413
+ }
414
+
415
+ void mt_gl_reset_loader(void)
416
+ {
417
+ mtlang_gl_loader = NULL;
418
+ mtlang_gl_reset_cache();
419
+ }
420
+
421
+ void mt_gl_set_loader_proc(mtlang_gl_loader_proc loader)
422
+ {
423
+ mtlang_gl_loader = loader;
424
+ mtlang_gl_reset_cache();
425
+ }
426
+
427
+ void mt_gl_use_glfw_loader(void)
428
+ {
429
+ #if defined(MT_LANG_GL_REGISTRY_HAVE_GLFW)
430
+ mt_gl_set_loader_proc((mtlang_gl_loader_proc) glfwGetProcAddress);
431
+ #else
432
+ fprintf(stderr, "OpenGL GLFW loader support is unavailable in this build\\n");
433
+ abort();
434
+ #endif
435
+ }
436
+
437
+ void mt_gl_use_sdl_loader(void)
438
+ {
439
+ #if defined(MT_LANG_GL_REGISTRY_HAVE_SDL3)
440
+ mt_gl_set_loader_proc((mtlang_gl_loader_proc) SDL_GL_GetProcAddress);
441
+ #else
442
+ fprintf(stderr, "OpenGL SDL3 loader support is unavailable in this build\\n");
443
+ abort();
444
+ #endif
445
+ }
446
+
447
+ void mt_gl_use_raylib_loader(void)
448
+ {
449
+ #if defined(MT_LANG_GL_REGISTRY_HAVE_RAYLIB)
450
+ mt_gl_set_loader_proc((mtlang_gl_loader_proc) rlGetProcAddress);
451
+ #else
452
+ fprintf(stderr, "OpenGL raylib loader support is unavailable in this build\\n");
453
+ abort();
454
+ #endif
455
+ }
456
+ C
457
+ end
458
+
459
+ def emit_command_definitions(commands)
460
+ return "" if commands.empty?
461
+
462
+ commands.map { |command| emit_command_definition(command) }.join("\n\n")
463
+ end
464
+
465
+ def emit_command_definition(command)
466
+ lines = []
467
+ lines << "#{command.return_type} MTLANG_GL_APIENTRY #{command.name}(#{command_parameter_list(command)})"
468
+ lines << "{"
469
+ lines << " if (#{command_proc_var_name(command)} == NULL)"
470
+ lines << " {"
471
+ lines << " #{command_proc_var_name(command)} = (#{command_proc_type_name(command)}) mtlang_gl_require_proc(\"#{command.name}\");"
472
+ lines << " }"
473
+
474
+ call = "#{command_proc_var_name(command)}(#{command_argument_list(command)})"
475
+ if command.return_type == "void"
476
+ lines << ""
477
+ lines << " #{call};"
478
+ else
479
+ lines << ""
480
+ lines << " return #{call};"
481
+ end
482
+
483
+ lines << "}"
484
+ lines.join("\n")
485
+ end
486
+
487
+ def command_proc_type_name(command)
488
+ "mtlang_gl_proc_#{command.name}"
489
+ end
490
+
491
+ def command_proc_var_name(command)
492
+ "mtlang_gl_ptr_#{command.name}"
493
+ end
494
+
495
+ def command_parameter_list(command)
496
+ return "void" if command.params.empty?
497
+
498
+ command.params.map(&:source).join(", ")
499
+ end
500
+
501
+ def command_argument_list(command)
502
+ command.params.map(&:name).join(", ")
503
+ end
504
+
505
+ def resolve_enum_value(entry, enums_by_name, seen = Set.new)
506
+ return entry.value unless entry.value.nil? || entry.value.empty?
507
+ return entry.alias_of if entry.alias_of.nil? || entry.alias_of.empty?
508
+ return entry.alias_of if seen.include?(entry.name)
509
+
510
+ seen << entry.name
511
+ aliased = enums_by_name[entry.alias_of]
512
+ return entry.alias_of if aliased.nil?
513
+
514
+ resolve_enum_value(aliased, enums_by_name, seen)
515
+ end
516
+
517
+ def rewrite_type_source(source)
518
+ rewritten = source.dup
519
+ rewritten.gsub!("#include <KHR/khrplatform.h>", "")
520
+ rewritten.gsub!(/\bGL_APIENTRY\b/, "MTLANG_GL_APIENTRY")
521
+ rewritten.gsub!(/\bAPIENTRY\b/, "MTLANG_GL_APIENTRY")
522
+ KHRONOS_TYPE_REWRITES.each do |original, replacement|
523
+ rewritten.gsub!(/\b#{Regexp.escape(original)}\b/, replacement)
524
+ end
525
+ rewritten.strip
526
+ end
527
+
528
+ def normalize_c_fragment(fragment)
529
+ fragment.gsub(/[ \t\n\r]+/, " ").strip
530
+ end
531
+
532
+ def flatten_node(node, omit_name: false)
533
+ case node
534
+ when REXML::Text
535
+ node.value
536
+ when REXML::Element
537
+ return "" if omit_name && node.name == "name"
538
+ return "MTLANG_GL_APIENTRY" if node.name == "apientry"
539
+
540
+ node.children.map { |child| flatten_node(child, omit_name:) }.join
541
+ else
542
+ ""
543
+ end
544
+ end
545
+ end
546
+ end
547
+ end