[pypy-svn] rev 2536 - in pypy/trunk/src/pypy/annotation: . test

arigo at codespeak.net arigo at codespeak.net
Thu Dec 18 21:30:30 CET 2003


Author: arigo
Date: Thu Dec 18 21:30:30 2003
New Revision: 2536

Modified:
   pypy/trunk/src/pypy/annotation/annset.py
   pypy/trunk/src/pypy/annotation/model.py
   pypy/trunk/src/pypy/annotation/test/test_annset.py
Log:
Argh. Still another refactoring of annotations.
This one makes things much, much simpler.  We kind of thing now that they are
the correct level of generality that we need for the translator.



Modified: pypy/trunk/src/pypy/annotation/annset.py
==============================================================================
--- pypy/trunk/src/pypy/annotation/annset.py	(original)
+++ pypy/trunk/src/pypy/annotation/annset.py	Thu Dec 18 21:30:30 2003
@@ -1,384 +1,161 @@
 from __future__ import generators
 import types
-from model import Annotation, SomeValue, QueryArgument, ANN, ConstPredicate
-from model import immutable_types, blackholevalue, basicannotations
+from model import SomeValue, ANN, immutable_types, debugname
 
-QUERYARG = QueryArgument()
 
-
-class IDontKnow(Exception):
-    pass
+class MostGeneralValue:
+    def __nonzero__(self):
+        return False
+mostgeneralvalue = MostGeneralValue()
+
+
+class About:
+    def __init__(self):
+        # 'annotations' maps Predicates to tuples
+        #   (SomeValue, {set-of-blocks-depending-on-this-annotation})
+        self.annotations = {}
+        self.subjects = {}  # set of SomeValues we are about
+    def __repr__(self):    # debugging
+        subjs = [debugname(c) for c in self.subjects]
+        subjs.sort()
+        lines = ['About %s:' % ' and '.join(subjs)]
+        annotations = [(str(pred), value)
+                       for pred, (value, deps) in self.annotations.items()]
+        annotations.sort()
+        for name, somevalue in annotations:
+            lines.append('%15s --> %r' % (name, somevalue))
+        return '\n'.join(lines)
 
 
 class AnnotationSet:
-    """An annotation set is a (large) family of Annotations."""
+    """An annotation set is a (large) family of annotations."""
 
-    # XXX STORED AS A PLAIN LIST, THE COMPLEXITY IS PLAINLY WRONG
+    # This is basically a mapping {SomeValue(): About()}
+    # with convenient methods.
 
-    def __init__(self, annlist=basicannotations):  
-        self.annlist = list(annlist)    # List of annotations
-        self._normalized = {}  # maps SomeValues to some 'standard' one that
-                               # is shared with it
-        self.mappings_to_normalize = [self._normalized]
-
-    def getbindings(self):
-        """Return a general-purpose mapping between whatever you want and
-        SomeValues.  The SomeValues are kept normalized by the AnnotationSet."""
-        bindings = {}
-        self.mappings_to_normalize.append(bindings)
-        return bindings
-
-    def normalized(self, someval):
-        return self._normalized.get(someval, someval)
-
-    def setshared(self, someval1, someval2):
-        someval1 = self.normalized(someval1)
-        someval2 = self.normalized(someval2)
-        for mapping in self.mappings_to_normalize:
-            for key, value in mapping.items():
-                if value is someval2:
-                    mapping[key] = someval1
-        self._normalized[someval2] = someval1
-    
-    def isshared(self, someval1, someval2):
-        return self.normalized(someval1) is self.normalized(someval2)
+    def __init__(self):
+        self.about = {}
+        self.inblock = None
 
-    def normalizeann(self, ann):
-        "Normalize the annotation's arguments in-place."
-        ann.args = [self.normalized(a) for a in ann.args]
-
-    def dump(self):     # debugging
-        for ann in self.enumerate():
-            print ann
-
-    def enumerate(self):
-        """Enumerates all annotations in the heap."""
-        return iter(self.annlist)
-
-    __iter__ = enumerate
-
-    def query(self, *querylist):
-        return [matchvalue for matchanns, matchvalue in self.match(*querylist)]
-
-    def match(self, query, *querylist):
-        """ yield (matchanns, matchvalue) tuples with 'matchanns'
-        beeing a list of matching annotations and 'matchvalue' beeing
-        the queried value. """
-        self.normalizeann(query)
-        for queryann in querylist:
-            self.normalizeann(queryann)
-
-        # slightly limited implementation for ease of coding :-)
-        assert query.args.count(QUERYARG) == 1, (
-            "sorry, the algorithm is a bit too naive for this case")
-        queryarg = query.args.index(QUERYARG)
-        for ann in self._annmatches(query):
-            # does the returned match also agree with the other queries?
-            match = ann.args[queryarg]
-            depends = [ann]
-            for queryann in querylist:
-                boundquery = queryann.copy(renameargs={QUERYARG: match})
-                ann = self.findfirst(boundquery)
-                if ann is None:
-                    break
-                depends.append(ann)
-            else:
-                yield depends, match
-
-    def _annmatches(self, queryann):
-        """ yield annotations matching the given queryannotation. """
-        self.normalizeann(queryann)
-        testindices = [i for i in range(queryann.predicate.arity)
-                             if isinstance(queryann.args[i], SomeValue)]
-        for ann in self.annlist:
-            if ann.predicate == queryann.predicate:
-                for i in testindices:
-                    if ann.args[i] is not queryann.args[i]:
-                        break
-                else:
-                    yield ann
+    def enter(self, block, callback):
+        self.inblock = block
+        self.callback = callback
 
