【发布时间】:2021-06-30 18:06:57
【问题描述】:
我编写了一个简单的解析器,用于解析带有 AND/OR/NOT 操作的简单语句。
import pyparsing
class ClauseExpression:
def __init__(self, tokens):
self.tokens = tokens
def __repr__(self):
return "field: {}, op: {}, value: {}".format(*self.tokens)
def asDict(self):
return self.tokens.asDict()
clause = (
pyparsing.oneOf("group tag").setResultsName("field")
+ pyparsing.Literal("=").setResultsName("op")
+ pyparsing.Word(pyparsing.alphanums).setResultsName("value")
).setParseAction(ClauseExpression)
class OpNode:
def __repr__(self):
return "{}:{!r}".format(self.op, self.items)
class UnOp(OpNode):
def __init__(self, tokens):
self.op = tokens[0][0]
self.items = [tokens[0][1]]
class BinOp(OpNode):
def __init__(self, tokens):
self.op = tokens[0][1]
self.items = tokens[0][::2]
statement = pyparsing.infixNotation(
clause,
[
(pyparsing.CaselessLiteral("NOT"), 1, pyparsing.opAssoc.RIGHT, UnOp),
(pyparsing.CaselessLiteral("AND"), 2, pyparsing.opAssoc.LEFT, BinOp),
(pyparsing.CaselessLiteral("OR"), 2, pyparsing.opAssoc.LEFT, BinOp),
],
)
def parse_result(result):
if isinstance(result, ClauseExpression):
return result.asDict()
elif isinstance(result, OpNode):
return {
"op": result.op,
"items": [parse_result(item) for item in result.items],
}
raise TypeError("Oh Noe! Something is not right.")
if __name__ == "__main__":
tests = (
"tag=TagA",
"tag=TagA AND tag=TagB",
"tag=TagA OR NOT tag=TagB",
)
for item in tests:
print("=" * 40)
print("INPUT:", item)
results = statement.parseString(item)
print("OUTPUT:", parse_result(results[0]))
print("=" * 40)
输出
========================================
INPUT: tag=TagA
OUTPUT: {'field': 'tag', 'op': '=', 'value': 'TagA'}
========================================
========================================
INPUT: tag=TagA AND tag=TagB
OUTPUT: {'op': 'AND', 'items': [{'field': 'tag', 'op': '=', 'value': 'TagA'}, {'field': 'tag', 'op': '=', 'value': 'TagB'}]}
========================================
========================================
INPUT: tag=TagA OR NOT tag=TagB
OUTPUT: {'op': 'OR', 'items': [{'field': 'tag', 'op': '=', 'value': 'TagA'}, {'op': 'NOT', 'items': [{'field': 'tag', 'op': '=', 'value': 'TagB'}]}]}
========================================
通过这些输出,我可以使用 django Q 对象动态生成复杂的查找。
但是我不知道如何处理空格和符号(例如“&”)
对于输入 => tag=Tag C AND tag=E&F
输出 => {'field': 'tag', 'op': '=', 'value': 'Tag'}
我了解默认情况下,pyparsing 会忽略空格。
不知何故,我需要使用 pyparsing.White() 并排除 AND/OR/NOT 例如 pyparsing.NotAny(pyparsing.Or(['AND', 'OR', 'NOT']))
我是 pyparsing 的菜鸟,我们将不胜感激。
【问题讨论】:
-
逻辑大致基于@m1lt0n的一篇博文见m1lt0n.github.io/python/parsing/pyparsing/2020/11/08/…
-
与其编写自己的测试循环,不如试试
statement.runTests(tests)- 你会得到更好的诊断结果。