git @ Cat's Eye Technologies Dieter / master src / dieter / type.py
master

Tree @master (Download .tar.gz)

type.py @masterraw · history · blame

# -*- coding: utf-8 -*-

"""
type.py -- data type structures for the Dieter programming language.
$Id: type.py 382 2010-01-28 23:40:43Z cpressey $
"""

import logging

logger = logging.getLogger("type")


class Type(object):
    """
    A class representing types.  Note that types are *not* AST nodes.
    After typechecking, every AST node has a type (even if only TypeVoid.)
    'Simple' types like TypeVoid may be aliased (there need not be multiple
    instances of TypeVoid; all expressions of type void can point to a
    single TypeVoid object.)  For this reason it is a good idea for code
    to treat Type objects as immutable.
    
    Note that this class only deals with the structual aspect of types:
    constructing and unifying them.
    Type-checking is dealt with in the TypingContext class.
    """

    def __init__(self):
        self.qualifiers = []

    def qualify(self, qual):
        """
        Returns a new type which is the same as this type except
        with the given qualifier added to it.
        """
        t = self.clone()
        if qual not in self.qualifiers:
            t.qualifiers.append(qual)
        return t

    def unqualify(self, qual):
        """
        Returns a new type which is the same as this type except
        without the given qualifier.
        """
        t = self.clone()
        if qual in t.qualifiers:
            t.qualifiers.remove(qual)
        return t

    def has_qualifier(self, qual):
        """
        Returns True if this type has the given qualifier.
        """
        return qual in self.get_all_qualifiers()

    def get_binding(self):
        """
        Returns the concrete type that this type represents.  In the case of
        actual concrete types, this just returns the same type, but in the case
        of type variables, the chain of equivalence relations is followed to
        find the basic concrete type, if any (or None if the variable is unbound.)
        """
        return self

    def get_all_qualifiers(self):
        """
        Returns all qualifiers on this type.  In the case of concrete types,
        this is just the set of qualifiers as stored internally, but in the
        case of type variables, the chain of equivalence relations is followed
        to collect all qualifiers involved in the binding.
        """
        return self.qualifiers

    def can_receive(receptor, provider):
        """
        The rule for unification with type qualifiers is this: the qualifiers of
        the provider must be a superset of the qualifiers of the receptor.
        
        A type can receive another type if the other type is at least as qualified
        as this type.
        """
        for qual in receptor.get_all_qualifiers():
            if not provider.has_qualifier(qual):
                return False
        return True

    def is_bound(self):
        return True

    def isa(self, python_type):
        """
        Checks if the type this is bound to is the given type.
        Crashes on unbound type variables.
        """
        return isinstance(self.get_binding(), python_type)

    def is_callable(self):
        return False

    def __str__(self):
        """
        Returns a human-readable string representation of this type.
        """
        d = ""
        for qual in self.qualifiers:
            d += qual + ' '
        return d

    def clone(self):
        """
        Returns a deep, fresh copy of this type.  Fresh meaning all type
        variables in the new copy are unbound.
        
        The default implementation of this method is appropriate for
        primitive types -- it just returns another reference to the type,
        which is safe when the type is immutable, as all primitives are.
        """
        return self

    def bestow_qualifiers_onto(self, other):
        for qual in self.get_all_qualifiers():
            if not other.has_qualifier(qual):
                other.qualifiers.append(qual)

    def unify(receptor, provider):
        """
        Returns true, and possibly modifies the given types, if this type
        can be unified with the given type.  By "unify" we mean "make
        equivalent to by assigning appropriate types to type variables."

        Because these types have special rules regarding type qualifiers,
        the unify operation is not commutative (as it would usually be).
        The 'self' type is considered to be the receptor type, whereas the
        argument is the provider type.  The rule for unification with type
        qualifiers is this: the qualifiers of the provider must be a
        superset of the qualifiers of the receptor.
        """
        logger.info("unifying " + str(receptor) + " (receptor) with " + str(provider) + " (provider)")
        if not receptor.can_receive(provider):
            logger.info("receptor cannot receive provider: unification failed")
            return False
        if not provider.is_bound():
            provider.bind_to(receptor)
            return True
        else:
            return provider.isa(receptor.__class__)


class TypeVoid(Type):
    def __str__(self):
        return Type.__str__(self) + 'void'


class TypeBool(Type):
    def __str__(self):
        return Type.__str__(self) + 'bool'


class TypeInt(Type):
    def __str__(self):
        return Type.__str__(self) + 'int'