-    def findfirst(self, checkann):
-        """ return the first matching annotation.""" 
-        # note that we are usually not interested in multiple matching 
-        # annotations; e.g. killing an annotation will take care
-        # that all matching annotations are removed, and thus also 
-        # all dependencies listed on any of the duplicate annotation.
-        for ann in self._annmatches(checkann):
-            return ann  # :-)
-        else:
-            return None
+    def leave(self):
+        self.inblock = None
 
-    def findall(self, checkann):
-        """ list all matching annotations."""
-        return list(self._annmatches(checkann))
-
-    def record(self, recfunc, *args):
-        """ invoke the given 'recording' function by passing it a new 
-        Recorder instance and letting it use its modification API.  This API will
-        make sure that for the typical read/decide/write usage the read 
-        annotations are a perequisite of the later write/modification 
-        operation. Thus if the "causing" annotation gets invalidated we
-        know which "depending" annotations need to be removed. """
-        rec = Recorder(self)
-        return recfunc(rec, *args)
-
-    def kill(self, *annlist):
-        self.simplify(kill=annlist)
-
-    def simplify(self, kill=[]):
-        """Kill annotations in the 'kill' list, and normalize and remove
-        duplicates."""
-        # temporarykey() returns a tuple with all the information about
-        # the annotation; equal temporarykey() means equal annotations.
-        # Such keys are temporary because making SomeValues shared can
-        # change the temporarykey(), but this doesn't occur during
-        # one call to simplify().
-
-        def temporarykey(ann):
-            self.normalizeann(ann)
-            return ann.predicate, tuple(ann.args)
-        
-        allkeys = {}   # map temporarykeys to Annotation instances
-        for ann in self.annlist:
-            key = temporarykey(ann)
-            if key not in allkeys:  # if not duplicate
-                allkeys[key] = ann
-
-        for ann in kill:
-            key = temporarykey(ann)
-            if key in allkeys:
-                del allkeys[key]
-
-        self.annlist = allkeys.values()
-
-    def merge(self, oldcell, newcell):
-        """Update the heap to account for the merging of oldcell and newcell.
-        Return the merged cell."""
-        oldcell = self.normalized(oldcell)
-        newcell = self.normalized(newcell)
-        
-        if newcell is blackholevalue or newcell is oldcell:
-            return oldcell
-        elif oldcell is blackholevalue:
-            return newcell
-
-        # if 'oldcell' or 'newcell' is immutable, we should not
-        # modify the annotations about it.  If one of them is mutable,
-        # then we must update its annotations and return it.  As a
-        # consequence if both are mutable then we must return them both,
-        # i.e. make them shared.
-
-        mutablecells = []
-        deleting = []
-        annlist = self.annlist
-        for cell, othercell in [(oldcell, newcell), (newcell, oldcell)]:
-            if ANN.immutable[cell] not in annlist:
-                # for each mutable 'cell', kill the annotation that are
-                # talking about 'cell' but not existing for 'othercell'.
-                for ann in annlist:
-                    if cell in ann.args:
-                        otherann = ann.copy(renameargs={cell: othercell})
-                        if otherann not in annlist:
-                            deleting.append(ann)
-                mutablecells.append(cell)
-
-        if mutablecells:
-            # if there is at least one mutable cell we must return it.
-            # if there are two mutable cells we must merge them.
-            if len(mutablecells) == 2:
-                self.setshared(oldcell, newcell)
-            self.simplify(kill=deleting)
-            return self.normalized(mutablecells[0])
-        else:
-            # no mutable cell, we can create a new result cell
-            # with only the common annotations.
-            common = []
-            deleting = False  # False if annotations of oldcell
-                              #         == annotations common annotations
-            for ann in annlist:
-                if oldcell in ann.args:
-                    newann = ann.copy(renameargs={oldcell: newcell})
-                    try:
-                        i = annlist.index(newann)
-                    except ValueError:
-                        deleting = True  # this annotation about 'oldcell'
-                                         # is no longer there about 'newcell'.
-                    else:
-                        newann = annlist[i]  # existing Annotation
-                        common.append((ann, newann))
-
-            if not deleting:
-                return oldcell  # nothing must be removed from oldcell
-            else:
-                resultcell = SomeValue()  # invent a new cell
-                for oldann, newann in common:
-                    resultann = newann.copy(renameargs={newcell: resultcell})
-                    annlist.append(resultann)
-                return resultcell
-
-    def get(self, *querylist):
-        """Like query() but asserts that there is at most one answer.
-        Returns None if there isn't any answer."""
-        resultlist = self.query(*querylist)
-        assert len(resultlist) <= 1, "Confusing annotations..."
-        if resultlist:
-            return resultlist[0]
-        else:
-            return None
+    def isshared(self, someval1, someval2):
+        return self.about[someval1] is self.about[someval2]
 
