micropython

Changeset

554:1805fbc407ad
2012-06-27 Paul Boddie raw files shortlog changelog graph Made separate modules for object set support and some basic data structures (instance, constant), mix-ins and related utilities. Moved the location abstraction into the program module.
micropython/__init__.py (file) micropython/basicdata.py (file) micropython/common.py (file) micropython/data.py (file) micropython/objectset.py (file) micropython/program.py (file)
     1.1 --- a/micropython/__init__.py	Tue Jun 26 23:49:49 2012 +0200
     1.2 +++ b/micropython/__init__.py	Wed Jun 27 01:02:32 2012 +0200
     1.3 @@ -39,6 +39,7 @@
     1.4  
     1.5  from micropython.common import *
     1.6  from micropython.data import *
     1.7 +from micropython.program import Location
     1.8  import micropython.ast
     1.9  import micropython.native
    1.10  import micropython.opt
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/micropython/basicdata.py	Wed Jun 27 01:02:32 2012 +0200
     2.3 @@ -0,0 +1,124 @@
     2.4 +#!/usr/bin/env python
     2.5 +
     2.6 +"""
     2.7 +Fundamental program data structure abstractions.
     2.8 +
     2.9 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie <paul@boddie.org.uk>
    2.10 +
    2.11 +This program is free software; you can redistribute it and/or modify it under
    2.12 +the terms of the GNU General Public License as published by the Free Software
    2.13 +Foundation; either version 3 of the License, or (at your option) any later
    2.14 +version.
    2.15 +
    2.16 +This program is distributed in the hope that it will be useful, but WITHOUT
    2.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    2.18 +FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    2.19 +details.
    2.20 +
    2.21 +You should have received a copy of the GNU General Public License along with
    2.22 +this program.  If not, see <http://www.gnu.org/licenses/>.
    2.23 +"""
    2.24 +
    2.25 +# Short representation display support.
    2.26 +
    2.27 +def shortrepr(obj):
    2.28 +    if obj is None:
    2.29 +        return repr(None)
    2.30 +    else:
    2.31 +        return obj.__shortrepr__()
    2.32 +
    2.33 +# Mix-ins and abstract classes.
    2.34 +
    2.35 +class Naming:
    2.36 +
    2.37 +    "A mix-in providing naming conveniences."
    2.38 +
    2.39 +    def full_name(self):
    2.40 +        if self.name is not None:
    2.41 +            return self.parent.full_name() + "." + self.name
    2.42 +        else:
    2.43 +            return self.parent.full_name()
    2.44 +
    2.45 +# Instances are special in that they need to be wrapped together with context in
    2.46 +# a running program, but they are not generally constant.
    2.47 +
    2.48 +class Instance:
    2.49 +
    2.50 +    "A placeholder indicating the involvement of an instance."
    2.51 +
    2.52 +    def __init__(self):
    2.53 +        self.parent = None
    2.54 +
    2.55 +        # Image generation details.
    2.56 +
    2.57 +        self.location = None
    2.58 +
    2.59 +    def __repr__(self):
    2.60 +        return "<instance>"
    2.61 +
    2.62 +    def __eq__(self, other):
    2.63 +        return other.__class__ is Instance
    2.64 +
    2.65 +    def __ne__(self, other):
    2.66 +        return not self.__eq__(other)
    2.67 +
    2.68 +    def __hash__(self):
    2.69 +        return 0
    2.70 +
    2.71 +    __shortrepr__ = __repr__
    2.72 +
    2.73 +# Common instance construction.
    2.74 +
    2.75 +common_instance = Instance()
    2.76 +
    2.77 +def make_instance():
    2.78 +    return common_instance
    2.79 +
    2.80 +class Constant:
    2.81 +
    2.82 +    "A superclass for all constant or context-free structures."
    2.83 +
    2.84 +    pass
    2.85 +
    2.86 +# Data objects appearing in programs before run-time.
    2.87 +
    2.88 +class Const(Constant, Instance):
    2.89 +
    2.90 +    "A constant object with no context."
    2.91 +
    2.92 +    def __init__(self, value):
    2.93 +        Instance.__init__(self)
    2.94 +        self.value = value
    2.95 +
    2.96 +    def get_value(self):
    2.97 +        return self.value
    2.98 +
    2.99 +    def __repr__(self):
   2.100 +        if self.location is not None:
   2.101 +            return "Const(%r, location=%r)" % (self.value, self.location)
   2.102 +        else:
   2.103 +            return "Const(%r)" % self.value
   2.104 +
   2.105 +    __shortrepr__ = __repr__
   2.106 +
   2.107 +    # Support constants as dictionary keys in order to build constant tables.
   2.108 +
   2.109 +    def __eq__(self, other):
   2.110 +        return other is not None and isinstance(other, Const) and \
   2.111 +            self.value == other.value and self.value.__class__ is other.value.__class__
   2.112 +
   2.113 +    def __ne__(self, other):
   2.114 +        return not self.__eq__(other)
   2.115 +
   2.116 +    def __hash__(self):
   2.117 +        return hash(self.value)
   2.118 +
   2.119 +    # Constants are instances of various built-in types.
   2.120 +
   2.121 +    def value_type_name(self):
   2.122 +        return ".".join(self.value_type_name_parts())
   2.123 +
   2.124 +    def value_type_name_parts(self):
   2.125 +        return "__builtins__", self.value.__class__.__name__
   2.126 +
   2.127 +# vim: tabstop=4 expandtab shiftwidth=4
     3.1 --- a/micropython/common.py	Tue Jun 26 23:49:49 2012 +0200
     3.2 +++ b/micropython/common.py	Wed Jun 27 01:02:32 2012 +0200
     3.3 @@ -19,256 +19,9 @@
     3.4  this program.  If not, see <http://www.gnu.org/licenses/>.
     3.5  """
     3.6  
     3.7 -import operator
     3.8 +from micropython.basicdata import Instance
     3.9  import sys
    3.10  
    3.11 -try:
    3.12 -    set
    3.13 -except NameError:
    3.14 -    from sets import Set as set
    3.15 -
    3.16 -class ObjectSet:
    3.17 -
    3.18 -    "A set of objects with optional associated data."
    3.19 -
    3.20 -    def __init__(self, d=None):
    3.21 -        if d is None:
    3.22 -            self.objects = {}
    3.23 -        elif hasattr(d, "items"):
    3.24 -            self.objects = dict(d)
    3.25 -        else:
    3.26 -            self.objects = {}
    3.27 -            for key in d:
    3.28 -                self.add(key)
    3.29 -
    3.30 -    def __repr__(self):
    3.31 -        out = ["{"]
    3.32 -        first = 1
    3.33 -        for key, value in self.items():
    3.34 -            if not first:
    3.35 -                out.append(", ")
    3.36 -            else:
    3.37 -                first = 0
    3.38 -            out.append(repr(key))
    3.39 -            if value:
    3.40 -                out.append(" : ")
    3.41 -                out.append(repr(value))
    3.42 -        out.append("}")
    3.43 -        return "".join(out)
    3.44 -
    3.45 -    def __iter__(self):
    3.46 -        return iter(self.keys())
    3.47 -
    3.48 -    def __nonzero__(self):
    3.49 -        return self.objects != {}
    3.50 -
    3.51 -    # List methods.
    3.52 -
    3.53 -    def __add__(self, other):
    3.54 -        obj = ObjectSet(self)
    3.55 -        for key in other:
    3.56 -            obj.add(key)
    3.57 -        return obj
    3.58 -
    3.59 -    # Set membership and comparisons.
    3.60 -
    3.61 -    def __hash__(self):
    3.62 -        l = self.keys()
    3.63 -        l.sort()
    3.64 -        return hash(tuple(l))
    3.65 -
    3.66 -    def __eq__(self, other):
    3.67 -        if hasattr(other, "objects"):
    3.68 -            return self.objects == other.objects
    3.69 -        else:
    3.70 -            return set(self.objects.keys()) == set(other)
    3.71 -
    3.72 -    # Set methods.
    3.73 -
    3.74 -    def add(self, obj):
    3.75 -        if not self.has_key(obj):
    3.76 -            self[obj] = set()
    3.77 -
    3.78 -    def issubset(self, other):
    3.79 -        return set(self).issubset(other)
    3.80 -
    3.81 -    # Dictionary and related methods.
    3.82 -
    3.83 -    def __getitem__(self, key):
    3.84 -        return self.objects[key]
    3.85 -
    3.86 -    def get(self, key, default=None):
    3.87 -        return self.objects.get(key, default)
    3.88 -
    3.89 -    def __setitem__(self, key, value):
    3.90 -        self.objects[key] = value
    3.91 -
    3.92 -    def has_key(self, key):
    3.93 -        return self.objects.has_key(key)
    3.94 -
    3.95 -    def keys(self):
    3.96 -        return self.objects.keys()
    3.97 -
    3.98 -    def values(self):
    3.99 -        return self.objects.values()
   3.100 -
   3.101 -    def items(self):
   3.102 -        return self.objects.items()
   3.103 -
   3.104 -    def update(self, other):
   3.105 -
   3.106 -        # Combining dictionary-like objects involves combining values.
   3.107 -
   3.108 -        if hasattr(other, "items"):
   3.109 -            for key, value in other.items():
   3.110 -                self[key] = add_sets(value, self.get(key, []))
   3.111 -
   3.112 -        # Combining sequence-like objects involves just adding members.
   3.113 -
   3.114 -        else:
   3.115 -            for key in other:
   3.116 -                self.add(key)
   3.117 -
   3.118 -    def merge(self, other):
   3.119 -
   3.120 -        """
   3.121 -        Merge this object set with an 'other' set, combining the values where
   3.122 -        possible, and incorporating values present in only one of the sets.
   3.123 -        """
   3.124 -
   3.125 -        return combine(self, other, ObjectSet(), add_sets)
   3.126 -
   3.127 -def deepen_mapping_dict(d):
   3.128 -
   3.129 -    "Convert the values of 'd' to be elements of a potentially larger set."
   3.130 -
   3.131 -    new_dict = {}
   3.132 -    for key, value in d.items():
   3.133 -        if value is None:
   3.134 -            new_dict[key] = None
   3.135 -        else:
   3.136 -            new_dict[key] = ObjectSet([value])
   3.137 -    return new_dict
   3.138 -
   3.139 -def merge_mapping_dicts(dicts):
   3.140 -
   3.141 -    "Merge the given 'dicts' mapping keys to sets of objects."
   3.142 -
   3.143 -    new_dict = {}
   3.144 -    update_mapping_dict(new_dict, dicts)
   3.145 -    return new_dict
   3.146 -
   3.147 -def update_mapping_dict(new_dict, dicts):
   3.148 -
   3.149 -    """
   3.150 -    Update 'new_dict' with the contents of the set dictionary 'dicts'.
   3.151 -    None entries may be incorporated into object sets. For example:
   3.152 -
   3.153 -    d1: {'a' : None}
   3.154 -    d2: {'a' : x}
   3.155 -    ->  {'a' : {None, x}}
   3.156 -
   3.157 -    Here, None might be used to represent an empty set and x may be an existing
   3.158 -    set.
   3.159 -    """
   3.160 -
   3.161 -    for old_dict in dicts:
   3.162 -        for key, value in old_dict.items():
   3.163 -
   3.164 -            # Add existing mappings within an object set.
   3.165 -
   3.166 -            if not new_dict.has_key(key):
   3.167 -                if value is not None:
   3.168 -                    new_dict[key] = ObjectSet(value)
   3.169 -                else:
   3.170 -                    new_dict[key] = None
   3.171 -            elif new_dict[key] is not None:
   3.172 -                if value is not None:
   3.173 -                    new_dict[key].update(value)
   3.174 -                else:
   3.175 -                    new_dict[key].add(value)
   3.176 -            else:
   3.177 -                if value is not None:
   3.178 -                    objset = new_dict[key] = ObjectSet(value)
   3.179 -                    objset.add(None)
   3.180 -
   3.181 -def combine_mapping_dicts(d1, d2):
   3.182 -
   3.183 -    """
   3.184 -    Combine dictionaries 'd1' and 'd2' in a resulting dictionary, with the
   3.185 -    values of the contributing dictionaries being themselves combined such that
   3.186 -    a "product" of the values for a given key are stored in the combined
   3.187 -    dictionary.
   3.188 -
   3.189 -    For example:
   3.190 -
   3.191 -    d1: {'a' : [{'f', 'g'}, {'f', 'h'}], ...}
   3.192 -    d2: {'a' : [{'f'}, {'e', 'f', 'g'}], ...}
   3.193 -    ->  {'a' : [{'f', 'g'}, {'f', 'h'}, {'e', 'f', 'g'}, {'e', 'f', 'g', 'h'}], ...}
   3.194 -
   3.195 -    Note that items of 'd2' whose keys are not in 'd1' are not added to 'd1'
   3.196 -    since this, in the context of propagating attribute usage observations,
   3.197 -    would result in spurious usage details being made available in places where
   3.198 -    the names may not have been defined.
   3.199 -    """
   3.200 -
   3.201 -    return combine(d1, d2, {}, combine_object_set_lists, True)
   3.202 -
   3.203 -def combine(d1, d2, combined, combine_op, only_d1_keys=False):
   3.204 -
   3.205 -    """
   3.206 -    Combine dictionaries 'd1' and 'd2' in the 'combined' object provided, using
   3.207 -    the 'combine_op' to merge values from the dictionaries.
   3.208 -
   3.209 -    If 'only_d1_keys' is set to a true value, items from 'd2' employing keys not
   3.210 -    in 'd1' will not be added to 'd1'.
   3.211 -    """
   3.212 -
   3.213 -    if d2 is not None:
   3.214 -        d2_keys = d2.keys()
   3.215 -
   3.216 -        for key in d2_keys:
   3.217 -            if not d1.has_key(key):
   3.218 -                if not only_d1_keys:
   3.219 -                    combined[key] = d2[key]
   3.220 -            else:
   3.221 -                combined[key] = combine_op(d1[key], d2[key])
   3.222 -    else:
   3.223 -        d2_keys = ()
   3.224 -
   3.225 -    for key in d1.keys():
   3.226 -        if key not in d2_keys:
   3.227 -            combined[key] = d1[key]
   3.228 -
   3.229 -    return combined
   3.230 -
   3.231 -def combine_object_set_lists(l1, l2):
   3.232 -
   3.233 -    """
   3.234 -    Combine lists of object sets 'l1' and 'l2' to make a product of their
   3.235 -    members.
   3.236 -    """
   3.237 -
   3.238 -    # If either list is undefined (indicated by None), return the defined list,
   3.239 -    # or return None if neither is defined.
   3.240 -
   3.241 -    if l1 is None:
   3.242 -        if l2 is None:
   3.243 -            return None
   3.244 -        else:
   3.245 -            return l2 + []
   3.246 -    elif l2 is None:
   3.247 -        return l1 + []
   3.248 -
   3.249 -    combined = ObjectSet()
   3.250 -    for i1 in l1:
   3.251 -        for i2 in l2:
   3.252 -            combined.add(i1.merge(i2))
   3.253 -    return combined
   3.254 -
   3.255 -def add_sets(s1, s2):
   3.256 -    return set(list(s1) + list(s2))
   3.257 -
   3.258  # Visitors and activities related to node annotations.
   3.259  
   3.260  class ASTVisitor:
   3.261 @@ -507,43 +260,6 @@
   3.262      def __repr__(self):
   3.263          return "AtLeast(%r)" % self.count
   3.264  
   3.265 -class Location:
   3.266 -
   3.267 -    """
   3.268 -    A special representation for locations which are to be compared to program
   3.269 -    objects.
   3.270 -    """
   3.271 -
   3.272 -    def __init__(self, location):
   3.273 -        self.location = location
   3.274 -
   3.275 -    def _op(self, other, op):
   3.276 -        if hasattr(other, "location"):
   3.277 -            return op(self.location, other.location)
   3.278 -        else:
   3.279 -            raise NotImplemented
   3.280 -
   3.281 -    def __eq__(self, other):
   3.282 -        return self._op(other, operator.eq)
   3.283 -
   3.284 -    def __ne__(self, other):
   3.285 -        return self._op(other, operator.ne)
   3.286 -
   3.287 -    def __lt__(self, other):
   3.288 -        return self._op(other, operator.lt)
   3.289 -
   3.290 -    def __le__(self, other):
   3.291 -        return self._op(other, operator.le)
   3.292 -
   3.293 -    def __gt__(self, other):
   3.294 -        return self._op(other, operator.gt)
   3.295 -
   3.296 -    def __ge__(self, other):
   3.297 -        return self._op(other, operator.ge)
   3.298 -
   3.299 -    def __repr__(self):
   3.300 -        return "Location(%r)" % self.location
   3.301 -
   3.302  # Useful data.
   3.303  
   3.304  operator_functions = {
     4.1 --- a/micropython/data.py	Tue Jun 26 23:49:49 2012 +0200
     4.2 +++ b/micropython/data.py	Wed Jun 27 01:02:32 2012 +0200
     4.3 @@ -53,37 +53,11 @@
     4.4  """
     4.5  
     4.6  from micropython.program import ReplaceableContext, PlaceholderContext
     4.7 +from micropython.basicdata import *
     4.8  from micropython.common import *
     4.9 +from micropython.objectset import *
    4.10  import sys
    4.11  
    4.12 -def shortrepr(obj):
    4.13 -    if obj is None:
    4.14 -        return repr(None)
    4.15 -    else:
    4.16 -        return obj.__shortrepr__()
    4.17 -
    4.18 -lambda_index = 0
    4.19 -
    4.20 -def new_lambda():
    4.21 -
    4.22 -    "Return a new sequence number for a lambda definition."
    4.23 -
    4.24 -    global lambda_index
    4.25 -    lambda_index += 1
    4.26 -    return lambda_index
    4.27 -
    4.28 -# Mix-ins and abstract classes.
    4.29 -
    4.30 -class Naming:
    4.31 -
    4.32 -    "A mix-in providing naming conveniences."
    4.33 -
    4.34 -    def full_name(self):
    4.35 -        if self.name is not None:
    4.36 -            return self.parent.full_name() + "." + self.name
    4.37 -        else:
    4.38 -            return self.parent.full_name()
    4.39 -
    4.40  class NamespaceDict:
    4.41  
    4.42      "A mix-in providing dictionary methods."
    4.43 @@ -1233,86 +1207,6 @@
    4.44              l.append("(c=%s, v=%s)" % (shortrepr(c), shortrepr(v)))
    4.45          return ", ".join(l)
    4.46  
    4.47 -# Instances are special in that they need to be wrapped together with context in
    4.48 -# a running program, but they are not generally constant.
    4.49 -
    4.50 -class Instance:
    4.51 -
    4.52 -    "A placeholder indicating the involvement of an instance."
    4.53 -
    4.54 -    def __init__(self):
    4.55 -        self.parent = None
    4.56 -
    4.57 -        # Image generation details.
    4.58 -
    4.59 -        self.location = None
    4.60 -
    4.61 -    def __repr__(self):
    4.62 -        return "<instance>"
    4.63 -
    4.64 -    def __eq__(self, other):
    4.65 -        return other.__class__ is Instance
    4.66 -
    4.67 -    def __ne__(self, other):
    4.68 -        return not self.__eq__(other)
    4.69 -
    4.70 -    def __hash__(self):
    4.71 -        return 0
    4.72 -
    4.73 -    __shortrepr__ = __repr__
    4.74 -
    4.75 -common_instance = Instance()
    4.76 -
    4.77 -def make_instance():
    4.78 -    return common_instance
    4.79 -
    4.80 -class Constant:
    4.81 -
    4.82 -    "A superclass for all constant or context-free structures."
    4.83 -
    4.84 -    pass
    4.85 -
    4.86 -# Data objects appearing in programs before run-time.
    4.87 -
    4.88 -class Const(Constant, Instance):
    4.89 -
    4.90 -    "A constant object with no context."
    4.91 -
    4.92 -    def __init__(self, value):
    4.93 -        Instance.__init__(self)
    4.94 -        self.value = value
    4.95 -
    4.96 -    def get_value(self):
    4.97 -        return self.value
    4.98 -
    4.99 -    def __repr__(self):
   4.100 -        if self.location is not None:
   4.101 -            return "Const(%r, location=%r)" % (self.value, self.location)
   4.102 -        else:
   4.103 -            return "Const(%r)" % self.value
   4.104 -
   4.105 -    __shortrepr__ = __repr__
   4.106 -
   4.107 -    # Support constants as dictionary keys in order to build constant tables.
   4.108 -
   4.109 -    def __eq__(self, other):
   4.110 -        return other is not None and isinstance(other, Const) and \
   4.111 -            self.value == other.value and self.value.__class__ is other.value.__class__
   4.112 -
   4.113 -    def __ne__(self, other):
   4.114 -        return not self.__eq__(other)
   4.115 -
   4.116 -    def __hash__(self):
   4.117 -        return hash(self.value)
   4.118 -
   4.119 -    # Constants are instances of various built-in types.
   4.120 -
   4.121 -    def value_type_name(self):
   4.122 -        return ".".join(self.value_type_name_parts())
   4.123 -
   4.124 -    def value_type_name_parts(self):
   4.125 -        return "__builtins__", self.value.__class__.__name__
   4.126 -
   4.127  class Class(NamespaceDict, Naming, Constant):
   4.128  
   4.129      "A base class for common/normal classes and the type class."
   4.130 @@ -2162,4 +2056,16 @@
   4.131      else:
   4.132          return CommonClass(name, parent, module, node)
   4.133  
   4.134 +# Lambda sequence numbering.
   4.135 +
   4.136 +lambda_index = 0
   4.137 +
   4.138 +def new_lambda():
   4.139 +
   4.140 +    "Return a new sequence number for a lambda definition."
   4.141 +
   4.142 +    global lambda_index
   4.143 +    lambda_index += 1
   4.144 +    return lambda_index
   4.145 +
   4.146  # vim: tabstop=4 expandtab shiftwidth=4
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/micropython/objectset.py	Wed Jun 27 01:02:32 2012 +0200
     5.3 @@ -0,0 +1,271 @@
     5.4 +#!/usr/bin/env python
     5.5 +
     5.6 +"""
     5.7 +Object set support.
     5.8 +
     5.9 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie <paul@boddie.org.uk>
    5.10 +
    5.11 +This program is free software; you can redistribute it and/or modify it under
    5.12 +the terms of the GNU General Public License as published by the Free Software
    5.13 +Foundation; either version 3 of the License, or (at your option) any later
    5.14 +version.
    5.15 +
    5.16 +This program is distributed in the hope that it will be useful, but WITHOUT
    5.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    5.18 +FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    5.19 +details.
    5.20 +
    5.21 +You should have received a copy of the GNU General Public License along with
    5.22 +this program.  If not, see <http://www.gnu.org/licenses/>.
    5.23 +"""
    5.24 +
    5.25 +import operator
    5.26 +
    5.27 +try:
    5.28 +    set
    5.29 +except NameError:
    5.30 +    from sets import Set as set
    5.31 +
    5.32 +class ObjectSet:
    5.33 +
    5.34 +    "A set of objects with optional associated data."
    5.35 +
    5.36 +    def __init__(self, d=None):
    5.37 +        if d is None:
    5.38 +            self.objects = {}
    5.39 +        elif hasattr(d, "items"):
    5.40 +            self.objects = dict(d)
    5.41 +        else:
    5.42 +            self.objects = {}
    5.43 +            for key in d:
    5.44 +                self.add(key)
    5.45 +
    5.46 +    def __repr__(self):
    5.47 +        out = ["{"]
    5.48 +        first = 1
    5.49 +        for key, value in self.items():
    5.50 +            if not first:
    5.51 +                out.append(", ")
    5.52 +            else:
    5.53 +                first = 0
    5.54 +            out.append(repr(key))
    5.55 +            if value:
    5.56 +                out.append(" : ")
    5.57 +                out.append(repr(value))
    5.58 +        out.append("}")
    5.59 +        return "".join(out)
    5.60 +
    5.61 +    def __iter__(self):
    5.62 +        return iter(self.keys())
    5.63 +
    5.64 +    def __nonzero__(self):
    5.65 +        return self.objects != {}
    5.66 +
    5.67 +    # List methods.
    5.68 +
    5.69 +    def __add__(self, other):
    5.70 +        obj = ObjectSet(self)
    5.71 +        for key in other:
    5.72 +            obj.add(key)
    5.73 +        return obj
    5.74 +
    5.75 +    # Set membership and comparisons.
    5.76 +
    5.77 +    def __hash__(self):
    5.78 +        l = self.keys()
    5.79 +        l.sort()
    5.80 +        return hash(tuple(l))
    5.81 +
    5.82 +    def __eq__(self, other):
    5.83 +        if hasattr(other, "objects"):
    5.84 +            return self.objects == other.objects
    5.85 +        else:
    5.86 +            return set(self.objects.keys()) == set(other)
    5.87 +
    5.88 +    # Set methods.
    5.89 +
    5.90 +    def add(self, obj):
    5.91 +        if not self.has_key(obj):
    5.92 +            self[obj] = set()
    5.93 +
    5.94 +    def issubset(self, other):
    5.95 +        return set(self).issubset(other)
    5.96 +
    5.97 +    # Dictionary and related methods.
    5.98 +
    5.99 +    def __getitem__(self, key):
   5.100 +        return self.objects[key]
   5.101 +
   5.102 +    def get(self, key, default=None):
   5.103 +        return self.objects.get(key, default)
   5.104 +
   5.105 +    def __setitem__(self, key, value):
   5.106 +        self.objects[key] = value
   5.107 +
   5.108 +    def has_key(self, key):
   5.109 +        return self.objects.has_key(key)
   5.110 +
   5.111 +    def keys(self):
   5.112 +        return self.objects.keys()
   5.113 +
   5.114 +    def values(self):
   5.115 +        return self.objects.values()
   5.116 +
   5.117 +    def items(self):
   5.118 +        return self.objects.items()
   5.119 +
   5.120 +    def update(self, other):
   5.121 +
   5.122 +        # Combining dictionary-like objects involves combining values.
   5.123 +
   5.124 +        if hasattr(other, "items"):
   5.125 +            for key, value in other.items():
   5.126 +                self[key] = add_sets(value, self.get(key, []))
   5.127 +
   5.128 +        # Combining sequence-like objects involves just adding members.
   5.129 +
   5.130 +        else:
   5.131 +            for key in other:
   5.132 +                self.add(key)
   5.133 +
   5.134 +    def merge(self, other):
   5.135 +
   5.136 +        """
   5.137 +        Merge this object set with an 'other' set, combining the values where
   5.138 +        possible, and incorporating values present in only one of the sets.
   5.139 +        """
   5.140 +
   5.141 +        return combine(self, other, ObjectSet(), add_sets)
   5.142 +
   5.143 +def deepen_mapping_dict(d):
   5.144 +
   5.145 +    "Convert the values of 'd' to be elements of a potentially larger set."
   5.146 +
   5.147 +    new_dict = {}
   5.148 +    for key, value in d.items():
   5.149 +        if value is None:
   5.150 +            new_dict[key] = None
   5.151 +        else:
   5.152 +            new_dict[key] = ObjectSet([value])
   5.153 +    return new_dict
   5.154 +
   5.155 +def merge_mapping_dicts(dicts):
   5.156 +
   5.157 +    "Merge the given 'dicts' mapping keys to sets of objects."
   5.158 +
   5.159 +    new_dict = {}
   5.160 +    update_mapping_dict(new_dict, dicts)
   5.161 +    return new_dict
   5.162 +
   5.163 +def update_mapping_dict(new_dict, dicts):
   5.164 +
   5.165 +    """
   5.166 +    Update 'new_dict' with the contents of the set dictionary 'dicts'.
   5.167 +    None entries may be incorporated into object sets. For example:
   5.168 +
   5.169 +    d1: {'a' : None}
   5.170 +    d2: {'a' : x}
   5.171 +    ->  {'a' : {None, x}}
   5.172 +
   5.173 +    Here, None might be used to represent an empty set and x may be an existing
   5.174 +    set.
   5.175 +    """
   5.176 +
   5.177 +    for old_dict in dicts:
   5.178 +        for key, value in old_dict.items():
   5.179 +
   5.180 +            # Add existing mappings within an object set.
   5.181 +
   5.182 +            if not new_dict.has_key(key):
   5.183 +                if value is not None:
   5.184 +                    new_dict[key] = ObjectSet(value)
   5.185 +                else:
   5.186 +                    new_dict[key] = None
   5.187 +            elif new_dict[key] is not None:
   5.188 +                if value is not None:
   5.189 +                    new_dict[key].update(value)
   5.190 +                else:
   5.191 +                    new_dict[key].add(value)
   5.192 +            else:
   5.193 +                if value is not None:
   5.194 +                    objset = new_dict[key] = ObjectSet(value)
   5.195 +                    objset.add(None)
   5.196 +
   5.197 +def combine_mapping_dicts(d1, d2):
   5.198 +
   5.199 +    """
   5.200 +    Combine dictionaries 'd1' and 'd2' in a resulting dictionary, with the
   5.201 +    values of the contributing dictionaries being themselves combined such that
   5.202 +    a "product" of the values for a given key are stored in the combined
   5.203 +    dictionary.
   5.204 +
   5.205 +    For example:
   5.206 +
   5.207 +    d1: {'a' : [{'f', 'g'}, {'f', 'h'}], ...}
   5.208 +    d2: {'a' : [{'f'}, {'e', 'f', 'g'}], ...}
   5.209 +    ->  {'a' : [{'f', 'g'}, {'f', 'h'}, {'e', 'f', 'g'}, {'e', 'f', 'g', 'h'}], ...}
   5.210 +
   5.211 +    Note that items of 'd2' whose keys are not in 'd1' are not added to 'd1'
   5.212 +    since this, in the context of propagating attribute usage observations,
   5.213 +    would result in spurious usage details being made available in places where
   5.214 +    the names may not have been defined.
   5.215 +    """
   5.216 +
   5.217 +    return combine(d1, d2, {}, combine_object_set_lists, True)
   5.218 +
   5.219 +def combine(d1, d2, combined, combine_op, only_d1_keys=False):
   5.220 +
   5.221 +    """
   5.222 +    Combine dictionaries 'd1' and 'd2' in the 'combined' object provided, using
   5.223 +    the 'combine_op' to merge values from the dictionaries.
   5.224 +
   5.225 +    If 'only_d1_keys' is set to a true value, items from 'd2' employing keys not
   5.226 +    in 'd1' will not be added to 'd1'.
   5.227 +    """
   5.228 +
   5.229 +    if d2 is not None:
   5.230 +        d2_keys = d2.keys()
   5.231 +
   5.232 +        for key in d2_keys:
   5.233 +            if not d1.has_key(key):
   5.234 +                if not only_d1_keys:
   5.235 +                    combined[key] = d2[key]
   5.236 +            else:
   5.237 +                combined[key] = combine_op(d1[key], d2[key])
   5.238 +    else:
   5.239 +        d2_keys = ()
   5.240 +
   5.241 +    for key in d1.keys():
   5.242 +        if key not in d2_keys:
   5.243 +            combined[key] = d1[key]
   5.244 +
   5.245 +    return combined
   5.246 +
   5.247 +def combine_object_set_lists(l1, l2):
   5.248 +
   5.249 +    """
   5.250 +    Combine lists of object sets 'l1' and 'l2' to make a product of their
   5.251 +    members.
   5.252 +    """
   5.253 +
   5.254 +    # If either list is undefined (indicated by None), return the defined list,
   5.255 +    # or return None if neither is defined.
   5.256 +
   5.257 +    if l1 is None:
   5.258 +        if l2 is None:
   5.259 +            return None
   5.260 +        else:
   5.261 +            return l2 + []
   5.262 +    elif l2 is None:
   5.263 +        return l1 + []
   5.264 +
   5.265 +    combined = ObjectSet()
   5.266 +    for i1 in l1:
   5.267 +        for i2 in l2:
   5.268 +            combined.add(i1.merge(i2))
   5.269 +    return combined
   5.270 +
   5.271 +def add_sets(s1, s2):
   5.272 +    return set(list(s1) + list(s2))
   5.273 +
   5.274 +# vim: tabstop=4 expandtab shiftwidth=4
     6.1 --- a/micropython/program.py	Tue Jun 26 23:49:49 2012 +0200
     6.2 +++ b/micropython/program.py	Wed Jun 27 01:02:32 2012 +0200
     6.3 @@ -3,7 +3,7 @@
     6.4  """
     6.5  Program code and data representations.
     6.6  
     6.7 -Copyright (C) 2009, 2011 Paul Boddie <paul@boddie.org.uk>
     6.8 +Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Paul Boddie <paul@boddie.org.uk>
     6.9  
    6.10  This program is free software; you can redistribute it and/or modify it under
    6.11  the terms of the GNU General Public License as published by the Free Software
    6.12 @@ -19,6 +19,43 @@
    6.13  this program.  If not, see <http://www.gnu.org/licenses/>.
    6.14  """
    6.15  
    6.16 +class Location:
    6.17 +
    6.18 +    """
    6.19 +    A special representation for locations which are to be compared to program
    6.20 +    objects.
    6.21 +    """
    6.22 +
    6.23 +    def __init__(self, location):
    6.24 +        self.location = location
    6.25 +
    6.26 +    def _op(self, other, op):
    6.27 +        if hasattr(other, "location"):
    6.28 +            return op(self.location, other.location)
    6.29 +        else:
    6.30 +            raise NotImplemented
    6.31 +
    6.32 +    def __eq__(self, other):
    6.33 +        return self._op(other, operator.eq)
    6.34 +
    6.35 +    def __ne__(self, other):
    6.36 +        return self._op(other, operator.ne)
    6.37 +
    6.38 +    def __lt__(self, other):
    6.39 +        return self._op(other, operator.lt)
    6.40 +
    6.41 +    def __le__(self, other):
    6.42 +        return self._op(other, operator.le)
    6.43 +
    6.44 +    def __gt__(self, other):
    6.45 +        return self._op(other, operator.gt)
    6.46 +
    6.47 +    def __ge__(self, other):
    6.48 +        return self._op(other, operator.ge)
    6.49 +
    6.50 +    def __repr__(self):
    6.51 +        return "Location(%r)" % self.location
    6.52 +
    6.53  class Block:
    6.54  
    6.55      "A code block."