Lichen

Annotated referencing.py

96:2219668ae7d9
2016-10-14 Paul Boddie Introduced access mode information for unambiguously-traversed attributes so that the appropriate instruction can be generated. Removed the generation of augmented attribute access plans and the computation of general attribute position ambiguity, since the information will not be used: in cases where ambiguity might need to be determined, attributes must be checked to determine their exact nature even if unambiguous.
paul@0 1
#!/usr/bin/env python
paul@0 2
paul@0 3
"""
paul@0 4
Reference abstractions.
paul@0 5
paul@0 6
Copyright (C) 2016 Paul Boddie <paul@boddie.org.uk>
paul@0 7
paul@0 8
This program is free software; you can redistribute it and/or modify it under
paul@0 9
the terms of the GNU General Public License as published by the Free Software
paul@0 10
Foundation; either version 3 of the License, or (at your option) any later
paul@0 11
version.
paul@0 12
paul@0 13
This program is distributed in the hope that it will be useful, but WITHOUT
paul@0 14
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@0 15
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@0 16
details.
paul@0 17
paul@0 18
You should have received a copy of the GNU General Public License along with
paul@0 19
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@0 20
"""
paul@0 21
paul@0 22
class Reference:
paul@0 23
paul@0 24
    "A reference abstraction."
paul@0 25
paul@0 26
    def __init__(self, kind, origin=None, name=None):
paul@0 27
paul@0 28
        """
paul@0 29
        Initialise a reference using 'kind' to indicate the kind of object,
paul@0 30
        'origin' to indicate the actual origin of a referenced object, and a
paul@0 31
        'name' indicating an alias for the object in the program structure.
paul@0 32
        """
paul@0 33
paul@0 34
        if isinstance(kind, Reference):
paul@0 35
            raise ValueError, (kind, origin)
paul@0 36
        self.kind = kind
paul@0 37
        self.origin = origin
paul@0 38
        self.name = name
paul@0 39
paul@0 40
    def __repr__(self):
paul@0 41
        return "Reference(%r, %r, %r)" % (self.kind, self.origin, self.name)
paul@0 42
paul@0 43
    def __str__(self):
paul@0 44
paul@0 45
        """
paul@0 46
        Serialise the reference as '<var>' or a description incorporating the
paul@0 47
        kind and origin.
paul@0 48
        """
paul@0 49
paul@0 50
        if self.kind == "<var>":
paul@0 51
            return self.kind
paul@0 52
        else:
paul@0 53
            return "%s:%s" % (self.kind, self.origin)
paul@0 54
paul@0 55
    def __hash__(self):
paul@0 56
paul@0 57
        "Hash instances using the kind and origin only."
paul@0 58
paul@0 59
        return hash((self.kind, self.get_origin()))
paul@0 60
paul@0 61
    def __cmp__(self, other):
paul@0 62
paul@0 63
        "Compare with 'other' using the kind and origin only."
paul@0 64
paul@0 65
        if isinstance(other, Reference):
paul@0 66
            return cmp((self.kind, self.get_origin()), (other.kind, other.get_origin()))
paul@0 67
        else:
paul@0 68
            return cmp(str(self), other)
paul@0 69
paul@0 70
    def get_name(self):
paul@0 71
paul@0 72
        "Return the name used for this reference."
paul@0 73
paul@0 74
        return self.name
paul@0 75
paul@0 76
    def get_origin(self):
paul@0 77
paul@0 78
        "Return the origin of the reference."
paul@0 79
paul@0 80
        return self.kind != "<var>" and self.origin or None
paul@0 81
paul@0 82
    def get_kind(self):
paul@0 83
paul@0 84
        "Return the kind of object referenced."
paul@0 85
paul@0 86
        return self.kind
paul@0 87
paul@0 88
    def has_kind(self, kinds):
paul@0 89
paul@0 90
        """
paul@0 91
        Return whether the reference describes an object from the given 'kinds',
paul@0 92
        where such kinds may be "<class>", "<function>", "<instance>",
paul@0 93
        "<module>" or "<var>".
paul@0 94
        """
paul@0 95
paul@0 96
        if not isinstance(kinds, (list, tuple)):
paul@0 97
            kinds = [kinds]
paul@0 98
        return self.get_kind() in kinds
paul@0 99
paul@0 100
    def get_path(self):
paul@0 101
paul@0 102
        "Return the attribute names comprising the path to the origin."
paul@0 103
paul@0 104
        return self.get_origin().split(".")
paul@0 105
paul@0 106
    def static(self):
paul@0 107
paul@0 108
        "Return this reference if it refers to a static object, None otherwise."
paul@0 109
paul@0 110
        return not self.has_kind(["<var>", "<instance>"]) and self or None
paul@0 111
paul@0 112
    def final(self):
paul@0 113
paul@0 114
        "Return a reference to either a static object or None."
paul@0 115
paul@0 116
        static = self.static()
paul@0 117
        return static and static.origin or None
paul@0 118
paul@0 119
    def instance_of(self):
paul@0 120
paul@0 121
        "Return a reference to an instance of the referenced class."
paul@0 122
paul@0 123
        return self.has_kind("<class>") and Reference("<instance>", self.origin) or None
paul@0 124
paul@0 125
    def as_var(self):
paul@0 126
paul@0 127
        """
paul@0 128
        Return a variable version of this reference. Any origin information is
paul@0 129
        discarded since variable references are deliberately ambiguous.
paul@0 130
        """