-    def get_del(self, *querylist):
-        """Like get() but kills the matching annotation."""
-        resultlist = list(self.match(*querylist))
-        assert len(resultlist) <= 1, "Confusing annotations..."
-        if resultlist:
-            matchanns, matchvalue = resultlist[0]
-            self.kill(*matchanns)
-            return matchvalue
-        else:
-            return None
+    def __repr__(self):     # debugging
+        lines = ['=====  AnnotationSet  =====']
+        abouts = [(repr(somevalue), about)
+                  for somevalue, about in self.about.items()]
+        abouts.sort()
+        alreadyseen = {}
+        for name, about in abouts:
+            if about not in alreadyseen:
+                if about.annotations:  # ignore empty Abouts
+                    lines.append(repr(about))
+                alreadyseen[about] = True
+        return '\n'.join(lines)
+
+    def get(self, predicate, subject):
+        about = self._about(subject)
+        result = about.annotations.get(predicate)
+        if result:
+            answer, dependencies = result
+            if self.inblock:
+                dependencies[self.inblock] = True
+            return answer
+        else:
+            return mostgeneralvalue
+
+    def _about(self, somevalue):
+        try:
+            return self.about[somevalue]
+        except KeyError:
+            if somevalue is mostgeneralvalue:
+                raise ValueError, "Unexpected mostgeneralvalue"
+            about = self.about[somevalue] = About()
+            about.subjects[somevalue] = True
+            return about
+
+    def set(self, predicate, subject, answer=True):
+        about = self._about(subject)
+        if predicate in about.annotations:
+            raise ValueError, "There is already an annotation for %r" % subject
+        if answer is not mostgeneralvalue:
+            about.annotations[predicate] = answer, {}
+
+    def kill(self, predicate, subject):
+        about = self._about(subject)
+        if predicate in about.annotations:
+            someval, deps = about.annotations[predicate]
+            del about.annotations[predicate]
+            # perform invalidations
+            for block in deps:
+                self.callback(block)
+
+    def merge(self, oldvalue, newvalue):
+        """Update the heap to account for the merging of oldvalue and newvalue.
+        Return the merged somevalue."""
+        if newvalue is mostgeneralvalue or oldvalue is mostgeneralvalue:
+            return mostgeneralvalue
+
+        # build an About set that is the intersection of the two incoming ones
+        about1 = self._about(oldvalue)
+        about2 = self._about(newvalue)
+        about3 = About()
+        for pred in about1.annotations:
+            if pred in about2.annotations:
+                someval1, dep1 = about1.annotations[pred]
+                someval2, dep2 = about2.annotations[pred]
+                if someval1 == someval2:
+                    someval3 = someval1
+                elif (isinstance(someval1, SomeValue) and
+                      isinstance(someval2, SomeValue)):
+                    someval3 = self.merge(someval1, someval2)
+                    if someval3 is mostgeneralvalue:
+                        continue
+                else:
+                    continue   # annotation not in common
+                dep3 = dep1.copy()
+                dep3.update(dep2)
+                about3.annotations[pred] = someval3, dep3
 
-    def getconstant(self, cell):
-        """If cell is a constant, return its value; otherwise, raise IDontKnow.
-        Also accepts a None for convenience."""
-        if not cell:
-            raise IDontKnow
-        cell = self.normalized(cell)
-        for ann in self.annlist:
-            if isinstance(ann.predicate, ConstPredicate) and ann.args[0] is cell:
-                return ann.predicate.value
-        raise IDontKnow, cell
-
-    def set(self, ann):
-        """Insert the annotation into the AnnotationSet."""
-        self.normalizeann(ann)
-        self.annlist.append(ann)
-
-    def delete(self, queryann):
-        """Kill the annotations matching the pattern."""
-        matchannlist = self.findall(queryann)
-        self.simplify(kill=matchannlist)
-
-    def checktype(self, someval, checktype):
-        if isinstance(checktype, tuple):
-            for t in checktype:
-                if self.checktype(someval, t):
-                    return True
-            else:
-                return False
-        else:
-            return bool(self.query(ANN.type[someval, QUERYARG],
-                                   ANN.constant(checktype)[QUERYARG]))
+        # if 'oldvalue' or 'newvalue' is immutable, we should not
+        # modify the annotations about it.  If one of them is mutable,
+        # then we must replace its About set with 'about3'.
+        invalidatedblocks = {}
+        for value, about in [(oldvalue, about1), (newvalue, about2)]:
+            if ANN.immutable not in about.annotations:
+                # find all annotations that are removed or generalized
+                for pred, (someval, deps) in about.annotations.items():
+                    if (pred not in about3.annotations or
+                        about3.annotations[pred][0] != someval):
+                        invalidatedblocks.update(deps)
+                # patch 'value' to use the new 'about3'.
+                for sharedvalue in about.subjects:   # this includes 'value'
+                    self.about[sharedvalue] = about3
+                    about3.subjects[sharedvalue] = True
+
+        if not about3.subjects:
+            value3 = SomeValue()
+            self.about[value3] = about3
+            about3.subjects[value3] = True
+
+        # perform invalidations
+        for block in invalidatedblocks:
+            self.callback(block)
+        
+        return about3.subjects.iterkeys().next()
 
     def settype(self, someval, knowntype):
-        typeval = SomeValue()
-        self.set(ANN.type[someval, typeval])
-        self.set(ANN.constant(knowntype)[typeval])
+        self.set(ANN.type, someval, knowntype)
         if knowntype in immutable_types:
-            self.set(ANN.immutable[someval])
+            self.set(ANN.immutable, someval)
 
     def copytype(self, oldcell, newcell):
