I've seen Ville's 'find_b' and 'find_h' which allows
xpath-like/jquery-like chainable searches of Leo body  and head
strings.  That functionality does not deal much with tree
structure--it always searches child nodes.  That is useful, but
sometimes more flexible traversal of tree structure is needed. I have
written some code that provides this flexible traversal.

Leo already has many tree iterators and in fact my code is mostly just
a thin wrapper around the already existing leo iterators.  However,
the chainability is a nice touch that makes tree traversal tasks more
pleasant.

The code is kind of long for an email, but I've included it
nonetheless.  Example uses at the bottom, but here is my favorite
example:

g.es ('\nParents of all the clones of the current position')
map (g.es, LeoTreeql(p).clones().parent().it())

The rest of the code:

import itertools

class LeoTreeql:
    def __init__(self,p):
        # The initial iterable is just the given p as a list.
        # With each step in the chain, this iterable will be
        # refined as a composition of the new iterable upon the
        # results of the old iterable
        self.i = [p]
        self.p = p

    def refine (self,f):
        # Here is the main engine which applies the new iterable to the
        # results of the previously stored iterable.  It took quite a bit of
        # experimentation to arrive at this and I don't fully understand
        # it to know whether it could be made clearer.  From my limited
        # testing, it seems to work.
        self.i = itertools.chain.from_iterable (itertools.imap (f, self.i))
        return

    def descendants (self):
        # Pass in the "subtree" method of the position class as the
        # refining iterable
        self.refine(type(self.p).subtree)
        return self

    def children (self):
        self.refine(type(self.p).children)
        return self

    def ancestors (self):
        self.refine(type(self.p).parents)
        return self

    def parent (self):
        # Returns just a single parent, but each step must be an
        # iterable, so make one.
        def parent_iter(p0):
            yield p0.parent()
            raise StopIteration
        self.refine(parent_iter)
        return self

    def following_siblings (self):
        self.refine(type(self.p).following_siblings)
        return self

    def self_and_siblings (self):
        self.refine(type(self.p).self_and_siblings)
        return self

    def root (self):
        # This is a strange one.  Leo's root is not the parent of all toplevel
        # nodes, it is simply the first toplevel node.  So walking the whole
        # tree is not q.root().descendants() and instead is
        # q.root().following_siblings().descendants()
        # Strange only because that is a different definition of root than
        # I've seen before
        def root_iter(p0):
            yield p0.findRootPosition()
        self.refine(root_iter)
        return self

    def clones(self):
        # My own all_positions that doesn't depend on 'c', since this
class only knows about 'p'
        def all_positions (p0):
            p = p0.findRootPosition()
            while p:
                yield p
                p.moveToThreadNext()
            raise StopIteration

        def clones_iter(p0):
            def isCloneOf(p1,p2=p0):
                return p1.v == p2.v

            # Finding clones of a node involves searching the whole tree
            return itertools.ifilter (isCloneOf, all_positions(p0))

        self.refine(clones_iter)
        return self

    # This is the "escape hatch" from the chaining.  This method will
    # typically be the last call in the chain
    def it(self):
        return self.i

# Looks to be working for at least a few cases
g.es('=======')

g.es('\nChildren, nieces, and nephews')
map (g.es, LeoTreeql(p).self_and_siblings().children().it())

g.es('\nAncestors of descendants of brothers and sisters')
map (g.es, LeoTreeql(p).self_and_siblings().descendants().ancestors().it())

g.es('\nChildren of parents (should be the same as self and parents)')
map (g.es, LeoTreeql(p).parent().children().it())

g.es('\nChildren of root (i.e. toplevel nodes except the first one)')
map (g.es, LeoTreeql(p).root().following_siblings().it())

g.es ('\nParents of all the clones of the current position')
map (g.es, LeoTreeql(p).clones().parent().it())

g.es ('\nCousins of current position and siblings and self')
map (g.es, LeoTreeql(p).parent().self_and_siblings().children().it())

g.es ('\nmultiple levels of empty iterators should be handled fine
(just an empty result):')
map (g.es, LeoTreeql(p).children().children().children().it())

Brian

-- 
You received this message because you are subscribed to the Google Groups 
"leo-editor" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/leo-editor?hl=en.

Reply via email to