Lichen

Annotated referencing.py

336:8c75cdf1a764
2016-12-06 Paul Boddie Introduced stream classes employing C-level FILE pointers, changing the sys stdin, stdout and stderr objects to be instances of these stream classes. Added fread and fwrite support to the native functions. Added support for raising EOFError.
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@249 51
            alias = self.name and ";%s" % self.name or ""
paul@249 52
            return "%s%s" % (self.kind, alias)
paul@0 53
        else:
paul@216 54
            alias = self.name and self.name != self.origin and ";%s" % self.name or ""
paul@203 55
            return "%s:%s%s" % (self.kind, self.origin, alias)
paul@0 56
paul@0 57
    def __hash__(self):
paul@0 58
paul@0 59
        "Hash instances using the kind and origin only."
paul@0 60
paul@0 61
        return hash((self.kind, self.get_origin()))
paul@0 62
paul@0 63
    def __cmp__(self, other):
paul@0 64
paul@0 65
        "Compare with 'other' using the kind and origin only."
paul@0 66
paul@0 67
        if isinstance(other, Reference):
paul@0 68
            return cmp((self.kind, self.get_origin()), (other.kind, other.get_origin()))
paul@0 69
        else:
paul@0 70
            return cmp(str(self), other)
paul@0 71
paul@0 72
    def get_name(self):
paul@0 73
paul@0 74
        "Return the name used for this reference."
paul@0 75
paul@0 76
        return self.name
paul@0 77
paul@0 78
    def get_origin(self):
paul@0 79
paul@0 80
        "Return the origin of the reference."
paul@0 81
paul@0 82
        return self.kind != "<var>" and self.origin or None
paul@0 83
paul@0 84
    def get_kind(self):
paul@0 85
paul@0 86
        "Return the kind of object referenced."
paul@0 87
paul@0 88
        return self.kind
paul@0 89
paul@0 90
    def has_kind(self, kinds):
paul@0 91
paul@0 92
        """
paul@0 93
        Return whether the reference describes an object from the given 'kinds',
paul@0 94
        where such kinds may be "<class>", "<function>", "<instance>",
paul@170 95
        "<module>" or "<var>". Unresolved references may also have kinds of
paul@170 96
        "<depends>" and "<invoke>".
paul@0 97
        """
paul@0 98
paul@0 99
        if not isinstance(kinds, (list, tuple)):
paul@0 100
            kinds = [kinds]
paul@0 101
        return self.get_kind() in kinds
paul@0 102
paul@0 103
    def get_path(self):
paul@0 104
paul@0 105
        "Return the attribute names comprising the path to the origin."
paul@0 106
paul@0 107
        return self.get_origin().split(".")
paul@0 108
paul@170 109
    def unresolved(self):
paul@170 110
paul@170 111
        "Return whether this reference is unresolved."
paul@170 112
paul@310 113
        return self.has_kind(["<depends>", "<invoke>", "<var>"])
paul@170 114
paul@0 115
    def static(self):
paul@0 116
paul@0 117
        "Return this reference if it refers to a static object, None otherwise."
paul@0 118
paul@185 119
        return self.has_kind(["<class>", "<function>", "<module>"]) and self or None
paul@0 120
paul@0 121
    def final(self):
paul@0 122
paul@0 123
        "Return a reference to either a static object or None."
paul@0 124
paul@0 125
        static = self.static()
paul@0 126
        return static and static.origin or None
paul@0 127
paul@0 128
    def instance_of(self):
paul@0 129
paul@0 130
        "Return a reference to an instance of the referenced class."
paul@0 131
paul@0 132
        return self.has_kind("<class>") and Reference("<instance>", self.origin) or None
paul@0 133
paul@0 134
    def as_var(self):
paul@0 135
paul@0 136
        """
paul@0 137
        Return a variable version of this reference. Any origin information is
paul@0 138
        discarded since variable references are deliberately ambiguous.
paul@0 139
        """
paul@0 140
paul@0 141
        return Reference("<var>", None, self.name)
paul@0 142
paul@0 143
    def alias(self, name):
paul@0 144
paul@0 145
        "Alias this reference employing 'name'."
paul@0 146
paul@0 147
        return Reference(self.get_kind(), self.get_origin(), name)
paul@0 148
paul@246 149
    def unaliased(self):
paul@246 150
paul@246 151
        "Return this reference without any alias."
paul@246 152
paul@246 153
        return Reference(self.get_kind(), self.get_origin())
paul@246 154
paul@35 155
    def mutate(self, ref):
paul@35 156
paul@35 157
        "Mutate this reference to have the same details as 'ref'."
paul@35 158
paul@35 159
        self.kind = ref.kind
paul@35 160
        self.origin = ref.origin
paul@35 161
        self.name = ref.name
paul@35 162
paul@86 163
    def parent(self):
paul@86 164
paul@86 165
        "Return the parent of this reference's origin."
paul@86 166
paul@86 167
        if not self.get_origin():
paul@86 168
            return None
paul@86 169
paul@86 170
        return self.get_origin().rsplit(".", 1)[0]