paul@0 131
paul@0 132
        return Reference("<var>", None, self.name)
paul@0 133
paul@0 134
    def alias(self, name):
paul@0 135
paul@0 136
        "Alias this reference employing 'name'."
paul@0 137
paul@0 138
        return Reference(self.get_kind(), self.get_origin(), name)
paul@0 139
paul@35 140
    def mutate(self, ref):
paul@35 141
paul@35 142
        "Mutate this reference to have the same details as 'ref'."
paul@35 143
paul@35 144
        self.kind = ref.kind
paul@35 145
        self.origin = ref.origin
paul@35 146
        self.name = ref.name
paul@35 147
paul@86 148
    def parent(self):
paul@86 149
paul@86 150
        "Return the parent of this reference's origin."
paul@86 151
paul@86 152
        if not self.get_origin():
paul@86 153
            return None
paul@86 154
paul@86 155
        return self.get_origin().rsplit(".", 1)[0]
paul@86 156
paul@16 157
    def ancestors(self):
paul@16 158
paul@16 159
        """
paul@16 160
        Return ancestors of this reference's origin in order of decreasing
paul@16 161
        depth.
paul@16 162
        """
paul@16 163
paul@86 164
        if not self.get_origin():
paul@16 165
            return None
paul@16 166
paul@16 167
        parts = self.get_origin().split(".")
paul@16 168
        ancestors = []
paul@16 169
paul@16 170
        for i in range(len(parts) - 1, 0, -1):
paul@16 171
            ancestors.append(".".join(parts[:i]))
paul@16 172
paul@16 173
        return ancestors
paul@16 174
paul@57 175
    def get_types(self):
paul@57 176
paul@57 177
        "Return class, instance-only and module types for this reference."
paul@57 178
paul@57 179
        class_types = self.has_kind("<class>") and [self.get_origin()] or []
paul@57 180
        instance_types = []
paul@57 181
        module_types = self.has_kind("<module>") and [self.get_origin()] or []
paul@57 182
        return class_types, instance_types, module_types
paul@57 183
paul@0 184
def decode_reference(s, name=None):
paul@0 185
paul@0 186
    "Decode 's', making a reference."
paul@0 187
paul@0 188
    if isinstance(s, Reference):
paul@0 189
        return s.alias(name)
paul@0 190
paul@0 191
    # Null value.
paul@0 192
paul@0 193
    elif not s:
paul@0 194
        return Reference("<var>", None, name)
paul@0 195
paul@0 196
    # Kind and origin.
paul@0 197
paul@0 198
    elif ":" in s:
paul@0 199
        kind, origin = s.split(":")
paul@0 200
        return Reference(kind, origin, name)
paul@0 201
paul@0 202
    # Kind-only, origin is indicated name.
paul@0 203
paul@0 204
    elif s[0] == "<":
paul@0 205
        return Reference(s, name, name)
paul@0 206
paul@0 207
    # Module-only.
paul@0 208
paul@0 209
    else:
paul@0 210
        return Reference("<module>", s, name)
paul@0 211
paul@57 212
paul@57 213
paul@57 214
# Type/reference collection functions.
paul@57 215
paul@57 216
def is_single_class_type(all_types):
paul@57 217
paul@57 218
    """
paul@57 219
    Return whether 'all_types' is a mixture of class and instance kinds for
paul@57 220
    a single class type.
paul@57 221
    """
paul@57 222
paul@57 223
    kinds = set()
paul@57 224
    types = set()
paul@57 225
paul@57 226
    for type in all_types:
paul@57 227
        kinds.add(type.get_kind())
paul@57 228
        types.add(type.get_origin())
paul@57 229
paul@57 230
    return len(types) == 1 and kinds == set(["<class>", "<instance>"])
paul@57 231
paul@57 232
def combine_types(class_types, instance_types, module_types):
paul@57 233
paul@57 234
    """
paul@57 235
    Combine 'class_types', 'instance_types', 'module_types' into a single
paul@57 236
    list of references.
paul@57 237
    """
paul@57 238
paul@57 239
    all_types = []
paul@57 240
    for kind, l in [("<class>", class_types), ("<instance>", instance_types), ("<module>", module_types)]:
paul@57 241
        for t in l:
paul@57 242
            all_types.append(Reference(kind, t))
paul@57 243
    return all_types
paul@57 244
paul@57 245
def separate_types(refs):
paul@57 246
paul@57 247
    """
paul@57 248
    Separate 'refs' into type-specific lists, returning a tuple containing
paul@57 249
    lists of class types, instance types, module types, function types and
paul@57 250
    unknown "var" types.
paul@57 251
    """
paul@57 252
paul@57 253
    class_types = []
paul@57 254
    instance_types = []
paul@57 255
    module_types = []
paul@57 256
    function_types = []
paul@57 257
    var_types = []
paul@57 258
paul@57 259
    for kind, l in [
paul@57 260
        ("<class>", class_types), ("<instance>", instance_types), ("<module>", module_types),
paul@57 261
        ("<function>", function_types), ("<var>", var_types)
paul@57 262
        ]:
paul@57 263
paul@57 264
        for ref in refs:
paul@57 265
            if ref.get_kind() == kind:
paul@57 266
                l.append(ref.get_origin())
paul@57 267
paul@57 268
    return class_types, instance_types, module_types, function_types, var_types
paul@57 269
paul@0 270
# vim: tabstop=4 expandtab shiftwidth=4