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