python - 在pyparsing中,当使用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
意思&
相同 - 如果条件之间没有布尔运算符,则隐含an
AND
- 查询可以用括号分层嵌套
- 值(在右侧
:
)永远不会有空格
我编写了一个可以工作的解析器(代码基于这个 SO 答案),但仅适用于所有运算符都存在时(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
缺少运算符的 a ,解析器似乎会停止在预期运算符的位置:
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()
其他一些建议:
为了更容易进行解析后处理,您可能希望Empty()
实际发出“&”运算符。您可以通过解析操作来做到这一点:
AND = oneOf(['AND', 'and', '&']) | pp.Empty().addParseAction(lambda: "&")
事实上,您可以再次将所有运算符规范化为“&”、“|”和“!”,以跳过任何“if operator == 'AND' or operator == 'and' or ...”代码. 将您的解析操作放在整个表达式上:
AND = (oneOf(['AND', 'and', '&']) | pp.Empty()).addParseAction(lambda: "&")
OR = oneOf(['OR', 'or', '|']).addParseAction(lambda: "|")
NOT = oneOf(['NOT', 'not', '!']).addParseAction(lambda: "!")
此外,考虑到您现在接受“”等同于“&”,您应该让 pyparsing 将您的运算符视为关键字 - 因此如果“oregon”不是“或 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')]
推荐阅读
- javascript - 将自定义 javascript 文件放在 GWT JSinterop java 程序中的什么位置?
- c++ - 有效计算大型 C++ 数组中的平均值
- reactjs - 如何在反应播放器中停止视频?
- python - 如何为特定功能的 python 调试设置 VsCode launch.json?
- r - 尝试从 R 中的 GLM 预测新数据时出现持续错误
- python - 每个类别的分数计数
- compiler-errors - 我收到有关 omnet 模拟中 udp 消息的序列化程序的错误。如何解决?
- linux - 不尊重清单中定义的用户
- java - 如何从 FireStore 读取 ArrayList?
- javascript - 如何在数组中添加人物?