我正在尝试解析和评估表达式,这些表达式是作为文件的输入提供的,格式为:
var[3] = 0 and var[2] = 1
var[0] = 1 and var[2] = 0 and var[3] = 1
...
(实际上我也允许"多位访问"(即var[X:Y]
)但是现在让我们忽略它...)
其中var是整数,[]
表示位访问
例如,对于var = 0x9
,上面的第一个表达式应该评估为False
,而第二个表达式应该从True
开始评估为0x9 = b1001
。
and
和=
是我允许的唯一二元运算符,对于=
运算符,左操作数始终为var[X]
,右操作数始终为数字。 />
我试着环顾一下,发现这可以通过Python pyparsing
来实现,但我在尝试实现它时遇到了一些困难。
这是我迄今为止所尝试的内容,大致基于this example(这是here提供的众多示例之一):
#!/usr/bin/env python
from pyparsing import Word, alphas, nums, infixNotation, opAssoc
class BoolAnd():
def __init__(self, pattern):
self.args = pattern[0][0::2]
def __bool__(self):
return all(bool(a) for a in self.args)
__nonzero__ = __bool__
class BoolEqual():
def __init__(self, pattern):
self.bit_offset = int(pattern[0][1])
self.value = int(pattern[0][-1])
def __bool__(self):
return True if (0xf >> self.bit_offset) & 0x1 == self.value else False # for now, let's assume var == 0xf
__nonzero__ = __bool__
variable_name = 'var'
bit_access = variable_name + '[' + Word(nums) + ']'
multibit_access = variable_name + '[' + Word(nums) + ':' + Word(nums) + ']'
value = Word(nums)
operand = bit_access | multibit_access | value
expression = infixNotation(operand,
[
('=', 2, opAssoc.LEFT, BoolEqual),
('AND', 2, opAssoc.LEFT, BoolAnd),
])
p = expression.parseString('var[3] = 1 AND var[1] = 0', True)
print 'SUCCESS' if bool(p) else 'FAIL'
我有三个需要帮助的问题。
var[X:Y] = Z
格式的多位访问,我该如何强制执行:
一个。 X > Y
Z < 2^{X - Y + 1}
var[X] = Y
形式的单位访问,我可以通过Y
将{{1}的语法强制执行。或} 0
,如果1
},这将导致expression.parseString()
失败并发生异常。Y != 0/1
?我做错了什么?SUCCESS
,它应该是打印var[3] = 1 AND var[1] = 0
(您可以在我的示例中看到我将FAIL
硬编码为var
,因此0xf
为{{1}但是var[3] = 1
是True
)。 var[1] = 0
不是False
的类成员,也不是全局的...有没有办法以某种方式将其发送到var
&# 39; s BoolEqual
功能? 答案 0 :(得分:3)
在解决问题之前,我建议对语法进行一些细微的修改,主要是包含结果名称。添加这些名称将使您的结果代码更加清晰和健壮。我还在pyparsing_common
命名空间类中使用了最近的pyparsing版本中添加的一些表达式:
from pyparsing import pyparsing_common
variable_name = pyparsing_common.identifier.copy()
integer = pyparsing_common.integer.copy()
bit_access = variable_name('name') + '[' + integer('bit') + ']'
multibit_access = variable_name('name') + '[' + integer('start_bit') + ':' + integer('end_bit') + ']'
第1a部分:在&#34; var [X:Y]&#34;
中执行有效值这种工作最好使用解析操作和条件完成。解析操作是解析时回调,您可以将其附加到您的pyparsing表达式,以修改,增强,过滤结果或在验证规则失败时引发异常。这些是使用以下方法附加的:
expr.addParseAction(parse_action_fn)
并且parse_action_fn可以具有以下任何签名:
def parse_action_fn(parse_string, parse_location, matched_tokens):
def parse_action_fn(parse_location, matched_tokens):
def parse_action_fn(matched_tokens):
def parse_action_fn():
(详见https://pythonhosted.org/pyparsing/pyparsing.ParserElement-class.html#addParseActio)n)
解析操作可以返回None,返回新标记,修改给定标记或引发异常。
如果所有解析操作都是基于输入标记评估某些条件,则可以将其写为返回True或False的简单函数,如果返回False,则pyparsing将引发异常。在您的情况下,您的第一个验证规则可以实现为:
def validate_multibit(tokens):
return tokens.end_bit > tokens.start_bit
multibit_access.addCondition(validate_multibit,
message="start bit must be less than end bit",
fatal=True)
甚至就像Python lambda函数一样:
multibit_access.addCondition(lambda t: t.end_bit > t.start_bit,
message="start bit must be less than end bit",
fatal=True)
现在您可以尝试使用:
multibit_access.parseString("var[3:0]")
你会得到这个例外:
pyparsing.ParseFatalException: start bit must be less than end bit (at char 0), (line:1, col:1)
第1b部分:在&#34; var [X:Y] = Z&#34;
中执行有效值您的第二个验证规则不仅处理var位范围,还处理它与之比较的值。这将需要附加到完整BoolEqual的解析操作。我们可以将它放在BoolEqual的__init__
方法中,但我更喜欢在可能的情况下将独立函数分开。由于我们将通过附加到infixNotation
级别来添加我们的验证,并且infixNotation
仅接受解析操作,因此我们需要将您的第二个验证规则编写为引发异常的解析操作。 (我们还将使用最近才在pyparsing 2.2.0中发布的新功能,在infixNotation
的某个级别附加多个解析操作。)
以下是我们希望执行的验证:
如果多位表达式var [X:Y],则值必须<&lt; 2 **(Y-X + 1)
def validate_equality_args(tokens): tokens = tokens[0] z = tokens[-1] if 'bit' in tokens: if z not in (0,1): raise ParseFatalException("invalid equality value - must be 0 or 1") else: x = tokens.start_bit y = tokens.end_bit if not z < 2**(y - x + 1): raise ParseFatalException("invalid equality value")
我们使用以下方法将此解析操作附加到infixNotation
expression = infixNotation(operand,
[
('=', 2, opAssoc.LEFT, (validate_equality_args, BoolEqual)),
('AND', 2, opAssoc.LEFT, BoolAnd),
])
第3部分:支持除0xf之外的其他var名称和值
要处理各种名称的变量,可以向BoolEqual添加类级别的dict:
class BoolEqual():
var_names = {}
并提前设定:
BoolEqual.var_names['var'] = 0xf
然后将__bool__
方法实现为:
return (self.var_names[self.var_name] >> self.bit_offset) & 0x1 == self.value
(这需要扩展为支持multibit,但总体思路是相同的。)
答案 1 :(得分:0)
如何将变量转换为1和0的列表,并使用eval
来计算布尔表达式(使用较小的修改,更改= into ==):
def parse(lines, v):
var = map(int,list(bin(v)[2:]))
result = []
for l in lines:
l = l.replace('=','==')
result.append(eval(l))
return result
inp = \
"""
var[3] = 0 and var[2] = 1
var[0] = 1 and var[2] = 0 and var[3] = 1
"""
lines = inp.split('\n')[1:-1]
v = 0x09
print parse(lines, v)
输出:
[False, True]
请注意,如果您信任输入,则只应使用eval
。