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.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/AUTHORS +3 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +82 -0
- data/LICENSE +21 -0
- data/README.md +1208 -0
- data/Rakefile +332 -0
- data/bin/mtc +8 -0
- data/bin/profile-mtc-checks +133 -0
- data/bin/tracy-profiler +0 -0
- data/docs/build-guide.md +536 -0
- data/docs/index.html +2778 -0
- data/docs/language-design.md +1519 -0
- data/docs/language-manual.md +1534 -0
- data/lib/milk_tea/base.rb +49 -0
- data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
- data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
- data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
- data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
- data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
- data/lib/milk_tea/bindings/bindgen.rb +207 -0
- data/lib/milk_tea/bindings/cli.rb +121 -0
- data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
- data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
- data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
- data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
- data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
- data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
- data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
- data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
- data/lib/milk_tea/bindings/steamworks.rb +629 -0
- data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
- data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
- data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
- data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
- data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
- data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
- data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
- data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
- data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
- data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
- data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
- data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
- data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
- data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
- data/lib/milk_tea/bindings.rb +23 -0
- data/lib/milk_tea/core/ast.rb +311 -0
- data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
- data/lib/milk_tea/core/binding_types.rb +18 -0
- data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
- data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
- data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
- data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
- data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
- data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
- data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
- data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
- data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
- data/lib/milk_tea/core/c_backend/statements.rb +563 -0
- data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
- data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
- data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
- data/lib/milk_tea/core/c_backend.rb +287 -0
- data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
- data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
- data/lib/milk_tea/core/compile_time.rb +329 -0
- data/lib/milk_tea/core/control_flow/builder.rb +582 -0
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
- data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
- data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
- data/lib/milk_tea/core/control_flow/graph.rb +90 -0
- data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
- data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
- data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
- data/lib/milk_tea/core/control_flow/termination.rb +31 -0
- data/lib/milk_tea/core/control_flow.rb +34 -0
- data/lib/milk_tea/core/cst.rb +48 -0
- data/lib/milk_tea/core/cst_builder.rb +19 -0
- data/lib/milk_tea/core/ir.rb +85 -0
- data/lib/milk_tea/core/keywords.rb +97 -0
- data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
- data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
- data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
- data/lib/milk_tea/core/lexer/indentation.rb +62 -0
- data/lib/milk_tea/core/lexer/numbers.rb +96 -0
- data/lib/milk_tea/core/lexer/recovery.rb +32 -0
- data/lib/milk_tea/core/lexer/strings.rb +167 -0
- data/lib/milk_tea/core/lexer/symbols.rb +71 -0
- data/lib/milk_tea/core/lexer/trivia.rb +53 -0
- data/lib/milk_tea/core/lexer.rb +430 -0
- data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
- data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
- data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
- data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
- data/lib/milk_tea/core/lowering/async.rb +714 -0
- data/lib/milk_tea/core/lowering/block.rb +1052 -0
- data/lib/milk_tea/core/lowering/calls.rb +1565 -0
- data/lib/milk_tea/core/lowering/declarations.rb +214 -0
- data/lib/milk_tea/core/lowering/dyn.rb +206 -0
- data/lib/milk_tea/core/lowering/events.rb +1054 -0
- data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
- data/lib/milk_tea/core/lowering/functions.rb +242 -0
- data/lib/milk_tea/core/lowering/loops.rb +1087 -0
- data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
- data/lib/milk_tea/core/lowering/proc.rb +419 -0
- data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
- data/lib/milk_tea/core/lowering/scans.rb +220 -0
- data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
- data/lib/milk_tea/core/lowering/utils.rb +1453 -0
- data/lib/milk_tea/core/lowering.rb +378 -0
- data/lib/milk_tea/core/module_binder.rb +181 -0
- data/lib/milk_tea/core/module_loader/errors.rb +21 -0
- data/lib/milk_tea/core/module_loader.rb +479 -0
- data/lib/milk_tea/core/module_path_resolver.rb +153 -0
- data/lib/milk_tea/core/module_roots.rb +88 -0
- data/lib/milk_tea/core/parser/attributes.rb +71 -0
- data/lib/milk_tea/core/parser/blocks.rb +119 -0
- data/lib/milk_tea/core/parser/declarations.rb +749 -0
- data/lib/milk_tea/core/parser/expressions.rb +624 -0
- data/lib/milk_tea/core/parser/recovery.rb +131 -0
- data/lib/milk_tea/core/parser/statements.rb +756 -0
- data/lib/milk_tea/core/parser/types.rb +271 -0
- data/lib/milk_tea/core/parser.rb +400 -0
- data/lib/milk_tea/core/prelude_installer.rb +29 -0
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
- data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
- data/lib/milk_tea/core/pretty_printer.rb +17 -0
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
- data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
- data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
- data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
- data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
- data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
- data/lib/milk_tea/core/token.rb +26 -0
- data/lib/milk_tea/core/token_stream.rb +30 -0
- data/lib/milk_tea/core/types/layout.rb +243 -0
- data/lib/milk_tea/core/types/predicates.rb +609 -0
- data/lib/milk_tea/core/types/registry.rb +83 -0
- data/lib/milk_tea/core/types/types.rb +1696 -0
- data/lib/milk_tea/core/types/visitor.rb +442 -0
- data/lib/milk_tea/core.rb +25 -0
- data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
- data/lib/milk_tea/dap/protocol.rb +58 -0
- data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
- data/lib/milk_tea/dap/server/debug_map.rb +291 -0
- data/lib/milk_tea/dap/server/handlers.rb +374 -0
- data/lib/milk_tea/dap/server/launch.rb +261 -0
- data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
- data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
- data/lib/milk_tea/dap/server/utilities.rb +160 -0
- data/lib/milk_tea/dap/server/wire.rb +55 -0
- data/lib/milk_tea/dap/server.rb +131 -0
- data/lib/milk_tea/dap/session.rb +152 -0
- data/lib/milk_tea/dap.rb +6 -0
- data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
- data/lib/milk_tea/lsp/diagnostics.rb +611 -0
- data/lib/milk_tea/lsp/protocol.rb +104 -0
- data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
- data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
- data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
- data/lib/milk_tea/lsp/server/completion.rb +1099 -0
- data/lib/milk_tea/lsp/server/configuration.rb +167 -0
- data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
- data/lib/milk_tea/lsp/server/definition.rb +779 -0
- data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
- data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
- data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
- data/lib/milk_tea/lsp/server/formatting.rb +575 -0
- data/lib/milk_tea/lsp/server/hover.rb +1465 -0
- data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
- data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
- data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
- data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
- data/lib/milk_tea/lsp/server/progress.rb +47 -0
- data/lib/milk_tea/lsp/server/references.rb +433 -0
- data/lib/milk_tea/lsp/server/rename.rb +598 -0
- data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
- data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
- data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
- data/lib/milk_tea/lsp/server/utilities.rb +520 -0
- data/lib/milk_tea/lsp/server.rb +415 -0
- data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
- data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
- data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
- data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
- data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
- data/lib/milk_tea/lsp/workspace/store.rb +154 -0
- data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
- data/lib/milk_tea/lsp/workspace.rb +127 -0
- data/lib/milk_tea/lsp.rb +13 -0
- data/lib/milk_tea/packages/atomic_write.rb +63 -0
- data/lib/milk_tea/packages/dependency_solver.rb +194 -0
- data/lib/milk_tea/packages/graph.rb +139 -0
- data/lib/milk_tea/packages/lock.rb +421 -0
- data/lib/milk_tea/packages/manager_cli.rb +485 -0
- data/lib/milk_tea/packages/manifest.rb +356 -0
- data/lib/milk_tea/packages/manifest_editor.rb +134 -0
- data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
- data/lib/milk_tea/packages/registry_store.rb +420 -0
- data/lib/milk_tea/packages/services.rb +41 -0
- data/lib/milk_tea/packages/source_cache.rb +71 -0
- data/lib/milk_tea/packages/source_fetcher.rb +124 -0
- data/lib/milk_tea/packages/source_resolver.rb +389 -0
- data/lib/milk_tea/packages/version.rb +164 -0
- data/lib/milk_tea/packages.rb +16 -0
- data/lib/milk_tea/tooling/asset_pack.rb +141 -0
- data/lib/milk_tea/tooling/build.rb +1124 -0
- data/lib/milk_tea/tooling/build_cache.rb +240 -0
- data/lib/milk_tea/tooling/cli.rb +2688 -0
- data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
- data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
- data/lib/milk_tea/tooling/debug_map.rb +248 -0
- data/lib/milk_tea/tooling/docs_app.rb +369 -0
- data/lib/milk_tea/tooling/error_formatter.rb +86 -0
- data/lib/milk_tea/tooling/formatter.rb +787 -0
- data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
- data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
- data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
- data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
- data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
- data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
- data/lib/milk_tea/tooling/linter/rules.rb +593 -0
- data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
- data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
- data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
- data/lib/milk_tea/tooling/linter.rb +798 -0
- data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
- data/lib/milk_tea/tooling/public/css/docs.css +166 -0
- data/lib/milk_tea/tooling/public/js/docs.js +94 -0
- data/lib/milk_tea/tooling/run.rb +408 -0
- data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
- data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
- data/lib/milk_tea/tooling/views/404.erb +7 -0
- data/lib/milk_tea/tooling/views/index.erb +87 -0
- data/lib/milk_tea/tooling/views/layout.erb +69 -0
- data/lib/milk_tea/tooling/views/module.erb +97 -0
- data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
- data/lib/milk_tea/tooling.rb +20 -0
- data/lib/milk_tea.rb +8 -0
- metadata +426 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MilkTea
|
|
4
|
+
module Bindgen
|
|
5
|
+
class Generator
|
|
6
|
+
module GeneratorEmitter
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def emitted_name(raw_name)
|
|
10
|
+
return raw_name unless @strip_leading_underscores
|
|
11
|
+
|
|
12
|
+
raw_name.sub(/\A_+/, "")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def emit_module(declarations)
|
|
16
|
+
declarations = synthetic_declarations_for(declarations) + declarations
|
|
17
|
+
|
|
18
|
+
lines = []
|
|
19
|
+
lines << "# generated by mtc bindgen from #{@header_path}"
|
|
20
|
+
lines << "external"
|
|
21
|
+
lines << ""
|
|
22
|
+
|
|
23
|
+
@module_imports.each do |import|
|
|
24
|
+
lines << %(import #{import.fetch(:module_name)} as #{import.fetch(:alias)})
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
directives = []
|
|
28
|
+
@link_libraries.each do |library|
|
|
29
|
+
directives << %(link #{library.dump})
|
|
30
|
+
end
|
|
31
|
+
includes = @include_directives && !@include_directives.empty? ? @include_directives : [File.basename(@header_path)]
|
|
32
|
+
includes.each do |include_name|
|
|
33
|
+
directives << %(include #{include_name.dump})
|
|
34
|
+
end
|
|
35
|
+
lines.concat(directives)
|
|
36
|
+
|
|
37
|
+
declarations.each do |declaration|
|
|
38
|
+
lines.concat(emit_declaration(declaration))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
source = lines.join("\n") + "\n"
|
|
42
|
+
Formatter.format_source(source, path: generated_module_path, mode: :tidy)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def generated_module_path
|
|
46
|
+
"#{@module_name.tr('.', '/')}" + ".mt"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def emit_declaration(declaration)
|
|
50
|
+
case declaration[:kind]
|
|
51
|
+
when "struct", "union"
|
|
52
|
+
emit_aggregate_declaration(declaration[:kind], declaration[:name], declaration[:node])
|
|
53
|
+
when "opaque"
|
|
54
|
+
name = emitted_name(declaration[:name])
|
|
55
|
+
line = "opaque #{name}"
|
|
56
|
+
line += " = c#{declaration[:linkage_name].inspect}" if declaration[:linkage_name]
|
|
57
|
+
[line]
|
|
58
|
+
when "enum", "flags"
|
|
59
|
+
emit_enum_declaration(declaration[:kind], emitted_name(declaration[:name]), declaration[:node])
|
|
60
|
+
when "type_alias"
|
|
61
|
+
["type #{emitted_name(declaration[:name])} = #{declaration[:mapped_type]}"]
|
|
62
|
+
when "const"
|
|
63
|
+
["const #{emitted_name(declaration[:name])}: #{declaration[:type]} = #{declaration[:value]}"]
|
|
64
|
+
when "function"
|
|
65
|
+
emit_function_declaration(declaration)
|
|
66
|
+
else
|
|
67
|
+
raise BindgenError, "unsupported bindgen declaration kind #{declaration[:kind]}"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def emit_aggregate_declaration(kind, name, node)
|
|
72
|
+
explicit_c_name = aggregate_explicit_c_name(name, node)
|
|
73
|
+
stripped = emitted_name(name)
|
|
74
|
+
header = "#{kind} #{stripped}"
|
|
75
|
+
header += " = c#{explicit_c_name.inspect}" if explicit_c_name
|
|
76
|
+
header += ":"
|
|
77
|
+
lines = [header]
|
|
78
|
+
fields = Array(node["inner"]).select { |child| child["kind"] == "FieldDecl" }
|
|
79
|
+
fields.each do |field|
|
|
80
|
+
field_type = aggregate_field_type(field, owner_name: name, aggregate_node: node)
|
|
81
|
+
mt_name, = bindgen_field_name(emitted_name(aggregate_field_name(field, aggregate_node: node)))
|
|
82
|
+
lines << " #{mt_name}: #{field_type}"
|
|
83
|
+
end
|
|
84
|
+
lines
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def bindgen_field_name(name)
|
|
88
|
+
name = emitted_name(name)
|
|
89
|
+
return [name, nil] unless Token::KEYWORDS.key?(name)
|
|
90
|
+
|
|
91
|
+
["#{name}_", nil]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def bindgen_param_name(name)
|
|
95
|
+
name = emitted_name(name)
|
|
96
|
+
return [name, nil] unless generated_binding_name_conflict?(name)
|
|
97
|
+
|
|
98
|
+
["#{name}_", nil]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def generated_binding_name_conflict?(name)
|
|
102
|
+
Token::KEYWORDS.key?(name) || Types::RESERVED_VALUE_TYPE_NAMES.include?(name)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def discover_synthetic_aggregate_dependencies(declarations)
|
|
106
|
+
pending = declarations.select { |declaration| %w[struct union].include?(declaration[:kind]) }.map do |declaration|
|
|
107
|
+
[declaration[:name], declaration[:node]]
|
|
108
|
+
end
|
|
109
|
+
seen = {}
|
|
110
|
+
|
|
111
|
+
until pending.empty?
|
|
112
|
+
owner_name, aggregate_node = pending.shift
|
|
113
|
+
key = [owner_name, aggregate_node["id"]]
|
|
114
|
+
next if seen[key]
|
|
115
|
+
|
|
116
|
+
seen[key] = true
|
|
117
|
+
|
|
118
|
+
Array(aggregate_node["inner"]).select { |child| child["kind"] == "FieldDecl" }.each do |field|
|
|
119
|
+
anonymous_record = anonymous_record_decl_for_field(field, aggregate_node)
|
|
120
|
+
next unless anonymous_record
|
|
121
|
+
|
|
122
|
+
synthetic_name = synthetic_aggregate_name(owner_name, field, aggregate_node)
|
|
123
|
+
unless @synthetic_declarations.any? { |declaration| declaration[:name] == synthetic_name }
|
|
124
|
+
@synthetic_declarations << { kind: anonymous_record.fetch("tagUsed"), name: synthetic_name, node: anonymous_record }
|
|
125
|
+
@aggregate_declarations[synthetic_name] = anonymous_record
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
pending << [synthetic_name, anonymous_record]
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def discover_synthetic_field_type_dependencies(declarations)
|
|
134
|
+
pending = declarations.select { |declaration| %w[struct union].include?(declaration[:kind]) }.map do |declaration|
|
|
135
|
+
[declaration[:name], declaration[:node]]
|
|
136
|
+
end
|
|
137
|
+
pending.concat(
|
|
138
|
+
@synthetic_declarations.select { |declaration| %w[struct union].include?(declaration[:kind]) }.map do |declaration|
|
|
139
|
+
[declaration[:name], declaration[:node]]
|
|
140
|
+
end,
|
|
141
|
+
)
|
|
142
|
+
seen = {}
|
|
143
|
+
|
|
144
|
+
until pending.empty?
|
|
145
|
+
owner_name, aggregate_node = pending.shift
|
|
146
|
+
key = [owner_name, aggregate_node["id"]]
|
|
147
|
+
next if seen[key]
|
|
148
|
+
|
|
149
|
+
seen[key] = true
|
|
150
|
+
|
|
151
|
+
Array(aggregate_node["inner"]).select { |child| child["kind"] == "FieldDecl" }.each do |field|
|
|
152
|
+
aggregate_field_type(field, owner_name:, aggregate_node:)
|
|
153
|
+
|
|
154
|
+
anonymous_record = anonymous_record_decl_for_field(field, aggregate_node)
|
|
155
|
+
next unless anonymous_record
|
|
156
|
+
|
|
157
|
+
pending << [synthetic_aggregate_name(owner_name, field, aggregate_node), anonymous_record]
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def aggregate_field_type(field, owner_name:, aggregate_node:)
|
|
163
|
+
override = field_type_override(owner_name, field["name"])
|
|
164
|
+
return override if override
|
|
165
|
+
|
|
166
|
+
anonymous_record = anonymous_record_decl_for_field(field, aggregate_node)
|
|
167
|
+
return synthetic_aggregate_name(owner_name, field, aggregate_node) if anonymous_record
|
|
168
|
+
|
|
169
|
+
map_type_node(field, context: "field #{owner_name}.#{field["name"]}")
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def aggregate_field_name(field, aggregate_node:)
|
|
173
|
+
name = field["name"]
|
|
174
|
+
return name if name && !name.empty?
|
|
175
|
+
|
|
176
|
+
anonymous_record = anonymous_record_decl_for_field(field, aggregate_node)
|
|
177
|
+
return name unless anonymous_record
|
|
178
|
+
|
|
179
|
+
field_index = Array(aggregate_node["inner"]).select { |child| child["kind"] == "FieldDecl" }.index { |child| child["id"] == field["id"] } || 0
|
|
180
|
+
"anonymous_#{anonymous_record.fetch("tagUsed")}_#{field_index}"
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def synthetic_aggregate_name(owner_name, field, aggregate_node)
|
|
184
|
+
"#{owner_name}_#{aggregate_field_name(field, aggregate_node:)}"
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def anonymous_record_decl_for_field(field, aggregate_node)
|
|
188
|
+
qual_type = type_qual_type(field)
|
|
189
|
+
return unless qual_type
|
|
190
|
+
|
|
191
|
+
tag_match = qual_type.match(/\A(struct|union)\b/)
|
|
192
|
+
return unless tag_match
|
|
193
|
+
return unless qual_type.include?("(unnamed at ") || qual_type.include?("(anonymous at ")
|
|
194
|
+
|
|
195
|
+
field_begin = source_location_key(field.dig("range", "begin"))
|
|
196
|
+
return unless field_begin
|
|
197
|
+
|
|
198
|
+
expected_tag = tag_match[1]
|
|
199
|
+
Array(aggregate_node["inner"]).find do |child|
|
|
200
|
+
next false unless child["kind"] == "RecordDecl"
|
|
201
|
+
next false unless child["tagUsed"] == expected_tag
|
|
202
|
+
|
|
203
|
+
source_location_key(child["loc"]) == field_begin
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def source_location_key(location)
|
|
208
|
+
return unless location
|
|
209
|
+
|
|
210
|
+
[
|
|
211
|
+
location["offset"],
|
|
212
|
+
location.dig("includedFrom", "file"),
|
|
213
|
+
location["file"],
|
|
214
|
+
]
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def emit_enum_declaration(kind, name, node)
|
|
218
|
+
members = enum_member_values(node)
|
|
219
|
+
backing_type = if members.empty?
|
|
220
|
+
"int"
|
|
221
|
+
else
|
|
222
|
+
map_c_type(members.first.dig(:node, "type", "qualType"), context: "enum #{name}")
|
|
223
|
+
end
|
|
224
|
+
lines = ["#{kind} #{name}: #{backing_type}"]
|
|
225
|
+
members.each do |member|
|
|
226
|
+
lines << " #{member[:node]["name"]} = #{member[:value]}"
|
|
227
|
+
end
|
|
228
|
+
lines
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def enum_member_values(node)
|
|
232
|
+
next_value = 0
|
|
233
|
+
|
|
234
|
+
Array(node["inner"]).filter_map do |child|
|
|
235
|
+
next unless child["kind"] == "EnumConstantDecl"
|
|
236
|
+
|
|
237
|
+
explicit_value = integer_value(child)
|
|
238
|
+
value = explicit_value ? Integer(explicit_value, 10) : next_value
|
|
239
|
+
next_value = value + 1
|
|
240
|
+
{ node: child, value: }
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def integer_value(node)
|
|
245
|
+
constant = Array(node["inner"]).find { |child| child["kind"] == "ConstantExpr" }
|
|
246
|
+
return constant["value"] if constant && constant.key?("value")
|
|
247
|
+
|
|
248
|
+
literal = Array(node["inner"]).find { |child| child["kind"] == "IntegerLiteral" }
|
|
249
|
+
literal&.[]("value")
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def emit_function_declaration(declaration)
|
|
253
|
+
original_name = declaration[:name]
|
|
254
|
+
stripped_name = emitted_name(original_name)
|
|
255
|
+
params = declaration[:params].map do |param|
|
|
256
|
+
param_name, = bindgen_param_name(param[:name])
|
|
257
|
+
"#{param_name}: #{param[:type]}"
|
|
258
|
+
end
|
|
259
|
+
params << "..." if declaration[:variadic]
|
|
260
|
+
line = "external function #{stripped_name}(#{params.join(', ')}) -> #{declaration[:return_type]}"
|
|
261
|
+
if stripped_name != original_name
|
|
262
|
+
line += " = c#{original_name.inspect}"
|
|
263
|
+
end
|
|
264
|
+
[line]
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def lower_constant_expression(node, expected_type:, context:)
|
|
268
|
+
raise BindgenError, "missing initializer for #{context}" unless node
|
|
269
|
+
|
|
270
|
+
case node["kind"]
|
|
271
|
+
when "IntegerLiteral"
|
|
272
|
+
integer_value = node.fetch("value")
|
|
273
|
+
typed_null = pointer_zero_literal(expected_type, integer_value)
|
|
274
|
+
return typed_null if typed_null
|
|
275
|
+
|
|
276
|
+
integer_value
|
|
277
|
+
when "FloatingLiteral"
|
|
278
|
+
emit_float_value(node.fetch("value"), expected_type)
|
|
279
|
+
when "StringLiteral"
|
|
280
|
+
%(c#{node.fetch("value")})
|
|
281
|
+
when "ImplicitValueInitExpr"
|
|
282
|
+
lower_zero_value(expected_type:, context:)
|
|
283
|
+
when "ImplicitCastExpr", "ConstantExpr", "CompoundLiteralExpr", "ParenExpr"
|
|
284
|
+
child = Array(node["inner"]).first
|
|
285
|
+
lower_constant_expression(child, expected_type:, context:)
|
|
286
|
+
when "UnaryOperator"
|
|
287
|
+
operator = node["opcode"]
|
|
288
|
+
operand = lower_constant_expression(Array(node["inner"]).first, expected_type:, context:)
|
|
289
|
+
"#{operator}#{operand}"
|
|
290
|
+
when "InitListExpr"
|
|
291
|
+
lower_init_list_expression(node, expected_type:, context:)
|
|
292
|
+
else
|
|
293
|
+
raise BindgenError, "unsupported constant initializer #{node["kind"]} for #{context}"
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def lower_init_list_expression(node, expected_type:, context:)
|
|
298
|
+
values = Array(node["inner"])
|
|
299
|
+
|
|
300
|
+
aggregate = @aggregate_declarations[expected_type]
|
|
301
|
+
return lower_aggregate_init_list(values, aggregate:, expected_type:, context:) if aggregate
|
|
302
|
+
|
|
303
|
+
element_type, length = parse_array_type(expected_type)
|
|
304
|
+
raise BindgenError, "unsupported aggregate constant type #{expected_type} for #{context}" unless element_type
|
|
305
|
+
raise BindgenError, "initializer field count mismatch for #{context}" if values.length > length
|
|
306
|
+
|
|
307
|
+
arguments = (0...length).map do |index|
|
|
308
|
+
value = values[index]
|
|
309
|
+
if value
|
|
310
|
+
lower_constant_expression(value, expected_type: element_type, context: "element #{index} of #{context}")
|
|
311
|
+
else
|
|
312
|
+
lower_zero_value(expected_type: element_type, context: "element #{index} of #{context}")
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
"#{expected_type}(#{arguments.join(', ')})"
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def emit_float_value(value, expected_type)
|
|
319
|
+
value.match?(/[.eE]/) ? value : "#{value}.0"
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def lower_aggregate_init_list(values, aggregate:, expected_type:, context:)
|
|
323
|
+
fields = Array(aggregate["inner"]).select { |child| child["kind"] == "FieldDecl" }
|
|
324
|
+
raise BindgenError, "initializer field count mismatch for #{context}" if values.length > fields.length
|
|
325
|
+
|
|
326
|
+
arguments = fields.each_with_index.map do |field, index|
|
|
327
|
+
field_type = map_type_node(field, context: "field #{expected_type}.#{field["name"]}")
|
|
328
|
+
value = values[index]
|
|
329
|
+
lowered = if value
|
|
330
|
+
lower_constant_expression(value, expected_type: field_type, context: "field #{field["name"]} of #{context}")
|
|
331
|
+
else
|
|
332
|
+
lower_zero_value(expected_type: field_type, context: "field #{field["name"]} of #{context}")
|
|
333
|
+
end
|
|
334
|
+
"#{field["name"]} = #{lowered}"
|
|
335
|
+
end
|
|
336
|
+
"#{expected_type}(#{arguments.join(', ')})"
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def lower_zero_value(expected_type:, context:)
|
|
340
|
+
return "false" if expected_type == "bool"
|
|
341
|
+
return "0" if %w[char byte ubyte short ushort int uint long ulong ptr_int ptr_uint].include?(expected_type)
|
|
342
|
+
return "0.0" if %w[float double].include?(expected_type)
|
|
343
|
+
return "null[ptr[char]]" if expected_type == "cstr"
|
|
344
|
+
return "null" if expected_type == "cstr?"
|
|
345
|
+
return "null[#{expected_type}]" if expected_type.start_with?("ptr[") || expected_type.start_with?("const_ptr[")
|
|
346
|
+
|
|
347
|
+
if @aggregate_declarations.key?(expected_type)
|
|
348
|
+
return lower_aggregate_init_list([], aggregate: @aggregate_declarations.fetch(expected_type), expected_type:, context:)
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
element_type, length = parse_array_type(expected_type)
|
|
352
|
+
if element_type
|
|
353
|
+
values = Array.new(length) do |index|
|
|
354
|
+
lower_zero_value(expected_type: element_type, context: "element #{index} of #{context}")
|
|
355
|
+
end
|
|
356
|
+
return "#{expected_type}(#{values.join(', ')})"
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
"#{expected_type}<-0"
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
def pointer_zero_literal(expected_type, value)
|
|
363
|
+
return nil unless Integer(value, 10).zero?
|
|
364
|
+
|
|
365
|
+
return "null[ptr[char]]" if expected_type == "cstr"
|
|
366
|
+
return "null" if expected_type == "cstr?"
|
|
367
|
+
return "null[#{expected_type}]" if expected_type.start_with?("ptr[") || expected_type.start_with?("const_ptr[")
|
|
368
|
+
|
|
369
|
+
nil
|
|
370
|
+
rescue ArgumentError
|
|
371
|
+
nil
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def parse_array_type(type)
|
|
375
|
+
return [nil, nil] unless type.start_with?("array[") && type.end_with?("]")
|
|
376
|
+
|
|
377
|
+
parts = split_top_level_csv(type.delete_prefix("array[").delete_suffix("]"))
|
|
378
|
+
return [nil, nil] unless parts.length == 2
|
|
379
|
+
|
|
380
|
+
[parts[0], Integer(parts[1], 10)]
|
|
381
|
+
rescue ArgumentError
|
|
382
|
+
[nil, nil]
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
end
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MilkTea
|
|
4
|
+
module Bindgen
|
|
5
|
+
class Generator
|
|
6
|
+
module GeneratorOverrides
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def function_param_type_override(function_name, param_name)
|
|
10
|
+
@function_param_type_overrides.dig(function_name, param_name)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def function_return_type_override(function_name)
|
|
14
|
+
@function_return_type_overrides[function_name]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def field_type_override(type_name, field_name)
|
|
18
|
+
@field_type_overrides.dig(type_name, field_name)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def normalize_function_param_type_overrides(overrides)
|
|
22
|
+
return {} if overrides.nil?
|
|
23
|
+
|
|
24
|
+
raise BindgenError, "function_param_type_overrides must be a hash" unless overrides.is_a?(Hash)
|
|
25
|
+
|
|
26
|
+
overrides.each_with_object({}) do |(function_name, param_overrides), normalized|
|
|
27
|
+
unless function_name.is_a?(String) || function_name.is_a?(Symbol)
|
|
28
|
+
raise BindgenError, "function_param_type_overrides function names must be strings or symbols"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
raise BindgenError, "function_param_type_overrides for #{function_name} must be a hash" unless param_overrides.is_a?(Hash)
|
|
32
|
+
|
|
33
|
+
normalized[function_name.to_s] = param_overrides.each_with_object({}) do |(param_name, type), params|
|
|
34
|
+
unless param_name.is_a?(String) || param_name.is_a?(Symbol)
|
|
35
|
+
raise BindgenError, "function_param_type_overrides parameter names must be strings or symbols"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
raise BindgenError, "function_param_type_overrides for #{function_name}.#{param_name} must be a non-empty string" unless type.is_a?(String) && !type.empty?
|
|
39
|
+
|
|
40
|
+
params[param_name.to_s] = type
|
|
41
|
+
end.freeze
|
|
42
|
+
end.freeze
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def normalize_field_type_overrides(overrides)
|
|
46
|
+
return {} if overrides.nil?
|
|
47
|
+
|
|
48
|
+
raise BindgenError, "field_type_overrides must be a hash" unless overrides.is_a?(Hash)
|
|
49
|
+
|
|
50
|
+
overrides.each_with_object({}) do |(type_name, field_overrides), normalized|
|
|
51
|
+
unless type_name.is_a?(String) || type_name.is_a?(Symbol)
|
|
52
|
+
raise BindgenError, "field_type_overrides type names must be strings or symbols"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
raise BindgenError, "field_type_overrides for #{type_name} must be a hash" unless field_overrides.is_a?(Hash)
|
|
56
|
+
|
|
57
|
+
normalized[type_name.to_s] = field_overrides.each_with_object({}) do |(field_name, type), fields|
|
|
58
|
+
unless field_name.is_a?(String) || field_name.is_a?(Symbol)
|
|
59
|
+
raise BindgenError, "field_type_overrides field names must be strings or symbols"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
raise BindgenError, "field_type_overrides for #{type_name}.#{field_name} must be a non-empty string" unless type.is_a?(String) && !type.empty?
|
|
63
|
+
|
|
64
|
+
fields[field_name.to_s] = type
|
|
65
|
+
end.freeze
|
|
66
|
+
end.freeze
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def normalize_module_imports(imports)
|
|
70
|
+
return [] if imports.nil?
|
|
71
|
+
raise BindgenError, "module_imports must be an array" unless imports.is_a?(Array)
|
|
72
|
+
|
|
73
|
+
imports.map do |entry|
|
|
74
|
+
raise BindgenError, "module_imports entries must be hashes" unless entry.is_a?(Hash)
|
|
75
|
+
|
|
76
|
+
module_name = entry.fetch(:module_name) { entry.fetch("module_name", nil) }
|
|
77
|
+
import_alias = entry.fetch(:alias) { entry.fetch("alias", nil) }
|
|
78
|
+
raise BindgenError, "module_imports module_name must be a non-empty string" unless module_name.is_a?(String) && !module_name.empty?
|
|
79
|
+
raise BindgenError, "module_imports alias must be a non-empty string" unless import_alias.is_a?(String) && !import_alias.empty?
|
|
80
|
+
|
|
81
|
+
{ module_name:, alias: import_alias }
|
|
82
|
+
end.freeze
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def normalize_type_overrides(overrides)
|
|
86
|
+
return {} if overrides.nil?
|
|
87
|
+
raise BindgenError, "type_overrides must be a hash" unless overrides.is_a?(Hash)
|
|
88
|
+
|
|
89
|
+
overrides.each_with_object({}) do |(type_name, mapped_type), normalized|
|
|
90
|
+
unless type_name.is_a?(String) || type_name.is_a?(Symbol)
|
|
91
|
+
raise BindgenError, "type_overrides type names must be strings or symbols"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
raise BindgenError, "type_overrides for #{type_name} must be a non-empty string" unless mapped_type.is_a?(String) && !mapped_type.empty?
|
|
95
|
+
|
|
96
|
+
normalized[type_name.to_s] = mapped_type
|
|
97
|
+
end.freeze
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def normalize_type_name_overrides(overrides)
|
|
101
|
+
return {} if overrides.nil?
|
|
102
|
+
raise BindgenError, "type_name_overrides must be a hash" unless overrides.is_a?(Hash)
|
|
103
|
+
|
|
104
|
+
overrides.each_with_object({}) do |(type_name, visible_name), normalized|
|
|
105
|
+
unless type_name.is_a?(String) || type_name.is_a?(Symbol)
|
|
106
|
+
raise BindgenError, "type_name_overrides type names must be strings or symbols"
|
|
107
|
+
end
|
|
108
|
+
unless visible_name.is_a?(String) && !visible_name.empty?
|
|
109
|
+
raise BindgenError, "type_name_overrides for #{type_name} must be a non-empty string"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
normalized[type_name.to_s] = visible_name
|
|
113
|
+
end.freeze
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def normalize_function_return_type_overrides(overrides)
|
|
117
|
+
return {} if overrides.nil?
|
|
118
|
+
|
|
119
|
+
raise BindgenError, "function_return_type_overrides must be a hash" unless overrides.is_a?(Hash)
|
|
120
|
+
|
|
121
|
+
overrides.each_with_object({}) do |(function_name, type), normalized|
|
|
122
|
+
unless function_name.is_a?(String) || function_name.is_a?(Symbol)
|
|
123
|
+
raise BindgenError, "function_return_type_overrides function names must be strings or symbols"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
raise BindgenError, "function_return_type_overrides for #{function_name} must be a non-empty string" unless type.is_a?(String) && !type.empty?
|
|
127
|
+
|
|
128
|
+
normalized[function_name.to_s] = type
|
|
129
|
+
end.freeze
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|