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,1114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MilkTea
|
|
4
|
+
module ImportedBindings
|
|
5
|
+
class Generator
|
|
6
|
+
module GeneratorPolicy
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def load_policy
|
|
10
|
+
JSON.parse(File.read(@policy_path))
|
|
11
|
+
rescue Errno::ENOENT
|
|
12
|
+
raise Error, "imported binding policy not found: #{@policy_path}"
|
|
13
|
+
rescue JSON::ParserError => e
|
|
14
|
+
raise Error, "failed to parse imported binding policy #{@policy_path}: #{e.message}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def validate_policy!(policy)
|
|
18
|
+
unless policy.is_a?(Hash)
|
|
19
|
+
raise Error, "imported binding policy #{@policy_path} must be a JSON object"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if policy.key?("extra_source")
|
|
23
|
+
raise Error, "extra_source in #{@policy_path} is no longer supported; move helper code into a normal .mt module"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
validate_allowed_keys!(policy, %w[module_name raw_module_name raw_import_alias imports types constants functions methods], context: "imported binding policy")
|
|
27
|
+
|
|
28
|
+
policy_module_name = policy.fetch("module_name")
|
|
29
|
+
if policy_module_name != @module_name
|
|
30
|
+
raise Error, "imported binding policy #{@policy_path} targets #{policy_module_name}, expected #{@module_name}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
policy_raw_module_name = policy.fetch("raw_module_name")
|
|
34
|
+
if policy_raw_module_name != @raw_module_name
|
|
35
|
+
raise Error, "imported binding policy #{@policy_path} expects raw module #{policy_raw_module_name}, expected #{@raw_module_name}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
import_alias = policy["raw_import_alias"]
|
|
39
|
+
return unless import_alias && import_alias != @import_alias
|
|
40
|
+
|
|
41
|
+
raise Error, "imported binding policy #{@policy_path} expects import alias #{import_alias}, expected #{@import_alias}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def validate_raw_module!(raw_ast)
|
|
45
|
+
unless raw_ast.module_kind == :raw_module && raw_ast.module_name&.to_s == @raw_module_name
|
|
46
|
+
raise Error, "expected #{@raw_module_path} to be external file #{@raw_module_name}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def raw_import_specs(raw_ast)
|
|
51
|
+
raw_ast.imports.map do |import|
|
|
52
|
+
{
|
|
53
|
+
module_name: import.path.parts.join("."),
|
|
54
|
+
alias: import.alias_name,
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def policy_import_specs(policy)
|
|
60
|
+
entries = policy["imports"] || []
|
|
61
|
+
raise Error, "imports in #{@policy_path} must be an array" unless entries.is_a?(Array)
|
|
62
|
+
|
|
63
|
+
entries.map.with_index do |entry, index|
|
|
64
|
+
raise Error, "imports[#{index}] in #{@policy_path} must be an object" unless entry.is_a?(Hash)
|
|
65
|
+
|
|
66
|
+
validate_allowed_keys!(entry, %w[module_name alias], context: "policy import")
|
|
67
|
+
|
|
68
|
+
module_name = entry.fetch("module_name")
|
|
69
|
+
alias_name = entry.fetch("alias")
|
|
70
|
+
raise Error, "policy import module_name in #{@policy_path} must be a non-empty string" unless module_name.is_a?(String) && !module_name.empty?
|
|
71
|
+
raise Error, "policy import alias in #{@policy_path} must be a non-empty string" unless alias_name.is_a?(String) && !alias_name.empty?
|
|
72
|
+
|
|
73
|
+
{
|
|
74
|
+
module_name:,
|
|
75
|
+
alias: alias_name,
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def generated_module_path
|
|
81
|
+
"#{@module_name.tr('.', '/')}" + ".mt"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def validate_import_specs!(import_specs)
|
|
85
|
+
duplicate_module = duplicate_name(import_specs.map { |spec| spec[:module_name] })
|
|
86
|
+
raise Error, "duplicate import #{duplicate_module} in #{@policy_path}" if duplicate_module
|
|
87
|
+
|
|
88
|
+
duplicate_alias = duplicate_name(([@import_alias] + import_specs.map { |spec| spec[:alias] }))
|
|
89
|
+
raise Error, "duplicate import alias #{duplicate_alias} in #{@policy_path}" if duplicate_alias
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def merge_import_specs(*groups)
|
|
93
|
+
seen = {}
|
|
94
|
+
|
|
95
|
+
groups.flatten.compact.each_with_object([]) do |spec, merged|
|
|
96
|
+
key = [spec[:module_name], spec[:alias]]
|
|
97
|
+
next if seen[key]
|
|
98
|
+
|
|
99
|
+
seen[key] = true
|
|
100
|
+
merged << spec
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def index_raw_declarations(raw_ast)
|
|
105
|
+
types = {}
|
|
106
|
+
type_order = []
|
|
107
|
+
values = {}
|
|
108
|
+
value_order = []
|
|
109
|
+
functions = {}
|
|
110
|
+
function_order = []
|
|
111
|
+
|
|
112
|
+
raw_ast.declarations.each do |declaration|
|
|
113
|
+
case declaration
|
|
114
|
+
when AST::TypeAliasDecl, AST::StructDecl, AST::UnionDecl, AST::EnumDecl, AST::FlagsDecl, AST::OpaqueDecl
|
|
115
|
+
types[declaration.name] = declaration
|
|
116
|
+
type_order << declaration.name
|
|
117
|
+
when AST::ConstDecl
|
|
118
|
+
values[declaration.name] = declaration
|
|
119
|
+
value_order << declaration.name
|
|
120
|
+
when AST::ExternFunctionDecl
|
|
121
|
+
functions[declaration.name] = declaration
|
|
122
|
+
function_order << declaration.name
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
{
|
|
127
|
+
types:,
|
|
128
|
+
type_order:,
|
|
129
|
+
values:,
|
|
130
|
+
value_order:,
|
|
131
|
+
functions:,
|
|
132
|
+
function_order:,
|
|
133
|
+
}
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def emit_type_aliases(spec, declarations, referenced_lines:, method_sources:)
|
|
137
|
+
overrides = index_alias_overrides(spec[:overrides], declarations[:types], context: "type")
|
|
138
|
+
seen_public_names = {}
|
|
139
|
+
selected_names = resolve_selected_names(spec, declarations[:types], declarations[:type_order], context: "type")
|
|
140
|
+
|
|
141
|
+
if spec[:include] == :all
|
|
142
|
+
shadowed_public_type_names = method_sources.values.flat_map(&:public_type_names).uniq
|
|
143
|
+
selected_names = selected_names.reject do |raw_name|
|
|
144
|
+
override = overrides[raw_name]
|
|
145
|
+
next false if override
|
|
146
|
+
|
|
147
|
+
public_name = alias_public_name(raw_name, spec:, override:, binding_kind: :type)
|
|
148
|
+
shadowed_public_type_names.include?(public_name) && !unqualified_type_name_referenced?(public_name, referenced_lines)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
selected_names.map do |raw_name|
|
|
153
|
+
raw_declaration = declarations[:types].fetch(raw_name)
|
|
154
|
+
override = overrides[raw_name]
|
|
155
|
+
public_name = alias_public_name(raw_name, spec:, override:, binding_kind: :type)
|
|
156
|
+
raise Error, "duplicate generated type #{public_name} in #{@policy_path}" if seen_public_names.key?(public_name)
|
|
157
|
+
|
|
158
|
+
seen_public_names[public_name] = true
|
|
159
|
+
case public_type_kind(raw_name, override:, raw_declaration:)
|
|
160
|
+
when :alias
|
|
161
|
+
mapping = override && override["mapping"] || @native_type_mapping[raw_name] || "#{@import_alias}.#{raw_name}"
|
|
162
|
+
"public type #{public_name} = #{mapping}"
|
|
163
|
+
when :opaque
|
|
164
|
+
opaque_c_name = raw_declaration.c_name || raw_name
|
|
165
|
+
"public opaque #{public_name} = c#{opaque_c_name.inspect}"
|
|
166
|
+
else
|
|
167
|
+
raise Error, "unsupported generated public type kind for #{raw_name} in #{@policy_path}"
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def unqualified_type_name_referenced?(name, lines)
|
|
173
|
+
pattern = /(?<![A-Za-z0-9_.])#{Regexp.escape(name)}(?![A-Za-z0-9_])/
|
|
174
|
+
|
|
175
|
+
lines.any? { |line| line.match?(pattern) }
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def emit_const_aliases(spec, declarations)
|
|
179
|
+
overrides = index_alias_overrides(spec[:overrides], declarations[:values], context: "constant")
|
|
180
|
+
seen_public_names = {}
|
|
181
|
+
|
|
182
|
+
resolve_selected_names(spec, declarations[:values], declarations[:value_order], context: "constant").map do |raw_name|
|
|
183
|
+
raw_declaration = declarations[:values].fetch(raw_name)
|
|
184
|
+
override = overrides[raw_name]
|
|
185
|
+
public_name = alias_public_name(raw_name, spec:, override:, binding_kind: :value)
|
|
186
|
+
raise Error, "duplicate generated constant #{public_name} in #{@policy_path}" if seen_public_names.key?(public_name)
|
|
187
|
+
|
|
188
|
+
seen_public_names[public_name] = true
|
|
189
|
+
const_type = override && override["type"] || render_type(raw_declaration.type)
|
|
190
|
+
mapping = override && override["mapping"] || "#{@import_alias}.#{raw_name}"
|
|
191
|
+
"public const #{public_name}: #{const_type} = #{mapping}"
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def emit_foreign_functions(function_entries)
|
|
196
|
+
function_entries.map do |entry|
|
|
197
|
+
build_foreign_signature(
|
|
198
|
+
entry.fetch(:public_name),
|
|
199
|
+
type_params: entry.fetch(:type_params),
|
|
200
|
+
params: entry.fetch(:params).map { |param| render_foreign_param(param) },
|
|
201
|
+
return_type: entry.fetch(:return_type),
|
|
202
|
+
mapping: entry.fetch(:mapping),
|
|
203
|
+
variadic: entry.fetch(:variadic, false),
|
|
204
|
+
)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def emit_methods(method_specs, function_entries, declarations, method_sources:)
|
|
209
|
+
return [] if method_specs.empty?
|
|
210
|
+
|
|
211
|
+
entries_by_raw_name = function_entries.group_by { |entry| entry.fetch(:raw_name) }
|
|
212
|
+
available_entries = entries_by_raw_name.transform_values(&:first)
|
|
213
|
+
ordered_names = declarations[:function_order].select { |raw_name| entries_by_raw_name.key?(raw_name) }
|
|
214
|
+
lines = []
|
|
215
|
+
|
|
216
|
+
method_specs.each do |spec|
|
|
217
|
+
if spec[:module_name]
|
|
218
|
+
source = method_sources.fetch(method_source_key(spec))
|
|
219
|
+
source_entries = source.functions
|
|
220
|
+
selected_names = resolve_selected_names(spec, source_entries, source.function_order, context: "method function")
|
|
221
|
+
next if selected_names.empty?
|
|
222
|
+
|
|
223
|
+
seen_method_names = {}
|
|
224
|
+
lines << "" unless lines.empty?
|
|
225
|
+
lines << "extending #{spec.fetch(:type)}:"
|
|
226
|
+
|
|
227
|
+
selected_names.each do |source_name|
|
|
228
|
+
method_name, wrapper_lines = render_method_wrapper(plan_method_source_function(source_entries.fetch(source_name), source:), spec:)
|
|
229
|
+
if seen_method_names.key?(method_name)
|
|
230
|
+
raise Error, "duplicate generated method #{method_name} in #{@policy_path}"
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
seen_method_names[method_name] = true
|
|
234
|
+
wrapper_lines.each { |line| lines << " #{line}" }
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
next
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
selected_names = resolve_selected_names(spec, available_entries, ordered_names, context: "method function")
|
|
241
|
+
next if selected_names.empty?
|
|
242
|
+
|
|
243
|
+
seen_method_names = {}
|
|
244
|
+
lines << "" unless lines.empty?
|
|
245
|
+
lines << "extending #{spec.fetch(:type)}:"
|
|
246
|
+
|
|
247
|
+
selected_names.each do |raw_name|
|
|
248
|
+
generated_entries = entries_by_raw_name.fetch(raw_name)
|
|
249
|
+
if generated_entries.length != 1
|
|
250
|
+
raise Error, "method generation for #{raw_name} in #{@policy_path} does not support multiple generated signatures"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
method_name, wrapper_lines = render_method_wrapper(generated_entries.first, spec:)
|
|
254
|
+
if seen_method_names.key?(method_name)
|
|
255
|
+
raise Error, "duplicate generated method #{method_name} in #{@policy_path}"
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
seen_method_names[method_name] = true
|
|
259
|
+
wrapper_lines.each { |line| lines << " #{line}" }
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
lines
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def plan_foreign_functions(spec, declarations)
|
|
267
|
+
validate_known_raw_names!(spec[:exclude], declarations[:functions], context: "function")
|
|
268
|
+
validate_known_raw_names!(spec[:include], declarations[:functions], context: "function") unless spec[:include] == :all
|
|
269
|
+
|
|
270
|
+
overrides = index_function_overrides(spec[:overrides], declarations[:functions])
|
|
271
|
+
seen_public_names = {}
|
|
272
|
+
|
|
273
|
+
declarations[:function_order].flat_map do |raw_name|
|
|
274
|
+
selected = selected_by_spec?(raw_name, spec) || overrides.key?(raw_name)
|
|
275
|
+
next [] unless selected
|
|
276
|
+
next [] if spec[:exclude].include?(raw_name)
|
|
277
|
+
|
|
278
|
+
raw_declaration = declarations[:functions].fetch(raw_name)
|
|
279
|
+
if raw_declaration.variadic
|
|
280
|
+
if overrides.key?(raw_name)
|
|
281
|
+
generated_entries = overrides.fetch(raw_name).map do |entry|
|
|
282
|
+
unless entry.key?("mapping")
|
|
283
|
+
raise Error, "variadic raw function #{raw_name} in #{@raw_module_name} requires an explicit mapping override"
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
plan_overridden_function(entry, raw_declaration, spec:)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
generated_entries.each do |entry|
|
|
290
|
+
public_name = entry.fetch(:public_name)
|
|
291
|
+
raise Error, "duplicate generated function #{public_name} in #{@policy_path}" if seen_public_names.key?(public_name)
|
|
292
|
+
|
|
293
|
+
seen_public_names[public_name] = true
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
next generated_entries
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
generated_entries = if overrides.key?(raw_name)
|
|
301
|
+
overrides.fetch(raw_name).map do |entry|
|
|
302
|
+
plan_overridden_function(entry, raw_declaration, spec:)
|
|
303
|
+
end
|
|
304
|
+
else
|
|
305
|
+
[plan_pass_through_foreign_function(raw_declaration, spec:)]
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
generated_entries.each do |entry|
|
|
309
|
+
public_name = entry.fetch(:public_name)
|
|
310
|
+
raise Error, "duplicate generated function #{public_name} in #{@policy_path}" if seen_public_names.key?(public_name)
|
|
311
|
+
|
|
312
|
+
seen_public_names[public_name] = true
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
generated_entries
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def normalize_alias_spec(value, context:)
|
|
320
|
+
case value
|
|
321
|
+
when nil
|
|
322
|
+
{
|
|
323
|
+
include: :all,
|
|
324
|
+
include_prefixes: [],
|
|
325
|
+
exclude: [],
|
|
326
|
+
overrides: [],
|
|
327
|
+
rename_rules: [],
|
|
328
|
+
strip_prefix: nil,
|
|
329
|
+
native_types: {},
|
|
330
|
+
}
|
|
331
|
+
when Array
|
|
332
|
+
{
|
|
333
|
+
include: normalize_name_list(value, context:, label: "include"),
|
|
334
|
+
include_prefixes: [],
|
|
335
|
+
exclude: [],
|
|
336
|
+
overrides: [],
|
|
337
|
+
rename_rules: [],
|
|
338
|
+
strip_prefix: nil,
|
|
339
|
+
native_types: {},
|
|
340
|
+
}
|
|
341
|
+
when Hash
|
|
342
|
+
allowed_keys = %w[include include_prefixes exclude overrides rename_rules strip_prefix native_types]
|
|
343
|
+
validate_allowed_keys!(value, allowed_keys, context: "#{context} section")
|
|
344
|
+
native_types = value["native_types"] || {}
|
|
345
|
+
raise Error, "native_types in #{@policy_path} must be an object" unless native_types.is_a?(Hash)
|
|
346
|
+
native_types.each do |raw_type, native_type|
|
|
347
|
+
raise Error, "native_types key '#{raw_type}' in #{@policy_path} must be a non-empty string" unless native_type.is_a?(String) && !native_type.empty?
|
|
348
|
+
end
|
|
349
|
+
{
|
|
350
|
+
include: default_include(value, context:),
|
|
351
|
+
include_prefixes: normalize_prefix_list(value["include_prefixes"], context:),
|
|
352
|
+
exclude: normalize_name_list(value["exclude"], context:, label: "exclude"),
|
|
353
|
+
overrides: normalize_alias_overrides(value["overrides"], context:),
|
|
354
|
+
rename_rules: normalize_rename_rules(value["rename_rules"], context:),
|
|
355
|
+
strip_prefix: normalize_strip_prefix(value["strip_prefix"], context:),
|
|
356
|
+
native_types:,
|
|
357
|
+
}
|
|
358
|
+
else
|
|
359
|
+
raise Error, "#{context} section in #{@policy_path} must be an array or object"
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def normalize_function_spec(value)
|
|
364
|
+
case value
|
|
365
|
+
when nil
|
|
366
|
+
{
|
|
367
|
+
include: [],
|
|
368
|
+
include_prefixes: [],
|
|
369
|
+
exclude: [],
|
|
370
|
+
overrides: [],
|
|
371
|
+
rename_rules: [],
|
|
372
|
+
strip_prefix: nil,
|
|
373
|
+
}
|
|
374
|
+
when Array
|
|
375
|
+
if value.all? { |entry| entry.is_a?(String) }
|
|
376
|
+
{
|
|
377
|
+
include: normalize_name_list(value, context: "function", label: "include"),
|
|
378
|
+
include_prefixes: [],
|
|
379
|
+
exclude: [],
|
|
380
|
+
overrides: [],
|
|
381
|
+
rename_rules: [],
|
|
382
|
+
strip_prefix: nil,
|
|
383
|
+
}
|
|
384
|
+
else
|
|
385
|
+
{
|
|
386
|
+
include: [],
|
|
387
|
+
include_prefixes: [],
|
|
388
|
+
exclude: [],
|
|
389
|
+
overrides: normalize_function_overrides(value),
|
|
390
|
+
rename_rules: [],
|
|
391
|
+
strip_prefix: nil,
|
|
392
|
+
}
|
|
393
|
+
end
|
|
394
|
+
when Hash
|
|
395
|
+
validate_allowed_keys!(value, %w[include include_prefixes exclude overrides rename_rules strip_prefix], context: "function section")
|
|
396
|
+
{
|
|
397
|
+
include: default_include(value, context: "function"),
|
|
398
|
+
include_prefixes: normalize_prefix_list(value["include_prefixes"], context: "function"),
|
|
399
|
+
exclude: normalize_name_list(value["exclude"], context: "function", label: "exclude"),
|
|
400
|
+
overrides: normalize_function_overrides(value["overrides"]),
|
|
401
|
+
rename_rules: normalize_rename_rules(value["rename_rules"], context: "function"),
|
|
402
|
+
strip_prefix: normalize_strip_prefix(value["strip_prefix"], context: "function"),
|
|
403
|
+
}
|
|
404
|
+
else
|
|
405
|
+
raise Error, "function section in #{@policy_path} must be an array or object"
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
def normalize_method_specs(value)
|
|
410
|
+
return [] if value.nil?
|
|
411
|
+
raise Error, "methods section in #{@policy_path} must be an array" unless value.is_a?(Array)
|
|
412
|
+
|
|
413
|
+
value.map do |entry|
|
|
414
|
+
raise Error, "methods entries in #{@policy_path} must be objects" unless entry.is_a?(Hash)
|
|
415
|
+
|
|
416
|
+
validate_allowed_keys!(entry, %w[type receiver_types include include_prefixes exclude rename_rules strip_prefix module_name module_import_alias], context: "methods entry")
|
|
417
|
+
|
|
418
|
+
type = normalize_method_type(entry["type"])
|
|
419
|
+
{
|
|
420
|
+
type:,
|
|
421
|
+
receiver_types: normalize_method_receiver_types(entry["receiver_types"], type:),
|
|
422
|
+
include: default_include(entry, context: "method"),
|
|
423
|
+
include_prefixes: normalize_prefix_list(entry["include_prefixes"], context: "method"),
|
|
424
|
+
exclude: normalize_name_list(entry["exclude"], context: "method", label: "exclude"),
|
|
425
|
+
rename_rules: normalize_rename_rules(entry["rename_rules"], context: "method"),
|
|
426
|
+
strip_prefix: normalize_strip_prefix(entry["strip_prefix"], context: "method"),
|
|
427
|
+
module_name: normalize_method_module_name(entry["module_name"]),
|
|
428
|
+
module_import_alias: normalize_method_module_import_alias(entry["module_import_alias"], module_name: entry["module_name"]),
|
|
429
|
+
}
|
|
430
|
+
end
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
def normalize_method_module_name(value)
|
|
434
|
+
return nil if value.nil?
|
|
435
|
+
raise Error, "methods entry module_name in #{@policy_path} must be a string" unless value.is_a?(String)
|
|
436
|
+
raise Error, "methods entry module_name in #{@policy_path} cannot be empty" if value.empty?
|
|
437
|
+
|
|
438
|
+
value
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
def normalize_method_module_import_alias(value, module_name:)
|
|
442
|
+
return nil if module_name.nil? && value.nil?
|
|
443
|
+
raise Error, "methods entry module_import_alias in #{@policy_path} requires module_name" if module_name.nil?
|
|
444
|
+
return module_name.split(".").last if value.nil?
|
|
445
|
+
raise Error, "methods entry module_import_alias in #{@policy_path} must be a string" unless value.is_a?(String)
|
|
446
|
+
raise Error, "methods entry module_import_alias in #{@policy_path} cannot be empty" if value.empty?
|
|
447
|
+
|
|
448
|
+
value
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
def normalize_method_type(value)
|
|
452
|
+
raise Error, "methods entry type in #{@policy_path} must be a string" unless value.is_a?(String)
|
|
453
|
+
raise Error, "methods entry type in #{@policy_path} cannot be empty" if value.empty?
|
|
454
|
+
|
|
455
|
+
value
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
def normalize_method_receiver_types(value, type:)
|
|
459
|
+
return [type] if value.nil?
|
|
460
|
+
|
|
461
|
+
normalize_name_list(value, context: "method receiver type", label: "method receiver type")
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
def normalize_alias_overrides(value, context:)
|
|
465
|
+
return [] if value.nil?
|
|
466
|
+
raise Error, "#{context} overrides in #{@policy_path} must be an array" unless value.is_a?(Array)
|
|
467
|
+
|
|
468
|
+
value.each do |entry|
|
|
469
|
+
raise Error, "#{context} overrides in #{@policy_path} must be objects" unless entry.is_a?(Hash)
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
value
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
def normalize_strip_prefix(value, context:)
|
|
476
|
+
return nil if value.nil?
|
|
477
|
+
raise Error, "#{context} strip_prefix in #{@policy_path} must be a string" unless value.is_a?(String)
|
|
478
|
+
raise Error, "#{context} strip_prefix in #{@policy_path} cannot be empty" if value.empty?
|
|
479
|
+
|
|
480
|
+
value
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
def normalize_rename_rules(value, context:)
|
|
484
|
+
return [] if value.nil?
|
|
485
|
+
raise Error, "#{context} rename_rules in #{@policy_path} must be an array" unless value.is_a?(Array)
|
|
486
|
+
|
|
487
|
+
value.map do |entry|
|
|
488
|
+
raise Error, "#{context} rename_rules in #{@policy_path} must be objects" unless entry.is_a?(Hash)
|
|
489
|
+
|
|
490
|
+
validate_allowed_keys!(entry, %w[kind match replace_with], context: "#{context} rename rule")
|
|
491
|
+
|
|
492
|
+
kind = entry.fetch("kind")
|
|
493
|
+
unless %w[prefix replace camelize opengl].include?(kind)
|
|
494
|
+
raise Error, "#{context} rename rule kind in #{@policy_path} must be prefix, replace, camelize, or opengl"
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
if %w[camelize opengl].include?(kind)
|
|
498
|
+
next {
|
|
499
|
+
kind: kind.to_sym,
|
|
500
|
+
match: nil,
|
|
501
|
+
replace_with: nil,
|
|
502
|
+
}
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
match = entry.fetch("match")
|
|
506
|
+
raise Error, "#{context} rename rule match in #{@policy_path} must be a string" unless match.is_a?(String)
|
|
507
|
+
raise Error, "#{context} rename rule match in #{@policy_path} cannot be empty" if match.empty?
|
|
508
|
+
|
|
509
|
+
replace_with = entry.fetch("replace_with", "")
|
|
510
|
+
raise Error, "#{context} rename rule replace_with in #{@policy_path} must be a string" unless replace_with.is_a?(String)
|
|
511
|
+
|
|
512
|
+
{
|
|
513
|
+
kind: kind.to_sym,
|
|
514
|
+
match:,
|
|
515
|
+
replace_with:,
|
|
516
|
+
}
|
|
517
|
+
end
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
def normalize_include(value, context:)
|
|
521
|
+
return :all if value.nil? || value == "all"
|
|
522
|
+
|
|
523
|
+
normalize_name_list(value, context:, label: "include")
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
def default_include(value, context:)
|
|
527
|
+
return normalize_include(value["include"], context:) if value.key?("include")
|
|
528
|
+
return [] if value.key?("include_prefixes")
|
|
529
|
+
|
|
530
|
+
:all
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
def normalize_prefix_list(value, context:)
|
|
534
|
+
return [] if value.nil?
|
|
535
|
+
raise Error, "include_prefixes #{context}s in #{@policy_path} must be an array" unless value.is_a?(Array)
|
|
536
|
+
|
|
537
|
+
prefixes = value.map do |entry|
|
|
538
|
+
raise Error, "include_prefixes #{context} entries in #{@policy_path} must be strings" unless entry.is_a?(String)
|
|
539
|
+
raise Error, "include_prefixes #{context} entries in #{@policy_path} cannot be empty" if entry.empty?
|
|
540
|
+
|
|
541
|
+
entry
|
|
542
|
+
end
|
|
543
|
+
|
|
544
|
+
duplicate = duplicate_name(prefixes)
|
|
545
|
+
raise Error, "duplicate include_prefixes #{context} #{duplicate} in #{@policy_path}" if duplicate
|
|
546
|
+
|
|
547
|
+
prefixes
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
def normalize_name_list(value, context:, label:)
|
|
551
|
+
return [] if value.nil?
|
|
552
|
+
raise Error, "#{label} #{context}s in #{@policy_path} must be an array" unless value.is_a?(Array)
|
|
553
|
+
|
|
554
|
+
names = value.map do |entry|
|
|
555
|
+
raise Error, "#{label} #{context} entries in #{@policy_path} must be strings" unless entry.is_a?(String)
|
|
556
|
+
|
|
557
|
+
entry
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
duplicate = duplicate_name(names)
|
|
561
|
+
raise Error, "duplicate #{label} #{context} #{duplicate} in #{@policy_path}" if duplicate
|
|
562
|
+
|
|
563
|
+
names
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
def normalize_function_overrides(value)
|
|
567
|
+
return [] if value.nil?
|
|
568
|
+
raise Error, "function overrides in #{@policy_path} must be an array" unless value.is_a?(Array)
|
|
569
|
+
|
|
570
|
+
value.each do |entry|
|
|
571
|
+
raise Error, "function overrides in #{@policy_path} must be objects" unless entry.is_a?(Hash)
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
value
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
def duplicate_name(names)
|
|
578
|
+
names.tally.each do |name, count|
|
|
579
|
+
return name if count > 1
|
|
580
|
+
end
|
|
581
|
+
|
|
582
|
+
nil
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
def validate_allowed_keys!(value, allowed_keys, context:)
|
|
586
|
+
unknown_keys = value.keys - allowed_keys
|
|
587
|
+
return if unknown_keys.empty?
|
|
588
|
+
|
|
589
|
+
raise Error, "#{context} in #{@policy_path} has unknown keys: #{unknown_keys.join(', ')}"
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
def resolve_selected_names(spec, declarations_by_name, ordered_names, context:)
|
|
593
|
+
validate_known_raw_names!(spec[:exclude], declarations_by_name, context:)
|
|
594
|
+
|
|
595
|
+
selected_names = if spec[:include] == :all
|
|
596
|
+
ordered_names.dup
|
|
597
|
+
else
|
|
598
|
+
validate_known_raw_names!(spec[:include], declarations_by_name, context:)
|
|
599
|
+
ordered_names.select { |raw_name| selected_by_spec?(raw_name, spec) }
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
selected_names.reject { |raw_name| spec[:exclude].include?(raw_name) }
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def selected_by_spec?(raw_name, spec)
|
|
606
|
+
return true if spec[:include] == :all
|
|
607
|
+
|
|
608
|
+
spec[:include].include?(raw_name) || spec[:include_prefixes].any? { |prefix| raw_name.start_with?(prefix) }
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
def validate_known_raw_names!(names, declarations_by_name, context:)
|
|
612
|
+
names.each do |name|
|
|
613
|
+
next if declarations_by_name.key?(name)
|
|
614
|
+
|
|
615
|
+
raise Error, "unknown raw #{context} #{name} in #{@raw_module_name}"
|
|
616
|
+
end
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
def build_public_type_names(spec, declarations)
|
|
620
|
+
overrides = index_alias_overrides(spec[:overrides], declarations[:types], context: "type")
|
|
621
|
+
public_names = {}
|
|
622
|
+
seen_public_names = {}
|
|
623
|
+
@public_type_kinds_by_raw_name = {}
|
|
624
|
+
|
|
625
|
+
resolve_selected_names(spec, declarations[:types], declarations[:type_order], context: "type").each do |raw_name|
|
|
626
|
+
raw_declaration = declarations[:types].fetch(raw_name)
|
|
627
|
+
override = overrides[raw_name]
|
|
628
|
+
public_name = alias_public_name(raw_name, spec:, override:, binding_kind: :type)
|
|
629
|
+
raise Error, "duplicate generated type #{public_name} in #{@policy_path}" if seen_public_names.key?(public_name)
|
|
630
|
+
|
|
631
|
+
seen_public_names[public_name] = true
|
|
632
|
+
@public_type_kinds_by_raw_name[raw_name] = public_type_kind(raw_name, override:, raw_declaration:)
|
|
633
|
+
public_names[raw_name] = public_name
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
public_names
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
def index_alias_overrides(entries, declarations_by_name, context:)
|
|
640
|
+
overrides = {}
|
|
641
|
+
allowed_keys = context == "constant" ? %w[raw name type mapping] : %w[raw name mapping kind]
|
|
642
|
+
|
|
643
|
+
entries.each do |entry|
|
|
644
|
+
validate_allowed_keys!(entry, allowed_keys, context: "#{context} override")
|
|
645
|
+
|
|
646
|
+
raw_name = entry.fetch("raw")
|
|
647
|
+
raise Error, "#{context} override raw names in #{@policy_path} must be strings" unless raw_name.is_a?(String)
|
|
648
|
+
raise Error, "unknown raw #{context} #{raw_name} in #{@raw_module_name}" unless declarations_by_name.key?(raw_name)
|
|
649
|
+
raise Error, "duplicate #{context} override #{raw_name} in #{@policy_path}" if overrides.key?(raw_name)
|
|
650
|
+
|
|
651
|
+
overrides[raw_name] = entry
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
overrides
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
def alias_public_name(raw_name, spec:, override:, binding_kind:)
|
|
658
|
+
return override["name"] if override && override.key?("name")
|
|
659
|
+
|
|
660
|
+
default_public_name(raw_name, spec:, context: "generated name", binding_kind:)
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
def public_type_kind(raw_name, override:, raw_declaration:)
|
|
664
|
+
kind = override && override["kind"]
|
|
665
|
+
return :alias if kind.nil?
|
|
666
|
+
raise Error, "type override kind for #{raw_name} in #{@policy_path} must be alias or opaque" unless %w[alias opaque].include?(kind)
|
|
667
|
+
|
|
668
|
+
return :alias if kind == "alias"
|
|
669
|
+
|
|
670
|
+
unless raw_declaration.is_a?(AST::OpaqueDecl)
|
|
671
|
+
raise Error, "type override #{raw_name} in #{@policy_path} can only use kind opaque for raw opaque declarations"
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
if override.key?("mapping")
|
|
675
|
+
raise Error, "type override #{raw_name} in #{@policy_path} cannot use mapping with kind opaque"
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
:opaque
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
def default_public_name(raw_name, spec:, context:, binding_kind:)
|
|
682
|
+
transformed = apply_rename_rules(raw_name, spec[:rename_rules], context:)
|
|
683
|
+
transformed = strip_prefix(transformed, spec[:strip_prefix], context:) if spec[:strip_prefix]
|
|
684
|
+
raise Error, "#{context} in #{@policy_path} cannot be empty" if transformed.empty?
|
|
685
|
+
|
|
686
|
+
sanitize_generated_binding_name(transformed, binding_kind:)
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
def sanitize_generated_binding_name(name, binding_kind:)
|
|
690
|
+
return name unless generated_binding_name_conflict?(name, binding_kind:)
|
|
691
|
+
|
|
692
|
+
"#{name}_"
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
def apply_rename_rules(name, rules, context:)
|
|
696
|
+
transformed = name
|
|
697
|
+
|
|
698
|
+
rules.each do |rule|
|
|
699
|
+
case rule[:kind]
|
|
700
|
+
when :prefix
|
|
701
|
+
next unless transformed.start_with?(rule[:match])
|
|
702
|
+
|
|
703
|
+
transformed = rule[:replace_with] + transformed.delete_prefix(rule[:match])
|
|
704
|
+
when :replace
|
|
705
|
+
transformed = transformed.gsub(rule[:match], rule[:replace_with])
|
|
706
|
+
when :camelize
|
|
707
|
+
transformed = camelize_binding_name(transformed)
|
|
708
|
+
when :opengl
|
|
709
|
+
transformed = openglize_binding_name(transformed)
|
|
710
|
+
else
|
|
711
|
+
raise Error, "unsupported #{context} rename rule #{rule[:kind]} in #{@policy_path}"
|
|
712
|
+
end
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
raise Error, "#{context} in #{@policy_path} cannot be empty" if transformed.empty?
|
|
716
|
+
|
|
717
|
+
transformed
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
def strip_prefix(name, prefix, context:)
|
|
721
|
+
stripped = prefix && name.start_with?(prefix) ? name.delete_prefix(prefix) : name
|
|
722
|
+
raise Error, "#{context} in #{@policy_path} cannot be empty" if stripped.empty?
|
|
723
|
+
|
|
724
|
+
stripped
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
def index_function_overrides(entries, declarations_by_name)
|
|
728
|
+
overrides = {}
|
|
729
|
+
|
|
730
|
+
entries.each do |entry|
|
|
731
|
+
validate_allowed_keys!(entry, %w[raw name type_params params return_type mapping], context: "function override")
|
|
732
|
+
|
|
733
|
+
raw_name = entry.fetch("raw")
|
|
734
|
+
raise Error, "function override raw names in #{@policy_path} must be strings" unless raw_name.is_a?(String)
|
|
735
|
+
raise Error, "unknown raw function #{raw_name} in #{@raw_module_name}" unless declarations_by_name.key?(raw_name)
|
|
736
|
+
|
|
737
|
+
(overrides[raw_name] ||= []) << entry
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
overrides
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
def render_overridden_function(entry, raw_declaration, spec:)
|
|
744
|
+
render_overridden_foreign_function(entry, raw_declaration, spec:)
|
|
745
|
+
end
|
|
746
|
+
|
|
747
|
+
def render_overridden_foreign_function(entry, raw_declaration, spec:)
|
|
748
|
+
raw_name = raw_declaration.name
|
|
749
|
+
function_name = entry["name"] || foreign_function_name(raw_name, spec:)
|
|
750
|
+
type_params = override_type_param_names(entry, raw_declaration)
|
|
751
|
+
params = override_param_specs(entry, raw_declaration).map { |param| render_foreign_param(param) }
|
|
752
|
+
return_type = entry["return_type"] || render_public_foreign_type(raw_declaration.return_type)
|
|
753
|
+
mapping = entry["mapping"] || "#{@import_alias}.#{raw_name}"
|
|
754
|
+
|
|
755
|
+
[function_name, [build_foreign_signature(function_name, type_params:, params:, return_type:, mapping:, variadic: false)]]
|
|
756
|
+
end
|
|
757
|
+
|
|
758
|
+
def render_pass_through_foreign_function(raw_declaration, spec:)
|
|
759
|
+
function_name = foreign_function_name(raw_declaration.name, spec:)
|
|
760
|
+
params = raw_declaration.params.map { |param| render_heuristic_foreign_param(param) }
|
|
761
|
+
return_type = render_public_foreign_type(raw_declaration.return_type)
|
|
762
|
+
mapping = "#{@import_alias}.#{raw_declaration.name}"
|
|
763
|
+
|
|
764
|
+
[function_name, [build_foreign_signature(function_name, type_params: raw_type_param_names(raw_declaration), params:, return_type:, mapping:, variadic: raw_declaration.variadic)]]
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
def override_type_param_names(entry, raw_declaration)
|
|
768
|
+
if entry.key?("type_params")
|
|
769
|
+
normalize_name_list(entry["type_params"], context: "function type parameter", label: "function type parameter")
|
|
770
|
+
else
|
|
771
|
+
raw_type_param_names(raw_declaration)
|
|
772
|
+
end
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
def override_param_specs(entry, raw_declaration)
|
|
776
|
+
if entry.key?("params")
|
|
777
|
+
Array(entry["params"]).map do |param|
|
|
778
|
+
param = param.dup
|
|
779
|
+
param["name"] = generated_foreign_param_name(param.fetch("name"))
|
|
780
|
+
param["type"] = apply_native_type_mapping(param["type"])
|
|
781
|
+
param
|
|
782
|
+
end
|
|
783
|
+
else
|
|
784
|
+
raw_declaration.params.map do |param|
|
|
785
|
+
{
|
|
786
|
+
"name" => generated_foreign_param_name(param.name),
|
|
787
|
+
"type" => render_public_foreign_type(param.type),
|
|
788
|
+
}
|
|
789
|
+
end
|
|
790
|
+
end
|
|
791
|
+
end
|
|
792
|
+
|
|
793
|
+
def apply_native_type_mapping(type_str)
|
|
794
|
+
return type_str unless type_str.is_a?(String)
|
|
795
|
+
return type_str if @native_type_mapping.empty?
|
|
796
|
+
|
|
797
|
+
type_str.gsub(/\b([A-Z][A-Za-z0-9_]*)\b/) do |match|
|
|
798
|
+
@native_type_mapping[match] || match
|
|
799
|
+
end
|
|
800
|
+
end
|
|
801
|
+
|
|
802
|
+
def plan_overridden_function(entry, raw_declaration, spec:)
|
|
803
|
+
raw_name = raw_declaration.name
|
|
804
|
+
return_type = entry["return_type"] || render_public_foreign_type(raw_declaration.return_type)
|
|
805
|
+
return_type = apply_native_type_mapping(return_type) if return_type
|
|
806
|
+
{
|
|
807
|
+
raw_name:,
|
|
808
|
+
public_name: entry["name"] || foreign_function_name(raw_name, spec:),
|
|
809
|
+
type_params: override_type_param_names(entry, raw_declaration),
|
|
810
|
+
params: override_param_specs(entry, raw_declaration),
|
|
811
|
+
return_type:,
|
|
812
|
+
mapping: entry["mapping"] || "#{@import_alias}.#{raw_name}",
|
|
813
|
+
variadic: false,
|
|
814
|
+
}
|
|
815
|
+
end
|
|
816
|
+
|
|
817
|
+
def plan_pass_through_foreign_function(raw_declaration, spec:)
|
|
818
|
+
{
|
|
819
|
+
raw_name: raw_declaration.name,
|
|
820
|
+
public_name: foreign_function_name(raw_declaration.name, spec:),
|
|
821
|
+
type_params: raw_type_param_names(raw_declaration),
|
|
822
|
+
params: raw_declaration.params.map { |param| heuristic_foreign_param_spec(param) },
|
|
823
|
+
return_type: render_public_foreign_type(raw_declaration.return_type),
|
|
824
|
+
mapping: "#{@import_alias}.#{raw_declaration.name}",
|
|
825
|
+
variadic: raw_declaration.variadic,
|
|
826
|
+
}
|
|
827
|
+
end
|
|
828
|
+
|
|
829
|
+
def foreign_function_name(raw_name, spec:)
|
|
830
|
+
normalize_generated_public_value_name(
|
|
831
|
+
default_public_name(raw_name, spec:, context: "generated function name", binding_kind: :value),
|
|
832
|
+
raw_name:,
|
|
833
|
+
spec:,
|
|
834
|
+
)
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
def render_method_wrapper(function_entry, spec:)
|
|
838
|
+
raw_name = function_entry.fetch(:raw_name)
|
|
839
|
+
method_name = normalize_generated_public_value_name(
|
|
840
|
+
default_public_name(raw_name, spec:, context: "generated method name", binding_kind: :value),
|
|
841
|
+
raw_name:,
|
|
842
|
+
spec:,
|
|
843
|
+
)
|
|
844
|
+
if function_entry.fetch(:variadic, false)
|
|
845
|
+
raise Error, "method generation for #{raw_name} in #{@policy_path} cannot wrap variadic functions"
|
|
846
|
+
end
|
|
847
|
+
method_kind = method_kind(function_entry, spec:, raw_name:)
|
|
848
|
+
method_params = method_kind == :static ? function_entry.fetch(:params) : function_entry.fetch(:params).drop(1)
|
|
849
|
+
if method_params.any? { |param| param["mode"] }
|
|
850
|
+
raise Error, "method generation for #{raw_name} in #{@policy_path} cannot wrap out/inout non-receiver parameters; exclude it from methods"
|
|
851
|
+
end
|
|
852
|
+
call_args = []
|
|
853
|
+
call_args << "this" unless method_kind == :static
|
|
854
|
+
call_args.concat(method_params.map { |param| param.fetch("name") })
|
|
855
|
+
call_expression = build_call_expression(function_entry.fetch(:call_name, function_entry.fetch(:public_name)), type_params: function_entry.fetch(:type_params), args: call_args)
|
|
856
|
+
body_line = function_entry.fetch(:return_type) == "void" ? call_expression : "return #{call_expression}"
|
|
857
|
+
|
|
858
|
+
[
|
|
859
|
+
method_name,
|
|
860
|
+
[
|
|
861
|
+
build_method_signature(
|
|
862
|
+
method_name,
|
|
863
|
+
type_params: function_entry.fetch(:type_params),
|
|
864
|
+
params: method_params.map { |param| render_method_param(param) },
|
|
865
|
+
return_type: function_entry.fetch(:return_type),
|
|
866
|
+
kind: method_kind,
|
|
867
|
+
),
|
|
868
|
+
" #{body_line}",
|
|
869
|
+
],
|
|
870
|
+
]
|
|
871
|
+
end
|
|
872
|
+
|
|
873
|
+
def normalize_generated_public_value_name(name, raw_name:, spec:)
|
|
874
|
+
normalized = snake_case(name)
|
|
875
|
+
return normalized unless spec[:rename_rules].any? { |rule| rule[:kind] == :opengl }
|
|
876
|
+
|
|
877
|
+
normalize_opengl_terminal_suffix(normalize_opengl_snake_case(normalized), raw_name)
|
|
878
|
+
end
|
|
879
|
+
|
|
880
|
+
def method_kind(function_entry, spec:, raw_name:)
|
|
881
|
+
first_param = function_entry.fetch(:params).first
|
|
882
|
+
return :static unless first_param
|
|
883
|
+
return :static unless method_receiver_type_match?(first_param.fetch("type"), spec.fetch(:receiver_types))
|
|
884
|
+
|
|
885
|
+
case first_param["mode"]
|
|
886
|
+
when nil
|
|
887
|
+
:instance
|
|
888
|
+
when "inout"
|
|
889
|
+
:editable
|
|
890
|
+
else
|
|
891
|
+
raise Error, "method generation for #{raw_name} in #{@policy_path} cannot use receiver mode #{first_param["mode"].inspect}"
|
|
892
|
+
end
|
|
893
|
+
end
|
|
894
|
+
|
|
895
|
+
def method_receiver_type_match?(param_type, receiver_types)
|
|
896
|
+
return true if receiver_types.include?(param_type)
|
|
897
|
+
|
|
898
|
+
receiver_types.any? { |rt| @native_type_mapping[rt] == param_type }
|
|
899
|
+
end
|
|
900
|
+
|
|
901
|
+
def build_foreign_signature(name, type_params:, params:, return_type:, mapping:, variadic: false, visibility: :public)
|
|
902
|
+
signature = +""
|
|
903
|
+
signature << "public " if visibility == :public
|
|
904
|
+
signature << "foreign function #{name}"
|
|
905
|
+
signature << render_type_params(type_params)
|
|
906
|
+
rendered_params = params.dup
|
|
907
|
+
rendered_params << "..." if variadic
|
|
908
|
+
signature << "(#{rendered_params.join(', ')}) -> #{return_type}"
|
|
909
|
+
signature << " = #{mapping}"
|
|
910
|
+
signature
|
|
911
|
+
end
|
|
912
|
+
|
|
913
|
+
def build_method_signature(name, type_params:, params:, return_type:, kind:, visibility: :public)
|
|
914
|
+
signature = +""
|
|
915
|
+
signature << "public " if visibility == :public
|
|
916
|
+
signature << case kind
|
|
917
|
+
when :static
|
|
918
|
+
"static function "
|
|
919
|
+
when :editable
|
|
920
|
+
"editable function "
|
|
921
|
+
else
|
|
922
|
+
"function "
|
|
923
|
+
end
|
|
924
|
+
signature << name
|
|
925
|
+
signature << render_type_params(type_params)
|
|
926
|
+
signature << "(#{params.join(', ')}) -> #{return_type}:"
|
|
927
|
+
signature
|
|
928
|
+
end
|
|
929
|
+
|
|
930
|
+
def build_call_expression(name, type_params:, args:)
|
|
931
|
+
expression = name.dup
|
|
932
|
+
expression << render_type_params(type_params)
|
|
933
|
+
expression << "(#{args.join(', ')})"
|
|
934
|
+
expression
|
|
935
|
+
end
|
|
936
|
+
|
|
937
|
+
def raw_type_param_names(raw_declaration)
|
|
938
|
+
Array(raw_declaration.type_params).map(&:name)
|
|
939
|
+
end
|
|
940
|
+
|
|
941
|
+
def render_raw_foreign_param(param)
|
|
942
|
+
"#{generated_foreign_param_name(param.name)}: #{render_public_foreign_type(param.type)}"
|
|
943
|
+
end
|
|
944
|
+
|
|
945
|
+
def render_method_param(param)
|
|
946
|
+
"#{param.fetch("name")}: #{param.fetch("type")}"
|
|
947
|
+
end
|
|
948
|
+
|
|
949
|
+
def render_heuristic_foreign_param(param)
|
|
950
|
+
type = param.type
|
|
951
|
+
|
|
952
|
+
if type.is_a?(AST::TypeRef) && type.name.to_s == "cstr" && type.arguments.empty? && !type.nullable
|
|
953
|
+
name = generated_foreign_param_name(param.name)
|
|
954
|
+
return render_foreign_param({ "name" => name, "type" => "str", "boundary_type" => "cstr" })
|
|
955
|
+
end
|
|
956
|
+
|
|
957
|
+
render_raw_foreign_param(param)
|
|
958
|
+
end
|
|
959
|
+
|
|
960
|
+
def heuristic_foreign_param_spec(param)
|
|
961
|
+
type = param.type
|
|
962
|
+
|
|
963
|
+
if type.is_a?(AST::TypeRef) && type.name.to_s == "cstr" && type.arguments.empty? && !type.nullable
|
|
964
|
+
name = generated_foreign_param_name(param.name)
|
|
965
|
+
return { "name" => name, "type" => "str", "boundary_type" => "cstr" }
|
|
966
|
+
end
|
|
967
|
+
|
|
968
|
+
{ "name" => generated_foreign_param_name(param.name), "type" => render_public_foreign_type(param.type) }
|
|
969
|
+
end
|
|
970
|
+
|
|
971
|
+
def generated_foreign_param_name(name)
|
|
972
|
+
normalized = snake_case(name)
|
|
973
|
+
return normalized unless generated_binding_name_conflict?(normalized, binding_kind: :value)
|
|
974
|
+
|
|
975
|
+
"#{normalized}_"
|
|
976
|
+
end
|
|
977
|
+
|
|
978
|
+
def generated_binding_name_conflict?(name, binding_kind:)
|
|
979
|
+
return true if Token::KEYWORDS.key?(name)
|
|
980
|
+
|
|
981
|
+
case binding_kind
|
|
982
|
+
when :type
|
|
983
|
+
Types::RESERVED_TYPE_BINDING_NAMES.include?(name)
|
|
984
|
+
when :value
|
|
985
|
+
Types::RESERVED_VALUE_TYPE_NAMES.include?(name)
|
|
986
|
+
else
|
|
987
|
+
raise Error, "unsupported generated binding kind #{binding_kind.inspect} in #{@policy_path}"
|
|
988
|
+
end
|
|
989
|
+
end
|
|
990
|
+
|
|
991
|
+
def render_public_foreign_type(type)
|
|
992
|
+
projected_handle = project_public_handle_type(type)
|
|
993
|
+
return projected_handle if projected_handle
|
|
994
|
+
|
|
995
|
+
render_type(type)
|
|
996
|
+
end
|
|
997
|
+
|
|
998
|
+
def project_public_handle_type(type)
|
|
999
|
+
return unless type.is_a?(AST::TypeRef)
|
|
1000
|
+
return unless %w[ptr const_ptr].include?(type.name.to_s)
|
|
1001
|
+
return unless type.arguments.length == 1
|
|
1002
|
+
|
|
1003
|
+
pointee = type.arguments.first.value
|
|
1004
|
+
return unless pointee.is_a?(AST::TypeRef)
|
|
1005
|
+
return unless pointee.arguments.empty?
|
|
1006
|
+
return unless @public_type_kinds_by_raw_name[pointee.name.to_s] == :opaque
|
|
1007
|
+
|
|
1008
|
+
text = rendered_type_name(pointee.name.to_s).dup
|
|
1009
|
+
text << "?" if type.nullable
|
|
1010
|
+
text
|
|
1011
|
+
end
|
|
1012
|
+
|
|
1013
|
+
def render_foreign_param(param)
|
|
1014
|
+
unless param.is_a?(Hash)
|
|
1015
|
+
raise Error, "function parameters in #{@policy_path} must be objects"
|
|
1016
|
+
end
|
|
1017
|
+
|
|
1018
|
+
text = +""
|
|
1019
|
+
mode = param["mode"]
|
|
1020
|
+
text << "#{mode} " if mode
|
|
1021
|
+
text << "#{param.fetch("name")}: #{param.fetch("type")}"
|
|
1022
|
+
boundary_type = param["boundary_type"]
|
|
1023
|
+
text << " as #{boundary_type}" if boundary_type
|
|
1024
|
+
text
|
|
1025
|
+
end
|
|
1026
|
+
|
|
1027
|
+
def render_type_params(type_params)
|
|
1028
|
+
return "" if type_params.empty?
|
|
1029
|
+
|
|
1030
|
+
"[#{type_params.join(', ')}]"
|
|
1031
|
+
end
|
|
1032
|
+
|
|
1033
|
+
def render_type(type)
|
|
1034
|
+
case type
|
|
1035
|
+
when AST::TypeRef
|
|
1036
|
+
text = rendered_type_name(type.name.to_s).dup
|
|
1037
|
+
unless type.arguments.empty?
|
|
1038
|
+
rendered_arguments = type.arguments.map { |argument| render_type_argument(argument.value) }
|
|
1039
|
+
text << "[#{rendered_arguments.join(', ')}]"
|
|
1040
|
+
end
|
|
1041
|
+
text << "?" if type.nullable
|
|
1042
|
+
text
|
|
1043
|
+
when AST::FunctionType
|
|
1044
|
+
params = type.params.map { |param| render_function_type_param(param) }.join(', ')
|
|
1045
|
+
"fn(#{params}) -> #{render_public_foreign_type(type.return_type)}"
|
|
1046
|
+
else
|
|
1047
|
+
raise Error, "unsupported raw type node #{type.class.name} in #{@raw_module_path}"
|
|
1048
|
+
end
|
|
1049
|
+
end
|
|
1050
|
+
|
|
1051
|
+
def render_function_type_param(param)
|
|
1052
|
+
case param
|
|
1053
|
+
when AST::Param
|
|
1054
|
+
"#{param.name}: #{render_public_foreign_type(param.type)}"
|
|
1055
|
+
when AST::ForeignParam
|
|
1056
|
+
render_foreign_param({ "name" => param.name, "type" => render_public_foreign_type(param.type), "mode" => param.mode, "boundary_type" => param.boundary_type && render_public_foreign_type(param.boundary_type) })
|
|
1057
|
+
else
|
|
1058
|
+
render_type(param)
|
|
1059
|
+
end
|
|
1060
|
+
end
|
|
1061
|
+
|
|
1062
|
+
def render_type_argument(argument)
|
|
1063
|
+
case argument
|
|
1064
|
+
when AST::TypeRef, AST::FunctionType
|
|
1065
|
+
render_type(argument)
|
|
1066
|
+
when AST::IntegerLiteral
|
|
1067
|
+
argument.lexeme
|
|
1068
|
+
when AST::Identifier
|
|
1069
|
+
argument.name
|
|
1070
|
+
else
|
|
1071
|
+
raise Error, "unsupported raw type argument #{argument.class.name} in #{@raw_module_path}"
|
|
1072
|
+
end
|
|
1073
|
+
end
|
|
1074
|
+
|
|
1075
|
+
def rendered_type_name(raw_name, seen = [])
|
|
1076
|
+
return @native_type_mapping[raw_name] if @native_type_mapping.key?(raw_name)
|
|
1077
|
+
|
|
1078
|
+
bare_name = raw_name.split(".").last
|
|
1079
|
+
return @native_type_mapping[bare_name] if @native_type_mapping.key?(bare_name)
|
|
1080
|
+
|
|
1081
|
+
public_name = @public_type_names_by_raw_name[raw_name]
|
|
1082
|
+
return public_name if public_name
|
|
1083
|
+
|
|
1084
|
+
raise Error, "cyclic raw type alias #{raw_name} in #{@raw_module_path}" if seen.include?(raw_name)
|
|
1085
|
+
|
|
1086
|
+
raw_declaration = @raw_type_declarations[raw_name]
|
|
1087
|
+
if raw_declaration.is_a?(AST::TypeAliasDecl) &&
|
|
1088
|
+
raw_declaration.target.is_a?(AST::TypeRef) &&
|
|
1089
|
+
raw_declaration.target.arguments.empty? &&
|
|
1090
|
+
!raw_declaration.target.nullable
|
|
1091
|
+
return rendered_type_name(raw_declaration.target.name.to_s, seen + [raw_name])
|
|
1092
|
+
end
|
|
1093
|
+
|
|
1094
|
+
raw_name
|
|
1095
|
+
end
|
|
1096
|
+
|
|
1097
|
+
def policy_label
|
|
1098
|
+
root_prefix = MilkTea.root.to_s + "/"
|
|
1099
|
+
return @policy_path.delete_prefix(root_prefix) if @policy_path.start_with?(root_prefix)
|
|
1100
|
+
|
|
1101
|
+
File.basename(@policy_path)
|
|
1102
|
+
end
|
|
1103
|
+
|
|
1104
|
+
def resolve_module_path(module_name)
|
|
1105
|
+
relative_path = File.join(*module_name.split(".")) + ".mt"
|
|
1106
|
+
candidate = @module_roots.lazy.map { |root| File.join(root, relative_path) }.find { |path| File.file?(path) }
|
|
1107
|
+
raise Error, "module not found: #{module_name}" unless candidate
|
|
1108
|
+
|
|
1109
|
+
File.expand_path(candidate)
|
|
1110
|
+
end
|
|
1111
|
+
end
|
|
1112
|
+
end
|
|
1113
|
+
end
|
|
1114
|
+
end
|