1 #!/usr/bin/env python 2 3 """ 4 Simplified program data. 5 6 Copyright (C) 2006, 2007 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 from simplify.simplified.utils import Structure, WithName, name 23 from simplify.simplified.program import Subprogram 24 25 # Housekeeping. 26 27 current_structures = set() 28 29 # Special non-program nodes. 30 31 class GeneralClass(Structure, WithName): 32 33 """ 34 The basis of a Python class. Classes with specific instantiation behaviour 35 should inherit from this class. 36 """ 37 38 def __init__(self, *args, **kw): 39 Structure.__init__(self, *args, **kw) 40 WithName.__init__(self) 41 42 def full_name(self): 43 return "class %s" % self._full_name 44 45 # Utility methods. 46 47 def get_visible_class_attributes(self): 48 49 "Return a dictionary of visible class attributes." 50 51 reverse_base_refs = self.base_refs 52 reverse_base_refs.reverse() 53 54 attributes = {} 55 56 for base_defs in reverse_base_refs: 57 for base_def in base_defs: 58 attributes.update(base_def.type.get_visible_class_attributes()) 59 60 for name, defs in self.namespace.items(): 61 attributes[name] = self, defs 62 63 return attributes 64 65 def get_instance_attribute_names(self): 66 67 "Return all attribute names used by the instances of this class." 68 69 names = set() 70 for instance in self.get_instances(): 71 for name in instance.namespace.keys(): 72 names.add(name) 73 return names 74 75 def get_names_to_instances(self, distinct=0): 76 77 """ 78 Return a tuple containing a mapping from names to instances, and a list 79 of sorted instance names. 80 """ 81 82 d = {} 83 names = [] 84 if distinct: 85 instances = set(self.get_distinct_instances().values()) 86 else: 87 instances = self.get_instances() 88 89 for instance in instances: 90 name = instance.full_name() 91 names.append(name) 92 d[name] = instance 93 94 names.sort() 95 return d, names 96 97 def get_distinct_instances(self): 98 99 """ 100 Return a dictionary mapping instances to a set of instances whose 101 attribute types are distinct. 102 """ 103 104 in_current_structure = (self in current_structures) 105 current_structures.add(self) 106 107 instances = {} 108 names_found = [] 109 instances_found = [] 110 111 # Rather than use the instances directly, get them in name order in 112 # order to favour those earlier according to the sorting. 113 114 names_to_instances, instance_names = self.get_names_to_instances() 115 116 for instance_name in instance_names: 117 instance = names_to_instances[instance_name] 118 119 if in_current_structure: 120 instances[instance] = instance 121 continue 122 123 names = instance.namespace.names 124 try: 125 i = names_found.index(names) 126 instances[instance] = instances_found[i] 127 except ValueError: 128 names_found.append(names) 129 instances_found.append(instance) 130 instances[instance] = instance 131 132 current_structures.remove(self) 133 134 return instances 135 136 class SingleInstanceClass(GeneralClass): 137 138 "A Python class producing only one instance." 139 140 def __init__(self, *args, **kw): 141 GeneralClass.__init__(self, *args, **kw) 142 self.instance = None 143 144 def has_instance(self, node): 145 return self.instance is not None 146 147 def add_instance(self, node, instance): 148 self.instance = instance 149 150 def get_instance(self, node): 151 return self.instance 152 153 def get_instance_name(self, instance): 154 return self._full_name 155 156 def get_instances(self): 157 if self.instance is not None: 158 return [self.instance] 159 else: 160 return [] 161 162 # Attribute propagation. 163 164 def get_attribute_for_instance(self, attribute, instance): 165 return attribute 166 167 class MultipleInstanceClass(GeneralClass): 168 169 "A Python class producing many instances." 170 171 def __init__(self, *args, **kw): 172 GeneralClass.__init__(self, *args, **kw) 173 self.instances = {} 174 self.attributes_for_instances = {} 175 176 def _get_key(self, node): 177 return getattr(node, "original", None) # self.module.original 178 179 def has_instance(self, node): 180 return self.instances.has_key(self._get_key(node)) 181 182 def add_instance(self, node, instance): 183 self.instances[self._get_key(node)] = instance 184 185 def get_instance(self, node): 186 return self.instances[self._get_key(node)] 187 188 def get_instance_name(self, instance): 189 return name(instance, self._full_name) 190 191 def get_instances(self): 192 return self.instances.values() 193 194 # Attribute propagation. 195 196 def get_attribute_for_instance(self, attribute, instance): 197 198 # Create specialised methods. 199 200 if isinstance(attribute.type, Subprogram): 201 subprogram = attribute.type 202 203 # Each instance may have its own version of the subprogram. 204 205 key = (subprogram, instance) 206 if not self.attributes_for_instances.has_key(key): 207 new_subprogram = subprogram.copy(instance, subprogram.full_name()) 208 subprogram.module.simplifier.subnames[new_subprogram.full_name()] = new_subprogram 209 self.attributes_for_instances[key] = Attribute(attribute.context, new_subprogram) 210 print "New subprogram", new_subprogram, "for", key 211 212 return self.attributes_for_instances[key] 213 214 # The original nodes are returned for other attributes. 215 216 else: 217 return attribute 218 219 class SelectiveMultipleInstanceClass(MultipleInstanceClass): 220 221 "A Python class which provides multiple instances depending on the class." 222 223 def _get_key(self, node): 224 if self.namespace.has_key("__atomic__"): 225 return self 226 else: 227 return MultipleInstanceClass._get_key(self, node) 228 229 class ProlificMultipleInstanceClass(MultipleInstanceClass): 230 231 """ 232 A Python class which provides multiple instances for different versions of 233 methods. In order to avoid unbounded instance production (since new 234 instances cause new copies of methods which in turn would cause new 235 instances), a relations dictionary is maintained which attempts to map 236 "requesting instances" to existing instances, suggesting such instances in 237 preference to new ones. 238 """ 239 240 def __init__(self, *args, **kw): 241 MultipleInstanceClass.__init__(self, *args, **kw) 242 self.instance_relations = {} 243 244 def _get_key(self, node): 245 if self.namespace.has_key("__atomic__"): 246 return self 247 else: 248 return node 249 250 def has_instance(self, node): 251 requesting_instance = getattr(node, "instance", None) 252 #return requesting_instance is not None and requesting_instance.get_class() is self or \ 253 return self.instance_relations.has_key(requesting_instance) or self.instances.has_key(self._get_key(node)) 254 255 def add_instance(self, node, instance): 256 requesting_instance = getattr(node, "instance", None) 257 print "New instance", instance, "for", id(node), requesting_instance 258 self.instances[self._get_key(node)] = instance 259 if requesting_instance is not None: 260 self.instance_relations[requesting_instance] = instance 261 requesting_instance.get_class().instance_relations[instance] = requesting_instance 262 263 def get_instance(self, node): 264 requesting_instance = getattr(node, "instance", None) 265 #if requesting_instance is not None and requesting_instance.get_class() is self: 266 # return requesting_instance 267 return self.instance_relations.get(requesting_instance) or self.instances[self._get_key(node)] 268 269 class Instance(Structure): 270 271 "An instance." 272 273 def full_name(self): 274 return self.get_class().get_instance_name(self) 275 276 def get_class(self): 277 for n in self.namespace.load("__class__"): 278 return n.type 279 else: 280 raise ValueError, "__class__" 281 282 class Attribute: 283 284 """ 285 An attribute abstraction, indicating the type of the attribute along with 286 its context or origin. 287 """ 288 289 def __init__(self, context, type): 290 self.context = context 291 self.type = type 292 293 def __repr__(self): 294 return "Attribute(%s, %s)" % (repr(self.context), repr(self.type)) 295 296 def __eq__(self, other): 297 return hasattr(other, "type") and other.type == self.type 298 299 def __hash__(self): 300 return hash(self.type) 301 302 # vim: tabstop=4 expandtab shiftwidth=4