Class: MilkTea::ModuleBinder

Inherits:
Object
  • Object
show all
Defined in:
lib/milk_tea/core/module_binder.rb

Constant Summary collapse

BUILTIN_EXPORTABLE_RECEIVER_TYPES =
%w[
  StringView Primitive Vector Matrix Quaternion SoA Simd Span Task Dyn
].map { |n| "Types::#{n}" }.freeze

Instance Method Summary collapse

Instance Method Details

#builtin_exportable_receiver?(receiver_type) ⇒ Boolean

Returns:

  • (Boolean)


137
138
139
# File 'lib/milk_tea/core/module_binder.rb', line 137

def builtin_exportable_receiver?(receiver_type)
  BUILTIN_EXPORTABLE_RECEIVER_TYPES.any? { |name| receiver_type.class.to_s.end_with?(name.split("::").last) }
end

#exported_declaration?(analysis, declaration) ⇒ Boolean

Returns:

  • (Boolean)


68
69
70
71
72
73
# File 'lib/milk_tea/core/module_binder.rb', line 68

def exported_declaration?(analysis, declaration)
  return true if analysis.module_kind == :raw_module
  return false unless declaration.respond_to?(:visibility)

  declaration.visibility == :public
end

#exported_interface_binding?(interface, analysis, exported_interfaces) ⇒ Boolean

Returns:

  • (Boolean)


127
128
129
130
131
# File 'lib/milk_tea/core/module_binder.rb', line 127

def exported_interface_binding?(interface, analysis, exported_interfaces)
  return true if exported_interfaces.value?(interface)

  imported_interface_binding?(interface, analysis.imports)
end

#exported_interface_implementations(analysis, exported_types, exported_interfaces) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/milk_tea/core/module_binder.rb', line 102

def exported_interface_implementations(analysis, exported_types, exported_interfaces)
  implemented_interfaces = {}

  analysis.implemented_interfaces.each do |receiver_type, interfaces|
    public_interfaces = interfaces.select do |interface|
      exported_method_receiver?(receiver_type, analysis, exported_types) &&
        exported_interface_binding?(interface, analysis, exported_interfaces) &&
        exported_interface_methods?(receiver_type, interface, analysis, exported_types)
    end

    implemented_interfaces[receiver_type] = public_interfaces.freeze unless public_interfaces.empty?
  end

  implemented_interfaces.freeze
end

#exported_interface_methods?(receiver_type, interface, analysis, exported_types) ⇒ Boolean

Returns:

  • (Boolean)


118
119
120
121
122
123
124
125
# File 'lib/milk_tea/core/module_binder.rb', line 118

def exported_interface_methods?(receiver_type, interface, analysis, exported_types)
  return false unless exported_method_receiver?(receiver_type, analysis, exported_types)

  interface.methods.each_key.all? do |method_name|
    binding = analysis.methods.fetch(receiver_type, {})[method_name]
    binding && binding.ast.respond_to?(:visibility) && binding.ast.visibility == :public
  end
end

#exported_method_receiver?(receiver_type, analysis, exported_types) ⇒ Boolean

Returns:

  • (Boolean)


141
142
143
144
145
146
147
148
149
150
151
# File 'lib/milk_tea/core/module_binder.rb', line 141

def exported_method_receiver?(receiver_type, analysis, exported_types)
  return true if builtin_exportable_receiver?(receiver_type)
  return true if exported_types.value?(receiver_type)
  return true if imported_receiver_type?(receiver_type, analysis.imports)
  return exported_method_receiver?(receiver_type.base, analysis, exported_types) if receiver_type.is_a?(Types::Nullable)
  return receiver_type.arguments.all? { |argument| exported_method_receiver_argument?(argument, analysis, exported_types) } if receiver_type.is_a?(Types::GenericInstance)
  return exported_types.value?(receiver_type.definition) || imported_receiver_type?(receiver_type.definition, analysis.imports) if receiver_type.is_a?(Types::VariantInstance)

  receiver_type.is_a?(Types::StructInstance) &&
    (exported_types.value?(receiver_type.definition) || imported_receiver_type?(receiver_type.definition, analysis.imports))
end

#exported_method_receiver_argument?(argument, analysis, exported_types) ⇒ Boolean

Returns:

  • (Boolean)


