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 # Special non-program nodes. 28 29 class _Class(Structure, WithName): 30 31 """ 32 The basis of a Python class. Classes with specific instantiation behaviour 33 should inherit from this class. 34 """ 35 36 def __init__(self, *args, **kw): 37 Structure.__init__(self, *args, **kw) 38 WithName.__init__(self) 39 40 self.cache = None 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 if self.cache is not None: 87 return self.cache 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 names = instance.namespace.names 101 try: 102 i = names_found.index(names) 103 instances[instance] = instances_found[i] 104 except ValueError: 105 names_found.append(names) 106 instances_found.append(instance) 107 instances[instance] = instance 108 109 self.cache = instances 110 return instances 111 112 class SingleInstanceClass(_Class): 113 114 "A Python class producing only one instance." 115 116 def __init__(self, *args, **kw): 117 _Class.__init__(self, *args, **kw) 118 self.instance = None 119 120 def has_instance(self, node): 121 return self.instance is not None 122 123 def add_instance(self, node, instance): 124 self.instance = instance 125 126 def get_instance(self, node): 127 return self.instance 128 129 def get_instance_name(self, instance): 130 return self._full_name 131 132 def get_instances(self): 133 if self.instance is not None: 134 return [self.instance] 135 else: 136 return [] 137 138 # Attribute propagation. 139 140 def get_attribute_for_instance(self, attribute, instance): 141 return attribute 142 143 class MultipleInstanceClass(_Class): 144 145 "A Python class producing many instances." 146 147 def __init__(self, *args, **kw): 148 _Class.__init__(self, *args, **kw) 149 self.instances = {} 150 self.attributes_for_instances = {} 151 152 def _get_key(self, node): 153 return getattr(node, "original", None) # self.module.original 154 155 def has_instance(self, node): 156 return self.instances.has_key(self._get_key(node)) 157 158 def add_instance(self, node, instance): 159 self.instances[self._get_key(node)] = instance 160 161 def get_instance(self, node): 162 return self.instances[self._get_key(node)] 163 164 def get_instance_name(self, instance): 165 return name(instance, self._full_name) 166 167 def get_instances(self): 168 return self.instances.values() 169 170 # Attribute propagation. 171 172 def get_attribute_for_instance(self, attribute, instance): 173 174 # Create specialised methods. 175 176 if isinstance(attribute.type, Subprogram): 177 subprogram = attribute.type 178 179 # Each instance may have its own version of the subprogram. 180 181 key = (subprogram, instance) 182 if not self.attributes_for_instances.has_key(key): 183 new_subprogram = subprogram.copy(instance, subprogram.full_name()) 184 subprogram.module.simplifier.subnames[new_subprogram.full_name()] = new_subprogram 185 self.attributes_for_instances[key] = Attribute(attribute.context, new_subprogram) 186 print "New subprogram", new_subprogram, "for", key 187 188 return self.attributes_for_instances[key] 189 190 # The original nodes are returned for other attributes. 191 192 else: 193 return attribute 194 195 class SelectiveMultipleInstanceClass(MultipleInstanceClass): 196 197 "A Python class which provides multiple instances depending on the class." 198 199 def _get_key(self, node): 200 if self.namespace.has_key("__atomic__"): 201 return self 202 else: 203 return MultipleInstanceClass._get_key(self, node) 204 205 class ProlificMultipleInstanceClass(MultipleInstanceClass): 206 207 """ 208 A Python class which provides multiple instances for different versions of 209 methods. In order to avoid unbounded instance production (since new 210 instances cause new copies of methods which in turn would cause new 211 instances), a relations dictionary is maintained which attempts to map 212 "requesting instances" to existing instances, suggesting such instances in 213 preference to new ones. 214 """ 215 216 def __init__(self, *args, **kw): 217 MultipleInstanceClass.__init__(self, *args, **kw) 218 self.instance_relations = {} 219 220 def _get_key(self, node): 221 if self.namespace.has_key("__atomic__"): 222 return self 223 else: 224 return node 225 226 def has_instance(self, node): 227 requesting_instance = getattr(node, "instance", None) 228 #return requesting_instance is not None and requesting_instance.get_class() is self or \ 229 return self.instance_relations.has_key(requesting_instance) or self.instances.has_key(self._get_key(node)) 230 231 def add_instance(self, node, instance): 232 requesting_instance = getattr(node, "instance", None) 233 print "New instance", instance, "for", id(node), requesting_instance 234 self.instances[self._get_key(node)] = instance 235 if requesting_instance is not None: 236 self.instance_relations[requesting_instance] = instance 237 requesting_instance.get_class().instance_relations[instance] = requesting_instance 238 239 def get_instance(self, node): 240 requesting_instance = getattr(node, "instance", None) 241 #if requesting_instance is not None and requesting_instance.get_class() is self: 242 # return requesting_instance 243 return self.instance_relations.get(requesting_instance) or self.instances[self._get_key(node)] 244 245 class Instance(Structure): 246 247 "An instance." 248 249 def full_name(self): 250 return self.get_class().get_instance_name(self) 251 252 def get_class(self): 253 for n in self.namespace.load("__class__"): 254 return n.type 255 else: 256 raise ValueError, "__class__" 257 258 class Constant: 259 260 "A constant initialised with a type name for future processing." 261 262 def __init__(self, name, value): 263 self.name = name 264 self.value = value 265 self.typename = self.value.__class__.__name__ 266 267 class Attribute: 268 269 """ 270 An attribute abstraction, indicating the type of the attribute along with 271 its context or origin. 272 """ 273 274 def __init__(self, context, type): 275 self.context = context 276 self.type = type 277 278 def __repr__(self): 279 return "Attribute(%s, %s)" % (repr(self.context), repr(self.type)) 280 281 def __eq__(self, other): 282 return hasattr(other, "type") and other.type == self.type 283 284 def __hash__(self): 285 return hash(self.type) 286 287 # vim: tabstop=4 expandtab shiftwidth=4