[Zope-CVS] CVS: Packages/pypes/pypes - expression.py:1.1

Casey Duncan casey at zope.com
Thu Jan 29 00:42:09 EST 2004


Update of /cvs-repository/Packages/pypes/pypes
In directory cvs.zope.org:/tmp/cvs-serv25620

Added Files:
	expression.py 
Log Message:
Add some formative expresser types for encapsulating expression declarations.


=== Added File Packages/pypes/pypes/expression.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Pypes expression declaration objects

Expressions are declared and stored in pypes using 'Expresser' objects.  When
expresser objects are manipulated, such as by accessing attributes, items or
performing arithmetic and comparison operations, the result is a new expresser
object which records the operation that were performed.

Expresser objects are used to record expressions that are going to be applied
to objects later. For example, suppose you want to define a filter to that
will later be applied to a set of objects. Using an expresser object named
'member', you could write the following::
    
  myfilter = member.status == "pending"
  
'myfilter' now contains an expresser that can be applied to a collection of
objects to determine which objects' 'status' attribute have the value "pending".
Much more complex expressions are, of course, possible.

Expressers are characterized by one or more operations. An operation is a
single comparison or truth-test of expresser terms. Multiple operations are
separated by the bitwise operators '&' (and) and '|' (or). The Python boolean
operators 'and' and 'or' cannot be used because of their shortcut semantics. 
An expresser term is a subexpression consisting of any Python operators except
for comparison and bitwise operators. A term ends once a comparison or bitwise
operation is performed. A new term begins on the right of a bitwise operator.
Let's extend the example above to explain::
    
  myfilter = (member.status == "pending") & (member.children.has_key("photo"))

(Note that the parenthesis are necessary because Python gives comparison
operators lower precedence then bitwise operators)

In this example 'myfilter' will contain an multi-operation expresser with two
elements. The first element is an operation representing the comparison of the
'status' attribute of 'member'. The second element is a truth test of the
result of calling the 'has_key' method of the children attribute of member
(note that this 'has_key' "call" is still a declaration resulting in a new
expresser). Using the '&' bitwise operator means that both operation 
expressers must evaluate to true for an object to satisfy 'myfilter'.

The expresser terms in the above are 'member.status' and 
'member.children.has_key("photo")'. These become important in the context of
pypes indexes and views. There they are used to define the source, filter and 
act as registration to allow the query service to optimize searching and
sorting operations.

