Module: Jade::Codegen::Implementation

Extended by:
Helpers, Implementation
Included in:
Implementation
Defined in:
lib/jade/codegen/implementation.rb

Constant Summary collapse

COMPARABLE_DERIVATIONS =
[
  "def <(other);  compare(other) in Jade::Basics::LT; end",
  "def >(other);  compare(other) in Jade::Basics::GT; end",
  "def <=(other); !(compare(other) in Jade::Basics::GT); end",
  "def >=(other); !(compare(other) in Jade::Basics::LT); end",
].freeze

Constants included from Helpers

Helpers::NATIVE_RUBY_CLASSES

Instance Method Summary collapse

Methods included from Helpers

data_define, dict_constraints, dict_synthetic_name, fn_constraints, fn_impl_synthetic_name, generate_many, generate_node, impl_synthetic_name, param_synthetic_name, resolve_callee_symbol, ruby_classes_for_type, to_qualified

Instance Method Details

#class_reopens(ruby_classes, method_defs) ⇒ Object



48
49
50
51
52
53
# File 'lib/jade/codegen/implementation.rb', line 48

def class_reopens(ruby_classes, method_defs)
  method_defs
    .join(Pretty.newline(2))
    .then { |body| ruby_classes.map { Pretty.block("class #{it}", body) } }
    .join(Pretty.newline(2))
end

#comparable_derivations(iface_qname) ⇒ Object



104
105
106
# File 'lib/jade/codegen/implementation.rb', line 104

def comparable_derivations(iface_qname)
  iface_qname == 'Basics.Comparable' ? COMPARABLE_DERIVATIONS : []
end

#generate(node, registry) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
# File 'lib/jade/codegen/implementation.rb', line 7

def generate(node, registry)
  node => AST::Implementation(symbol:)

  [
    generate_defs(node, registry),
    generate_registrations_for(node, registry),
    operator_impl_or_empty(node, registry, symbol.interface.qualified_name),
  ]
    .reject(&:empty?)
    .join(Pretty.newline(2))
end

#generate_defs(node, registry) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/jade/codegen/implementation.rb', line 108

def generate_defs(node, registry)
  node => AST::Implementation(interface:, applied_type:, functions:)

  type_name =
    case applied_type.constructor
    in AST::TypeName(type:) then type
    in AST::QualifiedTypeName(path:) then path.last
    end

  functions
    .filter_map { generate_function(it, registry, interface, type_name) }
    .join(Pretty.newline(2))
end

#generate_operator_impl(node, registry) ⇒ Object



25
26
27
28
29
30
31
32
33
34
# File 'lib/jade/codegen/implementation.rb', line 25

def generate_operator_impl(node, registry)
  node => AST::Implementation(functions:, symbol:)

  ruby_classes_for_type(symbol.type, registry).then { |ruby_classes|
    next "" if ruby_classes.empty?

    method_bodies_for(node, registry)
      .then { it.empty? ? "" : class_reopens(ruby_classes, it) }
  }
end

#generate_operator_method(impl_fn, impl_sym, registry) ⇒ Object



55
56
57
58
59
60
61
# File 'lib/jade/codegen/implementation.rb', line 55

def generate_operator_method(impl_fn, impl_sym, registry)
  impl_fn => AST::ImplementationFunction(name: fn_name, fn:)

  MethodNames
    .interface_method(impl_sym.interface.qualified_name, fn_name)
    &.then { |ruby_method| operator_method_body(ruby_method, fn, impl_sym, fn_name, registry) }
end

#generate_registrations_for(node, registry) ⇒ Object



122
123
124
125
# File 'lib/jade/codegen/implementation.rb', line 122

def generate_registrations_for(node, registry)
  node => AST::Implementation(symbol:)
  generate_registrations(symbol, registry)
end

#method_bodies_for(node, registry) ⇒ Object

Just the method-def strings (no surrounding ‘class … end`). Used by `Codegen.collect_dispatched_methods` to gather impl methods for inlining into the type’s ‘Data.define do … end` block.



39
40
41
42
43
44
45
46
# File 'lib/jade/codegen/implementation.rb', line 39

def method_bodies_for(node, registry)
  node => AST::Implementation(functions:, symbol:)

  [
    *functions.filter_map { generate_operator_method(it, symbol, registry) },
    *comparable_derivations(symbol.interface.qualified_name),
  ]
end

#operator_impl_or_empty(node, registry, iface_qname) ⇒ Object



19
20
21
22
23
# File 'lib/jade/codegen/implementation.rb', line 19

def operator_impl_or_empty(node, registry, iface_qname)
  MethodNames.operator_interface?(iface_qname) \
    ? generate_operator_impl(node, registry)
    : ""
end

#operator_method_body(ruby_method, fn, impl_sym, fn_name, registry) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/jade/codegen/implementation.rb', line 63

def operator_method_body(ruby_method, fn, impl_sym, fn_name, registry)
  case fn
  in AST::Lambda(params: [first_param, *rest_params], body:)
    return nil unless simple_lambda_param?(first_param)
    return nil unless rest_params.all? { simple_lambda_param?(it) }

    rest_str = rest_params.map { generate_node(it, registry) }.join(', ')
    first_name =
      case first_param
      in AST::Pattern::Binding(name:) then name
      else nil
      end

    Codegen
      .with_self_var_name(first_name) { generate_node(body, registry) }
      .then { Pretty.block("def #{ruby_method}(#{rest_str})", it) }


  # Operator-interface fns are all binary, so `(other)` is the signature.
  in AST::VariableReference | AST::FunctionCall
    impl_sym
      .functions[fn_name]
      .then { it.is_a?(Symbol::ValueRef) ? it : nil }
      &.then { "::#{to_qualified(it.module_name)}::Internal.#{it.name}" }
      &.then { Pretty.block("def #{ruby_method}(other)", "#{it}(self, other)") }
  end
end

#simple_lambda_param?(pattern) ⇒ Boolean

‘(Pepe(id), other) -> { … }` would rebind a destructured pattern to `self` — invalid Ruby. Fall back to the dispatch-table path.

Returns:

  • (Boolean)


93
94
95
# File 'lib/jade/codegen/implementation.rb', line 93

def simple_lambda_param?(pattern)
  pattern in AST::Pattern::Binding | AST::Pattern::Wildcard
end