-        for typecell in self.query(ANN.type[oldcell, QUERYARG]):
-            self.set(ANN.type[newcell, typecell])
-        if self.findfirst(ANN.immutable[oldcell]):
-            self.set(ANN.immutable[newcell])
-
-    def newconstant(self, value):
-        cell = SomeValue()
-        self.set(ANN.constant(value)[cell])
-        return cell
-
-'''
-class XXXTransaction:
-    """A transaction contains methods to look for annotations in the
-    AnnotationHeap and create new annotations accordingly.  Each
-    Transaction instance records which Annotations were needed, which
-    allows dependencies to be tracked."""
-
-    def __init__(self, heap):
-        self.heap = heap
-        self.using_annotations = []  # annotations that we have used
-
-    def _list_annotations(self, opname, args):
-        # patch(arglist) -> arglist with None plugged where
-        #                   there is a None in the input 'args'
-        def patch(arglist):
-            return arglist
-        for i in range(len(args)):
-            if args[i] is None:
-                def patch(arglist, prevpatch=patch, i=i):
-                    arglist = prevpatch(arglist)[:]
-                    arglist[i] = None
-                    return arglist
-        
-        matchann = []
-        for ann in self.heap.annlist:
-            if ann.opname == opname and patch(ann.args) == args:
-                matchann.append(ann)
-        return matchann
-
-    def get(self, opname, args):
-        """Return the Cell with the annotation 'opname(args) -> Cell',
-        or None if there is no such annotation or several different ones.
-        Hack to generalize: a None in the args matches anything."""
-        matchann = self._list_annotations(opname, args)
-        if not matchann:
-            return None
-        else:
-            result = matchann[0].result
-            for ann in matchann[1:]:
-                if result != ann.result:
-                    return None   # conflicting results
-            for ann in matchann:
-                self.using(ann)
-            return result
-
-    def delete(self, opname, args):
-        """Kill the annotations 'opname(args) -> *'."""
-        matchann = self._list_annotations(opname, args)
-        self.heap.simplify(kill=matchann)
-
-    def set(self, opname, args, result):
-        """Put a new annotation into the AnnotationHeap."""
-        ann = Annotation(opname, args, result)
-        for prev in self.using_annotations:
-            prev.forward_deps.append(ann)
-        self.heap.annlist.append(ann)
-
-    def get_type(self, cell):
-        """Get the type of 'cell', as specified by the annotations, or None.
-        Returns None if cell is None."""
-        if cell is None:
-            return None
-        assert isinstance(cell, XCell)
-        c = self.get('type', [cell])
-        if isinstance(c, XConstant):
-            return c.value
-        else:
-            return None
-
-    def set_type(self, cell, type):
-        """Register an annotation describing the type of the object 'cell'."""
-        self.set('type', [cell], XConstant(type))
-        if type in immutable_types:
-            self.set('immutable', [], cell)
-
-    def using(self, ann):
-        """Mark 'ann' as used in this transaction."""
-        self.using_annotations.append(ann)
-
-
-immutable_types = {
-    int: True,
-    long: True,
-    tuple: True,
-    str: True,
-    bool: True,
-    types.FunctionType: True,
-    }
-
-if __name__ == '__main__':
-    val1, val2, val3 = SomeValue(), SomeValue(), SomeValue()
-    annset = AnnotationSet()
-
-'''
+        self.set(ANN.type, newcell, self.get(ANN.type, oldcell))
+        self.set(ANN.immutable, newcell, self.get(ANN.immutable, oldcell))

Modified: pypy/trunk/src/pypy/annotation/model.py
==============================================================================
--- pypy/trunk/src/pypy/annotation/model.py	(original)
+++ pypy/trunk/src/pypy/annotation/model.py	Thu Dec 18 21:30:30 2003
@@ -1,82 +1,40 @@
 import types
 
 class SomeValue:
-    pass
-
-class QueryArgument:
-    pass
+    def __repr__(self):
+        return debugname(self)
 
 class Predicate:
-    def __init__(self, debugname, arity):
+    def __init__(self, debugname):
         self.debugname = debugname
-        self.arity = arity
-    def __getitem__(self, args):
-        if self.arity == 1:
-            args = (args,)
-        return Annotation(self, *args)
     def __str__(self):
         return self.debugname
 
-class ConstPredicate(Predicate):
-    def __init__(self, value):
-        Predicate.__init__(self, 'const%s' % value, 1)
-        self.value = value
-    def __eq__(self, other):
-        return self.__class__ is other.__class__ and self.value == other.value
-    def __ne__(self, other):
-        return not (self == other)
-    def __hash__(self):
-        return hash(self.value)
+class PredicateFamily:
+    def __init__(self, familyname):
+        self.familyname = familyname
+        self.instances = {}
+    def __getitem__(self, index):
+        try:
+            return self.instances[index]
+        except KeyError:
+            name = '%s[%r]' % (self.familyname, index)
+            pred = self.instances[index] = Predicate(name)
+            return pred
 
 class ANN:
-    add = Predicate('add', 3)
-    len = Predicate('len', 2)
-    getitem = Predicate('getitem', 3)
-    neg = Predicate('neg', 2)
-    constant = ConstPredicate
-    type = Predicate('type', 2)
-    immutable = Predicate('immutable', 1)
-
-class Annotation:
-    """An Annotation asserts something about SomeValues.  
-       It is a Predicate applied to some arguments. """
-    
-    def __init__(self, predicate, *args):
-        self.predicate = predicate      # the operation or predicate
-        self.args      = list(args)     # list of SomeValues
-        assert len(args) == predicate.arity
-        # note that for predicates that are simple operations like
-        # op.add, the result is stored as the last argument.
-        for someval in args:
-            assert isinstance(someval, (SomeValue, QueryArgument,
-                                        type(Ellipsis)))     # bug catcher
-
-    def copy(self, renameargs={}):
-        args = [renameargs.get(arg, arg) for arg in self.args]
-        return Annotation(self.predicate, *args)
-
-    def __repr__(self):
-        return "<ann %s[%s]>" % (
-                self.predicate, ", ".join(map(debugname, self.args)))
-
-    def __eq__(self, other):
-        return (self.__class__ is other.__class__ and
-                self.predicate == other.predicate and
-                self.args == other.args)
+    len       = Predicate('len')
+    listitems = Predicate('listitems')
+    tupleitem = PredicateFamily('tupleitem')
+    type      = Predicate('type')
+    immutable = Predicate('immutable')
 