153
154
155
156
157
158
# File 'lib/milk_tea/core/module_binder.rb', line 153

def exported_method_receiver_argument?(argument, analysis, exported_types)
  return true if argument.is_a?(Types::LiteralTypeArg)
  return true if argument.is_a?(Types::TypeVar)

  exported_method_receiver?(argument, analysis, exported_types)
end

#exported_methods(analysis, exported_types) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/milk_tea/core/module_binder.rb', line 75

def exported_methods(analysis, exported_types)
  methods = {}
  private_methods = {}

  analysis.methods.each do |receiver_type, bindings|
    public_bindings = {}
    hidden_bindings = {}

    bindings.each do |name, binding|
      visible = binding.ast.respond_to?(:visibility) &&
        binding.ast.visibility == :public &&
        exported_method_receiver?(receiver_type, analysis, exported_types)

      if visible
        public_bindings[name] = binding
      else
        hidden_bindings[name] = binding
      end
    end

    methods[receiver_type] = public_bindings unless public_bindings.empty?
    private_methods[receiver_type] = hidden_bindings unless hidden_bindings.empty?
  end

  [methods, private_methods]
end

#imported_interface_binding?(interface, imports) ⇒ Boolean

Returns:

  • (Boolean)


168
169
170
171
172
173
174
# File 'lib/milk_tea/core/module_binder.rb', line 168

def imported_interface_binding?(interface, imports)
  imports.each_value do |module_binding|
    return true if module_binding.interfaces.value?(interface)
  end

  false
end

#imported_receiver_type?(receiver_type, imports) ⇒ Boolean

Returns:

  • (Boolean)


160
161
162
163
164
165
166
# File 'lib/milk_tea/core/module_binder.rb', line 160

def imported_receiver_type?(receiver_type, imports)
  imports.each_value do |module_binding|
    return true if module_binding.types.value?(receiver_type)
  end

  false
end

#module_binding(analysis) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/milk_tea/core/module_binder.rb', line 5

def module_binding(analysis)
  types = {}
  type_declarations = {}
  interfaces = {}
  attributes = {}
  private_types = {}
  private_interfaces = {}
  private_attributes = {}
  values = {}
  private_values = {}
  functions = {}
  private_functions = {}

  analysis.ast.declarations.each do |declaration|
    case declaration
    when AST::StructDecl, AST::UnionDecl, AST::VariantDecl, AST::EnumDecl, AST::FlagsDecl, AST::OpaqueDecl, AST::TypeAliasDecl
      type_declarations[declaration.name] = declaration
      target = exported_declaration?(analysis, declaration) ? types : private_types
      resolved_type = analysis.types[declaration.name]
      target[declaration.name] = resolved_type if resolved_type
    when AST::InterfaceDecl
      target = exported_declaration?(analysis, declaration) ? interfaces : private_interfaces
      resolved_iface = analysis.interfaces[declaration.name]
      target[declaration.name] = resolved_iface if resolved_iface
    when AST::AttributeDecl
      target = exported_declaration?(analysis, declaration) ? attributes : private_attributes
      resolved_attr = analysis.attributes[declaration.name]
      target[declaration.name] = resolved_attr if resolved_attr
    when AST::ConstDecl, AST::VarDecl, AST::EventDecl
      target = exported_declaration?(analysis, declaration) ? values : private_values
      resolved_val = analysis.values[declaration.name]
      target[declaration.name] = resolved_val if resolved_val
    when AST::FunctionDef, AST::ExternFunctionDecl, AST::ForeignFunctionDecl
      target = exported_declaration?(analysis, declaration) ? functions : private_functions
      resolved_func = analysis.functions[declaration.name]
      target[declaration.name] = resolved_func if resolved_func
    end
  end

  methods, private_methods = exported_methods(analysis, types)
  implemented_interfaces = exported_interface_implementations(analysis, types, interfaces)

  Bindings::ModuleBinding.new(
    name: analysis.module_name,
    types:,
    type_declarations:,
    interfaces:,
    attributes:,
    attribute_applications: analysis.attribute_applications,
    values:,
    functions:,
    methods:,
    implemented_interfaces:,
    imports: analysis.imports,
    private_types:,
    private_interfaces:,
    private_attributes:,
    private_values:,
    private_functions:,
    private_methods:,
  )
end