############################################################################## # # Copyright (c) 2008-2009 Nexedi SA and Contributors. All Rights Reserved. # Vincent Pelletier <vincent@nexedi.com> # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential # consequences resulting from its eventual inadequacies and bugs # End users who are looking for a ready-to-use solution with commercial # garantees and support are strongly adviced to contract a Free Software # Service Company # # This program is Free Software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## from lexer import lexer, update_docstrings try: from Products.ZSQLCatalog.interfaces.abstract_syntax_node import INode, IValueNode, ILogicalNode, IColumnNode from Interface.Verify import verifyClass except ImportError: INode = None IValueNode = None ILogicalNode = None IColumnNode = None def verifyClass(*args, **kw): pass class Node(object): __allow_access_to_unprotected_subobjects__ = 1 __implements__ = INode def isLeaf(self): return False def isColumn(self): return False def push(self, logical_operator, node): return LogicalNode(logical_operator, self, node) verifyClass(INode, Node) class ValueNode(Node): __implements__ = IValueNode def __init__(self, value, comparison_operator=''): self.value = value self.comparison_operator = comparison_operator def isLeaf(self): return True def getComparisonOperator(self): return self.comparison_operator def getValue(self): return self.value def __repr__(self): return '<%s %r %r>' % (self.__class__.__name__, self.comparison_operator, self.value) verifyClass(INode, ValueNode) verifyClass(IValueNode, ValueNode) class NotNode(Node): __implements__ = ILogicalNode def __init__(self, node): self.node = node def getLogicalOperator(self): return 'not' def getNodeList(self): return [self.node] def __repr__(self): return '<%s %r>' % (self.__class__.__name__, self.node) verifyClass(INode, NotNode) verifyClass(ILogicalNode, NotNode) class LogicalNode(Node): __implements__ = ILogicalNode def __init__(self, logical_operator, node, other): self.logical_operator = logical_operator self.node_list = [] self._push(node) self._push(other) def getLogicalOperator(self): return self.logical_operator def getNodeList(self): return self.node_list def _push(self, node): if isinstance(node, LogicalNode) and node.logical_operator == self.logical_operator: self.node_list.extend(node.node_list) else: self.node_list.append(node) def __repr__(self): return '<%s %r %r>' % (self.__class__.__name__, self.logical_operator, self.node_list) verifyClass(INode, LogicalNode) verifyClass(ILogicalNode, LogicalNode) class ColumnNode(Node): __implements__ = IColumnNode def __init__(self, column_name, node): self.column_name = column_name self.node = node def isColumn(self): return True def getColumnName(self): return self.column_name def getSubNode(self): return self.node def __repr__(self): return '<%s %r %r>' % (self.__class__.__name__, self.column_name, self.node) verifyClass(INode, ColumnNode) verifyClass(IColumnNode, ColumnNode) class AdvancedSearchTextParser(lexer): # IMPORTANT: # In short: Don't remove any token definition below even if they look # useless. # In detail: The lex methods below are redefined here because of ply nice # feature of prioritizing tokens using the *line* *number* at which they # are defined. As we inherit those methods from another class from another # file (which doesn't match this file's content, of course) we must redefine # wrapper methods to enforce token priority. Kudos to ply for so much # customisable behaviour. Not. def t_LEFT_PARENTHESE(self, t): return lexer.t_LEFT_PARENTHESE(self, t) def t_RIGHT_PARENTHESE(self, t): return lexer.t_RIGHT_PARENTHESE(self, t) def t_OPERATOR(self, t): return lexer.t_OPERATOR(self, t) def t_STRING(self, t): return lexer.t_STRING(self, t) def t_COLUMN(self, t): if self.isColumn(t.value[:-1]): t = lexer.t_COLUMN(self, t) else: # t is a non-existing column, so it should be taken as a string prefix. t.type = 'STRING_PREFIX' return t def t_OR(self, t): return lexer.t_OR(self, t) def t_AND(self, t): return lexer.t_AND(self, t) def t_NOT(self, t): return lexer.t_NOT(self, t) def t_WORD(self, t): return lexer.t_WORD(self, t) def p_seach_text(self, p): '''search_text : and_expression | and_expression OR search_text''' if len(p) == 2: p[0] = p[1] else: p[0] = p[1].push('or', p[3]) def p_and_expression(self, p): '''and_expression : boolean_expression | boolean_expression and_expression | boolean_expression AND and_expression''' if len(p) == 2: p[0] = p[1] elif len(p) == 3: p[0] = p[1].push('and', p[2]) else: p[0] = p[1].push('and', p[3]) def p_boolean_expression(self, p): '''boolean_expression : NOT expression | expression''' if len(p) == 3: p[0] = NotNode(p[2]) else: p[0] = p[1] def p_expression(self, p): '''expression : LEFT_PARENTHESE search_text RIGHT_PARENTHESE | column | value''' if len(p) == 2: p[0] = p[1] else: p[0] = p[2] def p_column(self, p): '''column : COLUMN value_expression''' p[0] = ColumnNode(p[1], p[2]) def p_value_expression(self, p): '''value_expression : LEFT_PARENTHESE value_or_expression RIGHT_PARENTHESE | value''' if len(p) == 2: p[0] = p[1] else: p[0] = p[2] def p_value_or_expression(self, p): '''value_or_expression : value_and_expression | value_and_expression value_or_expression | value_and_expression OR value_or_expression''' if len(p) == 2: p[0] = p[1] elif len(p) == 3: p[0] = p[1].push('or', p[2]) else: p[0] = p[1].push('or', p[3]) def p_value_and_expression(self, p): '''value_and_expression : value_expression | value_expression AND value_and_expression''' if len(p) == 2: p[0] = p[1] else: p[0] = p[1].push('and', p[3]) def p_value(self, p): '''value : OPERATOR string | string''' if len(p) == 2: p[0] = ValueNode(p[1]) else: p[0] = ValueNode(p[2], comparison_operator=p[1]) def p_string(self, p): '''string : WORD | STRING | STRING_PREFIX string''' if len(p) == 3: p[0] = p[1] + p[2] else: p[0] = p[1] def __call__(self, input, is_column, *args, **kw): self.isColumn = is_column try: return self.parse(input, *args, **kw) finally: self.isColumn = None update_docstrings(AdvancedSearchTextParser)