![](/img/trans.png)
[英]Pyparsing: Grammar cannot parse whitespace while parsing arithmetic expression with exponential and logarithm functions
[英]Python's pyparsing: Implementing grammar to parse logical AND expression
我試圖解析和評估表達式,給我作為文件的輸入,形式:
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
來實現,但是我在嘗試實現它時遇到了一些困難。
這是我到目前為止所嘗試的內容,大致基於此示例 (這是此處提供的眾多示例之一):
#!/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
將為0
或1
,這將導致如果Y != 0/1
, expression.parseString()
失敗,異常。 SUCCESS
? 我究竟做錯了什么? var[3] = 1 AND var[1] = 0
它應該是打印FAIL
(你可以在我的例子中看到我硬編碼var
為0xf
,所以var[3] = 1
是True
但是var[1] = 0
為False
)。 var
不是BoolEqual
的類成員,也不是全局的...有沒有辦法以某種方式將它發送到BoolEqual
的__init__
函數? 在解決問題之前,我建議對語法進行一些小的改動,主要是包含結果名稱。 添加這些名稱將使您的結果代碼更加清晰和健壯。 我還使用了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部分:在“var [X:Y]”中強制執行有效值
這種工作最好使用解析操作和條件來完成。 解析操作是解析時回調,您可以將其附加到您的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部分:在“var [X:Y] = Z”中強制執行有效值
您的第二個驗證規則不僅處理var位范圍,還處理它與之比較的值。 這將需要附加到完整BoolEqual的解析操作。 我們可以把它放在BoolEqual的__init__
方法中,但我更喜歡在可能的情況下分離獨立的函數。 由於我們將通過附加到infixNotation
級別來添加我們的驗證,並且infixNotation
僅接受解析操作,因此我們需要將您的第二個驗證規則編寫為引發異常的解析操作。 (我們也將使用一個新功能,最近才在pyparsing 2.2.0發布,在一個水平附加多個解析動作infixNotation
。)
以下是我們希望執行的驗證:
如果多位表達式var [X:Y],則值必須<2 **(Y-X + 1)
def validate_equality_args(tokens):\n tokens = tokens [0]\n z =代幣[-1]\n 如果令牌中有'位':\n 如果z不在(0,1)中:\n 引發ParseFatalException(“無效的相等值 - 必須為0或1”)\n 其他:\n x = tokens.start_bit\n y = tokens.end_bit\n 如果不是z <2 **(y - x + 1):\n 引發ParseFatalException(“無效的相等值”)
我們使用以下命令將此解析操作附加到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
(這需要擴展到支持多位,但總體思路是一樣的。)
如何將變量轉換為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
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.