-    def __ne__(self, other):
-        return not (self == other)
 
 def debugname(someval, _seen = {}):
     """ return a simple name for a SomeValue. """
     try:
         return _seen[id(someval)]
     except KeyError:
-        if not _seen:
-            for name, value in globals().items():
-                if isinstance(value, SomeValue):
-                    _seen[id(value)] = name
-            return debugname(someval)
         name = "V%d" % len(_seen)
         _seen[id(someval)] = name
         return name
@@ -90,22 +48,3 @@
     slice: 'slice',
     types.FunctionType: 'function',
     }
-
-# a conventional value for representing 'all Annotations match this one'
-# or, equivalently, something for which it is currently impossible to exist
-# (when it will exist later it will have less annotations).
-blackholevalue = Ellipsis
-
-# a few values representing 'any value of the given type'
-# the following loops creates intvalue, strvalue, etc.
-basicannotations = []
-for _type, _name in immutable_types.items():
-    _val = globals()['%svalue' % _name] = SomeValue()
-    _tval = SomeValue()
-    basicannotations.append(ANN.type[_val, _tval])
-    basicannotations.append(ANN.constant(_type)[_tval])
-    basicannotations.append(ANN.immutable[_val])
-
-# 'any immutable value'
-immutablevalue = SomeValue()
-basicannotations.append(ANN.immutable[immutablevalue])

Modified: pypy/trunk/src/pypy/annotation/test/test_annset.py
==============================================================================
--- pypy/trunk/src/pypy/annotation/test/test_annset.py	(original)
+++ pypy/trunk/src/pypy/annotation/test/test_annset.py	Thu Dec 18 21:30:30 2003
@@ -2,313 +2,196 @@
 import autopath
 from pypy.tool import test
 
-from pypy.annotation.model import ANN, SomeValue, blackholevalue
-from pypy.annotation.annset import AnnotationSet, QUERYARG, IDontKnow
+from pypy.annotation.model import SomeValue, ANN, Predicate
+from pypy.annotation.annset import AnnotationSet, mostgeneralvalue
 
 
 c1,c2,c3,c4 = SomeValue(), SomeValue(), SomeValue(), SomeValue()
+c5,c6,c7,c8 = SomeValue(), SomeValue(), SomeValue(), SomeValue()
+
+def annset(*args, **kwds):
+    "This constructor is just a convenient hack for the tests."
+    annset = AnnotationSet()
+    groups = []
+    for a in args:
+        if isinstance(a, Predicate):
+            groups.append([a])
+        else:
+            groups[-1].append(a)    # hack hack hack
+    for args in groups:
+        annset.set(*args)
+    if 'links' in kwds:
+        links = kwds['links']
+        for i in range(0, len(links), 2):
+            if annset.about.get(links[i]) != annset.about.get(links[i+1]):
+                assert links[i] not in annset.about
+                about = annset.about[links[i]] = annset.about[links[i+1]]
+                about.subjects[links[i]] = True
+    return annset
 
 
 class TestAnnotationSet(test.IntTestCase):
-    def assertSameSet(self, annset, annotations):
-        for ann in annotations:
-            annset.normalizeann(ann)
-        a = list(annset)
-        b = annotations
-        # try to reorder a to match b, without failing if the lists
-        # are different -- this will be checked by assertEquals()
-        for i in range(len(b)):
-            try:
-                j = i + a[i:].index(b[i])
-            except ValueError:
-                pass
-            else:
-                a[i], a[j] = a[j], a[i]
-        self.assertEquals(a, b)
-
-    def assertSameCells(self, annset, *cells):
-        cells = [annset.normalized(c) for c in cells]
-        for c in cells[1:]:
-            self.assertEquals(cells[0], c)
-
-    def test_isshared(self):
-        a = AnnotationSet()
-        self.assert_(a.isshared(c1, c1))
-        self.failIf(a.isshared(c1, c2))
-        a.setshared(c1, c2)
-        self.assert_(a.isshared(c1, c2))
-        self.assert_(a.isshared(c2, c1))
-        self.failIf(a.isshared(c1, c3))
-        a.setshared(c2, c3)
-        self.assert_(a.isshared(c1, c3))
-        self.assert_(a.isshared(c2, c3))
-        self.assert_(a.isshared(c3, c1))
-
-    def test_normalizeann(self):
-        a = AnnotationSet()
-        ann1 = ANN.add[c1,c2,c3]
-        ann2 = ANN.add[c4,c2,c3]
-        a.setshared(c1,c4)
-        a.normalizeann(ann1)
-        a.normalizeann(ann2)
-        self.assertEquals(ann1, ann2)
+
+    def assertSameSet(self, annset1, annset2):
+        self.assertEquals(repr(annset1), repr(annset2))
     
