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