$Id: expression.py,v 1.1 2004/01/29 05:42:08 caseman Exp $"""

import types

_marker = object()


class ExpresserBase(object):
    """Common methods for all expresser types"""
    
    # Bitwise operators result in an expresser union or intersection object
    
    def __and__(self, other):
        return ExpresserIntersect(self, other)
        
    def __rand__(self, other):
        return ExpresserIntersect(other, self)
    
    def __or__(self, other):
        return ExpresserUnion(self, other)
        
    def __ror__(self, other):
        return ExpresserUnion(other, self)


class ExpresserTerm(ExpresserBase):
    """Declarative expression encapsulation. 
    
    Operations performed on expresser terms such as attribute or item access or
    comparison result in a new expresser object that captures the operation for
    later execution.
    """
    
    def __init__(
        self, expression='member', extends=None, prepends=None, parens=True):
        """Create a new expresser from the expression fragment passed.
        If extends is specified, then the expresser extends an existing
        one. If prepends is specified then the expression prepends the
        expresser"""
        assert extends is None or prepends is None, (
            'Cannot specify both extends and prepends')
        if parens:
            template = '(%s%s)'
        else:
            template = '%s%s'
        if extends is None and prepends is None:
            self.__expr = expression
            self.__locals = {}
        elif extends is not None:
            self.__expr = template % (extends._ExpresserTerm__expr, expression)
            self.__locals = extends._ExpresserTerm__locals.copy()
        elif prepends is not None:
            self.__expr = template % (expression, prepends._ExpresserTerm__expr)
            self.__locals = prepends._ExpresserTerm__locals.copy()
        
    def __str__(self):
        expression = self.__expr
        for name, value in self.__locals.items():
            expression = expression.replace(name, repr(value))
        return expression

    __repr__ = __str__        
            
    def __addLocal(self, value):
        """Add local variable of value and return the name"""
        localname = '__%d__' % len(self.__locals)
        self.__locals[localname] = value
        return localname
        
    def __extend(self, expr_template, value=_marker, parens=True):
        """Extend an expresser"""
        if value is not _marker:
            return self.__class__(
                expression=expr_template % self.__addLocal(value),
                extends=self, parens=parens)
        else:
            return self.__class__(
                expression=expr_template, extends=self, parens=parens)
        
    def __prepend(self, expr_template, value=_marker, parens=True):
        """Prepends an expresser"""
        if value is not _marker:
            return self.__class__(
                expression=expr_template % self.__addLocal(value),
                prepends=self, parens=parens)
        else:
            return self.__class__(
                expression=expr_template, prepends=self, parens=parens)
    
    def __getattr__(self, name):
        print name
        return self.__class__(
            expression='.%s' % name, extends=self, parens=False)
    
    def __getitem__(self, key):
        return self.__extend('[%s]', key, parens=False)
        
    # Binary math operators
    
    def __add__(self, other):
        return self.__extend(' + %s', other)
    
    def __radd__(self, other):
        return self.__prepend('%s + ', other)
    
    def __sub__(self, other):
        return self.__extend(' - %s', other)
    
    def __rsub__(self, other):
        return self.__prepend('%s - ', other)
        
    def __mul__(self, other):
        return self.__extend(' * %s', other)
    
    def __rmul__(self, other):
        return self.__prepend('%s * ', other)
        
    def __floordiv__(self, other):
        return self.__extend(' // %s', other)
    
    def __rfloordiv__(self, other):
        return self.__prepend('%s // ', other)
        
    def __div__(self, other):
        return self.__extend(' / %s', other)
    
    def __rdiv__(self, other):
        return self.__prepend('%s / ', other)
        
    def __truediv__(self, other):
        return self.__extend(' / %s', other)
    
    def __rtruediv__(self, other):
        return self.__prepend('%s / ', other)
    
    def __mod__(self, other):
        return self.__extend(' %% %s', other)
    
    def __rmod__(self, other):
        return self.__prepend('%s %% ', other)
    
    def __pow__(self, other):
        return self.__extend(' ** %s', other)
    
    def __rpow__(self, other):
        return self.__prepend('%s ** ', other)
    
    def __lshift__(self, other):
        return self.__extend(' << %s', other)
    
    def __rlshift__(self, other):
        return self.__prepend('%s << ', other)
    
    def __rshift__(self, other):
        return self.__extend(' >> %s', other)
    
    def __rrshift__(self, other):
        return self.__prepend('%s >> ', other)
    
    # Unary math operators
    
    def __neg__(self):
        return self.__prepend('-', parens=False)
    
    def __pos__(self):
        return self.__prepend('+', parens=False)
    
    def __invert__(self):
        return self.__prepend('~', parens=False)
        
    # Comparison operators result in an expresser operation object
    
    def __eq__(self, other):
        return ExpresserComparison(self, '==', other)
    
    def __ne__(self, other):
        return ExpresserComparison(self, '!=', other)
        
    def __lt__(self, other):
        return ExpresserComparison(self, '<', other)
        
    def __gt__(self, other):
        return ExpresserComparison(self, '>', other)
        
    def __le__(self, other):
        return ExpresserComparison(self, '<=', other)
        
    def __ge__(self, other):
        return ExpresserComparison(self, '>=', other)


class ExpresserComparison(ExpresserBase):
    """Encapsulation of expresser comparision operations"""
    
    def __init__(self, left_operand, operator, right_operand):
        self.left_operand = left_operand
        self.operator = operator
        self.right_operand = right_operand
    
    def __str__(self):
        return '%s %s %s' % (
            str(self.left_operand), self.operator, str(self.right_operand))

    # ExpresserComparisons are not themselves comparible
        
    def __eq__(self, other):
        raise RuntimeError, 'Cannot compare expresser operation objects'
    
    def __ne__(self, other):
        raise RuntimeError, 'Cannot compare expresser operation objects'
        
    def __lt__(self, other):
        raise RuntimeError, 'Cannot compare expresser operation objects'
        
    def __gt__(self, other):
        raise RuntimeError, 'Cannot compare expresser operation objects'
        
    def __le__(self, other):
        raise RuntimeError, 'Cannot compare expresser operation objects'
        
    def __ge__(self, other):
        raise RuntimeError, 'Cannot compare expresser operation objects'




More information about the Zope-CVS mailing list