lexer.py 3.95 KB
Newer Older
1
from __future__ import print_function
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
##############################################################################
#
# 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.
#
##############################################################################

30 31 32
import six
from io import BytesIO

33 34 35 36 37 38 39
from ply import lex, yacc
import sys

try:
  from zLOG import LOG
except ImportError:
  def LOG(channel, level, message):
40
    print(message, file=sys.stderr)
41 42 43 44 45 46 47 48 49 50 51 52 53

class ParserOrLexerError(Exception):
  pass

class LexerError(ParserOrLexerError):
  pass

class ParserError(ParserOrLexerError):
  pass

class lexer(object):
  def init(self, **kw):
    debug = kw.pop('debug', False)
54
    output = sys.stdout = sys.stderr = BytesIO()
55 56 57
    self.lexer = lex.lex(object=self, **kw)
    self.parser = yacc.yacc(module=self, debug=debug,
                            debugfile="%s.out" % (self.__class__.__name__, ),
58
                            write_tables=False)
59 60 61 62 63 64 65
    sys.stdout, sys.stderr = sys.__stdout__, sys.__stderr__
    # Emit all logs with regular Zope logging
    for line in output.getvalue().split('\n'):
      if len(line):
        LOG('lexer', 0, line)

  def t_error(self, t):
66
    raise LexerError('ERROR: Invalid character %r' % (t.value[0], ))
67 68

  def p_error(self, p):
69
    raise ParserError('Syntax error in input: %r' % (p, ))
70 71 72 73 74 75 76 77 78 79 80 81 82

  def input(self, string):
    self.lexer.input(string)

  def token(self):
    return self.lexer.token()

  tokens = (
    'OR',
    'AND',
    'NOT',
    'COLUMN',
    'STRING',
83
    'STRING_PREFIX',
84 85 86
    'WORD',
    'OPERATOR',
    'LEFT_PARENTHESE',
87 88 89
    'RIGHT_PARENTHESE',
    'NULL',
  )
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114

  t_ignore = ' '

  def t_LEFT_PARENTHESE(self, t):
    r'\('
    return t

  def t_RIGHT_PARENTHESE(self, t):
    r'\)'
    return t

  def t_OPERATOR(self, t):
    r'(>=?|<=?|!?=)'
    return t

  def t_STRING(self, t):
    r'"(\\.|[^\\"])*"'
    return t

  def t_COLUMN(self, t):
    r'[^><= :\(\)"][^ :\(\)"]*:'
    t.value = t.value[:-1]
    return t

  def t_OR(self, t):
115
    r'OR[ ]'
116 117 118
    return t

  def t_AND(self, t):
119
    r'AND[ ]'
120 121 122
    return t

  def t_NOT(self, t):
123
    r'NOT[ ]'
124 125 126 127 128 129
    return t

  def t_WORD(self, t):
    r'[^><= :\(\)"][^ :\(\)"]*'
    return t

130 131 132 133 134
  def t_NULL(self, t):
    r'NULL'
    t.value = None
    return t

135 136 137 138
  def parse(self, *args, **kw):
    kw['lexer'] = self
    return self.parser.parse(*args, **kw)

139 140
  def __call__(self, input, is_column, *args, **kw):
    raise NotImplementedError
141 142

def update_docstrings(klass):
143
  super_klass = super(klass, klass)
144 145
  for property in dir(klass):
    if property.startswith('t_'):
146
      source = getattr(super_klass, property, None)
147 148 149
      if callable(source):
        destination = getattr(klass, property)
        assert callable(destination)
150
        destination = six.get_unbound_function(destination)
151
        if destination.__doc__ is None:
152
          destination.__doc__ = source.__doc__