我正在使用 PyParsing 为查询语言编写解析器,并且我陷入了(我认为是)前瞻问题。查询中的一种子句类型旨在将字符串拆分为 3 个部分(字段名、运算符、值),其中字段名是一个单词,运算符是一个或多个单词,值是一个单词、带引号的字符串或带括号的列表这些。
我的数据看起来像
author is william
author is 'william shakespeare'
author is not shakespeare
author is in (william,'the bard',shakespeare)
我当前对该子句的解析器写为:
fieldname = Word(alphas)
operator = OneOrMore(Word(alphas))
single_value = Word(alphas) ^ QuotedString(quoteChar="'")
list_value = Literal("(") + Group(delimitedList(single_value)) + Literal(")")
value = single_value ^ list_value
clause = fieldname + originalTextFor(operator) + value
显然这失败了,因为operator
元素是贪婪的并且会吞噬value
如果可以的话。通过阅读其他类似的问题和文档,我发现我需要使用NotAny
or FollowedBy
,但我一直无法弄清楚如何使其发挥作用。
这是成为解析器的好地方。或者更准确地说,让解析器像你一样思考。问问自己,“在‘作者是莎士比亚’中,我怎么知道‘莎士比亚’不是运算符的一部分?”您知道“shakespeare”是值,因为它位于查询的末尾,后面没有其他内容。所以运算符词不仅仅是字母词,它们是字母单词,后面没有字符串结尾。现在将先行逻辑构建到您的定义中operator
:
operator = OneOrMore(Word(alphas) + ~FollowedBy(StringEnd()))
我认为这将为您提供更好的解析。
其他一些提示:
我仅在可能存在歧义的情况下使用“^”运算符,例如我要解析带有整数或十六进制数字的字符串。如果我用过Word(nums) | Word(hexnums)
,那么我可能会将“123ABC”误处理为前导“123”。通过改变“|”对于“^”,将测试所有替代方案,并选择最长的匹配。在我解析十进制或十六进制整数的示例中,我可以通过反转替代方案并测试来获得相同的结果Word(hexnums)
第一的。在您的查询语言中,无法将带引号的字符串与不带引号的单个单词值混淆(一个以'
or "
,另一个没有),所以没有理由使用'^','|'就足够了。类似的value = singleValue ^ listValue
.
-
将结果名称添加到查询字符串的关键组成部分将使以后更容易使用:
clause = fieldname("fieldname") + originalTextFor(operator)("operator") + value("value")
现在,您可以通过名称而不是通过解析位置访问解析的值(一旦您开始使用可选字段等变得更加复杂,这将变得棘手且容易出错):
queryParts = clause.parseString('author is william')
print queryParts.fieldname
print queryParts.operator
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)