Pyparsing 空格和一些符号

Pyparsing whitespace and some symbols

我写了一个简单的解析器来解析带有 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 的完全菜鸟,我们将不胜感激。

m1lton 的博客 post 非常适合您的专业形式。

使用 运行Tests 来 运行 你失败的测试字符串给出了这个:

tag=Tag C AND tag=E&F
        ^
FAIL: Expected end of text, found 'C'  (at char 8), (line:1, col:9)

表示多词标签值没有被正确解析。

通常当人们允许在标签值等内容中使用空格时,他们会要求将它们括在引号中。通过使用否定前瞻,我们避免了这里的问题,但如果您添加更多关键字或运算符,您可能必须要求这样做。

查看此代码,了解我如何定义一个 tag_value 表达式,该表达式将接受多个单词而不会意外匹配您的任何关键字:

# define keywords to not be accepted as tag words
any_keyword = (pyparsing.Keyword("AND")
               | pyparsing.Keyword("OR")
               | pyparsing.Keyword("NOT"))

# define a tag word using lookahead to avoid keywords - also need to add "&" symbol
tag_word = ~any_keyword + pyparsing.Word(pyparsing.alphanums+"&")

# define a tag_value that can consist of multiple tag_words
# originalTextFor will return the original text used to match, including any whitespace
tag_value = pyparsing.originalTextFor(tag_word[1, ...])

那么 clause 就变成了:

clause = (
    pyparsing.oneOf("group tag").setResultsName("field")
    + pyparsing.Literal("=").setResultsName("op")
    + tag_value.setResultsName("value")
).setParseAction(ClauseExpression)

给出这个测试输出:

tag=Tag C AND tag=E&F
[AND:[field: tag, op: =, value: Tag C, field: tag, op: =, value: E&F]]