首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >python为Word解析非匹配(关键字)条件

python为Word解析非匹配(关键字)条件
EN

Stack Overflow用户
提问于 2016-01-02 11:22:11
回答 1查看 729关注 0票数 2

我正在尝试创建一个解析器,解析由verilog字符串和引号字符串组成的不同类型的表达式。为了使其正常工作,我使用了MatchFirst构造。我遇到的一个问题是,我不知道如何创建一个不匹配的单词,如果后面跟着某些字符。

这个问题的简短版本

让我们假设,我想要一个词,可以接受字符'A‘和'B’,但如果他们后面有任何其他字母。所以这些应该是匹配的:

代码语言:javascript
复制
A
AB
BA
BAABBABABABA

但这不应该匹配:BABC

当前,解析器以部分匹配结束,这将导致结果混乱。

这个问题的长篇版本

这个问题与我之前问过的一个问题有关:python pyparsing "^" vs "|" keywords

下面是一个说明问题的python3测试案例。注意到如果我必须将解析器从使用MatchFirst构造更改为OR,则测试用例通过。也就是说,parser = (_get_verilog_num_parse() ^ pp.Literal("Some_demo_literal")) ^ pp.quotedString而不是parser = (_get_verilog_num_parse() ^ pp.Literal("Some_demo_literal")) | pp.quotedString,但是这也构成了一个更复杂的解析器的一部分,而且(我认为)我需要优先级来使它工作。

因此,最终的问题是,我如何才能让这场比赛不依赖于OR的“最长”匹配选择性?

TestCase

代码语言:javascript
复制
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

    See this link where I got help with geting this parser to work:
    https://stackoverflow.com/questions/34258011/python-pyparsing-vs-keywords
    """
    apos           = pp.Suppress(pp.Literal("'"))
    size_num        = pp.Word(pp.nums+'_'                  ).setParseAction(lambda x:int(x[0].replace('_', ''),10))
    #dec_num        = pp.Word(pp.nums+'_'   , asKeyword=True).setParseAction(lambda x:int(x[0].replace('_', ''),10))
    dec_num        = pp.Word(pp.nums+'_'                   ).setParseAction(lambda x:int(x[0].replace('_', ''),10))
    hex_num        = pp.Word(pp.hexnums+'_', asKeyword=True).setParseAction(lambda x:int(x[0].replace('_', ''),16))
    bin_num        = pp.Word('01'+'_',       asKeyword=True).setParseAction(lambda x:int(x[0].replace('_', ''),2))

    size           = pp.Optional(size_num).setResultsName('size')


    def size_mask(parser):
        size = parser.get('size')
        if size is not None:
            return parser['value'] & ((1<<size) -1)
        else:
            return parser['value']

    radix_int = pp.ungroup(pp.CaselessLiteral('d').suppress() + dec_num |
                           pp.CaselessLiteral('h').suppress() + hex_num |
                           pp.CaselessLiteral('b').suppress() + bin_num)
    #print(radix_int)
    return (size + apos + radix_int('value')).addParseAction(size_mask)

class test_PyParsing(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),
                ("12'h200",0x200),
                ("'blah'","'blah'"),
                ("'HARDWARE'","'HARDWARE'"),
                ("'HA'","'HA'"),
                ("'b101010'","'b101010'"),
                ("'d1010'","'d1010'"),
                ("'1010'","'1010'"),
                ]
        parser = (_get_verilog_num_parse() ^ pp.Literal("Some_demo_literal")) | pp.quotedString
        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]))

结果

代码语言:javascript
复制
self.assertEqual(expected,result[0], "test_string: {}, expected: {}, result: {}".format(expr, expected, result[0]))
AssertionError: "'HARDWARE'" != 10 : test_string: 'HARDWARE', expected: 'HARDWARE', result: 10

因此,在这里,teststring被解释为verilog数字'HA,它是10,而不是引用的字符串:'HARDWARE'

我尝试过使用asKeyword关键字参数,但我在这方面没有任何进展。

编辑

到目前为止,基于Paul的帮助,我在测试用例中添加了额外的检查,以进一步完善解决方案。我使用了Paul的建议,将asKeyword=True添加到for hex_num的定义中,这解决了我最初的问题,然后将它添加到bin_num的定义中,这也满足了添加的检查要求:

代码语言:javascript
复制
("'b101010'","'b101010'"),
("'d1010'","'d1010'"),

然后,我又添加了2张支票:

代码语言:javascript
复制
("'d1010'","'d1010'"),
("'1010'","'1010'"),

然后,测试用例失败,结果如下:

代码语言:javascript
复制
self.assertEqual(expected,result[0], "test_string: {}, expected: {}, result: {}".format(expr, expected, result[0]))
AssertionError: "'d1010'" != 1010 : test_string: 'd1010', expected: 'd1010', result: 1010

要尝试的逻辑是为asKeyword=True的定义添加dec_num。但这导致了一个奇怪的错误:

代码语言:javascript
复制
  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 2497, in parseImpl
  raise maxException
File "c:\users\gkuhn\appdata\local\continuum\anaconda3\lib\site-packages\pyparsing.py", line 2483, in parseImpl
  ret = e._parse( instring, loc, doActions )
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: Expected W:(0123...) (at char 3), (line:1, col:4)

备注

添加asKeyword=True似乎也会使数字解析工作变得混乱,而不是引用字符串。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-01-02 17:50:18

asKeyword参数用'\b'括号内的正则表达式。我认为你添加的excludeChars论点把事情搞砸了。只需将hex_num定义为:

代码语言:javascript
复制
hex_num = pp.Word(pp.hexnums+'_', asKeyword=True).setParseAction(
                                                  lambda x:int(x[0].replace('_', ''),16))

当我运行您的测试代码时,这是可行的。(我认为hexnums是3种数字中唯一一种需要这样做的数字,因为十进制和二进制对尾随字母没有任何歧义。)

FYI - excludeChars被添加到Word中,以简化定义“除‘:’以外的所有可打印的东西”或“除‘q’以外的所有在幻影中的东西”的字符组。(https://pythonhosted.org/pyparsing/pyparsing.Word-class.html)

编辑

我认为问题的一部分是,我们需要查看单个表达式中的前缀h/d/b字符和数字字符,以便对数字字符做正确的处理。我们希望在数字之后强制中断,而不是在前面的前缀和数字之间。恐怕最好的方法就是使用Regex。下面是一组表达式,它将前缀和数字组合成一个等价的正则表达式,并添加了尾随但不领先的单词中断:

代码语言:javascript
复制
make_num_expr = lambda prefix,numeric_chars,radix: pp.Regex(r"[%s%s](?P<num>[%s_]+)\b" % 
                                                                (prefix,prefix.upper(),numeric_chars)).setParseAction(
                                                                        lambda x: int(x.num.replace('_',''), radix))
dec_num = make_num_expr('d', pp.nums, 10).setName("dec_num")
hex_num = make_num_expr('h', pp.hexnums, 16).setName("hex_num")
bin_num = make_num_expr('b', '01', 2).setName("bin_num")

radix_int = (dec_num | hex_num | bin_num).setName("radix_int")

注意Regex的数值字段使用了命名组num。我还添加了setName调用,这些调用现在更重要了,因为Or和MatchFirst (正确地)枚举了所有选项( i11n )它们的异常消息。

编辑(2)

刚刚注意到我们在'HA'上失败了,我认为如果您更改解析器替代方案的顺序,就可以解决这个问题:

代码语言:javascript
复制
parser = pp.quotedString | (_get_verilog_num_parse() ^ pp.Literal("Some_demo_literal"))
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34565305

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档