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