[Zope-CVS] CVS: Products/ZCTextIndex - IQueryParser.py:1.1.2.1 QueryParser.py:1.1.2.1

Guido van Rossum guido@python.org
Tue, 30 Apr 2002 16:20:15 -0400


Update of /cvs-repository/Products/ZCTextIndex
In directory cvs.zope.org:/tmp/cvs-serv17061

Added Files:
      Tag: TextIndexDS9-branch
	IQueryParser.py QueryParser.py 
Log Message:
Query Parser.

=== Added File Products/ZCTextIndex/IQueryParser.py ===
##############################################################################
#
# Copyright (c) 2001 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
# 
##############################################################################

"""Query Parser Interface."""

import Interface

class IQueryParser(Interface.Base):
    """Interface for Query Parsers."""

    def parseQuery(query):
        """Parse a query string.

        Return a parse tree (which implements IQueryParseTree).
        """

class IQueryParseTree(Interface.Base):
    """Interface for parse trees returned by parseQuery()."""

    def nodeType():
        """Return the node type.

        This is one of 'AND', 'OR', 'NOT', or 'ATOM'.
        """

    def getValue():
        """Return a node-type specific value.

        For node type:    Return:
        'AND'             a list of parse trees
        'OR'              a list of parse trees
        'NOT'             a parse tree
        'ATOM'            a string
        """


=== Added File Products/ZCTextIndex/QueryParser.py ===
##############################################################################
#
# Copyright (c) 2001 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
# 
##############################################################################

"""Query Parser."""

import re

class ParseError(Exception):
    pass

class QueryParser:

    def __init__(self):
        pass # This parser has no persistent state

    def parseQuery(self, query):
        # Lexical analysis
        tokens = re.findall(r"[()]|[^\s()]+", query)
        self.__tokens = tokens
        self.__tokens.append(None) # EOF token
        self.__index = 0
        # Syntactical analysis
        tree = self._parseOrExpr()
        self._require(None)
        return tree

    # Recursive descent parser

    def _require(self, token):
        if not self._check(token):
            t = self.__tokens[self.__index]
            raise ParseError, "Token %r required, %r found" % (token, t)

    def _check(self, token):
        if self.__tokens[self.__index] == token:
            self.__index += 1
            return 1
        else:
            return 0

    def _get(self):
        t = self.__tokens[self.__index]
        self.__index += 1
        return t

    def _parseOrExpr(self):
        L = []
        L.append(self._parseAndExpr())
        while self._check("OR"):
            L.append(self._parseAndExpr())
        if len(L) == 1:
            return L[0]
        else:
            return OrNode(L)

    def _parseAndExpr(self):
        L = []
        L.append(self._parseNotExpr())
        while self._check("AND"):
            L.append(self._parseNotExpr())
        if len(L) == 1:
            return L[0]
        else:
            return AndNode(L)

    def _parseNotExpr(self):
        if self._check("NOT"):
            return NotNode(self._parseTerm())
        else:
            return self._parseTerm()

    def _parseTerm(self):
        if self._check("("):
            tree = self._parseOrExpr()
            self._require(")")
        else:
            t = self._get()
            if t in [")", "AND", "OR", "NOT", None]:
                raise ParseError("Token %r not expected" % t)
            tree = AtomNode(t)
        return tree


class ParseTreeNode:

    _nodeType = None

    def __init__(self, value):
        self._value = value

    def nodeType(self):
        return self._nodeType

    def getValue(self):
        return self._value

class NotNode(ParseTreeNode):

    _nodeType = "NOT"

class AndNode(ParseTreeNode):

    _nodeType = "AND"

class OrNode(ParseTreeNode):

    _nodeType = "OR"

class AtomNode(ParseTreeNode):

    _nodeType = "ATOM"