-    def test_query_one_annotation_arg(self):
-        lst = [ANN.add[c1, c3, c2]]
-        a = AnnotationSet(lst)
-        clist = a.query(ANN.add[c1, c3, QUERYARG])
-        self.assertEquals(clist, [c2])
-        clist = a.query(ANN.add[c1, QUERYARG, c2])
-        self.assertEquals(clist, [c3])
-        clist = a.query(ANN.add[QUERYARG, c3, c2])
-        self.assertEquals(clist, [c1])
-
-        clist = a.query(ANN.add[QUERYARG, c1, c2])
-        self.assertEquals(clist, [])
-
-    def test_query_multiple_annotations(self):
-        lst = [
-            ANN.add[c1, c3, c2],
-            ANN.type[c2, c3],
-        ]
-        a = AnnotationSet(lst)
-        clist = a.query(ANN.add[c1, c3, QUERYARG],
-                        ANN.type[QUERYARG, c3])
-        self.assertEquals(clist, [c2])
-
-    def test_constant(self):
-        lst = [
-            ANN.constant(42)[c1],
-        ]
-        a = AnnotationSet(lst)
-        clist = a.query(ANN.constant(42)[QUERYARG])
-        self.assertEquals(clist, [c1])
-
-    def test_newconstant(self):
-        a = AnnotationSet([])
-        c = a.newconstant(42)
-        self.assertSameSet(a, [ANN.constant(42)[c]])
-
-    def test_getconstant(self):
-        lst = [
-            ANN.constant(42)[c1],
-        ]
-        a = AnnotationSet(lst)
-        self.assertEquals(a.getconstant(c1), 42)
-        self.assertRaises(IDontKnow, a.getconstant, c2)
-
-    def test_query_blackholevalue(self):
-        lst = [
-            ANN.add[c1, c3, c2],
-            ANN.add[c1, c2, c4],
-            ANN.type[c2, c4],
-            ANN.type[c2, c3],
-        ]
-        a = AnnotationSet(lst)
-        clist = a.query(ANN.add[c1, ..., QUERYARG])
-        clist.sort()
-        expected = [c2, c4]
-        expected.sort()
-        self.assertEquals(clist, expected)
-        clist = a.query(ANN.add[c2, ..., QUERYARG])
-        self.assertEquals(clist, [])
-        clist = a.query(ANN.type[c2, QUERYARG],
-                        ANN.add[c1, QUERYARG, ...])
-        self.assertEquals(clist, [c3])
-
-    def test_simplify(self):
-        lst = [ANN.add[c1, c3, c2],
-               ANN.add[c1, c2, c2],
-               ANN.neg[c2, c3]]
-        a = AnnotationSet(lst)
-        a.simplify()
-        self.assertSameSet(a, lst)
-        
-        a.setshared(c2, c3)
-        a.simplify()
-        self.assertSameSet(a, lst[1:])
+    def assertSameCells(self, annset, firstcell, *cells):
+        for cell in cells:
+            self.assert_(annset.isshared(firstcell, cell))
+
+    def test_trivial(self):
+        a1 = annset(ANN.len, c1, c2,
+                    ANN.type, c2, int)
+        a2 = annset(ANN.len, c1, c2,
+                    ANN.type, c2, int)
+        self.assertSameSet(a1, a2)
+
+    def test_get(self):
+        a1 = annset(ANN.len, c1, c2,
+                    ANN.type, c2, int)
+        self.assertEquals(a1.get(ANN.len, c1), c2)
+        self.assertEquals(a1.get(ANN.len, c2), mostgeneralvalue)
+
+    def test_set(self):
+        a1 = annset(ANN.len, c1, c2,
+                    ANN.type, c2, int)
+        a1.set(ANN.len, c2, c3)
+        self.assertSameSet(a1,
+             annset(ANN.len, c1, c2,
+                    ANN.type, c2, int,
+                    ANN.len, c2, c3))
 
     def test_kill(self):
-        ann1 = ANN.add[c1, c3, c2]
-        lst = [ann1,
-               ANN.add[c1, c2, c2],
-               ANN.neg[c2, c3]]
-        a = AnnotationSet(lst)
-        a.kill(ann1)
-        self.assertSameSet(a, lst[1:])
-
-    def test_merge_blackholevalue(self):
-        lst = [ANN.add[c1, c3, c2],
-               ANN.neg[c2, c3]]
-        a = AnnotationSet(lst)
-        # (c3) inter (all annotations) == (c3)
-        c = a.merge(c3, blackholevalue)
-        self.assertEquals(c, c3)
-        self.assertSameSet(a, lst)
+        a1 = annset(ANN.len, c1, c2,
+                    ANN.type, c2, int)
+        for i in range(2):
+            a1.kill(ANN.len, c1)
+            self.assertSameSet(a1,
+                 annset(ANN.type, c2, int))
+
+    def test_merge_mostgeneralvalue(self):
+        a1 = annset(ANN.len, c1, c2,
+                    ANN.type, c2, int)
+        a2 = annset(ANN.len, c1, c2,
+                    ANN.type, c2, int)
+        # (c3) inter (mostgeneralvalue) == (mostgeneralvalue)
+        c = a1.merge(c3, mostgeneralvalue)
+        self.assertEquals(c, mostgeneralvalue)
+        self.assertSameSet(a1, a2)
 
     def test_merge_mutable1(self):
