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.instances.values(): 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.instances.values() 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 # Attribute propagation. 133 134 def get_attribute_for_instance(self, attribute, instance): 135 return attribute 136 137 class MultipleInstanceClass(_Class): 138 139 "A Python class producing many instances." 140 141 def __init__(self, *args, **kw): 142 _Class.__init__(self, *args, **kw) 143 self.instances = {} 144 self.attributes_for_instances = {} 145 146 def _get_key(self, node): 147 return getattr(node, "original", None) # self.module.original 148 149 def has_instance(self, node): 150 return self.instances.has_key(self._get_key(node)) 151 152 def add_instance(self, node, instance): 153 self.instances[self._get_key(node)] = instance 154 155 def get_instance(self, node): 156 return self.instances[self._get_key(node)] 157 158 def get_instance_name(self, instance): 159 return name(instance, self._full_name) 160 161 # Attribute propagation. 162 163 def get_attribute_for_instance(self, attribute, instance): 164 165 # Create specialised methods. 166 167 if isinstance(attribute.type, Subprogram): 168 subprogram = attribute.type 169 170 # Each instance may have its own version of the subprogram. 171 172 key = (subprogram, instance) 173 if not self.attributes_for_instances.has_key(key): 174 new_subprogram = subprogram.copy(instance, subprogram.full_name()) 175 subprogram.module.simplifier.subnames[new_subprogram.full_name()] = new_subprogram 176 self.attributes_for_instances[key] = Attribute(attribute.context, new_subprogram) 177 print "New subprogram", new_subprogram, "for", key 178 179 return self.attributes_for_instances[key] 180 181 # The original nodes are returned for other attributes. 182 183 else: 184 return attribute 185 186 class SelectiveMultipleInstanceClass(MultipleInstanceClass): 187 188 "A Python class which provides multiple instances depending on the class." 189 190 def _get_key(self, node): 191 if self.namespace.has_key("__atomic__"): 192 return self 193 else: 194 return MultipleInstanceClass._get_key(self, node) 195 196 class ProlificMultipleInstanceClass(MultipleInstanceClass): 197 198 """ 199 A Python class which provides multiple instances for different versions of 200 methods. In order to avoid unbounded instance production (since new 201 instances cause new copies of methods which in turn would cause new 202 instances), a relations dictionary is maintained which attempts to map 203 "requesting instances" to existing instances, suggesting such instances in 204 preference to new ones. 205 """ 206 207 def __init__(self, *args, **kw): 208 MultipleInstanceClass.__init__(self, *args, **kw) 209 self.instance_relations = {} 210 211 def _get_key(self, node): 212 if self.namespace.has_key("__atomic__"): 213 return self 214 else: 215 return node 216 217 def has_instance(self, node): 218 requesting_instance = getattr(node, "instance", None) 219 #return requesting_instance is not None and requesting_instance.get_class() is self or \ 220 return self.instance_relations.has_key(requesting_instance) or self.instances.has_key(self._get_key(node)) 221 222 def add_instance(self, node, instance): 223 requesting_instance = getattr(node, "instance", None) 224 print "New instance", instance, "for", id(node), requesting_instance 225 self.instances[self._get_key(node)] = instance 226 if requesting_instance is not None: 227 self.instance_relations[requesting_instance] = instance 228 requesting_instance.get_class().instance_relations[instance] = requesting_instance 229 230 def get_instance(self, node): 231 requesting_instance = getattr(node, "instance", None) 232 #if requesting_instance is not None and requesting_instance.get_class() is self: 233 # return requesting_instance 234 return self.instance_relations.get(requesting_instance) or self.instances[self._get_key(node)] 235 236 class Instance(Structure): 237 238 "An instance." 239 240 def full_name(self): 241 return self.get_class().get_instance_name(self) 242 243 def get_class(self): 244 for n in self.namespace.load("__class__"): 245 return n.type 246 else: 247 raise ValueError, "__class__" 248 249 class Constant: 250 251 "A constant initialised with a type name for future processing." 252 253 def __init__(self, name, value): 254 self.name = name 255 self.value = value 256 self.typename = self.value.__class__.__name__ 257 258 class Attribute: 259 260 """ 261 An attribute abstraction, indicating the type of the attribute along with 262 its context or origin. 263 """ 264 265 def __init__(self, context, type): 266 self.context = context 267 self.type = type 268 269 def __repr__(self): 270 return "Attribute(%s, %s)" % (repr(self.context), repr(self.type)) 271 272 def __eq__(self, other): 273 return hasattr(other, "type") and other.type == self.type 274 275 def __hash__(self): 276 return hash(self.type) 277 278 # vim: tabstop=4 expandtab shiftwidth=4