Имеют ли строки AND, OR особое значение в PLY?

При использовании PLY (http://www.dabeaz.com/ply/) я заметил, что кажется очень странной проблемой: когда я использую токены типа & для соединения, программа ниже работает, но когда я использую AND в том же месте, PLY заявляет о синтаксической ошибке.

Программа:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
import os

from ply import lex

import ply.yacc as yacc

parser = None
lexer = None


def flatten_list(lst):
    flat = []
    for x in lst:
        if isinstance(x, list):
            flat.extend(flatten_list(x))
        else:
            flat.append(x)
    return flat


############## Tokenization ##############

tokens = (
    'number',
    'lparen',
    'rparen',
    'textw',
    'titlew',
    'qword',
    'AND'
)

t_lparen = r'\('
t_rparen = r'\)'
t_textw = r'TEXTW:'
t_titlew = r'TITLEW:'
t_qword = r'\w+'
t_AND = r'AND'


def t_number(t):
    r'\d+'
    t.value = int(t.value)
    return t


t_ignore = ' \t'


def t_error(t):
    raise ValueError(
        'Illegal character "{}" at position {}, query text: {}'.format(t.value[0], t.lexpos, t.lexer.lexdata))


lexer = lex.lex()



################# Parsing #################


def p_querylist_boolop(p):
    """querylist : subquery AND subquery"""
    print >> sys.stderr, 'p_querylist', list(p)
    p[0] = []
    p[0].append(p[1])
    p[0].append(p[3])


def p_subquery(p):
    """subquery : lparen querykw qwordseq rparen"""
    print >> sys.stderr, 'p_subquery', list(p)
    p[0] = flatten_list(p[3])


def p_querykw(p):
    """querykw : textw
                | titlew"""
    print >> sys.stderr, 'p_querykw', list(p)
    p[0] = p[1]


def p_qwordseq(p):
    """qwordseq : qwordseq qword
                  | qwordseq number
                  | qword
                  | number"""
    print >> sys.stderr, 'p_qwordseq', list(p)
    if p[0]:
        p[0].extend(p[1:])
    else:
        p[0] = p[1:]



def p_error(p):
    global parser
    if p:
        tok = parser.token()
        if tok:
            msg = 'Syntax error in input, token "{}" at position {}, query text: {}'.format(tok.value, tok.lexpos,
                                                                                            lexer.lexdata)
            raise ValueError(msg)
    msg = 'Syntax error at the end of input, query text: {}'.format(lexer.lexdata)
    raise ValueError(msg)


parser = yacc.yacc()
# parser = yacc.yacc(debug=0, write_tables=0)


def parse_query(q):
    return parser.parse(q)


if __name__ == '__main__':
    query_texts = ["""(TEXTW: one article) AND (TEXTW: two books)"""]
    for qt in query_texts:
        res = parse_query(qt)
        print '***', res

Это производит:

ValueError: Syntax error in input, token "(" at position 19, query text: ( TEXTW: abc ) AND ( TEXTW: aaa )

Однако, когда я меняю следующее на:

t_AND = r'&'
query_texts = ["""(TEXTW: one article) & (TEXTW: two books)"""]

.. он отлично работает:

*** [['one', 'article'], ['two', 'books']]

person LetMeSOThat4U    schedule 21.09.2016    source источник
comment
Проблема, по-видимому, в том, что строка «AND» будет соответствовать t_qword = r'\w+' в первой продукции p_qwordseq, т. е. вам нужно устранить неоднозначность вашей грамматики.   -  person Konrad Rudolph    schedule 21.09.2016


Ответы (1)


У Ply немного эксцентричный подход к упорядочению регулярных выражений токенов, отчасти потому, что он зависит от базовой библиотеки регулярных выражений Python. Токены, определенные с помощью функций, таких как ваш токен number, распознаются в том порядке, в котором они появляются, и, в отличие от многих генераторов лексических сканеров, Ply не пытается выполнить самое длинное совпадение. Токены, определенные присваиванием — все другие ваши типы токенов — имеют более низкий приоритет, чем функции, и располагаются в порядке уменьшения длины (регулярного выражения).

В руководстве по Ply (раздел 4.3) настоятельно рекомендуется не использовать стиль присвоения переменных для токенов ключевых слов, таких как AND, потому что шаблон r'AND', например, распознает первые три символа, например, ANDROGYNOUS, которые, как вы, вероятно, ожидаете, будут переменная. Вместо этого рекомендуется использовать функцию с простым шаблоном, чтобы сначала распознать все ключевые слова и переменные как простые слова, а затем использовать словарь для распознавания конкретных ключевых слов. Пример кода и менее телеграфное объяснение находятся в руководстве Ply (в разделе, который я цитировал выше).

person rici    schedule 22.09.2016