class LIT::TypeChecker

@api private @since 0.1.0

Constants

AST
PRIMITIVE_TYPE_NAMESPACE

Public Class Methods

new(mod) click to toggle source
# File lib/lit/type_checker.rb, line 10
def initialize(mod)
  @mod = mod
end

Public Instance Methods

check_type!(type, value) click to toggle source
# File lib/lit/type_checker.rb, line 14
def check_type!(type, value)
  is_primitive =
    if type.is_a?(Module)
      type.name.include?(PRIMITIVE_TYPE_NAMESPACE)
    else
      type.class.name.include?(PRIMITIVE_TYPE_NAMESPACE)
    end

  if is_primitive
    check_primitive_type!(type, value)
  elsif type.is_a?(AST::Type::Alias)
    check_type_alias!(type.name, value)
  else
    raise InvalidTypeError, "invalid type: #{type}"
  end
end

Private Instance Methods

check_primitive_type!(type, value) click to toggle source

rubocop:disable Metrics/MethodLength

# File lib/lit/type_checker.rb, line 34
def check_primitive_type!(type, value)
  if type == AST::Type::Primitive::String
    unless value.is_a?(String)
      raise TypeError, "expected #{value} to be a String"
    end
  elsif type == AST::Type::Primitive::Integer
    unless value.is_a?(Integer)
      raise TypeError, "expected #{value} to be an Integer"
    end
  elsif type == AST::Type::Primitive::Float
    unless value.is_a?(Float)
      raise TypeError, "expected #{value} to be a Float"
    end
  elsif type == AST::Type::Primitive::Boolean
    unless value == true || value == false
      raise TypeError, "expected #{value} to be a Boolean"
    end
  elsif type == AST::Type::Primitive::Unit
    unless value.nil?
      raise TypeError, "expected #{value} to be a Unit (nil)"
    end
  elsif type.is_a?(AST::Type::Primitive::Option)
    if value.is_a?(::LIT::Object::Option::Some)
      check_primitive_type!(type.type, value.value)
    elsif value == ::LIT::Object::Option::None
      # ok
    else
      raise TypeError, "expected #{value} to be an instance of Option"
    end
  elsif type.is_a?(AST::Type::Primitive::Array)
    if value.is_a?(::LIT::Object::Array)
      value.each { |v| check_primitive_type!(type.type, v) }
    else
      raise TypeError, "expected #{value} to be an array"
    end
  elsif type.is_a?(AST::Type::Primitive::Map)
    if value.is_a?(::LIT::Object::Map)
      value.values.each do |(k, v)|
        check_primitive_type!(type.key_type, k)
        check_primitive_type!(type.value_type, v)
      end
    else
      raise TypeError, "expected #{value} to be a Map"
    end
  elsif type.is_a?(AST::Type::Alias)
    check_type_alias!(type.name, value)
  else
    raise InvalidTypeError, "invalid primitive type: #{type}"
  end
end
check_type_alias!(name, value) click to toggle source

rubocop:enable Metrics/MethodLength

# File lib/lit/type_checker.rb, line 86
def check_type_alias!(name, value)
  const = @mod.const_get(name)

  if const <= Object::Enum
    unless value.respond_to?(:__parent__) && value.__parent__ == const
      raise TypeError, "expected #{value} to be of type #{name}"
    end
  elsif const <= Object::Struct
    # ok (checked upon struct initialization)
  elsif const <= Object::Option
    type = const.const_get("TYPE")
    check_type!(type, value)
  elsif const <= Object::Map
    type = const.const_get("TYPE")
    check_type!(type, value)
  elsif const <= Object::Array
    type = const.const_get("TYPE")
    check_type!(type, value)
  else
    type = const.const_get("TYPE")
    check_primitive_type!(type, value)
  end
end