-        lst = [ANN.type[c1, c3],
-               ANN.type[c2, c3],
-               ANN.add[c2, c3, c3]]
-        a = AnnotationSet(lst)
-        # (c1) inter (c2) == (c1 shared with c2)
-        c = a.merge(c1, c2)
-        self.assertSameCells(a, c, c1, c2)
-        self.assertSameSet(a, [ANN.type[c, c3]])
+        a1 = annset(ANN.len, c1, c2,
+                    ANN.len, c3, c2)
+        a2 = annset(ANN.len, c1, c2, links=[c3,c1])
+        # (c1) inter (c3) == (c1 shared with c3)
+        c = a1.merge(c1, c3)
+        self.assertSameCells(a1, c, c1, c3)
+        self.assertSameSet(a1, a2)
 
     def test_merge_mutable2(self):
-        lst = [ANN.type[c1, c3],
-               ANN.type[c2, c3],
-               ANN.add[c1, c3, c3]]
-        a = AnnotationSet(lst)
-        # (c1) inter (c2) == (c1 shared with c2)
-        c = a.merge(c1, c2)
-        self.assertSameCells(a, c, c1, c2)
-        self.assertSameSet(a, [ANN.type[c, c3]])
+        a1 = annset(ANN.len, c1, c2,
+                    ANN.len, c3, c2,
+                    ANN.type, c1, list,
+                    ANN.type, c2, str)
+        a2 = annset(ANN.len, c1, c2,
+                    ANN.type, c2, str,
+                    links=[c3,c1])
+        # (c1) inter (c3) == (c1 shared with c3)
+        c = a1.merge(c1, c3)
+        self.assertSameCells(a1, c, c1, c3)
+        self.assertSameSet(a1, a2)
 
     def test_merge_immutable1(self):
-        lst = [ANN.type[c1, c3],
-               ANN.type[c2, c3],
-               ANN.immutable[c1],
-               ANN.immutable[c2],
-               ANN.add[c2, c3, c3]]
-        a = AnnotationSet(lst)
-        # (c1) inter (c2) == (c1)
-        c = a.merge(c1, c2)
-        self.assertSameCells(a, c, c1)
-        self.failIf(a.isshared(c1, c2))
-        self.assertSameSet(a, lst)
+        a1 = annset(ANN.len, c1, c2,
+                    ANN.immutable, c1,
+                    ANN.len, c3, c2,
+                    ANN.immutable, c3)
+        # (c1) inter (c3) == (some new c)
+        c = a1.merge(c1, c3)
+        a2 = annset(ANN.len, c1, c2,
+                    ANN.immutable, c1,
+                    ANN.len, c3, c2,
+                    ANN.immutable, c3,
+                    ANN.len, c, c2,
+                    ANN.immutable, c)
+        self.assertSameSet(a1, a2)
 
     def test_merge_immutable2(self):
-        lst = [ANN.type[c1, c3],
-               ANN.type[c2, c3],
-               ANN.immutable[c1],
-               ANN.immutable[c2],
-               ANN.add[c1, c3, c3]]
-        a = AnnotationSet(lst)
-        # (c1) inter (c2) == (some new c)
-        c = a.merge(c1, c2)
-        self.failIf(a.isshared(c, c1))
-        self.failIf(a.isshared(c, c2))  # maybe not needed, but we check that
-        self.failIf(a.isshared(c1, c2))
-        lst += [ANN.type[c, c3],
-                ANN.immutable[c]]
-        self.assertSameSet(a, lst)
-
-    def test_merge_mutable_ex(self):
-        lst = [ANN.add[c1, c2, c2],
-               ANN.neg[c2, c1],
-               ANN.add[c3, c2, c2],
-               ANN.immutable[c2]]
-        a = AnnotationSet(lst)
-        # (c1) inter (c3) == (c1 shared with c3)
-        c = a.merge(c1, c3)
-        self.assertSameCells(a, c, c1, c3)
-        self.assertSameSet(a, [lst[0], lst[3]])
-        self.assertSameSet(a, [lst[2], lst[3]])
-
-    def test_merge_immutable_ex(self):
-        lst = [ANN.add[c1, c2, c2],
-               ANN.neg[c2, c1],
-               ANN.add[c3, c2, c2],
-               ANN.immutable[c1],
-               ANN.immutable[c2],
-               ANN.immutable[c3]]
-        a = AnnotationSet(lst)
+        a1 = annset(ANN.len, c1, c2,
+                    ANN.immutable, c1,
+                    ANN.len, c3, c2,
+                    ANN.immutable, c3,
+                    ANN.type, c1, list,
+                    ANN.type, c2, str)
         # (c1) inter (c3) == (some new c)
