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