require "set"
require "./dio.rb"
class B
end
class A < B
end
class D
end
class C < D
end
class M
sig
def fun()
p []
end
sig Integer
def fun(p1)
p [p1]
end
sig Integer, Integer
def fun(p1, p2)
p [p1, p2]
end
sig Integer, Integer, Integer
def fun(p1, p2, p3)
p "Integer# #{[p1, p2, p3].inspect}"
end
sig String, String, String, String
def fun(p1, p2, p3, p4)
p "String# #{[p1, p2, p3, p4].inspect}"
end
sig Array, Array, Array
def fun(p1, p2, p3)
p "Array# #{[p1, p2, p3].inspect}"
end
sig Hash, Hash
def fun(p1, p2)
fun(p1, p2, {})
end
sig Hash, Hash, Hash
def fun(p1, p2, p3)
p "Hash# #{[p1, p2, p3].inspect}"
end
sig Set, Set, Set
def fun(p1, p2, p3)
p "Set# #{[p1, p2, p3].inspect}"
end
sig A, D
def test(a, d)
p "A & D"
end
sig B, C
def test(b, c)
p "B & C"
end
end
M.new.fun()
M.new.fun(1)
M.new.fun(1, 2)
M.new.fun(1, 2, 3)
M.new.fun("A", "B", "C", "D")
M.new.fun({}, {})
M.new.fun(Set.new, Set.new, Set.new)
M.new.test(A.new, C.new)
puts M.new.overloading_methods("fun8")
class Inconsistent < StandardError
attr_reader :object
def initialize(object)
@object = object
end
end
class ConflictDefinition < StandardError
attr_reader :object
def initialize(object)
@object = object
end
end
class DuplicateSignature < StandardError
attr_reader :object
def initialize(object)
@object = object
end
end
class AmbiguousMethodCall < StandardError
attr_reader :object
def initialize(object)
@object = object
end
end
class MethodDefinition
attr_accessor :method_ref, :type, :name
def initialize(method_ref, type, name)
@method_ref = method_ref
@type = type
@name = name
end
def inspect
"#{name}(#{type.join(", ")})"
end
end
class Object
def self.method_added(name, &block)
if $flag
@@expect = nil
$flag = false
return
end
return if !signature_declared?
return if name.to_s.end_with?("__hook")
@@poly_definition ||= {}
@@poly_definition[name] ||= []
method = self.instance_method(name)
if method.arity != @@expect.size
raise Inconsistent.new(self), "Declaration is inconsistent with implementation type parameters"
end
raise ConflictDefinition.new(self), "Conflict definition" if @@poly_definition[name].find{ |definition|
definition.type.size == method.arity && @@expect.zip(definition.type).all?{ |curr, prev| curr == prev }
}
@@poly_definition[name] << MethodDefinition.new(method, @@expect, name)
self.define_method("#{name}__hook") do |*args, &block|
definitions = @@poly_definition[name].select{ |definition|
definition.type.size == args.size && definition
.type
.zip(args.map(&:class))
.all?{ |expect, actual| actual.ancestors.include?(expect) }
}
if definitions.empty?
raise NoMethodError.new(self), "#{name}(#{args.map(&:class).join(", ")})"
else
definition_set = args.map(&:class).map.with_index{ |clazz, i|
ancestors = clazz.ancestors
min_type = definitions.min_by{ |definition| ancestors.index(definition.type[i]) }.type[i]
Set.new(definitions.select{ |definition| definition.type[i] == min_type })
}.reduce(Set.new(definitions), :&)
if definition_set.empty?
raise AmbiguousMethodCall.new(self), "Ambiguous method call"
end
end
definition_set.first.method_ref.bind(self).call(*args, &block)
end
$flag = true
self.class_eval "alias #{name} #{name}__hook"
end
def self.sig(*expect)
if signature_declared?
raise DuplicateSignature.new(self), "DuplicateSignature"
end
@@expect = expect
end
def signature_declared?
class_variable_defined?("@@expect") && !@@expect.nil?
end
def overloading_methods(name)
@@poly_definition[name.to_sym]&.map(&:inspect) || []
end
def static_methods
@@poly_definition.keys
end
end