python pyparsing“^”vs“|”关键字

时间:2015-12-14 00:15:26

标签: python pyparsing

我创建了一个小的测试用例来说明我用“^”运算符看到的问题。当我尝试使用^运算符而不是|下面的运算符,我收到一个错误。

编辑:只是为了让其他人阅读它,让问题更清晰(尽管已经得到了回答)。问题是为什么我不能使用“^”运算符代替“|”运算符在以下程序中。

测试用例:

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)

1 个答案:

答案 0 :(得分:3)

这是一个很难的案例,它需要对一些pyparsing的内部结构有一点了解,才能将解析器重构为工作版本。

问题的“完美风暴”结合了以下因素:

  • 动态解析器​​元素(verilog_lits
  • 动态定义相关解析器元素内容的解析操作
  • 将该解析操作附加到Or表达式
  • 中的元素

MatchFirst(使用'|'运算符创建),可以直接查看其替代列表,尝试依次解析每个,并在成功时返回。这样做,如果存在解析操作,则在解析成功之后,解析操作将按预期运行。在您的情况下,此解析操作会为二进制,十六进制或十进制值的值部分注入正确的数值表达式。

但是Or不能遵循同样的策略。在编写pyparsing时,除了只使用解析后的标记之外,我无法预测任何给定的解析操作是否有副作用或其他含义。因此,当Or通过其替代方案,寻找最长的匹配时,它必须这样做而不用调用解析操作。如果存在更新解析器中的动态元素的解析操作,则在选择成功的替代选项之前不会调用该操作。由于您依赖解析操作来完成解析器,因此如果定义动态表达式的触发器是Or的一部分,则会失败。

基于此,我通过替换以下内容重构了您对“基数后跟其类型特定允许值”的定义:

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个特定的替代方案,这个表达式现在可以安全地包含在“全部计算并选择最长”{{1}表达。