class TypedRb::Runtime::TypeParser

Public Class Methods

parse(type, klass) click to toggle source
# File lib/typed/runtime/type_parser.rb, line 7
def parse(type, klass)
  fail TypedRb::Types::TypeParsingError, 'Error parsing type: nil value.' if type.nil?
  if type == 'unit'
    Types::TyUnit.new
  elsif type == 'Boolean'
    Types::TyBoolean.new
  elsif type.is_a?(Array)
    parse_function_type(type, klass)
  elsif type.is_a?(Hash) && (type[:kind] == :type_var || type[:kind] == :method_type_var)
    maybe_class = Class.for_name(type[:type]) rescue false
    if maybe_class
      type[:type] = maybe_class
    else
      type[:type] = "#{klass}:#{type[:type]}"
    end
    parse_type_var(type)
  elsif type.is_a?(Hash) && type[:kind] == :generic_type
    parse_concrete_type(type, klass)
  elsif type.is_a?(Hash) && type[:kind] == :rest
    parse_rest_args(type, klass)
  elsif type == :unit
    Types::TyUnit.new
  else
    parse_singleton_object_type(type)
  end
end
parse_concrete_type(type, klass) click to toggle source
# File lib/typed/runtime/type_parser.rb, line 83
def parse_concrete_type(type, klass)
  # parameter_names -> container class type vars
  TypedRb.log(binding, :debug, "Parsing concrete type #{type} within #{klass}")

  #TODO: GET RID OF THIS?
  parameter_names = BasicObject::TypeRegistry.type_vars_for(klass).each_with_object({}) do |variable, acc|
    acc[variable.name.split(':').last] = variable
  end
  # this is the concrete argument to parse
  # it might refer to type vars in the container class
  ruby_type = Class.for_name(type[:type])
  is_generic = false
  concrete_type_vars = []
  # for each parameter:
  # - klass.is_variable? -> variable -> generic singletion
  # - klass.is not variable? ->  bound_type -> generic object
  rrt = BasicObject::TypeRegistry.type_vars_for(ruby_type)
  rrt.each_with_index do |type_var, i|
    #TODO: GET RID OF THIS?
    param = type[:parameters][i]
    maybe_bound_param = parameter_names[param[:type]]
    parsed_type_var = if maybe_bound_param
                        is_generic = true
                        maybe_bound_param
                      else
                        if param[:kind] == :generic_type
                          # It is a nested generic type
                          #klass = Class.for_name(param[:type])
                          bound = parse(param, klass)
                          concrete_param = Types::Polymorphism::TypeVariable.new(type_var.name,
                                                                                 :upper_bound => bound,
                                                                                 :lower_bound => bound,
                                                                                 :gen_name => false)
                          concrete_param.bind(bound)
                          is_generic = bound.is_a?(Types::TyGenericSingletonObject) ? true : false
                          concrete_param
                        elsif param[:bound]
                          # A type parameter that is not bound in the generic type declaration.
                          # It has to be local to the method or a wildcard '?'
                          is_generic = true
                          # TODO: add some reference to the method if the variable is method specific?
                          if param[:type] == '?' # [? < Type]
                            param[:type] = "#{type_var.name}:#{type_application_counter}:#{param[:type]}"
                          else # [E < Type]
                            param[:type] = "#{type_var.name}:#{param[:type]}:#{type_application_counter}"
                          end
                          parse(param, klass)
                        elsif param[:type] == '?' # [?]
                          is_generic = true
                          Types::Polymorphism::TypeVariable.new(param[:type], :gen_name => false)
                        elsif param[:sub_kind] == :method_type_var # method[E] / [E]
                          # A type parameter that is not bound in the generic type declaration.
                          # It has to be local to the method
                          is_generic = true
                          Types::Polymorphism::TypeVariable.new("#{klass}:#{param[:type]}", :gen_name => false)
                        else  # [Type]
                          begin
                            # The Generic type is bound to a concrete type: bound == upper_bound == lower_bound
                            bound = Types::TySingletonObject.new(Class.for_name(param[:type]))
                            concrete_param = Types::Polymorphism::TypeVariable.new(type_var.name,
                                                                                   :upper_bound => bound,
                                                                                   :lower_bound => bound,
                                                                                   :gen_name => false)
                            concrete_param.bind(bound)
                            concrete_param
                          rescue NameError # [E] / E != ruby type
                            # TODO: transform this into the method_type_var shown before
                            is_generic = true
                            Types::Polymorphism::TypeVariable.new(param[:type], :gen_name => false)
                          end
                        end
                      end
    concrete_type_vars << parsed_type_var
  end

  if is_generic
    Types::TyGenericSingletonObject.new(ruby_type, concrete_type_vars)
  else
    Types::TyGenericObject.new(ruby_type, concrete_type_vars)
  end
