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