Do 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']]

Ply对标记正则表达式的排序方法有点古怪,部分原因是它依赖于底层的python正则表达式库。用函数定义的令牌,如number令牌,会按照它们出现的顺序重新签名,与许多词法扫描生成器不同,Ply不会尝试执行最长的匹配。通过赋值定义的令牌——所有其他令牌类型——的优先级都低于函数,并且按照(正则表达式的(长度递减的顺序排列。

Ply手册(第4.3节(强烈建议不要对关键字标记(如AND(使用变量赋值样式,因为例如,模式r'AND'将识别ANDROGYNOUS的前三个字符,您可能会认为它是一个变量。相反,它建议使用一个具有简单模式的函数,首先将所有关键字和变量识别为简单单词,然后使用字典来识别特定的关键字。Ply手册(在我上面引用的章节中(中提供了示例代码和不太电报的解释。

最新更新