python pyparsing“^”与“|”关键词
python pyparsing "^" vs "|" keywords
我创建了一个小测试用例来说明我遇到的“^”运算符问题。当我尝试使用 ^ 运算符而不是 |下面的运算符,我得到一个错误。
编辑: 只是为了让阅读它的其他人更清楚地了解问题(尽管它已经得到回答)。问题是为什么我不能使用“^”运算符代替“|”以下程序中的运算符。
测试用例:
import unittest
import pyparsing as pp
def _get_verilog_num_parse():
"""Get a parser that can read a verilog number
return: Parser for verilog numbers
rtype: PyParsing parser object
"""
apos = pp.Suppress(pp.Literal("'"))
radix = pp.Word('bdhBDH', exact=1).setResultsName('radix')
dec_num = pp.Word(pp.nums+'_' ).setParseAction(lambda x:int(x[0].replace('_', ''),10))
hex_num = pp.Word(pp.hexnums+'_').setParseAction(lambda x:int(x[0].replace('_', ''),16))
bin_num = pp.Word('01'+'_' ).setParseAction(lambda x:int(x[0].replace('_', ''),2))
size = pp.Optional(dec_num).setResultsName('size')
valid_nums = {'b':bin_num,'d':dec_num,'h':hex_num}
verilog_lits = pp.Forward()
def size_mask(parser):
size = parser.get('size')
if size is not None:
print("In size_mask. size: {} parser[value]: {}".format(size, parser['value']))
return parser['value'] & ((1<<size) -1)
else:
print("In size_mask. no size. parser[value]: {}".format(parser['value']))
return parser['value']
def radix_parse_action(toks):
verilog_lits << (valid_nums[toks.get('radix').lower()])
radix.addParseAction(radix_parse_action)
#return size, apos + radix + verilog_lits
return (size + apos + radix + verilog_lits.setResultsName('value')).addParseAction(size_mask)
class CheckPyParsing(unittest.TestCase):
'''Check that the Expression Parser works with the expressions
defined in this test'''
def test_or(self):
"""Check basic expressions not involving referenced parameters"""
expressions_to_test = [
("8'd255",255),
("'d255",255),
]
parser = _get_verilog_num_parse() | pp.Literal("Some_demo_literal")
for expr,expected in expressions_to_test:
result = parser.parseString(expr)
print("result: {}, val: {}".format(result, result[0]))
self.assertEqual(expected,result[0], "test_string: {} expected: {} result: {}".format(expr, expected, result[0]))
当我使用 |我明白了:
test_or (yoda_interface.tests.CheckPyParsing_test.CheckPyParsing)
Check basic expressions not involving referenced parameters ... In size_mask. size: 8 parser[value]: 255
result: [255], val: 255
In size_mask. no size. parser[value]: 255
result: [255], val: 255
ok
当我使用 ^ 我得到:
test_or (yoda_interface.tests.CheckPyParsing_test.CheckPyParsing)
Check basic expressions not involving referenced parameters ... ERROR
======================================================================
ERROR: test_or (yoda_interface.tests.CheckPyParsing_test.CheckPyParsing)
Check basic expressions not involving referenced parameters
----------------------------------------------------------------------
Traceback (most recent call last):
File "c:\projects\check_private\yoda_interface\tests\CheckPyParsing_test.py", line 45, in test_or
result = parser.parseString(expr)
File "C:\Users\gkuhn\AppData\Local\Continuum\Anaconda3\lib\site-packages\pyparsing.py", line 1125, in parseString
raise exc
File "C:\Users\gkuhn\AppData\Local\Continuum\Anaconda3\lib\site-packages\pyparsing.py", line 1115, in parseString
loc, tokens = self._parse( instring, 0 )
File "C:\Users\gkuhn\AppData\Local\Continuum\Anaconda3\lib\site-packages\pyparsing.py", line 989, in _parseNoCache
loc,tokens = self.parseImpl( instring, preloc, doActions )
File "C:\Users\gkuhn\AppData\Local\Continuum\Anaconda3\lib\site-packages\pyparsing.py", line 2440, in parseImpl
raise maxException
pyparsing.ParseException: (at char 3), (line:1, col:4)
----------------------------------------------------------------------
Ran 1 test in 0.012s
FAILED (errors=1)
这是一个困难的案例,需要对 pyparsing 的一些内部结构有一点了解才能将您的解析器重构为工作版本。
您的 "perfect storm" 个问题综合了这些因素:
- 一个动态解析器元素(
verilog_lits
)
- 动态定义相关解析器元素内容的解析操作
- 将该解析操作附加到
Or
表达式中的元素
MatchFirst
(使用“|”运算符创建),只需 c运行ch 遍历其备选方案列表,尝试依次解析每个备选方案,并在成功时返回。这样做的话,如果有解析动作,那么解析成功后,解析动作得到运行,符合预期。在您的情况下,此解析操作会为二进制、十六进制或十进制值的值部分注入正确的数字表达式。
但是Or
不能遵循同样的策略。在编写 pyparsing 时,除了仅使用已解析的标记外,我无法预测任何给定的解析操作是否具有副作用或其他含义。因此,当 Or
遍历其备选方案时,寻找 最长的 匹配项时,它必须 不 调用解析操作。如果在解析器中有更新动态元素的解析操作,则在选择成功的替代项之前不会调用该操作。由于您依赖解析操作来完成解析器,因此如果定义动态表达式的触发器是 Or
.
的一部分,这将失败
基于此,我重构了您对 "a radix followed by its type-specific allowed value" 的定义,替换为:
return (size + apos + radix + verilog_lits.setResultsName('value')).addParseAction(size_mask)
和
radix_int = pp.ungroup(pp.CaselessLiteral('d').suppress() + dec_num |
pp.CaselessLiteral('h').suppress() + hex_num |
pp.CaselessLiteral('b').suppress() + bin_num)
return (size + apos + radix_int('value')).addParseAction(size_mask)
这可能没有动态子表达式的魅力,但通过将动态表达式扩展为一组 3 个特定的替代项,该表达式现在可以安全地包含在 "evaluate all and choose the longest" Or
中表达式。
我创建了一个小测试用例来说明我遇到的“^”运算符问题。当我尝试使用 ^ 运算符而不是 |下面的运算符,我得到一个错误。
编辑: 只是为了让阅读它的其他人更清楚地了解问题(尽管它已经得到回答)。问题是为什么我不能使用“^”运算符代替“|”以下程序中的运算符。
测试用例:
import unittest
import pyparsing as pp
def _get_verilog_num_parse():
"""Get a parser that can read a verilog number
return: Parser for verilog numbers
rtype: PyParsing parser object
"""
apos = pp.Suppress(pp.Literal("'"))
radix = pp.Word('bdhBDH', exact=1).setResultsName('radix')
dec_num = pp.Word(pp.nums+'_' ).setParseAction(lambda x:int(x[0].replace('_', ''),10))
hex_num = pp.Word(pp.hexnums+'_').setParseAction(lambda x:int(x[0].replace('_', ''),16))
bin_num = pp.Word('01'+'_' ).setParseAction(lambda x:int(x[0].replace('_', ''),2))
size = pp.Optional(dec_num).setResultsName('size')
valid_nums = {'b':bin_num,'d':dec_num,'h':hex_num}
verilog_lits = pp.Forward()
def size_mask(parser):
size = parser.get('size')
if size is not None:
print("In size_mask. size: {} parser[value]: {}".format(size, parser['value']))
return parser['value'] & ((1<<size) -1)
else:
print("In size_mask. no size. parser[value]: {}".format(parser['value']))
return parser['value']
def radix_parse_action(toks):
verilog_lits << (valid_nums[toks.get('radix').lower()])
radix.addParseAction(radix_parse_action)
#return size, apos + radix + verilog_lits
return (size + apos + radix + verilog_lits.setResultsName('value')).addParseAction(size_mask)
class CheckPyParsing(unittest.TestCase):
'''Check that the Expression Parser works with the expressions
defined in this test'''
def test_or(self):
"""Check basic expressions not involving referenced parameters"""
expressions_to_test = [
("8'd255",255),
("'d255",255),
]
parser = _get_verilog_num_parse() | pp.Literal("Some_demo_literal")
for expr,expected in expressions_to_test:
result = parser.parseString(expr)
print("result: {}, val: {}".format(result, result[0]))
self.assertEqual(expected,result[0], "test_string: {} expected: {} result: {}".format(expr, expected, result[0]))
当我使用 |我明白了:
test_or (yoda_interface.tests.CheckPyParsing_test.CheckPyParsing)
Check basic expressions not involving referenced parameters ... In size_mask. size: 8 parser[value]: 255
result: [255], val: 255
In size_mask. no size. parser[value]: 255
result: [255], val: 255
ok
当我使用 ^ 我得到:
test_or (yoda_interface.tests.CheckPyParsing_test.CheckPyParsing)
Check basic expressions not involving referenced parameters ... ERROR
======================================================================
ERROR: test_or (yoda_interface.tests.CheckPyParsing_test.CheckPyParsing)
Check basic expressions not involving referenced parameters
----------------------------------------------------------------------
Traceback (most recent call last):
File "c:\projects\check_private\yoda_interface\tests\CheckPyParsing_test.py", line 45, in test_or
result = parser.parseString(expr)
File "C:\Users\gkuhn\AppData\Local\Continuum\Anaconda3\lib\site-packages\pyparsing.py", line 1125, in parseString
raise exc
File "C:\Users\gkuhn\AppData\Local\Continuum\Anaconda3\lib\site-packages\pyparsing.py", line 1115, in parseString
loc, tokens = self._parse( instring, 0 )
File "C:\Users\gkuhn\AppData\Local\Continuum\Anaconda3\lib\site-packages\pyparsing.py", line 989, in _parseNoCache
loc,tokens = self.parseImpl( instring, preloc, doActions )
File "C:\Users\gkuhn\AppData\Local\Continuum\Anaconda3\lib\site-packages\pyparsing.py", line 2440, in parseImpl
raise maxException
pyparsing.ParseException: (at char 3), (line:1, col:4)
----------------------------------------------------------------------
Ran 1 test in 0.012s
FAILED (errors=1)
这是一个困难的案例,需要对 pyparsing 的一些内部结构有一点了解才能将您的解析器重构为工作版本。
您的 "perfect storm" 个问题综合了这些因素:
- 一个动态解析器元素(
verilog_lits
) - 动态定义相关解析器元素内容的解析操作
- 将该解析操作附加到
Or
表达式中的元素
MatchFirst
(使用“|”运算符创建),只需 c运行ch 遍历其备选方案列表,尝试依次解析每个备选方案,并在成功时返回。这样做的话,如果有解析动作,那么解析成功后,解析动作得到运行,符合预期。在您的情况下,此解析操作会为二进制、十六进制或十进制值的值部分注入正确的数字表达式。
但是Or
不能遵循同样的策略。在编写 pyparsing 时,除了仅使用已解析的标记外,我无法预测任何给定的解析操作是否具有副作用或其他含义。因此,当 Or
遍历其备选方案时,寻找 最长的 匹配项时,它必须 不 调用解析操作。如果在解析器中有更新动态元素的解析操作,则在选择成功的替代项之前不会调用该操作。由于您依赖解析操作来完成解析器,因此如果定义动态表达式的触发器是 Or
.
基于此,我重构了您对 "a radix followed by its type-specific allowed value" 的定义,替换为:
return (size + apos + radix + verilog_lits.setResultsName('value')).addParseAction(size_mask)
和
radix_int = pp.ungroup(pp.CaselessLiteral('d').suppress() + dec_num |
pp.CaselessLiteral('h').suppress() + hex_num |
pp.CaselessLiteral('b').suppress() + bin_num)
return (size + apos + radix_int('value')).addParseAction(size_mask)
这可能没有动态子表达式的魅力,但通过将动态表达式扩展为一组 3 个特定的替代项,该表达式现在可以安全地包含在 "evaluate all and choose the longest" Or
中表达式。