-        c = a.merge(c1, c3)
-        self.failIf(a.isshared(c, c1))
-        self.failIf(a.isshared(c, c3))
-        self.failIf(a.isshared(c1, c3))
-        lst += [ANN.add[c, c2, c2],
-                ANN.immutable[c]]
-        self.assertSameSet(a, lst)
-
-##    def dont_test_merge_mutable_ex(self):
-##        # This test is expected to fail at this point because the algorithms
-##        # are not 100% theoretically correct, but probably quite good and
-##        # clear enough right now.  In theory in the intersection below
-##        # 'add' should be kept.  In practice the extra 'c3' messes things
-##        # up.  I can only think about much-more-obscure algos to fix that.
-##        lst = [ANN.add', [c1, c3], c2),
-##               ANN.neg', [c2], c1),
-##               ANN.add', [c3, c3], c2),
-##               ANN.immutable', [], c2)]
-##        a = AnnotationHeap(lst)
-##        # (c1) inter (c3) == (c1 shared with c3)
-##        c = a.merge(c1, c3)
-##        self.assertEquals(c, c1)
-##        self.assertEquals(c, c3)
-##        self.assertEquals(c1, c3)
-##        self.assertSameSet(a, [lst[0], lst[3]])
-##        self.assertSameSet(a, [lst[2], lst[3]])
-
-##    def dont_test_merge_immutable_ex(self):
-##        # Disabled -- same as above.
-##        lst = [ANN.add', [c1, c3], c2),
-##               ANN.neg', [c2], c1),
-##               ANN.add', [c3, c3], c2),
-##               ANN.immutable', [], c1),
-##               ANN.immutable', [], c2),
-##               ANN.immutable', [], c3)]
-##        a = AnnotationHeap(lst)
-##        # (c1) inter (c3) == (some new c4)
-##        c = a.merge(c1, c3)
-##        self.failIfEqual(c, c1)
-##        self.failIfEqual(c, c3)
-##        lst += [ANN.add', [c, c3], c2),
-##                ANN.immutable', [], c)]
-##        self.assertSameSet(a, lst)
-
-    def test_set_kill(self):
-        lst = [
-            ANN.add[c1, c3, c2],
-            ANN.type[c1, c4],
-            ANN.constant(int)[c4],
-        ]
-        a = AnnotationSet(lst)
-        a.set(ANN.type[c1, c3])
-        lst += [ANN.type[c1, c3]]
-        self.assertSameSet(a, lst)
-
-        a.kill(lst[0])
-        del lst[0]
-        self.assertSameSet(a, lst)
-
-    def test_type(self):
-        lst = [
-            ANN.add[c1, c3, c2],
-            ANN.type[c1, c4],
-            ANN.constant(int)[c4],
-        ]
-        a = AnnotationSet(lst)
-        self.assert_(a.checktype(c1, int))
-        self.assert_(a.checktype(c1, (int, long)))
-        self.failIf(a.checktype(c1, str))
-        a.settype(c2, str)
-        self.assert_(a.query(ANN.type[c2, QUERYARG],
-                             ANN.constant(str)[QUERYARG]))
-
-    def test_delete(self):
-        lst = [
-            ANN.add[c1, c3, c2],
-            ANN.type[c1, c4],
-            ANN.constant(int)[c4],
-        ]
-        a = AnnotationSet(lst)
-        a.delete(ANN.add[c1, c3, ...])
-        self.assertSameSet(a, lst[1:])
-
-    def test_get_del(self):
-        lst = [
-            ANN.add[c1, c3, c2],
-            ANN.type[c1, c4],
-            ANN.constant(int)[c4],
-        ]
-        a = AnnotationSet(lst)
-        c = a.get_del(ANN.add[c1, c3, QUERYARG])
-        self.assertSameCells(a, c, c2)
-        self.assertSameSet(a, lst[1:])
+        c = a1.merge(c1, c3)
+        a2 = annset(ANN.len, c1, c2,
+                    ANN.immutable, c1,
+                    ANN.len, c3, c2,
+                    ANN.immutable, c3,
+                    ANN.type, c1, list,
+                    ANN.type, c2, str,
+                    ANN.len, c, c2,
+                    ANN.immutable, c)
+        self.assertSameSet(a1, a2)
+
+    def test_recursive_merge(self):
+        a1 = annset(ANN.tupleitem[2], c1, c2,
+                    ANN.immutable, c1,
+                    ANN.type, c2, list,
+                    ANN.listitems, c2, c3,
+                    ANN.type, c3, int,
+                    ANN.immutable, c3,
+                    ANN.tupleitem[2], c5, c6,
+                    ANN.immutable, c5,
+                    ANN.type, c6, list,
+                    ANN.listitems, c6, c7,
+                    ANN.type, c7, float,
+                    ANN.immutable, c7)
+        c9  = a1.merge(c1, c5)
+        c10 = a1.get(ANN.tupleitem[2], c9)
+        c11 = a1.get(ANN.listitems, c10)
+        self.assertSameCells(a1, c2, c6, c10)
+        
+        a2 = annset(ANN.tupleitem[2], c1, c2,
+                    ANN.immutable, c1,
+                    ANN.type, c3, int,
+                    ANN.immutable, c3,
+                    ANN.tupleitem[2], c5, c6,
+                    ANN.immutable, c5,
+                    ANN.type, c7, float,
+                    ANN.immutable, c7,
+
+                    ANN.tupleitem[2], c9, c10,
+                    ANN.immutable, c9,
+                    ANN.type, c10, list,
+                    ANN.listitems, c10, c11,
+                    ANN.immutable, c11,
+                    links=[c2,c10,c6,c10])
+        self.assertSameSet(a1, a2)
+
+    def test_settype(self):
+        a = annset()
+        a.settype(c1, int)
+        a.settype(c2, list)
+        self.assertSameSet(a,
+            annset(ANN.type, c1, int,
+                   ANN.immutable, c1,
+                   ANN.type, c2, list))
+
+    def test_copytype(self):
+        a = annset(ANN.type, c1, int,
+                   ANN.immutable, c1,
+                   ANN.type, c2, list)
+        a.copytype(c1, c3)
+        a.copytype(c2, c4)
+        self.assertSameSet(a,
+            annset(ANN.type, c1, int,
+                   ANN.immutable, c1,
+                   ANN.type, c2, list,
+                   ANN.type, c3, int,
+                   ANN.immutable, c3,
+                   ANN.type, c4, list))
 
 
 if __name__ == '__main__':


More information about the Pypy-commit mailing list