我正在研究一个非常简单的“查询语法”,它可供具有合理技术技能的人使用(即,不是程序员本身,但能够触及主题)
他们在表格上输入的内容的典型示例是:
address like street
AND
vote = True
AND
(
(
age>=25
AND
gender = M
)
OR
(
age between [20,30]
AND
gender = F
)
OR
(
age >= 70
AND
eyes != blue
)
)
与
我正在使用pyparsing(好吧,无论如何都在尝试)并达到以下目的:
^{pr2}$输出为:
[['address',
'like',
'street',
'AND',
'vote',
'=',
'True',
'AND',
[['age>=25', 'AND', 'gender', '=', 'M'],
'OR',
['age', 'between', '[20,30]', 'AND', 'gender', '=', 'F'],
'OR',
['age', '>=', '70', 'AND', 'eyes', '!=', 'blue']]]]
我只对其部分满意-主要原因是期望的最终输出如下所示:
[
{
"field" : "address",
"operator" : "like",
"value" : "street",
},
'AND',
{
"field" : "vote",
"operator" : "=",
"value" : True,
},
'AND',
[
[
{
"field" : "age",
"operator" : ">=",
"value" : 25,
},
'AND'
{
"field" : "gender",
"operator" : "=",
"value" : "M",
}
],
'OR',
[
{
"field" : "age",
"operator" : "between",
"value" : [20,30],
},
'AND'
{
"field" : "gender",
"operator" : "=",
"value" : "F",
}
],
'OR',
[
{
"field" : "age",
"operator" : ">=",
"value" : 70,
},
'AND'
{
"field" : "eyes",
"operator" : "!=",
"value" : "blue",
}
],
]
]
非常感谢!在
编辑
在Paul的回答之后,代码就是这样的。显然,它的效果更好:-)
unicode_printables = u''.join(unichr(c) for c in xrange(65536)
if not unichr(c).isspace())
user_input = ' '.join(user_input.split())
AND = oneOf(['AND', '&'])
OR = oneOf(['OR', '|'])
FIELD = Word(alphanums)
OPERATOR = oneOf(OPERATORS)
VALUE = Word(unicode_printables)
COMPARISON = FIELD + OPERATOR + VALUE
QUERY = infixNotation(
COMPARISON,
[
(AND, 2, opAssoc.LEFT,),
(OR, 2, 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.asList())
COMPARISON.addParseAction(ComparisonExpr)
RESULT = QUERY.parseString(user_input).asList()
print type(RESULT)
from pprint import pprint
pprint(RESULT)
输出为:
[
[
<[snip]ComparisonExpr instance at 0x043D0918>,
'AND',
<[snip]ComparisonExpr instance at 0x043D0F08>,
'AND',
[
[
<[snip]ComparisonExpr instance at 0x043D3878>,
'AND',
<[snip]ComparisonExpr instance at 0x043D3170>
],
'OR',
[
[
<[snip]ComparisonExpr instance at 0x043D3030>,
'AND',
<[snip]ComparisonExpr instance at 0x043D3620>
],
'AND',
[
<[snip]ComparisonExpr instance at 0x043D3210>,
'AND',
<[snip]ComparisonExpr instance at 0x043D34E0>
]
]
]
]
]
有没有办法用字典而不是ComparisonExpr
实例返回结果?在
编辑2
想出了一个天真而具体的解决方案,但到目前为止,它对我很有效:
[snip]
class ComparisonExpr:
def __init__(self, tokens):
self.tokens = tokens
def __str__(self):
return "Comparison:('field': {!r}, 'operator': {!r}, 'value': {!r})".format(*self.tokens.asList())
def asDict(self):
return {
"field": self.tokens.asList()[0],
"operator": self.tokens.asList()[1],
"value": self.tokens.asList()[2]
}
[snip]
RESULT = QUERY.parseString(user_input).asList()[0]
def convert(list):
final = []
for item in list:
if item.__class__.__name__ == 'ComparisonExpr':
final.append(item.asDict())
elif item in ['AND', 'OR']:
final.append(item)
elif item.__class__.__name__ == 'list':
final.append(convert(item))
else:
print 'ooops forgotten something maybe?'
return final
FINAL = convert(RESULT)
pprint(FINAL)
哪些输出:
[{'field': 'address', 'operator': 'LIKE', 'value': 'street'},
'AND',
{'field': 'vote', 'operator': '=', 'value': 'true'},
'AND',
[[{'field': 'age', 'operator': '>=', 'value': '25'},
'AND',
{'field': 'gender', 'operator': '=', 'value': 'M'}],
'OR',
[[{'field': 'age', 'operator': 'BETWEEN', 'value': '[20,30]'},
'AND',
{'field': 'gender', 'operator': '=', 'value': 'F'}],
'AND',
[{'field': 'age', 'operator': '>=', 'value': '70'},
'AND',
{'field': 'eyes', 'operator': '!=', 'value': 'blue'}]]]]
再次感谢保罗给我指出了一个正确的方向!在
我唯一不知道的就是把'true'
变成{
nestedExpr
是pyparsing中的一个方便表达式,它使定义具有匹配的开头和结尾字符的文本变得容易。当您想解析嵌套的内容时,nestedExpr
通常结构不好。在使用pyparsing的
infixNotation
方法可以更好地处理您试图解析的查询语法。您可以在pyparsing wiki的示例页面上看到几个示例—SimpleBool与您正在解析的内容非常相似。在“中缀表示法”是表达式的一般解析术语,其中运算符位于相关操作数之间(与“后缀表示法”相比,运算符跟在操作数之后,如“23+”而不是“2+3”;或“前缀表示法”,看起来像“+23”)。运算符在求值时可以有一个优先顺序,可以覆盖从左到右的顺序,例如,在“2+3*4”中,运算的优先级规定乘法在加法之前计算。中缀表示法还支持使用括号或其他分组字符来覆盖该优先级,如“(2+3)*4”中强制首先完成加法操作。在
pyparsing的
infixNotation
方法采用一个基本操作数表达式,然后是一个运算符定义元组的列表,按优先顺序排列。例如,4函数整数运算如下:这意味着我们将按顺序解析整数操作数,包括“*”和“/”二进制左关联运算以及“+”和“-”二进制运算。对括号重写顺序的支持内置于
infixNotation
。在查询字符串通常是布尔操作NOT、AND、OR的某种组合,通常按优先顺序进行计算。在您的例子中,这些运算符的操作数是比较表达式,例如“address=street”或“age between[20,30]”。因此,如果您为比较表达式定义一个表达式,形式为
^{pr2}$fieldname operator value
,那么您可以使用infixNotation
对AND和OR进行正确的分组:最后,我建议您定义一个类,将比较标记作为类init args,然后您可以将行为附加到该类以计算比较并输出调试字符串,如下所示:
然后可以得到如下输出:
在SimpleBool.py这个例子有更多的细节来展示如何创建这个类,以及NOT,and,and或操作符的相关类。在
编辑:
“有没有一种方法可以用字典而不是ComparisonExpr实例返回结果?” 正在调用您的
ComparisonExpr
类上的__repr__
方法,而不是__str__
。最简单的解决方案是在类中添加:或者将
__str__
重命名为__repr__
。在“我唯一不知道的就是把‘真’变成真,‘20,30’‘变成了‘20,30’”
尝试:
然后将这些添加到值表达式中:
最后:
我已经厌倦了导入
pprint
来完成这个任务,我只是把它添加到ParseResults
的API中。尝试:或者
编辑2
最后,结果名称是很好的学习。如果将此更改为“比较”,则一切仍按现有方式工作:
但现在你可以写:
您可以通过名称而不是索引位置访问解析后的值(使用
result['field']
表示法或result.field
表示法)。在相关问题 更多 >
编程相关推荐