Lichen

referencing.py

910:a5450be2b2f2
22 months ago Paul Boddie Fixed int limit macros by actually using the size of the int type. For wider types such as long, which may be usable on 64-bit platforms in place of int, 1L would need to be used instead of 1 in the calculation. An abstract native integer type might be introduced here and to the native library to allow wider types if appropriate.
     1 #!/usr/bin/env python     2      3 """     4 Reference abstractions.     5      6 Copyright (C) 2016, 2017 Paul Boddie <paul@boddie.org.uk>     7      8 This program is free software; you can redistribute it and/or modify it under     9 the terms of the GNU General Public License as published by the Free Software    10 Foundation; either version 3 of the License, or (at your option) any later    11 version.    12     13 This program is distributed in the hope that it will be useful, but WITHOUT    14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS    15 FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more    16 details.    17     18 You should have received a copy of the GNU General Public License along with    19 this program.  If not, see <http://www.gnu.org/licenses/>.    20 """    21     22 class Reference:    23     24     "A reference abstraction."    25     26     def __init__(self, kind, origin=None, name=None):    27     28         """    29         Initialise a reference using 'kind' to indicate the kind of object,    30         'origin' to indicate the actual origin of a referenced object, and a    31         'name' indicating an alias for the object in the program structure.    32         """    33     34         if isinstance(kind, Reference):    35             raise ValueError, (kind, origin)    36         self.kind = kind    37         self.origin = origin    38         self.name = name    39     40     def __repr__(self):    41         return "Reference(%r, %r, %r)" % (self.kind, self.origin, self.name)    42     43     def __str__(self):    44     45         """    46         Serialise the reference as '<var>' or a description incorporating the    47         kind and origin.    48         """    49     50         if self.kind == "<var>":    51             alias = self.name and ";%s" % self.name or ""    52             return "%s%s" % (self.kind, alias)    53         else:    54             alias = self.name and self.name != self.origin and ";%s" % self.name or ""    55             return "%s:%s%s" % (self.kind, self.origin, alias)    56     57     def __hash__(self):    58     59         "Hash instances using the kind and origin only."    60     61         return hash((self.kind, self.get_origin()))    62     63     def __cmp__(self, other):    64     65         "Compare with 'other' using the kind and origin only."    66     67         if isinstance(other, Reference):    68             return cmp((self.kind, self.get_origin()), (other.kind, other.get_origin()))    69         else:    70             return cmp(str(self), other)    71     72     def get_name(self):    73     74         "Return the name used for this reference."    75     76         return self.name    77     78     def get_origin(self):    79     80         "Return the origin of the reference."    81     82         return self.kind != "<var>" and self.origin or None    83     84     def get_kind(self):    85     86         "Return the kind of object referenced."    87     88         return self.kind    89     90     def has_kind(self, kinds):    91     92         """    93         Return whether the reference describes an object from the given 'kinds',    94         where such kinds may be "<class>", "<function>", "<instance>",    95         "<module>" or "<var>". Unresolved references may also have kinds of    96         "<depends>" and "<invoke>".    97         """    98     99         if not isinstance(kinds, (list, tuple)):   100             kinds = [kinds]   101         return self.get_kind() in kinds   102    103     def get_path(self):   104    105         "Return the attribute names comprising the path to the origin."   106    107         return self.get_origin().split(".")   108    109     def unresolved(self):   110    111         "Return whether this reference is unresolved."   112    113         return self.has_kind(["<depends>", "<invoke>", "<var>"])   114    115     def static(self):   116    117         "Return this reference if it refers to a static object, None otherwise."   118    119         return self.has_kind(["<class>", "<function>", "<module>"]) and self or None   120    121     def final(self):   122    123         "Return a reference to either a static object or None."   124    125         static = self.static()   126         return static and static.origin or None   127    128     def instance_of(self, alias=None):   129    130         """   131         Return a reference to an instance of the referenced class, indicating an   132         'alias' for the instance if specified.   133         """   134    135         return self.has_kind("<class>") and Reference("<instance>", self.origin, alias) or None   136    137     def as_var(self):   138    139         """   140         Return a variable version of this reference. Any origin information is   141         discarded since variable references are deliberately ambiguous.   142         """   143    144         return Reference("<var>", None, self.name)   145    146     def copy(self):   147    148         "Copy this reference."   149    150         return Reference(self.get_kind(), self.get_origin(), self.get_name())   151    152     def alias(self, name):   153    154         "Alias this reference employing 'name'."   155    156         return Reference(self.get_kind(), self.get_origin(), name)   157    158     def unaliased(self):   159    160         "Return this reference without any alias."   161    162         return Reference(self.get_kind(), self.get_origin())   163    164     def mutate(self, ref):   165    166         "Mutate this reference to have the same details as 'ref'."   167    168         self.kind = ref.kind   169         self.origin = ref.origin   170         self.name = ref.name   171    172     def parent(self):   173    174         "Return the parent of this reference's origin."   175    176         if not self.get_origin():   177             return None   178    179         return self.get_origin().rsplit(".", 1)[0]   180    181     def name_parent(self):   182    183         "Return the parent of this reference's aliased name."   184    185         if not self.get_name():   186             return None   187    188         return self.get_name().rsplit(".", 1)[0]   189    190     def leaf(self):   191    192         "Return the leafname of the reference's origin."   193    194         if not self.get_origin():   195             return None   196    197         return self.get_origin().rsplit(".", 1)[-1]   198    199     def ancestors(self):   200    201         """   202         Return ancestors of this reference's origin in order of decreasing   203         depth.   204         """   205    206         origin = self.get_origin()   207         if not origin:   208             return None   209    210         parts = origin.split(".")   211         ancestors = []   212    213         for i in range(len(parts) - 1, 0, -1):   214             ancestors.append(".".join(parts[:i]))   215    216         return ancestors   217    218     def is_constant_alias(self):   219    220         "Return whether this reference is an alias for a constant."   221    222         name = self.get_name()   223         return name and name.rsplit(".")[-1].startswith("$c")   224    225     def is_predefined_value(self):   226    227         "Return whether this reference identifies a predefined value."   228    229         # NOTE: Details of built-in types employed.   230    231         return self.get_origin() in ("__builtins__.none.NoneType", "__builtins__.boolean.boolean")   232    233     def get_types(self):   234    235         "Return class, instance-only and module types for this reference."   236    237         class_types = self.has_kind("<class>") and [self.get_origin()] or []   238         instance_types = []   239         module_types = self.has_kind("<module>") and [self.get_origin()] or []   240         return class_types, instance_types, module_types   241    242 def decode_reference(s, name=None):   243    244     "Decode 's', making a reference."   245    246     if isinstance(s, Reference):   247         return s.alias(name)   248    249     # Null value.   250    251     elif not s:   252         return Reference("<var>", None, name)   253    254     # Kind and origin.   255    256     elif ":" in s:   257         kind, origin = s.split(":")   258         if ";" in origin:   259             origin, name = origin.split(";")   260         return Reference(kind, origin, name)   261    262     # Kind and name.   263    264     elif ";" in s:   265         kind, name = s.split(";")   266         return Reference(kind, None, name)   267    268     # Kind-only, origin is indicated name.   269    270     elif s[0] == "<":   271         return Reference(s, name, name)   272    273     # Module-only.   274    275     else:   276         return Reference("<module>", s, name)   277    278    279    280 # Type/reference collection functions.   281    282 def is_single_class_type(all_types):   283    284     """   285     Return whether 'all_types' is a mixture of class and instance kinds for   286     a single class type.   287     """   288    289     kinds = set()   290     types = set()   291    292     for type in all_types:   293         kinds.add(type.get_kind())   294         types.add(type.get_origin())   295    296     return len(types) == 1 and kinds == set(["<class>", "<instance>"])   297    298 def combine_types(class_types, instance_types, module_types):   299    300     """   301     Combine 'class_types', 'instance_types', 'module_types' into a single   302     list of references.   303     """   304    305     all_types = []   306     for kind, l in [("<class>", class_types), ("<instance>", instance_types), ("<module>", module_types)]:   307         for t in l:   308             all_types.append(Reference(kind, t))   309     return all_types   310    311 def separate_types(refs):   312    313     """   314     Separate 'refs' into type-specific lists, returning a tuple containing   315     lists of class types, instance types, module types, function types and   316     unknown "var" types.   317     """   318    319     class_types = []   320     instance_types = []   321     module_types = []   322     function_types = []   323     var_types = []   324    325     for kind, l in [   326         ("<class>", class_types), ("<instance>", instance_types), ("<module>", module_types),   327         ("<function>", function_types), ("<var>", var_types)   328         ]:   329    330         for ref in refs:   331             if ref.get_kind() == kind:   332                 l.append(ref.get_origin())   333    334     return class_types, instance_types, module_types, function_types, var_types   335    336 # vim: tabstop=4 expandtab shiftwidth=4