Module: Jade::Codegen::FunctionCall

Extended by:
FunctionCall, Helpers
Included in:
FunctionCall
Defined in:
lib/jade/codegen/function_call.rb

Constant Summary

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

#callee_qname(callee, registry) ⇒ Object



58
59
60
61
62
63
# File 'lib/jade/codegen/function_call.rb', line 58

def callee_qname(callee, registry)
  case resolve_callee_symbol(callee, registry)
  in Symbol::InterfaceFunction | Symbol::StdlibFunction => sym then sym.qualified_name
  else nil
  end
end

#constructor_call(callee, args, registry) ⇒ Object



27
28
29
30
31
# File 'lib/jade/codegen/function_call.rb', line 27

def constructor_call(callee, args, registry)
  resolve_callee_symbol(callee, registry)
    .then { to_qualified(it.qualified_name) }
    .then { "#{it}[#{generate_many(args, registry)}]" }
end

#constructor_callee?(callee, registry) ⇒ Boolean

Returns:

  • (Boolean)


33
34
35
36
37
# File 'lib/jade/codegen/function_call.rb', line 33

def constructor_callee?(callee, registry)
  return true if callee.is_a?(AST::ConstructorReference)

  resolve_callee_symbol(callee, registry).is_a?(Symbol::Constructor)
end

#dispatch_for_dep(dep, registry) ⇒ Object

An Implementation’s dep is one of two dictionary-slot shapes: a concrete Implementation (recurse to a ‘=> ruby_code` hash) or a Type::Constraint(Var) marker for a free-var dep that’s late-bound from the caller’s dict env (emit the raw dict reference as a String —build_impl_arg threads it into the impl_arg slot without re-wrapping).



87
88
89
90
91
92
93
94
95
# File 'lib/jade/codegen/function_call.rb', line 87

def dispatch_for_dep(dep, registry)
  case dep
  in Symbol::Implementation
    generate_impl_dispatch(dep, registry)

  in Type::Constraint(type: Type::Var)
    dispatch_value(dep, registry)
  end
end

#dispatch_value(entry, registry) ⇒ Object



97
98
99
100
101
102
103
104
105
# File 'lib/jade/codegen/function_call.rb', line 97

def dispatch_value(entry, registry)
  case entry
  in Type::Constraint(interface:, type: Type::Var(id:))
    Codegen.dict_env[[interface, id]]

  in Symbol::Implementation
    Pretty.hash(generate_impl_dispatch(entry, registry))
  end
end

#emit_operator(op, args, registry) ⇒ Object



65
66
67
68
69
# File 'lib/jade/codegen/function_call.rb', line 65

def emit_operator(op, args, registry)
  args
    .map { generate_node(it, registry) }
    .then { |(a, b)| op == 'compare' ? "#{a}.compare(#{b})" : "(#{a} #{op} #{b})" }
end

#generate(node, registry) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/jade/codegen/function_call.rb', line 7

def generate(node, registry)
  node => AST::FunctionCall(callee:, args:, dictionaries:)

  variant_sym = keyed_variant_constructor(callee, registry)
  return generate_keyed_variant_call(variant_sym, args, registry) if variant_sym

  try_operator_call(callee, args, registry)
    .then { return it if it }

  Inline.try_for(callee, args, dictionaries, registry)
    .then { return it if it }

  return constructor_call(callee, args, registry) if constructor_callee?(callee, registry)

  [generate_many(args, registry), generate_dict_args(callee, dictionaries, registry)]
    .reject(&:empty?)
    .join(', ')
    .then { "#{generate_callee(callee, args, registry, dictionaries)}#{invocation_op(callee, registry)}(#{it})" }
end

#generate_impl_dispatch(impl, registry) ⇒ Object



71
72
73
74
75
76
77
78
79
80
# File 'lib/jade/codegen/function_call.rb', line 71

def generate_impl_dispatch(impl, registry)
  impl
    .deps
    .map { |dep| dispatch_for_dep(dep, registry) }
    .then do |dep_dispatches|
      impl.functions.transform_values do |fn|
        generate_impl_fn(fn, dep_dispatches, impl.functions, registry)
      end
    end
end

#invocation_op(callee, registry) ⇒ Object

Direct-def call sites (‘Foo::Internal.name(args)`) for plain user fns; `.call(args)` for everything else (lambdas, Procs, Methods).



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

def invocation_op(callee, registry)
  case callee.symbol
  in Symbol::ValueRef => ref then invocation_op_for(registry.lookup(ref))
  in symbol                  then invocation_op_for(symbol)
  end
end

#invocation_op_for(symbol) ⇒ Object



48
49
50
# File 'lib/jade/codegen/function_call.rb', line 48

def invocation_op_for(symbol)
  symbol.is_a?(Symbol::Function) ? '' : '.call'
end

#reference_with_dictionaries(symbol, dictionaries, registry) ⇒ Object

Polymorphic fn referenced as a value (not called). Wraps the fn with its dispatched dictionaries so the result is a monomorphic callable matching the type at the use site. Returns nil when the symbol doesn’t need wrapping; the caller falls back to its default reference emission.



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/jade/codegen/function_call.rb', line 112

def reference_with_dictionaries(symbol, dictionaries, registry)
  return nil if dictionaries.empty?

  case symbol
  in Symbol::StdlibFunction => fn if fn.constraints.any?
    dictionaries
      .map { dispatch_dict(it, registry) }
      .then { generate_impl_fn(fn.codegen, it, {}, registry) }

  in Symbol::Function => fn if dict_constraints(fn, registry).any?
    param_names = fn.params.size.times.map { param_synthetic_name(it) }

    fn_constraints(fn, registry)
      .each_with_index
      .filter_map { |c, i| dispatch_value(dictionaries[i], registry) if c.type.is_a?(Type::Var) }
      .then { (param_names + it).join(', ') }
      .then { "#{to_qualified(fn.module_name)}::Internal.#{fn_target_name(fn, registry)}(#{it})" }
      .then { Pretty.lambda(param_names.join(', '), it) }

  in Symbol::InterfaceFunction => fn
    dispatch_value(dictionaries.first, registry)
      &.then { "#{it}[#{fn.name.inspect}]" } ||
      fail("no dict in scope to reference interface method `#{fn.qualified_name}` as a value")

  else
    nil
  end
end

#try_operator_call(callee, args, registry) ⇒ Object



52
53
54
55
56
# File 'lib/jade/codegen/function_call.rb', line 52

def try_operator_call(callee, args, registry)
  MethodNames
    .call_operator(callee_qname(callee, registry))
    &.then { |op| emit_operator(op, args, registry) }
end