end
parse_existential_object_type(type) click to toggle source
# File lib/typed/runtime/type_parser.rb, line 170
def parse_existential_object_type(type)
  ruby_type = Class.for_name(type)
  BasicObject::TypeRegistry.find_existential_type(ruby_type)
rescue StandardError => e
  TypedRb.log(binding, :error, "Error parsing existential object from type #{type}, #{e.message}")
  raise TypedRb::Types::TypeParsingError, "Unknown Ruby type #{type}"
end
parse_function_type(arg_types, klass) click to toggle source
# File lib/typed/runtime/type_parser.rb, line 192
def parse_function_type(arg_types, klass)
  return_type = parse(arg_types.pop, klass)
  block_type = if arg_types.last.is_a?(Hash) && arg_types.last[:kind] == :block_arg
                 block_type = arg_types.pop
                 parse_function_type(block_type[:block], klass)
               end
  parsed_arg_types = arg_types.map { |arg| parse(arg, klass) }
  is_generic = (parsed_arg_types + [return_type]).any? do |var|
    var.is_a?(Types::TyGenericSingletonObject) ||
    var.is_a?(Types::Polymorphism::TypeVariable)
  end

  is_generic ||= block_type.generic? if block_type

  function_class = is_generic ? Types::TyGenericFunction : Types::TyFunction
  function_type = function_class.new(parsed_arg_types, return_type, arg_types)
  function_type.local_typing_context = Types::TypingContext.empty_typing_context if function_type.generic?
  function_type.with_block_type(block_type) if block_type
  function_type
end
parse_rest_args(type, klass) click to toggle source
# File lib/typed/runtime/type_parser.rb, line 68
def parse_rest_args(type,  klass)
  parsed_parameter = parse(type[:parameters].first, klass)
  if parsed_parameter.is_a?(Types::Polymorphism::TypeVariable) ||
     parsed_parameter.is_a?(Types::TyGenericSingletonObject)
    # TODO: should I use #parse_singleton_object_type here?
    Types::TyGenericSingletonObject.new(Array, [parsed_parameter])
  else
    type_var = Types::Polymorphism::TypeVariable.new('Array:T', :gen_name => false,
                                                                :upper_bound => parsed_parameter,
                                                                :lower_bound => parsed_parameter)
    type_var.bind(parsed_parameter)
    Types::TyGenericObject.new(Array, [type_var])
  end
end
parse_singleton_object_type(type, node = nil) click to toggle source
# File lib/typed/runtime/type_parser.rb, line 178
def parse_singleton_object_type(type, node = nil)
  ruby_type = Class.for_name(type)
  generic_type = BasicObject::TypeRegistry.find_generic_type(ruby_type)
  if generic_type
    generic_type.node = node
    generic_type
  else
    Types::TySingletonObject.new(ruby_type, node)
  end
rescue StandardError => e
  TypedRb.log(binding, :error, "Error parsing singleton object from type #{type}, #{e.message}")
  raise TypedRb::Types::TypeParsingError, "Unknown Ruby type #{type}"
end
parse_type_var(type) click to toggle source
# File lib/typed/runtime/type_parser.rb, line 34
def parse_type_var(type)
  if type[:binding] == '<'
    upper_bound = Class.for_name(type[:bound]) rescue type[:bound]
    upper_bound = if upper_bound.is_a?(Module)
                    Types::TySingletonObject.new(upper_bound)
                  else
                    Types::Polymorphism::TypeVariable.new(upper_bound, :gen_name => false)
                  end
    Types::Polymorphism::TypeVariable.new(type[:type],
                                          :upper_bound => upper_bound,
                                          :gen_name    => false)
  elsif type[:binding] == '>'
    lower_bound = Class.for_name(type[:bound])
    lower_bound = if lower_bound.is_a?(Module)
                    Types::TySingletonObject.new(lower_bound)
                  else
                    Types::Polymorphism::TypeVariable.new(lower_bound, :gen_name => false)
                  end
    Types::Polymorphism::TypeVariable.new(type[:type],
                                          :lower_bound => lower_bound,
                                          :gen_name    => false)
  elsif type[:type].is_a?(Module)
    type_object = Types::TyObject.new(type[:type])
    type_var = Types::Polymorphism::TypeVariable.new("#{type[:type]}:T",
                                                     :gen_name => false,
                                                     :upper_bound => type_object,
                                                     :lower_bound => type_object)
    type_var.bind(type_object)
    type_var
  else
    Types::Polymorphism::TypeVariable.new(type[:type], :gen_name => false)
  end
end
type_application_counter() click to toggle source
# File lib/typed/runtime/type_parser.rb, line 165
def type_application_counter
  @type_application_counter ||= 0
  @type_application_counter += 1
end