paul@86 171
paul@101 172
    def name_parent(self):
paul@101 173
paul@101 174
        "Return the parent of this reference's aliased name."
paul@101 175
paul@101 176
        if not self.get_name():
paul@101 177
            return None
paul@101 178
paul@101 179
        return self.get_name().rsplit(".", 1)[0]
paul@101 180
paul@16 181
    def ancestors(self):
paul@16 182
paul@16 183
        """
paul@16 184
        Return ancestors of this reference's origin in order of decreasing
paul@16 185
        depth.
paul@16 186
        """
paul@16 187
paul@86 188
        if not self.get_origin():
paul@16 189
            return None
paul@16 190
paul@16 191
        parts = self.get_origin().split(".")
paul@16 192
        ancestors = []
paul@16 193
paul@16 194
        for i in range(len(parts) - 1, 0, -1):
paul@16 195
            ancestors.append(".".join(parts[:i]))
paul@16 196
paul@16 197
        return ancestors
paul@16 198
paul@57 199
    def get_types(self):
paul@57 200
paul@57 201
        "Return class, instance-only and module types for this reference."
paul@57 202
paul@57 203
        class_types = self.has_kind("<class>") and [self.get_origin()] or []
paul@57 204
        instance_types = []
paul@57 205
        module_types = self.has_kind("<module>") and [self.get_origin()] or []
paul@57 206
        return class_types, instance_types, module_types
paul@57 207
paul@0 208
def decode_reference(s, name=None):
paul@0 209
paul@0 210
    "Decode 's', making a reference."
paul@0 211
paul@0 212
    if isinstance(s, Reference):
paul@0 213
        return s.alias(name)
paul@0 214
paul@0 215
    # Null value.
paul@0 216
paul@0 217
    elif not s:
paul@0 218
        return Reference("<var>", None, name)
paul@0 219
paul@0 220
    # Kind and origin.
paul@0 221
paul@0 222
    elif ":" in s:
paul@0 223
        kind, origin = s.split(":")
paul@203 224
        if ";" in origin:
paul@203 225
            origin, name = origin.split(";")
paul@0 226
        return Reference(kind, origin, name)
paul@0 227
paul@249 228
    # Kind and name.
paul@249 229
paul@249 230
    elif ";" in s:
paul@249 231
        kind, name = s.split(";")
paul@249 232
        return Reference(kind, None, name)
paul@249 233
paul@0 234
    # Kind-only, origin is indicated name.
paul@0 235
paul@0 236
    elif s[0] == "<":
paul@0 237
        return Reference(s, name, name)
paul@0 238
paul@0 239
    # Module-only.
paul@0 240
paul@0 241
    else:
paul@0 242
        return Reference("<module>", s, name)
paul@0 243
paul@57 244
paul@57 245
paul@57 246
# Type/reference collection functions.
paul@57 247
paul@57 248
def is_single_class_type(all_types):
paul@57 249
paul@57 250
    """
paul@57 251
    Return whether 'all_types' is a mixture of class and instance kinds for
paul@57 252
    a single class type.
paul@57 253
    """
paul@57 254
paul@57 255
    kinds = set()
paul@57 256
    types = set()
paul@57 257
paul@57 258
    for type in all_types:
paul@57 259
        kinds.add(type.get_kind())
paul@57 260
        types.add(type.get_origin())
paul@57 261
paul@57 262
    return len(types) == 1 and kinds == set(["<class>", "<instance>"])
paul@57 263
paul@57 264
def combine_types(class_types, instance_types, module_types):
paul@57 265
paul@57 266
    """
paul@57 267
    Combine 'class_types', 'instance_types', 'module_types' into a single
paul@57 268
    list of references.
paul@57 269
    """
paul@57 270
paul@57 271
    all_types = []
paul@57 272
    for kind, l in [("<class>", class_types), ("<instance>", instance_types), ("<module>", module_types)]:
paul@57 273
        for t in l:
paul@57 274
            all_types.append(Reference(kind, t))
paul@57 275
    return all_types
paul@57 276
paul@57 277
def separate_types(refs):
paul@57 278
paul@57 279
    """
paul@57 280
    Separate 'refs' into type-specific lists, returning a tuple containing
paul@57 281
    lists of class types, instance types, module types, function types and
paul@57 282
    unknown "var" types.
paul@57 283
    """
paul@57 284
paul@57 285
    class_types = []
paul@57 286
    instance_types = []
paul@57 287
    module_types = []
paul@57 288
    function_types = []
paul@57 289
    var_types = []
paul@57 290
paul@57 291
    for kind, l in [
paul@57 292
        ("<class>", class_types), ("<instance>", instance_types), ("<module>", module_types),
paul@57 293
        ("<function>", function_types), ("<var>", var_types)
paul@57 294
        ]:
paul@57 295
paul@57 296
        for ref in refs:
paul@57 297
            if ref.get_kind() == kind:
paul@57 298
                l.append(ref.get_origin())
paul@57 299
paul@57 300
    return class_types, instance_types, module_types, function_types, var_types
paul@57 301
paul@0 302
# vim: tabstop=4 expandtab shiftwidth=4