On Thu, Jul 8, 2010 at 3:51 AM, Ville M. Vainio <[email protected]> wrote:
> On Thu, Jul 8, 2010 at 3:40 AM, Brian Theado <[email protected]> wrote:
>
> These methods could also be added to 'poslist' class that you get from
> find_h / find_b. LeoTreeql is basically the same thing with the
> difference that it doesn't inherit from list.
>

Just to play around with it, I added select_h and select_b to
LeoTreeql.  The equivalent LeoTreeql code is more explicit than
poslist but rather more verbose.  With poslist:

   c.find_h('@thin.*py').select_h('class.*').select_b('import (.*)')

should be similar to this in LeoTreeql:

  LeoTreeql(p).\
      all_positions().select_h('@thin.*py').\
      children().select_h('class.*'). \
      children().select_b('import (.*)')

I have only minimal experience with Python  (just from playing around
with Leo a few times over the years) and don't know all the tradeoffs
between lists and iterables.  I just know the itertools module and
Leo's built-in iterators made this easy to implement.

I discovered in the Python docs I could make the LeoTreeql class
itself an iterable just be renaming the 'it' method to '_iter_', so
this:

    LeoTreeql(p).clones().parent().it()

becomes:

   LeoTreeql(p).clones().parent()


Here is my latest code:

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.  After each step self.i
        # will be a flat iterable containing position elements
        self.i = [p]
        self.p = p

    def __iter__(self):
        # With this method, LeoTreeql objects themselves are iterable
        return self.i

    def mapchain (self,f):
        # This method is intended for a function f which takes a position
        # as input and outputs an iterable of positions.  That function
        # is mapped over the self.i iterable of positions.  Then
        # chain.from_iterable is used to flatten the "iterable of
        # iterables of position" to just "iterable of positions"
        self.i = itertools.chain.from_iterable (itertools.imap (f, self.i))
        return self

    def map (self, f):
        # For functions that map a single 'p' to another 'p'
        self.i = itertools.imap(f, self.i)
        return self

    def slice (self, *args):
        self.i = itertools.islice(self.i, *args)
        return self

    def filter (self, f):
        self.i = itertools.ifilter (f, self.i)
        return self

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

    def self_and_subtree (self):
        return self.mapchain(type(self.p).self_and_subtree)

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

    def parents (self):
        return self.mapchain(type(self.p).parents)
    ancestors = parents

    def parent (self):
        # Mapchain not needed here since p.parent() returns
        # just a single position and not an iterable of
        # positions
        return self.map (type(self.p).parent)

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

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

    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
        return self.map(type(self.p).findRootPosition)

    def toplevels (self):
        return self.root().self_and_siblings()

    def all_positions(self):
        return self.toplevels().self_and_subtree()

    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))

        return self.mapchain(clones_iter)

    def select_h (self, regex, flags = re.IGNORECASE):
        # Leo's select_h also stores the match object on each
        # position--not done here
        pat = re.compile(regex, flags)
        return self.filter (lambda x: re.match(pat, x.h))

    # Not working yet and I'm not sure why
    def select_b (self, regex, flags = re.IGNORECASE):
        # Leo's select_b also stores the match object iterator on
        # each position--not done here
        pat = re.compile(regex, flags)
        return self.filter (lambda x: re.match(pat, x.b))

# 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())
g.es('\nAncestors of descendants of brothers and sisters')
map (g.es, LeoTreeql(p).self_and_siblings().descendants().ancestors())
g.es('\nChildren of parents (should be the same as self and siblings)')
map (g.es, LeoTreeql(p).parent().children())
g.es('\nChildren of root (i.e. toplevel nodes except the first one)')
map (g.es, LeoTreeql(p).root().following_siblings())
g.es ('\nParents of all the clones of the current position')
map (g.es, LeoTreeql(p).clones().parent())
g.es ('\nCousins of current position and siblings and self')
map (g.es, LeoTreeql(p).parent().self_and_siblings().children())
g.es ('\nCousins of current position and siblings and self with
headline=newHeadline')
map (g.es, LeoTreeql(p).parent().self_and_siblings().children().filter(lambda
x: x.h == 'newHeadline'))
g.es ('\nmultiple levels of empty iterators should be handled fine
(just an empty result):')
map (g.es, LeoTreeql(p).children().children().children())
g.es ('\nAnother parent test')
map (g.es, LeoTreeql(p).self_and_siblings().parent())
g.es ('\nselect_h')
map (g.es, LeoTreeql(p).toplevels().descendants().select_h ('new.*').parent())
g.es ('\nselect_b')
map (g.es, LeoTreeql(p).all_positions().select_b('.*filter.*'))

-- 
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