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 中表达式。