在 pyparsing 中,当使用 infixNotation 时满足某些条件时,我可以将空格视为标记吗?
In pyparsing, can I treat whitespace as a token when certain conditions are met when using infixNotation?
我正在尝试使用 pyparsing==2.4.7
来解析具有 field:value
格式的搜索查询。
我要解析的字符串示例包括:
field1:value1
field1:value1 field2:value2
field1:value1 AND field2:value2
(field1:value1a OR field1:value1b) field2:value2
(field1:value1a | field1:value1b) & (field2:value2a | field2:value2b)
注意几点:
- 我使用
OR
和 |
都表示“或”,与 AND
和 &
表示相同的意思
- 如果条件之间没有布尔运算符,则隐含
AND
- 查询可以用括号分层嵌套
- 值(在
:
的右侧)永远不会有空格
我已经编写了一个有效的解析器(代码基于 ),但仅适用于所有运算符都存在的情况(AND
和 OR
):
import pyparsing as pp
from pyparsing import Word, alphas, alphanums, White, Combine, OneOrMore, Literal, oneOf
field_name = Word(alphanums).setResultsName('field_name')
search_value = Word(alphanums + '-').setResultsName('search_value')
operator = Literal(':')
query = field_name + operator + search_value
AND = oneOf(['AND', 'and', '&', ' '])
OR = oneOf(['OR', 'or', '|'])
NOT = oneOf(['NOT', 'not', '!'])
query_expr = pp.infixNotation(query, [
(NOT, 1, pp.opAssoc.RIGHT, ),
(AND, 2, pp.opAssoc.LEFT, ),
(OR, 2, pp.opAssoc.LEFT, ),
])
class ComparisonExpr:
def __init__(self, tokens):
self.tokens = tokens
def __str__(self):
return "Comparison:('field': {!r}, 'operator': {!r}, 'value': {!r})".format(*self.tokens)
def __repr__(self):
return self.__str__()
query.addParseAction(ComparisonExpr)
sample = "(field1:value1a | field1:value1b) & (field2:value2a | field2:value2b)"
result = query_expr.parseString(sample).asList()
from pprint import pprint
>>> pprint(result)
[[[Comparison:('field': 'field1', 'operator': ':', 'value': 'value1a'),
'|',
Comparison:('field': 'field1', 'operator': ':', 'value': 'value1b')],
'&',
[Comparison:('field': 'field2', 'operator': ':', 'value': 'value2a'),
'|',
Comparison:('field': 'field2', 'operator': ':', 'value': 'value2b')]]]
但是,如果我尝试使用缺少运算符的 sample
,解析器似乎会在需要运算符的位置停止:
sample = "(field1:value1a | field1:value1b) (field2:value2a | field2:value2b)"
result = query_expr.parseString(sample).asList()
from pprint import pprint
pprint(result)
[[Comparison:('field': 'field1', 'operator': ':', 'value': 'value1a'),
'|',
Comparison:('field': 'field1', 'operator': ':', 'value': 'value1b')]]
如果没有运算符分隔项,是否有办法使空格成为“隐式 AND
”?
简答:
将您对 AND
的定义替换为:
AND = oneOf(['AND', 'and', '&']) | pp.Empty()
其他一些建议:
为了更轻松地 post-parse 处理,您可能希望 Empty()
实际发出一个“&”运算符。您可以通过解析操作来做到这一点:
AND = oneOf(['AND', 'and', '&']) | pp.Empty().addParseAction(lambda: "&")
事实上,您可以将所有运算符规范化为“&”、“|”和“!”,再次跳过任何“if operator == 'AND' 或 operator == 'and' 或 ..." 代码。将您的解析操作放在整个表达式上:
AND = (oneOf(['AND', 'and', '&']) | pp.Empty()).addParseAction(lambda: "&")
OR = oneOf(['OR', 'or', '|']).addParseAction(lambda: "|")
NOT = oneOf(['NOT', 'not', '!']).addParseAction(lambda: "!")
此外,考虑到您现在接受“”等同于“&”,您应该让 pyparsing 像对待关键字一样对待您的运算符——这样如果“oregon”不是“or egon”就不会混淆。将 asKeyword
参数添加到所有 oneOf
表达式中:
AND = (oneOf(['AND', 'and', '&'], asKeyword=True)
| pp.Empty()).addParseAction(lambda: "&")
OR = oneOf(['OR', 'or', '|'], asKeyword=True).addParseAction(lambda: "|")
NOT = oneOf(['NOT', 'not', '!'], asKeyword=True).addParseAction(lambda: "!")
最后,当您想编写测试字符串时,您可以跳过字符串循环或捕获 ParseExceptions - 只需使用 runTests
:
query_expr.runTests("""\
(field1:value1a | field1:value1b) & (field2:value2a | field2:value2b)
(field1:value1a | field1:value1b) (field2:value2a | field2:value2b)
""")
将打印每个测试字符串,然后是解析结果或解析异常以及发生异常的'^':
(field1:value1a | field1:value1b) & (field2:value2a | field2:value2b)
[[[Comparison:('field': 'field1', 'operator': ':', 'value': 'value1a'), '|', Comparison:('field': 'field1', 'operator': ':', 'value': 'value1b')], '&', [Comparison:('field': 'field2', 'operator': ':', 'value': 'value2a'), '|', Comparison:('field': 'field2', 'operator': ':', 'value': 'value2b')]]]
[0]:
[[Comparison:('field': 'field1', 'operator': ':', 'value': 'value1a'), '|', Comparison:('field': 'field1', 'operator': ':', 'value': 'value1b')], '&', [Comparison:('field': 'field2', 'operator': ':', 'value': 'value2a'), '|', Comparison:('field': 'field2', 'operator': ':', 'value': 'value2b')]]
[0]:
[Comparison:('field': 'field1', 'operator': ':', 'value': 'value1a'), '|', Comparison:('field': 'field1', 'operator': ':', 'value': 'value1b')]
[1]:
&
[2]:
[Comparison:('field': 'field2', 'operator': ':', 'value': 'value2a'), '|', Comparison:('field': 'field2', 'operator': ':', 'value': 'value2b')]
(field1:value1a | field1:value1b) (field2:value2a | field2:value2b)
[[[Comparison:('field': 'field1', 'operator': ':', 'value': 'value1a'), '|', Comparison:('field': 'field1', 'operator': ':', 'value': 'value1b')], '&', [Comparison:('field': 'field2', 'operator': ':', 'value': 'value2a'), '|', Comparison:('field': 'field2', 'operator': ':', 'value': 'value2b')]]]
[0]:
[[Comparison:('field': 'field1', 'operator': ':', 'value': 'value1a'), '|', Comparison:('field': 'field1', 'operator': ':', 'value': 'value1b')], '&', [Comparison:('field': 'field2', 'operator': ':', 'value': 'value2a'), '|', Comparison:('field': 'field2', 'operator': ':', 'value': 'value2b')]]
[0]:
[Comparison:('field': 'field1', 'operator': ':', 'value': 'value1a'), '|', Comparison:('field': 'field1', 'operator': ':', 'value': 'value1b')]
[1]:
&
[2]:
[Comparison:('field': 'field2', 'operator': ':', 'value': 'value2a'), '|', Comparison:('field': 'field2', 'operator': ':', 'value': 'value2b')]
我正在尝试使用 pyparsing==2.4.7
来解析具有 field:value
格式的搜索查询。
我要解析的字符串示例包括:
field1:value1
field1:value1 field2:value2
field1:value1 AND field2:value2
(field1:value1a OR field1:value1b) field2:value2
(field1:value1a | field1:value1b) & (field2:value2a | field2:value2b)
注意几点:
- 我使用
OR
和|
都表示“或”,与AND
和&
表示相同的意思 - 如果条件之间没有布尔运算符,则隐含
AND
- 查询可以用括号分层嵌套
- 值(在
:
的右侧)永远不会有空格
我已经编写了一个有效的解析器(代码基于 AND
和 OR
):
import pyparsing as pp
from pyparsing import Word, alphas, alphanums, White, Combine, OneOrMore, Literal, oneOf
field_name = Word(alphanums).setResultsName('field_name')
search_value = Word(alphanums + '-').setResultsName('search_value')
operator = Literal(':')
query = field_name + operator + search_value
AND = oneOf(['AND', 'and', '&', ' '])
OR = oneOf(['OR', 'or', '|'])
NOT = oneOf(['NOT', 'not', '!'])
query_expr = pp.infixNotation(query, [
(NOT, 1, pp.opAssoc.RIGHT, ),
(AND, 2, pp.opAssoc.LEFT, ),
(OR, 2, pp.opAssoc.LEFT, ),
])
class ComparisonExpr:
def __init__(self, tokens):
self.tokens = tokens
def __str__(self):
return "Comparison:('field': {!r}, 'operator': {!r}, 'value': {!r})".format(*self.tokens)
def __repr__(self):
return self.__str__()
query.addParseAction(ComparisonExpr)
sample = "(field1:value1a | field1:value1b) & (field2:value2a | field2:value2b)"
result = query_expr.parseString(sample).asList()
from pprint import pprint
>>> pprint(result)
[[[Comparison:('field': 'field1', 'operator': ':', 'value': 'value1a'),
'|',
Comparison:('field': 'field1', 'operator': ':', 'value': 'value1b')],
'&',
[Comparison:('field': 'field2', 'operator': ':', 'value': 'value2a'),
'|',
Comparison:('field': 'field2', 'operator': ':', 'value': 'value2b')]]]
但是,如果我尝试使用缺少运算符的 sample
,解析器似乎会在需要运算符的位置停止:
sample = "(field1:value1a | field1:value1b) (field2:value2a | field2:value2b)"
result = query_expr.parseString(sample).asList()
from pprint import pprint
pprint(result)
[[Comparison:('field': 'field1', 'operator': ':', 'value': 'value1a'),
'|',
Comparison:('field': 'field1', 'operator': ':', 'value': 'value1b')]]
如果没有运算符分隔项,是否有办法使空格成为“隐式 AND
”?
简答:
将您对 AND
的定义替换为:
AND = oneOf(['AND', 'and', '&']) | pp.Empty()
其他一些建议:
为了更轻松地 post-parse 处理,您可能希望 Empty()
实际发出一个“&”运算符。您可以通过解析操作来做到这一点:
AND = oneOf(['AND', 'and', '&']) | pp.Empty().addParseAction(lambda: "&")
事实上,您可以将所有运算符规范化为“&”、“|”和“!”,再次跳过任何“if operator == 'AND' 或 operator == 'and' 或 ..." 代码。将您的解析操作放在整个表达式上:
AND = (oneOf(['AND', 'and', '&']) | pp.Empty()).addParseAction(lambda: "&")
OR = oneOf(['OR', 'or', '|']).addParseAction(lambda: "|")
NOT = oneOf(['NOT', 'not', '!']).addParseAction(lambda: "!")
此外,考虑到您现在接受“”等同于“&”,您应该让 pyparsing 像对待关键字一样对待您的运算符——这样如果“oregon”不是“or egon”就不会混淆。将 asKeyword
参数添加到所有 oneOf
表达式中:
AND = (oneOf(['AND', 'and', '&'], asKeyword=True)
| pp.Empty()).addParseAction(lambda: "&")
OR = oneOf(['OR', 'or', '|'], asKeyword=True).addParseAction(lambda: "|")
NOT = oneOf(['NOT', 'not', '!'], asKeyword=True).addParseAction(lambda: "!")
最后,当您想编写测试字符串时,您可以跳过字符串循环或捕获 ParseExceptions - 只需使用 runTests
:
query_expr.runTests("""\
(field1:value1a | field1:value1b) & (field2:value2a | field2:value2b)
(field1:value1a | field1:value1b) (field2:value2a | field2:value2b)
""")
将打印每个测试字符串,然后是解析结果或解析异常以及发生异常的'^':
(field1:value1a | field1:value1b) & (field2:value2a | field2:value2b)
[[[Comparison:('field': 'field1', 'operator': ':', 'value': 'value1a'), '|', Comparison:('field': 'field1', 'operator': ':', 'value': 'value1b')], '&', [Comparison:('field': 'field2', 'operator': ':', 'value': 'value2a'), '|', Comparison:('field': 'field2', 'operator': ':', 'value': 'value2b')]]]
[0]:
[[Comparison:('field': 'field1', 'operator': ':', 'value': 'value1a'), '|', Comparison:('field': 'field1', 'operator': ':', 'value': 'value1b')], '&', [Comparison:('field': 'field2', 'operator': ':', 'value': 'value2a'), '|', Comparison:('field': 'field2', 'operator': ':', 'value': 'value2b')]]
[0]:
[Comparison:('field': 'field1', 'operator': ':', 'value': 'value1a'), '|', Comparison:('field': 'field1', 'operator': ':', 'value': 'value1b')]
[1]:
&
[2]:
[Comparison:('field': 'field2', 'operator': ':', 'value': 'value2a'), '|', Comparison:('field': 'field2', 'operator': ':', 'value': 'value2b')]
(field1:value1a | field1:value1b) (field2:value2a | field2:value2b)
[[[Comparison:('field': 'field1', 'operator': ':', 'value': 'value1a'), '|', Comparison:('field': 'field1', 'operator': ':', 'value': 'value1b')], '&', [Comparison:('field': 'field2', 'operator': ':', 'value': 'value2a'), '|', Comparison:('field': 'field2', 'operator': ':', 'value': 'value2b')]]]
[0]:
[[Comparison:('field': 'field1', 'operator': ':', 'value': 'value1a'), '|', Comparison:('field': 'field1', 'operator': ':', 'value': 'value1b')], '&', [Comparison:('field': 'field2', 'operator': ':', 'value': 'value2a'), '|', Comparison:('field': 'field2', 'operator': ':', 'value': 'value2b')]]
[0]:
[Comparison:('field': 'field1', 'operator': ':', 'value': 'value1a'), '|', Comparison:('field': 'field1', 'operator': ':', 'value': 'value1b')]
[1]:
&
[2]:
[Comparison:('field': 'field2', 'operator': ':', 'value': 'value2a'), '|', Comparison:('field': 'field2', 'operator': ':', 'value': 'value2b')]