class TypeRat(Type):
    def __str__(self):
        return Type.__str__(self) + 'rat'


class TypeString(Type):
    def __str__(self):
        return Type.__str__(self) + 'string'


class TypeRef(Type):
    def __str__(self):
        return Type.__str__(self) + 'ref'


# Non-simple types follow.

class TypeMap(Type):

    # NB.  from_type can be None.

    def __init__(self, to_type, from_type):
        Type.__init__(self)
        self.to_type = to_type
        self.from_type = from_type

    def __str__(self):
        s = Type.__str__(self) + 'map '
        if self.from_type is not None:
            s += 'from ' + str(self.from_type)
        s += ' to ' + str(self.to_type)
        return s

    def unify(receptor, provider):
        logger.info("unifying " + str(receptor) + " with " + str(provider))
        if not receptor.can_receive(provider):
            logger.info("receptor cannot receive provider: unification failed")
            return False
        if not provider.is_bound():
            provider.bind_to(receptor)
            return True
        provider = provider.get_binding()
        if not (provider.isa(TypeMap)):
            return False
        if receptor.from_type is not None:
            if not receptor.from_type.unify(provider.from_type):
                return False
        return receptor.to_type.unify(provider.to_type)

    def clone(self):
        from_clone = None
        if self.from_type is not None:
            from_clone = self.from_type.clone()
        new_type = TypeMap(self.to_type.clone(), from_clone)
        self.bestow_qualifiers_onto(new_type)
        return new_type


class TypeProc(Type):
    def __init__(self, return_type):
        Type.__init__(self)
        self.arg_types = []
        self.return_type = return_type

    def is_callable(self):
        return True

    def add_arg_type(self, arg_type):
        self.arg_types.append(arg_type)

    def get_arg_type(self, index):
        return self.arg_types[index]

    def __str__(self):
        d = Type.__str__(self) + 'proc('
        arg_type_strings = []
        for arg_type in self.arg_types:
            arg_type_strings.append(str(arg_type))
        d += ','.join(arg_type_strings) + "): " + str(self.return_type)
        return d

    def unify(receptor, provider):
        logger.info("unifying " + str(receptor) + " with " + str(provider))
        if not receptor.can_receive(provider):
            logger.info("receptor cannot receive provider: unification failed")
            return False
        if not provider.is_bound():
            provider.bind_to(receptor)
            return True
        provider = provider.get_binding()
        if not provider.isa(TypeProc):
            return False
        i = 0
        for arg_type in receptor.arg_types:
            if not arg_type.unify(provider.get_arg_type(i)):
                return False
            i += 1
        return receptor.return_type.unify(provider.return_type)

    def clone(self):
        t = TypeProc(self.return_type.clone())
        for arg_type in self.arg_types:
            t.add_arg_type(arg_type.clone())
        self.bestow_qualifiers_onto(t)
        return t


class TypeVar(Type):

    def __init__(self, name):
        Type.__init__(self)
        self.name = name
        self.bound_to = None

    def __str__(self):
        return Type.__str__(self) + '*' + self.name

    def __unicode__(self):
        return Type.__unicode__(self) + u'♥' + self.name

    def is_variable(self):
        return True

    def is_bound(self):
        return self.bound_to is not None

    def get_binding(self):
        if self.bound_to is None:
            return None
        if self.bound_to is self:
            return self
        return self.bound_to.get_binding()
        
    def bind_to(self, target):
        # Note that when setting the binding for a type variable, we
        # point to the top of the chain, not the bottom as we probably
        # normally would in performing disjoint set union in a common
        # unify.  We do this, even though it is less efficient, in order
        # to pick up all the type qualifiers that are mentioned along
        # the unification chain.
        self.bound_to = target

    def get_all_qualifiers(self):
        quals = []
        quals += self.qualifiers
        type = self
        while type.bound_to is not None:
            type = type.bound_to
            quals += type.qualifiers
        return quals

    def unify(receptor, provider):
        logger.info("unifying. receptor:" + str(receptor) + ", provider:" + str(provider))
        if not receptor.can_receive(provider):
            logger.info("receptor cannot receive provider: unification failed")
            return False

        if not receptor.is_bound():
            receptor.bind_to(provider)
            return True
        
        if not provider.is_bound():
            provider.bind_to(receptor)
            return True

        lhs_type = receptor.get_binding()
        rhs_type = provider.get_binding()
        
        return lhs_type.unify(rhs_type)

    def clone(self):
        t = TypeVar(self.name)
        self.bestow_qualifiers_onto(t)
        return t